vis-rails 0.0.6 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (62) hide show
  1. checksums.yaml +4 -4
  2. data/lib/vis/rails/version.rb +1 -1
  3. data/vendor/assets/javascripts/vis.js +2 -9
  4. data/vendor/assets/vis/DataSet.js +17 -9
  5. data/vendor/assets/vis/graph/Edge.js +49 -24
  6. data/vendor/assets/vis/graph/Graph.js +268 -64
  7. data/vendor/assets/vis/graph/Groups.js +1 -1
  8. data/vendor/assets/vis/graph/Node.js +18 -67
  9. data/vendor/assets/vis/graph/Popup.js +40 -13
  10. data/vendor/assets/vis/graph/css/graph-navigation.css +18 -14
  11. data/vendor/assets/vis/graph/graphMixins/ClusterMixin.js +7 -5
  12. data/vendor/assets/vis/graph/graphMixins/HierarchicalLayoutMixin.js +20 -5
  13. data/vendor/assets/vis/graph/graphMixins/ManipulationMixin.js +33 -33
  14. data/vendor/assets/vis/graph/graphMixins/MixinLoader.js +30 -32
  15. data/vendor/assets/vis/graph/graphMixins/NavigationMixin.js +33 -1
  16. data/vendor/assets/vis/graph/graphMixins/SectorsMixin.js +2 -2
  17. data/vendor/assets/vis/graph/graphMixins/SelectionMixin.js +72 -60
  18. data/vendor/assets/vis/graph/graphMixins/physics/BarnesHut.js +43 -18
  19. data/vendor/assets/vis/graph/graphMixins/physics/HierarchialRepulsion.js +8 -8
  20. data/vendor/assets/vis/graph/graphMixins/physics/PhysicsMixin.js +309 -129
  21. data/vendor/assets/vis/graph/graphMixins/physics/Repulsion.js +10 -10
  22. data/vendor/assets/vis/module/exports.js +1 -2
  23. data/vendor/assets/vis/module/header.js +2 -2
  24. data/vendor/assets/vis/timeline/Range.js +53 -93
  25. data/vendor/assets/vis/timeline/Timeline.js +328 -224
  26. data/vendor/assets/vis/timeline/component/Component.js +17 -95
  27. data/vendor/assets/vis/timeline/component/CurrentTime.js +54 -59
  28. data/vendor/assets/vis/timeline/component/CustomTime.js +55 -83
  29. data/vendor/assets/vis/timeline/component/Group.js +398 -75
  30. data/vendor/assets/vis/timeline/component/ItemSet.js +662 -403
  31. data/vendor/assets/vis/timeline/component/Panel.js +118 -60
  32. data/vendor/assets/vis/timeline/component/RootPanel.js +80 -132
  33. data/vendor/assets/vis/timeline/component/TimeAxis.js +191 -277
  34. data/vendor/assets/vis/timeline/component/css/item.css +16 -23
  35. data/vendor/assets/vis/timeline/component/css/itemset.css +25 -4
  36. data/vendor/assets/vis/timeline/component/css/labelset.css +34 -0
  37. data/vendor/assets/vis/timeline/component/css/panel.css +15 -1
  38. data/vendor/assets/vis/timeline/component/css/timeaxis.css +8 -8
  39. data/vendor/assets/vis/timeline/component/item/Item.js +48 -26
  40. data/vendor/assets/vis/timeline/component/item/ItemBox.js +156 -230
  41. data/vendor/assets/vis/timeline/component/item/ItemPoint.js +118 -166
  42. data/vendor/assets/vis/timeline/component/item/ItemRange.js +135 -187
  43. data/vendor/assets/vis/timeline/component/item/ItemRangeOverflow.js +29 -92
  44. data/vendor/assets/vis/timeline/stack.js +112 -0
  45. data/vendor/assets/vis/util.js +136 -38
  46. metadata +4 -18
  47. data/vendor/assets/vis/.gitignore +0 -1
  48. data/vendor/assets/vis/EventBus.js +0 -89
  49. data/vendor/assets/vis/events.js +0 -116
  50. data/vendor/assets/vis/graph/ClusterMixin.js +0 -1019
  51. data/vendor/assets/vis/graph/NavigationMixin.js +0 -245
  52. data/vendor/assets/vis/graph/SectorsMixin.js +0 -547
  53. data/vendor/assets/vis/graph/SelectionMixin.js +0 -515
  54. data/vendor/assets/vis/graph/img/downarrow.png +0 -0
  55. data/vendor/assets/vis/graph/img/leftarrow.png +0 -0
  56. data/vendor/assets/vis/graph/img/rightarrow.png +0 -0
  57. data/vendor/assets/vis/graph/img/uparrow.png +0 -0
  58. data/vendor/assets/vis/timeline/Controller.js +0 -183
  59. data/vendor/assets/vis/timeline/Stack.js +0 -190
  60. data/vendor/assets/vis/timeline/component/ContentPanel.js +0 -113
  61. data/vendor/assets/vis/timeline/component/GroupSet.js +0 -580
  62. data/vendor/assets/vis/timeline/component/css/groupset.css +0 -59
@@ -1,20 +1,15 @@
1
1
  /**
2
2
  * @constructor Group
3
- * @param {GroupSet} parent
4
3
  * @param {Number | String} groupId
5
- * @param {Object} [options] Options to set initial property values
6
- * // TODO: describe available options
7
- * @extends Component
4
+ * @param {Object} data
5
+ * @param {ItemSet} itemSet
8
6
  */
9
- function Group (parent, groupId, options) {
10
- this.id = util.randomUUID();
11
- this.parent = parent;
12
-
7
+ function Group (groupId, data, itemSet) {
13
8
  this.groupId = groupId;
14
- this.itemset = null; // ItemSet
15
- this.options = options || {};
16
- this.options.top = 0;
17
9
 
10
+ this.itemSet = itemSet;
11
+
12
+ this.dom = {};
18
13
  this.props = {
19
14
  label: {
20
15
  width: 0,
@@ -22,108 +17,436 @@ function Group (parent, groupId, options) {
22
17
  }
23
18
  };
24
19
 
25
- this.top = 0;
26
- this.left = 0;
27
- this.width = 0;
28
- this.height = 0;
29
- }
20
+ this.items = {}; // items filtered by groupId of this group
21
+ this.visibleItems = []; // items currently visible in window
22
+ this.orderedItems = { // items sorted by start and by end
23
+ byStart: [],
24
+ byEnd: []
25
+ };
30
26
 
31
- Group.prototype = new Component();
27
+ this._create();
32
28
 
33
- // TODO: comment
34
- Group.prototype.setOptions = Component.prototype.setOptions;
29
+ this.setData(data);
30
+ }
35
31
 
36
32
  /**
37
- * Get the container element of the panel, which can be used by a child to
38
- * add its own widgets.
39
- * @returns {HTMLElement} container
33
+ * Create DOM elements for the group
34
+ * @private
40
35
  */
41
- Group.prototype.getContainer = function () {
42
- return this.parent.getContainer();
36
+ Group.prototype._create = function() {
37
+ var label = document.createElement('div');
38
+ label.className = 'vlabel';
39
+ this.dom.label = label;
40
+
41
+ var inner = document.createElement('div');
42
+ inner.className = 'inner';
43
+ label.appendChild(inner);
44
+ this.dom.inner = inner;
45
+
46
+ var foreground = document.createElement('div');
47
+ foreground.className = 'group';
48
+ foreground['timeline-group'] = this;
49
+ this.dom.foreground = foreground;
50
+
51
+ this.dom.background = document.createElement('div');
52
+
53
+ this.dom.axis = document.createElement('div');
43
54
  };
44
55
 
45
56
  /**
46
- * Set item set for the group. The group will create a view on the itemset,
47
- * filtered by the groups id.
48
- * @param {DataSet | DataView} items
57
+ * Set the group data for this group
58
+ * @param {Object} data Group data, can contain properties content and className
49
59
  */
50
- Group.prototype.setItems = function setItems(items) {
51
- if (this.itemset) {
52
- // remove current item set
53
- this.itemset.hide();
54
- this.itemset.setItems();
60
+ Group.prototype.setData = function setData(data) {
61
+ // update contents
62
+ var content = data && data.content;
63
+ if (content instanceof Element) {
64
+ this.dom.inner.appendChild(content);
65
+ }
66
+ else if (content != undefined) {
67
+ this.dom.inner.innerHTML = content;
68
+ }
69
+ else {
70
+ this.dom.inner.innerHTML = this.groupId;
71
+ }
55
72
 
56
- this.parent.controller.remove(this.itemset);
57
- this.itemset = null;
73
+ // update className
74
+ var className = data && data.className;
75
+ if (className) {
76
+ util.addClassName(this.dom.label, className);
58
77
  }
78
+ };
59
79
 
60
- if (items) {
61
- var groupId = this.groupId;
80
+ /**
81
+ * Get the foreground container element
82
+ * @return {HTMLElement} foreground
83
+ */
84
+ Group.prototype.getForeground = function getForeground() {
85
+ return this.dom.foreground;
86
+ };
62
87
 
63
- var itemsetOptions = Object.create(this.options);
64
- this.itemset = new ItemSet(this, null, itemsetOptions);
65
- this.itemset.setRange(this.parent.range);
88
+ /**
89
+ * Get the background container element
90
+ * @return {HTMLElement} background
91
+ */
92
+ Group.prototype.getBackground = function getBackground() {
93
+ return this.dom.background;
94
+ };
66
95
 
67
- this.view = new DataView(items, {
68
- filter: function (item) {
69
- return item.group == groupId;
70
- }
96
+ /**
97
+ * Get the axis container element
98
+ * @return {HTMLElement} axis
99
+ */
100
+ Group.prototype.getAxis = function getAxis() {
101
+ return this.dom.axis;
102
+ };
103
+
104
+ /**
105
+ * Get the width of the group label
106
+ * @return {number} width
107
+ */
108
+ Group.prototype.getLabelWidth = function getLabelWidth() {
109
+ return this.props.label.width;
110
+ };
111
+
112
+
113
+ /**
114
+ * Repaint this group
115
+ * @param {{start: number, end: number}} range
116
+ * @param {{item: number, axis: number}} margin
117
+ * @param {boolean} [restack=false] Force restacking of all items
118
+ * @return {boolean} Returns true if the group is resized
119
+ */
120
+ Group.prototype.repaint = function repaint(range, margin, restack) {
121
+ var resized = false;
122
+
123
+ this.visibleItems = this._updateVisibleItems(this.orderedItems, this.visibleItems, range);
124
+
125
+ // reposition visible items vertically
126
+ if (this.itemSet.options.stack) { // TODO: ugly way to access options...
127
+ stack.stack(this.visibleItems, margin, restack);
128
+ }
129
+ else { // no stacking
130
+ stack.nostack(this.visibleItems, margin);
131
+ }
132
+ this.stackDirty = false;
133
+ for (var i = 0, ii = this.visibleItems.length; i < ii; i++) {
134
+ var item = this.visibleItems[i];
135
+ item.repositionY();
136
+ }
137
+
138
+ // recalculate the height of the group
139
+ var height;
140
+ var visibleItems = this.visibleItems;
141
+ if (visibleItems.length) {
142
+ var min = visibleItems[0].top;
143
+ var max = visibleItems[0].top + visibleItems[0].height;
144
+ util.forEach(visibleItems, function (item) {
145
+ min = Math.min(min, item.top);
146
+ max = Math.max(max, (item.top + item.height));
71
147
  });
72
- this.itemset.setItems(this.view);
148
+ height = (max - min) + margin.axis + margin.item;
149
+ }
150
+ else {
151
+ height = margin.axis + margin.item;
152
+ }
153
+ height = Math.max(height, this.props.label.height);
154
+
155
+ // calculate actual size and position
156
+ var foreground = this.dom.foreground;
157
+ this.top = foreground.offsetTop;
158
+ this.left = foreground.offsetLeft;
159
+ this.width = foreground.offsetWidth;
160
+ resized = util.updateProperty(this, 'height', height) || resized;
161
+
162
+ // recalculate size of label
163
+ resized = util.updateProperty(this.props.label, 'width', this.dom.inner.clientWidth) || resized;
164
+ resized = util.updateProperty(this.props.label, 'height', this.dom.inner.clientHeight) || resized;
165
+
166
+ // apply new height
167
+ foreground.style.height = height + 'px';
168
+ this.dom.label.style.height = height + 'px';
169
+
170
+ return resized;
171
+ };
73
172
 
74
- this.parent.controller.add(this.itemset);
173
+ /**
174
+ * Show this group: attach to the DOM
175
+ */
176
+ Group.prototype.show = function show() {
177
+ if (!this.dom.label.parentNode) {
178
+ this.itemSet.getLabelSet().appendChild(this.dom.label);
179
+ }
180
+
181
+ if (!this.dom.foreground.parentNode) {
182
+ this.itemSet.getForeground().appendChild(this.dom.foreground);
183
+ }
184
+
185
+ if (!this.dom.background.parentNode) {
186
+ this.itemSet.getBackground().appendChild(this.dom.background);
187
+ }
188
+
189
+ if (!this.dom.axis.parentNode) {
190
+ this.itemSet.getAxis().appendChild(this.dom.axis);
75
191
  }
76
192
  };
77
193
 
78
194
  /**
79
- * Set selected items by their id. Replaces the current selection.
80
- * Unknown id's are silently ignored.
81
- * @param {Array} [ids] An array with zero or more id's of the items to be
82
- * selected. If ids is an empty array, all items will be
83
- * unselected.
195
+ * Hide this group: remove from the DOM
84
196
  */
85
- Group.prototype.setSelection = function setSelection(ids) {
86
- if (this.itemset) this.itemset.setSelection(ids);
197
+ Group.prototype.hide = function hide() {
198
+ var label = this.dom.label;
199
+ if (label.parentNode) {
200
+ label.parentNode.removeChild(label);
201
+ }
202
+
203
+ var foreground = this.dom.foreground;
204
+ if (foreground.parentNode) {
205
+ foreground.parentNode.removeChild(foreground);
206
+ }
207
+
208
+ var background = this.dom.background;
209
+ if (background.parentNode) {
210
+ background.parentNode.removeChild(background);
211
+ }
212
+
213
+ var axis = this.dom.axis;
214
+ if (axis.parentNode) {
215
+ axis.parentNode.removeChild(axis);
216
+ }
87
217
  };
88
218
 
89
219
  /**
90
- * Get the selected items by their id
91
- * @return {Array} ids The ids of the selected items
220
+ * Add an item to the group
221
+ * @param {Item} item
92
222
  */
93
- Group.prototype.getSelection = function getSelection() {
94
- return this.itemset ? this.itemset.getSelection() : [];
223
+ Group.prototype.add = function add(item) {
224
+ this.items[item.id] = item;
225
+ item.setParent(this);
226
+
227
+ if (item instanceof ItemRange && this.visibleItems.indexOf(item) == -1) {
228
+ var range = this.itemSet.range; // TODO: not nice accessing the range like this
229
+ this._checkIfVisible(item, this.visibleItems, range);
230
+ }
231
+ };
232
+
233
+ /**
234
+ * Remove an item from the group
235
+ * @param {Item} item
236
+ */
237
+ Group.prototype.remove = function remove(item) {
238
+ delete this.items[item.id];
239
+ item.setParent(this.itemSet);
240
+
241
+ // remove from visible items
242
+ var index = this.visibleItems.indexOf(item);
243
+ if (index != -1) this.visibleItems.splice(index, 1);
244
+
245
+ // TODO: also remove from ordered items?
246
+ };
247
+
248
+ /**
249
+ * Remove an item from the corresponding DataSet
250
+ * @param {Item} item
251
+ */
252
+ Group.prototype.removeFromDataSet = function removeFromDataSet(item) {
253
+ this.itemSet.removeItem(item.id);
254
+ };
255
+
256
+ /**
257
+ * Reorder the items
258
+ */
259
+ Group.prototype.order = function order() {
260
+ var array = util.toArray(this.items);
261
+ this.orderedItems.byStart = array;
262
+ this.orderedItems.byEnd = this._constructByEndArray(array);
263
+
264
+ stack.orderByStart(this.orderedItems.byStart);
265
+ stack.orderByEnd(this.orderedItems.byEnd);
95
266
  };
96
267
 
97
268
  /**
98
- * Repaint the item
99
- * @return {Boolean} changed
269
+ * Create an array containing all items being a range (having an end date)
270
+ * @param {Item[]} array
271
+ * @returns {ItemRange[]}
272
+ * @private
100
273
  */
101
- Group.prototype.repaint = function repaint() {
102
- return false;
274
+ Group.prototype._constructByEndArray = function _constructByEndArray(array) {
275
+ var endArray = [];
276
+
277
+ for (var i = 0; i < array.length; i++) {
278
+ if (array[i] instanceof ItemRange) {
279
+ endArray.push(array[i]);
280
+ }
281
+ }
282
+ return endArray;
283
+ };
284
+
285
+ /**
286
+ * Update the visible items
287
+ * @param {{byStart: Item[], byEnd: Item[]}} orderedItems All items ordered by start date and by end date
288
+ * @param {Item[]} visibleItems The previously visible items.
289
+ * @param {{start: number, end: number}} range Visible range
290
+ * @return {Item[]} visibleItems The new visible items.
291
+ * @private
292
+ */
293
+ Group.prototype._updateVisibleItems = function _updateVisibleItems(orderedItems, visibleItems, range) {
294
+ var initialPosByStart,
295
+ newVisibleItems = [],
296
+ i;
297
+
298
+ // first check if the items that were in view previously are still in view.
299
+ // this handles the case for the ItemRange that is both before and after the current one.
300
+ if (visibleItems.length > 0) {
301
+ for (i = 0; i < visibleItems.length; i++) {
302
+ this._checkIfVisible(visibleItems[i], newVisibleItems, range);
303
+ }
304
+ }
305
+
306
+ // If there were no visible items previously, use binarySearch to find a visible ItemPoint or ItemRange (based on startTime)
307
+ if (newVisibleItems.length == 0) {
308
+ initialPosByStart = this._binarySearch(orderedItems, range, false);
309
+ }
310
+ else {
311
+ initialPosByStart = orderedItems.byStart.indexOf(newVisibleItems[0]);
312
+ }
313
+
314
+ // use visible search to find a visible ItemRange (only based on endTime)
315
+ var initialPosByEnd = this._binarySearch(orderedItems, range, true);
316
+
317
+ // if we found a initial ID to use, trace it up and down until we meet an invisible item.
318
+ if (initialPosByStart != -1) {
319
+ for (i = initialPosByStart; i >= 0; i--) {
320
+ if (this._checkIfInvisible(orderedItems.byStart[i], newVisibleItems, range)) {break;}
321
+ }
322
+ for (i = initialPosByStart + 1; i < orderedItems.byStart.length; i++) {
323
+ if (this._checkIfInvisible(orderedItems.byStart[i], newVisibleItems, range)) {break;}
324
+ }
325
+ }
326
+
327
+ // if we found a initial ID to use, trace it up and down until we meet an invisible item.
328
+ if (initialPosByEnd != -1) {
329
+ for (i = initialPosByEnd; i >= 0; i--) {
330
+ if (this._checkIfInvisible(orderedItems.byEnd[i], newVisibleItems, range)) {break;}
331
+ }
332
+ for (i = initialPosByEnd + 1; i < orderedItems.byEnd.length; i++) {
333
+ if (this._checkIfInvisible(orderedItems.byEnd[i], newVisibleItems, range)) {break;}
334
+ }
335
+ }
336
+
337
+ return newVisibleItems;
103
338
  };
104
339
 
105
340
  /**
106
- * Reflow the item
107
- * @return {Boolean} resized
341
+ * This function does a binary search for a visible item. The user can select either the this.orderedItems.byStart or .byEnd
342
+ * arrays. This is done by giving a boolean value true if you want to use the byEnd.
343
+ * This is done to be able to select the correct if statement (we do not want to check if an item is visible, we want to check
344
+ * if the time we selected (start or end) is within the current range).
345
+ *
346
+ * The trick is that every interval has to either enter the screen at the initial load or by dragging. The case of the ItemRange that is
347
+ * before and after the current range is handled by simply checking if it was in view before and if it is again. For all the rest,
348
+ * either the start OR end time has to be in the range.
349
+ *
350
+ * @param {{byStart: Item[], byEnd: Item[]}} orderedItems
351
+ * @param {{start: number, end: number}} range
352
+ * @param {Boolean} byEnd
353
+ * @returns {number}
354
+ * @private
108
355
  */
109
- Group.prototype.reflow = function reflow() {
110
- var changed = 0,
111
- update = util.updateProperty;
356
+ Group.prototype._binarySearch = function _binarySearch(orderedItems, range, byEnd) {
357
+ var array = [];
358
+ var byTime = byEnd ? 'end' : 'start';
359
+ if (byEnd == true) {array = orderedItems.byEnd; }
360
+ else {array = orderedItems.byStart;}
361
+
362
+ var interval = range.end - range.start;
112
363
 
113
- changed += update(this, 'top', this.itemset ? this.itemset.top : 0);
114
- changed += update(this, 'height', this.itemset ? this.itemset.height : 0);
364
+ var found = false;
365
+ var low = 0;
366
+ var high = array.length;
367
+ var guess = Math.floor(0.5*(high+low));
368
+ var newGuess;
115
369
 
116
- // TODO: reckon with the height of the group label
370
+ if (high == 0) {guess = -1;}
371
+ else if (high == 1) {
372
+ if ((array[guess].data[byTime] > range.start - interval) && (array[guess].data[byTime] < range.end)) {
373
+ guess = 0;
374
+ }
375
+ else {
376
+ guess = -1;
377
+ }
378
+ }
379
+ else {
380
+ high -= 1;
381
+ while (found == false) {
382
+ if ((array[guess].data[byTime] > range.start - interval) && (array[guess].data[byTime] < range.end)) {
383
+ found = true;
384
+ }
385
+ else {
386
+ if (array[guess].data[byTime] < range.start - interval) { // it is too small --> increase low
387
+ low = Math.floor(0.5*(high+low));
388
+ }
389
+ else { // it is too big --> decrease high
390
+ high = Math.floor(0.5*(high+low));
391
+ }
392
+ newGuess = Math.floor(0.5*(high+low));
393
+ // not in list;
394
+ if (guess == newGuess) {
395
+ guess = -1;
396
+ found = true;
397
+ }
398
+ else {
399
+ guess = newGuess;
400
+ }
401
+ }
402
+ }
403
+ }
404
+ return guess;
405
+ };
117
406
 
118
- if (this.label) {
119
- var inner = this.label.firstChild;
120
- changed += update(this.props.label, 'width', inner.clientWidth);
121
- changed += update(this.props.label, 'height', inner.clientHeight);
407
+ /**
408
+ * this function checks if an item is invisible. If it is NOT we make it visible
409
+ * and add it to the global visible items. If it is, return true.
410
+ *
411
+ * @param {Item} item
412
+ * @param {Item[]} visibleItems
413
+ * @param {{start:number, end:number}} range
414
+ * @returns {boolean}
415
+ * @private
416
+ */
417
+ Group.prototype._checkIfInvisible = function _checkIfInvisible(item, visibleItems, range) {
418
+ if (item.isVisible(range)) {
419
+ if (!item.displayed) item.show();
420
+ item.repositionX();
421
+ if (visibleItems.indexOf(item) == -1) {
422
+ visibleItems.push(item);
423
+ }
424
+ return false;
122
425
  }
123
426
  else {
124
- changed += update(this.props.label, 'width', 0);
125
- changed += update(this.props.label, 'height', 0);
427
+ return true;
126
428
  }
429
+ };
127
430
 
128
- return (changed > 0);
431
+ /**
432
+ * this function is very similar to the _checkIfInvisible() but it does not
433
+ * return booleans, hides the item if it should not be seen and always adds to
434
+ * the visibleItems.
435
+ * this one is for brute forcing and hiding.
436
+ *
437
+ * @param {Item} item
438
+ * @param {Array} visibleItems
439
+ * @param {{start:number, end:number}} range
440
+ * @private
441
+ */
442
+ Group.prototype._checkIfVisible = function _checkIfVisible(item, visibleItems, range) {
443
+ if (item.isVisible(range)) {
444
+ if (!item.displayed) item.show();
445
+ // reposition item horizontally
446
+ item.repositionX();
447
+ visibleItems.push(item);
448
+ }
449
+ else {
450
+ if (item.displayed) item.hide();
451
+ }
129
452
  };