UNPKG

10.8 kBJavaScriptView Raw
1// Utility functions for ordering and stacking of items
2var EPSILON = 0.001; // used when checking collisions, to prevent round-off errors
3
4/**
5 * Order items by their start data
6 * @param {Item[]} items
7 */
8exports.orderByStart = function(items) {
9 items.sort(function (a, b) {
10 return a.data.start - b.data.start;
11 });
12};
13
14/**
15 * Order items by their end date. If they have no end date, their start date
16 * is used.
17 * @param {Item[]} items
18 */
19exports.orderByEnd = function(items) {
20 items.sort(function (a, b) {
21 var aTime = ('end' in a.data) ? a.data.end : a.data.start,
22 bTime = ('end' in b.data) ? b.data.end : b.data.start;
23
24 return aTime - bTime;
25 });
26};
27
28/**
29 * Adjust vertical positions of the items such that they don't overlap each
30 * other.
31 * @param {Item[]} items
32 * All visible items
33 * @param {{item: {horizontal: number, vertical: number}, axis: number}} margin
34 * Margins between items and between items and the axis.
35 * @param {boolean} [force=false]
36 * If true, all items will be repositioned. If false (default), only
37 * items having a top===null will be re-stacked
38 * @param {function} shouldBailItemsRedrawFunction
39 * bailing function
40 * @return {boolean} shouldBail
41 */
42exports.stack = function(items, margin, force, shouldBailItemsRedrawFunction) {
43 if (force) {
44 // reset top position of all items
45 for (var i = 0; i < items.length; i++) {
46 items[i].top = null;
47 }
48 }
49
50 // calculate new, non-overlapping positions
51 for (var i = 0; i < items.length; i++) { // eslint-disable-line no-redeclare
52 var item = items[i];
53 if (item.stack && item.top === null) {
54 // initialize top position
55 item.top = margin.axis;
56 var shouldBail = false;
57
58 do {
59 // TODO: optimize checking for overlap. when there is a gap without items,
60 // you only need to check for items from the next item on, not from zero
61 var collidingItem = null;
62 for (var j = 0, jj = items.length; j < jj; j++) {
63 var other = items[j];
64 shouldBail = shouldBailItemsRedrawFunction() || false;
65
66 if (shouldBail) { return true; }
67
68 if (other.top !== null && other !== item && other.stack && exports.collision(item, other, margin.item, other.options.rtl)) {
69 collidingItem = other;
70 break;
71 }
72 }
73
74 if (collidingItem != null) {
75 // There is a collision. Reposition the items above the colliding element
76 item.top = collidingItem.top + collidingItem.height + margin.item.vertical;
77 }
78 } while (collidingItem);
79 }
80 }
81 return shouldBail;
82};
83
84/**
85 * Adjust vertical positions of the items within a single subgroup such that they
86 * don't overlap each other.
87 * @param {Item[]} items
88 * All items withina subgroup
89 * @param {{item: {horizontal: number, vertical: number}, axis: number}} margin
90 * Margins between items and between items and the axis.
91 * @param {subgroup} subgroup
92 * The subgroup that is being stacked
93 */
94exports.substack = function (items, margin, subgroup) {
95 for (var i = 0; i < items.length; i++) {
96 items[i].top = null;
97 }
98
99 // Set the initial height
100 var subgroupHeight = subgroup.height;
101
102 // calculate new, non-overlapping positions
103 for (i = 0; i < items.length; i++) {
104 var item = items[i];
105
106 if (item.stack && item.top === null) {
107 // initialize top position
108 item.top = item.baseTop;//margin.axis + item.baseTop;
109
110 do {
111 // TODO: optimize checking for overlap. when there is a gap without items,
112 // you only need to check for items from the next item on, not from zero
113 var collidingItem = null;
114 for (var j = 0, jj = items.length; j < jj; j++) {
115 var other = items[j];
116 if (other.top !== null && other !== item /*&& other.stack*/ && exports.collision(item, other, margin.item, other.options.rtl)) {
117 collidingItem = other;
118 break;
119 }
120 }
121
122 if (collidingItem != null) {
123 // There is a collision. Reposition the items above the colliding element
124 item.top = collidingItem.top + collidingItem.height + margin.item.vertical;// + item.baseTop;
125 }
126
127 if (item.top + item.height > subgroupHeight) {
128 subgroupHeight = item.top + item.height;
129 }
130 } while (collidingItem);
131 }
132 }
133
134 // Set the new height
135 subgroup.height = subgroupHeight - subgroup.top + 0.5 * margin.item.vertical;
136};
137
138/**
139 * Adjust vertical positions of the items without stacking them
140 * @param {Item[]} items
141 * All visible items
142 * @param {{item: {horizontal: number, vertical: number}, axis: number}} margin
143 * Margins between items and between items and the axis.
144 * @param {subgroups[]} subgroups
145 * All subgroups
146 * @param {boolean} stackSubgroups
147 */
148exports.nostack = function(items, margin, subgroups, stackSubgroups) {
149 for (var i = 0; i < items.length; i++) {
150 if (items[i].data.subgroup == undefined) {
151 items[i].top = margin.item.vertical;
152 } else if (items[i].data.subgroup !== undefined && stackSubgroups) {
153 var newTop = 0;
154 for (var subgroup in subgroups) {
155 if (subgroups.hasOwnProperty(subgroup)) {
156 if (subgroups[subgroup].visible == true && subgroups[subgroup].index < subgroups[items[i].data.subgroup].index) {
157 newTop += subgroups[subgroup].height;
158 subgroups[items[i].data.subgroup].top = newTop;
159 }
160 }
161 }
162 items[i].top = newTop + 0.5 * margin.item.vertical;
163 }
164 }
165 if (!stackSubgroups) {
166 exports.stackSubgroups(items, margin, subgroups)
167 }
168};
169
170/**
171 * Adjust vertical positions of the subgroups such that they don't overlap each
172 * other.
173 * @param {Array.<vis.Item>} items
174 * @param {{item: {horizontal: number, vertical: number}, axis: number}} margin Margins between items and between items and the axis.
175 * @param {subgroups[]} subgroups
176 * All subgroups
177 */
178exports.stackSubgroups = function(items, margin, subgroups) {
179 for (var subgroup in subgroups) {
180 if (subgroups.hasOwnProperty(subgroup)) {
181
182
183 subgroups[subgroup].top = 0;
184 do {
185 // TODO: optimize checking for overlap. when there is a gap without items,
186 // you only need to check for items from the next item on, not from zero
187 var collidingItem = null;
188 for (var otherSubgroup in subgroups) {
189 if (subgroups[otherSubgroup].top !== null && otherSubgroup !== subgroup && subgroups[subgroup].index > subgroups[otherSubgroup].index && exports.collisionByTimes(subgroups[subgroup], subgroups[otherSubgroup])) {
190 collidingItem = subgroups[otherSubgroup];
191 break;
192 }
193 }
194
195 if (collidingItem != null) {
196 // There is a collision. Reposition the subgroups above the colliding element
197 subgroups[subgroup].top = collidingItem.top + collidingItem.height;
198 }
199 } while (collidingItem);
200 }
201 }
202 for (var i = 0; i < items.length; i++) {
203 if (items[i].data.subgroup !== undefined) {
204 items[i].top = subgroups[items[i].data.subgroup].top + 0.5 * margin.item.vertical;
205 }
206 }
207};
208
209/**
210 * Adjust vertical positions of the subgroups such that they don't overlap each
211 * other, then stacks the contents of each subgroup individually.
212 * @param {Item[]} subgroupItems
213 * All the items in a subgroup
214 * @param {{item: {horizontal: number, vertical: number}, axis: number}} margin
215 * Margins between items and between items and the axis.
216 * @param {subgroups[]} subgroups
217 * All subgroups
218 */
219exports.stackSubgroupsWithInnerStack = function (subgroupItems, margin, subgroups) {
220 var doSubStack = false;
221
222 // Run subgroups in their order (if any)
223 var subgroupOrder = [];
224
225 for(var subgroup in subgroups) {
226 if (subgroups[subgroup].hasOwnProperty("index")) {
227 subgroupOrder[subgroups[subgroup].index] = subgroup;
228 }
229 else {
230 subgroupOrder.push(subgroup);
231 }
232 }
233
234 for(var j = 0; j < subgroupOrder.length; j++) {
235 subgroup = subgroupOrder[j];
236 if (subgroups.hasOwnProperty(subgroup)) {
237
238 doSubStack = doSubStack || subgroups[subgroup].stack;
239 subgroups[subgroup].top = 0;
240
241 for (var otherSubgroup in subgroups) {
242 if (subgroups[otherSubgroup].visible && subgroups[subgroup].index > subgroups[otherSubgroup].index) {
243 subgroups[subgroup].top += subgroups[otherSubgroup].height;
244 }
245 }
246
247 var items = subgroupItems[subgroup];
248 for(var i = 0; i < items.length; i++) {
249 if (items[i].data.subgroup !== undefined) {
250 items[i].top = subgroups[items[i].data.subgroup].top + 0.5 * margin.item.vertical;
251
252 if (subgroups[subgroup].stack) {
253 items[i].baseTop = items[i].top;
254 }
255 }
256 }
257
258 if (doSubStack && subgroups[subgroup].stack) {
259 exports.substack(subgroupItems[subgroup], margin, subgroups[subgroup]);
260 }
261 }
262 }
263};
264
265/**
266 * Test if the two provided items collide
267 * The items must have parameters left, width, top, and height.
268 * @param {Item} a The first item
269 * @param {Item} b The second item
270 * @param {{horizontal: number, vertical: number}} margin
271 * An object containing a horizontal and vertical
272 * minimum required margin.
273 * @param {boolean} rtl
274 * @return {boolean} true if a and b collide, else false
275 */
276exports.collision = function(a, b, margin, rtl) {
277 if (rtl) {
278 return ((a.right - margin.horizontal + EPSILON) < (b.right + b.width) &&
279 (a.right + a.width + margin.horizontal - EPSILON) > b.right &&
280 (a.top - margin.vertical + EPSILON) < (b.top + b.height) &&
281 (a.top + a.height + margin.vertical - EPSILON) > b.top);
282 } else {
283 return ((a.left - margin.horizontal + EPSILON) < (b.left + b.width) &&
284 (a.left + a.width + margin.horizontal - EPSILON) > b.left &&
285 (a.top - margin.vertical + EPSILON) < (b.top + b.height) &&
286 (a.top + a.height + margin.vertical - EPSILON) > b.top);
287 }
288};
289
290/**
291 * Test if the two provided objects collide
292 * The objects must have parameters start, end, top, and height.
293 * @param {Object} a The first Object
294 * @param {Object} b The second Object
295 * @return {boolean} true if a and b collide, else false
296 */
297exports.collisionByTimes = function(a, b) {
298 return (
299 (a.start <= b.start && a.end >= b.start && a.top < (b.top + b.height) && (a.top + a.height) > b.top ) ||
300 (b.start <= a.start && b.end >= a.start && b.top < (a.top + a.height) && (b.top + b.height) > a.top )
301 )
302}
\No newline at end of file