GUI -- Fixed turbulent behavior of incoming nodes with no fixed position.
Change-Id: Ic73c0c8b91bd5ab07faec84ffcd0b67d2e357b29
diff --git a/web/gui/src/main/webapp/topo2.css b/web/gui/src/main/webapp/topo2.css
index aeaad2d..2dd2a05 100644
--- a/web/gui/src/main/webapp/topo2.css
+++ b/web/gui/src/main/webapp/topo2.css
@@ -24,14 +24,33 @@
opacity: 0.5;
}
+
/* NODES */
-#topo svg .node.device {
- stroke: none;
- stroke-width: 1.5px;
+#topo svg .node {
cursor: pointer;
}
+#topo svg .node.selected rect,
+#topo svg .node.selected circle {
+ filter: url(#blue-glow);
+}
+
+/* for debugging */
+#topo svg .node circle.debug {
+ fill: white;
+ stroke: red;
+}
+
+#topo svg .node text {
+ pointer-events: none;
+}
+
+/* Device Nodes */
+
+#topo svg .node.device {
+}
+
#topo svg .node.device rect {
stroke-width: 1.5px;
}
@@ -54,31 +73,28 @@
fill: #03c;
}
-#topo svg .node.host {
- fill: #846;
-}
-
/* note: device is offline without the 'online' class */
#topo svg .node.device text {
fill: #aaa;
font: 10pt sans-serif;
- pointer-events: none;
}
#topo svg .node.device.online text {
fill: white;
}
+
+/* Host Nodes */
+
+#topo svg .node.host {
+ fill: #846;
+}
+
#topo svg .node.host text {
fill: #846;
font: 9pt sans-serif;
- pointer-events: none;
}
-#topo svg .node.selected rect,
-#topo svg .node.selected circle {
- filter: url(#blue-glow);
-}
/* LINKS */
@@ -91,20 +107,13 @@
stroke-width: 6px;
}
-/* for debugging */
-#topo svg .node circle.debug {
- fill: white;
- stroke: red;
-}
-
-/* detail topo-detail pane */
+/* Fly-in details pane */
#topo-detail {
/* gets base CSS from .fpanel in floatPanel.css */
}
-
#topo-detail h2 {
margin: 8px 4px;
color: black;
@@ -128,7 +137,6 @@
}
#topo-detail td.value {
-
}
#topo-detail hr {
diff --git a/web/gui/src/main/webapp/topo2.js b/web/gui/src/main/webapp/topo2.js
index a23f48d..681562e 100644
--- a/web/gui/src/main/webapp/topo2.js
+++ b/web/gui/src/main/webapp/topo2.js
@@ -127,7 +127,8 @@
P: togglePorts,
U: unpin,
- X: requestPath
+ Z: requestPath,
+ X: cancelMonitor
};
// state variables
@@ -518,6 +519,13 @@
sendMessage('requestPath', payload);
}
+ function cancelMonitor() {
+ var payload = {
+ id: "need_the_intent_id" // FIXME: where are we storing this?
+ };
+ sendMessage('cancelMonitor', payload);
+ }
+
// request details for the selected element
function requestDetails() {
var data = getSel(0).obj,
@@ -701,18 +709,57 @@
function positionNode(node) {
var meta = node.metaUi,
- x = 0,
- y = 0;
+ x = meta && meta.x,
+ y = meta && meta.y,
+ xy;
- if (meta) {
- x = meta.x;
- y = meta.y;
- }
+ // If we have [x,y] already, use that...
if (x && y) {
node.fixed = true;
+ node.x = x;
+ node.y = y;
+ return;
}
- node.x = x || network.view.width() / 2;
- node.y = y || network.view.height() / 2;
+
+ // Note: Placing incoming unpinned nodes at exactly the same point
+ // (center of the view) causes them to explode outwards when
+ // the force layout kicks in. So, we spread them out a bit
+ // initially, to provide a more serene layout convergence.
+ // Additionally, if the node is a host, we place it near
+ // the device it is connected to.
+
+ function spread(s) {
+ return Math.floor((Math.random() * s) - s/2);
+ }
+
+ function randDim(dim) {
+ return dim / 2 + spread(dim * 0.7071);
+ }
+
+ function rand() {
+ return {
+ x: randDim(network.view.width()),
+ y: randDim(network.view.height())
+ };
+ }
+
+ function near(node) {
+ var min = 12,
+ dx = spread(12),
+ dy = spread(12);
+ return {
+ x: node.x + min + dx,
+ y: node.y + min + dy
+ };
+ }
+
+ function getDevice(cp) {
+ var d = network.lookup[cp.device];
+ return d || rand();
+ }
+
+ xy = (node.class === 'host') ? near(getDevice(node.cp)) : rand();
+ $.extend(node, xy);
}
function iconUrl(d) {