PageSourceSearch

https://johnbradford.github.io/rmarkdown-libs/forceNetwork-binding/forceNetwork.js

js johnbradford.github.io collected 2026-10-03 09:24:28 UTC 9,221 bytes, 291 lines download raw bytes

1HTMLWidgets.widget({
2
3  name: "forceNetwork",
4
5  type: "output",
6
7  initialize: function(el, width, height) {
8
9    d3.select(el).append("svg")
10        .attr("width", width)
11        .attr("height", height);
12
13    return d3.forceSimulation();
14  },
15
16  resize: function(el, width, height, force) {
17
18    d3.select(el).select("svg")
19        .attr("width", width)
20        .attr("height", height);
21
22    force.force("center", d3.forceCenter(width / 2, height / 2))
23        .restart();
24  },
25
26  renderValue: function(el, x, force) {
27
28  // Compute the node radius  using the javascript math expression specified
29    function nodeSize(d) {
30            if(options.nodesize){
31                    return eval(options.radiusCalculation);
32
33            }else{
34                    return 6}
35
36    }
37
38
39    // alias options
40    var options = x.options;
41
42    // convert links and nodes data frames to d3 friendly format
43    var links = HTMLWidgets.dataframeToD3(x.links);
44    var nodes = HTMLWidgets.dataframeToD3(x.nodes);
45
46    // create linkedByIndex to quickly search for node neighbors
47    // adapted from: http://stackoverflow.com/a/8780277/4389763
48    var linkedByIndex = {};
49    links.forEach(function(d) {
50      linkedByIndex[d.source + "," + d.target] = 1;
51      linkedByIndex[d.target + "," + d.source] = 1;
52    });
53    function neighboring(a, b) {
54      return linkedByIndex[a.index + "," + b.index];
55    }
56
57    // get the width and height
58    var width = el.offsetWidth;
59    var height = el.offsetHeight;
60
61    var color = eval(options.colourScale);
62
63    // set this up even if zoom = F
64    var zoom = d3.zoom();
65
66    // create d3 force layout
67    force
68      .nodes(d3.values(nodes))
69      .force("link", d3.forceLink(links).distance(options.linkDistance))
70      .force("center", d3.forceCenter(width / 2, height / 2))
71      .force("charge", d3.forceManyBody().strength(options.charge))
72      .on("tick", tick);
73
74    force.alpha(1).restart();
75
76      var drag = d3.drag()
77        .on("start", dragstart)
78        .on("drag", dragged)
79        .on("end", dragended)
80      function dragstart(d) {
81        if (!d3.event.active) force.alphaTarget(0.3).restart();
82        d.fx = d.x;
83        d.fy = d.y;
84      }
85      function dragged(d) {
86        d.fx = d3.event.x;
87        d.fy = d3.event.y;
88      }
89      function dragended(d) {
90        if (!d3.event.active) force.alphaTarget(0);
91        d.fx = null;
92        d.fy = null;
93      }
94
95    // select the svg element and remove existing children
96    var svg = d3.select(el).select("svg");
97    svg.selectAll("*").remove();
98    // add two g layers; the first will be zoom target if zoom = T
99    //  fine to have two g layers even if zoom = F
100    svg = svg
101        .append("g").attr("class","zoom-layer")
102        .append("g")
103
104    // add zooming if requested
105    if (options.zoom) {
106      function redraw() {
107        d3.select(el).select(".zoom-layer")
108          .attr("transform", d3.event.transform);
109      }
110      zoom.on("zoom", redraw)
111
112      d3.select(el).select("svg")
113        .attr("pointer-events", "all")
114        .call(zoom);
115
116    } else {
117      zoom.on("zoom", null);
118    }
119
120    // draw links
121    var link = svg.selectAll(".link")
122      .data(links)
123      .enter().append("line")
124      .attr("class", "link")
125      .style("stroke", function(d) { return d.colour ; })
126      //.style("stroke", options.linkColour)
127      .style("opacity", options.opacity)
128      .style("stroke-width", eval("(" + options.linkWidth + ")"))
129      .on("mouseover", function(d) {
130          d3.select(this)
131            .style("opacity", 1);
132      })
133      .on("mouseout", function(d) {
134          d3.select(this)
135            .style("opacity", options.opacity);
136      });
137
138    if (options.arrows) {
139      link.style("marker-end",  function(d) { return "url(#arrow-" + d.colour + ")"; });
140
141      var linkColoursArr = d3.nest().key(function(d) { return d.colour; }).entries(links);
142
143      svg.append("defs").selectAll("marker")
144          .data(linkColoursArr)
145          .enter().append("marker")
146            .attr("id", function(d) { return "arrow-" + d.key; })
147            .attr("viewBox", "0, -5, 10, 10")
148            .attr("refX", 0)
149            .attr("markerWidth", 4)
150            .attr("markerHeight", 4)
151            .attr("orient", "auto")
152            .style("fill", "context-fill")
153            .style("fill", function(d) { return d.key; })
154            .style("opacity", options.opacity)
155          .append("path")
156            .attr("d", "M0,-5 L10,0 L0,5");
157    }
158
159    // draw nodes
160    var node = svg.selectAll(".node")
161      .data(force.nodes())
162      .enter().append("g")
163      .attr("class", "node")
164      .style("fill", function(d) { return color(d.group); })
165      .style("opacity", options.opacity)
166      .on("mouseover", mouseover)
167      .on("mouseout", mouseout)
168      .on("click", click)
169      .call(drag);
170
171    node.append("circle")
172      .attr("r", function(d){return nodeSize(d);})
173      .style("stroke", "#fff")
174      .style("opacity", options.opacity)
175      .style("stroke-width", "1.5px");
176
177    node.append("svg:text")
178      .attr("class", "nodetext")
179      .attr("dx", 12)
180      .attr("dy", ".35em")
181      .text(function(d) { return d.name })
182      .style("font", options.fontSize + "px " + options.fontFamily)
183      .style("opacity", options.opacityNoHover)
184      .style("pointer-events", "none");
185
186    function tick() {
187      node.attr("transform", function(d) {
188        if(options.bounded){ // adds bounding box
189            d.x = Math.max(nodeSize(d), Math.min(width - nodeSize(d), d.x));
190            d.y = Math.max(nodeSize(d), Math.min(height - nodeSize(d), d.y));
191        }
192
193        return "translate(" + d.x + "," + d.y + ")"});
194
195      function idx(d, type) {
196        var linkWidthFunc = eval("(" + options.linkWidth + ")");
197			  var a = d.target.x - d.source.x;
198			  var b = d.target.y - d.source.y;
199			  var c = Math.sqrt(Math.pow(a, 2) + Math.pow(b, 2));
200  			if (type == "x1") return (d.source.x + ((nodeSize(d.source) * a) / c));
201  			if (type == "y1") return (d.source.y + ((nodeSize(d.source) * b) / c));
202  			if (options.arrows) {
203  			  if (type == "x2") return (d.target.x - ((((5 * linkWidthFunc(d)) + nodeSize(d.target)) * a) / c));
204  			  if (type == "y2") return (d.target.y - ((((5 * linkWidthFunc(d)) + nodeSize(d.target)) * b) / c));
205  			} else {
206  			  if (type == "x2") return (d.target.x - ((nodeSize(d.target) * a) / c));
207  			  if (type == "y2") return (d.target.y - ((nodeSize(d.target) * b) / c));
208  			}
209		  }
210
211      link
212        .attr("x1", function(d) { return idx(d, "x1"); })
213        .attr("y1", function(d) { return idx(d, "y1"); })
214        .attr("x2", function(d) { return idx(d, "x2"); })
215        .attr("y2", function(d) { return idx(d, "y2"); });
216    }
217
218    function mouseover(d) {
219      // unfocus non-connected links and nodes
220      //if (options.focusOnHover) {
221        var unfocusDivisor = 4;
222
223        link.transition().duration(200)
224          .style("opacity", function(l) { return d != l.source && d != l.target ? +options.opacity / unfocusDivisor : +options.opacity });
225
226        node.transition().duration(200)
227          .style("opacity", function(o) { return d.index == o.index || neighboring(d, o) ? +options.opacity : +options.opacity / unfocusDivisor; });
228      //}
229
230      d3.select(this).select("circle").transition()
231        .duration(750)
232        .attr("r", function(d){return nodeSize(d)+5;});
233      d3.select(this).select("text").transition()
234        .duration(750)
235        .attr("x", 13)
236        .style("stroke-width", ".5px")
237        .style("font", options.clickTextSize + "px ")
238        .style("opacity", 1);
239    }
240
241    function mouseout() {
242      node.style("opacity", +options.opacity);
243      link.style("opacity", +options.opacity);
244
245      d3.select(this).select("circle").transition()
246        .duration(750)
247        .attr("r", function(d){return nodeSize(d);});
248      d3.select(this).select("text").transition()
249        .duration(1250)
250        .attr("x", 0)
251        .style("font", options.fontSize + "px ")
252        .style("opacity", options.opacityNoHover);
253    }
254
255    function click(d) {
256      return eval(options.clickAction)
257    }
258
259    // add legend option
260    if(options.legend){
261        var legendRectSize = 18;
262        var legendSpacing = 4;
263        var legend = svg.selectAll('.legend')
264          .data(color.domain())
265          .enter()
266          .append('g')
267          .attr('class', 'legend')
268          .attr('transform', function(d, i) {
269            var height = legendRectSize + legendSpacing;
270            var offset =  height * color.domain().length / 2;
271            var horz = legendRectSize;
272            var vert = i * height+4;
273            return 'translate(' + horz + ',' + vert + ')';
274          });
275
276        legend.append('rect')
277          .attr('width', legendRectSize)
278          .attr('height', legendRectSize)
279          .style('fill', color)
280          .style('stroke', color);
281
282        legend.append('text')
283          .attr('x', legendRectSize + legendSpacing)
284          .attr('y', legendRectSize - legendSpacing)
285          .text(function(d) { return d; });
286    }
287
288    // make font-family consistent across all elements
289    d3.select(el).selectAll('text').style('font-family', options.fontFamily);
290  },
291});

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.