blob: 48309f09fd28f725306a3cdc3bb876450c8686b0 [file] [log] [blame]
Simon Hunt737c89f2015-01-28 12:23:19 -08001/*
2 * Copyright 2015 Open Networking Laboratory
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17/*
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 Hunt86b7c882015-04-02 23:06:08 -070026 var $log, $timeout, fs, sus, is, ts, flash, wss,
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -070027 tis, tms, td3, tss, tts, tos, fltr, tls, uplink, svg;
Simon Huntac4c6f72015-02-03 19:50:53 -080028
29 // configuration
Simon Hunt1894d792015-02-04 17:09:20 -080030 var linkConfig = {
31 light: {
32 baseColor: '#666',
33 inColor: '#66f',
Simon Hunt3a6eec02015-02-09 21:16:43 -080034 outColor: '#f00'
Simon Hunt1894d792015-02-04 17:09:20 -080035 },
36 dark: {
Simon Hunt5724fb42015-02-05 16:59:40 -080037 baseColor: '#aaa',
Simon Hunt1894d792015-02-04 17:09:20 -080038 inColor: '#66f',
Simon Hunt5724fb42015-02-05 16:59:40 -080039 outColor: '#f66'
Simon Hunt1894d792015-02-04 17:09:20 -080040 },
41 inWidth: 12,
42 outWidth: 10
43 };
44
Simon Hunt737c89f2015-01-28 12:23:19 -080045 // internal state
Simon Huntac4c6f72015-02-03 19:50:53 -080046 var settings, // merged default settings and options
Simon Hunt737c89f2015-01-28 12:23:19 -080047 force, // force layout object
48 drag, // drag behavior handler
49 network = {
50 nodes: [],
51 links: [],
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -070052 linksByDevice: {},
Simon Hunt737c89f2015-01-28 12:23:19 -080053 lookup: {},
54 revLinkToKey: {}
Simon Huntac4c6f72015-02-03 19:50:53 -080055 },
Simon Hunt3ab20282015-02-26 20:32:19 -080056 lu, // shorthand for lookup
57 rlk, // shorthand for revLinktoKey
Simon Hunta142dd22015-02-12 22:07:51 -080058 showHosts = false, // whether hosts are displayed
Simon Hunt5724fb42015-02-05 16:59:40 -080059 showOffline = true, // whether offline devices are displayed
Simon Hunt445e8152015-02-06 13:00:12 -080060 nodeLock = false, // whether nodes can be dragged or not (locked)
Thomas Vachuska1a989c12015-06-09 18:29:22 -070061 fTimer, // timer for delayed force layout
62 fNodesTimer, // timer for delayed nodes update
63 fLinksTimer, // timer for delayed links update
Simon Hunt08f841d02015-02-10 14:39:20 -080064 dim; // the dimensions of the force layout [w,h]
Simon Hunt737c89f2015-01-28 12:23:19 -080065
66 // SVG elements;
Simon Hunt1a5301e2015-02-25 15:31:25 -080067 var linkG, linkLabelG, portLabelG, nodeG;
Simon Hunt737c89f2015-01-28 12:23:19 -080068
69 // D3 selections;
70 var link, linkLabel, node;
71
72 // default settings for force layout
73 var defaultSettings = {
74 gravity: 0.4,
75 friction: 0.7,
76 charge: {
77 // note: key is node.class
78 device: -8000,
79 host: -5000,
80 _def_: -12000
81 },
82 linkDistance: {
83 // note: key is link.type
84 direct: 100,
85 optical: 120,
86 hostLink: 3,
87 _def_: 50
88 },
89 linkStrength: {
90 // note: key is link.type
91 // range: {0.0 ... 1.0}
92 //direct: 1.0,
93 //optical: 1.0,
94 //hostLink: 1.0,
95 _def_: 1.0
96 }
97 };
98
99
Simon Huntac4c6f72015-02-03 19:50:53 -0800100 // ==========================
101 // === EVENT HANDLERS
102
103 function addDevice(data) {
104 var id = data.id,
105 d;
106
Simon Hunt1894d792015-02-04 17:09:20 -0800107 uplink.showNoDevs(false);
Simon Huntac4c6f72015-02-03 19:50:53 -0800108
109 // although this is an add device event, if we already have the
110 // device, treat it as an update instead..
Simon Hunt1894d792015-02-04 17:09:20 -0800111 if (lu[id]) {
Simon Huntac4c6f72015-02-03 19:50:53 -0800112 updateDevice(data);
113 return;
114 }
115
Simon Hunt3a6eec02015-02-09 21:16:43 -0800116 d = tms.createDeviceNode(data);
Simon Huntac4c6f72015-02-03 19:50:53 -0800117 network.nodes.push(d);
Simon Hunt1894d792015-02-04 17:09:20 -0800118 lu[id] = d;
Simon Huntac4c6f72015-02-03 19:50:53 -0800119 updateNodes();
120 fStart();
121 }
122
123 function updateDevice(data) {
124 var id = data.id,
Simon Hunt1894d792015-02-04 17:09:20 -0800125 d = lu[id],
Simon Huntac4c6f72015-02-03 19:50:53 -0800126 wasOnline;
127
128 if (d) {
129 wasOnline = d.online;
130 angular.extend(d, data);
Simon Hunt3a6eec02015-02-09 21:16:43 -0800131 if (tms.positionNode(d, true)) {
Simon Hunt445e8152015-02-06 13:00:12 -0800132 sendUpdateMeta(d);
Simon Huntac4c6f72015-02-03 19:50:53 -0800133 }
134 updateNodes();
135 if (wasOnline !== d.online) {
Simon Huntdc6adea2015-02-09 22:29:36 -0800136 tms.findAttachedLinks(d.id).forEach(restyleLinkElement);
Simon Hunt5724fb42015-02-05 16:59:40 -0800137 updateOfflineVisibility(d);
Simon Huntac4c6f72015-02-03 19:50:53 -0800138 }
Simon Huntac4c6f72015-02-03 19:50:53 -0800139 }
140 }
141
Simon Hunt1894d792015-02-04 17:09:20 -0800142 function removeDevice(data) {
143 var id = data.id,
144 d = lu[id];
145 if (d) {
146 removeDeviceElement(d);
Simon Hunt1894d792015-02-04 17:09:20 -0800147 }
148 }
149
150 function addHost(data) {
151 var id = data.id,
152 d, lnk;
153
154 // although this is an add host event, if we already have the
155 // host, treat it as an update instead..
156 if (lu[id]) {
157 updateHost(data);
158 return;
159 }
160
Simon Hunt3a6eec02015-02-09 21:16:43 -0800161 d = tms.createHostNode(data);
Simon Hunt1894d792015-02-04 17:09:20 -0800162 network.nodes.push(d);
163 lu[id] = d;
Simon Hunt1894d792015-02-04 17:09:20 -0800164 updateNodes();
165
Simon Hunt3a6eec02015-02-09 21:16:43 -0800166 lnk = tms.createHostLink(data);
Simon Hunt1894d792015-02-04 17:09:20 -0800167 if (lnk) {
Simon Hunt1894d792015-02-04 17:09:20 -0800168 d.linkData = lnk; // cache ref on its host
169 network.links.push(lnk);
170 lu[d.ingress] = lnk;
171 lu[d.egress] = lnk;
172 updateLinks();
173 }
174
175 fStart();
176 }
177
178 function updateHost(data) {
179 var id = data.id,
180 d = lu[id];
181 if (d) {
182 angular.extend(d, data);
Simon Hunt3a6eec02015-02-09 21:16:43 -0800183 if (tms.positionNode(d, true)) {
Simon Hunt445e8152015-02-06 13:00:12 -0800184 sendUpdateMeta(d);
Simon Hunt1894d792015-02-04 17:09:20 -0800185 }
186 updateNodes();
Simon Hunt1894d792015-02-04 17:09:20 -0800187 }
188 }
189
190 function removeHost(data) {
191 var id = data.id,
192 d = lu[id];
193 if (d) {
194 removeHostElement(d, true);
Simon Hunt1894d792015-02-04 17:09:20 -0800195 }
196 }
197
198 function addLink(data) {
Simon Huntdc6adea2015-02-09 22:29:36 -0800199 var result = tms.findLink(data, 'add'),
Simon Hunt1894d792015-02-04 17:09:20 -0800200 bad = result.badLogic,
201 d = result.ldata;
202
203 if (bad) {
204 //logicError(bad + ': ' + link.id);
205 return;
206 }
207
208 if (d) {
209 // we already have a backing store link for src/dst nodes
210 addLinkUpdate(d, data);
211 return;
212 }
213
214 // no backing store link yet
Simon Hunt3a6eec02015-02-09 21:16:43 -0800215 d = tms.createLink(data);
Simon Hunt1894d792015-02-04 17:09:20 -0800216 if (d) {
217 network.links.push(d);
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -0700218 aggregateLink(d, data);
Simon Hunt1894d792015-02-04 17:09:20 -0800219 lu[d.key] = d;
220 updateLinks();
221 fStart();
222 }
223 }
224
225 function updateLink(data) {
Simon Huntdc6adea2015-02-09 22:29:36 -0800226 var result = tms.findLink(data, 'update'),
Simon Hunt1894d792015-02-04 17:09:20 -0800227 bad = result.badLogic;
228 if (bad) {
229 //logicError(bad + ': ' + link.id);
230 return;
231 }
232 result.updateWith(link);
233 }
234
235 function removeLink(data) {
Simon Hunta4242de2015-02-24 17:11:55 -0800236 var result = tms.findLink(data, 'remove');
237
238 if (!result.badLogic) {
239 result.removeRawLink();
Simon Hunt1894d792015-02-04 17:09:20 -0800240 }
Simon Hunt1894d792015-02-04 17:09:20 -0800241 }
242
243 // ========================
244
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -0700245 function makeNodeKey(node1, node2) {
246 return node1 + '-' + node2;
247 }
248
249 function findNodePair(key, keyRev) {
250 if (network.linksByDevice[key]) {
251 return key;
252 } else if (network.linksByDevice[keyRev]) {
253 return keyRev;
254 } else {
255 return false;
256 }
257 }
258
259 function aggregateLink(ldata, link) {
260 var key = makeNodeKey(link.src, link.dst),
261 keyRev = makeNodeKey(link.dst, link.src),
262 found = findNodePair(key, keyRev);
263
264 if (found) {
265 network.linksByDevice[found].push(ldata);
266 ldata.devicePair = found;
267 } else {
268 network.linksByDevice[key] = [ ldata ];
269 ldata.devicePair = key;
270 }
271 }
272
Simon Hunt1894d792015-02-04 17:09:20 -0800273 function addLinkUpdate(ldata, link) {
274 // add link event, but we already have the reverse link installed
275 ldata.fromTarget = link;
Simon Huntdc6adea2015-02-09 22:29:36 -0800276 rlk[link.id] = ldata.key;
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -0700277 // possible solution to el being undefined in restyleLinkElement:
278 //_updateLinks();
Simon Hunt1894d792015-02-04 17:09:20 -0800279 restyleLinkElement(ldata);
280 }
281
Simon Hunt1894d792015-02-04 17:09:20 -0800282
283 var widthRatio = 1.4,
284 linkScale = d3.scale.linear()
285 .domain([1, 12])
286 .range([widthRatio, 12 * widthRatio])
Simon Hunt5724fb42015-02-05 16:59:40 -0800287 .clamp(true),
Simon Hunt3a6eec02015-02-09 21:16:43 -0800288 allLinkTypes = 'direct indirect optical tunnel';
Simon Hunt1894d792015-02-04 17:09:20 -0800289
Simon Hunta142dd22015-02-12 22:07:51 -0800290 function restyleLinkElement(ldata, immediate) {
Simon Hunt1894d792015-02-04 17:09:20 -0800291 // this fn's job is to look at raw links and decide what svg classes
292 // need to be applied to the line element in the DOM
293 var th = ts.theme(),
294 el = ldata.el,
295 type = ldata.type(),
296 lw = ldata.linkWidth(),
Simon Hunta142dd22015-02-12 22:07:51 -0800297 online = ldata.online(),
298 delay = immediate ? 0 : 1000;
Simon Hunt1894d792015-02-04 17:09:20 -0800299
Thomas Vachuskacb5016f2015-05-18 14:11:43 -0700300 // FIXME: understand why el is sometimes undefined on addLink events...
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -0700301 // Investigated:
302 // el is undefined when it's a reverse link that is being added.
303 // updateLinks (which sets ldata.el) isn't called before this is called.
304 // Calling _updateLinks in addLinkUpdate fixes it, but there might be
305 // a more efficient way to fix it.
306 if (el && !el.empty()) {
Thomas Vachuskacb5016f2015-05-18 14:11:43 -0700307 el.classed('link', true);
308 el.classed('inactive', !online);
309 el.classed(allLinkTypes, false);
310 if (type) {
311 el.classed(type, true);
312 }
313 el.transition()
314 .duration(delay)
315 .attr('stroke-width', linkScale(lw))
316 .attr('stroke', linkConfig[th].baseColor);
Simon Hunt1894d792015-02-04 17:09:20 -0800317 }
Simon Hunt1894d792015-02-04 17:09:20 -0800318 }
319
Simon Hunt1894d792015-02-04 17:09:20 -0800320 function removeLinkElement(d) {
321 var idx = fs.find(d.key, network.links, 'key'),
322 removed;
323 if (idx >=0) {
324 // remove from links array
325 removed = network.links.splice(idx, 1);
326 // remove from lookup cache
327 delete lu[removed[0].key];
328 updateLinks();
329 fResume();
330 }
331 }
332
333 function removeHostElement(d, upd) {
334 // first, remove associated hostLink...
335 removeLinkElement(d.linkData);
336
337 // remove hostLink bindings
338 delete lu[d.ingress];
339 delete lu[d.egress];
340
341 // remove from lookup cache
342 delete lu[d.id];
343 // remove from nodes array
344 var idx = fs.find(d.id, network.nodes);
345 network.nodes.splice(idx, 1);
346
347 // remove from SVG
348 // NOTE: upd is false if we were called from removeDeviceElement()
349 if (upd) {
350 updateNodes();
351 fResume();
352 }
353 }
354
355 function removeDeviceElement(d) {
356 var id = d.id;
357 // first, remove associated hosts and links..
Simon Huntdc6adea2015-02-09 22:29:36 -0800358 tms.findAttachedHosts(id).forEach(removeHostElement);
359 tms.findAttachedLinks(id).forEach(removeLinkElement);
Simon Hunt1894d792015-02-04 17:09:20 -0800360
361 // remove from lookup cache
362 delete lu[id];
363 // remove from nodes array
364 var idx = fs.find(id, network.nodes);
365 network.nodes.splice(idx, 1);
366
367 if (!network.nodes.length) {
Simon Huntdc6adea2015-02-09 22:29:36 -0800368 uplink.showNoDevs(true);
Simon Hunt1894d792015-02-04 17:09:20 -0800369 }
370
371 // remove from SVG
372 updateNodes();
373 fResume();
374 }
375
Simon Hunt5724fb42015-02-05 16:59:40 -0800376 function updateHostVisibility() {
Simon Hunt18bf9822015-02-12 17:35:45 -0800377 sus.visible(nodeG.selectAll('.host'), showHosts);
378 sus.visible(linkG.selectAll('.hostLink'), showHosts);
Simon Hunt8eb4d3a2015-02-23 18:23:29 -0800379 sus.visible(linkLabelG.selectAll('.hostLinkLabel'), showHosts);
Simon Hunt5724fb42015-02-05 16:59:40 -0800380 }
381
382 function updateOfflineVisibility(dev) {
383 function updDev(d, show) {
Simon Hunt8eb4d3a2015-02-23 18:23:29 -0800384 var b;
Simon Hunt18bf9822015-02-12 17:35:45 -0800385 sus.visible(d.el, show);
Simon Hunt5724fb42015-02-05 16:59:40 -0800386
Simon Huntdc6adea2015-02-09 22:29:36 -0800387 tms.findAttachedLinks(d.id).forEach(function (link) {
Simon Hunt5724fb42015-02-05 16:59:40 -0800388 b = show && ((link.type() !== 'hostLink') || showHosts);
Simon Hunt18bf9822015-02-12 17:35:45 -0800389 sus.visible(link.el, b);
Simon Hunt5724fb42015-02-05 16:59:40 -0800390 });
Simon Huntdc6adea2015-02-09 22:29:36 -0800391 tms.findAttachedHosts(d.id).forEach(function (host) {
Simon Hunt5724fb42015-02-05 16:59:40 -0800392 b = show && showHosts;
Simon Hunt18bf9822015-02-12 17:35:45 -0800393 sus.visible(host.el, b);
Simon Hunt5724fb42015-02-05 16:59:40 -0800394 });
395 }
396
397 if (dev) {
398 // updating a specific device that just toggled off/on-line
399 updDev(dev, dev.online || showOffline);
400 } else {
401 // updating all offline devices
Simon Huntdc6adea2015-02-09 22:29:36 -0800402 tms.findDevices(true).forEach(function (d) {
Simon Hunt5724fb42015-02-05 16:59:40 -0800403 updDev(d, showOffline);
404 });
405 }
406 }
407
Simon Hunt1894d792015-02-04 17:09:20 -0800408
Simon Hunt445e8152015-02-06 13:00:12 -0800409 function sendUpdateMeta(d, clearPos) {
Simon Huntac4c6f72015-02-03 19:50:53 -0800410 var metaUi = {},
411 ll;
412
Simon Hunt445e8152015-02-06 13:00:12 -0800413 // if we are not clearing the position data (unpinning),
414 // attach the x, y, longitude, latitude...
415 if (!clearPos) {
Simon Hunt3a6eec02015-02-09 21:16:43 -0800416 ll = tms.lngLatFromCoord([d.x, d.y]);
Simon Huntdc6adea2015-02-09 22:29:36 -0800417 metaUi = {x: d.x, y: d.y, lng: ll[0], lat: ll[1]};
Simon Hunt1894d792015-02-04 17:09:20 -0800418 }
419 d.metaUi = metaUi;
Simon Hunt237676b52015-03-10 19:04:26 -0700420 wss.sendEvent('updateMeta', {
Simon Hunt1894d792015-02-04 17:09:20 -0800421 id: d.id,
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -0700422 class: d.class,
Simon Hunt1894d792015-02-04 17:09:20 -0800423 memento: metaUi
424 });
Simon Huntac4c6f72015-02-03 19:50:53 -0800425 }
426
Simon Hunt1894d792015-02-04 17:09:20 -0800427
Simon Huntac4c6f72015-02-03 19:50:53 -0800428 function mkSvgClass(d) {
429 return d.fixed ? d.svgClass + ' fixed' : d.svgClass;
430 }
431
Simon Hunt5724fb42015-02-05 16:59:40 -0800432 function vis(b) {
433 return b ? 'visible' : 'hidden';
434 }
435
Simon Huntfcbde892015-04-16 12:05:28 -0700436 function toggleHosts(x) {
437 var kev = (x === 'keyev'),
438 on = kev ? !showHosts : !!x;
439
440 showHosts = on;
Simon Hunt5724fb42015-02-05 16:59:40 -0800441 updateHostVisibility();
Simon Huntfcbde892015-04-16 12:05:28 -0700442 flash.flash('Hosts ' + vis(on));
443 return on;
Simon Hunt5724fb42015-02-05 16:59:40 -0800444 }
445
Simon Huntfcbde892015-04-16 12:05:28 -0700446 function toggleOffline(x) {
447 var kev = (x === 'keyev'),
448 on = kev ? !showOffline : !!x;
449
450 showOffline = on;
Simon Hunt5724fb42015-02-05 16:59:40 -0800451 updateOfflineVisibility();
Simon Huntfcbde892015-04-16 12:05:28 -0700452 flash.flash('Offline devices ' + vis(on));
453 return on;
Simon Hunt5724fb42015-02-05 16:59:40 -0800454 }
455
456 function cycleDeviceLabels() {
Bri Prebilic Cole9cf1a8d2015-04-21 13:15:29 -0700457 flash.flash(td3.incDevLabIndex());
Simon Huntdc6adea2015-02-09 22:29:36 -0800458 tms.findDevices().forEach(function (d) {
Simon Hunta4242de2015-02-24 17:11:55 -0800459 td3.updateDeviceLabel(d);
Simon Hunt1c367112015-02-05 18:02:46 -0800460 });
Simon Hunt5724fb42015-02-05 16:59:40 -0800461 }
462
Simon Hunt445e8152015-02-06 13:00:12 -0800463 function unpin() {
Simon Hunt08f841d02015-02-10 14:39:20 -0800464 var hov = tss.hovered();
465 if (hov) {
466 sendUpdateMeta(hov, true);
467 hov.fixed = false;
468 hov.el.classed('fixed', false);
Simon Hunt445e8152015-02-06 13:00:12 -0800469 fResume();
470 }
471 }
472
Simon Hunta142dd22015-02-12 22:07:51 -0800473 function showMastership(masterId) {
474 if (!masterId) {
475 restoreLayerState();
476 } else {
477 showMastershipFor(masterId);
478 }
479 }
480
481 function restoreLayerState() {
482 // NOTE: this level of indirection required, for when we have
483 // the layer filter functionality re-implemented
484 suppressLayers(false);
485 }
486
487 function showMastershipFor(id) {
488 suppressLayers(true);
489 node.each(function (n) {
490 if (n.master === id) {
491 n.el.classed('suppressed', false);
492 }
493 });
494 }
495
496 function suppressLayers(b) {
497 node.classed('suppressed', b);
498 link.classed('suppressed', b);
499// d3.selectAll('svg .port').classed('inactive', b);
500// d3.selectAll('svg .portText').classed('inactive', b);
501 }
Simon Hunt445e8152015-02-06 13:00:12 -0800502
Simon Hunt86b7c882015-04-02 23:06:08 -0700503 function showBadLinks() {
504 var badLinks = tms.findBadLinks();
505 flash.flash('Bad Links: ' + badLinks.length);
506 $log.debug('Bad Link List (' + badLinks.length + '):');
507 badLinks.forEach(function (d) {
508 $log.debug('bad link: (' + d.bad + ') ' + d.key, d);
509 if (d.el) {
510 d.el.attr('stroke-width', linkScale(2.8))
511 .attr('stroke', 'red');
512 }
513 });
514 // back to normal after 2 seconds...
515 $timeout(updateLinks, 2000);
516 }
517
Simon Hunt5724fb42015-02-05 16:59:40 -0800518 // ==========================================
519
Simon Huntac4c6f72015-02-03 19:50:53 -0800520 function updateNodes() {
Thomas Vachuska1a989c12015-06-09 18:29:22 -0700521 if (fNodesTimer) {
522 $timeout.cancel(fNodesTimer);
523 }
524 fNodesTimer = $timeout(_updateNodes, 150);
525 }
526
527 function _updateNodes() {
Simon Hunt1894d792015-02-04 17:09:20 -0800528 // select all the nodes in the layout:
Simon Huntac4c6f72015-02-03 19:50:53 -0800529 node = nodeG.selectAll('.node')
530 .data(network.nodes, function (d) { return d.id; });
531
Simon Hunt1894d792015-02-04 17:09:20 -0800532 // operate on existing nodes:
Simon Hunta4242de2015-02-24 17:11:55 -0800533 node.filter('.device').each(td3.deviceExisting);
534 node.filter('.host').each(td3.hostExisting);
Simon Huntac4c6f72015-02-03 19:50:53 -0800535
536 // operate on entering nodes:
537 var entering = node.enter()
538 .append('g')
539 .attr({
540 id: function (d) { return sus.safeId(d.id); },
541 class: mkSvgClass,
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -0700542 transform: function (d) {
543 // sometimes says d.x and d.y are NaN?
544 // I have a feeling it's a timing issue
545 return sus.translate(d.x, d.y);
546 },
Simon Huntac4c6f72015-02-03 19:50:53 -0800547 opacity: 0
548 })
549 .call(drag)
Simon Hunt08f841d02015-02-10 14:39:20 -0800550 .on('mouseover', tss.nodeMouseOver)
551 .on('mouseout', tss.nodeMouseOut)
Simon Huntac4c6f72015-02-03 19:50:53 -0800552 .transition()
553 .attr('opacity', 1);
554
Simon Hunt1894d792015-02-04 17:09:20 -0800555 // augment entering nodes:
Simon Hunta4242de2015-02-24 17:11:55 -0800556 entering.filter('.device').each(td3.deviceEnter);
557 entering.filter('.host').each(td3.hostEnter);
Simon Huntac4c6f72015-02-03 19:50:53 -0800558
Simon Hunt51056592015-02-03 21:48:07 -0800559 // operate on both existing and new nodes:
Simon Hunta4242de2015-02-24 17:11:55 -0800560 td3.updateDeviceColors();
Simon Huntac4c6f72015-02-03 19:50:53 -0800561
562 // operate on exiting nodes:
563 // Note that the node is removed after 2 seconds.
564 // Sub element animations should be shorter than 2 seconds.
565 var exiting = node.exit()
566 .transition()
567 .duration(2000)
568 .style('opacity', 0)
569 .remove();
570
Simon Hunt1894d792015-02-04 17:09:20 -0800571 // exiting node specifics:
Simon Hunta4242de2015-02-24 17:11:55 -0800572 exiting.filter('.host').each(td3.hostExit);
573 exiting.filter('.device').each(td3.deviceExit);
Simon Huntac4c6f72015-02-03 19:50:53 -0800574 }
575
Simon Hunt51056592015-02-03 21:48:07 -0800576 // ==========================
Simon Hunt1894d792015-02-04 17:09:20 -0800577
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700578 function getDefaultPos(link) {
579 return {
580 x1: link.source.x,
581 y1: link.source.y,
582 x2: link.target.x,
583 y2: link.target.y
584 };
585 }
586
587 // returns amount of adjustment along the normal for given link
588 function amt(numLinks, linkIdx) {
589 var gap = 6;
590 return (linkIdx - ((numLinks - 1) / 2)) * gap;
591 }
592
593 function calcMovement(d, amt, flipped) {
594 var pos = getDefaultPos(d),
595 mult = flipped ? -amt : amt,
596 dx = pos.x2 - pos.x1,
597 dy = pos.y2 - pos.y1,
598 length = Math.sqrt((dx * dx) + (dy * dy));
599
600 return {
601 x1: pos.x1 + (mult * dy / length),
602 y1: pos.y1 + (mult * -dx / length),
603 x2: pos.x2 + (mult * dy / length),
604 y2: pos.y2 + (mult * -dx / length)
605 };
606 }
607
608 function calcPosition() {
609 var lines = this,
610 linkSrcId;
611 lines.each(function (d) {
612 if (d.type() === 'hostLink') {
613 d.position = getDefaultPos(d);
614 }
615 });
616
617 function normalizeLinkSrc(link) {
618 // ensure source device is consistent across set of links
619 // temporary measure until link modeling is refactored
620 if (!linkSrcId) {
621 linkSrcId = link.source.id;
622 return false;
623 }
624
625 return link.source.id !== linkSrcId;
626 }
627
628 angular.forEach(network.linksByDevice, function (linkArr) {
629 var numLinks = linkArr.length,
630 link;
631
632 if (numLinks === 1) {
633 link = linkArr[0];
634 link.position = getDefaultPos(link);
635 } else if (numLinks >= 5) {
636 // this code is inefficient, in the future the way links
637 // are modeled will be changed
638 angular.forEach(linkArr, function (link) {
639 link.position = getDefaultPos(link);
640 link.position.multiLink = true;
641 });
642 } else {
643 // calculate position of links
644 linkSrcId = null;
645 angular.forEach(linkArr, function (link, index) {
646 var offsetAmt = amt(numLinks, index),
647 needToFlip = normalizeLinkSrc(link);
648 link.position = calcMovement(link, offsetAmt, needToFlip);
649 });
650 }
651 });
652 }
653
Simon Hunt1894d792015-02-04 17:09:20 -0800654 function updateLinks() {
Thomas Vachuska1a989c12015-06-09 18:29:22 -0700655 if (fLinksTimer) {
656 $timeout.cancel(fLinksTimer);
657 }
658 fLinksTimer = $timeout(_updateLinks, 150);
659 }
660
661 function _updateLinks() {
Simon Hunt1894d792015-02-04 17:09:20 -0800662 var th = ts.theme();
663
664 link = linkG.selectAll('.link')
665 .data(network.links, function (d) { return d.key; });
666
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -0700667 // This seems to do nothing? I've only triggered it on timeout errors
668 // when adding links, link var is empty because there aren't any links
669 // when removing links, link var is empty already
Simon Hunt1894d792015-02-04 17:09:20 -0800670 // operate on existing links:
Simon Huntd5264122015-02-25 10:17:43 -0800671 link.each(function (d) {
672 // this is supposed to be an existing link, but we have observed
673 // occasions (where links are deleted and added rapidly?) where
674 // the DOM element has not been defined. So protect against that...
675 if (d.el) {
676 restyleLinkElement(d, true);
677 }
678 });
Simon Hunt1894d792015-02-04 17:09:20 -0800679
680 // operate on entering links:
681 var entering = link.enter()
682 .append('line')
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700683 .call(calcPosition)
Simon Hunt1894d792015-02-04 17:09:20 -0800684 .attr({
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700685 x1: function (d) { return d.position.x1; },
686 y1: function (d) { return d.position.y1; },
687 x2: function (d) { return d.position.x2; },
688 y2: function (d) { return d.position.y2; },
Simon Hunt1894d792015-02-04 17:09:20 -0800689 stroke: linkConfig[th].inColor,
690 'stroke-width': linkConfig.inWidth
691 });
692
693 // augment links
Simon Hunta4242de2015-02-24 17:11:55 -0800694 entering.each(td3.linkEntering);
Simon Hunt1894d792015-02-04 17:09:20 -0800695
696 // operate on both existing and new links:
697 //link.each(...)
698
699 // apply or remove labels
Simon Hunta4242de2015-02-24 17:11:55 -0800700 td3.applyLinkLabels();
Simon Hunt1894d792015-02-04 17:09:20 -0800701
702 // operate on exiting links:
703 link.exit()
704 .attr('stroke-dasharray', '3 3')
Simon Hunt5724fb42015-02-05 16:59:40 -0800705 .attr('stroke', linkConfig[th].outColor)
Simon Hunt1894d792015-02-04 17:09:20 -0800706 .style('opacity', 0.5)
707 .transition()
708 .duration(1500)
709 .attr({
710 'stroke-dasharray': '3 12',
Simon Hunt1894d792015-02-04 17:09:20 -0800711 'stroke-width': linkConfig.outWidth
712 })
713 .style('opacity', 0.0)
714 .remove();
Simon Hunt1894d792015-02-04 17:09:20 -0800715 }
716
Simon Huntac4c6f72015-02-03 19:50:53 -0800717
718 // ==========================
Simon Hunt737c89f2015-01-28 12:23:19 -0800719 // force layout tick function
Simon Hunt737c89f2015-01-28 12:23:19 -0800720
Simon Hunt5724fb42015-02-05 16:59:40 -0800721 function fResume() {
Simon Huntc3c5b672015-02-20 11:32:13 -0800722 if (!tos.isOblique()) {
Simon Hunt5724fb42015-02-05 16:59:40 -0800723 force.resume();
724 }
725 }
726
727 function fStart() {
Simon Huntc3c5b672015-02-20 11:32:13 -0800728 if (!tos.isOblique()) {
Thomas Vachuska1a989c12015-06-09 18:29:22 -0700729 if (fTimer) {
730 $timeout.cancel(fTimer);
731 }
732 fTimer = $timeout(function () {
733 $log.debug("Starting force-layout");
734 force.start();
735 }, 200);
Simon Hunt5724fb42015-02-05 16:59:40 -0800736 }
737 }
738
739 var tickStuff = {
740 nodeAttr: {
741 transform: function (d) { return sus.translate(d.x, d.y); }
742 },
743 linkAttr: {
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700744 x1: function (d) { return d.position.x1; },
745 y1: function (d) { return d.position.y1; },
746 x2: function (d) { return d.position.x2; },
747 y2: function (d) { return d.position.y2; }
Simon Hunt5724fb42015-02-05 16:59:40 -0800748 },
749 linkLabelAttr: {
750 transform: function (d) {
Simon Huntdc6adea2015-02-09 22:29:36 -0800751 var lnk = tms.findLinkById(d.key);
Simon Hunt5724fb42015-02-05 16:59:40 -0800752 if (lnk) {
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700753 return td3.transformLabel(lnk.position);
Simon Hunt5724fb42015-02-05 16:59:40 -0800754 }
755 }
756 }
757 };
758
759 function tick() {
Simon Hunt3ab20282015-02-26 20:32:19 -0800760 // guard against null (which can happen when our view pages out)...
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -0700761 if (node) {
762 node.attr(tickStuff.nodeAttr);
763 }
764 if (link) {
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700765 link.call(calcPosition)
766 .attr(tickStuff.linkAttr);
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -0700767 }
768 if (linkLabel) {
769 linkLabel.attr(tickStuff.linkLabelAttr);
770 }
Simon Hunt737c89f2015-01-28 12:23:19 -0800771 }
772
773
Simon Huntac4c6f72015-02-03 19:50:53 -0800774 // ==========================
775 // === MOUSE GESTURE HANDLERS
776
Simon Hunt205099e2015-02-07 13:12:01 -0800777 function zoomingOrPanning(ev) {
778 return ev.metaKey || ev.altKey;
Simon Hunt445e8152015-02-06 13:00:12 -0800779 }
780
781 function atDragEnd(d) {
782 // once we've finished moving, pin the node in position
783 d.fixed = true;
784 d3.select(this).classed('fixed', true);
785 sendUpdateMeta(d);
Simon Hunt0c6b2d32015-03-26 17:46:29 -0700786 tss.clickConsumed(true);
Simon Hunt445e8152015-02-06 13:00:12 -0800787 }
788
789 // predicate that indicates when dragging is active
790 function dragEnabled() {
791 var ev = d3.event.sourceEvent;
792 // nodeLock means we aren't allowing nodes to be dragged...
Simon Hunt205099e2015-02-07 13:12:01 -0800793 return !nodeLock && !zoomingOrPanning(ev);
Simon Hunt445e8152015-02-06 13:00:12 -0800794 }
795
796 // predicate that indicates when clicking is active
797 function clickEnabled() {
798 return true;
799 }
Simon Hunt737c89f2015-01-28 12:23:19 -0800800
Simon Huntf542d842015-02-11 16:20:33 -0800801 // ==========================
802 // function entry points for traffic module
803
Simon Hunte50829c2015-06-09 08:39:28 -0700804 var allTrafficClasses = 'primary secondary optical animated ' +
805 'port-traffic-Kbps port-traffic-Mbps port-traffic-Gbps ' +
806 'port-traffic-Gbps-choked';
Simon Huntf542d842015-02-11 16:20:33 -0800807
808 function clearLinkTrafficStyle() {
809 link.style('stroke-width', null)
810 .classed(allTrafficClasses, false);
811 }
812
813 function removeLinkLabels() {
814 network.links.forEach(function (d) {
815 d.label = '';
816 });
817 }
Simon Hunt737c89f2015-01-28 12:23:19 -0800818
Simon Hunta4242de2015-02-24 17:11:55 -0800819 function updateLinkLabelModel() {
820 // create the backing data for showing labels..
821 var data = [];
822 link.each(function (d) {
823 if (d.label) {
824 data.push({
825 id: 'lab-' + d.key,
826 key: d.key,
827 label: d.label,
828 ldata: d
829 });
830 }
831 });
832
833 linkLabel = linkLabelG.selectAll('.linkLabel')
834 .data(data, function (d) { return d.id; });
835 }
836
Simon Hunt737c89f2015-01-28 12:23:19 -0800837 // ==========================
Simon Huntac4c6f72015-02-03 19:50:53 -0800838 // Module definition
Simon Hunt737c89f2015-01-28 12:23:19 -0800839
Simon Huntdc6adea2015-02-09 22:29:36 -0800840 function mkModelApi(uplink) {
841 return {
842 projection: uplink.projection,
843 network: network,
844 restyleLinkElement: restyleLinkElement,
845 removeLinkElement: removeLinkElement
846 };
847 }
848
Simon Hunta4242de2015-02-24 17:11:55 -0800849 function mkD3Api(uplink) {
850 return {
851 node: function () { return node; },
852 link: function () { return link; },
853 linkLabel: function () { return linkLabel; },
854 instVisible: function () { return tis.isVisible(); },
855 posNode: tms.positionNode,
856 showHosts: function () { return showHosts; },
857 restyleLinkElement: restyleLinkElement,
858 updateLinkLabelModel: updateLinkLabelModel
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -0700859 };
Simon Hunta4242de2015-02-24 17:11:55 -0800860 }
861
Simon Hunt08f841d02015-02-10 14:39:20 -0800862 function mkSelectApi(uplink) {
863 return {
864 node: function () { return node; },
865 zoomingOrPanning: zoomingOrPanning,
Simon Hunt0c6b2d32015-03-26 17:46:29 -0700866 updateDeviceColors: td3.updateDeviceColors,
867 deselectLink: tls.deselectLink
Simon Hunt08f841d02015-02-10 14:39:20 -0800868 };
869 }
870
Simon Huntf542d842015-02-11 16:20:33 -0800871 function mkTrafficApi(uplink) {
872 return {
873 clearLinkTrafficStyle: clearLinkTrafficStyle,
874 removeLinkLabels: removeLinkLabels,
875 updateLinks: updateLinks,
876 findLinkById: tms.findLinkById,
877 hovered: tss.hovered,
878 validateSelectionContext: tss.validateSelectionContext,
Simon Hunt237676b52015-03-10 19:04:26 -0700879 selectOrder: tss.selectOrder
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -0700880 };
Simon Huntf542d842015-02-11 16:20:33 -0800881 }
882
Simon Huntc3c5b672015-02-20 11:32:13 -0800883 function mkObliqueApi(uplink, fltr) {
Simon Hunt96f88c62015-02-19 17:57:25 -0800884 return {
Simon Huntc3c5b672015-02-20 11:32:13 -0800885 force: function() { return force; },
886 zoomLayer: uplink.zoomLayer,
887 nodeGBBox: function() { return nodeG.node().getBBox(); },
Simon Hunt96f88c62015-02-19 17:57:25 -0800888 node: function () { return node; },
Simon Huntc3c5b672015-02-20 11:32:13 -0800889 link: function () { return link; },
890 linkLabel: function () { return linkLabel; },
891 nodes: function () { return network.nodes; },
892 tickStuff: tickStuff,
893 nodeLock: function (b) {
894 var old = nodeLock;
895 nodeLock = b;
896 return old;
897 },
898 opacifyMap: uplink.opacifyMap,
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700899 inLayer: fltr.inLayer,
900 calcLinkPos: calcPosition
Simon Hunt96f88c62015-02-19 17:57:25 -0800901 };
902 }
903
Simon Hunteb0fa052015-02-17 19:20:28 -0800904 function mkFilterApi(uplink) {
905 return {
906 node: function () { return node; },
907 link: function () { return link; }
908 };
909 }
910
Simon Hunt9e2104c2015-02-26 10:48:59 -0800911 function mkLinkApi(svg, uplink) {
Simon Huntfb8ea1f2015-02-24 21:38:09 -0800912 return {
913 svg: svg,
Simon Huntfb8ea1f2015-02-24 21:38:09 -0800914 zoomer: uplink.zoomer(),
915 network: network,
Simon Hunt1a5301e2015-02-25 15:31:25 -0800916 portLabelG: function () { return portLabelG; },
Simon Huntfb8ea1f2015-02-24 21:38:09 -0800917 showHosts: function () { return showHosts; }
918 };
919 }
920
Simon Hunt737c89f2015-01-28 12:23:19 -0800921 angular.module('ovTopo')
922 .factory('TopoForceService',
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -0700923 ['$log', '$timeout', 'FnService', 'SvgUtilService',
Simon Hunt86b7c882015-04-02 23:06:08 -0700924 'ThemeService', 'FlashService', 'WebSocketService',
Simon Hunt237676b52015-03-10 19:04:26 -0700925 'TopoInstService', 'TopoModelService',
Simon Hunta4242de2015-02-24 17:11:55 -0800926 'TopoD3Service', 'TopoSelectService', 'TopoTrafficService',
Simon Huntfb8ea1f2015-02-24 21:38:09 -0800927 'TopoObliqueService', 'TopoFilterService', 'TopoLinkService',
Simon Hunt737c89f2015-01-28 12:23:19 -0800928
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -0700929 function (_$log_, _$timeout_, _fs_, _sus_, _ts_, _flash_, _wss_,
Simon Huntfb8ea1f2015-02-24 21:38:09 -0800930 _tis_, _tms_, _td3_, _tss_, _tts_, _tos_, _fltr_, _tls_) {
Simon Hunt737c89f2015-01-28 12:23:19 -0800931 $log = _$log_;
Simon Hunt86b7c882015-04-02 23:06:08 -0700932 $timeout = _$timeout_;
Simon Hunt1894d792015-02-04 17:09:20 -0800933 fs = _fs_;
Simon Hunt737c89f2015-01-28 12:23:19 -0800934 sus = _sus_;
Simon Huntac4c6f72015-02-03 19:50:53 -0800935 ts = _ts_;
Simon Hunt5724fb42015-02-05 16:59:40 -0800936 flash = _flash_;
Simon Hunt237676b52015-03-10 19:04:26 -0700937 wss = _wss_;
Simon Huntac4c6f72015-02-03 19:50:53 -0800938 tis = _tis_;
Simon Hunt3a6eec02015-02-09 21:16:43 -0800939 tms = _tms_;
Simon Hunta4242de2015-02-24 17:11:55 -0800940 td3 = _td3_;
Simon Hunt08f841d02015-02-10 14:39:20 -0800941 tss = _tss_;
Simon Huntf542d842015-02-11 16:20:33 -0800942 tts = _tts_;
Simon Hunt96f88c62015-02-19 17:57:25 -0800943 tos = _tos_;
Simon Hunteb0fa052015-02-17 19:20:28 -0800944 fltr = _fltr_;
Simon Huntfb8ea1f2015-02-24 21:38:09 -0800945 tls = _tls_;
Simon Hunt737c89f2015-01-28 12:23:19 -0800946
Simon Hunta142dd22015-02-12 22:07:51 -0800947 var themeListener = ts.addListener(function () {
948 updateLinks();
949 updateNodes();
950 });
951
Simon Hunt737c89f2015-01-28 12:23:19 -0800952 // forceG is the SVG group to display the force layout in
Simon Huntdc6adea2015-02-09 22:29:36 -0800953 // uplink is the api from the main topo source file
Simon Hunt3a6eec02015-02-09 21:16:43 -0800954 // dim is the initial dimensions of the SVG as [w,h]
Simon Hunt737c89f2015-01-28 12:23:19 -0800955 // opts are, well, optional :)
Simon Hunt3ab20282015-02-26 20:32:19 -0800956 function initForce(_svg_, forceG, _uplink_, _dim_, opts) {
Simon Hunt1894d792015-02-04 17:09:20 -0800957 uplink = _uplink_;
Simon Hunt3a6eec02015-02-09 21:16:43 -0800958 dim = _dim_;
Simon Hunt3ab20282015-02-26 20:32:19 -0800959 svg = _svg_;
960
961 lu = network.lookup;
962 rlk = network.revLinkToKey;
Simon Hunt3a6eec02015-02-09 21:16:43 -0800963
964 $log.debug('initForce().. dim = ' + dim);
965
Simon Huntdc6adea2015-02-09 22:29:36 -0800966 tms.initModel(mkModelApi(uplink), dim);
Simon Hunta4242de2015-02-24 17:11:55 -0800967 td3.initD3(mkD3Api(uplink));
Simon Hunt08f841d02015-02-10 14:39:20 -0800968 tss.initSelect(mkSelectApi(uplink));
Simon Huntf542d842015-02-11 16:20:33 -0800969 tts.initTraffic(mkTrafficApi(uplink));
Simon Huntc3c5b672015-02-20 11:32:13 -0800970 tos.initOblique(mkObliqueApi(uplink, fltr));
Bri Prebilic Coleb5f2b152015-04-07 14:58:09 -0700971 fltr.initFilter(mkFilterApi(uplink));
Simon Hunt9e2104c2015-02-26 10:48:59 -0800972 tls.initLink(mkLinkApi(svg, uplink), td3);
Simon Hunta11b4eb2015-01-28 16:20:50 -0800973
Simon Hunt737c89f2015-01-28 12:23:19 -0800974 settings = angular.extend({}, defaultSettings, opts);
975
976 linkG = forceG.append('g').attr('id', 'topo-links');
977 linkLabelG = forceG.append('g').attr('id', 'topo-linkLabels');
978 nodeG = forceG.append('g').attr('id', 'topo-nodes');
Simon Hunt1a5301e2015-02-25 15:31:25 -0800979 portLabelG = forceG.append('g').attr('id', 'topo-portLabels');
Simon Hunt737c89f2015-01-28 12:23:19 -0800980
981 link = linkG.selectAll('.link');
982 linkLabel = linkLabelG.selectAll('.linkLabel');
983 node = nodeG.selectAll('.node');
984
985 force = d3.layout.force()
Simon Hunt3a6eec02015-02-09 21:16:43 -0800986 .size(dim)
Simon Hunt737c89f2015-01-28 12:23:19 -0800987 .nodes(network.nodes)
988 .links(network.links)
989 .gravity(settings.gravity)
990 .friction(settings.friction)
991 .charge(settings.charge._def_)
992 .linkDistance(settings.linkDistance._def_)
993 .linkStrength(settings.linkStrength._def_)
994 .on('tick', tick);
995
996 drag = sus.createDragBehavior(force,
Simon Hunt08f841d02015-02-10 14:39:20 -0800997 tss.selectObject, atDragEnd, dragEnabled, clickEnabled);
Simon Hunt737c89f2015-01-28 12:23:19 -0800998 }
999
Simon Hunt3a6eec02015-02-09 21:16:43 -08001000 function newDim(_dim_) {
1001 dim = _dim_;
1002 force.size(dim);
1003 tms.newDim(dim);
Simon Hunt737c89f2015-01-28 12:23:19 -08001004 }
1005
Simon Hunt3a6eec02015-02-09 21:16:43 -08001006 function destroyForce() {
Simon Hunt3ab20282015-02-26 20:32:19 -08001007 force.stop();
1008
Simon Huntfb8ea1f2015-02-24 21:38:09 -08001009 tls.destroyLink();
Simon Hunt96f88c62015-02-19 17:57:25 -08001010 tos.destroyOblique();
Simon Huntf542d842015-02-11 16:20:33 -08001011 tts.destroyTraffic();
1012 tss.destroySelect();
Simon Hunta4242de2015-02-24 17:11:55 -08001013 td3.destroyD3();
Simon Huntf542d842015-02-11 16:20:33 -08001014 tms.destroyModel();
Simon Hunta142dd22015-02-12 22:07:51 -08001015 ts.removeListener(themeListener);
1016 themeListener = null;
Simon Hunt3ab20282015-02-26 20:32:19 -08001017
1018 // clean up the DOM
1019 svg.selectAll('g').remove();
1020 svg.selectAll('defs').remove();
1021
1022 // clean up internal state
1023 network.nodes = [];
1024 network.links = [];
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -07001025 network.linksByDevice = {};
Simon Hunt3ab20282015-02-26 20:32:19 -08001026 network.lookup = {};
1027 network.revLinkToKey = {};
1028
1029 linkG = linkLabelG = nodeG = portLabelG = null;
1030 link = linkLabel = node = null;
1031 force = drag = null;
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -07001032
1033 // clean up $timeout promises
1034 if (fTimer) {
1035 $timeout.cancel(fTimer);
1036 }
1037 if (fNodesTimer) {
1038 $timeout.cancel(fNodesTimer);
1039 }
1040 if (fLinksTimer) {
1041 $timeout.cancel(fLinksTimer);
1042 }
Simon Hunt3a6eec02015-02-09 21:16:43 -08001043 }
1044
Simon Hunt737c89f2015-01-28 12:23:19 -08001045 return {
1046 initForce: initForce,
Simon Hunt3a6eec02015-02-09 21:16:43 -08001047 newDim: newDim,
1048 destroyForce: destroyForce,
Simon Huntac4c6f72015-02-03 19:50:53 -08001049
Simon Hunta4242de2015-02-24 17:11:55 -08001050 updateDeviceColors: td3.updateDeviceColors,
Simon Hunt5724fb42015-02-05 16:59:40 -08001051 toggleHosts: toggleHosts,
Simon Hunt9e2104c2015-02-26 10:48:59 -08001052 togglePorts: tls.togglePorts,
Simon Hunt5724fb42015-02-05 16:59:40 -08001053 toggleOffline: toggleOffline,
1054 cycleDeviceLabels: cycleDeviceLabels,
Simon Hunt445e8152015-02-06 13:00:12 -08001055 unpin: unpin,
Simon Hunta142dd22015-02-12 22:07:51 -08001056 showMastership: showMastership,
Simon Hunt86b7c882015-04-02 23:06:08 -07001057 showBadLinks: showBadLinks,
Simon Huntac4c6f72015-02-03 19:50:53 -08001058
1059 addDevice: addDevice,
Simon Hunt1894d792015-02-04 17:09:20 -08001060 updateDevice: updateDevice,
1061 removeDevice: removeDevice,
1062 addHost: addHost,
1063 updateHost: updateHost,
1064 removeHost: removeHost,
1065 addLink: addLink,
1066 updateLink: updateLink,
1067 removeLink: removeLink
Simon Hunt737c89f2015-01-28 12:23:19 -08001068 };
1069 }]);
1070}());