GUI -- Completed implementation of Instance events (add, update, remove)
- fixed instance color selection (using cat7() function) - miscellaneous additions to utility functions. - etc. and so on... Change-Id: I61895489ccc60fa17beda9e920e65742e0f2c526
Showing
14 changed files
with
359 additions
and
15 deletions
... | @@ -132,8 +132,97 @@ | ... | @@ -132,8 +132,97 @@ |
132 | $log.warn('SvgUtilService: loadGlow -- To Be Implemented'); | 132 | $log.warn('SvgUtilService: loadGlow -- To Be Implemented'); |
133 | } | 133 | } |
134 | 134 | ||
135 | + // --- Ordinal scales for 7 values. | ||
136 | + // TODO: tune colors for light and dark themes | ||
137 | + // Note: These colors look good on the white background. Still, need to tune for dark. | ||
138 | + | ||
139 | + // blue brown brick red sea green purple dark teal lime | ||
140 | + var lightNorm = ['#3E5780', '#78533B', '#CB4D28', '#018D61', '#8A2979', '#006D73', '#56AF00'], | ||
141 | + lightMute = ['#A8B8CC', '#CCB3A8', '#FFC2BD', '#96D6BF', '#D19FCE', '#8FCCCA', '#CAEAA4'], | ||
142 | + | ||
143 | + darkNorm = ['#3E5780', '#78533B', '#CB4D28', '#018D61', '#8A2979', '#006D73', '#56AF00'], | ||
144 | + darkMute = ['#A8B8CC', '#CCB3A8', '#FFC2BD', '#96D6BF', '#D19FCE', '#8FCCCA', '#CAEAA4']; | ||
145 | + | ||
146 | + var colors= { | ||
147 | + light: { | ||
148 | + norm: d3.scale.ordinal().range(lightNorm), | ||
149 | + mute: d3.scale.ordinal().range(lightMute) | ||
150 | + }, | ||
151 | + dark: { | ||
152 | + norm: d3.scale.ordinal().range(darkNorm), | ||
153 | + mute: d3.scale.ordinal().range(darkMute) | ||
154 | + } | ||
155 | + }; | ||
156 | + | ||
135 | function cat7() { | 157 | function cat7() { |
136 | - $log.warn('SvgUtilService: cat7 -- To Be Implemented'); | 158 | + var tcid = 'd3utilTestCard'; |
159 | + | ||
160 | + function getColor(id, muted, theme) { | ||
161 | + // NOTE: since we are lazily assigning domain ids, we need to | ||
162 | + // get the color from all 4 scales, to keep the domains | ||
163 | + // in sync. | ||
164 | + var ln = colors.light.norm(id), | ||
165 | + lm = colors.light.mute(id), | ||
166 | + dn = colors.dark.norm(id), | ||
167 | + dm = colors.dark.mute(id); | ||
168 | + if (theme === 'dark') { | ||
169 | + return muted ? dm : dn; | ||
170 | + } else { | ||
171 | + return muted ? lm : ln; | ||
172 | + } | ||
173 | + } | ||
174 | + | ||
175 | + function testCard(svg) { | ||
176 | + var g = svg.select('g#' + tcid), | ||
177 | + dom = d3.range(7), | ||
178 | + k, muted, theme, what; | ||
179 | + | ||
180 | + if (!g.empty()) { | ||
181 | + g.remove(); | ||
182 | + | ||
183 | + } else { | ||
184 | + g = svg.append('g') | ||
185 | + .attr('id', tcid) | ||
186 | + .attr('transform', 'scale(4)translate(20,20)'); | ||
187 | + | ||
188 | + for (k=0; k<4; k++) { | ||
189 | + muted = k%2; | ||
190 | + what = muted ? ' muted' : ' normal'; | ||
191 | + theme = k < 2 ? 'light' : 'dark'; | ||
192 | + dom.forEach(function (id, i) { | ||
193 | + var x = i * 20, | ||
194 | + y = k * 20, | ||
195 | + f = get(id, muted, theme); | ||
196 | + g.append('circle').attr({ | ||
197 | + cx: x, | ||
198 | + cy: y, | ||
199 | + r: 5, | ||
200 | + fill: f | ||
201 | + }); | ||
202 | + }); | ||
203 | + g.append('rect').attr({ | ||
204 | + x: 140, | ||
205 | + y: k * 20 - 5, | ||
206 | + width: 32, | ||
207 | + height: 10, | ||
208 | + rx: 2, | ||
209 | + fill: '#888' | ||
210 | + }); | ||
211 | + g.append('text').text(theme + what) | ||
212 | + .attr({ | ||
213 | + x: 142, | ||
214 | + y: k * 20 + 2, | ||
215 | + fill: 'white' | ||
216 | + }) | ||
217 | + .style('font-size', '4pt'); | ||
218 | + } | ||
219 | + } | ||
220 | + } | ||
221 | + | ||
222 | + return { | ||
223 | + testCard: testCard, | ||
224 | + getColor: getColor | ||
225 | + }; | ||
137 | } | 226 | } |
138 | 227 | ||
139 | function translate(x, y) { | 228 | function translate(x, y) { | ... | ... |
... | @@ -45,7 +45,38 @@ | ... | @@ -45,7 +45,38 @@ |
45 | 45 | ||
46 | // Returns true if all names in the array are defined as functions | 46 | // Returns true if all names in the array are defined as functions |
47 | // on the given api object; false otherwise. | 47 | // on the given api object; false otherwise. |
48 | + // Also returns false if there are properties on the api that are NOT | ||
49 | + // listed in the array of names. | ||
48 | function areFunctions(api, fnNames) { | 50 | function areFunctions(api, fnNames) { |
51 | + var fnLookup = {}, | ||
52 | + extraFound = false; | ||
53 | + | ||
54 | + if (!isA(fnNames)) { | ||
55 | + return false; | ||
56 | + } | ||
57 | + var n = fnNames.length, | ||
58 | + i, name; | ||
59 | + for (i=0; i<n; i++) { | ||
60 | + name = fnNames[i]; | ||
61 | + if (!isF(api[name])) { | ||
62 | + return false; | ||
63 | + } | ||
64 | + fnLookup[name] = true; | ||
65 | + } | ||
66 | + | ||
67 | + // check for properties on the API that are not listed in the array, | ||
68 | + angular.forEach(api, function (value, key) { | ||
69 | + if (!fnLookup[key]) { | ||
70 | + extraFound = true; | ||
71 | + } | ||
72 | + }); | ||
73 | + return !extraFound; | ||
74 | + } | ||
75 | + | ||
76 | + // Returns true if all names in the array are defined as functions | ||
77 | + // on the given api object; false otherwise. This is a non-strict version | ||
78 | + // that does not care about other properties on the api. | ||
79 | + function areFunctionsNonStrict(api, fnNames) { | ||
49 | if (!isA(fnNames)) { | 80 | if (!isA(fnNames)) { |
50 | return false; | 81 | return false; |
51 | } | 82 | } |
... | @@ -71,6 +102,21 @@ | ... | @@ -71,6 +102,21 @@ |
71 | }; | 102 | }; |
72 | } | 103 | } |
73 | 104 | ||
105 | + // search through an array of objects, looking for the one with the | ||
106 | + // tagged property matching the given key. tag defaults to 'id'. | ||
107 | + // returns the index of the matching object, or -1 for no match. | ||
108 | + function find(key, array, tag) { | ||
109 | + var _tag = tag || 'id', | ||
110 | + idx, n, d; | ||
111 | + for (idx = 0, n = array.length; idx < n; idx++) { | ||
112 | + d = array[idx]; | ||
113 | + if (d[_tag] === key) { | ||
114 | + return idx; | ||
115 | + } | ||
116 | + } | ||
117 | + return -1; | ||
118 | + } | ||
119 | + | ||
74 | angular.module('onosUtil') | 120 | angular.module('onosUtil') |
75 | .factory('FnService', ['$window', function (_$window_) { | 121 | .factory('FnService', ['$window', function (_$window_) { |
76 | $window = _$window_; | 122 | $window = _$window_; |
... | @@ -82,7 +128,9 @@ | ... | @@ -82,7 +128,9 @@ |
82 | isO: isO, | 128 | isO: isO, |
83 | contains: contains, | 129 | contains: contains, |
84 | areFunctions: areFunctions, | 130 | areFunctions: areFunctions, |
85 | - windowSize: windowSize | 131 | + areFunctionsNonStrict: areFunctionsNonStrict, |
132 | + windowSize: windowSize, | ||
133 | + find: find | ||
86 | }; | 134 | }; |
87 | }]); | 135 | }]); |
88 | 136 | ... | ... |
... | @@ -30,7 +30,9 @@ | ... | @@ -30,7 +30,9 @@ |
30 | 30 | ||
31 | var evHandler = { | 31 | var evHandler = { |
32 | showSummary: showSummary, | 32 | showSummary: showSummary, |
33 | - addInstance: addInstance | 33 | + addInstance: addInstance, |
34 | + updateInstance: updateInstance, | ||
35 | + removeInstance: removeInstance | ||
34 | // TODO: implement remaining handlers.. | 36 | // TODO: implement remaining handlers.. |
35 | 37 | ||
36 | }; | 38 | }; |
... | @@ -51,6 +53,16 @@ | ... | @@ -51,6 +53,16 @@ |
51 | tis.addInstance(ev.payload); | 53 | tis.addInstance(ev.payload); |
52 | } | 54 | } |
53 | 55 | ||
56 | + function updateInstance(ev) { | ||
57 | + $log.debug(' **** Update Instance **** ', ev.payload); | ||
58 | + tis.updateInstance(ev.payload); | ||
59 | + } | ||
60 | + | ||
61 | + function removeInstance(ev) { | ||
62 | + $log.debug(' **** Remove Instance **** ', ev.payload); | ||
63 | + tis.removeInstance(ev.payload); | ||
64 | + } | ||
65 | + | ||
54 | // ========================== | 66 | // ========================== |
55 | 67 | ||
56 | var dispatcher = { | 68 | var dispatcher = { | ... | ... |
... | @@ -23,7 +23,7 @@ | ... | @@ -23,7 +23,7 @@ |
23 | 'use strict'; | 23 | 'use strict'; |
24 | 24 | ||
25 | // injected refs | 25 | // injected refs |
26 | - var $log, ps, sus, gs; | 26 | + var $log, ps, sus, gs, ts, fs; |
27 | 27 | ||
28 | // configuration | 28 | // configuration |
29 | var instCfg = { | 29 | var instCfg = { |
... | @@ -79,6 +79,23 @@ | ... | @@ -79,6 +79,23 @@ |
79 | } | 79 | } |
80 | } | 80 | } |
81 | 81 | ||
82 | + function removeInstance(data) { | ||
83 | + var id = data.id, | ||
84 | + d = onosInstances[id]; | ||
85 | + if (d) { | ||
86 | + var idx = fs.find(id, onosOrder); | ||
87 | + if (idx >= 0) { | ||
88 | + onosOrder.splice(idx, 1); | ||
89 | + } | ||
90 | + delete onosInstances[id]; | ||
91 | + updateInstances(); | ||
92 | + } else { | ||
93 | + logicError('removeInstance lookup fail. ID = "' + id + '"'); | ||
94 | + } | ||
95 | + } | ||
96 | + | ||
97 | + // ========================== | ||
98 | + | ||
82 | function computeDim(self) { | 99 | function computeDim(self) { |
83 | var css = window.getComputedStyle(self); | 100 | var css = window.getComputedStyle(self); |
84 | return { | 101 | return { |
... | @@ -143,9 +160,7 @@ | ... | @@ -143,9 +160,7 @@ |
143 | } | 160 | } |
144 | 161 | ||
145 | function instColor(id, online) { | 162 | function instColor(id, online) { |
146 | - // TODO: fix this.. | 163 | + return sus.cat7().getColor(id, !online, ts.theme()); |
147 | - //return cat7.get(id, !online, network.view.getTheme()); | ||
148 | - return '#3E5780'; | ||
149 | } | 164 | } |
150 | 165 | ||
151 | // ============================== | 166 | // ============================== |
... | @@ -288,17 +303,22 @@ | ... | @@ -288,17 +303,22 @@ |
288 | angular.module('ovTopo') | 303 | angular.module('ovTopo') |
289 | .factory('TopoInstService', | 304 | .factory('TopoInstService', |
290 | ['$log', 'PanelService', 'SvgUtilService', 'GlyphService', | 305 | ['$log', 'PanelService', 'SvgUtilService', 'GlyphService', |
306 | + 'ThemeService', 'FnService', | ||
291 | 307 | ||
292 | - function (_$log_, _ps_, _sus_, _gs_) { | 308 | + function (_$log_, _ps_, _sus_, _gs_, _ts_, _fs_) { |
293 | $log = _$log_; | 309 | $log = _$log_; |
294 | ps = _ps_; | 310 | ps = _ps_; |
295 | sus = _sus_; | 311 | sus = _sus_; |
296 | gs = _gs_; | 312 | gs = _gs_; |
313 | + ts = _ts_; | ||
314 | + fs = _fs_; | ||
297 | 315 | ||
298 | return { | 316 | return { |
299 | initInst: initInst, | 317 | initInst: initInst, |
300 | destroyInst: destroyInst, | 318 | destroyInst: destroyInst, |
301 | - addInstance: addInstance | 319 | + addInstance: addInstance, |
320 | + updateInstance: updateInstance, | ||
321 | + removeInstance: removeInstance | ||
302 | }; | 322 | }; |
303 | }]); | 323 | }]); |
304 | }()); | 324 | }()); | ... | ... |
... | @@ -36,7 +36,7 @@ describe('factory: fw/remote/wsevent.js', function () { | ... | @@ -36,7 +36,7 @@ describe('factory: fw/remote/wsevent.js', function () { |
36 | 36 | ||
37 | it('should define api functions', function () { | 37 | it('should define api functions', function () { |
38 | expect(fs.areFunctions(wse, [ | 38 | expect(fs.areFunctions(wse, [ |
39 | - 'sendEvent' | 39 | + 'sendEvent', 'resetSid' |
40 | ])).toBeTruthy(); | 40 | ])).toBeTruthy(); |
41 | }); | 41 | }); |
42 | 42 | ... | ... |
... | @@ -39,15 +39,57 @@ describe('factory: fw/svg/svgUtil.js', function() { | ... | @@ -39,15 +39,57 @@ describe('factory: fw/svg/svgUtil.js', function() { |
39 | 39 | ||
40 | it('should define api functions', function () { | 40 | it('should define api functions', function () { |
41 | expect(fs.areFunctions(sus, [ | 41 | expect(fs.areFunctions(sus, [ |
42 | - 'createDragBehavior', 'loadGlow', 'cat7', 'translate' | 42 | + 'createDragBehavior', 'loadGlow', 'cat7', 'translate', 'stripPx' |
43 | ])).toBeTruthy(); | 43 | ])).toBeTruthy(); |
44 | }); | 44 | }); |
45 | 45 | ||
46 | 46 | ||
47 | // TODO: add unit tests for drag behavior | 47 | // TODO: add unit tests for drag behavior |
48 | // TODO: add unit tests for loadGlow | 48 | // TODO: add unit tests for loadGlow |
49 | - // TODO: add unit tests for cat7 | ||
50 | 49 | ||
50 | + // === cat7 | ||
51 | + | ||
52 | + it('should define two methods on the api', function () { | ||
53 | + var cat7 = sus.cat7(); | ||
54 | + expect(fs.areFunctions(cat7, [ | ||
55 | + 'testCard', 'getColor' | ||
56 | + ])).toBeTruthy(); | ||
57 | + }); | ||
58 | + | ||
59 | + it('should provide a certain shade of blue', function () { | ||
60 | + expect(sus.cat7().getColor('foo', false, 'light')).toEqual('#3E5780'); | ||
61 | + }); | ||
62 | + | ||
63 | + it('should not matter what the ID really is for shade of blue', function () { | ||
64 | + expect(sus.cat7().getColor('bar', false, 'light')).toEqual('#3E5780'); | ||
65 | + }); | ||
66 | + | ||
67 | + it('should provide different shade of blue for muted', function () { | ||
68 | + expect(sus.cat7().getColor('foo', true, 'light')).toEqual('#A8B8CC'); | ||
69 | + }); | ||
70 | + | ||
71 | + | ||
72 | + it('should provide an alternate (dark) shade of blue', function () { | ||
73 | + expect(sus.cat7().getColor('foo', false, 'dark')).toEqual('#3E5780'); | ||
74 | + }); | ||
75 | + | ||
76 | + it('should provide an alternate (dark) shade of blue for muted', function () { | ||
77 | + expect(sus.cat7().getColor('foo', true, 'dark')).toEqual('#A8B8CC'); | ||
78 | + }); | ||
79 | + | ||
80 | + it('should iterate across the colors', function () { | ||
81 | + expect(sus.cat7().getColor('foo', false, 'light')).toEqual('#3E5780'); | ||
82 | + expect(sus.cat7().getColor('bar', false, 'light')).toEqual('#78533B'); | ||
83 | + expect(sus.cat7().getColor('baz', false, 'light')).toEqual('#CB4D28'); | ||
84 | + expect(sus.cat7().getColor('goo', false, 'light')).toEqual('#018D61'); | ||
85 | + expect(sus.cat7().getColor('zoo', false, 'light')).toEqual('#8A2979'); | ||
86 | + expect(sus.cat7().getColor('pip', false, 'light')).toEqual('#006D73'); | ||
87 | + expect(sus.cat7().getColor('sdh', false, 'light')).toEqual('#56AF00'); | ||
88 | + // and cycle back to the first color for item #8 | ||
89 | + expect(sus.cat7().getColor('bri', false, 'light')).toEqual('#3E5780'); | ||
90 | + // and return the same color for the same ID | ||
91 | + expect(sus.cat7().getColor('zoo', false, 'light')).toEqual('#8A2979'); | ||
92 | + }); | ||
51 | 93 | ||
52 | // === translate() | 94 | // === translate() |
53 | 95 | ... | ... |
... | @@ -48,7 +48,7 @@ describe('factory: fw/svg/zoom.js', function() { | ... | @@ -48,7 +48,7 @@ describe('factory: fw/svg/zoom.js', function() { |
48 | 48 | ||
49 | function verifyZoomerApi() { | 49 | function verifyZoomerApi() { |
50 | expect(fs.areFunctions(zoomer, [ | 50 | expect(fs.areFunctions(zoomer, [ |
51 | - 'panZoom', 'reset', 'translate', 'scale' | 51 | + 'panZoom', 'reset', 'translate', 'scale', 'scaleExtent' |
52 | ])).toBeTruthy(); | 52 | ])).toBeTruthy(); |
53 | } | 53 | } |
54 | 54 | ... | ... |
... | @@ -38,7 +38,6 @@ describe('factory: fw/util/fn.js', function() { | ... | @@ -38,7 +38,6 @@ describe('factory: fw/util/fn.js', function() { |
38 | $window.innerHeight = 200; | 38 | $window.innerHeight = 200; |
39 | })); | 39 | })); |
40 | 40 | ||
41 | - | ||
42 | // === Tests for isF() | 41 | // === Tests for isF() |
43 | it('isF(): null for undefined', function () { | 42 | it('isF(): null for undefined', function () { |
44 | expect(fs.isF(undefined)).toBeNull(); | 43 | expect(fs.isF(undefined)).toBeNull(); |
... | @@ -181,15 +180,33 @@ describe('factory: fw/util/fn.js', function() { | ... | @@ -181,15 +180,33 @@ describe('factory: fw/util/fn.js', function() { |
181 | b: 'not-a-function' | 180 | b: 'not-a-function' |
182 | }, ['b', 'a'])).toBeFalsy(); | 181 | }, ['b', 'a'])).toBeFalsy(); |
183 | }); | 182 | }); |
184 | - it('areFunctions(): extraneous stuff ignored', function () { | 183 | + it('areFunctions(): extraneous stuff NOT ignored', function () { |
185 | expect(fs.areFunctions({ | 184 | expect(fs.areFunctions({ |
186 | a: function () {}, | 185 | a: function () {}, |
187 | b: function () {}, | 186 | b: function () {}, |
188 | c: 1, | 187 | c: 1, |
189 | d: 'foo' | 188 | d: 'foo' |
189 | + }, ['a', 'b'])).toBeFalsy(); | ||
190 | + }); | ||
191 | + it('areFunctions(): extraneous stuff ignored (alternate fn)', function () { | ||
192 | + expect(fs.areFunctionsNonStrict({ | ||
193 | + a: function () {}, | ||
194 | + b: function () {}, | ||
195 | + c: 1, | ||
196 | + d: 'foo' | ||
190 | }, ['a', 'b'])).toBeTruthy(); | 197 | }, ['a', 'b'])).toBeTruthy(); |
191 | }); | 198 | }); |
192 | 199 | ||
200 | + // == use the now-tested areFunctions on our own api: | ||
201 | + it('should define api functions', function () { | ||
202 | + expect(fs.areFunctions(fs, [ | ||
203 | + 'isF', 'isA', 'isS', 'isO', 'contains', | ||
204 | + 'areFunctions', 'areFunctionsNonStrict', 'windowSize', 'find' | ||
205 | + ])).toBeTruthy(); | ||
206 | + }); | ||
207 | + | ||
208 | + | ||
209 | + | ||
193 | 210 | ||
194 | // === Tests for windowSize() | 211 | // === Tests for windowSize() |
195 | it('windowSize(): noargs', function () { | 212 | it('windowSize(): noargs', function () { |
... | @@ -215,4 +232,7 @@ describe('factory: fw/util/fn.js', function() { | ... | @@ -215,4 +232,7 @@ describe('factory: fw/util/fn.js', function() { |
215 | expect(dim.width).toEqual(199); | 232 | expect(dim.width).toEqual(199); |
216 | expect(dim.height).toEqual(99); | 233 | expect(dim.height).toEqual(99); |
217 | }); | 234 | }); |
235 | + | ||
236 | + // TODO: write unit tests for find() | ||
237 | + | ||
218 | }); | 238 | }); | ... | ... |
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 -- Topo View -- Topo Instance Service - Unit Tests | ||
19 | + */ | ||
20 | +describe('factory: view/topo/topoInst.js', function() { | ||
21 | + var $log, fs, tis; | ||
22 | + | ||
23 | + beforeEach(module('ovTopo', 'onosUtil', 'onosLayer')); | ||
24 | + | ||
25 | + beforeEach(inject(function (_$log_, FnService, TopoInstService) { | ||
26 | + $log = _$log_; | ||
27 | + fs = FnService; | ||
28 | + tis = TopoInstService; | ||
29 | + })); | ||
30 | + | ||
31 | + it('should define TopoInstService', function () { | ||
32 | + expect(tis).toBeDefined(); | ||
33 | + }); | ||
34 | + | ||
35 | + it('should define api functions', function () { | ||
36 | + expect(fs.areFunctions(tis, [ | ||
37 | + 'initInst', 'destroyInst', | ||
38 | + 'addInstance', 'updateInstance', 'removeInstance' | ||
39 | + ])).toBeTruthy(); | ||
40 | + }); | ||
41 | + | ||
42 | + // TODO: more tests... | ||
43 | +}); |
-
Please register or login to post a comment