// Generated by CoffeeScript 1.10.0 (function() { var R; R = 20; window.NodeLink = Backbone.D3View.extend({ tagName: 'svg', initialize: function() { var defs, graph_layer, throttled_render, zoom, zoomable_layer; this.d3el.classed('node_link', true); defs = this.d3el.append('defs'); defs.append('marker').attr({ id: 'end-arrow', viewBox: '0 0 10 10', refX: 4 + R, refY: 5, orient: 'auto' }).append('path').attr({ d: 'M0,0 L0,10 L10,5 z' }); zoomable_layer = this.d3el.append('g'); zoom = d3.behavior.zoom().scaleExtent([-Infinity, Infinity]).on('zoom', function() { return zoomable_layer.attr({ transform: "translate(" + (zoom.translate()) + ")scale(" + (zoom.scale()) + ")" }); }); this.d3el.call(zoom); graph_layer = zoomable_layer.append('g').attr({ transform: "translate(0," + (-2 * R) + ")" }); this.links_layer = graph_layer.append('g'); this.nodes_layer = graph_layer.append('g'); throttled_render = _.debounce(((function(_this) { return function() { return _this.render(); }; })(this)), 800, { trailing: true }); return this.listenTo(this.model, 'change:graph', throttled_render); }, render: function() { var PAD_MULTIPLIER, d3cola, drag, enter_nodes, graph, height, i, j, k, l, len, len1, len2, len3, len4, len5, levels, links, m, n, nn, nodes, o, p, q, ref, ref1, ref2, ref3, ref4, topological_order, width; width = this.el.getBoundingClientRect().width; height = this.el.getBoundingClientRect().height; graph = this.model.get('graph'); /* store the node index into the node itself */ ref = graph.nodes; for (i = j = 0, len = ref.length; j < len; i = ++j) { n = ref[i]; n.i = i; } /* store neighbor nodes into each node */ ref1 = graph.nodes; for (i = k = 0, len1 = ref1.length; k < len1; i = ++k) { n = ref1[i]; n.in_neighbors = []; n.out_neighbors = []; } ref2 = graph.links; for (m = 0, len2 = ref2.length; m < len2; m++) { l = ref2[m]; l.source.out_neighbors.push(l.target); l.target.in_neighbors.push(l.source); } /* compute longest distances */ topological_order = tsort(graph.links.map(function(l) { return [l.source.i, l.target.i]; })); ref3 = graph.nodes; for (o = 0, len3 = ref3.length; o < len3; o++) { n = ref3[o]; n.longest_dist = -Infinity; } graph.dummy_root.longest_dist = 0; for (p = 0, len4 = topological_order.length; p < len4; p++) { i = topological_order[p]; n = graph.nodes[i]; ref4 = n.out_neighbors; for (q = 0, len5 = ref4.length; q < len5; q++) { nn = ref4[q]; if (nn.longest_dist < n.longest_dist + 1) { nn.longest_dist = n.longest_dist + 1; } } } /* CONSTRAINTS */ graph.constraints = []; /* create the alignment contraints */ levels = _.uniq(graph.nodes.map(function(n) { return n.longest_dist; })); levels.forEach(function(depth) { return graph.constraints.push({ type: 'alignment', axis: 'y', offsets: graph.nodes.filter(function(n) { return n.longest_dist === depth; }).map(function(n) { return { node: n.i, offset: 0 }; }) }); }); PAD_MULTIPLIER = 3.5; /* create the position contraints */ levels.forEach(function(depth, i) { var n1, n2; if (i < levels.length - 1) { n1 = _.find(graph.nodes, function(n) { return n.longest_dist === depth; }); n2 = _.find(graph.nodes, function(n) { return n.longest_dist === depth + 1; }); return graph.constraints.push({ axis: 'y', left: n1.i, right: n2.i, gap: 2 * R }); } }); nodes = this.nodes_layer.selectAll('.node').data(graph.nodes, function(d) { return d.id; }); enter_nodes = nodes.enter().append('g').attr({ "class": 'node', display: function(d) { if (d.dummy) { return 'none'; } else { return null; } } }); enter_nodes.append('circle').attr({ r: R }); enter_nodes.append('text').text(function(d) { return d.id; }).attr({ dy: '0.35em' }); nodes.exit().remove(); links = this.links_layer.selectAll('.link').data(graph.links, function(d) { return d.id; }); links.enter().append('line').attr({ "class": 'link', display: function(d) { if (d.source.dummy || d.target.dummy) { return 'none'; } else { return null; } } }); links.classed('directed', function(d) { return d.directed; }); links.exit().remove(); /* cola layout */ graph.nodes.forEach(function(v) { v.width = 3 * R; return v.height = 3 * R; }); d3cola = cola.d3adaptor().size([width, height]).linkDistance(60).avoidOverlaps(true).constraints(graph.constraints).nodes(graph.nodes).links(graph.links).on('tick', function() { nodes.attr('transform', function(d) { return "translate(" + d.x + "," + d.y + ")"; }); return links.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; }); }); drag = d3cola.drag(); drag.on('dragstart', function() { return d3.event.sourceEvent.stopPropagation(); }); nodes.call(drag); return d3cola.start(100, 30, 30); } }); }).call(this);