Simon Hunt

GUI -- User Prefs written as a service; persistent topo settings updated a bit; still WIP.

Change-Id: I6945dd9eb4b325a8f1637c44e2c4b271126b2bc4
1 +/*
2 + * Copyright 2015 Open Networking Laboratory
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + */
16 +
17 +/*
18 + ONOS GUI -- Util -- User Preference Service
19 + */
20 +(function () {
21 + 'use strict';
22 +
23 + // injected refs
24 + var $log, $cookies;
25 +
26 + // internal state
27 + var cache = {};
28 +
29 + // NOTE: in Angular 1.3.5, $cookies is just a simple object, and
30 + // cookie values are just strings. From the 1.3.5 docs:
31 + //
32 + // "Only a simple Object is exposed and by adding or removing
33 + // properties to/from this object, new cookies are created/deleted
34 + // at the end of current $eval. The object's properties can only
35 + // be strings."
36 + //
37 + // We may want to upgrade the version of Angular sometime soon
38 + // since later version support objects as cookie values.
39 +
40 + // NOTE: prefs represented as simple name/value(number) pairs
41 + // => a temporary restriction while we are encoding into cookies
42 + /*
43 + {
44 + foo: 1,
45 + bar: 0,
46 + goo: 2
47 + }
48 +
49 + stored as "foo:1,bar:0,goo:2"
50 + */
51 +
52 + // reads cookie with given name and returns an object rep of its value
53 + // or null if no such cookie is set
54 + function getPrefs(name) {
55 + var cook = $cookies[name],
56 + bits,
57 + obj = {};
58 +
59 + if (cook) {
60 + bits = cook.split(',');
61 + bits.forEach(function (value) {
62 + var x = value.split(':');
63 + obj[x[0]] = Number(x[1]);
64 + });
65 +
66 + // update the cache
67 + cache[name] = obj;
68 + return obj;
69 + }
70 + // perhaps we have a cached copy..
71 + return cache[name];
72 + }
73 +
74 + function setPrefs(name, obj) {
75 + var bits = [],
76 + str;
77 +
78 + angular.forEach(obj, function (value, key) {
79 + bits.push(key + ':' + value);
80 + });
81 + str = bits.join(',');
82 +
83 + // keep a cached copy of the object
84 + cache[name] = obj;
85 +
86 + // The angular way of doing this...
87 + // $cookies[name] = str;
88 + // ...but it appears that this gets delayed, and doesn't 'stick' ??
89 +
90 + // FORCE cookie to be set by writing directly to document.cookie...
91 + document.cookie = name + '=' + encodeURIComponent(str);
92 + $log.debug('<<>> Wrote cookie <'+name+'>:', str);
93 + }
94 +
95 + angular.module('onosUtil')
96 + .factory('PrefsService', ['$log', '$cookies',
97 + function (_$log_, _$cookies_) {
98 + $log = _$log_;
99 + $cookies = _$cookies_;
100 +
101 + return {
102 + getPrefs: getPrefs,
103 + setPrefs: setPrefs
104 + };
105 + }]);
106 +
107 +}());
...@@ -29,7 +29,7 @@ ...@@ -29,7 +29,7 @@
29 ]; 29 ];
30 30
31 // references to injected services etc. 31 // references to injected services etc.
32 - var $log, $cookies, fs, ks, zs, gs, ms, sus, flash, wss, 32 + var $log, $cookies, fs, ks, zs, gs, ms, sus, flash, wss, ps,
33 tes, tfs, tps, tis, tss, tls, tts, tos, fltr, ttbs; 33 tes, tfs, tps, tis, tss, tls, tts, tos, fltr, ttbs;
34 34
35 // DOM elements 35 // DOM elements
...@@ -100,7 +100,7 @@ ...@@ -100,7 +100,7 @@
100 function toggleInstances(x) { 100 function toggleInstances(x) {
101 if (x === 'keyev') { 101 if (x === 'keyev') {
102 tis.toggle(); 102 tis.toggle();
103 - updateCookieState('insts', tis.isVisible()); 103 + updatePrefsState('insts', tis.isVisible());
104 } else if (x) { 104 } else if (x) {
105 tis.show(); 105 tis.show();
106 } else { 106 } else {
...@@ -112,7 +112,7 @@ ...@@ -112,7 +112,7 @@
112 function toggleMap(x) { 112 function toggleMap(x) {
113 var on = (x === 'keyev') ? !sus.visible(mapG) : !!x; 113 var on = (x === 'keyev') ? !sus.visible(mapG) : !!x;
114 sus.visible(mapG, on); 114 sus.visible(mapG, on);
115 - updateCookieState('bg', on); 115 + updatePrefsState('bg', on);
116 } 116 }
117 117
118 // TODO: need wrapper functions for state changes needed in cookies 118 // TODO: need wrapper functions for state changes needed in cookies
...@@ -248,22 +248,9 @@ ...@@ -248,22 +248,9 @@
248 .attr('opacity', b ? 1 : 0); 248 .attr('opacity', b ? 1 : 0);
249 } 249 }
250 250
251 - // --- Config from Cookies ------------------------------------------- 251 + // --- User Preferemces ----------------------------------------------
252 252
253 - // TODO: write a general purpose cookie service, rather than custom here 253 + var defaultPrefsState = {
254 -
255 - // NOTE: in Angular 1.3.5, $cookies is just a simple object, and
256 - // cookie values are just strings. From the 1.3.5 docs:
257 - //
258 - // "Only a simple Object is exposed and by adding or removing
259 - // properties to/from this object, new cookies are created/deleted
260 - // at the end of current $eval. The object's properties can only
261 - // be strings."
262 - //
263 - // We may want to upgrade the version of Angular sometime soon
264 - // since later version support objects as cookie values.
265 -
266 - var defaultCookieState = {
267 bg: 1, 254 bg: 1,
268 insts: 1, 255 insts: 1,
269 summary: 1, 256 summary: 1,
...@@ -271,54 +258,27 @@ ...@@ -271,54 +258,27 @@
271 hosts: 0 258 hosts: 0
272 }; 259 };
273 260
274 - var cookieState = {}; 261 + var prefsState = {};
275 -
276 - function writeCookieState() {
277 - var bits = [],
278 - str;
279 - angular.forEach(cookieState, function (value, key) {
280 - bits.push(key + ':' + value);
281 - });
282 - str = bits.join(',');
283 -
284 - // The angular way of doing this...
285 - // $cookies.topo_state = str;
286 - // ...but it appears that this gets delayed, and doesn't 'stick' ??
287 262
288 - // FORCE cookie to be set by writing directly to document.cookie... 263 + function topoDefPrefs() {
289 - document.cookie = 'topo_state=' + encodeURIComponent(str); 264 + return angular.extend({}, defaultPrefsState);
290 - $log.debug('<<>> Wrote cookie:', str);
291 } 265 }
292 266
293 - function readCookieState() { 267 + function updatePrefsState(what, b) {
294 - var cook = $cookies.topo_state || '', 268 + prefsState[what] = b ? 1 : 0;
295 - bits; 269 + ps.setPrefs('topo_prefs', prefsState);
296 -
297 - if (!cook) {
298 - cookieState = angular.extend({}, defaultCookieState);
299 - writeCookieState(); // seed the pot
300 -
301 - } else {
302 - bits = cook.split(',');
303 - bits.forEach(function (value) {
304 - var x = value.split(':');
305 - cookieState[x[0]] = Number(x[1]);
306 - });
307 - }
308 } 270 }
309 271
310 - function updateCookieState(what, b) {
311 - cookieState[what] = b ? 1 : 0;
312 - writeCookieState();
313 - }
314 272
315 - function restoreConfigFromCookies() { 273 + function restoreConfigFromPrefs() {
316 - readCookieState(); 274 + // NOTE: toolbar will have set this for us..
317 - $log.debug('Cookie State:', cookieState); 275 + prefsState = ps.getPrefs('topo_prefs');
276 +
277 + $log.debug('TOPO---- Prefs State:', prefsState);
318 278
319 - toggleInstances(cookieState.insts); 279 + toggleInstances(prefsState.insts);
320 - tps.toggleSummary(cookieState.summary); 280 + tps.toggleSummary(prefsState.summary);
321 - tps.toggleDetails(cookieState.detail); 281 + tps.toggleDetails(prefsState.detail);
322 } 282 }
323 283
324 284
...@@ -328,15 +288,15 @@ ...@@ -328,15 +288,15 @@
328 .controller('OvTopoCtrl', ['$scope', '$log', '$location', '$timeout', 288 .controller('OvTopoCtrl', ['$scope', '$log', '$location', '$timeout',
329 '$cookies', 'FnService', 'MastService', 'KeyService', 'ZoomService', 289 '$cookies', 'FnService', 'MastService', 'KeyService', 'ZoomService',
330 'GlyphService', 'MapService', 'SvgUtilService', 'FlashService', 290 'GlyphService', 'MapService', 'SvgUtilService', 'FlashService',
331 - 'WebSocketService', 291 + 'WebSocketService', 'PrefsService',
332 'TopoEventService', 'TopoForceService', 'TopoPanelService', 292 'TopoEventService', 'TopoForceService', 'TopoPanelService',
333 'TopoInstService', 'TopoSelectService', 'TopoLinkService', 293 'TopoInstService', 'TopoSelectService', 'TopoLinkService',
334 'TopoTrafficService', 'TopoObliqueService', 'TopoFilterService', 294 'TopoTrafficService', 'TopoObliqueService', 'TopoFilterService',
335 'TopoToolbarService', 295 'TopoToolbarService',
336 296
337 function ($scope, _$log_, $loc, $timeout, _$cookies_, _fs_, mast, _ks_, 297 function ($scope, _$log_, $loc, $timeout, _$cookies_, _fs_, mast, _ks_,
338 - _zs_, _gs_, _ms_, _sus_, _flash_, _wss_, _tes_, _tfs_, _tps_, 298 + _zs_, _gs_, _ms_, _sus_, _flash_, _wss_, _ps_, _tes_, _tfs_,
339 - _tis_, _tss_, _tls_, _tts_, _tos_, _fltr_, _ttbs_) { 299 + _tps_, _tis_, _tss_, _tls_, _tts_, _tos_, _fltr_, _ttbs_) {
340 var self = this, 300 var self = this,
341 projection, 301 projection,
342 dim, 302 dim,
...@@ -359,6 +319,7 @@ ...@@ -359,6 +319,7 @@
359 sus = _sus_; 319 sus = _sus_;
360 flash = _flash_; 320 flash = _flash_;
361 wss = _wss_; 321 wss = _wss_;
322 + ps = _ps_;
362 tes = _tes_; 323 tes = _tes_;
363 tfs = _tfs_; 324 tfs = _tfs_;
364 // TODO: consider funnelling actions through TopoForceService... 325 // TODO: consider funnelling actions through TopoForceService...
...@@ -403,7 +364,7 @@ ...@@ -403,7 +364,7 @@
403 function (proj) { 364 function (proj) {
404 projection = proj; 365 projection = proj;
405 $log.debug('** We installed the projection: ', proj); 366 $log.debug('** We installed the projection: ', proj);
406 - toggleMap(cookieState.bg); 367 + toggleMap(prefsState.bg);
407 } 368 }
408 ); 369 );
409 370
...@@ -414,7 +375,7 @@ ...@@ -414,7 +375,7 @@
414 tes.start(); 375 tes.start();
415 376
416 // temporary solution for persisting user settings 377 // temporary solution for persisting user settings
417 - restoreConfigFromCookies(); 378 + restoreConfigFromPrefs();
418 379
419 $log.log('OvTopoCtrl has been created'); 380 $log.log('OvTopoCtrl has been created');
420 }]); 381 }]);
......
...@@ -23,13 +23,14 @@ ...@@ -23,13 +23,14 @@
23 'use strict'; 23 'use strict';
24 24
25 // injected references 25 // injected references
26 - var $log, tbs, api; 26 + var $log, tbs, ps, api;
27 27
28 // internal state 28 // internal state
29 var toolbar, keyData; 29 var toolbar, keyData;
30 30
31 // constants 31 // constants
32 - var name = 'topo-tbar'; 32 + var name = 'topo-tbar',
33 + cooktag = 'topo_prefs';
33 34
34 // key to button mapping data 35 // key to button mapping data
35 var k2b = { 36 var k2b = {
...@@ -58,8 +59,46 @@ ...@@ -58,8 +59,46 @@
58 E: { id: 'eqMaster-btn', gid: 'eqMaster' } 59 E: { id: 'eqMaster-btn', gid: 'eqMaster' }
59 }; 60 };
60 61
62 + // initial toggle state: default settings and tag to key mapping
63 + var defaultPrefsState = {
64 + bg: 1,
65 + insts: 1,
66 + summary: 1,
67 + detail: 1,
68 + hosts: 0
69 + },
70 + prefsMap = {
71 + bg: 'B',
72 + insts: 'I',
73 + summary: 'O',
74 + details: 'D',
75 + hosts: 'H'
76 + };
77 +
61 function init(_api_) { 78 function init(_api_) {
62 api = _api_; 79 api = _api_;
80 +
81 + // retrieve initial toggle button settings from user prefs
82 + setInitToggleState();
83 + }
84 +
85 + function topoDefPrefs() {
86 + return angular.extend({}, defaultPrefsState);
87 + }
88 +
89 + function setInitToggleState() {
90 + var state = ps.getPrefs(cooktag);
91 + $log.debug('TOOLBAR---- read prefs state:', state);
92 +
93 + if (!state) {
94 + state = topoDefPrefs();
95 + ps.setPrefs(cooktag, state);
96 + $log.debug('TOOLBAR---- Set default prefs state:', state);
97 + }
98 +
99 + angular.forEach(prefsMap, function (v, k) {
100 + k2b[v].isel = !!state[k];
101 + });
63 } 102 }
64 103
65 function initKeyData() { 104 function initKeyData() {
...@@ -142,11 +181,13 @@ ...@@ -142,11 +181,13 @@
142 } 181 }
143 182
144 angular.module('ovTopo') 183 angular.module('ovTopo')
145 - .factory('TopoToolbarService', ['$log', 'ToolbarService', 184 + .factory('TopoToolbarService',
185 + ['$log', 'ToolbarService', 'PrefsService',
146 186
147 - function (_$log_, _tbs_) { 187 + function (_$log_, _tbs_, _ps_) {
148 $log = _$log_; 188 $log = _$log_;
149 tbs = _tbs_; 189 tbs = _tbs_;
190 + ps = _ps_;
150 191
151 return { 192 return {
152 init: init, 193 init: init,
......
...@@ -45,6 +45,7 @@ ...@@ -45,6 +45,7 @@
45 <script src="app/fw/util/random.js"></script> 45 <script src="app/fw/util/random.js"></script>
46 <script src="app/fw/util/theme.js"></script> 46 <script src="app/fw/util/theme.js"></script>
47 <script src="app/fw/util/keys.js"></script> 47 <script src="app/fw/util/keys.js"></script>
48 + <script src="app/fw/util/prefs.js"></script>
48 49
49 <script src="app/fw/mast/mast.js"></script> 50 <script src="app/fw/mast/mast.js"></script>
50 <script src="app/fw/nav/nav.js"></script> 51 <script src="app/fw/nav/nav.js"></script>
......
1 +/*
2 + * Copyright 2015 Open Networking Laboratory
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + */
16 +
17 +/*
18 + ONOS GUI -- Util -- User Preference Service - Unit Tests
19 + */
20 +describe('factory: fw/util/prefs.js', function() {
21 + var $cookies, ps, fs;
22 +
23 + beforeEach(module('onosUtil'));
24 +
25 + var mockCookies = {
26 + foo: 'bar'
27 + };
28 +
29 + beforeEach(function () {
30 + module(function ($provide) {
31 + $provide.value('$cookies', mockCookies);
32 + });
33 + });
34 +
35 + beforeEach(inject(function (PrefsService, FnService, _$cookies_) {
36 + ps = PrefsService;
37 + fs = FnService;
38 + $cookies = _$cookies_;
39 + }));
40 +
41 + it('should define PrefsService', function () {
42 + expect(ps).toBeDefined();
43 + });
44 +
45 + it('should define api functions', function () {
46 + expect(fs.areFunctions(ps, [
47 + 'getPrefs', 'setPrefs'
48 + ])).toBe(true);
49 + });
50 +
51 + // === Tests for getPrefs()
52 + // TODO unit tests for getPrefs()
53 +
54 + // === Tests for setPrefs()
55 + // TODO unit tests for setPrefs()
56 +
57 +});
...@@ -19,6 +19,7 @@ module.exports = function(config) { ...@@ -19,6 +19,7 @@ module.exports = function(config) {
19 '../tp/angular.js', 19 '../tp/angular.js',
20 '../tp/angular-mocks.js', 20 '../tp/angular-mocks.js',
21 '../tp/angular-route.js', 21 '../tp/angular-route.js',
22 + '../tp/angular-cookies.js',
22 '../tp/d3.js', 23 '../tp/d3.js',
23 '../tp/topojson.v1.min.js', 24 '../tp/topojson.v1.min.js',
24 25
......