forcegraph.js 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  1. // TODO
  2. // - window size
  3. // - avoid sidebar
  4. // - pan to node
  5. // - pan and zoom to link
  6. define(["d3"], function (d3) {
  7. return function (linkScale, sidebar, router) {
  8. var self = this
  9. var vis, link, node, label
  10. var nodesDict, linksDict
  11. var force
  12. function nodeName(d) {
  13. if (d.node && d.node.nodeinfo)
  14. return d.node.nodeinfo.hostname
  15. else
  16. return d.id
  17. }
  18. function dragstart(d) {
  19. d3.event.sourceEvent.stopPropagation()
  20. d.fixed |= 2
  21. }
  22. function dragmove(d) {
  23. d.px = d3.event.x
  24. d.py = d3.event.y
  25. force.resume()
  26. }
  27. function dragend(d) {
  28. d3.event.sourceEvent.stopPropagation()
  29. d.fixed &= 1
  30. }
  31. function panzoom() {
  32. vis.attr("transform",
  33. "translate(" + d3.event.translate + ") "
  34. + "scale(" + d3.event.scale + ")")
  35. }
  36. function tickEvent() {
  37. link.selectAll("line")
  38. .attr("x1", function(d) { return d.source.x })
  39. .attr("y1", function(d) { return d.source.y })
  40. .attr("x2", function(d) { return d.target.x })
  41. .attr("y2", function(d) { return d.target.y })
  42. node
  43. .attr("cx", function(d) { return d.x })
  44. .attr("cy", function(d) { return d.y })
  45. label.attr("transform", function(d) {
  46. return "translate(" + d.x + "," + d.y + ")"
  47. })
  48. }
  49. var el = document.createElement("div")
  50. el.classList.add("graph")
  51. self.div = el
  52. vis = d3.select(el).append("svg")
  53. .attr("pointer-events", "all")
  54. .call(d3.behavior.zoom().on("zoom", panzoom))
  55. .append("g")
  56. vis.append("g").attr("class", "links")
  57. vis.append("g").attr("class", "nodes")
  58. vis.append("g").attr("class", "labels").attr("pointer-events", "none")
  59. force = d3.layout.force()
  60. .size([500, 500])
  61. .charge(-100)
  62. .gravity(0.05)
  63. .friction(0.73)
  64. .theta(0.8)
  65. .linkDistance(70)
  66. .linkStrength(0.2)
  67. .on("tick", tickEvent)
  68. var draggableNode = d3.behavior.drag()
  69. .on("dragstart", dragstart)
  70. .on("drag", dragmove)
  71. .on("dragend", dragend)
  72. self.setData = function (data) {
  73. var links = data.graph.links.filter( function (d) {
  74. return !d.vpn
  75. })
  76. link = vis.select("g.links")
  77. .selectAll("g.link")
  78. .data(links, linkId)
  79. var linkEnter = link.enter().append("g")
  80. .attr("class", "link")
  81. .on("click", function (d) {
  82. if (!d3.event.defaultPrevented)
  83. router.link(d)()
  84. })
  85. linkEnter.append("line")
  86. .append("title")
  87. link.selectAll("line")
  88. .style("stroke", function (d) { return linkScale(d.tq) })
  89. link.selectAll("title").text(showTq)
  90. linksDict = {}
  91. link.each( function (d) {
  92. if (d.source.node && d.target.node)
  93. linksDict[linkId(d)] = this
  94. })
  95. var nodes = data.graph.nodes
  96. node = vis.select("g.nodes")
  97. .selectAll(".node")
  98. .data(nodes,
  99. function(d) {
  100. return d.id
  101. }
  102. )
  103. var nodeEnter = node.enter().append("circle")
  104. .attr("r", 8)
  105. .on("click", function (d) {
  106. if (!d3.event.defaultPrevented)
  107. router.node(d.node)()
  108. })
  109. .call(draggableNode)
  110. node.attr("class", function (d) {
  111. var s = ["node"]
  112. if (!d.node)
  113. s.push("unknown")
  114. return s.join(" ")
  115. })
  116. nodesDict = {}
  117. node.each( function (d) {
  118. if (d.node)
  119. nodesDict[d.node.nodeinfo.node_id] = this
  120. })
  121. label = vis.select("g.labels")
  122. .selectAll("g.label")
  123. .data(data.graph.nodes, function (d) {
  124. return d.id
  125. })
  126. var labelEnter = label.enter()
  127. .append("g")
  128. .attr("class", "label")
  129. labelEnter.append("path").attr("class", "clients")
  130. labelEnter.append("text")
  131. .attr("class", "name")
  132. .attr("text-anchor", "middle")
  133. .attr("y", "21px")
  134. .attr("x", "0px")
  135. label.selectAll("text.name").text(nodeName)
  136. var labelTextWidth = function (e) {
  137. return e.parentNode.querySelector("text").getBBox().width + 3
  138. }
  139. labelEnter.insert("rect", "text")
  140. .attr("y", "10px")
  141. .attr("x", function() { return labelTextWidth(this) / (-2)})
  142. .attr("width", function() { return labelTextWidth(this)})
  143. .attr("height", "15px")
  144. nodeEnter.append("title")
  145. node.selectAll("title").text(nodeName)
  146. force.nodes(nodes)
  147. .links(links)
  148. .alpha(0.1)
  149. .start()
  150. }
  151. self.resetView = function () {
  152. node.classed("highlight", false)
  153. link.classed("highlight", false)
  154. }
  155. self.gotoNode = function (d) {
  156. link.classed("highlight", false)
  157. node.classed("highlight", function (e) {
  158. return e.node === d && d !== undefined
  159. })
  160. }
  161. self.gotoLink = function (d) {
  162. node.classed("highlight", false)
  163. link.classed("highlight", function (e) {
  164. return e === d && d !== undefined
  165. })
  166. }
  167. return self
  168. }
  169. })