Added Key Commands to topo2

Change-Id: I83f596dfa7d906a2760cbcdc2fcd2d2aedb88832
diff --git a/web/gui/src/main/webapp/app/view/topo/topoD3.js b/web/gui/src/main/webapp/app/view/topo/topoD3.js
index 5b669ce..3219012 100644
--- a/web/gui/src/main/webapp/app/view/topo/topoD3.js
+++ b/web/gui/src/main/webapp/app/view/topo/topoD3.js
@@ -102,7 +102,7 @@
     }
 
     function incDevLabIndex() {
-        setDevLabIndex(deviceLabelIndex+1);
+        setDevLabIndex(device3ndex+1);
         switch(deviceLabelIndex) {
             case 0: return 'Hide device labels';
             case 1: return 'Show friendly device labels';
diff --git a/web/gui/src/main/webapp/app/view/topo2/topo2.js b/web/gui/src/main/webapp/app/view/topo2/topo2.js
index e1dcb3e..fa3b36e 100644
--- a/web/gui/src/main/webapp/app/view/topo2/topo2.js
+++ b/web/gui/src/main/webapp/app/view/topo2/topo2.js
@@ -25,7 +25,7 @@
 
     // references to injected services
     var $scope, $log, fs, mast, ks, zs,
-        gs, sus, ps, t2es, t2fs, t2is, t2bcs;
+        gs, sus, ps, t2es, t2fs, t2is, t2bcs, t2kcs;
 
     // DOM elements
     var ovtopo2, svg, defs, zoomLayer, forceG;
@@ -87,13 +87,13 @@
         'GlyphService', 'MapService', 'SvgUtilService', 'FlashService',
         'WebSocketService', 'PrefsService', 'ThemeService',
         'Topo2EventService', 'Topo2ForceService', 'Topo2InstanceService',
-        'Topo2BreadcrumbService',
+        'Topo2BreadcrumbService', 'Topo2KeyCommandService',
 
         function (_$scope_, _$log_, _$loc_,
             _fs_, _mast_, _ks_, _zs_,
             _gs_, _ms_, _sus_, _flash_,
             _wss_, _ps_, _th_,
-            _t2es_, _t2fs_, _t2is_, _t2bcs_) {
+            _t2es_, _t2fs_, _t2is_, _t2bcs_, _t2kcs_) {
 
             var params = _$loc_.search(),
                 dim,
@@ -125,6 +125,9 @@
             t2fs = _t2fs_;
             t2is = _t2is_;
             t2bcs = _t2bcs_;
+            t2kcs = _t2kcs_;
+
+            t2kcs.init(t2fs);
 
             // capture selected intent parameters (if they are set in the
             //  query string) so that the traffic overlay can highlight
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 9c4ccc2..ac3e244 100644
--- a/web/gui/src/main/webapp/app/view/topo2/topo2Device.js
+++ b/web/gui/src/main/webapp/app/view/topo2/topo2Device.js
@@ -61,15 +61,6 @@
         return remappedDeviceTypes[type] || type || 'unknown';
     }
 
-    function iconBox(dim, labelWidth) {
-        return {
-            x: -dim / 2,
-            y: -dim / 2,
-            width: dim + labelWidth,
-            height: dim
-        };
-    }
-
     // note: these are the device icon colors without affinity (no master)
     var dColTheme = {
         light: {
@@ -130,11 +121,11 @@
                         // Label
                         var labelElements = this.addLabelElements(label);
                         labelWidth = label ? this.computeLabelWidth(node) : 0;
-                        labelElements.rect.attr(iconBox(devIconDim, labelWidth));
+                        labelElements.rect.attr(this.iconBox(devIconDim, labelWidth));
 
                         // Icon
                         glyph = is.addDeviceIcon(node, glyphId, devIconDim);
-                        glyph.attr(iconBox(devIconDim, 0));
+                        glyph.attr(this.iconBox(devIconDim, 0));
 
                         node.attr('transform', sus.translate(-halfDevIcon, -halfDevIcon));
                         this.render();
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 8eb89d1..a4aa096 100644
--- a/web/gui/src/main/webapp/app/view/topo2/topo2Force.js
+++ b/web/gui/src/main/webapp/app/view/topo2/topo2Force.js
@@ -190,6 +190,13 @@
 
     // ========================== Main Service Definition
 
+    function updateNodes() {
+        var allNodes = t2rs.regionNodes();
+        angular.forEach(allNodes, function (node) {
+            node.update();
+        })
+    }
+
     angular.module('ovTopo2')
     .factory('Topo2ForceService',
         ['$log', 'WebSocketService', 'Topo2InstanceService', 'Topo2RegionService',
@@ -216,7 +223,9 @@
                 topo2StartDone: startDone,
 
                 showMastership: showMastership,
-                topo2PeerRegions: topo2PeerRegions
+                topo2PeerRegions: topo2PeerRegions,
+
+                updateNodes: updateNodes
             };
         }]);
 })();
diff --git a/web/gui/src/main/webapp/app/view/topo2/topo2KeyCommands.js b/web/gui/src/main/webapp/app/view/topo2/topo2KeyCommands.js
new file mode 100644
index 0000000..f3b444d
--- /dev/null
+++ b/web/gui/src/main/webapp/app/view/topo2/topo2KeyCommands.js
@@ -0,0 +1,67 @@
+/*
+* Copyright 2016-present Open Networking Laboratory
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+(function () {
+
+    // Injected Services
+    var ks, t2ps;
+    var topo2ForceService;
+
+    // Commmands
+    var actionMap = {
+        L: [cycleDeviceLabels, 'Cycle device labels']
+    };
+
+    function init(t2fs) {
+        topo2ForceService = t2fs;
+        bindCommands();
+    }
+
+    function bindCommands() {
+
+        ks.keyBindings(actionMap);
+
+        ks.gestureNotes([
+            ['click', 'Select the item and show details'],
+            ['shift-click', 'Toggle selection state'],
+            ['drag', 'Reposition (and pin) device / host'],
+            ['cmd-scroll', 'Zoom in / out'],
+            ['cmd-drag', 'Pan']
+        ]);
+    }
+
+    function cycleDeviceLabels() {
+        var deviceLabelIndex = t2ps.get('dlbls') + 1;
+        t2ps.set('dlbls', deviceLabelIndex % 3);
+        topo2ForceService.updateNodes();
+    }
+
+    angular.module('ovTopo2')
+    .factory('Topo2KeyCommandService',
+    ['KeyService', 'Topo2PrefsService',
+
+        function (_ks_, _t2ps_) {
+
+            t2ps = _t2ps_;
+            ks = _ks_;
+
+            return {
+                init: init,
+                bindCommands: bindCommands
+            };
+        }
+    ]);
+})();
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 05dc4b9..2b91781 100644
--- a/web/gui/src/main/webapp/app/view/topo2/topo2NodeModel.js
+++ b/web/gui/src/main/webapp/app/view/topo2/topo2NodeModel.js
@@ -22,7 +22,7 @@
 (function () {
     'use strict';
 
-    var randomService;
+    var randomService, ps;
     var fn;
 
     // Internal state;
@@ -107,11 +107,12 @@
 
     angular.module('ovTopo2')
     .factory('Topo2NodeModel',
-        ['Topo2Model', 'FnService', 'RandomService',
-        function (Model, _fn_, _RandomService_) {
+        ['Topo2Model', 'FnService', 'RandomService', 'Topo2PrefsService',
+        function (Model, _fn_, _RandomService_, _ps_) {
 
             randomService = _RandomService_;
             fn = _fn_;
+            ps = _ps_;
 
             return Model.extend({
                 initialize: function () {
@@ -125,7 +126,7 @@
                         id = this.get('id'),
                         friendlyName = props ? props.name : id,
                         labels = ['', friendlyName, id],
-                        nli = nodeLabelIndex,
+                        nli = ps.get('dlbls'),
                         idx = (nli < labels.length) ? nli : 0;
 
                     return labels[idx];
@@ -150,6 +151,14 @@
                         text: text
                     };
                 },
+                iconBox: function(dim, labelWidth) {
+                    return {
+                        x: -dim / 2,
+                        y: -dim / 2,
+                        width: dim + labelWidth,
+                        height: dim
+                    };
+                },
                 svgClassName: function () {
                     return fn.classNames('node',
                         this.nodeType,
@@ -159,6 +168,21 @@
                         }
                     );
                 },
+                update: function () {
+                    this.updateLabel();
+                },
+                updateLabel: function () {
+                    var node = this.el,
+                        label = this.trimLabel(this.label()),
+                        labelWidth;
+
+                    node.select('text').text(label);
+                    labelWidth = label ? this.computeLabelWidth(node) : 0;
+
+                    node.select('rect')
+                        .transition()
+                        .attr(this.iconBox(devIconDim, labelWidth));
+                },
                 createNode: function () {
 
                     var node = angular.extend({}, this.attributes);
diff --git a/web/gui/src/main/webapp/app/view/topo2/topo2Prefs.js b/web/gui/src/main/webapp/app/view/topo2/topo2Prefs.js
new file mode 100644
index 0000000..9eaef00
--- /dev/null
+++ b/web/gui/src/main/webapp/app/view/topo2/topo2Prefs.js
@@ -0,0 +1,66 @@
+/*
+* Copyright 2016-present Open Networking Laboratory
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+(function () {
+
+    // Injected Services
+    var ps;
+
+    var defaultPrefsState = {
+        insts: 1,
+        summary: 1,
+        detail: 1,
+        hosts: 0,
+        offdev: 1,
+        dlbls: 0,
+        porthl: 1,
+        bg: 0,
+        spr: 0,
+        ovidx: 1,   // default to traffic overlay
+        toolbar: 0
+    };
+
+    function topo2Prefs() {
+        return ps.getPrefs('topo_prefs', defaultPrefsState);
+    }
+
+    function get(key) {
+        var preferences = topo2Prefs();
+        return preferences[key];
+    }
+
+    function set(key, value) {
+        var preferences = topo2Prefs();
+        preferences[key] = value;
+        ps.setPrefs('topo_prefs', preferences);
+        return preferences[key];
+    }
+
+    angular.module('ovTopo2')
+    .factory('Topo2PrefsService',
+    ['PrefsService',
+
+        function (_ps_) {
+
+            ps = _ps_;
+
+            return {
+                get: get,
+                set: set
+            };
+        }
+    ]);
+})();
diff --git a/web/gui/src/main/webapp/index.html b/web/gui/src/main/webapp/index.html
index 3c0d721..2b6fc57 100644
--- a/web/gui/src/main/webapp/index.html
+++ b/web/gui/src/main/webapp/index.html
@@ -135,10 +135,12 @@
     <script src="app/view/topo2/topo2Force.js"></script>
     <script src="app/view/topo2/topo2Host.js"></script>
     <script src="app/view/topo2/topo2Instance.js"></script>
+    <script src="app/view/topo2/topo2KeyCommands.js"></script>
     <script src="app/view/topo2/topo2Layout.js"></script>
     <script src="app/view/topo2/topo2Link.js"></script>
     <script src="app/view/topo2/topo2Model.js"></script>
     <script src="app/view/topo2/topo2NodeModel.js"></script>
+    <script src="app/view/topo2/topo2Prefs.js"></script>
     <script src="app/view/topo2/topo2Region.js"></script>
     <script src="app/view/topo2/topo2Select.js"></script>
     <script src="app/view/topo2/topo2SubRegion.js"></script>