/* define a fixed random seed, to avoid to have a different layout on each page reload. change the string to randomize */ (function() { var force, graph, graph_layer, height, l, n, svg, update, width, zoom, _i, _j, _len, _len2, _ref, _ref2; Math.seedrandom('abcde'); width = 960; height = 500; /* create the SVG */ svg = d3.select('body').append('svg').attr('width', width).attr('height', height); /* create some fake data */ graph = { nodes: [ { id: 'A' }, { id: 'B' }, { id: 'C' }, { id: 'D' }, { id: 'E' }, { id: 'F' }, { id: 'G' }, { id: 'H' }, { id: 'I' }, { id: 'J' }, { id: 'K' }, { id: 'L' }, { id: 'M' } ], links: [ { id: 1, source: 'A', target: 'B' }, { id: 2, source: 'B', target: 'C' }, { id: 3, source: 'C', target: 'A' }, { id: 4, source: 'B', target: 'D' }, { id: 5, source: 'D', target: 'C' }, { id: 6, source: 'D', target: 'E' }, { id: 7, source: 'E', target: 'F' }, { id: 8, source: 'F', target: 'G' }, { id: 9, source: 'F', target: 'H' }, { id: 10, source: 'G', target: 'H' }, { id: 11, source: 'G', target: 'I' }, { id: 12, source: 'H', target: 'I' }, { id: 13, source: 'J', target: 'E' }, { id: 14, source: 'J', target: 'L' }, { id: 15, source: 'J', target: 'K' }, { id: 16, source: 'K', target: 'L' }, { id: 17, source: 'L', target: 'M' }, { id: 18, source: 'M', target: 'K' } ] }; /* objectify the graph */ /* resolve node IDs (not optimized at all!) */ _ref = graph.links; for (_i = 0, _len = _ref.length; _i < _len; _i++) { l = _ref[_i]; _ref2 = graph.nodes; for (_j = 0, _len2 = _ref2.length; _j < _len2; _j++) { n = _ref2[_j]; if (l.source === n.id) l.source = n; if (l.target === n.id) l.target = n; } } /* store the graph in a zoomable layer */ graph_layer = svg.append('g'); /* define a zoom behavior */ zoom = d3.behavior.zoom().scaleExtent([1, 10]).on('zoom', function() { /* whenever the user zooms, */ /* modify translation and scale of the zoom group accordingly */ return graph_layer.attr('transform', "translate(" + (zoom.translate()) + ")scale(" + (zoom.scale()) + ")"); }); /* bind the zoom behavior to the main SVG */ svg.call(zoom); /* initialize the force layout */ force = d3.layout.force().size([width, height]).charge(-400).linkDistance(60).on('tick', (function() { /* update nodes and links */ graph_layer.selectAll('.node').attr('transform', function(d) { return "translate(" + d.x + "," + d.y + ")"; }); return graph_layer.selectAll('.link').attr('x1', function(d) { return d.source.x; }).attr('y1', function(d) { return d.source.y; }).attr('x2', function(d) { return d.target.x; }).attr('y2', function(d) { return d.target.y; }); })); update = function() { /* update the layout */ var links, new_nodes, nodes; force.nodes(graph.nodes).links(graph.links).start(); /* create nodes and links */ /* (links are drawn first to make them appear under the nodes) */ /* also, overwrite the selections with their databound version */ links = graph_layer.selectAll('.link').data(graph.links, function(d) { return d.id; }); links.enter().append('line').attr('class', 'link'); links.exit().remove(); /* dragged nodes become fixed */ nodes = graph_layer.selectAll('.node').data(graph.nodes, function(d) { return d.id; }); new_nodes = nodes.enter().append('g').attr('class', 'node'); new_nodes.append('circle').attr('r', 18); /* draw the label */ new_nodes.append('text').text(function(d) { return d.id; }).attr('dy', '0.35em'); return nodes.exit().remove(); }; update(); }).call(this);