GUI -- Major rework to link processing so that we consolidate links A->B and B->…
…A into a single backing object. - added blue glow to ONOS instance when showing switch affinity. Change-Id: Ia2a52d9d0571bc8c5eed964c85862f5798c7c5db
Showing
20 changed files
with
472 additions
and
49 deletions
1 | +{ | ||
2 | + "event": "addLink", | ||
3 | + "payload": { | ||
4 | + "id": "of:0000ffffffff0008/10-of:0000ffffffff0003/20", | ||
5 | + "type": "direct", | ||
6 | + "linkWidth": 2, | ||
7 | + "src": "of:0000ffffffff0008", | ||
8 | + "srcPort": "10", | ||
9 | + "dst": "of:0000ffffffff0003", | ||
10 | + "dstPort": "20", | ||
11 | + "props" : { | ||
12 | + "BW": "90 Gb" | ||
13 | + } | ||
14 | + } | ||
15 | +} |
1 | +{ | ||
2 | + "event": "addHost", | ||
3 | + "payload": { | ||
4 | + "id": "0E:2A:69:30:13:88/-1", | ||
5 | + "ingress": "0E:2A:69:30:13:88/-1/0-of:0000ffffffff0003/1", | ||
6 | + "egress": "of:0000ffffffff0003/1-0E:2A:69:30:13:88/-1/0", | ||
7 | + "cp": { | ||
8 | + "device": "of:0000ffffffff0003", | ||
9 | + "port": 1 | ||
10 | + }, | ||
11 | + "labels": [ | ||
12 | + "Host-A", | ||
13 | + "0E:2A:69:30:13:88" | ||
14 | + ], | ||
15 | + "props": {} | ||
16 | + } | ||
17 | +} |
1 | +{ | ||
2 | + "event": "addHost", | ||
3 | + "payload": { | ||
4 | + "id": "0E:2A:69:30:13:89/-1", | ||
5 | + "ingress": "0E:2A:69:30:13:89/-1/0-of:0000ffffffff0007/1", | ||
6 | + "egress": "of:0000ffffffff0007/1-0E:2A:69:30:13:89/-1/0", | ||
7 | + "cp": { | ||
8 | + "device": "of:0000ffffffff0007", | ||
9 | + "port": 1 | ||
10 | + }, | ||
11 | + "labels": [ | ||
12 | + "Host-B", | ||
13 | + "0E:2A:69:30:13:89" | ||
14 | + ], | ||
15 | + "props": {} | ||
16 | + } | ||
17 | +} |
1 | +{ | ||
2 | + "event": "addHost", | ||
3 | + "payload": { | ||
4 | + "id": "0E:2A:69:30:13:8A/-1", | ||
5 | + "ingress": "0E:2A:69:30:13:8A/-1/0-of:0000ffffffff0008/1", | ||
6 | + "egress": "of:0000ffffffff0008/1-0E:2A:69:30:13:8A/-1/0", | ||
7 | + "cp": { | ||
8 | + "device": "of:0000ffffffff0008", | ||
9 | + "port": 1 | ||
10 | + }, | ||
11 | + "labels": [ | ||
12 | + "Host-C", | ||
13 | + "0E:2A:69:30:13:8A" | ||
14 | + ], | ||
15 | + "props": {} | ||
16 | + } | ||
17 | +} |
1 | +{ | ||
2 | + "event": "updateLink", | ||
3 | + "payload": { | ||
4 | + "id": "of:0000ffffffff0007/10-of:0000ffffffff0008/20", | ||
5 | + "src": "of:0000ffffffff0007", | ||
6 | + "srcPort": "10", | ||
7 | + "dst": "of:0000ffffffff0008", | ||
8 | + "dstPort": "20", | ||
9 | + | ||
10 | + "type": "direct", | ||
11 | + "linkWidth": 2, | ||
12 | + "online": true, | ||
13 | + "props" : { | ||
14 | + "BW": "90 Gb" | ||
15 | + } | ||
16 | + } | ||
17 | +} |
1 | +{ | ||
2 | + "event": "updateLink", | ||
3 | + "payload": { | ||
4 | + "id": "of:0000ffffffff0007/20-of:0000ffffffff0003/10", | ||
5 | + "src": "of:0000ffffffff0007", | ||
6 | + "srcPort": "20", | ||
7 | + "dst": "of:0000ffffffff0003", | ||
8 | + "dstPort": "10", | ||
9 | + | ||
10 | + "type": "direct", | ||
11 | + "linkWidth": 6, | ||
12 | + "online": true, | ||
13 | + "props" : { | ||
14 | + "BW": "90 Gb" | ||
15 | + } | ||
16 | + } | ||
17 | +} |
1 | +{ | ||
2 | + "event": "removeLink", | ||
3 | + "payload": { | ||
4 | + "id": "of:0000ffffffff0007/20-of:0000ffffffff0003/10", | ||
5 | + "type": "direct", | ||
6 | + "linkWidth": 2, | ||
7 | + "src": "of:0000ffffffff0007", | ||
8 | + "srcPort": "20", | ||
9 | + "dst": "of:0000ffffffff0003", | ||
10 | + "dstPort": "10", | ||
11 | + "props" : { | ||
12 | + "BW": "90 Gb" | ||
13 | + } | ||
14 | + } | ||
15 | +} |
1 | +{ | ||
2 | + "event": "removeLink", | ||
3 | + "payload": { | ||
4 | + "id": "of:0000ffffffff0003/10-of:0000ffffffff0007/20", | ||
5 | + "type": "direct", | ||
6 | + "linkWidth": 2, | ||
7 | + "src": "of:0000ffffffff0003", | ||
8 | + "srcPort": "10", | ||
9 | + "dst": "of:0000ffffffff0007", | ||
10 | + "dstPort": "20", | ||
11 | + "props" : { | ||
12 | + "BW": "90 Gb" | ||
13 | + } | ||
14 | + } | ||
15 | +} |
1 | +{ | ||
2 | + "event": "addLink", | ||
3 | + "payload": { | ||
4 | + "id": "of:0000ffffffff0007/10-of:0000ffffffff0008/20", | ||
5 | + "type": "direct", | ||
6 | + "linkWidth": 2, | ||
7 | + "src": "of:0000ffffffff0007", | ||
8 | + "srcPort": "10", | ||
9 | + "dst": "of:0000ffffffff0008", | ||
10 | + "dstPort": "20", | ||
11 | + "props" : { | ||
12 | + "BW": "90 Gb" | ||
13 | + } | ||
14 | + } | ||
15 | +} |
1 | +{ | ||
2 | + "event": "addLink", | ||
3 | + "payload": { | ||
4 | + "id": "of:0000ffffffff0008/20-of:0000ffffffff0007/10", | ||
5 | + "type": "direct", | ||
6 | + "linkWidth": 2, | ||
7 | + "src": "of:0000ffffffff0008", | ||
8 | + "srcPort": "20", | ||
9 | + "dst": "of:0000ffffffff0007", | ||
10 | + "dstPort": "10", | ||
11 | + "props" : { | ||
12 | + "BW": "90 Gb" | ||
13 | + } | ||
14 | + } | ||
15 | +} |
1 | +{ | ||
2 | + "event": "addLink", | ||
3 | + "payload": { | ||
4 | + "id": "of:0000ffffffff0003/10-of:0000ffffffff0007/20", | ||
5 | + "type": "direct", | ||
6 | + "linkWidth": 2, | ||
7 | + "src": "of:0000ffffffff0003", | ||
8 | + "srcPort": "10", | ||
9 | + "dst": "of:0000ffffffff0007", | ||
10 | + "dstPort": "20", | ||
11 | + "props" : { | ||
12 | + "BW": "90 Gb" | ||
13 | + } | ||
14 | + } | ||
15 | +} |
1 | +{ | ||
2 | + "event": "addLink", | ||
3 | + "payload": { | ||
4 | + "id": "of:0000ffffffff0007/20-of:0000ffffffff0003/10", | ||
5 | + "type": "direct", | ||
6 | + "linkWidth": 2, | ||
7 | + "src": "of:0000ffffffff0007", | ||
8 | + "srcPort": "20", | ||
9 | + "dst": "of:0000ffffffff0003", | ||
10 | + "dstPort": "10", | ||
11 | + "props" : { | ||
12 | + "BW": "90 Gb" | ||
13 | + } | ||
14 | + } | ||
15 | +} |
1 | +{ | ||
2 | + "event": "addLink", | ||
3 | + "payload": { | ||
4 | + "id": "of:0000ffffffff0003/20-of:0000ffffffff0008/10", | ||
5 | + "type": "direct", | ||
6 | + "linkWidth": 2, | ||
7 | + "src": "of:0000ffffffff0003", | ||
8 | + "srcPort": "20", | ||
9 | + "dst": "of:0000ffffffff0008", | ||
10 | + "dstPort": "10", | ||
11 | + "props" : { | ||
12 | + "BW": "90 Gb" | ||
13 | + } | ||
14 | + } | ||
15 | +} |
1 | +{ | ||
2 | + "comments": [ | ||
3 | + "Stepping through link events" | ||
4 | + ], | ||
5 | + "title": "Process Link Events Scenario", | ||
6 | + "params": { | ||
7 | + "lastAuto": 13 | ||
8 | + }, | ||
9 | + "description": [ | ||
10 | + "Develop link event handling.", | ||
11 | + "", | ||
12 | + "Press 'S' to load initial events.", | ||
13 | + "", | ||
14 | + "Press spacebar to complete the scenario..." | ||
15 | + ] | ||
16 | +} |
... | @@ -339,13 +339,16 @@ | ... | @@ -339,13 +339,16 @@ |
339 | link: { | 339 | link: { |
340 | hostLink: 'pkt', | 340 | hostLink: 'pkt', |
341 | direct: 'pkt', | 341 | direct: 'pkt', |
342 | + indirect: '', | ||
343 | + tunnel: '', | ||
342 | optical: 'opt' | 344 | optical: 'opt' |
343 | } | 345 | } |
344 | }; | 346 | }; |
345 | 347 | ||
346 | function inLayer(d, layer) { | 348 | function inLayer(d, layer) { |
347 | - var look = layerLookup[d.class], | 349 | + var type = d.class === 'link' ? d.type() : d.type, |
348 | - lyr = look && look[d.type]; | 350 | + look = layerLookup[d.class], |
351 | + lyr = look && look[type]; | ||
349 | return lyr === layer; | 352 | return lyr === layer; |
350 | } | 353 | } |
351 | 354 | ||
... | @@ -408,6 +411,115 @@ | ... | @@ -408,6 +411,115 @@ |
408 | }); | 411 | }); |
409 | } | 412 | } |
410 | 413 | ||
414 | + function makeNodeKey(d, what) { | ||
415 | + var port = what + 'Port'; | ||
416 | + return d[what] + '/' + d[port]; | ||
417 | + } | ||
418 | + | ||
419 | + function makeLinkKey(d, flipped) { | ||
420 | + var one = flipped ? makeNodeKey(d, 'dst') : makeNodeKey(d, 'src'), | ||
421 | + two = flipped ? makeNodeKey(d, 'src') : makeNodeKey(d, 'dst'); | ||
422 | + return one + '-' + two; | ||
423 | + } | ||
424 | + | ||
425 | + function findLink(linkData, op) { | ||
426 | + var key = makeLinkKey(linkData), | ||
427 | + keyrev = makeLinkKey(linkData, 1), | ||
428 | + link = network.lookup[key], | ||
429 | + linkRev = network.lookup[keyrev], | ||
430 | + result = {}, | ||
431 | + ldata = link || linkRev, | ||
432 | + rawLink; | ||
433 | + | ||
434 | + if (op === 'add') { | ||
435 | + if (link) { | ||
436 | + // trying to add a link that we already know about | ||
437 | + result.ldata = link; | ||
438 | + result.badLogic = 'addLink: link already added'; | ||
439 | + | ||
440 | + } else if (linkRev) { | ||
441 | + // we found the reverse of the link to be added | ||
442 | + result.ldata = linkRev; | ||
443 | + if (linkRev.fromTarget) { | ||
444 | + result.badLogic = 'addLink: link already added'; | ||
445 | + } | ||
446 | + } | ||
447 | + } else if (op === 'update') { | ||
448 | + if (!ldata) { | ||
449 | + result.badLogic = 'updateLink: link not found'; | ||
450 | + } else { | ||
451 | + rawLink = link ? ldata.fromSource : ldata.fromTarget; | ||
452 | + result.updateWith = function (data) { | ||
453 | + $.extend(rawLink, data); | ||
454 | + restyleLinkElement(ldata); | ||
455 | + } | ||
456 | + } | ||
457 | + } else if (op === 'remove') { | ||
458 | + if (!ldata) { | ||
459 | + result.badLogic = 'removeLink: link not found'; | ||
460 | + } else { | ||
461 | + rawLink = link ? ldata.fromSource : ldata.fromTarget; | ||
462 | + | ||
463 | + if (!rawLink) { | ||
464 | + result.badLogic = 'removeLink: link not found'; | ||
465 | + | ||
466 | + } else { | ||
467 | + result.removeRawLink = function () { | ||
468 | + if (link) { | ||
469 | + // remove fromSource | ||
470 | + ldata.fromSource = null; | ||
471 | + if (ldata.fromTarget) { | ||
472 | + // promote target into source position | ||
473 | + ldata.fromSource = ldata.fromTarget; | ||
474 | + ldata.fromTarget = null; | ||
475 | + ldata.key = keyrev; | ||
476 | + delete network.lookup[key]; | ||
477 | + network.lookup[keyrev] = ldata; | ||
478 | + } | ||
479 | + } else { | ||
480 | + // remove fromTarget | ||
481 | + ldata.fromTarget = null; | ||
482 | + } | ||
483 | + if (ldata.fromSource) { | ||
484 | + restyleLinkElement(ldata); | ||
485 | + } else { | ||
486 | + removeLinkElement(ldata); | ||
487 | + } | ||
488 | + } | ||
489 | + } | ||
490 | + } | ||
491 | + } | ||
492 | + return result; | ||
493 | + } | ||
494 | + | ||
495 | + function addLinkUpdate(ldata, link) { | ||
496 | + // add link event, but we already have the reverse link installed | ||
497 | + ldata.fromTarget = link; | ||
498 | + restyleLinkElement(ldata); | ||
499 | + } | ||
500 | + | ||
501 | + var allLinkTypes = 'direct indirect optical tunnel', | ||
502 | + defaultLinkType = 'direct'; | ||
503 | + | ||
504 | + function restyleLinkElement(ldata) { | ||
505 | + // this fn's job is to look at raw links and decide what svg classes | ||
506 | + // need to be applied to the line element in the DOM | ||
507 | + var el = ldata.el, | ||
508 | + type = ldata.type(), | ||
509 | + lw = ldata.linkWidth(), | ||
510 | + online = ldata.online(); | ||
511 | + | ||
512 | + el.classed('link', true); | ||
513 | + el.classed('inactive', !online); | ||
514 | + el.classed(allLinkTypes, false); | ||
515 | + if (type) { | ||
516 | + el.classed(type, true); | ||
517 | + } | ||
518 | + el.transition() | ||
519 | + .duration(1000) | ||
520 | + .attr('stroke-width', linkScale(lw)) | ||
521 | + .attr('stroke', '#666'); // TODO: remove explicit stroke (use CSS) | ||
522 | + } | ||
411 | 523 | ||
412 | // ============================== | 524 | // ============================== |
413 | // Event handlers for server-pushed events | 525 | // Event handlers for server-pushed events |
... | @@ -465,10 +577,26 @@ | ... | @@ -465,10 +577,26 @@ |
465 | function addLink(data) { | 577 | function addLink(data) { |
466 | evTrace(data); | 578 | evTrace(data); |
467 | var link = data.payload, | 579 | var link = data.payload, |
468 | - lnk = createLink(link); | 580 | + result = findLink(link, 'add'), |
469 | - if (lnk) { | 581 | + bad = result.badLogic, |
470 | - network.links.push(lnk); | 582 | + ldata = result.ldata; |
471 | - network.lookup[lnk.id] = lnk; | 583 | + |
584 | + if (bad) { | ||
585 | + logicError(bad + ': ' + link.id); | ||
586 | + return; | ||
587 | + } | ||
588 | + | ||
589 | + if (ldata) { | ||
590 | + // we already have a backing store link for src/dst nodes | ||
591 | + addLinkUpdate(ldata, link); | ||
592 | + return; | ||
593 | + } | ||
594 | + | ||
595 | + // no backing store link yet | ||
596 | + ldata = createLink(link); | ||
597 | + if (ldata) { | ||
598 | + network.links.push(ldata); | ||
599 | + network.lookup[ldata.key] = ldata; | ||
472 | updateLinks(); | 600 | updateLinks(); |
473 | network.force.start(); | 601 | network.force.start(); |
474 | } | 602 | } |
... | @@ -511,14 +639,13 @@ | ... | @@ -511,14 +639,13 @@ |
511 | function updateLink(data) { | 639 | function updateLink(data) { |
512 | evTrace(data); | 640 | evTrace(data); |
513 | var link = data.payload, | 641 | var link = data.payload, |
514 | - id = link.id, | 642 | + result = findLink(link, 'update'), |
515 | - linkData = network.lookup[id]; | 643 | + bad = result.badLogic; |
516 | - if (linkData) { | 644 | + if (bad) { |
517 | - $.extend(linkData, link); | 645 | + logicError(bad + ': ' + link.id); |
518 | - updateLinkState(linkData); | 646 | + return; |
519 | - } else { | ||
520 | - logicError('updateLink lookup fail. ID = "' + id + '"'); | ||
521 | } | 647 | } |
648 | + result.updateWith(link); | ||
522 | } | 649 | } |
523 | 650 | ||
524 | function updateHost(data) { | 651 | function updateHost(data) { |
... | @@ -538,13 +665,13 @@ | ... | @@ -538,13 +665,13 @@ |
538 | function removeLink(data) { | 665 | function removeLink(data) { |
539 | evTrace(data); | 666 | evTrace(data); |
540 | var link = data.payload, | 667 | var link = data.payload, |
541 | - id = link.id, | 668 | + result = findLink(link, 'remove'), |
542 | - linkData = network.lookup[id]; | 669 | + bad = result.badLogic; |
543 | - if (linkData) { | 670 | + if (bad) { |
544 | - removeLinkElement(linkData); | 671 | + logicError(bad + ': ' + link.id); |
545 | - } else { | 672 | + return; |
546 | - logicError('removeLink lookup fail. ID = "' + id + '"'); | ||
547 | } | 673 | } |
674 | + result.removeRawLink(); | ||
548 | } | 675 | } |
549 | 676 | ||
550 | function removeHost(data) { | 677 | function removeHost(data) { |
... | @@ -805,11 +932,13 @@ | ... | @@ -805,11 +932,13 @@ |
805 | 932 | ||
806 | // Synthesize link ... | 933 | // Synthesize link ... |
807 | $.extend(lnk, { | 934 | $.extend(lnk, { |
808 | - id: id, | 935 | + key: id, |
809 | class: 'link', | 936 | class: 'link', |
810 | - type: 'hostLink', | 937 | + |
811 | - svgClass: 'link hostLink', | 938 | + type: function () { return 'hostLink'; }, |
812 | - linkWidth: 1 | 939 | + // TODO: ideally, we should see if our edge switch is online... |
940 | + online: function () { return true; }, | ||
941 | + linkWidth: function () { return 1; } | ||
813 | }); | 942 | }); |
814 | return lnk; | 943 | return lnk; |
815 | } | 944 | } |
... | @@ -822,10 +951,29 @@ | ... | @@ -822,10 +951,29 @@ |
822 | return null; | 951 | return null; |
823 | } | 952 | } |
824 | 953 | ||
825 | - // merge in remaining data | 954 | + $.extend(lnk, { |
826 | - $.extend(lnk, link, { | 955 | + key: link.id, |
827 | class: 'link', | 956 | class: 'link', |
828 | - svgClass: (type ? 'link ' + type : 'link') | 957 | + fromSource: link, |
958 | + | ||
959 | + // functions to aggregate dual link state | ||
960 | + type: function () { | ||
961 | + var s = lnk.fromSource, | ||
962 | + t = lnk.fromTarget; | ||
963 | + return (s && s.type) || (t && t.type) || defaultLinkType; | ||
964 | + }, | ||
965 | + online: function () { | ||
966 | + var s = lnk.fromSource, | ||
967 | + t = lnk.fromTarget; | ||
968 | + return (s && s.online) || (t && t.online); | ||
969 | + }, | ||
970 | + linkWidth: function () { | ||
971 | + var s = lnk.fromSource, | ||
972 | + t = lnk.fromTarget, | ||
973 | + ws = (s && s.linkWidth) || 0, | ||
974 | + wt = (t && t.linkWidth) || 0; | ||
975 | + return Math.max(ws, wt); | ||
976 | + } | ||
829 | }); | 977 | }); |
830 | return lnk; | 978 | return lnk; |
831 | } | 979 | } |
... | @@ -836,17 +984,9 @@ | ... | @@ -836,17 +984,9 @@ |
836 | .range([widthRatio, 12 * widthRatio]) | 984 | .range([widthRatio, 12 * widthRatio]) |
837 | .clamp(true); | 985 | .clamp(true); |
838 | 986 | ||
839 | - function updateLinkWidth (d) { | ||
840 | - // TODO: watch out for .showPath/.showTraffic classes | ||
841 | - d.el.transition() | ||
842 | - .duration(1000) | ||
843 | - .attr('stroke-width', linkScale(d.linkWidth)); | ||
844 | - } | ||
845 | - | ||
846 | - | ||
847 | function updateLinks() { | 987 | function updateLinks() { |
848 | link = linkG.selectAll('.link') | 988 | link = linkG.selectAll('.link') |
849 | - .data(network.links, function (d) { return d.id; }); | 989 | + .data(network.links, function (d) { return d.key; }); |
850 | 990 | ||
851 | // operate on existing links, if necessary | 991 | // operate on existing links, if necessary |
852 | // link .foo() .bar() ... | 992 | // link .foo() .bar() ... |
... | @@ -855,19 +995,12 @@ | ... | @@ -855,19 +995,12 @@ |
855 | var entering = link.enter() | 995 | var entering = link.enter() |
856 | .append('line') | 996 | .append('line') |
857 | .attr({ | 997 | .attr({ |
858 | - class: function (d) { return d.svgClass; }, | ||
859 | x1: function (d) { return d.x1; }, | 998 | x1: function (d) { return d.x1; }, |
860 | y1: function (d) { return d.y1; }, | 999 | y1: function (d) { return d.y1; }, |
861 | x2: function (d) { return d.x2; }, | 1000 | x2: function (d) { return d.x2; }, |
862 | y2: function (d) { return d.y2; }, | 1001 | y2: function (d) { return d.y2; }, |
863 | stroke: config.topo.linkInColor, | 1002 | stroke: config.topo.linkInColor, |
864 | 'stroke-width': config.topo.linkInWidth | 1003 | 'stroke-width': config.topo.linkInWidth |
865 | - }) | ||
866 | - .classed('inactive', function(d) { return !d.online; }) | ||
867 | - .transition().duration(1000) | ||
868 | - .attr({ | ||
869 | - 'stroke-width': function (d) { return linkScale(d.linkWidth); }, | ||
870 | - stroke: '#666' // TODO: remove explicit stroke, rather... | ||
871 | }); | 1004 | }); |
872 | 1005 | ||
873 | // augment links | 1006 | // augment links |
... | @@ -875,6 +1008,7 @@ | ... | @@ -875,6 +1008,7 @@ |
875 | var link = d3.select(this); | 1008 | var link = d3.select(this); |
876 | // provide ref to element selection from backing data.... | 1009 | // provide ref to element selection from backing data.... |
877 | d.el = link; | 1010 | d.el = link; |
1011 | + restyleLinkElement(d); | ||
878 | 1012 | ||
879 | // TODO: add src/dst port labels etc. | 1013 | // TODO: add src/dst port labels etc. |
880 | }); | 1014 | }); |
... | @@ -1240,9 +1374,9 @@ | ... | @@ -1240,9 +1374,9 @@ |
1240 | // TODO: device node exits | 1374 | // TODO: device node exits |
1241 | } | 1375 | } |
1242 | 1376 | ||
1243 | - function find(id, array) { | 1377 | + function find(key, array) { |
1244 | for (var idx = 0, n = array.length; idx < n; idx++) { | 1378 | for (var idx = 0, n = array.length; idx < n; idx++) { |
1245 | - if (array[idx].id === id) { | 1379 | + if (array[idx].key === key) { |
1246 | return idx; | 1380 | return idx; |
1247 | } | 1381 | } |
1248 | } | 1382 | } |
... | @@ -1250,15 +1384,17 @@ | ... | @@ -1250,15 +1384,17 @@ |
1250 | } | 1384 | } |
1251 | 1385 | ||
1252 | function removeLinkElement(linkData) { | 1386 | function removeLinkElement(linkData) { |
1253 | - // remove from lookup cache | 1387 | + var idx = find(linkData.key, network.links), |
1254 | - delete network.lookup[linkData.id]; | 1388 | + removed; |
1389 | + if (idx >=0) { | ||
1255 | // remove from links array | 1390 | // remove from links array |
1256 | - var idx = find(linkData.id, network.links); | 1391 | + removed = network.links.splice(idx, 1); |
1257 | - network.links.splice(idx, 1); | 1392 | + // remove from lookup cache |
1258 | - // remove from SVG | 1393 | + delete network.lookup[removed[0].key]; |
1259 | updateLinks(); | 1394 | updateLinks(); |
1260 | network.force.resume(); | 1395 | network.force.resume(); |
1261 | } | 1396 | } |
1397 | + } | ||
1262 | 1398 | ||
1263 | function removeHostElement(hostData) { | 1399 | function removeHostElement(hostData) { |
1264 | // first, remove associated hostLink... | 1400 | // first, remove associated hostLink... | ... | ... |
-
Please register or login to post a comment