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
@@ -20,7 +20,6 @@ function Component () {
20
20
  * set.
21
21
  * @param {Object} options Available parameters:
22
22
  * {String | function} [className]
23
- * {EventBus} [eventBus]
24
23
  * {String | Number | function} [left]
25
24
  * {String | Number | function} [top]
26
25
  * {String | Number | function} [width]
@@ -55,6 +54,23 @@ Component.prototype.getOption = function getOption(name) {
55
54
  return value;
56
55
  };
57
56
 
57
+ /**
58
+ * Set controller for this component, or remove current controller by passing
59
+ * null as parameter value.
60
+ * @param {Controller | null} controller
61
+ */
62
+ Component.prototype.setController = function setController (controller) {
63
+ this.controller = controller || null;
64
+ };
65
+
66
+ /**
67
+ * Get controller of this component
68
+ * @return {Controller} controller
69
+ */
70
+ Component.prototype.getController = function getController () {
71
+ return this.controller;
72
+ };
73
+
58
74
  /**
59
75
  * Get the container element of the component, which can be used by a child to
60
76
  * add its own widgets. Not all components do have a container for childs, in
@@ -126,7 +142,7 @@ Component.prototype.show = function show() {
126
142
  */
127
143
  Component.prototype.requestRepaint = function requestRepaint() {
128
144
  if (this.controller) {
129
- this.controller.requestRepaint();
145
+ this.controller.emit('request-repaint');
130
146
  }
131
147
  else {
132
148
  throw new Error('Cannot request a repaint: no controller configured');
@@ -139,7 +155,7 @@ Component.prototype.requestRepaint = function requestRepaint() {
139
155
  */
140
156
  Component.prototype.requestReflow = function requestReflow() {
141
157
  if (this.controller) {
142
- this.controller.requestReflow();
158
+ this.controller.emit('request-reflow');
143
159
  }
144
160
  else {
145
161
  throw new Error('Cannot request a reflow: no controller configured');
@@ -56,7 +56,7 @@ CurrentTime.prototype.repaint = function () {
56
56
  delete this.frame;
57
57
  }
58
58
 
59
- return;
59
+ return false;
60
60
  }
61
61
 
62
62
  if (!bar) {
@@ -19,12 +19,14 @@ function CustomTime (parent, depends, options) {
19
19
  showCustomTime: false
20
20
  };
21
21
 
22
- this.listeners = [];
23
22
  this.customTime = new Date();
23
+ this.eventParams = {}; // stores state parameters while dragging the bar
24
24
  }
25
25
 
26
26
  CustomTime.prototype = new Component();
27
27
 
28
+ Emitter(CustomTime.prototype);
29
+
28
30
  CustomTime.prototype.setOptions = Component.prototype.setOptions;
29
31
 
30
32
  /**
@@ -42,13 +44,13 @@ CustomTime.prototype.getContainer = function () {
42
44
  */
43
45
  CustomTime.prototype.repaint = function () {
44
46
  var bar = this.frame,
45
- parent = this.parent,
46
- parentContainer = parent.parent.getContainer();
47
+ parent = this.parent;
47
48
 
48
49
  if (!parent) {
49
50
  throw new Error('Cannot repaint bar: no parent attached');
50
51
  }
51
52
 
53
+ var parentContainer = parent.parent.getContainer();
52
54
  if (!parentContainer) {
53
55
  throw new Error('Cannot repaint bar: parent has no container element');
54
56
  }
@@ -59,7 +61,7 @@ CustomTime.prototype.repaint = function () {
59
61
  delete this.frame;
60
62
  }
61
63
 
62
- return;
64
+ return false;
63
65
  }
64
66
 
65
67
  if (!bar) {
@@ -81,7 +83,13 @@ CustomTime.prototype.repaint = function () {
81
83
 
82
84
  this.frame = bar;
83
85
 
84
- this.subscribe(this, 'movetime');
86
+ // attach event listeners
87
+ this.hammer = Hammer(bar, {
88
+ prevent_default: true
89
+ });
90
+ this.hammer.on('dragstart', this._onDragStart.bind(this));
91
+ this.hammer.on('drag', this._onDrag.bind(this));
92
+ this.hammer.on('dragend', this._onDragEnd.bind(this));
85
93
  }
86
94
 
87
95
  if (!parent.conversion) {
@@ -100,7 +108,7 @@ CustomTime.prototype.repaint = function () {
100
108
  * Set custom time.
101
109
  * @param {Date} time
102
110
  */
103
- CustomTime.prototype._setCustomTime = function(time) {
111
+ CustomTime.prototype.setCustomTime = function(time) {
104
112
  this.customTime = new Date(time.valueOf());
105
113
  this.repaint();
106
114
  };
@@ -109,147 +117,58 @@ CustomTime.prototype._setCustomTime = function(time) {
109
117
  * Retrieve the current custom time.
110
118
  * @return {Date} customTime
111
119
  */
112
- CustomTime.prototype._getCustomTime = function() {
120
+ CustomTime.prototype.getCustomTime = function() {
113
121
  return new Date(this.customTime.valueOf());
114
122
  };
115
123
 
116
- /**
117
- * Add listeners for mouse and touch events to the component
118
- * @param {Component} component
119
- */
120
- CustomTime.prototype.subscribe = function (component, event) {
121
- var me = this;
122
- var listener = {
123
- component: component,
124
- event: event,
125
- callback: function (event) {
126
- me._onMouseDown(event, listener);
127
- },
128
- params: {}
129
- };
130
-
131
- component.on('mousedown', listener.callback);
132
- me.listeners.push(listener);
133
-
134
- };
135
-
136
- /**
137
- * Event handler
138
- * @param {String} event name of the event, for example 'click', 'mousemove'
139
- * @param {function} callback callback handler, invoked with the raw HTML Event
140
- * as parameter.
141
- */
142
- CustomTime.prototype.on = function (event, callback) {
143
- var bar = this.frame;
144
- if (!bar) {
145
- throw new Error('Cannot add event listener: no parent attached');
146
- }
147
-
148
- events.addListener(this, event, callback);
149
- util.addEventListener(bar, event, callback);
150
- };
151
-
152
124
  /**
153
125
  * Start moving horizontally
154
126
  * @param {Event} event
155
- * @param {Object} listener Listener containing the component and params
156
127
  * @private
157
128
  */
158
- CustomTime.prototype._onMouseDown = function(event, listener) {
159
- event = event || window.event;
160
- var params = listener.params;
161
-
162
- // only react on left mouse button down
163
- var leftButtonDown = event.which ? (event.which == 1) : (event.button == 1);
164
- if (!leftButtonDown) {
165
- return;
166
- }
167
-
168
- // get mouse position
169
- params.mouseX = util.getPageX(event);
170
- params.moved = false;
171
-
172
- params.customTime = this.customTime;
173
-
174
- // add event listeners to handle moving the custom time bar
175
- var me = this;
176
- if (!params.onMouseMove) {
177
- params.onMouseMove = function (event) {
178
- me._onMouseMove(event, listener);
179
- };
180
- util.addEventListener(document, 'mousemove', params.onMouseMove);
181
- }
182
- if (!params.onMouseUp) {
183
- params.onMouseUp = function (event) {
184
- me._onMouseUp(event, listener);
185
- };
186
- util.addEventListener(document, 'mouseup', params.onMouseUp);
187
- }
129
+ CustomTime.prototype._onDragStart = function(event) {
130
+ this.eventParams.customTime = this.customTime;
188
131
 
189
- util.stopPropagation(event);
190
- util.preventDefault(event);
132
+ event.stopPropagation();
133
+ event.preventDefault();
191
134
  };
192
135
 
193
136
  /**
194
137
  * Perform moving operating.
195
- * This function activated from within the funcion CustomTime._onMouseDown().
196
138
  * @param {Event} event
197
- * @param {Object} listener
198
139
  * @private
199
140
  */
200
- CustomTime.prototype._onMouseMove = function (event, listener) {
201
- event = event || window.event;
202
- var params = listener.params;
203
- var parent = this.parent;
141
+ CustomTime.prototype._onDrag = function (event) {
142
+ var deltaX = event.gesture.deltaX,
143
+ x = this.parent.toScreen(this.eventParams.customTime) + deltaX,
144
+ time = this.parent.toTime(x);
204
145
 
205
- // calculate change in mouse position
206
- var mouseX = util.getPageX(event);
207
-
208
- if (params.mouseX === undefined) {
209
- params.mouseX = mouseX;
210
- }
211
-
212
- var diff = mouseX - params.mouseX;
213
-
214
- // if mouse movement is big enough, register it as a "moved" event
215
- if (Math.abs(diff) >= 1) {
216
- params.moved = true;
217
- }
218
-
219
- var x = parent.toScreen(params.customTime);
220
- var xnew = x + diff;
221
- var time = parent.toTime(xnew);
222
- this._setCustomTime(time);
146
+ this.setCustomTime(time);
223
147
 
224
148
  // fire a timechange event
225
- events.trigger(this, 'timechange', {customTime: this.customTime});
149
+ if (this.controller) {
150
+ this.controller.emit('timechange', {
151
+ time: this.customTime
152
+ })
153
+ }
226
154
 
227
- util.preventDefault(event);
155
+ event.stopPropagation();
156
+ event.preventDefault();
228
157
  };
229
158
 
230
159
  /**
231
160
  * Stop moving operating.
232
- * This function activated from within the function CustomTime._onMouseDown().
233
161
  * @param {event} event
234
- * @param {Object} listener
235
162
  * @private
236
163
  */
237
- CustomTime.prototype._onMouseUp = function (event, listener) {
238
- event = event || window.event;
239
- var params = listener.params;
240
-
241
- // remove event listeners here, important for Safari
242
- if (params.onMouseMove) {
243
- util.removeEventListener(document, 'mousemove', params.onMouseMove);
244
- params.onMouseMove = null;
245
- }
246
- if (params.onMouseUp) {
247
- util.removeEventListener(document, 'mouseup', params.onMouseUp);
248
- params.onMouseUp = null;
164
+ CustomTime.prototype._onDragEnd = function (event) {
165
+ // fire a timechanged event
166
+ if (this.controller) {
167
+ this.controller.emit('timechanged', {
168
+ time: this.customTime
169
+ })
249
170
  }
250
171
 
251
- if (params.moved) {
252
- // fire a timechanged event
253
- events.trigger(this, 'timechanged', {customTime: this.customTime});
254
- }
172
+ event.stopPropagation();
173
+ event.preventDefault();
255
174
  };
@@ -132,7 +132,7 @@ GroupSet.prototype.setGroups = function setGroups(groups) {
132
132
  // subscribe to new dataset
133
133
  var id = this.id;
134
134
  util.forEach(this.listeners, function (callback, event) {
135
- me.groupsData.subscribe(event, callback, id);
135
+ me.groupsData.on(event, callback, id);
136
136
  });
137
137
 
138
138
  // draw all new groups
@@ -216,6 +216,7 @@ GroupSet.prototype.repaint = function repaint() {
216
216
  if (!frame) {
217
217
  frame = document.createElement('div');
218
218
  frame.className = 'groupset';
219
+ frame['timeline-groupset'] = this;
219
220
  this.dom.frame = frame;
220
221
 
221
222
  var className = options.className;
@@ -544,3 +545,36 @@ GroupSet.prototype._toQueue = function _toQueue(ids, action) {
544
545
  this.requestRepaint();
545
546
  }
546
547
  };
548
+
549
+ /**
550
+ * Find the Group from an event target:
551
+ * searches for the attribute 'timeline-groupset' in the event target's element
552
+ * tree, then finds the right group in this groupset
553
+ * @param {Event} event
554
+ * @return {Group | null} group
555
+ */
556
+ GroupSet.groupFromTarget = function groupFromTarget (event) {
557
+ var groupset,
558
+ target = event.target;
559
+
560
+ while (target) {
561
+ if (target.hasOwnProperty('timeline-groupset')) {
562
+ groupset = target['timeline-groupset'];
563
+ break;
564
+ }
565
+ target = target.parentNode;
566
+ }
567
+
568
+ if (groupset) {
569
+ for (var groupId in groupset.groups) {
570
+ if (groupset.groups.hasOwnProperty(groupId)) {
571
+ var group = groupset.groups[groupId];
572
+ if (group.itemset && ItemSet.itemSetFromTarget(event) == group.itemset) {
573
+ return group;
574
+ }
575
+ }
576
+ }
577
+ }
578
+
579
+ return null;
580
+ };
@@ -16,6 +16,13 @@ function ItemSet(parent, depends, options) {
16
16
  this.parent = parent;
17
17
  this.depends = depends;
18
18
 
19
+ // event listeners
20
+ this.eventListeners = {
21
+ dragstart: this._onDragStart.bind(this),
22
+ drag: this._onDrag.bind(this),
23
+ dragend: this._onDragEnd.bind(this)
24
+ };
25
+
19
26
  // one options object is shared by this itemset and all its items
20
27
  this.options = options || {};
21
28
  this.defaultOptions = {
@@ -35,6 +42,7 @@ function ItemSet(parent, depends, options) {
35
42
  this.itemsData = null; // DataSet
36
43
  this.range = null; // Range or Object {start: number, end: number}
37
44
 
45
+ // data change listeners
38
46
  this.listeners = {
39
47
  'add': function (event, params, senderId) {
40
48
  if (senderId != me.id) {
@@ -59,6 +67,8 @@ function ItemSet(parent, depends, options) {
59
67
  this.stack = new Stack(this, Object.create(this.options));
60
68
  this.conversion = null;
61
69
 
70
+ this.touchParams = {}; // stores properties while dragging
71
+
62
72
  // TODO: ItemSet should also attach event listeners for rangechange and rangechanged, like timeaxis
63
73
  }
64
74
 
@@ -96,9 +106,61 @@ ItemSet.types = {
96
106
  * {Number} padding
97
107
  * Padding of the contents of an item in pixels.
98
108
  * Must correspond with the items css. Default is 5.
109
+ * {Function} snap
110
+ * Function to let items snap to nice dates when
111
+ * dragging items.
99
112
  */
100
113
  ItemSet.prototype.setOptions = Component.prototype.setOptions;
101
114
 
115
+
116
+
117
+ /**
118
+ * Set controller for this component
119
+ * @param {Controller | null} controller
120
+ */
121
+ ItemSet.prototype.setController = function setController (controller) {
122
+ var event;
123
+
124
+ // unregister old event listeners
125
+ if (this.controller) {
126
+ for (event in this.eventListeners) {
127
+ if (this.eventListeners.hasOwnProperty(event)) {
128
+ this.controller.off(event, this.eventListeners[event]);
129
+ }
130
+ }
131
+ }
132
+
133
+ this.controller = controller || null;
134
+
135
+ // register new event listeners
136
+ if (this.controller) {
137
+ for (event in this.eventListeners) {
138
+ if (this.eventListeners.hasOwnProperty(event)) {
139
+ this.controller.on(event, this.eventListeners[event]);
140
+ }
141
+ }
142
+ }
143
+ };
144
+
145
+ // attach event listeners for dragging items to the controller
146
+ (function (me) {
147
+ var _controller = null;
148
+ var _onDragStart = null;
149
+ var _onDrag = null;
150
+ var _onDragEnd = null;
151
+
152
+ Object.defineProperty(me, 'controller', {
153
+ get: function () {
154
+ return _controller;
155
+ },
156
+
157
+ set: function (controller) {
158
+
159
+ }
160
+ });
161
+ }) (this);
162
+
163
+
102
164
  /**
103
165
  * Set range (start and end).
104
166
  * @param {Range | Object} range A Range or an object containing start and end.
@@ -144,12 +206,6 @@ ItemSet.prototype.setSelection = function setSelection(ids) {
144
206
  }
145
207
  }
146
208
 
147
- // trigger a select event
148
- selection = this.selection.concat([]);
149
- events.trigger(this, 'select', {
150
- ids: selection
151
- });
152
-
153
209
  if (this.controller) {
154
210
  this.requestRepaint();
155
211
  }
@@ -195,6 +251,7 @@ ItemSet.prototype.repaint = function repaint() {
195
251
  if (!frame) {
196
252
  frame = document.createElement('div');
197
253
  frame.className = 'itemset';
254
+ frame['timeline-itemset'] = this;
198
255
 
199
256
  var className = options.className;
200
257
  if (className) {
@@ -389,8 +446,8 @@ ItemSet.prototype.getAxis = function getAxis() {
389
446
  ItemSet.prototype.reflow = function reflow () {
390
447
  var changed = 0,
391
448
  options = this.options,
392
- marginAxis = options.margin && options.margin.axis || this.defaultOptions.margin.axis,
393
- marginItem = options.margin && options.margin.item || this.defaultOptions.margin.item,
449
+ marginAxis = (options.margin && 'axis' in options.margin) ? options.margin.axis : this.defaultOptions.margin.axis,
450
+ marginItem = (options.margin && 'item' in options.margin) ? options.margin.item : this.defaultOptions.margin.item,
394
451
  update = util.updateProperty,
395
452
  asNumber = util.option.asNumber,
396
453
  asSize = util.option.asSize,
@@ -501,7 +558,7 @@ ItemSet.prototype.setItems = function setItems(items) {
501
558
  // subscribe to new dataset
502
559
  var id = this.id;
503
560
  util.forEach(this.listeners, function (callback, event) {
504
- me.itemsData.subscribe(event, callback, id);
561
+ me.itemsData.on(event, callback, id);
505
562
  });
506
563
 
507
564
  // draw all new items
@@ -518,6 +575,24 @@ ItemSet.prototype.getItems = function getItems() {
518
575
  return this.itemsData;
519
576
  };
520
577
 
578
+ /**
579
+ * Remove an item by its id
580
+ * @param {String | Number} id
581
+ */
582
+ ItemSet.prototype.removeItem = function removeItem (id) {
583
+ var item = this.itemsData.get(id),
584
+ dataset = this._myDataSet();
585
+
586
+ if (item) {
587
+ // confirm deletion
588
+ this.options.onRemove(item, function (item) {
589
+ if (item) {
590
+ dataset.remove(item);
591
+ }
592
+ });
593
+ }
594
+ };
595
+
521
596
  /**
522
597
  * Handle updated items
523
598
  * @param {Number[]} ids
@@ -610,3 +685,191 @@ ItemSet.prototype.toScreen = function toScreen(time) {
610
685
  var conversion = this.conversion;
611
686
  return (time.valueOf() - conversion.offset) * conversion.scale;
612
687
  };
688
+
689
+ /**
690
+ * Start dragging the selected events
691
+ * @param {Event} event
692
+ * @private
693
+ */
694
+ ItemSet.prototype._onDragStart = function (event) {
695
+ if (!this.options.editable) {
696
+ return;
697
+ }
698
+
699
+ var item = ItemSet.itemFromTarget(event),
700
+ me = this;
701
+
702
+ if (item && item.selected) {
703
+ var dragLeftItem = event.target.dragLeftItem;
704
+ var dragRightItem = event.target.dragRightItem;
705
+
706
+ if (dragLeftItem) {
707
+ this.touchParams.itemProps = [{
708
+ item: dragLeftItem,
709
+ start: item.data.start.valueOf()
710
+ }];
711
+ }
712
+ else if (dragRightItem) {
713
+ this.touchParams.itemProps = [{
714
+ item: dragRightItem,
715
+ end: item.data.end.valueOf()
716
+ }];
717
+ }
718
+ else {
719
+ this.touchParams.itemProps = this.getSelection().map(function (id) {
720
+ var item = me.items[id];
721
+ var props = {
722
+ item: item
723
+ };
724
+
725
+ if ('start' in item.data) {
726
+ props.start = item.data.start.valueOf()
727
+ }
728
+ if ('end' in item.data) {
729
+ props.end = item.data.end.valueOf()
730
+ }
731
+
732
+ return props;
733
+ });
734
+ }
735
+
736
+ event.stopPropagation();
737
+ }
738
+ };
739
+
740
+ /**
741
+ * Drag selected items
742
+ * @param {Event} event
743
+ * @private
744
+ */
745
+ ItemSet.prototype._onDrag = function (event) {
746
+ if (this.touchParams.itemProps) {
747
+ var snap = this.options.snap || null,
748
+ deltaX = event.gesture.deltaX,
749
+ offset = deltaX / this.conversion.scale;
750
+
751
+ // move
752
+ this.touchParams.itemProps.forEach(function (props) {
753
+ if ('start' in props) {
754
+ var start = new Date(props.start + offset);
755
+ props.item.data.start = snap ? snap(start) : start;
756
+ }
757
+ if ('end' in props) {
758
+ var end = new Date(props.end + offset);
759
+ props.item.data.end = snap ? snap(end) : end;
760
+ }
761
+ });
762
+
763
+ // TODO: implement onMoving handler
764
+
765
+ // TODO: implement dragging from one group to another
766
+
767
+ this.requestReflow();
768
+
769
+ event.stopPropagation();
770
+ }
771
+ };
772
+
773
+ /**
774
+ * End of dragging selected items
775
+ * @param {Event} event
776
+ * @private
777
+ */
778
+ ItemSet.prototype._onDragEnd = function (event) {
779
+ if (this.touchParams.itemProps) {
780
+ // prepare a change set for the changed items
781
+ var changes = [],
782
+ me = this,
783
+ dataset = this._myDataSet(),
784
+ type;
785
+
786
+ this.touchParams.itemProps.forEach(function (props) {
787
+ var id = props.item.id,
788
+ item = me.itemsData.get(id);
789
+
790
+ var changed = false;
791
+ if ('start' in props.item.data) {
792
+ changed = (props.start != props.item.data.start.valueOf());
793
+ item.start = util.convert(props.item.data.start, dataset.convert['start']);
794
+ }
795
+ if ('end' in props.item.data) {
796
+ changed = changed || (props.end != props.item.data.end.valueOf());
797
+ item.end = util.convert(props.item.data.end, dataset.convert['end']);
798
+ }
799
+
800
+ // only apply changes when start or end is actually changed
801
+ if (changed) {
802
+ me.options.onMove(item, function (item) {
803
+ if (item) {
804
+ // apply changes
805
+ changes.push(item);
806
+ }
807
+ else {
808
+ // restore original values
809
+ if ('start' in props) props.item.data.start = props.start;
810
+ if ('end' in props) props.item.data.end = props.end;
811
+ me.requestReflow();
812
+ }
813
+ });
814
+ }
815
+ });
816
+ this.touchParams.itemProps = null;
817
+
818
+ // apply the changes to the data (if there are changes)
819
+ if (changes.length) {
820
+ dataset.update(changes);
821
+ }
822
+
823
+ event.stopPropagation();
824
+ }
825
+ };
826
+
827
+ /**
828
+ * Find an item from an event target:
829
+ * searches for the attribute 'timeline-item' in the event target's element tree
830
+ * @param {Event} event
831
+ * @return {Item | null} item
832
+ */
833
+ ItemSet.itemFromTarget = function itemFromTarget (event) {
834
+ var target = event.target;
835
+ while (target) {
836
+ if (target.hasOwnProperty('timeline-item')) {
837
+ return target['timeline-item'];
838
+ }
839
+ target = target.parentNode;
840
+ }
841
+
842
+ return null;
843
+ };
844
+
845
+ /**
846
+ * Find the ItemSet from an event target:
847
+ * searches for the attribute 'timeline-itemset' in the event target's element tree
848
+ * @param {Event} event
849
+ * @return {ItemSet | null} item
850
+ */
851
+ ItemSet.itemSetFromTarget = function itemSetFromTarget (event) {
852
+ var target = event.target;
853
+ while (target) {
854
+ if (target.hasOwnProperty('timeline-itemset')) {
855
+ return target['timeline-itemset'];
856
+ }
857
+ target = target.parentNode;
858
+ }
859
+
860
+ return null;
861
+ };
862
+
863
+ /**
864
+ * Find the DataSet to which this ItemSet is connected
865
+ * @returns {null | DataSet} dataset
866
+ * @private
867
+ */
868
+ ItemSet.prototype._myDataSet = function _myDataSet() {
869
+ // find the root DataSet
870
+ var dataset = this.itemsData;
871
+ while (dataset instanceof DataView) {
872
+ dataset = dataset.data;
873
+ }
874
+ return dataset;
875
+ };