GUI -- First cut at persisting user settings (on topology view).

Change-Id: Id58a744bf3514852b31d9d8c053c6e167974e1ef
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 a6624ca..5843b56 100644
--- a/web/gui/src/main/webapp/app/view/topo/topo.js
+++ b/web/gui/src/main/webapp/app/view/topo/topo.js
@@ -22,13 +22,14 @@
     'use strict';
 
     var moduleDependencies = [
+        'ngCookies',
         'onosUtil',
         'onosSvg',
         'onosRemote'
     ];
 
     // references to injected services etc.
-    var $log, fs, ks, zs, gs, ms, sus, flash, wss,
+    var $log, $cookies, fs, ks, zs, gs, ms, sus, flash, wss,
         tes, tfs, tps, tis, tss, tls, tts, tos, fltr, ttbs;
 
     // DOM elements
@@ -43,9 +44,9 @@
         // key bindings need to be made after the services have been injected
         // thus, deferred to here...
         actionMap = {
-            I: [toggleInstances, 'Toggle ONOS instances pane'],
-            O: [tps.toggleSummary, 'Toggle ONOS summary pane'],
-            D: [tps.toggleDetails, 'Disable / enable details pane'],
+            I: [toggleInstances, 'Toggle ONOS instances panel'],
+            O: [tps.toggleSummary, 'Toggle ONOS summary panel'],
+            D: [tps.toggleDetails, 'Disable / enable details panel'],
 
             H: [tfs.toggleHosts, 'Toggle host visibility'],
             M: [tfs.toggleOffline, 'Toggle offline visibility'],
@@ -96,15 +97,26 @@
 
     // NOTE: this really belongs in the TopoPanelService -- but how to
     //       cleanly link in the updateDeviceColors() call? To be fixed later.
-    function toggleInstances() {
-        tis.toggle();
+    function toggleInstances(x) {
+        if (x === 'keyev') {
+            tis.toggle();
+            updateCookieState('insts', tis.isVisible());
+        } else if (x) {
+            tis.show();
+        } else {
+            tis.hide();
+        }
         tfs.updateDeviceColors();
     }
 
-    function toggleMap() {
-        sus.visible(mapG, !sus.visible(mapG));
+    function toggleMap(x) {
+        var on = (x === 'keyev') ? !sus.visible(mapG) : !!x;
+        sus.visible(mapG, on);
+        updateCookieState('bg', on);
     }
 
+    //  TODO: need wrapper functions for state changes needed in cookies
+
     function resetZoom() {
         zoomer.reset();
     }
@@ -236,11 +248,85 @@
             .attr('opacity', b ? 1 : 0);
     }
 
+    // --- Config from Cookies -------------------------------------------
+
+    // TODO: write a general purpose cookie service, rather than custom here
+
+    // 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.
+
+    var defaultCookieState = {
+        bg: 1,
+        insts: 1,
+        summary: 1,
+        detail: 1,
+        hosts: 0
+    };
+
+    var cookieState = {};
+
+    function writeCookieState() {
+        var bits = [],
+            str;
+        angular.forEach(cookieState, function (value, key) {
+            bits.push(key + ':' + value);
+        });
+        str = bits.join(',');
+
+        // The angular way of doing this...
+        // $cookies.topo_state = 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 = 'topo_state=' + encodeURIComponent(str);
+        $log.debug('<<>> Wrote cookie:', str);
+    }
+
+    function readCookieState() {
+        var cook = $cookies.topo_state || '',
+            bits;
+
+        if (!cook) {
+            cookieState = angular.extend({}, defaultCookieState);
+            writeCookieState(); // seed the pot
+
+        } else {
+            bits = cook.split(',');
+            bits.forEach(function (value) {
+                var x = value.split(':');
+                cookieState[x[0]] = Number(x[1]);
+            });
+        }
+    }
+
+    function updateCookieState(what, b) {
+        cookieState[what] = b ? 1 : 0;
+        writeCookieState();
+    }
+
+    function restoreConfigFromCookies() {
+        readCookieState();
+        $log.debug('Cookie State:', cookieState);
+
+        toggleInstances(cookieState.insts);
+        tps.toggleSummary(cookieState.summary);
+        tps.toggleDetails(cookieState.detail);
+    }
+
+
     // --- Controller Definition -----------------------------------------
 
     angular.module('ovTopo', moduleDependencies)
         .controller('OvTopoCtrl', ['$scope', '$log', '$location', '$timeout',
-            'FnService', 'MastService', 'KeyService', 'ZoomService',
+            '$cookies', 'FnService', 'MastService', 'KeyService', 'ZoomService',
             'GlyphService', 'MapService', 'SvgUtilService', 'FlashService',
             'WebSocketService',
             'TopoEventService', 'TopoForceService', 'TopoPanelService',
@@ -248,8 +334,8 @@
             'TopoTrafficService', 'TopoObliqueService', 'TopoFilterService',
             'TopoToolbarService',
 
-        function ($scope, _$log_, $loc, $timeout, _fs_, mast, _ks_, _zs_,
-                  _gs_, _ms_, _sus_, _flash_, _wss_, _tes_, _tfs_, _tps_,
+        function ($scope, _$log_, $loc, $timeout, _$cookies_, _fs_, mast, _ks_,
+                  _zs_, _gs_, _ms_, _sus_, _flash_, _wss_, _tes_, _tfs_, _tps_,
                   _tis_, _tss_, _tls_, _tts_, _tos_, _fltr_, _ttbs_) {
             var self = this,
                 projection,
@@ -264,6 +350,7 @@
                 };
 
             $log = _$log_;
+            $cookies = _$cookies_;
             fs = _fs_;
             ks = _ks_;
             zs = _zs_;
@@ -316,6 +403,7 @@
                 function (proj) {
                     projection = proj;
                     $log.debug('** We installed the projection: ', proj);
+                    toggleMap(cookieState.bg);
                 }
             );
 
@@ -325,6 +413,9 @@
             tps.initPanels();
             tes.start();
 
+            // temporary solution for persisting user settings
+            restoreConfigFromCookies();
+
             $log.log('OvTopoCtrl has been created');
         }]);
 }());
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 2d54328..d95c4f5 100644
--- a/web/gui/src/main/webapp/app/view/topo/topoPanel.js
+++ b/web/gui/src/main/webapp/app/view/topo/topoPanel.js
@@ -225,13 +225,15 @@
         showSummaryPanel();
     }
 
-    function toggleSummary() {
-        if (summaryPanel.isVisible()) {
-            hideSummaryPanel();
-        } else {
+    function toggleSummary(x) {
+        var on = (x === 'keyev') ? !summaryPanel.isVisible() : !!x;
+
+        if (on) {
             // ask server to start sending summary data.
             wss.sendEvent('requestSummary');
             // note: the summary panel will appear, once data arrives
+        } else {
+            hideSummaryPanel();
         }
     }
 
@@ -289,8 +291,8 @@
         dp.up = function (cb) { dp._move(dp.ypos.up, cb); };
     }
 
-    function toggleDetails() {
-        useDetails = !useDetails;
+    function toggleDetails(x) {
+        useDetails = (x === 'keyev') ? !useDetails : !!x;
         if (useDetails) {
             flash.flash('Enable details panel');
             if (haveDetails) {
diff --git a/web/gui/src/main/webapp/app/view/topo/topoSelect.js b/web/gui/src/main/webapp/app/view/topo/topoSelect.js
index 5d456aa..c83373c 100644
--- a/web/gui/src/main/webapp/app/view/topo/topoSelect.js
+++ b/web/gui/src/main/webapp/app/view/topo/topoSelect.js
@@ -61,7 +61,7 @@
 
     function nodeMouseOver(m) {
         if (!m.dragStarted) {
-            $log.debug("MouseOver()...", m);
+            //$log.debug("MouseOver()...", m);
             if (hovered != m) {
                 hovered = m;
                 tts.requestTrafficForMode();
@@ -75,7 +75,7 @@
                 hovered = null;
                 tts.requestTrafficForMode();
             }
-            $log.debug("MouseOut()...", m);
+            //$log.debug("MouseOut()...", m);
         }
     }
 
diff --git a/web/gui/src/main/webapp/index.html b/web/gui/src/main/webapp/index.html
index de19409..06e0336 100644
--- a/web/gui/src/main/webapp/index.html
+++ b/web/gui/src/main/webapp/index.html
@@ -30,6 +30,7 @@
     <!--TODO: use minified versions, once debugging is complete -->
     <script src="tp/angular.js"></script>
     <script src="tp/angular-route.js"></script>
+    <script src="tp/angular-cookies.js"></script>
 
     <script src="tp/d3.js"></script>
     <script src="tp/topojson.v1.min.js"></script>
diff --git a/web/gui/src/main/webapp/tp/angular-cookies.js b/web/gui/src/main/webapp/tp/angular-cookies.js
new file mode 100644
index 0000000..f400db2
--- /dev/null
+++ b/web/gui/src/main/webapp/tp/angular-cookies.js
@@ -0,0 +1,206 @@
+/**
+ * @license AngularJS v1.3.5
+ * (c) 2010-2014 Google, Inc. http://angularjs.org
+ * License: MIT
+ */
+(function(window, angular, undefined) {'use strict';
+
+/**
+ * @ngdoc module
+ * @name ngCookies
+ * @description
+ *
+ * # ngCookies
+ *
+ * The `ngCookies` module provides a convenient wrapper for reading and writing browser cookies.
+ *
+ *
+ * <div doc-module-components="ngCookies"></div>
+ *
+ * See {@link ngCookies.$cookies `$cookies`} and
+ * {@link ngCookies.$cookieStore `$cookieStore`} for usage.
+ */
+
+
+angular.module('ngCookies', ['ng']).
+  /**
+   * @ngdoc service
+   * @name $cookies
+   *
+   * @description
+   * Provides read/write access to browser's cookies.
+   *
+   * 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.
+   *
+   * Requires the {@link ngCookies `ngCookies`} module to be installed.
+   *
+   * @example
+   *
+   * ```js
+   * angular.module('cookiesExample', ['ngCookies'])
+   *   .controller('ExampleController', ['$cookies', function($cookies) {
+   *     // Retrieving a cookie
+   *     var favoriteCookie = $cookies.myFavorite;
+   *     // Setting a cookie
+   *     $cookies.myFavorite = 'oatmeal';
+   *   }]);
+   * ```
+   */
+   factory('$cookies', ['$rootScope', '$browser', function($rootScope, $browser) {
+      var cookies = {},
+          lastCookies = {},
+          lastBrowserCookies,
+          runEval = false,
+          copy = angular.copy,
+          isUndefined = angular.isUndefined;
+
+      //creates a poller fn that copies all cookies from the $browser to service & inits the service
+      $browser.addPollFn(function() {
+        var currentCookies = $browser.cookies();
+        if (lastBrowserCookies != currentCookies) { //relies on browser.cookies() impl
+          lastBrowserCookies = currentCookies;
+          copy(currentCookies, lastCookies);
+          copy(currentCookies, cookies);
+          if (runEval) $rootScope.$apply();
+        }
+      })();
+
+      runEval = true;
+
+      //at the end of each eval, push cookies
+      //TODO: this should happen before the "delayed" watches fire, because if some cookies are not
+      //      strings or browser refuses to store some cookies, we update the model in the push fn.
+      $rootScope.$watch(push);
+
+      return cookies;
+
+
+      /**
+       * Pushes all the cookies from the service to the browser and verifies if all cookies were
+       * stored.
+       */
+      function push() {
+        var name,
+            value,
+            browserCookies,
+            updated;
+
+        //delete any cookies deleted in $cookies
+        for (name in lastCookies) {
+          if (isUndefined(cookies[name])) {
+            $browser.cookies(name, undefined);
+          }
+        }
+
+        //update all cookies updated in $cookies
+        for (name in cookies) {
+          value = cookies[name];
+          if (!angular.isString(value)) {
+            value = '' + value;
+            cookies[name] = value;
+          }
+          if (value !== lastCookies[name]) {
+            $browser.cookies(name, value);
+            updated = true;
+          }
+        }
+
+        //verify what was actually stored
+        if (updated) {
+          updated = false;
+          browserCookies = $browser.cookies();
+
+          for (name in cookies) {
+            if (cookies[name] !== browserCookies[name]) {
+              //delete or reset all cookies that the browser dropped from $cookies
+              if (isUndefined(browserCookies[name])) {
+                delete cookies[name];
+              } else {
+                cookies[name] = browserCookies[name];
+              }
+              updated = true;
+            }
+          }
+        }
+      }
+    }]).
+
+
+  /**
+   * @ngdoc service
+   * @name $cookieStore
+   * @requires $cookies
+   *
+   * @description
+   * Provides a key-value (string-object) storage, that is backed by session cookies.
+   * Objects put or retrieved from this storage are automatically serialized or
+   * deserialized by angular's toJson/fromJson.
+   *
+   * Requires the {@link ngCookies `ngCookies`} module to be installed.
+   *
+   * @example
+   *
+   * ```js
+   * angular.module('cookieStoreExample', ['ngCookies'])
+   *   .controller('ExampleController', ['$cookieStore', function($cookieStore) {
+   *     // Put cookie
+   *     $cookieStore.put('myFavorite','oatmeal');
+   *     // Get cookie
+   *     var favoriteCookie = $cookieStore.get('myFavorite');
+   *     // Removing a cookie
+   *     $cookieStore.remove('myFavorite');
+   *   }]);
+   * ```
+   */
+   factory('$cookieStore', ['$cookies', function($cookies) {
+
+      return {
+        /**
+         * @ngdoc method
+         * @name $cookieStore#get
+         *
+         * @description
+         * Returns the value of given cookie key
+         *
+         * @param {string} key Id to use for lookup.
+         * @returns {Object} Deserialized cookie value.
+         */
+        get: function(key) {
+          var value = $cookies[key];
+          return value ? angular.fromJson(value) : value;
+        },
+
+        /**
+         * @ngdoc method
+         * @name $cookieStore#put
+         *
+         * @description
+         * Sets a value for given cookie key
+         *
+         * @param {string} key Id for the `value`.
+         * @param {Object} value Value to be stored.
+         */
+        put: function(key, value) {
+          $cookies[key] = angular.toJson(value);
+        },
+
+        /**
+         * @ngdoc method
+         * @name $cookieStore#remove
+         *
+         * @description
+         * Remove given cookie
+         *
+         * @param {string} key Id of the key-value pair to delete.
+         */
+        remove: function(key) {
+          delete $cookies[key];
+        }
+      };
+
+    }]);
+
+
+})(window, window.angular);
diff --git a/web/gui/src/main/webapp/tp/angular-cookies.min.js b/web/gui/src/main/webapp/tp/angular-cookies.min.js
new file mode 100644
index 0000000..20333da
--- /dev/null
+++ b/web/gui/src/main/webapp/tp/angular-cookies.min.js
@@ -0,0 +1,8 @@
+/*
+ AngularJS v1.3.5
+ (c) 2010-2014 Google, Inc. http://angularjs.org
+ License: MIT
+*/
+(function(p,f,n){'use strict';f.module("ngCookies",["ng"]).factory("$cookies",["$rootScope","$browser",function(e,b){var c={},g={},h,k=!1,l=f.copy,m=f.isUndefined;b.addPollFn(function(){var a=b.cookies();h!=a&&(h=a,l(a,g),l(a,c),k&&e.$apply())})();k=!0;e.$watch(function(){var a,d,e;for(a in g)m(c[a])&&b.cookies(a,n);for(a in c)d=c[a],f.isString(d)||(d=""+d,c[a]=d),d!==g[a]&&(b.cookies(a,d),e=!0);if(e)for(a in d=b.cookies(),c)c[a]!==d[a]&&(m(d[a])?delete c[a]:c[a]=d[a])});return c}]).factory("$cookieStore",
+["$cookies",function(e){return{get:function(b){return(b=e[b])?f.fromJson(b):b},put:function(b,c){e[b]=f.toJson(c)},remove:function(b){delete e[b]}}}])})(window,window.angular);
+//# sourceMappingURL=angular-cookies.min.js.map