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.
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
  };