vis-rails 0.0.6 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/vis/rails/version.rb +1 -1
- data/vendor/assets/javascripts/vis.js +2 -9
- data/vendor/assets/vis/DataSet.js +17 -9
- data/vendor/assets/vis/graph/Edge.js +49 -24
- data/vendor/assets/vis/graph/Graph.js +268 -64
- data/vendor/assets/vis/graph/Groups.js +1 -1
- data/vendor/assets/vis/graph/Node.js +18 -67
- data/vendor/assets/vis/graph/Popup.js +40 -13
- data/vendor/assets/vis/graph/css/graph-navigation.css +18 -14
- data/vendor/assets/vis/graph/graphMixins/ClusterMixin.js +7 -5
- data/vendor/assets/vis/graph/graphMixins/HierarchicalLayoutMixin.js +20 -5
- data/vendor/assets/vis/graph/graphMixins/ManipulationMixin.js +33 -33
- data/vendor/assets/vis/graph/graphMixins/MixinLoader.js +30 -32
- data/vendor/assets/vis/graph/graphMixins/NavigationMixin.js +33 -1
- data/vendor/assets/vis/graph/graphMixins/SectorsMixin.js +2 -2
- data/vendor/assets/vis/graph/graphMixins/SelectionMixin.js +72 -60
- data/vendor/assets/vis/graph/graphMixins/physics/BarnesHut.js +43 -18
- data/vendor/assets/vis/graph/graphMixins/physics/HierarchialRepulsion.js +8 -8
- data/vendor/assets/vis/graph/graphMixins/physics/PhysicsMixin.js +309 -129
- data/vendor/assets/vis/graph/graphMixins/physics/Repulsion.js +10 -10
- data/vendor/assets/vis/module/exports.js +1 -2
- data/vendor/assets/vis/module/header.js +2 -2
- data/vendor/assets/vis/timeline/Range.js +53 -93
- data/vendor/assets/vis/timeline/Timeline.js +328 -224
- data/vendor/assets/vis/timeline/component/Component.js +17 -95
- data/vendor/assets/vis/timeline/component/CurrentTime.js +54 -59
- data/vendor/assets/vis/timeline/component/CustomTime.js +55 -83
- data/vendor/assets/vis/timeline/component/Group.js +398 -75
- data/vendor/assets/vis/timeline/component/ItemSet.js +662 -403
- data/vendor/assets/vis/timeline/component/Panel.js +118 -60
- data/vendor/assets/vis/timeline/component/RootPanel.js +80 -132
- data/vendor/assets/vis/timeline/component/TimeAxis.js +191 -277
- data/vendor/assets/vis/timeline/component/css/item.css +16 -23
- data/vendor/assets/vis/timeline/component/css/itemset.css +25 -4
- data/vendor/assets/vis/timeline/component/css/labelset.css +34 -0
- data/vendor/assets/vis/timeline/component/css/panel.css +15 -1
- data/vendor/assets/vis/timeline/component/css/timeaxis.css +8 -8
- data/vendor/assets/vis/timeline/component/item/Item.js +48 -26
- data/vendor/assets/vis/timeline/component/item/ItemBox.js +156 -230
- data/vendor/assets/vis/timeline/component/item/ItemPoint.js +118 -166
- data/vendor/assets/vis/timeline/component/item/ItemRange.js +135 -187
- data/vendor/assets/vis/timeline/component/item/ItemRangeOverflow.js +29 -92
- data/vendor/assets/vis/timeline/stack.js +112 -0
- data/vendor/assets/vis/util.js +136 -38
- metadata +4 -18
- data/vendor/assets/vis/.gitignore +0 -1
- data/vendor/assets/vis/EventBus.js +0 -89
- data/vendor/assets/vis/events.js +0 -116
- data/vendor/assets/vis/graph/ClusterMixin.js +0 -1019
- data/vendor/assets/vis/graph/NavigationMixin.js +0 -245
- data/vendor/assets/vis/graph/SectorsMixin.js +0 -547
- data/vendor/assets/vis/graph/SelectionMixin.js +0 -515
- data/vendor/assets/vis/graph/img/downarrow.png +0 -0
- data/vendor/assets/vis/graph/img/leftarrow.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/timeline/Controller.js +0 -183
- data/vendor/assets/vis/timeline/Stack.js +0 -190
- data/vendor/assets/vis/timeline/component/ContentPanel.js +0 -113
- data/vendor/assets/vis/timeline/component/GroupSet.js +0 -580
- data/vendor/assets/vis/timeline/component/css/groupset.css +0 -59
@@ -1,75 +1,72 @@
|
|
1
|
+
var UNGROUPED = '__ungrouped__'; // reserved group id for ungrouped items
|
2
|
+
|
1
3
|
/**
|
2
4
|
* An ItemSet holds a set of items and ranges which can be displayed in a
|
3
5
|
* range. The width is determined by the parent of the ItemSet, and the height
|
4
6
|
* is determined by the size of the items.
|
5
|
-
* @param {
|
6
|
-
*
|
7
|
-
*
|
8
|
-
*
|
9
|
-
*
|
7
|
+
* @param {Panel} backgroundPanel Panel which can be used to display the
|
8
|
+
* vertical lines of box items.
|
9
|
+
* @param {Panel} axisPanel Panel on the axis where the dots of box-items
|
10
|
+
* can be displayed.
|
11
|
+
* @param {Panel} sidePanel Left side panel holding labels
|
12
|
+
* @param {Object} [options] See ItemSet.setOptions for the available options.
|
10
13
|
* @constructor ItemSet
|
11
14
|
* @extends Panel
|
12
15
|
*/
|
13
|
-
|
14
|
-
function ItemSet(parent, depends, options) {
|
16
|
+
function ItemSet(backgroundPanel, axisPanel, sidePanel, options) {
|
15
17
|
this.id = util.randomUUID();
|
16
|
-
this.parent = parent;
|
17
|
-
this.depends = depends;
|
18
|
-
|
19
|
-
// event listeners
|
20
|
-
this.eventListeners = {
|
21
|
-
dragstart: this._onDragStart.bind(this),
|
22
|
-
drag: this._onDrag.bind(this),
|
23
|
-
dragend: this._onDragEnd.bind(this)
|
24
|
-
};
|
25
18
|
|
26
19
|
// one options object is shared by this itemset and all its items
|
27
20
|
this.options = options || {};
|
28
|
-
this.
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
margin: {
|
33
|
-
axis: 20,
|
34
|
-
item: 10
|
35
|
-
},
|
36
|
-
padding: 5
|
37
|
-
};
|
38
|
-
|
21
|
+
this.backgroundPanel = backgroundPanel;
|
22
|
+
this.axisPanel = axisPanel;
|
23
|
+
this.sidePanel = sidePanel;
|
24
|
+
this.itemOptions = Object.create(this.options);
|
39
25
|
this.dom = {};
|
26
|
+
this.hammer = null;
|
40
27
|
|
41
28
|
var me = this;
|
42
|
-
this.itemsData = null;
|
43
|
-
this.
|
29
|
+
this.itemsData = null; // DataSet
|
30
|
+
this.groupsData = null; // DataSet
|
31
|
+
this.range = null; // Range or Object {start: number, end: number}
|
44
32
|
|
45
|
-
//
|
46
|
-
this.
|
33
|
+
// listeners for the DataSet of the items
|
34
|
+
this.itemListeners = {
|
47
35
|
'add': function (event, params, senderId) {
|
48
|
-
if (senderId != me.id)
|
49
|
-
me._onAdd(params.items);
|
50
|
-
}
|
36
|
+
if (senderId != me.id) me._onAdd(params.items);
|
51
37
|
},
|
52
38
|
'update': function (event, params, senderId) {
|
53
|
-
if (senderId != me.id)
|
54
|
-
me._onUpdate(params.items);
|
55
|
-
}
|
39
|
+
if (senderId != me.id) me._onUpdate(params.items);
|
56
40
|
},
|
57
41
|
'remove': function (event, params, senderId) {
|
58
|
-
if (senderId != me.id)
|
59
|
-
|
60
|
-
|
42
|
+
if (senderId != me.id) me._onRemove(params.items);
|
43
|
+
}
|
44
|
+
};
|
45
|
+
|
46
|
+
// listeners for the DataSet of the groups
|
47
|
+
this.groupListeners = {
|
48
|
+
'add': function (event, params, senderId) {
|
49
|
+
if (senderId != me.id) me._onAddGroups(params.items);
|
50
|
+
},
|
51
|
+
'update': function (event, params, senderId) {
|
52
|
+
if (senderId != me.id) me._onUpdateGroups(params.items);
|
53
|
+
},
|
54
|
+
'remove': function (event, params, senderId) {
|
55
|
+
if (senderId != me.id) me._onRemoveGroups(params.items);
|
61
56
|
}
|
62
57
|
};
|
63
58
|
|
64
59
|
this.items = {}; // object with an Item for every data item
|
60
|
+
this.groups = {}; // Group object for every group
|
61
|
+
this.groupIds = [];
|
62
|
+
|
65
63
|
this.selection = []; // list with the ids of all selected nodes
|
66
|
-
this.
|
67
|
-
this.stack = new Stack(this, Object.create(this.options));
|
68
|
-
this.conversion = null;
|
64
|
+
this.stackDirty = true; // if true, all items will be restacked on next repaint
|
69
65
|
|
70
66
|
this.touchParams = {}; // stores properties while dragging
|
67
|
+
// create the HTML DOM
|
71
68
|
|
72
|
-
|
69
|
+
this._create();
|
73
70
|
}
|
74
71
|
|
75
72
|
ItemSet.prototype = new Panel();
|
@@ -82,6 +79,51 @@ ItemSet.types = {
|
|
82
79
|
point: ItemPoint
|
83
80
|
};
|
84
81
|
|
82
|
+
/**
|
83
|
+
* Create the HTML DOM for the ItemSet
|
84
|
+
*/
|
85
|
+
ItemSet.prototype._create = function _create(){
|
86
|
+
var frame = document.createElement('div');
|
87
|
+
frame['timeline-itemset'] = this;
|
88
|
+
this.frame = frame;
|
89
|
+
|
90
|
+
// create background panel
|
91
|
+
var background = document.createElement('div');
|
92
|
+
background.className = 'background';
|
93
|
+
this.backgroundPanel.frame.appendChild(background);
|
94
|
+
this.dom.background = background;
|
95
|
+
|
96
|
+
// create foreground panel
|
97
|
+
var foreground = document.createElement('div');
|
98
|
+
foreground.className = 'foreground';
|
99
|
+
frame.appendChild(foreground);
|
100
|
+
this.dom.foreground = foreground;
|
101
|
+
|
102
|
+
// create axis panel
|
103
|
+
var axis = document.createElement('div');
|
104
|
+
axis.className = 'axis';
|
105
|
+
this.dom.axis = axis;
|
106
|
+
this.axisPanel.frame.appendChild(axis);
|
107
|
+
|
108
|
+
// create labelset
|
109
|
+
var labelSet = document.createElement('div');
|
110
|
+
labelSet.className = 'labelset';
|
111
|
+
this.dom.labelSet = labelSet;
|
112
|
+
this.sidePanel.frame.appendChild(labelSet);
|
113
|
+
|
114
|
+
// create ungrouped Group
|
115
|
+
this._updateUngrouped();
|
116
|
+
|
117
|
+
// attach event listeners
|
118
|
+
// TODO: use event listeners from the rootpanel to improve performance?
|
119
|
+
this.hammer = Hammer(frame, {
|
120
|
+
prevent_default: true
|
121
|
+
});
|
122
|
+
this.hammer.on('dragstart', this._onDragStart.bind(this));
|
123
|
+
this.hammer.on('drag', this._onDrag.bind(this));
|
124
|
+
this.hammer.on('dragend', this._onDragEnd.bind(this));
|
125
|
+
};
|
126
|
+
|
85
127
|
/**
|
86
128
|
* Set options for the ItemSet. Existing options will be extended/overwritten.
|
87
129
|
* @param {Object} [options] The following options are available:
|
@@ -110,56 +152,58 @@ ItemSet.types = {
|
|
110
152
|
* Function to let items snap to nice dates when
|
111
153
|
* dragging items.
|
112
154
|
*/
|
113
|
-
ItemSet.prototype.setOptions =
|
114
|
-
|
115
|
-
|
155
|
+
ItemSet.prototype.setOptions = function setOptions(options) {
|
156
|
+
Component.prototype.setOptions.call(this, options);
|
157
|
+
};
|
116
158
|
|
117
159
|
/**
|
118
|
-
*
|
119
|
-
* @param {Controller | null} controller
|
160
|
+
* Mark the ItemSet dirty so it will refresh everything with next repaint
|
120
161
|
*/
|
121
|
-
ItemSet.prototype.
|
122
|
-
|
162
|
+
ItemSet.prototype.markDirty = function markDirty() {
|
163
|
+
this.groupIds = [];
|
164
|
+
this.stackDirty = true;
|
165
|
+
};
|
123
166
|
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
167
|
+
/**
|
168
|
+
* Hide the component from the DOM
|
169
|
+
*/
|
170
|
+
ItemSet.prototype.hide = function hide() {
|
171
|
+
// remove the axis with dots
|
172
|
+
if (this.dom.axis.parentNode) {
|
173
|
+
this.dom.axis.parentNode.removeChild(this.dom.axis);
|
131
174
|
}
|
132
175
|
|
133
|
-
|
176
|
+
// remove the background with vertical lines
|
177
|
+
if (this.dom.background.parentNode) {
|
178
|
+
this.dom.background.parentNode.removeChild(this.dom.background);
|
179
|
+
}
|
134
180
|
|
135
|
-
//
|
136
|
-
if (this.
|
137
|
-
|
138
|
-
if (this.eventListeners.hasOwnProperty(event)) {
|
139
|
-
this.controller.on(event, this.eventListeners[event]);
|
140
|
-
}
|
141
|
-
}
|
181
|
+
// remove the labelset containing all group labels
|
182
|
+
if (this.dom.labelSet.parentNode) {
|
183
|
+
this.dom.labelSet.parentNode.removeChild(this.dom.labelSet);
|
142
184
|
}
|
143
185
|
};
|
144
186
|
|
145
|
-
|
146
|
-
(
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
return _controller;
|
155
|
-
},
|
156
|
-
|
157
|
-
set: function (controller) {
|
187
|
+
/**
|
188
|
+
* Show the component in the DOM (when not already visible).
|
189
|
+
* @return {Boolean} changed
|
190
|
+
*/
|
191
|
+
ItemSet.prototype.show = function show() {
|
192
|
+
// show axis with dots
|
193
|
+
if (!this.dom.axis.parentNode) {
|
194
|
+
this.axisPanel.frame.appendChild(this.dom.axis);
|
195
|
+
}
|
158
196
|
|
159
|
-
|
160
|
-
|
161
|
-
|
197
|
+
// show background with vertical lines
|
198
|
+
if (!this.dom.background.parentNode) {
|
199
|
+
this.backgroundPanel.frame.appendChild(this.dom.background);
|
200
|
+
}
|
162
201
|
|
202
|
+
// show labelset containing labels
|
203
|
+
if (!this.dom.labelSet.parentNode) {
|
204
|
+
this.sidePanel.frame.appendChild(this.dom.labelSet);
|
205
|
+
}
|
206
|
+
};
|
163
207
|
|
164
208
|
/**
|
165
209
|
* Set range (start and end).
|
@@ -181,7 +225,7 @@ ItemSet.prototype.setRange = function setRange(range) {
|
|
181
225
|
* unselected.
|
182
226
|
*/
|
183
227
|
ItemSet.prototype.setSelection = function setSelection(ids) {
|
184
|
-
var i, ii, id, item
|
228
|
+
var i, ii, id, item;
|
185
229
|
|
186
230
|
if (ids) {
|
187
231
|
if (!Array.isArray(ids)) {
|
@@ -205,10 +249,6 @@ ItemSet.prototype.setSelection = function setSelection(ids) {
|
|
205
249
|
item.select();
|
206
250
|
}
|
207
251
|
}
|
208
|
-
|
209
|
-
if (this.controller) {
|
210
|
-
this.requestRepaint();
|
211
|
-
}
|
212
252
|
}
|
213
253
|
};
|
214
254
|
|
@@ -235,184 +275,145 @@ ItemSet.prototype._deselect = function _deselect(id) {
|
|
235
275
|
}
|
236
276
|
};
|
237
277
|
|
278
|
+
/**
|
279
|
+
* Return the item sets frame
|
280
|
+
* @returns {HTMLElement} frame
|
281
|
+
*/
|
282
|
+
ItemSet.prototype.getFrame = function getFrame() {
|
283
|
+
return this.frame;
|
284
|
+
};
|
285
|
+
|
238
286
|
/**
|
239
287
|
* Repaint the component
|
240
|
-
* @return {
|
288
|
+
* @return {boolean} Returns true if the component is resized
|
241
289
|
*/
|
242
290
|
ItemSet.prototype.repaint = function repaint() {
|
243
|
-
var
|
244
|
-
|
291
|
+
var margin = this.options.margin,
|
292
|
+
range = this.range,
|
245
293
|
asSize = util.option.asSize,
|
294
|
+
asString = util.option.asString,
|
246
295
|
options = this.options,
|
247
296
|
orientation = this.getOption('orientation'),
|
248
|
-
|
297
|
+
resized = false,
|
249
298
|
frame = this.frame;
|
250
299
|
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
if (className) {
|
258
|
-
util.addClassName(frame, util.option.asString(className));
|
259
|
-
}
|
260
|
-
|
261
|
-
// create background panel
|
262
|
-
var background = document.createElement('div');
|
263
|
-
background.className = 'background';
|
264
|
-
frame.appendChild(background);
|
265
|
-
this.dom.background = background;
|
266
|
-
|
267
|
-
// create foreground panel
|
268
|
-
var foreground = document.createElement('div');
|
269
|
-
foreground.className = 'foreground';
|
270
|
-
frame.appendChild(foreground);
|
271
|
-
this.dom.foreground = foreground;
|
272
|
-
|
273
|
-
// create axis panel
|
274
|
-
var axis = document.createElement('div');
|
275
|
-
axis.className = 'itemset-axis';
|
276
|
-
//frame.appendChild(axis);
|
277
|
-
this.dom.axis = axis;
|
278
|
-
|
279
|
-
this.frame = frame;
|
280
|
-
changed += 1;
|
300
|
+
// TODO: document this feature to specify one margin for both item and axis distance
|
301
|
+
if (typeof margin === 'number') {
|
302
|
+
margin = {
|
303
|
+
item: margin,
|
304
|
+
axis: margin
|
305
|
+
};
|
281
306
|
}
|
282
307
|
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
308
|
+
// update className
|
309
|
+
frame.className = 'itemset' + (options.className ? (' ' + asString(options.className)) : '');
|
310
|
+
|
311
|
+
// reorder the groups (if needed)
|
312
|
+
resized = this._orderGroups() || resized;
|
313
|
+
|
314
|
+
// check whether zoomed (in that case we need to re-stack everything)
|
315
|
+
// TODO: would be nicer to get this as a trigger from Range
|
316
|
+
var visibleInterval = this.range.end - this.range.start;
|
317
|
+
var zoomed = (visibleInterval != this.lastVisibleInterval) || (this.width != this.lastWidth);
|
318
|
+
if (zoomed) this.stackDirty = true;
|
319
|
+
this.lastVisibleInterval = visibleInterval;
|
320
|
+
this.lastWidth = this.width;
|
321
|
+
|
322
|
+
// repaint all groups
|
323
|
+
var restack = this.stackDirty,
|
324
|
+
firstGroup = this._firstGroup(),
|
325
|
+
firstMargin = {
|
326
|
+
item: margin.item,
|
327
|
+
axis: margin.axis
|
328
|
+
},
|
329
|
+
nonFirstMargin = {
|
330
|
+
item: margin.item,
|
331
|
+
axis: margin.item / 2
|
332
|
+
},
|
333
|
+
height = 0,
|
334
|
+
minHeight = margin.axis + margin.item;
|
335
|
+
util.forEach(this.groups, function (group) {
|
336
|
+
var groupMargin = (group == firstGroup) ? firstMargin : nonFirstMargin;
|
337
|
+
resized = group.repaint(range, groupMargin, restack) || resized;
|
338
|
+
height += group.height;
|
339
|
+
});
|
340
|
+
height = Math.max(height, minHeight);
|
341
|
+
this.stackDirty = false;
|
298
342
|
|
299
343
|
// reposition frame
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
344
|
+
frame.style.left = asSize(options.left, '');
|
345
|
+
frame.style.right = asSize(options.right, '');
|
346
|
+
frame.style.top = asSize((orientation == 'top') ? '0' : '');
|
347
|
+
frame.style.bottom = asSize((orientation == 'top') ? '' : '0');
|
348
|
+
frame.style.width = asSize(options.width, '100%');
|
349
|
+
frame.style.height = asSize(height);
|
350
|
+
//frame.style.height = asSize('height' in options ? options.height : height); // TODO: reckon with height
|
351
|
+
|
352
|
+
// calculate actual size and position
|
353
|
+
this.top = frame.offsetTop;
|
354
|
+
this.left = frame.offsetLeft;
|
355
|
+
this.width = frame.offsetWidth;
|
356
|
+
this.height = height;
|
304
357
|
|
305
358
|
// reposition axis
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
changed += update(this.dom.axis.style, 'top', this.top + 'px');
|
313
|
-
}
|
314
|
-
|
315
|
-
this._updateConversion();
|
316
|
-
|
317
|
-
var me = this,
|
318
|
-
queue = this.queue,
|
319
|
-
itemsData = this.itemsData,
|
320
|
-
items = this.items,
|
321
|
-
dataOptions = {
|
322
|
-
// TODO: cleanup
|
323
|
-
// fields: [(itemsData && itemsData.fieldId || 'id'), 'start', 'end', 'content', 'type', 'className']
|
324
|
-
};
|
325
|
-
|
326
|
-
// show/hide added/changed/removed items
|
327
|
-
for (var id in queue) {
|
328
|
-
if (queue.hasOwnProperty(id)) {
|
329
|
-
var entry = queue[id],
|
330
|
-
item = items[id],
|
331
|
-
action = entry.action;
|
332
|
-
|
333
|
-
//noinspection FallthroughInSwitchStatementJS
|
334
|
-
switch (action) {
|
335
|
-
case 'add':
|
336
|
-
case 'update':
|
337
|
-
var itemData = itemsData && itemsData.get(id, dataOptions);
|
338
|
-
|
339
|
-
if (itemData) {
|
340
|
-
var type = itemData.type ||
|
341
|
-
(itemData.start && itemData.end && 'range') ||
|
342
|
-
options.type ||
|
343
|
-
'box';
|
344
|
-
var constructor = ItemSet.types[type];
|
345
|
-
|
346
|
-
// TODO: how to handle items with invalid data? hide them and give a warning? or throw an error?
|
347
|
-
if (item) {
|
348
|
-
// update item
|
349
|
-
if (!constructor || !(item instanceof constructor)) {
|
350
|
-
// item type has changed, hide and delete the item
|
351
|
-
changed += item.hide();
|
352
|
-
item = null;
|
353
|
-
}
|
354
|
-
else {
|
355
|
-
item.data = itemData; // TODO: create a method item.setData ?
|
356
|
-
changed++;
|
357
|
-
}
|
358
|
-
}
|
359
|
-
|
360
|
-
if (!item) {
|
361
|
-
// create item
|
362
|
-
if (constructor) {
|
363
|
-
item = new constructor(me, itemData, options, defaultOptions);
|
364
|
-
item.id = entry.id; // we take entry.id, as id itself is stringified
|
365
|
-
changed++;
|
366
|
-
}
|
367
|
-
else {
|
368
|
-
throw new TypeError('Unknown item type "' + type + '"');
|
369
|
-
}
|
370
|
-
}
|
371
|
-
|
372
|
-
// force a repaint (not only a reposition)
|
373
|
-
item.repaint();
|
374
|
-
|
375
|
-
items[id] = item;
|
376
|
-
}
|
359
|
+
this.dom.axis.style.left = asSize(options.left, '0');
|
360
|
+
this.dom.axis.style.right = asSize(options.right, '');
|
361
|
+
this.dom.axis.style.width = asSize(options.width, '100%');
|
362
|
+
this.dom.axis.style.height = asSize(0);
|
363
|
+
this.dom.axis.style.top = asSize((orientation == 'top') ? '0' : '');
|
364
|
+
this.dom.axis.style.bottom = asSize((orientation == 'top') ? '' : '0');
|
377
365
|
|
378
|
-
|
379
|
-
|
380
|
-
break;
|
366
|
+
// check if this component is resized
|
367
|
+
resized = this._isResized() || resized;
|
381
368
|
|
382
|
-
|
383
|
-
|
384
|
-
// remove the item from the set selected items
|
385
|
-
if (item.selected) {
|
386
|
-
me._deselect(id);
|
387
|
-
}
|
369
|
+
return resized;
|
370
|
+
};
|
388
371
|
|
389
|
-
|
390
|
-
|
391
|
-
|
372
|
+
/**
|
373
|
+
* Get the first group, aligned with the axis
|
374
|
+
* @return {Group | null} firstGroup
|
375
|
+
* @private
|
376
|
+
*/
|
377
|
+
ItemSet.prototype._firstGroup = function _firstGroup() {
|
378
|
+
var firstGroupIndex = (this.options.orientation == 'top') ? 0 : (this.groupIds.length - 1);
|
379
|
+
var firstGroupId = this.groupIds[firstGroupIndex];
|
380
|
+
var firstGroup = this.groups[firstGroupId] || this.groups[UNGROUPED];
|
392
381
|
|
393
|
-
|
394
|
-
|
395
|
-
delete queue[id];
|
396
|
-
break;
|
382
|
+
return firstGroup || null;
|
383
|
+
};
|
397
384
|
|
398
|
-
|
399
|
-
|
400
|
-
|
385
|
+
/**
|
386
|
+
* Create or delete the group holding all ungrouped items. This group is used when
|
387
|
+
* there are no groups specified.
|
388
|
+
* @protected
|
389
|
+
*/
|
390
|
+
ItemSet.prototype._updateUngrouped = function _updateUngrouped() {
|
391
|
+
var ungrouped = this.groups[UNGROUPED];
|
392
|
+
|
393
|
+
if (this.groupsData) {
|
394
|
+
// remove the group holding all ungrouped items
|
395
|
+
if (ungrouped) {
|
396
|
+
ungrouped.hide();
|
397
|
+
delete this.groups[UNGROUPED];
|
401
398
|
}
|
402
399
|
}
|
400
|
+
else {
|
401
|
+
// create a group holding all (unfiltered) items
|
402
|
+
if (!ungrouped) {
|
403
|
+
var id = null;
|
404
|
+
var data = null;
|
405
|
+
ungrouped = new Group(id, data, this);
|
406
|
+
this.groups[UNGROUPED] = ungrouped;
|
407
|
+
|
408
|
+
for (var itemId in this.items) {
|
409
|
+
if (this.items.hasOwnProperty(itemId)) {
|
410
|
+
ungrouped.add(this.items[itemId]);
|
411
|
+
}
|
412
|
+
}
|
403
413
|
|
404
|
-
|
405
|
-
util.forEach(this.items, function (item) {
|
406
|
-
if (item.visible) {
|
407
|
-
changed += item.show();
|
408
|
-
item.reposition();
|
414
|
+
ungrouped.show();
|
409
415
|
}
|
410
|
-
|
411
|
-
changed += item.hide();
|
412
|
-
}
|
413
|
-
});
|
414
|
-
|
415
|
-
return (changed > 0);
|
416
|
+
}
|
416
417
|
};
|
417
418
|
|
418
419
|
/**
|
@@ -440,87 +441,11 @@ ItemSet.prototype.getAxis = function getAxis() {
|
|
440
441
|
};
|
441
442
|
|
442
443
|
/**
|
443
|
-
*
|
444
|
-
* @return {
|
445
|
-
*/
|
446
|
-
ItemSet.prototype.reflow = function reflow () {
|
447
|
-
var changed = 0,
|
448
|
-
options = this.options,
|
449
|
-
marginAxis = (options.margin && 'axis' in options.margin) ? options.margin.axis : this.defaultOptions.margin.axis,
|
450
|
-
marginItem = (options.margin && 'item' in options.margin) ? options.margin.item : this.defaultOptions.margin.item,
|
451
|
-
update = util.updateProperty,
|
452
|
-
asNumber = util.option.asNumber,
|
453
|
-
asSize = util.option.asSize,
|
454
|
-
frame = this.frame;
|
455
|
-
|
456
|
-
if (frame) {
|
457
|
-
this._updateConversion();
|
458
|
-
|
459
|
-
util.forEach(this.items, function (item) {
|
460
|
-
changed += item.reflow();
|
461
|
-
});
|
462
|
-
|
463
|
-
// TODO: stack.update should be triggered via an event, in stack itself
|
464
|
-
// TODO: only update the stack when there are changed items
|
465
|
-
this.stack.update();
|
466
|
-
|
467
|
-
var maxHeight = asNumber(options.maxHeight);
|
468
|
-
var fixedHeight = (asSize(options.height) != null);
|
469
|
-
var height;
|
470
|
-
if (fixedHeight) {
|
471
|
-
height = frame.offsetHeight;
|
472
|
-
}
|
473
|
-
else {
|
474
|
-
// height is not specified, determine the height from the height and positioned items
|
475
|
-
var visibleItems = this.stack.ordered; // TODO: not so nice way to get the filtered items
|
476
|
-
if (visibleItems.length) {
|
477
|
-
var min = visibleItems[0].top;
|
478
|
-
var max = visibleItems[0].top + visibleItems[0].height;
|
479
|
-
util.forEach(visibleItems, function (item) {
|
480
|
-
min = Math.min(min, item.top);
|
481
|
-
max = Math.max(max, (item.top + item.height));
|
482
|
-
});
|
483
|
-
height = (max - min) + marginAxis + marginItem;
|
484
|
-
}
|
485
|
-
else {
|
486
|
-
height = marginAxis + marginItem;
|
487
|
-
}
|
488
|
-
}
|
489
|
-
if (maxHeight != null) {
|
490
|
-
height = Math.min(height, maxHeight);
|
491
|
-
}
|
492
|
-
changed += update(this, 'height', height);
|
493
|
-
|
494
|
-
// calculate height from items
|
495
|
-
changed += update(this, 'top', frame.offsetTop);
|
496
|
-
changed += update(this, 'left', frame.offsetLeft);
|
497
|
-
changed += update(this, 'width', frame.offsetWidth);
|
498
|
-
}
|
499
|
-
else {
|
500
|
-
changed += 1;
|
501
|
-
}
|
502
|
-
|
503
|
-
return (changed > 0);
|
504
|
-
};
|
505
|
-
|
506
|
-
/**
|
507
|
-
* Hide this component from the DOM
|
508
|
-
* @return {Boolean} changed
|
444
|
+
* Get the element for the labelset
|
445
|
+
* @return {HTMLElement} labelSet
|
509
446
|
*/
|
510
|
-
ItemSet.prototype.
|
511
|
-
|
512
|
-
|
513
|
-
// remove the DOM
|
514
|
-
if (this.frame && this.frame.parentNode) {
|
515
|
-
this.frame.parentNode.removeChild(this.frame);
|
516
|
-
changed = true;
|
517
|
-
}
|
518
|
-
if (this.dom.axis && this.dom.axis.parentNode) {
|
519
|
-
this.dom.axis.parentNode.removeChild(this.dom.axis);
|
520
|
-
changed = true;
|
521
|
-
}
|
522
|
-
|
523
|
-
return changed;
|
447
|
+
ItemSet.prototype.getLabelSet = function getLabelSet() {
|
448
|
+
return this.dom.labelSet;
|
524
449
|
};
|
525
450
|
|
526
451
|
/**
|
@@ -540,12 +465,12 @@ ItemSet.prototype.setItems = function setItems(items) {
|
|
540
465
|
this.itemsData = items;
|
541
466
|
}
|
542
467
|
else {
|
543
|
-
throw new TypeError('Data must be an instance of DataSet');
|
468
|
+
throw new TypeError('Data must be an instance of DataSet or DataView');
|
544
469
|
}
|
545
470
|
|
546
471
|
if (oldItemsData) {
|
547
472
|
// unsubscribe from old dataset
|
548
|
-
util.forEach(this.
|
473
|
+
util.forEach(this.itemListeners, function (callback, event) {
|
549
474
|
oldItemsData.unsubscribe(event, callback);
|
550
475
|
});
|
551
476
|
|
@@ -557,24 +482,86 @@ ItemSet.prototype.setItems = function setItems(items) {
|
|
557
482
|
if (this.itemsData) {
|
558
483
|
// subscribe to new dataset
|
559
484
|
var id = this.id;
|
560
|
-
util.forEach(this.
|
485
|
+
util.forEach(this.itemListeners, function (callback, event) {
|
561
486
|
me.itemsData.on(event, callback, id);
|
562
487
|
});
|
563
488
|
|
564
|
-
//
|
489
|
+
// add all new items
|
565
490
|
ids = this.itemsData.getIds();
|
566
491
|
this._onAdd(ids);
|
492
|
+
|
493
|
+
// update the group holding all ungrouped items
|
494
|
+
this._updateUngrouped();
|
567
495
|
}
|
568
496
|
};
|
569
497
|
|
570
498
|
/**
|
571
|
-
* Get the current items
|
499
|
+
* Get the current items
|
572
500
|
* @returns {vis.DataSet | null}
|
573
501
|
*/
|
574
502
|
ItemSet.prototype.getItems = function getItems() {
|
575
503
|
return this.itemsData;
|
576
504
|
};
|
577
505
|
|
506
|
+
/**
|
507
|
+
* Set groups
|
508
|
+
* @param {vis.DataSet} groups
|
509
|
+
*/
|
510
|
+
ItemSet.prototype.setGroups = function setGroups(groups) {
|
511
|
+
var me = this,
|
512
|
+
ids;
|
513
|
+
|
514
|
+
// unsubscribe from current dataset
|
515
|
+
if (this.groupsData) {
|
516
|
+
util.forEach(this.groupListeners, function (callback, event) {
|
517
|
+
me.groupsData.unsubscribe(event, callback);
|
518
|
+
});
|
519
|
+
|
520
|
+
// remove all drawn groups
|
521
|
+
ids = this.groupsData.getIds();
|
522
|
+
this._onRemoveGroups(ids);
|
523
|
+
}
|
524
|
+
|
525
|
+
// replace the dataset
|
526
|
+
if (!groups) {
|
527
|
+
this.groupsData = null;
|
528
|
+
}
|
529
|
+
else if (groups instanceof DataSet || groups instanceof DataView) {
|
530
|
+
this.groupsData = groups;
|
531
|
+
}
|
532
|
+
else {
|
533
|
+
throw new TypeError('Data must be an instance of DataSet or DataView');
|
534
|
+
}
|
535
|
+
|
536
|
+
if (this.groupsData) {
|
537
|
+
// subscribe to new dataset
|
538
|
+
var id = this.id;
|
539
|
+
util.forEach(this.groupListeners, function (callback, event) {
|
540
|
+
me.groupsData.on(event, callback, id);
|
541
|
+
});
|
542
|
+
|
543
|
+
// draw all ms
|
544
|
+
ids = this.groupsData.getIds();
|
545
|
+
this._onAddGroups(ids);
|
546
|
+
}
|
547
|
+
|
548
|
+
// update the group holding all ungrouped items
|
549
|
+
this._updateUngrouped();
|
550
|
+
|
551
|
+
// update the order of all items in each group
|
552
|
+
this._order();
|
553
|
+
|
554
|
+
this.emit('change');
|
555
|
+
};
|
556
|
+
|
557
|
+
/**
|
558
|
+
* Get the current groups
|
559
|
+
* @returns {vis.DataSet | null} groups
|
560
|
+
*/
|
561
|
+
ItemSet.prototype.getGroups = function getGroups() {
|
562
|
+
return this.groupsData;
|
563
|
+
};
|
564
|
+
|
578
565
|
/**
|
579
566
|
* Remove an item by its id
|
580
567
|
* @param {String | Number} id
|
@@ -587,7 +574,9 @@ ItemSet.prototype.removeItem = function removeItem (id) {
|
|
587
574
|
// confirm deletion
|
588
575
|
this.options.onRemove(item, function (item) {
|
589
576
|
if (item) {
|
590
|
-
|
577
|
+
// remove by id here, it is possible that an item has no id defined
|
578
|
+
// itself, so better not delete by the item itself
|
579
|
+
dataset.remove(id);
|
591
580
|
}
|
592
581
|
});
|
593
582
|
}
|
@@ -596,94 +585,307 @@ ItemSet.prototype.removeItem = function removeItem (id) {
|
|
596
585
|
/**
|
597
586
|
* Handle updated items
|
598
587
|
* @param {Number[]} ids
|
599
|
-
* @
|
588
|
+
* @protected
|
600
589
|
*/
|
601
590
|
ItemSet.prototype._onUpdate = function _onUpdate(ids) {
|
602
|
-
this
|
591
|
+
var me = this,
|
592
|
+
items = this.items,
|
593
|
+
itemOptions = this.itemOptions;
|
594
|
+
|
595
|
+
ids.forEach(function (id) {
|
596
|
+
var itemData = me.itemsData.get(id),
|
597
|
+
item = items[id],
|
598
|
+
type = itemData.type ||
|
599
|
+
(itemData.start && itemData.end && 'range') ||
|
600
|
+
me.options.type ||
|
601
|
+
'box';
|
602
|
+
|
603
|
+
var constructor = ItemSet.types[type];
|
604
|
+
|
605
|
+
if (item) {
|
606
|
+
// update item
|
607
|
+
if (!constructor || !(item instanceof constructor)) {
|
608
|
+
// item type has changed, delete the item and recreate it
|
609
|
+
me._removeItem(item);
|
610
|
+
item = null;
|
611
|
+
}
|
612
|
+
else {
|
613
|
+
me._updateItem(item, itemData);
|
614
|
+
}
|
615
|
+
}
|
616
|
+
|
617
|
+
if (!item) {
|
618
|
+
// create item
|
619
|
+
if (constructor) {
|
620
|
+
item = new constructor(itemData, me.options, itemOptions);
|
621
|
+
item.id = id; // TODO: not so nice setting id afterwards
|
622
|
+
me._addItem(item);
|
623
|
+
}
|
624
|
+
else {
|
625
|
+
throw new TypeError('Unknown item type "' + type + '"');
|
626
|
+
}
|
627
|
+
}
|
628
|
+
});
|
629
|
+
|
630
|
+
this._order();
|
631
|
+
this.stackDirty = true; // force re-stacking of all items next repaint
|
632
|
+
this.emit('change');
|
603
633
|
};
|
604
634
|
|
605
635
|
/**
|
606
|
-
* Handle
|
636
|
+
* Handle added items
|
637
|
+
* @param {Number[]} ids
|
638
|
+
* @protected
|
639
|
+
*/
|
640
|
+
ItemSet.prototype._onAdd = ItemSet.prototype._onUpdate;
|
641
|
+
|
642
|
+
/**
|
643
|
+
* Handle removed items
|
607
644
|
* @param {Number[]} ids
|
645
|
+
* @protected
|
646
|
+
*/
|
647
|
+
ItemSet.prototype._onRemove = function _onRemove(ids) {
|
648
|
+
var count = 0;
|
649
|
+
var me = this;
|
650
|
+
ids.forEach(function (id) {
|
651
|
+
var item = me.items[id];
|
652
|
+
if (item) {
|
653
|
+
count++;
|
654
|
+
me._removeItem(item);
|
655
|
+
}
|
656
|
+
});
|
657
|
+
|
658
|
+
if (count) {
|
659
|
+
// update order
|
660
|
+
this._order();
|
661
|
+
this.stackDirty = true; // force re-stacking of all items next repaint
|
662
|
+
this.emit('change');
|
663
|
+
}
|
664
|
+
};
|
665
|
+
|
666
|
+
/**
|
667
|
+
* Update the order of item in all groups
|
608
668
|
* @private
|
609
669
|
*/
|
610
|
-
ItemSet.prototype.
|
611
|
-
|
670
|
+
ItemSet.prototype._order = function _order() {
|
671
|
+
// reorder the items in all groups
|
672
|
+
// TODO: optimization: only reorder groups affected by the changed items
|
673
|
+
util.forEach(this.groups, function (group) {
|
674
|
+
group.order();
|
675
|
+
});
|
612
676
|
};
|
613
677
|
|
614
678
|
/**
|
615
|
-
* Handle
|
679
|
+
* Handle updated groups
|
616
680
|
* @param {Number[]} ids
|
617
681
|
* @private
|
618
682
|
*/
|
619
|
-
ItemSet.prototype.
|
620
|
-
this.
|
683
|
+
ItemSet.prototype._onUpdateGroups = function _onUpdateGroups(ids) {
|
684
|
+
this._onAddGroups(ids);
|
621
685
|
};
|
622
686
|
|
623
687
|
/**
|
624
|
-
*
|
625
|
-
* @param {String} action can be 'add', 'update', 'remove'
|
688
|
+
* Handle changed groups
|
626
689
|
* @param {Number[]} ids
|
690
|
+
* @private
|
627
691
|
*/
|
628
|
-
ItemSet.prototype.
|
629
|
-
var
|
692
|
+
ItemSet.prototype._onAddGroups = function _onAddGroups(ids) {
|
693
|
+
var me = this;
|
694
|
+
|
630
695
|
ids.forEach(function (id) {
|
631
|
-
|
632
|
-
|
633
|
-
|
634
|
-
|
696
|
+
var groupData = me.groupsData.get(id);
|
697
|
+
var group = me.groups[id];
|
698
|
+
|
699
|
+
if (!group) {
|
700
|
+
// check for reserved ids
|
701
|
+
if (id == UNGROUPED) {
|
702
|
+
throw new Error('Illegal group id. ' + id + ' is a reserved id.');
|
703
|
+
}
|
704
|
+
|
705
|
+
var groupOptions = Object.create(me.options);
|
706
|
+
util.extend(groupOptions, {
|
707
|
+
height: null
|
708
|
+
});
|
709
|
+
|
710
|
+
group = new Group(id, groupData, me);
|
711
|
+
me.groups[id] = group;
|
712
|
+
|
713
|
+
// add items with this groupId to the new group
|
714
|
+
for (var itemId in me.items) {
|
715
|
+
if (me.items.hasOwnProperty(itemId)) {
|
716
|
+
var item = me.items[itemId];
|
717
|
+
if (item.data.group == id) {
|
718
|
+
group.add(item);
|
719
|
+
}
|
720
|
+
}
|
721
|
+
}
|
722
|
+
|
723
|
+
group.order();
|
724
|
+
group.show();
|
725
|
+
}
|
726
|
+
else {
|
727
|
+
// update group
|
728
|
+
group.setData(groupData);
|
729
|
+
}
|
635
730
|
});
|
636
731
|
|
637
|
-
|
638
|
-
//this.requestReflow();
|
639
|
-
this.requestRepaint();
|
640
|
-
}
|
732
|
+
this.emit('change');
|
641
733
|
};
|
642
734
|
|
643
735
|
/**
|
644
|
-
*
|
645
|
-
*
|
646
|
-
* After the method _updateConversion is executed once, the methods toTime
|
647
|
-
* and toScreen can be used.
|
736
|
+
* Handle removed groups
|
737
|
+
* @param {Number[]} ids
|
648
738
|
* @private
|
649
739
|
*/
|
650
|
-
ItemSet.prototype.
|
651
|
-
var
|
652
|
-
|
653
|
-
|
654
|
-
|
740
|
+
ItemSet.prototype._onRemoveGroups = function _onRemoveGroups(ids) {
|
741
|
+
var groups = this.groups;
|
742
|
+
ids.forEach(function (id) {
|
743
|
+
var group = groups[id];
|
744
|
+
|
745
|
+
if (group) {
|
746
|
+
group.hide();
|
747
|
+
delete groups[id];
|
748
|
+
}
|
749
|
+
});
|
750
|
+
|
751
|
+
this.markDirty();
|
655
752
|
|
656
|
-
|
657
|
-
|
753
|
+
this.emit('change');
|
754
|
+
};
|
755
|
+
|
756
|
+
/**
|
757
|
+
* Reorder the groups if needed
|
758
|
+
* @return {boolean} changed
|
759
|
+
* @private
|
760
|
+
*/
|
761
|
+
ItemSet.prototype._orderGroups = function () {
|
762
|
+
if (this.groupsData) {
|
763
|
+
// reorder the groups
|
764
|
+
var groupIds = this.groupsData.getIds({
|
765
|
+
order: this.options.groupOrder
|
766
|
+
});
|
767
|
+
|
768
|
+
var changed = !util.equalArray(groupIds, this.groupIds);
|
769
|
+
if (changed) {
|
770
|
+
// hide all groups, removes them from the DOM
|
771
|
+
var groups = this.groups;
|
772
|
+
groupIds.forEach(function (groupId) {
|
773
|
+
var group = groups[groupId];
|
774
|
+
group.hide();
|
775
|
+
});
|
776
|
+
|
777
|
+
// show the groups again, attach them to the DOM in correct order
|
778
|
+
groupIds.forEach(function (groupId) {
|
779
|
+
groups[groupId].show();
|
780
|
+
});
|
781
|
+
|
782
|
+
this.groupIds = groupIds;
|
783
|
+
}
|
784
|
+
|
785
|
+
return changed;
|
658
786
|
}
|
659
787
|
else {
|
660
|
-
|
788
|
+
return false;
|
661
789
|
}
|
662
790
|
};
|
663
791
|
|
664
792
|
/**
|
665
|
-
*
|
666
|
-
*
|
667
|
-
*
|
668
|
-
* @param {int} x Position on the screen in pixels
|
669
|
-
* @return {Date} time The datetime the corresponds with given position x
|
793
|
+
* Add a new item
|
794
|
+
* @param {Item} item
|
795
|
+
* @private
|
670
796
|
*/
|
671
|
-
ItemSet.prototype.
|
672
|
-
|
673
|
-
|
797
|
+
ItemSet.prototype._addItem = function _addItem(item) {
|
798
|
+
this.items[item.id] = item;
|
799
|
+
|
800
|
+
// add to group
|
801
|
+
var groupId = this.groupsData ? item.data.group : UNGROUPED;
|
802
|
+
var group = this.groups[groupId];
|
803
|
+
if (group) group.add(item);
|
674
804
|
};
|
675
805
|
|
676
806
|
/**
|
677
|
-
*
|
678
|
-
*
|
679
|
-
*
|
680
|
-
* @
|
681
|
-
* @return {int} x The position on the screen in pixels which corresponds
|
682
|
-
* with the given date.
|
807
|
+
* Update an existing item
|
808
|
+
* @param {Item} item
|
809
|
+
* @param {Object} itemData
|
810
|
+
* @private
|
683
811
|
*/
|
684
|
-
ItemSet.prototype.
|
685
|
-
var
|
686
|
-
|
812
|
+
ItemSet.prototype._updateItem = function _updateItem(item, itemData) {
|
813
|
+
var oldGroupId = item.data.group;
|
814
|
+
|
815
|
+
item.data = itemData;
|
816
|
+
item.repaint();
|
817
|
+
|
818
|
+
// update group
|
819
|
+
if (oldGroupId != item.data.group) {
|
820
|
+
var oldGroup = this.groups[oldGroupId];
|
821
|
+
if (oldGroup) oldGroup.remove(item);
|
822
|
+
|
823
|
+
var groupId = this.groupsData ? item.data.group : UNGROUPED;
|
824
|
+
var group = this.groups[groupId];
|
825
|
+
if (group) group.add(item);
|
826
|
+
}
|
827
|
+
};
|
828
|
+
|
829
|
+
/**
|
830
|
+
* Delete an item from the ItemSet: remove it from the DOM, from the map
|
831
|
+
* with items, and from the map with visible items, and from the selection
|
832
|
+
* @param {Item} item
|
833
|
+
* @private
|
834
|
+
*/
|
835
|
+
ItemSet.prototype._removeItem = function _removeItem(item) {
|
836
|
+
// remove from DOM
|
837
|
+
item.hide();
|
838
|
+
|
839
|
+
// remove from items
|
840
|
+
delete this.items[item.id];
|
841
|
+
|
842
|
+
// remove from selection
|
843
|
+
var index = this.selection.indexOf(item.id);
|
844
|
+
if (index != -1) this.selection.splice(index, 1);
|
845
|
+
|
846
|
+
// remove from group
|
847
|
+
var groupId = this.groupsData ? item.data.group : UNGROUPED;
|
848
|
+
var group = this.groups[groupId];
|
849
|
+
if (group) group.remove(item);
|
850
|
+
};
|
851
|
+
|
852
|
+
/**
|
853
|
+
* Create an array containing all items being a range (having an end date)
|
854
|
+
* @param array
|
855
|
+
* @returns {Array}
|
856
|
+
* @private
|
857
|
+
*/
|
858
|
+
ItemSet.prototype._constructByEndArray = function _constructByEndArray(array) {
|
859
|
+
var endArray = [];
|
860
|
+
|
861
|
+
for (var i = 0; i < array.length; i++) {
|
862
|
+
if (array[i] instanceof ItemRange) {
|
863
|
+
endArray.push(array[i]);
|
864
|
+
}
|
865
|
+
}
|
866
|
+
return endArray;
|
867
|
+
};
|
868
|
+
|
869
|
+
/**
|
870
|
+
* Get the width of the group labels
|
871
|
+
* @return {Number} width
|
872
|
+
*/
|
873
|
+
ItemSet.prototype.getLabelsWidth = function getLabelsWidth() {
|
874
|
+
var width = 0;
|
875
|
+
|
876
|
+
util.forEach(this.groups, function (group) {
|
877
|
+
width = Math.max(width, group.getLabelWidth());
|
878
|
+
});
|
879
|
+
|
880
|
+
return width;
|
881
|
+
};
|
882
|
+
|
883
|
+
/**
|
884
|
+
* Get the height of the itemsets background
|
885
|
+
* @return {Number} height
|
886
|
+
*/
|
887
|
+
ItemSet.prototype.getBackgroundHeight = function getBackgroundHeight() {
|
888
|
+
return this.height;
|
687
889
|
};
|
688
890
|
|
689
891
|
/**
|
@@ -692,28 +894,45 @@ ItemSet.prototype.toScreen = function toScreen(time) {
|
|
692
894
|
* @private
|
693
895
|
*/
|
694
896
|
ItemSet.prototype._onDragStart = function (event) {
|
695
|
-
if (!this.options.editable) {
|
897
|
+
if (!this.options.editable.updateTime && !this.options.editable.updateGroup) {
|
696
898
|
return;
|
697
899
|
}
|
698
900
|
|
699
901
|
var item = ItemSet.itemFromTarget(event),
|
700
|
-
me = this
|
902
|
+
me = this,
|
903
|
+
props;
|
701
904
|
|
702
905
|
if (item && item.selected) {
|
703
906
|
var dragLeftItem = event.target.dragLeftItem;
|
704
907
|
var dragRightItem = event.target.dragRightItem;
|
705
908
|
|
706
909
|
if (dragLeftItem) {
|
707
|
-
|
708
|
-
item: dragLeftItem
|
709
|
-
|
710
|
-
|
910
|
+
props = {
|
911
|
+
item: dragLeftItem
|
912
|
+
};
|
913
|
+
|
914
|
+
if (me.options.editable.updateTime) {
|
915
|
+
props.start = item.data.start.valueOf();
|
916
|
+
}
|
917
|
+
if (me.options.editable.updateGroup) {
|
918
|
+
if ('group' in item.data) props.group = item.data.group;
|
919
|
+
}
|
920
|
+
|
921
|
+
this.touchParams.itemProps = [props];
|
711
922
|
}
|
712
923
|
else if (dragRightItem) {
|
713
|
-
|
714
|
-
item: dragRightItem
|
715
|
-
|
716
|
-
|
924
|
+
props = {
|
925
|
+
item: dragRightItem
|
926
|
+
};
|
927
|
+
|
928
|
+
if (me.options.editable.updateTime) {
|
929
|
+
props.end = item.data.end.valueOf();
|
930
|
+
}
|
931
|
+
if (me.options.editable.updateGroup) {
|
932
|
+
if ('group' in item.data) props.group = item.data.group;
|
933
|
+
}
|
934
|
+
|
935
|
+
this.touchParams.itemProps = [props];
|
717
936
|
}
|
718
937
|
else {
|
719
938
|
this.touchParams.itemProps = this.getSelection().map(function (id) {
|
@@ -722,11 +941,12 @@ ItemSet.prototype._onDragStart = function (event) {
|
|
722
941
|
item: item
|
723
942
|
};
|
724
943
|
|
725
|
-
if (
|
726
|
-
props.start = item.data.start.valueOf()
|
944
|
+
if (me.options.editable.updateTime) {
|
945
|
+
if ('start' in item.data) props.start = item.data.start.valueOf();
|
946
|
+
if ('end' in item.data) props.end = item.data.end.valueOf();
|
727
947
|
}
|
728
|
-
if (
|
729
|
-
props.
|
948
|
+
if (me.options.editable.updateGroup) {
|
949
|
+
if ('group' in item.data) props.group = item.data.group;
|
730
950
|
}
|
731
951
|
|
732
952
|
return props;
|
@@ -746,7 +966,8 @@ ItemSet.prototype._onDrag = function (event) {
|
|
746
966
|
if (this.touchParams.itemProps) {
|
747
967
|
var snap = this.options.snap || null,
|
748
968
|
deltaX = event.gesture.deltaX,
|
749
|
-
|
969
|
+
scale = (this.width / (this.range.end - this.range.start)),
|
970
|
+
offset = deltaX / scale;
|
750
971
|
|
751
972
|
// move
|
752
973
|
this.touchParams.itemProps.forEach(function (props) {
|
@@ -754,17 +975,31 @@ ItemSet.prototype._onDrag = function (event) {
|
|
754
975
|
var start = new Date(props.start + offset);
|
755
976
|
props.item.data.start = snap ? snap(start) : start;
|
756
977
|
}
|
978
|
+
|
757
979
|
if ('end' in props) {
|
758
980
|
var end = new Date(props.end + offset);
|
759
981
|
props.item.data.end = snap ? snap(end) : end;
|
760
982
|
}
|
983
|
+
|
984
|
+
if ('group' in props) {
|
985
|
+
// drag from one group to another
|
986
|
+
var group = ItemSet.groupFromTarget(event);
|
987
|
+
if (group && group.groupId != props.item.data.group) {
|
988
|
+
var oldGroup = props.item.parent;
|
989
|
+
oldGroup.remove(props.item);
|
990
|
+
oldGroup.order();
|
991
|
+
group.add(props.item);
|
992
|
+
group.order();
|
993
|
+
|
994
|
+
props.item.data.group = group.groupId;
|
995
|
+
}
|
996
|
+
}
|
761
997
|
});
|
762
998
|
|
763
999
|
// TODO: implement onMoving handler
|
764
1000
|
|
765
|
-
//
|
766
|
-
|
767
|
-
this.requestReflow();
|
1001
|
+
this.stackDirty = true; // force re-stacking of all items next repaint
|
1002
|
+
this.emit('change');
|
768
1003
|
|
769
1004
|
event.stopPropagation();
|
770
1005
|
}
|
@@ -780,35 +1015,41 @@ ItemSet.prototype._onDragEnd = function (event) {
|
|
780
1015
|
// prepare a change set for the changed items
|
781
1016
|
var changes = [],
|
782
1017
|
me = this,
|
783
|
-
dataset = this._myDataSet()
|
784
|
-
type;
|
1018
|
+
dataset = this._myDataSet();
|
785
1019
|
|
786
1020
|
this.touchParams.itemProps.forEach(function (props) {
|
787
1021
|
var id = props.item.id,
|
788
|
-
|
1022
|
+
itemData = me.itemsData.get(id);
|
789
1023
|
|
790
1024
|
var changed = false;
|
791
1025
|
if ('start' in props.item.data) {
|
792
1026
|
changed = (props.start != props.item.data.start.valueOf());
|
793
|
-
|
1027
|
+
itemData.start = util.convert(props.item.data.start, dataset.convert['start']);
|
794
1028
|
}
|
795
1029
|
if ('end' in props.item.data) {
|
796
1030
|
changed = changed || (props.end != props.item.data.end.valueOf());
|
797
|
-
|
1031
|
+
itemData.end = util.convert(props.item.data.end, dataset.convert['end']);
|
1032
|
+
}
|
1033
|
+
if ('group' in props.item.data) {
|
1034
|
+
changed = changed || (props.group != props.item.data.group);
|
1035
|
+
itemData.group = props.item.data.group;
|
798
1036
|
}
|
799
1037
|
|
800
1038
|
// only apply changes when start or end is actually changed
|
801
1039
|
if (changed) {
|
802
|
-
me.options.onMove(
|
803
|
-
if (
|
1040
|
+
me.options.onMove(itemData, function (itemData) {
|
1041
|
+
if (itemData) {
|
804
1042
|
// apply changes
|
805
|
-
|
1043
|
+
itemData[dataset.fieldId] = id; // ensure the item contains its id (can be undefined)
|
1044
|
+
changes.push(itemData);
|
806
1045
|
}
|
807
1046
|
else {
|
808
1047
|
// restore original values
|
809
1048
|
if ('start' in props) props.item.data.start = props.start;
|
810
1049
|
if ('end' in props) props.item.data.end = props.end;
|
811
|
-
|
1050
|
+
|
1051
|
+
me.stackDirty = true; // force re-stacking of all items next repaint
|
1052
|
+
me.emit('change');
|
812
1053
|
}
|
813
1054
|
});
|
814
1055
|
}
|
@@ -842,6 +1083,24 @@ ItemSet.itemFromTarget = function itemFromTarget (event) {
|
|
842
1083
|
return null;
|
843
1084
|
};
|
844
1085
|
|
1086
|
+
/**
|
1087
|
+
* Find the Group from an event target:
|
1088
|
+
* searches for the attribute 'timeline-group' in the event target's element tree
|
1089
|
+
* @param {Event} event
|
1090
|
+
* @return {Group | null} group
|
1091
|
+
*/
|
1092
|
+
ItemSet.groupFromTarget = function groupFromTarget (event) {
|
1093
|
+
var target = event.target;
|
1094
|
+
while (target) {
|
1095
|
+
if (target.hasOwnProperty('timeline-group')) {
|
1096
|
+
return target['timeline-group'];
|
1097
|
+
}
|
1098
|
+
target = target.parentNode;
|
1099
|
+
}
|
1100
|
+
|
1101
|
+
return null;
|
1102
|
+
};
|
1103
|
+
|
845
1104
|
/**
|
846
1105
|
* Find the ItemSet from an event target:
|
847
1106
|
* searches for the attribute 'timeline-itemset' in the event target's element tree
|