vis-rails 0.0.4 → 0.0.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -13
- data/lib/vis/rails/version.rb +1 -1
- data/vendor/assets/component/emitter.js +162 -0
- data/vendor/assets/javascripts/vis.js +1 -0
- data/vendor/assets/vis/DataSet.js +8 -2
- data/vendor/assets/vis/DataView.js +8 -4
- data/vendor/assets/vis/graph/Edge.js +210 -78
- data/vendor/assets/vis/graph/Graph.js +474 -652
- data/vendor/assets/vis/graph/Node.js +119 -82
- data/vendor/assets/vis/graph/css/graph-manipulation.css +128 -0
- data/vendor/assets/vis/graph/css/graph-navigation.css +62 -0
- data/vendor/assets/vis/graph/graphMixins/ClusterMixin.js +1141 -0
- data/vendor/assets/vis/graph/graphMixins/HierarchicalLayoutMixin.js +296 -0
- data/vendor/assets/vis/graph/graphMixins/ManipulationMixin.js +433 -0
- data/vendor/assets/vis/graph/graphMixins/MixinLoader.js +201 -0
- data/vendor/assets/vis/graph/graphMixins/NavigationMixin.js +173 -0
- data/vendor/assets/vis/graph/graphMixins/SectorsMixin.js +552 -0
- data/vendor/assets/vis/graph/graphMixins/SelectionMixin.js +558 -0
- data/vendor/assets/vis/graph/graphMixins/physics/BarnesHut.js +373 -0
- data/vendor/assets/vis/graph/graphMixins/physics/HierarchialRepulsion.js +64 -0
- data/vendor/assets/vis/graph/graphMixins/physics/PhysicsMixin.js +513 -0
- data/vendor/assets/vis/graph/graphMixins/physics/Repulsion.js +66 -0
- 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/rightArrow.png +0 -0
- data/vendor/assets/vis/graph/img/upArrow.png +0 -0
- data/vendor/assets/vis/module/exports.js +0 -2
- data/vendor/assets/vis/module/header.js +2 -2
- data/vendor/assets/vis/module/imports.js +1 -2
- data/vendor/assets/vis/timeline/Controller.js +56 -45
- data/vendor/assets/vis/timeline/Range.js +68 -62
- data/vendor/assets/vis/timeline/Stack.js +11 -13
- data/vendor/assets/vis/timeline/TimeStep.js +43 -38
- data/vendor/assets/vis/timeline/Timeline.js +215 -93
- data/vendor/assets/vis/timeline/component/Component.js +19 -3
- data/vendor/assets/vis/timeline/component/CurrentTime.js +1 -1
- data/vendor/assets/vis/timeline/component/CustomTime.js +39 -120
- data/vendor/assets/vis/timeline/component/GroupSet.js +35 -1
- data/vendor/assets/vis/timeline/component/ItemSet.js +272 -9
- data/vendor/assets/vis/timeline/component/RootPanel.js +59 -47
- data/vendor/assets/vis/timeline/component/TimeAxis.js +10 -0
- data/vendor/assets/vis/timeline/component/css/item.css +53 -22
- data/vendor/assets/vis/timeline/component/item/Item.js +40 -5
- data/vendor/assets/vis/timeline/component/item/ItemBox.js +3 -1
- data/vendor/assets/vis/timeline/component/item/ItemPoint.js +3 -1
- data/vendor/assets/vis/timeline/component/item/ItemRange.js +67 -3
- data/vendor/assets/vis/timeline/component/item/ItemRangeOverflow.js +37 -9
- data/vendor/assets/vis/timeline/img/delete.png +0 -0
- data/vendor/assets/vis/util.js +169 -30
- metadata +39 -12
@@ -1,11 +1,11 @@
|
|
1
1
|
/**
|
2
2
|
* @constructor Stack
|
3
3
|
* Stacks items on top of each other.
|
4
|
-
* @param {ItemSet}
|
4
|
+
* @param {ItemSet} itemset
|
5
5
|
* @param {Object} [options]
|
6
6
|
*/
|
7
|
-
function Stack (
|
8
|
-
this.
|
7
|
+
function Stack (itemset, options) {
|
8
|
+
this.itemset = itemset;
|
9
9
|
|
10
10
|
this.options = options || {};
|
11
11
|
this.defaultOptions = {
|
@@ -43,14 +43,14 @@ function Stack (parent, options) {
|
|
43
43
|
/**
|
44
44
|
* Set options for the stack
|
45
45
|
* @param {Object} options Available options:
|
46
|
-
* {ItemSet}
|
46
|
+
* {ItemSet} itemset
|
47
47
|
* {Number} margin
|
48
48
|
* {function} order Stacking order
|
49
49
|
*/
|
50
50
|
Stack.prototype.setOptions = function setOptions (options) {
|
51
51
|
util.extend(this.options, options);
|
52
52
|
|
53
|
-
// TODO: register on data changes at the connected
|
53
|
+
// TODO: register on data changes at the connected itemset, and update the changed part only and immediately
|
54
54
|
};
|
55
55
|
|
56
56
|
/**
|
@@ -63,16 +63,14 @@ Stack.prototype.update = function update() {
|
|
63
63
|
};
|
64
64
|
|
65
65
|
/**
|
66
|
-
* Order the items.
|
67
|
-
*
|
68
|
-
* If a custom order function has been provided via the options, then this will
|
69
|
-
* be used.
|
66
|
+
* Order the items. If a custom order function has been provided via the options,
|
67
|
+
* then this will be used.
|
70
68
|
* @private
|
71
69
|
*/
|
72
70
|
Stack.prototype._order = function _order () {
|
73
|
-
var items = this.
|
71
|
+
var items = this.itemset.items;
|
74
72
|
if (!items) {
|
75
|
-
throw new Error('Cannot stack items:
|
73
|
+
throw new Error('Cannot stack items: ItemSet does not contain items');
|
76
74
|
}
|
77
75
|
|
78
76
|
// TODO: store the sorted items, to have less work later on
|
@@ -185,8 +183,8 @@ Stack.prototype.checkOverlap = function checkOverlap (items, itemIndex,
|
|
185
183
|
* @return {boolean} true if a and b collide, else false
|
186
184
|
*/
|
187
185
|
Stack.prototype.collision = function collision (a, b, margin) {
|
188
|
-
return ((a.left - margin) < (b.left + b.
|
189
|
-
(a.left + a.
|
186
|
+
return ((a.left - margin) < (b.left + b.width) &&
|
187
|
+
(a.left + a.width + margin) > b.left &&
|
190
188
|
(a.top - margin) < (b.top + b.height) &&
|
191
189
|
(a.top + a.height + margin) > b.top);
|
192
190
|
};
|
@@ -281,35 +281,38 @@ TimeStep.prototype.setMinimumStep = function(minimumStep) {
|
|
281
281
|
};
|
282
282
|
|
283
283
|
/**
|
284
|
-
* Snap a date to a rounded value.
|
285
|
-
* current scale and step.
|
286
|
-
* @param {Date} date the date to be snapped
|
284
|
+
* Snap a date to a rounded value.
|
285
|
+
* The snap intervals are dependent on the current scale and step.
|
286
|
+
* @param {Date} date the date to be snapped.
|
287
|
+
* @return {Date} snappedDate
|
287
288
|
*/
|
288
289
|
TimeStep.prototype.snap = function(date) {
|
290
|
+
var clone = new Date(date.valueOf());
|
291
|
+
|
289
292
|
if (this.scale == TimeStep.SCALE.YEAR) {
|
290
|
-
var year =
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
293
|
+
var year = clone.getFullYear() + Math.round(clone.getMonth() / 12);
|
294
|
+
clone.setFullYear(Math.round(year / this.step) * this.step);
|
295
|
+
clone.setMonth(0);
|
296
|
+
clone.setDate(0);
|
297
|
+
clone.setHours(0);
|
298
|
+
clone.setMinutes(0);
|
299
|
+
clone.setSeconds(0);
|
300
|
+
clone.setMilliseconds(0);
|
298
301
|
}
|
299
302
|
else if (this.scale == TimeStep.SCALE.MONTH) {
|
300
|
-
if (
|
301
|
-
|
302
|
-
|
303
|
+
if (clone.getDate() > 15) {
|
304
|
+
clone.setDate(1);
|
305
|
+
clone.setMonth(clone.getMonth() + 1);
|
303
306
|
// important: first set Date to 1, after that change the month.
|
304
307
|
}
|
305
308
|
else {
|
306
|
-
|
309
|
+
clone.setDate(1);
|
307
310
|
}
|
308
311
|
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
312
|
+
clone.setHours(0);
|
313
|
+
clone.setMinutes(0);
|
314
|
+
clone.setSeconds(0);
|
315
|
+
clone.setMilliseconds(0);
|
313
316
|
}
|
314
317
|
else if (this.scale == TimeStep.SCALE.DAY ||
|
315
318
|
this.scale == TimeStep.SCALE.WEEKDAY) {
|
@@ -317,56 +320,58 @@ TimeStep.prototype.snap = function(date) {
|
|
317
320
|
switch (this.step) {
|
318
321
|
case 5:
|
319
322
|
case 2:
|
320
|
-
|
323
|
+
clone.setHours(Math.round(clone.getHours() / 24) * 24); break;
|
321
324
|
default:
|
322
|
-
|
325
|
+
clone.setHours(Math.round(clone.getHours() / 12) * 12); break;
|
323
326
|
}
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
+
clone.setMinutes(0);
|
328
|
+
clone.setSeconds(0);
|
329
|
+
clone.setMilliseconds(0);
|
327
330
|
}
|
328
331
|
else if (this.scale == TimeStep.SCALE.HOUR) {
|
329
332
|
switch (this.step) {
|
330
333
|
case 4:
|
331
|
-
|
334
|
+
clone.setMinutes(Math.round(clone.getMinutes() / 60) * 60); break;
|
332
335
|
default:
|
333
|
-
|
336
|
+
clone.setMinutes(Math.round(clone.getMinutes() / 30) * 30); break;
|
334
337
|
}
|
335
|
-
|
336
|
-
|
338
|
+
clone.setSeconds(0);
|
339
|
+
clone.setMilliseconds(0);
|
337
340
|
} else if (this.scale == TimeStep.SCALE.MINUTE) {
|
338
341
|
//noinspection FallthroughInSwitchStatementJS
|
339
342
|
switch (this.step) {
|
340
343
|
case 15:
|
341
344
|
case 10:
|
342
|
-
|
343
|
-
|
345
|
+
clone.setMinutes(Math.round(clone.getMinutes() / 5) * 5);
|
346
|
+
clone.setSeconds(0);
|
344
347
|
break;
|
345
348
|
case 5:
|
346
|
-
|
349
|
+
clone.setSeconds(Math.round(clone.getSeconds() / 60) * 60); break;
|
347
350
|
default:
|
348
|
-
|
351
|
+
clone.setSeconds(Math.round(clone.getSeconds() / 30) * 30); break;
|
349
352
|
}
|
350
|
-
|
353
|
+
clone.setMilliseconds(0);
|
351
354
|
}
|
352
355
|
else if (this.scale == TimeStep.SCALE.SECOND) {
|
353
356
|
//noinspection FallthroughInSwitchStatementJS
|
354
357
|
switch (this.step) {
|
355
358
|
case 15:
|
356
359
|
case 10:
|
357
|
-
|
358
|
-
|
360
|
+
clone.setSeconds(Math.round(clone.getSeconds() / 5) * 5);
|
361
|
+
clone.setMilliseconds(0);
|
359
362
|
break;
|
360
363
|
case 5:
|
361
|
-
|
364
|
+
clone.setMilliseconds(Math.round(clone.getMilliseconds() / 1000) * 1000); break;
|
362
365
|
default:
|
363
|
-
|
366
|
+
clone.setMilliseconds(Math.round(clone.getMilliseconds() / 500) * 500); break;
|
364
367
|
}
|
365
368
|
}
|
366
369
|
else if (this.scale == TimeStep.SCALE.MILLISECOND) {
|
367
370
|
var step = this.step > 5 ? this.step / 2 : 1;
|
368
|
-
|
371
|
+
clone.setMilliseconds(Math.round(clone.getMilliseconds() / step) * step);
|
369
372
|
}
|
373
|
+
|
374
|
+
return clone;
|
370
375
|
};
|
371
376
|
|
372
377
|
/**
|
@@ -1,7 +1,7 @@
|
|
1
1
|
/**
|
2
2
|
* Create a timeline visualization
|
3
3
|
* @param {HTMLElement} container
|
4
|
-
* @param {vis.DataSet | Array | DataTable} [items]
|
4
|
+
* @param {vis.DataSet | Array | google.visualization.DataTable} [items]
|
5
5
|
* @param {Object} [options] See Timeline.setOptions for the available options.
|
6
6
|
* @constructor
|
7
7
|
*/
|
@@ -10,17 +10,35 @@ function Timeline (container, items, options) {
|
|
10
10
|
var now = moment().hours(0).minutes(0).seconds(0).milliseconds(0);
|
11
11
|
this.options = {
|
12
12
|
orientation: 'bottom',
|
13
|
+
autoResize: true,
|
14
|
+
editable: false,
|
15
|
+
selectable: true,
|
16
|
+
snap: null, // will be specified after timeaxis is created
|
17
|
+
|
13
18
|
min: null,
|
14
19
|
max: null,
|
15
20
|
zoomMin: 10, // milliseconds
|
16
21
|
zoomMax: 1000 * 60 * 60 * 24 * 365 * 10000, // milliseconds
|
17
22
|
// moveable: true, // TODO: option moveable
|
18
23
|
// zoomable: true, // TODO: option zoomable
|
24
|
+
|
19
25
|
showMinorLabels: true,
|
20
26
|
showMajorLabels: true,
|
21
27
|
showCurrentTime: false,
|
22
28
|
showCustomTime: false,
|
23
|
-
|
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
|
+
}
|
24
42
|
};
|
25
43
|
|
26
44
|
// controller
|
@@ -45,6 +63,15 @@ function Timeline (container, items, options) {
|
|
45
63
|
this.rootPanel = new RootPanel(container, rootOptions);
|
46
64
|
this.controller.add(this.rootPanel);
|
47
65
|
|
66
|
+
// single select (or unselect) when tapping an item
|
67
|
+
this.controller.on('tap', this._onSelectItem.bind(this));
|
68
|
+
|
69
|
+
// multi select when holding mouse/touch, or on ctrl+click
|
70
|
+
this.controller.on('hold', this._onMultiSelectItem.bind(this));
|
71
|
+
|
72
|
+
// add item on doubletap
|
73
|
+
this.controller.on('doubletap', this._onAddItem.bind(this));
|
74
|
+
|
48
75
|
// item panel
|
49
76
|
var itemOptions = Object.create(this.options);
|
50
77
|
itemOptions.left = function () {
|
@@ -82,28 +109,19 @@ function Timeline (container, items, options) {
|
|
82
109
|
now.clone().add('days', 4).valueOf()
|
83
110
|
);
|
84
111
|
|
85
|
-
|
86
|
-
|
87
|
-
this.range.subscribe(this.rootPanel, 'move', 'horizontal');
|
88
|
-
this.range.subscribe(this.rootPanel, 'zoom', 'horizontal');
|
112
|
+
this.range.subscribe(this.controller, this.rootPanel, 'move', 'horizontal');
|
113
|
+
this.range.subscribe(this.controller, this.rootPanel, 'zoom', 'horizontal');
|
89
114
|
this.range.on('rangechange', function (properties) {
|
90
115
|
var force = true;
|
91
|
-
me.controller.
|
92
|
-
me.
|
116
|
+
me.controller.emit('rangechange', properties);
|
117
|
+
me.controller.emit('request-reflow', force);
|
93
118
|
});
|
94
119
|
this.range.on('rangechanged', function (properties) {
|
95
120
|
var force = true;
|
96
|
-
me.controller.
|
97
|
-
me.
|
121
|
+
me.controller.emit('rangechanged', properties);
|
122
|
+
me.controller.emit('request-reflow', force);
|
98
123
|
});
|
99
124
|
|
100
|
-
// single select (or unselect) when tapping an item
|
101
|
-
// TODO: implement ctrl+click
|
102
|
-
this.rootPanel.on('tap', this._onSelectItem.bind(this));
|
103
|
-
|
104
|
-
// multi select when holding mouse/touch, or on ctrl+click
|
105
|
-
this.rootPanel.on('hold', this._onMultiSelectItem.bind(this));
|
106
|
-
|
107
125
|
// time axis
|
108
126
|
var timeaxisOptions = Object.create(rootOptions);
|
109
127
|
timeaxisOptions.range = this.range;
|
@@ -114,6 +132,7 @@ function Timeline (container, items, options) {
|
|
114
132
|
this.timeaxis = new TimeAxis(this.itemPanel, [], timeaxisOptions);
|
115
133
|
this.timeaxis.setRange(this.range);
|
116
134
|
this.controller.add(this.timeaxis);
|
135
|
+
this.options.snap = this.timeaxis.snap.bind(this.timeaxis);
|
117
136
|
|
118
137
|
// current time bar
|
119
138
|
this.currenttime = new CurrentTime(this.timeaxis, [], rootOptions);
|
@@ -140,6 +159,25 @@ function Timeline (container, items, options) {
|
|
140
159
|
}
|
141
160
|
}
|
142
161
|
|
162
|
+
/**
|
163
|
+
* Add an event listener to the timeline
|
164
|
+
* @param {String} event Available events: select, rangechange, rangechanged,
|
165
|
+
* timechange, timechanged
|
166
|
+
* @param {function} callback
|
167
|
+
*/
|
168
|
+
Timeline.prototype.on = function on (event, callback) {
|
169
|
+
this.controller.on(event, callback);
|
170
|
+
};
|
171
|
+
|
172
|
+
/**
|
173
|
+
* Add an event listener from the timeline
|
174
|
+
* @param {String} event
|
175
|
+
* @param {function} callback
|
176
|
+
*/
|
177
|
+
Timeline.prototype.off = function off (event, callback) {
|
178
|
+
this.controller.off(event, callback);
|
179
|
+
};
|
180
|
+
|
143
181
|
/**
|
144
182
|
* Set options
|
145
183
|
* @param {Object} options TODO: describe the available options
|
@@ -151,6 +189,25 @@ Timeline.prototype.setOptions = function (options) {
|
|
151
189
|
// both start and end are optional
|
152
190
|
this.range.setRange(options.start, options.end);
|
153
191
|
|
192
|
+
if ('editable' in options || 'selectable' in options) {
|
193
|
+
if (this.options.selectable) {
|
194
|
+
// force update of selection
|
195
|
+
this.setSelection(this.getSelection());
|
196
|
+
}
|
197
|
+
else {
|
198
|
+
// remove selection
|
199
|
+
this.setSelection([]);
|
200
|
+
}
|
201
|
+
}
|
202
|
+
|
203
|
+
// validate the callback functions
|
204
|
+
var validateCallback = (function (fn) {
|
205
|
+
if (!(this.options[fn] instanceof Function) || this.options[fn].length != 2) {
|
206
|
+
throw new Error('option ' + fn + ' must be a function ' + fn + '(item, callback)');
|
207
|
+
}
|
208
|
+
}).bind(this);
|
209
|
+
['onAdd', 'onUpdate', 'onRemove', 'onMove'].forEach(validateCallback);
|
210
|
+
|
154
211
|
this.controller.reflow();
|
155
212
|
this.controller.repaint();
|
156
213
|
};
|
@@ -160,7 +217,11 @@ Timeline.prototype.setOptions = function (options) {
|
|
160
217
|
* @param {Date} time
|
161
218
|
*/
|
162
219
|
Timeline.prototype.setCustomTime = function (time) {
|
163
|
-
this.customtime
|
220
|
+
if (!this.customtime) {
|
221
|
+
throw new Error('Cannot get custom time: Custom time bar is not enabled');
|
222
|
+
}
|
223
|
+
|
224
|
+
this.customtime.setCustomTime(time);
|
164
225
|
};
|
165
226
|
|
166
227
|
/**
|
@@ -168,37 +229,41 @@ Timeline.prototype.setCustomTime = function (time) {
|
|
168
229
|
* @return {Date} customTime
|
169
230
|
*/
|
170
231
|
Timeline.prototype.getCustomTime = function() {
|
171
|
-
|
232
|
+
if (!this.customtime) {
|
233
|
+
throw new Error('Cannot get custom time: Custom time bar is not enabled');
|
234
|
+
}
|
235
|
+
|
236
|
+
return this.customtime.getCustomTime();
|
172
237
|
};
|
173
238
|
|
174
239
|
/**
|
175
240
|
* Set items
|
176
|
-
* @param {vis.DataSet | Array | DataTable | null} items
|
241
|
+
* @param {vis.DataSet | Array | google.visualization.DataTable | null} items
|
177
242
|
*/
|
178
243
|
Timeline.prototype.setItems = function(items) {
|
179
244
|
var initialLoad = (this.itemsData == null);
|
180
245
|
|
181
246
|
// convert to type DataSet when needed
|
182
|
-
var
|
247
|
+
var newDataSet;
|
183
248
|
if (!items) {
|
184
|
-
|
249
|
+
newDataSet = null;
|
185
250
|
}
|
186
251
|
else if (items instanceof DataSet) {
|
187
|
-
|
252
|
+
newDataSet = items;
|
188
253
|
}
|
189
254
|
if (!(items instanceof DataSet)) {
|
190
|
-
|
255
|
+
newDataSet = new DataSet({
|
191
256
|
convert: {
|
192
257
|
start: 'Date',
|
193
258
|
end: 'Date'
|
194
259
|
}
|
195
260
|
});
|
196
|
-
|
261
|
+
newDataSet.add(items);
|
197
262
|
}
|
198
263
|
|
199
264
|
// set items
|
200
|
-
this.itemsData =
|
201
|
-
this.content.setItems(
|
265
|
+
this.itemsData = newDataSet;
|
266
|
+
this.content.setItems(newDataSet);
|
202
267
|
|
203
268
|
if (initialLoad && (this.options.start == undefined || this.options.end == undefined)) {
|
204
269
|
// apply the data range as range
|
@@ -234,7 +299,7 @@ Timeline.prototype.setItems = function(items) {
|
|
234
299
|
|
235
300
|
/**
|
236
301
|
* Set groups
|
237
|
-
* @param {vis.DataSet | Array | DataTable} groups
|
302
|
+
* @param {vis.DataSet | Array | google.visualization.DataTable} groups
|
238
303
|
*/
|
239
304
|
Timeline.prototype.setGroups = function(groups) {
|
240
305
|
var me = this;
|
@@ -368,41 +433,25 @@ Timeline.prototype.getSelection = function getSelection() {
|
|
368
433
|
};
|
369
434
|
|
370
435
|
/**
|
371
|
-
*
|
372
|
-
*
|
373
|
-
*
|
374
|
-
* @param {
|
375
|
-
* where properties is an optional object containing
|
376
|
-
* event specific properties.
|
436
|
+
* Set the visible window. Both parameters are optional, you can change only
|
437
|
+
* start or only end.
|
438
|
+
* @param {Date | Number | String} [start] Start date of visible window
|
439
|
+
* @param {Date | Number | String} [end] End date of visible window
|
377
440
|
*/
|
378
|
-
Timeline.prototype.
|
379
|
-
|
380
|
-
|
381
|
-
if (available.indexOf(event) == -1) {
|
382
|
-
throw new Error('Unknown event "' + event + '". Choose from ' + available.join());
|
383
|
-
}
|
384
|
-
|
385
|
-
events.addListener(this, event, callback);
|
441
|
+
Timeline.prototype.setWindow = function setWindow(start, end) {
|
442
|
+
this.range.setRange(start, end);
|
386
443
|
};
|
387
444
|
|
388
445
|
/**
|
389
|
-
*
|
390
|
-
* @
|
391
|
-
* @param {function} callback Callback function
|
392
|
-
*/
|
393
|
-
Timeline.prototype.off = function off (event, callback) {
|
394
|
-
events.removeListener(this, event, callback);
|
395
|
-
};
|
396
|
-
|
397
|
-
/**
|
398
|
-
* Trigger an event
|
399
|
-
* @param {String} event Event name, available events: 'rangechange',
|
400
|
-
* 'rangechanged', 'select'
|
401
|
-
* @param {Object} [properties] Event specific properties
|
402
|
-
* @private
|
446
|
+
* Get the visible window
|
447
|
+
* @return {{start: Date, end: Date}} Visible range
|
403
448
|
*/
|
404
|
-
Timeline.prototype.
|
405
|
-
|
449
|
+
Timeline.prototype.getWindow = function setWindow() {
|
450
|
+
var range = this.range.getRange();
|
451
|
+
return {
|
452
|
+
start: new Date(range.start),
|
453
|
+
end: new Date(range.end)
|
454
|
+
};
|
406
455
|
};
|
407
456
|
|
408
457
|
/**
|
@@ -410,13 +459,23 @@ Timeline.prototype._trigger = function _trigger(event, properties) {
|
|
410
459
|
* @param {Event} event
|
411
460
|
* @private
|
412
461
|
*/
|
462
|
+
// TODO: move this function to ItemSet
|
413
463
|
Timeline.prototype._onSelectItem = function (event) {
|
414
|
-
|
464
|
+
if (!this.options.selectable) return;
|
465
|
+
|
466
|
+
var ctrlKey = event.gesture.srcEvent && event.gesture.srcEvent.ctrlKey;
|
467
|
+
var shiftKey = event.gesture.srcEvent && event.gesture.srcEvent.shiftKey;
|
468
|
+
if (ctrlKey || shiftKey) {
|
469
|
+
this._onMultiSelectItem(event);
|
470
|
+
return;
|
471
|
+
}
|
472
|
+
|
473
|
+
var item = ItemSet.itemFromTarget(event);
|
415
474
|
|
416
475
|
var selection = item ? [item.id] : [];
|
417
476
|
this.setSelection(selection);
|
418
477
|
|
419
|
-
this.
|
478
|
+
this.controller.emit('select', {
|
420
479
|
items: this.getSelection()
|
421
480
|
});
|
422
481
|
|
@@ -424,53 +483,116 @@ Timeline.prototype._onSelectItem = function (event) {
|
|
424
483
|
};
|
425
484
|
|
426
485
|
/**
|
427
|
-
* Handle
|
428
|
-
* @param
|
486
|
+
* Handle creation and updates of an item on double tap
|
487
|
+
* @param event
|
429
488
|
* @private
|
430
489
|
*/
|
431
|
-
Timeline.prototype.
|
432
|
-
|
433
|
-
|
490
|
+
Timeline.prototype._onAddItem = function (event) {
|
491
|
+
if (!this.options.selectable) return;
|
492
|
+
if (!this.options.editable) return;
|
434
493
|
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
494
|
+
var me = this,
|
495
|
+
item = ItemSet.itemFromTarget(event);
|
496
|
+
|
497
|
+
if (item) {
|
498
|
+
// update item
|
439
499
|
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
|
500
|
+
// execute async handler to update the item (or cancel it)
|
501
|
+
var itemData = me.itemsData.get(item.id); // get a clone of the data from the dataset
|
502
|
+
this.options.onUpdate(itemData, function (itemData) {
|
503
|
+
if (itemData) {
|
504
|
+
me.itemsData.update(itemData);
|
505
|
+
}
|
506
|
+
});
|
445
507
|
}
|
446
508
|
else {
|
447
|
-
// item
|
448
|
-
|
449
|
-
|
450
|
-
|
509
|
+
// add item
|
510
|
+
var xAbs = vis.util.getAbsoluteLeft(this.rootPanel.frame);
|
511
|
+
var x = event.gesture.center.pageX - xAbs;
|
512
|
+
var newItem = {
|
513
|
+
start: this.timeaxis.snap(this._toTime(x)),
|
514
|
+
content: 'new item'
|
515
|
+
};
|
516
|
+
|
517
|
+
var id = util.randomUUID();
|
518
|
+
newItem[this.itemsData.fieldId] = id;
|
519
|
+
|
520
|
+
var group = GroupSet.groupFromTarget(event);
|
521
|
+
if (group) {
|
522
|
+
newItem.group = group.groupId;
|
523
|
+
}
|
451
524
|
|
452
|
-
|
453
|
-
|
454
|
-
|
525
|
+
// execute async handler to customize (or cancel) adding an item
|
526
|
+
this.options.onAdd(newItem, function (item) {
|
527
|
+
if (item) {
|
528
|
+
me.itemsData.add(newItem);
|
455
529
|
|
456
|
-
|
530
|
+
// select the created item after it is repainted
|
531
|
+
me.controller.once('repaint', function () {
|
532
|
+
me.setSelection([id]);
|
533
|
+
|
534
|
+
me.controller.emit('select', {
|
535
|
+
items: me.getSelection()
|
536
|
+
});
|
537
|
+
}.bind(me));
|
538
|
+
}
|
539
|
+
});
|
540
|
+
}
|
457
541
|
};
|
458
542
|
|
459
543
|
/**
|
460
|
-
*
|
461
|
-
* searches for the attribute 'timeline-item' in the event target's element tree
|
544
|
+
* Handle selecting/deselecting multiple items when holding an item
|
462
545
|
* @param {Event} event
|
463
|
-
* @return {Item | null| item
|
464
546
|
* @private
|
465
547
|
*/
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
|
470
|
-
|
548
|
+
// TODO: move this function to ItemSet
|
549
|
+
Timeline.prototype._onMultiSelectItem = function (event) {
|
550
|
+
if (!this.options.selectable) return;
|
551
|
+
|
552
|
+
var selection,
|
553
|
+
item = ItemSet.itemFromTarget(event);
|
554
|
+
|
555
|
+
if (item) {
|
556
|
+
// multi select items
|
557
|
+
selection = this.getSelection(); // current selection
|
558
|
+
var index = selection.indexOf(item.id);
|
559
|
+
if (index == -1) {
|
560
|
+
// item is not yet selected -> select it
|
561
|
+
selection.push(item.id);
|
562
|
+
}
|
563
|
+
else {
|
564
|
+
// item is already selected -> deselect it
|
565
|
+
selection.splice(index, 1);
|
471
566
|
}
|
472
|
-
|
567
|
+
this.setSelection(selection);
|
568
|
+
|
569
|
+
this.controller.emit('select', {
|
570
|
+
items: this.getSelection()
|
571
|
+
});
|
572
|
+
|
573
|
+
event.stopPropagation();
|
473
574
|
}
|
575
|
+
};
|
474
576
|
|
475
|
-
|
476
|
-
|
577
|
+
/**
|
578
|
+
* Convert a position on screen (pixels) to a datetime
|
579
|
+
* @param {int} x Position on the screen in pixels
|
580
|
+
* @return {Date} time The datetime the corresponds with given position x
|
581
|
+
* @private
|
582
|
+
*/
|
583
|
+
Timeline.prototype._toTime = function _toTime(x) {
|
584
|
+
var conversion = this.range.conversion(this.content.width);
|
585
|
+
return new Date(x / conversion.scale + conversion.offset);
|
586
|
+
};
|
587
|
+
|
588
|
+
/**
|
589
|
+
* Convert a datetime (Date object) into a position on the screen
|
590
|
+
* @param {Date} time A date
|
591
|
+
* @return {int} x The position on the screen in pixels which corresponds
|
592
|
+
* with the given date.
|
593
|
+
* @private
|
594
|
+
*/
|
595
|
+
Timeline.prototype._toScreen = function _toScreen(time) {
|
596
|
+
var conversion = this.range.conversion(this.content.width);
|
597
|
+
return (time.valueOf() - conversion.offset) * conversion.scale;
|
598
|
+
};
|