vis-rails 1.0.1 → 1.0.2

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1ab2d5d6af664775072379c257129f02c0895707
4
- data.tar.gz: 49b0ee9bc14b7cbd5ee8ace9790af0793fd20dfb
3
+ metadata.gz: d28f18ffa726cedf2df18f07ed3f34a5881b8693
4
+ data.tar.gz: 716adb84278baadcd222bbecf802759ddd0a9eb2
5
5
  SHA512:
6
- metadata.gz: f24f212105db1437094cf354652456e0d4a20bbdfc25a0972a13b6d2148826ad4cec7510ea695f20fc313d05d54aa80eeb49c78542914b161e16ea886cdfa16d
7
- data.tar.gz: ed541ec0771e8ea6e3245e2860a08e7a5753316a12d9e84d243b3c6f6f1442ec0aabd02310977586b1a41a431a15fff141e39e6f4515ffdc16e58d675c7ffc41
6
+ metadata.gz: 90d77f98a4d5d2379703b87f96341180716d49e0e124bcc4d2fed368518255b5861863bf5efdd890ee8df7c98ecb753ccb89c40492928695959612cc1c0b38be
7
+ data.tar.gz: 56d1c95072854f202bda9c12dcd197cba4c005009b05cce79f4250d096da5c346ce488d0f6bbd2b01508591074fcdc45e4fa457be32490106400d1b0b68fcd09
@@ -1,5 +1,5 @@
1
1
  module Vis
2
2
  module Rails
3
- VERSION = "1.0.1"
3
+ VERSION = "1.0.2"
4
4
  end
5
5
  end
@@ -179,7 +179,9 @@ function Graph (container, data, options) {
179
179
  border: '#666',
180
180
  background: '#FFFFC6'
181
181
  }
182
- }
182
+ },
183
+ moveable: true,
184
+ zoomable: true
183
185
  };
184
186
  this.editMode = this.constants.dataManipulation.initiallyVisible;
185
187
 
@@ -211,8 +213,11 @@ function Graph (container, data, options) {
211
213
  this._loadHierarchySystem();
212
214
 
213
215
  // apply options
216
+ this._setTranslation(this.frame.clientWidth / 2, this.frame.clientHeight / 2);
217
+ this._setScale(1);
214
218
  this.setOptions(options);
215
219
 
220
+
216
221
  // other vars
217
222
  this.freezeSimulation = false;// freeze the simulation
218
223
  this.cachedFunctions = {};
@@ -490,15 +495,19 @@ Graph.prototype.setData = function(data, disableStart) {
490
495
  if (!disableStart) {
491
496
  // find a stable position or start animating to a stable position
492
497
  if (this.stabilize) {
493
- this._stabilize();
498
+ var me = this;
499
+ setTimeout(function() {me._stabilize(); me.start();},0)
500
+ }
501
+ else {
502
+ this.start();
494
503
  }
495
- this.start();
496
504
  }
497
505
  };
498
506
 
499
507
  /**
500
508
  * Set options
501
509
  * @param {Object} options
510
+ * @param {Boolean} [initializeView] | set zoom and translation to default.
502
511
  */
503
512
  Graph.prototype.setOptions = function (options) {
504
513
  if (options) {
@@ -512,7 +521,8 @@ Graph.prototype.setOptions = function (options) {
512
521
  if (options.freezeForStabilization !== undefined) {this.constants.freezeForStabilization = options.freezeForStabilization;}
513
522
  if (options.configurePhysics !== undefined){this.constants.configurePhysics = options.configurePhysics;}
514
523
  if (options.stabilizationIterations !== undefined) {this.constants.stabilizationIterations = options.stabilizationIterations;}
515
-
524
+ if (options.moveable !== undefined) {this.constants.moveable = options.moveable;}
525
+ if (options.zoomable !== undefined) {this.constants.zoomable = options.zoomable;}
516
526
 
517
527
 
518
528
  if (options.labels !== undefined) {
@@ -557,6 +567,17 @@ Graph.prototype.setOptions = function (options) {
557
567
  }
558
568
  }
559
569
  }
570
+
571
+ if (options.physics.hierarchicalRepulsion) {
572
+ this.constants.hierarchicalLayout.enabled = true;
573
+ this.constants.physics.hierarchicalRepulsion.enabled = true;
574
+ this.constants.physics.barnesHut.enabled = false;
575
+ for (prop in options.physics.hierarchicalRepulsion) {
576
+ if (options.physics.hierarchicalRepulsion.hasOwnProperty(prop)) {
577
+ this.constants.physics.hierarchicalRepulsion[prop] = options.physics.hierarchicalRepulsion[prop];
578
+ }
579
+ }
580
+ }
560
581
  }
561
582
 
562
583
  if (options.hierarchicalLayout) {
@@ -716,11 +737,10 @@ Graph.prototype.setOptions = function (options) {
716
737
 
717
738
  // bind keys. If disabled, this will not do anything;
718
739
  this._createKeyBinds();
719
-
720
740
  this.setSize(this.width, this.height);
721
- this._setTranslation(this.frame.clientWidth / 2, this.frame.clientHeight / 2);
722
- this._setScale(1);
723
- this._redraw();
741
+ this.moving = true;
742
+ this.start();
743
+
724
744
  };
725
745
 
726
746
  /**
@@ -936,11 +956,11 @@ Graph.prototype._handleOnDrag = function(event) {
936
956
  var node = s.node;
937
957
 
938
958
  if (!s.xFixed) {
939
- node.x = me._canvasToX(me._xToCanvas(s.x) + deltaX);
959
+ node.x = me._XconvertDOMtoCanvas(me._XconvertCanvasToDOM(s.x) + deltaX);
940
960
  }
941
961
 
942
962
  if (!s.yFixed) {
943
- node.y = me._canvasToY(me._yToCanvas(s.y) + deltaY);
963
+ node.y = me._YconvertDOMtoCanvas(me._YconvertCanvasToDOM(s.y) + deltaY);
944
964
  }
945
965
  });
946
966
 
@@ -951,15 +971,18 @@ Graph.prototype._handleOnDrag = function(event) {
951
971
  }
952
972
  }
953
973
  else {
954
- // move the graph
955
- var diffX = pointer.x - this.drag.pointer.x;
956
- var diffY = pointer.y - this.drag.pointer.y;
957
-
958
- this._setTranslation(
959
- this.drag.translation.x + diffX,
960
- this.drag.translation.y + diffY);
961
- this._redraw();
962
- this.moving = true;
974
+ if (this.constants.moveable == true) {
975
+ // move the graph
976
+ var diffX = pointer.x - this.drag.pointer.x;
977
+ var diffY = pointer.y - this.drag.pointer.y;
978
+
979
+ this._setTranslation(
980
+ this.drag.translation.x + diffX,
981
+ this.drag.translation.y + diffY);
982
+ this._redraw();
983
+ this.moving = true;
984
+ this.start();
985
+ }
963
986
  }
964
987
  };
965
988
 
@@ -1047,30 +1070,38 @@ Graph.prototype._onPinch = function (event) {
1047
1070
  * @private
1048
1071
  */
1049
1072
  Graph.prototype._zoom = function(scale, pointer) {
1050
- var scaleOld = this._getScale();
1051
- if (scale < 0.00001) {
1052
- scale = 0.00001;
1053
- }
1054
- if (scale > 10) {
1055
- scale = 10;
1056
- }
1057
- // + this.frame.canvas.clientHeight / 2
1058
- var translation = this._getTranslation();
1073
+ if (this.constants.zoomable == true) {
1074
+ var scaleOld = this._getScale();
1075
+ if (scale < 0.00001) {
1076
+ scale = 0.00001;
1077
+ }
1078
+ if (scale > 10) {
1079
+ scale = 10;
1080
+ }
1081
+ // + this.frame.canvas.clientHeight / 2
1082
+ var translation = this._getTranslation();
1059
1083
 
1060
- var scaleFrac = scale / scaleOld;
1061
- var tx = (1 - scaleFrac) * pointer.x + translation.x * scaleFrac;
1062
- var ty = (1 - scaleFrac) * pointer.y + translation.y * scaleFrac;
1084
+ var scaleFrac = scale / scaleOld;
1085
+ var tx = (1 - scaleFrac) * pointer.x + translation.x * scaleFrac;
1086
+ var ty = (1 - scaleFrac) * pointer.y + translation.y * scaleFrac;
1063
1087
 
1064
- this.areaCenter = {"x" : this._canvasToX(pointer.x),
1065
- "y" : this._canvasToY(pointer.y)};
1088
+ this.areaCenter = {"x" : this._XconvertDOMtoCanvas(pointer.x),
1089
+ "y" : this._YconvertDOMtoCanvas(pointer.y)};
1066
1090
 
1067
- this._setScale(scale);
1068
- this._setTranslation(tx, ty);
1069
- this.updateClustersDefault();
1070
- this._redraw();
1091
+ this._setScale(scale);
1092
+ this._setTranslation(tx, ty);
1093
+ this.updateClustersDefault();
1094
+ this._redraw();
1071
1095
 
1096
+ if (scaleOld < scale) {
1097
+ this.emit("zoom", {direction:"+"});
1098
+ }
1099
+ else {
1100
+ this.emit("zoom", {direction:"-"});
1101
+ }
1072
1102
 
1073
- return scale;
1103
+ return scale;
1104
+ }
1074
1105
  };
1075
1106
 
1076
1107
 
@@ -1156,10 +1187,10 @@ Graph.prototype._onMouseMoveTitle = function (event) {
1156
1187
  */
1157
1188
  Graph.prototype._checkShowPopup = function (pointer) {
1158
1189
  var obj = {
1159
- left: this._canvasToX(pointer.x),
1160
- top: this._canvasToY(pointer.y),
1161
- right: this._canvasToX(pointer.x),
1162
- bottom: this._canvasToY(pointer.y)
1190
+ left: this._XconvertDOMtoCanvas(pointer.x),
1191
+ top: this._YconvertDOMtoCanvas(pointer.y),
1192
+ right: this._XconvertDOMtoCanvas(pointer.x),
1193
+ bottom: this._YconvertDOMtoCanvas(pointer.y)
1163
1194
  };
1164
1195
 
1165
1196
  var id;
@@ -1623,12 +1654,12 @@ Graph.prototype._redraw = function() {
1623
1654
  ctx.scale(this.scale, this.scale);
1624
1655
 
1625
1656
  this.canvasTopLeft = {
1626
- "x": this._canvasToX(0),
1627
- "y": this._canvasToY(0)
1657
+ "x": this._XconvertDOMtoCanvas(0),
1658
+ "y": this._YconvertDOMtoCanvas(0)
1628
1659
  };
1629
1660
  this.canvasBottomRight = {
1630
- "x": this._canvasToX(this.frame.canvas.clientWidth),
1631
- "y": this._canvasToY(this.frame.canvas.clientHeight)
1661
+ "x": this._XconvertDOMtoCanvas(this.frame.canvas.clientWidth),
1662
+ "y": this._YconvertDOMtoCanvas(this.frame.canvas.clientHeight)
1632
1663
  };
1633
1664
 
1634
1665
  this._doInAllSectors("_drawAllSectorNodes",ctx);
@@ -1662,6 +1693,8 @@ Graph.prototype._setTranslation = function(offsetX, offsetY) {
1662
1693
  if (offsetY !== undefined) {
1663
1694
  this.translation.y = offsetY;
1664
1695
  }
1696
+
1697
+ this.emit('viewChanged');
1665
1698
  };
1666
1699
 
1667
1700
  /**
@@ -1695,45 +1728,70 @@ Graph.prototype._getScale = function() {
1695
1728
  };
1696
1729
 
1697
1730
  /**
1698
- * Convert a horizontal point on the HTML canvas to the x-value of the model
1731
+ * Convert the X coordinate in DOM-space (coordinate point in browser relative to the container div) to
1732
+ * the X coordinate in canvas-space (the simulation sandbox, which the camera looks upon)
1699
1733
  * @param {number} x
1700
1734
  * @returns {number}
1701
1735
  * @private
1702
1736
  */
1703
- Graph.prototype._canvasToX = function(x) {
1737
+ Graph.prototype._XconvertDOMtoCanvas = function(x) {
1704
1738
  return (x - this.translation.x) / this.scale;
1705
1739
  };
1706
1740
 
1707
1741
  /**
1708
- * Convert an x-value in the model to a horizontal point on the HTML canvas
1742
+ * Convert the X coordinate in canvas-space (the simulation sandbox, which the camera looks upon) to
1743
+ * the X coordinate in DOM-space (coordinate point in browser relative to the container div)
1709
1744
  * @param {number} x
1710
1745
  * @returns {number}
1711
1746
  * @private
1712
1747
  */
1713
- Graph.prototype._xToCanvas = function(x) {
1748
+ Graph.prototype._XconvertCanvasToDOM = function(x) {
1714
1749
  return x * this.scale + this.translation.x;
1715
1750
  };
1716
1751
 
1717
1752
  /**
1718
- * Convert a vertical point on the HTML canvas to the y-value of the model
1753
+ * Convert the Y coordinate in DOM-space (coordinate point in browser relative to the container div) to
1754
+ * the Y coordinate in canvas-space (the simulation sandbox, which the camera looks upon)
1719
1755
  * @param {number} y
1720
1756
  * @returns {number}
1721
1757
  * @private
1722
1758
  */
1723
- Graph.prototype._canvasToY = function(y) {
1759
+ Graph.prototype._YconvertDOMtoCanvas = function(y) {
1724
1760
  return (y - this.translation.y) / this.scale;
1725
1761
  };
1726
1762
 
1727
1763
  /**
1728
- * Convert an y-value in the model to a vertical point on the HTML canvas
1764
+ * Convert the Y coordinate in canvas-space (the simulation sandbox, which the camera looks upon) to
1765
+ * the Y coordinate in DOM-space (coordinate point in browser relative to the container div)
1729
1766
  * @param {number} y
1730
1767
  * @returns {number}
1731
1768
  * @private
1732
1769
  */
1733
- Graph.prototype._yToCanvas = function(y) {
1770
+ Graph.prototype._YconvertCanvasToDOM = function(y) {
1734
1771
  return y * this.scale + this.translation.y ;
1735
1772
  };
1736
1773
 
1774
+
1775
+ /**
1776
+ *
1777
+ * @param {object} pos = {x: number, y: number}
1778
+ * @returns {{x: number, y: number}}
1779
+ * @constructor
1780
+ */
1781
+ Graph.prototype.canvasToDOM = function(pos) {
1782
+ return {x:this._XconvertCanvasToDOM(pos.x),y:this._YconvertCanvasToDOM(pos.y)};
1783
+ }
1784
+
1785
+ /**
1786
+ *
1787
+ * @param {object} pos = {x: number, y: number}
1788
+ * @returns {{x: number, y: number}}
1789
+ * @constructor
1790
+ */
1791
+ Graph.prototype.DOMtoCanvas = function(pos) {
1792
+ return {x:this._XconvertDOMtoCanvas(pos.x),y:this._YconvertDOMtoCanvas(pos.y)};
1793
+ }
1794
+
1737
1795
  /**
1738
1796
  * Redraw all nodes
1739
1797
  * The 2d context of a HTML canvas can be retrieved by canvas.getContext('2d');
@@ -2,10 +2,10 @@
2
2
  * @class Groups
3
3
  * This class can store groups and properties specific for groups.
4
4
  */
5
- Groups = function () {
5
+ function Groups() {
6
6
  this.clear();
7
7
  this.defaultIndex = 0;
8
- };
8
+ }
9
9
 
10
10
 
11
11
  /**
@@ -2,11 +2,11 @@
2
2
  * @class Images
3
3
  * This class loads images and keeps them stored.
4
4
  */
5
- Images = function () {
5
+ function Images() {
6
6
  this.images = {};
7
7
 
8
8
  this.callback = undefined;
9
- };
9
+ }
10
10
 
11
11
  /**
12
12
  * Set an onload callback function. This will be called each time an image
@@ -243,10 +243,10 @@ var manipulationMixin = {
243
243
  this.cachedFunctions["_handleOnDrag"] = this._handleOnDrag;
244
244
  this._handleOnDrag = function(event) {
245
245
  var pointer = this._getPointer(event.gesture.center);
246
- this.sectors['support']['nodes']['targetNode'].x = this._canvasToX(pointer.x);
247
- this.sectors['support']['nodes']['targetNode'].y = this._canvasToY(pointer.y);
248
- this.sectors['support']['nodes']['targetViaNode'].x = 0.5 * (this._canvasToX(pointer.x) + this.edges['connectionEdge'].from.x);
249
- this.sectors['support']['nodes']['targetViaNode'].y = this._canvasToY(pointer.y);
246
+ this.sectors['support']['nodes']['targetNode'].x = this._XconvertDOMtoCanvas(pointer.x);
247
+ this.sectors['support']['nodes']['targetNode'].y = this._YconvertDOMtoCanvas(pointer.y);
248
+ this.sectors['support']['nodes']['targetViaNode'].x = 0.5 * (this._XconvertDOMtoCanvas(pointer.x) + this.edges['connectionEdge'].from.x);
249
+ this.sectors['support']['nodes']['targetViaNode'].y = this._YconvertDOMtoCanvas(pointer.y);
250
250
  };
251
251
 
252
252
  this.moving = true;
@@ -40,8 +40,8 @@ var SelectionMixin = {
40
40
  * @private
41
41
  */
42
42
  _pointerToPositionObject : function(pointer) {
43
- var x = this._canvasToX(pointer.x);
44
- var y = this._canvasToY(pointer.y);
43
+ var x = this._XconvertDOMtoCanvas(pointer.x);
44
+ var y = this._YconvertDOMtoCanvas(pointer.y);
45
45
 
46
46
  return {left: x,
47
47
  top: y,
@@ -433,8 +433,8 @@ var SelectionMixin = {
433
433
  var node = this._getNodeAt(pointer);
434
434
  if (node != null && node !== undefined) {
435
435
  // we reset the areaCenter here so the opening of the node will occur
436
- this.areaCenter = {"x" : this._canvasToX(pointer.x),
437
- "y" : this._canvasToY(pointer.y)};
436
+ this.areaCenter = {"x" : this._XconvertDOMtoCanvas(pointer.x),
437
+ "y" : this._YconvertDOMtoCanvas(pointer.y)};
438
438
  this.openCluster(node);
439
439
  }
440
440
  this.emit("doubleClick", this.getSelection());
@@ -188,7 +188,7 @@ var physicsMixin = {
188
188
  */
189
189
  _calculateSpringForces: function () {
190
190
  var edgeLength, edge, edgeId;
191
- var dx, dy, fx, fy, springForce, length;
191
+ var dx, dy, fx, fy, springForce, distance;
192
192
  var edges = this.edges;
193
193
 
194
194
  // forces caused by the edges, modelled as springs
@@ -204,13 +204,14 @@ var physicsMixin = {
204
204
 
205
205
  dx = (edge.from.x - edge.to.x);
206
206
  dy = (edge.from.y - edge.to.y);
207
- length = Math.sqrt(dx * dx + dy * dy);
207
+ distance = Math.sqrt(dx * dx + dy * dy);
208
208
 
209
- if (length == 0) {
210
- length = 0.01;
209
+ if (distance == 0) {
210
+ distance = 0.01;
211
211
  }
212
212
 
213
- springForce = this.constants.physics.springConstant * (edgeLength - length) / length;
213
+ // the 1/distance is so the fx and fy can be calculated without sine or cosine.
214
+ springForce = this.constants.physics.springConstant * (edgeLength - distance) / distance;
214
215
 
215
216
  fx = dx * springForce;
216
217
  fy = dy * springForce;
@@ -272,17 +273,18 @@ var physicsMixin = {
272
273
  * @private
273
274
  */
274
275
  _calculateSpringForce: function (node1, node2, edgeLength) {
275
- var dx, dy, fx, fy, springForce, length;
276
+ var dx, dy, fx, fy, springForce, distance;
276
277
 
277
278
  dx = (node1.x - node2.x);
278
279
  dy = (node1.y - node2.y);
279
- length = Math.sqrt(dx * dx + dy * dy);
280
+ distance = Math.sqrt(dx * dx + dy * dy);
280
281
 
281
- if (length == 0) {
282
- length = 0.01;
282
+ if (distance == 0) {
283
+ distance = 0.01;
283
284
  }
284
285
 
285
- springForce = this.constants.physics.springConstant * (edgeLength - length) / length;
286
+ // the 1/distance is so the fx and fy can be calculated without sine or cosine.
287
+ springForce = this.constants.physics.springConstant * (edgeLength - distance) / distance;
286
288
 
287
289
  fx = dx * springForce;
288
290
  fy = dy * springForce;
@@ -317,7 +319,7 @@ var physicsMixin = {
317
319
  '<table id="graph_BH_table" style="display:none">' +
318
320
  '<tr><td><b>Barnes Hut</b></td></tr>' +
319
321
  '<tr>' +
320
- '<td width="150px">gravitationalConstant</td><td>0</td><td><input type="range" min="500" max="20000" value="' + (-1 * this.constants.physics.barnesHut.gravitationalConstant) + '" step="25" style="width:300px" id="graph_BH_gc"></td><td width="50px">-20000</td><td><input value="' + (-1 * this.constants.physics.barnesHut.gravitationalConstant) + '" id="graph_BH_gc_value" style="width:60px"></td>' +
322
+ '<td width="150px">gravitationalConstant</td><td>0</td><td><input type="range" min="0" max="20000" value="' + (-1 * this.constants.physics.barnesHut.gravitationalConstant) + '" step="25" style="width:300px" id="graph_BH_gc"></td><td width="50px">-20000</td><td><input value="' + (-1 * this.constants.physics.barnesHut.gravitationalConstant) + '" id="graph_BH_gc_value" style="width:60px"></td>' +
321
323
  '</tr>' +
322
324
  '<tr>' +
323
325
  '<td width="150px">centralGravity</td><td>0</td><td><input type="range" min="0" max="3" value="' + this.constants.physics.barnesHut.centralGravity + '" step="0.05" style="width:300px" id="graph_BH_cg"></td><td>3</td><td><input value="' + this.constants.physics.barnesHut.centralGravity + '" id="graph_BH_cg_value" style="width:60px"></td>' +
@@ -641,10 +643,12 @@ function switchConfigurations () {
641
643
  this.constants.physics.barnesHut.enabled = false;
642
644
  }
643
645
  else if (radioButton == "H") {
644
- this.constants.hierarchicalLayout.enabled = true;
645
- this.constants.physics.hierarchicalRepulsion.enabled = true;
646
- this.constants.physics.barnesHut.enabled = false;
647
- this._setupHierarchicalLayout();
646
+ if (this.constants.hierarchicalLayout.enabled == false) {
647
+ this.constants.hierarchicalLayout.enabled = true;
648
+ this.constants.physics.hierarchicalRepulsion.enabled = true;
649
+ this.constants.physics.barnesHut.enabled = false;
650
+ this._setupHierarchicalLayout();
651
+ }
648
652
  }
649
653
  else {
650
654
  this.constants.hierarchicalLayout.enabled = false;
@@ -3,6 +3,7 @@
3
3
  */
4
4
  var vis = {
5
5
  util: util,
6
+ moment: moment,
6
7
 
7
8
  DataSet: DataSet,
8
9
  DataView: DataView,
@@ -24,7 +24,7 @@
24
24
  * @param {Date} [end] The end date
25
25
  * @param {Number} [minimumStep] Optional. Minimum step size in milliseconds
26
26
  */
27
- TimeStep = function(start, end, minimumStep) {
27
+ function TimeStep(start, end, minimumStep) {
28
28
  // variables
29
29
  this.current = new Date();
30
30
  this._start = new Date();
@@ -36,7 +36,7 @@ TimeStep = function(start, end, minimumStep) {
36
36
 
37
37
  // initialize the range
38
38
  this.setRange(start, end, minimumStep);
39
- };
39
+ }
40
40
 
41
41
  /// enum scale
42
42
  TimeStep.SCALE = {
@@ -314,8 +314,7 @@ TimeStep.prototype.snap = function(date) {
314
314
  clone.setSeconds(0);
315
315
  clone.setMilliseconds(0);
316
316
  }
317
- else if (this.scale == TimeStep.SCALE.DAY ||
318
- this.scale == TimeStep.SCALE.WEEKDAY) {
317
+ else if (this.scale == TimeStep.SCALE.DAY) {
319
318
  //noinspection FallthroughInSwitchStatementJS
320
319
  switch (this.step) {
321
320
  case 5:
@@ -328,6 +327,19 @@ TimeStep.prototype.snap = function(date) {
328
327
  clone.setSeconds(0);
329
328
  clone.setMilliseconds(0);
330
329
  }
330
+ else if (this.scale == TimeStep.SCALE.WEEKDAY) {
331
+ //noinspection FallthroughInSwitchStatementJS
332
+ switch (this.step) {
333
+ case 5:
334
+ case 2:
335
+ clone.setHours(Math.round(clone.getHours() / 12) * 12); break;
336
+ default:
337
+ clone.setHours(Math.round(clone.getHours() / 6) * 6); break;
338
+ }
339
+ clone.setMinutes(0);
340
+ clone.setSeconds(0);
341
+ clone.setMilliseconds(0);
342
+ }
331
343
  else if (this.scale == TimeStep.SCALE.HOUR) {
332
344
  switch (this.step) {
333
345
  case 4:
@@ -11,7 +11,7 @@ function Timeline (container, items, options) {
11
11
 
12
12
  var me = this;
13
13
  var now = moment().hours(0).minutes(0).seconds(0).milliseconds(0);
14
- this.options = {
14
+ this.defaultOptions = {
15
15
  orientation: 'bottom',
16
16
  direction: 'horizontal', // 'horizontal' or 'vertical'
17
17
  autoResize: true,
@@ -25,7 +25,6 @@ function Timeline (container, items, options) {
25
25
  },
26
26
 
27
27
  selectable: true,
28
- snap: null, // will be specified after timeaxis is created
29
28
 
30
29
  min: null,
31
30
  max: null,
@@ -39,6 +38,13 @@ function Timeline (container, items, options) {
39
38
  showCurrentTime: false,
40
39
  showCustomTime: false,
41
40
 
41
+ groupOrder: null,
42
+
43
+ width: null,
44
+ height: null,
45
+ maxHeight: null,
46
+ minHeight: null,
47
+
42
48
  type: 'box',
43
49
  align: 'center',
44
50
  margin: {
@@ -58,11 +64,17 @@ function Timeline (container, items, options) {
58
64
  },
59
65
  onRemove: function (item, callback) {
60
66
  callback(item);
61
- },
67
+ }
68
+ };
69
+
70
+ this.options = {};
71
+ util.deepExtend(this.options, this.defaultOptions);
72
+ util.deepExtend(this.options, {
73
+ snap: null, // will be specified after timeaxis is created
62
74
 
63
75
  toScreen: me._toScreen.bind(me),
64
76
  toTime: me._toTime.bind(me)
65
- };
77
+ });
66
78
 
67
79
  // root panel
68
80
  var rootOptions = util.extend(Object.create(this.options), {
@@ -280,7 +292,7 @@ Emitter(Timeline.prototype);
280
292
  * @param {Object} options TODO: describe the available options
281
293
  */
282
294
  Timeline.prototype.setOptions = function (options) {
283
- util.extend(this.options, options);
295
+ util.deepExtend(this.options, options);
284
296
 
285
297
  if ('editable' in options) {
286
298
  var isBoolean = typeof options.editable === 'boolean';
@@ -439,6 +451,33 @@ Timeline.prototype.setGroups = function setGroups(groups) {
439
451
  this.itemSet.setGroups(newDataSet);
440
452
  };
441
453
 
454
+ /**
455
+ * Clear the Timeline. By Default, items, groups and options are cleared.
456
+ * Example usage:
457
+ *
458
+ * timeline.clear(); // clear items, groups, and options
459
+ * timeline.clear({options: true}); // clear options only
460
+ *
461
+ * @param {Object} [what] Optionally specify what to clear. By default:
462
+ * {items: true, groups: true, options: true}
463
+ */
464
+ Timeline.prototype.clear = function clear(what) {
465
+ // clear items
466
+ if (!what || what.items) {
467
+ this.setItems(null);
468
+ }
469
+
470
+ // clear groups
471
+ if (!what || what.groups) {
472
+ this.setGroups(null);
473
+ }
474
+
475
+ // clear options
476
+ if (!what || what.options) {
477
+ this.setOptions(this.defaultOptions);
478
+ }
479
+ };
480
+
442
481
  /**
443
482
  * Set Timeline window such that it fits all items
444
483
  */
@@ -535,7 +574,7 @@ Timeline.prototype.getSelection = function getSelection() {
535
574
  * Where start and end can be a Date, number, or string, and range is an
536
575
  * object with properties start and end.
537
576
  *
538
- * @param {Date | Number | String} [start] Start date of visible window
577
+ * @param {Date | Number | String | Object} [start] Start date of visible window
539
578
  * @param {Date | Number | String} [end] End date of visible window
540
579
  */
541
580
  Timeline.prototype.setWindow = function setWindow(start, end) {
@@ -560,6 +599,14 @@ Timeline.prototype.getWindow = function setWindow() {
560
599
  };
561
600
  };
562
601
 
602
+ /**
603
+ * Force a repaint of the Timeline. Can be useful to manually repaint when
604
+ * option autoResize=false
605
+ */
606
+ Timeline.prototype.repaint = function repaint() {
607
+ this.rootPanel.repaint();
608
+ };
609
+
563
610
  /**
564
611
  * Handle selecting/deselecting an item when tapping it
565
612
  * @param {Event} event
@@ -626,6 +673,11 @@ Timeline.prototype._onAddItem = function (event) {
626
673
  content: 'new item'
627
674
  };
628
675
 
676
+ // when default type is a range, add a default end date to the new item
677
+ if (this.options.type === 'range' || this.options.type == 'rangeoverflow') {
678
+ newItem.end = this.timeAxis.snap(this._toTime(x + this.rootPanel.width / 5));
679
+ }
680
+
629
681
  var id = util.randomUUID();
630
682
  newItem[this.itemsData.fieldId] = id;
631
683
 
@@ -51,6 +51,14 @@ Group.prototype._create = function() {
51
51
  this.dom.background = document.createElement('div');
52
52
 
53
53
  this.dom.axis = document.createElement('div');
54
+
55
+ // create a hidden marker to detect when the Timelines container is attached
56
+ // to the DOM, or the style of a parent of the Timeline is changed from
57
+ // display:none is changed to visible.
58
+ this.dom.marker = document.createElement('div');
59
+ this.dom.marker.style.visibility = 'hidden';
60
+ this.dom.marker.innerHTML = '?';
61
+ this.dom.background.appendChild(this.dom.marker);
54
62
  };
55
63
 
56
64
  /**
@@ -122,6 +130,20 @@ Group.prototype.repaint = function repaint(range, margin, restack) {
122
130
 
123
131
  this.visibleItems = this._updateVisibleItems(this.orderedItems, this.visibleItems, range);
124
132
 
133
+ // force recalculation of the height of the items when the marker height changed
134
+ // (due to the Timeline being attached to the DOM or changed from display:none to visible)
135
+ var markerHeight = this.dom.marker.clientHeight;
136
+ if (markerHeight != this.lastMarkerHeight) {
137
+ this.lastMarkerHeight = markerHeight;
138
+
139
+ util.forEach(this.items, function (item) {
140
+ item.dirty = true;
141
+ if (item.displayed) item.repaint();
142
+ });
143
+
144
+ restack = true;
145
+ }
146
+
125
147
  // reposition visible items vertically
126
148
  if (this.itemSet.options.stack) { // TODO: ugly way to access options...
127
149
  stack.stack(this.visibleItems, margin, restack);
@@ -129,7 +151,6 @@ Group.prototype.repaint = function repaint(range, margin, restack) {
129
151
  else { // no stacking
130
152
  stack.nostack(this.visibleItems, margin);
131
153
  }
132
- this.stackDirty = false;
133
154
  for (var i = 0, ii = this.visibleItems.length; i < ii; i++) {
134
155
  var item = this.visibleItems[i];
135
156
  item.repositionY();
@@ -519,7 +519,8 @@ ItemSet.prototype.setGroups = function setGroups(groups) {
519
519
 
520
520
  // remove all drawn groups
521
521
  ids = this.groupsData.getIds();
522
- this._onRemoveGroups(ids);
522
+ this.groupsData = null;
523
+ this._onRemoveGroups(ids); // note: this will cause a repaint
523
524
  }
524
525
 
525
526
  // replace the dataset
@@ -770,8 +771,7 @@ ItemSet.prototype._orderGroups = function () {
770
771
  // hide all groups, removes them from the DOM
771
772
  var groups = this.groups;
772
773
  groupIds.forEach(function (groupId) {
773
- var group = groups[groupId];
774
- group.hide();
774
+ groups[groupId].hide();
775
775
  });
776
776
 
777
777
  // show the groups again, attach them to the DOM in correct order
@@ -813,7 +813,9 @@ ItemSet.prototype._updateItem = function _updateItem(item, itemData) {
813
813
  var oldGroupId = item.data.group;
814
814
 
815
815
  item.data = itemData;
816
- item.repaint();
816
+ if (item.displayed) {
817
+ item.repaint();
818
+ }
817
819
 
818
820
  // update group
819
821
  if (oldGroupId != item.data.group) {
@@ -101,6 +101,7 @@ RootPanel.prototype.repaint = function repaint() {
101
101
 
102
102
  // update frame size
103
103
  this.frame.style.maxHeight = util.option.asSize(this.options.maxHeight, '');
104
+ this.frame.style.minHeight = util.option.asSize(this.options.minHeight, '');
104
105
  this._updateSize();
105
106
 
106
107
  // if the root panel or any of its childs is resized, repaint again,
@@ -407,32 +407,32 @@ TimeAxis.prototype._repaintLine = function() {
407
407
  * @private
408
408
  */
409
409
  TimeAxis.prototype._calculateCharSize = function () {
410
- // determine the char width and height on the minor axis
411
- if (!('minorCharHeight' in this.props)) {
412
- var textMinor = document.createTextNode('0');
413
- var measureCharMinor = document.createElement('DIV');
414
- measureCharMinor.className = 'text minor measure';
415
- measureCharMinor.appendChild(textMinor);
416
- this.frame.appendChild(measureCharMinor);
410
+ // Note: We calculate char size with every repaint. Size may change, for
411
+ // example when any of the timelines parents had display:none for example.
417
412
 
418
- this.props.minorCharHeight = measureCharMinor.clientHeight;
419
- this.props.minorCharWidth = measureCharMinor.clientWidth;
413
+ // determine the char width and height on the minor axis
414
+ if (!this.dom.measureCharMinor) {
415
+ this.dom.measureCharMinor = document.createElement('DIV');
416
+ this.dom.measureCharMinor.className = 'text minor measure';
417
+ this.dom.measureCharMinor.style.position = 'absolute';
420
418
 
421
- this.frame.removeChild(measureCharMinor);
419
+ this.dom.measureCharMinor.appendChild(document.createTextNode('0'));
420
+ this.frame.appendChild(this.dom.measureCharMinor);
422
421
  }
422
+ this.props.minorCharHeight = this.dom.measureCharMinor.clientHeight;
423
+ this.props.minorCharWidth = this.dom.measureCharMinor.clientWidth;
423
424
 
424
- if (!('majorCharHeight' in this.props)) {
425
- var textMajor = document.createTextNode('0');
426
- var measureCharMajor = document.createElement('DIV');
427
- measureCharMajor.className = 'text major measure';
428
- measureCharMajor.appendChild(textMajor);
429
- this.frame.appendChild(measureCharMajor);
430
-
431
- this.props.majorCharHeight = measureCharMajor.clientHeight;
432
- this.props.majorCharWidth = measureCharMajor.clientWidth;
425
+ // determine the char width and height on the major axis
426
+ if (!this.dom.measureCharMajor) {
427
+ this.dom.measureCharMajor = document.createElement('DIV');
428
+ this.dom.measureCharMajor.className = 'text minor measure';
429
+ this.dom.measureCharMajor.style.position = 'absolute';
433
430
 
434
- this.frame.removeChild(measureCharMajor);
431
+ this.dom.measureCharMajor.appendChild(document.createTextNode('0'));
432
+ this.frame.appendChild(this.dom.measureCharMajor);
435
433
  }
434
+ this.props.majorCharHeight = this.dom.measureCharMajor.clientHeight;
435
+ this.props.majorCharWidth = this.dom.measureCharMajor.clientWidth;
436
436
  };
437
437
 
438
438
  /**
@@ -46,9 +46,10 @@ ItemRangeOverflow.prototype.repositionX = function repositionX() {
46
46
 
47
47
  this.left = start;
48
48
  var boxWidth = Math.max(end - start, 1);
49
- this.width = (this.props.content.width < boxWidth) ?
50
- boxWidth :
51
- start + contentLeft + this.props.content.width;
49
+ this.width = boxWidth + this.props.content.width;
50
+ // Note: The calculation of width is an optimistic calculation, giving
51
+ // a width which will not change when moving the Timeline
52
+ // So no restacking needed, which is nicer for the eye
52
53
 
53
54
  this.dom.box.style.left = this.left + 'px';
54
55
  this.dom.box.style.width = boxWidth + 'px';
@@ -97,6 +97,40 @@ util.extend = function (a, b) {
97
97
  return a;
98
98
  };
99
99
 
100
+ /**
101
+ * Deep extend an object a with the properties of object b
102
+ * @param {Object} a
103
+ * @param {Object} b
104
+ * @returns {Object}
105
+ */
106
+ util.deepExtend = function deepExtend (a, b) {
107
+ // TODO: add support for Arrays to deepExtend
108
+ if (Array.isArray(b)) {
109
+ throw new TypeError('Arrays are not supported by deepExtend');
110
+ }
111
+
112
+ for (var prop in b) {
113
+ if (b.hasOwnProperty(prop)) {
114
+ if (b[prop] && b[prop].constructor === Object) {
115
+ if (a[prop] === undefined) {
116
+ a[prop] = {};
117
+ }
118
+ if (a[prop].constructor === Object) {
119
+ deepExtend(a[prop], b[prop]);
120
+ }
121
+ else {
122
+ a[prop] = b[prop];
123
+ }
124
+ } else if (Array.isArray(b[prop])) {
125
+ throw new TypeError('Arrays are not supported by deepExtend');
126
+ } else {
127
+ a[prop] = b[prop];
128
+ }
129
+ }
130
+ }
131
+ return a;
132
+ };
133
+
100
134
  /**
101
135
  * Test whether all elements in two arrays are equal.
102
136
  * @param {Array} a
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: vis-rails
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.1
4
+ version: 1.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - AlexVangelov
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-05-03 00:00:00.000000000 Z
11
+ date: 2014-06-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: momentjs-rails