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.
Files changed (73) hide show
  1. checksums.yaml +4 -4
  2. data/lib/vis/rails/version.rb +1 -1
  3. data/vendor/assets/javascripts/vis.js +26 -26
  4. metadata +16 -85
  5. data/vendor/assets/vis/DataSet.js +0 -926
  6. data/vendor/assets/vis/DataView.js +0 -283
  7. data/vendor/assets/vis/graph/Edge.js +0 -957
  8. data/vendor/assets/vis/graph/Graph.js +0 -2291
  9. data/vendor/assets/vis/graph/Groups.js +0 -80
  10. data/vendor/assets/vis/graph/Images.js +0 -41
  11. data/vendor/assets/vis/graph/Node.js +0 -966
  12. data/vendor/assets/vis/graph/Popup.js +0 -132
  13. data/vendor/assets/vis/graph/css/graph-manipulation.css +0 -128
  14. data/vendor/assets/vis/graph/css/graph-navigation.css +0 -66
  15. data/vendor/assets/vis/graph/dotparser.js +0 -829
  16. data/vendor/assets/vis/graph/graphMixins/ClusterMixin.js +0 -1143
  17. data/vendor/assets/vis/graph/graphMixins/HierarchicalLayoutMixin.js +0 -311
  18. data/vendor/assets/vis/graph/graphMixins/ManipulationMixin.js +0 -576
  19. data/vendor/assets/vis/graph/graphMixins/MixinLoader.js +0 -199
  20. data/vendor/assets/vis/graph/graphMixins/NavigationMixin.js +0 -205
  21. data/vendor/assets/vis/graph/graphMixins/SectorsMixin.js +0 -552
  22. data/vendor/assets/vis/graph/graphMixins/SelectionMixin.js +0 -648
  23. data/vendor/assets/vis/graph/graphMixins/physics/BarnesHut.js +0 -398
  24. data/vendor/assets/vis/graph/graphMixins/physics/HierarchialRepulsion.js +0 -64
  25. data/vendor/assets/vis/graph/graphMixins/physics/PhysicsMixin.js +0 -697
  26. data/vendor/assets/vis/graph/graphMixins/physics/Repulsion.js +0 -66
  27. data/vendor/assets/vis/graph/img/acceptDeleteIcon.png +0 -0
  28. data/vendor/assets/vis/graph/img/addNodeIcon.png +0 -0
  29. data/vendor/assets/vis/graph/img/backIcon.png +0 -0
  30. data/vendor/assets/vis/graph/img/connectIcon.png +0 -0
  31. data/vendor/assets/vis/graph/img/cross.png +0 -0
  32. data/vendor/assets/vis/graph/img/cross2.png +0 -0
  33. data/vendor/assets/vis/graph/img/deleteIcon.png +0 -0
  34. data/vendor/assets/vis/graph/img/downArrow.png +0 -0
  35. data/vendor/assets/vis/graph/img/editIcon.png +0 -0
  36. data/vendor/assets/vis/graph/img/leftArrow.png +0 -0
  37. data/vendor/assets/vis/graph/img/minus.png +0 -0
  38. data/vendor/assets/vis/graph/img/plus.png +0 -0
  39. data/vendor/assets/vis/graph/img/rightArrow.png +0 -0
  40. data/vendor/assets/vis/graph/img/upArrow.png +0 -0
  41. data/vendor/assets/vis/graph/img/zoomExtends.png +0 -0
  42. data/vendor/assets/vis/graph/shapes.js +0 -225
  43. data/vendor/assets/vis/graph3d/Graph3d.js +0 -3306
  44. data/vendor/assets/vis/module/exports.js +0 -65
  45. data/vendor/assets/vis/module/header.js +0 -24
  46. data/vendor/assets/vis/module/imports.js +0 -31
  47. data/vendor/assets/vis/shim.js +0 -252
  48. data/vendor/assets/vis/timeline/Range.js +0 -532
  49. data/vendor/assets/vis/timeline/TimeStep.js +0 -466
  50. data/vendor/assets/vis/timeline/Timeline.js +0 -851
  51. data/vendor/assets/vis/timeline/component/Component.js +0 -52
  52. data/vendor/assets/vis/timeline/component/CurrentTime.js +0 -128
  53. data/vendor/assets/vis/timeline/component/CustomTime.js +0 -182
  54. data/vendor/assets/vis/timeline/component/Group.js +0 -470
  55. data/vendor/assets/vis/timeline/component/ItemSet.js +0 -1332
  56. data/vendor/assets/vis/timeline/component/TimeAxis.js +0 -389
  57. data/vendor/assets/vis/timeline/component/css/animation.css +0 -33
  58. data/vendor/assets/vis/timeline/component/css/currenttime.css +0 -5
  59. data/vendor/assets/vis/timeline/component/css/customtime.css +0 -6
  60. data/vendor/assets/vis/timeline/component/css/item.css +0 -107
  61. data/vendor/assets/vis/timeline/component/css/itemset.css +0 -33
  62. data/vendor/assets/vis/timeline/component/css/labelset.css +0 -36
  63. data/vendor/assets/vis/timeline/component/css/panel.css +0 -71
  64. data/vendor/assets/vis/timeline/component/css/timeaxis.css +0 -48
  65. data/vendor/assets/vis/timeline/component/css/timeline.css +0 -2
  66. data/vendor/assets/vis/timeline/component/item/Item.js +0 -139
  67. data/vendor/assets/vis/timeline/component/item/ItemBox.js +0 -230
  68. data/vendor/assets/vis/timeline/component/item/ItemPoint.js +0 -190
  69. data/vendor/assets/vis/timeline/component/item/ItemRange.js +0 -262
  70. data/vendor/assets/vis/timeline/component/item/ItemRangeOverflow.js +0 -57
  71. data/vendor/assets/vis/timeline/img/delete.png +0 -0
  72. data/vendor/assets/vis/timeline/stack.js +0 -112
  73. 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
- };