blob: 5fb6841ae7fbb33a580227c8c2f99267d48fef05 [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
Bri Prebilic Cole80401762015-07-16 11:36:18 -070064 dim, // the dimensions of the force layout [w,h]
65 linkNums = []; // array of link number labels
Simon Hunt737c89f2015-01-28 12:23:19 -080066
67 // SVG elements;
Bri Prebilic Cole80401762015-07-16 11:36:18 -070068 var linkG, linkLabelG, numLinkLblsG, portLabelG, nodeG;
Simon Hunt737c89f2015-01-28 12:23:19 -080069
70 // D3 selections;
71 var link, linkLabel, node;
72
73 // default settings for force layout
74 var defaultSettings = {
75 gravity: 0.4,
76 friction: 0.7,
77 charge: {
78 // note: key is node.class
79 device: -8000,
80 host: -5000,
81 _def_: -12000
82 },
83 linkDistance: {
84 // note: key is link.type
85 direct: 100,
86 optical: 120,
87 hostLink: 3,
88 _def_: 50
89 },
90 linkStrength: {
91 // note: key is link.type
92 // range: {0.0 ... 1.0}
93 //direct: 1.0,
94 //optical: 1.0,
95 //hostLink: 1.0,
96 _def_: 1.0
97 }
98 };
99
100
Simon Huntac4c6f72015-02-03 19:50:53 -0800101 // ==========================
102 // === EVENT HANDLERS
103
104 function addDevice(data) {
105 var id = data.id,
106 d;
107
Simon Hunt1894d792015-02-04 17:09:20 -0800108 uplink.showNoDevs(false);
Simon Huntac4c6f72015-02-03 19:50:53 -0800109
110 // although this is an add device event, if we already have the
111 // device, treat it as an update instead..
Simon Hunt1894d792015-02-04 17:09:20 -0800112 if (lu[id]) {
Simon Huntac4c6f72015-02-03 19:50:53 -0800113 updateDevice(data);
114 return;
115 }
116
Simon Hunt3a6eec02015-02-09 21:16:43 -0800117 d = tms.createDeviceNode(data);
Simon Huntac4c6f72015-02-03 19:50:53 -0800118 network.nodes.push(d);
Simon Hunt1894d792015-02-04 17:09:20 -0800119 lu[id] = d;
Simon Huntac4c6f72015-02-03 19:50:53 -0800120 updateNodes();
121 fStart();
122 }
123
124 function updateDevice(data) {
125 var id = data.id,
Simon Hunt1894d792015-02-04 17:09:20 -0800126 d = lu[id],
Simon Huntac4c6f72015-02-03 19:50:53 -0800127 wasOnline;
128
129 if (d) {
130 wasOnline = d.online;
131 angular.extend(d, data);
Simon Hunt3a6eec02015-02-09 21:16:43 -0800132 if (tms.positionNode(d, true)) {
Simon Hunt445e8152015-02-06 13:00:12 -0800133 sendUpdateMeta(d);
Simon Huntac4c6f72015-02-03 19:50:53 -0800134 }
135 updateNodes();
136 if (wasOnline !== d.online) {
Simon Huntdc6adea2015-02-09 22:29:36 -0800137 tms.findAttachedLinks(d.id).forEach(restyleLinkElement);
Simon Hunt5724fb42015-02-05 16:59:40 -0800138 updateOfflineVisibility(d);
Simon Huntac4c6f72015-02-03 19:50:53 -0800139 }
Simon Huntac4c6f72015-02-03 19:50:53 -0800140 }
141 }
142
Simon Hunt1894d792015-02-04 17:09:20 -0800143 function removeDevice(data) {
144 var id = data.id,
145 d = lu[id];
146 if (d) {
147 removeDeviceElement(d);
Simon Hunt1894d792015-02-04 17:09:20 -0800148 }
149 }
150
151 function addHost(data) {
152 var id = data.id,
153 d, lnk;
154
155 // although this is an add host event, if we already have the
156 // host, treat it as an update instead..
157 if (lu[id]) {
158 updateHost(data);
159 return;
160 }
161
Simon Hunt3a6eec02015-02-09 21:16:43 -0800162 d = tms.createHostNode(data);
Simon Hunt1894d792015-02-04 17:09:20 -0800163 network.nodes.push(d);
164 lu[id] = d;
Simon Hunt1894d792015-02-04 17:09:20 -0800165 updateNodes();
166
Simon Hunt3a6eec02015-02-09 21:16:43 -0800167 lnk = tms.createHostLink(data);
Simon Hunt1894d792015-02-04 17:09:20 -0800168 if (lnk) {
Simon Hunt1894d792015-02-04 17:09:20 -0800169 d.linkData = lnk; // cache ref on its host
170 network.links.push(lnk);
171 lu[d.ingress] = lnk;
172 lu[d.egress] = lnk;
173 updateLinks();
174 }
175
176 fStart();
177 }
178
179 function updateHost(data) {
180 var id = data.id,
181 d = lu[id];
182 if (d) {
183 angular.extend(d, data);
Simon Hunt3a6eec02015-02-09 21:16:43 -0800184 if (tms.positionNode(d, true)) {
Simon Hunt445e8152015-02-06 13:00:12 -0800185 sendUpdateMeta(d);
Simon Hunt1894d792015-02-04 17:09:20 -0800186 }
187 updateNodes();
Simon Hunt1894d792015-02-04 17:09:20 -0800188 }
189 }
190
191 function removeHost(data) {
192 var id = data.id,
193 d = lu[id];
194 if (d) {
195 removeHostElement(d, true);
Simon Hunt1894d792015-02-04 17:09:20 -0800196 }
197 }
198
199 function addLink(data) {
Simon Huntdc6adea2015-02-09 22:29:36 -0800200 var result = tms.findLink(data, 'add'),
Simon Hunt1894d792015-02-04 17:09:20 -0800201 bad = result.badLogic,
202 d = result.ldata;
203
204 if (bad) {
205 //logicError(bad + ': ' + link.id);
206 return;
207 }
208
209 if (d) {
210 // we already have a backing store link for src/dst nodes
211 addLinkUpdate(d, data);
212 return;
213 }
214
215 // no backing store link yet
Simon Hunt3a6eec02015-02-09 21:16:43 -0800216 d = tms.createLink(data);
Simon Hunt1894d792015-02-04 17:09:20 -0800217 if (d) {
218 network.links.push(d);
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -0700219 aggregateLink(d, data);
Simon Hunt1894d792015-02-04 17:09:20 -0800220 lu[d.key] = d;
221 updateLinks();
222 fStart();
223 }
224 }
225
226 function updateLink(data) {
Simon Huntdc6adea2015-02-09 22:29:36 -0800227 var result = tms.findLink(data, 'update'),
Simon Hunt1894d792015-02-04 17:09:20 -0800228 bad = result.badLogic;
229 if (bad) {
230 //logicError(bad + ': ' + link.id);
231 return;
232 }
233 result.updateWith(link);
234 }
235
236 function removeLink(data) {
Simon Hunta4242de2015-02-24 17:11:55 -0800237 var result = tms.findLink(data, 'remove');
238
239 if (!result.badLogic) {
240 result.removeRawLink();
Simon Hunt1894d792015-02-04 17:09:20 -0800241 }
Simon Hunt1894d792015-02-04 17:09:20 -0800242 }
243
244 // ========================
245
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -0700246 function makeNodeKey(node1, node2) {
247 return node1 + '-' + node2;
248 }
249
250 function findNodePair(key, keyRev) {
251 if (network.linksByDevice[key]) {
252 return key;
253 } else if (network.linksByDevice[keyRev]) {
254 return keyRev;
255 } else {
256 return false;
257 }
258 }
259
260 function aggregateLink(ldata, link) {
261 var key = makeNodeKey(link.src, link.dst),
262 keyRev = makeNodeKey(link.dst, link.src),
263 found = findNodePair(key, keyRev);
264
265 if (found) {
266 network.linksByDevice[found].push(ldata);
267 ldata.devicePair = found;
268 } else {
269 network.linksByDevice[key] = [ ldata ];
270 ldata.devicePair = key;
271 }
272 }
273
Simon Hunt1894d792015-02-04 17:09:20 -0800274 function addLinkUpdate(ldata, link) {
275 // add link event, but we already have the reverse link installed
276 ldata.fromTarget = link;
Simon Huntdc6adea2015-02-09 22:29:36 -0800277 rlk[link.id] = ldata.key;
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -0700278 // possible solution to el being undefined in restyleLinkElement:
279 //_updateLinks();
Simon Hunt1894d792015-02-04 17:09:20 -0800280 restyleLinkElement(ldata);
281 }
282
Simon Hunt1894d792015-02-04 17:09:20 -0800283
284 var widthRatio = 1.4,
285 linkScale = d3.scale.linear()
286 .domain([1, 12])
287 .range([widthRatio, 12 * widthRatio])
Simon Hunt5724fb42015-02-05 16:59:40 -0800288 .clamp(true),
Simon Hunt3a6eec02015-02-09 21:16:43 -0800289 allLinkTypes = 'direct indirect optical tunnel';
Simon Hunt1894d792015-02-04 17:09:20 -0800290
Simon Hunta142dd22015-02-12 22:07:51 -0800291 function restyleLinkElement(ldata, immediate) {
Simon Hunt1894d792015-02-04 17:09:20 -0800292 // this fn's job is to look at raw links and decide what svg classes
293 // need to be applied to the line element in the DOM
294 var th = ts.theme(),
295 el = ldata.el,
296 type = ldata.type(),
297 lw = ldata.linkWidth(),
Simon Hunta142dd22015-02-12 22:07:51 -0800298 online = ldata.online(),
299 delay = immediate ? 0 : 1000;
Simon Hunt1894d792015-02-04 17:09:20 -0800300
Thomas Vachuskacb5016f2015-05-18 14:11:43 -0700301 // FIXME: understand why el is sometimes undefined on addLink events...
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -0700302 // Investigated:
303 // el is undefined when it's a reverse link that is being added.
304 // updateLinks (which sets ldata.el) isn't called before this is called.
305 // Calling _updateLinks in addLinkUpdate fixes it, but there might be
306 // a more efficient way to fix it.
307 if (el && !el.empty()) {
Thomas Vachuskacb5016f2015-05-18 14:11:43 -0700308 el.classed('link', true);
309 el.classed('inactive', !online);
310 el.classed(allLinkTypes, false);
311 if (type) {
312 el.classed(type, true);
313 }
314 el.transition()
315 .duration(delay)
316 .attr('stroke-width', linkScale(lw))
317 .attr('stroke', linkConfig[th].baseColor);
Simon Hunt1894d792015-02-04 17:09:20 -0800318 }
Simon Hunt1894d792015-02-04 17:09:20 -0800319 }
320
Simon Hunt1894d792015-02-04 17:09:20 -0800321 function removeLinkElement(d) {
322 var idx = fs.find(d.key, network.links, 'key'),
323 removed;
324 if (idx >=0) {
325 // remove from links array
326 removed = network.links.splice(idx, 1);
327 // remove from lookup cache
328 delete lu[removed[0].key];
329 updateLinks();
330 fResume();
331 }
332 }
333
334 function removeHostElement(d, upd) {
335 // first, remove associated hostLink...
336 removeLinkElement(d.linkData);
337
338 // remove hostLink bindings
339 delete lu[d.ingress];
340 delete lu[d.egress];
341
342 // remove from lookup cache
343 delete lu[d.id];
344 // remove from nodes array
345 var idx = fs.find(d.id, network.nodes);
346 network.nodes.splice(idx, 1);
347
348 // remove from SVG
349 // NOTE: upd is false if we were called from removeDeviceElement()
350 if (upd) {
351 updateNodes();
352 fResume();
353 }
354 }
355
356 function removeDeviceElement(d) {
357 var id = d.id;
358 // first, remove associated hosts and links..
Simon Huntdc6adea2015-02-09 22:29:36 -0800359 tms.findAttachedHosts(id).forEach(removeHostElement);
360 tms.findAttachedLinks(id).forEach(removeLinkElement);
Simon Hunt1894d792015-02-04 17:09:20 -0800361
362 // remove from lookup cache
363 delete lu[id];
364 // remove from nodes array
365 var idx = fs.find(id, network.nodes);
366 network.nodes.splice(idx, 1);
367
368 if (!network.nodes.length) {
Simon Huntdc6adea2015-02-09 22:29:36 -0800369 uplink.showNoDevs(true);
Simon Hunt1894d792015-02-04 17:09:20 -0800370 }
371
372 // remove from SVG
373 updateNodes();
374 fResume();
375 }
376
Simon Hunt5724fb42015-02-05 16:59:40 -0800377 function updateHostVisibility() {
Simon Hunt18bf9822015-02-12 17:35:45 -0800378 sus.visible(nodeG.selectAll('.host'), showHosts);
379 sus.visible(linkG.selectAll('.hostLink'), showHosts);
Simon Hunt8eb4d3a2015-02-23 18:23:29 -0800380 sus.visible(linkLabelG.selectAll('.hostLinkLabel'), showHosts);
Simon Hunt5724fb42015-02-05 16:59:40 -0800381 }
382
383 function updateOfflineVisibility(dev) {
384 function updDev(d, show) {
Simon Hunt8eb4d3a2015-02-23 18:23:29 -0800385 var b;
Simon Hunt18bf9822015-02-12 17:35:45 -0800386 sus.visible(d.el, show);
Simon Hunt5724fb42015-02-05 16:59:40 -0800387
Simon Huntdc6adea2015-02-09 22:29:36 -0800388 tms.findAttachedLinks(d.id).forEach(function (link) {
Simon Hunt5724fb42015-02-05 16:59:40 -0800389 b = show && ((link.type() !== 'hostLink') || showHosts);
Simon Hunt18bf9822015-02-12 17:35:45 -0800390 sus.visible(link.el, b);
Simon Hunt5724fb42015-02-05 16:59:40 -0800391 });
Simon Huntdc6adea2015-02-09 22:29:36 -0800392 tms.findAttachedHosts(d.id).forEach(function (host) {
Simon Hunt5724fb42015-02-05 16:59:40 -0800393 b = show && showHosts;
Simon Hunt18bf9822015-02-12 17:35:45 -0800394 sus.visible(host.el, b);
Simon Hunt5724fb42015-02-05 16:59:40 -0800395 });
396 }
397
398 if (dev) {
399 // updating a specific device that just toggled off/on-line
400 updDev(dev, dev.online || showOffline);
401 } else {
402 // updating all offline devices
Simon Huntdc6adea2015-02-09 22:29:36 -0800403 tms.findDevices(true).forEach(function (d) {
Simon Hunt5724fb42015-02-05 16:59:40 -0800404 updDev(d, showOffline);
405 });
406 }
407 }
408
Simon Hunt1894d792015-02-04 17:09:20 -0800409
Simon Hunt445e8152015-02-06 13:00:12 -0800410 function sendUpdateMeta(d, clearPos) {
Simon Huntac4c6f72015-02-03 19:50:53 -0800411 var metaUi = {},
412 ll;
413
Simon Hunt445e8152015-02-06 13:00:12 -0800414 // if we are not clearing the position data (unpinning),
415 // attach the x, y, longitude, latitude...
416 if (!clearPos) {
Simon Hunt3a6eec02015-02-09 21:16:43 -0800417 ll = tms.lngLatFromCoord([d.x, d.y]);
Simon Huntdc6adea2015-02-09 22:29:36 -0800418 metaUi = {x: d.x, y: d.y, lng: ll[0], lat: ll[1]};
Simon Hunt1894d792015-02-04 17:09:20 -0800419 }
420 d.metaUi = metaUi;
Simon Hunt237676b52015-03-10 19:04:26 -0700421 wss.sendEvent('updateMeta', {
Simon Hunt1894d792015-02-04 17:09:20 -0800422 id: d.id,
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -0700423 class: d.class,
Simon Hunt1894d792015-02-04 17:09:20 -0800424 memento: metaUi
425 });
Simon Huntac4c6f72015-02-03 19:50:53 -0800426 }
427
Simon Hunt1894d792015-02-04 17:09:20 -0800428
Simon Huntac4c6f72015-02-03 19:50:53 -0800429 function mkSvgClass(d) {
430 return d.fixed ? d.svgClass + ' fixed' : d.svgClass;
431 }
432
Simon Hunt5724fb42015-02-05 16:59:40 -0800433 function vis(b) {
434 return b ? 'visible' : 'hidden';
435 }
436
Simon Huntfcbde892015-04-16 12:05:28 -0700437 function toggleHosts(x) {
438 var kev = (x === 'keyev'),
439 on = kev ? !showHosts : !!x;
440
441 showHosts = on;
Simon Hunt5724fb42015-02-05 16:59:40 -0800442 updateHostVisibility();
Simon Huntfcbde892015-04-16 12:05:28 -0700443 flash.flash('Hosts ' + vis(on));
444 return on;
Simon Hunt5724fb42015-02-05 16:59:40 -0800445 }
446
Simon Huntfcbde892015-04-16 12:05:28 -0700447 function toggleOffline(x) {
448 var kev = (x === 'keyev'),
449 on = kev ? !showOffline : !!x;
450
451 showOffline = on;
Simon Hunt5724fb42015-02-05 16:59:40 -0800452 updateOfflineVisibility();
Simon Huntfcbde892015-04-16 12:05:28 -0700453 flash.flash('Offline devices ' + vis(on));
454 return on;
Simon Hunt5724fb42015-02-05 16:59:40 -0800455 }
456
457 function cycleDeviceLabels() {
Bri Prebilic Cole9cf1a8d2015-04-21 13:15:29 -0700458 flash.flash(td3.incDevLabIndex());
Simon Huntdc6adea2015-02-09 22:29:36 -0800459 tms.findDevices().forEach(function (d) {
Simon Hunta4242de2015-02-24 17:11:55 -0800460 td3.updateDeviceLabel(d);
Simon Hunt1c367112015-02-05 18:02:46 -0800461 });
Simon Hunt5724fb42015-02-05 16:59:40 -0800462 }
463
Simon Hunt445e8152015-02-06 13:00:12 -0800464 function unpin() {
Simon Hunt08f841d02015-02-10 14:39:20 -0800465 var hov = tss.hovered();
466 if (hov) {
467 sendUpdateMeta(hov, true);
468 hov.fixed = false;
469 hov.el.classed('fixed', false);
Simon Hunt445e8152015-02-06 13:00:12 -0800470 fResume();
471 }
472 }
473
Simon Hunta142dd22015-02-12 22:07:51 -0800474 function showMastership(masterId) {
475 if (!masterId) {
476 restoreLayerState();
477 } else {
478 showMastershipFor(masterId);
479 }
480 }
481
482 function restoreLayerState() {
483 // NOTE: this level of indirection required, for when we have
484 // the layer filter functionality re-implemented
485 suppressLayers(false);
486 }
487
488 function showMastershipFor(id) {
489 suppressLayers(true);
490 node.each(function (n) {
491 if (n.master === id) {
492 n.el.classed('suppressed', false);
493 }
494 });
495 }
496
497 function suppressLayers(b) {
498 node.classed('suppressed', b);
499 link.classed('suppressed', b);
500// d3.selectAll('svg .port').classed('inactive', b);
501// d3.selectAll('svg .portText').classed('inactive', b);
502 }
Simon Hunt445e8152015-02-06 13:00:12 -0800503
Simon Hunt86b7c882015-04-02 23:06:08 -0700504 function showBadLinks() {
505 var badLinks = tms.findBadLinks();
506 flash.flash('Bad Links: ' + badLinks.length);
507 $log.debug('Bad Link List (' + badLinks.length + '):');
508 badLinks.forEach(function (d) {
509 $log.debug('bad link: (' + d.bad + ') ' + d.key, d);
510 if (d.el) {
511 d.el.attr('stroke-width', linkScale(2.8))
512 .attr('stroke', 'red');
513 }
514 });
515 // back to normal after 2 seconds...
516 $timeout(updateLinks, 2000);
517 }
518
Simon Hunt5724fb42015-02-05 16:59:40 -0800519 // ==========================================
520
Simon Huntac4c6f72015-02-03 19:50:53 -0800521 function updateNodes() {
Thomas Vachuska1a989c12015-06-09 18:29:22 -0700522 if (fNodesTimer) {
523 $timeout.cancel(fNodesTimer);
524 }
525 fNodesTimer = $timeout(_updateNodes, 150);
526 }
527
528 function _updateNodes() {
Simon Hunt1894d792015-02-04 17:09:20 -0800529 // select all the nodes in the layout:
Simon Huntac4c6f72015-02-03 19:50:53 -0800530 node = nodeG.selectAll('.node')
531 .data(network.nodes, function (d) { return d.id; });
532
Simon Hunt1894d792015-02-04 17:09:20 -0800533 // operate on existing nodes:
Simon Hunta4242de2015-02-24 17:11:55 -0800534 node.filter('.device').each(td3.deviceExisting);
535 node.filter('.host').each(td3.hostExisting);
Simon Huntac4c6f72015-02-03 19:50:53 -0800536
537 // operate on entering nodes:
538 var entering = node.enter()
539 .append('g')
540 .attr({
541 id: function (d) { return sus.safeId(d.id); },
542 class: mkSvgClass,
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -0700543 transform: function (d) {
544 // sometimes says d.x and d.y are NaN?
545 // I have a feeling it's a timing issue
546 return sus.translate(d.x, d.y);
547 },
Simon Huntac4c6f72015-02-03 19:50:53 -0800548 opacity: 0
549 })
550 .call(drag)
Simon Hunt08f841d02015-02-10 14:39:20 -0800551 .on('mouseover', tss.nodeMouseOver)
552 .on('mouseout', tss.nodeMouseOut)
Simon Huntac4c6f72015-02-03 19:50:53 -0800553 .transition()
554 .attr('opacity', 1);
555
Simon Hunt1894d792015-02-04 17:09:20 -0800556 // augment entering nodes:
Simon Hunta4242de2015-02-24 17:11:55 -0800557 entering.filter('.device').each(td3.deviceEnter);
558 entering.filter('.host').each(td3.hostEnter);
Simon Huntac4c6f72015-02-03 19:50:53 -0800559
Simon Hunt51056592015-02-03 21:48:07 -0800560 // operate on both existing and new nodes:
Simon Hunta4242de2015-02-24 17:11:55 -0800561 td3.updateDeviceColors();
Simon Huntac4c6f72015-02-03 19:50:53 -0800562
563 // operate on exiting nodes:
564 // Note that the node is removed after 2 seconds.
565 // Sub element animations should be shorter than 2 seconds.
566 var exiting = node.exit()
567 .transition()
568 .duration(2000)
569 .style('opacity', 0)
570 .remove();
571
Simon Hunt1894d792015-02-04 17:09:20 -0800572 // exiting node specifics:
Simon Hunta4242de2015-02-24 17:11:55 -0800573 exiting.filter('.host').each(td3.hostExit);
574 exiting.filter('.device').each(td3.deviceExit);
Simon Huntac4c6f72015-02-03 19:50:53 -0800575 }
576
Simon Hunt51056592015-02-03 21:48:07 -0800577 // ==========================
Simon Hunt1894d792015-02-04 17:09:20 -0800578
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700579 function getDefaultPos(link) {
580 return {
581 x1: link.source.x,
582 y1: link.source.y,
583 x2: link.target.x,
584 y2: link.target.y
585 };
586 }
587
588 // returns amount of adjustment along the normal for given link
589 function amt(numLinks, linkIdx) {
590 var gap = 6;
591 return (linkIdx - ((numLinks - 1) / 2)) * gap;
592 }
593
594 function calcMovement(d, amt, flipped) {
595 var pos = getDefaultPos(d),
596 mult = flipped ? -amt : amt,
597 dx = pos.x2 - pos.x1,
598 dy = pos.y2 - pos.y1,
599 length = Math.sqrt((dx * dx) + (dy * dy));
600
601 return {
602 x1: pos.x1 + (mult * dy / length),
603 y1: pos.y1 + (mult * -dx / length),
604 x2: pos.x2 + (mult * dy / length),
605 y2: pos.y2 + (mult * -dx / length)
606 };
607 }
608
609 function calcPosition() {
610 var lines = this,
611 linkSrcId;
Bri Prebilic Cole80401762015-07-16 11:36:18 -0700612 linkNums = [];
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700613 lines.each(function (d) {
614 if (d.type() === 'hostLink') {
615 d.position = getDefaultPos(d);
616 }
617 });
618
619 function normalizeLinkSrc(link) {
620 // ensure source device is consistent across set of links
621 // temporary measure until link modeling is refactored
622 if (!linkSrcId) {
623 linkSrcId = link.source.id;
624 return false;
625 }
626
627 return link.source.id !== linkSrcId;
628 }
629
Bri Prebilic Cole80401762015-07-16 11:36:18 -0700630 angular.forEach(network.linksByDevice, function (linkArr, key) {
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700631 var numLinks = linkArr.length,
632 link;
633
634 if (numLinks === 1) {
635 link = linkArr[0];
636 link.position = getDefaultPos(link);
Bri Prebilic Cole80401762015-07-16 11:36:18 -0700637 link.position.multiLink = false;
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700638 } else if (numLinks >= 5) {
639 // this code is inefficient, in the future the way links
640 // are modeled will be changed
641 angular.forEach(linkArr, function (link) {
642 link.position = getDefaultPos(link);
643 link.position.multiLink = true;
644 });
Bri Prebilic Cole80401762015-07-16 11:36:18 -0700645 linkNums.push({
646 id: key,
647 num: numLinks,
648 linkCoords: linkArr[0].position
649 });
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700650 } else {
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700651 linkSrcId = null;
652 angular.forEach(linkArr, function (link, index) {
653 var offsetAmt = amt(numLinks, index),
654 needToFlip = normalizeLinkSrc(link);
655 link.position = calcMovement(link, offsetAmt, needToFlip);
Bri Prebilic Cole80401762015-07-16 11:36:18 -0700656 link.position.multiLink = false;
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700657 });
658 }
659 });
660 }
661
Simon Hunt1894d792015-02-04 17:09:20 -0800662 function updateLinks() {
Thomas Vachuska1a989c12015-06-09 18:29:22 -0700663 if (fLinksTimer) {
664 $timeout.cancel(fLinksTimer);
665 }
666 fLinksTimer = $timeout(_updateLinks, 150);
667 }
668
669 function _updateLinks() {
Simon Hunt1894d792015-02-04 17:09:20 -0800670 var th = ts.theme();
671
672 link = linkG.selectAll('.link')
673 .data(network.links, function (d) { return d.key; });
674
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -0700675 // This seems to do nothing? I've only triggered it on timeout errors
676 // when adding links, link var is empty because there aren't any links
677 // when removing links, link var is empty already
Simon Hunt1894d792015-02-04 17:09:20 -0800678 // operate on existing links:
Simon Huntd5264122015-02-25 10:17:43 -0800679 link.each(function (d) {
680 // this is supposed to be an existing link, but we have observed
681 // occasions (where links are deleted and added rapidly?) where
682 // the DOM element has not been defined. So protect against that...
683 if (d.el) {
684 restyleLinkElement(d, true);
685 }
686 });
Simon Hunt1894d792015-02-04 17:09:20 -0800687
688 // operate on entering links:
689 var entering = link.enter()
690 .append('line')
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700691 .call(calcPosition)
Simon Hunt1894d792015-02-04 17:09:20 -0800692 .attr({
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700693 x1: function (d) { return d.position.x1; },
694 y1: function (d) { return d.position.y1; },
695 x2: function (d) { return d.position.x2; },
696 y2: function (d) { return d.position.y2; },
Simon Hunt1894d792015-02-04 17:09:20 -0800697 stroke: linkConfig[th].inColor,
698 'stroke-width': linkConfig.inWidth
699 });
700
701 // augment links
Simon Hunta4242de2015-02-24 17:11:55 -0800702 entering.each(td3.linkEntering);
Simon Hunt1894d792015-02-04 17:09:20 -0800703
704 // operate on both existing and new links:
705 //link.each(...)
706
Bri Prebilic Cole80401762015-07-16 11:36:18 -0700707 // add labels for how many links are in a thick line
708 td3.applyNumLinkLabels(linkNums, numLinkLblsG);
709
Simon Hunt1894d792015-02-04 17:09:20 -0800710 // apply or remove labels
Simon Hunta4242de2015-02-24 17:11:55 -0800711 td3.applyLinkLabels();
Simon Hunt1894d792015-02-04 17:09:20 -0800712
713 // operate on exiting links:
714 link.exit()
715 .attr('stroke-dasharray', '3 3')
Simon Hunt5724fb42015-02-05 16:59:40 -0800716 .attr('stroke', linkConfig[th].outColor)
Simon Hunt1894d792015-02-04 17:09:20 -0800717 .style('opacity', 0.5)
718 .transition()
719 .duration(1500)
720 .attr({
721 'stroke-dasharray': '3 12',
Simon Hunt1894d792015-02-04 17:09:20 -0800722 'stroke-width': linkConfig.outWidth
723 })
724 .style('opacity', 0.0)
725 .remove();
Simon Hunt1894d792015-02-04 17:09:20 -0800726 }
727
Simon Huntac4c6f72015-02-03 19:50:53 -0800728
729 // ==========================
Simon Hunt737c89f2015-01-28 12:23:19 -0800730 // force layout tick function
Simon Hunt737c89f2015-01-28 12:23:19 -0800731
Simon Hunt5724fb42015-02-05 16:59:40 -0800732 function fResume() {
Simon Huntc3c5b672015-02-20 11:32:13 -0800733 if (!tos.isOblique()) {
Simon Hunt5724fb42015-02-05 16:59:40 -0800734 force.resume();
735 }
736 }
737
738 function fStart() {
Simon Huntc3c5b672015-02-20 11:32:13 -0800739 if (!tos.isOblique()) {
Thomas Vachuska1a989c12015-06-09 18:29:22 -0700740 if (fTimer) {
741 $timeout.cancel(fTimer);
742 }
743 fTimer = $timeout(function () {
744 $log.debug("Starting force-layout");
745 force.start();
746 }, 200);
Simon Hunt5724fb42015-02-05 16:59:40 -0800747 }
748 }
749
750 var tickStuff = {
751 nodeAttr: {
752 transform: function (d) { return sus.translate(d.x, d.y); }
753 },
754 linkAttr: {
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700755 x1: function (d) { return d.position.x1; },
756 y1: function (d) { return d.position.y1; },
757 x2: function (d) { return d.position.x2; },
758 y2: function (d) { return d.position.y2; }
Simon Hunt5724fb42015-02-05 16:59:40 -0800759 },
760 linkLabelAttr: {
761 transform: function (d) {
Simon Huntdc6adea2015-02-09 22:29:36 -0800762 var lnk = tms.findLinkById(d.key);
Simon Hunt5724fb42015-02-05 16:59:40 -0800763 if (lnk) {
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700764 return td3.transformLabel(lnk.position);
Simon Hunt5724fb42015-02-05 16:59:40 -0800765 }
766 }
767 }
768 };
769
770 function tick() {
Simon Hunt3ab20282015-02-26 20:32:19 -0800771 // guard against null (which can happen when our view pages out)...
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -0700772 if (node) {
773 node.attr(tickStuff.nodeAttr);
774 }
775 if (link) {
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700776 link.call(calcPosition)
777 .attr(tickStuff.linkAttr);
Bri Prebilic Cole80401762015-07-16 11:36:18 -0700778 td3.applyNumLinkLabels(linkNums, numLinkLblsG);
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -0700779 }
780 if (linkLabel) {
781 linkLabel.attr(tickStuff.linkLabelAttr);
782 }
Simon Hunt737c89f2015-01-28 12:23:19 -0800783 }
784
785
Simon Huntac4c6f72015-02-03 19:50:53 -0800786 // ==========================
787 // === MOUSE GESTURE HANDLERS
788
Simon Hunt205099e2015-02-07 13:12:01 -0800789 function zoomingOrPanning(ev) {
790 return ev.metaKey || ev.altKey;
Simon Hunt445e8152015-02-06 13:00:12 -0800791 }
792
793 function atDragEnd(d) {
794 // once we've finished moving, pin the node in position
795 d.fixed = true;
796 d3.select(this).classed('fixed', true);
797 sendUpdateMeta(d);
Simon Hunt0c6b2d32015-03-26 17:46:29 -0700798 tss.clickConsumed(true);
Simon Hunt445e8152015-02-06 13:00:12 -0800799 }
800
801 // predicate that indicates when dragging is active
802 function dragEnabled() {
803 var ev = d3.event.sourceEvent;
804 // nodeLock means we aren't allowing nodes to be dragged...
Simon Hunt205099e2015-02-07 13:12:01 -0800805 return !nodeLock && !zoomingOrPanning(ev);
Simon Hunt445e8152015-02-06 13:00:12 -0800806 }
807
808 // predicate that indicates when clicking is active
809 function clickEnabled() {
810 return true;
811 }
Simon Hunt737c89f2015-01-28 12:23:19 -0800812
Simon Huntf542d842015-02-11 16:20:33 -0800813 // ==========================
814 // function entry points for traffic module
815
Simon Hunte50829c2015-06-09 08:39:28 -0700816 var allTrafficClasses = 'primary secondary optical animated ' +
817 'port-traffic-Kbps port-traffic-Mbps port-traffic-Gbps ' +
818 'port-traffic-Gbps-choked';
Simon Huntf542d842015-02-11 16:20:33 -0800819
820 function clearLinkTrafficStyle() {
821 link.style('stroke-width', null)
822 .classed(allTrafficClasses, false);
823 }
824
825 function removeLinkLabels() {
826 network.links.forEach(function (d) {
827 d.label = '';
828 });
829 }
Simon Hunt737c89f2015-01-28 12:23:19 -0800830
Simon Hunta4242de2015-02-24 17:11:55 -0800831 function updateLinkLabelModel() {
832 // create the backing data for showing labels..
833 var data = [];
834 link.each(function (d) {
835 if (d.label) {
836 data.push({
837 id: 'lab-' + d.key,
838 key: d.key,
839 label: d.label,
840 ldata: d
841 });
842 }
843 });
844
845 linkLabel = linkLabelG.selectAll('.linkLabel')
846 .data(data, function (d) { return d.id; });
847 }
848
Simon Hunt737c89f2015-01-28 12:23:19 -0800849 // ==========================
Simon Huntac4c6f72015-02-03 19:50:53 -0800850 // Module definition
Simon Hunt737c89f2015-01-28 12:23:19 -0800851
Simon Huntdc6adea2015-02-09 22:29:36 -0800852 function mkModelApi(uplink) {
853 return {
854 projection: uplink.projection,
855 network: network,
856 restyleLinkElement: restyleLinkElement,
857 removeLinkElement: removeLinkElement
858 };
859 }
860
Simon Hunta4242de2015-02-24 17:11:55 -0800861 function mkD3Api(uplink) {
862 return {
863 node: function () { return node; },
864 link: function () { return link; },
865 linkLabel: function () { return linkLabel; },
866 instVisible: function () { return tis.isVisible(); },
867 posNode: tms.positionNode,
868 showHosts: function () { return showHosts; },
869 restyleLinkElement: restyleLinkElement,
Bri Prebilic Cole80401762015-07-16 11:36:18 -0700870 updateLinkLabelModel: updateLinkLabelModel,
871 linkConfig: function () { return linkConfig; }
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -0700872 };
Simon Hunta4242de2015-02-24 17:11:55 -0800873 }
874
Simon Hunt08f841d02015-02-10 14:39:20 -0800875 function mkSelectApi(uplink) {
876 return {
877 node: function () { return node; },
878 zoomingOrPanning: zoomingOrPanning,
Simon Hunt0c6b2d32015-03-26 17:46:29 -0700879 updateDeviceColors: td3.updateDeviceColors,
880 deselectLink: tls.deselectLink
Simon Hunt08f841d02015-02-10 14:39:20 -0800881 };
882 }
883
Simon Huntf542d842015-02-11 16:20:33 -0800884 function mkTrafficApi(uplink) {
885 return {
886 clearLinkTrafficStyle: clearLinkTrafficStyle,
887 removeLinkLabels: removeLinkLabels,
888 updateLinks: updateLinks,
889 findLinkById: tms.findLinkById,
890 hovered: tss.hovered,
891 validateSelectionContext: tss.validateSelectionContext,
Simon Hunt237676b52015-03-10 19:04:26 -0700892 selectOrder: tss.selectOrder
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -0700893 };
Simon Huntf542d842015-02-11 16:20:33 -0800894 }
895
Simon Huntc3c5b672015-02-20 11:32:13 -0800896 function mkObliqueApi(uplink, fltr) {
Simon Hunt96f88c62015-02-19 17:57:25 -0800897 return {
Simon Huntc3c5b672015-02-20 11:32:13 -0800898 force: function() { return force; },
899 zoomLayer: uplink.zoomLayer,
900 nodeGBBox: function() { return nodeG.node().getBBox(); },
Simon Hunt96f88c62015-02-19 17:57:25 -0800901 node: function () { return node; },
Simon Huntc3c5b672015-02-20 11:32:13 -0800902 link: function () { return link; },
903 linkLabel: function () { return linkLabel; },
904 nodes: function () { return network.nodes; },
905 tickStuff: tickStuff,
906 nodeLock: function (b) {
907 var old = nodeLock;
908 nodeLock = b;
909 return old;
910 },
911 opacifyMap: uplink.opacifyMap,
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700912 inLayer: fltr.inLayer,
Bri Prebilic Cole80401762015-07-16 11:36:18 -0700913 calcLinkPos: calcPosition,
914 applyNumLinkLabels: function () {
915 td3.applyNumLinkLabels(linkNums, numLinkLblsG);
916 }
Simon Hunt96f88c62015-02-19 17:57:25 -0800917 };
918 }
919
Simon Hunteb0fa052015-02-17 19:20:28 -0800920 function mkFilterApi(uplink) {
921 return {
922 node: function () { return node; },
923 link: function () { return link; }
924 };
925 }
926
Simon Hunt9e2104c2015-02-26 10:48:59 -0800927 function mkLinkApi(svg, uplink) {
Simon Huntfb8ea1f2015-02-24 21:38:09 -0800928 return {
929 svg: svg,
Simon Huntfb8ea1f2015-02-24 21:38:09 -0800930 zoomer: uplink.zoomer(),
931 network: network,
Simon Hunt1a5301e2015-02-25 15:31:25 -0800932 portLabelG: function () { return portLabelG; },
Simon Huntfb8ea1f2015-02-24 21:38:09 -0800933 showHosts: function () { return showHosts; }
934 };
935 }
936
Simon Hunt737c89f2015-01-28 12:23:19 -0800937 angular.module('ovTopo')
938 .factory('TopoForceService',
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -0700939 ['$log', '$timeout', 'FnService', 'SvgUtilService',
Simon Hunt86b7c882015-04-02 23:06:08 -0700940 'ThemeService', 'FlashService', 'WebSocketService',
Simon Hunt237676b52015-03-10 19:04:26 -0700941 'TopoInstService', 'TopoModelService',
Simon Hunta4242de2015-02-24 17:11:55 -0800942 'TopoD3Service', 'TopoSelectService', 'TopoTrafficService',
Simon Huntfb8ea1f2015-02-24 21:38:09 -0800943 'TopoObliqueService', 'TopoFilterService', 'TopoLinkService',
Simon Hunt737c89f2015-01-28 12:23:19 -0800944
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -0700945 function (_$log_, _$timeout_, _fs_, _sus_, _ts_, _flash_, _wss_,
Simon Huntfb8ea1f2015-02-24 21:38:09 -0800946 _tis_, _tms_, _td3_, _tss_, _tts_, _tos_, _fltr_, _tls_) {
Simon Hunt737c89f2015-01-28 12:23:19 -0800947 $log = _$log_;
Simon Hunt86b7c882015-04-02 23:06:08 -0700948 $timeout = _$timeout_;
Simon Hunt1894d792015-02-04 17:09:20 -0800949 fs = _fs_;
Simon Hunt737c89f2015-01-28 12:23:19 -0800950 sus = _sus_;
Simon Huntac4c6f72015-02-03 19:50:53 -0800951 ts = _ts_;
Simon Hunt5724fb42015-02-05 16:59:40 -0800952 flash = _flash_;
Simon Hunt237676b52015-03-10 19:04:26 -0700953 wss = _wss_;
Simon Huntac4c6f72015-02-03 19:50:53 -0800954 tis = _tis_;
Simon Hunt3a6eec02015-02-09 21:16:43 -0800955 tms = _tms_;
Simon Hunta4242de2015-02-24 17:11:55 -0800956 td3 = _td3_;
Simon Hunt08f841d02015-02-10 14:39:20 -0800957 tss = _tss_;
Simon Huntf542d842015-02-11 16:20:33 -0800958 tts = _tts_;
Simon Hunt96f88c62015-02-19 17:57:25 -0800959 tos = _tos_;
Simon Hunteb0fa052015-02-17 19:20:28 -0800960 fltr = _fltr_;
Simon Huntfb8ea1f2015-02-24 21:38:09 -0800961 tls = _tls_;
Simon Hunt737c89f2015-01-28 12:23:19 -0800962
Simon Hunta142dd22015-02-12 22:07:51 -0800963 var themeListener = ts.addListener(function () {
964 updateLinks();
965 updateNodes();
966 });
967
Simon Hunt737c89f2015-01-28 12:23:19 -0800968 // forceG is the SVG group to display the force layout in
Simon Huntdc6adea2015-02-09 22:29:36 -0800969 // uplink is the api from the main topo source file
Simon Hunt3a6eec02015-02-09 21:16:43 -0800970 // dim is the initial dimensions of the SVG as [w,h]
Simon Hunt737c89f2015-01-28 12:23:19 -0800971 // opts are, well, optional :)
Simon Hunt3ab20282015-02-26 20:32:19 -0800972 function initForce(_svg_, forceG, _uplink_, _dim_, opts) {
Simon Hunt1894d792015-02-04 17:09:20 -0800973 uplink = _uplink_;
Simon Hunt3a6eec02015-02-09 21:16:43 -0800974 dim = _dim_;
Simon Hunt3ab20282015-02-26 20:32:19 -0800975 svg = _svg_;
976
977 lu = network.lookup;
978 rlk = network.revLinkToKey;
Simon Hunt3a6eec02015-02-09 21:16:43 -0800979
980 $log.debug('initForce().. dim = ' + dim);
981
Simon Huntdc6adea2015-02-09 22:29:36 -0800982 tms.initModel(mkModelApi(uplink), dim);
Simon Hunta4242de2015-02-24 17:11:55 -0800983 td3.initD3(mkD3Api(uplink));
Simon Hunt08f841d02015-02-10 14:39:20 -0800984 tss.initSelect(mkSelectApi(uplink));
Simon Huntf542d842015-02-11 16:20:33 -0800985 tts.initTraffic(mkTrafficApi(uplink));
Simon Huntc3c5b672015-02-20 11:32:13 -0800986 tos.initOblique(mkObliqueApi(uplink, fltr));
Bri Prebilic Coleb5f2b152015-04-07 14:58:09 -0700987 fltr.initFilter(mkFilterApi(uplink));
Simon Hunt9e2104c2015-02-26 10:48:59 -0800988 tls.initLink(mkLinkApi(svg, uplink), td3);
Simon Hunta11b4eb2015-01-28 16:20:50 -0800989
Simon Hunt737c89f2015-01-28 12:23:19 -0800990 settings = angular.extend({}, defaultSettings, opts);
991
992 linkG = forceG.append('g').attr('id', 'topo-links');
993 linkLabelG = forceG.append('g').attr('id', 'topo-linkLabels');
Bri Prebilic Cole80401762015-07-16 11:36:18 -0700994 numLinkLblsG = forceG.append('g').attr('id', 'topo-numLinkLabels');
Simon Hunt737c89f2015-01-28 12:23:19 -0800995 nodeG = forceG.append('g').attr('id', 'topo-nodes');
Simon Hunt1a5301e2015-02-25 15:31:25 -0800996 portLabelG = forceG.append('g').attr('id', 'topo-portLabels');
Simon Hunt737c89f2015-01-28 12:23:19 -0800997
998 link = linkG.selectAll('.link');
999 linkLabel = linkLabelG.selectAll('.linkLabel');
1000 node = nodeG.selectAll('.node');
1001
1002 force = d3.layout.force()
Simon Hunt3a6eec02015-02-09 21:16:43 -08001003 .size(dim)
Simon Hunt737c89f2015-01-28 12:23:19 -08001004 .nodes(network.nodes)
1005 .links(network.links)
1006 .gravity(settings.gravity)
1007 .friction(settings.friction)
1008 .charge(settings.charge._def_)
1009 .linkDistance(settings.linkDistance._def_)
1010 .linkStrength(settings.linkStrength._def_)
1011 .on('tick', tick);
1012
1013 drag = sus.createDragBehavior(force,
Simon Hunt08f841d02015-02-10 14:39:20 -08001014 tss.selectObject, atDragEnd, dragEnabled, clickEnabled);
Simon Hunt737c89f2015-01-28 12:23:19 -08001015 }
1016
Simon Hunt3a6eec02015-02-09 21:16:43 -08001017 function newDim(_dim_) {
1018 dim = _dim_;
1019 force.size(dim);
1020 tms.newDim(dim);
Simon Hunt737c89f2015-01-28 12:23:19 -08001021 }
1022
Simon Hunt3a6eec02015-02-09 21:16:43 -08001023 function destroyForce() {
Simon Hunt3ab20282015-02-26 20:32:19 -08001024 force.stop();
1025
Simon Huntfb8ea1f2015-02-24 21:38:09 -08001026 tls.destroyLink();
Simon Hunt96f88c62015-02-19 17:57:25 -08001027 tos.destroyOblique();
Simon Huntf542d842015-02-11 16:20:33 -08001028 tts.destroyTraffic();
1029 tss.destroySelect();
Simon Hunta4242de2015-02-24 17:11:55 -08001030 td3.destroyD3();
Simon Huntf542d842015-02-11 16:20:33 -08001031 tms.destroyModel();
Simon Hunta142dd22015-02-12 22:07:51 -08001032 ts.removeListener(themeListener);
1033 themeListener = null;
Simon Hunt3ab20282015-02-26 20:32:19 -08001034
1035 // clean up the DOM
1036 svg.selectAll('g').remove();
1037 svg.selectAll('defs').remove();
1038
1039 // clean up internal state
1040 network.nodes = [];
1041 network.links = [];
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -07001042 network.linksByDevice = {};
Simon Hunt3ab20282015-02-26 20:32:19 -08001043 network.lookup = {};
1044 network.revLinkToKey = {};
1045
Bri Prebilic Cole80401762015-07-16 11:36:18 -07001046 linkNums = [];
1047
1048 linkG = linkLabelG = numLinkLblsG = nodeG = portLabelG = null;
Simon Hunt3ab20282015-02-26 20:32:19 -08001049 link = linkLabel = node = null;
1050 force = drag = null;
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -07001051
1052 // clean up $timeout promises
1053 if (fTimer) {
1054 $timeout.cancel(fTimer);
1055 }
1056 if (fNodesTimer) {
1057 $timeout.cancel(fNodesTimer);
1058 }
1059 if (fLinksTimer) {
1060 $timeout.cancel(fLinksTimer);
1061 }
Simon Hunt3a6eec02015-02-09 21:16:43 -08001062 }
1063
Simon Hunt737c89f2015-01-28 12:23:19 -08001064 return {
1065 initForce: initForce,
Simon Hunt3a6eec02015-02-09 21:16:43 -08001066 newDim: newDim,
1067 destroyForce: destroyForce,
Simon Huntac4c6f72015-02-03 19:50:53 -08001068
Simon Hunta4242de2015-02-24 17:11:55 -08001069 updateDeviceColors: td3.updateDeviceColors,
Simon Hunt5724fb42015-02-05 16:59:40 -08001070 toggleHosts: toggleHosts,
Simon Hunt9e2104c2015-02-26 10:48:59 -08001071 togglePorts: tls.togglePorts,
Simon Hunt5724fb42015-02-05 16:59:40 -08001072 toggleOffline: toggleOffline,
1073 cycleDeviceLabels: cycleDeviceLabels,
Simon Hunt445e8152015-02-06 13:00:12 -08001074 unpin: unpin,
Simon Hunta142dd22015-02-12 22:07:51 -08001075 showMastership: showMastership,
Simon Hunt86b7c882015-04-02 23:06:08 -07001076 showBadLinks: showBadLinks,
Simon Huntac4c6f72015-02-03 19:50:53 -08001077
1078 addDevice: addDevice,
Simon Hunt1894d792015-02-04 17:09:20 -08001079 updateDevice: updateDevice,
1080 removeDevice: removeDevice,
1081 addHost: addHost,
1082 updateHost: updateHost,
1083 removeHost: removeHost,
1084 addLink: addLink,
1085 updateLink: updateLink,
1086 removeLink: removeLink
Simon Hunt737c89f2015-01-28 12:23:19 -08001087 };
1088 }]);
1089}());