vis-rails 0.0.4 → 0.0.5

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