Topo2: unpin nodes when a node is hovered and 'U' us pressed

Change-Id: I5f25243073ea7e32354c4777576ad402e6124296
diff --git a/web/gui/src/main/webapp/app/view/topo2/topo2-theme.css b/web/gui/src/main/webapp/app/view/topo2/topo2-theme.css
index a90bae5..7ce65ad 100644
--- a/web/gui/src/main/webapp/app/view/topo2/topo2-theme.css
+++ b/web/gui/src/main/webapp/app/view/topo2/topo2-theme.css
@@ -187,12 +187,16 @@
     fill: #454545;
 }
 
-
 #ov-topo2 svg .node.selected .node-container {
     stroke-width: 2.0;
     stroke: #009fdb;
 }
 
+#ov-topo2 svg .node.hovered .node-container {
+    stroke-width: 2.0;
+    stroke: #454545;
+}
+
 /* Badges */
 /* (... works for bothand dark themes...) */
 #ov-topo2 svg .node .badge circle {
diff --git a/web/gui/src/main/webapp/app/view/topo2/topo2Device.js b/web/gui/src/main/webapp/app/view/topo2/topo2Device.js
index ddb9b55..823387c 100644
--- a/web/gui/src/main/webapp/app/view/topo2/topo2Device.js
+++ b/web/gui/src/main/webapp/app/view/topo2/topo2Device.js
@@ -74,6 +74,7 @@
                     },
                     onClick: function () {
 
+                        if (d3.event.defaultPrevented) return;
                         var selected = this.select(d3.event);
 
                         if (_.isArray(selected) && selected.length > 0) {
diff --git a/web/gui/src/main/webapp/app/view/topo2/topo2Force.js b/web/gui/src/main/webapp/app/view/topo2/topo2Force.js
index d034b7a..78f37a3 100644
--- a/web/gui/src/main/webapp/app/view/topo2/topo2Force.js
+++ b/web/gui/src/main/webapp/app/view/topo2/topo2Force.js
@@ -84,7 +84,6 @@
     function currentRegion(data) {
         $log.debug('>> topo2CurrentRegion event:', data);
         t2rs.addRegion(data);
-        t2ls.createForceLayout();
     }
 
     function topo2PeerRegions(data) {
@@ -169,14 +168,16 @@
     }
 
     function unpin() {
+
         var hovered = t2rs.filterRegionNodes(function (model) {
             return model.get('hovered');
         });
 
         angular.forEach(hovered, function (model) {
-            model.fixed = false;
-            model.el.classed('fixed', false);
+            model.fix(false);
         });
+
+        t2ls.start();
     }
 
     angular.module('ovTopo2')
diff --git a/web/gui/src/main/webapp/app/view/topo2/topo2Host.js b/web/gui/src/main/webapp/app/view/topo2/topo2Host.js
index 40755fe..787819b 100644
--- a/web/gui/src/main/webapp/app/view/topo2/topo2Host.js
+++ b/web/gui/src/main/webapp/app/view/topo2/topo2Host.js
@@ -68,6 +68,7 @@
                     }
                 },
                 onClick: function () {
+                    if (d3.event.defaultPrevented) return;
                     var selected = this.select(d3.select);
 
                     if (selected.length > 0) {
diff --git a/web/gui/src/main/webapp/app/view/topo2/topo2Layout.js b/web/gui/src/main/webapp/app/view/topo2/topo2Layout.js
index c3bfd58..36a5ced 100644
--- a/web/gui/src/main/webapp/app/view/topo2/topo2Layout.js
+++ b/web/gui/src/main/webapp/app/view/topo2/topo2Layout.js
@@ -33,22 +33,19 @@
             // note: key is node.class
             device: -8000,
             host: -20000,
-            region: -5000,
+            region: -8000,
             _def_: -12000
         },
         linkDistance: {
             // note: key is link.type
             direct: 100,
             optical: 120,
-            UiEdgeLink: 30,
+            UiEdgeLink: 100,
             _def_: 50
         },
         linkStrength: {
             // note: key is link.type
             // range: {0.0 ... 1.0}
-            direct: 1.0,
-            optical: 1.0,
-            UiEdgeLink: 15.0,
             _def_: 1.0
         }
     };
@@ -245,7 +242,8 @@
 
                         entering.filter('.device').each(t2d3.nodeEnter);
                         entering.filter('.sub-region').each(t2d3.nodeEnter);
-                        entering.filter('.host').each(t2d3.hostEnter);
+                        entering.filter('.host').each(t2d3.nodeEnter);
+                        entering.filter('.peer-region').each(t2d3.nodeEnter);
                     },
                     updateLinks: function () {
 
@@ -267,7 +265,7 @@
                                 'stroke-width': linkConfig.inWidth
                             });
 
-                        entering.each(t2d3.linkEntering);
+                        entering.each(t2d3.nodeEnter);
 
                         // operate on exiting links:
                         this.link.exit()
@@ -333,8 +331,7 @@
                     },
                     atDragEnd: function (d) {
                         // once we've finished moving, pin the node in position
-                        d.fixed = true;
-                        d3.select(this).classed('fixed', true);
+                        d.fix(true);
                         instance.sendUpdateMeta(d);
                         t2ss.clickConsumed(true);
                     },
diff --git a/web/gui/src/main/webapp/app/view/topo2/topo2NodeModel.js b/web/gui/src/main/webapp/app/view/topo2/topo2NodeModel.js
index c713dc0..94ab15f 100644
--- a/web/gui/src/main/webapp/app/view/topo2/topo2NodeModel.js
+++ b/web/gui/src/main/webapp/app/view/topo2/topo2NodeModel.js
@@ -109,10 +109,14 @@
                     });
                 },
                 mouseoverHandler: function () {
-                    this.set('hovered', true, { silent: true });
+                    this.set('hovered', true);
                 },
                 mouseoutHandler: function () {
-                    this.set('hovered', false, { silent: true });
+                    this.set('hovered', false);
+                },
+                fix: function (fixed) {
+                    this.set({ fixed: fixed });
+                    this.fixed = fixed;
                 },
                 icon: function () {
                     return 'unknown';
@@ -195,7 +199,9 @@
                         this.get('type'),
                         {
                             online: this.get('online'),
-                            selected: this.get('selected')
+                            selected: this.get('selected'),
+                            hovered: this.get('hovered'),
+                            fixed: this.get('fixed')
                         }
                     );
                 },
diff --git a/web/gui/src/main/webapp/app/view/topo2/topo2NodePosition.js b/web/gui/src/main/webapp/app/view/topo2/topo2NodePosition.js
index f30401f..2c78a00 100644
--- a/web/gui/src/main/webapp/app/view/topo2/topo2NodePosition.js
+++ b/web/gui/src/main/webapp/app/view/topo2/topo2NodePosition.js
@@ -43,7 +43,7 @@
 
         // else if we have [x,y] cached in meta data, use that...
         if (x !== undefined && y !== undefined) {
-            node.fixed = true;
+            node.fix(true);
             node.px = node.x = x;
             node.py = node.y = y;
             return;
@@ -100,7 +100,7 @@
             }
 
             coord = coordFromLngLat(loc);
-            el.fixed = true;
+            el.fix(true);
             el.x = el.px = coord[0];
             el.y = el.py = coord[1];
 
@@ -114,7 +114,7 @@
             }
 
             coord = coordFromXY(loc);
-            el.fixed = true;
+            el.fix(true);
             el.x = el.px = coord[0];
             el.y = el.py = coord[1];
 
diff --git a/web/gui/src/main/webapp/app/view/topo2/topo2Region.js b/web/gui/src/main/webapp/app/view/topo2/topo2Region.js
index 54c88c7..5895871 100644
--- a/web/gui/src/main/webapp/app/view/topo2/topo2Region.js
+++ b/web/gui/src/main/webapp/app/view/topo2/topo2Region.js
@@ -37,7 +37,8 @@
         'Topo2HostService', 'Topo2LinkService', 'Topo2ZoomService', 'Topo2DetailsPanelService',
         'Topo2BreadcrumbService', 'Topo2ViewController', 'Topo2SpriteLayerService', 'Topo2MapService',
         'Topo2MapConfigService',
-        function ($log, _Model_, t2sr, t2ds, t2hs, t2ls, t2zs, t2dps, t2bcs, ViewController, t2sls, t2ms, t2mcs) {
+        function ($log, _Model_, t2sr, t2ds, t2hs, t2ls, t2zs, t2dps, t2bcs, ViewController,
+                  t2sls, t2ms, t2mcs) {
 
             Model = _Model_;
 
@@ -46,6 +47,13 @@
                     instance = this;
                     this.model = null;
                     this.bgRendered = false;
+
+                    var RegionModel = Model.extend({
+                        findNodeById: this.findNodeById,
+                        nodes: this.regionNodes.bind(this)
+                    });
+
+                    this.model = new RegionModel();
                 },
                 backgroundRendered: function () {
                     this.bgRendered = true;
@@ -64,17 +72,9 @@
                 },
                 startRegion: function () {
 
-                    var RegionModel = Model.extend({
-                        findNodeById: this.findNodeById,
-                        nodes: this.regionNodes.bind(this)
-                    });
-
-                    this.model = new RegionModel({
-                        id: this.regionData.id,
-                        layerOrder: this.regionData.layerOrder
-                    });
-
                     this.model.set({
+                        id: this.regionData.id,
+                        layerOrder: this.regionData.layerOrder,
                         subregions: t2sr.createSubRegionCollection(this.regionData.subregions, this),
                         devices: t2ds.createDeviceCollection(this.regionData.devices, this),
                         hosts: t2hs.createHostCollection(this.regionData.hosts, this),
@@ -90,7 +90,7 @@
                         t2bcs.hide();
                     }
 
-                    // this.layout.update();
+                    this.layout.createForceLayout();
                 },
                 clear: function () {
 
@@ -122,7 +122,6 @@
                         this.model.get('subregions').get(id);
                 },
                 regionNodes: function () {
-
                     if (this.model) {
                         return [].concat(
                             this.model.get('devices').models,
@@ -130,11 +129,10 @@
                             this.model.get('subregions').models
                         );
                     }
-
                     return [];
                 },
                 regionLinks: function () {
-                    return (this.model) ? this.model.get('links').models : [];
+                    return this.model.get('links').models;
                 },
                 getLink: function (linkId) {
                     return this.model.get('links').get(linkId);
diff --git a/web/gui/src/main/webapp/app/view/topo2/topo2Select.js b/web/gui/src/main/webapp/app/view/topo2/topo2Select.js
index ad99749..242d55a0 100644
--- a/web/gui/src/main/webapp/app/view/topo2/topo2Select.js
+++ b/web/gui/src/main/webapp/app/view/topo2/topo2Select.js
@@ -122,7 +122,11 @@
             return mdist(p, m) <= proximity;
         }
 
-        var links = t2rs.regionLinks();
+        var links = [];
+
+        if (t2rs.model.get('links')) {
+            links = (t2rs.backgroundRendered) ? t2rs.regionLinks() : [];
+        }
 
         if (links.length) {
             minDist = proximity * 2;
diff --git a/web/gui/src/main/webapp/app/view/topo2/topo2SubRegion.js b/web/gui/src/main/webapp/app/view/topo2/topo2SubRegion.js
index b3259d4..02c8b73 100644
--- a/web/gui/src/main/webapp/app/view/topo2/topo2SubRegion.js
+++ b/web/gui/src/main/webapp/app/view/topo2/topo2SubRegion.js
@@ -68,6 +68,8 @@
                     return remappedDeviceTypes[type] || type || 'm_cloud';
                 },
                 onClick: function () {
+                    if (d3.event.defaultPrevented) return;
+
                     var selected = this.select(d3.event);
 
                     if (selected.length > 0) {