vis-rails 0.0.6 → 1.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 (62) hide show
  1. checksums.yaml +4 -4
  2. data/lib/vis/rails/version.rb +1 -1
  3. data/vendor/assets/javascripts/vis.js +2 -9
  4. data/vendor/assets/vis/DataSet.js +17 -9
  5. data/vendor/assets/vis/graph/Edge.js +49 -24
  6. data/vendor/assets/vis/graph/Graph.js +268 -64
  7. data/vendor/assets/vis/graph/Groups.js +1 -1
  8. data/vendor/assets/vis/graph/Node.js +18 -67
  9. data/vendor/assets/vis/graph/Popup.js +40 -13
  10. data/vendor/assets/vis/graph/css/graph-navigation.css +18 -14
  11. data/vendor/assets/vis/graph/graphMixins/ClusterMixin.js +7 -5
  12. data/vendor/assets/vis/graph/graphMixins/HierarchicalLayoutMixin.js +20 -5
  13. data/vendor/assets/vis/graph/graphMixins/ManipulationMixin.js +33 -33
  14. data/vendor/assets/vis/graph/graphMixins/MixinLoader.js +30 -32
  15. data/vendor/assets/vis/graph/graphMixins/NavigationMixin.js +33 -1
  16. data/vendor/assets/vis/graph/graphMixins/SectorsMixin.js +2 -2
  17. data/vendor/assets/vis/graph/graphMixins/SelectionMixin.js +72 -60
  18. data/vendor/assets/vis/graph/graphMixins/physics/BarnesHut.js +43 -18
  19. data/vendor/assets/vis/graph/graphMixins/physics/HierarchialRepulsion.js +8 -8
  20. data/vendor/assets/vis/graph/graphMixins/physics/PhysicsMixin.js +309 -129
  21. data/vendor/assets/vis/graph/graphMixins/physics/Repulsion.js +10 -10
  22. data/vendor/assets/vis/module/exports.js +1 -2
  23. data/vendor/assets/vis/module/header.js +2 -2
  24. data/vendor/assets/vis/timeline/Range.js +53 -93
  25. data/vendor/assets/vis/timeline/Timeline.js +328 -224
  26. data/vendor/assets/vis/timeline/component/Component.js +17 -95
  27. data/vendor/assets/vis/timeline/component/CurrentTime.js +54 -59
  28. data/vendor/assets/vis/timeline/component/CustomTime.js +55 -83
  29. data/vendor/assets/vis/timeline/component/Group.js +398 -75
  30. data/vendor/assets/vis/timeline/component/ItemSet.js +662 -403
  31. data/vendor/assets/vis/timeline/component/Panel.js +118 -60
  32. data/vendor/assets/vis/timeline/component/RootPanel.js +80 -132
  33. data/vendor/assets/vis/timeline/component/TimeAxis.js +191 -277
  34. data/vendor/assets/vis/timeline/component/css/item.css +16 -23
  35. data/vendor/assets/vis/timeline/component/css/itemset.css +25 -4
  36. data/vendor/assets/vis/timeline/component/css/labelset.css +34 -0
  37. data/vendor/assets/vis/timeline/component/css/panel.css +15 -1
  38. data/vendor/assets/vis/timeline/component/css/timeaxis.css +8 -8
  39. data/vendor/assets/vis/timeline/component/item/Item.js +48 -26
  40. data/vendor/assets/vis/timeline/component/item/ItemBox.js +156 -230
  41. data/vendor/assets/vis/timeline/component/item/ItemPoint.js +118 -166
  42. data/vendor/assets/vis/timeline/component/item/ItemRange.js +135 -187
  43. data/vendor/assets/vis/timeline/component/item/ItemRangeOverflow.js +29 -92
  44. data/vendor/assets/vis/timeline/stack.js +112 -0
  45. data/vendor/assets/vis/util.js +136 -38
  46. metadata +4 -18
  47. data/vendor/assets/vis/.gitignore +0 -1
  48. data/vendor/assets/vis/EventBus.js +0 -89
  49. data/vendor/assets/vis/events.js +0 -116
  50. data/vendor/assets/vis/graph/ClusterMixin.js +0 -1019
  51. data/vendor/assets/vis/graph/NavigationMixin.js +0 -245
  52. data/vendor/assets/vis/graph/SectorsMixin.js +0 -547
  53. data/vendor/assets/vis/graph/SelectionMixin.js +0 -515
  54. data/vendor/assets/vis/graph/img/downarrow.png +0 -0
  55. data/vendor/assets/vis/graph/img/leftarrow.png +0 -0
  56. data/vendor/assets/vis/graph/img/rightarrow.png +0 -0
  57. data/vendor/assets/vis/graph/img/uparrow.png +0 -0
  58. data/vendor/assets/vis/timeline/Controller.js +0 -183
  59. data/vendor/assets/vis/timeline/Stack.js +0 -190
  60. data/vendor/assets/vis/timeline/component/ContentPanel.js +0 -113
  61. data/vendor/assets/vis/timeline/component/GroupSet.js +0 -580
  62. data/vendor/assets/vis/timeline/component/css/groupset.css +0 -59
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 72bb277b8b348027f521d538a6630e33341b5922
4
- data.tar.gz: 76acf26b438efc47c2d736e6a68b3cebfe1dd460
3
+ metadata.gz: c6eb0cd9faac65244e63f4d943060b2c98d0d9ff
4
+ data.tar.gz: 553f0d4bde3e42a6ba3e01e14d8cdc5d3db53257
5
5
  SHA512:
6
- metadata.gz: ab131ae94d19541e0b976941c19d1b569046b8dbc8415155dd249f3237445c1aeb142b86e183a2468cb431a2e2a0c5b1a2122ab90d121c0df4126598be1b78dd
7
- data.tar.gz: e9ada6d621cfbc956847d4f10393a57a10284e25809c192f0ec1db29b36cde68be23f609814da15a9b6c763a1d4cf98b68fe8f3de938c514b88b67bf50d157ce
6
+ metadata.gz: ed8d40a2f290023a0b43263d279a0b7870a14b695f3e7f96a7660d70d13c7cb308fb06312132485c9caf594f3b897cd49aa714e8a2b435e4f6610d1e047f5e9f
7
+ data.tar.gz: d908d701447e25d31788658b46392fd962f7afca586addb6d5e630cf60a75d66b25e978f6e734aaab9986f321a87c538a490e5b8f890209782c96a6af7f1a1bb
@@ -1,5 +1,5 @@
1
1
  module Vis
2
2
  module Rails
3
- VERSION = "0.0.6"
3
+ VERSION = "1.0.0"
4
4
  end
5
5
  end
@@ -5,15 +5,12 @@
5
5
 
6
6
  //= require ../vis/shim
7
7
  //= require ../vis/util
8
- //= require ../vis/events
9
- //= require ../vis/EventBus
10
8
  //= require ../vis/DataSet
11
9
  //= require ../vis/DataView
12
10
 
11
+ //= require ../vis/timeline/stack
13
12
  //= require ../vis/timeline/TimeStep
14
- //= require ../vis/timeline/Stack
15
13
  //= require ../vis/timeline/Range
16
- //= require ../vis/timeline/Controller
17
14
  //= require ../vis/timeline/component/Component
18
15
  //= require ../vis/timeline/component/Panel
19
16
  //= require ../vis/timeline/component/RootPanel
@@ -23,7 +20,6 @@
23
20
  //= require_tree ../vis/timeline/component/item
24
21
  //= require ../vis/timeline/component/ItemSet
25
22
  //= require ../vis/timeline/component/Group
26
- //= require ../vis/timeline/component/GroupSet
27
23
  //= require ../vis/timeline/Timeline
28
24
 
29
25
  //= require ../vis/graph/dotparser
@@ -33,10 +29,7 @@
33
29
  //= require ../vis/graph/Popup
34
30
  //= require ../vis/graph/Groups
35
31
  //= require ../vis/graph/Images
36
- //= require ../vis/graph/SectorsMixin
37
- //= require ../vis/graph/ClusterMixin
38
- //= require ../vis/graph/SelectionMixin
39
- //= require ../vis/graph/NavigationMixin
32
+ //= require_tree ../vis/graph/graphMixins
40
33
  //= require ../vis/graph/Graph
41
34
 
42
35
  //= require ../vis/module/exports
@@ -26,6 +26,7 @@
26
26
  * - gives triggers upon changes in the data
27
27
  * - can import/export data in various data formats
28
28
  *
29
+ * @param {Array | DataTable} [data] Optional array with initial data
29
30
  * @param {Object} [options] Available options:
30
31
  * {String} fieldId Field name of the id in the
31
32
  * items, 'id' by default.
@@ -35,9 +36,15 @@
35
36
  * @constructor DataSet
36
37
  */
37
38
  // TODO: add a DataSet constructor DataSet(data, options)
38
- function DataSet (options) {
39
+ function DataSet (data, options) {
39
40
  this.id = util.randomUUID();
40
41
 
42
+ // correctly read optional arguments
43
+ if (data && !Array.isArray(data) && !util.isDataTable(data)) {
44
+ options = data;
45
+ data = null;
46
+ }
47
+
41
48
  this.options = options || {};
42
49
  this.data = {}; // map with data indexed by id
43
50
  this.fieldId = this.options.fieldId || 'id'; // name of the field containing id
@@ -58,10 +65,13 @@ function DataSet (options) {
58
65
  }
59
66
  }
60
67
 
61
- // event subscribers
62
- this.subscribers = {};
68
+ this.subscribers = {}; // event subscribers
69
+ this.internalIds = {}; // internally generated id's
63
70
 
64
- this.internalIds = {}; // internally generated id's
71
+ // add initial data when provided
72
+ if (data) {
73
+ this.add(data);
74
+ }
65
75
  }
66
76
 
67
77
  /**
@@ -511,7 +521,6 @@ DataSet.prototype.getIds = function (options) {
511
521
 
512
522
  /**
513
523
  * Execute a callback function for every item in the dataset.
514
- * The order of the items is not determined.
515
524
  * @param {function} callback
516
525
  * @param {Object} [options] Available options:
517
526
  * {Object.<String, String>} [convert]
@@ -757,9 +766,8 @@ DataSet.prototype.min = function (field) {
757
766
  /**
758
767
  * Find all distinct values of a specified field
759
768
  * @param {String} field
760
- * @return {Array} values Array containing all distinct values. If the data
761
- * items do not contain the specified field, an array
762
- * containing a single value undefined is returned.
769
+ * @return {Array} values Array containing all distinct values. If data items
770
+ * do not contain the specified field are ignored.
763
771
  * The returned array is unordered.
764
772
  */
765
773
  DataSet.prototype.distinct = function (field) {
@@ -779,7 +787,7 @@ DataSet.prototype.distinct = function (field) {
779
787
  break;
780
788
  }
781
789
  }
782
- if (!exists) {
790
+ if (!exists && (value !== undefined)) {
783
791
  values[count] = value;
784
792
  count++;
785
793
  }
@@ -35,6 +35,7 @@ function Edge (properties, graph, constants) {
35
35
  this.customLength = false;
36
36
  this.selected = false;
37
37
  this.smooth = constants.smoothCurves;
38
+ this.arrowScaleFactor = constants.edges.arrowScaleFactor;
38
39
 
39
40
  this.from = null; // a node
40
41
  this.to = null; // a node
@@ -52,7 +53,8 @@ function Edge (properties, graph, constants) {
52
53
  // 2012-08-08
53
54
  this.dash = util.extend({}, constants.edges.dash); // contains properties length, gap, altLength
54
55
 
55
- this.color = constants.edges.color;
56
+ this.color = {color:constants.edges.color.color,
57
+ highlight:constants.edges.color.highlight};
56
58
  this.widthFixed = false;
57
59
  this.lengthFixed = false;
58
60
 
@@ -80,9 +82,12 @@ Edge.prototype.setProperties = function(properties, constants) {
80
82
  this.fontSize = constants.edges.fontSize;
81
83
  this.fontFace = constants.edges.fontFace;
82
84
  this.fontColor = constants.edges.fontColor;
85
+ this.fontFill = constants.edges.fontFill;
86
+
83
87
  if (properties.fontColor !== undefined) {this.fontColor = properties.fontColor;}
84
88
  if (properties.fontSize !== undefined) {this.fontSize = properties.fontSize;}
85
89
  if (properties.fontFace !== undefined) {this.fontFace = properties.fontFace;}
90
+ if (properties.fontFill !== undefined) {this.fontFill = properties.fontFill;}
86
91
  }
87
92
 
88
93
  if (properties.title !== undefined) {this.title = properties.title;}
@@ -91,6 +96,9 @@ Edge.prototype.setProperties = function(properties, constants) {
91
96
  if (properties.length !== undefined) {this.length = properties.length;
92
97
  this.customLength = true;}
93
98
 
99
+ // scale the arrow
100
+ if (properties.arrowScaleFactor !== undefined) {this.arrowScaleFactor = properties.arrowScaleFactor;}
101
+
94
102
  // Added to support dashed lines
95
103
  // David Jordan
96
104
  // 2012-08-08
@@ -100,7 +108,16 @@ Edge.prototype.setProperties = function(properties, constants) {
100
108
  if (properties.dash.altLength !== undefined) {this.dash.altLength = properties.dash.altLength;}
101
109
  }
102
110
 
103
- if (properties.color !== undefined) {this.color = properties.color;}
111
+ if (properties.color !== undefined) {
112
+ if (util.isString(properties.color)) {
113
+ this.color.color = properties.color;
114
+ this.color.highlight = properties.color;
115
+ }
116
+ else {
117
+ if (properties.color.color !== undefined) {this.color.color = properties.color.color;}
118
+ if (properties.color.highlight !== undefined) {this.color.highlight = properties.color.highlight;}
119
+ }
120
+ }
104
121
 
105
122
  // A node is connected when it has a from and to node.
106
123
  this.connect();
@@ -164,7 +181,7 @@ Edge.prototype.disconnect = function () {
164
181
  * has been set.
165
182
  */
166
183
  Edge.prototype.getTitle = function() {
167
- return this.title;
184
+ return typeof this.title === "function" ? this.title() : this.title;
168
185
  };
169
186
 
170
187
 
@@ -205,18 +222,22 @@ Edge.prototype.draw = function(ctx) {
205
222
  * @return {boolean} True if location is located on the edge
206
223
  */
207
224
  Edge.prototype.isOverlappingWith = function(obj) {
208
- var distMax = 10;
209
-
210
- var xFrom = this.from.x;
211
- var yFrom = this.from.y;
212
- var xTo = this.to.x;
213
- var yTo = this.to.y;
214
- var xObj = obj.left;
215
- var yObj = obj.top;
225
+ if (this.connected) {
226
+ var distMax = 10;
227
+ var xFrom = this.from.x;
228
+ var yFrom = this.from.y;
229
+ var xTo = this.to.x;
230
+ var yTo = this.to.y;
231
+ var xObj = obj.left;
232
+ var yObj = obj.top;
216
233
 
217
- var dist = this._getDistanceToEdge(xFrom, yFrom, xTo, yTo, xObj, yObj);
234
+ var dist = this._getDistanceToEdge(xFrom, yFrom, xTo, yTo, xObj, yObj);
218
235
 
219
- return (dist < distMax);
236
+ return (dist < distMax);
237
+ }
238
+ else {
239
+ return false
240
+ }
220
241
  };
221
242
 
222
243
 
@@ -229,7 +250,8 @@ Edge.prototype.isOverlappingWith = function(obj) {
229
250
  */
230
251
  Edge.prototype._drawLine = function(ctx) {
231
252
  // set style
232
- ctx.strokeStyle = this.color;
253
+ if (this.selected == true) {ctx.strokeStyle = this.color.highlight;}
254
+ else {ctx.strokeStyle = this.color.color;}
233
255
  ctx.lineWidth = this._getLineWidth();
234
256
 
235
257
  if (this.from != this.to) {
@@ -332,7 +354,7 @@ Edge.prototype._label = function (ctx, text, x, y) {
332
354
  // TODO: cache the calculated size
333
355
  ctx.font = ((this.from.selected || this.to.selected) ? "bold " : "") +
334
356
  this.fontSize + "px " + this.fontFace;
335
- ctx.fillStyle = 'white';
357
+ ctx.fillStyle = this.fontFill;
336
358
  var width = ctx.measureText(text).width;
337
359
  var height = this.fontSize;
338
360
  var left = x - width / 2;
@@ -359,7 +381,9 @@ Edge.prototype._label = function (ctx, text, x, y) {
359
381
  */
360
382
  Edge.prototype._drawDashLine = function(ctx) {
361
383
  // set style
362
- ctx.strokeStyle = this.color;
384
+ if (this.selected == true) {ctx.strokeStyle = this.color.highlight;}
385
+ else {ctx.strokeStyle = this.color.color;}
386
+
363
387
  ctx.lineWidth = this._getLineWidth();
364
388
 
365
389
  // only firefox and chrome support this method, else we use the legacy one.
@@ -482,8 +506,8 @@ Edge.prototype._pointOnCircle = function (x, y, radius, percentage) {
482
506
  Edge.prototype._drawArrowCenter = function(ctx) {
483
507
  var point;
484
508
  // set style
485
- ctx.strokeStyle = this.color;
486
- ctx.fillStyle = this.color;
509
+ if (this.selected == true) {ctx.strokeStyle = this.color.highlight; ctx.fillStyle = this.color.highlight;}
510
+ else {ctx.strokeStyle = this.color.color; ctx.fillStyle = this.color.color;}
487
511
  ctx.lineWidth = this._getLineWidth();
488
512
 
489
513
  if (this.from != this.to) {
@@ -491,7 +515,7 @@ Edge.prototype._drawArrowCenter = function(ctx) {
491
515
  this._line(ctx);
492
516
 
493
517
  var angle = Math.atan2((this.to.y - this.from.y), (this.to.x - this.from.x));
494
- var length = 10 + 5 * this.width; // TODO: make customizable?
518
+ var length = (10 + 5 * this.width) * this.arrowScaleFactor;
495
519
  // draw an arrow halfway the line
496
520
  if (this.smooth == true) {
497
521
  var midpointX = 0.5*(0.5*(this.from.x + this.via.x) + 0.5*(this.to.x + this.via.x));
@@ -531,7 +555,7 @@ Edge.prototype._drawArrowCenter = function(ctx) {
531
555
 
532
556
  // draw all arrows
533
557
  var angle = 0.2 * Math.PI;
534
- var length = 10 + 5 * this.width; // TODO: make customizable?
558
+ var length = (10 + 5 * this.width) * this.arrowScaleFactor;
535
559
  point = this._pointOnCircle(x, y, radius, 0.5);
536
560
  ctx.arrow(point.x, point.y, angle, length);
537
561
  ctx.fill();
@@ -556,8 +580,9 @@ Edge.prototype._drawArrowCenter = function(ctx) {
556
580
  */
557
581
  Edge.prototype._drawArrow = function(ctx) {
558
582
  // set style
559
- ctx.strokeStyle = this.color;
560
- ctx.fillStyle = this.color;
583
+ if (this.selected == true) {ctx.strokeStyle = this.color.highlight; ctx.fillStyle = this.color.highlight;}
584
+ else {ctx.strokeStyle = this.color.color; ctx.fillStyle = this.color.color;}
585
+
561
586
  ctx.lineWidth = this._getLineWidth();
562
587
 
563
588
  var angle, length;
@@ -604,7 +629,7 @@ Edge.prototype._drawArrow = function(ctx) {
604
629
  ctx.stroke();
605
630
 
606
631
  // draw arrow at the end of the line
607
- length = 10 + 5 * this.width;
632
+ length = (10 + 5 * this.width) * this.arrowScaleFactor;
608
633
  ctx.arrow(xTo, yTo, angle, length);
609
634
  ctx.fill();
610
635
  ctx.stroke();
@@ -655,7 +680,7 @@ Edge.prototype._drawArrow = function(ctx) {
655
680
  ctx.stroke();
656
681
 
657
682
  // draw all arrows
658
- length = 10 + 5 * this.width; // TODO: make customizable?
683
+ var length = (10 + 5 * this.width) * this.arrowScaleFactor;
659
684
  ctx.arrow(arrow.x, arrow.y, arrow.angle, length);
660
685
  ctx.fill();
661
686
  ctx.stroke();
@@ -22,13 +22,15 @@ function Graph (container, data, options) {
22
22
  this.renderRefreshRate = 60; // hz (fps)
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
- this.maxRenderSteps = 3; // max amount of physics ticks per render step.
25
+ this.maxPhysicsTicksPerRender = 3; // max amount of physics ticks per render step.
26
+ this.physicsDiscreteStepsize = 0.65; // discrete stepsize of the simulation
26
27
 
27
28
  this.stabilize = true; // stabilize before displaying the graph
28
29
  this.selectable = true;
30
+ this.initializing = true;
29
31
 
30
32
  // these functions are triggered when the dataset is edited
31
- this.triggerFunctions = {add:null,edit:null,connect:null,delete:null};
33
+ this.triggerFunctions = {add:null,edit:null,connect:null,del:null};
32
34
 
33
35
  // set constant values
34
36
  this.constants = {
@@ -63,10 +65,15 @@ function Graph (container, data, options) {
63
65
  widthMax: 15,
64
66
  width: 1,
65
67
  style: 'line',
66
- color: '#848484',
68
+ color: {
69
+ color:'#848484',
70
+ highlight:'#848484'
71
+ },
67
72
  fontColor: '#343434',
68
73
  fontSize: 14, // px
69
74
  fontFace: 'arial',
75
+ fontFill: 'white',
76
+ arrowScaleFactor: 1,
70
77
  dash: {
71
78
  length: 10,
72
79
  gap: 5,
@@ -80,8 +87,8 @@ function Graph (container, data, options) {
80
87
  theta: 1 / 0.6, // inverted to save time during calculation
81
88
  gravitationalConstant: -2000,
82
89
  centralGravity: 0.3,
83
- springLength: 100,
84
- springConstant: 0.05,
90
+ springLength: 95,
91
+ springConstant: 0.04,
85
92
  damping: 0.09
86
93
  },
87
94
  repulsion: {
@@ -142,10 +149,37 @@ function Graph (container, data, options) {
142
149
  nodeSpacing: 100,
143
150
  direction: "UD" // UD, DU, LR, RL
144
151
  },
152
+ freezeForStabilization: false,
145
153
  smoothCurves: true,
146
154
  maxVelocity: 10,
147
155
  minVelocity: 0.1, // px/s
148
- maxIterations: 1000 // maximum number of iteration to stabilize
156
+ stabilizationIterations: 1000, // maximum number of iteration to stabilize
157
+ labels:{
158
+ add:"Add Node",
159
+ edit:"Edit",
160
+ link:"Add Link",
161
+ del:"Delete selected",
162
+ editNode:"Edit Node",
163
+ back:"Back",
164
+ addDescription:"Click in an empty space to place a new node.",
165
+ linkDescription:"Click on a node and drag the edge to another node to connect them.",
166
+ addError:"The function for add does not support two arguments (data,callback).",
167
+ linkError:"The function for connect does not support two arguments (data,callback).",
168
+ editError:"The function for edit does not support two arguments (data, callback).",
169
+ editBoundError:"No edit function has been bound to this button.",
170
+ deleteError:"The function for delete does not support two arguments (data, callback).",
171
+ deleteClusterError:"Clusters cannot be deleted."
172
+ },
173
+ tooltip: {
174
+ delay: 300,
175
+ fontColor: 'black',
176
+ fontSize: 14, // px
177
+ fontFace: 'verdana',
178
+ color: {
179
+ border: '#666',
180
+ background: '#FFFFC6'
181
+ }
182
+ }
149
183
  };
150
184
  this.editMode = this.constants.dataManipulation.initiallyVisible;
151
185
 
@@ -240,6 +274,7 @@ function Graph (container, data, options) {
240
274
  this.setData(data,this.constants.clustering.enabled || this.constants.hierarchicalLayout.enabled);
241
275
 
242
276
  // hierarchical layout
277
+ this.initializing = false;
243
278
  if (this.constants.hierarchicalLayout.enabled == true) {
244
279
  this._setupHierarchicalLayout();
245
280
  }
@@ -298,6 +333,9 @@ Graph.prototype._getRange = function() {
298
333
  if (maxY < (node.y)) {maxY = node.y;}
299
334
  }
300
335
  }
336
+ if (minX == 1e9 && maxX == -1e9 && minY == 1e9 && maxY == -1e9) {
337
+ minY = 0, maxY = 0, minX = 0, maxX = 0;
338
+ }
301
339
  return {minX: minX, maxX: maxX, minY: minY, maxY: maxY};
302
340
  };
303
341
 
@@ -334,6 +372,7 @@ Graph.prototype._centerGraph = function(range) {
334
372
  * This function zooms out to fit all data on screen based on amount of nodes
335
373
  *
336
374
  * @param {Boolean} [initialZoom] | zoom based on fitted formula or range, true = fitted, default = false;
375
+ * @param {Boolean} [disableStart] | If true, start is not called.
337
376
  */
338
377
  Graph.prototype.zoomExtent = function(initialZoom, disableStart) {
339
378
  if (initialZoom === undefined) {
@@ -451,7 +490,7 @@ Graph.prototype.setData = function(data, disableStart) {
451
490
  if (!disableStart) {
452
491
  // find a stable position or start animating to a stable position
453
492
  if (this.stabilize) {
454
- this._doStabilize();
493
+ this._stabilize();
455
494
  }
456
495
  this.start();
457
496
  }
@@ -470,7 +509,19 @@ Graph.prototype.setOptions = function (options) {
470
509
  if (options.stabilize !== undefined) {this.stabilize = options.stabilize;}
471
510
  if (options.selectable !== undefined) {this.selectable = options.selectable;}
472
511
  if (options.smoothCurves !== undefined) {this.constants.smoothCurves = options.smoothCurves;}
512
+ if (options.freezeForStabilization !== undefined) {this.constants.freezeForStabilization = options.freezeForStabilization;}
473
513
  if (options.configurePhysics !== undefined){this.constants.configurePhysics = options.configurePhysics;}
514
+ if (options.stabilizationIterations !== undefined) {this.constants.stabilizationIterations = options.stabilizationIterations;}
515
+
516
+
517
+
518
+ if (options.labels !== undefined) {
519
+ for (prop in options.labels) {
520
+ if (options.labels.hasOwnProperty(prop)) {
521
+ this.constants.labels[prop] = options.labels[prop];
522
+ }
523
+ }
524
+ }
474
525
 
475
526
  if (options.onAdd) {
476
527
  this.triggerFunctions.add = options.onAdd;
@@ -485,7 +536,7 @@ Graph.prototype.setOptions = function (options) {
485
536
  }
486
537
 
487
538
  if (options.onDelete) {
488
- this.triggerFunctions.delete = options.onDelete;
539
+ this.triggerFunctions.del = options.onDelete;
489
540
  }
490
541
 
491
542
  if (options.physics) {
@@ -572,12 +623,30 @@ Graph.prototype.setOptions = function (options) {
572
623
  if (options.edges) {
573
624
  for (prop in options.edges) {
574
625
  if (options.edges.hasOwnProperty(prop)) {
575
- this.constants.edges[prop] = options.edges[prop];
626
+ if (typeof options.edges[prop] != "object") {
627
+ this.constants.edges[prop] = options.edges[prop];
628
+ }
629
+ }
630
+ }
631
+
632
+
633
+ if (options.edges.color !== undefined) {
634
+ if (util.isString(options.edges.color)) {
635
+ this.constants.edges.color = {};
636
+ this.constants.edges.color.color = options.edges.color;
637
+ this.constants.edges.color.highlight = options.edges.color;
638
+ }
639
+ else {
640
+ if (options.edges.color.color !== undefined) {this.constants.edges.color.color = options.edges.color.color;}
641
+ if (options.edges.color.highlight !== undefined) {this.constants.edges.color.highlight = options.edges.color.highlight;}
576
642
  }
577
643
  }
578
644
 
579
645
  if (!options.edges.fontColor) {
580
- this.constants.edges.fontColor = options.edges.color;
646
+ if (options.edges.color !== undefined) {
647
+ if (util.isString(options.edges.color)) {this.constants.edges.fontColor = options.edges.color;}
648
+ else if (options.edges.color.color !== undefined) {this.constants.edges.fontColor = options.edges.color.color;}
649
+ }
581
650
  }
582
651
 
583
652
  // Added to support dashed lines
@@ -604,7 +673,7 @@ Graph.prototype.setOptions = function (options) {
604
673
  }
605
674
 
606
675
  if (options.nodes.color) {
607
- this.constants.nodes.color = Node.parseColor(options.nodes.color);
676
+ this.constants.nodes.color = util.parseColor(options.nodes.color);
608
677
  }
609
678
 
610
679
  /*
@@ -620,6 +689,17 @@ Graph.prototype.setOptions = function (options) {
620
689
  }
621
690
  }
622
691
  }
692
+
693
+ if (options.tooltip) {
694
+ for (prop in options.tooltip) {
695
+ if (options.tooltip.hasOwnProperty(prop)) {
696
+ this.constants.tooltip[prop] = options.tooltip[prop];
697
+ }
698
+ }
699
+ if (options.tooltip.color) {
700
+ this.constants.tooltip.color = util.parseColor(options.tooltip.color);
701
+ }
702
+ }
623
703
  }
624
704
 
625
705
 
@@ -660,7 +740,6 @@ Graph.prototype._create = function () {
660
740
  this.frame.className = 'graph-frame';
661
741
  this.frame.style.position = 'relative';
662
742
  this.frame.style.overflow = 'hidden';
663
- this.frame.style.zIndex = "1";
664
743
 
665
744
  // create the graph canvas (HTML canvas element)
666
745
  this.frame.canvas = document.createElement( 'canvas' );
@@ -798,26 +877,24 @@ Graph.prototype._handleDragStart = function() {
798
877
  }
799
878
 
800
879
  // create an array with the selected nodes and their original location and status
801
- for (var objectId in this.selectionObj) {
802
- if (this.selectionObj.hasOwnProperty(objectId)) {
803
- var object = this.selectionObj[objectId];
804
- if (object instanceof Node) {
805
- var s = {
806
- id: object.id,
807
- node: object,
808
-
809
- // store original x, y, xFixed and yFixed, make the node temporarily Fixed
810
- x: object.x,
811
- y: object.y,
812
- xFixed: object.xFixed,
813
- yFixed: object.yFixed
814
- };
815
-
816
- object.xFixed = true;
817
- object.yFixed = true;
818
-
819
- drag.selection.push(s);
820
- }
880
+ for (var objectId in this.selectionObj.nodes) {
881
+ if (this.selectionObj.nodes.hasOwnProperty(objectId)) {
882
+ var object = this.selectionObj.nodes[objectId];
883
+ var s = {
884
+ id: object.id,
885
+ node: object,
886
+
887
+ // store original x, y, xFixed and yFixed, make the node temporarily Fixed
888
+ x: object.x,
889
+ y: object.y,
890
+ xFixed: object.xFixed,
891
+ yFixed: object.yFixed
892
+ };
893
+
894
+ object.xFixed = true;
895
+ object.yFixed = true;
896
+
897
+ drag.selection.push(s);
821
898
  }
822
899
  }
823
900
  }
@@ -882,7 +959,7 @@ Graph.prototype._handleOnDrag = function(event) {
882
959
  this.drag.translation.x + diffX,
883
960
  this.drag.translation.y + diffY);
884
961
  this._redraw();
885
- this.moved = true;
962
+ this.moving = true;
886
963
  }
887
964
  };
888
965
 
@@ -992,6 +1069,7 @@ Graph.prototype._zoom = function(scale, pointer) {
992
1069
  this.updateClustersDefault();
993
1070
  this._redraw();
994
1071
 
1072
+
995
1073
  return scale;
996
1074
  };
997
1075
 
@@ -1064,7 +1142,7 @@ Graph.prototype._onMouseMoveTitle = function (event) {
1064
1142
  clearInterval(this.popupTimer); // stop any running calculationTimer
1065
1143
  }
1066
1144
  if (!this.drag.dragging) {
1067
- this.popupTimer = setTimeout(checkShow, 300);
1145
+ this.popupTimer = setTimeout(checkShow, this.constants.tooltip.delay);
1068
1146
  }
1069
1147
  };
1070
1148
 
@@ -1121,7 +1199,7 @@ Graph.prototype._checkShowPopup = function (pointer) {
1121
1199
  if (this.popupNode != lastPopupNode) {
1122
1200
  var me = this;
1123
1201
  if (!me.popup) {
1124
- me.popup = new Popup(me.frame);
1202
+ me.popup = new Popup(me.frame, me.constants.tooltip);
1125
1203
  }
1126
1204
 
1127
1205
  // adjust a small offset such that the mouse cursor is located in the
@@ -1174,7 +1252,13 @@ Graph.prototype.setSize = function(width, height) {
1174
1252
  this.frame.canvas.height = this.frame.canvas.clientHeight;
1175
1253
 
1176
1254
  if (this.manipulationDiv !== undefined) {
1177
- this.manipulationDiv.style.width = this.frame.canvas.clientWidth;
1255
+ this.manipulationDiv.style.width = this.frame.canvas.clientWidth + "px";
1256
+ }
1257
+ if (this.navigationDivs !== undefined) {
1258
+ if (this.navigationDivs['wrapper'] !== undefined) {
1259
+ this.navigationDivs['wrapper'].style.width = this.frame.canvas.clientWidth + "px";
1260
+ this.navigationDivs['wrapper'].style.height = this.frame.canvas.clientHeight + "px";
1261
+ }
1178
1262
  }
1179
1263
 
1180
1264
  this.emit('resize', {width:this.frame.canvas.width,height:this.frame.canvas.height});
@@ -1239,18 +1323,19 @@ Graph.prototype._addNodes = function(ids) {
1239
1323
  var node = new Node(data, this.images, this.groups, this.constants);
1240
1324
  this.nodes[id] = node; // note: this may replace an existing node
1241
1325
 
1242
- if ((node.xFixed == false || node.yFixed == false) && this.createNodeOnClick != true) {
1326
+ if ((node.xFixed == false || node.yFixed == false) && (node.x === null || node.y === null)) {
1243
1327
  var radius = 10 * 0.1*ids.length;
1244
1328
  var angle = 2 * Math.PI * Math.random();
1245
1329
  if (node.xFixed == false) {node.x = radius * Math.cos(angle);}
1246
1330
  if (node.yFixed == false) {node.y = radius * Math.sin(angle);}
1247
-
1248
- // note: no not use node.isMoving() here, as that gives the current
1249
- // velocity of the node, which is zero after creation of the node.
1250
- this.moving = true;
1251
1331
  }
1332
+ this.moving = true;
1252
1333
  }
1253
1334
  this._updateNodeIndexList();
1335
+ if (this.constants.hierarchicalLayout.enabled == true && this.initializing == false) {
1336
+ this._resetLevels();
1337
+ this._setupHierarchicalLayout();
1338
+ }
1254
1339
  this._updateCalculationNodes();
1255
1340
  this._reconnectEdges();
1256
1341
  this._updateValueRange(this.nodes);
@@ -1277,12 +1362,13 @@ Graph.prototype._updateNodes = function(ids) {
1277
1362
  // create node
1278
1363
  node = new Node(properties, this.images, this.groups, this.constants);
1279
1364
  nodes[id] = node;
1280
-
1281
- if (!node.isFixed()) {
1282
- this.moving = true;
1283
- }
1284
1365
  }
1285
1366
  }
1367
+ this.moving = true;
1368
+ if (this.constants.hierarchicalLayout.enabled == true && this.initializing == false) {
1369
+ this._resetLevels();
1370
+ this._setupHierarchicalLayout();
1371
+ }
1286
1372
  this._updateNodeIndexList();
1287
1373
  this._reconnectEdges();
1288
1374
  this._updateValueRange(nodes);
@@ -1300,6 +1386,11 @@ Graph.prototype._removeNodes = function(ids) {
1300
1386
  delete nodes[id];
1301
1387
  }
1302
1388
  this._updateNodeIndexList();
1389
+ if (this.constants.hierarchicalLayout.enabled == true && this.initializing == false) {
1390
+ this._resetLevels();
1391
+ this._setupHierarchicalLayout();
1392
+ }
1393
+ this._updateCalculationNodes();
1303
1394
  this._reconnectEdges();
1304
1395
  this._updateSelection();
1305
1396
  this._updateValueRange(nodes);
@@ -1377,6 +1468,10 @@ Graph.prototype._addEdges = function (ids) {
1377
1468
  this.moving = true;
1378
1469
  this._updateValueRange(edges);
1379
1470
  this._createBezierNodes();
1471
+ if (this.constants.hierarchicalLayout.enabled == true && this.initializing == false) {
1472
+ this._resetLevels();
1473
+ this._setupHierarchicalLayout();
1474
+ }
1380
1475
  this._updateCalculationNodes();
1381
1476
  };
1382
1477
 
@@ -1407,6 +1502,10 @@ Graph.prototype._updateEdges = function (ids) {
1407
1502
  }
1408
1503
 
1409
1504
  this._createBezierNodes();
1505
+ if (this.constants.hierarchicalLayout.enabled == true && this.initializing == false) {
1506
+ this._resetLevels();
1507
+ this._setupHierarchicalLayout();
1508
+ }
1410
1509
  this.moving = true;
1411
1510
  this._updateValueRange(edges);
1412
1511
  };
@@ -1432,6 +1531,10 @@ Graph.prototype._removeEdges = function (ids) {
1432
1531
 
1433
1532
  this.moving = true;
1434
1533
  this._updateValueRange(edges);
1534
+ if (this.constants.hierarchicalLayout.enabled == true && this.initializing == false) {
1535
+ this._resetLevels();
1536
+ this._setupHierarchicalLayout();
1537
+ }
1435
1538
  this._updateCalculationNodes();
1436
1539
  };
1437
1540
 
@@ -1692,18 +1795,60 @@ Graph.prototype._drawEdges = function(ctx) {
1692
1795
  * Find a stable position for all nodes
1693
1796
  * @private
1694
1797
  */
1695
- Graph.prototype._doStabilize = function() {
1798
+ Graph.prototype._stabilize = function() {
1799
+ if (this.constants.freezeForStabilization == true) {
1800
+ this._freezeDefinedNodes();
1801
+ }
1802
+
1696
1803
  // find stable position
1697
1804
  var count = 0;
1698
- while (this.moving && count < this.constants.maxIterations) {
1805
+ while (this.moving && count < this.constants.stabilizationIterations) {
1699
1806
  this._physicsTick();
1700
1807
  count++;
1701
1808
  }
1702
-
1703
1809
  this.zoomExtent(false,true);
1810
+ if (this.constants.freezeForStabilization == true) {
1811
+ this._restoreFrozenNodes();
1812
+ }
1813
+ this.emit("stabilized",{iterations:count});
1704
1814
  };
1705
1815
 
1816
+ /**
1817
+ * When initializing and stabilizing, we can freeze nodes with a predefined position. This greatly speeds up stabilization
1818
+ * because only the supportnodes for the smoothCurves have to settle.
1819
+ *
1820
+ * @private
1821
+ */
1822
+ Graph.prototype._freezeDefinedNodes = function() {
1823
+ var nodes = this.nodes;
1824
+ for (var id in nodes) {
1825
+ if (nodes.hasOwnProperty(id)) {
1826
+ if (nodes[id].x != null && nodes[id].y != null) {
1827
+ nodes[id].fixedData.x = nodes[id].xFixed;
1828
+ nodes[id].fixedData.y = nodes[id].yFixed;
1829
+ nodes[id].xFixed = true;
1830
+ nodes[id].yFixed = true;
1831
+ }
1832
+ }
1833
+ }
1834
+ };
1706
1835
 
1836
+ /**
1837
+ * Unfreezes the nodes that have been frozen by _freezeDefinedNodes.
1838
+ *
1839
+ * @private
1840
+ */
1841
+ Graph.prototype._restoreFrozenNodes = function() {
1842
+ var nodes = this.nodes;
1843
+ for (var id in nodes) {
1844
+ if (nodes.hasOwnProperty(id)) {
1845
+ if (nodes[id].fixedData.x != null) {
1846
+ nodes[id].xFixed = nodes[id].fixedData.x;
1847
+ nodes[id].yFixed = nodes[id].fixedData.y;
1848
+ }
1849
+ }
1850
+ }
1851
+ };
1707
1852
 
1708
1853
 
1709
1854
  /**
@@ -1730,14 +1875,16 @@ Graph.prototype._isMoving = function(vmin) {
1730
1875
  * @private
1731
1876
  */
1732
1877
  Graph.prototype._discreteStepNodes = function() {
1733
- var interval = 0.65;
1878
+ var interval = this.physicsDiscreteStepsize;
1734
1879
  var nodes = this.nodes;
1735
1880
  var nodeId;
1881
+ var nodesPresent = false;
1736
1882
 
1737
1883
  if (this.constants.maxVelocity > 0) {
1738
1884
  for (nodeId in nodes) {
1739
1885
  if (nodes.hasOwnProperty(nodeId)) {
1740
1886
  nodes[nodeId].discreteStepLimited(interval, this.constants.maxVelocity);
1887
+ nodesPresent = true;
1741
1888
  }
1742
1889
  }
1743
1890
  }
@@ -1745,27 +1892,35 @@ Graph.prototype._discreteStepNodes = function() {
1745
1892
  for (nodeId in nodes) {
1746
1893
  if (nodes.hasOwnProperty(nodeId)) {
1747
1894
  nodes[nodeId].discreteStep(interval);
1895
+ nodesPresent = true;
1748
1896
  }
1749
1897
  }
1750
1898
  }
1751
- var vminCorrected = this.constants.minVelocity / Math.max(this.scale,0.05);
1752
- if (vminCorrected > 0.5*this.constants.maxVelocity) {
1753
- this.moving = true;
1754
- }
1755
- else {
1756
- this.moving = this._isMoving(vminCorrected);
1899
+
1900
+ if (nodesPresent == true) {
1901
+ var vminCorrected = this.constants.minVelocity / Math.max(this.scale,0.05);
1902
+ if (vminCorrected > 0.5*this.constants.maxVelocity) {
1903
+ this.moving = true;
1904
+ }
1905
+ else {
1906
+ this.moving = this._isMoving(vminCorrected);
1907
+ }
1757
1908
  }
1758
1909
  };
1759
1910
 
1760
-
1911
+ /**
1912
+ * A single simulation step (or "tick") in the physics simulation
1913
+ *
1914
+ * @private
1915
+ */
1761
1916
  Graph.prototype._physicsTick = function() {
1762
1917
  if (!this.freezeSimulation) {
1763
1918
  if (this.moving) {
1764
1919
  this._doInAllActiveSectors("_initializeForceCalculation");
1920
+ this._doInAllActiveSectors("_discreteStepNodes");
1765
1921
  if (this.constants.smoothCurves) {
1766
1922
  this._doInSupportSector("_discreteStepNodes");
1767
1923
  }
1768
- this._doInAllActiveSectors("_discreteStepNodes");
1769
1924
  this._findCenter(this._getRange())
1770
1925
  }
1771
1926
  }
@@ -1792,7 +1947,7 @@ Graph.prototype._animationStep = function() {
1792
1947
  var maxSteps = 1;
1793
1948
  this._physicsTick();
1794
1949
  var timeRequired = Date.now() - calculationTime;
1795
- while (timeRequired < (this.renderTimestep - this.renderTime) && maxSteps < this.maxRenderSteps) {
1950
+ while (timeRequired < (this.renderTimestep - this.renderTime) && maxSteps < this.maxPhysicsTicksPerRender) {
1796
1951
  this._physicsTick();
1797
1952
  timeRequired = Date.now() - calculationTime;
1798
1953
  maxSteps++;
@@ -1812,13 +1967,28 @@ if (typeof window !== 'undefined') {
1812
1967
 
1813
1968
  /**
1814
1969
  * Schedule a animation step with the refreshrate interval.
1815
- *
1816
- * @poram {Boolean} runCalculationStep
1817
1970
  */
1818
1971
  Graph.prototype.start = function() {
1819
1972
  if (this.moving || this.xIncrement != 0 || this.yIncrement != 0 || this.zoomIncrement != 0) {
1820
- if (!this.timer) {
1821
- this.timer = window.requestAnimationFrame(this._animationStep.bind(this), this.renderTimestep); // wait this.renderTimeStep milliseconds and perform the animation step function
1973
+ if (!this.timer) {
1974
+ var ua = navigator.userAgent.toLowerCase();
1975
+
1976
+ var requiresTimeout = false;
1977
+ if (ua.indexOf('msie 9.0') != -1) { // IE 9
1978
+ requiresTimeout = true;
1979
+ }
1980
+ else if (ua.indexOf('safari') != -1) { // safari
1981
+ if (ua.indexOf('chrome') <= -1) {
1982
+ requiresTimeout = true;
1983
+ }
1984
+ }
1985
+
1986
+ if (requiresTimeout == true) {
1987
+ this.timer = window.setTimeout(this._animationStep.bind(this), this.renderTimestep); // wait this.renderTimeStep milliseconds and perform the animation step function
1988
+ }
1989
+ else{
1990
+ this.timer = window.requestAnimationFrame(this._animationStep.bind(this), this.renderTimestep); // wait this.renderTimeStep milliseconds and perform the animation step function
1991
+ }
1822
1992
  }
1823
1993
  }
1824
1994
  else {
@@ -1861,7 +2031,12 @@ Graph.prototype.toggleFreeze = function() {
1861
2031
  };
1862
2032
 
1863
2033
 
1864
-
2034
+ /**
2035
+ * This function cleans the support nodes if they are not needed and adds them when they are.
2036
+ *
2037
+ * @param {boolean} [disableStart]
2038
+ * @private
2039
+ */
1865
2040
  Graph.prototype._configureSmoothCurves = function(disableStart) {
1866
2041
  if (disableStart === undefined) {
1867
2042
  disableStart = true;
@@ -1887,6 +2062,13 @@ Graph.prototype._configureSmoothCurves = function(disableStart) {
1887
2062
  }
1888
2063
  };
1889
2064
 
2065
+
2066
+ /**
2067
+ * Bezier curves require an anchor point to calculate the smooth flow. These points are nodes. These nodes are invisible but
2068
+ * are used for the force calculation.
2069
+ *
2070
+ * @private
2071
+ */
1890
2072
  Graph.prototype._createBezierNodes = function() {
1891
2073
  if (this.constants.smoothCurves == true) {
1892
2074
  for (var edgeId in this.edges) {
@@ -1899,6 +2081,7 @@ Graph.prototype._createBezierNodes = function() {
1899
2081
  {id:nodeId,
1900
2082
  mass:1,
1901
2083
  shape:'circle',
2084
+ image:"",
1902
2085
  internalMultiplier:1
1903
2086
  },{},{},this.constants);
1904
2087
  edge.via = this.sectors['support']['nodes'][nodeId];
@@ -1910,7 +2093,11 @@ Graph.prototype._createBezierNodes = function() {
1910
2093
  }
1911
2094
  };
1912
2095
 
1913
-
2096
+ /**
2097
+ * load the functions that load the mixins into the prototype.
2098
+ *
2099
+ * @private
2100
+ */
1914
2101
  Graph.prototype._initializeMixinLoaders = function () {
1915
2102
  for (var mixinFunction in graphMixinLoaders) {
1916
2103
  if (graphMixinLoaders.hasOwnProperty(mixinFunction)) {
@@ -1919,6 +2106,23 @@ Graph.prototype._initializeMixinLoaders = function () {
1919
2106
  }
1920
2107
  };
1921
2108
 
2109
+ /**
2110
+ * Load the XY positions of the nodes into the dataset.
2111
+ */
2112
+ Graph.prototype.storePosition = function() {
2113
+ var dataArray = [];
2114
+ for (var nodeId in this.nodes) {
2115
+ if (this.nodes.hasOwnProperty(nodeId)) {
2116
+ var node = this.nodes[nodeId];
2117
+ var allowedToMoveX = !this.nodes.xFixed;
2118
+ var allowedToMoveY = !this.nodes.yFixed;
2119
+ if (this.nodesData.data[nodeId].x != Math.round(node.x) || this.nodesData.data[nodeId].y != Math.round(node.y)) {
2120
+ dataArray.push({id:nodeId,x:Math.round(node.x),y:Math.round(node.y),allowedToMoveX:allowedToMoveX,allowedToMoveY:allowedToMoveY});
2121
+ }
2122
+ }
2123
+ }
2124
+ this.nodesData.update(dataArray);
2125
+ };
1922
2126
 
1923
2127
 
1924
2128