vis-rails 2.0.0 → 2.0.1
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 +26 -26
- metadata +16 -85
- data/vendor/assets/vis/DataSet.js +0 -926
- data/vendor/assets/vis/DataView.js +0 -283
- data/vendor/assets/vis/graph/Edge.js +0 -957
- data/vendor/assets/vis/graph/Graph.js +0 -2291
- data/vendor/assets/vis/graph/Groups.js +0 -80
- data/vendor/assets/vis/graph/Images.js +0 -41
- data/vendor/assets/vis/graph/Node.js +0 -966
- data/vendor/assets/vis/graph/Popup.js +0 -132
- data/vendor/assets/vis/graph/css/graph-manipulation.css +0 -128
- data/vendor/assets/vis/graph/css/graph-navigation.css +0 -66
- data/vendor/assets/vis/graph/dotparser.js +0 -829
- data/vendor/assets/vis/graph/graphMixins/ClusterMixin.js +0 -1143
- data/vendor/assets/vis/graph/graphMixins/HierarchicalLayoutMixin.js +0 -311
- data/vendor/assets/vis/graph/graphMixins/ManipulationMixin.js +0 -576
- data/vendor/assets/vis/graph/graphMixins/MixinLoader.js +0 -199
- data/vendor/assets/vis/graph/graphMixins/NavigationMixin.js +0 -205
- data/vendor/assets/vis/graph/graphMixins/SectorsMixin.js +0 -552
- data/vendor/assets/vis/graph/graphMixins/SelectionMixin.js +0 -648
- data/vendor/assets/vis/graph/graphMixins/physics/BarnesHut.js +0 -398
- data/vendor/assets/vis/graph/graphMixins/physics/HierarchialRepulsion.js +0 -64
- data/vendor/assets/vis/graph/graphMixins/physics/PhysicsMixin.js +0 -697
- data/vendor/assets/vis/graph/graphMixins/physics/Repulsion.js +0 -66
- data/vendor/assets/vis/graph/img/acceptDeleteIcon.png +0 -0
- data/vendor/assets/vis/graph/img/addNodeIcon.png +0 -0
- data/vendor/assets/vis/graph/img/backIcon.png +0 -0
- data/vendor/assets/vis/graph/img/connectIcon.png +0 -0
- data/vendor/assets/vis/graph/img/cross.png +0 -0
- data/vendor/assets/vis/graph/img/cross2.png +0 -0
- data/vendor/assets/vis/graph/img/deleteIcon.png +0 -0
- data/vendor/assets/vis/graph/img/downArrow.png +0 -0
- data/vendor/assets/vis/graph/img/editIcon.png +0 -0
- data/vendor/assets/vis/graph/img/leftArrow.png +0 -0
- data/vendor/assets/vis/graph/img/minus.png +0 -0
- data/vendor/assets/vis/graph/img/plus.png +0 -0
- data/vendor/assets/vis/graph/img/rightArrow.png +0 -0
- data/vendor/assets/vis/graph/img/upArrow.png +0 -0
- data/vendor/assets/vis/graph/img/zoomExtends.png +0 -0
- data/vendor/assets/vis/graph/shapes.js +0 -225
- data/vendor/assets/vis/graph3d/Graph3d.js +0 -3306
- data/vendor/assets/vis/module/exports.js +0 -65
- data/vendor/assets/vis/module/header.js +0 -24
- data/vendor/assets/vis/module/imports.js +0 -31
- data/vendor/assets/vis/shim.js +0 -252
- data/vendor/assets/vis/timeline/Range.js +0 -532
- data/vendor/assets/vis/timeline/TimeStep.js +0 -466
- data/vendor/assets/vis/timeline/Timeline.js +0 -851
- data/vendor/assets/vis/timeline/component/Component.js +0 -52
- data/vendor/assets/vis/timeline/component/CurrentTime.js +0 -128
- data/vendor/assets/vis/timeline/component/CustomTime.js +0 -182
- data/vendor/assets/vis/timeline/component/Group.js +0 -470
- data/vendor/assets/vis/timeline/component/ItemSet.js +0 -1332
- data/vendor/assets/vis/timeline/component/TimeAxis.js +0 -389
- data/vendor/assets/vis/timeline/component/css/animation.css +0 -33
- data/vendor/assets/vis/timeline/component/css/currenttime.css +0 -5
- data/vendor/assets/vis/timeline/component/css/customtime.css +0 -6
- data/vendor/assets/vis/timeline/component/css/item.css +0 -107
- data/vendor/assets/vis/timeline/component/css/itemset.css +0 -33
- data/vendor/assets/vis/timeline/component/css/labelset.css +0 -36
- data/vendor/assets/vis/timeline/component/css/panel.css +0 -71
- data/vendor/assets/vis/timeline/component/css/timeaxis.css +0 -48
- data/vendor/assets/vis/timeline/component/css/timeline.css +0 -2
- data/vendor/assets/vis/timeline/component/item/Item.js +0 -139
- data/vendor/assets/vis/timeline/component/item/ItemBox.js +0 -230
- data/vendor/assets/vis/timeline/component/item/ItemPoint.js +0 -190
- data/vendor/assets/vis/timeline/component/item/ItemRange.js +0 -262
- data/vendor/assets/vis/timeline/component/item/ItemRangeOverflow.js +0 -57
- data/vendor/assets/vis/timeline/img/delete.png +0 -0
- data/vendor/assets/vis/timeline/stack.js +0 -112
- data/vendor/assets/vis/util.js +0 -990
@@ -1,851 +0,0 @@
|
|
1
|
-
/**
|
2
|
-
* Create a timeline visualization
|
3
|
-
* @param {HTMLElement} container
|
4
|
-
* @param {vis.DataSet | Array | google.visualization.DataTable} [items]
|
5
|
-
* @param {Object} [options] See Timeline.setOptions for the available options.
|
6
|
-
* @constructor
|
7
|
-
*/
|
8
|
-
function Timeline (container, items, options) {
|
9
|
-
var me = this;
|
10
|
-
this.defaultOptions = {
|
11
|
-
start: null,
|
12
|
-
end: null,
|
13
|
-
|
14
|
-
autoResize: true,
|
15
|
-
|
16
|
-
orientation: 'bottom',
|
17
|
-
width: null,
|
18
|
-
height: null,
|
19
|
-
maxHeight: null,
|
20
|
-
minHeight: null
|
21
|
-
};
|
22
|
-
this.options = util.deepExtend({}, this.defaultOptions);
|
23
|
-
|
24
|
-
// Create the DOM, props, and emitter
|
25
|
-
this._create(container);
|
26
|
-
|
27
|
-
// all components listed here will be repainted automatically
|
28
|
-
this.components = [];
|
29
|
-
|
30
|
-
this.body = {
|
31
|
-
dom: this.dom,
|
32
|
-
domProps: this.props,
|
33
|
-
emitter: {
|
34
|
-
on: this.on.bind(this),
|
35
|
-
off: this.off.bind(this),
|
36
|
-
emit: this.emit.bind(this)
|
37
|
-
},
|
38
|
-
util: {
|
39
|
-
snap: null, // will be specified after TimeAxis is created
|
40
|
-
toScreen: me._toScreen.bind(me),
|
41
|
-
toTime: me._toTime.bind(me)
|
42
|
-
}
|
43
|
-
};
|
44
|
-
|
45
|
-
// range
|
46
|
-
this.range = new Range(this.body);
|
47
|
-
this.components.push(this.range);
|
48
|
-
this.body.range = this.range;
|
49
|
-
|
50
|
-
// time axis
|
51
|
-
this.timeAxis = new TimeAxis(this.body);
|
52
|
-
this.components.push(this.timeAxis);
|
53
|
-
this.body.util.snap = this.timeAxis.snap.bind(this.timeAxis);
|
54
|
-
|
55
|
-
// current time bar
|
56
|
-
this.currentTime = new CurrentTime(this.body);
|
57
|
-
this.components.push(this.currentTime);
|
58
|
-
|
59
|
-
// custom time bar
|
60
|
-
// Note: time bar will be attached in this.setOptions when selected
|
61
|
-
this.customTime = new CustomTime(this.body);
|
62
|
-
this.components.push(this.customTime);
|
63
|
-
|
64
|
-
// item set
|
65
|
-
this.itemSet = new ItemSet(this.body);
|
66
|
-
this.components.push(this.itemSet);
|
67
|
-
|
68
|
-
this.itemsData = null; // DataSet
|
69
|
-
this.groupsData = null; // DataSet
|
70
|
-
|
71
|
-
// apply options
|
72
|
-
if (options) {
|
73
|
-
this.setOptions(options);
|
74
|
-
}
|
75
|
-
|
76
|
-
// create itemset
|
77
|
-
if (items) {
|
78
|
-
this.setItems(items);
|
79
|
-
}
|
80
|
-
else {
|
81
|
-
this.redraw();
|
82
|
-
}
|
83
|
-
}
|
84
|
-
|
85
|
-
// turn Timeline into an event emitter
|
86
|
-
Emitter(Timeline.prototype);
|
87
|
-
|
88
|
-
/**
|
89
|
-
* Create the main DOM for the Timeline: a root panel containing left, right,
|
90
|
-
* top, bottom, content, and background panel.
|
91
|
-
* @param {Element} container The container element where the Timeline will
|
92
|
-
* be attached.
|
93
|
-
* @private
|
94
|
-
*/
|
95
|
-
Timeline.prototype._create = function (container) {
|
96
|
-
this.dom = {};
|
97
|
-
|
98
|
-
this.dom.root = document.createElement('div');
|
99
|
-
this.dom.background = document.createElement('div');
|
100
|
-
this.dom.backgroundVertical = document.createElement('div');
|
101
|
-
this.dom.backgroundHorizontal = document.createElement('div');
|
102
|
-
this.dom.centerContainer = document.createElement('div');
|
103
|
-
this.dom.leftContainer = document.createElement('div');
|
104
|
-
this.dom.rightContainer = document.createElement('div');
|
105
|
-
this.dom.center = document.createElement('div');
|
106
|
-
this.dom.left = document.createElement('div');
|
107
|
-
this.dom.right = document.createElement('div');
|
108
|
-
this.dom.top = document.createElement('div');
|
109
|
-
this.dom.bottom = document.createElement('div');
|
110
|
-
this.dom.shadowTop = document.createElement('div');
|
111
|
-
this.dom.shadowBottom = document.createElement('div');
|
112
|
-
this.dom.shadowTopLeft = document.createElement('div');
|
113
|
-
this.dom.shadowBottomLeft = document.createElement('div');
|
114
|
-
this.dom.shadowTopRight = document.createElement('div');
|
115
|
-
this.dom.shadowBottomRight = document.createElement('div');
|
116
|
-
|
117
|
-
this.dom.background.className = 'vispanel background';
|
118
|
-
this.dom.backgroundVertical.className = 'vispanel background vertical';
|
119
|
-
this.dom.backgroundHorizontal.className = 'vispanel background horizontal';
|
120
|
-
this.dom.centerContainer.className = 'vispanel center';
|
121
|
-
this.dom.leftContainer.className = 'vispanel left';
|
122
|
-
this.dom.rightContainer.className = 'vispanel right';
|
123
|
-
this.dom.top.className = 'vispanel top';
|
124
|
-
this.dom.bottom.className = 'vispanel bottom';
|
125
|
-
this.dom.left.className = 'content';
|
126
|
-
this.dom.center.className = 'content';
|
127
|
-
this.dom.right.className = 'content';
|
128
|
-
this.dom.shadowTop.className = 'shadow top';
|
129
|
-
this.dom.shadowBottom.className = 'shadow bottom';
|
130
|
-
this.dom.shadowTopLeft.className = 'shadow top';
|
131
|
-
this.dom.shadowBottomLeft.className = 'shadow bottom';
|
132
|
-
this.dom.shadowTopRight.className = 'shadow top';
|
133
|
-
this.dom.shadowBottomRight.className = 'shadow bottom';
|
134
|
-
|
135
|
-
this.dom.root.appendChild(this.dom.background);
|
136
|
-
this.dom.root.appendChild(this.dom.backgroundVertical);
|
137
|
-
this.dom.root.appendChild(this.dom.backgroundHorizontal);
|
138
|
-
this.dom.root.appendChild(this.dom.centerContainer);
|
139
|
-
this.dom.root.appendChild(this.dom.leftContainer);
|
140
|
-
this.dom.root.appendChild(this.dom.rightContainer);
|
141
|
-
this.dom.root.appendChild(this.dom.top);
|
142
|
-
this.dom.root.appendChild(this.dom.bottom);
|
143
|
-
|
144
|
-
this.dom.centerContainer.appendChild(this.dom.center);
|
145
|
-
this.dom.leftContainer.appendChild(this.dom.left);
|
146
|
-
this.dom.rightContainer.appendChild(this.dom.right);
|
147
|
-
|
148
|
-
this.dom.centerContainer.appendChild(this.dom.shadowTop);
|
149
|
-
this.dom.centerContainer.appendChild(this.dom.shadowBottom);
|
150
|
-
this.dom.leftContainer.appendChild(this.dom.shadowTopLeft);
|
151
|
-
this.dom.leftContainer.appendChild(this.dom.shadowBottomLeft);
|
152
|
-
this.dom.rightContainer.appendChild(this.dom.shadowTopRight);
|
153
|
-
this.dom.rightContainer.appendChild(this.dom.shadowBottomRight);
|
154
|
-
|
155
|
-
this.on('rangechange', this.redraw.bind(this));
|
156
|
-
this.on('change', this.redraw.bind(this));
|
157
|
-
this.on('touch', this._onTouch.bind(this));
|
158
|
-
this.on('pinch', this._onPinch.bind(this));
|
159
|
-
this.on('dragstart', this._onDragStart.bind(this));
|
160
|
-
this.on('drag', this._onDrag.bind(this));
|
161
|
-
|
162
|
-
// create event listeners for all interesting events, these events will be
|
163
|
-
// emitted via emitter
|
164
|
-
this.hammer = Hammer(this.dom.root, {
|
165
|
-
prevent_default: true
|
166
|
-
});
|
167
|
-
this.listeners = {};
|
168
|
-
|
169
|
-
var me = this;
|
170
|
-
var events = [
|
171
|
-
'touch', 'pinch',
|
172
|
-
'tap', 'doubletap', 'hold',
|
173
|
-
'dragstart', 'drag', 'dragend',
|
174
|
-
'mousewheel', 'DOMMouseScroll' // DOMMouseScroll is needed for Firefox
|
175
|
-
];
|
176
|
-
events.forEach(function (event) {
|
177
|
-
var listener = function () {
|
178
|
-
var args = [event].concat(Array.prototype.slice.call(arguments, 0));
|
179
|
-
me.emit.apply(me, args);
|
180
|
-
};
|
181
|
-
me.hammer.on(event, listener);
|
182
|
-
me.listeners[event] = listener;
|
183
|
-
});
|
184
|
-
|
185
|
-
// size properties of each of the panels
|
186
|
-
this.props = {
|
187
|
-
root: {},
|
188
|
-
background: {},
|
189
|
-
centerContainer: {},
|
190
|
-
leftContainer: {},
|
191
|
-
rightContainer: {},
|
192
|
-
center: {},
|
193
|
-
left: {},
|
194
|
-
right: {},
|
195
|
-
top: {},
|
196
|
-
bottom: {},
|
197
|
-
border: {},
|
198
|
-
scrollTop: 0,
|
199
|
-
scrollTopMin: 0
|
200
|
-
};
|
201
|
-
this.touch = {}; // store state information needed for touch events
|
202
|
-
|
203
|
-
// attach the root panel to the provided container
|
204
|
-
if (!container) throw new Error('No container provided');
|
205
|
-
container.appendChild(this.dom.root);
|
206
|
-
};
|
207
|
-
|
208
|
-
/**
|
209
|
-
* Destroy the Timeline, clean up all DOM elements and event listeners.
|
210
|
-
*/
|
211
|
-
Timeline.prototype.destroy = function () {
|
212
|
-
// unbind datasets
|
213
|
-
this.clear();
|
214
|
-
|
215
|
-
// remove all event listeners
|
216
|
-
this.off();
|
217
|
-
|
218
|
-
// stop checking for changed size
|
219
|
-
this._stopAutoResize();
|
220
|
-
|
221
|
-
// remove from DOM
|
222
|
-
if (this.dom.root.parentNode) {
|
223
|
-
this.dom.root.parentNode.removeChild(this.dom.root);
|
224
|
-
}
|
225
|
-
this.dom = null;
|
226
|
-
|
227
|
-
// cleanup hammer touch events
|
228
|
-
for (var event in this.listeners) {
|
229
|
-
if (this.listeners.hasOwnProperty(event)) {
|
230
|
-
delete this.listeners[event];
|
231
|
-
}
|
232
|
-
}
|
233
|
-
this.listeners = null;
|
234
|
-
this.hammer = null;
|
235
|
-
|
236
|
-
// give all components the opportunity to cleanup
|
237
|
-
this.components.forEach(function (component) {
|
238
|
-
component.destroy();
|
239
|
-
});
|
240
|
-
|
241
|
-
this.body = null;
|
242
|
-
};
|
243
|
-
|
244
|
-
/**
|
245
|
-
* Set options. Options will be passed to all components loaded in the Timeline.
|
246
|
-
* @param {Object} [options]
|
247
|
-
* {String} orientation
|
248
|
-
* Vertical orientation for the Timeline,
|
249
|
-
* can be 'bottom' (default) or 'top'.
|
250
|
-
* {String | Number} width
|
251
|
-
* Width for the timeline, a number in pixels or
|
252
|
-
* a css string like '1000px' or '75%'. '100%' by default.
|
253
|
-
* {String | Number} height
|
254
|
-
* Fixed height for the Timeline, a number in pixels or
|
255
|
-
* a css string like '400px' or '75%'. If undefined,
|
256
|
-
* The Timeline will automatically size such that
|
257
|
-
* its contents fit.
|
258
|
-
* {String | Number} minHeight
|
259
|
-
* Minimum height for the Timeline, a number in pixels or
|
260
|
-
* a css string like '400px' or '75%'.
|
261
|
-
* {String | Number} maxHeight
|
262
|
-
* Maximum height for the Timeline, a number in pixels or
|
263
|
-
* a css string like '400px' or '75%'.
|
264
|
-
* {Number | Date | String} start
|
265
|
-
* Start date for the visible window
|
266
|
-
* {Number | Date | String} end
|
267
|
-
* End date for the visible window
|
268
|
-
*/
|
269
|
-
Timeline.prototype.setOptions = function (options) {
|
270
|
-
if (options) {
|
271
|
-
// copy the known options
|
272
|
-
var fields = ['width', 'height', 'minHeight', 'maxHeight', 'autoResize', 'start', 'end', 'orientation'];
|
273
|
-
util.selectiveExtend(fields, this.options, options);
|
274
|
-
|
275
|
-
// enable/disable autoResize
|
276
|
-
this._initAutoResize();
|
277
|
-
}
|
278
|
-
|
279
|
-
// propagate options to all components
|
280
|
-
this.components.forEach(function (component) {
|
281
|
-
component.setOptions(options);
|
282
|
-
});
|
283
|
-
|
284
|
-
// TODO: remove deprecation error one day (deprecated since version 0.8.0)
|
285
|
-
if (options && options.order) {
|
286
|
-
throw new Error('Option order is deprecated. There is no replacement for this feature.');
|
287
|
-
}
|
288
|
-
|
289
|
-
// redraw everything
|
290
|
-
this.redraw();
|
291
|
-
};
|
292
|
-
|
293
|
-
/**
|
294
|
-
* Set a custom time bar
|
295
|
-
* @param {Date} time
|
296
|
-
*/
|
297
|
-
Timeline.prototype.setCustomTime = function (time) {
|
298
|
-
if (!this.customTime) {
|
299
|
-
throw new Error('Cannot get custom time: Custom time bar is not enabled');
|
300
|
-
}
|
301
|
-
|
302
|
-
this.customTime.setCustomTime(time);
|
303
|
-
};
|
304
|
-
|
305
|
-
/**
|
306
|
-
* Retrieve the current custom time.
|
307
|
-
* @return {Date} customTime
|
308
|
-
*/
|
309
|
-
Timeline.prototype.getCustomTime = function() {
|
310
|
-
if (!this.customTime) {
|
311
|
-
throw new Error('Cannot get custom time: Custom time bar is not enabled');
|
312
|
-
}
|
313
|
-
|
314
|
-
return this.customTime.getCustomTime();
|
315
|
-
};
|
316
|
-
|
317
|
-
/**
|
318
|
-
* Set items
|
319
|
-
* @param {vis.DataSet | Array | google.visualization.DataTable | null} items
|
320
|
-
*/
|
321
|
-
Timeline.prototype.setItems = function(items) {
|
322
|
-
var initialLoad = (this.itemsData == null);
|
323
|
-
|
324
|
-
// convert to type DataSet when needed
|
325
|
-
var newDataSet;
|
326
|
-
if (!items) {
|
327
|
-
newDataSet = null;
|
328
|
-
}
|
329
|
-
else if (items instanceof DataSet || items instanceof DataView) {
|
330
|
-
newDataSet = items;
|
331
|
-
}
|
332
|
-
else {
|
333
|
-
// turn an array into a dataset
|
334
|
-
newDataSet = new DataSet(items, {
|
335
|
-
type: {
|
336
|
-
start: 'Date',
|
337
|
-
end: 'Date'
|
338
|
-
}
|
339
|
-
});
|
340
|
-
}
|
341
|
-
|
342
|
-
// set items
|
343
|
-
this.itemsData = newDataSet;
|
344
|
-
this.itemSet && this.itemSet.setItems(newDataSet);
|
345
|
-
|
346
|
-
if (initialLoad && ('start' in this.options || 'end' in this.options)) {
|
347
|
-
this.fit();
|
348
|
-
|
349
|
-
var start = ('start' in this.options) ? util.convert(this.options.start, 'Date') : null;
|
350
|
-
var end = ('end' in this.options) ? util.convert(this.options.end, 'Date') : null;
|
351
|
-
|
352
|
-
this.setWindow(start, end);
|
353
|
-
}
|
354
|
-
};
|
355
|
-
|
356
|
-
/**
|
357
|
-
* Set groups
|
358
|
-
* @param {vis.DataSet | Array | google.visualization.DataTable} groups
|
359
|
-
*/
|
360
|
-
Timeline.prototype.setGroups = function(groups) {
|
361
|
-
// convert to type DataSet when needed
|
362
|
-
var newDataSet;
|
363
|
-
if (!groups) {
|
364
|
-
newDataSet = null;
|
365
|
-
}
|
366
|
-
else if (groups instanceof DataSet || groups instanceof DataView) {
|
367
|
-
newDataSet = groups;
|
368
|
-
}
|
369
|
-
else {
|
370
|
-
// turn an array into a dataset
|
371
|
-
newDataSet = new DataSet(groups);
|
372
|
-
}
|
373
|
-
|
374
|
-
this.groupsData = newDataSet;
|
375
|
-
this.itemSet.setGroups(newDataSet);
|
376
|
-
};
|
377
|
-
|
378
|
-
/**
|
379
|
-
* Clear the Timeline. By Default, items, groups and options are cleared.
|
380
|
-
* Example usage:
|
381
|
-
*
|
382
|
-
* timeline.clear(); // clear items, groups, and options
|
383
|
-
* timeline.clear({options: true}); // clear options only
|
384
|
-
*
|
385
|
-
* @param {Object} [what] Optionally specify what to clear. By default:
|
386
|
-
* {items: true, groups: true, options: true}
|
387
|
-
*/
|
388
|
-
Timeline.prototype.clear = function(what) {
|
389
|
-
// clear items
|
390
|
-
if (!what || what.items) {
|
391
|
-
this.setItems(null);
|
392
|
-
}
|
393
|
-
|
394
|
-
// clear groups
|
395
|
-
if (!what || what.groups) {
|
396
|
-
this.setGroups(null);
|
397
|
-
}
|
398
|
-
|
399
|
-
// clear options of timeline and of each of the components
|
400
|
-
if (!what || what.options) {
|
401
|
-
this.components.forEach(function (component) {
|
402
|
-
component.setOptions(component.defaultOptions);
|
403
|
-
});
|
404
|
-
|
405
|
-
this.setOptions(this.defaultOptions); // this will also do a redraw
|
406
|
-
}
|
407
|
-
};
|
408
|
-
|
409
|
-
/**
|
410
|
-
* Set Timeline window such that it fits all items
|
411
|
-
*/
|
412
|
-
Timeline.prototype.fit = function() {
|
413
|
-
// apply the data range as range
|
414
|
-
var dataRange = this.getItemRange();
|
415
|
-
|
416
|
-
// add 5% space on both sides
|
417
|
-
var start = dataRange.min;
|
418
|
-
var end = dataRange.max;
|
419
|
-
if (start != null && end != null) {
|
420
|
-
var interval = (end.valueOf() - start.valueOf());
|
421
|
-
if (interval <= 0) {
|
422
|
-
// prevent an empty interval
|
423
|
-
interval = 24 * 60 * 60 * 1000; // 1 day
|
424
|
-
}
|
425
|
-
start = new Date(start.valueOf() - interval * 0.05);
|
426
|
-
end = new Date(end.valueOf() + interval * 0.05);
|
427
|
-
}
|
428
|
-
|
429
|
-
// skip range set if there is no start and end date
|
430
|
-
if (start === null && end === null) {
|
431
|
-
return;
|
432
|
-
}
|
433
|
-
|
434
|
-
this.range.setRange(start, end);
|
435
|
-
};
|
436
|
-
|
437
|
-
/**
|
438
|
-
* Get the data range of the item set.
|
439
|
-
* @returns {{min: Date, max: Date}} range A range with a start and end Date.
|
440
|
-
* When no minimum is found, min==null
|
441
|
-
* When no maximum is found, max==null
|
442
|
-
*/
|
443
|
-
Timeline.prototype.getItemRange = function() {
|
444
|
-
// calculate min from start filed
|
445
|
-
var itemsData = this.itemsData,
|
446
|
-
min = null,
|
447
|
-
max = null;
|
448
|
-
|
449
|
-
if (itemsData) {
|
450
|
-
// calculate the minimum value of the field 'start'
|
451
|
-
var minItem = itemsData.min('start');
|
452
|
-
min = minItem ? util.convert(minItem.start, 'Date').valueOf() : null;
|
453
|
-
// Note: we convert first to Date and then to number because else
|
454
|
-
// a conversion from ISODate to Number will fail
|
455
|
-
|
456
|
-
// calculate maximum value of fields 'start' and 'end'
|
457
|
-
var maxStartItem = itemsData.max('start');
|
458
|
-
if (maxStartItem) {
|
459
|
-
max = util.convert(maxStartItem.start, 'Date').valueOf();
|
460
|
-
}
|
461
|
-
var maxEndItem = itemsData.max('end');
|
462
|
-
if (maxEndItem) {
|
463
|
-
if (max == null) {
|
464
|
-
max = util.convert(maxEndItem.end, 'Date').valueOf();
|
465
|
-
}
|
466
|
-
else {
|
467
|
-
max = Math.max(max, util.convert(maxEndItem.end, 'Date').valueOf());
|
468
|
-
}
|
469
|
-
}
|
470
|
-
}
|
471
|
-
|
472
|
-
return {
|
473
|
-
min: (min != null) ? new Date(min) : null,
|
474
|
-
max: (max != null) ? new Date(max) : null
|
475
|
-
};
|
476
|
-
};
|
477
|
-
|
478
|
-
/**
|
479
|
-
* Set selected items by their id. Replaces the current selection
|
480
|
-
* Unknown id's are silently ignored.
|
481
|
-
* @param {Array} [ids] An array with zero or more id's of the items to be
|
482
|
-
* selected. If ids is an empty array, all items will be
|
483
|
-
* unselected.
|
484
|
-
*/
|
485
|
-
Timeline.prototype.setSelection = function(ids) {
|
486
|
-
this.itemSet && this.itemSet.setSelection(ids);
|
487
|
-
};
|
488
|
-
|
489
|
-
/**
|
490
|
-
* Get the selected items by their id
|
491
|
-
* @return {Array} ids The ids of the selected items
|
492
|
-
*/
|
493
|
-
Timeline.prototype.getSelection = function() {
|
494
|
-
return this.itemSet && this.itemSet.getSelection() || [];
|
495
|
-
};
|
496
|
-
|
497
|
-
/**
|
498
|
-
* Set the visible window. Both parameters are optional, you can change only
|
499
|
-
* start or only end. Syntax:
|
500
|
-
*
|
501
|
-
* TimeLine.setWindow(start, end)
|
502
|
-
* TimeLine.setWindow(range)
|
503
|
-
*
|
504
|
-
* Where start and end can be a Date, number, or string, and range is an
|
505
|
-
* object with properties start and end.
|
506
|
-
*
|
507
|
-
* @param {Date | Number | String | Object} [start] Start date of visible window
|
508
|
-
* @param {Date | Number | String} [end] End date of visible window
|
509
|
-
*/
|
510
|
-
Timeline.prototype.setWindow = function(start, end) {
|
511
|
-
if (arguments.length == 1) {
|
512
|
-
var range = arguments[0];
|
513
|
-
this.range.setRange(range.start, range.end);
|
514
|
-
}
|
515
|
-
else {
|
516
|
-
this.range.setRange(start, end);
|
517
|
-
}
|
518
|
-
};
|
519
|
-
|
520
|
-
/**
|
521
|
-
* Get the visible window
|
522
|
-
* @return {{start: Date, end: Date}} Visible range
|
523
|
-
*/
|
524
|
-
Timeline.prototype.getWindow = function() {
|
525
|
-
var range = this.range.getRange();
|
526
|
-
return {
|
527
|
-
start: new Date(range.start),
|
528
|
-
end: new Date(range.end)
|
529
|
-
};
|
530
|
-
};
|
531
|
-
|
532
|
-
/**
|
533
|
-
* Force a redraw of the Timeline. Can be useful to manually redraw when
|
534
|
-
* option autoResize=false
|
535
|
-
*/
|
536
|
-
Timeline.prototype.redraw = function() {
|
537
|
-
var resized = false,
|
538
|
-
options = this.options,
|
539
|
-
props = this.props,
|
540
|
-
dom = this.dom;
|
541
|
-
|
542
|
-
if (!dom) return; // when destroyed
|
543
|
-
|
544
|
-
// update class names
|
545
|
-
dom.root.className = 'vis timeline root ' + options.orientation;
|
546
|
-
|
547
|
-
// update root width and height options
|
548
|
-
dom.root.style.maxHeight = util.option.asSize(options.maxHeight, '');
|
549
|
-
dom.root.style.minHeight = util.option.asSize(options.minHeight, '');
|
550
|
-
dom.root.style.width = util.option.asSize(options.width, '');
|
551
|
-
|
552
|
-
// calculate border widths
|
553
|
-
props.border.left = (dom.centerContainer.offsetWidth - dom.centerContainer.clientWidth) / 2;
|
554
|
-
props.border.right = props.border.left;
|
555
|
-
props.border.top = (dom.centerContainer.offsetHeight - dom.centerContainer.clientHeight) / 2;
|
556
|
-
props.border.bottom = props.border.top;
|
557
|
-
var borderRootHeight= dom.root.offsetHeight - dom.root.clientHeight;
|
558
|
-
var borderRootWidth = dom.root.offsetWidth - dom.root.clientWidth;
|
559
|
-
|
560
|
-
// calculate the heights. If any of the side panels is empty, we set the height to
|
561
|
-
// minus the border width, such that the border will be invisible
|
562
|
-
props.center.height = dom.center.offsetHeight;
|
563
|
-
props.left.height = dom.left.offsetHeight;
|
564
|
-
props.right.height = dom.right.offsetHeight;
|
565
|
-
props.top.height = dom.top.clientHeight || -props.border.top;
|
566
|
-
props.bottom.height = dom.bottom.clientHeight || -props.border.bottom;
|
567
|
-
|
568
|
-
// TODO: compensate borders when any of the panels is empty.
|
569
|
-
|
570
|
-
// apply auto height
|
571
|
-
// TODO: only calculate autoHeight when needed (else we cause an extra reflow/repaint of the DOM)
|
572
|
-
var contentHeight = Math.max(props.left.height, props.center.height, props.right.height);
|
573
|
-
var autoHeight = props.top.height + contentHeight + props.bottom.height +
|
574
|
-
borderRootHeight + props.border.top + props.border.bottom;
|
575
|
-
dom.root.style.height = util.option.asSize(options.height, autoHeight + 'px');
|
576
|
-
|
577
|
-
// calculate heights of the content panels
|
578
|
-
props.root.height = dom.root.offsetHeight;
|
579
|
-
props.background.height = props.root.height - borderRootHeight;
|
580
|
-
var containerHeight = props.root.height - props.top.height - props.bottom.height -
|
581
|
-
borderRootHeight;
|
582
|
-
props.centerContainer.height = containerHeight;
|
583
|
-
props.leftContainer.height = containerHeight;
|
584
|
-
props.rightContainer.height = props.leftContainer.height;
|
585
|
-
|
586
|
-
// calculate the widths of the panels
|
587
|
-
props.root.width = dom.root.offsetWidth;
|
588
|
-
props.background.width = props.root.width - borderRootWidth;
|
589
|
-
props.left.width = dom.leftContainer.clientWidth || -props.border.left;
|
590
|
-
props.leftContainer.width = props.left.width;
|
591
|
-
props.right.width = dom.rightContainer.clientWidth || -props.border.right;
|
592
|
-
props.rightContainer.width = props.right.width;
|
593
|
-
var centerWidth = props.root.width - props.left.width - props.right.width - borderRootWidth;
|
594
|
-
props.center.width = centerWidth;
|
595
|
-
props.centerContainer.width = centerWidth;
|
596
|
-
props.top.width = centerWidth;
|
597
|
-
props.bottom.width = centerWidth;
|
598
|
-
|
599
|
-
// resize the panels
|
600
|
-
dom.background.style.height = props.background.height + 'px';
|
601
|
-
dom.backgroundVertical.style.height = props.background.height + 'px';
|
602
|
-
dom.backgroundHorizontal.style.height = props.centerContainer.height + 'px';
|
603
|
-
dom.centerContainer.style.height = props.centerContainer.height + 'px';
|
604
|
-
dom.leftContainer.style.height = props.leftContainer.height + 'px';
|
605
|
-
dom.rightContainer.style.height = props.rightContainer.height + 'px';
|
606
|
-
|
607
|
-
dom.background.style.width = props.background.width + 'px';
|
608
|
-
dom.backgroundVertical.style.width = props.centerContainer.width + 'px';
|
609
|
-
dom.backgroundHorizontal.style.width = props.background.width + 'px';
|
610
|
-
dom.centerContainer.style.width = props.center.width + 'px';
|
611
|
-
dom.top.style.width = props.top.width + 'px';
|
612
|
-
dom.bottom.style.width = props.bottom.width + 'px';
|
613
|
-
|
614
|
-
// reposition the panels
|
615
|
-
dom.background.style.left = '0';
|
616
|
-
dom.background.style.top = '0';
|
617
|
-
dom.backgroundVertical.style.left = props.left.width + 'px';
|
618
|
-
dom.backgroundVertical.style.top = '0';
|
619
|
-
dom.backgroundHorizontal.style.left = '0';
|
620
|
-
dom.backgroundHorizontal.style.top = props.top.height + 'px';
|
621
|
-
dom.centerContainer.style.left = props.left.width + 'px';
|
622
|
-
dom.centerContainer.style.top = props.top.height + 'px';
|
623
|
-
dom.leftContainer.style.left = '0';
|
624
|
-
dom.leftContainer.style.top = props.top.height + 'px';
|
625
|
-
dom.rightContainer.style.left = (props.left.width + props.center.width) + 'px';
|
626
|
-
dom.rightContainer.style.top = props.top.height + 'px';
|
627
|
-
dom.top.style.left = props.left.width + 'px';
|
628
|
-
dom.top.style.top = '0';
|
629
|
-
dom.bottom.style.left = props.left.width + 'px';
|
630
|
-
dom.bottom.style.top = (props.top.height + props.centerContainer.height) + 'px';
|
631
|
-
|
632
|
-
// update the scrollTop, feasible range for the offset can be changed
|
633
|
-
// when the height of the Timeline or of the contents of the center changed
|
634
|
-
this._updateScrollTop();
|
635
|
-
|
636
|
-
// reposition the scrollable contents
|
637
|
-
var offset = this.props.scrollTop;
|
638
|
-
if (options.orientation == 'bottom') {
|
639
|
-
offset += Math.max(this.props.centerContainer.height - this.props.center.height, 0);
|
640
|
-
}
|
641
|
-
dom.center.style.left = '0';
|
642
|
-
dom.center.style.top = offset + 'px';
|
643
|
-
dom.left.style.left = '0';
|
644
|
-
dom.left.style.top = offset + 'px';
|
645
|
-
dom.right.style.left = '0';
|
646
|
-
dom.right.style.top = offset + 'px';
|
647
|
-
|
648
|
-
// show shadows when vertical scrolling is available
|
649
|
-
var visibilityTop = this.props.scrollTop == 0 ? 'hidden' : '';
|
650
|
-
var visibilityBottom = this.props.scrollTop == this.props.scrollTopMin ? 'hidden' : '';
|
651
|
-
dom.shadowTop.style.visibility = visibilityTop;
|
652
|
-
dom.shadowBottom.style.visibility = visibilityBottom;
|
653
|
-
dom.shadowTopLeft.style.visibility = visibilityTop;
|
654
|
-
dom.shadowBottomLeft.style.visibility = visibilityBottom;
|
655
|
-
dom.shadowTopRight.style.visibility = visibilityTop;
|
656
|
-
dom.shadowBottomRight.style.visibility = visibilityBottom;
|
657
|
-
|
658
|
-
// redraw all components
|
659
|
-
this.components.forEach(function (component) {
|
660
|
-
resized = component.redraw() || resized;
|
661
|
-
});
|
662
|
-
if (resized) {
|
663
|
-
// keep repainting until all sizes are settled
|
664
|
-
this.redraw();
|
665
|
-
}
|
666
|
-
};
|
667
|
-
|
668
|
-
// TODO: deprecated since version 1.1.0, remove some day
|
669
|
-
Timeline.prototype.repaint = function () {
|
670
|
-
throw new Error('Function repaint is deprecated. Use redraw instead.');
|
671
|
-
};
|
672
|
-
|
673
|
-
/**
|
674
|
-
* Convert a position on screen (pixels) to a datetime
|
675
|
-
* @param {int} x Position on the screen in pixels
|
676
|
-
* @return {Date} time The datetime the corresponds with given position x
|
677
|
-
* @private
|
678
|
-
*/
|
679
|
-
// TODO: move this function to Range
|
680
|
-
Timeline.prototype._toTime = function(x) {
|
681
|
-
var conversion = this.range.conversion(this.props.center.width);
|
682
|
-
return new Date(x / conversion.scale + conversion.offset);
|
683
|
-
};
|
684
|
-
|
685
|
-
/**
|
686
|
-
* Convert a datetime (Date object) into a position on the screen
|
687
|
-
* @param {Date} time A date
|
688
|
-
* @return {int} x The position on the screen in pixels which corresponds
|
689
|
-
* with the given date.
|
690
|
-
* @private
|
691
|
-
*/
|
692
|
-
// TODO: move this function to Range
|
693
|
-
Timeline.prototype._toScreen = function(time) {
|
694
|
-
var conversion = this.range.conversion(this.props.center.width);
|
695
|
-
return (time.valueOf() - conversion.offset) * conversion.scale;
|
696
|
-
};
|
697
|
-
|
698
|
-
/**
|
699
|
-
* Initialize watching when option autoResize is true
|
700
|
-
* @private
|
701
|
-
*/
|
702
|
-
Timeline.prototype._initAutoResize = function () {
|
703
|
-
if (this.options.autoResize == true) {
|
704
|
-
this._startAutoResize();
|
705
|
-
}
|
706
|
-
else {
|
707
|
-
this._stopAutoResize();
|
708
|
-
}
|
709
|
-
};
|
710
|
-
|
711
|
-
/**
|
712
|
-
* Watch for changes in the size of the container. On resize, the Panel will
|
713
|
-
* automatically redraw itself.
|
714
|
-
* @private
|
715
|
-
*/
|
716
|
-
Timeline.prototype._startAutoResize = function () {
|
717
|
-
var me = this;
|
718
|
-
|
719
|
-
this._stopAutoResize();
|
720
|
-
|
721
|
-
this._onResize = function() {
|
722
|
-
if (me.options.autoResize != true) {
|
723
|
-
// stop watching when the option autoResize is changed to false
|
724
|
-
me._stopAutoResize();
|
725
|
-
return;
|
726
|
-
}
|
727
|
-
|
728
|
-
if (me.dom.root) {
|
729
|
-
// check whether the frame is resized
|
730
|
-
if ((me.dom.root.clientWidth != me.props.lastWidth) ||
|
731
|
-
(me.dom.root.clientHeight != me.props.lastHeight)) {
|
732
|
-
me.props.lastWidth = me.dom.root.clientWidth;
|
733
|
-
me.props.lastHeight = me.dom.root.clientHeight;
|
734
|
-
|
735
|
-
me.emit('change');
|
736
|
-
}
|
737
|
-
}
|
738
|
-
};
|
739
|
-
|
740
|
-
// add event listener to window resize
|
741
|
-
util.addEventListener(window, 'resize', this._onResize);
|
742
|
-
|
743
|
-
this.watchTimer = setInterval(this._onResize, 1000);
|
744
|
-
};
|
745
|
-
|
746
|
-
/**
|
747
|
-
* Stop watching for a resize of the frame.
|
748
|
-
* @private
|
749
|
-
*/
|
750
|
-
Timeline.prototype._stopAutoResize = function () {
|
751
|
-
if (this.watchTimer) {
|
752
|
-
clearInterval(this.watchTimer);
|
753
|
-
this.watchTimer = undefined;
|
754
|
-
}
|
755
|
-
|
756
|
-
// remove event listener on window.resize
|
757
|
-
util.removeEventListener(window, 'resize', this._onResize);
|
758
|
-
this._onResize = null;
|
759
|
-
};
|
760
|
-
|
761
|
-
/**
|
762
|
-
* Start moving the timeline vertically
|
763
|
-
* @param {Event} event
|
764
|
-
* @private
|
765
|
-
*/
|
766
|
-
Timeline.prototype._onTouch = function (event) {
|
767
|
-
this.touch.allowDragging = true;
|
768
|
-
};
|
769
|
-
|
770
|
-
/**
|
771
|
-
* Start moving the timeline vertically
|
772
|
-
* @param {Event} event
|
773
|
-
* @private
|
774
|
-
*/
|
775
|
-
Timeline.prototype._onPinch = function (event) {
|
776
|
-
this.touch.allowDragging = false;
|
777
|
-
};
|
778
|
-
|
779
|
-
/**
|
780
|
-
* Start moving the timeline vertically
|
781
|
-
* @param {Event} event
|
782
|
-
* @private
|
783
|
-
*/
|
784
|
-
Timeline.prototype._onDragStart = function (event) {
|
785
|
-
this.touch.initialScrollTop = this.props.scrollTop;
|
786
|
-
};
|
787
|
-
|
788
|
-
/**
|
789
|
-
* Move the timeline vertically
|
790
|
-
* @param {Event} event
|
791
|
-
* @private
|
792
|
-
*/
|
793
|
-
Timeline.prototype._onDrag = function (event) {
|
794
|
-
// refuse to drag when we where pinching to prevent the timeline make a jump
|
795
|
-
// when releasing the fingers in opposite order from the touch screen
|
796
|
-
if (!this.touch.allowDragging) return;
|
797
|
-
|
798
|
-
var delta = event.gesture.deltaY;
|
799
|
-
|
800
|
-
var oldScrollTop = this._getScrollTop();
|
801
|
-
var newScrollTop = this._setScrollTop(this.touch.initialScrollTop + delta);
|
802
|
-
|
803
|
-
if (newScrollTop != oldScrollTop) {
|
804
|
-
this.redraw(); // TODO: this causes two redraws when dragging, the other is triggered by rangechange already
|
805
|
-
}
|
806
|
-
};
|
807
|
-
|
808
|
-
/**
|
809
|
-
* Apply a scrollTop
|
810
|
-
* @param {Number} scrollTop
|
811
|
-
* @returns {Number} scrollTop Returns the applied scrollTop
|
812
|
-
* @private
|
813
|
-
*/
|
814
|
-
Timeline.prototype._setScrollTop = function (scrollTop) {
|
815
|
-
this.props.scrollTop = scrollTop;
|
816
|
-
this._updateScrollTop();
|
817
|
-
return this.props.scrollTop;
|
818
|
-
};
|
819
|
-
|
820
|
-
/**
|
821
|
-
* Update the current scrollTop when the height of the containers has been changed
|
822
|
-
* @returns {Number} scrollTop Returns the applied scrollTop
|
823
|
-
* @private
|
824
|
-
*/
|
825
|
-
Timeline.prototype._updateScrollTop = function () {
|
826
|
-
// recalculate the scrollTopMin
|
827
|
-
var scrollTopMin = Math.min(this.props.centerContainer.height - this.props.center.height, 0); // is negative or zero
|
828
|
-
if (scrollTopMin != this.props.scrollTopMin) {
|
829
|
-
// in case of bottom orientation, change the scrollTop such that the contents
|
830
|
-
// do not move relative to the time axis at the bottom
|
831
|
-
if (this.options.orientation == 'bottom') {
|
832
|
-
this.props.scrollTop += (scrollTopMin - this.props.scrollTopMin);
|
833
|
-
}
|
834
|
-
this.props.scrollTopMin = scrollTopMin;
|
835
|
-
}
|
836
|
-
|
837
|
-
// limit the scrollTop to the feasible scroll range
|
838
|
-
if (this.props.scrollTop > 0) this.props.scrollTop = 0;
|
839
|
-
if (this.props.scrollTop < scrollTopMin) this.props.scrollTop = scrollTopMin;
|
840
|
-
|
841
|
-
return this.props.scrollTop;
|
842
|
-
};
|
843
|
-
|
844
|
-
/**
|
845
|
-
* Get the current scrollTop
|
846
|
-
* @returns {number} scrollTop
|
847
|
-
* @private
|
848
|
-
*/
|
849
|
-
Timeline.prototype._getScrollTop = function () {
|
850
|
-
return this.props.scrollTop;
|
851
|
-
};
|