GUI -- Completed Show Summary panel.
- added GlyphService.addGlyph(). - added SvgUtilService.translate(). Change-Id: I0bbc51a8f1d9c24b8b4f1377236570070da6f160
Showing
8 changed files
with
201 additions
and
94 deletions
web/gui/src/main/webapp/app/fw/svg/glyph.css
0 → 100644
| 1 | +/* | ||
| 2 | + * Copyright 2014,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 -- Glyph Service -- CSS file | ||
| 19 | + */ | ||
| 20 | + | ||
| 21 | +svg .glyph { | ||
| 22 | + stroke: none; | ||
| 23 | + fill-rule: evenodd; | ||
| 24 | +} | ||
| 25 | + | ||
| 26 | +.light svg .glyph, | ||
| 27 | +.dark svg .glyph.overlay { | ||
| 28 | + fill: black; | ||
| 29 | +} | ||
| 30 | + | ||
| 31 | +.dark svg .glyph, | ||
| 32 | +.light svg .glyph.overlay { | ||
| 33 | + fill: white; | ||
| 34 | +} |
| ... | @@ -20,9 +20,11 @@ | ... | @@ -20,9 +20,11 @@ |
| 20 | (function () { | 20 | (function () { |
| 21 | 'use strict'; | 21 | 'use strict'; |
| 22 | 22 | ||
| 23 | - var $log, | 23 | + // injected references |
| 24 | - fs, | 24 | + var $log, fs, sus; |
| 25 | - glyphs = d3.map(), | 25 | + |
| 26 | + // internal state | ||
| 27 | + var glyphs = d3.map(), | ||
| 26 | msgGS = 'GlyphService.'; | 28 | msgGS = 'GlyphService.'; |
| 27 | 29 | ||
| 28 | // ---------------------------------------------------------------------- | 30 | // ---------------------------------------------------------------------- |
| ... | @@ -133,78 +135,102 @@ | ... | @@ -133,78 +135,102 @@ |
| 133 | 135 | ||
| 134 | // ---------------------------------------------------------------------- | 136 | // ---------------------------------------------------------------------- |
| 135 | 137 | ||
| 136 | - angular.module('onosSvg') | 138 | + function clear() { |
| 137 | - .factory('GlyphService', ['$log', 'FnService', function (_$log_, _fs_) { | 139 | + // start with a fresh map |
| 138 | - $log = _$log_; | 140 | + glyphs = d3.map(); |
| 139 | - fs = _fs_; | 141 | + } |
| 140 | - | 142 | + |
| 141 | - function clear() { | 143 | + function init() { |
| 142 | - // start with a fresh map | 144 | + clear(); |
| 143 | - glyphs = d3.map(); | 145 | + register(birdViewBox, birdData); |
| 146 | + register(glyphViewBox, glyphData); | ||
| 147 | + register(badgeViewBox, badgeData); | ||
| 148 | + } | ||
| 149 | + | ||
| 150 | + function register(viewBox, data, overwrite) { | ||
| 151 | + var dmap = d3.map(data), | ||
| 152 | + dups = [], | ||
| 153 | + ok; | ||
| 154 | + | ||
| 155 | + dmap.forEach(function (key, value) { | ||
| 156 | + if (!overwrite && glyphs.get(key)) { | ||
| 157 | + dups.push(key); | ||
| 158 | + } else { | ||
| 159 | + glyphs.set(key, {id: key, vb: viewBox, d: value}); | ||
| 144 | } | 160 | } |
| 145 | - | 161 | + }); |
| 146 | - function init() { | 162 | + ok = (dups.length == 0); |
| 147 | - clear(); | 163 | + if (!ok) { |
| 148 | - register(birdViewBox, birdData); | 164 | + dups.forEach(function (id) { |
| 149 | - register(glyphViewBox, glyphData); | 165 | + $log.warn(msgGS + 'register(): ID collision: "'+id+'"'); |
| 150 | - register(badgeViewBox, badgeData); | 166 | + }); |
| 151 | - } | 167 | + } |
| 152 | - | 168 | + return ok; |
| 153 | - function register(viewBox, data, overwrite) { | 169 | + } |
| 154 | - var dmap = d3.map(data), | 170 | + |
| 155 | - dups = [], | 171 | + function ids() { |
| 156 | - ok; | 172 | + return glyphs.keys(); |
| 157 | - | 173 | + } |
| 158 | - dmap.forEach(function (key, value) { | 174 | + |
| 159 | - if (!overwrite && glyphs.get(key)) { | 175 | + function glyph(id) { |
| 160 | - dups.push(key); | 176 | + return glyphs.get(id); |
| 161 | - } else { | 177 | + } |
| 162 | - glyphs.set(key, {id: key, vb: viewBox, d: value}); | 178 | + |
| 179 | + // Note: defs should be a D3 selection of a single <defs> element | ||
| 180 | + function loadDefs(defs, glyphIds, noClear) { | ||
| 181 | + var list = fs.isA(glyphIds) || ids(), | ||
| 182 | + clearCache = !noClear; | ||
| 183 | + | ||
| 184 | + if (clearCache) { | ||
| 185 | + // remove all existing content | ||
| 186 | + defs.html(null); | ||
| 187 | + } | ||
| 188 | + | ||
| 189 | + // load up the requested glyphs | ||
| 190 | + list.forEach(function (id) { | ||
| 191 | + var g = glyph(id); | ||
| 192 | + if (g) { | ||
| 193 | + if (noClear) { | ||
| 194 | + // quick exit if symbol is already present | ||
| 195 | + if (defs.select('symbol#' + g.id).size() > 0) { | ||
| 196 | + return; | ||
| 163 | } | 197 | } |
| 164 | - }); | ||
| 165 | - ok = (dups.length == 0); | ||
| 166 | - if (!ok) { | ||
| 167 | - dups.forEach(function (id) { | ||
| 168 | - $log.warn(msgGS + 'register(): ID collision: "'+id+'"'); | ||
| 169 | - }); | ||
| 170 | } | 198 | } |
| 171 | - return ok; | 199 | + defs.append('symbol') |
| 200 | + .attr({ id: g.id, viewBox: g.vb }) | ||
| 201 | + .append('path').attr('d', g.d); | ||
| 172 | } | 202 | } |
| 203 | + }); | ||
| 204 | + } | ||
| 205 | + | ||
| 206 | + function addGlyph(elem, glyphId, size, overlay, trans) { | ||
| 207 | + var sz = size || 40, | ||
| 208 | + ovr = !!overlay, | ||
| 209 | + xns = fs.isA(trans), | ||
| 210 | + atr = { | ||
| 211 | + width: sz, | ||
| 212 | + height: sz, | ||
| 213 | + 'class': 'glyph', | ||
| 214 | + 'xlink:href': '#' + glyphId | ||
| 215 | + }; | ||
| 173 | 216 | ||
| 174 | - function ids() { | 217 | + if (xns) { |
| 175 | - return glyphs.keys(); | 218 | + atr.transform = sus.translate(trans); |
| 176 | - } | 219 | + } |
| 220 | + elem.append('use').attr(atr).classed('overlay', ovr); | ||
| 177 | 221 | ||
| 178 | - function glyph(id) { | 222 | + } |
| 179 | - return glyphs.get(id); | ||
| 180 | - } | ||
| 181 | 223 | ||
| 182 | - // Note: defs should be a D3 selection of a single <defs> element | 224 | + // ---------------------------------------------------------------------- |
| 183 | - function loadDefs(defs, glyphIds, noClear) { | ||
| 184 | - var list = fs.isA(glyphIds) || ids(), | ||
| 185 | - clearCache = !noClear; | ||
| 186 | 225 | ||
| 187 | - if (clearCache) { | 226 | + angular.module('onosSvg') |
| 188 | - // remove all existing content | 227 | + .factory('GlyphService', |
| 189 | - defs.html(null); | 228 | + ['$log', 'FnService', 'SvgUtilService', |
| 190 | - } | ||
| 191 | 229 | ||
| 192 | - // load up the requested glyphs | 230 | + function (_$log_, _fs_, _sus_) { |
| 193 | - list.forEach(function (id) { | 231 | + $log = _$log_; |
| 194 | - var g = glyph(id); | 232 | + fs = _fs_; |
| 195 | - if (g) { | 233 | + sus = _sus_; |
| 196 | - if (noClear) { | ||
| 197 | - // quick exit if symbol is already present | ||
| 198 | - if (defs.select('symbol#' + g.id).size() > 0) { | ||
| 199 | - return; | ||
| 200 | - } | ||
| 201 | - } | ||
| 202 | - defs.append('symbol') | ||
| 203 | - .attr({ id: g.id, viewBox: g.vb }) | ||
| 204 | - .append('path').attr('d', g.d); | ||
| 205 | - } | ||
| 206 | - }); | ||
| 207 | - } | ||
| 208 | 234 | ||
| 209 | return { | 235 | return { |
| 210 | clear: clear, | 236 | clear: clear, |
| ... | @@ -212,8 +238,10 @@ | ... | @@ -212,8 +238,10 @@ |
| 212 | register: register, | 238 | register: register, |
| 213 | ids: ids, | 239 | ids: ids, |
| 214 | glyph: glyph, | 240 | glyph: glyph, |
| 215 | - loadDefs: loadDefs | 241 | + loadDefs: loadDefs, |
| 242 | + addGlyph: addGlyph | ||
| 216 | }; | 243 | }; |
| 217 | - }]); | 244 | + }] |
| 245 | + ); | ||
| 218 | 246 | ||
| 219 | }()); | 247 | }()); | ... | ... |
| ... | @@ -136,10 +136,18 @@ | ... | @@ -136,10 +136,18 @@ |
| 136 | $log.warn('SvgUtilService: cat7 -- To Be Implemented'); | 136 | $log.warn('SvgUtilService: cat7 -- To Be Implemented'); |
| 137 | } | 137 | } |
| 138 | 138 | ||
| 139 | + function translate(x, y) { | ||
| 140 | + if (fs.isA(x) && x.length === 2 && !y) { | ||
| 141 | + return 'translate(' + x[0] + ',' + x[1] + ')'; | ||
| 142 | + } | ||
| 143 | + return 'translate(' + x + ',' + y + ')'; | ||
| 144 | + } | ||
| 145 | + | ||
| 139 | return { | 146 | return { |
| 140 | createDragBehavior: createDragBehavior, | 147 | createDragBehavior: createDragBehavior, |
| 141 | loadGlow: loadGlow, | 148 | loadGlow: loadGlow, |
| 142 | - cat7: cat7 | 149 | + cat7: cat7, |
| 150 | + translate: translate | ||
| 143 | }; | 151 | }; |
| 144 | }]); | 152 | }]); |
| 145 | }()); | 153 | }()); | ... | ... |
| ... | @@ -66,6 +66,7 @@ | ... | @@ -66,6 +66,7 @@ |
| 66 | <link rel="stylesheet" href="onos.css"> | 66 | <link rel="stylesheet" href="onos.css"> |
| 67 | <link rel="stylesheet" href="common.css"> | 67 | <link rel="stylesheet" href="common.css"> |
| 68 | <link rel="stylesheet" href="fw/mast/mast.css"> | 68 | <link rel="stylesheet" href="fw/mast/mast.css"> |
| 69 | + <link rel="stylesheet" href="fw/svg/glyph.css"> | ||
| 69 | <link rel="stylesheet" href="fw/svg/icon.css"> | 70 | <link rel="stylesheet" href="fw/svg/icon.css"> |
| 70 | <link rel="stylesheet" href="fw/layer/panel.css"> | 71 | <link rel="stylesheet" href="fw/layer/panel.css"> |
| 71 | <link rel="stylesheet" href="fw/nav/nav.css"> | 72 | <link rel="stylesheet" href="fw/nav/nav.css"> | ... | ... |
| ... | @@ -56,17 +56,6 @@ | ... | @@ -56,17 +56,6 @@ |
| 56 | height: 42px; | 56 | height: 42px; |
| 57 | } | 57 | } |
| 58 | 58 | ||
| 59 | -#topo-p-summary svg .glyphIcon { | ||
| 60 | - stroke: none; | ||
| 61 | - fill-rule: evenodd; | ||
| 62 | -} | ||
| 63 | -.light #topo-p-summary svg .glyphIcon { | ||
| 64 | - fill: black; | ||
| 65 | -} | ||
| 66 | -.dark #topo-p-summary svg .glyphIcon { | ||
| 67 | - fill: #ddd; | ||
| 68 | -} | ||
| 69 | - | ||
| 70 | #topo-p-summary h2 { | 59 | #topo-p-summary h2 { |
| 71 | position: absolute; | 60 | position: absolute; |
| 72 | margin: 0 4px; | 61 | margin: 0 4px; | ... | ... |
| ... | @@ -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; | 26 | + var $log, ps, gs; |
| 27 | 27 | ||
| 28 | // constants | 28 | // constants |
| 29 | var idSum = 'topo-p-summary', | 29 | var idSum = 'topo-p-summary', |
| ... | @@ -70,18 +70,15 @@ | ... | @@ -70,18 +70,15 @@ |
| 70 | function populateSummary(data) { | 70 | function populateSummary(data) { |
| 71 | summaryPanel.empty(); | 71 | summaryPanel.empty(); |
| 72 | 72 | ||
| 73 | - var svg = summaryPanel.append('svg').attr({ | 73 | + var svg = summaryPanel.append('svg'); //.style('background-color', 'goldenrod'), |
| 74 | - width: 40, | 74 | + //iid = '#' + (data.type || 'unknown'); |
| 75 | - height: 40 | ||
| 76 | - }).style('background-color', 'goldenrod'), | ||
| 77 | - iid = '#' + (data.type || 'unknown'); | ||
| 78 | 75 | ||
| 79 | var title = summaryPanel.append('h2'), | 76 | var title = summaryPanel.append('h2'), |
| 80 | table = summaryPanel.append('table'), | 77 | table = summaryPanel.append('table'), |
| 81 | tbody = table.append('tbody'); | 78 | tbody = table.append('tbody'); |
| 82 | 79 | ||
| 83 | - // append glyph iid to SVG // black fill | 80 | + gs.addGlyph(svg, 'node', 40); |
| 84 | - // append glyph bird to SVG // white fill | 81 | + gs.addGlyph(svg, 'bird', 24, true, [8,12]); |
| 85 | 82 | ||
| 86 | title.text(data.id); | 83 | title.text(data.id); |
| 87 | 84 | ||
| ... | @@ -103,11 +100,12 @@ | ... | @@ -103,11 +100,12 @@ |
| 103 | 100 | ||
| 104 | angular.module('ovTopo') | 101 | angular.module('ovTopo') |
| 105 | .factory('TopoPanelService', | 102 | .factory('TopoPanelService', |
| 106 | - ['$log', 'PanelService', | 103 | + ['$log', 'PanelService', 'GlyphService', |
| 107 | 104 | ||
| 108 | - function (_$log_, _ps_) { | 105 | + function (_$log_, _ps_, _gs_) { |
| 109 | $log = _$log_; | 106 | $log = _$log_; |
| 110 | ps = _ps_; | 107 | ps = _ps_; |
| 108 | + gs = _gs_; | ||
| 111 | 109 | ||
| 112 | function initPanels() { | 110 | function initPanels() { |
| 113 | summaryPanel = ps.createPanel(idSum, panelOpts); | 111 | summaryPanel = ps.createPanel(idSum, panelOpts); | ... | ... |
| ... | @@ -18,7 +18,7 @@ | ... | @@ -18,7 +18,7 @@ |
| 18 | ONOS GUI -- SVG -- Glyph Service - Unit Tests | 18 | ONOS GUI -- SVG -- Glyph Service - Unit Tests |
| 19 | */ | 19 | */ |
| 20 | describe('factory: fw/svg/glyph.js', function() { | 20 | describe('factory: fw/svg/glyph.js', function() { |
| 21 | - var $log, fs, gs, d3Elem; | 21 | + var $log, fs, gs, d3Elem, svg; |
| 22 | 22 | ||
| 23 | var numBaseGlyphs = 13, | 23 | var numBaseGlyphs = 13, |
| 24 | vbBird = '352 224 113 112', | 24 | vbBird = '352 224 113 112', |
| ... | @@ -47,13 +47,16 @@ describe('factory: fw/svg/glyph.js', function() { | ... | @@ -47,13 +47,16 @@ describe('factory: fw/svg/glyph.js', function() { |
| 47 | beforeEach(module('onosUtil', 'onosSvg')); | 47 | beforeEach(module('onosUtil', 'onosSvg')); |
| 48 | 48 | ||
| 49 | beforeEach(inject(function (_$log_, FnService, GlyphService) { | 49 | beforeEach(inject(function (_$log_, FnService, GlyphService) { |
| 50 | + var body = d3.select('body'); | ||
| 50 | $log = _$log_; | 51 | $log = _$log_; |
| 51 | fs = FnService; | 52 | fs = FnService; |
| 52 | gs = GlyphService; | 53 | gs = GlyphService; |
| 53 | - d3Elem = d3.select('body').append('defs').attr('id', 'myDefs'); | 54 | + d3Elem = body.append('defs').attr('id', 'myDefs'); |
| 55 | + svg = body.append('svg').attr('id', 'mySvg'); | ||
| 54 | })); | 56 | })); |
| 55 | 57 | ||
| 56 | afterEach(function () { | 58 | afterEach(function () { |
| 59 | + d3.select('#mySvg').remove(); | ||
| 57 | d3.select('#myDefs').remove(); | 60 | d3.select('#myDefs').remove(); |
| 58 | gs.clear(); | 61 | gs.clear(); |
| 59 | }); | 62 | }); |
| ... | @@ -64,7 +67,7 @@ describe('factory: fw/svg/glyph.js', function() { | ... | @@ -64,7 +67,7 @@ describe('factory: fw/svg/glyph.js', function() { |
| 64 | 67 | ||
| 65 | it('should define api functions', function () { | 68 | it('should define api functions', function () { |
| 66 | expect(fs.areFunctions(gs, [ | 69 | expect(fs.areFunctions(gs, [ |
| 67 | - 'clear', 'init', 'register', 'ids', 'glyph', 'loadDefs' | 70 | + 'clear', 'init', 'register', 'ids', 'glyph', 'loadDefs', 'addGlyph' |
| 68 | ])).toBeTruthy(); | 71 | ])).toBeTruthy(); |
| 69 | }); | 72 | }); |
| 70 | 73 | ||
| ... | @@ -246,4 +249,40 @@ describe('factory: fw/svg/glyph.js', function() { | ... | @@ -246,4 +249,40 @@ describe('factory: fw/svg/glyph.js', function() { |
| 246 | verifyLoadedInDom('chain', vbGlyph); | 249 | verifyLoadedInDom('chain', vbGlyph); |
| 247 | verifyLoadedInDom('node', vbGlyph); | 250 | verifyLoadedInDom('node', vbGlyph); |
| 248 | }); | 251 | }); |
| 252 | + | ||
| 253 | + it('should add a glyph with default size', function () { | ||
| 254 | + gs.init(); | ||
| 255 | + gs.addGlyph(svg, 'crown'); | ||
| 256 | + var what = svg.selectAll('use'); | ||
| 257 | + expect(what.size()).toEqual(1); | ||
| 258 | + expect(what.attr('width')).toEqual('40'); | ||
| 259 | + expect(what.attr('height')).toEqual('40'); | ||
| 260 | + expect(what.attr('xlink:href')).toEqual('#crown'); | ||
| 261 | + expect(what.classed('glyph')).toBeTruthy(); | ||
| 262 | + expect(what.classed('overlay')).toBeFalsy(); | ||
| 263 | + }); | ||
| 264 | + | ||
| 265 | + it('should add a glyph with given size', function () { | ||
| 266 | + gs.init(); | ||
| 267 | + gs.addGlyph(svg, 'crown', 37); | ||
| 268 | + var what = svg.selectAll('use'); | ||
| 269 | + expect(what.size()).toEqual(1); | ||
| 270 | + expect(what.attr('width')).toEqual('37'); | ||
| 271 | + expect(what.attr('height')).toEqual('37'); | ||
| 272 | + expect(what.attr('xlink:href')).toEqual('#crown'); | ||
| 273 | + expect(what.classed('glyph')).toBeTruthy(); | ||
| 274 | + expect(what.classed('overlay')).toBeFalsy(); | ||
| 275 | + }); | ||
| 276 | + | ||
| 277 | + it('should add a glyph marked as overlay', function () { | ||
| 278 | + gs.init(); | ||
| 279 | + gs.addGlyph(svg, 'crown', 20, true); | ||
| 280 | + var what = svg.selectAll('use'); | ||
| 281 | + expect(what.size()).toEqual(1); | ||
| 282 | + expect(what.attr('width')).toEqual('20'); | ||
| 283 | + expect(what.attr('height')).toEqual('20'); | ||
| 284 | + expect(what.attr('xlink:href')).toEqual('#crown'); | ||
| 285 | + expect(what.classed('glyph')).toBeTruthy(); | ||
| 286 | + expect(what.classed('overlay')).toBeTruthy(); | ||
| 287 | + }); | ||
| 249 | }); | 288 | }); | ... | ... |
| ... | @@ -39,8 +39,18 @@ describe('factory: fw/svg/svgUtil.js', function() { | ... | @@ -39,8 +39,18 @@ 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' | 42 | + 'createDragBehavior', 'loadGlow', 'cat7', 'translate' |
| 43 | ])).toBeTruthy(); | 43 | ])).toBeTruthy(); |
| 44 | }); | 44 | }); |
| 45 | 45 | ||
| 46 | + // TODO: add unit tests for drag behavior etc. | ||
| 47 | + | ||
| 48 | + it('should translate from two args', function () { | ||
| 49 | + expect(sus.translate(1,2)).toEqual('translate(1,2)'); | ||
| 50 | + }); | ||
| 51 | + | ||
| 52 | + it('should translate from an array', function () { | ||
| 53 | + expect(sus.translate([3,4])).toEqual('translate(3,4)'); | ||
| 54 | + }); | ||
| 55 | + | ||
| 46 | }); | 56 | }); | ... | ... |
-
Please register or login to post a comment