Steven Burrows

Topo2: Reset node position and unpin

Refactored NodeModel
Added class to the surrounding rect for selected class
Renamed Panels to avoid conflict with classic topo
Topo2: Details Panel for single device selection
Topo2: Added Equalize Masters keyboard shortcut
Topo2: Toggle Link Port highlighting
Topo2: Node Labels was returning empty string
       if friendly name was null
Topo2: Reset map zoom and panning

Change-Id: I0a949b2f8205e1abcfcac5aaec65c18d76e77cff
...@@ -66,7 +66,6 @@ ...@@ -66,7 +66,6 @@
66 } 66 }
67 67
68 function updatePrefs(data) { 68 function updatePrefs(data) {
69 - $log.info('User properties updated');
70 cache = data; 69 cache = data;
71 listeners.forEach(function (lsnr) { lsnr(); }); 70 listeners.forEach(function (lsnr) { lsnr(); });
72 } 71 }
......
...@@ -3,7 +3,8 @@ module.exports = { ...@@ -3,7 +3,8 @@ module.exports = {
3 "installedESLint": true, 3 "installedESLint": true,
4 "globals": { 4 "globals": {
5 "angular": true, 5 "angular": true,
6 - "d3": true 6 + "d3": true,
7 + "_": true
7 }, 8 },
8 "rules": { 9 "rules": {
9 "brace-style": 0, 10 "brace-style": 0,
......
...@@ -181,7 +181,7 @@ ...@@ -181,7 +181,7 @@
181 } 181 }
182 182
183 183
184 -#ov-topo2 svg .node.device.selected rect { 184 +#ov-topo2 svg .node.device.selected .node-container {
185 stroke-width: 2.0; 185 stroke-width: 2.0;
186 stroke: #009fdb; 186 stroke: #009fdb;
187 } 187 }
......
...@@ -99,3 +99,29 @@ ...@@ -99,3 +99,29 @@
99 cursor: pointer; 99 cursor: pointer;
100 fill-rule: evenodd; 100 fill-rule: evenodd;
101 } 101 }
102 +
103 +/* --- Topo Summary Panel --- */
104 +
105 +#topo2-p-summary {
106 + padding: 16px;
107 +}
108 +
109 +
110 +/* --- Topo Detail Panel --- */
111 +
112 +#topo2-p-detail {
113 + padding: 16px;
114 + top: 370px;
115 +}
116 +html[data-platform='iPad'] #topo2-p-detail {
117 + top: 386px;
118 +}
119 +
120 +#topo2-p-detail .actionBtns .actionBtn {
121 + display: inline-block;
122 +}
123 +#topo2-p-detail .actionBtns .actionBtn svg {
124 + width: 28px;
125 + height: 28px;
126 +}
127 +
......
...@@ -24,7 +24,7 @@ ...@@ -24,7 +24,7 @@
24 'use strict'; 24 'use strict';
25 25
26 // references to injected services 26 // references to injected services
27 - var $scope, $log, fs, mast, ks, zs, 27 + var $scope, $log, fs, mast, ks,
28 gs, sus, ps, t2es, t2fs, t2is, t2bcs, t2kcs, t2ms, t2mcs, t2zs; 28 gs, sus, ps, t2es, t2fs, t2is, t2bcs, t2kcs, t2ms, t2mcs, t2zs;
29 29
30 // DOM elements 30 // DOM elements
...@@ -81,19 +81,20 @@ ...@@ -81,19 +81,20 @@
81 angular.module('ovTopo2', ['onosUtil', 'onosSvg', 'onosRemote']) 81 angular.module('ovTopo2', ['onosUtil', 'onosSvg', 'onosRemote'])
82 .controller('OvTopo2Ctrl', 82 .controller('OvTopo2Ctrl',
83 ['$scope', '$log', '$location', 83 ['$scope', '$log', '$location',
84 - 'FnService', 'MastService', 'KeyService', 'ZoomService', 84 + 'FnService', 'MastService', 'KeyService',
85 'GlyphService', 'MapService', 'SvgUtilService', 'FlashService', 85 'GlyphService', 'MapService', 'SvgUtilService', 'FlashService',
86 'WebSocketService', 'PrefsService', 'ThemeService', 86 'WebSocketService', 'PrefsService', 'ThemeService',
87 'Topo2EventService', 'Topo2ForceService', 'Topo2InstanceService', 87 'Topo2EventService', 'Topo2ForceService', 'Topo2InstanceService',
88 'Topo2BreadcrumbService', 'Topo2KeyCommandService', 'Topo2MapService', 88 'Topo2BreadcrumbService', 'Topo2KeyCommandService', 'Topo2MapService',
89 - 'Topo2MapConfigService', 'Topo2SummaryPanelService', 'Topo2ZoomService', 89 + 'Topo2MapConfigService', 'Topo2ZoomService',
90 + 'Topo2SummaryPanelService', 'Topo2DeviceDetailsPanel',
90 91
91 function (_$scope_, _$log_, _$loc_, 92 function (_$scope_, _$log_, _$loc_,
92 - _fs_, _mast_, _ks_, _zs_, 93 + _fs_, _mast_, _ks_,
93 _gs_, _ms_, _sus_, _flash_, 94 _gs_, _ms_, _sus_, _flash_,
94 _wss_, _ps_, _th_, 95 _wss_, _ps_, _th_,
95 _t2es_, _t2fs_, _t2is_, _t2bcs_, _t2kcs_, _t2ms_, _t2mcs_, 96 _t2es_, _t2fs_, _t2is_, _t2bcs_, _t2kcs_, _t2ms_, _t2mcs_,
96 - summaryPanel, _t2zs_ 97 + _t2zs_, summaryPanel, detailsPanel
97 ) { 98 ) {
98 99
99 var params = _$loc_.search(), 100 var params = _$loc_.search(),
...@@ -115,7 +116,6 @@ ...@@ -115,7 +116,6 @@
115 fs = _fs_; 116 fs = _fs_;
116 mast = _mast_; 117 mast = _mast_;
117 ks = _ks_; 118 ks = _ks_;
118 - zs = _zs_;
119 119
120 gs = _gs_; 120 gs = _gs_;
121 sus = _sus_; 121 sus = _sus_;
...@@ -156,6 +156,8 @@ ...@@ -156,6 +156,8 @@
156 ks.unbindKeys(); 156 ks.unbindKeys();
157 t2fs.destroy(); 157 t2fs.destroy();
158 t2is.destroy(); 158 t2is.destroy();
159 + summaryPanel.destroy();
160 + detailsPanel.destroy();
159 }); 161 });
160 162
161 // svg layer and initialization of components 163 // svg layer and initialization of components
...@@ -176,7 +178,7 @@ ...@@ -176,7 +178,7 @@
176 t2es.bindHandlers(); 178 t2es.bindHandlers();
177 179
178 // Add the map SVG Group 180 // Add the map SVG Group
179 - t2ms.init(zoomLayer).then( 181 + t2ms.init(zoomLayer, zoomer).then(
180 function (proj) { 182 function (proj) {
181 var z = ps.getPrefs('topo_zoom', { tx: 0, ty: 0, sc: 1 }); 183 var z = ps.getPrefs('topo_zoom', { tx: 0, ty: 0, sc: 1 });
182 zoomer.panZoom([z.tx, z.ty], z.sc); 184 zoomer.panZoom([z.tx, z.ty], z.sc);
...@@ -219,6 +221,7 @@ ...@@ -219,6 +221,7 @@
219 // $log.debug('registered overlays...', tov.list()); 221 // $log.debug('registered overlays...', tov.list());
220 222
221 summaryPanel.init(); 223 summaryPanel.init();
224 + detailsPanel.init();
222 225
223 $log.log('OvTopo2Ctrl has been created'); 226 $log.log('OvTopo2Ctrl has been created');
224 }]); 227 }]);
......
...@@ -34,10 +34,10 @@ ...@@ -34,10 +34,10 @@
34 var DeviceCollection = Collection.extend({ 34 var DeviceCollection = Collection.extend({
35 model: Model, 35 model: Model,
36 comparator: function (a, b) { 36 comparator: function (a, b) {
37 - var order = region.get('layerOrder'), 37 + // var order = region.get('layerOrder'),
38 - aLayer = a.get('layer'), 38 + // aLayer = a.get('id'),
39 - bLayer = b.get('layer'); 39 + // bLayer = b.get('layer');
40 - return order.indexOf(aLayer - order.indexOf(bLayer)); 40 + // return order.indexOf(aLayer - order.indexOf(bLayer));
41 } 41 }
42 }); 42 });
43 43
...@@ -54,11 +54,10 @@ ...@@ -54,11 +54,10 @@
54 return deviceCollection; 54 return deviceCollection;
55 } 55 }
56 56
57 -
58 angular.module('ovTopo2') 57 angular.module('ovTopo2')
59 .factory('Topo2DeviceService', 58 .factory('Topo2DeviceService',
60 - ['Topo2Collection', 'Topo2NodeModel', 59 + ['Topo2Collection', 'Topo2NodeModel', 'Topo2DeviceDetailsPanel',
61 - function (_c_, _nm_) { 60 + function (_c_, _nm_, detailsPanel) {
62 61
63 Collection = _c_; 62 Collection = _c_;
64 63
...@@ -67,11 +66,27 @@ ...@@ -67,11 +66,27 @@
67 this.super = this.constructor.__super__; 66 this.super = this.constructor.__super__;
68 this.super.initialize.apply(this, arguments); 67 this.super.initialize.apply(this, arguments);
69 }, 68 },
69 + events: {
70 + 'click': 'onClick'
71 + },
70 nodeType: 'device', 72 nodeType: 'device',
71 icon: function () { 73 icon: function () {
72 var type = this.get('type'); 74 var type = this.get('type');
73 return remappedDeviceTypes[type] || type || 'unknown'; 75 return remappedDeviceTypes[type] || type || 'unknown';
74 }, 76 },
77 + onClick: function () {
78 +
79 + if (this.get('selected')) {
80 + this.set('selected', false);
81 + detailsPanel.hide();
82 + } else {
83 + this.set('selected', true);
84 + detailsPanel.updateDetails(this.get('id'), this.get('nodeType'));
85 + detailsPanel.show();
86 + }
87 +
88 + this.el.attr('class', this.svgClassName());
89 + },
75 onExit: function () { 90 onExit: function () {
76 var node = this.el; 91 var node = this.el;
77 node.select('use') 92 node.select('use')
......
1 +/*
2 + * Copyright 2016-present 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 -- Topology View Module.
19 + Module that displays the details panel for selected nodes
20 + */
21 +
22 +(function () {
23 + 'use strict';
24 +
25 + // Injected Services
26 + var Panel, gs, wss, flash, bs, fs, ns;
27 +
28 + // Internal State
29 + var detailsPanel;
30 +
31 + // configuration
32 + var id = 'topo2-p-detail',
33 + className = 'topo-p',
34 + panelOpts = {
35 + width: 260 // summary and detail panel width
36 + },
37 + handlerMap = {
38 + 'showDetails': showDetails
39 + };
40 +
41 + var coreButtons = {
42 + showDeviceView: {
43 + gid: 'switch',
44 + tt: 'Show Device View',
45 + path: 'device'
46 + },
47 + showFlowView: {
48 + gid: 'flowTable',
49 + tt: 'Show Flow View for this Device',
50 + path: 'flow'
51 + },
52 + showPortView: {
53 + gid: 'portTable',
54 + tt: 'Show Port View for this Device',
55 + path: 'port'
56 + },
57 + showGroupView: {
58 + gid: 'groupTable',
59 + tt: 'Show Group View for this Device',
60 + path: 'group'
61 + },
62 + showMeterView: {
63 + gid: 'meterTable',
64 + tt: 'Show Meter View for this Device',
65 + path: 'meter'
66 + }
67 + };
68 +
69 + function init() {
70 +
71 + bindHandlers();
72 +
73 + var options = angular.extend({}, panelOpts, {
74 + class: className
75 + });
76 +
77 + detailsPanel = new Panel(id, options);
78 + detailsPanel.p.classed(className, true);
79 + }
80 +
81 + function addProp(tbody, label, value) {
82 + var tr = tbody.append('tr'),
83 + lab;
84 + if (typeof label === 'string') {
85 + lab = label.replace(/_/g, ' ');
86 + } else {
87 + lab = label;
88 + }
89 +
90 + function addCell(cls, txt) {
91 + tr.append('td').attr('class', cls).html(txt);
92 + }
93 + addCell('label', lab + ' :');
94 + addCell('value', value);
95 + }
96 +
97 + function addSep(tbody) {
98 + tbody.append('tr').append('td').attr('colspan', 2).append('hr');
99 + }
100 +
101 + function listProps(tbody, data) {
102 + data.propOrder.forEach(function (p) {
103 + if (p === '-') {
104 + addSep(tbody);
105 + } else {
106 + addProp(tbody, p, data.props[p]);
107 + }
108 + });
109 + }
110 +
111 + function addBtnFooter() {
112 + detailsPanel.appendToFooter('hr');
113 + detailsPanel.appendToFooter('div').classed('actionBtns', true);
114 + }
115 +
116 + function addAction(o) {
117 + var btnDiv = d3.select('#' + id)
118 + .select('.actionBtns')
119 + .append('div')
120 + .classed('actionBtn', true);
121 + bs.button(btnDiv, id + '-' + o.id, o.gid, o.cb, o.tt);
122 + }
123 +
124 + function installButtons(buttons, data, devId) {
125 + buttons.forEach(function (id) {
126 + var btn = coreButtons[id],
127 + gid = btn && btn.gid,
128 + tt = btn && btn.tt,
129 + path = btn && btn.path;
130 +
131 + if (btn) {
132 + addAction({
133 + id: 'core-' + id,
134 + gid: gid,
135 + tt: tt,
136 + cb: function () { ns.navTo(path, { devId: devId }); }
137 + });
138 + }
139 + // else if (btn = _getButtonDef(id, data)) {
140 + // addAction(btn);
141 + // }
142 + });
143 + }
144 +
145 + function renderSingle(data) {
146 +
147 + detailsPanel.emptyRegions();
148 +
149 + var svg = detailsPanel.appendToHeader('div')
150 + .classed('icon clickable', true)
151 + .append('svg'),
152 + title = detailsPanel.appendToHeader('h2')
153 + .classed('clickable', true),
154 + table = detailsPanel.appendToBody('table'),
155 + tbody = table.append('tbody'),
156 + navFn;
157 +
158 + gs.addGlyph(svg, (data.type || 'unknown'), 26);
159 + title.text(data.title);
160 +
161 + // // only add navigation when displaying a device
162 + // if (isDevice[data.type]) {
163 + // navFn = function () {
164 + // ns.navTo(devPath, { devId: data.id });
165 + // };
166 + //
167 + // svg.on('click', navFn);
168 + // title.on('click', navFn);
169 + // }
170 +
171 + listProps(tbody, data);
172 + addBtnFooter();
173 + }
174 +
175 +
176 + function bindHandlers() {
177 + wss.bindHandlers(handlerMap);
178 + }
179 +
180 + function updateDetails(id, nodeType) {
181 + wss.sendEvent('requestDetails', {
182 + id: id,
183 + class: nodeType
184 + });
185 + }
186 +
187 + function showDetails(data) {
188 + var buttons = fs.isA(data.buttons) || [];
189 + renderSingle(data);
190 + installButtons(buttons, data, data.id);
191 + }
192 +
193 + function toggle() {
194 + var on = detailsPanel.p.toggle(),
195 + verb = on ? 'Show' : 'Hide';
196 + flash.flash(verb + ' Summary Panel');
197 + }
198 +
199 + function show() {
200 + detailsPanel.p.show();
201 + }
202 +
203 + function hide() {
204 + detailsPanel.p.hide();
205 + }
206 +
207 + function destroy() {
208 + wss.unbindHandlers(handlerMap);
209 + detailsPanel.destroy();
210 + }
211 +
212 + angular.module('ovTopo2')
213 + .factory('Topo2DeviceDetailsPanel',
214 + ['Topo2PanelService', 'GlyphService', 'WebSocketService', 'FlashService',
215 + 'ButtonService', 'FnService', 'NavService',
216 + function (_ps_, _gs_, _wss_, _flash_, _bs_, _fs_, _ns_) {
217 +
218 + Panel = _ps_;
219 + gs = _gs_;
220 + wss = _wss_;
221 + flash = _flash_;
222 + bs = _bs_;
223 + fs = _fs_;
224 + ns = _ns_;
225 +
226 + return {
227 + init: init,
228 + updateDetails: updateDetails,
229 +
230 + toggle: toggle,
231 + show: show,
232 + hide: hide,
233 + destroy: destroy
234 + };
235 + }
236 + ]);
237 +})();
...@@ -190,18 +190,49 @@ ...@@ -190,18 +190,49 @@
190 190
191 // ========================== Main Service Definition 191 // ========================== Main Service Definition
192 192
193 + function update(elements) {
194 + angular.forEach(elements, function (el) {
195 + el.update();
196 + });
197 + }
198 +
193 function updateNodes() { 199 function updateNodes() {
194 - var allNodes = t2rs.regionNodes(); 200 + update(t2rs.regionNodes());
195 - angular.forEach(allNodes, function (node) { 201 + }
196 - node.update(); 202 +
203 + function updateLinks() {
204 + update(t2rs.regionLinks());
205 + }
206 +
207 + function resetAllLocations() {
208 + var nodes = t2rs.regionNodes();
209 +
210 + angular.forEach(nodes, function (node) {
211 + node.resetPosition();
212 + });
213 +
214 + t2ls.update();
215 + t2ls.tick();
216 + }
217 +
218 + function unpin() {
219 + var hovered = t2rs.filterRegionNodes(function (model) {
220 + return model.get('hovered');
221 + });
222 +
223 + angular.forEach(hovered, function (model) {
224 + model.fixed = false;
225 + model.el.classed('fixed', false);
197 }); 226 });
198 } 227 }
199 228
200 angular.module('ovTopo2') 229 angular.module('ovTopo2')
201 .factory('Topo2ForceService', 230 .factory('Topo2ForceService',
202 - ['$log', 'WebSocketService', 'Topo2InstanceService', 'Topo2RegionService', 231 + ['$log', 'WebSocketService', 'Topo2InstanceService',
203 - 'Topo2LayoutService', 'Topo2ViewService', 'Topo2BreadcrumbService', 'Topo2ZoomService', 232 + 'Topo2RegionService', 'Topo2LayoutService', 'Topo2ViewService',
204 - function (_$log_, _wss_, _t2is_, _t2rs_, _t2ls_, _t2vs_, _t2bcs_, zoomService) { 233 + 'Topo2BreadcrumbService', 'Topo2ZoomService',
234 + function (_$log_, _wss_, _t2is_, _t2rs_, _t2ls_,
235 + _t2vs_, _t2bcs_, zoomService) {
205 236
206 $log = _$log_; 237 $log = _$log_;
207 wss = _wss_; 238 wss = _wss_;
...@@ -238,7 +269,10 @@ ...@@ -238,7 +269,10 @@
238 showMastership: showMastership, 269 showMastership: showMastership,
239 topo2PeerRegions: topo2PeerRegions, 270 topo2PeerRegions: topo2PeerRegions,
240 271
241 - updateNodes: updateNodes 272 + updateNodes: updateNodes,
273 + updateLinks: updateLinks,
274 + resetAllLocations: resetAllLocations,
275 + unpin: unpin
242 }; 276 };
243 }]); 277 }]);
244 })(); 278 })();
......
...@@ -17,9 +17,9 @@ ...@@ -17,9 +17,9 @@
17 (function () { 17 (function () {
18 18
19 // Injected Services 19 // Injected Services
20 - var ks, t2ps, t2ms, ps, t2is, t2sp; 20 + var ks, flash, wss, t2ps, t2ms, ps, t2is, t2sp, t2vs;
21 21
22 - var topo2ForceService; 22 + var t2fs;
23 23
24 // Commmands 24 // Commmands
25 var actionMap = { 25 var actionMap = {
...@@ -27,11 +27,16 @@ ...@@ -27,11 +27,16 @@
27 G: [openMapSelection, 'Select background geo map'], 27 G: [openMapSelection, 'Select background geo map'],
28 B: [toggleMap, 'Toggle background geo map'], 28 B: [toggleMap, 'Toggle background geo map'],
29 I: [toggleInstancePanel, 'Toggle ONOS Instance Panel'], 29 I: [toggleInstancePanel, 'Toggle ONOS Instance Panel'],
30 - O: [toggleSummary, 'Toggle the Summary Panel'] 30 + O: [toggleSummary, 'Toggle the Summary Panel'],
31 + R: [resetZoom, 'Reset pan / zoom'],
32 + P: [togglePorts, 'Toggle Port Highlighting'],
33 + E: [equalizeMasters, 'Equalize mastership roles'],
34 + X: [resetAllNodeLocations, 'Reset Node Location'],
35 + U: [unpinNode, 'Unpin node (mouse over)']
31 }; 36 };
32 37
33 - function init(t2fs) { 38 + function init(_t2fs_) {
34 - topo2ForceService = t2fs; 39 + t2fs = _t2fs_;
35 bindCommands(); 40 bindCommands();
36 } 41 }
37 42
...@@ -58,7 +63,7 @@ ...@@ -58,7 +63,7 @@
58 function cycleDeviceLabels() { 63 function cycleDeviceLabels() {
59 var deviceLabelIndex = t2ps.get('dlbls') + 1; 64 var deviceLabelIndex = t2ps.get('dlbls') + 1;
60 t2ps.set('dlbls', deviceLabelIndex % 3); 65 t2ps.set('dlbls', deviceLabelIndex % 3);
61 - topo2ForceService.updateNodes(); 66 + t2fs.updateNodes();
62 } 67 }
63 68
64 function openMapSelection() { 69 function openMapSelection() {
...@@ -77,17 +82,47 @@ ...@@ -77,17 +82,47 @@
77 t2sp.toggle(); 82 t2sp.toggle();
78 } 83 }
79 84
85 + function resetZoom() {
86 + t2ms.resetZoom();
87 + flash.flash('Pan and zoom reset');
88 + }
89 +
90 + function togglePorts(x) {
91 + updatePrefsState('porthl', t2vs.togglePortHighlights(x));
92 + t2fs.updateLinks();
93 + }
94 +
95 + function equalizeMasters() {
96 + wss.sendEvent('equalizeMasters');
97 + flash.flash('Equalizing master roles');
98 + }
99 +
100 + function resetAllNodeLocations() {
101 + t2fs.resetAllLocations();
102 + flash.flash('Reset node locations');
103 + }
104 +
105 + function unpinNode() {
106 + t2fs.unpin();
107 + flash.flash('Unpin node');
108 + }
109 +
80 angular.module('ovTopo2') 110 angular.module('ovTopo2')
81 .factory('Topo2KeyCommandService', 111 .factory('Topo2KeyCommandService',
82 - ['KeyService', 'Topo2PrefsService', 'Topo2MapService', 'PrefsService', 112 + ['KeyService', 'FlashService', 'WebSocketService', 'Topo2PrefsService',
83 - 'Topo2InstanceService', 'Topo2SummaryPanelService', 113 + 'Topo2MapService', 'PrefsService', 'Topo2InstanceService',
84 - function (_ks_, _t2ps_, _t2ms_, _ps_, _t2is_, _t2sp_) { 114 + 'Topo2SummaryPanelService', 'Topo2ViewService',
115 + function (_ks_, _flash_, _wss_, _t2ps_, _t2ms_, _ps_, _t2is_, _t2sp_, _t2vs_) {
116 +
117 + ks = _ks_;
118 + flash = _flash_;
119 + wss = _wss_;
85 t2ps = _t2ps_; 120 t2ps = _t2ps_;
86 t2ms = _t2ms_; 121 t2ms = _t2ms_;
87 t2is = _t2is_; 122 t2is = _t2is_;
88 ps = _ps_; 123 ps = _ps_;
89 - ks = _ks_;
90 t2sp = _t2sp_; 124 t2sp = _t2sp_;
125 + t2vs = _t2vs_;
91 126
92 return { 127 return {
93 init: init, 128 init: init,
......
...@@ -438,6 +438,7 @@ ...@@ -438,6 +438,7 @@
438 init: init, 438 init: init,
439 createForceLayout: createForceLayout, 439 createForceLayout: createForceLayout,
440 update: update, 440 update: update,
441 + tick: tick,
441 start: start, 442 start: start,
442 443
443 setDimensions: setDimensions 444 setDimensions: setDimensions
......
...@@ -22,8 +22,7 @@ ...@@ -22,8 +22,7 @@
22 (function () { 22 (function () {
23 'use strict'; 23 'use strict';
24 24
25 - var $log; 25 + var $log, Collection, Model, ts, sus, t2zs, t2vs;
26 - var Collection, Model, ts, sus, t2zs;
27 26
28 var linkLabelOffset = '0.35em'; 27 var linkLabelOffset = '0.35em';
29 28
...@@ -59,7 +58,6 @@ ...@@ -59,7 +58,6 @@
59 var attrs = angular.extend({}, linkPoints, { 58 var attrs = angular.extend({}, linkPoints, {
60 key: this.get('id'), 59 key: this.get('id'),
61 class: 'link', 60 class: 'link',
62 - weight: 1,
63 srcPort: this.get('srcPort'), 61 srcPort: this.get('srcPort'),
64 tgtPort: this.get('dstPort'), 62 tgtPort: this.get('dstPort'),
65 position: { 63 position: {
...@@ -144,6 +142,8 @@ ...@@ -144,6 +142,8 @@
144 }); 142 });
145 143
146 this.el.classed('enhanced', true); 144 this.el.classed('enhanced', true);
145 +
146 + if (showPort()) {
147 point = this.locatePortLabel(); 147 point = this.locatePortLabel();
148 angular.extend(point, { 148 angular.extend(point, {
149 id: 'topo-port-tgt', 149 id: 'topo-port-tgt',
...@@ -182,6 +182,7 @@ ...@@ -182,6 +182,7 @@
182 182
183 el.attr('transform', sus.translate(d.x, d.y)); 183 el.attr('transform', sus.translate(d.x, d.y));
184 }); 184 });
185 + }
185 }, 186 },
186 unenhance: function () { 187 unenhance: function () {
187 this.el.classed('enhanced', false); 188 this.el.classed('enhanced', false);
...@@ -248,6 +249,11 @@ ...@@ -248,6 +249,11 @@
248 setScale: function () { 249 setScale: function () {
249 var width = linkScale(widthRatio / t2zs.scale()); 250 var width = linkScale(widthRatio / t2zs.scale());
250 this.el.style('stroke-width', width + 'px'); 251 this.el.style('stroke-width', width + 'px');
252 + },
253 + update: function () {
254 + if (this.el.classed('enhanced')) {
255 + this.enhance();
256 + }
251 } 257 }
252 }); 258 });
253 259
...@@ -258,17 +264,23 @@ ...@@ -258,17 +264,23 @@
258 return new LinkCollection(data); 264 return new LinkCollection(data);
259 } 265 }
260 266
267 + function showPort() {
268 + return t2vs.getPortHighlighting();
269 + }
270 +
261 angular.module('ovTopo2') 271 angular.module('ovTopo2')
262 .factory('Topo2LinkService', 272 .factory('Topo2LinkService',
263 ['$log', 'Topo2Collection', 'Topo2Model', 273 ['$log', 'Topo2Collection', 'Topo2Model',
264 'ThemeService', 'SvgUtilService', 'Topo2ZoomService', 274 'ThemeService', 'SvgUtilService', 'Topo2ZoomService',
265 - 275 + 'Topo2ViewService',
266 - function (_$log_, _Collection_, _Model_, _ts_, _sus_, _t2zs_) { 276 + function (_$log_, _Collection_, _Model_, _ts_, _sus_,
277 + _t2zs_, _t2vs_) {
267 278
268 $log = _$log_; 279 $log = _$log_;
269 ts = _ts_; 280 ts = _ts_;
270 sus = _sus_; 281 sus = _sus_;
271 t2zs = _t2zs_; 282 t2zs = _t2zs_;
283 + t2vs = _t2vs_;
272 Collection = _Collection_; 284 Collection = _Collection_;
273 Model = _Model_; 285 Model = _Model_;
274 286
......
...@@ -29,13 +29,15 @@ ...@@ -29,13 +29,15 @@
29 var MapSelectionDialog; 29 var MapSelectionDialog;
30 30
31 // internal state 31 // internal state
32 - var mapG; 32 + var mapG, zoomLayer, zoomer;
33 33
34 - function init(zoomLayer) { 34 + function init(_zoomLayer_, _zoomer_) {
35 - return setUpMap(zoomLayer); 35 + zoomLayer = _zoomLayer_;
36 + zoomer = _zoomer_;
37 + return setUpMap();
36 } 38 }
37 39
38 - function setUpMap(zoomLayer) { 40 + function setUpMap() {
39 var prefs = currentMap(), 41 var prefs = currentMap(),
40 mapId = prefs.mapid, 42 mapId = prefs.mapid,
41 mapFilePath = prefs.mapfilepath, 43 mapFilePath = prefs.mapfilepath,
...@@ -124,6 +126,10 @@ ...@@ -124,6 +126,10 @@
124 }).open(); 126 }).open();
125 } 127 }
126 128
129 + function resetZoom() {
130 + zoomer.reset();
131 + }
132 +
127 angular.module('ovTopo2') 133 angular.module('ovTopo2')
128 .factory('Topo2MapService', 134 .factory('Topo2MapService',
129 ['$location', 'PrefsService', 'MapService', 135 ['$location', 'PrefsService', 'MapService',
...@@ -140,7 +146,9 @@ ...@@ -140,7 +146,9 @@
140 return { 146 return {
141 init: init, 147 init: init,
142 openMapSelection: openMapSelection, 148 openMapSelection: openMapSelection,
143 - toggle: toggle 149 + toggle: toggle,
150 +
151 + resetZoom: resetZoom
144 }; 152 };
145 } 153 }
146 ]); 154 ]);
......
...@@ -15,18 +15,14 @@ ...@@ -15,18 +15,14 @@
15 */ 15 */
16 16
17 /* 17 /*
18 - ONOS GUI -- Topology Layout Module. 18 + ONOS GUI -- Topology Node Module.
19 - Module that contains the d3.force.layout logic 19 + Module that contains model for nodes within the topology
20 */ 20 */
21 21
22 (function () { 22 (function () {
23 'use strict'; 23 'use strict';
24 24
25 - var randomService, ps, sus, is, ts, t2mcs; 25 + var ps, sus, is, ts, t2mcs, t2nps, fn;
26 - var fn;
27 -
28 - // Internal state;
29 - var nearDist = 15;
30 26
31 var devIconDim = 36, 27 var devIconDim = 36,
32 devIconDimMin = 20, 28 devIconDimMin = 20,
...@@ -56,133 +52,56 @@ ...@@ -56,133 +52,56 @@
56 dColTheme[ts.theme()][otag]; 52 dColTheme[ts.theme()][otag];
57 } 53 }
58 54
59 - function positionNode(node, forUpdate) {
60 - var meta = node.get('metaUi'),
61 - x = meta && meta.x,
62 - y = meta && meta.y,
63 - dim = [800, 600],
64 - xy;
65 -
66 - // If the device contains explicit LONG/LAT data, use that to position
67 - if (setLongLat(node)) {
68 - // Indicate we want to update cached meta data...
69 - return true;
70 - }
71 -
72 - // else if we have [x,y] cached in meta data, use that...
73 - if (x !== undefined && y !== undefined) {
74 - node.fixed = true;
75 - node.px = node.x = x;
76 - node.py = node.y = y;
77 - return;
78 - }
79 -
80 - // if this is a node update (not a node add).. skip randomizer
81 - if (forUpdate) {
82 - return;
83 - }
84 -
85 - // Note: Placing incoming unpinned nodes at exactly the same point
86 - // (center of the view) causes them to explode outwards when
87 - // the force layout kicks in. So, we spread them out a bit
88 - // initially, to provide a more serene layout convergence.
89 - // Additionally, if the node is a host, we place it near
90 - // the device it is connected to.
91 -
92 - function rand() {
93 - return {
94 - x: randomService.randDim(dim[0]),
95 - y: randomService.randDim(dim[1])
96 - };
97 - }
98 -
99 - function near(node) {
100 - return {
101 - x: node.x + nearDist + randomService.spread(nearDist),
102 - y: node.y + nearDist + randomService.spread(nearDist)
103 - };
104 - }
105 -
106 - function getDevice(cp) {
107 - return rand();
108 - }
109 -
110 - xy = (node.class === 'host') ? near(getDevice(node.cp)) : rand();
111 -
112 - if (node.class === 'sub-region') {
113 - xy = rand();
114 - node.x = node.px = xy.x;
115 - node.y = node.py = xy.y;
116 - }
117 - angular.extend(node, xy);
118 - }
119 -
120 - function setLongLat(el) {
121 - var loc = el.get('location'),
122 - coord;
123 -
124 - if (loc && loc.type === 'lnglat') {
125 -
126 - if (loc.lat === 0 && loc.lng === 0) {
127 - return false;
128 - }
129 -
130 - coord = coordFromLngLat(loc);
131 - el.fixed = true;
132 - el.x = el.px = coord[0];
133 - el.y = el.py = coord[1];
134 -
135 - return true;
136 - }
137 - }
138 -
139 - function coordFromLngLat(loc) {
140 - var p = t2mcs.projection();
141 - return p ? p([loc.lng, loc.lat]) : [0, 0];
142 - }
143 -
144 angular.module('ovTopo2') 55 angular.module('ovTopo2')
145 .factory('Topo2NodeModel', 56 .factory('Topo2NodeModel',
146 - ['Topo2Model', 'FnService', 'RandomService', 'Topo2PrefsService', 57 + ['Topo2Model', 'FnService', 'Topo2PrefsService',
147 'SvgUtilService', 'IconService', 'ThemeService', 58 'SvgUtilService', 'IconService', 'ThemeService',
148 - 'Topo2MapConfigService', 'Topo2ZoomService', 59 + 'Topo2MapConfigService', 'Topo2ZoomService', 'Topo2NodePositionService',
149 - function (Model, _fn_, _RandomService_, _ps_, _sus_, _is_, _ts_, 60 + function (Model, _fn_, _ps_, _sus_, _is_, _ts_,
150 - _t2mcs_, zoomService) { 61 + _t2mcs_, zoomService, _t2nps_) {
151 62
152 - randomService = _RandomService_;
153 ts = _ts_; 63 ts = _ts_;
154 fn = _fn_; 64 fn = _fn_;
155 ps = _ps_; 65 ps = _ps_;
156 sus = _sus_; 66 sus = _sus_;
157 is = _is_; 67 is = _is_;
158 t2mcs = _t2mcs_; 68 t2mcs = _t2mcs_;
69 + t2nps = _t2nps_;
159 70
160 return Model.extend({ 71 return Model.extend({
161 initialize: function () { 72 initialize: function () {
162 - this.set('class', this.nodeType);
163 - this.set('svgClass', this.svgClassName());
164 this.node = this.createNode(); 73 this.node = this.createNode();
74 + this._events = {
75 + 'mouseover': 'mouseoverHandler',
76 + 'mouseout': 'mouseoutHandler'
77 + };
165 }, 78 },
166 createNode: function () { 79 createNode: function () {
167 - this.set('class', this.nodeType);
168 this.set('svgClass', this.svgClassName()); 80 this.set('svgClass', this.svgClassName());
169 - positionNode(this); 81 + t2nps.positionNode(this);
170 return this; 82 return this;
171 }, 83 },
172 setUpEvents: function () { 84 setUpEvents: function () {
173 - var _this = this; 85 + var _this = this,
174 - angular.forEach(this.events, function (handler, key) { 86 + events = angular.extend({}, this._events, this.events);
87 + angular.forEach(events, function (handler, key) {
175 _this.el.on(key, _this[handler].bind(_this)); 88 _this.el.on(key, _this[handler].bind(_this));
176 }); 89 });
177 }, 90 },
91 + mouseoverHandler: function () {
92 + this.set('hovered', true);
93 + },
94 + mouseoutHandler: function () {
95 + this.set('hovered', false);
96 + },
178 icon: function () { 97 icon: function () {
179 return 'unknown'; 98 return 'unknown';
180 }, 99 },
181 label: function () { 100 label: function () {
182 var props = this.get('props'), 101 var props = this.get('props'),
183 id = this.get('id'), 102 id = this.get('id'),
184 - friendlyName = props ? props.name : id, 103 + friendlyName = props && props.name ? props.name : id,
185 - labels = ['', friendlyName, id], 104 + labels = ['', friendlyName || id, id],
186 nli = ps.get('dlbls'), 105 nli = ps.get('dlbls'),
187 idx = (nli < labels.length) ? nli : 0; 106 idx = (nli < labels.length) ? nli : 0;
188 107
...@@ -197,7 +116,8 @@ ...@@ -197,7 +116,8 @@
197 return box.width + labelPad * 2; 116 return box.width + labelPad * 2;
198 }, 117 },
199 addLabelElements: function (label) { 118 addLabelElements: function (label) {
200 - var rect = this.el.append('rect'); 119 + var rect = this.el.append('rect')
120 + .attr('class', 'node-container');
201 var glythRect = this.el.append('rect') 121 var glythRect = this.el.append('rect')
202 .attr('y', -halfDevIcon) 122 .attr('y', -halfDevIcon)
203 .attr('x', -halfDevIcon) 123 .attr('x', -halfDevIcon)
...@@ -243,7 +163,8 @@ ...@@ -243,7 +163,8 @@
243 this.nodeType, 163 this.nodeType,
244 this.get('type'), 164 this.get('type'),
245 { 165 {
246 - online: this.get('online') 166 + online: this.get('online'),
167 + selected: this.get('selected')
247 } 168 }
248 ); 169 );
249 }, 170 },
...@@ -251,6 +172,9 @@ ...@@ -251,6 +172,9 @@
251 var p = t2mcs.projection(); 172 var p = t2mcs.projection();
252 return p ? p.invert(coord) : [0, 0]; 173 return p ? p.invert(coord) : [0, 0];
253 }, 174 },
175 + resetPosition: function () {
176 + t2nps.setLongLat(this);
177 + },
254 update: function () { 178 update: function () {
255 this.updateLabel(); 179 this.updateLabel();
256 }, 180 },
...@@ -281,8 +205,8 @@ ...@@ -281,8 +205,8 @@
281 multipler = devIconDimMax / (dim * zoomService.scale()); 205 multipler = devIconDimMax / (dim * zoomService.scale());
282 } 206 }
283 207
284 - 208 + this.el.selectAll('*')
285 - this.el.selectAll('*').style('transform', 'scale(' + multipler + ')'); 209 + .style('transform', 'scale(' + multipler + ')');
286 }, 210 },
287 render: function () { 211 render: function () {
288 var node = this.el, 212 var node = this.el,
...@@ -293,7 +217,8 @@ ...@@ -293,7 +217,8 @@
293 // Label 217 // Label
294 var labelElements = this.addLabelElements(label); 218 var labelElements = this.addLabelElements(label);
295 labelWidth = label ? this.computeLabelWidth(node) : 0; 219 labelWidth = label ? this.computeLabelWidth(node) : 0;
296 - labelElements.rect.attr(this.labelBox(devIconDim, labelWidth)); 220 + labelElements.rect
221 + .attr(this.labelBox(devIconDim, labelWidth));
297 222
298 // Icon 223 // Icon
299 glyph = is.addDeviceIcon(node, glyphId, devIconDim); 224 glyph = is.addDeviceIcon(node, glyphId, devIconDim);
......
1 +/*
2 + * Copyright 2016-present 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 -- Topology Node Position Module.
19 + Module that helps position nodes in the topology
20 + */
21 +
22 +(function () {
23 + 'use strict';
24 +
25 + // Injected vars
26 + var rs, t2mcs;
27 +
28 + // Internal state;
29 + var nearDist = 15;
30 +
31 + function positionNode(node, forUpdate) {
32 + var meta = node.get('metaUi'),
33 + x = meta && meta.x,
34 + y = meta && meta.y,
35 + dim = [800, 600],
36 + xy;
37 +
38 + // If the device contains explicit LONG/LAT data, use that to position
39 + if (setLongLat(node)) {
40 + // Indicate we want to update cached meta data...
41 + return true;
42 + }
43 +
44 + // else if we have [x,y] cached in meta data, use that...
45 + if (x !== undefined && y !== undefined) {
46 + node.fixed = true;
47 + node.px = node.x = x;
48 + node.py = node.y = y;
49 + return;
50 + }
51 +
52 + // if this is a node update (not a node add).. skip randomizer
53 + if (forUpdate) {
54 + return;
55 + }
56 +
57 + // Note: Placing incoming unpinned nodes at exactly the same point
58 + // (center of the view) causes them to explode outwards when
59 + // the force layout kicks in. So, we spread them out a bit
60 + // initially, to provide a more serene layout convergence.
61 + // Additionally, if the node is a host, we place it near
62 + // the device it is connected to.
63 +
64 + function rand() {
65 + return {
66 + x: rs.randDim(dim[0]),
67 + y: rs.randDim(dim[1])
68 + };
69 + }
70 +
71 + function near(node) {
72 + return {
73 + x: node.x + nearDist + rs.spread(nearDist),
74 + y: node.y + nearDist + rs.spread(nearDist)
75 + };
76 + }
77 +
78 + function getDevice(cp) {
79 + return rand();
80 + }
81 +
82 + xy = (node.class === 'host') ? near(getDevice(node.cp)) : rand();
83 +
84 + if (node.class === 'sub-region') {
85 + xy = rand();
86 + node.x = node.px = xy.x;
87 + node.y = node.py = xy.y;
88 + }
89 + angular.extend(node, xy);
90 + }
91 +
92 + function setLongLat(el) {
93 + var loc = el.get('location'),
94 + coord;
95 +
96 + if (loc && loc.type === 'lnglat') {
97 +
98 + if (loc.lat === 0 && loc.lng === 0) {
99 + return false;
100 + }
101 +
102 + coord = coordFromLngLat(loc);
103 + el.fixed = true;
104 + el.x = el.px = coord[0];
105 + el.y = el.py = coord[1];
106 +
107 + return true;
108 + }
109 + }
110 +
111 + function coordFromLngLat(loc) {
112 + var p = t2mcs.projection();
113 + return p ? p([loc.lng, loc.lat]) : [0, 0];
114 + }
115 +
116 + angular.module('ovTopo2')
117 + .factory('Topo2NodePositionService',
118 + ['RandomService',
119 + function (_rs_, _t2mcs_) {
120 +
121 + rs = _rs_;
122 + t2mcs = _t2mcs_;
123 +
124 + return {
125 + positionNode: positionNode,
126 + setLongLat: setLongLat
127 + };
128 + }
129 + ]);
130 +})();
...@@ -29,7 +29,9 @@ ...@@ -29,7 +29,9 @@
29 this.p = ps.createPanel(this.id, options); 29 this.p = ps.createPanel(this.id, options);
30 this.setup(); 30 this.setup();
31 31
32 + if (options.show) {
32 this.p.show(); 33 this.p.show();
34 + }
33 }; 35 };
34 36
35 Panel.prototype = { 37 Panel.prototype = {
...@@ -59,8 +61,8 @@ ...@@ -59,8 +61,8 @@
59 this.body.selectAll("*").remove(); 61 this.body.selectAll("*").remove();
60 this.footer.selectAll("*").remove(); 62 this.footer.selectAll("*").remove();
61 }, 63 },
62 - destory: function () { 64 + destroy: function () {
63 - ps.destroy(this.id); 65 + ps.destroyPanel(this.id);
64 } 66 }
65 }; 67 };
66 68
......
...@@ -80,6 +80,11 @@ ...@@ -80,6 +80,11 @@
80 return []; 80 return [];
81 } 81 }
82 82
83 + function filterRegionNodes(predicate) {
84 + var nodes = regionNodes();
85 + return _.filter(nodes, predicate);
86 + }
87 +
83 function regionLinks() { 88 function regionLinks() {
84 return (region) ? region.get('links').models : []; 89 return (region) ? region.get('links').models : [];
85 } 90 }
...@@ -105,6 +110,7 @@ ...@@ -105,6 +110,7 @@
105 addRegion: addRegion, 110 addRegion: addRegion,
106 regionNodes: regionNodes, 111 regionNodes: regionNodes,
107 regionLinks: regionLinks, 112 regionLinks: regionLinks,
113 + filterRegionNodes: filterRegionNodes,
108 114
109 getSubRegions: t2sr.getSubRegions 115 getSubRegions: t2sr.getSubRegions
110 }; 116 };
......
...@@ -29,8 +29,12 @@ ...@@ -29,8 +29,12 @@
29 var summaryPanel, summaryData; 29 var summaryPanel, summaryData;
30 30
31 // configuration 31 // configuration
32 - var id = 'topo-p-summary', 32 + var id = 'topo2-p-summary',
33 className = 'topo-p', 33 className = 'topo-p',
34 + panelOpts = {
35 + show: true,
36 + width: 260 // summary and detail panel width
37 + },
34 handlerMap = { 38 handlerMap = {
35 showSummary: handleSummaryData 39 showSummary: handleSummaryData
36 }; 40 };
...@@ -40,10 +44,12 @@ ...@@ -40,10 +44,12 @@
40 bindHandlers(); 44 bindHandlers();
41 wss.sendEvent('requestSummary'); 45 wss.sendEvent('requestSummary');
42 46
43 - summaryPanel = new Panel(id, { 47 + var options = angular.extend({}, panelOpts, {
44 class: className 48 class: className
45 }); 49 });
46 50
51 + summaryPanel = new Panel(id, options);
52 +
47 summaryPanel.p.classed(className, true); 53 summaryPanel.p.classed(className, true);
48 } 54 }
49 55
...@@ -107,6 +113,11 @@ ...@@ -107,6 +113,11 @@
107 flash.flash(verb + ' Summary Panel'); 113 flash.flash(verb + ' Summary Panel');
108 } 114 }
109 115
116 + function destroy() {
117 + wss.unbindHandlers(handlerMap);
118 + summaryPanel.destroy();
119 + }
120 +
110 angular.module('ovTopo2') 121 angular.module('ovTopo2')
111 .factory('Topo2SummaryPanelService', 122 .factory('Topo2SummaryPanelService',
112 ['Topo2PanelService', 'GlyphService', 'WebSocketService', 'FlashService', 123 ['Topo2PanelService', 'GlyphService', 'WebSocketService', 'FlashService',
...@@ -120,7 +131,8 @@ ...@@ -120,7 +131,8 @@
120 return { 131 return {
121 init: init, 132 init: init,
122 133
123 - toggle: toggle 134 + toggle: toggle,
135 + destroy: destroy
124 }; 136 };
125 } 137 }
126 ]); 138 ]);
......
...@@ -15,14 +15,21 @@ ...@@ -15,14 +15,21 @@
15 */ 15 */
16 16
17 /* 17 /*
18 - ONOS GUI -- Topology Layout Module. 18 + ONOS GUI -- Topology View Module.
19 - Module that contains the d3.force.layout logic 19 + Module that contains the topology view variables
20 */ 20 */
21 21
22 (function () { 22 (function () {
23 'use strict'; 23 'use strict';
24 24
25 - var dimensions; 25 + // Injected Services
26 + var flash;
27 +
28 + // Internal State
29 + var dimensions,
30 + viewOptions = {
31 + linkPortHighlighting: true
32 + };
26 33
27 function newDim(_dimensions) { 34 function newDim(_dimensions) {
28 dimensions = _dimensions; 35 dimensions = _dimensions;
...@@ -32,13 +39,33 @@ ...@@ -32,13 +39,33 @@
32 return dimensions; 39 return dimensions;
33 } 40 }
34 41
42 + function togglePortHighlights(x) {
43 + var kev = (x === 'keyev'),
44 + on = kev ? !viewOptions.linkPortHighlighting : Boolean(x),
45 + what = on ? 'Enable' : 'Disable';
46 +
47 + viewOptions.linkPortHighlighting = on;
48 + flash.flash(what + ' port highlighting');
49 + return on;
50 + }
51 +
52 + function getPortHighlighting() {
53 + return viewOptions.linkPortHighlighting;
54 + }
55 +
35 angular.module('ovTopo2') 56 angular.module('ovTopo2')
36 .factory('Topo2ViewService', 57 .factory('Topo2ViewService',
37 - [ 58 + ['FlashService',
38 - function () { 59 + function (_flash_) {
60 +
61 + flash = _flash_;
62 +
39 return { 63 return {
40 newDim: newDim, 64 newDim: newDim,
41 - getDimensions: getDimensions 65 + getDimensions: getDimensions,
66 +
67 + togglePortHighlights: togglePortHighlights,
68 + getPortHighlighting: getPortHighlighting
42 }; 69 };
43 } 70 }
44 ] 71 ]
......
...@@ -40,6 +40,7 @@ ...@@ -40,6 +40,7 @@
40 40
41 <script src="tp/Chart.min.js"></script> 41 <script src="tp/Chart.min.js"></script>
42 <script src="tp/angular-chart.min.js"></script> 42 <script src="tp/angular-chart.min.js"></script>
43 + <script src="tp/lodash.min.js"></script>
43 44
44 <!-- {INJECTED-USER-START} --> 45 <!-- {INJECTED-USER-START} -->
45 <!-- {INJECTED-USER-END} --> 46 <!-- {INJECTED-USER-END} -->
...@@ -132,6 +133,7 @@ ...@@ -132,6 +133,7 @@
132 <script src="app/view/topo2/topo2D3.js"></script> 133 <script src="app/view/topo2/topo2D3.js"></script>
133 <script src="app/view/topo2/topo2Dialog.js"></script> 134 <script src="app/view/topo2/topo2Dialog.js"></script>
134 <script src="app/view/topo2/topo2Device.js"></script> 135 <script src="app/view/topo2/topo2Device.js"></script>
136 + <script src="app/view/topo2/topo2DeviceDetailsPanel.js"></script>
135 <script src="app/view/topo2/topo2Event.js"></script> 137 <script src="app/view/topo2/topo2Event.js"></script>
136 <script src="app/view/topo2/topo2Force.js"></script> 138 <script src="app/view/topo2/topo2Force.js"></script>
137 <script src="app/view/topo2/topo2Host.js"></script> 139 <script src="app/view/topo2/topo2Host.js"></script>
...@@ -145,6 +147,7 @@ ...@@ -145,6 +147,7 @@
145 <script src="app/view/topo2/topo2MapDialog.js"></script> 147 <script src="app/view/topo2/topo2MapDialog.js"></script>
146 <script src="app/view/topo2/topo2Model.js"></script> 148 <script src="app/view/topo2/topo2Model.js"></script>
147 <script src="app/view/topo2/topo2NodeModel.js"></script> 149 <script src="app/view/topo2/topo2NodeModel.js"></script>
150 + <script src="app/view/topo2/topo2NodePosition.js"></script>
148 <script src="app/view/topo2/topo2Panel.js"></script> 151 <script src="app/view/topo2/topo2Panel.js"></script>
149 <script src="app/view/topo2/topo2Prefs.js"></script> 152 <script src="app/view/topo2/topo2Prefs.js"></script>
150 <script src="app/view/topo2/topo2Region.js"></script> 153 <script src="app/view/topo2/topo2Region.js"></script>
......
This diff is collapsed. Click to expand it.