Adding server-side user preferences.

More work still needs to get done to allow client to process
server-pushed preferences updates.

Change-Id: I6e80e3f3677285cb19cfa3b6240c1b13aac56622
diff --git a/web/gui/src/main/webapp/app/fw/layer/loading.js b/web/gui/src/main/webapp/app/fw/layer/loading.js
index 86dd56d..a9e141eb 100644
--- a/web/gui/src/main/webapp/app/fw/layer/loading.js
+++ b/web/gui/src/main/webapp/app/fw/layer/loading.js
@@ -122,9 +122,9 @@
 
     angular.module('onosLayer')
         .factory('LoadingService',
-        ['$log', '$timeout', 'ThemeService', 'FnService',
+        ['$log', '$timeout', 'ThemeService', 'FnService', 'WebSocketService',
 
-            function (_$log_, _$timeout_, _ts_, _fs_) {
+            function (_$log_, _$timeout_, _ts_, _fs_, wss) {
             $log = _$log_;
             $timeout = _$timeout_;
             ts = _ts_;
@@ -132,11 +132,13 @@
 
             preloadImages();
 
-            return {
+            var self = {
                 start: start,
                 stop: stop,
                 waiting: waiting
             };
+            wss._setLoadingDelegate(self);
+            return self;
         }]);
 
 }());
\ No newline at end of file
diff --git a/web/gui/src/main/webapp/app/fw/layer/veil.js b/web/gui/src/main/webapp/app/fw/layer/veil.js
index fc0530a..fddfe7f 100644
--- a/web/gui/src/main/webapp/app/fw/layer/veil.js
+++ b/web/gui/src/main/webapp/app/fw/layer/veil.js
@@ -79,21 +79,23 @@
 
     angular.module('onosLayer')
     .factory('VeilService',
-        ['$log', '$route', 'FnService', 'KeyService', 'GlyphService',
+        ['$log', '$route', 'FnService', 'KeyService', 'GlyphService', 'WebSocketService',
 
-        function (_$log_, _$route_, _fs_, _ks_, _gs_) {
+        function (_$log_, _$route_, _fs_, _ks_, _gs_, wss) {
             $log = _$log_;
             $route = _$route_;
             fs = _fs_;
             ks = _ks_;
             gs = _gs_;
 
-            return {
+            var self = {
                 init: init,
                 show: show,
                 hide: hide,
                 lostServer: lostServer
             };
+            wss._setVeilDelegate(self);
+            return self;
     }]);
 
 }());
diff --git a/web/gui/src/main/webapp/app/fw/remote/websocket.js b/web/gui/src/main/webapp/app/fw/remote/websocket.js
index e39a7f9..92d2d2e 100644
--- a/web/gui/src/main/webapp/app/fw/remote/websocket.js
+++ b/web/gui/src/main/webapp/app/fw/remote/websocket.js
@@ -59,7 +59,7 @@
 
     function handleOpen() {
         $log.info('Web socket open - ', url);
-        vs.hide();
+        vs && vs.hide();
 
         if (fs.debugOn('txrx')) {
             $log.debug('Sending ' + pendingEvents.length + ' pending event(s)...');
@@ -105,14 +105,14 @@
         var gsucc;
 
         $log.info('Web socket closed');
-        ls.stop();
+        ls && ls.stop();
         wsUp = false;
 
         if (gsucc = findGuiSuccessor()) {
             createWebSocket(webSockOpts, gsucc);
         } else {
             // If no controllers left to contact, show the Veil...
-            vs.show([
+            vs && vs.show([
                 'Oops!',
                 'Web-socket connection to server closed...',
                 'Try refreshing the page.'
@@ -296,22 +296,29 @@
         }
     }
 
+    // Binds the veil service as a delegate
+    function setVeilDelegate(vd) {
+        vs = vd;
+    }
+
+    // Binds the loading service as a delegate
+    function setLoadingDelegate(ld) {
+        ls = ld;
+    }
+
 
     // ============================
     // ===== Definition of module
     angular.module('onosRemote')
     .factory('WebSocketService',
         ['$log', '$location', 'FnService', 'UrlFnService', 'WSock',
-            'VeilService', 'LoadingService',
 
-        function (_$log_, _$loc_, _fs_, _ufs_, _wsock_, _vs_, _ls_) {
+        function (_$log_, _$loc_, _fs_, _ufs_, _wsock_) {
             $log = _$log_;
             $loc = _$loc_;
             fs = _fs_;
             ufs = _ufs_;
             wsock = _wsock_;
-            vs = _vs_;
-            ls = _ls_;
 
             bindHandlers(builtinHandlers);
 
@@ -324,7 +331,10 @@
                 addOpenListener: addOpenListener,
                 removeOpenListener: removeOpenListener,
                 sendEvent: sendEvent,
-                isConnected: function () { return wsUp; }
+                isConnected: function () { return wsUp; },
+
+                _setVeilDelegate: setVeilDelegate,
+                _setLoadingDelegate: setLoadingDelegate
             };
         }
     ]);
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 02a2390..ad3dc04 100644
--- a/web/gui/src/main/webapp/app/fw/util/prefs.js
+++ b/web/gui/src/main/webapp/app/fw/util/prefs.js
@@ -21,54 +21,14 @@
     'use strict';
 
     // injected refs
-    var $log, $cookies, fs;
+    var $log, fs, wss;
 
     // internal state
     var cache = {};
 
-    // NOTE: in Angular 1.3.5, $cookies is just a simple object, and
-    //       cookie values are just strings. From the 1.3.5 docs:
-    //
-    //       "Only a simple Object is exposed and by adding or removing
-    //        properties to/from this object, new cookies are created/deleted
-    //        at the end of current $eval. The object's properties can only
-    //        be strings."
-    //
-    //       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 pairs
-    //       => a temporary restriction while we are encoding into cookies
-    /*
-        {
-          foo: 1,
-          bar: 0,
-          goo: 2
-        }
-
-        stored as "foo:1,bar:0,goo:2"
-     */
-
-    // reads cookie with given name and returns an object rep of its value
-    // or null if no such cookie is set
-    function getPrefs(name) {
-        var cook = $cookies[name],
-            bits,
-            obj = {};
-
-        if (cook) {
-            bits = cook.split(',');
-            bits.forEach(function (value) {
-                var x = value.split(':');
-                obj[x[0]] = x[1];
-            });
-
-            // update the cache
-            cache[name] = obj;
-            return obj;
-        }
-        // perhaps we have a cached copy..
-        return cache[name];
+    // returns the preference by the specified name
+    function getPrefs(name, defaults) {
+        return cache[name] || defaults;
     }
 
     // converts string values to numbers for selected (or all) keys
@@ -89,34 +49,28 @@
     }
 
     function setPrefs(name, obj) {
-        var bits = [],
-            str;
-
-        angular.forEach(obj, function (value, key) {
-            bits.push(key + ':' + value);
-        });
-        str = bits.join(',');
-
-        // keep a cached copy of the object
+        // keep a cached copy of the object and send an update to server
         cache[name] = obj;
-
-        // The angular way of doing this...
-        // $cookies[name] = str;
-        //  ...but it appears that this gets delayed, and doesn't 'stick' ??
-
-        // FORCE cookie to be set by writing directly to document.cookie...
-        document.cookie = name + '=' + encodeURIComponent(str);
-        if (fs.debugOn('prefs')) {
-            $log.debug('<<>> Wrote cookie <'+name+'>:', str);
-        }
+        wss.sendEvent('updatePrefReq', { key: name, value: obj });
+    }
+    
+    function updatePrefs(data) {
+        $log.info('User properties updated');
+        cache[data.key] = data.value;
     }
 
     angular.module('onosUtil')
-    .factory('PrefsService', ['$log', '$cookies', 'FnService',
-        function (_$log_, _$cookies_, _fs_) {
+    .factory('PrefsService', ['$log', 'FnService', 'WebSocketService',
+        function (_$log_, _fs_, _wss_) {
             $log = _$log_;
-            $cookies = _$cookies_;
             fs = _fs_;
+            wss = _wss_;
+
+            cache = userPrefs;
+
+            wss.bindHandlers({
+                updatePrefs: updatePrefs
+            });
 
             return {
                 getPrefs: getPrefs,
diff --git a/web/gui/src/main/webapp/app/fw/util/theme.js b/web/gui/src/main/webapp/app/fw/util/theme.js
index 0e0ee64..4441402 100644
--- a/web/gui/src/main/webapp/app/fw/util/theme.js
+++ b/web/gui/src/main/webapp/app/fw/util/theme.js
@@ -20,7 +20,7 @@
 (function () {
     'use strict';
 
-    var $log, fs;
+    var $log, fs, ps;
 
     var themes = ['light', 'dark'],
         themeStr = themes.join(' '),
@@ -29,7 +29,7 @@
         nextListenerId = 1;
 
     function init() {
-        thidx = 0;
+        thidx = ps.getPrefs('theme', { idx: 0 }).idx;
         updateBodyClass();
     }
 
@@ -37,10 +37,11 @@
         return themes[thidx];
     }
 
-    function setTheme(t) {
+    function setTheme(t, force) {
         var idx = themes.indexOf(t);
-        if (idx > -1 && idx !== thidx) {
+        if (force || idx > -1 && idx !== thidx) {
             thidx = idx;
+            ps.setPrefs('theme', { idx: thidx });
             updateBodyClass();
             themeEvent('set');
         }
@@ -49,6 +50,7 @@
     function toggleTheme() {
         var i = thidx + 1;
         thidx = (i===themes.length) ? 0 : i;
+        ps.setPrefs('theme', { idx: thidx });
         updateBodyClass();
         themeEvent('toggle');
         return getTheme();
@@ -97,11 +99,11 @@
     }
 
     angular.module('onosUtil')
-        .factory('ThemeService', ['$log', 'FnService',
-        function (_$log_, _fs_) {
+        .factory('ThemeService', ['$log', 'FnService', 'PrefsService',
+        function (_$log_, _fs_, _ps_) {
             $log = _$log_;
             fs = _fs_;
-            thidx = 0;
+            ps = _ps_;
 
             return {
                 init: init,
diff --git a/web/gui/src/main/webapp/app/fw/widget/toolbar.js b/web/gui/src/main/webapp/app/fw/widget/toolbar.js
index 050afd0..3b9e5ed 100644
--- a/web/gui/src/main/webapp/app/fw/widget/toolbar.js
+++ b/web/gui/src/main/webapp/app/fw/widget/toolbar.js
@@ -219,6 +219,10 @@
             }
         }
 
+        function isVisible() {
+            return panel.isVisible();
+        }
+
         return {
             addButton: addButton,
             addToggle: addToggle,
@@ -228,7 +232,8 @@
 
             show: show,
             hide: hide,
-            toggle: toggle
+            toggle: toggle,
+            isVisible: isVisible
         };
     }
 
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 9b532f6..069fa42 100644
--- a/web/gui/src/main/webapp/app/view/topo/topo.js
+++ b/web/gui/src/main/webapp/app/view/topo/topo.js
@@ -30,7 +30,7 @@
 
     // references to injected services
     var $scope, $log, $cookies, fs, ks, zs, gs, ms, sus, flash, wss, ps, th,
-        tds, tes, tfs, tps, tis, tss, tls, tts, tos, fltr, ttbs, tspr, ttip, tov;
+        tds, t3s, tes, tfs, tps, tis, tss, tls, tts, tos, fltr, ttbs, tspr, ttip, tov;
 
     // DOM elements
     var ovtopo, svg, defs, zoomLayer, mapG, spriteG, forceG, noDevsLayer;
@@ -369,16 +369,14 @@
 
     function setUpMap($loc) {
         var qp = $loc.search(),
-            pr = ps.getPrefs('topo_mapid'),
-            mi1 = qp.mapid,
-            mi2 = pr && pr.id,
-            mapId = mi1 || mi2 || 'usa',
-            ms1 = qp.mapscale,
-            ms2 = pr && pr.scale,
-            mapScale = ms1 || ms2 || 1,
-            t1 = qp.tint,
-            t2 = pr && pr.tint,
-            tint = t1 || t2 || 'off',
+            pr = ps.getPrefs('topo_mapid', {
+                id: qp.mapid || 'usa',
+                scale: qp.mapscale || 1,
+                tint: qp.tint || 'off'
+            }),
+            mapId = pr.id,
+            mapScale = pr.scale,
+            tint = pr.tint,
             promise,
             cfilter;
 
@@ -441,8 +439,8 @@
 
     function setUpSprites($loc, tspr) {
         var s1 = $loc.search().sprites,
-            s2 = ps.getPrefs('topo_sprites'),
-            sprId = s1 || (s2 && s2.id);
+            s2 = ps.getPrefs('topo_sprites', { id: s1 }),
+            sprId = s2.id;
 
         spriteG = zoomLayer.append ('g').attr('id', 'topo-sprites');
         if (sprId) {
@@ -463,7 +461,7 @@
 
     function restoreConfigFromPrefs() {
         // NOTE: toolbar will have set this for us..
-        prefsState = ps.asNumbers(ps.getPrefs('topo_prefs'));
+        prefsState = ps.asNumbers(ps.getPrefs('topo_prefs', ttbs.defaultPrefs));
 
         $log.debug('TOPO- Prefs State:', prefsState);
 
@@ -476,6 +474,7 @@
         togglePorts(prefsState.porthl);
         toggleMap(prefsState.bg);
         toggleSprites(prefsState.spr);
+        t3s.setDevLabIndex(prefsState.dlbls);
         flash.enable(true);
     }
 
@@ -484,7 +483,7 @@
     //  have opened the websocket to the server; hence this extra function
     // invoked after tes.start()
     function restoreSummaryFromPrefs() {
-        prefsState = ps.asNumbers(ps.getPrefs('topo_prefs'));
+        prefsState = ps.asNumbers(ps.getPrefs('topo_prefs', ttbs.defaultPrefs));
         $log.debug('TOPO- Prefs SUMMARY State:', prefsState.summary);
 
         flash.enable(false);
@@ -506,7 +505,7 @@
             '$cookies', 'FnService', 'MastService', 'KeyService', 'ZoomService',
             'GlyphService', 'MapService', 'SvgUtilService', 'FlashService',
             'WebSocketService', 'PrefsService', 'ThemeService',
-            'TopoDialogService',
+            'TopoDialogService', 'TopoD3Service',
             'TopoEventService', 'TopoForceService', 'TopoPanelService',
             'TopoInstService', 'TopoSelectService', 'TopoLinkService',
             'TopoTrafficService', 'TopoObliqueService', 'TopoFilterService',
@@ -515,7 +514,7 @@
 
         function (_$scope_, _$log_, $loc, $timeout, _$cookies_, _fs_, mast, _ks_,
                   _zs_, _gs_, _ms_, _sus_, _flash_, _wss_, _ps_, _th_,
-                  _tds_, _tes_,
+                  _tds_, _t3s_, _tes_,
                   _tfs_, _tps_, _tis_, _tss_, _tls_, _tts_, _tos_, _fltr_,
                   _ttbs_, _tspr_, _ttip_, _tov_) {
             var params = $loc.search(),
@@ -545,6 +544,7 @@
             ps = _ps_;
             th = _th_;
             tds = _tds_;
+            t3s = _t3s_;
             tes = _tes_;
             tfs = _tfs_;
             // TODO: consider funnelling actions through TopoForceService...
@@ -601,7 +601,7 @@
             setUpNoDevs();
             setUpMap($loc).then(
                 function (proj) {
-                    var z = ps.getPrefs('topo_zoom') || {tx:0, ty:0, sc:1};
+                    var z = ps.getPrefs('topo_zoom', { tx:0, ty:0, sc:1 });
                     zoomer.panZoom([z.tx, z.ty], z.sc);
                     $log.debug('** Zoom restored:', z);
 
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 a14ccbe..ef704c7 100644
--- a/web/gui/src/main/webapp/app/view/topo/topoD3.js
+++ b/web/gui/src/main/webapp/app/view/topo/topoD3.js
@@ -23,7 +23,7 @@
     'use strict';
 
     // injected refs
-    var $log, fs, sus, is, ts;
+    var $log, fs, sus, is, ts, ps, ttbs;
 
     // api to topoForce
     var api;
@@ -159,7 +159,7 @@
     // ====
 
     function incDevLabIndex() {
-        deviceLabelIndex = (deviceLabelIndex+1) % 3;
+        setDevLabIndex(deviceLabelIndex+1);
         switch(deviceLabelIndex) {
             case 0: return 'Hide device labels';
             case 1: return 'Show friendly device labels';
@@ -167,6 +167,13 @@
         }
     }
 
+    function setDevLabIndex(mode) {
+        deviceLabelIndex = mode % 3;
+        var p = ps.getPrefs('topo_prefs', ttbs.defaultPrefs);
+        p.dlbls = deviceLabelIndex;
+        ps.setPrefs('topo_prefs', p);
+    }
+
     // Returns the newly computed bounding box of the rectangle
     function adjustRectToFitText(n) {
         var text = n.select('text'),
@@ -599,13 +606,16 @@
     angular.module('ovTopo')
     .factory('TopoD3Service',
         ['$log', 'FnService', 'SvgUtilService', 'IconService', 'ThemeService',
+            'PrefsService', 'TopoToolbarService',
 
-        function (_$log_, _fs_, _sus_, _is_, _ts_) {
+        function (_$log_, _fs_, _sus_, _is_, _ts_, _ps_, _ttbs_) {
             $log = _$log_;
             fs = _fs_;
             sus = _sus_;
             is = _is_;
             ts = _ts_;
+            ps = _ps_;
+            ttbs = _ttbs_;
 
             icfg = is.iconConfig();
 
@@ -620,6 +630,7 @@
                 destroyD3: destroyD3,
 
                 incDevLabIndex: incDevLabIndex,
+                setDevLabIndex: setDevLabIndex,
                 adjustRectToFitText: adjustRectToFitText,
                 hostLabel: hostLabel,
                 deviceLabel: deviceLabel,
diff --git a/web/gui/src/main/webapp/app/view/topo/topoPanel.js b/web/gui/src/main/webapp/app/view/topo/topoPanel.js
index 34e85f3..e1cef91 100644
--- a/web/gui/src/main/webapp/app/view/topo/topoPanel.js
+++ b/web/gui/src/main/webapp/app/view/topo/topoPanel.js
@@ -529,6 +529,7 @@
 
                 showSummary: showSummary,
                 toggleSummary: toggleSummary,
+                hideSummary: hideSummaryPanel,
 
                 toggleUseDetailsFlag: toggleUseDetailsFlag,
                 displaySingle: displaySingle,
@@ -538,8 +539,6 @@
                 displaySomething: displaySomething,
                 addAction: addAction,
 
-                hideSummaryPanel: hideSummaryPanel,
-
                 detailVisible: function () { return detail.panel().isVisible(); },
                 summaryVisible: function () { return summary.panel().isVisible(); }
             };
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 deb49a5..535cd17 100644
--- a/web/gui/src/main/webapp/app/view/topo/topoToolbar.js
+++ b/web/gui/src/main/webapp/app/view/topo/topoToolbar.js
@@ -70,11 +70,12 @@
 
     // initial toggle state: default settings and tag to key mapping
     var defaultPrefsState = {
-            summary: 1,
             insts: 1,
+            summary: 1,
             detail: 1,
             hosts: 0,
             offdev: 1,
+            dlbls: 0,
             porthl: 1,
             bg: 0,
             spr: 0,
@@ -104,7 +105,7 @@
     }
 
     function setInitToggleState() {
-        cachedState = ps.asNumbers(ps.getPrefs(cooktag));
+        cachedState = ps.asNumbers(ps.getPrefs(cooktag, defaultPrefsState));
         $log.debug('TOOLBAR---- read prefs state:', cachedState);
 
         if (!cachedState) {
@@ -264,6 +265,9 @@
 
     function toggleToolbar() {
         toolbar.toggle();
+        var prefs = ps.getPrefs(cooktag, defaultPrefsState);
+        prefs.toolbar = !prefs.toolbar;
+        ps.setPrefs('topo_prefs', prefs);
     }
 
     function setDefaultOverlay() {
@@ -298,6 +302,7 @@
                 keyListener: keyListener,
                 toggleToolbar: toggleToolbar,
                 setDefaultOverlay: setDefaultOverlay,
+                defaultPrefs: defaultPrefsState,
                 fnkey: fnkey
             };
         }]);