diff --git a/web/gui/src/main/webapp/app/fw/util/prefs.js b/web/gui/src/main/webapp/app/fw/util/prefs.js new file mode 100644 index 0000000000..f940ca7296 --- /dev/null +++ b/web/gui/src/main/webapp/app/fw/util/prefs.js @@ -0,0 +1,107 @@ +/* + * Copyright 2015 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. + */ + +/* + ONOS GUI -- Util -- User Preference Service + */ +(function () { + 'use strict'; + + // injected refs + var $log, $cookies; + + // 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(number) 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]] = Number(x[1]); + }); + + // update the cache + cache[name] = obj; + return obj; + } + // perhaps we have a cached copy.. + return cache[name]; + } + + 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 + 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); + $log.debug('<<>> Wrote cookie <'+name+'>:', str); + } + + angular.module('onosUtil') + .factory('PrefsService', ['$log', '$cookies', + function (_$log_, _$cookies_) { + $log = _$log_; + $cookies = _$cookies_; + + return { + getPrefs: getPrefs, + 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 5843b568e5..402ad8631d 100644 --- a/web/gui/src/main/webapp/app/view/topo/topo.js +++ b/web/gui/src/main/webapp/app/view/topo/topo.js @@ -29,7 +29,7 @@ ]; // references to injected services etc. - var $log, $cookies, fs, ks, zs, gs, ms, sus, flash, wss, + var $log, $cookies, fs, ks, zs, gs, ms, sus, flash, wss, ps, tes, tfs, tps, tis, tss, tls, tts, tos, fltr, ttbs; // DOM elements @@ -100,7 +100,7 @@ function toggleInstances(x) { if (x === 'keyev') { tis.toggle(); - updateCookieState('insts', tis.isVisible()); + updatePrefsState('insts', tis.isVisible()); } else if (x) { tis.show(); } else { @@ -112,7 +112,7 @@ function toggleMap(x) { var on = (x === 'keyev') ? !sus.visible(mapG) : !!x; sus.visible(mapG, on); - updateCookieState('bg', on); + updatePrefsState('bg', on); } // TODO: need wrapper functions for state changes needed in cookies @@ -248,22 +248,9 @@ .attr('opacity', b ? 1 : 0); } - // --- Config from Cookies ------------------------------------------- + // --- User Preferemces ---------------------------------------------- - // 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 = { + var defaultPrefsState = { bg: 1, insts: 1, summary: 1, @@ -271,54 +258,27 @@ hosts: 0 }; - var cookieState = {}; + var prefsState = {}; - 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 topoDefPrefs() { + return angular.extend({}, defaultPrefsState); } - 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 updatePrefsState(what, b) { + prefsState[what] = b ? 1 : 0; + ps.setPrefs('topo_prefs', prefsState); } - function updateCookieState(what, b) { - cookieState[what] = b ? 1 : 0; - writeCookieState(); - } - function restoreConfigFromCookies() { - readCookieState(); - $log.debug('Cookie State:', cookieState); + function restoreConfigFromPrefs() { + // NOTE: toolbar will have set this for us.. + prefsState = ps.getPrefs('topo_prefs'); - toggleInstances(cookieState.insts); - tps.toggleSummary(cookieState.summary); - tps.toggleDetails(cookieState.detail); + $log.debug('TOPO---- Prefs State:', prefsState); + + toggleInstances(prefsState.insts); + tps.toggleSummary(prefsState.summary); + tps.toggleDetails(prefsState.detail); } @@ -328,15 +288,15 @@ .controller('OvTopoCtrl', ['$scope', '$log', '$location', '$timeout', '$cookies', 'FnService', 'MastService', 'KeyService', 'ZoomService', 'GlyphService', 'MapService', 'SvgUtilService', 'FlashService', - 'WebSocketService', + 'WebSocketService', 'PrefsService', 'TopoEventService', 'TopoForceService', 'TopoPanelService', 'TopoInstService', 'TopoSelectService', 'TopoLinkService', 'TopoTrafficService', 'TopoObliqueService', 'TopoFilterService', 'TopoToolbarService', 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_) { + _zs_, _gs_, _ms_, _sus_, _flash_, _wss_, _ps_, _tes_, _tfs_, + _tps_, _tis_, _tss_, _tls_, _tts_, _tos_, _fltr_, _ttbs_) { var self = this, projection, dim, @@ -359,6 +319,7 @@ sus = _sus_; flash = _flash_; wss = _wss_; + ps = _ps_; tes = _tes_; tfs = _tfs_; // TODO: consider funnelling actions through TopoForceService... @@ -403,7 +364,7 @@ function (proj) { projection = proj; $log.debug('** We installed the projection: ', proj); - toggleMap(cookieState.bg); + toggleMap(prefsState.bg); } ); @@ -414,7 +375,7 @@ tes.start(); // temporary solution for persisting user settings - restoreConfigFromCookies(); + restoreConfigFromPrefs(); $log.log('OvTopoCtrl has been created'); }]); 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 eb7c0069fd..8a1895b9ba 100644 --- a/web/gui/src/main/webapp/app/view/topo/topoToolbar.js +++ b/web/gui/src/main/webapp/app/view/topo/topoToolbar.js @@ -23,13 +23,14 @@ 'use strict'; // injected references - var $log, tbs, api; + var $log, tbs, ps, api; // internal state var toolbar, keyData; // constants - var name = 'topo-tbar'; + var name = 'topo-tbar', + cooktag = 'topo_prefs'; // key to button mapping data var k2b = { @@ -58,8 +59,46 @@ E: { id: 'eqMaster-btn', gid: 'eqMaster' } }; + // initial toggle state: default settings and tag to key mapping + var defaultPrefsState = { + bg: 1, + insts: 1, + summary: 1, + detail: 1, + hosts: 0 + }, + prefsMap = { + bg: 'B', + insts: 'I', + summary: 'O', + details: 'D', + hosts: 'H' + }; + function init(_api_) { api = _api_; + + // retrieve initial toggle button settings from user prefs + setInitToggleState(); + } + + function topoDefPrefs() { + return angular.extend({}, defaultPrefsState); + } + + function setInitToggleState() { + var state = ps.getPrefs(cooktag); + $log.debug('TOOLBAR---- read prefs state:', state); + + if (!state) { + state = topoDefPrefs(); + ps.setPrefs(cooktag, state); + $log.debug('TOOLBAR---- Set default prefs state:', state); + } + + angular.forEach(prefsMap, function (v, k) { + k2b[v].isel = !!state[k]; + }); } function initKeyData() { @@ -142,11 +181,13 @@ } angular.module('ovTopo') - .factory('TopoToolbarService', ['$log', 'ToolbarService', + .factory('TopoToolbarService', + ['$log', 'ToolbarService', 'PrefsService', - function (_$log_, _tbs_) { + function (_$log_, _tbs_, _ps_) { $log = _$log_; tbs = _tbs_; + ps = _ps_; return { init: init, diff --git a/web/gui/src/main/webapp/index.html b/web/gui/src/main/webapp/index.html index 06e0336a74..3a335a7a83 100644 --- a/web/gui/src/main/webapp/index.html +++ b/web/gui/src/main/webapp/index.html @@ -45,6 +45,7 @@ + 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 new file mode 100644 index 0000000000..2b8a15c9fe --- /dev/null +++ b/web/gui/src/main/webapp/tests/app/fw/util/prefs-spec.js @@ -0,0 +1,57 @@ +/* + * Copyright 2015 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. + */ + +/* + ONOS GUI -- Util -- User Preference Service - Unit Tests + */ +describe('factory: fw/util/prefs.js', function() { + var $cookies, ps, fs; + + beforeEach(module('onosUtil')); + + var mockCookies = { + foo: 'bar' + }; + + beforeEach(function () { + module(function ($provide) { + $provide.value('$cookies', mockCookies); + }); + }); + + beforeEach(inject(function (PrefsService, FnService, _$cookies_) { + ps = PrefsService; + fs = FnService; + $cookies = _$cookies_; + })); + + it('should define PrefsService', function () { + expect(ps).toBeDefined(); + }); + + it('should define api functions', function () { + expect(fs.areFunctions(ps, [ + 'getPrefs', 'setPrefs' + ])).toBe(true); + }); + + // === Tests for getPrefs() + // TODO unit tests for getPrefs() + + // === Tests for setPrefs() + // TODO unit tests for setPrefs() + +}); diff --git a/web/gui/src/main/webapp/tests/karma.conf.js b/web/gui/src/main/webapp/tests/karma.conf.js index afd40ec6d4..bd572c1b05 100644 --- a/web/gui/src/main/webapp/tests/karma.conf.js +++ b/web/gui/src/main/webapp/tests/karma.conf.js @@ -19,6 +19,7 @@ module.exports = function(config) { '../tp/angular.js', '../tp/angular-mocks.js', '../tp/angular-route.js', + '../tp/angular-cookies.js', '../tp/d3.js', '../tp/topojson.v1.min.js',