vis-rails 1.0.2 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1 -1
  3. data/README.md +2 -0
  4. data/lib/vis/rails/version.rb +1 -1
  5. data/vendor/assets/javascripts/module/exports-only-timeline.js +55 -0
  6. data/vendor/assets/javascripts/vis-only-timeline.js +23 -0
  7. data/vendor/assets/javascripts/vis.js +3 -3
  8. data/vendor/assets/stylesheets/vis-only-timeline.css +3 -0
  9. data/vendor/assets/vis/DataSet.js +106 -130
  10. data/vendor/assets/vis/DataView.js +35 -37
  11. data/vendor/assets/vis/graph/Edge.js +225 -45
  12. data/vendor/assets/vis/graph/Graph.js +120 -24
  13. data/vendor/assets/vis/graph/Node.js +16 -16
  14. data/vendor/assets/vis/graph/graphMixins/HierarchicalLayoutMixin.js +1 -1
  15. data/vendor/assets/vis/graph/graphMixins/ManipulationMixin.js +143 -0
  16. data/vendor/assets/vis/graph/graphMixins/SelectionMixin.js +81 -3
  17. data/vendor/assets/vis/graph3d/Graph3d.js +3306 -0
  18. data/vendor/assets/vis/module/exports.js +2 -3
  19. data/vendor/assets/vis/timeline/Range.js +93 -80
  20. data/vendor/assets/vis/timeline/Timeline.js +525 -428
  21. data/vendor/assets/vis/timeline/component/Component.js +19 -53
  22. data/vendor/assets/vis/timeline/component/CurrentTime.js +57 -25
  23. data/vendor/assets/vis/timeline/component/CustomTime.js +55 -19
  24. data/vendor/assets/vis/timeline/component/Group.js +47 -50
  25. data/vendor/assets/vis/timeline/component/ItemSet.js +402 -206
  26. data/vendor/assets/vis/timeline/component/TimeAxis.js +112 -169
  27. data/vendor/assets/vis/timeline/component/css/animation.css +33 -0
  28. data/vendor/assets/vis/timeline/component/css/currenttime.css +1 -1
  29. data/vendor/assets/vis/timeline/component/css/customtime.css +1 -1
  30. data/vendor/assets/vis/timeline/component/css/item.css +1 -11
  31. data/vendor/assets/vis/timeline/component/css/itemset.css +13 -18
  32. data/vendor/assets/vis/timeline/component/css/labelset.css +8 -6
  33. data/vendor/assets/vis/timeline/component/css/panel.css +56 -13
  34. data/vendor/assets/vis/timeline/component/css/timeaxis.css +15 -8
  35. data/vendor/assets/vis/timeline/component/item/Item.js +16 -15
  36. data/vendor/assets/vis/timeline/component/item/ItemBox.js +30 -30
  37. data/vendor/assets/vis/timeline/component/item/ItemPoint.js +20 -21
  38. data/vendor/assets/vis/timeline/component/item/ItemRange.js +23 -24
  39. data/vendor/assets/vis/timeline/component/item/ItemRangeOverflow.js +10 -10
  40. data/vendor/assets/vis/timeline/stack.js +5 -5
  41. data/vendor/assets/vis/util.js +81 -35
  42. metadata +7 -4
  43. data/vendor/assets/vis/timeline/component/Panel.js +0 -170
  44. data/vendor/assets/vis/timeline/component/RootPanel.js +0 -176
@@ -23,14 +23,14 @@ function Graph (container, data, options) {
23
23
  this.renderTimestep = 1000 / this.renderRefreshRate; // ms -- saves calculation later on
24
24
  this.renderTime = 0.5 * this.renderTimestep; // measured time it takes to render a frame
25
25
  this.maxPhysicsTicksPerRender = 3; // max amount of physics ticks per render step.
26
- this.physicsDiscreteStepsize = 0.65; // discrete stepsize of the simulation
26
+ this.physicsDiscreteStepsize = 0.50; // discrete stepsize of the simulation
27
27
 
28
28
  this.stabilize = true; // stabilize before displaying the graph
29
29
  this.selectable = true;
30
30
  this.initializing = true;
31
31
 
32
32
  // these functions are triggered when the dataset is edited
33
- this.triggerFunctions = {add:null,edit:null,connect:null,del:null};
33
+ this.triggerFunctions = {add:null,edit:null,editEdge:null,connect:null,del:null};
34
34
 
35
35
  // set constant values
36
36
  this.constants = {
@@ -53,6 +53,10 @@ function Graph (container, data, options) {
53
53
  highlight: {
54
54
  border: '#2B7CE9',
55
55
  background: '#D2E5FF'
56
+ },
57
+ hover: {
58
+ border: '#2B7CE9',
59
+ background: '#D2E5FF'
56
60
  }
57
61
  },
58
62
  borderColor: '#2B7CE9',
@@ -64,10 +68,12 @@ function Graph (container, data, options) {
64
68
  widthMin: 1,
65
69
  widthMax: 15,
66
70
  width: 1,
71
+ hoverWidth: 1.5,
67
72
  style: 'line',
68
73
  color: {
69
74
  color:'#848484',
70
- highlight:'#848484'
75
+ highlight:'#848484',
76
+ hover: '#848484'
71
77
  },
72
78
  fontColor: '#343434',
73
79
  fontSize: 14, // px
@@ -160,9 +166,11 @@ function Graph (container, data, options) {
160
166
  link:"Add Link",
161
167
  del:"Delete selected",
162
168
  editNode:"Edit Node",
169
+ editEdge:"Edit Edge",
163
170
  back:"Back",
164
171
  addDescription:"Click in an empty space to place a new node.",
165
172
  linkDescription:"Click on a node and drag the edge to another node to connect them.",
173
+ editEdgeDescription:"Click on the control points and drag them to a node to connect to it.",
166
174
  addError:"The function for add does not support two arguments (data,callback).",
167
175
  linkError:"The function for connect does not support two arguments (data,callback).",
168
176
  editError:"The function for edit does not support two arguments (data, callback).",
@@ -180,10 +188,13 @@ function Graph (container, data, options) {
180
188
  background: '#FFFFC6'
181
189
  }
182
190
  },
183
- moveable: true,
184
- zoomable: true
191
+ dragGraph: true,
192
+ dragNodes: true,
193
+ zoomable: true,
194
+ hover: false
185
195
  };
186
- this.editMode = this.constants.dataManipulation.initiallyVisible;
196
+ this.hoverObj = {nodes:{},edges:{}};
197
+
187
198
 
188
199
  // Node variables
189
200
  var graph = this;
@@ -217,7 +228,6 @@ function Graph (container, data, options) {
217
228
  this._setScale(1);
218
229
  this.setOptions(options);
219
230
 
220
-
221
231
  // other vars
222
232
  this.freezeSimulation = false;// freeze the simulation
223
233
  this.cachedFunctions = {};
@@ -521,9 +531,10 @@ Graph.prototype.setOptions = function (options) {
521
531
  if (options.freezeForStabilization !== undefined) {this.constants.freezeForStabilization = options.freezeForStabilization;}
522
532
  if (options.configurePhysics !== undefined){this.constants.configurePhysics = options.configurePhysics;}
523
533
  if (options.stabilizationIterations !== undefined) {this.constants.stabilizationIterations = options.stabilizationIterations;}
524
- if (options.moveable !== undefined) {this.constants.moveable = options.moveable;}
525
- if (options.zoomable !== undefined) {this.constants.zoomable = options.zoomable;}
526
-
534
+ if (options.dragGraph !== undefined) {this.constants.dragGraph = options.dragGraph;}
535
+ if (options.dragNodes !== undefined) {this.constants.dragNodes = options.dragNodes;}
536
+ if (options.zoomable !== undefined) {this.constants.zoomable = options.zoomable;}
537
+ if (options.hover !== undefined) {this.constants.hover = options.hover;}
527
538
 
528
539
  if (options.labels !== undefined) {
529
540
  for (prop in options.labels) {
@@ -541,6 +552,10 @@ Graph.prototype.setOptions = function (options) {
541
552
  this.triggerFunctions.edit = options.onEdit;
542
553
  }
543
554
 
555
+ if (options.onEditEdge) {
556
+ this.triggerFunctions.editEdge = options.onEditEdge;
557
+ }
558
+
544
559
  if (options.onConnect) {
545
560
  this.triggerFunctions.connect = options.onConnect;
546
561
  }
@@ -635,6 +650,7 @@ Graph.prototype.setOptions = function (options) {
635
650
  this.constants.dataManipulation[prop] = options.dataManipulation[prop];
636
651
  }
637
652
  }
653
+ this.editMode = this.constants.dataManipulation.initiallyVisible;
638
654
  }
639
655
  else if (options.dataManipulation !== undefined) {
640
656
  this.constants.dataManipulation.enabled = false;
@@ -656,10 +672,12 @@ Graph.prototype.setOptions = function (options) {
656
672
  this.constants.edges.color = {};
657
673
  this.constants.edges.color.color = options.edges.color;
658
674
  this.constants.edges.color.highlight = options.edges.color;
675
+ this.constants.edges.color.hover = options.edges.color;
659
676
  }
660
677
  else {
661
678
  if (options.edges.color.color !== undefined) {this.constants.edges.color.color = options.edges.color.color;}
662
679
  if (options.edges.color.highlight !== undefined) {this.constants.edges.color.highlight = options.edges.color.highlight;}
680
+ if (options.edges.color.hover !== undefined) {this.constants.edges.color.hover = options.edges.color.hover;}
663
681
  }
664
682
  }
665
683
 
@@ -946,7 +964,7 @@ Graph.prototype._handleOnDrag = function(event) {
946
964
  var me = this,
947
965
  drag = this.drag,
948
966
  selection = drag.selection;
949
- if (selection && selection.length) {
967
+ if (selection && selection.length && this.constants.dragNodes == true) {
950
968
  // calculate delta's and new location
951
969
  var deltaX = pointer.x - drag.pointer.x,
952
970
  deltaY = pointer.y - drag.pointer.y;
@@ -971,7 +989,7 @@ Graph.prototype._handleOnDrag = function(event) {
971
989
  }
972
990
  }
973
991
  else {
974
- if (this.constants.moveable == true) {
992
+ if (this.constants.dragGraph == true) {
975
993
  // move the graph
976
994
  var diffX = pointer.x - this.drag.pointer.x;
977
995
  var diffY = pointer.y - this.drag.pointer.y;
@@ -1159,7 +1177,7 @@ Graph.prototype._onMouseMoveTitle = function (event) {
1159
1177
  var pointer = this._getPointer(gesture.center);
1160
1178
 
1161
1179
  // check if the previously selected node is still selected
1162
- if (this.popupNode) {
1180
+ if (this.popupObj) {
1163
1181
  this._checkHidePopup(pointer);
1164
1182
  }
1165
1183
 
@@ -1175,6 +1193,40 @@ Graph.prototype._onMouseMoveTitle = function (event) {
1175
1193
  if (!this.drag.dragging) {
1176
1194
  this.popupTimer = setTimeout(checkShow, this.constants.tooltip.delay);
1177
1195
  }
1196
+
1197
+
1198
+ /**
1199
+ * Adding hover highlights
1200
+ */
1201
+ if (this.constants.hover == true) {
1202
+ // removing all hover highlights
1203
+ for (var edgeId in this.hoverObj.edges) {
1204
+ if (this.hoverObj.edges.hasOwnProperty(edgeId)) {
1205
+ this.hoverObj.edges[edgeId].hover = false;
1206
+ delete this.hoverObj.edges[edgeId];
1207
+ }
1208
+ }
1209
+
1210
+ // adding hover highlights
1211
+ var obj = this._getNodeAt(pointer);
1212
+ if (obj == null) {
1213
+ obj = this._getEdgeAt(pointer);
1214
+ }
1215
+ if (obj != null) {
1216
+ this._hoverObject(obj);
1217
+ }
1218
+
1219
+ // removing all node hover highlights except for the selected one.
1220
+ for (var nodeId in this.hoverObj.nodes) {
1221
+ if (this.hoverObj.nodes.hasOwnProperty(nodeId)) {
1222
+ if (obj instanceof Node && obj.id != nodeId || obj instanceof Edge || obj == null) {
1223
+ this._blurObject(this.hoverObj.nodes[nodeId]);
1224
+ delete this.hoverObj.nodes[nodeId];
1225
+ }
1226
+ }
1227
+ }
1228
+ this.redraw();
1229
+ }
1178
1230
  };
1179
1231
 
1180
1232
  /**
@@ -1194,23 +1246,23 @@ Graph.prototype._checkShowPopup = function (pointer) {
1194
1246
  };
1195
1247
 
1196
1248
  var id;
1197
- var lastPopupNode = this.popupNode;
1249
+ var lastPopupNode = this.popupObj;
1198
1250
 
1199
- if (this.popupNode == undefined) {
1251
+ if (this.popupObj == undefined) {
1200
1252
  // search the nodes for overlap, select the top one in case of multiple nodes
1201
1253
  var nodes = this.nodes;
1202
1254
  for (id in nodes) {
1203
1255
  if (nodes.hasOwnProperty(id)) {
1204
1256
  var node = nodes[id];
1205
1257
  if (node.getTitle() !== undefined && node.isOverlappingWith(obj)) {
1206
- this.popupNode = node;
1258
+ this.popupObj = node;
1207
1259
  break;
1208
1260
  }
1209
1261
  }
1210
1262
  }
1211
1263
  }
1212
1264
 
1213
- if (this.popupNode === undefined) {
1265
+ if (this.popupObj === undefined) {
1214
1266
  // search the edges for overlap
1215
1267
  var edges = this.edges;
1216
1268
  for (id in edges) {
@@ -1218,16 +1270,16 @@ Graph.prototype._checkShowPopup = function (pointer) {
1218
1270
  var edge = edges[id];
1219
1271
  if (edge.connected && (edge.getTitle() !== undefined) &&
1220
1272
  edge.isOverlappingWith(obj)) {
1221
- this.popupNode = edge;
1273
+ this.popupObj = edge;
1222
1274
  break;
1223
1275
  }
1224
1276
  }
1225
1277
  }
1226
1278
  }
1227
1279
 
1228
- if (this.popupNode) {
1280
+ if (this.popupObj) {
1229
1281
  // show popup message window
1230
- if (this.popupNode != lastPopupNode) {
1282
+ if (this.popupObj != lastPopupNode) {
1231
1283
  var me = this;
1232
1284
  if (!me.popup) {
1233
1285
  me.popup = new Popup(me.frame, me.constants.tooltip);
@@ -1237,7 +1289,7 @@ Graph.prototype._checkShowPopup = function (pointer) {
1237
1289
  // bottom left location of the popup, and you can easily move over the
1238
1290
  // popup area
1239
1291
  me.popup.setPosition(pointer.x - 3, pointer.y - 3);
1240
- me.popup.setText(me.popupNode.getTitle());
1292
+ me.popup.setText(me.popupObj.getTitle());
1241
1293
  me.popup.show();
1242
1294
  }
1243
1295
  }
@@ -1256,8 +1308,8 @@ Graph.prototype._checkShowPopup = function (pointer) {
1256
1308
  * @private
1257
1309
  */
1258
1310
  Graph.prototype._checkHidePopup = function (pointer) {
1259
- if (!this.popupNode || !this._getNodeAt(pointer) ) {
1260
- this.popupNode = undefined;
1311
+ if (!this.popupObj || !this._getNodeAt(pointer) ) {
1312
+ this.popupObj = undefined;
1261
1313
  if (this.popup) {
1262
1314
  this.popup.hide();
1263
1315
  }
@@ -1633,7 +1685,6 @@ Graph.prototype._updateValueRange = function(obj) {
1633
1685
  */
1634
1686
  Graph.prototype.redraw = function() {
1635
1687
  this.setSize(this.width, this.height);
1636
-
1637
1688
  this._redraw();
1638
1689
  };
1639
1690
 
@@ -1665,6 +1716,7 @@ Graph.prototype._redraw = function() {
1665
1716
  this._doInAllSectors("_drawAllSectorNodes",ctx);
1666
1717
  this._doInAllSectors("_drawEdges",ctx);
1667
1718
  this._doInAllSectors("_drawNodes",ctx,false);
1719
+ this._doInAllSectors("_drawControlNodes",ctx);
1668
1720
 
1669
1721
  // this._doInSupportSector("_drawNodes",ctx,true);
1670
1722
  // this._drawTree(ctx,"#F00F0F");
@@ -1849,6 +1901,21 @@ Graph.prototype._drawEdges = function(ctx) {
1849
1901
  }
1850
1902
  };
1851
1903
 
1904
+ /**
1905
+ * Redraw all edges
1906
+ * The 2d context of a HTML canvas can be retrieved by canvas.getContext('2d');
1907
+ * @param {CanvasRenderingContext2D} ctx
1908
+ * @private
1909
+ */
1910
+ Graph.prototype._drawControlNodes = function(ctx) {
1911
+ var edges = this.edges;
1912
+ for (var id in edges) {
1913
+ if (edges.hasOwnProperty(id)) {
1914
+ edges[id]._drawControlNodes(ctx);
1915
+ }
1916
+ }
1917
+ };
1918
+
1852
1919
  /**
1853
1920
  * Find a stable position for all nodes
1854
1921
  * @private
@@ -2183,7 +2250,36 @@ Graph.prototype.storePosition = function() {
2183
2250
  };
2184
2251
 
2185
2252
 
2253
+ /**
2254
+ * Center a node in view.
2255
+ *
2256
+ * @param {Number} nodeId
2257
+ * @param {Number} [zoomLevel]
2258
+ */
2259
+ Graph.prototype.focusOnNode = function (nodeId, zoomLevel) {
2260
+ if (this.nodes.hasOwnProperty(nodeId)) {
2261
+ if (zoomLevel === undefined) {
2262
+ zoomLevel = this._getScale();
2263
+ }
2264
+ var nodePosition= {x: this.nodes[nodeId].x, y: this.nodes[nodeId].y};
2265
+
2266
+ var requiredScale = zoomLevel;
2267
+ this._setScale(requiredScale);
2268
+
2269
+ var canvasCenter = this.DOMtoCanvas({x:0.5 * this.frame.canvas.width,y:0.5 * this.frame.canvas.height});
2270
+ var translation = this._getTranslation();
2186
2271
 
2272
+ var distanceFromCenter = {x:canvasCenter.x - nodePosition.x,
2273
+ y:canvasCenter.y - nodePosition.y};
2274
+
2275
+ this._setTranslation(translation.x + requiredScale * distanceFromCenter.x,
2276
+ translation.y + requiredScale * distanceFromCenter.y);
2277
+ this.redraw();
2278
+ }
2279
+ else {
2280
+ console.log("This nodeId cannot be found.")
2281
+ }
2282
+ };
2187
2283
 
2188
2284
 
2189
2285
 
@@ -25,13 +25,14 @@
25
25
  */
26
26
  function Node(properties, imagelist, grouplist, constants) {
27
27
  this.selected = false;
28
+ this.hover = false;
28
29
 
29
30
  this.edges = []; // all edges connected to this node
30
31
  this.dynamicEdges = [];
31
32
  this.reroutedEdges = {};
32
- this.group = constants.nodes.group;
33
33
 
34
- this.fontSize = constants.nodes.fontSize;
34
+ this.group = constants.nodes.group;
35
+ this.fontSize = Number(constants.nodes.fontSize);
35
36
  this.fontFace = constants.nodes.fontFace;
36
37
  this.fontColor = constants.nodes.fontColor;
37
38
  this.fontDrawThreshold = 3;
@@ -569,7 +570,7 @@ Node.prototype._drawBox = function (ctx) {
569
570
  var clusterLineWidth = 2.5;
570
571
  var selectionLineWidth = 2;
571
572
 
572
- ctx.strokeStyle = this.selected ? this.color.highlight.border : this.color.border;
573
+ ctx.strokeStyle = this.selected ? this.color.highlight.border : this.hover ? this.color.hover.border : this.color.border;
573
574
 
574
575
  // draw the outer border
575
576
  if (this.clusterSize > 1) {
@@ -618,7 +619,7 @@ Node.prototype._drawDatabase = function (ctx) {
618
619
  var clusterLineWidth = 2.5;
619
620
  var selectionLineWidth = 2;
620
621
 
621
- ctx.strokeStyle = this.selected ? this.color.highlight.border : this.color.border;
622
+ ctx.strokeStyle = this.selected ? this.color.highlight.border : this.hover ? this.color.hover.border : this.color.border;
622
623
 
623
624
  // draw the outer border
624
625
  if (this.clusterSize > 1) {
@@ -633,7 +634,7 @@ Node.prototype._drawDatabase = function (ctx) {
633
634
  ctx.lineWidth *= this.graphScaleInv;
634
635
  ctx.lineWidth = Math.min(0.1 * this.width,ctx.lineWidth);
635
636
 
636
- ctx.fillStyle = this.selected ? this.color.highlight.background : this.color.background;
637
+ ctx.fillStyle = this.selected ? this.color.highlight.background : this.hover ? this.color.hover.background : this.color.background;
637
638
  ctx.database(this.x - this.width/2, this.y - this.height*0.5, this.width, this.height);
638
639
  ctx.fill();
639
640
  ctx.stroke();
@@ -668,7 +669,7 @@ Node.prototype._drawCircle = function (ctx) {
668
669
  var clusterLineWidth = 2.5;
669
670
  var selectionLineWidth = 2;
670
671
 
671
- ctx.strokeStyle = this.selected ? this.color.highlight.border : this.color.border;
672
+ ctx.strokeStyle = this.selected ? this.color.highlight.border : this.hover ? this.color.hover.border : this.color.border;
672
673
 
673
674
  // draw the outer border
674
675
  if (this.clusterSize > 1) {
@@ -683,7 +684,7 @@ Node.prototype._drawCircle = function (ctx) {
683
684
  ctx.lineWidth *= this.graphScaleInv;
684
685
  ctx.lineWidth = Math.min(0.1 * this.width,ctx.lineWidth);
685
686
 
686
- ctx.fillStyle = this.selected ? this.color.highlight.background : this.color.background;
687
+ ctx.fillStyle = this.selected ? this.color.highlight.background : this.hover ? this.color.hover.background : this.color.background;
687
688
  ctx.circle(this.x, this.y, this.radius);
688
689
  ctx.fill();
689
690
  ctx.stroke();
@@ -718,7 +719,7 @@ Node.prototype._drawEllipse = function (ctx) {
718
719
  var clusterLineWidth = 2.5;
719
720
  var selectionLineWidth = 2;
720
721
 
721
- ctx.strokeStyle = this.selected ? this.color.highlight.border : this.color.border;
722
+ ctx.strokeStyle = this.selected ? this.color.highlight.border : this.hover ? this.color.hover.border : this.color.border;
722
723
 
723
724
  // draw the outer border
724
725
  if (this.clusterSize > 1) {
@@ -733,7 +734,7 @@ Node.prototype._drawEllipse = function (ctx) {
733
734
  ctx.lineWidth *= this.graphScaleInv;
734
735
  ctx.lineWidth = Math.min(0.1 * this.width,ctx.lineWidth);
735
736
 
736
- ctx.fillStyle = this.selected ? this.color.highlight.background : this.color.background;
737
+ ctx.fillStyle = this.selected ? this.color.highlight.background : this.hover ? this.color.hover.background : this.color.background;
737
738
 
738
739
  ctx.ellipse(this.left, this.top, this.width, this.height);
739
740
  ctx.fill();
@@ -795,7 +796,7 @@ Node.prototype._drawShape = function (ctx, shape) {
795
796
  case 'star': radiusMultiplier = 4; break;
796
797
  }
797
798
 
798
- ctx.strokeStyle = this.selected ? this.color.highlight.border : this.color.border;
799
+ ctx.strokeStyle = this.selected ? this.color.highlight.border : this.hover ? this.color.hover.border : this.color.border;
799
800
 
800
801
  // draw the outer border
801
802
  if (this.clusterSize > 1) {
@@ -810,8 +811,7 @@ Node.prototype._drawShape = function (ctx, shape) {
810
811
  ctx.lineWidth *= this.graphScaleInv;
811
812
  ctx.lineWidth = Math.min(0.1 * this.width,ctx.lineWidth);
812
813
 
813
- ctx.fillStyle = this.selected ? this.color.highlight.background : this.color.background;
814
-
814
+ ctx.fillStyle = this.selected ? this.color.highlight.background : this.hover ? this.color.hover.background : this.color.background;
815
815
  ctx[shape](this.x, this.y, this.radius);
816
816
  ctx.fill();
817
817
  ctx.stroke();
@@ -892,10 +892,10 @@ Node.prototype.getTextSize = function(ctx) {
892
892
  */
893
893
  Node.prototype.inArea = function() {
894
894
  if (this.width !== undefined) {
895
- return (this.x + this.width*this.graphScaleInv >= this.canvasTopLeft.x &&
896
- this.x - this.width*this.graphScaleInv < this.canvasBottomRight.x &&
897
- this.y + this.height*this.graphScaleInv >= this.canvasTopLeft.y &&
898
- this.y - this.height*this.graphScaleInv < this.canvasBottomRight.y);
895
+ return (this.x + this.width *this.graphScaleInv >= this.canvasTopLeft.x &&
896
+ this.x - this.width *this.graphScaleInv < this.canvasBottomRight.x &&
897
+ this.y + this.height*this.graphScaleInv >= this.canvasTopLeft.y &&
898
+ this.y - this.height*this.graphScaleInv < this.canvasBottomRight.y);
899
899
  }
900
900
  else {
901
901
  return true;
@@ -20,7 +20,7 @@ var HierarchicalLayoutMixin = {
20
20
  * @private
21
21
  */
22
22
  _setupHierarchicalLayout : function() {
23
- if (this.constants.hierarchicalLayout.enabled == true) {
23
+ if (this.constants.hierarchicalLayout.enabled == true && this.nodeIndices.length > 0) {
24
24
  if (this.constants.hierarchicalLayout.direction == "RL" || this.constants.hierarchicalLayout.direction == "DU") {
25
25
  this.constants.hierarchicalLayout.levelSeparation *= -1;
26
26
  }
@@ -65,6 +65,11 @@ var manipulationMixin = {
65
65
  if (this.boundFunction) {
66
66
  this.off('select', this.boundFunction);
67
67
  }
68
+ if (this.edgeBeingEdited !== undefined) {
69
+ this.edgeBeingEdited._disableControlNodes();
70
+ this.edgeBeingEdited = undefined;
71
+ this.selectedControlNode = null;
72
+ }
68
73
 
69
74
  // restore overloaded functions
70
75
  this._restoreOverloadedFunctions();
@@ -93,6 +98,12 @@ var manipulationMixin = {
93
98
  "<span class='graph-manipulationUI edit' id='graph-manipulate-editNode'>" +
94
99
  "<span class='graph-manipulationLabel'>"+this.constants.labels['editNode'] +"</span></span>";
95
100
  }
101
+ else if (this._getSelectedEdgeCount() == 1 && this._getSelectedNodeCount() == 0) {
102
+ this.manipulationDiv.innerHTML += "" +
103
+ "<div class='graph-seperatorLine'></div>" +
104
+ "<span class='graph-manipulationUI edit' id='graph-manipulate-editEdge'>" +
105
+ "<span class='graph-manipulationLabel'>"+this.constants.labels['editEdge'] +"</span></span>";
106
+ }
96
107
  if (this._selectionIsEmpty() == false) {
97
108
  this.manipulationDiv.innerHTML += "" +
98
109
  "<div class='graph-seperatorLine'></div>" +
@@ -110,6 +121,10 @@ var manipulationMixin = {
110
121
  var editButton = document.getElementById("graph-manipulate-editNode");
111
122
  editButton.onclick = this._editNode.bind(this);
112
123
  }
124
+ else if (this._getSelectedEdgeCount() == 1 && this._getSelectedNodeCount() == 0) {
125
+ var editButton = document.getElementById("graph-manipulate-editEdge");
126
+ editButton.onclick = this._createEditEdgeToolbar.bind(this);
127
+ }
113
128
  if (this._selectionIsEmpty() == false) {
114
129
  var deleteButton = document.getElementById("graph-manipulate-delete");
115
130
  deleteButton.onclick = this._deleteSelected.bind(this);
@@ -203,10 +218,106 @@ var manipulationMixin = {
203
218
 
204
219
  // redraw to show the unselect
205
220
  this._redraw();
221
+ },
206
222
 
223
+ /**
224
+ * create the toolbar to edit edges
225
+ *
226
+ * @private
227
+ */
228
+ _createEditEdgeToolbar : function() {
229
+ // clear the toolbar
230
+ this._clearManipulatorBar();
231
+
232
+ if (this.boundFunction) {
233
+ this.off('select', this.boundFunction);
234
+ }
235
+
236
+ this.edgeBeingEdited = this._getSelectedEdge();
237
+ this.edgeBeingEdited._enableControlNodes();
238
+
239
+ this.manipulationDiv.innerHTML = "" +
240
+ "<span class='graph-manipulationUI back' id='graph-manipulate-back'>" +
241
+ "<span class='graph-manipulationLabel'>" + this.constants.labels['back'] + " </span></span>" +
242
+ "<div class='graph-seperatorLine'></div>" +
243
+ "<span class='graph-manipulationUI none' id='graph-manipulate-back'>" +
244
+ "<span id='graph-manipulatorLabel' class='graph-manipulationLabel'>" + this.constants.labels['editEdgeDescription'] + "</span></span>";
245
+
246
+ // bind the icon
247
+ var backButton = document.getElementById("graph-manipulate-back");
248
+ backButton.onclick = this._createManipulatorBar.bind(this);
249
+
250
+ // temporarily overload functions
251
+ this.cachedFunctions["_handleTouch"] = this._handleTouch;
252
+ this.cachedFunctions["_handleOnRelease"] = this._handleOnRelease;
253
+ this.cachedFunctions["_handleTap"] = this._handleTap;
254
+ this.cachedFunctions["_handleDragStart"] = this._handleDragStart;
255
+ this.cachedFunctions["_handleOnDrag"] = this._handleOnDrag;
256
+ this._handleTouch = this._selectControlNode;
257
+ this._handleTap = function () {};
258
+ this._handleOnDrag = this._controlNodeDrag;
259
+ this._handleDragStart = function () {}
260
+ this._handleOnRelease = this._releaseControlNode;
261
+
262
+ // redraw to show the unselect
263
+ this._redraw();
207
264
  },
208
265
 
209
266
 
267
+
268
+
269
+
270
+ /**
271
+ * the function bound to the selection event. It checks if you want to connect a cluster and changes the description
272
+ * to walk the user through the process.
273
+ *
274
+ * @private
275
+ */
276
+ _selectControlNode : function(pointer) {
277
+ this.edgeBeingEdited.controlNodes.from.unselect();
278
+ this.edgeBeingEdited.controlNodes.to.unselect();
279
+ this.selectedControlNode = this.edgeBeingEdited._getSelectedControlNode(this._XconvertDOMtoCanvas(pointer.x),this._YconvertDOMtoCanvas(pointer.y));
280
+ if (this.selectedControlNode !== null) {
281
+ this.selectedControlNode.select();
282
+ this.freezeSimulation = true;
283
+ }
284
+ this._redraw();
285
+ },
286
+
287
+ /**
288
+ * the function bound to the selection event. It checks if you want to connect a cluster and changes the description
289
+ * to walk the user through the process.
290
+ *
291
+ * @private
292
+ */
293
+ _controlNodeDrag : function(event) {
294
+ var pointer = this._getPointer(event.gesture.center);
295
+ if (this.selectedControlNode !== null && this.selectedControlNode !== undefined) {
296
+ this.selectedControlNode.x = this._XconvertDOMtoCanvas(pointer.x);
297
+ this.selectedControlNode.y = this._YconvertDOMtoCanvas(pointer.y);
298
+ }
299
+ this._redraw();
300
+ },
301
+
302
+ _releaseControlNode : function(pointer) {
303
+ var newNode = this._getNodeAt(pointer);
304
+ if (newNode != null) {
305
+ if (this.edgeBeingEdited.controlNodes.from.selected == true) {
306
+ this._editEdge(newNode.id, this.edgeBeingEdited.to.id);
307
+ this.edgeBeingEdited.controlNodes.from.unselect();
308
+ }
309
+ if (this.edgeBeingEdited.controlNodes.to.selected == true) {
310
+ this._editEdge(this.edgeBeingEdited.from.id, newNode.id);
311
+ this.edgeBeingEdited.controlNodes.to.unselect();
312
+ }
313
+ }
314
+ else {
315
+ this.edgeBeingEdited._restoreControlNodes();
316
+ }
317
+ this.freezeSimulation = false;
318
+ this._redraw();
319
+ },
320
+
210
321
  /**
211
322
  * the function bound to the selection event. It checks if you want to connect a cluster and changes the description
212
323
  * to walk the user through the process.
@@ -351,6 +462,36 @@ var manipulationMixin = {
351
462
  }
352
463
  },
353
464
 
465
+ /**
466
+ * connect two nodes with a new edge.
467
+ *
468
+ * @private
469
+ */
470
+ _editEdge : function(sourceNodeId,targetNodeId) {
471
+ if (this.editMode == true) {
472
+ var defaultData = {id: this.edgeBeingEdited.id, from:sourceNodeId, to:targetNodeId};
473
+ if (this.triggerFunctions.editEdge) {
474
+ if (this.triggerFunctions.editEdge.length == 2) {
475
+ var me = this;
476
+ this.triggerFunctions.editEdge(defaultData, function(finalizedData) {
477
+ me.edgesData.update(finalizedData);
478
+ me.moving = true;
479
+ me.start();
480
+ });
481
+ }
482
+ else {
483
+ alert(this.constants.labels["linkError"]);
484
+ this.moving = true;
485
+ this.start();
486
+ }
487
+ }
488
+ else {
489
+ this.edgesData.update(defaultData);
490
+ this.moving = true;
491
+ this.start();
492
+ }
493
+ }
494
+ },
354
495
 
355
496
  /**
356
497
  * Create the toolbar to edit the selected node. The label and the color can be changed. Other colors are derived from the chosen color.
@@ -391,6 +532,8 @@ var manipulationMixin = {
391
532
  },
392
533
 
393
534
 
535
+
536
+
394
537
  /**
395
538
  * delete everything in the selection
396
539
  *