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