GUI -- TopoView - Added Toggle Port Highlighting enable/disable with 'P' key.
 - add/remove mousemove handler.
 - removed commented out test code.

Change-Id: Ice47db36491d466d2d73f6cef1dfc90ff9d8b088
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 2aebee8..4463936 100644
--- a/web/gui/src/main/webapp/app/view/topo/topo.js
+++ b/web/gui/src/main/webapp/app/view/topo/topo.js
@@ -49,7 +49,7 @@
             H: [tfs.toggleHosts, 'Toggle host visibility'],
             M: [tfs.toggleOffline, 'Toggle offline visibility'],
             B: [toggleMap, 'Toggle background map'],
-            //P: togglePorts,
+            P: [tfs.togglePorts, 'Toggle Port Highlighting'],
 
             //X: [toggleNodeLock, 'Lock / unlock node positions'],
             Z: [tos.toggleOblique, 'Toggle oblique view (Experimental)'],
@@ -69,7 +69,7 @@
             esc: handleEscape,
 
             _helpFormat: [
-                ['O', 'I', 'D', '-', 'H', 'M', 'B', 'P' ],
+                ['O', 'I', 'D', '-', 'H', 'M', 'P', 'B' ],
                 ['X', 'Z', 'L', 'U', 'R' ],
                 ['V', 'rightArrow', 'leftArrow', 'W', 'A', 'F', '-', 'E' ]
             ]
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 3660973..6a84f73 100644
--- a/web/gui/src/main/webapp/app/view/topo/topoForce.js
+++ b/web/gui/src/main/webapp/app/view/topo/topoForce.js
@@ -735,10 +735,9 @@
         };
     }
 
-    function mkLinkApi(svg, forceG, uplink) {
+    function mkLinkApi(svg, uplink) {
         return {
             svg: svg,
-            forceG: forceG,
             zoomer: uplink.zoomer(),
             network: network,
             portLabelG: function () { return portLabelG; },
@@ -793,7 +792,7 @@
                 tts.initTraffic(mkTrafficApi(uplink));
                 tos.initOblique(mkObliqueApi(uplink, fltr));
                 fltr.initFilter(mkFilterApi(uplink), d3.select('#mast-right'));
-                tls.initLink(mkLinkApi(svg, forceG, uplink), td3);
+                tls.initLink(mkLinkApi(svg, uplink), td3);
 
                 settings = angular.extend({}, defaultSettings, opts);
 
@@ -847,6 +846,7 @@
 
                 updateDeviceColors: td3.updateDeviceColors,
                 toggleHosts: toggleHosts,
+                togglePorts: tls.togglePorts,
                 toggleOffline: toggleOffline,
                 cycleDeviceLabels: cycleDeviceLabels,
                 unpin: unpin,
diff --git a/web/gui/src/main/webapp/app/view/topo/topoLink.js b/web/gui/src/main/webapp/app/view/topo/topoLink.js
index f13032c..3686bc5 100644
--- a/web/gui/src/main/webapp/app/view/topo/topoLink.js
+++ b/web/gui/src/main/webapp/app/view/topo/topoLink.js
@@ -23,7 +23,7 @@
     'use strict';
 
     // injected refs
-    var $log, fs, sus, ts;
+    var $log, fs, sus, ts, flash;
 
     var api,
         td3,
@@ -31,53 +31,33 @@
         enhancedLink = null;    // the link which the mouse is hovering over
 
     // SVG elements;
-    var svg, mouseG;
+    var svg;
+
+    // internal state
+    var showPorts = true;       // enable port highlighting by default
 
 
     // ======== ALGORITHM TO FIND LINK CLOSEST TO MOUSE ========
 
-    function setupMouse(forceG, zoomer) {
-        $log.debug('set up mouse handlers for mouse move');
-        mouseG = forceG.append('g').attr('id', 'topo-mouse');
-        //mouseG.append('circle')
-        //    .attr({
-        //        r: 5,
-        //        opacity: 0
-        //    })
-        //    .style('fill', 'red');
-
-        svg.on('mouseenter', function () {
-            //$log.log('M--ENTER');
-            //mouseG.selectAll('circle').attr('opacity', 1);
-        })
-            .on('mouseleave', function () {
-                //$log.log('M--LEAVE');
-                //mouseG.selectAll('circle').attr('opacity', 0);
-            })
-            .on('mousemove', function () {
-                var m = d3.mouse(this),
-                    sc = zoomer.scale(),
-                    tr = zoomer.translate(),
-                    mx = (m[0] - tr[0]) / sc,
-                    my = (m[1] - tr[1]) / sc;
-
-                //$log.log('M--MOVE', m);
-
-                //mouseG.selectAll('circle')
-                //    .attr({
-                //        cx: mx,
-                //        cy: my
-                //    });
-                updatePerps({x: mx, y: my}, zoomer);
-            });
+    function mouseMoveHandler() {
+        var m = d3.mouse(this),
+            sc = api.zoomer.scale(),
+            tr = api.zoomer.translate(),
+            mx = (m[0] - tr[0]) / sc,
+            my = (m[1] - tr[1]) / sc;
+        computeNearestLink({x: mx, y: my});
     }
 
-    function updatePerps(mouse, zoomer) {
-        var proximity = 30 / zoomer.scale(),
-            perpData, perps, nearest, minDist;
+    function computeNearestLink(mouse) {
+        var proximity = 30 / api.zoomer.scale(),
+            nearest, minDist;
 
         function sq(x) { return x * x; }
 
+        function mdist(p, m) {
+            return Math.sqrt(sq(p.x - m.x) + sq(p.y - m.y));
+        }
+
         function pdrop(line, mouse) {
             var x1 = line.x1,
                 y1 = line.y1,
@@ -92,10 +72,6 @@
             return {x:x4, y:y4};
         }
 
-        function mdist(p, m) {
-            return Math.sqrt(sq(p.x - m.x) + sq(p.y - m.y));
-        }
-
         function lineSeg(d) {
             return {
                 x1: d.source.x,
@@ -115,7 +91,6 @@
         }
 
         if (network.links.length) {
-            perpData = [];
             nearest = null;
             minDist = proximity * 2;
 
@@ -135,41 +110,9 @@
                         minDist = dist;
                         nearest = d;
                     }
-                    /*
-                     perpData.push({
-                     key: d.key,
-                     x1: mouse.x,
-                     y1: mouse.y,
-                     x2: point.x,
-                     y2: point.y
-                     });
-                     */
                 }
             });
 
-            /*
-             perps = mouseG.selectAll('line')
-             .data(perpData, function (d) { return d.key; })
-             .attr({
-             x1: function (d) { return d.x1; },
-             y1: function (d) { return d.y1; },
-             x2: function (d) { return d.x2; },
-             y2: function (d) { return d.y2; }
-             });
-
-             perps.enter().append('line')
-             .attr({
-             x1: function (d) { return d.x1; },
-             y1: function (d) { return d.y1; },
-             x2: function (d) { return d.x2; },
-             y2: function (d) { return d.y2; }
-             })
-             .style('stroke-width', 2)
-             .style('stroke', 'limegreen');
-
-             perps.exit().remove();
-             */
-
             enhanceNearestLink(nearest);
         }
     }
@@ -245,36 +188,52 @@
         return {x: k * dx + ln.x, y: k * dy + ln.y};
     }
 
+    function togglePorts() {
+        showPorts = !showPorts;
+
+        var what = showPorts ? 'Enable' : 'Disable',
+            handler = showPorts ? mouseMoveHandler : null;
+
+        if (!showPorts) {
+            enhanceNearestLink(null);
+        }
+        svg.on('mousemove', handler);
+        flash.flash(what + ' port highlighting');
+    }
+
     // ==========================
     // Module definition
 
     angular.module('ovTopo')
         .factory('TopoLinkService',
-        ['$log', 'FnService', 'SvgUtilService', 'ThemeService',
+        ['$log', 'FnService', 'SvgUtilService', 'ThemeService', 'FlashService',
 
-            function (_$log_, _fs_, _sus_, _ts_) {
-                $log = _$log_;
-                fs = _fs_;
-                sus = _sus_;
-                ts = _ts_;
+        function (_$log_, _fs_, _sus_, _ts_, _flash_) {
+            $log = _$log_;
+            fs = _fs_;
+            sus = _sus_;
+            ts = _ts_;
+            flash = _flash_;
 
-                function initLink(_api_, _td3_) {
-                    api = _api_;
-                    td3 = _td3_;
-                    svg = api.svg;
-                    network = api.network;
-                    setupMouse(api.forceG, api.zoomer);
+            function initLink(_api_, _td3_) {
+                api = _api_;
+                td3 = _td3_;
+                svg = api.svg;
+                network = api.network;
+                if (showPorts) {
+                    svg.on('mousemove', mouseMoveHandler);
                 }
+            }
 
-                function destroyLink() {
-                    svg.on('mouseenter', null)
-                        .on('mouseleave', null)
-                        .on('mousemove', null);
-                }
+            function destroyLink() {
+                // unconditionally remove any mousemove event handler
+                svg.on('mousemove', null);
+            }
 
-                return {
-                    initLink: initLink,
-                    destroyLink: destroyLink
-                };
-            }]);
+            return {
+                initLink: initLink,
+                destroyLink: destroyLink,
+                togglePorts: togglePorts
+            };
+        }]);
 }());