vis-rails 1.0.2 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
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
  *