GUI -- TopoView - re-instated Instance selection showing mastership of switches.
- default showHosts to equal false.
- fixed bug on link immediate restyling on theme change.

Change-Id: I3fc456086cc104df456725290f285a20309cdfe8
diff --git a/web/gui/src/main/webapp/app/view/topo/topo.css b/web/gui/src/main/webapp/app/view/topo/topo.css
index 5b8c190..b9613d4 100644
--- a/web/gui/src/main/webapp/app/view/topo/topo.css
+++ b/web/gui/src/main/webapp/app/view/topo/topo.css
@@ -300,6 +300,10 @@
 
 /* --- Topo Nodes --- */
 
+#ov-topo svg .suppressed {
+    opacity: 0.2 !important;
+}
+
 #ov-topo svg .node {
     cursor: pointer;
 }
diff --git a/web/gui/src/main/webapp/app/view/topo/topo.js b/web/gui/src/main/webapp/app/view/topo/topo.js
index 41edfc4..ca24d8a 100644
--- a/web/gui/src/main/webapp/app/view/topo/topo.js
+++ b/web/gui/src/main/webapp/app/view/topo/topo.js
@@ -103,9 +103,9 @@
     }
 
     function handleEscape() {
-        if (false) {
-            // TODO: if an instance is selected, cancel the affinity mapping
-            // tis.cancelAffinity()
+        if (tis.showMaster()) {
+            // if an instance is selected, cancel the affinity mapping
+            tis.cancelAffinity()
 
         } else if (tss.haveDetails()) {
             // else if we have node selections, deselect them all
@@ -288,7 +288,7 @@
 
             forceG = zoomLayer.append('g').attr('id', 'topo-force');
             tfs.initForce(forceG, uplink, dim);
-            tis.initInst();
+            tis.initInst({ showMastership: tfs.showMastership });
             tps.initPanels({ sendEvent: tes.sendEvent });
             tes.openSock();
 
diff --git a/web/gui/src/main/webapp/app/view/topo/topoForce.js b/web/gui/src/main/webapp/app/view/topo/topoForce.js
index 8f15b947..10e924f 100644
--- a/web/gui/src/main/webapp/app/view/topo/topoForce.js
+++ b/web/gui/src/main/webapp/app/view/topo/topoForce.js
@@ -73,7 +73,7 @@
         rlk = network.revLinkToKey,
         deviceLabelIndex = 0,   // for device label cycling
         hostLabelIndex = 0,     // for host label cycling
-        showHosts = true,       // whether hosts are displayed
+        showHosts = false,      // whether hosts are displayed
         showOffline = true,     // whether offline devices are displayed
         oblique = false,        // whether we are in the oblique view
         nodeLock = false,       // whether nodes can be dragged or not (locked)
@@ -287,14 +287,15 @@
             .clamp(true),
         allLinkTypes = 'direct indirect optical tunnel';
 
-    function restyleLinkElement(ldata) {
+    function restyleLinkElement(ldata, immediate) {
         // this fn's job is to look at raw links and decide what svg classes
         // need to be applied to the line element in the DOM
         var th = ts.theme(),
             el = ldata.el,
             type = ldata.type(),
             lw = ldata.linkWidth(),
-            online = ldata.online();
+            online = ldata.online(),
+            delay = immediate ? 0 : 1000;
 
         el.classed('link', true);
         el.classed('inactive', !online);
@@ -303,7 +304,7 @@
             el.classed(type, true);
         }
         el.transition()
-            .duration(1000)
+            .duration(delay)
             .attr('stroke-width', linkScale(lw))
             .attr('stroke', linkConfig[th].baseColor);
     }
@@ -548,6 +549,35 @@
         }
     }
 
+    function showMastership(masterId) {
+        if (!masterId) {
+            restoreLayerState();
+        } else {
+            showMastershipFor(masterId);
+        }
+    }
+
+    function restoreLayerState() {
+        // NOTE: this level of indirection required, for when we have
+        //          the layer filter functionality re-implemented
+        suppressLayers(false);
+    }
+
+    function showMastershipFor(id) {
+        suppressLayers(true);
+        node.each(function (n) {
+            if (n.master === id) {
+                n.el.classed('suppressed', false);
+            }
+        });
+    }
+
+    function suppressLayers(b) {
+        node.classed('suppressed', b);
+        link.classed('suppressed', b);
+//        d3.selectAll('svg .port').classed('inactive', b);
+//        d3.selectAll('svg .portText').classed('inactive', b);
+    }
 
     // ==========================================
 
@@ -777,7 +807,7 @@
             .data(network.links, function (d) { return d.key; });
 
         // operate on existing links:
-        //link.each(linkExisting);
+        link.each(linkExisting);
 
         // operate on entering links:
         var entering = link.enter()
@@ -842,7 +872,9 @@
         return data;
     }
 
-    //function linkExisting(d) { }
+    function linkExisting(d) {
+        restyleLinkElement(d, true);
+    }
 
     function linkEntering(d) {
         var link = d3.select(this);
@@ -1067,6 +1099,11 @@
 
             icfg = is.iconConfig();
 
+            var themeListener = ts.addListener(function () {
+                updateLinks();
+                updateNodes();
+            });
+
             // forceG is the SVG group to display the force layout in
             // uplink is the api from the main topo source file
             // dim is the initial dimensions of the SVG as [w,h]
@@ -1117,6 +1154,8 @@
                 tts.destroyTraffic();
                 tss.destroySelect();
                 tms.destroyModel();
+                ts.removeListener(themeListener);
+                themeListener = null;
             }
 
             return {
@@ -1129,6 +1168,7 @@
                 toggleOffline: toggleOffline,
                 cycleDeviceLabels: cycleDeviceLabels,
                 unpin: unpin,
+                showMastership: showMastership,
 
                 addDevice: addDevice,
                 updateDevice: updateDevice,
diff --git a/web/gui/src/main/webapp/app/view/topo/topoInst.js b/web/gui/src/main/webapp/app/view/topo/topoInst.js
index 49e129c..718fec3 100644
--- a/web/gui/src/main/webapp/app/view/topo/topoInst.js
+++ b/web/gui/src/main/webapp/app/view/topo/topoInst.js
@@ -25,6 +25,12 @@
     // injected refs
     var $log, ps, sus, gs, ts, fs;
 
+    // api from topo
+    var api;
+    /*
+        showMastership( id )
+     */
+
     // configuration
     var instCfg = {
             rectPad: 8,
@@ -120,13 +126,8 @@
             .classed('affinity', false);
         el.classed('affinity', true);
 
-        // TODO: suppress the layers and highlight only specific nodes...
-        //suppressLayers(true);
-        //node.each(function (n) {
-        //    if (n.master === d.id) {
-        //        n.el.classed('suppressed', false);
-        //    }
-        //});
+        // suppress all elements except nodes whose master is this instance
+        api.showMastership(d.id);
         oiShowMaster = true;
     }
 
@@ -134,8 +135,7 @@
         d3.selectAll('.onosInst')
             .classed('mastership affinity', false);
 
-        // TODO: restore layer state
-        //restoreLayerState();
+        api.showMastership(null);
         oiShowMaster = false;
     }
 
@@ -284,7 +284,8 @@
         }
     }
 
-    function initInst() {
+    function initInst(_api_) {
+        api = _api_;
         oiBox = ps.createPanel(idIns, instOpts);
         oiBox.show();
 
@@ -327,10 +328,13 @@
                 updateInstance: updateInstance,
                 removeInstance: removeInstance,
 
+                cancelAffinity: cancelAffinity,
+
                 isVisible: function () { return oiBox.isVisible(); },
                 show: function () { oiBox.show(); },
                 hide: function () { oiBox.hide(); },
-                toggle: function () { oiBox.toggle(); }
+                toggle: function () { oiBox.toggle(); },
+                showMaster: function () { return oiShowMaster; }
             };
         }]);
 }());