blob: 912e851ccbcc7b6597810ddf31297547fc256bac [file] [log] [blame]
Simon Hunt737c89f2015-01-28 12:23:19 -08001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2015-present Open Networking Foundation
Simon Hunt737c89f2015-01-28 12:23:19 -08003 *
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/*
Simon Hunt3a6eec02015-02-09 21:16:43 -080018 ONOS GUI -- Topology Force Module.
19 Visualization of the topology in an SVG layer, using a D3 Force Layout.
Simon Hunt737c89f2015-01-28 12:23:19 -080020 */
21
22(function () {
23 'use strict';
24
25 // injected refs
Simon Hunt8d22c4b2015-08-06 16:24:43 -070026 var $log, $timeout, fs, sus, ts, flash, wss, tov,
Andrea Campanella732ea832017-02-06 09:25:59 -080027 tis, tms, td3, tss, tts, tos, fltr, tls, uplink, svg, tpis;
Simon Huntac4c6f72015-02-03 19:50:53 -080028
Simon Hunte2d9dc72017-08-10 15:21:04 -070029 // function to be replaced by the localization bundle function
30 var topoLion = function (x) {
31 return '#tfs#' + x + '#';
32 };
33
Simon Huntac4c6f72015-02-03 19:50:53 -080034 // configuration
Simon Hunt1894d792015-02-04 17:09:20 -080035 var linkConfig = {
36 light: {
Simon Huntf44d7262016-06-14 14:46:56 -070037 baseColor: '#939598',
Simon Hunt1894d792015-02-04 17:09:20 -080038 inColor: '#66f',
Steven Burrows1c2a9682017-07-14 16:52:46 +010039 outColor: '#f00',
Simon Hunt1894d792015-02-04 17:09:20 -080040 },
41 dark: {
Simon Huntf44d7262016-06-14 14:46:56 -070042 // TODO : theme
43 baseColor: '#939598',
Simon Hunt1894d792015-02-04 17:09:20 -080044 inColor: '#66f',
Steven Burrows1c2a9682017-07-14 16:52:46 +010045 outColor: '#f00',
Simon Hunt1894d792015-02-04 17:09:20 -080046 },
47 inWidth: 12,
Steven Burrows1c2a9682017-07-14 16:52:46 +010048 outWidth: 10,
Simon Hunt1894d792015-02-04 17:09:20 -080049 };
50
Simon Hunt737c89f2015-01-28 12:23:19 -080051 // internal state
Steven Burrows1c2a9682017-07-14 16:52:46 +010052 var settings, // merged default settings and options
53 force, // force layout object
54 drag, // drag behavior handler
Simon Hunt737c89f2015-01-28 12:23:19 -080055 network = {
56 nodes: [],
57 links: [],
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -070058 linksByDevice: {},
Simon Hunt737c89f2015-01-28 12:23:19 -080059 lookup: {},
Steven Burrows1c2a9682017-07-14 16:52:46 +010060 revLinkToKey: {},
Simon Huntac4c6f72015-02-03 19:50:53 -080061 },
Steven Burrows1c2a9682017-07-14 16:52:46 +010062 lu, // shorthand for lookup
63 rlk, // shorthand for revLinktoKey
64 showHosts = false, // whether hosts are displayed
65 showOffline = true, // whether offline devices are displayed
66 nodeLock = false, // whether nodes can be dragged or not (locked)
67 fTimer, // timer for delayed force layout
68 fNodesTimer, // timer for delayed nodes update
69 fLinksTimer, // timer for delayed links update
70 dim, // the dimensions of the force layout [w,h]
71 linkNums = [], // array of link number labels
72 devIconDim = 36, // node target dimension
73 devIconDimMin = 20, // node minimum dimension when zoomed out
Thomas Vachuskac8c8f462021-03-01 11:22:56 -080074 devIconDimMax = 70, // node maximum dimension when zoomed in
Steven Burrowsf17f0ab2017-04-11 11:03:58 -070075 portLabelDim = 30;
Simon Hunt737c89f2015-01-28 12:23:19 -080076
77 // SVG elements;
Bri Prebilic Cole80401762015-07-16 11:36:18 -070078 var linkG, linkLabelG, numLinkLblsG, portLabelG, nodeG;
Simon Hunt737c89f2015-01-28 12:23:19 -080079
80 // D3 selections;
81 var link, linkLabel, node;
82
83 // default settings for force layout
84 var defaultSettings = {
85 gravity: 0.4,
86 friction: 0.7,
87 charge: {
88 // note: key is node.class
89 device: -8000,
90 host: -5000,
Steven Burrows1c2a9682017-07-14 16:52:46 +010091 _def_: -12000,
Simon Hunt737c89f2015-01-28 12:23:19 -080092 },
93 linkDistance: {
94 // note: key is link.type
95 direct: 100,
96 optical: 120,
97 hostLink: 3,
Steven Burrows1c2a9682017-07-14 16:52:46 +010098 _def_: 50,
Simon Hunt737c89f2015-01-28 12:23:19 -080099 },
100 linkStrength: {
101 // note: key is link.type
102 // range: {0.0 ... 1.0}
Steven Burrows1c2a9682017-07-14 16:52:46 +0100103 // direct: 1.0,
104 // optical: 1.0,
105 // hostLink: 1.0,
106 _def_: 1.0,
107 },
Simon Hunt737c89f2015-01-28 12:23:19 -0800108 };
109
Thomas Vachuskac8c8f462021-03-01 11:22:56 -0800110 var hostScaleFactor = {icon: 1.0, text: 1.0};
Simon Hunt737c89f2015-01-28 12:23:19 -0800111
Simon Huntac4c6f72015-02-03 19:50:53 -0800112 // ==========================
113 // === EVENT HANDLERS
114
Thomas Vachuskac616e172018-04-17 16:57:12 -0700115 function mergeNodeData(o, n) {
116 angular.extend(o, n);
117 if (!n.location) {
118 delete o.location;
119 }
120 }
121
Simon Huntac4c6f72015-02-03 19:50:53 -0800122 function addDevice(data) {
123 var id = data.id,
124 d;
125
Simon Hunt1894d792015-02-04 17:09:20 -0800126 uplink.showNoDevs(false);
Simon Huntac4c6f72015-02-03 19:50:53 -0800127
128 // although this is an add device event, if we already have the
129 // device, treat it as an update instead..
Simon Hunt1894d792015-02-04 17:09:20 -0800130 if (lu[id]) {
Simon Huntac4c6f72015-02-03 19:50:53 -0800131 updateDevice(data);
132 return;
133 }
134
Simon Hunt3a6eec02015-02-09 21:16:43 -0800135 d = tms.createDeviceNode(data);
Simon Huntac4c6f72015-02-03 19:50:53 -0800136 network.nodes.push(d);
Simon Hunt1894d792015-02-04 17:09:20 -0800137 lu[id] = d;
Simon Huntac4c6f72015-02-03 19:50:53 -0800138 updateNodes();
Simon Hunta17fa672015-08-19 18:42:22 -0700139 fStart();
Simon Huntac4c6f72015-02-03 19:50:53 -0800140 }
141
142 function updateDevice(data) {
143 var id = data.id,
Simon Hunt1894d792015-02-04 17:09:20 -0800144 d = lu[id],
Simon Huntac4c6f72015-02-03 19:50:53 -0800145 wasOnline;
146
147 if (d) {
148 wasOnline = d.online;
Thomas Vachuskac616e172018-04-17 16:57:12 -0700149 mergeNodeData(d, data);
Simon Hunt3a6eec02015-02-09 21:16:43 -0800150 if (tms.positionNode(d, true)) {
Simon Hunt445e8152015-02-06 13:00:12 -0800151 sendUpdateMeta(d);
Simon Huntac4c6f72015-02-03 19:50:53 -0800152 }
153 updateNodes();
Thomas Vachuskac616e172018-04-17 16:57:12 -0700154 tick();
Simon Huntac4c6f72015-02-03 19:50:53 -0800155 if (wasOnline !== d.online) {
Simon Huntdc6adea2015-02-09 22:29:36 -0800156 tms.findAttachedLinks(d.id).forEach(restyleLinkElement);
Simon Hunt5724fb42015-02-05 16:59:40 -0800157 updateOfflineVisibility(d);
Simon Huntac4c6f72015-02-03 19:50:53 -0800158 }
Thomas Vachuskac616e172018-04-17 16:57:12 -0700159 fStart();
Simon Huntac4c6f72015-02-03 19:50:53 -0800160 }
161 }
162
Simon Hunt1894d792015-02-04 17:09:20 -0800163 function removeDevice(data) {
164 var id = data.id,
165 d = lu[id];
166 if (d) {
167 removeDeviceElement(d);
Simon Hunt1894d792015-02-04 17:09:20 -0800168 }
169 }
170
171 function addHost(data) {
172 var id = data.id,
Simon Hunt12c79ed2017-09-12 11:58:44 -0700173 d;
Simon Hunt1894d792015-02-04 17:09:20 -0800174
175 // although this is an add host event, if we already have the
176 // host, treat it as an update instead..
177 if (lu[id]) {
178 updateHost(data);
179 return;
180 }
181
Simon Hunt3a6eec02015-02-09 21:16:43 -0800182 d = tms.createHostNode(data);
Simon Hunt1894d792015-02-04 17:09:20 -0800183 network.nodes.push(d);
184 lu[id] = d;
Simon Hunt1894d792015-02-04 17:09:20 -0800185 updateNodes();
186
Simon Hunt12c79ed2017-09-12 11:58:44 -0700187 // need to handle possible multiple links (multi-homed host)
Simon Hunt7df764f2017-09-14 21:26:14 -0700188 createHostLinks(data.allCps, d);
Simon Hunt12c79ed2017-09-12 11:58:44 -0700189
190 if (d.links.length) {
Simon Hunt1894d792015-02-04 17:09:20 -0800191 updateLinks();
192 }
Simon Hunta17fa672015-08-19 18:42:22 -0700193 fStart();
Simon Hunt1894d792015-02-04 17:09:20 -0800194 }
195
196 function updateHost(data) {
197 var id = data.id,
198 d = lu[id];
199 if (d) {
Thomas Vachuskac616e172018-04-17 16:57:12 -0700200 mergeNodeData(d, data);
Simon Hunt3a6eec02015-02-09 21:16:43 -0800201 if (tms.positionNode(d, true)) {
Simon Hunt445e8152015-02-06 13:00:12 -0800202 sendUpdateMeta(d);
Simon Hunt1894d792015-02-04 17:09:20 -0800203 }
204 updateNodes();
Thomas Vachuskac616e172018-04-17 16:57:12 -0700205 tick();
206 fStart();
Simon Hunt1894d792015-02-04 17:09:20 -0800207 }
208 }
209
Simon Hunt7df764f2017-09-14 21:26:14 -0700210 function createHostLinks(cps, model) {
211 model.links = [];
212 cps.forEach(function (cp) {
213 var linkData = {
214 key: model.id + '/0-' + cp.device + '/' + cp.port,
215 dst: cp.device,
216 dstPort: cp.port,
217 };
218 model.links.push(linkData);
219
Thomas Vachuskaf06f5d62021-03-10 14:24:47 -0800220 // Make cell-phone devices default to wireless; others have to be annotated explicitly
221 var cType = model.type === 'cellPhone' ? "wireless" : model.connectionType
222 var lnk = tms.createHostLink(model.id, cp.device, cp.port, cType);
Simon Hunt7df764f2017-09-14 21:26:14 -0700223 if (lnk) {
224 network.links.push(lnk);
225 lu[linkData.key] = lnk;
226 }
227 });
228 }
229
Simon Hunt95d56fd2015-11-12 11:06:44 -0800230 function moveHost(data) {
231 var id = data.id,
Simon Hunt7df764f2017-09-14 21:26:14 -0700232 d = lu[id];
Simon Hunt12c79ed2017-09-12 11:58:44 -0700233
Simon Hunt95d56fd2015-11-12 11:06:44 -0800234 if (d) {
Simon Hunt7df764f2017-09-14 21:26:14 -0700235 removeAllLinkElements(d.links);
Simon Hunt95d56fd2015-11-12 11:06:44 -0800236
237 // merge new data
238 angular.extend(d, data);
239 if (tms.positionNode(d, true)) {
240 sendUpdateMeta(d);
241 }
242
Simon Hunt7df764f2017-09-14 21:26:14 -0700243 // now create new host link(s)
244 createHostLinks(data.allCps, d);
Simon Hunt95d56fd2015-11-12 11:06:44 -0800245
246 updateNodes();
247 updateLinks();
248 fResume();
249 }
250 }
251
Simon Hunt1894d792015-02-04 17:09:20 -0800252 function removeHost(data) {
253 var id = data.id,
254 d = lu[id];
255 if (d) {
256 removeHostElement(d, true);
Simon Hunt1894d792015-02-04 17:09:20 -0800257 }
258 }
259
260 function addLink(data) {
Simon Huntdc6adea2015-02-09 22:29:36 -0800261 var result = tms.findLink(data, 'add'),
Simon Hunt1894d792015-02-04 17:09:20 -0800262 bad = result.badLogic,
263 d = result.ldata;
264
265 if (bad) {
Simon Hunteb18f522016-01-28 19:22:23 -0800266 $log.debug(bad + ': ' + link.id);
Simon Hunt1894d792015-02-04 17:09:20 -0800267 return;
268 }
269
270 if (d) {
271 // we already have a backing store link for src/dst nodes
272 addLinkUpdate(d, data);
273 return;
274 }
275
276 // no backing store link yet
Simon Hunt3a6eec02015-02-09 21:16:43 -0800277 d = tms.createLink(data);
Simon Hunt1894d792015-02-04 17:09:20 -0800278 if (d) {
279 network.links.push(d);
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -0700280 aggregateLink(d, data);
Simon Hunt1894d792015-02-04 17:09:20 -0800281 lu[d.key] = d;
282 updateLinks();
Simon Hunta17fa672015-08-19 18:42:22 -0700283 fStart();
Simon Hunt1894d792015-02-04 17:09:20 -0800284 }
285 }
286
287 function updateLink(data) {
Simon Huntdc6adea2015-02-09 22:29:36 -0800288 var result = tms.findLink(data, 'update'),
Simon Hunt1894d792015-02-04 17:09:20 -0800289 bad = result.badLogic;
290 if (bad) {
Simon Hunteb18f522016-01-28 19:22:23 -0800291 $log.debug(bad + ': ' + link.id);
Simon Hunt1894d792015-02-04 17:09:20 -0800292 return;
293 }
Simon Hunteb18f522016-01-28 19:22:23 -0800294 result.updateWith(data);
Simon Hunt1894d792015-02-04 17:09:20 -0800295 }
296
297 function removeLink(data) {
Simon Hunta4242de2015-02-24 17:11:55 -0800298 var result = tms.findLink(data, 'remove');
299
300 if (!result.badLogic) {
301 result.removeRawLink();
Simon Hunt1894d792015-02-04 17:09:20 -0800302 }
Simon Hunt1894d792015-02-04 17:09:20 -0800303 }
304
Simon Hunt4a6b54b2015-10-27 22:08:25 -0700305 function topoStartDone(data) {
306 // called when the initial barrage of data has been sent from server
307 uplink.topoStartDone();
308 }
309
Simon Hunt1894d792015-02-04 17:09:20 -0800310 // ========================
311
Simon Hunt94f7dae2015-08-26 17:40:59 -0700312 function nodeById(id) {
313 return lu[id];
314 }
315
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -0700316 function makeNodeKey(node1, node2) {
317 return node1 + '-' + node2;
318 }
319
320 function findNodePair(key, keyRev) {
321 if (network.linksByDevice[key]) {
322 return key;
323 } else if (network.linksByDevice[keyRev]) {
324 return keyRev;
325 } else {
326 return false;
327 }
328 }
329
330 function aggregateLink(ldata, link) {
331 var key = makeNodeKey(link.src, link.dst),
332 keyRev = makeNodeKey(link.dst, link.src),
333 found = findNodePair(key, keyRev);
334
335 if (found) {
336 network.linksByDevice[found].push(ldata);
337 ldata.devicePair = found;
338 } else {
Steven Burrows1c2a9682017-07-14 16:52:46 +0100339 network.linksByDevice[key] = [ldata];
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -0700340 ldata.devicePair = key;
341 }
342 }
343
Simon Hunt1894d792015-02-04 17:09:20 -0800344 function addLinkUpdate(ldata, link) {
345 // add link event, but we already have the reverse link installed
346 ldata.fromTarget = link;
Simon Huntdc6adea2015-02-09 22:29:36 -0800347 rlk[link.id] = ldata.key;
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -0700348 // possible solution to el being undefined in restyleLinkElement:
Steven Burrows1c2a9682017-07-14 16:52:46 +0100349 // _updateLinks();
Simon Hunt1894d792015-02-04 17:09:20 -0800350 restyleLinkElement(ldata);
351 }
352
Simon Hunt1894d792015-02-04 17:09:20 -0800353
354 var widthRatio = 1.4,
355 linkScale = d3.scale.linear()
356 .domain([1, 12])
357 .range([widthRatio, 12 * widthRatio])
Simon Hunt5724fb42015-02-05 16:59:40 -0800358 .clamp(true),
Ray Milkeyb7f0f642016-01-22 16:08:14 -0800359 allLinkTypes = 'direct indirect optical tunnel',
360 allLinkSubTypes = 'inactive not-permitted';
Simon Hunt1894d792015-02-04 17:09:20 -0800361
Simon Hunta142dd22015-02-12 22:07:51 -0800362 function restyleLinkElement(ldata, immediate) {
Simon Hunt1894d792015-02-04 17:09:20 -0800363 // this fn's job is to look at raw links and decide what svg classes
364 // need to be applied to the line element in the DOM
365 var th = ts.theme(),
366 el = ldata.el,
367 type = ldata.type(),
Thomas Vachuskaa0410b82021-03-29 14:01:18 -0700368 connectionType = ldata.connectionType,
Simon Hunt1894d792015-02-04 17:09:20 -0800369 lw = ldata.linkWidth(),
Simon Hunta142dd22015-02-12 22:07:51 -0800370 online = ldata.online(),
Ray Milkeyb7f0f642016-01-22 16:08:14 -0800371 modeCls = ldata.expected() ? 'inactive' : 'not-permitted',
Simon Hunta142dd22015-02-12 22:07:51 -0800372 delay = immediate ? 0 : 1000;
Simon Hunt1894d792015-02-04 17:09:20 -0800373
Simon Huntf44d7262016-06-14 14:46:56 -0700374 // NOTE: understand why el is sometimes undefined on addLink events...
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -0700375 // Investigated:
376 // el is undefined when it's a reverse link that is being added.
377 // updateLinks (which sets ldata.el) isn't called before this is called.
378 // Calling _updateLinks in addLinkUpdate fixes it, but there might be
379 // a more efficient way to fix it.
380 if (el && !el.empty()) {
Thomas Vachuskacb5016f2015-05-18 14:11:43 -0700381 el.classed('link', true);
Ray Milkeyb7f0f642016-01-22 16:08:14 -0800382 el.classed(allLinkSubTypes, false);
383 el.classed(modeCls, !online);
Thomas Vachuskacb5016f2015-05-18 14:11:43 -0700384 el.classed(allLinkTypes, false);
385 if (type) {
386 el.classed(type, true);
387 }
Thomas Vachuskaf06f5d62021-03-10 14:24:47 -0800388
389 if (connectionType === 'wireless') {
390 el.transition()
391 .duration(delay)
392 .attr('stroke-width', linkScale(lw))
393 .attr('stroke', linkConfig[th].baseColor)
394 .attr('stroke-dasharray', '3 6');
395 } else {
396 el.transition()
397 .duration(delay)
398 .attr('stroke-width', linkScale(lw))
399 .attr('stroke', linkConfig[th].baseColor);
400 }
Simon Hunt1894d792015-02-04 17:09:20 -0800401 }
Simon Hunt1894d792015-02-04 17:09:20 -0800402 }
403
Simon Hunt7df764f2017-09-14 21:26:14 -0700404 function removeAllLinkElements(links) {
405 links.forEach(function (lnk) {
406 removeLinkElement(lnk);
407 });
408 }
409
Simon Hunt1894d792015-02-04 17:09:20 -0800410 function removeLinkElement(d) {
411 var idx = fs.find(d.key, network.links, 'key'),
412 removed;
413 if (idx >=0) {
414 // remove from links array
415 removed = network.links.splice(idx, 1);
416 // remove from lookup cache
417 delete lu[removed[0].key];
418 updateLinks();
Simon Hunta17fa672015-08-19 18:42:22 -0700419 fResume();
Simon Hunt1894d792015-02-04 17:09:20 -0800420 }
421 }
422
423 function removeHostElement(d, upd) {
Simon Hunt1fb00552017-09-15 09:21:14 -0700424 // first, remove associated hostLink(s)...
425 removeAllLinkElements(d.links);
Simon Hunt1894d792015-02-04 17:09:20 -0800426
427 // remove from lookup cache
428 delete lu[d.id];
429 // remove from nodes array
430 var idx = fs.find(d.id, network.nodes);
431 network.nodes.splice(idx, 1);
432
433 // remove from SVG
434 // NOTE: upd is false if we were called from removeDeviceElement()
435 if (upd) {
436 updateNodes();
Simon Hunta17fa672015-08-19 18:42:22 -0700437 fResume();
Simon Hunt1894d792015-02-04 17:09:20 -0800438 }
439 }
440
441 function removeDeviceElement(d) {
Bri Prebilic Cole8d003782015-07-31 15:33:06 -0700442 var id = d.id,
443 idx;
Simon Hunt1894d792015-02-04 17:09:20 -0800444 // first, remove associated hosts and links..
Simon Huntdc6adea2015-02-09 22:29:36 -0800445 tms.findAttachedHosts(id).forEach(removeHostElement);
446 tms.findAttachedLinks(id).forEach(removeLinkElement);
Simon Hunt1894d792015-02-04 17:09:20 -0800447
448 // remove from lookup cache
449 delete lu[id];
450 // remove from nodes array
Bri Prebilic Cole8d003782015-07-31 15:33:06 -0700451 idx = fs.find(id, network.nodes);
452 if (idx > -1) {
453 network.nodes.splice(idx, 1);
454 }
Simon Hunt1894d792015-02-04 17:09:20 -0800455
456 if (!network.nodes.length) {
Simon Huntdc6adea2015-02-09 22:29:36 -0800457 uplink.showNoDevs(true);
Simon Hunt1894d792015-02-04 17:09:20 -0800458 }
459
460 // remove from SVG
461 updateNodes();
Simon Hunta17fa672015-08-19 18:42:22 -0700462 fResume();
Simon Hunt1894d792015-02-04 17:09:20 -0800463 }
464
Simon Hunt5724fb42015-02-05 16:59:40 -0800465 function updateHostVisibility() {
Simon Hunt18bf9822015-02-12 17:35:45 -0800466 sus.visible(nodeG.selectAll('.host'), showHosts);
467 sus.visible(linkG.selectAll('.hostLink'), showHosts);
Simon Hunt8eb4d3a2015-02-23 18:23:29 -0800468 sus.visible(linkLabelG.selectAll('.hostLinkLabel'), showHosts);
Simon Hunt5724fb42015-02-05 16:59:40 -0800469 }
470
471 function updateOfflineVisibility(dev) {
472 function updDev(d, show) {
Simon Hunt8eb4d3a2015-02-23 18:23:29 -0800473 var b;
Simon Hunt18bf9822015-02-12 17:35:45 -0800474 sus.visible(d.el, show);
Simon Hunt5724fb42015-02-05 16:59:40 -0800475
Simon Huntdc6adea2015-02-09 22:29:36 -0800476 tms.findAttachedLinks(d.id).forEach(function (link) {
Simon Hunt5724fb42015-02-05 16:59:40 -0800477 b = show && ((link.type() !== 'hostLink') || showHosts);
Simon Hunt18bf9822015-02-12 17:35:45 -0800478 sus.visible(link.el, b);
Simon Hunt5724fb42015-02-05 16:59:40 -0800479 });
Simon Huntdc6adea2015-02-09 22:29:36 -0800480 tms.findAttachedHosts(d.id).forEach(function (host) {
Simon Hunt5724fb42015-02-05 16:59:40 -0800481 b = show && showHosts;
Simon Hunt18bf9822015-02-12 17:35:45 -0800482 sus.visible(host.el, b);
Simon Hunt5724fb42015-02-05 16:59:40 -0800483 });
484 }
485
486 if (dev) {
487 // updating a specific device that just toggled off/on-line
488 updDev(dev, dev.online || showOffline);
489 } else {
490 // updating all offline devices
Simon Huntdc6adea2015-02-09 22:29:36 -0800491 tms.findDevices(true).forEach(function (d) {
Simon Hunt5724fb42015-02-05 16:59:40 -0800492 updDev(d, showOffline);
493 });
494 }
495 }
496
Simon Hunt1894d792015-02-04 17:09:20 -0800497
Simon Hunt445e8152015-02-06 13:00:12 -0800498 function sendUpdateMeta(d, clearPos) {
Simon Huntac4c6f72015-02-03 19:50:53 -0800499 var metaUi = {},
500 ll;
501
Simon Hunt445e8152015-02-06 13:00:12 -0800502 // if we are not clearing the position data (unpinning),
Simon Huntfd7106c2016-02-09 15:05:26 -0800503 // attach the x, y, (and equivalent longitude, latitude)...
Simon Hunt445e8152015-02-06 13:00:12 -0800504 if (!clearPos) {
Simon Hunt3a6eec02015-02-09 21:16:43 -0800505 ll = tms.lngLatFromCoord([d.x, d.y]);
Simon Huntfd7106c2016-02-09 15:05:26 -0800506 metaUi = {
507 x: d.x,
508 y: d.y,
509 equivLoc: {
510 lng: ll[0],
Steven Burrows1c2a9682017-07-14 16:52:46 +0100511 lat: ll[1],
512 },
Simon Huntfd7106c2016-02-09 15:05:26 -0800513 };
Simon Hunt1894d792015-02-04 17:09:20 -0800514 }
515 d.metaUi = metaUi;
Simon Hunt237676b52015-03-10 19:04:26 -0700516 wss.sendEvent('updateMeta', {
Simon Hunt1894d792015-02-04 17:09:20 -0800517 id: d.id,
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -0700518 class: d.class,
Steven Burrows1c2a9682017-07-14 16:52:46 +0100519 memento: metaUi,
Simon Hunt1894d792015-02-04 17:09:20 -0800520 });
Simon Huntac4c6f72015-02-03 19:50:53 -0800521 }
522
Simon Hunt1894d792015-02-04 17:09:20 -0800523
Simon Huntac4c6f72015-02-03 19:50:53 -0800524 function mkSvgClass(d) {
525 return d.fixed ? d.svgClass + ' fixed' : d.svgClass;
526 }
527
Simon Hunt5724fb42015-02-05 16:59:40 -0800528 function vis(b) {
Simon Hunt1603c692017-08-10 19:53:35 -0700529 return topoLion(b ? 'visible' : 'hidden');
Simon Hunt5724fb42015-02-05 16:59:40 -0800530 }
531
Simon Huntfcbde892015-04-16 12:05:28 -0700532 function toggleHosts(x) {
533 var kev = (x === 'keyev'),
534 on = kev ? !showHosts : !!x;
535
536 showHosts = on;
Simon Hunt5724fb42015-02-05 16:59:40 -0800537 updateHostVisibility();
Simon Hunte2d9dc72017-08-10 15:21:04 -0700538 flash.flash(topoLion('hosts') + ' ' + vis(on));
Simon Huntfcbde892015-04-16 12:05:28 -0700539 return on;
Simon Hunt5724fb42015-02-05 16:59:40 -0800540 }
541
Simon Huntfcbde892015-04-16 12:05:28 -0700542 function toggleOffline(x) {
543 var kev = (x === 'keyev'),
544 on = kev ? !showOffline : !!x;
545
546 showOffline = on;
Simon Hunt5724fb42015-02-05 16:59:40 -0800547 updateOfflineVisibility();
Simon Hunte2d9dc72017-08-10 15:21:04 -0700548 flash.flash(topoLion('fl_offline_devices') + ' ' + vis(on));
Simon Huntfcbde892015-04-16 12:05:28 -0700549 return on;
Simon Hunt5724fb42015-02-05 16:59:40 -0800550 }
551
552 function cycleDeviceLabels() {
Bri Prebilic Cole9cf1a8d2015-04-21 13:15:29 -0700553 flash.flash(td3.incDevLabIndex());
Simon Huntdc6adea2015-02-09 22:29:36 -0800554 tms.findDevices().forEach(function (d) {
Simon Hunta4242de2015-02-24 17:11:55 -0800555 td3.updateDeviceLabel(d);
Simon Hunt1c367112015-02-05 18:02:46 -0800556 });
Simon Hunt5724fb42015-02-05 16:59:40 -0800557 }
558
Simon Hunt10618f62017-06-15 19:30:52 -0700559 function cycleHostLabels() {
560 flash.flash(td3.incHostLabIndex());
561 tms.findHosts().forEach(function (d) {
562 td3.updateHostLabel(d);
563 });
564 }
565
Thomas Vachuskaa4eac6a2021-03-01 15:11:59 -0800566 function cycleLinkLabels() {
567 td3.toggleLinkLabels();
568 }
569
Simon Hunt445e8152015-02-06 13:00:12 -0800570 function unpin() {
Simon Hunt08f841d02015-02-10 14:39:20 -0800571 var hov = tss.hovered();
572 if (hov) {
573 sendUpdateMeta(hov, true);
574 hov.fixed = false;
575 hov.el.classed('fixed', false);
Simon Hunt445e8152015-02-06 13:00:12 -0800576 fResume();
577 }
578 }
579
Simon Hunta142dd22015-02-12 22:07:51 -0800580 function showMastership(masterId) {
581 if (!masterId) {
582 restoreLayerState();
583 } else {
584 showMastershipFor(masterId);
585 }
586 }
587
588 function restoreLayerState() {
589 // NOTE: this level of indirection required, for when we have
590 // the layer filter functionality re-implemented
591 suppressLayers(false);
592 }
593
594 function showMastershipFor(id) {
595 suppressLayers(true);
596 node.each(function (n) {
597 if (n.master === id) {
Simon Hunt743a8492015-08-25 16:18:19 -0700598 n.el.classed('suppressedmax', false);
Simon Hunta142dd22015-02-12 22:07:51 -0800599 }
600 });
601 }
602
Simon Hunt743a8492015-08-25 16:18:19 -0700603 function supAmt(less) {
Steven Burrows1c2a9682017-07-14 16:52:46 +0100604 return less ? 'suppressed' : 'suppressedmax';
Simon Hunt743a8492015-08-25 16:18:19 -0700605 }
606
607 function suppressLayers(b, less) {
608 var cls = supAmt(less);
609 node.classed(cls, b);
610 link.classed(cls, b);
611 }
612
613 function unsuppressNode(id, less) {
614 var cls = supAmt(less);
615 node.each(function (n) {
616 if (n.id === id) {
617 n.el.classed(cls, false);
618 }
619 });
620 }
621
Simon Hunt94f7dae2015-08-26 17:40:59 -0700622 function unsuppressLink(key, less) {
Simon Hunt743a8492015-08-25 16:18:19 -0700623 var cls = supAmt(less);
624 link.each(function (n) {
Simon Hunt94f7dae2015-08-26 17:40:59 -0700625 if (n.key === key) {
Simon Hunt743a8492015-08-25 16:18:19 -0700626 n.el.classed(cls, false);
627 }
628 });
Simon Hunta142dd22015-02-12 22:07:51 -0800629 }
Simon Hunt445e8152015-02-06 13:00:12 -0800630
Simon Hunt86b7c882015-04-02 23:06:08 -0700631 function showBadLinks() {
632 var badLinks = tms.findBadLinks();
Simon Hunte2d9dc72017-08-10 15:21:04 -0700633 flash.flash(topoLion('fl_bad_links') + ': ' + badLinks.length);
Simon Hunt86b7c882015-04-02 23:06:08 -0700634 $log.debug('Bad Link List (' + badLinks.length + '):');
635 badLinks.forEach(function (d) {
636 $log.debug('bad link: (' + d.bad + ') ' + d.key, d);
637 if (d.el) {
638 d.el.attr('stroke-width', linkScale(2.8))
639 .attr('stroke', 'red');
640 }
641 });
642 // back to normal after 2 seconds...
643 $timeout(updateLinks, 2000);
644 }
645
Thomas Vachuskac8c8f462021-03-01 11:22:56 -0800646 function deviceScale(scaleFactor) {
647 var scale = uplink.zoomer().scale() * scaleFactor,
Steven Burrowsf17f0ab2017-04-11 11:03:58 -0700648 dim = devIconDim,
649 multiplier = 1;
650
651 if (dim * scale < devIconDimMin) {
652 multiplier = devIconDimMin / (dim * scale);
653 } else if (dim * scale > devIconDimMax) {
654 multiplier = devIconDimMax / (dim * scale);
655 }
656
657 return multiplier;
658 }
659
Thomas Vachuskac8c8f462021-03-01 11:22:56 -0800660 function linkWidthScale() {
Steven Burrowsf17f0ab2017-04-11 11:03:58 -0700661 var scale = uplink.zoomer().scale();
662 return linkScale(widthRatio) / scale;
663 }
664
Thomas Vachuskac8c8f462021-03-01 11:22:56 -0800665 function portLabelScale() {
Steven Burrowsf17f0ab2017-04-11 11:03:58 -0700666 var scale = uplink.zoomer().scale();
667 return portLabelDim / (portLabelDim * scale);
668 }
669
Thomas Vachuskac8c8f462021-03-01 11:22:56 -0800670 function adjustNodeScale() {
Steven Burrowsf17f0ab2017-04-11 11:03:58 -0700671 // Scale the network nodes
672 _.each(network.nodes, function (node) {
673 if (node.class === 'host') {
Thomas Vachuskac8c8f462021-03-01 11:22:56 -0800674 node.el.selectAll('g').style('transform', 'scale(' + deviceScale(hostScaleFactor.icon) + ')');
675 node.el.selectAll('text').style('transform', 'scale(' + deviceScale(hostScaleFactor.text) + ')');
Steven Burrowsf17f0ab2017-04-11 11:03:58 -0700676 return;
677 }
Thomas Vachuskac8c8f462021-03-01 11:22:56 -0800678 node.el.selectAll('*').style('transform', 'scale(' + deviceScale(1.0) + ')');
Steven Burrowsf17f0ab2017-04-11 11:03:58 -0700679 });
680
681 // Scale the network links
682 _.each(network.links, function (link) {
Thomas Vachuskac8c8f462021-03-01 11:22:56 -0800683 link.el.style('stroke-width', linkWidthScale() + 'px');
Steven Burrowsf17f0ab2017-04-11 11:03:58 -0700684 });
685
686 d3.select('#topo-portLabels')
687 .selectAll('.portLabel')
688 .selectAll('*')
Thomas Vachuskac8c8f462021-03-01 11:22:56 -0800689 .style('transform', 'scale(' + portLabelScale() + ')');
690 }
691
692
693 function toggleScale(scale) {
694 if (scale < 0.5) {
695 return 1.0
696 }
697 return scale - 0.2;
698 }
699
700 function toggleHostTextSize() {
701 hostScaleFactor.text = toggleScale(hostScaleFactor.text);
702 adjustNodeScale();
703 }
704
705 function toggleHostIconSize() {
706 hostScaleFactor.icon = toggleScale(hostScaleFactor.icon);
707 adjustNodeScale();
Steven Burrowsf17f0ab2017-04-11 11:03:58 -0700708 }
709
Simon Huntfd7106c2016-02-09 15:05:26 -0800710 function resetAllLocations() {
711 tms.resetAllLocations();
712 updateNodes();
713 tick(); // force nodes to be redrawn in their new locations
Simon Hunte2d9dc72017-08-10 15:21:04 -0700714 flash.flash(topoLion('fl_reset_node_locations'));
Simon Huntfd7106c2016-02-09 15:05:26 -0800715 }
716
Simon Hunt5724fb42015-02-05 16:59:40 -0800717 // ==========================================
718
Simon Huntac4c6f72015-02-03 19:50:53 -0800719 function updateNodes() {
Thomas Vachuska1a989c12015-06-09 18:29:22 -0700720 if (fNodesTimer) {
721 $timeout.cancel(fNodesTimer);
722 }
723 fNodesTimer = $timeout(_updateNodes, 150);
724 }
725
Simon Hunta17fa672015-08-19 18:42:22 -0700726 // IMPLEMENTATION NOTE: _updateNodes() should NOT stop, start, or resume
727 // the force layout; that needs to be determined and implemented elsewhere
Thomas Vachuska1a989c12015-06-09 18:29:22 -0700728 function _updateNodes() {
Simon Hunt1894d792015-02-04 17:09:20 -0800729 // select all the nodes in the layout:
Simon Huntac4c6f72015-02-03 19:50:53 -0800730 node = nodeG.selectAll('.node')
731 .data(network.nodes, function (d) { return d.id; });
732
Simon Hunt1894d792015-02-04 17:09:20 -0800733 // operate on existing nodes:
Simon Hunta4242de2015-02-24 17:11:55 -0800734 node.filter('.device').each(td3.deviceExisting);
735 node.filter('.host').each(td3.hostExisting);
Simon Huntac4c6f72015-02-03 19:50:53 -0800736
737 // operate on entering nodes:
738 var entering = node.enter()
739 .append('g')
740 .attr({
741 id: function (d) { return sus.safeId(d.id); },
742 class: mkSvgClass,
Simon Hunta17fa672015-08-19 18:42:22 -0700743 transform: function (d) {
744 // Need to guard against NaN here ??
745 return sus.translate(d.x, d.y);
746 },
Steven Burrows1c2a9682017-07-14 16:52:46 +0100747 opacity: 0,
Simon Huntac4c6f72015-02-03 19:50:53 -0800748 })
749 .call(drag)
Simon Hunt08f841d02015-02-10 14:39:20 -0800750 .on('mouseover', tss.nodeMouseOver)
751 .on('mouseout', tss.nodeMouseOut)
Simon Huntac4c6f72015-02-03 19:50:53 -0800752 .transition()
753 .attr('opacity', 1);
754
Simon Hunt1894d792015-02-04 17:09:20 -0800755 // augment entering nodes:
Simon Hunta4242de2015-02-24 17:11:55 -0800756 entering.filter('.device').each(td3.deviceEnter);
757 entering.filter('.host').each(td3.hostEnter);
Simon Huntac4c6f72015-02-03 19:50:53 -0800758
Simon Hunt51056592015-02-03 21:48:07 -0800759 // operate on both existing and new nodes:
Simon Hunta4242de2015-02-24 17:11:55 -0800760 td3.updateDeviceColors();
Simon Huntac4c6f72015-02-03 19:50:53 -0800761
762 // operate on exiting nodes:
763 // Note that the node is removed after 2 seconds.
764 // Sub element animations should be shorter than 2 seconds.
765 var exiting = node.exit()
766 .transition()
767 .duration(2000)
768 .style('opacity', 0)
769 .remove();
770
Simon Hunt1894d792015-02-04 17:09:20 -0800771 // exiting node specifics:
Simon Hunta4242de2015-02-24 17:11:55 -0800772 exiting.filter('.host').each(td3.hostExit);
773 exiting.filter('.device').each(td3.deviceExit);
Thomas Vachuskac616e172018-04-17 16:57:12 -0700774 tick();
Simon Huntac4c6f72015-02-03 19:50:53 -0800775 }
776
Simon Hunt51056592015-02-03 21:48:07 -0800777 // ==========================
Simon Hunt1894d792015-02-04 17:09:20 -0800778
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700779 function getDefaultPos(link) {
780 return {
781 x1: link.source.x,
782 y1: link.source.y,
783 x2: link.target.x,
Steven Burrows1c2a9682017-07-14 16:52:46 +0100784 y2: link.target.y,
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700785 };
786 }
787
788 // returns amount of adjustment along the normal for given link
789 function amt(numLinks, linkIdx) {
790 var gap = 6;
791 return (linkIdx - ((numLinks - 1) / 2)) * gap;
792 }
793
794 function calcMovement(d, amt, flipped) {
795 var pos = getDefaultPos(d),
796 mult = flipped ? -amt : amt,
797 dx = pos.x2 - pos.x1,
798 dy = pos.y2 - pos.y1,
799 length = Math.sqrt((dx * dx) + (dy * dy));
800
801 return {
802 x1: pos.x1 + (mult * dy / length),
803 y1: pos.y1 + (mult * -dx / length),
804 x2: pos.x2 + (mult * dy / length),
Steven Burrows1c2a9682017-07-14 16:52:46 +0100805 y2: pos.y2 + (mult * -dx / length),
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700806 };
807 }
808
809 function calcPosition() {
810 var lines = this,
811 linkSrcId;
Bri Prebilic Cole80401762015-07-16 11:36:18 -0700812 linkNums = [];
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700813 lines.each(function (d) {
814 if (d.type() === 'hostLink') {
815 d.position = getDefaultPos(d);
816 }
817 });
818
819 function normalizeLinkSrc(link) {
820 // ensure source device is consistent across set of links
821 // temporary measure until link modeling is refactored
822 if (!linkSrcId) {
823 linkSrcId = link.source.id;
824 return false;
825 }
826
827 return link.source.id !== linkSrcId;
828 }
829
Bri Prebilic Cole80401762015-07-16 11:36:18 -0700830 angular.forEach(network.linksByDevice, function (linkArr, key) {
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700831 var numLinks = linkArr.length,
832 link;
833
834 if (numLinks === 1) {
835 link = linkArr[0];
836 link.position = getDefaultPos(link);
Bri Prebilic Cole80401762015-07-16 11:36:18 -0700837 link.position.multiLink = false;
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700838 } else if (numLinks >= 5) {
839 // this code is inefficient, in the future the way links
840 // are modeled will be changed
841 angular.forEach(linkArr, function (link) {
842 link.position = getDefaultPos(link);
843 link.position.multiLink = true;
844 });
Bri Prebilic Cole80401762015-07-16 11:36:18 -0700845 linkNums.push({
846 id: key,
847 num: numLinks,
Steven Burrows1c2a9682017-07-14 16:52:46 +0100848 linkCoords: linkArr[0].position,
Bri Prebilic Cole80401762015-07-16 11:36:18 -0700849 });
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700850 } else {
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700851 linkSrcId = null;
852 angular.forEach(linkArr, function (link, index) {
853 var offsetAmt = amt(numLinks, index),
854 needToFlip = normalizeLinkSrc(link);
855 link.position = calcMovement(link, offsetAmt, needToFlip);
Bri Prebilic Cole80401762015-07-16 11:36:18 -0700856 link.position.multiLink = false;
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700857 });
858 }
859 });
860 }
861
Simon Hunt1894d792015-02-04 17:09:20 -0800862 function updateLinks() {
Thomas Vachuska1a989c12015-06-09 18:29:22 -0700863 if (fLinksTimer) {
864 $timeout.cancel(fLinksTimer);
865 }
866 fLinksTimer = $timeout(_updateLinks, 150);
867 }
868
Simon Hunta17fa672015-08-19 18:42:22 -0700869 // IMPLEMENTATION NOTE: _updateLinks() should NOT stop, start, or resume
870 // the force layout; that needs to be determined and implemented elsewhere
Thomas Vachuska1a989c12015-06-09 18:29:22 -0700871 function _updateLinks() {
Simon Hunt1894d792015-02-04 17:09:20 -0800872 var th = ts.theme();
873
874 link = linkG.selectAll('.link')
875 .data(network.links, function (d) { return d.key; });
876
877 // operate on existing links:
Simon Huntd5264122015-02-25 10:17:43 -0800878 link.each(function (d) {
879 // this is supposed to be an existing link, but we have observed
880 // occasions (where links are deleted and added rapidly?) where
881 // the DOM element has not been defined. So protect against that...
882 if (d.el) {
883 restyleLinkElement(d, true);
884 }
885 });
Simon Hunt1894d792015-02-04 17:09:20 -0800886
887 // operate on entering links:
888 var entering = link.enter()
889 .append('line')
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700890 .call(calcPosition)
Simon Hunt1894d792015-02-04 17:09:20 -0800891 .attr({
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700892 x1: function (d) { return d.position.x1; },
893 y1: function (d) { return d.position.y1; },
894 x2: function (d) { return d.position.x2; },
895 y2: function (d) { return d.position.y2; },
Simon Hunt1894d792015-02-04 17:09:20 -0800896 stroke: linkConfig[th].inColor,
Steven Burrows1c2a9682017-07-14 16:52:46 +0100897 'stroke-width': linkConfig.inWidth,
Simon Hunt1894d792015-02-04 17:09:20 -0800898 });
899
900 // augment links
Simon Hunta4242de2015-02-24 17:11:55 -0800901 entering.each(td3.linkEntering);
Simon Hunt1894d792015-02-04 17:09:20 -0800902
903 // operate on both existing and new links:
Steven Burrows1c2a9682017-07-14 16:52:46 +0100904 // link.each(...)
Simon Hunt1894d792015-02-04 17:09:20 -0800905
Bri Prebilic Cole80401762015-07-16 11:36:18 -0700906 // add labels for how many links are in a thick line
907 td3.applyNumLinkLabels(linkNums, numLinkLblsG);
908
Simon Hunt1894d792015-02-04 17:09:20 -0800909 // apply or remove labels
Simon Hunta4242de2015-02-24 17:11:55 -0800910 td3.applyLinkLabels();
Simon Hunt1894d792015-02-04 17:09:20 -0800911
912 // operate on exiting links:
913 link.exit()
914 .attr('stroke-dasharray', '3 3')
Simon Hunt5724fb42015-02-05 16:59:40 -0800915 .attr('stroke', linkConfig[th].outColor)
Simon Hunt1894d792015-02-04 17:09:20 -0800916 .style('opacity', 0.5)
917 .transition()
918 .duration(1500)
919 .attr({
920 'stroke-dasharray': '3 12',
Steven Burrows1c2a9682017-07-14 16:52:46 +0100921 'stroke-width': linkConfig.outWidth,
Simon Hunt1894d792015-02-04 17:09:20 -0800922 })
923 .style('opacity', 0.0)
924 .remove();
Simon Hunt1894d792015-02-04 17:09:20 -0800925 }
926
Simon Huntac4c6f72015-02-03 19:50:53 -0800927
928 // ==========================
Simon Hunt737c89f2015-01-28 12:23:19 -0800929 // force layout tick function
Simon Hunt737c89f2015-01-28 12:23:19 -0800930
Simon Hunt5724fb42015-02-05 16:59:40 -0800931 function fResume() {
Simon Huntc3c5b672015-02-20 11:32:13 -0800932 if (!tos.isOblique()) {
Simon Hunt5724fb42015-02-05 16:59:40 -0800933 force.resume();
934 }
935 }
936
937 function fStart() {
Simon Huntc3c5b672015-02-20 11:32:13 -0800938 if (!tos.isOblique()) {
Simon Hunta17fa672015-08-19 18:42:22 -0700939 if (fTimer) {
940 $timeout.cancel(fTimer);
941 }
942 fTimer = $timeout(function () {
Steven Burrows1c2a9682017-07-14 16:52:46 +0100943 $log.debug('Starting force-layout');
Simon Hunta17fa672015-08-19 18:42:22 -0700944 force.start();
945 }, 200);
Simon Hunt5724fb42015-02-05 16:59:40 -0800946 }
947 }
948
949 var tickStuff = {
950 nodeAttr: {
Simon Hunta17fa672015-08-19 18:42:22 -0700951 transform: function (d) {
952 var dx = isNaN(d.x) ? 0 : d.x,
953 dy = isNaN(d.y) ? 0 : d.y;
954 return sus.translate(dx, dy);
Steven Burrows1c2a9682017-07-14 16:52:46 +0100955 },
Simon Hunt5724fb42015-02-05 16:59:40 -0800956 },
957 linkAttr: {
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700958 x1: function (d) { return d.position.x1; },
959 y1: function (d) { return d.position.y1; },
960 x2: function (d) { return d.position.x2; },
Steven Burrows1c2a9682017-07-14 16:52:46 +0100961 y2: function (d) { return d.position.y2; },
Simon Hunt5724fb42015-02-05 16:59:40 -0800962 },
963 linkLabelAttr: {
964 transform: function (d) {
Simon Huntdc6adea2015-02-09 22:29:36 -0800965 var lnk = tms.findLinkById(d.key);
Simon Hunt5724fb42015-02-05 16:59:40 -0800966 if (lnk) {
Carmelo Casconed01eda62016-08-02 10:19:15 -0700967 return td3.transformLabel(lnk.position, d.key);
Simon Hunt5724fb42015-02-05 16:59:40 -0800968 }
Steven Burrows1c2a9682017-07-14 16:52:46 +0100969 },
970 },
Simon Hunt5724fb42015-02-05 16:59:40 -0800971 };
972
973 function tick() {
Simon Hunt3ab20282015-02-26 20:32:19 -0800974 // guard against null (which can happen when our view pages out)...
Bri Prebilic Cole8d003782015-07-31 15:33:06 -0700975 if (node && node.size()) {
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -0700976 node.attr(tickStuff.nodeAttr);
977 }
Bri Prebilic Cole8d003782015-07-31 15:33:06 -0700978 if (link && link.size()) {
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700979 link.call(calcPosition)
980 .attr(tickStuff.linkAttr);
Bri Prebilic Cole80401762015-07-16 11:36:18 -0700981 td3.applyNumLinkLabels(linkNums, numLinkLblsG);
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -0700982 }
Bri Prebilic Cole8d003782015-07-31 15:33:06 -0700983 if (linkLabel && linkLabel.size()) {
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -0700984 linkLabel.attr(tickStuff.linkLabelAttr);
985 }
Simon Hunt737c89f2015-01-28 12:23:19 -0800986 }
987
988
Simon Huntac4c6f72015-02-03 19:50:53 -0800989 // ==========================
990 // === MOUSE GESTURE HANDLERS
991
Simon Hunt205099e2015-02-07 13:12:01 -0800992 function zoomingOrPanning(ev) {
993 return ev.metaKey || ev.altKey;
Simon Hunt445e8152015-02-06 13:00:12 -0800994 }
995
996 function atDragEnd(d) {
997 // once we've finished moving, pin the node in position
998 d.fixed = true;
999 d3.select(this).classed('fixed', true);
1000 sendUpdateMeta(d);
Simon Hunt0c6b2d32015-03-26 17:46:29 -07001001 tss.clickConsumed(true);
Simon Hunt445e8152015-02-06 13:00:12 -08001002 }
1003
1004 // predicate that indicates when dragging is active
1005 function dragEnabled() {
1006 var ev = d3.event.sourceEvent;
1007 // nodeLock means we aren't allowing nodes to be dragged...
Simon Hunt205099e2015-02-07 13:12:01 -08001008 return !nodeLock && !zoomingOrPanning(ev);
Simon Hunt445e8152015-02-06 13:00:12 -08001009 }
1010
1011 // predicate that indicates when clicking is active
1012 function clickEnabled() {
1013 return true;
1014 }
Simon Hunt737c89f2015-01-28 12:23:19 -08001015
Simon Hunt8d22c4b2015-08-06 16:24:43 -07001016 // =============================================
1017 // function entry points for overlay module
Simon Huntf542d842015-02-11 16:20:33 -08001018
Simon Hunt8d22c4b2015-08-06 16:24:43 -07001019 // TODO: find an automatic way of tracking via the "showHighlights" events
Simon Hunte50829c2015-06-09 08:39:28 -07001020 var allTrafficClasses = 'primary secondary optical animated ' +
Simon Hunt21281fd2017-03-30 22:28:28 -07001021 'port-traffic-green port-traffic-yellow port-traffic-orange ' +
1022 'port-traffic-red';
Simon Huntf542d842015-02-11 16:20:33 -08001023
1024 function clearLinkTrafficStyle() {
1025 link.style('stroke-width', null)
Thomas Vachuska2b4de872021-03-30 16:31:34 -07001026 .style('stroke', null)
Simon Huntf542d842015-02-11 16:20:33 -08001027 .classed(allTrafficClasses, false);
1028 }
1029
1030 function removeLinkLabels() {
1031 network.links.forEach(function (d) {
1032 d.label = '';
1033 });
1034 }
Simon Hunt737c89f2015-01-28 12:23:19 -08001035
Simon Hunte9343f32015-10-21 18:07:46 -07001036 function clearNodeDeco() {
1037 node.selectAll('g.badge').remove();
1038 }
1039
1040 function removeNodeBadges() {
1041 network.nodes.forEach(function (d) {
1042 d.badge = null;
1043 });
1044 }
1045
Simon Hunta4242de2015-02-24 17:11:55 -08001046 function updateLinkLabelModel() {
1047 // create the backing data for showing labels..
1048 var data = [];
1049 link.each(function (d) {
1050 if (d.label) {
1051 data.push({
1052 id: 'lab-' + d.key,
1053 key: d.key,
1054 label: d.label,
Steven Burrows1c2a9682017-07-14 16:52:46 +01001055 ldata: d,
Simon Hunta4242de2015-02-24 17:11:55 -08001056 });
1057 }
1058 });
1059
1060 linkLabel = linkLabelG.selectAll('.linkLabel')
1061 .data(data, function (d) { return d.id; });
1062 }
1063
Simon Hunt737c89f2015-01-28 12:23:19 -08001064 // ==========================
Simon Huntac4c6f72015-02-03 19:50:53 -08001065 // Module definition
Simon Hunt737c89f2015-01-28 12:23:19 -08001066
Simon Huntdc6adea2015-02-09 22:29:36 -08001067 function mkModelApi(uplink) {
1068 return {
1069 projection: uplink.projection,
1070 network: network,
1071 restyleLinkElement: restyleLinkElement,
Steven Burrows1c2a9682017-07-14 16:52:46 +01001072 removeLinkElement: removeLinkElement,
Simon Huntdc6adea2015-02-09 22:29:36 -08001073 };
1074 }
1075
Simon Hunt8d22c4b2015-08-06 16:24:43 -07001076 function mkD3Api() {
Simon Hunta4242de2015-02-24 17:11:55 -08001077 return {
1078 node: function () { return node; },
1079 link: function () { return link; },
1080 linkLabel: function () { return linkLabel; },
1081 instVisible: function () { return tis.isVisible(); },
1082 posNode: tms.positionNode,
1083 showHosts: function () { return showHosts; },
1084 restyleLinkElement: restyleLinkElement,
Bri Prebilic Cole80401762015-07-16 11:36:18 -07001085 updateLinkLabelModel: updateLinkLabelModel,
Steven Burrowsf17f0ab2017-04-11 11:03:58 -07001086 linkConfig: function () { return linkConfig; },
1087 deviceScale: deviceScale,
Steven Burrows1c2a9682017-07-14 16:52:46 +01001088 linkWidthScale: linkWidthScale,
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -07001089 };
Simon Hunta4242de2015-02-24 17:11:55 -08001090 }
1091
Simon Hunt8d22c4b2015-08-06 16:24:43 -07001092 function mkSelectApi() {
Simon Hunt08f841d02015-02-10 14:39:20 -08001093 return {
1094 node: function () { return node; },
1095 zoomingOrPanning: zoomingOrPanning,
Simon Hunt0c6b2d32015-03-26 17:46:29 -07001096 updateDeviceColors: td3.updateDeviceColors,
Steven Burrows1c2a9682017-07-14 16:52:46 +01001097 deselectAllLinks: tls.deselectAllLinks,
Simon Hunt08f841d02015-02-10 14:39:20 -08001098 };
1099 }
1100
Simon Hunt8d22c4b2015-08-06 16:24:43 -07001101 function mkTrafficApi() {
1102 return {
1103 hovered: tss.hovered,
1104 somethingSelected: tss.somethingSelected,
Steven Burrows1c2a9682017-07-14 16:52:46 +01001105 selectOrder: tss.selectOrder,
Simon Hunt8d22c4b2015-08-06 16:24:43 -07001106 };
1107 }
1108
1109 function mkOverlayApi() {
Simon Huntf542d842015-02-11 16:20:33 -08001110 return {
Simon Hunte9343f32015-10-21 18:07:46 -07001111 clearNodeDeco: clearNodeDeco,
1112 removeNodeBadges: removeNodeBadges,
Simon Huntf542d842015-02-11 16:20:33 -08001113 clearLinkTrafficStyle: clearLinkTrafficStyle,
1114 removeLinkLabels: removeLinkLabels,
Simon Hunt743a8492015-08-25 16:18:19 -07001115 findLinkById: tms.findLinkById,
Simon Hunt94f7dae2015-08-26 17:40:59 -07001116 findNodeById: nodeById,
Simon Huntf542d842015-02-11 16:20:33 -08001117 updateLinks: updateLinks,
Simon Hunt743a8492015-08-25 16:18:19 -07001118 updateNodes: updateNodes,
1119 supLayers: suppressLayers,
1120 unsupNode: unsuppressNode,
Steven Burrows1c2a9682017-07-14 16:52:46 +01001121 unsupLink: unsuppressLink,
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -07001122 };
Simon Huntf542d842015-02-11 16:20:33 -08001123 }
1124
Simon Huntc3c5b672015-02-20 11:32:13 -08001125 function mkObliqueApi(uplink, fltr) {
Simon Hunt96f88c62015-02-19 17:57:25 -08001126 return {
Steven Burrows1c2a9682017-07-14 16:52:46 +01001127 force: function () { return force; },
Simon Huntc3c5b672015-02-20 11:32:13 -08001128 zoomLayer: uplink.zoomLayer,
Steven Burrows1c2a9682017-07-14 16:52:46 +01001129 nodeGBBox: function () { return nodeG.node().getBBox(); },
Simon Hunt96f88c62015-02-19 17:57:25 -08001130 node: function () { return node; },
Simon Huntc3c5b672015-02-20 11:32:13 -08001131 link: function () { return link; },
1132 linkLabel: function () { return linkLabel; },
1133 nodes: function () { return network.nodes; },
1134 tickStuff: tickStuff,
1135 nodeLock: function (b) {
1136 var old = nodeLock;
1137 nodeLock = b;
1138 return old;
1139 },
1140 opacifyMap: uplink.opacifyMap,
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -07001141 inLayer: fltr.inLayer,
Bri Prebilic Cole80401762015-07-16 11:36:18 -07001142 calcLinkPos: calcPosition,
1143 applyNumLinkLabels: function () {
1144 td3.applyNumLinkLabels(linkNums, numLinkLblsG);
Steven Burrows1c2a9682017-07-14 16:52:46 +01001145 },
Simon Hunt96f88c62015-02-19 17:57:25 -08001146 };
1147 }
1148
Simon Hunt8d22c4b2015-08-06 16:24:43 -07001149 function mkFilterApi() {
Simon Hunteb0fa052015-02-17 19:20:28 -08001150 return {
1151 node: function () { return node; },
Steven Burrows1c2a9682017-07-14 16:52:46 +01001152 link: function () { return link; },
Simon Hunteb0fa052015-02-17 19:20:28 -08001153 };
1154 }
1155
Simon Hunt9e2104c2015-02-26 10:48:59 -08001156 function mkLinkApi(svg, uplink) {
Simon Huntfb8ea1f2015-02-24 21:38:09 -08001157 return {
1158 svg: svg,
Simon Huntfb8ea1f2015-02-24 21:38:09 -08001159 zoomer: uplink.zoomer(),
1160 network: network,
Simon Hunt1a5301e2015-02-25 15:31:25 -08001161 portLabelG: function () { return portLabelG; },
Steven Burrows1c2a9682017-07-14 16:52:46 +01001162 showHosts: function () { return showHosts; },
Simon Huntfb8ea1f2015-02-24 21:38:09 -08001163 };
1164 }
1165
Simon Huntf51bf462016-06-29 16:22:57 -07001166 function updateLinksAndNodes() {
1167 updateLinks();
1168 updateNodes();
1169 }
Steven Burrowsec1f45c2016-08-08 16:14:41 +01001170
Simon Hunte2d9dc72017-08-10 15:21:04 -07001171 // invoked after the localization bundle has been received from the server
1172 function setLionBundle(bundle) {
1173 topoLion = bundle;
1174 td3.setLionBundle(bundle);
1175 fltr.setLionBundle(bundle);
1176 tls.setLionBundle(bundle);
Simon Hunt1603c692017-08-10 19:53:35 -07001177 tos.setLionBundle(bundle);
1178 tov.setLionBundle(bundle);
Simon Huntcaed0412017-08-12 13:49:17 -07001179 tss.setLionBundle(bundle);
Simon Hunte2d9dc72017-08-10 15:21:04 -07001180 }
1181
Simon Hunt737c89f2015-01-28 12:23:19 -08001182 angular.module('ovTopo')
1183 .factory('TopoForceService',
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -07001184 ['$log', '$timeout', 'FnService', 'SvgUtilService',
Simon Hunt86b7c882015-04-02 23:06:08 -07001185 'ThemeService', 'FlashService', 'WebSocketService',
Simon Hunt8d22c4b2015-08-06 16:24:43 -07001186 'TopoOverlayService', 'TopoInstService', 'TopoModelService',
Simon Hunta4242de2015-02-24 17:11:55 -08001187 'TopoD3Service', 'TopoSelectService', 'TopoTrafficService',
Simon Huntfb8ea1f2015-02-24 21:38:09 -08001188 'TopoObliqueService', 'TopoFilterService', 'TopoLinkService',
Andrea Campanella732ea832017-02-06 09:25:59 -08001189 'TopoProtectedIntentsService',
Simon Hunt737c89f2015-01-28 12:23:19 -08001190
Simon Hunt8d22c4b2015-08-06 16:24:43 -07001191 function (_$log_, _$timeout_, _fs_, _sus_, _ts_, _flash_, _wss_, _tov_,
Andrea Campanella732ea832017-02-06 09:25:59 -08001192 _tis_, _tms_, _td3_, _tss_, _tts_, _tos_, _fltr_, _tls_, _tpis_) {
Simon Hunt737c89f2015-01-28 12:23:19 -08001193 $log = _$log_;
Simon Hunt86b7c882015-04-02 23:06:08 -07001194 $timeout = _$timeout_;
Simon Hunt1894d792015-02-04 17:09:20 -08001195 fs = _fs_;
Simon Hunt737c89f2015-01-28 12:23:19 -08001196 sus = _sus_;
Simon Huntac4c6f72015-02-03 19:50:53 -08001197 ts = _ts_;
Simon Hunt5724fb42015-02-05 16:59:40 -08001198 flash = _flash_;
Simon Hunt237676b52015-03-10 19:04:26 -07001199 wss = _wss_;
Simon Hunt8d22c4b2015-08-06 16:24:43 -07001200 tov = _tov_;
Simon Huntac4c6f72015-02-03 19:50:53 -08001201 tis = _tis_;
Simon Hunt3a6eec02015-02-09 21:16:43 -08001202 tms = _tms_;
Simon Hunta4242de2015-02-24 17:11:55 -08001203 td3 = _td3_;
Simon Hunt08f841d02015-02-10 14:39:20 -08001204 tss = _tss_;
Simon Huntf542d842015-02-11 16:20:33 -08001205 tts = _tts_;
Simon Hunt96f88c62015-02-19 17:57:25 -08001206 tos = _tos_;
Simon Hunteb0fa052015-02-17 19:20:28 -08001207 fltr = _fltr_;
Simon Huntfb8ea1f2015-02-24 21:38:09 -08001208 tls = _tls_;
Andrea Campanella732ea832017-02-06 09:25:59 -08001209 tpis = _tpis_;
Simon Hunt737c89f2015-01-28 12:23:19 -08001210
Simon Huntf51bf462016-06-29 16:22:57 -07001211 ts.addListener(updateLinksAndNodes);
Simon Hunta142dd22015-02-12 22:07:51 -08001212
Simon Hunt737c89f2015-01-28 12:23:19 -08001213 // forceG is the SVG group to display the force layout in
Simon Huntdc6adea2015-02-09 22:29:36 -08001214 // uplink is the api from the main topo source file
Simon Hunt3a6eec02015-02-09 21:16:43 -08001215 // dim is the initial dimensions of the SVG as [w,h]
Simon Hunt737c89f2015-01-28 12:23:19 -08001216 // opts are, well, optional :)
Simon Hunt3ab20282015-02-26 20:32:19 -08001217 function initForce(_svg_, forceG, _uplink_, _dim_, opts) {
Simon Hunt1894d792015-02-04 17:09:20 -08001218 uplink = _uplink_;
Simon Hunt3a6eec02015-02-09 21:16:43 -08001219 dim = _dim_;
Simon Hunt3ab20282015-02-26 20:32:19 -08001220 svg = _svg_;
1221
1222 lu = network.lookup;
1223 rlk = network.revLinkToKey;
Simon Hunt3a6eec02015-02-09 21:16:43 -08001224
1225 $log.debug('initForce().. dim = ' + dim);
1226
Simon Hunt8d22c4b2015-08-06 16:24:43 -07001227 tov.setApi(mkOverlayApi(), tss);
Simon Huntdc6adea2015-02-09 22:29:36 -08001228 tms.initModel(mkModelApi(uplink), dim);
Steven Burrowsf17f0ab2017-04-11 11:03:58 -07001229 td3.initD3(mkD3Api(), uplink.zoomer());
Simon Hunt8d22c4b2015-08-06 16:24:43 -07001230 tss.initSelect(mkSelectApi());
1231 tts.initTraffic(mkTrafficApi());
Andrea Campanella732ea832017-02-06 09:25:59 -08001232 tpis.initProtectedIntents(mkTrafficApi());
Simon Huntc3c5b672015-02-20 11:32:13 -08001233 tos.initOblique(mkObliqueApi(uplink, fltr));
Simon Hunt8d22c4b2015-08-06 16:24:43 -07001234 fltr.initFilter(mkFilterApi());
Simon Hunt9e2104c2015-02-26 10:48:59 -08001235 tls.initLink(mkLinkApi(svg, uplink), td3);
Simon Hunta11b4eb2015-01-28 16:20:50 -08001236
Simon Hunt737c89f2015-01-28 12:23:19 -08001237 settings = angular.extend({}, defaultSettings, opts);
1238
1239 linkG = forceG.append('g').attr('id', 'topo-links');
1240 linkLabelG = forceG.append('g').attr('id', 'topo-linkLabels');
Bri Prebilic Cole80401762015-07-16 11:36:18 -07001241 numLinkLblsG = forceG.append('g').attr('id', 'topo-numLinkLabels');
Simon Hunt737c89f2015-01-28 12:23:19 -08001242 nodeG = forceG.append('g').attr('id', 'topo-nodes');
Simon Hunt1a5301e2015-02-25 15:31:25 -08001243 portLabelG = forceG.append('g').attr('id', 'topo-portLabels');
Simon Hunt737c89f2015-01-28 12:23:19 -08001244
1245 link = linkG.selectAll('.link');
1246 linkLabel = linkLabelG.selectAll('.linkLabel');
1247 node = nodeG.selectAll('.node');
1248
1249 force = d3.layout.force()
Simon Hunt3a6eec02015-02-09 21:16:43 -08001250 .size(dim)
Simon Hunt737c89f2015-01-28 12:23:19 -08001251 .nodes(network.nodes)
1252 .links(network.links)
1253 .gravity(settings.gravity)
1254 .friction(settings.friction)
1255 .charge(settings.charge._def_)
1256 .linkDistance(settings.linkDistance._def_)
1257 .linkStrength(settings.linkStrength._def_)
1258 .on('tick', tick);
1259
1260 drag = sus.createDragBehavior(force,
Simon Hunt08f841d02015-02-10 14:39:20 -08001261 tss.selectObject, atDragEnd, dragEnabled, clickEnabled);
Simon Hunt737c89f2015-01-28 12:23:19 -08001262 }
1263
Simon Hunt3a6eec02015-02-09 21:16:43 -08001264 function newDim(_dim_) {
1265 dim = _dim_;
1266 force.size(dim);
1267 tms.newDim(dim);
Simon Hunt737c89f2015-01-28 12:23:19 -08001268 }
1269
Simon Hunt3a6eec02015-02-09 21:16:43 -08001270 function destroyForce() {
Simon Hunt3ab20282015-02-26 20:32:19 -08001271 force.stop();
1272
Simon Huntfb8ea1f2015-02-24 21:38:09 -08001273 tls.destroyLink();
Simon Hunt96f88c62015-02-19 17:57:25 -08001274 tos.destroyOblique();
Simon Huntf542d842015-02-11 16:20:33 -08001275 tts.destroyTraffic();
Andrea Campanella732ea832017-02-06 09:25:59 -08001276 tpis.destroyProtectedIntents();
Simon Huntf542d842015-02-11 16:20:33 -08001277 tss.destroySelect();
Simon Hunta4242de2015-02-24 17:11:55 -08001278 td3.destroyD3();
Simon Huntf542d842015-02-11 16:20:33 -08001279 tms.destroyModel();
Simon Hunt8d22c4b2015-08-06 16:24:43 -07001280 // note: no need to destroy overlay service
Simon Huntf51bf462016-06-29 16:22:57 -07001281 ts.removeListener(updateLinksAndNodes);
Simon Hunt3ab20282015-02-26 20:32:19 -08001282
1283 // clean up the DOM
1284 svg.selectAll('g').remove();
1285 svg.selectAll('defs').remove();
1286
1287 // clean up internal state
1288 network.nodes = [];
1289 network.links = [];
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -07001290 network.linksByDevice = {};
Simon Hunt3ab20282015-02-26 20:32:19 -08001291 network.lookup = {};
1292 network.revLinkToKey = {};
1293
Bri Prebilic Cole80401762015-07-16 11:36:18 -07001294 linkNums = [];
1295
1296 linkG = linkLabelG = numLinkLblsG = nodeG = portLabelG = null;
Simon Hunt3ab20282015-02-26 20:32:19 -08001297 link = linkLabel = node = null;
1298 force = drag = null;
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -07001299
1300 // clean up $timeout promises
Simon Hunta17fa672015-08-19 18:42:22 -07001301 if (fTimer) {
1302 $timeout.cancel(fTimer);
1303 }
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -07001304 if (fNodesTimer) {
1305 $timeout.cancel(fNodesTimer);
1306 }
1307 if (fLinksTimer) {
1308 $timeout.cancel(fLinksTimer);
1309 }
Simon Hunt3a6eec02015-02-09 21:16:43 -08001310 }
1311
Simon Hunt737c89f2015-01-28 12:23:19 -08001312 return {
1313 initForce: initForce,
Simon Hunt3a6eec02015-02-09 21:16:43 -08001314 newDim: newDim,
1315 destroyForce: destroyForce,
Simon Huntac4c6f72015-02-03 19:50:53 -08001316
Simon Hunta4242de2015-02-24 17:11:55 -08001317 updateDeviceColors: td3.updateDeviceColors,
Simon Hunt5724fb42015-02-05 16:59:40 -08001318 toggleHosts: toggleHosts,
Simon Hunt9e2104c2015-02-26 10:48:59 -08001319 togglePorts: tls.togglePorts,
Simon Hunt5724fb42015-02-05 16:59:40 -08001320 toggleOffline: toggleOffline,
1321 cycleDeviceLabels: cycleDeviceLabels,
Simon Hunt10618f62017-06-15 19:30:52 -07001322 cycleHostLabels: cycleHostLabels,
Thomas Vachuskaa4eac6a2021-03-01 15:11:59 -08001323 cycleLinkLabels: cycleLinkLabels,
Simon Hunt445e8152015-02-06 13:00:12 -08001324 unpin: unpin,
Simon Hunta142dd22015-02-12 22:07:51 -08001325 showMastership: showMastership,
Simon Hunt86b7c882015-04-02 23:06:08 -07001326 showBadLinks: showBadLinks,
Thomas Vachuskac8c8f462021-03-01 11:22:56 -08001327 adjustNodeScale: adjustNodeScale,
1328
1329 toggleHostTextSize: toggleHostTextSize,
1330 toggleHostIconSize: toggleHostIconSize,
Simon Huntac4c6f72015-02-03 19:50:53 -08001331
Simon Huntfd7106c2016-02-09 15:05:26 -08001332 resetAllLocations: resetAllLocations,
Simon Huntac4c6f72015-02-03 19:50:53 -08001333 addDevice: addDevice,
Simon Hunt1894d792015-02-04 17:09:20 -08001334 updateDevice: updateDevice,
1335 removeDevice: removeDevice,
1336 addHost: addHost,
1337 updateHost: updateHost,
Simon Hunt95d56fd2015-11-12 11:06:44 -08001338 moveHost: moveHost,
Simon Hunt1894d792015-02-04 17:09:20 -08001339 removeHost: removeHost,
1340 addLink: addLink,
1341 updateLink: updateLink,
Simon Hunt4a6b54b2015-10-27 22:08:25 -07001342 removeLink: removeLink,
Steven Burrows1c2a9682017-07-14 16:52:46 +01001343 topoStartDone: topoStartDone,
Simon Hunte2d9dc72017-08-10 15:21:04 -07001344
1345 setLionBundle: setLionBundle,
Simon Hunt737c89f2015-01-28 12:23:19 -08001346 };
1347 }]);
1348}());