Showing
2 changed files
with
156 additions
and
40 deletions
| ... | @@ -87,7 +87,12 @@ | ... | @@ -87,7 +87,12 @@ |
| 87 | padLR: 8, | 87 | padLR: 8, |
| 88 | padTB: 6, | 88 | padTB: 6, |
| 89 | marginLR: 3, | 89 | marginLR: 3, |
| 90 | - marginTB: 2 | 90 | + marginTB: 2, |
| 91 | + port: { | ||
| 92 | + gap: 2, | ||
| 93 | + width: 12, | ||
| 94 | + height: 12 | ||
| 95 | + } | ||
| 91 | }, | 96 | }, |
| 92 | icons: { | 97 | icons: { |
| 93 | w: 32, | 98 | w: 32, |
| ... | @@ -113,7 +118,8 @@ | ... | @@ -113,7 +118,8 @@ |
| 113 | selected = {}, | 118 | selected = {}, |
| 114 | highlighted = null, | 119 | highlighted = null, |
| 115 | hovered = null, | 120 | hovered = null, |
| 116 | - viewMode = 'showAll'; | 121 | + viewMode = 'showAll', |
| 122 | + portLabelsOn = false; | ||
| 117 | 123 | ||
| 118 | 124 | ||
| 119 | function debug(what) { | 125 | function debug(what) { |
| ... | @@ -128,10 +134,13 @@ | ... | @@ -128,10 +134,13 @@ |
| 128 | return urlData().jsonUrl; | 134 | return urlData().jsonUrl; |
| 129 | } | 135 | } |
| 130 | 136 | ||
| 137 | + function safeId(id) { | ||
| 138 | + return id.replace(/[^a-z0-9]/gi, '_'); | ||
| 139 | + } | ||
| 140 | + | ||
| 131 | function detailJsonUrl(id) { | 141 | function detailJsonUrl(id) { |
| 132 | var u = urlData(), | 142 | var u = urlData(), |
| 133 | - encId = config.useLiveData ? encodeURIComponent(id) | 143 | + encId = config.useLiveData ? encodeURIComponent(id) : safeId(id); |
| 134 | - : id.replace(/[^a-z0-9]/gi, '_'); | ||
| 135 | return u.detailPrefix + encId + u.detailSuffix; | 144 | return u.detailPrefix + encId + u.detailSuffix; |
| 136 | } | 145 | } |
| 137 | 146 | ||
| ... | @@ -303,7 +312,9 @@ | ... | @@ -303,7 +312,9 @@ |
| 303 | } | 312 | } |
| 304 | 313 | ||
| 305 | function togglePorts() { | 314 | function togglePorts() { |
| 306 | - console.log('Toggle Ports - context = ' + contextLabel()); | 315 | + portLabelsOn = !portLabelsOn; |
| 316 | + var portVis = portLabelsOn ? 'visible' : 'hidden'; | ||
| 317 | + d3.selectAll('.port').style('visibility', portVis); | ||
| 307 | } | 318 | } |
| 308 | 319 | ||
| 309 | function unpin() { | 320 | function unpin() { |
| ... | @@ -347,7 +358,7 @@ | ... | @@ -347,7 +358,7 @@ |
| 347 | ix = Math.random() * 0.6 * nw + 0.2 * nw, | 358 | ix = Math.random() * 0.6 * nw + 0.2 * nw, |
| 348 | iy = ypc * nh, | 359 | iy = ypc * nh, |
| 349 | node = { | 360 | node = { |
| 350 | - id: n.id, | 361 | + id: safeId(n.id), |
| 351 | labels: n.labels, | 362 | labels: n.labels, |
| 352 | class: 'device', | 363 | class: 'device', |
| 353 | icon: 'device', | 364 | icon: 'device', |
| ... | @@ -369,7 +380,7 @@ | ... | @@ -369,7 +380,7 @@ |
| 369 | ix = Math.random() * 0.6 * nw + 0.2 * nw, | 380 | ix = Math.random() * 0.6 * nw + 0.2 * nw, |
| 370 | iy = ypc * nh, | 381 | iy = ypc * nh, |
| 371 | node = { | 382 | node = { |
| 372 | - id: n.id, | 383 | + id: safeId(n.id), |
| 373 | labels: n.labels, | 384 | labels: n.labels, |
| 374 | class: 'host', | 385 | class: 'host', |
| 375 | icon: 'host', | 386 | icon: 'host', |
| ... | @@ -390,7 +401,7 @@ | ... | @@ -390,7 +401,7 @@ |
| 390 | network.data.links.forEach(function(lnk) { | 401 | network.data.links.forEach(function(lnk) { |
| 391 | var src = network.lookup[lnk.src], | 402 | var src = network.lookup[lnk.src], |
| 392 | dst = network.lookup[lnk.dst], | 403 | dst = network.lookup[lnk.dst], |
| 393 | - id = src.id + "~" + dst.id; | 404 | + id = src.id + "-" + dst.id; |
| 394 | 405 | ||
| 395 | var link = { | 406 | var link = { |
| 396 | class: 'infra', | 407 | class: 'infra', |
| ... | @@ -398,7 +409,9 @@ | ... | @@ -398,7 +409,9 @@ |
| 398 | type: lnk.type, | 409 | type: lnk.type, |
| 399 | width: lnk.linkWidth, | 410 | width: lnk.linkWidth, |
| 400 | source: src, | 411 | source: src, |
| 412 | + srcPort: lnk.srcPort, | ||
| 401 | target: dst, | 413 | target: dst, |
| 414 | + tgtPort: lnk.dstPort, | ||
| 402 | strength: config.force.linkStrength.infra | 415 | strength: config.force.linkStrength.infra |
| 403 | }; | 416 | }; |
| 404 | network.links.push(link); | 417 | network.links.push(link); |
| ... | @@ -408,7 +421,7 @@ | ... | @@ -408,7 +421,7 @@ |
| 408 | network.data.hosts.forEach(function(n) { | 421 | network.data.hosts.forEach(function(n) { |
| 409 | var src = network.lookup[n.id], | 422 | var src = network.lookup[n.id], |
| 410 | dst = network.lookup[n.cp.device], | 423 | dst = network.lookup[n.cp.device], |
| 411 | - id = src.id + "~" + dst.id; | 424 | + id = src.id + "-" + dst.id; |
| 412 | 425 | ||
| 413 | var link = { | 426 | var link = { |
| 414 | class: 'host', | 427 | class: 'host', |
| ... | @@ -490,13 +503,6 @@ | ... | @@ -490,13 +503,6 @@ |
| 490 | // }); | 503 | // }); |
| 491 | 504 | ||
| 492 | 505 | ||
| 493 | - // add links to the display | ||
| 494 | - network.link = network.svg.append('g').selectAll('.link') | ||
| 495 | - .data(network.force.links(), function(d) {return d.id}) | ||
| 496 | - .enter().append('line') | ||
| 497 | - .attr('class', function(d) {return 'link ' + d.class}); | ||
| 498 | - | ||
| 499 | - | ||
| 500 | // TODO: move drag behavior into separate method. | 506 | // TODO: move drag behavior into separate method. |
| 501 | // == define node drag behavior... | 507 | // == define node drag behavior... |
| 502 | network.draggedThreshold = d3.scale.linear() | 508 | network.draggedThreshold = d3.scale.linear() |
| ... | @@ -551,6 +557,53 @@ | ... | @@ -551,6 +557,53 @@ |
| 551 | } | 557 | } |
| 552 | }); | 558 | }); |
| 553 | 559 | ||
| 560 | + // ............................................................... | ||
| 561 | + | ||
| 562 | + // add links to the display | ||
| 563 | + network.link = network.svg.append('g').attr('id', 'links') | ||
| 564 | + .selectAll('.link') | ||
| 565 | + .data(network.force.links(), function(d) {return d.id}) | ||
| 566 | + .enter().append('line') | ||
| 567 | + .attr('class', function(d) {return 'link ' + d.class}); | ||
| 568 | + | ||
| 569 | + network.linkSrcPort = network.svg.append('g') | ||
| 570 | + .attr({ | ||
| 571 | + id: 'srcPorts', | ||
| 572 | + class: 'portLayer' | ||
| 573 | + }); | ||
| 574 | + network.linkTgtPort = network.svg.append('g') | ||
| 575 | + .attr({ | ||
| 576 | + id: 'tgtPorts', | ||
| 577 | + class: 'portLayer' | ||
| 578 | + }); | ||
| 579 | + | ||
| 580 | + var portVis = portLabelsOn ? 'visible' : 'hidden'; | ||
| 581 | + | ||
| 582 | + network.link.filter('.infra').each(function(d, i) { | ||
| 583 | + network.linkSrcPort.append('rect').attr({ | ||
| 584 | + id: 'srcPort-' + d.id, | ||
| 585 | + class: 'port', | ||
| 586 | + width: 12, | ||
| 587 | + height: 12, | ||
| 588 | + x: i * 20, | ||
| 589 | + y: 0 | ||
| 590 | + }) | ||
| 591 | + .style('visibility', portVis) | ||
| 592 | + .append('text').text(d.srcPort); | ||
| 593 | + | ||
| 594 | + network.linkTgtPort.append('rect').attr({ | ||
| 595 | + id: 'tgtPort-' + d.id, | ||
| 596 | + class: 'port', | ||
| 597 | + width: 12, | ||
| 598 | + height: 12, | ||
| 599 | + x: i * 20, | ||
| 600 | + y: 20 | ||
| 601 | + }) | ||
| 602 | + .style('visibility', portVis); | ||
| 603 | + | ||
| 604 | + }); | ||
| 605 | + | ||
| 606 | + // ............................................................... | ||
| 554 | 607 | ||
| 555 | // add nodes to the display | 608 | // add nodes to the display |
| 556 | network.node = network.svg.selectAll('.node') | 609 | network.node = network.svg.selectAll('.node') |
| ... | @@ -659,14 +712,18 @@ | ... | @@ -659,14 +712,18 @@ |
| 659 | 712 | ||
| 660 | // this function is scheduled to happen soon after the given thread ends | 713 | // this function is scheduled to happen soon after the given thread ends |
| 661 | setTimeout(function() { | 714 | setTimeout(function() { |
| 715 | + var lab = config.labels, | ||
| 716 | + portGap = lab.port.gap, | ||
| 717 | + midW = portGap + lab.port.width/ 2, | ||
| 718 | + midH = portGap + lab.port.height / 2; | ||
| 719 | + | ||
| 662 | // post process the device nodes, to pad their size to fit the | 720 | // post process the device nodes, to pad their size to fit the |
| 663 | // label text and attach the icon to the right location. | 721 | // label text and attach the icon to the right location. |
| 664 | network.node.filter('.device').each(function(d) { | 722 | network.node.filter('.device').each(function(d) { |
| 665 | // for every node, recompute size, padding, etc. so text fits | 723 | // for every node, recompute size, padding, etc. so text fits |
| 666 | var node = d3.select(this), | 724 | var node = d3.select(this), |
| 667 | text = node.select('text'), | 725 | text = node.select('text'), |
| 668 | - box = adjustRectToFitText(node), | 726 | + box = adjustRectToFitText(node); |
| 669 | - lab = config.labels; | ||
| 670 | 727 | ||
| 671 | // now make the computed adjustment | 728 | // now make the computed adjustment |
| 672 | node.select('rect') | 729 | node.select('rect') |
| ... | @@ -676,7 +733,13 @@ | ... | @@ -676,7 +733,13 @@ |
| 676 | .attr('x', box.x + config.icons.xoff) | 733 | .attr('x', box.x + config.icons.xoff) |
| 677 | .attr('y', box.y + config.icons.yoff); | 734 | .attr('y', box.y + config.icons.yoff); |
| 678 | 735 | ||
| 679 | - var bounds = boundsFromBox(box); | 736 | + var bounds = boundsFromBox(box), |
| 737 | + portBounds = { | ||
| 738 | + x1: bounds.x1 - midW, | ||
| 739 | + x2: bounds.x2 + midW, | ||
| 740 | + y1: bounds.y1 - midH, | ||
| 741 | + y2: bounds.y2 + midH | ||
| 742 | + }; | ||
| 680 | 743 | ||
| 681 | // todo: clean up extent and edge work.. | 744 | // todo: clean up extent and edge work.. |
| 682 | d.extent = { | 745 | d.extent = { |
| ... | @@ -693,6 +756,21 @@ | ... | @@ -693,6 +756,21 @@ |
| 693 | bottom : new geo.LineSegment(bounds.x1, bounds.y2, bounds.x2, bounds.y2) | 756 | bottom : new geo.LineSegment(bounds.x1, bounds.y2, bounds.x2, bounds.y2) |
| 694 | }; | 757 | }; |
| 695 | 758 | ||
| 759 | + d.portEdge = { | ||
| 760 | + left : new geo.LineSegment( | ||
| 761 | + portBounds.x1, portBounds.y1, portBounds.x1, portBounds.y2 | ||
| 762 | + ), | ||
| 763 | + right : new geo.LineSegment( | ||
| 764 | + portBounds.x2, portBounds.y1, portBounds.x2, portBounds.y2 | ||
| 765 | + ), | ||
| 766 | + top : new geo.LineSegment( | ||
| 767 | + portBounds.x1, portBounds.y1, portBounds.x2, portBounds.y1 | ||
| 768 | + ), | ||
| 769 | + bottom : new geo.LineSegment( | ||
| 770 | + portBounds.x1, portBounds.y2, portBounds.x2, portBounds.y2 | ||
| 771 | + ) | ||
| 772 | + }; | ||
| 773 | + | ||
| 696 | }); | 774 | }); |
| 697 | 775 | ||
| 698 | network.numTicks = 0; | 776 | network.numTicks = 0; |
| ... | @@ -840,39 +918,71 @@ | ... | @@ -840,39 +918,71 @@ |
| 840 | preventCollisions(); | 918 | preventCollisions(); |
| 841 | } | 919 | } |
| 842 | 920 | ||
| 921 | + var portHalfW = config.labels.port.width / 2, | ||
| 922 | + portHalfH = config.labels.port.height / 2; | ||
| 923 | + | ||
| 843 | // clip visualization of links at bounds of nodes... | 924 | // clip visualization of links at bounds of nodes... |
| 844 | network.link.each(function(d) { | 925 | network.link.each(function(d) { |
| 845 | - var xs = d.source.x, | 926 | + var xs = d.source.x, |
| 846 | - ys = d.source.y, | 927 | + ys = d.source.y, |
| 847 | - xt = d.target.x, | 928 | + xt = d.target.x, |
| 848 | - yt = d.target.y, | 929 | + yt = d.target.y, |
| 849 | - line = new geo.LineSegment(xs, ys, xt, yt), | 930 | + line = new geo.LineSegment(xs, ys, xt, yt), |
| 850 | - e, ix; | 931 | + e, ix, |
| 932 | + exs, eys, ext, eyt, | ||
| 933 | + pxs, pys, pxt, pyt; | ||
| 934 | + | ||
| 935 | + if (d.class === 'host') { | ||
| 936 | + // no adjustment for source end of link, since hosts are dots | ||
| 937 | + exs = xs; | ||
| 938 | + eys = ys; | ||
| 851 | 939 | ||
| 940 | + } else { | ||
| 852 | for (e in d.source.edge) { | 941 | for (e in d.source.edge) { |
| 853 | ix = line.intersect(d.source.edge[e].offset(xs, ys)); | 942 | ix = line.intersect(d.source.edge[e].offset(xs, ys)); |
| 854 | if (ix.in1 && ix.in2) { | 943 | if (ix.in1 && ix.in2) { |
| 855 | - xs = ix.x; | 944 | + exs = ix.x; |
| 856 | - ys = ix.y; | 945 | + eys = ix.y; |
| 946 | + | ||
| 947 | + // also pick off the port label intersection | ||
| 948 | + ix = line.intersect(d.source.portEdge[e].offset(xs, ys)); | ||
| 949 | + pxs = ix.x; | ||
| 950 | + pys = ix.y; | ||
| 857 | break; | 951 | break; |
| 858 | } | 952 | } |
| 859 | } | 953 | } |
| 954 | + } | ||
| 860 | 955 | ||
| 861 | - for (e in d.target.edge) { | 956 | + for (e in d.target.edge) { |
| 862 | - ix = line.intersect(d.target.edge[e].offset(xt, yt)); | 957 | + ix = line.intersect(d.target.edge[e].offset(xt, yt)); |
| 863 | - if (ix.in1 && ix.in2) { | 958 | + if (ix.in1 && ix.in2) { |
| 864 | - xt = ix.x; | 959 | + ext = ix.x; |
| 865 | - yt = ix.y; | 960 | + eyt = ix.y; |
| 866 | - break; | 961 | + |
| 867 | - } | 962 | + // also pick off the port label intersection |
| 963 | + ix = line.intersect(d.target.portEdge[e].offset(xt, yt)); | ||
| 964 | + pxt = ix.x; | ||
| 965 | + pyt = ix.y; | ||
| 966 | + break; | ||
| 868 | } | 967 | } |
| 968 | + } | ||
| 869 | 969 | ||
| 870 | - d3.select(this) | 970 | + // adjust the endpoints of the link's line to match rectangles |
| 871 | - .attr('x1', xs) | 971 | + d3.select(this) |
| 872 | - .attr('y1', ys) | 972 | + .attr('x1', exs) |
| 873 | - .attr('x2', xt) | 973 | + .attr('y1', eys) |
| 874 | - .attr('y2', yt); | 974 | + .attr('x2', ext) |
| 875 | - }); | 975 | + .attr('y2', eyt); |
| 976 | + | ||
| 977 | + d3.select('#srcPort-' + d.id) | ||
| 978 | + .attr('x', pxs - portHalfW) | ||
| 979 | + .attr('y', pys - portHalfH); | ||
| 980 | + | ||
| 981 | + d3.select('#tgtPort-' + d.id) | ||
| 982 | + .attr('x', pxt - portHalfW) | ||
| 983 | + .attr('y', pyt - portHalfH); | ||
| 984 | + | ||
| 985 | + }); | ||
| 876 | 986 | ||
| 877 | // position each node by translating the node (group) by x,y | 987 | // position each node by translating the node (group) by x,y |
| 878 | network.node | 988 | network.node | ... | ... |
| ... | @@ -106,6 +106,12 @@ svg .link.host { | ... | @@ -106,6 +106,12 @@ svg .link.host { |
| 106 | Xstroke-dasharray: 3,3; | 106 | Xstroke-dasharray: 3,3; |
| 107 | } | 107 | } |
| 108 | 108 | ||
| 109 | +svg g.portLayer rect.port { | ||
| 110 | + stroke-width: 1; | ||
| 111 | + stroke: black; | ||
| 112 | + fill: white; | ||
| 113 | +} | ||
| 114 | + | ||
| 109 | svg .node.device rect { | 115 | svg .node.device rect { |
| 110 | stroke-width: 1.5px; | 116 | stroke-width: 1.5px; |
| 111 | 117 | ... | ... |
-
Please register or login to post a comment