vis-rails 0.0.4 → 0.0.5

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 (58) hide show
  1. checksums.yaml +5 -13
  2. data/lib/vis/rails/version.rb +1 -1
  3. data/vendor/assets/component/emitter.js +162 -0
  4. data/vendor/assets/javascripts/vis.js +1 -0
  5. data/vendor/assets/vis/DataSet.js +8 -2
  6. data/vendor/assets/vis/DataView.js +8 -4
  7. data/vendor/assets/vis/graph/Edge.js +210 -78
  8. data/vendor/assets/vis/graph/Graph.js +474 -652
  9. data/vendor/assets/vis/graph/Node.js +119 -82
  10. data/vendor/assets/vis/graph/css/graph-manipulation.css +128 -0
  11. data/vendor/assets/vis/graph/css/graph-navigation.css +62 -0
  12. data/vendor/assets/vis/graph/graphMixins/ClusterMixin.js +1141 -0
  13. data/vendor/assets/vis/graph/graphMixins/HierarchicalLayoutMixin.js +296 -0
  14. data/vendor/assets/vis/graph/graphMixins/ManipulationMixin.js +433 -0
  15. data/vendor/assets/vis/graph/graphMixins/MixinLoader.js +201 -0
  16. data/vendor/assets/vis/graph/graphMixins/NavigationMixin.js +173 -0
  17. data/vendor/assets/vis/graph/graphMixins/SectorsMixin.js +552 -0
  18. data/vendor/assets/vis/graph/graphMixins/SelectionMixin.js +558 -0
  19. data/vendor/assets/vis/graph/graphMixins/physics/BarnesHut.js +373 -0
  20. data/vendor/assets/vis/graph/graphMixins/physics/HierarchialRepulsion.js +64 -0
  21. data/vendor/assets/vis/graph/graphMixins/physics/PhysicsMixin.js +513 -0
  22. data/vendor/assets/vis/graph/graphMixins/physics/Repulsion.js +66 -0
  23. data/vendor/assets/vis/graph/img/acceptDeleteIcon.png +0 -0
  24. data/vendor/assets/vis/graph/img/addNodeIcon.png +0 -0
  25. data/vendor/assets/vis/graph/img/backIcon.png +0 -0
  26. data/vendor/assets/vis/graph/img/connectIcon.png +0 -0
  27. data/vendor/assets/vis/graph/img/cross.png +0 -0
  28. data/vendor/assets/vis/graph/img/cross2.png +0 -0
  29. data/vendor/assets/vis/graph/img/deleteIcon.png +0 -0
  30. data/vendor/assets/vis/graph/img/downArrow.png +0 -0
  31. data/vendor/assets/vis/graph/img/editIcon.png +0 -0
  32. data/vendor/assets/vis/graph/img/leftArrow.png +0 -0
  33. data/vendor/assets/vis/graph/img/rightArrow.png +0 -0
  34. data/vendor/assets/vis/graph/img/upArrow.png +0 -0
  35. data/vendor/assets/vis/module/exports.js +0 -2
  36. data/vendor/assets/vis/module/header.js +2 -2
  37. data/vendor/assets/vis/module/imports.js +1 -2
  38. data/vendor/assets/vis/timeline/Controller.js +56 -45
  39. data/vendor/assets/vis/timeline/Range.js +68 -62
  40. data/vendor/assets/vis/timeline/Stack.js +11 -13
  41. data/vendor/assets/vis/timeline/TimeStep.js +43 -38
  42. data/vendor/assets/vis/timeline/Timeline.js +215 -93
  43. data/vendor/assets/vis/timeline/component/Component.js +19 -3
  44. data/vendor/assets/vis/timeline/component/CurrentTime.js +1 -1
  45. data/vendor/assets/vis/timeline/component/CustomTime.js +39 -120
  46. data/vendor/assets/vis/timeline/component/GroupSet.js +35 -1
  47. data/vendor/assets/vis/timeline/component/ItemSet.js +272 -9
  48. data/vendor/assets/vis/timeline/component/RootPanel.js +59 -47
  49. data/vendor/assets/vis/timeline/component/TimeAxis.js +10 -0
  50. data/vendor/assets/vis/timeline/component/css/item.css +53 -22
  51. data/vendor/assets/vis/timeline/component/item/Item.js +40 -5
  52. data/vendor/assets/vis/timeline/component/item/ItemBox.js +3 -1
  53. data/vendor/assets/vis/timeline/component/item/ItemPoint.js +3 -1
  54. data/vendor/assets/vis/timeline/component/item/ItemRange.js +67 -3
  55. data/vendor/assets/vis/timeline/component/item/ItemRangeOverflow.js +37 -9
  56. data/vendor/assets/vis/timeline/img/delete.png +0 -0
  57. data/vendor/assets/vis/util.js +169 -30
  58. metadata +39 -12
@@ -0,0 +1,66 @@
1
+ /**
2
+ * Created by Alex on 2/10/14.
3
+ */
4
+
5
+ var repulsionMixin = {
6
+
7
+
8
+ /**
9
+ * Calculate the forces the nodes apply on eachother based on a repulsion field.
10
+ * This field is linearly approximated.
11
+ *
12
+ * @private
13
+ */
14
+ _calculateNodeForces : function() {
15
+ var dx, dy, angle, distance, fx, fy, combinedClusterSize,
16
+ repulsingForce, node1, node2, i, j;
17
+
18
+ var nodes = this.calculationNodes;
19
+ var nodeIndices = this.calculationNodeIndices;
20
+
21
+ // approximation constants
22
+ var a_base = -2/3;
23
+ var b = 4/3;
24
+
25
+ // repulsing forces between nodes
26
+ var nodeDistance = this.constants.physics.repulsion.nodeDistance;
27
+ var minimumDistance = nodeDistance;
28
+
29
+ // we loop from i over all but the last entree in the array
30
+ // j loops from i+1 to the last. This way we do not double count any of the indices, nor i == j
31
+ for (i = 0; i < nodeIndices.length-1; i++) {
32
+ node1 = nodes[nodeIndices[i]];
33
+ for (j = i+1; j < nodeIndices.length; j++) {
34
+ node2 = nodes[nodeIndices[j]];
35
+ combinedClusterSize = node1.clusterSize + node2.clusterSize - 2;
36
+
37
+ dx = node2.x - node1.x;
38
+ dy = node2.y - node1.y;
39
+ distance = Math.sqrt(dx * dx + dy * dy);
40
+
41
+ minimumDistance = (combinedClusterSize == 0) ? nodeDistance : (nodeDistance * (1 + combinedClusterSize * this.constants.clustering.distanceAmplification));
42
+ var a = a_base / minimumDistance;
43
+ if (distance < 2*minimumDistance) {
44
+ if (distance < 0.5*minimumDistance) {
45
+ repulsingForce = 1.0;
46
+ }
47
+ else {
48
+ repulsingForce = a * distance + b; // linear approx of 1 / (1 + Math.exp((distance / minimumDistance - 1) * steepness))
49
+ }
50
+
51
+ // amplify the repulsion for clusters.
52
+ repulsingForce *= (combinedClusterSize == 0) ? 1 : 1 + combinedClusterSize * this.constants.clustering.forceAmplification;
53
+ repulsingForce = repulsingForce/distance;
54
+
55
+ fx = dx * repulsingForce;
56
+ fy = dy * repulsingForce;
57
+
58
+ node1.fx -= fx;
59
+ node1.fy -= fy;
60
+ node2.fx += fx;
61
+ node2.fy += fy;
62
+ }
63
+ }
64
+ }
65
+ }
66
+ }
@@ -3,7 +3,6 @@
3
3
  */
4
4
  var vis = {
5
5
  util: util,
6
- events: events,
7
6
 
8
7
  Controller: Controller,
9
8
  DataSet: DataSet,
@@ -11,7 +10,6 @@ var vis = {
11
10
  Range: Range,
12
11
  Stack: Stack,
13
12
  TimeStep: TimeStep,
14
- EventBus: EventBus,
15
13
 
16
14
  components: {
17
15
  items: {
@@ -4,8 +4,8 @@
4
4
  *
5
5
  * A dynamic, browser-based visualization library.
6
6
  *
7
- * @version @@version
8
- * @date @@date
7
+ * @version 0.6.1
8
+ * @date 2014-03-06e
9
9
  *
10
10
  * @license
11
11
  * Copyright (C) 2011-2014 Almende B.V, http://almende.com
@@ -6,6 +6,7 @@
6
6
  // If not available there, load via require.
7
7
 
8
8
  var moment = (typeof window !== 'undefined') && window['moment'] || require('moment');
9
+ var Emitter = require('emitter-component');
9
10
 
10
11
  var Hammer;
11
12
  if (typeof window !== 'undefined') {
@@ -28,5 +29,3 @@ else {
28
29
  throw Error('mouseTrap is only available in a browser, not in node.js.');
29
30
  }
30
31
  }
31
-
32
-
@@ -1,16 +1,59 @@
1
1
  /**
2
2
  * @constructor Controller
3
3
  *
4
- * A Controller controls the reflows and repaints of all visual components
4
+ * A Controller controls the reflows and repaints of all components,
5
+ * and is used as an event bus for all components.
5
6
  */
6
7
  function Controller () {
8
+ var me = this;
9
+
7
10
  this.id = util.randomUUID();
8
11
  this.components = {};
9
12
 
10
- this.repaintTimer = undefined;
11
- this.reflowTimer = undefined;
13
+ /**
14
+ * Listen for a 'request-reflow' event. The controller will schedule a reflow
15
+ * @param {Boolean} [force] If true, an immediate reflow is forced. Default
16
+ * is false.
17
+ */
18
+ var reflowTimer = null;
19
+ this.on('request-reflow', function requestReflow(force) {
20
+ if (force) {
21
+ me.reflow();
22
+ }
23
+ else {
24
+ if (!reflowTimer) {
25
+ reflowTimer = setTimeout(function () {
26
+ reflowTimer = null;
27
+ me.reflow();
28
+ }, 0);
29
+ }
30
+ }
31
+ });
32
+
33
+ /**
34
+ * Request a repaint. The controller will schedule a repaint
35
+ * @param {Boolean} [force] If true, an immediate repaint is forced. Default
36
+ * is false.
37
+ */
38
+ var repaintTimer = null;
39
+ this.on('request-repaint', function requestRepaint(force) {
40
+ if (force) {
41
+ me.repaint();
42
+ }
43
+ else {
44
+ if (!repaintTimer) {
45
+ repaintTimer = setTimeout(function () {
46
+ repaintTimer = null;
47
+ me.repaint();
48
+ }, 0);
49
+ }
50
+ }
51
+ });
12
52
  }
13
53
 
54
+ // Extend controller with Emitter mixin
55
+ Emitter(Controller.prototype);
56
+
14
57
  /**
15
58
  * Add a component to the controller
16
59
  * @param {Component} component
@@ -26,7 +69,7 @@ Controller.prototype.add = function add(component) {
26
69
  }
27
70
 
28
71
  // add the component
29
- component.controller = this;
72
+ component.setController(this);
30
73
  this.components[component.id] = component;
31
74
  };
32
75
 
@@ -38,54 +81,18 @@ Controller.prototype.remove = function remove(component) {
38
81
  var id;
39
82
  for (id in this.components) {
40
83
  if (this.components.hasOwnProperty(id)) {
41
- if (id == component || this.components[id] == component) {
84
+ if (id == component || this.components[id] === component) {
42
85
  break;
43
86
  }
44
87
  }
45
88
  }
46
89
 
47
90
  if (id) {
48
- delete this.components[id];
49
- }
50
- };
91
+ // unregister the controller (gives the component the ability to unregister
92
+ // event listeners and clean up other stuff)
93
+ this.components[id].setController(null);
51
94
 
52
- /**
53
- * Request a reflow. The controller will schedule a reflow
54
- * @param {Boolean} [force] If true, an immediate reflow is forced. Default
55
- * is false.
56
- */
57
- Controller.prototype.requestReflow = function requestReflow(force) {
58
- if (force) {
59
- this.reflow();
60
- }
61
- else {
62
- if (!this.reflowTimer) {
63
- var me = this;
64
- this.reflowTimer = setTimeout(function () {
65
- me.reflowTimer = undefined;
66
- me.reflow();
67
- }, 0);
68
- }
69
- }
70
- };
71
-
72
- /**
73
- * Request a repaint. The controller will schedule a repaint
74
- * @param {Boolean} [force] If true, an immediate repaint is forced. Default
75
- * is false.
76
- */
77
- Controller.prototype.requestRepaint = function requestRepaint(force) {
78
- if (force) {
79
- this.repaint();
80
- }
81
- else {
82
- if (!this.repaintTimer) {
83
- var me = this;
84
- this.repaintTimer = setTimeout(function () {
85
- me.repaintTimer = undefined;
86
- me.repaint();
87
- }, 0);
88
- }
95
+ delete this.components[id];
89
96
  }
90
97
  };
91
98
 
@@ -123,6 +130,8 @@ Controller.prototype.repaint = function repaint() {
123
130
 
124
131
  util.forEach(this.components, repaint);
125
132
 
133
+ this.emit('repaint');
134
+
126
135
  // immediately reflow when needed
127
136
  if (changed) {
128
137
  this.reflow();
@@ -164,6 +173,8 @@ Controller.prototype.reflow = function reflow() {
164
173
 
165
174
  util.forEach(this.components, reflow);
166
175
 
176
+ this.emit('reflow');
177
+
167
178
  // immediately repaint when needed
168
179
  if (resized) {
169
180
  this.repaint();
@@ -16,6 +16,9 @@ function Range(options) {
16
16
  this.setOptions(options);
17
17
  }
18
18
 
19
+ // extend the Range prototype with an event emitter mixin
20
+ Emitter(Range.prototype);
21
+
19
22
  /**
20
23
  * Set options for the range controller
21
24
  * @param {Object} options Available options:
@@ -48,42 +51,48 @@ function validateDirection (direction) {
48
51
 
49
52
  /**
50
53
  * Add listeners for mouse and touch events to the component
51
- * @param {Component} component
54
+ * @param {Controller} controller
55
+ * @param {Component} component Should be a rootpanel
52
56
  * @param {String} event Available events: 'move', 'zoom'
53
57
  * @param {String} direction Available directions: 'horizontal', 'vertical'
54
58
  */
55
- Range.prototype.subscribe = function (component, event, direction) {
59
+ Range.prototype.subscribe = function (controller, component, event, direction) {
56
60
  var me = this;
57
61
 
58
62
  if (event == 'move') {
59
63
  // drag start listener
60
- component.on('dragstart', function (event) {
64
+ controller.on('dragstart', function (event) {
61
65
  me._onDragStart(event, component);
62
66
  });
63
67
 
64
68
  // drag listener
65
- component.on('drag', function (event) {
69
+ controller.on('drag', function (event) {
66
70
  me._onDrag(event, component, direction);
67
71
  });
68
72
 
69
73
  // drag end listener
70
- component.on('dragend', function (event) {
74
+ controller.on('dragend', function (event) {
71
75
  me._onDragEnd(event, component);
72
76
  });
77
+
78
+ // ignore dragging when holding
79
+ controller.on('hold', function (event) {
80
+ me._onHold();
81
+ });
73
82
  }
74
83
  else if (event == 'zoom') {
75
84
  // mouse wheel
76
85
  function mousewheel (event) {
77
86
  me._onMouseWheel(event, component, direction);
78
87
  }
79
- component.on('mousewheel', mousewheel);
80
- component.on('DOMMouseScroll', mousewheel); // For FF
88
+ controller.on('mousewheel', mousewheel);
89
+ controller.on('DOMMouseScroll', mousewheel); // For FF
81
90
 
82
91
  // pinch
83
- component.on('touch', function (event) {
84
- me._onTouch();
92
+ controller.on('touch', function (event) {
93
+ me._onTouch(event);
85
94
  });
86
- component.on('pinch', function (event) {
95
+ controller.on('pinch', function (event) {
87
96
  me._onPinch(event, component, direction);
88
97
  });
89
98
  }
@@ -93,44 +102,6 @@ Range.prototype.subscribe = function (component, event, direction) {
93
102
  }
94
103
  };
95
104
 
96
- /**
97
- * Add event listener
98
- * @param {String} event Name of the event.
99
- * Available events: 'rangechange', 'rangechanged'
100
- * @param {function} callback Callback function, invoked as callback({start: Date, end: Date})
101
- */
102
- Range.prototype.on = function on (event, callback) {
103
- var available = ['rangechange', 'rangechanged'];
104
-
105
- if (available.indexOf(event) == -1) {
106
- throw new Error('Unknown event "' + event + '". Choose from ' + available.join());
107
- }
108
-
109
- events.addListener(this, event, callback);
110
- };
111
-
112
- /**
113
- * Remove an event listener
114
- * @param {String} event name of the event
115
- * @param {function} callback callback handler
116
- */
117
- Range.prototype.off = function off (event, callback) {
118
- events.removeListener(this, event, callback);
119
- };
120
-
121
- /**
122
- * Trigger an event
123
- * @param {String} event name of the event, available events: 'rangechange',
124
- * 'rangechanged'
125
- * @private
126
- */
127
- Range.prototype._trigger = function (event) {
128
- events.trigger(this, event, {
129
- start: this.start,
130
- end: this.end
131
- });
132
- };
133
-
134
105
  /**
135
106
  * Set a new start and end range
136
107
  * @param {Number} [start]
@@ -139,8 +110,12 @@ Range.prototype._trigger = function (event) {
139
110
  Range.prototype.setRange = function(start, end) {
140
111
  var changed = this._applyRange(start, end);
141
112
  if (changed) {
142
- this._trigger('rangechange');
143
- this._trigger('rangechanged');
113
+ var params = {
114
+ start: this.start,
115
+ end: this.end
116
+ };
117
+ this.emit('rangechange', params);
118
+ this.emit('rangechanged', params);
144
119
  }
145
120
  };
146
121
 
@@ -311,7 +286,9 @@ var touchParams = {};
311
286
  Range.prototype._onDragStart = function(event, component) {
312
287
  // refuse to drag when we where pinching to prevent the timeline make a jump
313
288
  // when releasing the fingers in opposite order from the touch screen
314
- if (touchParams.pinching) return;
289
+ if (touchParams.ignore) return;
290
+
291
+ // TODO: reckon with option movable
315
292
 
316
293
  touchParams.start = this.start;
317
294
  touchParams.end = this.end;
@@ -332,9 +309,12 @@ Range.prototype._onDragStart = function(event, component) {
332
309
  Range.prototype._onDrag = function (event, component, direction) {
333
310
  validateDirection(direction);
334
311
 
312
+ // TODO: reckon with option movable
313
+
314
+
335
315
  // refuse to drag when we where pinching to prevent the timeline make a jump
336
316
  // when releasing the fingers in opposite order from the touch screen
337
- if (touchParams.pinching) return;
317
+ if (touchParams.ignore) return;
338
318
 
339
319
  var delta = (direction == 'horizontal') ? event.gesture.deltaX : event.gesture.deltaY,
340
320
  interval = (touchParams.end - touchParams.start),
@@ -343,8 +323,10 @@ Range.prototype._onDrag = function (event, component, direction) {
343
323
 
344
324
  this._applyRange(touchParams.start + diffRange, touchParams.end + diffRange);
345
325
 
346
- // fire a rangechange event
347
- this._trigger('rangechange');
326
+ this.emit('rangechange', {
327
+ start: this.start,
328
+ end: this.end
329
+ });
348
330
  };
349
331
 
350
332
  /**
@@ -356,14 +338,19 @@ Range.prototype._onDrag = function (event, component, direction) {
356
338
  Range.prototype._onDragEnd = function (event, component) {
357
339
  // refuse to drag when we where pinching to prevent the timeline make a jump
358
340
  // when releasing the fingers in opposite order from the touch screen
359
- if (touchParams.pinching) return;
341
+ if (touchParams.ignore) return;
342
+
343
+ // TODO: reckon with option movable
360
344
 
361
345
  if (component.frame) {
362
346
  component.frame.style.cursor = 'auto';
363
347
  }
364
348
 
365
349
  // fire a rangechanged event
366
- this._trigger('rangechanged');
350
+ this.emit('rangechanged', {
351
+ start: this.start,
352
+ end: this.end
353
+ });
367
354
  };
368
355
 
369
356
  /**
@@ -377,6 +364,8 @@ Range.prototype._onDragEnd = function (event, component) {
377
364
  Range.prototype._onMouseWheel = function(event, component, direction) {
378
365
  validateDirection(direction);
379
366
 
367
+ // TODO: reckon with option zoomable
368
+
380
369
  // retrieve delta
381
370
  var delta = 0;
382
371
  if (event.wheelDelta) { /* IE/Opera. */
@@ -405,7 +394,7 @@ Range.prototype._onMouseWheel = function(event, component, direction) {
405
394
 
406
395
  // calculate center, the date to zoom around
407
396
  var gesture = util.fakeGesture(this, event),
408
- pointer = getPointer(gesture.touches[0], component.frame),
397
+ pointer = getPointer(gesture.center, component.frame),
409
398
  pointerDate = this._pointerToDate(component, direction, pointer);
410
399
 
411
400
  this.zoom(scale, pointerDate);
@@ -413,18 +402,33 @@ Range.prototype._onMouseWheel = function(event, component, direction) {
413
402
 
414
403
  // Prevent default actions caused by mouse wheel
415
404
  // (else the page and timeline both zoom and scroll)
416
- util.preventDefault(event);
405
+ event.preventDefault();
417
406
  };
418
407
 
419
408
  /**
420
- * On start of a touch gesture, initialize scale to 1
409
+ * Start of a touch gesture
421
410
  * @private
422
411
  */
423
- Range.prototype._onTouch = function () {
412
+ Range.prototype._onTouch = function (event) {
424
413
  touchParams.start = this.start;
425
414
  touchParams.end = this.end;
426
- touchParams.pinching = false;
415
+ touchParams.ignore = false;
427
416
  touchParams.center = null;
417
+
418
+ // don't move the range when dragging a selected event
419
+ // TODO: it's not so neat to have to know about the state of the ItemSet
420
+ var item = ItemSet.itemFromTarget(event);
421
+ if (item && item.selected && this.options.editable) {
422
+ touchParams.ignore = true;
423
+ }
424
+ };
425
+
426
+ /**
427
+ * On start of a hold gesture
428
+ * @private
429
+ */
430
+ Range.prototype._onHold = function () {
431
+ touchParams.ignore = true;
428
432
  };
429
433
 
430
434
  /**
@@ -435,7 +439,9 @@ Range.prototype._onTouch = function () {
435
439
  * @private
436
440
  */
437
441
  Range.prototype._onPinch = function (event, component, direction) {
438
- touchParams.pinching = true;
442
+ touchParams.ignore = true;
443
+
444
+ // TODO: reckon with option zoomable
439
445
 
440
446
  if (event.gesture.touches.length > 1) {
441
447
  if (!touchParams.center) {