vis-rails 2.0.0 → 2.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/vis/rails/version.rb +1 -1
- data/vendor/assets/javascripts/vis.js +26 -26
- metadata +16 -85
- data/vendor/assets/vis/DataSet.js +0 -926
- data/vendor/assets/vis/DataView.js +0 -283
- data/vendor/assets/vis/graph/Edge.js +0 -957
- data/vendor/assets/vis/graph/Graph.js +0 -2291
- data/vendor/assets/vis/graph/Groups.js +0 -80
- data/vendor/assets/vis/graph/Images.js +0 -41
- data/vendor/assets/vis/graph/Node.js +0 -966
- data/vendor/assets/vis/graph/Popup.js +0 -132
- data/vendor/assets/vis/graph/css/graph-manipulation.css +0 -128
- data/vendor/assets/vis/graph/css/graph-navigation.css +0 -66
- data/vendor/assets/vis/graph/dotparser.js +0 -829
- data/vendor/assets/vis/graph/graphMixins/ClusterMixin.js +0 -1143
- data/vendor/assets/vis/graph/graphMixins/HierarchicalLayoutMixin.js +0 -311
- data/vendor/assets/vis/graph/graphMixins/ManipulationMixin.js +0 -576
- data/vendor/assets/vis/graph/graphMixins/MixinLoader.js +0 -199
- data/vendor/assets/vis/graph/graphMixins/NavigationMixin.js +0 -205
- data/vendor/assets/vis/graph/graphMixins/SectorsMixin.js +0 -552
- data/vendor/assets/vis/graph/graphMixins/SelectionMixin.js +0 -648
- data/vendor/assets/vis/graph/graphMixins/physics/BarnesHut.js +0 -398
- data/vendor/assets/vis/graph/graphMixins/physics/HierarchialRepulsion.js +0 -64
- data/vendor/assets/vis/graph/graphMixins/physics/PhysicsMixin.js +0 -697
- data/vendor/assets/vis/graph/graphMixins/physics/Repulsion.js +0 -66
- data/vendor/assets/vis/graph/img/acceptDeleteIcon.png +0 -0
- data/vendor/assets/vis/graph/img/addNodeIcon.png +0 -0
- data/vendor/assets/vis/graph/img/backIcon.png +0 -0
- data/vendor/assets/vis/graph/img/connectIcon.png +0 -0
- data/vendor/assets/vis/graph/img/cross.png +0 -0
- data/vendor/assets/vis/graph/img/cross2.png +0 -0
- data/vendor/assets/vis/graph/img/deleteIcon.png +0 -0
- data/vendor/assets/vis/graph/img/downArrow.png +0 -0
- data/vendor/assets/vis/graph/img/editIcon.png +0 -0
- data/vendor/assets/vis/graph/img/leftArrow.png +0 -0
- data/vendor/assets/vis/graph/img/minus.png +0 -0
- data/vendor/assets/vis/graph/img/plus.png +0 -0
- data/vendor/assets/vis/graph/img/rightArrow.png +0 -0
- data/vendor/assets/vis/graph/img/upArrow.png +0 -0
- data/vendor/assets/vis/graph/img/zoomExtends.png +0 -0
- data/vendor/assets/vis/graph/shapes.js +0 -225
- data/vendor/assets/vis/graph3d/Graph3d.js +0 -3306
- data/vendor/assets/vis/module/exports.js +0 -65
- data/vendor/assets/vis/module/header.js +0 -24
- data/vendor/assets/vis/module/imports.js +0 -31
- data/vendor/assets/vis/shim.js +0 -252
- data/vendor/assets/vis/timeline/Range.js +0 -532
- data/vendor/assets/vis/timeline/TimeStep.js +0 -466
- data/vendor/assets/vis/timeline/Timeline.js +0 -851
- data/vendor/assets/vis/timeline/component/Component.js +0 -52
- data/vendor/assets/vis/timeline/component/CurrentTime.js +0 -128
- data/vendor/assets/vis/timeline/component/CustomTime.js +0 -182
- data/vendor/assets/vis/timeline/component/Group.js +0 -470
- data/vendor/assets/vis/timeline/component/ItemSet.js +0 -1332
- data/vendor/assets/vis/timeline/component/TimeAxis.js +0 -389
- data/vendor/assets/vis/timeline/component/css/animation.css +0 -33
- data/vendor/assets/vis/timeline/component/css/currenttime.css +0 -5
- data/vendor/assets/vis/timeline/component/css/customtime.css +0 -6
- data/vendor/assets/vis/timeline/component/css/item.css +0 -107
- data/vendor/assets/vis/timeline/component/css/itemset.css +0 -33
- data/vendor/assets/vis/timeline/component/css/labelset.css +0 -36
- data/vendor/assets/vis/timeline/component/css/panel.css +0 -71
- data/vendor/assets/vis/timeline/component/css/timeaxis.css +0 -48
- data/vendor/assets/vis/timeline/component/css/timeline.css +0 -2
- data/vendor/assets/vis/timeline/component/item/Item.js +0 -139
- data/vendor/assets/vis/timeline/component/item/ItemBox.js +0 -230
- data/vendor/assets/vis/timeline/component/item/ItemPoint.js +0 -190
- data/vendor/assets/vis/timeline/component/item/ItemRange.js +0 -262
- data/vendor/assets/vis/timeline/component/item/ItemRangeOverflow.js +0 -57
- data/vendor/assets/vis/timeline/img/delete.png +0 -0
- data/vendor/assets/vis/timeline/stack.js +0 -112
- data/vendor/assets/vis/util.js +0 -990
@@ -1,1332 +0,0 @@
|
|
1
|
-
var UNGROUPED = '__ungrouped__'; // reserved group id for ungrouped items
|
2
|
-
|
3
|
-
/**
|
4
|
-
* An ItemSet holds a set of items and ranges which can be displayed in a
|
5
|
-
* range. The width is determined by the parent of the ItemSet, and the height
|
6
|
-
* is determined by the size of the items.
|
7
|
-
* @param {{dom: Object, domProps: Object, emitter: Emitter, range: Range}} body
|
8
|
-
* @param {Object} [options] See ItemSet.setOptions for the available options.
|
9
|
-
* @constructor ItemSet
|
10
|
-
* @extends Component
|
11
|
-
*/
|
12
|
-
function ItemSet(body, options) {
|
13
|
-
this.body = body;
|
14
|
-
|
15
|
-
this.defaultOptions = {
|
16
|
-
type: 'box',
|
17
|
-
orientation: 'bottom', // 'top' or 'bottom'
|
18
|
-
align: 'center', // alignment of box items
|
19
|
-
stack: true,
|
20
|
-
groupOrder: null,
|
21
|
-
|
22
|
-
selectable: true,
|
23
|
-
editable: {
|
24
|
-
updateTime: false,
|
25
|
-
updateGroup: false,
|
26
|
-
add: false,
|
27
|
-
remove: false
|
28
|
-
},
|
29
|
-
|
30
|
-
onAdd: function (item, callback) {
|
31
|
-
callback(item);
|
32
|
-
},
|
33
|
-
onUpdate: function (item, callback) {
|
34
|
-
callback(item);
|
35
|
-
},
|
36
|
-
onMove: function (item, callback) {
|
37
|
-
callback(item);
|
38
|
-
},
|
39
|
-
onRemove: function (item, callback) {
|
40
|
-
callback(item);
|
41
|
-
},
|
42
|
-
|
43
|
-
margin: {
|
44
|
-
item: 10,
|
45
|
-
axis: 20
|
46
|
-
},
|
47
|
-
padding: 5
|
48
|
-
};
|
49
|
-
|
50
|
-
// options is shared by this ItemSet and all its items
|
51
|
-
this.options = util.extend({}, this.defaultOptions);
|
52
|
-
|
53
|
-
// options for getting items from the DataSet with the correct type
|
54
|
-
this.itemOptions = {
|
55
|
-
type: {start: 'Date', end: 'Date'}
|
56
|
-
};
|
57
|
-
|
58
|
-
this.conversion = {
|
59
|
-
toScreen: body.util.toScreen,
|
60
|
-
toTime: body.util.toTime
|
61
|
-
};
|
62
|
-
this.dom = {};
|
63
|
-
this.props = {};
|
64
|
-
this.hammer = null;
|
65
|
-
|
66
|
-
var me = this;
|
67
|
-
this.itemsData = null; // DataSet
|
68
|
-
this.groupsData = null; // DataSet
|
69
|
-
|
70
|
-
// listeners for the DataSet of the items
|
71
|
-
this.itemListeners = {
|
72
|
-
'add': function (event, params, senderId) {
|
73
|
-
me._onAdd(params.items);
|
74
|
-
},
|
75
|
-
'update': function (event, params, senderId) {
|
76
|
-
me._onUpdate(params.items);
|
77
|
-
},
|
78
|
-
'remove': function (event, params, senderId) {
|
79
|
-
me._onRemove(params.items);
|
80
|
-
}
|
81
|
-
};
|
82
|
-
|
83
|
-
// listeners for the DataSet of the groups
|
84
|
-
this.groupListeners = {
|
85
|
-
'add': function (event, params, senderId) {
|
86
|
-
me._onAddGroups(params.items);
|
87
|
-
},
|
88
|
-
'update': function (event, params, senderId) {
|
89
|
-
me._onUpdateGroups(params.items);
|
90
|
-
},
|
91
|
-
'remove': function (event, params, senderId) {
|
92
|
-
me._onRemoveGroups(params.items);
|
93
|
-
}
|
94
|
-
};
|
95
|
-
|
96
|
-
this.items = {}; // object with an Item for every data item
|
97
|
-
this.groups = {}; // Group object for every group
|
98
|
-
this.groupIds = [];
|
99
|
-
|
100
|
-
this.selection = []; // list with the ids of all selected nodes
|
101
|
-
this.stackDirty = true; // if true, all items will be restacked on next redraw
|
102
|
-
|
103
|
-
this.touchParams = {}; // stores properties while dragging
|
104
|
-
// create the HTML DOM
|
105
|
-
|
106
|
-
this._create();
|
107
|
-
|
108
|
-
this.setOptions(options);
|
109
|
-
}
|
110
|
-
|
111
|
-
ItemSet.prototype = new Component();
|
112
|
-
|
113
|
-
// available item types will be registered here
|
114
|
-
ItemSet.types = {
|
115
|
-
box: ItemBox,
|
116
|
-
range: ItemRange,
|
117
|
-
rangeoverflow: ItemRangeOverflow,
|
118
|
-
point: ItemPoint
|
119
|
-
};
|
120
|
-
|
121
|
-
/**
|
122
|
-
* Create the HTML DOM for the ItemSet
|
123
|
-
*/
|
124
|
-
ItemSet.prototype._create = function(){
|
125
|
-
var frame = document.createElement('div');
|
126
|
-
frame.className = 'itemset';
|
127
|
-
frame['timeline-itemset'] = this;
|
128
|
-
this.dom.frame = frame;
|
129
|
-
|
130
|
-
// create background panel
|
131
|
-
var background = document.createElement('div');
|
132
|
-
background.className = 'background';
|
133
|
-
frame.appendChild(background);
|
134
|
-
this.dom.background = background;
|
135
|
-
|
136
|
-
// create foreground panel
|
137
|
-
var foreground = document.createElement('div');
|
138
|
-
foreground.className = 'foreground';
|
139
|
-
frame.appendChild(foreground);
|
140
|
-
this.dom.foreground = foreground;
|
141
|
-
|
142
|
-
// create axis panel
|
143
|
-
var axis = document.createElement('div');
|
144
|
-
axis.className = 'axis';
|
145
|
-
this.dom.axis = axis;
|
146
|
-
|
147
|
-
// create labelset
|
148
|
-
var labelSet = document.createElement('div');
|
149
|
-
labelSet.className = 'labelset';
|
150
|
-
this.dom.labelSet = labelSet;
|
151
|
-
|
152
|
-
// create ungrouped Group
|
153
|
-
this._updateUngrouped();
|
154
|
-
|
155
|
-
// attach event listeners
|
156
|
-
// Note: we bind to the centerContainer for the case where the height
|
157
|
-
// of the center container is larger than of the ItemSet, so we
|
158
|
-
// can click in the empty area to create a new item or deselect an item.
|
159
|
-
this.hammer = Hammer(this.body.dom.centerContainer, {
|
160
|
-
prevent_default: true
|
161
|
-
});
|
162
|
-
|
163
|
-
// drag items when selected
|
164
|
-
this.hammer.on('touch', this._onTouch.bind(this));
|
165
|
-
this.hammer.on('dragstart', this._onDragStart.bind(this));
|
166
|
-
this.hammer.on('drag', this._onDrag.bind(this));
|
167
|
-
this.hammer.on('dragend', this._onDragEnd.bind(this));
|
168
|
-
|
169
|
-
// single select (or unselect) when tapping an item
|
170
|
-
this.hammer.on('tap', this._onSelectItem.bind(this));
|
171
|
-
|
172
|
-
// multi select when holding mouse/touch, or on ctrl+click
|
173
|
-
this.hammer.on('hold', this._onMultiSelectItem.bind(this));
|
174
|
-
|
175
|
-
// add item on doubletap
|
176
|
-
this.hammer.on('doubletap', this._onAddItem.bind(this));
|
177
|
-
|
178
|
-
// attach to the DOM
|
179
|
-
this.show();
|
180
|
-
};
|
181
|
-
|
182
|
-
/**
|
183
|
-
* Set options for the ItemSet. Existing options will be extended/overwritten.
|
184
|
-
* @param {Object} [options] The following options are available:
|
185
|
-
* {String} type
|
186
|
-
* Default type for the items. Choose from 'box'
|
187
|
-
* (default), 'point', or 'range'. The default
|
188
|
-
* Style can be overwritten by individual items.
|
189
|
-
* {String} align
|
190
|
-
* Alignment for the items, only applicable for
|
191
|
-
* ItemBox. Choose 'center' (default), 'left', or
|
192
|
-
* 'right'.
|
193
|
-
* {String} orientation
|
194
|
-
* Orientation of the item set. Choose 'top' or
|
195
|
-
* 'bottom' (default).
|
196
|
-
* {Function} groupOrder
|
197
|
-
* A sorting function for ordering groups
|
198
|
-
* {Boolean} stack
|
199
|
-
* If true (deafult), items will be stacked on
|
200
|
-
* top of each other.
|
201
|
-
* {Number} margin.axis
|
202
|
-
* Margin between the axis and the items in pixels.
|
203
|
-
* Default is 20.
|
204
|
-
* {Number} margin.item
|
205
|
-
* Margin between items in pixels. Default is 10.
|
206
|
-
* {Number} margin
|
207
|
-
* Set margin for both axis and items in pixels.
|
208
|
-
* {Number} padding
|
209
|
-
* Padding of the contents of an item in pixels.
|
210
|
-
* Must correspond with the items css. Default is 5.
|
211
|
-
* {Boolean} selectable
|
212
|
-
* If true (default), items can be selected.
|
213
|
-
* {Boolean} editable
|
214
|
-
* Set all editable options to true or false
|
215
|
-
* {Boolean} editable.updateTime
|
216
|
-
* Allow dragging an item to an other moment in time
|
217
|
-
* {Boolean} editable.updateGroup
|
218
|
-
* Allow dragging an item to an other group
|
219
|
-
* {Boolean} editable.add
|
220
|
-
* Allow creating new items on double tap
|
221
|
-
* {Boolean} editable.remove
|
222
|
-
* Allow removing items by clicking the delete button
|
223
|
-
* top right of a selected item.
|
224
|
-
* {Function(item: Item, callback: Function)} onAdd
|
225
|
-
* Callback function triggered when an item is about to be added:
|
226
|
-
* when the user double taps an empty space in the Timeline.
|
227
|
-
* {Function(item: Item, callback: Function)} onUpdate
|
228
|
-
* Callback function fired when an item is about to be updated.
|
229
|
-
* This function typically has to show a dialog where the user
|
230
|
-
* change the item. If not implemented, nothing happens.
|
231
|
-
* {Function(item: Item, callback: Function)} onMove
|
232
|
-
* Fired when an item has been moved. If not implemented,
|
233
|
-
* the move action will be accepted.
|
234
|
-
* {Function(item: Item, callback: Function)} onRemove
|
235
|
-
* Fired when an item is about to be deleted.
|
236
|
-
* If not implemented, the item will be always removed.
|
237
|
-
*/
|
238
|
-
ItemSet.prototype.setOptions = function(options) {
|
239
|
-
if (options) {
|
240
|
-
// copy all options that we know
|
241
|
-
var fields = ['type', 'align', 'orientation', 'padding', 'stack', 'selectable', 'groupOrder'];
|
242
|
-
util.selectiveExtend(fields, this.options, options);
|
243
|
-
|
244
|
-
if ('margin' in options) {
|
245
|
-
if (typeof options.margin === 'number') {
|
246
|
-
this.options.margin.axis = options.margin;
|
247
|
-
this.options.margin.item = options.margin;
|
248
|
-
}
|
249
|
-
else if (typeof options.margin === 'object'){
|
250
|
-
util.selectiveExtend(['axis', 'item'], this.options.margin, options.margin);
|
251
|
-
}
|
252
|
-
}
|
253
|
-
|
254
|
-
if ('editable' in options) {
|
255
|
-
if (typeof options.editable === 'boolean') {
|
256
|
-
this.options.editable.updateTime = options.editable;
|
257
|
-
this.options.editable.updateGroup = options.editable;
|
258
|
-
this.options.editable.add = options.editable;
|
259
|
-
this.options.editable.remove = options.editable;
|
260
|
-
}
|
261
|
-
else if (typeof options.editable === 'object') {
|
262
|
-
util.selectiveExtend(['updateTime', 'updateGroup', 'add', 'remove'], this.options.editable, options.editable);
|
263
|
-
}
|
264
|
-
}
|
265
|
-
|
266
|
-
// callback functions
|
267
|
-
var addCallback = (function (name) {
|
268
|
-
if (name in options) {
|
269
|
-
var fn = options[name];
|
270
|
-
if (!(fn instanceof Function) || fn.length != 2) {
|
271
|
-
throw new Error('option ' + name + ' must be a function ' + name + '(item, callback)');
|
272
|
-
}
|
273
|
-
this.options[name] = fn;
|
274
|
-
}
|
275
|
-
}).bind(this);
|
276
|
-
['onAdd', 'onUpdate', 'onRemove', 'onMove'].forEach(addCallback);
|
277
|
-
|
278
|
-
// force the itemSet to refresh: options like orientation and margins may be changed
|
279
|
-
this.markDirty();
|
280
|
-
}
|
281
|
-
};
|
282
|
-
|
283
|
-
/**
|
284
|
-
* Mark the ItemSet dirty so it will refresh everything with next redraw
|
285
|
-
*/
|
286
|
-
ItemSet.prototype.markDirty = function() {
|
287
|
-
this.groupIds = [];
|
288
|
-
this.stackDirty = true;
|
289
|
-
};
|
290
|
-
|
291
|
-
/**
|
292
|
-
* Destroy the ItemSet
|
293
|
-
*/
|
294
|
-
ItemSet.prototype.destroy = function() {
|
295
|
-
this.hide();
|
296
|
-
this.setItems(null);
|
297
|
-
this.setGroups(null);
|
298
|
-
|
299
|
-
this.hammer = null;
|
300
|
-
|
301
|
-
this.body = null;
|
302
|
-
this.conversion = null;
|
303
|
-
};
|
304
|
-
|
305
|
-
/**
|
306
|
-
* Hide the component from the DOM
|
307
|
-
*/
|
308
|
-
ItemSet.prototype.hide = function() {
|
309
|
-
// remove the frame containing the items
|
310
|
-
if (this.dom.frame.parentNode) {
|
311
|
-
this.dom.frame.parentNode.removeChild(this.dom.frame);
|
312
|
-
}
|
313
|
-
|
314
|
-
// remove the axis with dots
|
315
|
-
if (this.dom.axis.parentNode) {
|
316
|
-
this.dom.axis.parentNode.removeChild(this.dom.axis);
|
317
|
-
}
|
318
|
-
|
319
|
-
// remove the labelset containing all group labels
|
320
|
-
if (this.dom.labelSet.parentNode) {
|
321
|
-
this.dom.labelSet.parentNode.removeChild(this.dom.labelSet);
|
322
|
-
}
|
323
|
-
};
|
324
|
-
|
325
|
-
/**
|
326
|
-
* Show the component in the DOM (when not already visible).
|
327
|
-
* @return {Boolean} changed
|
328
|
-
*/
|
329
|
-
ItemSet.prototype.show = function() {
|
330
|
-
// show frame containing the items
|
331
|
-
if (!this.dom.frame.parentNode) {
|
332
|
-
this.body.dom.center.appendChild(this.dom.frame);
|
333
|
-
}
|
334
|
-
|
335
|
-
// show axis with dots
|
336
|
-
if (!this.dom.axis.parentNode) {
|
337
|
-
this.body.dom.backgroundVertical.appendChild(this.dom.axis);
|
338
|
-
}
|
339
|
-
|
340
|
-
// show labelset containing labels
|
341
|
-
if (!this.dom.labelSet.parentNode) {
|
342
|
-
this.body.dom.left.appendChild(this.dom.labelSet);
|
343
|
-
}
|
344
|
-
};
|
345
|
-
|
346
|
-
/**
|
347
|
-
* Set selected items by their id. Replaces the current selection
|
348
|
-
* Unknown id's are silently ignored.
|
349
|
-
* @param {Array} [ids] An array with zero or more id's of the items to be
|
350
|
-
* selected. If ids is an empty array, all items will be
|
351
|
-
* unselected.
|
352
|
-
*/
|
353
|
-
ItemSet.prototype.setSelection = function(ids) {
|
354
|
-
var i, ii, id, item;
|
355
|
-
|
356
|
-
if (ids) {
|
357
|
-
if (!Array.isArray(ids)) {
|
358
|
-
throw new TypeError('Array expected');
|
359
|
-
}
|
360
|
-
|
361
|
-
// unselect currently selected items
|
362
|
-
for (i = 0, ii = this.selection.length; i < ii; i++) {
|
363
|
-
id = this.selection[i];
|
364
|
-
item = this.items[id];
|
365
|
-
if (item) item.unselect();
|
366
|
-
}
|
367
|
-
|
368
|
-
// select items
|
369
|
-
this.selection = [];
|
370
|
-
for (i = 0, ii = ids.length; i < ii; i++) {
|
371
|
-
id = ids[i];
|
372
|
-
item = this.items[id];
|
373
|
-
if (item) {
|
374
|
-
this.selection.push(id);
|
375
|
-
item.select();
|
376
|
-
}
|
377
|
-
}
|
378
|
-
}
|
379
|
-
};
|
380
|
-
|
381
|
-
/**
|
382
|
-
* Get the selected items by their id
|
383
|
-
* @return {Array} ids The ids of the selected items
|
384
|
-
*/
|
385
|
-
ItemSet.prototype.getSelection = function() {
|
386
|
-
return this.selection.concat([]);
|
387
|
-
};
|
388
|
-
|
389
|
-
/**
|
390
|
-
* Deselect a selected item
|
391
|
-
* @param {String | Number} id
|
392
|
-
* @private
|
393
|
-
*/
|
394
|
-
ItemSet.prototype._deselect = function(id) {
|
395
|
-
var selection = this.selection;
|
396
|
-
for (var i = 0, ii = selection.length; i < ii; i++) {
|
397
|
-
if (selection[i] == id) { // non-strict comparison!
|
398
|
-
selection.splice(i, 1);
|
399
|
-
break;
|
400
|
-
}
|
401
|
-
}
|
402
|
-
};
|
403
|
-
|
404
|
-
/**
|
405
|
-
* Repaint the component
|
406
|
-
* @return {boolean} Returns true if the component is resized
|
407
|
-
*/
|
408
|
-
ItemSet.prototype.redraw = function() {
|
409
|
-
var margin = this.options.margin,
|
410
|
-
range = this.body.range,
|
411
|
-
asSize = util.option.asSize,
|
412
|
-
options = this.options,
|
413
|
-
orientation = options.orientation,
|
414
|
-
resized = false,
|
415
|
-
frame = this.dom.frame,
|
416
|
-
editable = options.editable.updateTime || options.editable.updateGroup;
|
417
|
-
|
418
|
-
// update class name
|
419
|
-
frame.className = 'itemset' + (editable ? ' editable' : '');
|
420
|
-
|
421
|
-
// reorder the groups (if needed)
|
422
|
-
resized = this._orderGroups() || resized;
|
423
|
-
|
424
|
-
// check whether zoomed (in that case we need to re-stack everything)
|
425
|
-
// TODO: would be nicer to get this as a trigger from Range
|
426
|
-
var visibleInterval = range.end - range.start;
|
427
|
-
var zoomed = (visibleInterval != this.lastVisibleInterval) || (this.props.width != this.props.lastWidth);
|
428
|
-
if (zoomed) this.stackDirty = true;
|
429
|
-
this.lastVisibleInterval = visibleInterval;
|
430
|
-
this.props.lastWidth = this.props.width;
|
431
|
-
|
432
|
-
// redraw all groups
|
433
|
-
var restack = this.stackDirty,
|
434
|
-
firstGroup = this._firstGroup(),
|
435
|
-
firstMargin = {
|
436
|
-
item: margin.item,
|
437
|
-
axis: margin.axis
|
438
|
-
},
|
439
|
-
nonFirstMargin = {
|
440
|
-
item: margin.item,
|
441
|
-
axis: margin.item / 2
|
442
|
-
},
|
443
|
-
height = 0,
|
444
|
-
minHeight = margin.axis + margin.item;
|
445
|
-
util.forEach(this.groups, function (group) {
|
446
|
-
var groupMargin = (group == firstGroup) ? firstMargin : nonFirstMargin;
|
447
|
-
var groupResized = group.redraw(range, groupMargin, restack);
|
448
|
-
resized = groupResized || resized;
|
449
|
-
height += group.height;
|
450
|
-
});
|
451
|
-
height = Math.max(height, minHeight);
|
452
|
-
this.stackDirty = false;
|
453
|
-
|
454
|
-
// update frame height
|
455
|
-
frame.style.height = asSize(height);
|
456
|
-
|
457
|
-
// calculate actual size and position
|
458
|
-
this.props.top = frame.offsetTop;
|
459
|
-
this.props.left = frame.offsetLeft;
|
460
|
-
this.props.width = frame.offsetWidth;
|
461
|
-
this.props.height = height;
|
462
|
-
|
463
|
-
// reposition axis
|
464
|
-
this.dom.axis.style.top = asSize((orientation == 'top') ?
|
465
|
-
(this.body.domProps.top.height + this.body.domProps.border.top) :
|
466
|
-
(this.body.domProps.top.height + this.body.domProps.centerContainer.height));
|
467
|
-
this.dom.axis.style.left = this.body.domProps.border.left + 'px';
|
468
|
-
|
469
|
-
// check if this component is resized
|
470
|
-
resized = this._isResized() || resized;
|
471
|
-
|
472
|
-
return resized;
|
473
|
-
};
|
474
|
-
|
475
|
-
/**
|
476
|
-
* Get the first group, aligned with the axis
|
477
|
-
* @return {Group | null} firstGroup
|
478
|
-
* @private
|
479
|
-
*/
|
480
|
-
ItemSet.prototype._firstGroup = function() {
|
481
|
-
var firstGroupIndex = (this.options.orientation == 'top') ? 0 : (this.groupIds.length - 1);
|
482
|
-
var firstGroupId = this.groupIds[firstGroupIndex];
|
483
|
-
var firstGroup = this.groups[firstGroupId] || this.groups[UNGROUPED];
|
484
|
-
|
485
|
-
return firstGroup || null;
|
486
|
-
};
|
487
|
-
|
488
|
-
/**
|
489
|
-
* Create or delete the group holding all ungrouped items. This group is used when
|
490
|
-
* there are no groups specified.
|
491
|
-
* @protected
|
492
|
-
*/
|
493
|
-
ItemSet.prototype._updateUngrouped = function() {
|
494
|
-
var ungrouped = this.groups[UNGROUPED];
|
495
|
-
|
496
|
-
if (this.groupsData) {
|
497
|
-
// remove the group holding all ungrouped items
|
498
|
-
if (ungrouped) {
|
499
|
-
ungrouped.hide();
|
500
|
-
delete this.groups[UNGROUPED];
|
501
|
-
}
|
502
|
-
}
|
503
|
-
else {
|
504
|
-
// create a group holding all (unfiltered) items
|
505
|
-
if (!ungrouped) {
|
506
|
-
var id = null;
|
507
|
-
var data = null;
|
508
|
-
ungrouped = new Group(id, data, this);
|
509
|
-
this.groups[UNGROUPED] = ungrouped;
|
510
|
-
|
511
|
-
for (var itemId in this.items) {
|
512
|
-
if (this.items.hasOwnProperty(itemId)) {
|
513
|
-
ungrouped.add(this.items[itemId]);
|
514
|
-
}
|
515
|
-
}
|
516
|
-
|
517
|
-
ungrouped.show();
|
518
|
-
}
|
519
|
-
}
|
520
|
-
};
|
521
|
-
|
522
|
-
/**
|
523
|
-
* Get the element for the labelset
|
524
|
-
* @return {HTMLElement} labelSet
|
525
|
-
*/
|
526
|
-
ItemSet.prototype.getLabelSet = function() {
|
527
|
-
return this.dom.labelSet;
|
528
|
-
};
|
529
|
-
|
530
|
-
/**
|
531
|
-
* Set items
|
532
|
-
* @param {vis.DataSet | null} items
|
533
|
-
*/
|
534
|
-
ItemSet.prototype.setItems = function(items) {
|
535
|
-
var me = this,
|
536
|
-
ids,
|
537
|
-
oldItemsData = this.itemsData;
|
538
|
-
|
539
|
-
// replace the dataset
|
540
|
-
if (!items) {
|
541
|
-
this.itemsData = null;
|
542
|
-
}
|
543
|
-
else if (items instanceof DataSet || items instanceof DataView) {
|
544
|
-
this.itemsData = items;
|
545
|
-
}
|
546
|
-
else {
|
547
|
-
throw new TypeError('Data must be an instance of DataSet or DataView');
|
548
|
-
}
|
549
|
-
|
550
|
-
if (oldItemsData) {
|
551
|
-
// unsubscribe from old dataset
|
552
|
-
util.forEach(this.itemListeners, function (callback, event) {
|
553
|
-
oldItemsData.off(event, callback);
|
554
|
-
});
|
555
|
-
|
556
|
-
// remove all drawn items
|
557
|
-
ids = oldItemsData.getIds();
|
558
|
-
this._onRemove(ids);
|
559
|
-
}
|
560
|
-
|
561
|
-
if (this.itemsData) {
|
562
|
-
// subscribe to new dataset
|
563
|
-
var id = this.id;
|
564
|
-
util.forEach(this.itemListeners, function (callback, event) {
|
565
|
-
me.itemsData.on(event, callback, id);
|
566
|
-
});
|
567
|
-
|
568
|
-
// add all new items
|
569
|
-
ids = this.itemsData.getIds();
|
570
|
-
this._onAdd(ids);
|
571
|
-
|
572
|
-
// update the group holding all ungrouped items
|
573
|
-
this._updateUngrouped();
|
574
|
-
}
|
575
|
-
};
|
576
|
-
|
577
|
-
/**
|
578
|
-
* Get the current items
|
579
|
-
* @returns {vis.DataSet | null}
|
580
|
-
*/
|
581
|
-
ItemSet.prototype.getItems = function() {
|
582
|
-
return this.itemsData;
|
583
|
-
};
|
584
|
-
|
585
|
-
/**
|
586
|
-
* Set groups
|
587
|
-
* @param {vis.DataSet} groups
|
588
|
-
*/
|
589
|
-
ItemSet.prototype.setGroups = function(groups) {
|
590
|
-
var me = this,
|
591
|
-
ids;
|
592
|
-
|
593
|
-
// unsubscribe from current dataset
|
594
|
-
if (this.groupsData) {
|
595
|
-
util.forEach(this.groupListeners, function (callback, event) {
|
596
|
-
me.groupsData.unsubscribe(event, callback);
|
597
|
-
});
|
598
|
-
|
599
|
-
// remove all drawn groups
|
600
|
-
ids = this.groupsData.getIds();
|
601
|
-
this.groupsData = null;
|
602
|
-
this._onRemoveGroups(ids); // note: this will cause a redraw
|
603
|
-
}
|
604
|
-
|
605
|
-
// replace the dataset
|
606
|
-
if (!groups) {
|
607
|
-
this.groupsData = null;
|
608
|
-
}
|
609
|
-
else if (groups instanceof DataSet || groups instanceof DataView) {
|
610
|
-
this.groupsData = groups;
|
611
|
-
}
|
612
|
-
else {
|
613
|
-
throw new TypeError('Data must be an instance of DataSet or DataView');
|
614
|
-
}
|
615
|
-
|
616
|
-
if (this.groupsData) {
|
617
|
-
// subscribe to new dataset
|
618
|
-
var id = this.id;
|
619
|
-
util.forEach(this.groupListeners, function (callback, event) {
|
620
|
-
me.groupsData.on(event, callback, id);
|
621
|
-
});
|
622
|
-
|
623
|
-
// draw all ms
|
624
|
-
ids = this.groupsData.getIds();
|
625
|
-
this._onAddGroups(ids);
|
626
|
-
}
|
627
|
-
|
628
|
-
// update the group holding all ungrouped items
|
629
|
-
this._updateUngrouped();
|
630
|
-
|
631
|
-
// update the order of all items in each group
|
632
|
-
this._order();
|
633
|
-
|
634
|
-
this.body.emitter.emit('change');
|
635
|
-
};
|
636
|
-
|
637
|
-
/**
|
638
|
-
* Get the current groups
|
639
|
-
* @returns {vis.DataSet | null} groups
|
640
|
-
*/
|
641
|
-
ItemSet.prototype.getGroups = function() {
|
642
|
-
return this.groupsData;
|
643
|
-
};
|
644
|
-
|
645
|
-
/**
|
646
|
-
* Remove an item by its id
|
647
|
-
* @param {String | Number} id
|
648
|
-
*/
|
649
|
-
ItemSet.prototype.removeItem = function(id) {
|
650
|
-
var item = this.itemsData.get(id),
|
651
|
-
dataset = this._myDataSet();
|
652
|
-
|
653
|
-
if (item) {
|
654
|
-
// confirm deletion
|
655
|
-
this.options.onRemove(item, function (item) {
|
656
|
-
if (item) {
|
657
|
-
// remove by id here, it is possible that an item has no id defined
|
658
|
-
// itself, so better not delete by the item itself
|
659
|
-
dataset.remove(id);
|
660
|
-
}
|
661
|
-
});
|
662
|
-
}
|
663
|
-
};
|
664
|
-
|
665
|
-
/**
|
666
|
-
* Handle updated items
|
667
|
-
* @param {Number[]} ids
|
668
|
-
* @protected
|
669
|
-
*/
|
670
|
-
ItemSet.prototype._onUpdate = function(ids) {
|
671
|
-
var me = this;
|
672
|
-
|
673
|
-
ids.forEach(function (id) {
|
674
|
-
var itemData = me.itemsData.get(id, me.itemOptions),
|
675
|
-
item = me.items[id],
|
676
|
-
type = itemData.type ||
|
677
|
-
(itemData.start && itemData.end && 'range') ||
|
678
|
-
me.options.type ||
|
679
|
-
'box';
|
680
|
-
|
681
|
-
var constructor = ItemSet.types[type];
|
682
|
-
|
683
|
-
if (item) {
|
684
|
-
// update item
|
685
|
-
if (!constructor || !(item instanceof constructor)) {
|
686
|
-
// item type has changed, delete the item and recreate it
|
687
|
-
me._removeItem(item);
|
688
|
-
item = null;
|
689
|
-
}
|
690
|
-
else {
|
691
|
-
me._updateItem(item, itemData);
|
692
|
-
}
|
693
|
-
}
|
694
|
-
|
695
|
-
if (!item) {
|
696
|
-
// create item
|
697
|
-
if (constructor) {
|
698
|
-
item = new constructor(itemData, me.conversion, me.options);
|
699
|
-
item.id = id; // TODO: not so nice setting id afterwards
|
700
|
-
me._addItem(item);
|
701
|
-
}
|
702
|
-
else {
|
703
|
-
throw new TypeError('Unknown item type "' + type + '"');
|
704
|
-
}
|
705
|
-
}
|
706
|
-
});
|
707
|
-
|
708
|
-
this._order();
|
709
|
-
this.stackDirty = true; // force re-stacking of all items next redraw
|
710
|
-
this.body.emitter.emit('change');
|
711
|
-
};
|
712
|
-
|
713
|
-
/**
|
714
|
-
* Handle added items
|
715
|
-
* @param {Number[]} ids
|
716
|
-
* @protected
|
717
|
-
*/
|
718
|
-
ItemSet.prototype._onAdd = ItemSet.prototype._onUpdate;
|
719
|
-
|
720
|
-
/**
|
721
|
-
* Handle removed items
|
722
|
-
* @param {Number[]} ids
|
723
|
-
* @protected
|
724
|
-
*/
|
725
|
-
ItemSet.prototype._onRemove = function(ids) {
|
726
|
-
var count = 0;
|
727
|
-
var me = this;
|
728
|
-
ids.forEach(function (id) {
|
729
|
-
var item = me.items[id];
|
730
|
-
if (item) {
|
731
|
-
count++;
|
732
|
-
me._removeItem(item);
|
733
|
-
}
|
734
|
-
});
|
735
|
-
|
736
|
-
if (count) {
|
737
|
-
// update order
|
738
|
-
this._order();
|
739
|
-
this.stackDirty = true; // force re-stacking of all items next redraw
|
740
|
-
this.body.emitter.emit('change');
|
741
|
-
}
|
742
|
-
};
|
743
|
-
|
744
|
-
/**
|
745
|
-
* Update the order of item in all groups
|
746
|
-
* @private
|
747
|
-
*/
|
748
|
-
ItemSet.prototype._order = function() {
|
749
|
-
// reorder the items in all groups
|
750
|
-
// TODO: optimization: only reorder groups affected by the changed items
|
751
|
-
util.forEach(this.groups, function (group) {
|
752
|
-
group.order();
|
753
|
-
});
|
754
|
-
};
|
755
|
-
|
756
|
-
/**
|
757
|
-
* Handle updated groups
|
758
|
-
* @param {Number[]} ids
|
759
|
-
* @private
|
760
|
-
*/
|
761
|
-
ItemSet.prototype._onUpdateGroups = function(ids) {
|
762
|
-
this._onAddGroups(ids);
|
763
|
-
};
|
764
|
-
|
765
|
-
/**
|
766
|
-
* Handle changed groups
|
767
|
-
* @param {Number[]} ids
|
768
|
-
* @private
|
769
|
-
*/
|
770
|
-
ItemSet.prototype._onAddGroups = function(ids) {
|
771
|
-
var me = this;
|
772
|
-
|
773
|
-
ids.forEach(function (id) {
|
774
|
-
var groupData = me.groupsData.get(id);
|
775
|
-
var group = me.groups[id];
|
776
|
-
|
777
|
-
if (!group) {
|
778
|
-
// check for reserved ids
|
779
|
-
if (id == UNGROUPED) {
|
780
|
-
throw new Error('Illegal group id. ' + id + ' is a reserved id.');
|
781
|
-
}
|
782
|
-
|
783
|
-
var groupOptions = Object.create(me.options);
|
784
|
-
util.extend(groupOptions, {
|
785
|
-
height: null
|
786
|
-
});
|
787
|
-
|
788
|
-
group = new Group(id, groupData, me);
|
789
|
-
me.groups[id] = group;
|
790
|
-
|
791
|
-
// add items with this groupId to the new group
|
792
|
-
for (var itemId in me.items) {
|
793
|
-
if (me.items.hasOwnProperty(itemId)) {
|
794
|
-
var item = me.items[itemId];
|
795
|
-
if (item.data.group == id) {
|
796
|
-
group.add(item);
|
797
|
-
}
|
798
|
-
}
|
799
|
-
}
|
800
|
-
|
801
|
-
group.order();
|
802
|
-
group.show();
|
803
|
-
}
|
804
|
-
else {
|
805
|
-
// update group
|
806
|
-
group.setData(groupData);
|
807
|
-
}
|
808
|
-
});
|
809
|
-
|
810
|
-
this.body.emitter.emit('change');
|
811
|
-
};
|
812
|
-
|
813
|
-
/**
|
814
|
-
* Handle removed groups
|
815
|
-
* @param {Number[]} ids
|
816
|
-
* @private
|
817
|
-
*/
|
818
|
-
ItemSet.prototype._onRemoveGroups = function(ids) {
|
819
|
-
var groups = this.groups;
|
820
|
-
ids.forEach(function (id) {
|
821
|
-
var group = groups[id];
|
822
|
-
|
823
|
-
if (group) {
|
824
|
-
group.hide();
|
825
|
-
delete groups[id];
|
826
|
-
}
|
827
|
-
});
|
828
|
-
|
829
|
-
this.markDirty();
|
830
|
-
|
831
|
-
this.body.emitter.emit('change');
|
832
|
-
};
|
833
|
-
|
834
|
-
/**
|
835
|
-
* Reorder the groups if needed
|
836
|
-
* @return {boolean} changed
|
837
|
-
* @private
|
838
|
-
*/
|
839
|
-
ItemSet.prototype._orderGroups = function () {
|
840
|
-
if (this.groupsData) {
|
841
|
-
// reorder the groups
|
842
|
-
var groupIds = this.groupsData.getIds({
|
843
|
-
order: this.options.groupOrder
|
844
|
-
});
|
845
|
-
|
846
|
-
var changed = !util.equalArray(groupIds, this.groupIds);
|
847
|
-
if (changed) {
|
848
|
-
// hide all groups, removes them from the DOM
|
849
|
-
var groups = this.groups;
|
850
|
-
groupIds.forEach(function (groupId) {
|
851
|
-
groups[groupId].hide();
|
852
|
-
});
|
853
|
-
|
854
|
-
// show the groups again, attach them to the DOM in correct order
|
855
|
-
groupIds.forEach(function (groupId) {
|
856
|
-
groups[groupId].show();
|
857
|
-
});
|
858
|
-
|
859
|
-
this.groupIds = groupIds;
|
860
|
-
}
|
861
|
-
|
862
|
-
return changed;
|
863
|
-
}
|
864
|
-
else {
|
865
|
-
return false;
|
866
|
-
}
|
867
|
-
};
|
868
|
-
|
869
|
-
/**
|
870
|
-
* Add a new item
|
871
|
-
* @param {Item} item
|
872
|
-
* @private
|
873
|
-
*/
|
874
|
-
ItemSet.prototype._addItem = function(item) {
|
875
|
-
this.items[item.id] = item;
|
876
|
-
|
877
|
-
// add to group
|
878
|
-
var groupId = this.groupsData ? item.data.group : UNGROUPED;
|
879
|
-
var group = this.groups[groupId];
|
880
|
-
if (group) group.add(item);
|
881
|
-
};
|
882
|
-
|
883
|
-
/**
|
884
|
-
* Update an existing item
|
885
|
-
* @param {Item} item
|
886
|
-
* @param {Object} itemData
|
887
|
-
* @private
|
888
|
-
*/
|
889
|
-
ItemSet.prototype._updateItem = function(item, itemData) {
|
890
|
-
var oldGroupId = item.data.group;
|
891
|
-
|
892
|
-
item.data = itemData;
|
893
|
-
if (item.displayed) {
|
894
|
-
item.redraw();
|
895
|
-
}
|
896
|
-
|
897
|
-
// update group
|
898
|
-
if (oldGroupId != item.data.group) {
|
899
|
-
var oldGroup = this.groups[oldGroupId];
|
900
|
-
if (oldGroup) oldGroup.remove(item);
|
901
|
-
|
902
|
-
var groupId = this.groupsData ? item.data.group : UNGROUPED;
|
903
|
-
var group = this.groups[groupId];
|
904
|
-
if (group) group.add(item);
|
905
|
-
}
|
906
|
-
};
|
907
|
-
|
908
|
-
/**
|
909
|
-
* Delete an item from the ItemSet: remove it from the DOM, from the map
|
910
|
-
* with items, and from the map with visible items, and from the selection
|
911
|
-
* @param {Item} item
|
912
|
-
* @private
|
913
|
-
*/
|
914
|
-
ItemSet.prototype._removeItem = function(item) {
|
915
|
-
// remove from DOM
|
916
|
-
item.hide();
|
917
|
-
|
918
|
-
// remove from items
|
919
|
-
delete this.items[item.id];
|
920
|
-
|
921
|
-
// remove from selection
|
922
|
-
var index = this.selection.indexOf(item.id);
|
923
|
-
if (index != -1) this.selection.splice(index, 1);
|
924
|
-
|
925
|
-
// remove from group
|
926
|
-
var groupId = this.groupsData ? item.data.group : UNGROUPED;
|
927
|
-
var group = this.groups[groupId];
|
928
|
-
if (group) group.remove(item);
|
929
|
-
};
|
930
|
-
|
931
|
-
/**
|
932
|
-
* Create an array containing all items being a range (having an end date)
|
933
|
-
* @param array
|
934
|
-
* @returns {Array}
|
935
|
-
* @private
|
936
|
-
*/
|
937
|
-
ItemSet.prototype._constructByEndArray = function(array) {
|
938
|
-
var endArray = [];
|
939
|
-
|
940
|
-
for (var i = 0; i < array.length; i++) {
|
941
|
-
if (array[i] instanceof ItemRange) {
|
942
|
-
endArray.push(array[i]);
|
943
|
-
}
|
944
|
-
}
|
945
|
-
return endArray;
|
946
|
-
};
|
947
|
-
|
948
|
-
/**
|
949
|
-
* Register the clicked item on touch, before dragStart is initiated.
|
950
|
-
*
|
951
|
-
* dragStart is initiated from a mousemove event, which can have left the item
|
952
|
-
* already resulting in an item == null
|
953
|
-
*
|
954
|
-
* @param {Event} event
|
955
|
-
* @private
|
956
|
-
*/
|
957
|
-
ItemSet.prototype._onTouch = function (event) {
|
958
|
-
// store the touched item, used in _onDragStart
|
959
|
-
this.touchParams.item = ItemSet.itemFromTarget(event);
|
960
|
-
};
|
961
|
-
|
962
|
-
/**
|
963
|
-
* Start dragging the selected events
|
964
|
-
* @param {Event} event
|
965
|
-
* @private
|
966
|
-
*/
|
967
|
-
ItemSet.prototype._onDragStart = function (event) {
|
968
|
-
if (!this.options.editable.updateTime && !this.options.editable.updateGroup) {
|
969
|
-
return;
|
970
|
-
}
|
971
|
-
|
972
|
-
var item = this.touchParams.item || null,
|
973
|
-
me = this,
|
974
|
-
props;
|
975
|
-
|
976
|
-
if (item && item.selected) {
|
977
|
-
var dragLeftItem = event.target.dragLeftItem;
|
978
|
-
var dragRightItem = event.target.dragRightItem;
|
979
|
-
|
980
|
-
if (dragLeftItem) {
|
981
|
-
props = {
|
982
|
-
item: dragLeftItem
|
983
|
-
};
|
984
|
-
|
985
|
-
if (me.options.editable.updateTime) {
|
986
|
-
props.start = item.data.start.valueOf();
|
987
|
-
}
|
988
|
-
if (me.options.editable.updateGroup) {
|
989
|
-
if ('group' in item.data) props.group = item.data.group;
|
990
|
-
}
|
991
|
-
|
992
|
-
this.touchParams.itemProps = [props];
|
993
|
-
}
|
994
|
-
else if (dragRightItem) {
|
995
|
-
props = {
|
996
|
-
item: dragRightItem
|
997
|
-
};
|
998
|
-
|
999
|
-
if (me.options.editable.updateTime) {
|
1000
|
-
props.end = item.data.end.valueOf();
|
1001
|
-
}
|
1002
|
-
if (me.options.editable.updateGroup) {
|
1003
|
-
if ('group' in item.data) props.group = item.data.group;
|
1004
|
-
}
|
1005
|
-
|
1006
|
-
this.touchParams.itemProps = [props];
|
1007
|
-
}
|
1008
|
-
else {
|
1009
|
-
this.touchParams.itemProps = this.getSelection().map(function (id) {
|
1010
|
-
var item = me.items[id];
|
1011
|
-
var props = {
|
1012
|
-
item: item
|
1013
|
-
};
|
1014
|
-
|
1015
|
-
if (me.options.editable.updateTime) {
|
1016
|
-
if ('start' in item.data) props.start = item.data.start.valueOf();
|
1017
|
-
if ('end' in item.data) props.end = item.data.end.valueOf();
|
1018
|
-
}
|
1019
|
-
if (me.options.editable.updateGroup) {
|
1020
|
-
if ('group' in item.data) props.group = item.data.group;
|
1021
|
-
}
|
1022
|
-
|
1023
|
-
return props;
|
1024
|
-
});
|
1025
|
-
}
|
1026
|
-
|
1027
|
-
event.stopPropagation();
|
1028
|
-
}
|
1029
|
-
};
|
1030
|
-
|
1031
|
-
/**
|
1032
|
-
* Drag selected items
|
1033
|
-
* @param {Event} event
|
1034
|
-
* @private
|
1035
|
-
*/
|
1036
|
-
ItemSet.prototype._onDrag = function (event) {
|
1037
|
-
if (this.touchParams.itemProps) {
|
1038
|
-
var range = this.body.range,
|
1039
|
-
snap = this.body.util.snap || null,
|
1040
|
-
deltaX = event.gesture.deltaX,
|
1041
|
-
scale = (this.props.width / (range.end - range.start)),
|
1042
|
-
offset = deltaX / scale;
|
1043
|
-
|
1044
|
-
// move
|
1045
|
-
this.touchParams.itemProps.forEach(function (props) {
|
1046
|
-
if ('start' in props) {
|
1047
|
-
var start = new Date(props.start + offset);
|
1048
|
-
props.item.data.start = snap ? snap(start) : start;
|
1049
|
-
}
|
1050
|
-
|
1051
|
-
if ('end' in props) {
|
1052
|
-
var end = new Date(props.end + offset);
|
1053
|
-
props.item.data.end = snap ? snap(end) : end;
|
1054
|
-
}
|
1055
|
-
|
1056
|
-
if ('group' in props) {
|
1057
|
-
// drag from one group to another
|
1058
|
-
var group = ItemSet.groupFromTarget(event);
|
1059
|
-
if (group && group.groupId != props.item.data.group) {
|
1060
|
-
var oldGroup = props.item.parent;
|
1061
|
-
oldGroup.remove(props.item);
|
1062
|
-
oldGroup.order();
|
1063
|
-
group.add(props.item);
|
1064
|
-
group.order();
|
1065
|
-
|
1066
|
-
props.item.data.group = group.groupId;
|
1067
|
-
}
|
1068
|
-
}
|
1069
|
-
});
|
1070
|
-
|
1071
|
-
// TODO: implement onMoving handler
|
1072
|
-
|
1073
|
-
this.stackDirty = true; // force re-stacking of all items next redraw
|
1074
|
-
this.body.emitter.emit('change');
|
1075
|
-
|
1076
|
-
event.stopPropagation();
|
1077
|
-
}
|
1078
|
-
};
|
1079
|
-
|
1080
|
-
/**
|
1081
|
-
* End of dragging selected items
|
1082
|
-
* @param {Event} event
|
1083
|
-
* @private
|
1084
|
-
*/
|
1085
|
-
ItemSet.prototype._onDragEnd = function (event) {
|
1086
|
-
if (this.touchParams.itemProps) {
|
1087
|
-
// prepare a change set for the changed items
|
1088
|
-
var changes = [],
|
1089
|
-
me = this,
|
1090
|
-
dataset = this._myDataSet();
|
1091
|
-
|
1092
|
-
this.touchParams.itemProps.forEach(function (props) {
|
1093
|
-
var id = props.item.id,
|
1094
|
-
itemData = me.itemsData.get(id, me.itemOptions);
|
1095
|
-
|
1096
|
-
var changed = false;
|
1097
|
-
if ('start' in props.item.data) {
|
1098
|
-
changed = (props.start != props.item.data.start.valueOf());
|
1099
|
-
itemData.start = util.convert(props.item.data.start,
|
1100
|
-
dataset._options.type && dataset._options.type.start || 'Date');
|
1101
|
-
}
|
1102
|
-
if ('end' in props.item.data) {
|
1103
|
-
changed = changed || (props.end != props.item.data.end.valueOf());
|
1104
|
-
itemData.end = util.convert(props.item.data.end,
|
1105
|
-
dataset._options.type && dataset._options.type.end || 'Date');
|
1106
|
-
}
|
1107
|
-
if ('group' in props.item.data) {
|
1108
|
-
changed = changed || (props.group != props.item.data.group);
|
1109
|
-
itemData.group = props.item.data.group;
|
1110
|
-
}
|
1111
|
-
|
1112
|
-
// only apply changes when start or end is actually changed
|
1113
|
-
if (changed) {
|
1114
|
-
me.options.onMove(itemData, function (itemData) {
|
1115
|
-
if (itemData) {
|
1116
|
-
// apply changes
|
1117
|
-
itemData[dataset._fieldId] = id; // ensure the item contains its id (can be undefined)
|
1118
|
-
changes.push(itemData);
|
1119
|
-
}
|
1120
|
-
else {
|
1121
|
-
// restore original values
|
1122
|
-
if ('start' in props) props.item.data.start = props.start;
|
1123
|
-
if ('end' in props) props.item.data.end = props.end;
|
1124
|
-
|
1125
|
-
me.stackDirty = true; // force re-stacking of all items next redraw
|
1126
|
-
me.body.emitter.emit('change');
|
1127
|
-
}
|
1128
|
-
});
|
1129
|
-
}
|
1130
|
-
});
|
1131
|
-
this.touchParams.itemProps = null;
|
1132
|
-
|
1133
|
-
// apply the changes to the data (if there are changes)
|
1134
|
-
if (changes.length) {
|
1135
|
-
dataset.update(changes);
|
1136
|
-
}
|
1137
|
-
|
1138
|
-
event.stopPropagation();
|
1139
|
-
}
|
1140
|
-
};
|
1141
|
-
|
1142
|
-
/**
|
1143
|
-
* Handle selecting/deselecting an item when tapping it
|
1144
|
-
* @param {Event} event
|
1145
|
-
* @private
|
1146
|
-
*/
|
1147
|
-
ItemSet.prototype._onSelectItem = function (event) {
|
1148
|
-
if (!this.options.selectable) return;
|
1149
|
-
|
1150
|
-
var ctrlKey = event.gesture.srcEvent && event.gesture.srcEvent.ctrlKey;
|
1151
|
-
var shiftKey = event.gesture.srcEvent && event.gesture.srcEvent.shiftKey;
|
1152
|
-
if (ctrlKey || shiftKey) {
|
1153
|
-
this._onMultiSelectItem(event);
|
1154
|
-
return;
|
1155
|
-
}
|
1156
|
-
|
1157
|
-
var oldSelection = this.getSelection();
|
1158
|
-
|
1159
|
-
var item = ItemSet.itemFromTarget(event);
|
1160
|
-
var selection = item ? [item.id] : [];
|
1161
|
-
this.setSelection(selection);
|
1162
|
-
|
1163
|
-
var newSelection = this.getSelection();
|
1164
|
-
|
1165
|
-
// emit a select event,
|
1166
|
-
// except when old selection is empty and new selection is still empty
|
1167
|
-
if (newSelection.length > 0 || oldSelection.length > 0) {
|
1168
|
-
this.body.emitter.emit('select', {
|
1169
|
-
items: this.getSelection()
|
1170
|
-
});
|
1171
|
-
}
|
1172
|
-
|
1173
|
-
event.stopPropagation();
|
1174
|
-
};
|
1175
|
-
|
1176
|
-
/**
|
1177
|
-
* Handle creation and updates of an item on double tap
|
1178
|
-
* @param event
|
1179
|
-
* @private
|
1180
|
-
*/
|
1181
|
-
ItemSet.prototype._onAddItem = function (event) {
|
1182
|
-
if (!this.options.selectable) return;
|
1183
|
-
if (!this.options.editable.add) return;
|
1184
|
-
|
1185
|
-
var me = this,
|
1186
|
-
snap = this.body.util.snap || null,
|
1187
|
-
item = ItemSet.itemFromTarget(event);
|
1188
|
-
|
1189
|
-
if (item) {
|
1190
|
-
// update item
|
1191
|
-
|
1192
|
-
// execute async handler to update the item (or cancel it)
|
1193
|
-
var itemData = me.itemsData.get(item.id); // get a clone of the data from the dataset
|
1194
|
-
this.options.onUpdate(itemData, function (itemData) {
|
1195
|
-
if (itemData) {
|
1196
|
-
me.itemsData.update(itemData);
|
1197
|
-
}
|
1198
|
-
});
|
1199
|
-
}
|
1200
|
-
else {
|
1201
|
-
// add item
|
1202
|
-
var xAbs = vis.util.getAbsoluteLeft(this.dom.frame);
|
1203
|
-
var x = event.gesture.center.pageX - xAbs;
|
1204
|
-
var start = this.body.util.toTime(x);
|
1205
|
-
var newItem = {
|
1206
|
-
start: snap ? snap(start) : start,
|
1207
|
-
content: 'new item'
|
1208
|
-
};
|
1209
|
-
|
1210
|
-
// when default type is a range, add a default end date to the new item
|
1211
|
-
if (this.options.type === 'range' || this.options.type == 'rangeoverflow') {
|
1212
|
-
var end = this.body.util.toTime(x + this.props.width / 5);
|
1213
|
-
newItem.end = snap ? snap(end) : end;
|
1214
|
-
}
|
1215
|
-
|
1216
|
-
newItem[this.itemsData.fieldId] = util.randomUUID();
|
1217
|
-
|
1218
|
-
var group = ItemSet.groupFromTarget(event);
|
1219
|
-
if (group) {
|
1220
|
-
newItem.group = group.groupId;
|
1221
|
-
}
|
1222
|
-
|
1223
|
-
// execute async handler to customize (or cancel) adding an item
|
1224
|
-
this.options.onAdd(newItem, function (item) {
|
1225
|
-
if (item) {
|
1226
|
-
me.itemsData.add(newItem);
|
1227
|
-
// TODO: need to trigger a redraw?
|
1228
|
-
}
|
1229
|
-
});
|
1230
|
-
}
|
1231
|
-
};
|
1232
|
-
|
1233
|
-
/**
|
1234
|
-
* Handle selecting/deselecting multiple items when holding an item
|
1235
|
-
* @param {Event} event
|
1236
|
-
* @private
|
1237
|
-
*/
|
1238
|
-
ItemSet.prototype._onMultiSelectItem = function (event) {
|
1239
|
-
if (!this.options.selectable) return;
|
1240
|
-
|
1241
|
-
var selection,
|
1242
|
-
item = ItemSet.itemFromTarget(event);
|
1243
|
-
|
1244
|
-
if (item) {
|
1245
|
-
// multi select items
|
1246
|
-
selection = this.getSelection(); // current selection
|
1247
|
-
var index = selection.indexOf(item.id);
|
1248
|
-
if (index == -1) {
|
1249
|
-
// item is not yet selected -> select it
|
1250
|
-
selection.push(item.id);
|
1251
|
-
}
|
1252
|
-
else {
|
1253
|
-
// item is already selected -> deselect it
|
1254
|
-
selection.splice(index, 1);
|
1255
|
-
}
|
1256
|
-
this.setSelection(selection);
|
1257
|
-
|
1258
|
-
this.body.emitter.emit('select', {
|
1259
|
-
items: this.getSelection()
|
1260
|
-
});
|
1261
|
-
|
1262
|
-
event.stopPropagation();
|
1263
|
-
}
|
1264
|
-
};
|
1265
|
-
|
1266
|
-
/**
|
1267
|
-
* Find an item from an event target:
|
1268
|
-
* searches for the attribute 'timeline-item' in the event target's element tree
|
1269
|
-
* @param {Event} event
|
1270
|
-
* @return {Item | null} item
|
1271
|
-
*/
|
1272
|
-
ItemSet.itemFromTarget = function(event) {
|
1273
|
-
var target = event.target;
|
1274
|
-
while (target) {
|
1275
|
-
if (target.hasOwnProperty('timeline-item')) {
|
1276
|
-
return target['timeline-item'];
|
1277
|
-
}
|
1278
|
-
target = target.parentNode;
|
1279
|
-
}
|
1280
|
-
|
1281
|
-
return null;
|
1282
|
-
};
|
1283
|
-
|
1284
|
-
/**
|
1285
|
-
* Find the Group from an event target:
|
1286
|
-
* searches for the attribute 'timeline-group' in the event target's element tree
|
1287
|
-
* @param {Event} event
|
1288
|
-
* @return {Group | null} group
|
1289
|
-
*/
|
1290
|
-
ItemSet.groupFromTarget = function(event) {
|
1291
|
-
var target = event.target;
|
1292
|
-
while (target) {
|
1293
|
-
if (target.hasOwnProperty('timeline-group')) {
|
1294
|
-
return target['timeline-group'];
|
1295
|
-
}
|
1296
|
-
target = target.parentNode;
|
1297
|
-
}
|
1298
|
-
|
1299
|
-
return null;
|
1300
|
-
};
|
1301
|
-
|
1302
|
-
/**
|
1303
|
-
* Find the ItemSet from an event target:
|
1304
|
-
* searches for the attribute 'timeline-itemset' in the event target's element tree
|
1305
|
-
* @param {Event} event
|
1306
|
-
* @return {ItemSet | null} item
|
1307
|
-
*/
|
1308
|
-
ItemSet.itemSetFromTarget = function(event) {
|
1309
|
-
var target = event.target;
|
1310
|
-
while (target) {
|
1311
|
-
if (target.hasOwnProperty('timeline-itemset')) {
|
1312
|
-
return target['timeline-itemset'];
|
1313
|
-
}
|
1314
|
-
target = target.parentNode;
|
1315
|
-
}
|
1316
|
-
|
1317
|
-
return null;
|
1318
|
-
};
|
1319
|
-
|
1320
|
-
/**
|
1321
|
-
* Find the DataSet to which this ItemSet is connected
|
1322
|
-
* @returns {null | DataSet} dataset
|
1323
|
-
* @private
|
1324
|
-
*/
|
1325
|
-
ItemSet.prototype._myDataSet = function() {
|
1326
|
-
// find the root DataSet
|
1327
|
-
var dataset = this.itemsData;
|
1328
|
-
while (dataset instanceof DataView) {
|
1329
|
-
dataset = dataset.data;
|
1330
|
-
}
|
1331
|
-
return dataset;
|
1332
|
-
};
|