|
5 | 5 | root.Graph = factory(root.d3, root.SVGWidget, root.IGraph, root.Vertex, root.GraphData, root.GraphLayouts);
|
6 | 6 | }
|
7 | 7 | }(this, function (d3, SVGWidget, IGraph, Vertex, GraphData, GraphLayouts) {
|
| 8 | + function SelectionBag() { |
| 9 | + this.items = {}; |
| 10 | + }; |
| 11 | + |
| 12 | + SelectionBag.prototype = { |
| 13 | + clear: function () { |
| 14 | + for (var key in this.items) { |
| 15 | + this.items[key].element().classed("selected", false); |
| 16 | + } |
| 17 | + this.items = {}; |
| 18 | + }, |
| 19 | + append: function (item) { |
| 20 | + this.items[item._id] = item; |
| 21 | + item.element().classed("selected", true); |
| 22 | + }, |
| 23 | + remove: function (item) { |
| 24 | + this.items[item._id].element().classed("selected", false); |
| 25 | + delete this.items[item._id]; |
| 26 | + }, |
| 27 | + get: function () { |
| 28 | + var retVal = []; |
| 29 | + for (var key in this.items) { |
| 30 | + retVal.push(this.items[key]); |
| 31 | + } |
| 32 | + return retVal; |
| 33 | + }, |
| 34 | + set: function (itemArray) { |
| 35 | + this.clear(); |
| 36 | + itemArray.forEach(function (item, idx) { |
| 37 | + this.append(item); |
| 38 | + }, this); |
| 39 | + }, |
| 40 | + click: function (item, d3Event) { |
| 41 | + if (d3Event.ctrlKey) { |
| 42 | + if (this.items[item._id]) { |
| 43 | + this.remove(item); |
| 44 | + } else { |
| 45 | + this.append(item); |
| 46 | + } |
| 47 | + } else { |
| 48 | + this.clear(); |
| 49 | + this.append(item); |
| 50 | + } |
| 51 | + } |
| 52 | + }; |
| 53 | + |
8 | 54 | function Graph() {
|
9 | 55 | SVGWidget.call(this);
|
10 | 56 | IGraph.call(this);
|
|
27 | 73 | this._hierarchyOptions = { };
|
28 | 74 | this._snapToGrid = 0;
|
29 | 75 | this._allowDragging = true;
|
| 76 | + this._selection = new SelectionBag(); |
30 | 77 | };
|
31 | 78 | Graph.prototype = Object.create(SVGWidget.prototype);
|
32 | 79 | Graph.prototype.implements(IGraph.prototype);
|
|
49 | 96 | return retVal;
|
50 | 97 | };
|
51 | 98 |
|
| 99 | + Graph.prototype.clear = function () { |
| 100 | + this.data({ vertices: [], edges: [], hierarchy: [], merge: false }); |
| 101 | + }; |
| 102 | + |
52 | 103 | Graph.prototype.data = function (_) {
|
53 | 104 | var retVal = SVGWidget.prototype.data.apply(this, arguments);
|
54 | 105 | if (arguments.length) {
|
|
117 | 168 | return this;
|
118 | 169 | };
|
119 | 170 |
|
| 171 | + Graph.prototype.selection = function (_) { |
| 172 | + if (!arguments.length) return this._selection.get(); |
| 173 | + this._selection.set(_); |
| 174 | + return this; |
| 175 | + }; |
| 176 | + |
120 | 177 | Graph.prototype.setZoom = function (translation, scale, transitionDuration) {
|
121 | 178 | if (this.zoom) {
|
122 | 179 | this.zoom.translate(translation);
|
|
216 | 273 | .attr("y", -this._size.height / 2)
|
217 | 274 | .attr("width", this._size.width)
|
218 | 275 | .attr("height", this._size.height)
|
219 |
| - .call(this.zoom) |
220 | 276 | ;
|
221 | 277 |
|
222 | 278 | this.defs = element.append("defs");
|
223 | 279 | this.addMarkers();
|
224 | 280 |
|
| 281 | + element.call(this.zoom); |
| 282 | + |
225 | 283 | this.svg = element.append("g");
|
| 284 | + this.svgC = this.svg.append("g").attr("id", this._id + "C"); |
226 | 285 | this.svgE = this.svg.append("g").attr("id", this._id + "E");
|
227 | 286 | this.svgV = this.svg.append("g").attr("id", this._id + "V");
|
228 | 287 | };
|
|
278 | 337 | this.setZoom(translate, 1, transitionDuration);
|
279 | 338 | };
|
280 | 339 |
|
281 |
| - Graph.prototype.layout = function (_) { |
| 340 | + Graph.prototype.layout = function (_, transitionDuration) { |
282 | 341 | if (!arguments.length) return this._layout;
|
283 | 342 | this._layout = _;
|
284 | 343 | if (this._renderCount) {
|
|
322 | 381 | context._dragging = true;
|
323 | 382 | context.graphData.nodeValues().forEach(function (item) {
|
324 | 383 | var pos = layoutEngine.nodePos(item._id);
|
325 |
| - item.move({ x: pos.x, y: pos.y }, context._transitionDuration); |
| 384 | + item.move({ x: pos.x, y: pos.y }, transitionDuration); |
326 | 385 | if (pos.width && pos.height && !item.width() && !item.height()) {
|
327 | 386 | item
|
328 |
| - .size({ width: pos.width, height: pos.height }, context._transitionDuration) |
| 387 | + .size({ width: pos.width, height: pos.height }, transitionDuration) |
329 | 388 | .render()
|
330 | 389 | ;
|
331 | 390 | }
|
332 | 391 | });
|
333 | 392 | context.graphData.edgeValues().forEach(function (item) {
|
334 | 393 | var points = layoutEngine.edgePoints({v: item._sourceVertex.id(), w: item._targetVertex.id()});
|
335 | 394 | item
|
336 |
| - .points(points, context._transitionDuration) |
| 395 | + .points(points, transitionDuration) |
337 | 396 | ;
|
338 | 397 | });
|
339 | 398 |
|
|
344 | 403 | this._fixIEMarkers();
|
345 | 404 | setTimeout(function() {
|
346 | 405 | context._dragging = false;
|
347 |
| - }, context._transitionDuration + 50); // Prevents highlighting during morph --- |
| 406 | + }, transitionDuration ? transitionDuration + 50 : 50); // Prevents highlighting during morph --- |
348 | 407 | }
|
349 | 408 | }
|
350 | 409 | return this;
|
|
361 | 420 | .attr("class", "graphVertex")
|
362 | 421 | .style("opacity", 1e-6)
|
363 | 422 | // TODO: Events need to be optional ---
|
| 423 | + .on("click.selectionBag", function (d) { |
| 424 | + context._selection.click(d, d3.event); |
| 425 | + }) |
364 | 426 | .on("click", function (d) {
|
365 |
| - context.vertex_click(d); |
| 427 | + d3.event.stopPropagation(); |
| 428 | + context.vertex_click(d, d3.event); |
366 | 429 | })
|
367 | 430 | .on("dblclick", function (d) {
|
368 |
| - context.vertex_dblclick(d); |
| 431 | + d3.event.stopPropagation(); |
| 432 | + context.vertex_dblclick(d, d3.event); |
369 | 433 | })
|
370 | 434 | .on("mouseover", function (d) {
|
371 | 435 | if (context._dragging)
|
|
0 commit comments