UNPKG

16.7 kBJavaScriptView Raw
1
2/**
3 * used in Core to convert the options into a volatile variable
4 *
5 * @param {function} moment
6 * @param {Object} body
7 * @param {Array | Object} hiddenDates
8 * @returns {number}
9 */
10exports.convertHiddenOptions = function(moment, body, hiddenDates) {
11 if (hiddenDates && !Array.isArray(hiddenDates)) {
12 return exports.convertHiddenOptions(moment, body, [hiddenDates])
13 }
14
15 body.hiddenDates = [];
16 if (hiddenDates) {
17 if (Array.isArray(hiddenDates) == true) {
18 for (var i = 0; i < hiddenDates.length; i++) {
19 if (hiddenDates[i].repeat === undefined) {
20 var dateItem = {};
21 dateItem.start = moment(hiddenDates[i].start).toDate().valueOf();
22 dateItem.end = moment(hiddenDates[i].end).toDate().valueOf();
23 body.hiddenDates.push(dateItem);
24 }
25 }
26 body.hiddenDates.sort(function (a, b) {
27 return a.start - b.start;
28 }); // sort by start time
29 }
30 }
31};
32
33
34/**
35 * create new entrees for the repeating hidden dates
36 *
37 * @param {function} moment
38 * @param {Object} body
39 * @param {Array | Object} hiddenDates
40 * @returns {null}
41 */
42exports.updateHiddenDates = function (moment, body, hiddenDates) {
43 if (hiddenDates && !Array.isArray(hiddenDates)) {
44 return exports.updateHiddenDates(moment, body, [hiddenDates])
45 }
46
47 if (hiddenDates && body.domProps.centerContainer.width !== undefined) {
48 exports.convertHiddenOptions(moment, body, hiddenDates);
49
50 var start = moment(body.range.start);
51 var end = moment(body.range.end);
52
53 var totalRange = (body.range.end - body.range.start);
54 var pixelTime = totalRange / body.domProps.centerContainer.width;
55
56 for (var i = 0; i < hiddenDates.length; i++) {
57 if (hiddenDates[i].repeat !== undefined) {
58 var startDate = moment(hiddenDates[i].start);
59 var endDate = moment(hiddenDates[i].end);
60
61 if (startDate._d == "Invalid Date") {
62 throw new Error("Supplied start date is not valid: " + hiddenDates[i].start);
63 }
64 if (endDate._d == "Invalid Date") {
65 throw new Error("Supplied end date is not valid: " + hiddenDates[i].end);
66 }
67
68 var duration = endDate - startDate;
69 if (duration >= 4 * pixelTime) {
70
71 var offset = 0;
72 var runUntil = end.clone();
73 switch (hiddenDates[i].repeat) {
74 case "daily": // case of time
75 if (startDate.day() != endDate.day()) {
76 offset = 1;
77 }
78 startDate.dayOfYear(start.dayOfYear());
79 startDate.year(start.year());
80 startDate.subtract(7,'days');
81
82 endDate.dayOfYear(start.dayOfYear());
83 endDate.year(start.year());
84 endDate.subtract(7 - offset,'days');
85
86 runUntil.add(1, 'weeks');
87 break;
88 case "weekly":
89 var dayOffset = endDate.diff(startDate,'days');
90 var day = startDate.day();
91
92 // set the start date to the range.start
93 startDate.date(start.date());
94 startDate.month(start.month());
95 startDate.year(start.year());
96 endDate = startDate.clone();
97
98 // force
99 startDate.day(day);
100 endDate.day(day);
101 endDate.add(dayOffset,'days');
102
103 startDate.subtract(1,'weeks');
104 endDate.subtract(1,'weeks');
105
106 runUntil.add(1, 'weeks');
107 break;
108 case "monthly":
109 if (startDate.month() != endDate.month()) {
110 offset = 1;
111 }
112 startDate.month(start.month());
113 startDate.year(start.year());
114 startDate.subtract(1,'months');
115
116 endDate.month(start.month());
117 endDate.year(start.year());
118 endDate.subtract(1,'months');
119 endDate.add(offset,'months');
120
121 runUntil.add(1, 'months');
122 break;
123 case "yearly":
124 if (startDate.year() != endDate.year()) {
125 offset = 1;
126 }
127 startDate.year(start.year());
128 startDate.subtract(1,'years');
129 endDate.year(start.year());
130 endDate.subtract(1,'years');
131 endDate.add(offset,'years');
132
133 runUntil.add(1, 'years');
134 break;
135 default:
136 console.log("Wrong repeat format, allowed are: daily, weekly, monthly, yearly. Given:", hiddenDates[i].repeat);
137 return;
138 }
139 while (startDate < runUntil) {
140 body.hiddenDates.push({start: startDate.valueOf(), end: endDate.valueOf()});
141 switch (hiddenDates[i].repeat) {
142 case "daily":
143 startDate.add(1, 'days');
144 endDate.add(1, 'days');
145 break;
146 case "weekly":
147 startDate.add(1, 'weeks');
148 endDate.add(1, 'weeks');
149 break;
150 case "monthly":
151 startDate.add(1, 'months');
152 endDate.add(1, 'months');
153 break;
154 case "yearly":
155 startDate.add(1, 'y');
156 endDate.add(1, 'y');
157 break;
158 default:
159 console.log("Wrong repeat format, allowed are: daily, weekly, monthly, yearly. Given:", hiddenDates[i].repeat);
160 return;
161 }
162 }
163 body.hiddenDates.push({start: startDate.valueOf(), end: endDate.valueOf()});
164 }
165 }
166 }
167 // remove duplicates, merge where possible
168 exports.removeDuplicates(body);
169 // ensure the new positions are not on hidden dates
170 var startHidden = exports.isHidden(body.range.start, body.hiddenDates);
171 var endHidden = exports.isHidden(body.range.end,body.hiddenDates);
172 var rangeStart = body.range.start;
173 var rangeEnd = body.range.end;
174 if (startHidden.hidden == true) {rangeStart = body.range.startToFront == true ? startHidden.startDate - 1 : startHidden.endDate + 1;}
175 if (endHidden.hidden == true) {rangeEnd = body.range.endToFront == true ? endHidden.startDate - 1 : endHidden.endDate + 1;}
176 if (startHidden.hidden == true || endHidden.hidden == true) {
177 body.range._applyRange(rangeStart, rangeEnd);
178 }
179 }
180
181};
182
183
184/**
185 * remove duplicates from the hidden dates list. Duplicates are evil. They mess everything up.
186 * Scales with N^2
187 *
188 * @param {Object} body
189 */
190exports.removeDuplicates = function(body) {
191 var hiddenDates = body.hiddenDates;
192 var safeDates = [];
193 for (var i = 0; i < hiddenDates.length; i++) {
194 for (var j = 0; j < hiddenDates.length; j++) {
195 if (i != j && hiddenDates[j].remove != true && hiddenDates[i].remove != true) {
196 // j inside i
197 if (hiddenDates[j].start >= hiddenDates[i].start && hiddenDates[j].end <= hiddenDates[i].end) {
198 hiddenDates[j].remove = true;
199 }
200 // j start inside i
201 else if (hiddenDates[j].start >= hiddenDates[i].start && hiddenDates[j].start <= hiddenDates[i].end) {
202 hiddenDates[i].end = hiddenDates[j].end;
203 hiddenDates[j].remove = true;
204 }
205 // j end inside i
206 else if (hiddenDates[j].end >= hiddenDates[i].start && hiddenDates[j].end <= hiddenDates[i].end) {
207 hiddenDates[i].start = hiddenDates[j].start;
208 hiddenDates[j].remove = true;
209 }
210 }
211 }
212 }
213
214 for (i = 0; i < hiddenDates.length; i++) {
215 if (hiddenDates[i].remove !== true) {
216 safeDates.push(hiddenDates[i]);
217 }
218 }
219
220 body.hiddenDates = safeDates;
221 body.hiddenDates.sort(function (a, b) {
222 return a.start - b.start;
223 }); // sort by start time
224};
225
226exports.printDates = function(dates) {
227 for (var i =0; i < dates.length; i++) {
228 console.log(i, new Date(dates[i].start),new Date(dates[i].end), dates[i].start, dates[i].end, dates[i].remove);
229 }
230};
231
232/**
233 * Used in TimeStep to avoid the hidden times.
234 * @param {function} moment
235 * @param {TimeStep} timeStep
236 * @param {Date} previousTime
237 */
238exports.stepOverHiddenDates = function(moment, timeStep, previousTime) {
239 var stepInHidden = false;
240 var currentValue = timeStep.current.valueOf();
241 for (var i = 0; i < timeStep.hiddenDates.length; i++) {
242 var startDate = timeStep.hiddenDates[i].start;
243 var endDate = timeStep.hiddenDates[i].end;
244 if (currentValue >= startDate && currentValue < endDate) {
245 stepInHidden = true;
246 break;
247 }
248 }
249
250 if (stepInHidden == true && currentValue < timeStep._end.valueOf() && currentValue != previousTime) {
251 var prevValue = moment(previousTime);
252 var newValue = moment(endDate);
253 //check if the next step should be major
254 if (prevValue.year() != newValue.year()) {timeStep.switchedYear = true;}
255 else if (prevValue.month() != newValue.month()) {timeStep.switchedMonth = true;}
256 else if (prevValue.dayOfYear() != newValue.dayOfYear()) {timeStep.switchedDay = true;}
257
258 timeStep.current = newValue;
259 }
260};
261
262
263///**
264// * Used in TimeStep to avoid the hidden times.
265// * @param timeStep
266// * @param previousTime
267// */
268//exports.checkFirstStep = function(timeStep) {
269// var stepInHidden = false;
270// var currentValue = timeStep.current.valueOf();
271// for (var i = 0; i < timeStep.hiddenDates.length; i++) {
272// var startDate = timeStep.hiddenDates[i].start;
273// var endDate = timeStep.hiddenDates[i].end;
274// if (currentValue >= startDate && currentValue < endDate) {
275// stepInHidden = true;
276// break;
277// }
278// }
279//
280// if (stepInHidden == true && currentValue <= timeStep._end.valueOf()) {
281// var newValue = moment(endDate);
282// timeStep.current = newValue.toDate();
283// }
284//};
285
286/**
287 * replaces the Core toScreen methods
288 *
289 * @param {vis.Core} Core
290 * @param {Date} time
291 * @param {number} width
292 * @returns {number}
293 */
294exports.toScreen = function (Core, time, width) {
295 var conversion;
296 if (Core.body.hiddenDates.length == 0) {
297 conversion = Core.range.conversion(width);
298 return (time.valueOf() - conversion.offset) * conversion.scale;
299 } else {
300 var hidden = exports.isHidden(time, Core.body.hiddenDates);
301 if (hidden.hidden == true) {
302 time = hidden.startDate;
303 }
304
305 var duration = exports.getHiddenDurationBetween(Core.body.hiddenDates, Core.range.start, Core.range.end);
306 if (time < Core.range.start) {
307 conversion = Core.range.conversion(width, duration);
308 var hiddenBeforeStart = exports.getHiddenDurationBeforeStart(Core.body.hiddenDates, time, conversion.offset);
309 time = Core.options.moment(time).toDate().valueOf();
310 time = time + hiddenBeforeStart;
311 return -(conversion.offset - time.valueOf()) * conversion.scale;
312
313 } else if (time > Core.range.end) {
314 var rangeAfterEnd = {start: Core.range.start, end: time};
315 time = exports.correctTimeForHidden(Core.options.moment, Core.body.hiddenDates, rangeAfterEnd, time);
316 conversion = Core.range.conversion(width, duration);
317 return (time.valueOf() - conversion.offset) * conversion.scale;
318
319 } else {
320 time = exports.correctTimeForHidden(Core.options.moment, Core.body.hiddenDates, Core.range, time);
321 conversion = Core.range.conversion(width, duration);
322 return (time.valueOf() - conversion.offset) * conversion.scale;
323 }
324 }
325 };
326
327
328/**
329 * Replaces the core toTime methods
330 *
331 * @param {vis.Core} Core
332 * @param {number} x
333 * @param {number} width
334 * @returns {Date}
335 */
336exports.toTime = function(Core, x, width) {
337 if (Core.body.hiddenDates.length == 0) {
338 var conversion = Core.range.conversion(width);
339 return new Date(x / conversion.scale + conversion.offset);
340 }
341 else {
342 var hiddenDuration = exports.getHiddenDurationBetween(Core.body.hiddenDates, Core.range.start, Core.range.end);
343 var totalDuration = Core.range.end - Core.range.start - hiddenDuration;
344 var partialDuration = totalDuration * x / width;
345 var accumulatedHiddenDuration = exports.getAccumulatedHiddenDuration(Core.body.hiddenDates, Core.range, partialDuration);
346
347 return new Date(accumulatedHiddenDuration + partialDuration + Core.range.start);
348 }
349};
350
351
352/**
353 * Support function
354 *
355 * @param {Array.<{start: Window.start, end: *}>} hiddenDates
356 * @param {number} start
357 * @param {number} end
358 * @returns {number}
359 */
360exports.getHiddenDurationBetween = function(hiddenDates, start, end) {
361 var duration = 0;
362 for (var i = 0; i < hiddenDates.length; i++) {
363 var startDate = hiddenDates[i].start;
364 var endDate = hiddenDates[i].end;
365 // if time after the cutout, and the
366 if (startDate >= start && endDate < end) {
367 duration += endDate - startDate;
368 }
369 }
370 return duration;
371};
372
373/**
374 * Support function
375 *
376 * @param {Array.<{start: Window.start, end: *}>} hiddenDates
377 * @param {number} start
378 * @param {number} end
379 * @returns {number}
380 */
381exports.getHiddenDurationBeforeStart = function (hiddenDates, start, end) {
382 var duration = 0;
383 for (var i = 0; i < hiddenDates.length; i++) {
384 var startDate = hiddenDates[i].start;
385 var endDate = hiddenDates[i].end;
386
387 if (startDate >= start && endDate <= end) {
388 duration += endDate - startDate;
389 }
390 }
391 return duration;
392};
393
394
395/**
396 * Support function
397 * @param {function} moment
398 * @param {Array.<{start: Window.start, end: *}>} hiddenDates
399 * @param {{start: number, end: number}} range
400 * @param {Date} time
401 * @returns {number}
402 */
403exports.correctTimeForHidden = function(moment, hiddenDates, range, time) {
404 time = moment(time).toDate().valueOf();
405 time -= exports.getHiddenDurationBefore(moment, hiddenDates,range,time);
406 return time;
407};
408
409exports.getHiddenDurationBefore = function(moment, hiddenDates, range, time) {
410 var timeOffset = 0;
411 time = moment(time).toDate().valueOf();
412
413 for (var i = 0; i < hiddenDates.length; i++) {
414 var startDate = hiddenDates[i].start;
415 var endDate = hiddenDates[i].end;
416 // if time after the cutout, and the
417 if (startDate >= range.start && endDate < range.end) {
418 if (time >= endDate) {
419 timeOffset += (endDate - startDate);
420 }
421 }
422 }
423 return timeOffset;
424};
425
426/**
427 * sum the duration from start to finish, including the hidden duration,
428 * until the required amount has been reached, return the accumulated hidden duration
429 * @param {Array.<{start: Window.start, end: *}>} hiddenDates
430 * @param {{start: number, end: number}} range
431 * @param {number} [requiredDuration=0]
432 * @returns {number}
433 */
434exports.getAccumulatedHiddenDuration = function(hiddenDates, range, requiredDuration) {
435 var hiddenDuration = 0;
436 var duration = 0;
437 var previousPoint = range.start;
438 //exports.printDates(hiddenDates)
439 for (var i = 0; i < hiddenDates.length; i++) {
440 var startDate = hiddenDates[i].start;
441 var endDate = hiddenDates[i].end;
442 // if time after the cutout, and the
443 if (startDate >= range.start && endDate < range.end) {
444 duration += startDate - previousPoint;
445 previousPoint = endDate;
446 if (duration >= requiredDuration) {
447 break;
448 }
449 else {
450 hiddenDuration += endDate - startDate;
451 }
452 }
453 }
454
455 return hiddenDuration;
456};
457
458
459
460/**
461 * used to step over to either side of a hidden block. Correction is disabled on tablets, might be set to true
462 * @param {Array.<{start: Window.start, end: *}>} hiddenDates
463 * @param {Date} time
464 * @param {number} direction
465 * @param {boolean} correctionEnabled
466 * @returns {Date|number}
467 */
468exports.snapAwayFromHidden = function(hiddenDates, time, direction, correctionEnabled) {
469 var isHidden = exports.isHidden(time, hiddenDates);
470 if (isHidden.hidden == true) {
471 if (direction < 0) {
472 if (correctionEnabled == true) {
473 return isHidden.startDate - (isHidden.endDate - time) - 1;
474 }
475 else {
476 return isHidden.startDate - 1;
477 }
478 }
479 else {
480 if (correctionEnabled == true) {
481 return isHidden.endDate + (time - isHidden.startDate) + 1;
482 }
483 else {
484 return isHidden.endDate + 1;
485 }
486 }
487 }
488 else {
489 return time;
490 }
491
492};
493
494
495/**
496 * Check if a time is hidden
497 *
498 * @param {Date} time
499 * @param {Array.<{start: Window.start, end: *}>} hiddenDates
500 * @returns {{hidden: boolean, startDate: Window.start, endDate: *}}
501 */
502exports.isHidden = function(time, hiddenDates) {
503 for (var i = 0; i < hiddenDates.length; i++) {
504 var startDate = hiddenDates[i].start;
505 var endDate = hiddenDates[i].end;
506
507 if (time >= startDate && time < endDate) { // if the start is entering a hidden zone
508 return {hidden: true, startDate: startDate, endDate: endDate};
509 }
510 }
511 return {hidden: false, startDate: startDate, endDate: endDate};
512};