vis-rails 0.0.6 → 1.0.0

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