GUI -- Persisted state of hosts/offline-devices/port-hilite; and a little refactoring to boot.
- Added asNumbers() to prefs.js.

Change-Id: I58b98bb660a525bc1af2498d81e86be6a4b06e66
diff --git a/web/gui/src/main/webapp/app/fw/util/prefs.js b/web/gui/src/main/webapp/app/fw/util/prefs.js
index f940ca7..6138ca4 100644
--- a/web/gui/src/main/webapp/app/fw/util/prefs.js
+++ b/web/gui/src/main/webapp/app/fw/util/prefs.js
@@ -37,7 +37,7 @@
     //       We may want to upgrade the version of Angular sometime soon
     //        since later version support objects as cookie values.
 
-    // NOTE: prefs represented as simple name/value(number) pairs
+    // NOTE: prefs represented as simple name/value pairs
     //       => a temporary restriction while we are encoding into cookies
     /*
         {
@@ -60,7 +60,7 @@
             bits = cook.split(',');
             bits.forEach(function (value) {
                 var x = value.split(':');
-                obj[x[0]] = Number(x[1]);
+                obj[x[0]] = x[1];
             });
 
             // update the cache
@@ -71,6 +71,23 @@
         return cache[name];
     }
 
+    // converts string values to numbers for selected (or all) keys
+    function asNumbers(obj, keys) {
+        if (!obj) return null;
+
+        if (!keys) {
+            // do them all
+            angular.forEach(obj, function (v, k) {
+                obj[k] = Number(obj[k]);
+            });
+        } else {
+            keys.forEach(function (k) {
+                obj[k] = Number(obj[k]);
+            });
+        }
+        return obj;
+    }
+
     function setPrefs(name, obj) {
         var bits = [],
             str;
@@ -100,6 +117,7 @@
 
             return {
                 getPrefs: getPrefs,
+                asNumbers: asNumbers,
                 setPrefs: setPrefs
             };
         }]);
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 24b40bd..a548260 100644
--- a/web/gui/src/main/webapp/app/view/topo/topo.js
+++ b/web/gui/src/main/webapp/app/view/topo/topo.js
@@ -48,9 +48,9 @@
             O: [toggleSummary, 'Toggle ONOS summary panel'],
             D: [toggleDetails, 'Disable / enable details panel'],
 
-            H: [tfs.toggleHosts, 'Toggle host visibility'],
-            M: [tfs.toggleOffline, 'Toggle offline visibility'],
-            P: [tfs.togglePorts, 'Toggle Port Highlighting'],
+            H: [toggleHosts, 'Toggle host visibility'],
+            M: [toggleOffline, 'Toggle offline visibility'],
+            P: [togglePorts, 'Toggle Port Highlighting'],
             dash: [tfs.showBadLinks, 'Show bad links'],
             B: [toggleMap, 'Toggle background map'],
             S: [toggleSprites, 'Toggle sprite layer'],
@@ -109,20 +109,32 @@
         updatePrefsState('detail', tps.toggleDetails(x));
     }
 
-    function toggleMap(x) {
-        var on = (x === 'keyev') ? !sus.visible(mapG) : !!x,
+    function toggleHosts(x) {
+        updatePrefsState('hosts', tfs.toggleHosts(x));
+    }
+
+    function toggleOffline(x) {
+        updatePrefsState('offdev', tfs.toggleOffline(x));
+    }
+
+    function togglePorts(x) {
+        updatePrefsState('porthl', tfs.togglePorts(x));
+    }
+
+    function _togSvgLayer(x, G, tag, what) {
+        var on = (x === 'keyev') ? !sus.visible(G) : !!x,
             verb = on ? 'Show' : 'Hide';
-        sus.visible(mapG, on);
-        updatePrefsState('bg', on);
-        flash.flash(verb + ' background map');
+        sus.visible(G, on);
+        updatePrefsState(tag, on);
+        flash.flash(verb + ' ' + what);
+    }
+
+    function toggleMap(x) {
+        _togSvgLayer(x, mapG, 'bg', 'background map');
     }
 
     function toggleSprites(x) {
-        var on = (x === 'keyev') ? !sus.visible(spriteG) : !!x,
-            verb = on ? 'Show' : 'Hide';
-        sus.visible(spriteG, on);
-        updatePrefsState('sprites', on);
-        flash.flash(verb + ' sprite layer');
+        _togSvgLayer(x, spriteG, 'spr', 'sprite layer');
     }
 
     function resetZoom() {
@@ -269,7 +281,7 @@
 
     function restoreConfigFromPrefs() {
         // NOTE: toolbar will have set this for us..
-        prefsState = ps.getPrefs('topo_prefs');
+        prefsState = ps.asNumbers(ps.getPrefs('topo_prefs'));
 
         $log.debug('TOPO---- Prefs State:', prefsState);
 
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 425a87f..3097343 100644
--- a/web/gui/src/main/webapp/app/view/topo/topoForce.js
+++ b/web/gui/src/main/webapp/app/view/topo/topoForce.js
@@ -392,16 +392,24 @@
         return b ? 'visible' : 'hidden';
     }
 
-    function toggleHosts() {
-        showHosts = !showHosts;
+    function toggleHosts(x) {
+        var kev = (x === 'keyev'),
+            on = kev ? !showHosts : !!x;
+
+        showHosts = on;
         updateHostVisibility();
-        flash.flash('Hosts ' + vis(showHosts));
+        flash.flash('Hosts ' + vis(on));
+        return on;
     }
 
-    function toggleOffline() {
-        showOffline = !showOffline;
+    function toggleOffline(x) {
+        var kev = (x === 'keyev'),
+            on = kev ? !showOffline : !!x;
+
+        showOffline = on;
         updateOfflineVisibility();
-        flash.flash('Offline devices ' + vis(showOffline));
+        flash.flash('Offline devices ' + vis(on));
+        return on;
     }
 
     function cycleDeviceLabels() {
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 8c8fd82..49678d8 100644
--- a/web/gui/src/main/webapp/app/view/topo/topoLink.js
+++ b/web/gui/src/main/webapp/app/view/topo/topoLink.js
@@ -241,17 +241,20 @@
 
     // ======================
 
-    function togglePorts() {
-        showPorts = !showPorts;
+    function togglePorts(x) {
+        var kev = (x === 'keyev'),
+            on = kev ? !showPorts : !!x,
+            what = on ? 'Enable' : 'Disable',
+            handler = on ? mouseMoveHandler : null;
 
-        var what = showPorts ? 'Enable' : 'Disable',
-            handler = showPorts ? mouseMoveHandler : null;
+        showPorts = on;
 
-        if (!showPorts) {
+        if (!on) {
             enhanceLink(null);
         }
         svg.on('mousemove', handler);
         flash.flash(what + ' port highlighting');
+        return on;
     }
 
     function deselectLink() {
diff --git a/web/gui/src/main/webapp/app/view/topo/topoToolbar.js b/web/gui/src/main/webapp/app/view/topo/topoToolbar.js
index 47e2a00..110160c 100644
--- a/web/gui/src/main/webapp/app/view/topo/topoToolbar.js
+++ b/web/gui/src/main/webapp/app/view/topo/topoToolbar.js
@@ -26,7 +26,7 @@
     var $log, tbs, ps, api;
 
     // internal state
-    var toolbar, keyData;
+    var toolbar, keyData, cachedState;
 
     // constants
     var name = 'topo-tbar',
@@ -62,20 +62,26 @@
 
     // initial toggle state: default settings and tag to key mapping
     var defaultPrefsState = {
-            bg: 1,
-            sprites: 0,
-            insts: 1,
             summary: 1,
+            insts: 1,
             detail: 1,
-            hosts: 0
+            hosts: 0,
+            offdev: 1,
+            porthl: 1,
+            bg: 1,
+            spr: 0,
+            toolbar: 1
         },
         prefsMap = {
-            bg: 'B',
-            sprites: 'S',
-            insts: 'I',
             summary: 'O',
-            details: 'D',
-            hosts: 'H'
+            insts: 'I',
+            detail: 'D',
+            hosts: 'H',
+            offdev: 'M',
+            porthl: 'P',
+            bg: 'B',
+            spr: 'S'
+            // NOTE: toolbar state is handled separately
         };
 
     function init(_api_) {
@@ -90,17 +96,18 @@
     }
 
     function setInitToggleState() {
-        var state = ps.getPrefs(cooktag);
-        $log.debug('TOOLBAR---- read prefs state:', state);
+        cachedState = ps.asNumbers(ps.getPrefs(cooktag));
+        $log.debug('TOOLBAR---- read prefs state:', cachedState);
 
-        if (!state) {
-            state = topoDefPrefs();
-            ps.setPrefs(cooktag, state);
-            $log.debug('TOOLBAR---- Set default prefs state:', state);
+        if (!cachedState) {
+            cachedState = topoDefPrefs();
+            ps.setPrefs(cooktag, cachedState);
+            $log.debug('TOOLBAR---- Set default prefs state:', cachedState);
         }
 
         angular.forEach(prefsMap, function (v, k) {
-            k2b[v].isel = !!state[k];
+            var cfg = k2b[v];
+            cfg && (cfg.isel = !!cachedState[k]);
         });
     }
 
@@ -160,7 +167,11 @@
         addSecondRow();
         toolbar.addRow();
         addThirdRow();
-        toolbar.show();
+        if (cachedState.toolbar) {
+            toolbar.show();
+        } else {
+            toolbar.hide();
+        }
     }
 
     function destroyToolbar() {
diff --git a/web/gui/src/main/webapp/tests/app/fw/util/prefs-spec.js b/web/gui/src/main/webapp/tests/app/fw/util/prefs-spec.js
index 2b8a15c..0215289 100644
--- a/web/gui/src/main/webapp/tests/app/fw/util/prefs-spec.js
+++ b/web/gui/src/main/webapp/tests/app/fw/util/prefs-spec.js
@@ -44,13 +44,16 @@
 
     it('should define api functions', function () {
         expect(fs.areFunctions(ps, [
-            'getPrefs', 'setPrefs'
+            'getPrefs', 'asNumbers', 'setPrefs'
         ])).toBe(true);
     });
 
     // === Tests for getPrefs()
     // TODO unit tests for getPrefs()
 
+    // === Tests for asNumbers()
+    // TODO unit tests for asNumbers()
+
     // === Tests for setPrefs()
     // TODO unit tests for setPrefs()