UNPKG

1.2 MBJavaScriptView Raw
1/**
2 * vis-timeline - timeline-graph2d
3 * https://github.com/visjs/vis-timeline
4 *
5 * Create a fully customizable, interactive timeline with items and ranges.
6 *
7 * @version 5.1.0
8 * @date 2019-08-02T15:25:33Z
9 *
10 * @copyright (c) 2011-2017 Almende B.V, http://almende.com
11 * @copyright (c) 2018-2019 visjs contributors, https://github.com/visjs
12 *
13 * @license
14 * vis.js is dual licensed under both
15 *
16 * 1. The Apache 2.0 License
17 * http://www.apache.org/licenses/LICENSE-2.0
18 *
19 * and
20 *
21 * 2. The MIT License
22 * http://opensource.org/licenses/MIT
23 *
24 * vis.js may be distributed under either license.
25 */
26function _defineProperty(obj, key, value) {
27 if (key in obj) {
28 Object.defineProperty(obj, key, {
29 value: value,
30 enumerable: true,
31 configurable: true,
32 writable: true
33 });
34 } else {
35 obj[key] = value;
36 }
37
38 return obj;
39}
40
41var defineProperty = _defineProperty;
42
43function _arrayWithoutHoles(arr) {
44 if (Array.isArray(arr)) {
45 for (var i = 0, arr2 = new Array(arr.length); i < arr.length; i++) {
46 arr2[i] = arr[i];
47 }
48
49 return arr2;
50 }
51}
52
53var arrayWithoutHoles = _arrayWithoutHoles;
54
55function _iterableToArray(iter) {
56 if (Symbol.iterator in Object(iter) || Object.prototype.toString.call(iter) === "[object Arguments]") return Array.from(iter);
57}
58
59var iterableToArray = _iterableToArray;
60
61function _nonIterableSpread() {
62 throw new TypeError("Invalid attempt to spread non-iterable instance");
63}
64
65var nonIterableSpread = _nonIterableSpread;
66
67function _toConsumableArray(arr) {
68 return arrayWithoutHoles(arr) || iterableToArray(arr) || nonIterableSpread();
69}
70
71var toConsumableArray = _toConsumableArray;
72var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
73
74function commonjsRequire() {
75 throw new Error('Dynamic requires are not currently supported by rollup-plugin-commonjs');
76}
77
78function createCommonjsModule(fn, module) {
79 return module = {
80 exports: {}
81 }, fn(module, module.exports), module.exports;
82}
83
84var _typeof_1 = createCommonjsModule(function (module) {
85 function _typeof2(obj) {
86 if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") {
87 _typeof2 = function _typeof2(obj) {
88 return typeof obj;
89 };
90 } else {
91 _typeof2 = function _typeof2(obj) {
92 return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
93 };
94 }
95
96 return _typeof2(obj);
97 }
98
99 function _typeof(obj) {
100 if (typeof Symbol === "function" && _typeof2(Symbol.iterator) === "symbol") {
101 module.exports = _typeof = function _typeof(obj) {
102 return _typeof2(obj);
103 };
104 } else {
105 module.exports = _typeof = function _typeof(obj) {
106 return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : _typeof2(obj);
107 };
108 }
109
110 return _typeof(obj);
111 }
112
113 module.exports = _typeof;
114});
115
116var moment = createCommonjsModule(function (module, exports) {
117 (function (global, factory) {
118 module.exports = factory();
119 })(commonjsGlobal, function () {
120 var hookCallback;
121
122 function hooks() {
123 return hookCallback.apply(null, arguments);
124 } // This is done to register the method called with moment()
125 // without creating circular dependencies.
126
127
128 function setHookCallback(callback) {
129 hookCallback = callback;
130 }
131
132 function isArray(input) {
133 return input instanceof Array || Object.prototype.toString.call(input) === '[object Array]';
134 }
135
136 function isObject(input) {
137 // IE8 will treat undefined and null as object if it wasn't for
138 // input != null
139 return input != null && Object.prototype.toString.call(input) === '[object Object]';
140 }
141
142 function isObjectEmpty(obj) {
143 if (Object.getOwnPropertyNames) {
144 return Object.getOwnPropertyNames(obj).length === 0;
145 } else {
146 var k;
147
148 for (k in obj) {
149 if (obj.hasOwnProperty(k)) {
150 return false;
151 }
152 }
153
154 return true;
155 }
156 }
157
158 function isUndefined(input) {
159 return input === void 0;
160 }
161
162 function isNumber(input) {
163 return typeof input === 'number' || Object.prototype.toString.call(input) === '[object Number]';
164 }
165
166 function isDate(input) {
167 return input instanceof Date || Object.prototype.toString.call(input) === '[object Date]';
168 }
169
170 function map(arr, fn) {
171 var res = [],
172 i;
173
174 for (i = 0; i < arr.length; ++i) {
175 res.push(fn(arr[i], i));
176 }
177
178 return res;
179 }
180
181 function hasOwnProp(a, b) {
182 return Object.prototype.hasOwnProperty.call(a, b);
183 }
184
185 function extend(a, b) {
186 for (var i in b) {
187 if (hasOwnProp(b, i)) {
188 a[i] = b[i];
189 }
190 }
191
192 if (hasOwnProp(b, 'toString')) {
193 a.toString = b.toString;
194 }
195
196 if (hasOwnProp(b, 'valueOf')) {
197 a.valueOf = b.valueOf;
198 }
199
200 return a;
201 }
202
203 function createUTC(input, format, locale, strict) {
204 return createLocalOrUTC(input, format, locale, strict, true).utc();
205 }
206
207 function defaultParsingFlags() {
208 // We need to deep clone this object.
209 return {
210 empty: false,
211 unusedTokens: [],
212 unusedInput: [],
213 overflow: -2,
214 charsLeftOver: 0,
215 nullInput: false,
216 invalidMonth: null,
217 invalidFormat: false,
218 userInvalidated: false,
219 iso: false,
220 parsedDateParts: [],
221 meridiem: null,
222 rfc2822: false,
223 weekdayMismatch: false
224 };
225 }
226
227 function getParsingFlags(m) {
228 if (m._pf == null) {
229 m._pf = defaultParsingFlags();
230 }
231
232 return m._pf;
233 }
234
235 var some;
236
237 if (Array.prototype.some) {
238 some = Array.prototype.some;
239 } else {
240 some = function (fun) {
241 var t = Object(this);
242 var len = t.length >>> 0;
243
244 for (var i = 0; i < len; i++) {
245 if (i in t && fun.call(this, t[i], i, t)) {
246 return true;
247 }
248 }
249
250 return false;
251 };
252 }
253
254 function isValid(m) {
255 if (m._isValid == null) {
256 var flags = getParsingFlags(m);
257 var parsedParts = some.call(flags.parsedDateParts, function (i) {
258 return i != null;
259 });
260 var isNowValid = !isNaN(m._d.getTime()) && flags.overflow < 0 && !flags.empty && !flags.invalidMonth && !flags.invalidWeekday && !flags.weekdayMismatch && !flags.nullInput && !flags.invalidFormat && !flags.userInvalidated && (!flags.meridiem || flags.meridiem && parsedParts);
261
262 if (m._strict) {
263 isNowValid = isNowValid && flags.charsLeftOver === 0 && flags.unusedTokens.length === 0 && flags.bigHour === undefined;
264 }
265
266 if (Object.isFrozen == null || !Object.isFrozen(m)) {
267 m._isValid = isNowValid;
268 } else {
269 return isNowValid;
270 }
271 }
272
273 return m._isValid;
274 }
275
276 function createInvalid(flags) {
277 var m = createUTC(NaN);
278
279 if (flags != null) {
280 extend(getParsingFlags(m), flags);
281 } else {
282 getParsingFlags(m).userInvalidated = true;
283 }
284
285 return m;
286 } // Plugins that add properties should also add the key here (null value),
287 // so we can properly clone ourselves.
288
289
290 var momentProperties = hooks.momentProperties = [];
291
292 function copyConfig(to, from) {
293 var i, prop, val;
294
295 if (!isUndefined(from._isAMomentObject)) {
296 to._isAMomentObject = from._isAMomentObject;
297 }
298
299 if (!isUndefined(from._i)) {
300 to._i = from._i;
301 }
302
303 if (!isUndefined(from._f)) {
304 to._f = from._f;
305 }
306
307 if (!isUndefined(from._l)) {
308 to._l = from._l;
309 }
310
311 if (!isUndefined(from._strict)) {
312 to._strict = from._strict;
313 }
314
315 if (!isUndefined(from._tzm)) {
316 to._tzm = from._tzm;
317 }
318
319 if (!isUndefined(from._isUTC)) {
320 to._isUTC = from._isUTC;
321 }
322
323 if (!isUndefined(from._offset)) {
324 to._offset = from._offset;
325 }
326
327 if (!isUndefined(from._pf)) {
328 to._pf = getParsingFlags(from);
329 }
330
331 if (!isUndefined(from._locale)) {
332 to._locale = from._locale;
333 }
334
335 if (momentProperties.length > 0) {
336 for (i = 0; i < momentProperties.length; i++) {
337 prop = momentProperties[i];
338 val = from[prop];
339
340 if (!isUndefined(val)) {
341 to[prop] = val;
342 }
343 }
344 }
345
346 return to;
347 }
348
349 var updateInProgress = false; // Moment prototype object
350
351 function Moment(config) {
352 copyConfig(this, config);
353 this._d = new Date(config._d != null ? config._d.getTime() : NaN);
354
355 if (!this.isValid()) {
356 this._d = new Date(NaN);
357 } // Prevent infinite loop in case updateOffset creates new moment
358 // objects.
359
360
361 if (updateInProgress === false) {
362 updateInProgress = true;
363 hooks.updateOffset(this);
364 updateInProgress = false;
365 }
366 }
367
368 function isMoment(obj) {
369 return obj instanceof Moment || obj != null && obj._isAMomentObject != null;
370 }
371
372 function absFloor(number) {
373 if (number < 0) {
374 // -0 -> 0
375 return Math.ceil(number) || 0;
376 } else {
377 return Math.floor(number);
378 }
379 }
380
381 function toInt(argumentForCoercion) {
382 var coercedNumber = +argumentForCoercion,
383 value = 0;
384
385 if (coercedNumber !== 0 && isFinite(coercedNumber)) {
386 value = absFloor(coercedNumber);
387 }
388
389 return value;
390 } // compare two arrays, return the number of differences
391
392
393 function compareArrays(array1, array2, dontConvert) {
394 var len = Math.min(array1.length, array2.length),
395 lengthDiff = Math.abs(array1.length - array2.length),
396 diffs = 0,
397 i;
398
399 for (i = 0; i < len; i++) {
400 if (dontConvert && array1[i] !== array2[i] || !dontConvert && toInt(array1[i]) !== toInt(array2[i])) {
401 diffs++;
402 }
403 }
404
405 return diffs + lengthDiff;
406 }
407
408 function warn(msg) {
409 if (hooks.suppressDeprecationWarnings === false && typeof console !== 'undefined' && console.warn) {
410 console.warn('Deprecation warning: ' + msg);
411 }
412 }
413
414 function deprecate(msg, fn) {
415 var firstTime = true;
416 return extend(function () {
417 if (hooks.deprecationHandler != null) {
418 hooks.deprecationHandler(null, msg);
419 }
420
421 if (firstTime) {
422 var args = [];
423 var arg;
424
425 for (var i = 0; i < arguments.length; i++) {
426 arg = '';
427
428 if (typeof arguments[i] === 'object') {
429 arg += '\n[' + i + '] ';
430
431 for (var key in arguments[0]) {
432 arg += key + ': ' + arguments[0][key] + ', ';
433 }
434
435 arg = arg.slice(0, -2); // Remove trailing comma and space
436 } else {
437 arg = arguments[i];
438 }
439
440 args.push(arg);
441 }
442
443 warn(msg + '\nArguments: ' + Array.prototype.slice.call(args).join('') + '\n' + new Error().stack);
444 firstTime = false;
445 }
446
447 return fn.apply(this, arguments);
448 }, fn);
449 }
450
451 var deprecations = {};
452
453 function deprecateSimple(name, msg) {
454 if (hooks.deprecationHandler != null) {
455 hooks.deprecationHandler(name, msg);
456 }
457
458 if (!deprecations[name]) {
459 warn(msg);
460 deprecations[name] = true;
461 }
462 }
463
464 hooks.suppressDeprecationWarnings = false;
465 hooks.deprecationHandler = null;
466
467 function isFunction(input) {
468 return input instanceof Function || Object.prototype.toString.call(input) === '[object Function]';
469 }
470
471 function set(config) {
472 var prop, i;
473
474 for (i in config) {
475 prop = config[i];
476
477 if (isFunction(prop)) {
478 this[i] = prop;
479 } else {
480 this['_' + i] = prop;
481 }
482 }
483
484 this._config = config; // Lenient ordinal parsing accepts just a number in addition to
485 // number + (possibly) stuff coming from _dayOfMonthOrdinalParse.
486 // TODO: Remove "ordinalParse" fallback in next major release.
487
488 this._dayOfMonthOrdinalParseLenient = new RegExp((this._dayOfMonthOrdinalParse.source || this._ordinalParse.source) + '|' + /\d{1,2}/.source);
489 }
490
491 function mergeConfigs(parentConfig, childConfig) {
492 var res = extend({}, parentConfig),
493 prop;
494
495 for (prop in childConfig) {
496 if (hasOwnProp(childConfig, prop)) {
497 if (isObject(parentConfig[prop]) && isObject(childConfig[prop])) {
498 res[prop] = {};
499 extend(res[prop], parentConfig[prop]);
500 extend(res[prop], childConfig[prop]);
501 } else if (childConfig[prop] != null) {
502 res[prop] = childConfig[prop];
503 } else {
504 delete res[prop];
505 }
506 }
507 }
508
509 for (prop in parentConfig) {
510 if (hasOwnProp(parentConfig, prop) && !hasOwnProp(childConfig, prop) && isObject(parentConfig[prop])) {
511 // make sure changes to properties don't modify parent config
512 res[prop] = extend({}, res[prop]);
513 }
514 }
515
516 return res;
517 }
518
519 function Locale(config) {
520 if (config != null) {
521 this.set(config);
522 }
523 }
524
525 var keys;
526
527 if (Object.keys) {
528 keys = Object.keys;
529 } else {
530 keys = function (obj) {
531 var i,
532 res = [];
533
534 for (i in obj) {
535 if (hasOwnProp(obj, i)) {
536 res.push(i);
537 }
538 }
539
540 return res;
541 };
542 }
543
544 var defaultCalendar = {
545 sameDay: '[Today at] LT',
546 nextDay: '[Tomorrow at] LT',
547 nextWeek: 'dddd [at] LT',
548 lastDay: '[Yesterday at] LT',
549 lastWeek: '[Last] dddd [at] LT',
550 sameElse: 'L'
551 };
552
553 function calendar(key, mom, now) {
554 var output = this._calendar[key] || this._calendar['sameElse'];
555 return isFunction(output) ? output.call(mom, now) : output;
556 }
557
558 var defaultLongDateFormat = {
559 LTS: 'h:mm:ss A',
560 LT: 'h:mm A',
561 L: 'MM/DD/YYYY',
562 LL: 'MMMM D, YYYY',
563 LLL: 'MMMM D, YYYY h:mm A',
564 LLLL: 'dddd, MMMM D, YYYY h:mm A'
565 };
566
567 function longDateFormat(key) {
568 var format = this._longDateFormat[key],
569 formatUpper = this._longDateFormat[key.toUpperCase()];
570
571 if (format || !formatUpper) {
572 return format;
573 }
574
575 this._longDateFormat[key] = formatUpper.replace(/MMMM|MM|DD|dddd/g, function (val) {
576 return val.slice(1);
577 });
578 return this._longDateFormat[key];
579 }
580
581 var defaultInvalidDate = 'Invalid date';
582
583 function invalidDate() {
584 return this._invalidDate;
585 }
586
587 var defaultOrdinal = '%d';
588 var defaultDayOfMonthOrdinalParse = /\d{1,2}/;
589
590 function ordinal(number) {
591 return this._ordinal.replace('%d', number);
592 }
593
594 var defaultRelativeTime = {
595 future: 'in %s',
596 past: '%s ago',
597 s: 'a few seconds',
598 ss: '%d seconds',
599 m: 'a minute',
600 mm: '%d minutes',
601 h: 'an hour',
602 hh: '%d hours',
603 d: 'a day',
604 dd: '%d days',
605 M: 'a month',
606 MM: '%d months',
607 y: 'a year',
608 yy: '%d years'
609 };
610
611 function relativeTime(number, withoutSuffix, string, isFuture) {
612 var output = this._relativeTime[string];
613 return isFunction(output) ? output(number, withoutSuffix, string, isFuture) : output.replace(/%d/i, number);
614 }
615
616 function pastFuture(diff, output) {
617 var format = this._relativeTime[diff > 0 ? 'future' : 'past'];
618 return isFunction(format) ? format(output) : format.replace(/%s/i, output);
619 }
620
621 var aliases = {};
622
623 function addUnitAlias(unit, shorthand) {
624 var lowerCase = unit.toLowerCase();
625 aliases[lowerCase] = aliases[lowerCase + 's'] = aliases[shorthand] = unit;
626 }
627
628 function normalizeUnits(units) {
629 return typeof units === 'string' ? aliases[units] || aliases[units.toLowerCase()] : undefined;
630 }
631
632 function normalizeObjectUnits(inputObject) {
633 var normalizedInput = {},
634 normalizedProp,
635 prop;
636
637 for (prop in inputObject) {
638 if (hasOwnProp(inputObject, prop)) {
639 normalizedProp = normalizeUnits(prop);
640
641 if (normalizedProp) {
642 normalizedInput[normalizedProp] = inputObject[prop];
643 }
644 }
645 }
646
647 return normalizedInput;
648 }
649
650 var priorities = {};
651
652 function addUnitPriority(unit, priority) {
653 priorities[unit] = priority;
654 }
655
656 function getPrioritizedUnits(unitsObj) {
657 var units = [];
658
659 for (var u in unitsObj) {
660 units.push({
661 unit: u,
662 priority: priorities[u]
663 });
664 }
665
666 units.sort(function (a, b) {
667 return a.priority - b.priority;
668 });
669 return units;
670 }
671
672 function zeroFill(number, targetLength, forceSign) {
673 var absNumber = '' + Math.abs(number),
674 zerosToFill = targetLength - absNumber.length,
675 sign = number >= 0;
676 return (sign ? forceSign ? '+' : '' : '-') + Math.pow(10, Math.max(0, zerosToFill)).toString().substr(1) + absNumber;
677 }
678
679 var formattingTokens = /(\[[^\[]*\])|(\\)?([Hh]mm(ss)?|Mo|MM?M?M?|Do|DDDo|DD?D?D?|ddd?d?|do?|w[o|w]?|W[o|W]?|Qo?|YYYYYY|YYYYY|YYYY|YY|gg(ggg?)?|GG(GGG?)?|e|E|a|A|hh?|HH?|kk?|mm?|ss?|S{1,9}|x|X|zz?|ZZ?|.)/g;
680 var localFormattingTokens = /(\[[^\[]*\])|(\\)?(LTS|LT|LL?L?L?|l{1,4})/g;
681 var formatFunctions = {};
682 var formatTokenFunctions = {}; // token: 'M'
683 // padded: ['MM', 2]
684 // ordinal: 'Mo'
685 // callback: function () { this.month() + 1 }
686
687 function addFormatToken(token, padded, ordinal, callback) {
688 var func = callback;
689
690 if (typeof callback === 'string') {
691 func = function () {
692 return this[callback]();
693 };
694 }
695
696 if (token) {
697 formatTokenFunctions[token] = func;
698 }
699
700 if (padded) {
701 formatTokenFunctions[padded[0]] = function () {
702 return zeroFill(func.apply(this, arguments), padded[1], padded[2]);
703 };
704 }
705
706 if (ordinal) {
707 formatTokenFunctions[ordinal] = function () {
708 return this.localeData().ordinal(func.apply(this, arguments), token);
709 };
710 }
711 }
712
713 function removeFormattingTokens(input) {
714 if (input.match(/\[[\s\S]/)) {
715 return input.replace(/^\[|\]$/g, '');
716 }
717
718 return input.replace(/\\/g, '');
719 }
720
721 function makeFormatFunction(format) {
722 var array = format.match(formattingTokens),
723 i,
724 length;
725
726 for (i = 0, length = array.length; i < length; i++) {
727 if (formatTokenFunctions[array[i]]) {
728 array[i] = formatTokenFunctions[array[i]];
729 } else {
730 array[i] = removeFormattingTokens(array[i]);
731 }
732 }
733
734 return function (mom) {
735 var output = '',
736 i;
737
738 for (i = 0; i < length; i++) {
739 output += isFunction(array[i]) ? array[i].call(mom, format) : array[i];
740 }
741
742 return output;
743 };
744 } // format date using native date object
745
746
747 function formatMoment(m, format) {
748 if (!m.isValid()) {
749 return m.localeData().invalidDate();
750 }
751
752 format = expandFormat(format, m.localeData());
753 formatFunctions[format] = formatFunctions[format] || makeFormatFunction(format);
754 return formatFunctions[format](m);
755 }
756
757 function expandFormat(format, locale) {
758 var i = 5;
759
760 function replaceLongDateFormatTokens(input) {
761 return locale.longDateFormat(input) || input;
762 }
763
764 localFormattingTokens.lastIndex = 0;
765
766 while (i >= 0 && localFormattingTokens.test(format)) {
767 format = format.replace(localFormattingTokens, replaceLongDateFormatTokens);
768 localFormattingTokens.lastIndex = 0;
769 i -= 1;
770 }
771
772 return format;
773 }
774
775 var match1 = /\d/; // 0 - 9
776
777 var match2 = /\d\d/; // 00 - 99
778
779 var match3 = /\d{3}/; // 000 - 999
780
781 var match4 = /\d{4}/; // 0000 - 9999
782
783 var match6 = /[+-]?\d{6}/; // -999999 - 999999
784
785 var match1to2 = /\d\d?/; // 0 - 99
786
787 var match3to4 = /\d\d\d\d?/; // 999 - 9999
788
789 var match5to6 = /\d\d\d\d\d\d?/; // 99999 - 999999
790
791 var match1to3 = /\d{1,3}/; // 0 - 999
792
793 var match1to4 = /\d{1,4}/; // 0 - 9999
794
795 var match1to6 = /[+-]?\d{1,6}/; // -999999 - 999999
796
797 var matchUnsigned = /\d+/; // 0 - inf
798
799 var matchSigned = /[+-]?\d+/; // -inf - inf
800
801 var matchOffset = /Z|[+-]\d\d:?\d\d/gi; // +00:00 -00:00 +0000 -0000 or Z
802
803 var matchShortOffset = /Z|[+-]\d\d(?::?\d\d)?/gi; // +00 -00 +00:00 -00:00 +0000 -0000 or Z
804
805 var matchTimestamp = /[+-]?\d+(\.\d{1,3})?/; // 123456789 123456789.123
806 // any word (or two) characters or numbers including two/three word month in arabic.
807 // includes scottish gaelic two word and hyphenated months
808
809 var matchWord = /[0-9]{0,256}['a-z\u00A0-\u05FF\u0700-\uD7FF\uF900-\uFDCF\uFDF0-\uFF07\uFF10-\uFFEF]{1,256}|[\u0600-\u06FF\/]{1,256}(\s*?[\u0600-\u06FF]{1,256}){1,2}/i;
810 var regexes = {};
811
812 function addRegexToken(token, regex, strictRegex) {
813 regexes[token] = isFunction(regex) ? regex : function (isStrict, localeData) {
814 return isStrict && strictRegex ? strictRegex : regex;
815 };
816 }
817
818 function getParseRegexForToken(token, config) {
819 if (!hasOwnProp(regexes, token)) {
820 return new RegExp(unescapeFormat(token));
821 }
822
823 return regexes[token](config._strict, config._locale);
824 } // Code from http://stackoverflow.com/questions/3561493/is-there-a-regexp-escape-function-in-javascript
825
826
827 function unescapeFormat(s) {
828 return regexEscape(s.replace('\\', '').replace(/\\(\[)|\\(\])|\[([^\]\[]*)\]|\\(.)/g, function (matched, p1, p2, p3, p4) {
829 return p1 || p2 || p3 || p4;
830 }));
831 }
832
833 function regexEscape(s) {
834 return s.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
835 }
836
837 var tokens = {};
838
839 function addParseToken(token, callback) {
840 var i,
841 func = callback;
842
843 if (typeof token === 'string') {
844 token = [token];
845 }
846
847 if (isNumber(callback)) {
848 func = function (input, array) {
849 array[callback] = toInt(input);
850 };
851 }
852
853 for (i = 0; i < token.length; i++) {
854 tokens[token[i]] = func;
855 }
856 }
857
858 function addWeekParseToken(token, callback) {
859 addParseToken(token, function (input, array, config, token) {
860 config._w = config._w || {};
861 callback(input, config._w, config, token);
862 });
863 }
864
865 function addTimeToArrayFromToken(token, input, config) {
866 if (input != null && hasOwnProp(tokens, token)) {
867 tokens[token](input, config._a, config, token);
868 }
869 }
870
871 var YEAR = 0;
872 var MONTH = 1;
873 var DATE = 2;
874 var HOUR = 3;
875 var MINUTE = 4;
876 var SECOND = 5;
877 var MILLISECOND = 6;
878 var WEEK = 7;
879 var WEEKDAY = 8; // FORMATTING
880
881 addFormatToken('Y', 0, 0, function () {
882 var y = this.year();
883 return y <= 9999 ? '' + y : '+' + y;
884 });
885 addFormatToken(0, ['YY', 2], 0, function () {
886 return this.year() % 100;
887 });
888 addFormatToken(0, ['YYYY', 4], 0, 'year');
889 addFormatToken(0, ['YYYYY', 5], 0, 'year');
890 addFormatToken(0, ['YYYYYY', 6, true], 0, 'year'); // ALIASES
891
892 addUnitAlias('year', 'y'); // PRIORITIES
893
894 addUnitPriority('year', 1); // PARSING
895
896 addRegexToken('Y', matchSigned);
897 addRegexToken('YY', match1to2, match2);
898 addRegexToken('YYYY', match1to4, match4);
899 addRegexToken('YYYYY', match1to6, match6);
900 addRegexToken('YYYYYY', match1to6, match6);
901 addParseToken(['YYYYY', 'YYYYYY'], YEAR);
902 addParseToken('YYYY', function (input, array) {
903 array[YEAR] = input.length === 2 ? hooks.parseTwoDigitYear(input) : toInt(input);
904 });
905 addParseToken('YY', function (input, array) {
906 array[YEAR] = hooks.parseTwoDigitYear(input);
907 });
908 addParseToken('Y', function (input, array) {
909 array[YEAR] = parseInt(input, 10);
910 }); // HELPERS
911
912 function daysInYear(year) {
913 return isLeapYear(year) ? 366 : 365;
914 }
915
916 function isLeapYear(year) {
917 return year % 4 === 0 && year % 100 !== 0 || year % 400 === 0;
918 } // HOOKS
919
920
921 hooks.parseTwoDigitYear = function (input) {
922 return toInt(input) + (toInt(input) > 68 ? 1900 : 2000);
923 }; // MOMENTS
924
925
926 var getSetYear = makeGetSet('FullYear', true);
927
928 function getIsLeapYear() {
929 return isLeapYear(this.year());
930 }
931
932 function makeGetSet(unit, keepTime) {
933 return function (value) {
934 if (value != null) {
935 set$1(this, unit, value);
936 hooks.updateOffset(this, keepTime);
937 return this;
938 } else {
939 return get(this, unit);
940 }
941 };
942 }
943
944 function get(mom, unit) {
945 return mom.isValid() ? mom._d['get' + (mom._isUTC ? 'UTC' : '') + unit]() : NaN;
946 }
947
948 function set$1(mom, unit, value) {
949 if (mom.isValid() && !isNaN(value)) {
950 if (unit === 'FullYear' && isLeapYear(mom.year()) && mom.month() === 1 && mom.date() === 29) {
951 mom._d['set' + (mom._isUTC ? 'UTC' : '') + unit](value, mom.month(), daysInMonth(value, mom.month()));
952 } else {
953 mom._d['set' + (mom._isUTC ? 'UTC' : '') + unit](value);
954 }
955 }
956 } // MOMENTS
957
958
959 function stringGet(units) {
960 units = normalizeUnits(units);
961
962 if (isFunction(this[units])) {
963 return this[units]();
964 }
965
966 return this;
967 }
968
969 function stringSet(units, value) {
970 if (typeof units === 'object') {
971 units = normalizeObjectUnits(units);
972 var prioritized = getPrioritizedUnits(units);
973
974 for (var i = 0; i < prioritized.length; i++) {
975 this[prioritized[i].unit](units[prioritized[i].unit]);
976 }
977 } else {
978 units = normalizeUnits(units);
979
980 if (isFunction(this[units])) {
981 return this[units](value);
982 }
983 }
984
985 return this;
986 }
987
988 function mod(n, x) {
989 return (n % x + x) % x;
990 }
991
992 var indexOf;
993
994 if (Array.prototype.indexOf) {
995 indexOf = Array.prototype.indexOf;
996 } else {
997 indexOf = function (o) {
998 // I know
999 var i;
1000
1001 for (i = 0; i < this.length; ++i) {
1002 if (this[i] === o) {
1003 return i;
1004 }
1005 }
1006
1007 return -1;
1008 };
1009 }
1010
1011 function daysInMonth(year, month) {
1012 if (isNaN(year) || isNaN(month)) {
1013 return NaN;
1014 }
1015
1016 var modMonth = mod(month, 12);
1017 year += (month - modMonth) / 12;
1018 return modMonth === 1 ? isLeapYear(year) ? 29 : 28 : 31 - modMonth % 7 % 2;
1019 } // FORMATTING
1020
1021
1022 addFormatToken('M', ['MM', 2], 'Mo', function () {
1023 return this.month() + 1;
1024 });
1025 addFormatToken('MMM', 0, 0, function (format) {
1026 return this.localeData().monthsShort(this, format);
1027 });
1028 addFormatToken('MMMM', 0, 0, function (format) {
1029 return this.localeData().months(this, format);
1030 }); // ALIASES
1031
1032 addUnitAlias('month', 'M'); // PRIORITY
1033
1034 addUnitPriority('month', 8); // PARSING
1035
1036 addRegexToken('M', match1to2);
1037 addRegexToken('MM', match1to2, match2);
1038 addRegexToken('MMM', function (isStrict, locale) {
1039 return locale.monthsShortRegex(isStrict);
1040 });
1041 addRegexToken('MMMM', function (isStrict, locale) {
1042 return locale.monthsRegex(isStrict);
1043 });
1044 addParseToken(['M', 'MM'], function (input, array) {
1045 array[MONTH] = toInt(input) - 1;
1046 });
1047 addParseToken(['MMM', 'MMMM'], function (input, array, config, token) {
1048 var month = config._locale.monthsParse(input, token, config._strict); // if we didn't find a month name, mark the date as invalid.
1049
1050
1051 if (month != null) {
1052 array[MONTH] = month;
1053 } else {
1054 getParsingFlags(config).invalidMonth = input;
1055 }
1056 }); // LOCALES
1057
1058 var MONTHS_IN_FORMAT = /D[oD]?(\[[^\[\]]*\]|\s)+MMMM?/;
1059 var defaultLocaleMonths = 'January_February_March_April_May_June_July_August_September_October_November_December'.split('_');
1060
1061 function localeMonths(m, format) {
1062 if (!m) {
1063 return isArray(this._months) ? this._months : this._months['standalone'];
1064 }
1065
1066 return isArray(this._months) ? this._months[m.month()] : this._months[(this._months.isFormat || MONTHS_IN_FORMAT).test(format) ? 'format' : 'standalone'][m.month()];
1067 }
1068
1069 var defaultLocaleMonthsShort = 'Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec'.split('_');
1070
1071 function localeMonthsShort(m, format) {
1072 if (!m) {
1073 return isArray(this._monthsShort) ? this._monthsShort : this._monthsShort['standalone'];
1074 }
1075
1076 return isArray(this._monthsShort) ? this._monthsShort[m.month()] : this._monthsShort[MONTHS_IN_FORMAT.test(format) ? 'format' : 'standalone'][m.month()];
1077 }
1078
1079 function handleStrictParse(monthName, format, strict) {
1080 var i,
1081 ii,
1082 mom,
1083 llc = monthName.toLocaleLowerCase();
1084
1085 if (!this._monthsParse) {
1086 // this is not used
1087 this._monthsParse = [];
1088 this._longMonthsParse = [];
1089 this._shortMonthsParse = [];
1090
1091 for (i = 0; i < 12; ++i) {
1092 mom = createUTC([2000, i]);
1093 this._shortMonthsParse[i] = this.monthsShort(mom, '').toLocaleLowerCase();
1094 this._longMonthsParse[i] = this.months(mom, '').toLocaleLowerCase();
1095 }
1096 }
1097
1098 if (strict) {
1099 if (format === 'MMM') {
1100 ii = indexOf.call(this._shortMonthsParse, llc);
1101 return ii !== -1 ? ii : null;
1102 } else {
1103 ii = indexOf.call(this._longMonthsParse, llc);
1104 return ii !== -1 ? ii : null;
1105 }
1106 } else {
1107 if (format === 'MMM') {
1108 ii = indexOf.call(this._shortMonthsParse, llc);
1109
1110 if (ii !== -1) {
1111 return ii;
1112 }
1113
1114 ii = indexOf.call(this._longMonthsParse, llc);
1115 return ii !== -1 ? ii : null;
1116 } else {
1117 ii = indexOf.call(this._longMonthsParse, llc);
1118
1119 if (ii !== -1) {
1120 return ii;
1121 }
1122
1123 ii = indexOf.call(this._shortMonthsParse, llc);
1124 return ii !== -1 ? ii : null;
1125 }
1126 }
1127 }
1128
1129 function localeMonthsParse(monthName, format, strict) {
1130 var i, mom, regex;
1131
1132 if (this._monthsParseExact) {
1133 return handleStrictParse.call(this, monthName, format, strict);
1134 }
1135
1136 if (!this._monthsParse) {
1137 this._monthsParse = [];
1138 this._longMonthsParse = [];
1139 this._shortMonthsParse = [];
1140 } // TODO: add sorting
1141 // Sorting makes sure if one month (or abbr) is a prefix of another
1142 // see sorting in computeMonthsParse
1143
1144
1145 for (i = 0; i < 12; i++) {
1146 // make the regex if we don't have it already
1147 mom = createUTC([2000, i]);
1148
1149 if (strict && !this._longMonthsParse[i]) {
1150 this._longMonthsParse[i] = new RegExp('^' + this.months(mom, '').replace('.', '') + '$', 'i');
1151 this._shortMonthsParse[i] = new RegExp('^' + this.monthsShort(mom, '').replace('.', '') + '$', 'i');
1152 }
1153
1154 if (!strict && !this._monthsParse[i]) {
1155 regex = '^' + this.months(mom, '') + '|^' + this.monthsShort(mom, '');
1156 this._monthsParse[i] = new RegExp(regex.replace('.', ''), 'i');
1157 } // test the regex
1158
1159
1160 if (strict && format === 'MMMM' && this._longMonthsParse[i].test(monthName)) {
1161 return i;
1162 } else if (strict && format === 'MMM' && this._shortMonthsParse[i].test(monthName)) {
1163 return i;
1164 } else if (!strict && this._monthsParse[i].test(monthName)) {
1165 return i;
1166 }
1167 }
1168 } // MOMENTS
1169
1170
1171 function setMonth(mom, value) {
1172 var dayOfMonth;
1173
1174 if (!mom.isValid()) {
1175 // No op
1176 return mom;
1177 }
1178
1179 if (typeof value === 'string') {
1180 if (/^\d+$/.test(value)) {
1181 value = toInt(value);
1182 } else {
1183 value = mom.localeData().monthsParse(value); // TODO: Another silent failure?
1184
1185 if (!isNumber(value)) {
1186 return mom;
1187 }
1188 }
1189 }
1190
1191 dayOfMonth = Math.min(mom.date(), daysInMonth(mom.year(), value));
1192
1193 mom._d['set' + (mom._isUTC ? 'UTC' : '') + 'Month'](value, dayOfMonth);
1194
1195 return mom;
1196 }
1197
1198 function getSetMonth(value) {
1199 if (value != null) {
1200 setMonth(this, value);
1201 hooks.updateOffset(this, true);
1202 return this;
1203 } else {
1204 return get(this, 'Month');
1205 }
1206 }
1207
1208 function getDaysInMonth() {
1209 return daysInMonth(this.year(), this.month());
1210 }
1211
1212 var defaultMonthsShortRegex = matchWord;
1213
1214 function monthsShortRegex(isStrict) {
1215 if (this._monthsParseExact) {
1216 if (!hasOwnProp(this, '_monthsRegex')) {
1217 computeMonthsParse.call(this);
1218 }
1219
1220 if (isStrict) {
1221 return this._monthsShortStrictRegex;
1222 } else {
1223 return this._monthsShortRegex;
1224 }
1225 } else {
1226 if (!hasOwnProp(this, '_monthsShortRegex')) {
1227 this._monthsShortRegex = defaultMonthsShortRegex;
1228 }
1229
1230 return this._monthsShortStrictRegex && isStrict ? this._monthsShortStrictRegex : this._monthsShortRegex;
1231 }
1232 }
1233
1234 var defaultMonthsRegex = matchWord;
1235
1236 function monthsRegex(isStrict) {
1237 if (this._monthsParseExact) {
1238 if (!hasOwnProp(this, '_monthsRegex')) {
1239 computeMonthsParse.call(this);
1240 }
1241
1242 if (isStrict) {
1243 return this._monthsStrictRegex;
1244 } else {
1245 return this._monthsRegex;
1246 }
1247 } else {
1248 if (!hasOwnProp(this, '_monthsRegex')) {
1249 this._monthsRegex = defaultMonthsRegex;
1250 }
1251
1252 return this._monthsStrictRegex && isStrict ? this._monthsStrictRegex : this._monthsRegex;
1253 }
1254 }
1255
1256 function computeMonthsParse() {
1257 function cmpLenRev(a, b) {
1258 return b.length - a.length;
1259 }
1260
1261 var shortPieces = [],
1262 longPieces = [],
1263 mixedPieces = [],
1264 i,
1265 mom;
1266
1267 for (i = 0; i < 12; i++) {
1268 // make the regex if we don't have it already
1269 mom = createUTC([2000, i]);
1270 shortPieces.push(this.monthsShort(mom, ''));
1271 longPieces.push(this.months(mom, ''));
1272 mixedPieces.push(this.months(mom, ''));
1273 mixedPieces.push(this.monthsShort(mom, ''));
1274 } // Sorting makes sure if one month (or abbr) is a prefix of another it
1275 // will match the longer piece.
1276
1277
1278 shortPieces.sort(cmpLenRev);
1279 longPieces.sort(cmpLenRev);
1280 mixedPieces.sort(cmpLenRev);
1281
1282 for (i = 0; i < 12; i++) {
1283 shortPieces[i] = regexEscape(shortPieces[i]);
1284 longPieces[i] = regexEscape(longPieces[i]);
1285 }
1286
1287 for (i = 0; i < 24; i++) {
1288 mixedPieces[i] = regexEscape(mixedPieces[i]);
1289 }
1290
1291 this._monthsRegex = new RegExp('^(' + mixedPieces.join('|') + ')', 'i');
1292 this._monthsShortRegex = this._monthsRegex;
1293 this._monthsStrictRegex = new RegExp('^(' + longPieces.join('|') + ')', 'i');
1294 this._monthsShortStrictRegex = new RegExp('^(' + shortPieces.join('|') + ')', 'i');
1295 }
1296
1297 function createDate(y, m, d, h, M, s, ms) {
1298 // can't just apply() to create a date:
1299 // https://stackoverflow.com/q/181348
1300 var date; // the date constructor remaps years 0-99 to 1900-1999
1301
1302 if (y < 100 && y >= 0) {
1303 // preserve leap years using a full 400 year cycle, then reset
1304 date = new Date(y + 400, m, d, h, M, s, ms);
1305
1306 if (isFinite(date.getFullYear())) {
1307 date.setFullYear(y);
1308 }
1309 } else {
1310 date = new Date(y, m, d, h, M, s, ms);
1311 }
1312
1313 return date;
1314 }
1315
1316 function createUTCDate(y) {
1317 var date; // the Date.UTC function remaps years 0-99 to 1900-1999
1318
1319 if (y < 100 && y >= 0) {
1320 var args = Array.prototype.slice.call(arguments); // preserve leap years using a full 400 year cycle, then reset
1321
1322 args[0] = y + 400;
1323 date = new Date(Date.UTC.apply(null, args));
1324
1325 if (isFinite(date.getUTCFullYear())) {
1326 date.setUTCFullYear(y);
1327 }
1328 } else {
1329 date = new Date(Date.UTC.apply(null, arguments));
1330 }
1331
1332 return date;
1333 } // start-of-first-week - start-of-year
1334
1335
1336 function firstWeekOffset(year, dow, doy) {
1337 var // first-week day -- which january is always in the first week (4 for iso, 1 for other)
1338 fwd = 7 + dow - doy,
1339 // first-week day local weekday -- which local weekday is fwd
1340 fwdlw = (7 + createUTCDate(year, 0, fwd).getUTCDay() - dow) % 7;
1341 return -fwdlw + fwd - 1;
1342 } // https://en.wikipedia.org/wiki/ISO_week_date#Calculating_a_date_given_the_year.2C_week_number_and_weekday
1343
1344
1345 function dayOfYearFromWeeks(year, week, weekday, dow, doy) {
1346 var localWeekday = (7 + weekday - dow) % 7,
1347 weekOffset = firstWeekOffset(year, dow, doy),
1348 dayOfYear = 1 + 7 * (week - 1) + localWeekday + weekOffset,
1349 resYear,
1350 resDayOfYear;
1351
1352 if (dayOfYear <= 0) {
1353 resYear = year - 1;
1354 resDayOfYear = daysInYear(resYear) + dayOfYear;
1355 } else if (dayOfYear > daysInYear(year)) {
1356 resYear = year + 1;
1357 resDayOfYear = dayOfYear - daysInYear(year);
1358 } else {
1359 resYear = year;
1360 resDayOfYear = dayOfYear;
1361 }
1362
1363 return {
1364 year: resYear,
1365 dayOfYear: resDayOfYear
1366 };
1367 }
1368
1369 function weekOfYear(mom, dow, doy) {
1370 var weekOffset = firstWeekOffset(mom.year(), dow, doy),
1371 week = Math.floor((mom.dayOfYear() - weekOffset - 1) / 7) + 1,
1372 resWeek,
1373 resYear;
1374
1375 if (week < 1) {
1376 resYear = mom.year() - 1;
1377 resWeek = week + weeksInYear(resYear, dow, doy);
1378 } else if (week > weeksInYear(mom.year(), dow, doy)) {
1379 resWeek = week - weeksInYear(mom.year(), dow, doy);
1380 resYear = mom.year() + 1;
1381 } else {
1382 resYear = mom.year();
1383 resWeek = week;
1384 }
1385
1386 return {
1387 week: resWeek,
1388 year: resYear
1389 };
1390 }
1391
1392 function weeksInYear(year, dow, doy) {
1393 var weekOffset = firstWeekOffset(year, dow, doy),
1394 weekOffsetNext = firstWeekOffset(year + 1, dow, doy);
1395 return (daysInYear(year) - weekOffset + weekOffsetNext) / 7;
1396 } // FORMATTING
1397
1398
1399 addFormatToken('w', ['ww', 2], 'wo', 'week');
1400 addFormatToken('W', ['WW', 2], 'Wo', 'isoWeek'); // ALIASES
1401
1402 addUnitAlias('week', 'w');
1403 addUnitAlias('isoWeek', 'W'); // PRIORITIES
1404
1405 addUnitPriority('week', 5);
1406 addUnitPriority('isoWeek', 5); // PARSING
1407
1408 addRegexToken('w', match1to2);
1409 addRegexToken('ww', match1to2, match2);
1410 addRegexToken('W', match1to2);
1411 addRegexToken('WW', match1to2, match2);
1412 addWeekParseToken(['w', 'ww', 'W', 'WW'], function (input, week, config, token) {
1413 week[token.substr(0, 1)] = toInt(input);
1414 }); // HELPERS
1415 // LOCALES
1416
1417 function localeWeek(mom) {
1418 return weekOfYear(mom, this._week.dow, this._week.doy).week;
1419 }
1420
1421 var defaultLocaleWeek = {
1422 dow: 0,
1423 // Sunday is the first day of the week.
1424 doy: 6 // The week that contains Jan 6th is the first week of the year.
1425
1426 };
1427
1428 function localeFirstDayOfWeek() {
1429 return this._week.dow;
1430 }
1431
1432 function localeFirstDayOfYear() {
1433 return this._week.doy;
1434 } // MOMENTS
1435
1436
1437 function getSetWeek(input) {
1438 var week = this.localeData().week(this);
1439 return input == null ? week : this.add((input - week) * 7, 'd');
1440 }
1441
1442 function getSetISOWeek(input) {
1443 var week = weekOfYear(this, 1, 4).week;
1444 return input == null ? week : this.add((input - week) * 7, 'd');
1445 } // FORMATTING
1446
1447
1448 addFormatToken('d', 0, 'do', 'day');
1449 addFormatToken('dd', 0, 0, function (format) {
1450 return this.localeData().weekdaysMin(this, format);
1451 });
1452 addFormatToken('ddd', 0, 0, function (format) {
1453 return this.localeData().weekdaysShort(this, format);
1454 });
1455 addFormatToken('dddd', 0, 0, function (format) {
1456 return this.localeData().weekdays(this, format);
1457 });
1458 addFormatToken('e', 0, 0, 'weekday');
1459 addFormatToken('E', 0, 0, 'isoWeekday'); // ALIASES
1460
1461 addUnitAlias('day', 'd');
1462 addUnitAlias('weekday', 'e');
1463 addUnitAlias('isoWeekday', 'E'); // PRIORITY
1464
1465 addUnitPriority('day', 11);
1466 addUnitPriority('weekday', 11);
1467 addUnitPriority('isoWeekday', 11); // PARSING
1468
1469 addRegexToken('d', match1to2);
1470 addRegexToken('e', match1to2);
1471 addRegexToken('E', match1to2);
1472 addRegexToken('dd', function (isStrict, locale) {
1473 return locale.weekdaysMinRegex(isStrict);
1474 });
1475 addRegexToken('ddd', function (isStrict, locale) {
1476 return locale.weekdaysShortRegex(isStrict);
1477 });
1478 addRegexToken('dddd', function (isStrict, locale) {
1479 return locale.weekdaysRegex(isStrict);
1480 });
1481 addWeekParseToken(['dd', 'ddd', 'dddd'], function (input, week, config, token) {
1482 var weekday = config._locale.weekdaysParse(input, token, config._strict); // if we didn't get a weekday name, mark the date as invalid
1483
1484
1485 if (weekday != null) {
1486 week.d = weekday;
1487 } else {
1488 getParsingFlags(config).invalidWeekday = input;
1489 }
1490 });
1491 addWeekParseToken(['d', 'e', 'E'], function (input, week, config, token) {
1492 week[token] = toInt(input);
1493 }); // HELPERS
1494
1495 function parseWeekday(input, locale) {
1496 if (typeof input !== 'string') {
1497 return input;
1498 }
1499
1500 if (!isNaN(input)) {
1501 return parseInt(input, 10);
1502 }
1503
1504 input = locale.weekdaysParse(input);
1505
1506 if (typeof input === 'number') {
1507 return input;
1508 }
1509
1510 return null;
1511 }
1512
1513 function parseIsoWeekday(input, locale) {
1514 if (typeof input === 'string') {
1515 return locale.weekdaysParse(input) % 7 || 7;
1516 }
1517
1518 return isNaN(input) ? null : input;
1519 } // LOCALES
1520
1521
1522 function shiftWeekdays(ws, n) {
1523 return ws.slice(n, 7).concat(ws.slice(0, n));
1524 }
1525
1526 var defaultLocaleWeekdays = 'Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday'.split('_');
1527
1528 function localeWeekdays(m, format) {
1529 var weekdays = isArray(this._weekdays) ? this._weekdays : this._weekdays[m && m !== true && this._weekdays.isFormat.test(format) ? 'format' : 'standalone'];
1530 return m === true ? shiftWeekdays(weekdays, this._week.dow) : m ? weekdays[m.day()] : weekdays;
1531 }
1532
1533 var defaultLocaleWeekdaysShort = 'Sun_Mon_Tue_Wed_Thu_Fri_Sat'.split('_');
1534
1535 function localeWeekdaysShort(m) {
1536 return m === true ? shiftWeekdays(this._weekdaysShort, this._week.dow) : m ? this._weekdaysShort[m.day()] : this._weekdaysShort;
1537 }
1538
1539 var defaultLocaleWeekdaysMin = 'Su_Mo_Tu_We_Th_Fr_Sa'.split('_');
1540
1541 function localeWeekdaysMin(m) {
1542 return m === true ? shiftWeekdays(this._weekdaysMin, this._week.dow) : m ? this._weekdaysMin[m.day()] : this._weekdaysMin;
1543 }
1544
1545 function handleStrictParse$1(weekdayName, format, strict) {
1546 var i,
1547 ii,
1548 mom,
1549 llc = weekdayName.toLocaleLowerCase();
1550
1551 if (!this._weekdaysParse) {
1552 this._weekdaysParse = [];
1553 this._shortWeekdaysParse = [];
1554 this._minWeekdaysParse = [];
1555
1556 for (i = 0; i < 7; ++i) {
1557 mom = createUTC([2000, 1]).day(i);
1558 this._minWeekdaysParse[i] = this.weekdaysMin(mom, '').toLocaleLowerCase();
1559 this._shortWeekdaysParse[i] = this.weekdaysShort(mom, '').toLocaleLowerCase();
1560 this._weekdaysParse[i] = this.weekdays(mom, '').toLocaleLowerCase();
1561 }
1562 }
1563
1564 if (strict) {
1565 if (format === 'dddd') {
1566 ii = indexOf.call(this._weekdaysParse, llc);
1567 return ii !== -1 ? ii : null;
1568 } else if (format === 'ddd') {
1569 ii = indexOf.call(this._shortWeekdaysParse, llc);
1570 return ii !== -1 ? ii : null;
1571 } else {
1572 ii = indexOf.call(this._minWeekdaysParse, llc);
1573 return ii !== -1 ? ii : null;
1574 }
1575 } else {
1576 if (format === 'dddd') {
1577 ii = indexOf.call(this._weekdaysParse, llc);
1578
1579 if (ii !== -1) {
1580 return ii;
1581 }
1582
1583 ii = indexOf.call(this._shortWeekdaysParse, llc);
1584
1585 if (ii !== -1) {
1586 return ii;
1587 }
1588
1589 ii = indexOf.call(this._minWeekdaysParse, llc);
1590 return ii !== -1 ? ii : null;
1591 } else if (format === 'ddd') {
1592 ii = indexOf.call(this._shortWeekdaysParse, llc);
1593
1594 if (ii !== -1) {
1595 return ii;
1596 }
1597
1598 ii = indexOf.call(this._weekdaysParse, llc);
1599
1600 if (ii !== -1) {
1601 return ii;
1602 }
1603
1604 ii = indexOf.call(this._minWeekdaysParse, llc);
1605 return ii !== -1 ? ii : null;
1606 } else {
1607 ii = indexOf.call(this._minWeekdaysParse, llc);
1608
1609 if (ii !== -1) {
1610 return ii;
1611 }
1612
1613 ii = indexOf.call(this._weekdaysParse, llc);
1614
1615 if (ii !== -1) {
1616 return ii;
1617 }
1618
1619 ii = indexOf.call(this._shortWeekdaysParse, llc);
1620 return ii !== -1 ? ii : null;
1621 }
1622 }
1623 }
1624
1625 function localeWeekdaysParse(weekdayName, format, strict) {
1626 var i, mom, regex;
1627
1628 if (this._weekdaysParseExact) {
1629 return handleStrictParse$1.call(this, weekdayName, format, strict);
1630 }
1631
1632 if (!this._weekdaysParse) {
1633 this._weekdaysParse = [];
1634 this._minWeekdaysParse = [];
1635 this._shortWeekdaysParse = [];
1636 this._fullWeekdaysParse = [];
1637 }
1638
1639 for (i = 0; i < 7; i++) {
1640 // make the regex if we don't have it already
1641 mom = createUTC([2000, 1]).day(i);
1642
1643 if (strict && !this._fullWeekdaysParse[i]) {
1644 this._fullWeekdaysParse[i] = new RegExp('^' + this.weekdays(mom, '').replace('.', '\\.?') + '$', 'i');
1645 this._shortWeekdaysParse[i] = new RegExp('^' + this.weekdaysShort(mom, '').replace('.', '\\.?') + '$', 'i');
1646 this._minWeekdaysParse[i] = new RegExp('^' + this.weekdaysMin(mom, '').replace('.', '\\.?') + '$', 'i');
1647 }
1648
1649 if (!this._weekdaysParse[i]) {
1650 regex = '^' + this.weekdays(mom, '') + '|^' + this.weekdaysShort(mom, '') + '|^' + this.weekdaysMin(mom, '');
1651 this._weekdaysParse[i] = new RegExp(regex.replace('.', ''), 'i');
1652 } // test the regex
1653
1654
1655 if (strict && format === 'dddd' && this._fullWeekdaysParse[i].test(weekdayName)) {
1656 return i;
1657 } else if (strict && format === 'ddd' && this._shortWeekdaysParse[i].test(weekdayName)) {
1658 return i;
1659 } else if (strict && format === 'dd' && this._minWeekdaysParse[i].test(weekdayName)) {
1660 return i;
1661 } else if (!strict && this._weekdaysParse[i].test(weekdayName)) {
1662 return i;
1663 }
1664 }
1665 } // MOMENTS
1666
1667
1668 function getSetDayOfWeek(input) {
1669 if (!this.isValid()) {
1670 return input != null ? this : NaN;
1671 }
1672
1673 var day = this._isUTC ? this._d.getUTCDay() : this._d.getDay();
1674
1675 if (input != null) {
1676 input = parseWeekday(input, this.localeData());
1677 return this.add(input - day, 'd');
1678 } else {
1679 return day;
1680 }
1681 }
1682
1683 function getSetLocaleDayOfWeek(input) {
1684 if (!this.isValid()) {
1685 return input != null ? this : NaN;
1686 }
1687
1688 var weekday = (this.day() + 7 - this.localeData()._week.dow) % 7;
1689 return input == null ? weekday : this.add(input - weekday, 'd');
1690 }
1691
1692 function getSetISODayOfWeek(input) {
1693 if (!this.isValid()) {
1694 return input != null ? this : NaN;
1695 } // behaves the same as moment#day except
1696 // as a getter, returns 7 instead of 0 (1-7 range instead of 0-6)
1697 // as a setter, sunday should belong to the previous week.
1698
1699
1700 if (input != null) {
1701 var weekday = parseIsoWeekday(input, this.localeData());
1702 return this.day(this.day() % 7 ? weekday : weekday - 7);
1703 } else {
1704 return this.day() || 7;
1705 }
1706 }
1707
1708 var defaultWeekdaysRegex = matchWord;
1709
1710 function weekdaysRegex(isStrict) {
1711 if (this._weekdaysParseExact) {
1712 if (!hasOwnProp(this, '_weekdaysRegex')) {
1713 computeWeekdaysParse.call(this);
1714 }
1715
1716 if (isStrict) {
1717 return this._weekdaysStrictRegex;
1718 } else {
1719 return this._weekdaysRegex;
1720 }
1721 } else {
1722 if (!hasOwnProp(this, '_weekdaysRegex')) {
1723 this._weekdaysRegex = defaultWeekdaysRegex;
1724 }
1725
1726 return this._weekdaysStrictRegex && isStrict ? this._weekdaysStrictRegex : this._weekdaysRegex;
1727 }
1728 }
1729
1730 var defaultWeekdaysShortRegex = matchWord;
1731
1732 function weekdaysShortRegex(isStrict) {
1733 if (this._weekdaysParseExact) {
1734 if (!hasOwnProp(this, '_weekdaysRegex')) {
1735 computeWeekdaysParse.call(this);
1736 }
1737
1738 if (isStrict) {
1739 return this._weekdaysShortStrictRegex;
1740 } else {
1741 return this._weekdaysShortRegex;
1742 }
1743 } else {
1744 if (!hasOwnProp(this, '_weekdaysShortRegex')) {
1745 this._weekdaysShortRegex = defaultWeekdaysShortRegex;
1746 }
1747
1748 return this._weekdaysShortStrictRegex && isStrict ? this._weekdaysShortStrictRegex : this._weekdaysShortRegex;
1749 }
1750 }
1751
1752 var defaultWeekdaysMinRegex = matchWord;
1753
1754 function weekdaysMinRegex(isStrict) {
1755 if (this._weekdaysParseExact) {
1756 if (!hasOwnProp(this, '_weekdaysRegex')) {
1757 computeWeekdaysParse.call(this);
1758 }
1759
1760 if (isStrict) {
1761 return this._weekdaysMinStrictRegex;
1762 } else {
1763 return this._weekdaysMinRegex;
1764 }
1765 } else {
1766 if (!hasOwnProp(this, '_weekdaysMinRegex')) {
1767 this._weekdaysMinRegex = defaultWeekdaysMinRegex;
1768 }
1769
1770 return this._weekdaysMinStrictRegex && isStrict ? this._weekdaysMinStrictRegex : this._weekdaysMinRegex;
1771 }
1772 }
1773
1774 function computeWeekdaysParse() {
1775 function cmpLenRev(a, b) {
1776 return b.length - a.length;
1777 }
1778
1779 var minPieces = [],
1780 shortPieces = [],
1781 longPieces = [],
1782 mixedPieces = [],
1783 i,
1784 mom,
1785 minp,
1786 shortp,
1787 longp;
1788
1789 for (i = 0; i < 7; i++) {
1790 // make the regex if we don't have it already
1791 mom = createUTC([2000, 1]).day(i);
1792 minp = this.weekdaysMin(mom, '');
1793 shortp = this.weekdaysShort(mom, '');
1794 longp = this.weekdays(mom, '');
1795 minPieces.push(minp);
1796 shortPieces.push(shortp);
1797 longPieces.push(longp);
1798 mixedPieces.push(minp);
1799 mixedPieces.push(shortp);
1800 mixedPieces.push(longp);
1801 } // Sorting makes sure if one weekday (or abbr) is a prefix of another it
1802 // will match the longer piece.
1803
1804
1805 minPieces.sort(cmpLenRev);
1806 shortPieces.sort(cmpLenRev);
1807 longPieces.sort(cmpLenRev);
1808 mixedPieces.sort(cmpLenRev);
1809
1810 for (i = 0; i < 7; i++) {
1811 shortPieces[i] = regexEscape(shortPieces[i]);
1812 longPieces[i] = regexEscape(longPieces[i]);
1813 mixedPieces[i] = regexEscape(mixedPieces[i]);
1814 }
1815
1816 this._weekdaysRegex = new RegExp('^(' + mixedPieces.join('|') + ')', 'i');
1817 this._weekdaysShortRegex = this._weekdaysRegex;
1818 this._weekdaysMinRegex = this._weekdaysRegex;
1819 this._weekdaysStrictRegex = new RegExp('^(' + longPieces.join('|') + ')', 'i');
1820 this._weekdaysShortStrictRegex = new RegExp('^(' + shortPieces.join('|') + ')', 'i');
1821 this._weekdaysMinStrictRegex = new RegExp('^(' + minPieces.join('|') + ')', 'i');
1822 } // FORMATTING
1823
1824
1825 function hFormat() {
1826 return this.hours() % 12 || 12;
1827 }
1828
1829 function kFormat() {
1830 return this.hours() || 24;
1831 }
1832
1833 addFormatToken('H', ['HH', 2], 0, 'hour');
1834 addFormatToken('h', ['hh', 2], 0, hFormat);
1835 addFormatToken('k', ['kk', 2], 0, kFormat);
1836 addFormatToken('hmm', 0, 0, function () {
1837 return '' + hFormat.apply(this) + zeroFill(this.minutes(), 2);
1838 });
1839 addFormatToken('hmmss', 0, 0, function () {
1840 return '' + hFormat.apply(this) + zeroFill(this.minutes(), 2) + zeroFill(this.seconds(), 2);
1841 });
1842 addFormatToken('Hmm', 0, 0, function () {
1843 return '' + this.hours() + zeroFill(this.minutes(), 2);
1844 });
1845 addFormatToken('Hmmss', 0, 0, function () {
1846 return '' + this.hours() + zeroFill(this.minutes(), 2) + zeroFill(this.seconds(), 2);
1847 });
1848
1849 function meridiem(token, lowercase) {
1850 addFormatToken(token, 0, 0, function () {
1851 return this.localeData().meridiem(this.hours(), this.minutes(), lowercase);
1852 });
1853 }
1854
1855 meridiem('a', true);
1856 meridiem('A', false); // ALIASES
1857
1858 addUnitAlias('hour', 'h'); // PRIORITY
1859
1860 addUnitPriority('hour', 13); // PARSING
1861
1862 function matchMeridiem(isStrict, locale) {
1863 return locale._meridiemParse;
1864 }
1865
1866 addRegexToken('a', matchMeridiem);
1867 addRegexToken('A', matchMeridiem);
1868 addRegexToken('H', match1to2);
1869 addRegexToken('h', match1to2);
1870 addRegexToken('k', match1to2);
1871 addRegexToken('HH', match1to2, match2);
1872 addRegexToken('hh', match1to2, match2);
1873 addRegexToken('kk', match1to2, match2);
1874 addRegexToken('hmm', match3to4);
1875 addRegexToken('hmmss', match5to6);
1876 addRegexToken('Hmm', match3to4);
1877 addRegexToken('Hmmss', match5to6);
1878 addParseToken(['H', 'HH'], HOUR);
1879 addParseToken(['k', 'kk'], function (input, array, config) {
1880 var kInput = toInt(input);
1881 array[HOUR] = kInput === 24 ? 0 : kInput;
1882 });
1883 addParseToken(['a', 'A'], function (input, array, config) {
1884 config._isPm = config._locale.isPM(input);
1885 config._meridiem = input;
1886 });
1887 addParseToken(['h', 'hh'], function (input, array, config) {
1888 array[HOUR] = toInt(input);
1889 getParsingFlags(config).bigHour = true;
1890 });
1891 addParseToken('hmm', function (input, array, config) {
1892 var pos = input.length - 2;
1893 array[HOUR] = toInt(input.substr(0, pos));
1894 array[MINUTE] = toInt(input.substr(pos));
1895 getParsingFlags(config).bigHour = true;
1896 });
1897 addParseToken('hmmss', function (input, array, config) {
1898 var pos1 = input.length - 4;
1899 var pos2 = input.length - 2;
1900 array[HOUR] = toInt(input.substr(0, pos1));
1901 array[MINUTE] = toInt(input.substr(pos1, 2));
1902 array[SECOND] = toInt(input.substr(pos2));
1903 getParsingFlags(config).bigHour = true;
1904 });
1905 addParseToken('Hmm', function (input, array, config) {
1906 var pos = input.length - 2;
1907 array[HOUR] = toInt(input.substr(0, pos));
1908 array[MINUTE] = toInt(input.substr(pos));
1909 });
1910 addParseToken('Hmmss', function (input, array, config) {
1911 var pos1 = input.length - 4;
1912 var pos2 = input.length - 2;
1913 array[HOUR] = toInt(input.substr(0, pos1));
1914 array[MINUTE] = toInt(input.substr(pos1, 2));
1915 array[SECOND] = toInt(input.substr(pos2));
1916 }); // LOCALES
1917
1918 function localeIsPM(input) {
1919 // IE8 Quirks Mode & IE7 Standards Mode do not allow accessing strings like arrays
1920 // Using charAt should be more compatible.
1921 return (input + '').toLowerCase().charAt(0) === 'p';
1922 }
1923
1924 var defaultLocaleMeridiemParse = /[ap]\.?m?\.?/i;
1925
1926 function localeMeridiem(hours, minutes, isLower) {
1927 if (hours > 11) {
1928 return isLower ? 'pm' : 'PM';
1929 } else {
1930 return isLower ? 'am' : 'AM';
1931 }
1932 } // MOMENTS
1933 // Setting the hour should keep the time, because the user explicitly
1934 // specified which hour they want. So trying to maintain the same hour (in
1935 // a new timezone) makes sense. Adding/subtracting hours does not follow
1936 // this rule.
1937
1938
1939 var getSetHour = makeGetSet('Hours', true);
1940 var baseConfig = {
1941 calendar: defaultCalendar,
1942 longDateFormat: defaultLongDateFormat,
1943 invalidDate: defaultInvalidDate,
1944 ordinal: defaultOrdinal,
1945 dayOfMonthOrdinalParse: defaultDayOfMonthOrdinalParse,
1946 relativeTime: defaultRelativeTime,
1947 months: defaultLocaleMonths,
1948 monthsShort: defaultLocaleMonthsShort,
1949 week: defaultLocaleWeek,
1950 weekdays: defaultLocaleWeekdays,
1951 weekdaysMin: defaultLocaleWeekdaysMin,
1952 weekdaysShort: defaultLocaleWeekdaysShort,
1953 meridiemParse: defaultLocaleMeridiemParse
1954 }; // internal storage for locale config files
1955
1956 var locales = {};
1957 var localeFamilies = {};
1958 var globalLocale;
1959
1960 function normalizeLocale(key) {
1961 return key ? key.toLowerCase().replace('_', '-') : key;
1962 } // pick the locale from the array
1963 // try ['en-au', 'en-gb'] as 'en-au', 'en-gb', 'en', as in move through the list trying each
1964 // substring from most specific to least, but move to the next array item if it's a more specific variant than the current root
1965
1966
1967 function chooseLocale(names) {
1968 var i = 0,
1969 j,
1970 next,
1971 locale,
1972 split;
1973
1974 while (i < names.length) {
1975 split = normalizeLocale(names[i]).split('-');
1976 j = split.length;
1977 next = normalizeLocale(names[i + 1]);
1978 next = next ? next.split('-') : null;
1979
1980 while (j > 0) {
1981 locale = loadLocale(split.slice(0, j).join('-'));
1982
1983 if (locale) {
1984 return locale;
1985 }
1986
1987 if (next && next.length >= j && compareArrays(split, next, true) >= j - 1) {
1988 //the next array item is better than a shallower substring of this one
1989 break;
1990 }
1991
1992 j--;
1993 }
1994
1995 i++;
1996 }
1997
1998 return globalLocale;
1999 }
2000
2001 function loadLocale(name) {
2002 var oldLocale = null; // TODO: Find a better way to register and load all the locales in Node
2003
2004 if (!locales[name] && 'object' !== 'undefined' && module && module.exports) {
2005 try {
2006 oldLocale = globalLocale._abbr;
2007 var aliasedRequire = commonjsRequire;
2008 aliasedRequire('./locale/' + name);
2009 getSetGlobalLocale(oldLocale);
2010 } catch (e) {}
2011 }
2012
2013 return locales[name];
2014 } // This function will load locale and then set the global locale. If
2015 // no arguments are passed in, it will simply return the current global
2016 // locale key.
2017
2018
2019 function getSetGlobalLocale(key, values) {
2020 var data;
2021
2022 if (key) {
2023 if (isUndefined(values)) {
2024 data = getLocale(key);
2025 } else {
2026 data = defineLocale(key, values);
2027 }
2028
2029 if (data) {
2030 // moment.duration._locale = moment._locale = data;
2031 globalLocale = data;
2032 } else {
2033 if (typeof console !== 'undefined' && console.warn) {
2034 //warn user if arguments are passed but the locale could not be set
2035 console.warn('Locale ' + key + ' not found. Did you forget to load it?');
2036 }
2037 }
2038 }
2039
2040 return globalLocale._abbr;
2041 }
2042
2043 function defineLocale(name, config) {
2044 if (config !== null) {
2045 var locale,
2046 parentConfig = baseConfig;
2047 config.abbr = name;
2048
2049 if (locales[name] != null) {
2050 deprecateSimple('defineLocaleOverride', 'use moment.updateLocale(localeName, config) to change ' + 'an existing locale. moment.defineLocale(localeName, ' + 'config) should only be used for creating a new locale ' + 'See http://momentjs.com/guides/#/warnings/define-locale/ for more info.');
2051 parentConfig = locales[name]._config;
2052 } else if (config.parentLocale != null) {
2053 if (locales[config.parentLocale] != null) {
2054 parentConfig = locales[config.parentLocale]._config;
2055 } else {
2056 locale = loadLocale(config.parentLocale);
2057
2058 if (locale != null) {
2059 parentConfig = locale._config;
2060 } else {
2061 if (!localeFamilies[config.parentLocale]) {
2062 localeFamilies[config.parentLocale] = [];
2063 }
2064
2065 localeFamilies[config.parentLocale].push({
2066 name: name,
2067 config: config
2068 });
2069 return null;
2070 }
2071 }
2072 }
2073
2074 locales[name] = new Locale(mergeConfigs(parentConfig, config));
2075
2076 if (localeFamilies[name]) {
2077 localeFamilies[name].forEach(function (x) {
2078 defineLocale(x.name, x.config);
2079 });
2080 } // backwards compat for now: also set the locale
2081 // make sure we set the locale AFTER all child locales have been
2082 // created, so we won't end up with the child locale set.
2083
2084
2085 getSetGlobalLocale(name);
2086 return locales[name];
2087 } else {
2088 // useful for testing
2089 delete locales[name];
2090 return null;
2091 }
2092 }
2093
2094 function updateLocale(name, config) {
2095 if (config != null) {
2096 var locale,
2097 tmpLocale,
2098 parentConfig = baseConfig; // MERGE
2099
2100 tmpLocale = loadLocale(name);
2101
2102 if (tmpLocale != null) {
2103 parentConfig = tmpLocale._config;
2104 }
2105
2106 config = mergeConfigs(parentConfig, config);
2107 locale = new Locale(config);
2108 locale.parentLocale = locales[name];
2109 locales[name] = locale; // backwards compat for now: also set the locale
2110
2111 getSetGlobalLocale(name);
2112 } else {
2113 // pass null for config to unupdate, useful for tests
2114 if (locales[name] != null) {
2115 if (locales[name].parentLocale != null) {
2116 locales[name] = locales[name].parentLocale;
2117 } else if (locales[name] != null) {
2118 delete locales[name];
2119 }
2120 }
2121 }
2122
2123 return locales[name];
2124 } // returns locale data
2125
2126
2127 function getLocale(key) {
2128 var locale;
2129
2130 if (key && key._locale && key._locale._abbr) {
2131 key = key._locale._abbr;
2132 }
2133
2134 if (!key) {
2135 return globalLocale;
2136 }
2137
2138 if (!isArray(key)) {
2139 //short-circuit everything else
2140 locale = loadLocale(key);
2141
2142 if (locale) {
2143 return locale;
2144 }
2145
2146 key = [key];
2147 }
2148
2149 return chooseLocale(key);
2150 }
2151
2152 function listLocales() {
2153 return keys(locales);
2154 }
2155
2156 function checkOverflow(m) {
2157 var overflow;
2158 var a = m._a;
2159
2160 if (a && getParsingFlags(m).overflow === -2) {
2161 overflow = a[MONTH] < 0 || a[MONTH] > 11 ? MONTH : a[DATE] < 1 || a[DATE] > daysInMonth(a[YEAR], a[MONTH]) ? DATE : a[HOUR] < 0 || a[HOUR] > 24 || a[HOUR] === 24 && (a[MINUTE] !== 0 || a[SECOND] !== 0 || a[MILLISECOND] !== 0) ? HOUR : a[MINUTE] < 0 || a[MINUTE] > 59 ? MINUTE : a[SECOND] < 0 || a[SECOND] > 59 ? SECOND : a[MILLISECOND] < 0 || a[MILLISECOND] > 999 ? MILLISECOND : -1;
2162
2163 if (getParsingFlags(m)._overflowDayOfYear && (overflow < YEAR || overflow > DATE)) {
2164 overflow = DATE;
2165 }
2166
2167 if (getParsingFlags(m)._overflowWeeks && overflow === -1) {
2168 overflow = WEEK;
2169 }
2170
2171 if (getParsingFlags(m)._overflowWeekday && overflow === -1) {
2172 overflow = WEEKDAY;
2173 }
2174
2175 getParsingFlags(m).overflow = overflow;
2176 }
2177
2178 return m;
2179 } // Pick the first defined of two or three arguments.
2180
2181
2182 function defaults(a, b, c) {
2183 if (a != null) {
2184 return a;
2185 }
2186
2187 if (b != null) {
2188 return b;
2189 }
2190
2191 return c;
2192 }
2193
2194 function currentDateArray(config) {
2195 // hooks is actually the exported moment object
2196 var nowValue = new Date(hooks.now());
2197
2198 if (config._useUTC) {
2199 return [nowValue.getUTCFullYear(), nowValue.getUTCMonth(), nowValue.getUTCDate()];
2200 }
2201
2202 return [nowValue.getFullYear(), nowValue.getMonth(), nowValue.getDate()];
2203 } // convert an array to a date.
2204 // the array should mirror the parameters below
2205 // note: all values past the year are optional and will default to the lowest possible value.
2206 // [year, month, day , hour, minute, second, millisecond]
2207
2208
2209 function configFromArray(config) {
2210 var i,
2211 date,
2212 input = [],
2213 currentDate,
2214 expectedWeekday,
2215 yearToUse;
2216
2217 if (config._d) {
2218 return;
2219 }
2220
2221 currentDate = currentDateArray(config); //compute day of the year from weeks and weekdays
2222
2223 if (config._w && config._a[DATE] == null && config._a[MONTH] == null) {
2224 dayOfYearFromWeekInfo(config);
2225 } //if the day of the year is set, figure out what it is
2226
2227
2228 if (config._dayOfYear != null) {
2229 yearToUse = defaults(config._a[YEAR], currentDate[YEAR]);
2230
2231 if (config._dayOfYear > daysInYear(yearToUse) || config._dayOfYear === 0) {
2232 getParsingFlags(config)._overflowDayOfYear = true;
2233 }
2234
2235 date = createUTCDate(yearToUse, 0, config._dayOfYear);
2236 config._a[MONTH] = date.getUTCMonth();
2237 config._a[DATE] = date.getUTCDate();
2238 } // Default to current date.
2239 // * if no year, month, day of month are given, default to today
2240 // * if day of month is given, default month and year
2241 // * if month is given, default only year
2242 // * if year is given, don't default anything
2243
2244
2245 for (i = 0; i < 3 && config._a[i] == null; ++i) {
2246 config._a[i] = input[i] = currentDate[i];
2247 } // Zero out whatever was not defaulted, including time
2248
2249
2250 for (; i < 7; i++) {
2251 config._a[i] = input[i] = config._a[i] == null ? i === 2 ? 1 : 0 : config._a[i];
2252 } // Check for 24:00:00.000
2253
2254
2255 if (config._a[HOUR] === 24 && config._a[MINUTE] === 0 && config._a[SECOND] === 0 && config._a[MILLISECOND] === 0) {
2256 config._nextDay = true;
2257 config._a[HOUR] = 0;
2258 }
2259
2260 config._d = (config._useUTC ? createUTCDate : createDate).apply(null, input);
2261 expectedWeekday = config._useUTC ? config._d.getUTCDay() : config._d.getDay(); // Apply timezone offset from input. The actual utcOffset can be changed
2262 // with parseZone.
2263
2264 if (config._tzm != null) {
2265 config._d.setUTCMinutes(config._d.getUTCMinutes() - config._tzm);
2266 }
2267
2268 if (config._nextDay) {
2269 config._a[HOUR] = 24;
2270 } // check for mismatching day of week
2271
2272
2273 if (config._w && typeof config._w.d !== 'undefined' && config._w.d !== expectedWeekday) {
2274 getParsingFlags(config).weekdayMismatch = true;
2275 }
2276 }
2277
2278 function dayOfYearFromWeekInfo(config) {
2279 var w, weekYear, week, weekday, dow, doy, temp, weekdayOverflow;
2280 w = config._w;
2281
2282 if (w.GG != null || w.W != null || w.E != null) {
2283 dow = 1;
2284 doy = 4; // TODO: We need to take the current isoWeekYear, but that depends on
2285 // how we interpret now (local, utc, fixed offset). So create
2286 // a now version of current config (take local/utc/offset flags, and
2287 // create now).
2288
2289 weekYear = defaults(w.GG, config._a[YEAR], weekOfYear(createLocal(), 1, 4).year);
2290 week = defaults(w.W, 1);
2291 weekday = defaults(w.E, 1);
2292
2293 if (weekday < 1 || weekday > 7) {
2294 weekdayOverflow = true;
2295 }
2296 } else {
2297 dow = config._locale._week.dow;
2298 doy = config._locale._week.doy;
2299 var curWeek = weekOfYear(createLocal(), dow, doy);
2300 weekYear = defaults(w.gg, config._a[YEAR], curWeek.year); // Default to current week.
2301
2302 week = defaults(w.w, curWeek.week);
2303
2304 if (w.d != null) {
2305 // weekday -- low day numbers are considered next week
2306 weekday = w.d;
2307
2308 if (weekday < 0 || weekday > 6) {
2309 weekdayOverflow = true;
2310 }
2311 } else if (w.e != null) {
2312 // local weekday -- counting starts from beginning of week
2313 weekday = w.e + dow;
2314
2315 if (w.e < 0 || w.e > 6) {
2316 weekdayOverflow = true;
2317 }
2318 } else {
2319 // default to beginning of week
2320 weekday = dow;
2321 }
2322 }
2323
2324 if (week < 1 || week > weeksInYear(weekYear, dow, doy)) {
2325 getParsingFlags(config)._overflowWeeks = true;
2326 } else if (weekdayOverflow != null) {
2327 getParsingFlags(config)._overflowWeekday = true;
2328 } else {
2329 temp = dayOfYearFromWeeks(weekYear, week, weekday, dow, doy);
2330 config._a[YEAR] = temp.year;
2331 config._dayOfYear = temp.dayOfYear;
2332 }
2333 } // iso 8601 regex
2334 // 0000-00-00 0000-W00 or 0000-W00-0 + T + 00 or 00:00 or 00:00:00 or 00:00:00.000 + +00:00 or +0000 or +00)
2335
2336
2337 var extendedIsoRegex = /^\s*((?:[+-]\d{6}|\d{4})-(?:\d\d-\d\d|W\d\d-\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?::\d\d(?::\d\d(?:[.,]\d+)?)?)?)([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?$/;
2338 var basicIsoRegex = /^\s*((?:[+-]\d{6}|\d{4})(?:\d\d\d\d|W\d\d\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?:\d\d(?:\d\d(?:[.,]\d+)?)?)?)([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?$/;
2339 var tzRegex = /Z|[+-]\d\d(?::?\d\d)?/;
2340 var isoDates = [['YYYYYY-MM-DD', /[+-]\d{6}-\d\d-\d\d/], ['YYYY-MM-DD', /\d{4}-\d\d-\d\d/], ['GGGG-[W]WW-E', /\d{4}-W\d\d-\d/], ['GGGG-[W]WW', /\d{4}-W\d\d/, false], ['YYYY-DDD', /\d{4}-\d{3}/], ['YYYY-MM', /\d{4}-\d\d/, false], ['YYYYYYMMDD', /[+-]\d{10}/], ['YYYYMMDD', /\d{8}/], // YYYYMM is NOT allowed by the standard
2341 ['GGGG[W]WWE', /\d{4}W\d{3}/], ['GGGG[W]WW', /\d{4}W\d{2}/, false], ['YYYYDDD', /\d{7}/]]; // iso time formats and regexes
2342
2343 var isoTimes = [['HH:mm:ss.SSSS', /\d\d:\d\d:\d\d\.\d+/], ['HH:mm:ss,SSSS', /\d\d:\d\d:\d\d,\d+/], ['HH:mm:ss', /\d\d:\d\d:\d\d/], ['HH:mm', /\d\d:\d\d/], ['HHmmss.SSSS', /\d\d\d\d\d\d\.\d+/], ['HHmmss,SSSS', /\d\d\d\d\d\d,\d+/], ['HHmmss', /\d\d\d\d\d\d/], ['HHmm', /\d\d\d\d/], ['HH', /\d\d/]];
2344 var aspNetJsonRegex = /^\/?Date\((\-?\d+)/i; // date from iso format
2345
2346 function configFromISO(config) {
2347 var i,
2348 l,
2349 string = config._i,
2350 match = extendedIsoRegex.exec(string) || basicIsoRegex.exec(string),
2351 allowTime,
2352 dateFormat,
2353 timeFormat,
2354 tzFormat;
2355
2356 if (match) {
2357 getParsingFlags(config).iso = true;
2358
2359 for (i = 0, l = isoDates.length; i < l; i++) {
2360 if (isoDates[i][1].exec(match[1])) {
2361 dateFormat = isoDates[i][0];
2362 allowTime = isoDates[i][2] !== false;
2363 break;
2364 }
2365 }
2366
2367 if (dateFormat == null) {
2368 config._isValid = false;
2369 return;
2370 }
2371
2372 if (match[3]) {
2373 for (i = 0, l = isoTimes.length; i < l; i++) {
2374 if (isoTimes[i][1].exec(match[3])) {
2375 // match[2] should be 'T' or space
2376 timeFormat = (match[2] || ' ') + isoTimes[i][0];
2377 break;
2378 }
2379 }
2380
2381 if (timeFormat == null) {
2382 config._isValid = false;
2383 return;
2384 }
2385 }
2386
2387 if (!allowTime && timeFormat != null) {
2388 config._isValid = false;
2389 return;
2390 }
2391
2392 if (match[4]) {
2393 if (tzRegex.exec(match[4])) {
2394 tzFormat = 'Z';
2395 } else {
2396 config._isValid = false;
2397 return;
2398 }
2399 }
2400
2401 config._f = dateFormat + (timeFormat || '') + (tzFormat || '');
2402 configFromStringAndFormat(config);
2403 } else {
2404 config._isValid = false;
2405 }
2406 } // RFC 2822 regex: For details see https://tools.ietf.org/html/rfc2822#section-3.3
2407
2408
2409 var rfc2822 = /^(?:(Mon|Tue|Wed|Thu|Fri|Sat|Sun),?\s)?(\d{1,2})\s(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\s(\d{2,4})\s(\d\d):(\d\d)(?::(\d\d))?\s(?:(UT|GMT|[ECMP][SD]T)|([Zz])|([+-]\d{4}))$/;
2410
2411 function extractFromRFC2822Strings(yearStr, monthStr, dayStr, hourStr, minuteStr, secondStr) {
2412 var result = [untruncateYear(yearStr), defaultLocaleMonthsShort.indexOf(monthStr), parseInt(dayStr, 10), parseInt(hourStr, 10), parseInt(minuteStr, 10)];
2413
2414 if (secondStr) {
2415 result.push(parseInt(secondStr, 10));
2416 }
2417
2418 return result;
2419 }
2420
2421 function untruncateYear(yearStr) {
2422 var year = parseInt(yearStr, 10);
2423
2424 if (year <= 49) {
2425 return 2000 + year;
2426 } else if (year <= 999) {
2427 return 1900 + year;
2428 }
2429
2430 return year;
2431 }
2432
2433 function preprocessRFC2822(s) {
2434 // Remove comments and folding whitespace and replace multiple-spaces with a single space
2435 return s.replace(/\([^)]*\)|[\n\t]/g, ' ').replace(/(\s\s+)/g, ' ').replace(/^\s\s*/, '').replace(/\s\s*$/, '');
2436 }
2437
2438 function checkWeekday(weekdayStr, parsedInput, config) {
2439 if (weekdayStr) {
2440 // TODO: Replace the vanilla JS Date object with an indepentent day-of-week check.
2441 var weekdayProvided = defaultLocaleWeekdaysShort.indexOf(weekdayStr),
2442 weekdayActual = new Date(parsedInput[0], parsedInput[1], parsedInput[2]).getDay();
2443
2444 if (weekdayProvided !== weekdayActual) {
2445 getParsingFlags(config).weekdayMismatch = true;
2446 config._isValid = false;
2447 return false;
2448 }
2449 }
2450
2451 return true;
2452 }
2453
2454 var obsOffsets = {
2455 UT: 0,
2456 GMT: 0,
2457 EDT: -4 * 60,
2458 EST: -5 * 60,
2459 CDT: -5 * 60,
2460 CST: -6 * 60,
2461 MDT: -6 * 60,
2462 MST: -7 * 60,
2463 PDT: -7 * 60,
2464 PST: -8 * 60
2465 };
2466
2467 function calculateOffset(obsOffset, militaryOffset, numOffset) {
2468 if (obsOffset) {
2469 return obsOffsets[obsOffset];
2470 } else if (militaryOffset) {
2471 // the only allowed military tz is Z
2472 return 0;
2473 } else {
2474 var hm = parseInt(numOffset, 10);
2475 var m = hm % 100,
2476 h = (hm - m) / 100;
2477 return h * 60 + m;
2478 }
2479 } // date and time from ref 2822 format
2480
2481
2482 function configFromRFC2822(config) {
2483 var match = rfc2822.exec(preprocessRFC2822(config._i));
2484
2485 if (match) {
2486 var parsedArray = extractFromRFC2822Strings(match[4], match[3], match[2], match[5], match[6], match[7]);
2487
2488 if (!checkWeekday(match[1], parsedArray, config)) {
2489 return;
2490 }
2491
2492 config._a = parsedArray;
2493 config._tzm = calculateOffset(match[8], match[9], match[10]);
2494 config._d = createUTCDate.apply(null, config._a);
2495
2496 config._d.setUTCMinutes(config._d.getUTCMinutes() - config._tzm);
2497
2498 getParsingFlags(config).rfc2822 = true;
2499 } else {
2500 config._isValid = false;
2501 }
2502 } // date from iso format or fallback
2503
2504
2505 function configFromString(config) {
2506 var matched = aspNetJsonRegex.exec(config._i);
2507
2508 if (matched !== null) {
2509 config._d = new Date(+matched[1]);
2510 return;
2511 }
2512
2513 configFromISO(config);
2514
2515 if (config._isValid === false) {
2516 delete config._isValid;
2517 } else {
2518 return;
2519 }
2520
2521 configFromRFC2822(config);
2522
2523 if (config._isValid === false) {
2524 delete config._isValid;
2525 } else {
2526 return;
2527 } // Final attempt, use Input Fallback
2528
2529
2530 hooks.createFromInputFallback(config);
2531 }
2532
2533 hooks.createFromInputFallback = deprecate('value provided is not in a recognized RFC2822 or ISO format. moment construction falls back to js Date(), ' + 'which is not reliable across all browsers and versions. Non RFC2822/ISO date formats are ' + 'discouraged and will be removed in an upcoming major release. Please refer to ' + 'http://momentjs.com/guides/#/warnings/js-date/ for more info.', function (config) {
2534 config._d = new Date(config._i + (config._useUTC ? ' UTC' : ''));
2535 }); // constant that refers to the ISO standard
2536
2537 hooks.ISO_8601 = function () {}; // constant that refers to the RFC 2822 form
2538
2539
2540 hooks.RFC_2822 = function () {}; // date from string and format string
2541
2542
2543 function configFromStringAndFormat(config) {
2544 // TODO: Move this to another part of the creation flow to prevent circular deps
2545 if (config._f === hooks.ISO_8601) {
2546 configFromISO(config);
2547 return;
2548 }
2549
2550 if (config._f === hooks.RFC_2822) {
2551 configFromRFC2822(config);
2552 return;
2553 }
2554
2555 config._a = [];
2556 getParsingFlags(config).empty = true; // This array is used to make a Date, either with `new Date` or `Date.UTC`
2557
2558 var string = '' + config._i,
2559 i,
2560 parsedInput,
2561 tokens,
2562 token,
2563 skipped,
2564 stringLength = string.length,
2565 totalParsedInputLength = 0;
2566 tokens = expandFormat(config._f, config._locale).match(formattingTokens) || [];
2567
2568 for (i = 0; i < tokens.length; i++) {
2569 token = tokens[i];
2570 parsedInput = (string.match(getParseRegexForToken(token, config)) || [])[0]; // console.log('token', token, 'parsedInput', parsedInput,
2571 // 'regex', getParseRegexForToken(token, config));
2572
2573 if (parsedInput) {
2574 skipped = string.substr(0, string.indexOf(parsedInput));
2575
2576 if (skipped.length > 0) {
2577 getParsingFlags(config).unusedInput.push(skipped);
2578 }
2579
2580 string = string.slice(string.indexOf(parsedInput) + parsedInput.length);
2581 totalParsedInputLength += parsedInput.length;
2582 } // don't parse if it's not a known token
2583
2584
2585 if (formatTokenFunctions[token]) {
2586 if (parsedInput) {
2587 getParsingFlags(config).empty = false;
2588 } else {
2589 getParsingFlags(config).unusedTokens.push(token);
2590 }
2591
2592 addTimeToArrayFromToken(token, parsedInput, config);
2593 } else if (config._strict && !parsedInput) {
2594 getParsingFlags(config).unusedTokens.push(token);
2595 }
2596 } // add remaining unparsed input length to the string
2597
2598
2599 getParsingFlags(config).charsLeftOver = stringLength - totalParsedInputLength;
2600
2601 if (string.length > 0) {
2602 getParsingFlags(config).unusedInput.push(string);
2603 } // clear _12h flag if hour is <= 12
2604
2605
2606 if (config._a[HOUR] <= 12 && getParsingFlags(config).bigHour === true && config._a[HOUR] > 0) {
2607 getParsingFlags(config).bigHour = undefined;
2608 }
2609
2610 getParsingFlags(config).parsedDateParts = config._a.slice(0);
2611 getParsingFlags(config).meridiem = config._meridiem; // handle meridiem
2612
2613 config._a[HOUR] = meridiemFixWrap(config._locale, config._a[HOUR], config._meridiem);
2614 configFromArray(config);
2615 checkOverflow(config);
2616 }
2617
2618 function meridiemFixWrap(locale, hour, meridiem) {
2619 var isPm;
2620
2621 if (meridiem == null) {
2622 // nothing to do
2623 return hour;
2624 }
2625
2626 if (locale.meridiemHour != null) {
2627 return locale.meridiemHour(hour, meridiem);
2628 } else if (locale.isPM != null) {
2629 // Fallback
2630 isPm = locale.isPM(meridiem);
2631
2632 if (isPm && hour < 12) {
2633 hour += 12;
2634 }
2635
2636 if (!isPm && hour === 12) {
2637 hour = 0;
2638 }
2639
2640 return hour;
2641 } else {
2642 // this is not supposed to happen
2643 return hour;
2644 }
2645 } // date from string and array of format strings
2646
2647
2648 function configFromStringAndArray(config) {
2649 var tempConfig, bestMoment, scoreToBeat, i, currentScore;
2650
2651 if (config._f.length === 0) {
2652 getParsingFlags(config).invalidFormat = true;
2653 config._d = new Date(NaN);
2654 return;
2655 }
2656
2657 for (i = 0; i < config._f.length; i++) {
2658 currentScore = 0;
2659 tempConfig = copyConfig({}, config);
2660
2661 if (config._useUTC != null) {
2662 tempConfig._useUTC = config._useUTC;
2663 }
2664
2665 tempConfig._f = config._f[i];
2666 configFromStringAndFormat(tempConfig);
2667
2668 if (!isValid(tempConfig)) {
2669 continue;
2670 } // if there is any input that was not parsed add a penalty for that format
2671
2672
2673 currentScore += getParsingFlags(tempConfig).charsLeftOver; //or tokens
2674
2675 currentScore += getParsingFlags(tempConfig).unusedTokens.length * 10;
2676 getParsingFlags(tempConfig).score = currentScore;
2677
2678 if (scoreToBeat == null || currentScore < scoreToBeat) {
2679 scoreToBeat = currentScore;
2680 bestMoment = tempConfig;
2681 }
2682 }
2683
2684 extend(config, bestMoment || tempConfig);
2685 }
2686
2687 function configFromObject(config) {
2688 if (config._d) {
2689 return;
2690 }
2691
2692 var i = normalizeObjectUnits(config._i);
2693 config._a = map([i.year, i.month, i.day || i.date, i.hour, i.minute, i.second, i.millisecond], function (obj) {
2694 return obj && parseInt(obj, 10);
2695 });
2696 configFromArray(config);
2697 }
2698
2699 function createFromConfig(config) {
2700 var res = new Moment(checkOverflow(prepareConfig(config)));
2701
2702 if (res._nextDay) {
2703 // Adding is smart enough around DST
2704 res.add(1, 'd');
2705 res._nextDay = undefined;
2706 }
2707
2708 return res;
2709 }
2710
2711 function prepareConfig(config) {
2712 var input = config._i,
2713 format = config._f;
2714 config._locale = config._locale || getLocale(config._l);
2715
2716 if (input === null || format === undefined && input === '') {
2717 return createInvalid({
2718 nullInput: true
2719 });
2720 }
2721
2722 if (typeof input === 'string') {
2723 config._i = input = config._locale.preparse(input);
2724 }
2725
2726 if (isMoment(input)) {
2727 return new Moment(checkOverflow(input));
2728 } else if (isDate(input)) {
2729 config._d = input;
2730 } else if (isArray(format)) {
2731 configFromStringAndArray(config);
2732 } else if (format) {
2733 configFromStringAndFormat(config);
2734 } else {
2735 configFromInput(config);
2736 }
2737
2738 if (!isValid(config)) {
2739 config._d = null;
2740 }
2741
2742 return config;
2743 }
2744
2745 function configFromInput(config) {
2746 var input = config._i;
2747
2748 if (isUndefined(input)) {
2749 config._d = new Date(hooks.now());
2750 } else if (isDate(input)) {
2751 config._d = new Date(input.valueOf());
2752 } else if (typeof input === 'string') {
2753 configFromString(config);
2754 } else if (isArray(input)) {
2755 config._a = map(input.slice(0), function (obj) {
2756 return parseInt(obj, 10);
2757 });
2758 configFromArray(config);
2759 } else if (isObject(input)) {
2760 configFromObject(config);
2761 } else if (isNumber(input)) {
2762 // from milliseconds
2763 config._d = new Date(input);
2764 } else {
2765 hooks.createFromInputFallback(config);
2766 }
2767 }
2768
2769 function createLocalOrUTC(input, format, locale, strict, isUTC) {
2770 var c = {};
2771
2772 if (locale === true || locale === false) {
2773 strict = locale;
2774 locale = undefined;
2775 }
2776
2777 if (isObject(input) && isObjectEmpty(input) || isArray(input) && input.length === 0) {
2778 input = undefined;
2779 } // object construction must be done this way.
2780 // https://github.com/moment/moment/issues/1423
2781
2782
2783 c._isAMomentObject = true;
2784 c._useUTC = c._isUTC = isUTC;
2785 c._l = locale;
2786 c._i = input;
2787 c._f = format;
2788 c._strict = strict;
2789 return createFromConfig(c);
2790 }
2791
2792 function createLocal(input, format, locale, strict) {
2793 return createLocalOrUTC(input, format, locale, strict, false);
2794 }
2795
2796 var prototypeMin = deprecate('moment().min is deprecated, use moment.max instead. http://momentjs.com/guides/#/warnings/min-max/', function () {
2797 var other = createLocal.apply(null, arguments);
2798
2799 if (this.isValid() && other.isValid()) {
2800 return other < this ? this : other;
2801 } else {
2802 return createInvalid();
2803 }
2804 });
2805 var prototypeMax = deprecate('moment().max is deprecated, use moment.min instead. http://momentjs.com/guides/#/warnings/min-max/', function () {
2806 var other = createLocal.apply(null, arguments);
2807
2808 if (this.isValid() && other.isValid()) {
2809 return other > this ? this : other;
2810 } else {
2811 return createInvalid();
2812 }
2813 }); // Pick a moment m from moments so that m[fn](other) is true for all
2814 // other. This relies on the function fn to be transitive.
2815 //
2816 // moments should either be an array of moment objects or an array, whose
2817 // first element is an array of moment objects.
2818
2819 function pickBy(fn, moments) {
2820 var res, i;
2821
2822 if (moments.length === 1 && isArray(moments[0])) {
2823 moments = moments[0];
2824 }
2825
2826 if (!moments.length) {
2827 return createLocal();
2828 }
2829
2830 res = moments[0];
2831
2832 for (i = 1; i < moments.length; ++i) {
2833 if (!moments[i].isValid() || moments[i][fn](res)) {
2834 res = moments[i];
2835 }
2836 }
2837
2838 return res;
2839 } // TODO: Use [].sort instead?
2840
2841
2842 function min() {
2843 var args = [].slice.call(arguments, 0);
2844 return pickBy('isBefore', args);
2845 }
2846
2847 function max() {
2848 var args = [].slice.call(arguments, 0);
2849 return pickBy('isAfter', args);
2850 }
2851
2852 var now = function () {
2853 return Date.now ? Date.now() : +new Date();
2854 };
2855
2856 var ordering = ['year', 'quarter', 'month', 'week', 'day', 'hour', 'minute', 'second', 'millisecond'];
2857
2858 function isDurationValid(m) {
2859 for (var key in m) {
2860 if (!(indexOf.call(ordering, key) !== -1 && (m[key] == null || !isNaN(m[key])))) {
2861 return false;
2862 }
2863 }
2864
2865 var unitHasDecimal = false;
2866
2867 for (var i = 0; i < ordering.length; ++i) {
2868 if (m[ordering[i]]) {
2869 if (unitHasDecimal) {
2870 return false; // only allow non-integers for smallest unit
2871 }
2872
2873 if (parseFloat(m[ordering[i]]) !== toInt(m[ordering[i]])) {
2874 unitHasDecimal = true;
2875 }
2876 }
2877 }
2878
2879 return true;
2880 }
2881
2882 function isValid$1() {
2883 return this._isValid;
2884 }
2885
2886 function createInvalid$1() {
2887 return createDuration(NaN);
2888 }
2889
2890 function Duration(duration) {
2891 var normalizedInput = normalizeObjectUnits(duration),
2892 years = normalizedInput.year || 0,
2893 quarters = normalizedInput.quarter || 0,
2894 months = normalizedInput.month || 0,
2895 weeks = normalizedInput.week || normalizedInput.isoWeek || 0,
2896 days = normalizedInput.day || 0,
2897 hours = normalizedInput.hour || 0,
2898 minutes = normalizedInput.minute || 0,
2899 seconds = normalizedInput.second || 0,
2900 milliseconds = normalizedInput.millisecond || 0;
2901 this._isValid = isDurationValid(normalizedInput); // representation for dateAddRemove
2902
2903 this._milliseconds = +milliseconds + seconds * 1e3 + // 1000
2904 minutes * 6e4 + // 1000 * 60
2905 hours * 1000 * 60 * 60; //using 1000 * 60 * 60 instead of 36e5 to avoid floating point rounding errors https://github.com/moment/moment/issues/2978
2906 // Because of dateAddRemove treats 24 hours as different from a
2907 // day when working around DST, we need to store them separately
2908
2909 this._days = +days + weeks * 7; // It is impossible to translate months into days without knowing
2910 // which months you are are talking about, so we have to store
2911 // it separately.
2912
2913 this._months = +months + quarters * 3 + years * 12;
2914 this._data = {};
2915 this._locale = getLocale();
2916
2917 this._bubble();
2918 }
2919
2920 function isDuration(obj) {
2921 return obj instanceof Duration;
2922 }
2923
2924 function absRound(number) {
2925 if (number < 0) {
2926 return Math.round(-1 * number) * -1;
2927 } else {
2928 return Math.round(number);
2929 }
2930 } // FORMATTING
2931
2932
2933 function offset(token, separator) {
2934 addFormatToken(token, 0, 0, function () {
2935 var offset = this.utcOffset();
2936 var sign = '+';
2937
2938 if (offset < 0) {
2939 offset = -offset;
2940 sign = '-';
2941 }
2942
2943 return sign + zeroFill(~~(offset / 60), 2) + separator + zeroFill(~~offset % 60, 2);
2944 });
2945 }
2946
2947 offset('Z', ':');
2948 offset('ZZ', ''); // PARSING
2949
2950 addRegexToken('Z', matchShortOffset);
2951 addRegexToken('ZZ', matchShortOffset);
2952 addParseToken(['Z', 'ZZ'], function (input, array, config) {
2953 config._useUTC = true;
2954 config._tzm = offsetFromString(matchShortOffset, input);
2955 }); // HELPERS
2956 // timezone chunker
2957 // '+10:00' > ['10', '00']
2958 // '-1530' > ['-15', '30']
2959
2960 var chunkOffset = /([\+\-]|\d\d)/gi;
2961
2962 function offsetFromString(matcher, string) {
2963 var matches = (string || '').match(matcher);
2964
2965 if (matches === null) {
2966 return null;
2967 }
2968
2969 var chunk = matches[matches.length - 1] || [];
2970 var parts = (chunk + '').match(chunkOffset) || ['-', 0, 0];
2971 var minutes = +(parts[1] * 60) + toInt(parts[2]);
2972 return minutes === 0 ? 0 : parts[0] === '+' ? minutes : -minutes;
2973 } // Return a moment from input, that is local/utc/zone equivalent to model.
2974
2975
2976 function cloneWithOffset(input, model) {
2977 var res, diff;
2978
2979 if (model._isUTC) {
2980 res = model.clone();
2981 diff = (isMoment(input) || isDate(input) ? input.valueOf() : createLocal(input).valueOf()) - res.valueOf(); // Use low-level api, because this fn is low-level api.
2982
2983 res._d.setTime(res._d.valueOf() + diff);
2984
2985 hooks.updateOffset(res, false);
2986 return res;
2987 } else {
2988 return createLocal(input).local();
2989 }
2990 }
2991
2992 function getDateOffset(m) {
2993 // On Firefox.24 Date#getTimezoneOffset returns a floating point.
2994 // https://github.com/moment/moment/pull/1871
2995 return -Math.round(m._d.getTimezoneOffset() / 15) * 15;
2996 } // HOOKS
2997 // This function will be called whenever a moment is mutated.
2998 // It is intended to keep the offset in sync with the timezone.
2999
3000
3001 hooks.updateOffset = function () {}; // MOMENTS
3002 // keepLocalTime = true means only change the timezone, without
3003 // affecting the local hour. So 5:31:26 +0300 --[utcOffset(2, true)]-->
3004 // 5:31:26 +0200 It is possible that 5:31:26 doesn't exist with offset
3005 // +0200, so we adjust the time as needed, to be valid.
3006 //
3007 // Keeping the time actually adds/subtracts (one hour)
3008 // from the actual represented time. That is why we call updateOffset
3009 // a second time. In case it wants us to change the offset again
3010 // _changeInProgress == true case, then we have to adjust, because
3011 // there is no such time in the given timezone.
3012
3013
3014 function getSetOffset(input, keepLocalTime, keepMinutes) {
3015 var offset = this._offset || 0,
3016 localAdjust;
3017
3018 if (!this.isValid()) {
3019 return input != null ? this : NaN;
3020 }
3021
3022 if (input != null) {
3023 if (typeof input === 'string') {
3024 input = offsetFromString(matchShortOffset, input);
3025
3026 if (input === null) {
3027 return this;
3028 }
3029 } else if (Math.abs(input) < 16 && !keepMinutes) {
3030 input = input * 60;
3031 }
3032
3033 if (!this._isUTC && keepLocalTime) {
3034 localAdjust = getDateOffset(this);
3035 }
3036
3037 this._offset = input;
3038 this._isUTC = true;
3039
3040 if (localAdjust != null) {
3041 this.add(localAdjust, 'm');
3042 }
3043
3044 if (offset !== input) {
3045 if (!keepLocalTime || this._changeInProgress) {
3046 addSubtract(this, createDuration(input - offset, 'm'), 1, false);
3047 } else if (!this._changeInProgress) {
3048 this._changeInProgress = true;
3049 hooks.updateOffset(this, true);
3050 this._changeInProgress = null;
3051 }
3052 }
3053
3054 return this;
3055 } else {
3056 return this._isUTC ? offset : getDateOffset(this);
3057 }
3058 }
3059
3060 function getSetZone(input, keepLocalTime) {
3061 if (input != null) {
3062 if (typeof input !== 'string') {
3063 input = -input;
3064 }
3065
3066 this.utcOffset(input, keepLocalTime);
3067 return this;
3068 } else {
3069 return -this.utcOffset();
3070 }
3071 }
3072
3073 function setOffsetToUTC(keepLocalTime) {
3074 return this.utcOffset(0, keepLocalTime);
3075 }
3076
3077 function setOffsetToLocal(keepLocalTime) {
3078 if (this._isUTC) {
3079 this.utcOffset(0, keepLocalTime);
3080 this._isUTC = false;
3081
3082 if (keepLocalTime) {
3083 this.subtract(getDateOffset(this), 'm');
3084 }
3085 }
3086
3087 return this;
3088 }
3089
3090 function setOffsetToParsedOffset() {
3091 if (this._tzm != null) {
3092 this.utcOffset(this._tzm, false, true);
3093 } else if (typeof this._i === 'string') {
3094 var tZone = offsetFromString(matchOffset, this._i);
3095
3096 if (tZone != null) {
3097 this.utcOffset(tZone);
3098 } else {
3099 this.utcOffset(0, true);
3100 }
3101 }
3102
3103 return this;
3104 }
3105
3106 function hasAlignedHourOffset(input) {
3107 if (!this.isValid()) {
3108 return false;
3109 }
3110
3111 input = input ? createLocal(input).utcOffset() : 0;
3112 return (this.utcOffset() - input) % 60 === 0;
3113 }
3114
3115 function isDaylightSavingTime() {
3116 return this.utcOffset() > this.clone().month(0).utcOffset() || this.utcOffset() > this.clone().month(5).utcOffset();
3117 }
3118
3119 function isDaylightSavingTimeShifted() {
3120 if (!isUndefined(this._isDSTShifted)) {
3121 return this._isDSTShifted;
3122 }
3123
3124 var c = {};
3125 copyConfig(c, this);
3126 c = prepareConfig(c);
3127
3128 if (c._a) {
3129 var other = c._isUTC ? createUTC(c._a) : createLocal(c._a);
3130 this._isDSTShifted = this.isValid() && compareArrays(c._a, other.toArray()) > 0;
3131 } else {
3132 this._isDSTShifted = false;
3133 }
3134
3135 return this._isDSTShifted;
3136 }
3137
3138 function isLocal() {
3139 return this.isValid() ? !this._isUTC : false;
3140 }
3141
3142 function isUtcOffset() {
3143 return this.isValid() ? this._isUTC : false;
3144 }
3145
3146 function isUtc() {
3147 return this.isValid() ? this._isUTC && this._offset === 0 : false;
3148 } // ASP.NET json date format regex
3149
3150
3151 var aspNetRegex = /^(\-|\+)?(?:(\d*)[. ])?(\d+)\:(\d+)(?:\:(\d+)(\.\d*)?)?$/; // from http://docs.closure-library.googlecode.com/git/closure_goog_date_date.js.source.html
3152 // somewhat more in line with 4.4.3.2 2004 spec, but allows decimal anywhere
3153 // and further modified to allow for strings containing both week and day
3154
3155 var isoRegex = /^(-|\+)?P(?:([-+]?[0-9,.]*)Y)?(?:([-+]?[0-9,.]*)M)?(?:([-+]?[0-9,.]*)W)?(?:([-+]?[0-9,.]*)D)?(?:T(?:([-+]?[0-9,.]*)H)?(?:([-+]?[0-9,.]*)M)?(?:([-+]?[0-9,.]*)S)?)?$/;
3156
3157 function createDuration(input, key) {
3158 var duration = input,
3159 // matching against regexp is expensive, do it on demand
3160 match = null,
3161 sign,
3162 ret,
3163 diffRes;
3164
3165 if (isDuration(input)) {
3166 duration = {
3167 ms: input._milliseconds,
3168 d: input._days,
3169 M: input._months
3170 };
3171 } else if (isNumber(input)) {
3172 duration = {};
3173
3174 if (key) {
3175 duration[key] = input;
3176 } else {
3177 duration.milliseconds = input;
3178 }
3179 } else if (!!(match = aspNetRegex.exec(input))) {
3180 sign = match[1] === '-' ? -1 : 1;
3181 duration = {
3182 y: 0,
3183 d: toInt(match[DATE]) * sign,
3184 h: toInt(match[HOUR]) * sign,
3185 m: toInt(match[MINUTE]) * sign,
3186 s: toInt(match[SECOND]) * sign,
3187 ms: toInt(absRound(match[MILLISECOND] * 1000)) * sign // the millisecond decimal point is included in the match
3188
3189 };
3190 } else if (!!(match = isoRegex.exec(input))) {
3191 sign = match[1] === '-' ? -1 : 1;
3192 duration = {
3193 y: parseIso(match[2], sign),
3194 M: parseIso(match[3], sign),
3195 w: parseIso(match[4], sign),
3196 d: parseIso(match[5], sign),
3197 h: parseIso(match[6], sign),
3198 m: parseIso(match[7], sign),
3199 s: parseIso(match[8], sign)
3200 };
3201 } else if (duration == null) {
3202 // checks for null or undefined
3203 duration = {};
3204 } else if (typeof duration === 'object' && ('from' in duration || 'to' in duration)) {
3205 diffRes = momentsDifference(createLocal(duration.from), createLocal(duration.to));
3206 duration = {};
3207 duration.ms = diffRes.milliseconds;
3208 duration.M = diffRes.months;
3209 }
3210
3211 ret = new Duration(duration);
3212
3213 if (isDuration(input) && hasOwnProp(input, '_locale')) {
3214 ret._locale = input._locale;
3215 }
3216
3217 return ret;
3218 }
3219
3220 createDuration.fn = Duration.prototype;
3221 createDuration.invalid = createInvalid$1;
3222
3223 function parseIso(inp, sign) {
3224 // We'd normally use ~~inp for this, but unfortunately it also
3225 // converts floats to ints.
3226 // inp may be undefined, so careful calling replace on it.
3227 var res = inp && parseFloat(inp.replace(',', '.')); // apply sign while we're at it
3228
3229 return (isNaN(res) ? 0 : res) * sign;
3230 }
3231
3232 function positiveMomentsDifference(base, other) {
3233 var res = {};
3234 res.months = other.month() - base.month() + (other.year() - base.year()) * 12;
3235
3236 if (base.clone().add(res.months, 'M').isAfter(other)) {
3237 --res.months;
3238 }
3239
3240 res.milliseconds = +other - +base.clone().add(res.months, 'M');
3241 return res;
3242 }
3243
3244 function momentsDifference(base, other) {
3245 var res;
3246
3247 if (!(base.isValid() && other.isValid())) {
3248 return {
3249 milliseconds: 0,
3250 months: 0
3251 };
3252 }
3253
3254 other = cloneWithOffset(other, base);
3255
3256 if (base.isBefore(other)) {
3257 res = positiveMomentsDifference(base, other);
3258 } else {
3259 res = positiveMomentsDifference(other, base);
3260 res.milliseconds = -res.milliseconds;
3261 res.months = -res.months;
3262 }
3263
3264 return res;
3265 } // TODO: remove 'name' arg after deprecation is removed
3266
3267
3268 function createAdder(direction, name) {
3269 return function (val, period) {
3270 var dur, tmp; //invert the arguments, but complain about it
3271
3272 if (period !== null && !isNaN(+period)) {
3273 deprecateSimple(name, 'moment().' + name + '(period, number) is deprecated. Please use moment().' + name + '(number, period). ' + 'See http://momentjs.com/guides/#/warnings/add-inverted-param/ for more info.');
3274 tmp = val;
3275 val = period;
3276 period = tmp;
3277 }
3278
3279 val = typeof val === 'string' ? +val : val;
3280 dur = createDuration(val, period);
3281 addSubtract(this, dur, direction);
3282 return this;
3283 };
3284 }
3285
3286 function addSubtract(mom, duration, isAdding, updateOffset) {
3287 var milliseconds = duration._milliseconds,
3288 days = absRound(duration._days),
3289 months = absRound(duration._months);
3290
3291 if (!mom.isValid()) {
3292 // No op
3293 return;
3294 }
3295
3296 updateOffset = updateOffset == null ? true : updateOffset;
3297
3298 if (months) {
3299 setMonth(mom, get(mom, 'Month') + months * isAdding);
3300 }
3301
3302 if (days) {
3303 set$1(mom, 'Date', get(mom, 'Date') + days * isAdding);
3304 }
3305
3306 if (milliseconds) {
3307 mom._d.setTime(mom._d.valueOf() + milliseconds * isAdding);
3308 }
3309
3310 if (updateOffset) {
3311 hooks.updateOffset(mom, days || months);
3312 }
3313 }
3314
3315 var add = createAdder(1, 'add');
3316 var subtract = createAdder(-1, 'subtract');
3317
3318 function getCalendarFormat(myMoment, now) {
3319 var diff = myMoment.diff(now, 'days', true);
3320 return diff < -6 ? 'sameElse' : diff < -1 ? 'lastWeek' : diff < 0 ? 'lastDay' : diff < 1 ? 'sameDay' : diff < 2 ? 'nextDay' : diff < 7 ? 'nextWeek' : 'sameElse';
3321 }
3322
3323 function calendar$1(time, formats) {
3324 // We want to compare the start of today, vs this.
3325 // Getting start-of-today depends on whether we're local/utc/offset or not.
3326 var now = time || createLocal(),
3327 sod = cloneWithOffset(now, this).startOf('day'),
3328 format = hooks.calendarFormat(this, sod) || 'sameElse';
3329 var output = formats && (isFunction(formats[format]) ? formats[format].call(this, now) : formats[format]);
3330 return this.format(output || this.localeData().calendar(format, this, createLocal(now)));
3331 }
3332
3333 function clone() {
3334 return new Moment(this);
3335 }
3336
3337 function isAfter(input, units) {
3338 var localInput = isMoment(input) ? input : createLocal(input);
3339
3340 if (!(this.isValid() && localInput.isValid())) {
3341 return false;
3342 }
3343
3344 units = normalizeUnits(units) || 'millisecond';
3345
3346 if (units === 'millisecond') {
3347 return this.valueOf() > localInput.valueOf();
3348 } else {
3349 return localInput.valueOf() < this.clone().startOf(units).valueOf();
3350 }
3351 }
3352
3353 function isBefore(input, units) {
3354 var localInput = isMoment(input) ? input : createLocal(input);
3355
3356 if (!(this.isValid() && localInput.isValid())) {
3357 return false;
3358 }
3359
3360 units = normalizeUnits(units) || 'millisecond';
3361
3362 if (units === 'millisecond') {
3363 return this.valueOf() < localInput.valueOf();
3364 } else {
3365 return this.clone().endOf(units).valueOf() < localInput.valueOf();
3366 }
3367 }
3368
3369 function isBetween(from, to, units, inclusivity) {
3370 var localFrom = isMoment(from) ? from : createLocal(from),
3371 localTo = isMoment(to) ? to : createLocal(to);
3372
3373 if (!(this.isValid() && localFrom.isValid() && localTo.isValid())) {
3374 return false;
3375 }
3376
3377 inclusivity = inclusivity || '()';
3378 return (inclusivity[0] === '(' ? this.isAfter(localFrom, units) : !this.isBefore(localFrom, units)) && (inclusivity[1] === ')' ? this.isBefore(localTo, units) : !this.isAfter(localTo, units));
3379 }
3380
3381 function isSame(input, units) {
3382 var localInput = isMoment(input) ? input : createLocal(input),
3383 inputMs;
3384
3385 if (!(this.isValid() && localInput.isValid())) {
3386 return false;
3387 }
3388
3389 units = normalizeUnits(units) || 'millisecond';
3390
3391 if (units === 'millisecond') {
3392 return this.valueOf() === localInput.valueOf();
3393 } else {
3394 inputMs = localInput.valueOf();
3395 return this.clone().startOf(units).valueOf() <= inputMs && inputMs <= this.clone().endOf(units).valueOf();
3396 }
3397 }
3398
3399 function isSameOrAfter(input, units) {
3400 return this.isSame(input, units) || this.isAfter(input, units);
3401 }
3402
3403 function isSameOrBefore(input, units) {
3404 return this.isSame(input, units) || this.isBefore(input, units);
3405 }
3406
3407 function diff(input, units, asFloat) {
3408 var that, zoneDelta, output;
3409
3410 if (!this.isValid()) {
3411 return NaN;
3412 }
3413
3414 that = cloneWithOffset(input, this);
3415
3416 if (!that.isValid()) {
3417 return NaN;
3418 }
3419
3420 zoneDelta = (that.utcOffset() - this.utcOffset()) * 6e4;
3421 units = normalizeUnits(units);
3422
3423 switch (units) {
3424 case 'year':
3425 output = monthDiff(this, that) / 12;
3426 break;
3427
3428 case 'month':
3429 output = monthDiff(this, that);
3430 break;
3431
3432 case 'quarter':
3433 output = monthDiff(this, that) / 3;
3434 break;
3435
3436 case 'second':
3437 output = (this - that) / 1e3;
3438 break;
3439 // 1000
3440
3441 case 'minute':
3442 output = (this - that) / 6e4;
3443 break;
3444 // 1000 * 60
3445
3446 case 'hour':
3447 output = (this - that) / 36e5;
3448 break;
3449 // 1000 * 60 * 60
3450
3451 case 'day':
3452 output = (this - that - zoneDelta) / 864e5;
3453 break;
3454 // 1000 * 60 * 60 * 24, negate dst
3455
3456 case 'week':
3457 output = (this - that - zoneDelta) / 6048e5;
3458 break;
3459 // 1000 * 60 * 60 * 24 * 7, negate dst
3460
3461 default:
3462 output = this - that;
3463 }
3464
3465 return asFloat ? output : absFloor(output);
3466 }
3467
3468 function monthDiff(a, b) {
3469 // difference in months
3470 var wholeMonthDiff = (b.year() - a.year()) * 12 + (b.month() - a.month()),
3471 // b is in (anchor - 1 month, anchor + 1 month)
3472 anchor = a.clone().add(wholeMonthDiff, 'months'),
3473 anchor2,
3474 adjust;
3475
3476 if (b - anchor < 0) {
3477 anchor2 = a.clone().add(wholeMonthDiff - 1, 'months'); // linear across the month
3478
3479 adjust = (b - anchor) / (anchor - anchor2);
3480 } else {
3481 anchor2 = a.clone().add(wholeMonthDiff + 1, 'months'); // linear across the month
3482
3483 adjust = (b - anchor) / (anchor2 - anchor);
3484 } //check for negative zero, return zero if negative zero
3485
3486
3487 return -(wholeMonthDiff + adjust) || 0;
3488 }
3489
3490 hooks.defaultFormat = 'YYYY-MM-DDTHH:mm:ssZ';
3491 hooks.defaultFormatUtc = 'YYYY-MM-DDTHH:mm:ss[Z]';
3492
3493 function toString() {
3494 return this.clone().locale('en').format('ddd MMM DD YYYY HH:mm:ss [GMT]ZZ');
3495 }
3496
3497 function toISOString(keepOffset) {
3498 if (!this.isValid()) {
3499 return null;
3500 }
3501
3502 var utc = keepOffset !== true;
3503 var m = utc ? this.clone().utc() : this;
3504
3505 if (m.year() < 0 || m.year() > 9999) {
3506 return formatMoment(m, utc ? 'YYYYYY-MM-DD[T]HH:mm:ss.SSS[Z]' : 'YYYYYY-MM-DD[T]HH:mm:ss.SSSZ');
3507 }
3508
3509 if (isFunction(Date.prototype.toISOString)) {
3510 // native implementation is ~50x faster, use it when we can
3511 if (utc) {
3512 return this.toDate().toISOString();
3513 } else {
3514 return new Date(this.valueOf() + this.utcOffset() * 60 * 1000).toISOString().replace('Z', formatMoment(m, 'Z'));
3515 }
3516 }
3517
3518 return formatMoment(m, utc ? 'YYYY-MM-DD[T]HH:mm:ss.SSS[Z]' : 'YYYY-MM-DD[T]HH:mm:ss.SSSZ');
3519 }
3520 /**
3521 * Return a human readable representation of a moment that can
3522 * also be evaluated to get a new moment which is the same
3523 *
3524 * @link https://nodejs.org/dist/latest/docs/api/util.html#util_custom_inspect_function_on_objects
3525 */
3526
3527
3528 function inspect() {
3529 if (!this.isValid()) {
3530 return 'moment.invalid(/* ' + this._i + ' */)';
3531 }
3532
3533 var func = 'moment';
3534 var zone = '';
3535
3536 if (!this.isLocal()) {
3537 func = this.utcOffset() === 0 ? 'moment.utc' : 'moment.parseZone';
3538 zone = 'Z';
3539 }
3540
3541 var prefix = '[' + func + '("]';
3542 var year = 0 <= this.year() && this.year() <= 9999 ? 'YYYY' : 'YYYYYY';
3543 var datetime = '-MM-DD[T]HH:mm:ss.SSS';
3544 var suffix = zone + '[")]';
3545 return this.format(prefix + year + datetime + suffix);
3546 }
3547
3548 function format(inputString) {
3549 if (!inputString) {
3550 inputString = this.isUtc() ? hooks.defaultFormatUtc : hooks.defaultFormat;
3551 }
3552
3553 var output = formatMoment(this, inputString);
3554 return this.localeData().postformat(output);
3555 }
3556
3557 function from(time, withoutSuffix) {
3558 if (this.isValid() && (isMoment(time) && time.isValid() || createLocal(time).isValid())) {
3559 return createDuration({
3560 to: this,
3561 from: time
3562 }).locale(this.locale()).humanize(!withoutSuffix);
3563 } else {
3564 return this.localeData().invalidDate();
3565 }
3566 }
3567
3568 function fromNow(withoutSuffix) {
3569 return this.from(createLocal(), withoutSuffix);
3570 }
3571
3572 function to(time, withoutSuffix) {
3573 if (this.isValid() && (isMoment(time) && time.isValid() || createLocal(time).isValid())) {
3574 return createDuration({
3575 from: this,
3576 to: time
3577 }).locale(this.locale()).humanize(!withoutSuffix);
3578 } else {
3579 return this.localeData().invalidDate();
3580 }
3581 }
3582
3583 function toNow(withoutSuffix) {
3584 return this.to(createLocal(), withoutSuffix);
3585 } // If passed a locale key, it will set the locale for this
3586 // instance. Otherwise, it will return the locale configuration
3587 // variables for this instance.
3588
3589
3590 function locale(key) {
3591 var newLocaleData;
3592
3593 if (key === undefined) {
3594 return this._locale._abbr;
3595 } else {
3596 newLocaleData = getLocale(key);
3597
3598 if (newLocaleData != null) {
3599 this._locale = newLocaleData;
3600 }
3601
3602 return this;
3603 }
3604 }
3605
3606 var lang = deprecate('moment().lang() is deprecated. Instead, use moment().localeData() to get the language configuration. Use moment().locale() to change languages.', function (key) {
3607 if (key === undefined) {
3608 return this.localeData();
3609 } else {
3610 return this.locale(key);
3611 }
3612 });
3613
3614 function localeData() {
3615 return this._locale;
3616 }
3617
3618 var MS_PER_SECOND = 1000;
3619 var MS_PER_MINUTE = 60 * MS_PER_SECOND;
3620 var MS_PER_HOUR = 60 * MS_PER_MINUTE;
3621 var MS_PER_400_YEARS = (365 * 400 + 97) * 24 * MS_PER_HOUR; // actual modulo - handles negative numbers (for dates before 1970):
3622
3623 function mod$1(dividend, divisor) {
3624 return (dividend % divisor + divisor) % divisor;
3625 }
3626
3627 function localStartOfDate(y, m, d) {
3628 // the date constructor remaps years 0-99 to 1900-1999
3629 if (y < 100 && y >= 0) {
3630 // preserve leap years using a full 400 year cycle, then reset
3631 return new Date(y + 400, m, d) - MS_PER_400_YEARS;
3632 } else {
3633 return new Date(y, m, d).valueOf();
3634 }
3635 }
3636
3637 function utcStartOfDate(y, m, d) {
3638 // Date.UTC remaps years 0-99 to 1900-1999
3639 if (y < 100 && y >= 0) {
3640 // preserve leap years using a full 400 year cycle, then reset
3641 return Date.UTC(y + 400, m, d) - MS_PER_400_YEARS;
3642 } else {
3643 return Date.UTC(y, m, d);
3644 }
3645 }
3646
3647 function startOf(units) {
3648 var time;
3649 units = normalizeUnits(units);
3650
3651 if (units === undefined || units === 'millisecond' || !this.isValid()) {
3652 return this;
3653 }
3654
3655 var startOfDate = this._isUTC ? utcStartOfDate : localStartOfDate;
3656
3657 switch (units) {
3658 case 'year':
3659 time = startOfDate(this.year(), 0, 1);
3660 break;
3661
3662 case 'quarter':
3663 time = startOfDate(this.year(), this.month() - this.month() % 3, 1);
3664 break;
3665
3666 case 'month':
3667 time = startOfDate(this.year(), this.month(), 1);
3668 break;
3669
3670 case 'week':
3671 time = startOfDate(this.year(), this.month(), this.date() - this.weekday());
3672 break;
3673
3674 case 'isoWeek':
3675 time = startOfDate(this.year(), this.month(), this.date() - (this.isoWeekday() - 1));
3676 break;
3677
3678 case 'day':
3679 case 'date':
3680 time = startOfDate(this.year(), this.month(), this.date());
3681 break;
3682
3683 case 'hour':
3684 time = this._d.valueOf();
3685 time -= mod$1(time + (this._isUTC ? 0 : this.utcOffset() * MS_PER_MINUTE), MS_PER_HOUR);
3686 break;
3687
3688 case 'minute':
3689 time = this._d.valueOf();
3690 time -= mod$1(time, MS_PER_MINUTE);
3691 break;
3692
3693 case 'second':
3694 time = this._d.valueOf();
3695 time -= mod$1(time, MS_PER_SECOND);
3696 break;
3697 }
3698
3699 this._d.setTime(time);
3700
3701 hooks.updateOffset(this, true);
3702 return this;
3703 }
3704
3705 function endOf(units) {
3706 var time;
3707 units = normalizeUnits(units);
3708
3709 if (units === undefined || units === 'millisecond' || !this.isValid()) {
3710 return this;
3711 }
3712
3713 var startOfDate = this._isUTC ? utcStartOfDate : localStartOfDate;
3714
3715 switch (units) {
3716 case 'year':
3717 time = startOfDate(this.year() + 1, 0, 1) - 1;
3718 break;
3719
3720 case 'quarter':
3721 time = startOfDate(this.year(), this.month() - this.month() % 3 + 3, 1) - 1;
3722 break;
3723
3724 case 'month':
3725 time = startOfDate(this.year(), this.month() + 1, 1) - 1;
3726 break;
3727
3728 case 'week':
3729 time = startOfDate(this.year(), this.month(), this.date() - this.weekday() + 7) - 1;
3730 break;
3731
3732 case 'isoWeek':
3733 time = startOfDate(this.year(), this.month(), this.date() - (this.isoWeekday() - 1) + 7) - 1;
3734 break;
3735
3736 case 'day':
3737 case 'date':
3738 time = startOfDate(this.year(), this.month(), this.date() + 1) - 1;
3739 break;
3740
3741 case 'hour':
3742 time = this._d.valueOf();
3743 time += MS_PER_HOUR - mod$1(time + (this._isUTC ? 0 : this.utcOffset() * MS_PER_MINUTE), MS_PER_HOUR) - 1;
3744 break;
3745
3746 case 'minute':
3747 time = this._d.valueOf();
3748 time += MS_PER_MINUTE - mod$1(time, MS_PER_MINUTE) - 1;
3749 break;
3750
3751 case 'second':
3752 time = this._d.valueOf();
3753 time += MS_PER_SECOND - mod$1(time, MS_PER_SECOND) - 1;
3754 break;
3755 }
3756
3757 this._d.setTime(time);
3758
3759 hooks.updateOffset(this, true);
3760 return this;
3761 }
3762
3763 function valueOf() {
3764 return this._d.valueOf() - (this._offset || 0) * 60000;
3765 }
3766
3767 function unix() {
3768 return Math.floor(this.valueOf() / 1000);
3769 }
3770
3771 function toDate() {
3772 return new Date(this.valueOf());
3773 }
3774
3775 function toArray() {
3776 var m = this;
3777 return [m.year(), m.month(), m.date(), m.hour(), m.minute(), m.second(), m.millisecond()];
3778 }
3779
3780 function toObject() {
3781 var m = this;
3782 return {
3783 years: m.year(),
3784 months: m.month(),
3785 date: m.date(),
3786 hours: m.hours(),
3787 minutes: m.minutes(),
3788 seconds: m.seconds(),
3789 milliseconds: m.milliseconds()
3790 };
3791 }
3792
3793 function toJSON() {
3794 // new Date(NaN).toJSON() === null
3795 return this.isValid() ? this.toISOString() : null;
3796 }
3797
3798 function isValid$2() {
3799 return isValid(this);
3800 }
3801
3802 function parsingFlags() {
3803 return extend({}, getParsingFlags(this));
3804 }
3805
3806 function invalidAt() {
3807 return getParsingFlags(this).overflow;
3808 }
3809
3810 function creationData() {
3811 return {
3812 input: this._i,
3813 format: this._f,
3814 locale: this._locale,
3815 isUTC: this._isUTC,
3816 strict: this._strict
3817 };
3818 } // FORMATTING
3819
3820
3821 addFormatToken(0, ['gg', 2], 0, function () {
3822 return this.weekYear() % 100;
3823 });
3824 addFormatToken(0, ['GG', 2], 0, function () {
3825 return this.isoWeekYear() % 100;
3826 });
3827
3828 function addWeekYearFormatToken(token, getter) {
3829 addFormatToken(0, [token, token.length], 0, getter);
3830 }
3831
3832 addWeekYearFormatToken('gggg', 'weekYear');
3833 addWeekYearFormatToken('ggggg', 'weekYear');
3834 addWeekYearFormatToken('GGGG', 'isoWeekYear');
3835 addWeekYearFormatToken('GGGGG', 'isoWeekYear'); // ALIASES
3836
3837 addUnitAlias('weekYear', 'gg');
3838 addUnitAlias('isoWeekYear', 'GG'); // PRIORITY
3839
3840 addUnitPriority('weekYear', 1);
3841 addUnitPriority('isoWeekYear', 1); // PARSING
3842
3843 addRegexToken('G', matchSigned);
3844 addRegexToken('g', matchSigned);
3845 addRegexToken('GG', match1to2, match2);
3846 addRegexToken('gg', match1to2, match2);
3847 addRegexToken('GGGG', match1to4, match4);
3848 addRegexToken('gggg', match1to4, match4);
3849 addRegexToken('GGGGG', match1to6, match6);
3850 addRegexToken('ggggg', match1to6, match6);
3851 addWeekParseToken(['gggg', 'ggggg', 'GGGG', 'GGGGG'], function (input, week, config, token) {
3852 week[token.substr(0, 2)] = toInt(input);
3853 });
3854 addWeekParseToken(['gg', 'GG'], function (input, week, config, token) {
3855 week[token] = hooks.parseTwoDigitYear(input);
3856 }); // MOMENTS
3857
3858 function getSetWeekYear(input) {
3859 return getSetWeekYearHelper.call(this, input, this.week(), this.weekday(), this.localeData()._week.dow, this.localeData()._week.doy);
3860 }
3861
3862 function getSetISOWeekYear(input) {
3863 return getSetWeekYearHelper.call(this, input, this.isoWeek(), this.isoWeekday(), 1, 4);
3864 }
3865
3866 function getISOWeeksInYear() {
3867 return weeksInYear(this.year(), 1, 4);
3868 }
3869
3870 function getWeeksInYear() {
3871 var weekInfo = this.localeData()._week;
3872
3873 return weeksInYear(this.year(), weekInfo.dow, weekInfo.doy);
3874 }
3875
3876 function getSetWeekYearHelper(input, week, weekday, dow, doy) {
3877 var weeksTarget;
3878
3879 if (input == null) {
3880 return weekOfYear(this, dow, doy).year;
3881 } else {
3882 weeksTarget = weeksInYear(input, dow, doy);
3883
3884 if (week > weeksTarget) {
3885 week = weeksTarget;
3886 }
3887
3888 return setWeekAll.call(this, input, week, weekday, dow, doy);
3889 }
3890 }
3891
3892 function setWeekAll(weekYear, week, weekday, dow, doy) {
3893 var dayOfYearData = dayOfYearFromWeeks(weekYear, week, weekday, dow, doy),
3894 date = createUTCDate(dayOfYearData.year, 0, dayOfYearData.dayOfYear);
3895 this.year(date.getUTCFullYear());
3896 this.month(date.getUTCMonth());
3897 this.date(date.getUTCDate());
3898 return this;
3899 } // FORMATTING
3900
3901
3902 addFormatToken('Q', 0, 'Qo', 'quarter'); // ALIASES
3903
3904 addUnitAlias('quarter', 'Q'); // PRIORITY
3905
3906 addUnitPriority('quarter', 7); // PARSING
3907
3908 addRegexToken('Q', match1);
3909 addParseToken('Q', function (input, array) {
3910 array[MONTH] = (toInt(input) - 1) * 3;
3911 }); // MOMENTS
3912
3913 function getSetQuarter(input) {
3914 return input == null ? Math.ceil((this.month() + 1) / 3) : this.month((input - 1) * 3 + this.month() % 3);
3915 } // FORMATTING
3916
3917
3918 addFormatToken('D', ['DD', 2], 'Do', 'date'); // ALIASES
3919
3920 addUnitAlias('date', 'D'); // PRIORITY
3921
3922 addUnitPriority('date', 9); // PARSING
3923
3924 addRegexToken('D', match1to2);
3925 addRegexToken('DD', match1to2, match2);
3926 addRegexToken('Do', function (isStrict, locale) {
3927 // TODO: Remove "ordinalParse" fallback in next major release.
3928 return isStrict ? locale._dayOfMonthOrdinalParse || locale._ordinalParse : locale._dayOfMonthOrdinalParseLenient;
3929 });
3930 addParseToken(['D', 'DD'], DATE);
3931 addParseToken('Do', function (input, array) {
3932 array[DATE] = toInt(input.match(match1to2)[0]);
3933 }); // MOMENTS
3934
3935 var getSetDayOfMonth = makeGetSet('Date', true); // FORMATTING
3936
3937 addFormatToken('DDD', ['DDDD', 3], 'DDDo', 'dayOfYear'); // ALIASES
3938
3939 addUnitAlias('dayOfYear', 'DDD'); // PRIORITY
3940
3941 addUnitPriority('dayOfYear', 4); // PARSING
3942
3943 addRegexToken('DDD', match1to3);
3944 addRegexToken('DDDD', match3);
3945 addParseToken(['DDD', 'DDDD'], function (input, array, config) {
3946 config._dayOfYear = toInt(input);
3947 }); // HELPERS
3948 // MOMENTS
3949
3950 function getSetDayOfYear(input) {
3951 var dayOfYear = Math.round((this.clone().startOf('day') - this.clone().startOf('year')) / 864e5) + 1;
3952 return input == null ? dayOfYear : this.add(input - dayOfYear, 'd');
3953 } // FORMATTING
3954
3955
3956 addFormatToken('m', ['mm', 2], 0, 'minute'); // ALIASES
3957
3958 addUnitAlias('minute', 'm'); // PRIORITY
3959
3960 addUnitPriority('minute', 14); // PARSING
3961
3962 addRegexToken('m', match1to2);
3963 addRegexToken('mm', match1to2, match2);
3964 addParseToken(['m', 'mm'], MINUTE); // MOMENTS
3965
3966 var getSetMinute = makeGetSet('Minutes', false); // FORMATTING
3967
3968 addFormatToken('s', ['ss', 2], 0, 'second'); // ALIASES
3969
3970 addUnitAlias('second', 's'); // PRIORITY
3971
3972 addUnitPriority('second', 15); // PARSING
3973
3974 addRegexToken('s', match1to2);
3975 addRegexToken('ss', match1to2, match2);
3976 addParseToken(['s', 'ss'], SECOND); // MOMENTS
3977
3978 var getSetSecond = makeGetSet('Seconds', false); // FORMATTING
3979
3980 addFormatToken('S', 0, 0, function () {
3981 return ~~(this.millisecond() / 100);
3982 });
3983 addFormatToken(0, ['SS', 2], 0, function () {
3984 return ~~(this.millisecond() / 10);
3985 });
3986 addFormatToken(0, ['SSS', 3], 0, 'millisecond');
3987 addFormatToken(0, ['SSSS', 4], 0, function () {
3988 return this.millisecond() * 10;
3989 });
3990 addFormatToken(0, ['SSSSS', 5], 0, function () {
3991 return this.millisecond() * 100;
3992 });
3993 addFormatToken(0, ['SSSSSS', 6], 0, function () {
3994 return this.millisecond() * 1000;
3995 });
3996 addFormatToken(0, ['SSSSSSS', 7], 0, function () {
3997 return this.millisecond() * 10000;
3998 });
3999 addFormatToken(0, ['SSSSSSSS', 8], 0, function () {
4000 return this.millisecond() * 100000;
4001 });
4002 addFormatToken(0, ['SSSSSSSSS', 9], 0, function () {
4003 return this.millisecond() * 1000000;
4004 }); // ALIASES
4005
4006 addUnitAlias('millisecond', 'ms'); // PRIORITY
4007
4008 addUnitPriority('millisecond', 16); // PARSING
4009
4010 addRegexToken('S', match1to3, match1);
4011 addRegexToken('SS', match1to3, match2);
4012 addRegexToken('SSS', match1to3, match3);
4013 var token;
4014
4015 for (token = 'SSSS'; token.length <= 9; token += 'S') {
4016 addRegexToken(token, matchUnsigned);
4017 }
4018
4019 function parseMs(input, array) {
4020 array[MILLISECOND] = toInt(('0.' + input) * 1000);
4021 }
4022
4023 for (token = 'S'; token.length <= 9; token += 'S') {
4024 addParseToken(token, parseMs);
4025 } // MOMENTS
4026
4027
4028 var getSetMillisecond = makeGetSet('Milliseconds', false); // FORMATTING
4029
4030 addFormatToken('z', 0, 0, 'zoneAbbr');
4031 addFormatToken('zz', 0, 0, 'zoneName'); // MOMENTS
4032
4033 function getZoneAbbr() {
4034 return this._isUTC ? 'UTC' : '';
4035 }
4036
4037 function getZoneName() {
4038 return this._isUTC ? 'Coordinated Universal Time' : '';
4039 }
4040
4041 var proto = Moment.prototype;
4042 proto.add = add;
4043 proto.calendar = calendar$1;
4044 proto.clone = clone;
4045 proto.diff = diff;
4046 proto.endOf = endOf;
4047 proto.format = format;
4048 proto.from = from;
4049 proto.fromNow = fromNow;
4050 proto.to = to;
4051 proto.toNow = toNow;
4052 proto.get = stringGet;
4053 proto.invalidAt = invalidAt;
4054 proto.isAfter = isAfter;
4055 proto.isBefore = isBefore;
4056 proto.isBetween = isBetween;
4057 proto.isSame = isSame;
4058 proto.isSameOrAfter = isSameOrAfter;
4059 proto.isSameOrBefore = isSameOrBefore;
4060 proto.isValid = isValid$2;
4061 proto.lang = lang;
4062 proto.locale = locale;
4063 proto.localeData = localeData;
4064 proto.max = prototypeMax;
4065 proto.min = prototypeMin;
4066 proto.parsingFlags = parsingFlags;
4067 proto.set = stringSet;
4068 proto.startOf = startOf;
4069 proto.subtract = subtract;
4070 proto.toArray = toArray;
4071 proto.toObject = toObject;
4072 proto.toDate = toDate;
4073 proto.toISOString = toISOString;
4074 proto.inspect = inspect;
4075 proto.toJSON = toJSON;
4076 proto.toString = toString;
4077 proto.unix = unix;
4078 proto.valueOf = valueOf;
4079 proto.creationData = creationData;
4080 proto.year = getSetYear;
4081 proto.isLeapYear = getIsLeapYear;
4082 proto.weekYear = getSetWeekYear;
4083 proto.isoWeekYear = getSetISOWeekYear;
4084 proto.quarter = proto.quarters = getSetQuarter;
4085 proto.month = getSetMonth;
4086 proto.daysInMonth = getDaysInMonth;
4087 proto.week = proto.weeks = getSetWeek;
4088 proto.isoWeek = proto.isoWeeks = getSetISOWeek;
4089 proto.weeksInYear = getWeeksInYear;
4090 proto.isoWeeksInYear = getISOWeeksInYear;
4091 proto.date = getSetDayOfMonth;
4092 proto.day = proto.days = getSetDayOfWeek;
4093 proto.weekday = getSetLocaleDayOfWeek;
4094 proto.isoWeekday = getSetISODayOfWeek;
4095 proto.dayOfYear = getSetDayOfYear;
4096 proto.hour = proto.hours = getSetHour;
4097 proto.minute = proto.minutes = getSetMinute;
4098 proto.second = proto.seconds = getSetSecond;
4099 proto.millisecond = proto.milliseconds = getSetMillisecond;
4100 proto.utcOffset = getSetOffset;
4101 proto.utc = setOffsetToUTC;
4102 proto.local = setOffsetToLocal;
4103 proto.parseZone = setOffsetToParsedOffset;
4104 proto.hasAlignedHourOffset = hasAlignedHourOffset;
4105 proto.isDST = isDaylightSavingTime;
4106 proto.isLocal = isLocal;
4107 proto.isUtcOffset = isUtcOffset;
4108 proto.isUtc = isUtc;
4109 proto.isUTC = isUtc;
4110 proto.zoneAbbr = getZoneAbbr;
4111 proto.zoneName = getZoneName;
4112 proto.dates = deprecate('dates accessor is deprecated. Use date instead.', getSetDayOfMonth);
4113 proto.months = deprecate('months accessor is deprecated. Use month instead', getSetMonth);
4114 proto.years = deprecate('years accessor is deprecated. Use year instead', getSetYear);
4115 proto.zone = deprecate('moment().zone is deprecated, use moment().utcOffset instead. http://momentjs.com/guides/#/warnings/zone/', getSetZone);
4116 proto.isDSTShifted = deprecate('isDSTShifted is deprecated. See http://momentjs.com/guides/#/warnings/dst-shifted/ for more information', isDaylightSavingTimeShifted);
4117
4118 function createUnix(input) {
4119 return createLocal(input * 1000);
4120 }
4121
4122 function createInZone() {
4123 return createLocal.apply(null, arguments).parseZone();
4124 }
4125
4126 function preParsePostFormat(string) {
4127 return string;
4128 }
4129
4130 var proto$1 = Locale.prototype;
4131 proto$1.calendar = calendar;
4132 proto$1.longDateFormat = longDateFormat;
4133 proto$1.invalidDate = invalidDate;
4134 proto$1.ordinal = ordinal;
4135 proto$1.preparse = preParsePostFormat;
4136 proto$1.postformat = preParsePostFormat;
4137 proto$1.relativeTime = relativeTime;
4138 proto$1.pastFuture = pastFuture;
4139 proto$1.set = set;
4140 proto$1.months = localeMonths;
4141 proto$1.monthsShort = localeMonthsShort;
4142 proto$1.monthsParse = localeMonthsParse;
4143 proto$1.monthsRegex = monthsRegex;
4144 proto$1.monthsShortRegex = monthsShortRegex;
4145 proto$1.week = localeWeek;
4146 proto$1.firstDayOfYear = localeFirstDayOfYear;
4147 proto$1.firstDayOfWeek = localeFirstDayOfWeek;
4148 proto$1.weekdays = localeWeekdays;
4149 proto$1.weekdaysMin = localeWeekdaysMin;
4150 proto$1.weekdaysShort = localeWeekdaysShort;
4151 proto$1.weekdaysParse = localeWeekdaysParse;
4152 proto$1.weekdaysRegex = weekdaysRegex;
4153 proto$1.weekdaysShortRegex = weekdaysShortRegex;
4154 proto$1.weekdaysMinRegex = weekdaysMinRegex;
4155 proto$1.isPM = localeIsPM;
4156 proto$1.meridiem = localeMeridiem;
4157
4158 function get$1(format, index, field, setter) {
4159 var locale = getLocale();
4160 var utc = createUTC().set(setter, index);
4161 return locale[field](utc, format);
4162 }
4163
4164 function listMonthsImpl(format, index, field) {
4165 if (isNumber(format)) {
4166 index = format;
4167 format = undefined;
4168 }
4169
4170 format = format || '';
4171
4172 if (index != null) {
4173 return get$1(format, index, field, 'month');
4174 }
4175
4176 var i;
4177 var out = [];
4178
4179 for (i = 0; i < 12; i++) {
4180 out[i] = get$1(format, i, field, 'month');
4181 }
4182
4183 return out;
4184 } // ()
4185 // (5)
4186 // (fmt, 5)
4187 // (fmt)
4188 // (true)
4189 // (true, 5)
4190 // (true, fmt, 5)
4191 // (true, fmt)
4192
4193
4194 function listWeekdaysImpl(localeSorted, format, index, field) {
4195 if (typeof localeSorted === 'boolean') {
4196 if (isNumber(format)) {
4197 index = format;
4198 format = undefined;
4199 }
4200
4201 format = format || '';
4202 } else {
4203 format = localeSorted;
4204 index = format;
4205 localeSorted = false;
4206
4207 if (isNumber(format)) {
4208 index = format;
4209 format = undefined;
4210 }
4211
4212 format = format || '';
4213 }
4214
4215 var locale = getLocale(),
4216 shift = localeSorted ? locale._week.dow : 0;
4217
4218 if (index != null) {
4219 return get$1(format, (index + shift) % 7, field, 'day');
4220 }
4221
4222 var i;
4223 var out = [];
4224
4225 for (i = 0; i < 7; i++) {
4226 out[i] = get$1(format, (i + shift) % 7, field, 'day');
4227 }
4228
4229 return out;
4230 }
4231
4232 function listMonths(format, index) {
4233 return listMonthsImpl(format, index, 'months');
4234 }
4235
4236 function listMonthsShort(format, index) {
4237 return listMonthsImpl(format, index, 'monthsShort');
4238 }
4239
4240 function listWeekdays(localeSorted, format, index) {
4241 return listWeekdaysImpl(localeSorted, format, index, 'weekdays');
4242 }
4243
4244 function listWeekdaysShort(localeSorted, format, index) {
4245 return listWeekdaysImpl(localeSorted, format, index, 'weekdaysShort');
4246 }
4247
4248 function listWeekdaysMin(localeSorted, format, index) {
4249 return listWeekdaysImpl(localeSorted, format, index, 'weekdaysMin');
4250 }
4251
4252 getSetGlobalLocale('en', {
4253 dayOfMonthOrdinalParse: /\d{1,2}(th|st|nd|rd)/,
4254 ordinal: function (number) {
4255 var b = number % 10,
4256 output = toInt(number % 100 / 10) === 1 ? 'th' : b === 1 ? 'st' : b === 2 ? 'nd' : b === 3 ? 'rd' : 'th';
4257 return number + output;
4258 }
4259 }); // Side effect imports
4260
4261 hooks.lang = deprecate('moment.lang is deprecated. Use moment.locale instead.', getSetGlobalLocale);
4262 hooks.langData = deprecate('moment.langData is deprecated. Use moment.localeData instead.', getLocale);
4263 var mathAbs = Math.abs;
4264
4265 function abs() {
4266 var data = this._data;
4267 this._milliseconds = mathAbs(this._milliseconds);
4268 this._days = mathAbs(this._days);
4269 this._months = mathAbs(this._months);
4270 data.milliseconds = mathAbs(data.milliseconds);
4271 data.seconds = mathAbs(data.seconds);
4272 data.minutes = mathAbs(data.minutes);
4273 data.hours = mathAbs(data.hours);
4274 data.months = mathAbs(data.months);
4275 data.years = mathAbs(data.years);
4276 return this;
4277 }
4278
4279 function addSubtract$1(duration, input, value, direction) {
4280 var other = createDuration(input, value);
4281 duration._milliseconds += direction * other._milliseconds;
4282 duration._days += direction * other._days;
4283 duration._months += direction * other._months;
4284 return duration._bubble();
4285 } // supports only 2.0-style add(1, 's') or add(duration)
4286
4287
4288 function add$1(input, value) {
4289 return addSubtract$1(this, input, value, 1);
4290 } // supports only 2.0-style subtract(1, 's') or subtract(duration)
4291
4292
4293 function subtract$1(input, value) {
4294 return addSubtract$1(this, input, value, -1);
4295 }
4296
4297 function absCeil(number) {
4298 if (number < 0) {
4299 return Math.floor(number);
4300 } else {
4301 return Math.ceil(number);
4302 }
4303 }
4304
4305 function bubble() {
4306 var milliseconds = this._milliseconds;
4307 var days = this._days;
4308 var months = this._months;
4309 var data = this._data;
4310 var seconds, minutes, hours, years, monthsFromDays; // if we have a mix of positive and negative values, bubble down first
4311 // check: https://github.com/moment/moment/issues/2166
4312
4313 if (!(milliseconds >= 0 && days >= 0 && months >= 0 || milliseconds <= 0 && days <= 0 && months <= 0)) {
4314 milliseconds += absCeil(monthsToDays(months) + days) * 864e5;
4315 days = 0;
4316 months = 0;
4317 } // The following code bubbles up values, see the tests for
4318 // examples of what that means.
4319
4320
4321 data.milliseconds = milliseconds % 1000;
4322 seconds = absFloor(milliseconds / 1000);
4323 data.seconds = seconds % 60;
4324 minutes = absFloor(seconds / 60);
4325 data.minutes = minutes % 60;
4326 hours = absFloor(minutes / 60);
4327 data.hours = hours % 24;
4328 days += absFloor(hours / 24); // convert days to months
4329
4330 monthsFromDays = absFloor(daysToMonths(days));
4331 months += monthsFromDays;
4332 days -= absCeil(monthsToDays(monthsFromDays)); // 12 months -> 1 year
4333
4334 years = absFloor(months / 12);
4335 months %= 12;
4336 data.days = days;
4337 data.months = months;
4338 data.years = years;
4339 return this;
4340 }
4341
4342 function daysToMonths(days) {
4343 // 400 years have 146097 days (taking into account leap year rules)
4344 // 400 years have 12 months === 4800
4345 return days * 4800 / 146097;
4346 }
4347
4348 function monthsToDays(months) {
4349 // the reverse of daysToMonths
4350 return months * 146097 / 4800;
4351 }
4352
4353 function as(units) {
4354 if (!this.isValid()) {
4355 return NaN;
4356 }
4357
4358 var days;
4359 var months;
4360 var milliseconds = this._milliseconds;
4361 units = normalizeUnits(units);
4362
4363 if (units === 'month' || units === 'quarter' || units === 'year') {
4364 days = this._days + milliseconds / 864e5;
4365 months = this._months + daysToMonths(days);
4366
4367 switch (units) {
4368 case 'month':
4369 return months;
4370
4371 case 'quarter':
4372 return months / 3;
4373
4374 case 'year':
4375 return months / 12;
4376 }
4377 } else {
4378 // handle milliseconds separately because of floating point math errors (issue #1867)
4379 days = this._days + Math.round(monthsToDays(this._months));
4380
4381 switch (units) {
4382 case 'week':
4383 return days / 7 + milliseconds / 6048e5;
4384
4385 case 'day':
4386 return days + milliseconds / 864e5;
4387
4388 case 'hour':
4389 return days * 24 + milliseconds / 36e5;
4390
4391 case 'minute':
4392 return days * 1440 + milliseconds / 6e4;
4393
4394 case 'second':
4395 return days * 86400 + milliseconds / 1000;
4396 // Math.floor prevents floating point math errors here
4397
4398 case 'millisecond':
4399 return Math.floor(days * 864e5) + milliseconds;
4400
4401 default:
4402 throw new Error('Unknown unit ' + units);
4403 }
4404 }
4405 } // TODO: Use this.as('ms')?
4406
4407
4408 function valueOf$1() {
4409 if (!this.isValid()) {
4410 return NaN;
4411 }
4412
4413 return this._milliseconds + this._days * 864e5 + this._months % 12 * 2592e6 + toInt(this._months / 12) * 31536e6;
4414 }
4415
4416 function makeAs(alias) {
4417 return function () {
4418 return this.as(alias);
4419 };
4420 }
4421
4422 var asMilliseconds = makeAs('ms');
4423 var asSeconds = makeAs('s');
4424 var asMinutes = makeAs('m');
4425 var asHours = makeAs('h');
4426 var asDays = makeAs('d');
4427 var asWeeks = makeAs('w');
4428 var asMonths = makeAs('M');
4429 var asQuarters = makeAs('Q');
4430 var asYears = makeAs('y');
4431
4432 function clone$1() {
4433 return createDuration(this);
4434 }
4435
4436 function get$2(units) {
4437 units = normalizeUnits(units);
4438 return this.isValid() ? this[units + 's']() : NaN;
4439 }
4440
4441 function makeGetter(name) {
4442 return function () {
4443 return this.isValid() ? this._data[name] : NaN;
4444 };
4445 }
4446
4447 var milliseconds = makeGetter('milliseconds');
4448 var seconds = makeGetter('seconds');
4449 var minutes = makeGetter('minutes');
4450 var hours = makeGetter('hours');
4451 var days = makeGetter('days');
4452 var months = makeGetter('months');
4453 var years = makeGetter('years');
4454
4455 function weeks() {
4456 return absFloor(this.days() / 7);
4457 }
4458
4459 var round = Math.round;
4460 var thresholds = {
4461 ss: 44,
4462 // a few seconds to seconds
4463 s: 45,
4464 // seconds to minute
4465 m: 45,
4466 // minutes to hour
4467 h: 22,
4468 // hours to day
4469 d: 26,
4470 // days to month
4471 M: 11 // months to year
4472
4473 }; // helper function for moment.fn.from, moment.fn.fromNow, and moment.duration.fn.humanize
4474
4475 function substituteTimeAgo(string, number, withoutSuffix, isFuture, locale) {
4476 return locale.relativeTime(number || 1, !!withoutSuffix, string, isFuture);
4477 }
4478
4479 function relativeTime$1(posNegDuration, withoutSuffix, locale) {
4480 var duration = createDuration(posNegDuration).abs();
4481 var seconds = round(duration.as('s'));
4482 var minutes = round(duration.as('m'));
4483 var hours = round(duration.as('h'));
4484 var days = round(duration.as('d'));
4485 var months = round(duration.as('M'));
4486 var years = round(duration.as('y'));
4487 var a = seconds <= thresholds.ss && ['s', seconds] || seconds < thresholds.s && ['ss', seconds] || minutes <= 1 && ['m'] || minutes < thresholds.m && ['mm', minutes] || hours <= 1 && ['h'] || hours < thresholds.h && ['hh', hours] || days <= 1 && ['d'] || days < thresholds.d && ['dd', days] || months <= 1 && ['M'] || months < thresholds.M && ['MM', months] || years <= 1 && ['y'] || ['yy', years];
4488 a[2] = withoutSuffix;
4489 a[3] = +posNegDuration > 0;
4490 a[4] = locale;
4491 return substituteTimeAgo.apply(null, a);
4492 } // This function allows you to set the rounding function for relative time strings
4493
4494
4495 function getSetRelativeTimeRounding(roundingFunction) {
4496 if (roundingFunction === undefined) {
4497 return round;
4498 }
4499
4500 if (typeof roundingFunction === 'function') {
4501 round = roundingFunction;
4502 return true;
4503 }
4504
4505 return false;
4506 } // This function allows you to set a threshold for relative time strings
4507
4508
4509 function getSetRelativeTimeThreshold(threshold, limit) {
4510 if (thresholds[threshold] === undefined) {
4511 return false;
4512 }
4513
4514 if (limit === undefined) {
4515 return thresholds[threshold];
4516 }
4517
4518 thresholds[threshold] = limit;
4519
4520 if (threshold === 's') {
4521 thresholds.ss = limit - 1;
4522 }
4523
4524 return true;
4525 }
4526
4527 function humanize(withSuffix) {
4528 if (!this.isValid()) {
4529 return this.localeData().invalidDate();
4530 }
4531
4532 var locale = this.localeData();
4533 var output = relativeTime$1(this, !withSuffix, locale);
4534
4535 if (withSuffix) {
4536 output = locale.pastFuture(+this, output);
4537 }
4538
4539 return locale.postformat(output);
4540 }
4541
4542 var abs$1 = Math.abs;
4543
4544 function sign(x) {
4545 return (x > 0) - (x < 0) || +x;
4546 }
4547
4548 function toISOString$1() {
4549 // for ISO strings we do not use the normal bubbling rules:
4550 // * milliseconds bubble up until they become hours
4551 // * days do not bubble at all
4552 // * months bubble up until they become years
4553 // This is because there is no context-free conversion between hours and days
4554 // (think of clock changes)
4555 // and also not between days and months (28-31 days per month)
4556 if (!this.isValid()) {
4557 return this.localeData().invalidDate();
4558 }
4559
4560 var seconds = abs$1(this._milliseconds) / 1000;
4561 var days = abs$1(this._days);
4562 var months = abs$1(this._months);
4563 var minutes, hours, years; // 3600 seconds -> 60 minutes -> 1 hour
4564
4565 minutes = absFloor(seconds / 60);
4566 hours = absFloor(minutes / 60);
4567 seconds %= 60;
4568 minutes %= 60; // 12 months -> 1 year
4569
4570 years = absFloor(months / 12);
4571 months %= 12; // inspired by https://github.com/dordille/moment-isoduration/blob/master/moment.isoduration.js
4572
4573 var Y = years;
4574 var M = months;
4575 var D = days;
4576 var h = hours;
4577 var m = minutes;
4578 var s = seconds ? seconds.toFixed(3).replace(/\.?0+$/, '') : '';
4579 var total = this.asSeconds();
4580
4581 if (!total) {
4582 // this is the same as C#'s (Noda) and python (isodate)...
4583 // but not other JS (goog.date)
4584 return 'P0D';
4585 }
4586
4587 var totalSign = total < 0 ? '-' : '';
4588 var ymSign = sign(this._months) !== sign(total) ? '-' : '';
4589 var daysSign = sign(this._days) !== sign(total) ? '-' : '';
4590 var hmsSign = sign(this._milliseconds) !== sign(total) ? '-' : '';
4591 return totalSign + 'P' + (Y ? ymSign + Y + 'Y' : '') + (M ? ymSign + M + 'M' : '') + (D ? daysSign + D + 'D' : '') + (h || m || s ? 'T' : '') + (h ? hmsSign + h + 'H' : '') + (m ? hmsSign + m + 'M' : '') + (s ? hmsSign + s + 'S' : '');
4592 }
4593
4594 var proto$2 = Duration.prototype;
4595 proto$2.isValid = isValid$1;
4596 proto$2.abs = abs;
4597 proto$2.add = add$1;
4598 proto$2.subtract = subtract$1;
4599 proto$2.as = as;
4600 proto$2.asMilliseconds = asMilliseconds;
4601 proto$2.asSeconds = asSeconds;
4602 proto$2.asMinutes = asMinutes;
4603 proto$2.asHours = asHours;
4604 proto$2.asDays = asDays;
4605 proto$2.asWeeks = asWeeks;
4606 proto$2.asMonths = asMonths;
4607 proto$2.asQuarters = asQuarters;
4608 proto$2.asYears = asYears;
4609 proto$2.valueOf = valueOf$1;
4610 proto$2._bubble = bubble;
4611 proto$2.clone = clone$1;
4612 proto$2.get = get$2;
4613 proto$2.milliseconds = milliseconds;
4614 proto$2.seconds = seconds;
4615 proto$2.minutes = minutes;
4616 proto$2.hours = hours;
4617 proto$2.days = days;
4618 proto$2.weeks = weeks;
4619 proto$2.months = months;
4620 proto$2.years = years;
4621 proto$2.humanize = humanize;
4622 proto$2.toISOString = toISOString$1;
4623 proto$2.toString = toISOString$1;
4624 proto$2.toJSON = toISOString$1;
4625 proto$2.locale = locale;
4626 proto$2.localeData = localeData;
4627 proto$2.toIsoString = deprecate('toIsoString() is deprecated. Please use toISOString() instead (notice the capitals)', toISOString$1);
4628 proto$2.lang = lang; // Side effect imports
4629 // FORMATTING
4630
4631 addFormatToken('X', 0, 0, 'unix');
4632 addFormatToken('x', 0, 0, 'valueOf'); // PARSING
4633
4634 addRegexToken('x', matchSigned);
4635 addRegexToken('X', matchTimestamp);
4636 addParseToken('X', function (input, array, config) {
4637 config._d = new Date(parseFloat(input, 10) * 1000);
4638 });
4639 addParseToken('x', function (input, array, config) {
4640 config._d = new Date(toInt(input));
4641 }); // Side effect imports
4642
4643 hooks.version = '2.24.0';
4644 setHookCallback(createLocal);
4645 hooks.fn = proto;
4646 hooks.min = min;
4647 hooks.max = max;
4648 hooks.now = now;
4649 hooks.utc = createUTC;
4650 hooks.unix = createUnix;
4651 hooks.months = listMonths;
4652 hooks.isDate = isDate;
4653 hooks.locale = getSetGlobalLocale;
4654 hooks.invalid = createInvalid;
4655 hooks.duration = createDuration;
4656 hooks.isMoment = isMoment;
4657 hooks.weekdays = listWeekdays;
4658 hooks.parseZone = createInZone;
4659 hooks.localeData = getLocale;
4660 hooks.isDuration = isDuration;
4661 hooks.monthsShort = listMonthsShort;
4662 hooks.weekdaysMin = listWeekdaysMin;
4663 hooks.defineLocale = defineLocale;
4664 hooks.updateLocale = updateLocale;
4665 hooks.locales = listLocales;
4666 hooks.weekdaysShort = listWeekdaysShort;
4667 hooks.normalizeUnits = normalizeUnits;
4668 hooks.relativeTimeRounding = getSetRelativeTimeRounding;
4669 hooks.relativeTimeThreshold = getSetRelativeTimeThreshold;
4670 hooks.calendarFormat = getCalendarFormat;
4671 hooks.prototype = proto; // currently HTML5 input type only supports 24-hour formats
4672
4673 hooks.HTML5_FMT = {
4674 DATETIME_LOCAL: 'YYYY-MM-DDTHH:mm',
4675 // <input type="datetime-local" />
4676 DATETIME_LOCAL_SECONDS: 'YYYY-MM-DDTHH:mm:ss',
4677 // <input type="datetime-local" step="1" />
4678 DATETIME_LOCAL_MS: 'YYYY-MM-DDTHH:mm:ss.SSS',
4679 // <input type="datetime-local" step="0.001" />
4680 DATE: 'YYYY-MM-DD',
4681 // <input type="date" />
4682 TIME: 'HH:mm',
4683 // <input type="time" />
4684 TIME_SECONDS: 'HH:mm:ss',
4685 // <input type="time" step="1" />
4686 TIME_MS: 'HH:mm:ss.SSS',
4687 // <input type="time" step="0.001" />
4688 WEEK: 'GGGG-[W]WW',
4689 // <input type="week" />
4690 MONTH: 'YYYY-MM' // <input type="month" />
4691
4692 };
4693 return hooks;
4694 });
4695}); // Maps for number <-> hex string conversion
4696
4697var byteToHex = [];
4698
4699for (var i = 0; i < 256; i++) {
4700 byteToHex[i] = (i + 0x100).toString(16).substr(1);
4701}
4702/**
4703 * Represent binary UUID into it's string representation.
4704 *
4705 * @param buf - Buffer containing UUID bytes.
4706 * @param offset - Offset from the start of the buffer where the UUID is saved (not needed if the buffer starts with the UUID).
4707 *
4708 * @returns String representation of the UUID.
4709 */
4710
4711
4712function stringifyUUID(buf, offset) {
4713 var i = offset || 0;
4714 var bth = byteToHex;
4715 return bth[buf[i++]] + bth[buf[i++]] + bth[buf[i++]] + bth[buf[i++]] + '-' + bth[buf[i++]] + bth[buf[i++]] + '-' + bth[buf[i++]] + bth[buf[i++]] + '-' + bth[buf[i++]] + bth[buf[i++]] + '-' + bth[buf[i++]] + bth[buf[i++]] + bth[buf[i++]] + bth[buf[i++]] + bth[buf[i++]] + bth[buf[i++]];
4716}
4717/**
4718 * Generate 16 random bytes to be used as a base for UUID.
4719 *
4720 * @ignore
4721 */
4722
4723
4724var random = function () {
4725 if (typeof crypto !== 'undefined' && crypto.getRandomValues) {
4726 // WHATWG crypto-based RNG - http://wiki.whatwg.org/wiki/Crypto
4727 // Moderately fast, high quality
4728 var _rnds8 = new Uint8Array(16);
4729
4730 return function whatwgRNG() {
4731 crypto.getRandomValues(_rnds8);
4732 return _rnds8;
4733 };
4734 } // Math.random()-based (RNG)
4735 //
4736 // If all else fails, use Math.random().
4737 // It's fast, but is of unspecified quality.
4738
4739
4740 var _rnds = new Array(16);
4741
4742 return function () {
4743 for (var i = 0, r; i < 16; i++) {
4744 if ((i & 0x03) === 0) {
4745 r = Math.random() * 0x100000000;
4746 }
4747
4748 _rnds[i] = r >>> ((i & 0x03) << 3) & 0xff;
4749 }
4750
4751 return _rnds;
4752 }; // uuid.js
4753 //
4754 // Copyright (c) 2010-2012 Robert Kieffer
4755 // MIT License - http://opensource.org/licenses/mit-license.php
4756 // Unique ID creation requires a high quality random # generator. We feature
4757 // detect to determine the best RNG source, normalizing to a function that
4758 // returns 128-bits of randomness, since that's what's usually required
4759 // return require('./rng');
4760}();
4761
4762var byteToHex$1 = [];
4763
4764for (var i$1 = 0; i$1 < 256; i$1++) {
4765 byteToHex$1[i$1] = (i$1 + 0x100).toString(16).substr(1);
4766} // **`v1()` - Generate time-based UUID**
4767//
4768// Inspired by https://github.com/LiosK/UUID.js
4769// and http://docs.python.org/library/uuid.html
4770// random #'s we need to init node and clockseq
4771
4772
4773var seedBytes = random(); // Per 4.5, create and 48-bit node id, (47 random bits + multicast bit = 1)
4774
4775var defaultNodeId = [seedBytes[0] | 0x01, seedBytes[1], seedBytes[2], seedBytes[3], seedBytes[4], seedBytes[5]]; // Per 4.2.2, randomize (14 bit) clockseq
4776
4777var defaultClockseq = (seedBytes[6] << 8 | seedBytes[7]) & 0x3fff; // Previous uuid creation time
4778
4779/**
4780 * UUIDv4 options.
4781 */
4782
4783/**
4784 * Generate UUIDv4
4785 *
4786 * @param options - Options to be used instead of default generated values.
4787 * String 'binary' is a shorthand for uuid4({}, new Array(16)).
4788 * @param buf - If present the buffer will be filled with the generated UUID.
4789 * @param offset - Offset of the UUID from the start of the buffer.
4790 *
4791 * @returns UUIDv4
4792 */
4793
4794function uuid4() {
4795 var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
4796 var buf = arguments.length > 1 ? arguments[1] : undefined;
4797 var offset = arguments.length > 2 ? arguments[2] : undefined; // Deprecated - 'format' argument, as supported in v1.2
4798
4799 var i = buf && offset || 0;
4800
4801 if (typeof options === 'string') {
4802 buf = options === 'binary' ? new Array(16) : undefined;
4803 options = {};
4804 }
4805
4806 var rnds = options.random || (options.rng || random)(); // Per 4.4, set bits for version and `clock_seq_hi_and_reserved`
4807
4808 rnds[6] = rnds[6] & 0x0f | 0x40;
4809 rnds[8] = rnds[8] & 0x3f | 0x80; // Copy bytes to buffer, if provided
4810
4811 if (buf) {
4812 for (var ii = 0; ii < 16; ii++) {
4813 buf[i + ii] = rnds[ii];
4814 }
4815 }
4816
4817 return buf || stringifyUUID(rnds);
4818} // Rollup will complain about mixing default and named exports in UMD build,
4819
4820
4821function ownKeys(object, enumerableOnly) {
4822 var keys = Object.keys(object);
4823
4824 if (Object.getOwnPropertySymbols) {
4825 keys.push.apply(keys, Object.getOwnPropertySymbols(object));
4826 }
4827
4828 if (enumerableOnly) keys = keys.filter(function (sym) {
4829 return Object.getOwnPropertyDescriptor(object, sym).enumerable;
4830 });
4831 return keys;
4832}
4833
4834function _objectSpread(target) {
4835 for (var i = 1; i < arguments.length; i++) {
4836 var source = arguments[i] != null ? arguments[i] : {};
4837
4838 if (i % 2) {
4839 ownKeys(source, true).forEach(function (key) {
4840 defineProperty(target, key, source[key]);
4841 });
4842 } else if (Object.getOwnPropertyDescriptors) {
4843 Object.defineProperties(target, Object.getOwnPropertyDescriptors(source));
4844 } else {
4845 ownKeys(source).forEach(function (key) {
4846 Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key));
4847 });
4848 }
4849 }
4850
4851 return target;
4852} // for example '/Date(1198908717056)/' or '/Date(1198908717056-0700)/'
4853// code from http://momentjs.com/
4854
4855
4856var ASPDateRegex = /^\/?Date\((-?\d+)/i; // Hex color
4857
4858var fullHexRE = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i;
4859var shortHexRE = /^#?([a-f\d])([a-f\d])([a-f\d])$/i;
4860/**
4861 * Hue, Saturation, Value.
4862 */
4863
4864/**
4865 * Test whether given object is a number
4866 *
4867 * @param value - Input value of unknown type.
4868 *
4869 * @returns True if number, false otherwise.
4870 */
4871
4872function isNumber(value) {
4873 return value instanceof Number || typeof value === 'number';
4874}
4875/**
4876 * Remove everything in the DOM object
4877 *
4878 * @param DOMobject - Node whose child nodes will be recursively deleted.
4879 */
4880
4881
4882function recursiveDOMDelete(DOMobject) {
4883 if (DOMobject) {
4884 while (DOMobject.hasChildNodes() === true) {
4885 var child = DOMobject.firstChild;
4886
4887 if (child) {
4888 recursiveDOMDelete(child);
4889 DOMobject.removeChild(child);
4890 }
4891 }
4892 }
4893}
4894/**
4895 * Test whether given object is a string
4896 *
4897 * @param value - Input value of unknown type.
4898 *
4899 * @returns True if string, false otherwise.
4900 */
4901
4902
4903function isString(value) {
4904 return value instanceof String || typeof value === 'string';
4905}
4906/**
4907 * Test whether given object is a object (not primitive or null).
4908 *
4909 * @param value - Input value of unknown type.
4910 *
4911 * @returns True if not null object, false otherwise.
4912 */
4913
4914
4915function isObject(value) {
4916 return _typeof_1(value) === 'object' && value !== null;
4917}
4918/**
4919 * Test whether given object is a Date, or a String containing a Date
4920 *
4921 * @param value - Input value of unknown type.
4922 *
4923 * @returns True if Date instance or string date representation, false otherwise.
4924 */
4925
4926
4927function isDate(value) {
4928 if (value instanceof Date) {
4929 return true;
4930 } else if (isString(value)) {
4931 // test whether this string contains a date
4932 var match = ASPDateRegex.exec(value);
4933
4934 if (match) {
4935 return true;
4936 } else if (!isNaN(Date.parse(value))) {
4937 return true;
4938 }
4939 }
4940
4941 return false;
4942}
4943/**
4944 * Test whether given object is a Moment date.
4945 * @TODO: This is basically a workaround, if Moment was imported property it wouldn't necessary as moment.isMoment is a TS type guard.
4946 *
4947 * @param value - Input value of unknown type.
4948 *
4949 * @returns True if Moment instance, false otherwise.
4950 */
4951
4952
4953function isMoment(value) {
4954 return moment.isMoment(value);
4955}
4956/**
4957 * Copy property from b to a if property present in a.
4958 * If property in b explicitly set to null, delete it if `allowDeletion` set.
4959 *
4960 * Internal helper routine, should not be exported. Not added to `exports` for that reason.
4961 *
4962 * @param a - Target object.
4963 * @param b - Source object.
4964 * @param prop - Name of property to copy from b to a.
4965 * @param allowDeletion if true, delete property in a if explicitly set to null in b
4966 */
4967
4968
4969function copyOrDelete(a, b, prop, allowDeletion) {
4970 var doDeletion = false;
4971
4972 if (allowDeletion === true) {
4973 doDeletion = b[prop] === null && a[prop] !== undefined;
4974 }
4975
4976 if (doDeletion) {
4977 delete a[prop];
4978 } else {
4979 a[prop] = b[prop]; // Remember, this is a reference copy!
4980 }
4981}
4982/**
4983 * Fill an object with a possibly partially defined other object.
4984 *
4985 * Only copies values for the properties already present in a.
4986 * That means an object is not created on a property if only the b object has it.
4987 *
4988 * @param a - The object that will have it's properties updated.
4989 * @param b - The object with property updates.
4990 * @param allowDeletion - if true, delete properties in a that are explicitly set to null in b
4991 */
4992
4993
4994function fillIfDefined(a, b) {
4995 var allowDeletion = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false; // NOTE: iteration of properties of a
4996 // NOTE: prototype properties iterated over as well
4997
4998 for (var prop in a) {
4999 if (b[prop] !== undefined) {
5000 if (b[prop] === null || _typeof_1(b[prop]) !== 'object') {
5001 // Note: typeof null === 'object'
5002 copyOrDelete(a, b, prop, allowDeletion);
5003 } else {
5004 var aProp = a[prop];
5005 var bProp = b[prop];
5006
5007 if (isObject(aProp) && isObject(bProp)) {
5008 fillIfDefined(aProp, bProp, allowDeletion);
5009 }
5010 }
5011 }
5012 }
5013}
5014/**
5015 * Copy the values of all of the enumerable own properties from one or more source objects to a
5016 * target object. Returns the target object.
5017 *
5018 * @param target - The target object to copy to.
5019 * @param source - The source object from which to copy properties.
5020 *
5021 * @return The target object.
5022 */
5023
5024
5025var extend = Object.assign;
5026/**
5027 * Extend object a with selected properties of object b or a series of objects
5028 * Only properties with defined values are copied
5029 *
5030 * @param props - Properties to be copied to a.
5031 * @param a - The target.
5032 * @param others - The sources.
5033 *
5034 * @returns Argument a.
5035 */
5036
5037function selectiveExtend(props, a) {
5038 if (!Array.isArray(props)) {
5039 throw new Error('Array with property names expected as first argument');
5040 }
5041
5042 for (var _len = arguments.length, others = new Array(_len > 2 ? _len - 2 : 0), _key = 2; _key < _len; _key++) {
5043 others[_key - 2] = arguments[_key];
5044 }
5045
5046 for (var _i = 0, _others = others; _i < _others.length; _i++) {
5047 var other = _others[_i];
5048
5049 for (var p = 0; p < props.length; p++) {
5050 var prop = props[p];
5051
5052 if (other && Object.prototype.hasOwnProperty.call(other, prop)) {
5053 a[prop] = other[prop];
5054 }
5055 }
5056 }
5057
5058 return a;
5059}
5060/**
5061 * Extend object a with selected properties of object b.
5062 * Only properties with defined values are copied.
5063 *
5064 * **Note:** Previous version of this routine implied that multiple source objects
5065 * could be used; however, the implementation was **wrong**.
5066 * Since multiple (>1) sources weren't used anywhere in the `vis.js` code,
5067 * this has been removed
5068 *
5069 * @param props - Names of first-level properties to copy over.
5070 * @param a - Target object.
5071 * @param b - Source object.
5072 * @param allowDeletion - If true, delete property in a if explicitly set to null in b.
5073 *
5074 * @returns Argument a.
5075 */
5076
5077
5078function selectiveDeepExtend(props, a, b) {
5079 var allowDeletion = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false; // TODO: add support for Arrays to deepExtend
5080
5081 if (Array.isArray(b)) {
5082 throw new TypeError('Arrays are not supported by deepExtend');
5083 }
5084
5085 for (var p = 0; p < props.length; p++) {
5086 var prop = props[p];
5087
5088 if (Object.prototype.hasOwnProperty.call(b, prop)) {
5089 if (b[prop] && b[prop].constructor === Object) {
5090 if (a[prop] === undefined) {
5091 a[prop] = {};
5092 }
5093
5094 if (a[prop].constructor === Object) {
5095 deepExtend(a[prop], b[prop], false, allowDeletion);
5096 } else {
5097 copyOrDelete(a, b, prop, allowDeletion);
5098 }
5099 } else if (Array.isArray(b[prop])) {
5100 throw new TypeError('Arrays are not supported by deepExtend');
5101 } else {
5102 copyOrDelete(a, b, prop, allowDeletion);
5103 }
5104 }
5105 }
5106
5107 return a;
5108}
5109/**
5110 * Extend object `a` with properties of object `b`, ignoring properties which are explicitly
5111 * specified to be excluded.
5112 *
5113 * The properties of `b` are considered for copying.
5114 * Properties which are themselves objects are are also extended.
5115 * Only properties with defined values are copied
5116 *
5117 * @param propsToExclude - Names of properties which should *not* be copied.
5118 * @param a - Object to extend.
5119 * @param b - Object to take properties from for extension.
5120 * @param allowDeletion - If true, delete properties in a that are explicitly set to null in b.
5121 *
5122 * @returns Argument a.
5123 */
5124
5125
5126function selectiveNotDeepExtend(propsToExclude, a, b) {
5127 var allowDeletion = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false; // TODO: add support for Arrays to deepExtend
5128 // NOTE: array properties have an else-below; apparently, there is a problem here.
5129
5130 if (Array.isArray(b)) {
5131 throw new TypeError('Arrays are not supported by deepExtend');
5132 }
5133
5134 for (var prop in b) {
5135 if (!Object.prototype.hasOwnProperty.call(b, prop)) {
5136 continue;
5137 } // Handle local properties only
5138
5139
5140 if (propsToExclude.indexOf(prop) !== -1) {
5141 continue;
5142 } // In exclusion list, skip
5143
5144
5145 if (b[prop] && b[prop].constructor === Object) {
5146 if (a[prop] === undefined) {
5147 a[prop] = {};
5148 }
5149
5150 if (a[prop].constructor === Object) {
5151 deepExtend(a[prop], b[prop]); // NOTE: allowDeletion not propagated!
5152 } else {
5153 copyOrDelete(a, b, prop, allowDeletion);
5154 }
5155 } else if (Array.isArray(b[prop])) {
5156 a[prop] = [];
5157
5158 for (var i = 0; i < b[prop].length; i++) {
5159 a[prop].push(b[prop][i]);
5160 }
5161 } else {
5162 copyOrDelete(a, b, prop, allowDeletion);
5163 }
5164 }
5165
5166 return a;
5167}
5168/**
5169 * Deep extend an object a with the properties of object b
5170 *
5171 * @param a - Target object.
5172 * @param b - Source object.
5173 * @param protoExtend - If true, the prototype values will also be extended
5174 * (ie. the options objects that inherit from others will also get the inherited options).
5175 * @param allowDeletion - If true, the values of fields that are null will be deleted.
5176 *
5177 * @returns Argument a.
5178 */
5179
5180
5181function deepExtend(a, b) {
5182 var protoExtend = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
5183 var allowDeletion = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false;
5184
5185 for (var prop in b) {
5186 if (Object.prototype.hasOwnProperty.call(b, prop) || protoExtend === true) {
5187 if (b[prop] && b[prop].constructor === Object) {
5188 if (a[prop] === undefined) {
5189 a[prop] = {};
5190 }
5191
5192 if (a[prop].constructor === Object) {
5193 deepExtend(a[prop], b[prop], protoExtend); // NOTE: allowDeletion not propagated!
5194 } else {
5195 copyOrDelete(a, b, prop, allowDeletion);
5196 }
5197 } else if (Array.isArray(b[prop])) {
5198 a[prop] = [];
5199
5200 for (var i = 0; i < b[prop].length; i++) {
5201 a[prop].push(b[prop][i]);
5202 }
5203 } else {
5204 copyOrDelete(a, b, prop, allowDeletion);
5205 }
5206 }
5207 }
5208
5209 return a;
5210}
5211/**
5212 * Test whether all elements in two arrays are equal.
5213 *
5214 * @param a - First array.
5215 * @param b - Second array.
5216 *
5217 * @returns True if both arrays have the same length and same elements (1 = '1').
5218 */
5219
5220
5221function equalArray(a, b) {
5222 if (a.length !== b.length) {
5223 return false;
5224 }
5225
5226 for (var i = 0, len = a.length; i < len; i++) {
5227 if (a[i] != b[i]) {
5228 return false;
5229 }
5230 }
5231
5232 return true;
5233}
5234/**
5235 * Convert an object into another type
5236 *
5237 * @param object - Value of unknown type.
5238 * @param type - Name of the desired type.
5239 *
5240 * @returns Object in the desired type.
5241 * @throws Error
5242 */
5243
5244
5245function convert(object, type) {
5246 var match;
5247
5248 if (object === undefined) {
5249 return undefined;
5250 }
5251
5252 if (object === null) {
5253 return null;
5254 }
5255
5256 if (!type) {
5257 return object;
5258 }
5259
5260 if (!(typeof type === 'string') && !(type instanceof String)) {
5261 throw new Error('Type must be a string');
5262 } //noinspection FallthroughInSwitchStatementJS
5263
5264
5265 switch (type) {
5266 case 'boolean':
5267 case 'Boolean':
5268 return Boolean(object);
5269
5270 case 'number':
5271 case 'Number':
5272 if (isString(object) && !isNaN(Date.parse(object))) {
5273 return moment(object).valueOf();
5274 } else {
5275 // @TODO: I don't think that Number and String constructors are a good idea.
5276 // This could also fail if the object doesn't have valueOf method or if it's redefined.
5277 // For example: Object.create(null) or { valueOf: 7 }.
5278 return Number(object.valueOf());
5279 }
5280
5281 case 'string':
5282 case 'String':
5283 return String(object);
5284
5285 case 'Date':
5286 if (isNumber(object)) {
5287 return new Date(object);
5288 }
5289
5290 if (object instanceof Date) {
5291 return new Date(object.valueOf());
5292 } else if (isMoment(object)) {
5293 return new Date(object.valueOf());
5294 }
5295
5296 if (isString(object)) {
5297 match = ASPDateRegex.exec(object);
5298
5299 if (match) {
5300 // object is an ASP date
5301 return new Date(Number(match[1])); // parse number
5302 } else {
5303 return moment(new Date(object)).toDate(); // parse string
5304 }
5305 } else {
5306 throw new Error('Cannot convert object of type ' + getType(object) + ' to type Date');
5307 }
5308
5309 case 'Moment':
5310 if (isNumber(object)) {
5311 return moment(object);
5312 }
5313
5314 if (object instanceof Date) {
5315 return moment(object.valueOf());
5316 } else if (isMoment(object)) {
5317 return moment(object);
5318 }
5319
5320 if (isString(object)) {
5321 match = ASPDateRegex.exec(object);
5322
5323 if (match) {
5324 // object is an ASP date
5325 return moment(Number(match[1])); // parse number
5326 } else {
5327 return moment(object); // parse string
5328 }
5329 } else {
5330 throw new Error('Cannot convert object of type ' + getType(object) + ' to type Date');
5331 }
5332
5333 case 'ISODate':
5334 if (isNumber(object)) {
5335 return new Date(object);
5336 } else if (object instanceof Date) {
5337 return object.toISOString();
5338 } else if (isMoment(object)) {
5339 return object.toDate().toISOString();
5340 } else if (isString(object)) {
5341 match = ASPDateRegex.exec(object);
5342
5343 if (match) {
5344 // object is an ASP date
5345 return new Date(Number(match[1])).toISOString(); // parse number
5346 } else {
5347 return moment(object).format(); // ISO 8601
5348 }
5349 } else {
5350 throw new Error('Cannot convert object of type ' + getType(object) + ' to type ISODate');
5351 }
5352
5353 case 'ASPDate':
5354 if (isNumber(object)) {
5355 return '/Date(' + object + ')/';
5356 } else if (object instanceof Date || isMoment(object)) {
5357 return '/Date(' + object.valueOf() + ')/';
5358 } else if (isString(object)) {
5359 match = ASPDateRegex.exec(object);
5360
5361 var _value;
5362
5363 if (match) {
5364 // object is an ASP date
5365 _value = new Date(Number(match[1])).valueOf(); // parse number
5366 } else {
5367 _value = new Date(object).valueOf(); // parse string
5368 }
5369
5370 return '/Date(' + _value + ')/';
5371 } else {
5372 throw new Error('Cannot convert object of type ' + getType(object) + ' to type ASPDate');
5373 }
5374
5375 default:
5376 var never = type;
5377 throw new Error("Unknown type ".concat(never));
5378 }
5379}
5380/**
5381 * Get the type of an object, for example exports.getType([]) returns 'Array'
5382 *
5383 * @param object - Input value of unknown type.
5384 *
5385 * @returns Detected type.
5386 */
5387
5388
5389function getType(object) {
5390 var type = _typeof_1(object);
5391
5392 if (type === 'object') {
5393 if (object === null) {
5394 return 'null';
5395 }
5396
5397 if (object instanceof Boolean) {
5398 return 'Boolean';
5399 }
5400
5401 if (object instanceof Number) {
5402 return 'Number';
5403 }
5404
5405 if (object instanceof String) {
5406 return 'String';
5407 }
5408
5409 if (Array.isArray(object)) {
5410 return 'Array';
5411 }
5412
5413 if (object instanceof Date) {
5414 return 'Date';
5415 }
5416
5417 return 'Object';
5418 }
5419
5420 if (type === 'number') {
5421 return 'Number';
5422 }
5423
5424 if (type === 'boolean') {
5425 return 'Boolean';
5426 }
5427
5428 if (type === 'string') {
5429 return 'String';
5430 }
5431
5432 if (type === undefined) {
5433 return 'undefined';
5434 }
5435
5436 return type;
5437}
5438/**
5439 * Used to extend an array and copy it. This is used to propagate paths recursively.
5440 *
5441 * @param arr - First part.
5442 * @param newValue - The value to be aadded into the array.
5443 *
5444 * @returns A new array with all items from arr and newValue (which is last).
5445 */
5446
5447
5448function copyAndExtendArray(arr, newValue) {
5449 return [].concat(toConsumableArray(arr), [newValue]);
5450}
5451/**
5452 * Used to extend an array and copy it. This is used to propagate paths recursively.
5453 *
5454 * @param arr - The array to be copied.
5455 *
5456 * @returns Shallow copy of arr.
5457 */
5458
5459
5460function copyArray(arr) {
5461 return arr.slice();
5462}
5463/**
5464 * Retrieve the absolute left value of a DOM element
5465 *
5466 * @param elem - A dom element, for example a div.
5467 *
5468 * @returns The absolute left position of this element in the browser page.
5469 */
5470
5471
5472function getAbsoluteLeft(elem) {
5473 return elem.getBoundingClientRect().left;
5474}
5475/**
5476 * Retrieve the absolute right value of a DOM element
5477 *
5478 * @param elem - A dom element, for example a div.
5479 *
5480 * @returns The absolute right position of this element in the browser page.
5481 */
5482
5483
5484function getAbsoluteRight(elem) {
5485 return elem.getBoundingClientRect().right;
5486}
5487/**
5488 * Retrieve the absolute top value of a DOM element
5489 *
5490 * @param elem - A dom element, for example a div.
5491 *
5492 * @returns The absolute top position of this element in the browser page.
5493 */
5494
5495
5496function getAbsoluteTop(elem) {
5497 return elem.getBoundingClientRect().top;
5498}
5499/**
5500 * Add a className to the given elements style.
5501 *
5502 * @param elem - The element to which the classes will be added.
5503 * @param classNames - Space separated list of classes.
5504 */
5505
5506
5507function addClassName(elem, classNames) {
5508 var classes = elem.className.split(' ');
5509 var newClasses = classNames.split(' ');
5510 classes = classes.concat(newClasses.filter(function (className) {
5511 return classes.indexOf(className) < 0;
5512 }));
5513 elem.className = classes.join(' ');
5514}
5515/**
5516 * Remove a className from the given elements style.
5517 *
5518 * @param elem - The element from which the classes will be removed.
5519 * @param classNames - Space separated list of classes.
5520 */
5521
5522
5523function removeClassName(elem, classNames) {
5524 var classes = elem.className.split(' ');
5525 var oldClasses = classNames.split(' ');
5526 classes = classes.filter(function (className) {
5527 return oldClasses.indexOf(className) < 0;
5528 });
5529 elem.className = classes.join(' ');
5530}
5531/**
5532 * For each method for both arrays and objects.
5533 * In case of an array, the built-in Array.forEach() is applied (**No, it's not!**).
5534 * In case of an Object, the method loops over all properties of the object.
5535 *
5536 * @param object - An Object or Array to be iterated over.
5537 * @param callback - Array.forEach-like callback.
5538 */
5539
5540
5541function forEach(object, callback) {
5542 if (Array.isArray(object)) {
5543 // array
5544 var len = object.length;
5545
5546 for (var i = 0; i < len; i++) {
5547 callback(object[i], i, object);
5548 }
5549 } else {
5550 // object
5551 for (var _key2 in object) {
5552 if (Object.prototype.hasOwnProperty.call(object, _key2)) {
5553 callback(object[_key2], _key2, object);
5554 }
5555 }
5556 }
5557}
5558/**
5559 * Convert an object into an array: all objects properties are put into the array. The resulting array is unordered.
5560 *
5561 * @param o - Object that contains the properties and methods.
5562 *
5563 * @returns An array of unordered values.
5564 */
5565
5566
5567var toArray = Object.values;
5568/**
5569 * Update a property in an object
5570 *
5571 * @param object - The object whose property will be updated.
5572 * @param key - Name of the property to be updated.
5573 * @param value - The new value to be assigned.
5574 *
5575 * @returns Whether the value was updated (true) or already strictly the same in the original object (false).
5576 */
5577
5578function updateProperty(object, key, value) {
5579 if (object[key] !== value) {
5580 object[key] = value;
5581 return true;
5582 } else {
5583 return false;
5584 }
5585}
5586/**
5587 * Throttle the given function to be only executed once per animation frame.
5588 *
5589 * @param fn - The original function.
5590 *
5591 * @returns The throttled function.
5592 */
5593
5594
5595function throttle(fn) {
5596 var scheduled = false;
5597 return function () {
5598 if (!scheduled) {
5599 scheduled = true;
5600 requestAnimationFrame(function () {
5601 scheduled = false;
5602 fn();
5603 });
5604 }
5605 };
5606}
5607/**
5608 * Add and event listener. Works for all browsers.
5609 *
5610 * @param element - The element to bind the event listener to.
5611 * @param action - Same as Element.addEventListener(action, —, —).
5612 * @param listener - Same as Element.addEventListener(—, listener, —).
5613 * @param useCapture - Same as Element.addEventListener(—, —, useCapture).
5614 */
5615
5616
5617function addEventListener(element, action, listener, useCapture) {
5618 if (element.addEventListener) {
5619 if (useCapture === undefined) {
5620 useCapture = false;
5621 }
5622
5623 if (action === 'mousewheel' && navigator.userAgent.indexOf('Firefox') >= 0) {
5624 action = 'DOMMouseScroll'; // For Firefox
5625 }
5626
5627 element.addEventListener(action, listener, useCapture);
5628 } else {
5629 element.attachEvent('on' + action, listener); // IE browsers
5630 }
5631}
5632/**
5633 * Remove an event listener from an element
5634 *
5635 * @param element - The element to bind the event listener to.
5636 * @param action - Same as Element.removeEventListener(action, —, —).
5637 * @param listener - Same as Element.removeEventListener(—, listener, —).
5638 * @param useCapture - Same as Element.removeEventListener(—, —, useCapture).
5639 */
5640
5641
5642function removeEventListener(element, action, listener, useCapture) {
5643 if (element.removeEventListener) {
5644 // non-IE browsers
5645 if (useCapture === undefined) {
5646 useCapture = false;
5647 }
5648
5649 if (action === 'mousewheel' && navigator.userAgent.indexOf('Firefox') >= 0) {
5650 action = 'DOMMouseScroll'; // For Firefox
5651 }
5652
5653 element.removeEventListener(action, listener, useCapture);
5654 } else {
5655 element.detachEvent('on' + action, listener); // IE browsers
5656 }
5657}
5658/**
5659 * Cancels the event's default action if it is cancelable, without stopping further propagation of the event.
5660 *
5661 * @param event - The event whose default action should be prevented.
5662 */
5663
5664
5665function preventDefault(event) {
5666 if (!event) {
5667 event = window.event;
5668 }
5669
5670 if (!event) ;else if (event.preventDefault) {
5671 event.preventDefault(); // non-IE browsers
5672 } else {
5673 event.returnValue = false; // IE browsers
5674 }
5675}
5676/**
5677 * Get HTML element which is the target of the event.
5678 *
5679 * @param event - The event.
5680 *
5681 * @returns The element or null if not obtainable.
5682 */
5683
5684
5685function getTarget() {
5686 var event = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : window.event; // code from http://www.quirksmode.org/js/events_properties.html
5687 // @TODO: EventTarget can be almost anything, is it okay to return only Elements?
5688
5689 var target = null;
5690 if (!event) ;else if (event.target) {
5691 target = event.target;
5692 } else if (event.srcElement) {
5693 target = event.srcElement;
5694 }
5695
5696 if (!(target instanceof Element)) {
5697 return null;
5698 }
5699
5700 if (target.nodeType != null && target.nodeType == 3) {
5701 // defeat Safari bug
5702 target = target.parentNode;
5703
5704 if (!(target instanceof Element)) {
5705 return null;
5706 }
5707 }
5708
5709 return target;
5710}
5711/**
5712 * Check if given element contains given parent somewhere in the DOM tree
5713 *
5714 * @param element - The element to be tested.
5715 * @param parent - The ancestor (not necessarily parent) of the element.
5716 *
5717 * @returns True if parent is an ancestor of the element, false otherwise.
5718 */
5719
5720
5721function hasParent(element, parent) {
5722 var elem = element;
5723
5724 while (elem) {
5725 if (elem === parent) {
5726 return true;
5727 } else if (elem.parentNode) {
5728 elem = elem.parentNode;
5729 } else {
5730 return false;
5731 }
5732 }
5733
5734 return false;
5735}
5736
5737var option = {
5738 /**
5739 * Convert a value into a boolean.
5740 *
5741 * @param value - Value to be converted intoboolean, a function will be executed as (() => unknown).
5742 * @param defaultValue - If the value or the return value of the function == null then this will be returned.
5743 *
5744 * @returns Corresponding boolean value, if none then the default value, if none then null.
5745 */
5746 asBoolean: function asBoolean(value, defaultValue) {
5747 if (typeof value == 'function') {
5748 value = value();
5749 }
5750
5751 if (value != null) {
5752 return value != false;
5753 }
5754
5755 return defaultValue || null;
5756 },
5757
5758 /**
5759 * Convert a value into a number.
5760 *
5761 * @param value - Value to be converted intonumber, a function will be executed as (() => unknown).
5762 * @param defaultValue - If the value or the return value of the function == null then this will be returned.
5763 *
5764 * @returns Corresponding **boxed** number value, if none then the default value, if none then null.
5765 */
5766 asNumber: function asNumber(value, defaultValue) {
5767 if (typeof value == 'function') {
5768 value = value();
5769 }
5770
5771 if (value != null) {
5772 return Number(value) || defaultValue || null;
5773 }
5774
5775 return defaultValue || null;
5776 },
5777
5778 /**
5779 * Convert a value into a string.
5780 *
5781 * @param value - Value to be converted intostring, a function will be executed as (() => unknown).
5782 * @param defaultValue - If the value or the return value of the function == null then this will be returned.
5783 *
5784 * @returns Corresponding **boxed** string value, if none then the default value, if none then null.
5785 */
5786 asString: function asString(value, defaultValue) {
5787 if (typeof value == 'function') {
5788 value = value();
5789 }
5790
5791 if (value != null) {
5792 return String(value);
5793 }
5794
5795 return defaultValue || null;
5796 },
5797
5798 /**
5799 * Convert a value into a size.
5800 *
5801 * @param value - Value to be converted intosize, a function will be executed as (() => unknown).
5802 * @param defaultValue - If the value or the return value of the function == null then this will be returned.
5803 *
5804 * @returns Corresponding string value (number + 'px'), if none then the default value, if none then null.
5805 */
5806 asSize: function asSize(value, defaultValue) {
5807 if (typeof value == 'function') {
5808 value = value();
5809 }
5810
5811 if (isString(value)) {
5812 return value;
5813 } else if (isNumber(value)) {
5814 return value + 'px';
5815 } else {
5816 return defaultValue || null;
5817 }
5818 },
5819
5820 /**
5821 * Convert a value into a DOM Element.
5822 *
5823 * @param value - Value to be converted into DOM Element, a function will be executed as (() => unknown).
5824 * @param defaultValue - If the value or the return value of the function == null then this will be returned.
5825 *
5826 * @returns The DOM Element, if none then the default value, if none then null.
5827 */
5828 asElement: function asElement(value, defaultValue) {
5829 if (typeof value == 'function') {
5830 value = value();
5831 }
5832
5833 return value || defaultValue || null;
5834 }
5835};
5836/**
5837 * Convert hex color string into RGB color object.
5838 * http://stackoverflow.com/questions/5623838/rgb-to-hex-and-hex-to-rgb
5839 *
5840 * @param hex - Hex color string (3 or 6 digits, with or without #).
5841 *
5842 * @returns RGB color object.
5843 */
5844
5845function hexToRGB(hex) {
5846 var result;
5847
5848 switch (hex.length) {
5849 case 3:
5850 case 4:
5851 result = shortHexRE.exec(hex);
5852 return result ? {
5853 r: parseInt(result[1] + result[1], 16),
5854 g: parseInt(result[2] + result[2], 16),
5855 b: parseInt(result[3] + result[3], 16)
5856 } : null;
5857
5858 case 6:
5859 case 7:
5860 result = fullHexRE.exec(hex);
5861 return result ? {
5862 r: parseInt(result[1], 16),
5863 g: parseInt(result[2], 16),
5864 b: parseInt(result[3], 16)
5865 } : null;
5866
5867 default:
5868 return null;
5869 }
5870}
5871/**
5872 * This function takes string color in hex or RGB format and adds the opacity, RGBA is passed through unchanged.
5873 *
5874 * @param color - The color string (hex, RGB, RGBA).
5875 * @param opacity - The new opacity.
5876 *
5877 * @returns RGBA string, for example 'rgba(255, 0, 127, 0.3)'.
5878 */
5879
5880
5881function overrideOpacity(color, opacity) {
5882 if (color.indexOf('rgba') !== -1) {
5883 return color;
5884 } else if (color.indexOf('rgb') !== -1) {
5885 var rgb = color.substr(color.indexOf('(') + 1).replace(')', '').split(',');
5886 return 'rgba(' + rgb[0] + ',' + rgb[1] + ',' + rgb[2] + ',' + opacity + ')';
5887 } else {
5888 var _rgb = hexToRGB(color);
5889
5890 if (_rgb == null) {
5891 return color;
5892 } else {
5893 return 'rgba(' + _rgb.r + ',' + _rgb.g + ',' + _rgb.b + ',' + opacity + ')';
5894 }
5895 }
5896}
5897/**
5898 * Convert RGB <0, 255> into hex color string.
5899 *
5900 * @param red - Red channel.
5901 * @param green - Green channel.
5902 * @param blue - Blue channel.
5903 *
5904 * @returns Hex color string (for example: '#0acdc0').
5905 */
5906
5907
5908function RGBToHex(red, green, blue) {
5909 return '#' + ((1 << 24) + (red << 16) + (green << 8) + blue).toString(16).slice(1);
5910}
5911/**
5912 * Parse a color property into an object with border, background, and highlight colors
5913 *
5914 * @param inputColor - Shorthand color string or input color object.
5915 * @param defaultColor - Full color object to fill in missing values in inputColor.
5916 *
5917 * @returns Color object.
5918 */
5919
5920
5921function parseColor(inputColor, defaultColor) {
5922 if (isString(inputColor)) {
5923 var colorStr = inputColor;
5924
5925 if (isValidRGB(colorStr)) {
5926 var rgb = colorStr.substr(4).substr(0, colorStr.length - 5).split(',').map(function (value) {
5927 return parseInt(value);
5928 });
5929 colorStr = RGBToHex(rgb[0], rgb[1], rgb[2]);
5930 }
5931
5932 if (isValidHex(colorStr) === true) {
5933 var hsv = hexToHSV(colorStr);
5934 var lighterColorHSV = {
5935 h: hsv.h,
5936 s: hsv.s * 0.8,
5937 v: Math.min(1, hsv.v * 1.02)
5938 };
5939 var darkerColorHSV = {
5940 h: hsv.h,
5941 s: Math.min(1, hsv.s * 1.25),
5942 v: hsv.v * 0.8
5943 };
5944 var darkerColorHex = HSVToHex(darkerColorHSV.h, darkerColorHSV.s, darkerColorHSV.v);
5945 var lighterColorHex = HSVToHex(lighterColorHSV.h, lighterColorHSV.s, lighterColorHSV.v);
5946 return {
5947 background: colorStr,
5948 border: darkerColorHex,
5949 highlight: {
5950 background: lighterColorHex,
5951 border: darkerColorHex
5952 },
5953 hover: {
5954 background: lighterColorHex,
5955 border: darkerColorHex
5956 }
5957 };
5958 } else {
5959 return {
5960 background: colorStr,
5961 border: colorStr,
5962 highlight: {
5963 background: colorStr,
5964 border: colorStr
5965 },
5966 hover: {
5967 background: colorStr,
5968 border: colorStr
5969 }
5970 };
5971 }
5972 } else {
5973 if (defaultColor) {
5974 var color = {
5975 background: inputColor.background || defaultColor.background,
5976 border: inputColor.border || defaultColor.border,
5977 highlight: isString(inputColor.highlight) ? {
5978 border: inputColor.highlight,
5979 background: inputColor.highlight
5980 } : {
5981 background: inputColor.highlight && inputColor.highlight.background || defaultColor.highlight.background,
5982 border: inputColor.highlight && inputColor.highlight.border || defaultColor.highlight.border
5983 },
5984 hover: isString(inputColor.hover) ? {
5985 border: inputColor.hover,
5986 background: inputColor.hover
5987 } : {
5988 border: inputColor.hover && inputColor.hover.border || defaultColor.hover.border,
5989 background: inputColor.hover && inputColor.hover.background || defaultColor.hover.background
5990 }
5991 };
5992 return color;
5993 } else {
5994 var _color = {
5995 background: inputColor.background || undefined,
5996 border: inputColor.border || undefined,
5997 highlight: isString(inputColor.highlight) ? {
5998 border: inputColor.highlight,
5999 background: inputColor.highlight
6000 } : {
6001 background: inputColor.highlight && inputColor.highlight.background || undefined,
6002 border: inputColor.highlight && inputColor.highlight.border || undefined
6003 },
6004 hover: isString(inputColor.hover) ? {
6005 border: inputColor.hover,
6006 background: inputColor.hover
6007 } : {
6008 border: inputColor.hover && inputColor.hover.border || undefined,
6009 background: inputColor.hover && inputColor.hover.background || undefined
6010 }
6011 };
6012 return _color;
6013 }
6014 }
6015}
6016/**
6017 * Convert RGB <0, 255> into HSV object.
6018 * http://www.javascripter.net/faq/rgb2hsv.htm
6019 *
6020 * @param red - Red channel.
6021 * @param green - Green channel.
6022 * @param blue - Blue channel.
6023 *
6024 * @returns HSV color object.
6025 */
6026
6027
6028function RGBToHSV(red, green, blue) {
6029 red = red / 255;
6030 green = green / 255;
6031 blue = blue / 255;
6032 var minRGB = Math.min(red, Math.min(green, blue));
6033 var maxRGB = Math.max(red, Math.max(green, blue)); // Black-gray-white
6034
6035 if (minRGB === maxRGB) {
6036 return {
6037 h: 0,
6038 s: 0,
6039 v: minRGB
6040 };
6041 } // Colors other than black-gray-white:
6042
6043
6044 var d = red === minRGB ? green - blue : blue === minRGB ? red - green : blue - red;
6045 var h = red === minRGB ? 3 : blue === minRGB ? 1 : 5;
6046 var hue = 60 * (h - d / (maxRGB - minRGB)) / 360;
6047 var saturation = (maxRGB - minRGB) / maxRGB;
6048 var value = maxRGB;
6049 return {
6050 h: hue,
6051 s: saturation,
6052 v: value
6053 };
6054}
6055
6056var cssUtil = {
6057 // split a string with css styles into an object with key/values
6058 split: function split(cssText) {
6059 var styles = {};
6060 cssText.split(';').forEach(function (style) {
6061 if (style.trim() != '') {
6062 var parts = style.split(':');
6063
6064 var _key3 = parts[0].trim();
6065
6066 var _value2 = parts[1].trim();
6067
6068 styles[_key3] = _value2;
6069 }
6070 });
6071 return styles;
6072 },
6073 // build a css text string from an object with key/values
6074 join: function join(styles) {
6075 return Object.keys(styles).map(function (key) {
6076 return key + ': ' + styles[key];
6077 }).join('; ');
6078 }
6079};
6080/**
6081 * Append a string with css styles to an element
6082 *
6083 * @param element - The element that will receive new styles.
6084 * @param cssText - The styles to be appended.
6085 */
6086
6087function addCssText(element, cssText) {
6088 var currentStyles = cssUtil.split(element.style.cssText);
6089 var newStyles = cssUtil.split(cssText);
6090
6091 var styles = _objectSpread({}, currentStyles, {}, newStyles);
6092
6093 element.style.cssText = cssUtil.join(styles);
6094}
6095/**
6096 * Remove a string with css styles from an element
6097 *
6098 * @param element - The element from which styles should be removed.
6099 * @param cssText - The styles to be removed.
6100 */
6101
6102
6103function removeCssText(element, cssText) {
6104 var styles = cssUtil.split(element.style.cssText);
6105 var removeStyles = cssUtil.split(cssText);
6106
6107 for (var _key4 in removeStyles) {
6108 if (Object.prototype.hasOwnProperty.call(removeStyles, _key4)) {
6109 delete styles[_key4];
6110 }
6111 }
6112
6113 element.style.cssText = cssUtil.join(styles);
6114}
6115/**
6116 * Convert HSV <0, 1> into RGB color object.
6117 * https://gist.github.com/mjijackson/5311256
6118 *
6119 * @param h - Hue
6120 * @param s - Saturation
6121 * @param v - Value
6122 *
6123 * @returns RGB color object.
6124 */
6125
6126
6127function HSVToRGB(h, s, v) {
6128 var r;
6129 var g;
6130 var b;
6131 var i = Math.floor(h * 6);
6132 var f = h * 6 - i;
6133 var p = v * (1 - s);
6134 var q = v * (1 - f * s);
6135 var t = v * (1 - (1 - f) * s);
6136
6137 switch (i % 6) {
6138 case 0:
6139 r = v, g = t, b = p;
6140 break;
6141
6142 case 1:
6143 r = q, g = v, b = p;
6144 break;
6145
6146 case 2:
6147 r = p, g = v, b = t;
6148 break;
6149
6150 case 3:
6151 r = p, g = q, b = v;
6152 break;
6153
6154 case 4:
6155 r = t, g = p, b = v;
6156 break;
6157
6158 case 5:
6159 r = v, g = p, b = q;
6160 break;
6161 }
6162
6163 return {
6164 r: Math.floor(r * 255),
6165 g: Math.floor(g * 255),
6166 b: Math.floor(b * 255)
6167 };
6168}
6169/**
6170 * Convert HSV <0, 1> into hex color string.
6171 *
6172 * @param h - Hue
6173 * @param s - Saturation
6174 * @param v - Value
6175 *
6176 * @returns Hex color string.
6177 */
6178
6179
6180function HSVToHex(h, s, v) {
6181 var rgb = HSVToRGB(h, s, v);
6182 return RGBToHex(rgb.r, rgb.g, rgb.b);
6183}
6184/**
6185 * Convert hex color string into HSV <0, 1>.
6186 *
6187 * @param hex - Hex color string.
6188 *
6189 * @returns HSV color object.
6190 */
6191
6192
6193function hexToHSV(hex) {
6194 var rgb = hexToRGB(hex);
6195
6196 if (!rgb) {
6197 throw new TypeError("'".concat(hex, "' is not a valid color."));
6198 }
6199
6200 return RGBToHSV(rgb.r, rgb.g, rgb.b);
6201}
6202/**
6203 * Validate hex color string.
6204 *
6205 * @param hex - Unknown string that may contain a color.
6206 *
6207 * @returns True if the string is valid, false otherwise.
6208 */
6209
6210
6211function isValidHex(hex) {
6212 var isOk = /(^#[0-9A-F]{6}$)|(^#[0-9A-F]{3}$)/i.test(hex);
6213 return isOk;
6214}
6215/**
6216 * Validate RGB color string.
6217 *
6218 * @param rgb - Unknown string that may contain a color.
6219 *
6220 * @returns True if the string is valid, false otherwise.
6221 */
6222
6223
6224function isValidRGB(rgb) {
6225 rgb = rgb.replace(' ', '');
6226 var isOk = /rgb\((\d{1,3}),(\d{1,3}),(\d{1,3})\)/i.test(rgb);
6227 return isOk;
6228}
6229/**
6230 * Validate RGBA color string.
6231 *
6232 * @param rgba - Unknown string that may contain a color.
6233 *
6234 * @returns True if the string is valid, false otherwise.
6235 */
6236
6237
6238function isValidRGBA(rgba) {
6239 rgba = rgba.replace(' ', '');
6240 var isOk = /rgba\((\d{1,3}),(\d{1,3}),(\d{1,3}),(0?.{1,3})\)/i.test(rgba);
6241 return isOk;
6242}
6243/**
6244 * This recursively redirects the prototype of JSON objects to the referenceObject.
6245 * This is used for default options.
6246 *
6247 * @param fields - Names of properties to be bridged.
6248 * @param referenceObject - The original object.
6249 *
6250 * @returns A new object inheriting from the referenceObject.
6251 */
6252
6253
6254function selectiveBridgeObject(fields, referenceObject) {
6255 if (referenceObject !== null && _typeof_1(referenceObject) === 'object') {
6256 // !!! typeof null === 'object'
6257 var objectTo = Object.create(referenceObject);
6258
6259 for (var i = 0; i < fields.length; i++) {
6260 if (Object.prototype.hasOwnProperty.call(referenceObject, fields[i])) {
6261 if (_typeof_1(referenceObject[fields[i]]) == 'object') {
6262 objectTo[fields[i]] = bridgeObject(referenceObject[fields[i]]);
6263 }
6264 }
6265 }
6266
6267 return objectTo;
6268 } else {
6269 return null;
6270 }
6271}
6272/**
6273 * This recursively redirects the prototype of JSON objects to the referenceObject.
6274 * This is used for default options.
6275 *
6276 * @param referenceObject - The original object.
6277 *
6278 * @returns The Element if the referenceObject is an Element, or a new object inheriting from the referenceObject.
6279 */
6280
6281
6282function bridgeObject(referenceObject) {
6283 if (referenceObject === null || _typeof_1(referenceObject) !== 'object') {
6284 return null;
6285 }
6286
6287 if (referenceObject instanceof Element) {
6288 // Avoid bridging DOM objects
6289 return referenceObject;
6290 }
6291
6292 var objectTo = Object.create(referenceObject);
6293
6294 for (var i in referenceObject) {
6295 if (Object.prototype.hasOwnProperty.call(referenceObject, i)) {
6296 if (_typeof_1(referenceObject[i]) == 'object') {
6297 objectTo[i] = bridgeObject(referenceObject[i]);
6298 }
6299 }
6300 }
6301
6302 return objectTo;
6303}
6304/**
6305 * This method provides a stable sort implementation, very fast for presorted data.
6306 *
6307 * @param a - The array to be sorted (in-place).
6308 * @param compare - An order comparator.
6309 *
6310 * @returns The argument a.
6311 */
6312
6313
6314function insertSort(a, compare) {
6315 for (var i = 0; i < a.length; i++) {
6316 var k = a[i];
6317 var j = void 0;
6318
6319 for (j = i; j > 0 && compare(k, a[j - 1]) < 0; j--) {
6320 a[j] = a[j - 1];
6321 }
6322
6323 a[j] = k;
6324 }
6325
6326 return a;
6327}
6328/**
6329 * This is used to set the options of subobjects in the options object.
6330 *
6331 * A requirement of these subobjects is that they have an 'enabled' element
6332 * which is optional for the user but mandatory for the program.
6333 *
6334 * The added value here of the merge is that option 'enabled' is set as required.
6335 *
6336 * @param mergeTarget - Either this.options or the options used for the groups.
6337 * @param options - Options.
6338 * @param option - Option key in the options argument.
6339 * @param globalOptions - Global options, passed in to determine value of option 'enabled'.
6340 */
6341
6342
6343function mergeOptions(mergeTarget, options, option) {
6344 var globalOptions = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {}; // Local helpers
6345
6346 var isPresent = function isPresent(obj) {
6347 return obj !== null && obj !== undefined;
6348 };
6349
6350 var isObject = function isObject(obj) {
6351 return obj !== null && _typeof_1(obj) === 'object';
6352 }; // https://stackoverflow.com/a/34491287/1223531
6353
6354
6355 var isEmpty = function isEmpty(obj) {
6356 for (var x in obj) {
6357 if (Object.prototype.hasOwnProperty.call(obj, x)) {
6358 return false;
6359 }
6360 }
6361
6362 return true;
6363 }; // Guards
6364
6365
6366 if (!isObject(mergeTarget)) {
6367 throw new Error('Parameter mergeTarget must be an object');
6368 }
6369
6370 if (!isObject(options)) {
6371 throw new Error('Parameter options must be an object');
6372 }
6373
6374 if (!isPresent(option)) {
6375 throw new Error('Parameter option must have a value');
6376 }
6377
6378 if (!isObject(globalOptions)) {
6379 throw new Error('Parameter globalOptions must be an object');
6380 } //
6381 // Actual merge routine, separated from main logic
6382 // Only a single level of options is merged. Deeper levels are ref'd. This may actually be an issue.
6383 //
6384
6385
6386 var doMerge = function doMerge(target, options, option) {
6387 if (!isObject(target[option])) {
6388 target[option] = {};
6389 }
6390
6391 var src = options[option];
6392 var dst = target[option];
6393
6394 for (var prop in src) {
6395 if (Object.prototype.hasOwnProperty.call(src, prop)) {
6396 dst[prop] = src[prop];
6397 }
6398 }
6399 }; // Local initialization
6400
6401
6402 var srcOption = options[option];
6403 var globalPassed = isObject(globalOptions) && !isEmpty(globalOptions);
6404 var globalOption = globalPassed ? globalOptions[option] : undefined;
6405 var globalEnabled = globalOption ? globalOption.enabled : undefined; /////////////////////////////////////////
6406 // Main routine
6407 /////////////////////////////////////////
6408
6409 if (srcOption === undefined) {
6410 return; // Nothing to do
6411 }
6412
6413 if (typeof srcOption === 'boolean') {
6414 if (!isObject(mergeTarget[option])) {
6415 mergeTarget[option] = {};
6416 }
6417
6418 mergeTarget[option].enabled = srcOption;
6419 return;
6420 }
6421
6422 if (srcOption === null && !isObject(mergeTarget[option])) {
6423 // If possible, explicit copy from globals
6424 if (isPresent(globalOption)) {
6425 mergeTarget[option] = Object.create(globalOption);
6426 } else {
6427 return; // Nothing to do
6428 }
6429 }
6430
6431 if (!isObject(srcOption)) {
6432 return;
6433 } //
6434 // Ensure that 'enabled' is properly set. It is required internally
6435 // Note that the value from options will always overwrite the existing value
6436 //
6437
6438
6439 var enabled = true; // default value
6440
6441 if (srcOption.enabled !== undefined) {
6442 enabled = srcOption.enabled;
6443 } else {
6444 // Take from globals, if present
6445 if (globalEnabled !== undefined) {
6446 enabled = globalOption.enabled;
6447 }
6448 }
6449
6450 doMerge(mergeTarget, options, option);
6451 mergeTarget[option].enabled = enabled;
6452}
6453/**
6454 * This function does a binary search for a visible item in a sorted list. If we find a visible item, the code that uses
6455 * this function will then iterate in both directions over this sorted list to find all visible items.
6456 *
6457 * @param orderedItems - Items ordered by start
6458 * @param comparator - -1 is lower, 0 is equal, 1 is higher
6459 * @param field - Property name on an item (i.e. item[field]).
6460 * @param field2 - Second property name on an item (i.e. item[field][field2]).
6461 *
6462 * @returns Index of the found item or -1 if nothing was found.
6463 */
6464
6465
6466function binarySearchCustom(orderedItems, comparator, field, field2) {
6467 var maxIterations = 10000;
6468 var iteration = 0;
6469 var low = 0;
6470 var high = orderedItems.length - 1;
6471
6472 while (low <= high && iteration < maxIterations) {
6473 var middle = Math.floor((low + high) / 2);
6474 var item = orderedItems[middle];
6475
6476 var _value3 = field2 === undefined ? item[field] : item[field][field2];
6477
6478 var searchResult = comparator(_value3);
6479
6480 if (searchResult == 0) {
6481 // jihaa, found a visible item!
6482 return middle;
6483 } else if (searchResult == -1) {
6484 // it is too small --> increase low
6485 low = middle + 1;
6486 } else {
6487 // it is too big --> decrease high
6488 high = middle - 1;
6489 }
6490
6491 iteration++;
6492 }
6493
6494 return -1;
6495}
6496/**
6497 * This function does a binary search for a specific value in a sorted array. If it does not exist but is in between of
6498 * two values, we return either the one before or the one after, depending on user input
6499 * If it is found, we return the index, else -1.
6500 *
6501 * @param orderedItems - Sorted array.
6502 * @param target - The searched value.
6503 * @param field - Name of the property in items to be searched.
6504 * @param sidePreference - If the target is between two values, should the index of the before or the after be returned?
6505 * @param comparator - An optional comparator, returning -1, 0, 1 for <, ===, >.
6506 *
6507 * @returns The index of found value or -1 if nothing was found.
6508 */
6509
6510
6511function binarySearchValue(orderedItems, target, field, sidePreference, comparator) {
6512 var maxIterations = 10000;
6513 var iteration = 0;
6514 var low = 0;
6515 var high = orderedItems.length - 1;
6516 var prevValue;
6517 var value;
6518 var nextValue;
6519 var middle;
6520 comparator = comparator != undefined ? comparator : function (a, b) {
6521 return a == b ? 0 : a < b ? -1 : 1;
6522 };
6523
6524 while (low <= high && iteration < maxIterations) {
6525 // get a new guess
6526 middle = Math.floor(0.5 * (high + low));
6527 prevValue = orderedItems[Math.max(0, middle - 1)][field];
6528 value = orderedItems[middle][field];
6529 nextValue = orderedItems[Math.min(orderedItems.length - 1, middle + 1)][field];
6530
6531 if (comparator(value, target) == 0) {
6532 // we found the target
6533 return middle;
6534 } else if (comparator(prevValue, target) < 0 && comparator(value, target) > 0) {
6535 // target is in between of the previous and the current
6536 return sidePreference == 'before' ? Math.max(0, middle - 1) : middle;
6537 } else if (comparator(value, target) < 0 && comparator(nextValue, target) > 0) {
6538 // target is in between of the current and the next
6539 return sidePreference == 'before' ? middle : Math.min(orderedItems.length - 1, middle + 1);
6540 } else {
6541 // didnt find the target, we need to change our boundaries.
6542 if (comparator(value, target) < 0) {
6543 // it is too small --> increase low
6544 low = middle + 1;
6545 } else {
6546 // it is too big --> decrease high
6547 high = middle - 1;
6548 }
6549 }
6550
6551 iteration++;
6552 } // didnt find anything. Return -1.
6553
6554
6555 return -1;
6556}
6557/*
6558 * Easing Functions.
6559 * Only considering the t value for the range [0, 1] => [0, 1].
6560 *
6561 * Inspiration: from http://gizma.com/easing/
6562 * https://gist.github.com/gre/1650294
6563 */
6564
6565
6566var easingFunctions = {
6567 /**
6568 * no easing, no acceleration
6569 *
6570 * @param t - Time.
6571 *
6572 * @returns Value at time t.
6573 */
6574 linear: function linear(t) {
6575 return t;
6576 },
6577
6578 /**
6579 * accelerating from zero velocity
6580 *
6581 * @param t - Time.
6582 *
6583 * @returns Value at time t.
6584 */
6585 easeInQuad: function easeInQuad(t) {
6586 return t * t;
6587 },
6588
6589 /**
6590 * decelerating to zero velocity
6591 *
6592 * @param t - Time.
6593 *
6594 * @returns Value at time t.
6595 */
6596 easeOutQuad: function easeOutQuad(t) {
6597 return t * (2 - t);
6598 },
6599
6600 /**
6601 * acceleration until halfway, then deceleration
6602 *
6603 * @param t - Time.
6604 *
6605 * @returns Value at time t.
6606 */
6607 easeInOutQuad: function easeInOutQuad(t) {
6608 return t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t;
6609 },
6610
6611 /**
6612 * accelerating from zero velocity
6613 *
6614 * @param t - Time.
6615 *
6616 * @returns Value at time t.
6617 */
6618 easeInCubic: function easeInCubic(t) {
6619 return t * t * t;
6620 },
6621
6622 /**
6623 * decelerating to zero velocity
6624 *
6625 * @param t - Time.
6626 *
6627 * @returns Value at time t.
6628 */
6629 easeOutCubic: function easeOutCubic(t) {
6630 return --t * t * t + 1;
6631 },
6632
6633 /**
6634 * acceleration until halfway, then deceleration
6635 *
6636 * @param t - Time.
6637 *
6638 * @returns Value at time t.
6639 */
6640 easeInOutCubic: function easeInOutCubic(t) {
6641 return t < 0.5 ? 4 * t * t * t : (t - 1) * (2 * t - 2) * (2 * t - 2) + 1;
6642 },
6643
6644 /**
6645 * accelerating from zero velocity
6646 *
6647 * @param t - Time.
6648 *
6649 * @returns Value at time t.
6650 */
6651 easeInQuart: function easeInQuart(t) {
6652 return t * t * t * t;
6653 },
6654
6655 /**
6656 * decelerating to zero velocity
6657 *
6658 * @param t - Time.
6659 *
6660 * @returns Value at time t.
6661 */
6662 easeOutQuart: function easeOutQuart(t) {
6663 return 1 - --t * t * t * t;
6664 },
6665
6666 /**
6667 * acceleration until halfway, then deceleration
6668 *
6669 * @param t - Time.
6670 *
6671 * @returns Value at time t.
6672 */
6673 easeInOutQuart: function easeInOutQuart(t) {
6674 return t < 0.5 ? 8 * t * t * t * t : 1 - 8 * --t * t * t * t;
6675 },
6676
6677 /**
6678 * accelerating from zero velocity
6679 *
6680 * @param t - Time.
6681 *
6682 * @returns Value at time t.
6683 */
6684 easeInQuint: function easeInQuint(t) {
6685 return t * t * t * t * t;
6686 },
6687
6688 /**
6689 * decelerating to zero velocity
6690 *
6691 * @param t - Time.
6692 *
6693 * @returns Value at time t.
6694 */
6695 easeOutQuint: function easeOutQuint(t) {
6696 return 1 + --t * t * t * t * t;
6697 },
6698
6699 /**
6700 * acceleration until halfway, then deceleration
6701 *
6702 * @param t - Time.
6703 *
6704 * @returns Value at time t.
6705 */
6706 easeInOutQuint: function easeInOutQuint(t) {
6707 return t < 0.5 ? 16 * t * t * t * t * t : 1 + 16 * --t * t * t * t * t;
6708 }
6709};
6710/**
6711 * Experimentaly compute the width of the scrollbar for this browser.
6712 *
6713 * @returns The width in pixels.
6714 */
6715
6716function getScrollBarWidth() {
6717 var inner = document.createElement('p');
6718 inner.style.width = '100%';
6719 inner.style.height = '200px';
6720 var outer = document.createElement('div');
6721 outer.style.position = 'absolute';
6722 outer.style.top = '0px';
6723 outer.style.left = '0px';
6724 outer.style.visibility = 'hidden';
6725 outer.style.width = '200px';
6726 outer.style.height = '150px';
6727 outer.style.overflow = 'hidden';
6728 outer.appendChild(inner);
6729 document.body.appendChild(outer);
6730 var w1 = inner.offsetWidth;
6731 outer.style.overflow = 'scroll';
6732 var w2 = inner.offsetWidth;
6733
6734 if (w1 == w2) {
6735 w2 = outer.clientWidth;
6736 }
6737
6738 document.body.removeChild(outer);
6739 return w1 - w2;
6740} // @TODO: This doesn't work properly.
6741// It works only for single property objects,
6742// otherwise it combines all of the types in a union.
6743// export function topMost<K1 extends string, V1> (
6744// pile: Record<K1, undefined | V1>[],
6745// accessors: K1 | [K1]
6746// ): undefined | V1
6747// export function topMost<K1 extends string, K2 extends string, V1, V2> (
6748// pile: Record<K1, undefined | V1 | Record<K2, undefined | V2>>[],
6749// accessors: [K1, K2]
6750// ): undefined | V1 | V2
6751// export function topMost<K1 extends string, K2 extends string, K3 extends string, V1, V2, V3> (
6752// pile: Record<K1, undefined | V1 | Record<K2, undefined | V2 | Record<K3, undefined | V3>>>[],
6753// accessors: [K1, K2, K3]
6754// ): undefined | V1 | V2 | V3
6755
6756/**
6757 * Get the top most property value from a pile of objects.
6758 *
6759 * @param pile - Array of objects, no required format.
6760 * @param accessors - Array of property names (e.g. object['foo']['bar'] → ['foo', 'bar']).
6761 *
6762 * @returns Value of the property with given accessors path from the first pile item where it's not undefined.
6763 */
6764
6765
6766function topMost(pile, accessors) {
6767 var candidate;
6768
6769 if (!Array.isArray(accessors)) {
6770 accessors = [accessors];
6771 }
6772
6773 var _iteratorNormalCompletion = true;
6774 var _didIteratorError = false;
6775 var _iteratorError = undefined;
6776
6777 try {
6778 for (var _iterator = pile[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
6779 var member = _step.value;
6780
6781 if (member) {
6782 candidate = member[accessors[0]];
6783
6784 for (var i = 1; i < accessors.length; i++) {
6785 if (candidate) {
6786 candidate = candidate[accessors[i]];
6787 }
6788 }
6789
6790 if (typeof candidate !== 'undefined') {
6791 break;
6792 }
6793 }
6794 }
6795 } catch (err) {
6796 _didIteratorError = true;
6797 _iteratorError = err;
6798 } finally {
6799 try {
6800 if (!_iteratorNormalCompletion && _iterator.return != null) {
6801 _iterator.return();
6802 }
6803 } finally {
6804 if (_didIteratorError) {
6805 throw _iteratorError;
6806 }
6807 }
6808 }
6809
6810 return candidate;
6811}
6812
6813var util =
6814/*#__PURE__*/
6815Object.freeze({
6816 isNumber: isNumber,
6817 recursiveDOMDelete: recursiveDOMDelete,
6818 isString: isString,
6819 isObject: isObject,
6820 isDate: isDate,
6821 isMoment: isMoment,
6822 fillIfDefined: fillIfDefined,
6823 extend: extend,
6824 selectiveExtend: selectiveExtend,
6825 selectiveDeepExtend: selectiveDeepExtend,
6826 selectiveNotDeepExtend: selectiveNotDeepExtend,
6827 deepExtend: deepExtend,
6828 equalArray: equalArray,
6829 convert: convert,
6830 getType: getType,
6831 copyAndExtendArray: copyAndExtendArray,
6832 copyArray: copyArray,
6833 getAbsoluteLeft: getAbsoluteLeft,
6834 getAbsoluteRight: getAbsoluteRight,
6835 getAbsoluteTop: getAbsoluteTop,
6836 addClassName: addClassName,
6837 removeClassName: removeClassName,
6838 forEach: forEach,
6839 toArray: toArray,
6840 updateProperty: updateProperty,
6841 throttle: throttle,
6842 addEventListener: addEventListener,
6843 removeEventListener: removeEventListener,
6844 preventDefault: preventDefault,
6845 getTarget: getTarget,
6846 hasParent: hasParent,
6847 option: option,
6848 hexToRGB: hexToRGB,
6849 overrideOpacity: overrideOpacity,
6850 RGBToHex: RGBToHex,
6851 parseColor: parseColor,
6852 RGBToHSV: RGBToHSV,
6853 addCssText: addCssText,
6854 removeCssText: removeCssText,
6855 HSVToRGB: HSVToRGB,
6856 HSVToHex: HSVToHex,
6857 hexToHSV: hexToHSV,
6858 isValidHex: isValidHex,
6859 isValidRGB: isValidRGB,
6860 isValidRGBA: isValidRGBA,
6861 selectiveBridgeObject: selectiveBridgeObject,
6862 bridgeObject: bridgeObject,
6863 insertSort: insertSort,
6864 mergeOptions: mergeOptions,
6865 binarySearchCustom: binarySearchCustom,
6866 binarySearchValue: binarySearchValue,
6867 easingFunctions: easingFunctions,
6868 getScrollBarWidth: getScrollBarWidth,
6869 topMost: topMost,
6870 randomUUID: uuid4
6871}); // New API (tree shakeable).
6872
6873var commonjsGlobal$1 = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
6874
6875function commonjsRequire$1 () {
6876 throw new Error('Dynamic requires are not currently supported by rollup-plugin-commonjs');
6877}
6878
6879function createCommonjsModule$1(fn, module) {
6880 return module = { exports: {} }, fn(module, module.exports), module.exports;
6881}
6882
6883var DOMutil = createCommonjsModule$1(function (module, exports) {
6884 // DOM utility methods
6885
6886 /**
6887 * this prepares the JSON container for allocating SVG elements
6888 * @param {Object} JSONcontainer
6889 * @private
6890 */
6891 exports.prepareElements = function (JSONcontainer) {
6892 // cleanup the redundant svgElements;
6893 for (var elementType in JSONcontainer) {
6894 if (JSONcontainer.hasOwnProperty(elementType)) {
6895 JSONcontainer[elementType].redundant = JSONcontainer[elementType].used;
6896 JSONcontainer[elementType].used = [];
6897 }
6898 }
6899 };
6900 /**
6901 * this cleans up all the unused SVG elements. By asking for the parentNode, we only need to supply the JSON container from
6902 * which to remove the redundant elements.
6903 *
6904 * @param {Object} JSONcontainer
6905 * @private
6906 */
6907
6908
6909 exports.cleanupElements = function (JSONcontainer) {
6910 // cleanup the redundant svgElements;
6911 for (var elementType in JSONcontainer) {
6912 if (JSONcontainer.hasOwnProperty(elementType)) {
6913 if (JSONcontainer[elementType].redundant) {
6914 for (var i = 0; i < JSONcontainer[elementType].redundant.length; i++) {
6915 JSONcontainer[elementType].redundant[i].parentNode.removeChild(JSONcontainer[elementType].redundant[i]);
6916 }
6917
6918 JSONcontainer[elementType].redundant = [];
6919 }
6920 }
6921 }
6922 };
6923 /**
6924 * Ensures that all elements are removed first up so they can be recreated cleanly
6925 * @param {Object} JSONcontainer
6926 */
6927
6928
6929 exports.resetElements = function (JSONcontainer) {
6930 exports.prepareElements(JSONcontainer);
6931 exports.cleanupElements(JSONcontainer);
6932 exports.prepareElements(JSONcontainer);
6933 };
6934 /**
6935 * Allocate or generate an SVG element if needed. Store a reference to it in the JSON container and draw it in the svgContainer
6936 * the JSON container and the SVG container have to be supplied so other svg containers (like the legend) can use this.
6937 *
6938 * @param {string} elementType
6939 * @param {Object} JSONcontainer
6940 * @param {Object} svgContainer
6941 * @returns {Element}
6942 * @private
6943 */
6944
6945
6946 exports.getSVGElement = function (elementType, JSONcontainer, svgContainer) {
6947 var element; // allocate SVG element, if it doesnt yet exist, create one.
6948
6949 if (JSONcontainer.hasOwnProperty(elementType)) {
6950 // this element has been created before
6951 // check if there is an redundant element
6952 if (JSONcontainer[elementType].redundant.length > 0) {
6953 element = JSONcontainer[elementType].redundant[0];
6954 JSONcontainer[elementType].redundant.shift();
6955 } else {
6956 // create a new element and add it to the SVG
6957 element = document.createElementNS('http://www.w3.org/2000/svg', elementType);
6958 svgContainer.appendChild(element);
6959 }
6960 } else {
6961 // create a new element and add it to the SVG, also create a new object in the svgElements to keep track of it.
6962 element = document.createElementNS('http://www.w3.org/2000/svg', elementType);
6963 JSONcontainer[elementType] = {
6964 used: [],
6965 redundant: []
6966 };
6967 svgContainer.appendChild(element);
6968 }
6969
6970 JSONcontainer[elementType].used.push(element);
6971 return element;
6972 };
6973 /**
6974 * Allocate or generate an SVG element if needed. Store a reference to it in the JSON container and draw it in the svgContainer
6975 * the JSON container and the SVG container have to be supplied so other svg containers (like the legend) can use this.
6976 *
6977 * @param {string} elementType
6978 * @param {Object} JSONcontainer
6979 * @param {Element} DOMContainer
6980 * @param {Element} insertBefore
6981 * @returns {*}
6982 */
6983
6984
6985 exports.getDOMElement = function (elementType, JSONcontainer, DOMContainer, insertBefore) {
6986 var element; // allocate DOM element, if it doesnt yet exist, create one.
6987
6988 if (JSONcontainer.hasOwnProperty(elementType)) {
6989 // this element has been created before
6990 // check if there is an redundant element
6991 if (JSONcontainer[elementType].redundant.length > 0) {
6992 element = JSONcontainer[elementType].redundant[0];
6993 JSONcontainer[elementType].redundant.shift();
6994 } else {
6995 // create a new element and add it to the SVG
6996 element = document.createElement(elementType);
6997
6998 if (insertBefore !== undefined) {
6999 DOMContainer.insertBefore(element, insertBefore);
7000 } else {
7001 DOMContainer.appendChild(element);
7002 }
7003 }
7004 } else {
7005 // create a new element and add it to the SVG, also create a new object in the svgElements to keep track of it.
7006 element = document.createElement(elementType);
7007 JSONcontainer[elementType] = {
7008 used: [],
7009 redundant: []
7010 };
7011
7012 if (insertBefore !== undefined) {
7013 DOMContainer.insertBefore(element, insertBefore);
7014 } else {
7015 DOMContainer.appendChild(element);
7016 }
7017 }
7018
7019 JSONcontainer[elementType].used.push(element);
7020 return element;
7021 };
7022 /**
7023 * Draw a point object. This is a separate function because it can also be called by the legend.
7024 * The reason the JSONcontainer and the target SVG svgContainer have to be supplied is so the legend can use these functions
7025 * as well.
7026 *
7027 * @param {number} x
7028 * @param {number} y
7029 * @param {Object} groupTemplate: A template containing the necessary information to draw the datapoint e.g., {style: 'circle', size: 5, className: 'className' }
7030 * @param {Object} JSONcontainer
7031 * @param {Object} svgContainer
7032 * @param {Object} labelObj
7033 * @returns {vis.PointItem}
7034 */
7035
7036
7037 exports.drawPoint = function (x, y, groupTemplate, JSONcontainer, svgContainer, labelObj) {
7038 var point;
7039
7040 if (groupTemplate.style == 'circle') {
7041 point = exports.getSVGElement('circle', JSONcontainer, svgContainer);
7042 point.setAttributeNS(null, "cx", x);
7043 point.setAttributeNS(null, "cy", y);
7044 point.setAttributeNS(null, "r", 0.5 * groupTemplate.size);
7045 } else {
7046 point = exports.getSVGElement('rect', JSONcontainer, svgContainer);
7047 point.setAttributeNS(null, "x", x - 0.5 * groupTemplate.size);
7048 point.setAttributeNS(null, "y", y - 0.5 * groupTemplate.size);
7049 point.setAttributeNS(null, "width", groupTemplate.size);
7050 point.setAttributeNS(null, "height", groupTemplate.size);
7051 }
7052
7053 if (groupTemplate.styles !== undefined) {
7054 point.setAttributeNS(null, "style", groupTemplate.styles);
7055 }
7056
7057 point.setAttributeNS(null, "class", groupTemplate.className + " vis-point"); //handle label
7058
7059 if (labelObj) {
7060 var label = exports.getSVGElement('text', JSONcontainer, svgContainer);
7061
7062 if (labelObj.xOffset) {
7063 x = x + labelObj.xOffset;
7064 }
7065
7066 if (labelObj.yOffset) {
7067 y = y + labelObj.yOffset;
7068 }
7069
7070 if (labelObj.content) {
7071 label.textContent = labelObj.content;
7072 }
7073
7074 if (labelObj.className) {
7075 label.setAttributeNS(null, "class", labelObj.className + " vis-label");
7076 }
7077
7078 label.setAttributeNS(null, "x", x);
7079 label.setAttributeNS(null, "y", y);
7080 }
7081
7082 return point;
7083 };
7084 /**
7085 * draw a bar SVG element centered on the X coordinate
7086 *
7087 * @param {number} x
7088 * @param {number} y
7089 * @param {number} width
7090 * @param {number} height
7091 * @param {string} className
7092 * @param {Object} JSONcontainer
7093 * @param {Object} svgContainer
7094 * @param {string} style
7095 */
7096
7097
7098 exports.drawBar = function (x, y, width, height, className, JSONcontainer, svgContainer, style) {
7099 if (height != 0) {
7100 if (height < 0) {
7101 height *= -1;
7102 y -= height;
7103 }
7104
7105 var rect = exports.getSVGElement('rect', JSONcontainer, svgContainer);
7106 rect.setAttributeNS(null, "x", x - 0.5 * width);
7107 rect.setAttributeNS(null, "y", y);
7108 rect.setAttributeNS(null, "width", width);
7109 rect.setAttributeNS(null, "height", height);
7110 rect.setAttributeNS(null, "class", className);
7111
7112 if (style) {
7113 rect.setAttributeNS(null, "style", style);
7114 }
7115 }
7116 };
7117});
7118var DOMutil_1 = DOMutil.prepareElements;
7119var DOMutil_2 = DOMutil.cleanupElements;
7120var DOMutil_3 = DOMutil.resetElements;
7121var DOMutil_4 = DOMutil.getSVGElement;
7122var DOMutil_5 = DOMutil.getDOMElement;
7123var DOMutil_6 = DOMutil.drawPoint;
7124var DOMutil_7 = DOMutil.drawBar;
7125
7126/**
7127 * vis-data - data
7128 * http://visjs.org/
7129 *
7130 * Manage unstructured data using DataSet. Add, update, and remove data, and listen for changes in the data.
7131 *
7132 * @version 6.1.0
7133 * @date 2019-07-16T13:37:00Z
7134 *
7135 * @copyright (c) 2011-2017 Almende B.V, http://almende.com
7136 * @copyright (c) 2018-2019 visjs contributors, https://github.com/visjs
7137 *
7138 * @license
7139 * vis.js is dual licensed under both
7140 *
7141 * 1. The Apache 2.0 License
7142 * http://www.apache.org/licenses/LICENSE-2.0
7143 *
7144 * and
7145 *
7146 * 2. The MIT License
7147 * http://opensource.org/licenses/MIT
7148 *
7149 * vis.js may be distributed under either license.
7150 */
7151function _defineProperty$1(obj, key, value) {
7152 if (key in obj) {
7153 Object.defineProperty(obj, key, {
7154 value: value,
7155 enumerable: true,
7156 configurable: true,
7157 writable: true
7158 });
7159 } else {
7160 obj[key] = value;
7161 }
7162
7163 return obj;
7164}
7165
7166var defineProperty$1 = _defineProperty$1;
7167
7168function createCommonjsModule$2(fn, module) {
7169 return module = {
7170 exports: {}
7171 }, fn(module, module.exports), module.exports;
7172}
7173
7174var _typeof_1$1 = createCommonjsModule$2(function (module) {
7175 function _typeof2(obj) {
7176 if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") {
7177 _typeof2 = function _typeof2(obj) {
7178 return typeof obj;
7179 };
7180 } else {
7181 _typeof2 = function _typeof2(obj) {
7182 return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
7183 };
7184 }
7185
7186 return _typeof2(obj);
7187 }
7188
7189 function _typeof(obj) {
7190 if (typeof Symbol === "function" && _typeof2(Symbol.iterator) === "symbol") {
7191 module.exports = _typeof = function _typeof(obj) {
7192 return _typeof2(obj);
7193 };
7194 } else {
7195 module.exports = _typeof = function _typeof(obj) {
7196 return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : _typeof2(obj);
7197 };
7198 }
7199
7200 return _typeof(obj);
7201 }
7202
7203 module.exports = _typeof;
7204});
7205
7206function _classCallCheck(instance, Constructor) {
7207 if (!(instance instanceof Constructor)) {
7208 throw new TypeError("Cannot call a class as a function");
7209 }
7210}
7211
7212var classCallCheck = _classCallCheck;
7213
7214function _defineProperties(target, props) {
7215 for (var i = 0; i < props.length; i++) {
7216 var descriptor = props[i];
7217 descriptor.enumerable = descriptor.enumerable || false;
7218 descriptor.configurable = true;
7219 if ("value" in descriptor) descriptor.writable = true;
7220 Object.defineProperty(target, descriptor.key, descriptor);
7221 }
7222}
7223
7224function _createClass(Constructor, protoProps, staticProps) {
7225 if (protoProps) _defineProperties(Constructor.prototype, protoProps);
7226 if (staticProps) _defineProperties(Constructor, staticProps);
7227 return Constructor;
7228}
7229
7230var createClass = _createClass;
7231
7232function _assertThisInitialized(self) {
7233 if (self === void 0) {
7234 throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
7235 }
7236
7237 return self;
7238}
7239
7240var assertThisInitialized = _assertThisInitialized;
7241
7242function _possibleConstructorReturn(self, call) {
7243 if (call && (_typeof_1$1(call) === "object" || typeof call === "function")) {
7244 return call;
7245 }
7246
7247 return assertThisInitialized(self);
7248}
7249
7250var possibleConstructorReturn = _possibleConstructorReturn;
7251var getPrototypeOf = createCommonjsModule$2(function (module) {
7252 function _getPrototypeOf(o) {
7253 module.exports = _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) {
7254 return o.__proto__ || Object.getPrototypeOf(o);
7255 };
7256 return _getPrototypeOf(o);
7257 }
7258
7259 module.exports = _getPrototypeOf;
7260});
7261var setPrototypeOf = createCommonjsModule$2(function (module) {
7262 function _setPrototypeOf(o, p) {
7263 module.exports = _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) {
7264 o.__proto__ = p;
7265 return o;
7266 };
7267
7268 return _setPrototypeOf(o, p);
7269 }
7270
7271 module.exports = _setPrototypeOf;
7272});
7273
7274function _inherits(subClass, superClass) {
7275 if (typeof superClass !== "function" && superClass !== null) {
7276 throw new TypeError("Super expression must either be null or a function");
7277 }
7278
7279 subClass.prototype = Object.create(superClass && superClass.prototype, {
7280 constructor: {
7281 value: subClass,
7282 writable: true,
7283 configurable: true
7284 }
7285 });
7286 if (superClass) setPrototypeOf(subClass, superClass);
7287}
7288
7289var inherits = _inherits; // Maps for number <-> hex string conversion
7290
7291var byteToHex$2 = [];
7292
7293for (var i$2 = 0; i$2 < 256; i$2++) {
7294 byteToHex$2[i$2] = (i$2 + 0x100).toString(16).substr(1);
7295}
7296/**
7297 * Represent binary UUID into it's string representation.
7298 *
7299 * @param buf - Buffer containing UUID bytes.
7300 * @param offset - Offset from the start of the buffer where the UUID is saved (not needed if the buffer starts with the UUID).
7301 *
7302 * @returns String representation of the UUID.
7303 */
7304
7305
7306function stringifyUUID$1(buf, offset) {
7307 var i = offset || 0;
7308 var bth = byteToHex$2;
7309 return bth[buf[i++]] + bth[buf[i++]] + bth[buf[i++]] + bth[buf[i++]] + '-' + bth[buf[i++]] + bth[buf[i++]] + '-' + bth[buf[i++]] + bth[buf[i++]] + '-' + bth[buf[i++]] + bth[buf[i++]] + '-' + bth[buf[i++]] + bth[buf[i++]] + bth[buf[i++]] + bth[buf[i++]] + bth[buf[i++]] + bth[buf[i++]];
7310}
7311/**
7312 * Generate 16 random bytes to be used as a base for UUID.
7313 *
7314 * @ignore
7315 */
7316
7317
7318var random$1 = function () {
7319 if (typeof crypto !== 'undefined' && crypto.getRandomValues) {
7320 // WHATWG crypto-based RNG - http://wiki.whatwg.org/wiki/Crypto
7321 // Moderately fast, high quality
7322 var _rnds8 = new Uint8Array(16);
7323
7324 return function whatwgRNG() {
7325 crypto.getRandomValues(_rnds8);
7326 return _rnds8;
7327 };
7328 } // Math.random()-based (RNG)
7329 //
7330 // If all else fails, use Math.random().
7331 // It's fast, but is of unspecified quality.
7332
7333
7334 var _rnds = new Array(16);
7335
7336 return function () {
7337 for (var i = 0, r; i < 16; i++) {
7338 if ((i & 0x03) === 0) {
7339 r = Math.random() * 0x100000000;
7340 }
7341
7342 _rnds[i] = r >>> ((i & 0x03) << 3) & 0xff;
7343 }
7344
7345 return _rnds;
7346 }; // uuid.js
7347 //
7348 // Copyright (c) 2010-2012 Robert Kieffer
7349 // MIT License - http://opensource.org/licenses/mit-license.php
7350 // Unique ID creation requires a high quality random # generator. We feature
7351 // detect to determine the best RNG source, normalizing to a function that
7352 // returns 128-bits of randomness, since that's what's usually required
7353 // return require('./rng');
7354}();
7355
7356var byteToHex$1$1 = [];
7357
7358for (var i$1$1 = 0; i$1$1 < 256; i$1$1++) {
7359 byteToHex$1$1[i$1$1] = (i$1$1 + 0x100).toString(16).substr(1);
7360} // **`v1()` - Generate time-based UUID**
7361//
7362// Inspired by https://github.com/LiosK/UUID.js
7363// and http://docs.python.org/library/uuid.html
7364// random #'s we need to init node and clockseq
7365
7366
7367var seedBytes$1 = random$1(); // Per 4.5, create and 48-bit node id, (47 random bits + multicast bit = 1)
7368
7369var defaultNodeId$1 = [seedBytes$1[0] | 0x01, seedBytes$1[1], seedBytes$1[2], seedBytes$1[3], seedBytes$1[4], seedBytes$1[5]]; // Per 4.2.2, randomize (14 bit) clockseq
7370
7371var defaultClockseq$1 = (seedBytes$1[6] << 8 | seedBytes$1[7]) & 0x3fff; // Previous uuid creation time
7372
7373/**
7374 * UUIDv4 options.
7375 */
7376
7377/**
7378 * Generate UUIDv4
7379 *
7380 * @param options - Options to be used instead of default generated values.
7381 * String 'binary' is a shorthand for uuid4({}, new Array(16)).
7382 * @param buf - If present the buffer will be filled with the generated UUID.
7383 * @param offset - Offset of the UUID from the start of the buffer.
7384 *
7385 * @returns UUIDv4
7386 */
7387
7388function uuid4$1() {
7389 var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
7390 var buf = arguments.length > 1 ? arguments[1] : undefined;
7391 var offset = arguments.length > 2 ? arguments[2] : undefined; // Deprecated - 'format' argument, as supported in v1.2
7392
7393 var i = buf && offset || 0;
7394
7395 if (typeof options === 'string') {
7396 buf = options === 'binary' ? new Array(16) : undefined;
7397 options = {};
7398 }
7399
7400 var rnds = options.random || (options.rng || random$1)(); // Per 4.4, set bits for version and `clock_seq_hi_and_reserved`
7401
7402 rnds[6] = rnds[6] & 0x0f | 0x40;
7403 rnds[8] = rnds[8] & 0x3f | 0x80; // Copy bytes to buffer, if provided
7404
7405 if (buf) {
7406 for (var ii = 0; ii < 16; ii++) {
7407 buf[i + ii] = rnds[ii];
7408 }
7409 }
7410
7411 return buf || stringifyUUID$1(rnds);
7412} // Rollup will complain about mixing default and named exports in UMD build,
7413
7414
7415function _typeof(obj) {
7416 if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") {
7417 _typeof = function (obj) {
7418 return typeof obj;
7419 };
7420 } else {
7421 _typeof = function (obj) {
7422 return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
7423 };
7424 }
7425
7426 return _typeof(obj);
7427}
7428
7429var commonjsGlobal$2 = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
7430
7431function commonjsRequire$2() {
7432 throw new Error('Dynamic requires are not currently supported by rollup-plugin-commonjs');
7433}
7434
7435function createCommonjsModule$1$1(fn, module) {
7436 return module = {
7437 exports: {}
7438 }, fn(module, module.exports), module.exports;
7439}
7440
7441var moment$1 = createCommonjsModule$1$1(function (module, exports) {
7442 (function (global, factory) {
7443 module.exports = factory();
7444 })(commonjsGlobal$2, function () {
7445 var hookCallback;
7446
7447 function hooks() {
7448 return hookCallback.apply(null, arguments);
7449 } // This is done to register the method called with moment()
7450 // without creating circular dependencies.
7451
7452
7453 function setHookCallback(callback) {
7454 hookCallback = callback;
7455 }
7456
7457 function isArray(input) {
7458 return input instanceof Array || Object.prototype.toString.call(input) === '[object Array]';
7459 }
7460
7461 function isObject(input) {
7462 // IE8 will treat undefined and null as object if it wasn't for
7463 // input != null
7464 return input != null && Object.prototype.toString.call(input) === '[object Object]';
7465 }
7466
7467 function isObjectEmpty(obj) {
7468 if (Object.getOwnPropertyNames) {
7469 return Object.getOwnPropertyNames(obj).length === 0;
7470 } else {
7471 var k;
7472
7473 for (k in obj) {
7474 if (obj.hasOwnProperty(k)) {
7475 return false;
7476 }
7477 }
7478
7479 return true;
7480 }
7481 }
7482
7483 function isUndefined(input) {
7484 return input === void 0;
7485 }
7486
7487 function isNumber(input) {
7488 return typeof input === 'number' || Object.prototype.toString.call(input) === '[object Number]';
7489 }
7490
7491 function isDate(input) {
7492 return input instanceof Date || Object.prototype.toString.call(input) === '[object Date]';
7493 }
7494
7495 function map(arr, fn) {
7496 var res = [],
7497 i;
7498
7499 for (i = 0; i < arr.length; ++i) {
7500 res.push(fn(arr[i], i));
7501 }
7502
7503 return res;
7504 }
7505
7506 function hasOwnProp(a, b) {
7507 return Object.prototype.hasOwnProperty.call(a, b);
7508 }
7509
7510 function extend(a, b) {
7511 for (var i in b) {
7512 if (hasOwnProp(b, i)) {
7513 a[i] = b[i];
7514 }
7515 }
7516
7517 if (hasOwnProp(b, 'toString')) {
7518 a.toString = b.toString;
7519 }
7520
7521 if (hasOwnProp(b, 'valueOf')) {
7522 a.valueOf = b.valueOf;
7523 }
7524
7525 return a;
7526 }
7527
7528 function createUTC(input, format, locale, strict) {
7529 return createLocalOrUTC(input, format, locale, strict, true).utc();
7530 }
7531
7532 function defaultParsingFlags() {
7533 // We need to deep clone this object.
7534 return {
7535 empty: false,
7536 unusedTokens: [],
7537 unusedInput: [],
7538 overflow: -2,
7539 charsLeftOver: 0,
7540 nullInput: false,
7541 invalidMonth: null,
7542 invalidFormat: false,
7543 userInvalidated: false,
7544 iso: false,
7545 parsedDateParts: [],
7546 meridiem: null,
7547 rfc2822: false,
7548 weekdayMismatch: false
7549 };
7550 }
7551
7552 function getParsingFlags(m) {
7553 if (m._pf == null) {
7554 m._pf = defaultParsingFlags();
7555 }
7556
7557 return m._pf;
7558 }
7559
7560 var some;
7561
7562 if (Array.prototype.some) {
7563 some = Array.prototype.some;
7564 } else {
7565 some = function (fun) {
7566 var t = Object(this);
7567 var len = t.length >>> 0;
7568
7569 for (var i = 0; i < len; i++) {
7570 if (i in t && fun.call(this, t[i], i, t)) {
7571 return true;
7572 }
7573 }
7574
7575 return false;
7576 };
7577 }
7578
7579 function isValid(m) {
7580 if (m._isValid == null) {
7581 var flags = getParsingFlags(m);
7582 var parsedParts = some.call(flags.parsedDateParts, function (i) {
7583 return i != null;
7584 });
7585 var isNowValid = !isNaN(m._d.getTime()) && flags.overflow < 0 && !flags.empty && !flags.invalidMonth && !flags.invalidWeekday && !flags.weekdayMismatch && !flags.nullInput && !flags.invalidFormat && !flags.userInvalidated && (!flags.meridiem || flags.meridiem && parsedParts);
7586
7587 if (m._strict) {
7588 isNowValid = isNowValid && flags.charsLeftOver === 0 && flags.unusedTokens.length === 0 && flags.bigHour === undefined;
7589 }
7590
7591 if (Object.isFrozen == null || !Object.isFrozen(m)) {
7592 m._isValid = isNowValid;
7593 } else {
7594 return isNowValid;
7595 }
7596 }
7597
7598 return m._isValid;
7599 }
7600
7601 function createInvalid(flags) {
7602 var m = createUTC(NaN);
7603
7604 if (flags != null) {
7605 extend(getParsingFlags(m), flags);
7606 } else {
7607 getParsingFlags(m).userInvalidated = true;
7608 }
7609
7610 return m;
7611 } // Plugins that add properties should also add the key here (null value),
7612 // so we can properly clone ourselves.
7613
7614
7615 var momentProperties = hooks.momentProperties = [];
7616
7617 function copyConfig(to, from) {
7618 var i, prop, val;
7619
7620 if (!isUndefined(from._isAMomentObject)) {
7621 to._isAMomentObject = from._isAMomentObject;
7622 }
7623
7624 if (!isUndefined(from._i)) {
7625 to._i = from._i;
7626 }
7627
7628 if (!isUndefined(from._f)) {
7629 to._f = from._f;
7630 }
7631
7632 if (!isUndefined(from._l)) {
7633 to._l = from._l;
7634 }
7635
7636 if (!isUndefined(from._strict)) {
7637 to._strict = from._strict;
7638 }
7639
7640 if (!isUndefined(from._tzm)) {
7641 to._tzm = from._tzm;
7642 }
7643
7644 if (!isUndefined(from._isUTC)) {
7645 to._isUTC = from._isUTC;
7646 }
7647
7648 if (!isUndefined(from._offset)) {
7649 to._offset = from._offset;
7650 }
7651
7652 if (!isUndefined(from._pf)) {
7653 to._pf = getParsingFlags(from);
7654 }
7655
7656 if (!isUndefined(from._locale)) {
7657 to._locale = from._locale;
7658 }
7659
7660 if (momentProperties.length > 0) {
7661 for (i = 0; i < momentProperties.length; i++) {
7662 prop = momentProperties[i];
7663 val = from[prop];
7664
7665 if (!isUndefined(val)) {
7666 to[prop] = val;
7667 }
7668 }
7669 }
7670
7671 return to;
7672 }
7673
7674 var updateInProgress = false; // Moment prototype object
7675
7676 function Moment(config) {
7677 copyConfig(this, config);
7678 this._d = new Date(config._d != null ? config._d.getTime() : NaN);
7679
7680 if (!this.isValid()) {
7681 this._d = new Date(NaN);
7682 } // Prevent infinite loop in case updateOffset creates new moment
7683 // objects.
7684
7685
7686 if (updateInProgress === false) {
7687 updateInProgress = true;
7688 hooks.updateOffset(this);
7689 updateInProgress = false;
7690 }
7691 }
7692
7693 function isMoment(obj) {
7694 return obj instanceof Moment || obj != null && obj._isAMomentObject != null;
7695 }
7696
7697 function absFloor(number) {
7698 if (number < 0) {
7699 // -0 -> 0
7700 return Math.ceil(number) || 0;
7701 } else {
7702 return Math.floor(number);
7703 }
7704 }
7705
7706 function toInt(argumentForCoercion) {
7707 var coercedNumber = +argumentForCoercion,
7708 value = 0;
7709
7710 if (coercedNumber !== 0 && isFinite(coercedNumber)) {
7711 value = absFloor(coercedNumber);
7712 }
7713
7714 return value;
7715 } // compare two arrays, return the number of differences
7716
7717
7718 function compareArrays(array1, array2, dontConvert) {
7719 var len = Math.min(array1.length, array2.length),
7720 lengthDiff = Math.abs(array1.length - array2.length),
7721 diffs = 0,
7722 i;
7723
7724 for (i = 0; i < len; i++) {
7725 if (dontConvert && array1[i] !== array2[i] || !dontConvert && toInt(array1[i]) !== toInt(array2[i])) {
7726 diffs++;
7727 }
7728 }
7729
7730 return diffs + lengthDiff;
7731 }
7732
7733 function warn(msg) {
7734 if (hooks.suppressDeprecationWarnings === false && typeof console !== 'undefined' && console.warn) {
7735 console.warn('Deprecation warning: ' + msg);
7736 }
7737 }
7738
7739 function deprecate(msg, fn) {
7740 var firstTime = true;
7741 return extend(function () {
7742 if (hooks.deprecationHandler != null) {
7743 hooks.deprecationHandler(null, msg);
7744 }
7745
7746 if (firstTime) {
7747 var args = [];
7748 var arg;
7749
7750 for (var i = 0; i < arguments.length; i++) {
7751 arg = '';
7752
7753 if (typeof arguments[i] === 'object') {
7754 arg += '\n[' + i + '] ';
7755
7756 for (var key in arguments[0]) {
7757 arg += key + ': ' + arguments[0][key] + ', ';
7758 }
7759
7760 arg = arg.slice(0, -2); // Remove trailing comma and space
7761 } else {
7762 arg = arguments[i];
7763 }
7764
7765 args.push(arg);
7766 }
7767
7768 warn(msg + '\nArguments: ' + Array.prototype.slice.call(args).join('') + '\n' + new Error().stack);
7769 firstTime = false;
7770 }
7771
7772 return fn.apply(this, arguments);
7773 }, fn);
7774 }
7775
7776 var deprecations = {};
7777
7778 function deprecateSimple(name, msg) {
7779 if (hooks.deprecationHandler != null) {
7780 hooks.deprecationHandler(name, msg);
7781 }
7782
7783 if (!deprecations[name]) {
7784 warn(msg);
7785 deprecations[name] = true;
7786 }
7787 }
7788
7789 hooks.suppressDeprecationWarnings = false;
7790 hooks.deprecationHandler = null;
7791
7792 function isFunction(input) {
7793 return input instanceof Function || Object.prototype.toString.call(input) === '[object Function]';
7794 }
7795
7796 function set(config) {
7797 var prop, i;
7798
7799 for (i in config) {
7800 prop = config[i];
7801
7802 if (isFunction(prop)) {
7803 this[i] = prop;
7804 } else {
7805 this['_' + i] = prop;
7806 }
7807 }
7808
7809 this._config = config; // Lenient ordinal parsing accepts just a number in addition to
7810 // number + (possibly) stuff coming from _dayOfMonthOrdinalParse.
7811 // TODO: Remove "ordinalParse" fallback in next major release.
7812
7813 this._dayOfMonthOrdinalParseLenient = new RegExp((this._dayOfMonthOrdinalParse.source || this._ordinalParse.source) + '|' + /\d{1,2}/.source);
7814 }
7815
7816 function mergeConfigs(parentConfig, childConfig) {
7817 var res = extend({}, parentConfig),
7818 prop;
7819
7820 for (prop in childConfig) {
7821 if (hasOwnProp(childConfig, prop)) {
7822 if (isObject(parentConfig[prop]) && isObject(childConfig[prop])) {
7823 res[prop] = {};
7824 extend(res[prop], parentConfig[prop]);
7825 extend(res[prop], childConfig[prop]);
7826 } else if (childConfig[prop] != null) {
7827 res[prop] = childConfig[prop];
7828 } else {
7829 delete res[prop];
7830 }
7831 }
7832 }
7833
7834 for (prop in parentConfig) {
7835 if (hasOwnProp(parentConfig, prop) && !hasOwnProp(childConfig, prop) && isObject(parentConfig[prop])) {
7836 // make sure changes to properties don't modify parent config
7837 res[prop] = extend({}, res[prop]);
7838 }
7839 }
7840
7841 return res;
7842 }
7843
7844 function Locale(config) {
7845 if (config != null) {
7846 this.set(config);
7847 }
7848 }
7849
7850 var keys;
7851
7852 if (Object.keys) {
7853 keys = Object.keys;
7854 } else {
7855 keys = function (obj) {
7856 var i,
7857 res = [];
7858
7859 for (i in obj) {
7860 if (hasOwnProp(obj, i)) {
7861 res.push(i);
7862 }
7863 }
7864
7865 return res;
7866 };
7867 }
7868
7869 var defaultCalendar = {
7870 sameDay: '[Today at] LT',
7871 nextDay: '[Tomorrow at] LT',
7872 nextWeek: 'dddd [at] LT',
7873 lastDay: '[Yesterday at] LT',
7874 lastWeek: '[Last] dddd [at] LT',
7875 sameElse: 'L'
7876 };
7877
7878 function calendar(key, mom, now) {
7879 var output = this._calendar[key] || this._calendar['sameElse'];
7880 return isFunction(output) ? output.call(mom, now) : output;
7881 }
7882
7883 var defaultLongDateFormat = {
7884 LTS: 'h:mm:ss A',
7885 LT: 'h:mm A',
7886 L: 'MM/DD/YYYY',
7887 LL: 'MMMM D, YYYY',
7888 LLL: 'MMMM D, YYYY h:mm A',
7889 LLLL: 'dddd, MMMM D, YYYY h:mm A'
7890 };
7891
7892 function longDateFormat(key) {
7893 var format = this._longDateFormat[key],
7894 formatUpper = this._longDateFormat[key.toUpperCase()];
7895
7896 if (format || !formatUpper) {
7897 return format;
7898 }
7899
7900 this._longDateFormat[key] = formatUpper.replace(/MMMM|MM|DD|dddd/g, function (val) {
7901 return val.slice(1);
7902 });
7903 return this._longDateFormat[key];
7904 }
7905
7906 var defaultInvalidDate = 'Invalid date';
7907
7908 function invalidDate() {
7909 return this._invalidDate;
7910 }
7911
7912 var defaultOrdinal = '%d';
7913 var defaultDayOfMonthOrdinalParse = /\d{1,2}/;
7914
7915 function ordinal(number) {
7916 return this._ordinal.replace('%d', number);
7917 }
7918
7919 var defaultRelativeTime = {
7920 future: 'in %s',
7921 past: '%s ago',
7922 s: 'a few seconds',
7923 ss: '%d seconds',
7924 m: 'a minute',
7925 mm: '%d minutes',
7926 h: 'an hour',
7927 hh: '%d hours',
7928 d: 'a day',
7929 dd: '%d days',
7930 M: 'a month',
7931 MM: '%d months',
7932 y: 'a year',
7933 yy: '%d years'
7934 };
7935
7936 function relativeTime(number, withoutSuffix, string, isFuture) {
7937 var output = this._relativeTime[string];
7938 return isFunction(output) ? output(number, withoutSuffix, string, isFuture) : output.replace(/%d/i, number);
7939 }
7940
7941 function pastFuture(diff, output) {
7942 var format = this._relativeTime[diff > 0 ? 'future' : 'past'];
7943 return isFunction(format) ? format(output) : format.replace(/%s/i, output);
7944 }
7945
7946 var aliases = {};
7947
7948 function addUnitAlias(unit, shorthand) {
7949 var lowerCase = unit.toLowerCase();
7950 aliases[lowerCase] = aliases[lowerCase + 's'] = aliases[shorthand] = unit;
7951 }
7952
7953 function normalizeUnits(units) {
7954 return typeof units === 'string' ? aliases[units] || aliases[units.toLowerCase()] : undefined;
7955 }
7956
7957 function normalizeObjectUnits(inputObject) {
7958 var normalizedInput = {},
7959 normalizedProp,
7960 prop;
7961
7962 for (prop in inputObject) {
7963 if (hasOwnProp(inputObject, prop)) {
7964 normalizedProp = normalizeUnits(prop);
7965
7966 if (normalizedProp) {
7967 normalizedInput[normalizedProp] = inputObject[prop];
7968 }
7969 }
7970 }
7971
7972 return normalizedInput;
7973 }
7974
7975 var priorities = {};
7976
7977 function addUnitPriority(unit, priority) {
7978 priorities[unit] = priority;
7979 }
7980
7981 function getPrioritizedUnits(unitsObj) {
7982 var units = [];
7983
7984 for (var u in unitsObj) {
7985 units.push({
7986 unit: u,
7987 priority: priorities[u]
7988 });
7989 }
7990
7991 units.sort(function (a, b) {
7992 return a.priority - b.priority;
7993 });
7994 return units;
7995 }
7996
7997 function zeroFill(number, targetLength, forceSign) {
7998 var absNumber = '' + Math.abs(number),
7999 zerosToFill = targetLength - absNumber.length,
8000 sign = number >= 0;
8001 return (sign ? forceSign ? '+' : '' : '-') + Math.pow(10, Math.max(0, zerosToFill)).toString().substr(1) + absNumber;
8002 }
8003
8004 var formattingTokens = /(\[[^\[]*\])|(\\)?([Hh]mm(ss)?|Mo|MM?M?M?|Do|DDDo|DD?D?D?|ddd?d?|do?|w[o|w]?|W[o|W]?|Qo?|YYYYYY|YYYYY|YYYY|YY|gg(ggg?)?|GG(GGG?)?|e|E|a|A|hh?|HH?|kk?|mm?|ss?|S{1,9}|x|X|zz?|ZZ?|.)/g;
8005 var localFormattingTokens = /(\[[^\[]*\])|(\\)?(LTS|LT|LL?L?L?|l{1,4})/g;
8006 var formatFunctions = {};
8007 var formatTokenFunctions = {}; // token: 'M'
8008 // padded: ['MM', 2]
8009 // ordinal: 'Mo'
8010 // callback: function () { this.month() + 1 }
8011
8012 function addFormatToken(token, padded, ordinal, callback) {
8013 var func = callback;
8014
8015 if (typeof callback === 'string') {
8016 func = function () {
8017 return this[callback]();
8018 };
8019 }
8020
8021 if (token) {
8022 formatTokenFunctions[token] = func;
8023 }
8024
8025 if (padded) {
8026 formatTokenFunctions[padded[0]] = function () {
8027 return zeroFill(func.apply(this, arguments), padded[1], padded[2]);
8028 };
8029 }
8030
8031 if (ordinal) {
8032 formatTokenFunctions[ordinal] = function () {
8033 return this.localeData().ordinal(func.apply(this, arguments), token);
8034 };
8035 }
8036 }
8037
8038 function removeFormattingTokens(input) {
8039 if (input.match(/\[[\s\S]/)) {
8040 return input.replace(/^\[|\]$/g, '');
8041 }
8042
8043 return input.replace(/\\/g, '');
8044 }
8045
8046 function makeFormatFunction(format) {
8047 var array = format.match(formattingTokens),
8048 i,
8049 length;
8050
8051 for (i = 0, length = array.length; i < length; i++) {
8052 if (formatTokenFunctions[array[i]]) {
8053 array[i] = formatTokenFunctions[array[i]];
8054 } else {
8055 array[i] = removeFormattingTokens(array[i]);
8056 }
8057 }
8058
8059 return function (mom) {
8060 var output = '',
8061 i;
8062
8063 for (i = 0; i < length; i++) {
8064 output += isFunction(array[i]) ? array[i].call(mom, format) : array[i];
8065 }
8066
8067 return output;
8068 };
8069 } // format date using native date object
8070
8071
8072 function formatMoment(m, format) {
8073 if (!m.isValid()) {
8074 return m.localeData().invalidDate();
8075 }
8076
8077 format = expandFormat(format, m.localeData());
8078 formatFunctions[format] = formatFunctions[format] || makeFormatFunction(format);
8079 return formatFunctions[format](m);
8080 }
8081
8082 function expandFormat(format, locale) {
8083 var i = 5;
8084
8085 function replaceLongDateFormatTokens(input) {
8086 return locale.longDateFormat(input) || input;
8087 }
8088
8089 localFormattingTokens.lastIndex = 0;
8090
8091 while (i >= 0 && localFormattingTokens.test(format)) {
8092 format = format.replace(localFormattingTokens, replaceLongDateFormatTokens);
8093 localFormattingTokens.lastIndex = 0;
8094 i -= 1;
8095 }
8096
8097 return format;
8098 }
8099
8100 var match1 = /\d/; // 0 - 9
8101
8102 var match2 = /\d\d/; // 00 - 99
8103
8104 var match3 = /\d{3}/; // 000 - 999
8105
8106 var match4 = /\d{4}/; // 0000 - 9999
8107
8108 var match6 = /[+-]?\d{6}/; // -999999 - 999999
8109
8110 var match1to2 = /\d\d?/; // 0 - 99
8111
8112 var match3to4 = /\d\d\d\d?/; // 999 - 9999
8113
8114 var match5to6 = /\d\d\d\d\d\d?/; // 99999 - 999999
8115
8116 var match1to3 = /\d{1,3}/; // 0 - 999
8117
8118 var match1to4 = /\d{1,4}/; // 0 - 9999
8119
8120 var match1to6 = /[+-]?\d{1,6}/; // -999999 - 999999
8121
8122 var matchUnsigned = /\d+/; // 0 - inf
8123
8124 var matchSigned = /[+-]?\d+/; // -inf - inf
8125
8126 var matchOffset = /Z|[+-]\d\d:?\d\d/gi; // +00:00 -00:00 +0000 -0000 or Z
8127
8128 var matchShortOffset = /Z|[+-]\d\d(?::?\d\d)?/gi; // +00 -00 +00:00 -00:00 +0000 -0000 or Z
8129
8130 var matchTimestamp = /[+-]?\d+(\.\d{1,3})?/; // 123456789 123456789.123
8131 // any word (or two) characters or numbers including two/three word month in arabic.
8132 // includes scottish gaelic two word and hyphenated months
8133
8134 var matchWord = /[0-9]{0,256}['a-z\u00A0-\u05FF\u0700-\uD7FF\uF900-\uFDCF\uFDF0-\uFF07\uFF10-\uFFEF]{1,256}|[\u0600-\u06FF\/]{1,256}(\s*?[\u0600-\u06FF]{1,256}){1,2}/i;
8135 var regexes = {};
8136
8137 function addRegexToken(token, regex, strictRegex) {
8138 regexes[token] = isFunction(regex) ? regex : function (isStrict, localeData) {
8139 return isStrict && strictRegex ? strictRegex : regex;
8140 };
8141 }
8142
8143 function getParseRegexForToken(token, config) {
8144 if (!hasOwnProp(regexes, token)) {
8145 return new RegExp(unescapeFormat(token));
8146 }
8147
8148 return regexes[token](config._strict, config._locale);
8149 } // Code from http://stackoverflow.com/questions/3561493/is-there-a-regexp-escape-function-in-javascript
8150
8151
8152 function unescapeFormat(s) {
8153 return regexEscape(s.replace('\\', '').replace(/\\(\[)|\\(\])|\[([^\]\[]*)\]|\\(.)/g, function (matched, p1, p2, p3, p4) {
8154 return p1 || p2 || p3 || p4;
8155 }));
8156 }
8157
8158 function regexEscape(s) {
8159 return s.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
8160 }
8161
8162 var tokens = {};
8163
8164 function addParseToken(token, callback) {
8165 var i,
8166 func = callback;
8167
8168 if (typeof token === 'string') {
8169 token = [token];
8170 }
8171
8172 if (isNumber(callback)) {
8173 func = function (input, array) {
8174 array[callback] = toInt(input);
8175 };
8176 }
8177
8178 for (i = 0; i < token.length; i++) {
8179 tokens[token[i]] = func;
8180 }
8181 }
8182
8183 function addWeekParseToken(token, callback) {
8184 addParseToken(token, function (input, array, config, token) {
8185 config._w = config._w || {};
8186 callback(input, config._w, config, token);
8187 });
8188 }
8189
8190 function addTimeToArrayFromToken(token, input, config) {
8191 if (input != null && hasOwnProp(tokens, token)) {
8192 tokens[token](input, config._a, config, token);
8193 }
8194 }
8195
8196 var YEAR = 0;
8197 var MONTH = 1;
8198 var DATE = 2;
8199 var HOUR = 3;
8200 var MINUTE = 4;
8201 var SECOND = 5;
8202 var MILLISECOND = 6;
8203 var WEEK = 7;
8204 var WEEKDAY = 8; // FORMATTING
8205
8206 addFormatToken('Y', 0, 0, function () {
8207 var y = this.year();
8208 return y <= 9999 ? '' + y : '+' + y;
8209 });
8210 addFormatToken(0, ['YY', 2], 0, function () {
8211 return this.year() % 100;
8212 });
8213 addFormatToken(0, ['YYYY', 4], 0, 'year');
8214 addFormatToken(0, ['YYYYY', 5], 0, 'year');
8215 addFormatToken(0, ['YYYYYY', 6, true], 0, 'year'); // ALIASES
8216
8217 addUnitAlias('year', 'y'); // PRIORITIES
8218
8219 addUnitPriority('year', 1); // PARSING
8220
8221 addRegexToken('Y', matchSigned);
8222 addRegexToken('YY', match1to2, match2);
8223 addRegexToken('YYYY', match1to4, match4);
8224 addRegexToken('YYYYY', match1to6, match6);
8225 addRegexToken('YYYYYY', match1to6, match6);
8226 addParseToken(['YYYYY', 'YYYYYY'], YEAR);
8227 addParseToken('YYYY', function (input, array) {
8228 array[YEAR] = input.length === 2 ? hooks.parseTwoDigitYear(input) : toInt(input);
8229 });
8230 addParseToken('YY', function (input, array) {
8231 array[YEAR] = hooks.parseTwoDigitYear(input);
8232 });
8233 addParseToken('Y', function (input, array) {
8234 array[YEAR] = parseInt(input, 10);
8235 }); // HELPERS
8236
8237 function daysInYear(year) {
8238 return isLeapYear(year) ? 366 : 365;
8239 }
8240
8241 function isLeapYear(year) {
8242 return year % 4 === 0 && year % 100 !== 0 || year % 400 === 0;
8243 } // HOOKS
8244
8245
8246 hooks.parseTwoDigitYear = function (input) {
8247 return toInt(input) + (toInt(input) > 68 ? 1900 : 2000);
8248 }; // MOMENTS
8249
8250
8251 var getSetYear = makeGetSet('FullYear', true);
8252
8253 function getIsLeapYear() {
8254 return isLeapYear(this.year());
8255 }
8256
8257 function makeGetSet(unit, keepTime) {
8258 return function (value) {
8259 if (value != null) {
8260 set$1(this, unit, value);
8261 hooks.updateOffset(this, keepTime);
8262 return this;
8263 } else {
8264 return get(this, unit);
8265 }
8266 };
8267 }
8268
8269 function get(mom, unit) {
8270 return mom.isValid() ? mom._d['get' + (mom._isUTC ? 'UTC' : '') + unit]() : NaN;
8271 }
8272
8273 function set$1(mom, unit, value) {
8274 if (mom.isValid() && !isNaN(value)) {
8275 if (unit === 'FullYear' && isLeapYear(mom.year()) && mom.month() === 1 && mom.date() === 29) {
8276 mom._d['set' + (mom._isUTC ? 'UTC' : '') + unit](value, mom.month(), daysInMonth(value, mom.month()));
8277 } else {
8278 mom._d['set' + (mom._isUTC ? 'UTC' : '') + unit](value);
8279 }
8280 }
8281 } // MOMENTS
8282
8283
8284 function stringGet(units) {
8285 units = normalizeUnits(units);
8286
8287 if (isFunction(this[units])) {
8288 return this[units]();
8289 }
8290
8291 return this;
8292 }
8293
8294 function stringSet(units, value) {
8295 if (typeof units === 'object') {
8296 units = normalizeObjectUnits(units);
8297 var prioritized = getPrioritizedUnits(units);
8298
8299 for (var i = 0; i < prioritized.length; i++) {
8300 this[prioritized[i].unit](units[prioritized[i].unit]);
8301 }
8302 } else {
8303 units = normalizeUnits(units);
8304
8305 if (isFunction(this[units])) {
8306 return this[units](value);
8307 }
8308 }
8309
8310 return this;
8311 }
8312
8313 function mod(n, x) {
8314 return (n % x + x) % x;
8315 }
8316
8317 var indexOf;
8318
8319 if (Array.prototype.indexOf) {
8320 indexOf = Array.prototype.indexOf;
8321 } else {
8322 indexOf = function (o) {
8323 // I know
8324 var i;
8325
8326 for (i = 0; i < this.length; ++i) {
8327 if (this[i] === o) {
8328 return i;
8329 }
8330 }
8331
8332 return -1;
8333 };
8334 }
8335
8336 function daysInMonth(year, month) {
8337 if (isNaN(year) || isNaN(month)) {
8338 return NaN;
8339 }
8340
8341 var modMonth = mod(month, 12);
8342 year += (month - modMonth) / 12;
8343 return modMonth === 1 ? isLeapYear(year) ? 29 : 28 : 31 - modMonth % 7 % 2;
8344 } // FORMATTING
8345
8346
8347 addFormatToken('M', ['MM', 2], 'Mo', function () {
8348 return this.month() + 1;
8349 });
8350 addFormatToken('MMM', 0, 0, function (format) {
8351 return this.localeData().monthsShort(this, format);
8352 });
8353 addFormatToken('MMMM', 0, 0, function (format) {
8354 return this.localeData().months(this, format);
8355 }); // ALIASES
8356
8357 addUnitAlias('month', 'M'); // PRIORITY
8358
8359 addUnitPriority('month', 8); // PARSING
8360
8361 addRegexToken('M', match1to2);
8362 addRegexToken('MM', match1to2, match2);
8363 addRegexToken('MMM', function (isStrict, locale) {
8364 return locale.monthsShortRegex(isStrict);
8365 });
8366 addRegexToken('MMMM', function (isStrict, locale) {
8367 return locale.monthsRegex(isStrict);
8368 });
8369 addParseToken(['M', 'MM'], function (input, array) {
8370 array[MONTH] = toInt(input) - 1;
8371 });
8372 addParseToken(['MMM', 'MMMM'], function (input, array, config, token) {
8373 var month = config._locale.monthsParse(input, token, config._strict); // if we didn't find a month name, mark the date as invalid.
8374
8375
8376 if (month != null) {
8377 array[MONTH] = month;
8378 } else {
8379 getParsingFlags(config).invalidMonth = input;
8380 }
8381 }); // LOCALES
8382
8383 var MONTHS_IN_FORMAT = /D[oD]?(\[[^\[\]]*\]|\s)+MMMM?/;
8384 var defaultLocaleMonths = 'January_February_March_April_May_June_July_August_September_October_November_December'.split('_');
8385
8386 function localeMonths(m, format) {
8387 if (!m) {
8388 return isArray(this._months) ? this._months : this._months['standalone'];
8389 }
8390
8391 return isArray(this._months) ? this._months[m.month()] : this._months[(this._months.isFormat || MONTHS_IN_FORMAT).test(format) ? 'format' : 'standalone'][m.month()];
8392 }
8393
8394 var defaultLocaleMonthsShort = 'Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec'.split('_');
8395
8396 function localeMonthsShort(m, format) {
8397 if (!m) {
8398 return isArray(this._monthsShort) ? this._monthsShort : this._monthsShort['standalone'];
8399 }
8400
8401 return isArray(this._monthsShort) ? this._monthsShort[m.month()] : this._monthsShort[MONTHS_IN_FORMAT.test(format) ? 'format' : 'standalone'][m.month()];
8402 }
8403
8404 function handleStrictParse(monthName, format, strict) {
8405 var i,
8406 ii,
8407 mom,
8408 llc = monthName.toLocaleLowerCase();
8409
8410 if (!this._monthsParse) {
8411 // this is not used
8412 this._monthsParse = [];
8413 this._longMonthsParse = [];
8414 this._shortMonthsParse = [];
8415
8416 for (i = 0; i < 12; ++i) {
8417 mom = createUTC([2000, i]);
8418 this._shortMonthsParse[i] = this.monthsShort(mom, '').toLocaleLowerCase();
8419 this._longMonthsParse[i] = this.months(mom, '').toLocaleLowerCase();
8420 }
8421 }
8422
8423 if (strict) {
8424 if (format === 'MMM') {
8425 ii = indexOf.call(this._shortMonthsParse, llc);
8426 return ii !== -1 ? ii : null;
8427 } else {
8428 ii = indexOf.call(this._longMonthsParse, llc);
8429 return ii !== -1 ? ii : null;
8430 }
8431 } else {
8432 if (format === 'MMM') {
8433 ii = indexOf.call(this._shortMonthsParse, llc);
8434
8435 if (ii !== -1) {
8436 return ii;
8437 }
8438
8439 ii = indexOf.call(this._longMonthsParse, llc);
8440 return ii !== -1 ? ii : null;
8441 } else {
8442 ii = indexOf.call(this._longMonthsParse, llc);
8443
8444 if (ii !== -1) {
8445 return ii;
8446 }
8447
8448 ii = indexOf.call(this._shortMonthsParse, llc);
8449 return ii !== -1 ? ii : null;
8450 }
8451 }
8452 }
8453
8454 function localeMonthsParse(monthName, format, strict) {
8455 var i, mom, regex;
8456
8457 if (this._monthsParseExact) {
8458 return handleStrictParse.call(this, monthName, format, strict);
8459 }
8460
8461 if (!this._monthsParse) {
8462 this._monthsParse = [];
8463 this._longMonthsParse = [];
8464 this._shortMonthsParse = [];
8465 } // TODO: add sorting
8466 // Sorting makes sure if one month (or abbr) is a prefix of another
8467 // see sorting in computeMonthsParse
8468
8469
8470 for (i = 0; i < 12; i++) {
8471 // make the regex if we don't have it already
8472 mom = createUTC([2000, i]);
8473
8474 if (strict && !this._longMonthsParse[i]) {
8475 this._longMonthsParse[i] = new RegExp('^' + this.months(mom, '').replace('.', '') + '$', 'i');
8476 this._shortMonthsParse[i] = new RegExp('^' + this.monthsShort(mom, '').replace('.', '') + '$', 'i');
8477 }
8478
8479 if (!strict && !this._monthsParse[i]) {
8480 regex = '^' + this.months(mom, '') + '|^' + this.monthsShort(mom, '');
8481 this._monthsParse[i] = new RegExp(regex.replace('.', ''), 'i');
8482 } // test the regex
8483
8484
8485 if (strict && format === 'MMMM' && this._longMonthsParse[i].test(monthName)) {
8486 return i;
8487 } else if (strict && format === 'MMM' && this._shortMonthsParse[i].test(monthName)) {
8488 return i;
8489 } else if (!strict && this._monthsParse[i].test(monthName)) {
8490 return i;
8491 }
8492 }
8493 } // MOMENTS
8494
8495
8496 function setMonth(mom, value) {
8497 var dayOfMonth;
8498
8499 if (!mom.isValid()) {
8500 // No op
8501 return mom;
8502 }
8503
8504 if (typeof value === 'string') {
8505 if (/^\d+$/.test(value)) {
8506 value = toInt(value);
8507 } else {
8508 value = mom.localeData().monthsParse(value); // TODO: Another silent failure?
8509
8510 if (!isNumber(value)) {
8511 return mom;
8512 }
8513 }
8514 }
8515
8516 dayOfMonth = Math.min(mom.date(), daysInMonth(mom.year(), value));
8517
8518 mom._d['set' + (mom._isUTC ? 'UTC' : '') + 'Month'](value, dayOfMonth);
8519
8520 return mom;
8521 }
8522
8523 function getSetMonth(value) {
8524 if (value != null) {
8525 setMonth(this, value);
8526 hooks.updateOffset(this, true);
8527 return this;
8528 } else {
8529 return get(this, 'Month');
8530 }
8531 }
8532
8533 function getDaysInMonth() {
8534 return daysInMonth(this.year(), this.month());
8535 }
8536
8537 var defaultMonthsShortRegex = matchWord;
8538
8539 function monthsShortRegex(isStrict) {
8540 if (this._monthsParseExact) {
8541 if (!hasOwnProp(this, '_monthsRegex')) {
8542 computeMonthsParse.call(this);
8543 }
8544
8545 if (isStrict) {
8546 return this._monthsShortStrictRegex;
8547 } else {
8548 return this._monthsShortRegex;
8549 }
8550 } else {
8551 if (!hasOwnProp(this, '_monthsShortRegex')) {
8552 this._monthsShortRegex = defaultMonthsShortRegex;
8553 }
8554
8555 return this._monthsShortStrictRegex && isStrict ? this._monthsShortStrictRegex : this._monthsShortRegex;
8556 }
8557 }
8558
8559 var defaultMonthsRegex = matchWord;
8560
8561 function monthsRegex(isStrict) {
8562 if (this._monthsParseExact) {
8563 if (!hasOwnProp(this, '_monthsRegex')) {
8564 computeMonthsParse.call(this);
8565 }
8566
8567 if (isStrict) {
8568 return this._monthsStrictRegex;
8569 } else {
8570 return this._monthsRegex;
8571 }
8572 } else {
8573 if (!hasOwnProp(this, '_monthsRegex')) {
8574 this._monthsRegex = defaultMonthsRegex;
8575 }
8576
8577 return this._monthsStrictRegex && isStrict ? this._monthsStrictRegex : this._monthsRegex;
8578 }
8579 }
8580
8581 function computeMonthsParse() {
8582 function cmpLenRev(a, b) {
8583 return b.length - a.length;
8584 }
8585
8586 var shortPieces = [],
8587 longPieces = [],
8588 mixedPieces = [],
8589 i,
8590 mom;
8591
8592 for (i = 0; i < 12; i++) {
8593 // make the regex if we don't have it already
8594 mom = createUTC([2000, i]);
8595 shortPieces.push(this.monthsShort(mom, ''));
8596 longPieces.push(this.months(mom, ''));
8597 mixedPieces.push(this.months(mom, ''));
8598 mixedPieces.push(this.monthsShort(mom, ''));
8599 } // Sorting makes sure if one month (or abbr) is a prefix of another it
8600 // will match the longer piece.
8601
8602
8603 shortPieces.sort(cmpLenRev);
8604 longPieces.sort(cmpLenRev);
8605 mixedPieces.sort(cmpLenRev);
8606
8607 for (i = 0; i < 12; i++) {
8608 shortPieces[i] = regexEscape(shortPieces[i]);
8609 longPieces[i] = regexEscape(longPieces[i]);
8610 }
8611
8612 for (i = 0; i < 24; i++) {
8613 mixedPieces[i] = regexEscape(mixedPieces[i]);
8614 }
8615
8616 this._monthsRegex = new RegExp('^(' + mixedPieces.join('|') + ')', 'i');
8617 this._monthsShortRegex = this._monthsRegex;
8618 this._monthsStrictRegex = new RegExp('^(' + longPieces.join('|') + ')', 'i');
8619 this._monthsShortStrictRegex = new RegExp('^(' + shortPieces.join('|') + ')', 'i');
8620 }
8621
8622 function createDate(y, m, d, h, M, s, ms) {
8623 // can't just apply() to create a date:
8624 // https://stackoverflow.com/q/181348
8625 var date; // the date constructor remaps years 0-99 to 1900-1999
8626
8627 if (y < 100 && y >= 0) {
8628 // preserve leap years using a full 400 year cycle, then reset
8629 date = new Date(y + 400, m, d, h, M, s, ms);
8630
8631 if (isFinite(date.getFullYear())) {
8632 date.setFullYear(y);
8633 }
8634 } else {
8635 date = new Date(y, m, d, h, M, s, ms);
8636 }
8637
8638 return date;
8639 }
8640
8641 function createUTCDate(y) {
8642 var date; // the Date.UTC function remaps years 0-99 to 1900-1999
8643
8644 if (y < 100 && y >= 0) {
8645 var args = Array.prototype.slice.call(arguments); // preserve leap years using a full 400 year cycle, then reset
8646
8647 args[0] = y + 400;
8648 date = new Date(Date.UTC.apply(null, args));
8649
8650 if (isFinite(date.getUTCFullYear())) {
8651 date.setUTCFullYear(y);
8652 }
8653 } else {
8654 date = new Date(Date.UTC.apply(null, arguments));
8655 }
8656
8657 return date;
8658 } // start-of-first-week - start-of-year
8659
8660
8661 function firstWeekOffset(year, dow, doy) {
8662 var // first-week day -- which january is always in the first week (4 for iso, 1 for other)
8663 fwd = 7 + dow - doy,
8664 // first-week day local weekday -- which local weekday is fwd
8665 fwdlw = (7 + createUTCDate(year, 0, fwd).getUTCDay() - dow) % 7;
8666 return -fwdlw + fwd - 1;
8667 } // https://en.wikipedia.org/wiki/ISO_week_date#Calculating_a_date_given_the_year.2C_week_number_and_weekday
8668
8669
8670 function dayOfYearFromWeeks(year, week, weekday, dow, doy) {
8671 var localWeekday = (7 + weekday - dow) % 7,
8672 weekOffset = firstWeekOffset(year, dow, doy),
8673 dayOfYear = 1 + 7 * (week - 1) + localWeekday + weekOffset,
8674 resYear,
8675 resDayOfYear;
8676
8677 if (dayOfYear <= 0) {
8678 resYear = year - 1;
8679 resDayOfYear = daysInYear(resYear) + dayOfYear;
8680 } else if (dayOfYear > daysInYear(year)) {
8681 resYear = year + 1;
8682 resDayOfYear = dayOfYear - daysInYear(year);
8683 } else {
8684 resYear = year;
8685 resDayOfYear = dayOfYear;
8686 }
8687
8688 return {
8689 year: resYear,
8690 dayOfYear: resDayOfYear
8691 };
8692 }
8693
8694 function weekOfYear(mom, dow, doy) {
8695 var weekOffset = firstWeekOffset(mom.year(), dow, doy),
8696 week = Math.floor((mom.dayOfYear() - weekOffset - 1) / 7) + 1,
8697 resWeek,
8698 resYear;
8699
8700 if (week < 1) {
8701 resYear = mom.year() - 1;
8702 resWeek = week + weeksInYear(resYear, dow, doy);
8703 } else if (week > weeksInYear(mom.year(), dow, doy)) {
8704 resWeek = week - weeksInYear(mom.year(), dow, doy);
8705 resYear = mom.year() + 1;
8706 } else {
8707 resYear = mom.year();
8708 resWeek = week;
8709 }
8710
8711 return {
8712 week: resWeek,
8713 year: resYear
8714 };
8715 }
8716
8717 function weeksInYear(year, dow, doy) {
8718 var weekOffset = firstWeekOffset(year, dow, doy),
8719 weekOffsetNext = firstWeekOffset(year + 1, dow, doy);
8720 return (daysInYear(year) - weekOffset + weekOffsetNext) / 7;
8721 } // FORMATTING
8722
8723
8724 addFormatToken('w', ['ww', 2], 'wo', 'week');
8725 addFormatToken('W', ['WW', 2], 'Wo', 'isoWeek'); // ALIASES
8726
8727 addUnitAlias('week', 'w');
8728 addUnitAlias('isoWeek', 'W'); // PRIORITIES
8729
8730 addUnitPriority('week', 5);
8731 addUnitPriority('isoWeek', 5); // PARSING
8732
8733 addRegexToken('w', match1to2);
8734 addRegexToken('ww', match1to2, match2);
8735 addRegexToken('W', match1to2);
8736 addRegexToken('WW', match1to2, match2);
8737 addWeekParseToken(['w', 'ww', 'W', 'WW'], function (input, week, config, token) {
8738 week[token.substr(0, 1)] = toInt(input);
8739 }); // HELPERS
8740 // LOCALES
8741
8742 function localeWeek(mom) {
8743 return weekOfYear(mom, this._week.dow, this._week.doy).week;
8744 }
8745
8746 var defaultLocaleWeek = {
8747 dow: 0,
8748 // Sunday is the first day of the week.
8749 doy: 6 // The week that contains Jan 6th is the first week of the year.
8750
8751 };
8752
8753 function localeFirstDayOfWeek() {
8754 return this._week.dow;
8755 }
8756
8757 function localeFirstDayOfYear() {
8758 return this._week.doy;
8759 } // MOMENTS
8760
8761
8762 function getSetWeek(input) {
8763 var week = this.localeData().week(this);
8764 return input == null ? week : this.add((input - week) * 7, 'd');
8765 }
8766
8767 function getSetISOWeek(input) {
8768 var week = weekOfYear(this, 1, 4).week;
8769 return input == null ? week : this.add((input - week) * 7, 'd');
8770 } // FORMATTING
8771
8772
8773 addFormatToken('d', 0, 'do', 'day');
8774 addFormatToken('dd', 0, 0, function (format) {
8775 return this.localeData().weekdaysMin(this, format);
8776 });
8777 addFormatToken('ddd', 0, 0, function (format) {
8778 return this.localeData().weekdaysShort(this, format);
8779 });
8780 addFormatToken('dddd', 0, 0, function (format) {
8781 return this.localeData().weekdays(this, format);
8782 });
8783 addFormatToken('e', 0, 0, 'weekday');
8784 addFormatToken('E', 0, 0, 'isoWeekday'); // ALIASES
8785
8786 addUnitAlias('day', 'd');
8787 addUnitAlias('weekday', 'e');
8788 addUnitAlias('isoWeekday', 'E'); // PRIORITY
8789
8790 addUnitPriority('day', 11);
8791 addUnitPriority('weekday', 11);
8792 addUnitPriority('isoWeekday', 11); // PARSING
8793
8794 addRegexToken('d', match1to2);
8795 addRegexToken('e', match1to2);
8796 addRegexToken('E', match1to2);
8797 addRegexToken('dd', function (isStrict, locale) {
8798 return locale.weekdaysMinRegex(isStrict);
8799 });
8800 addRegexToken('ddd', function (isStrict, locale) {
8801 return locale.weekdaysShortRegex(isStrict);
8802 });
8803 addRegexToken('dddd', function (isStrict, locale) {
8804 return locale.weekdaysRegex(isStrict);
8805 });
8806 addWeekParseToken(['dd', 'ddd', 'dddd'], function (input, week, config, token) {
8807 var weekday = config._locale.weekdaysParse(input, token, config._strict); // if we didn't get a weekday name, mark the date as invalid
8808
8809
8810 if (weekday != null) {
8811 week.d = weekday;
8812 } else {
8813 getParsingFlags(config).invalidWeekday = input;
8814 }
8815 });
8816 addWeekParseToken(['d', 'e', 'E'], function (input, week, config, token) {
8817 week[token] = toInt(input);
8818 }); // HELPERS
8819
8820 function parseWeekday(input, locale) {
8821 if (typeof input !== 'string') {
8822 return input;
8823 }
8824
8825 if (!isNaN(input)) {
8826 return parseInt(input, 10);
8827 }
8828
8829 input = locale.weekdaysParse(input);
8830
8831 if (typeof input === 'number') {
8832 return input;
8833 }
8834
8835 return null;
8836 }
8837
8838 function parseIsoWeekday(input, locale) {
8839 if (typeof input === 'string') {
8840 return locale.weekdaysParse(input) % 7 || 7;
8841 }
8842
8843 return isNaN(input) ? null : input;
8844 } // LOCALES
8845
8846
8847 function shiftWeekdays(ws, n) {
8848 return ws.slice(n, 7).concat(ws.slice(0, n));
8849 }
8850
8851 var defaultLocaleWeekdays = 'Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday'.split('_');
8852
8853 function localeWeekdays(m, format) {
8854 var weekdays = isArray(this._weekdays) ? this._weekdays : this._weekdays[m && m !== true && this._weekdays.isFormat.test(format) ? 'format' : 'standalone'];
8855 return m === true ? shiftWeekdays(weekdays, this._week.dow) : m ? weekdays[m.day()] : weekdays;
8856 }
8857
8858 var defaultLocaleWeekdaysShort = 'Sun_Mon_Tue_Wed_Thu_Fri_Sat'.split('_');
8859
8860 function localeWeekdaysShort(m) {
8861 return m === true ? shiftWeekdays(this._weekdaysShort, this._week.dow) : m ? this._weekdaysShort[m.day()] : this._weekdaysShort;
8862 }
8863
8864 var defaultLocaleWeekdaysMin = 'Su_Mo_Tu_We_Th_Fr_Sa'.split('_');
8865
8866 function localeWeekdaysMin(m) {
8867 return m === true ? shiftWeekdays(this._weekdaysMin, this._week.dow) : m ? this._weekdaysMin[m.day()] : this._weekdaysMin;
8868 }
8869
8870 function handleStrictParse$1(weekdayName, format, strict) {
8871 var i,
8872 ii,
8873 mom,
8874 llc = weekdayName.toLocaleLowerCase();
8875
8876 if (!this._weekdaysParse) {
8877 this._weekdaysParse = [];
8878 this._shortWeekdaysParse = [];
8879 this._minWeekdaysParse = [];
8880
8881 for (i = 0; i < 7; ++i) {
8882 mom = createUTC([2000, 1]).day(i);
8883 this._minWeekdaysParse[i] = this.weekdaysMin(mom, '').toLocaleLowerCase();
8884 this._shortWeekdaysParse[i] = this.weekdaysShort(mom, '').toLocaleLowerCase();
8885 this._weekdaysParse[i] = this.weekdays(mom, '').toLocaleLowerCase();
8886 }
8887 }
8888
8889 if (strict) {
8890 if (format === 'dddd') {
8891 ii = indexOf.call(this._weekdaysParse, llc);
8892 return ii !== -1 ? ii : null;
8893 } else if (format === 'ddd') {
8894 ii = indexOf.call(this._shortWeekdaysParse, llc);
8895 return ii !== -1 ? ii : null;
8896 } else {
8897 ii = indexOf.call(this._minWeekdaysParse, llc);
8898 return ii !== -1 ? ii : null;
8899 }
8900 } else {
8901 if (format === 'dddd') {
8902 ii = indexOf.call(this._weekdaysParse, llc);
8903
8904 if (ii !== -1) {
8905 return ii;
8906 }
8907
8908 ii = indexOf.call(this._shortWeekdaysParse, llc);
8909
8910 if (ii !== -1) {
8911 return ii;
8912 }
8913
8914 ii = indexOf.call(this._minWeekdaysParse, llc);
8915 return ii !== -1 ? ii : null;
8916 } else if (format === 'ddd') {
8917 ii = indexOf.call(this._shortWeekdaysParse, llc);
8918
8919 if (ii !== -1) {
8920 return ii;
8921 }
8922
8923 ii = indexOf.call(this._weekdaysParse, llc);
8924
8925 if (ii !== -1) {
8926 return ii;
8927 }
8928
8929 ii = indexOf.call(this._minWeekdaysParse, llc);
8930 return ii !== -1 ? ii : null;
8931 } else {
8932 ii = indexOf.call(this._minWeekdaysParse, llc);
8933
8934 if (ii !== -1) {
8935 return ii;
8936 }
8937
8938 ii = indexOf.call(this._weekdaysParse, llc);
8939
8940 if (ii !== -1) {
8941 return ii;
8942 }
8943
8944 ii = indexOf.call(this._shortWeekdaysParse, llc);
8945 return ii !== -1 ? ii : null;
8946 }
8947 }
8948 }
8949
8950 function localeWeekdaysParse(weekdayName, format, strict) {
8951 var i, mom, regex;
8952
8953 if (this._weekdaysParseExact) {
8954 return handleStrictParse$1.call(this, weekdayName, format, strict);
8955 }
8956
8957 if (!this._weekdaysParse) {
8958 this._weekdaysParse = [];
8959 this._minWeekdaysParse = [];
8960 this._shortWeekdaysParse = [];
8961 this._fullWeekdaysParse = [];
8962 }
8963
8964 for (i = 0; i < 7; i++) {
8965 // make the regex if we don't have it already
8966 mom = createUTC([2000, 1]).day(i);
8967
8968 if (strict && !this._fullWeekdaysParse[i]) {
8969 this._fullWeekdaysParse[i] = new RegExp('^' + this.weekdays(mom, '').replace('.', '\\.?') + '$', 'i');
8970 this._shortWeekdaysParse[i] = new RegExp('^' + this.weekdaysShort(mom, '').replace('.', '\\.?') + '$', 'i');
8971 this._minWeekdaysParse[i] = new RegExp('^' + this.weekdaysMin(mom, '').replace('.', '\\.?') + '$', 'i');
8972 }
8973
8974 if (!this._weekdaysParse[i]) {
8975 regex = '^' + this.weekdays(mom, '') + '|^' + this.weekdaysShort(mom, '') + '|^' + this.weekdaysMin(mom, '');
8976 this._weekdaysParse[i] = new RegExp(regex.replace('.', ''), 'i');
8977 } // test the regex
8978
8979
8980 if (strict && format === 'dddd' && this._fullWeekdaysParse[i].test(weekdayName)) {
8981 return i;
8982 } else if (strict && format === 'ddd' && this._shortWeekdaysParse[i].test(weekdayName)) {
8983 return i;
8984 } else if (strict && format === 'dd' && this._minWeekdaysParse[i].test(weekdayName)) {
8985 return i;
8986 } else if (!strict && this._weekdaysParse[i].test(weekdayName)) {
8987 return i;
8988 }
8989 }
8990 } // MOMENTS
8991
8992
8993 function getSetDayOfWeek(input) {
8994 if (!this.isValid()) {
8995 return input != null ? this : NaN;
8996 }
8997
8998 var day = this._isUTC ? this._d.getUTCDay() : this._d.getDay();
8999
9000 if (input != null) {
9001 input = parseWeekday(input, this.localeData());
9002 return this.add(input - day, 'd');
9003 } else {
9004 return day;
9005 }
9006 }
9007
9008 function getSetLocaleDayOfWeek(input) {
9009 if (!this.isValid()) {
9010 return input != null ? this : NaN;
9011 }
9012
9013 var weekday = (this.day() + 7 - this.localeData()._week.dow) % 7;
9014 return input == null ? weekday : this.add(input - weekday, 'd');
9015 }
9016
9017 function getSetISODayOfWeek(input) {
9018 if (!this.isValid()) {
9019 return input != null ? this : NaN;
9020 } // behaves the same as moment#day except
9021 // as a getter, returns 7 instead of 0 (1-7 range instead of 0-6)
9022 // as a setter, sunday should belong to the previous week.
9023
9024
9025 if (input != null) {
9026 var weekday = parseIsoWeekday(input, this.localeData());
9027 return this.day(this.day() % 7 ? weekday : weekday - 7);
9028 } else {
9029 return this.day() || 7;
9030 }
9031 }
9032
9033 var defaultWeekdaysRegex = matchWord;
9034
9035 function weekdaysRegex(isStrict) {
9036 if (this._weekdaysParseExact) {
9037 if (!hasOwnProp(this, '_weekdaysRegex')) {
9038 computeWeekdaysParse.call(this);
9039 }
9040
9041 if (isStrict) {
9042 return this._weekdaysStrictRegex;
9043 } else {
9044 return this._weekdaysRegex;
9045 }
9046 } else {
9047 if (!hasOwnProp(this, '_weekdaysRegex')) {
9048 this._weekdaysRegex = defaultWeekdaysRegex;
9049 }
9050
9051 return this._weekdaysStrictRegex && isStrict ? this._weekdaysStrictRegex : this._weekdaysRegex;
9052 }
9053 }
9054
9055 var defaultWeekdaysShortRegex = matchWord;
9056
9057 function weekdaysShortRegex(isStrict) {
9058 if (this._weekdaysParseExact) {
9059 if (!hasOwnProp(this, '_weekdaysRegex')) {
9060 computeWeekdaysParse.call(this);
9061 }
9062
9063 if (isStrict) {
9064 return this._weekdaysShortStrictRegex;
9065 } else {
9066 return this._weekdaysShortRegex;
9067 }
9068 } else {
9069 if (!hasOwnProp(this, '_weekdaysShortRegex')) {
9070 this._weekdaysShortRegex = defaultWeekdaysShortRegex;
9071 }
9072
9073 return this._weekdaysShortStrictRegex && isStrict ? this._weekdaysShortStrictRegex : this._weekdaysShortRegex;
9074 }
9075 }
9076
9077 var defaultWeekdaysMinRegex = matchWord;
9078
9079 function weekdaysMinRegex(isStrict) {
9080 if (this._weekdaysParseExact) {
9081 if (!hasOwnProp(this, '_weekdaysRegex')) {
9082 computeWeekdaysParse.call(this);
9083 }
9084
9085 if (isStrict) {
9086 return this._weekdaysMinStrictRegex;
9087 } else {
9088 return this._weekdaysMinRegex;
9089 }
9090 } else {
9091 if (!hasOwnProp(this, '_weekdaysMinRegex')) {
9092 this._weekdaysMinRegex = defaultWeekdaysMinRegex;
9093 }
9094
9095 return this._weekdaysMinStrictRegex && isStrict ? this._weekdaysMinStrictRegex : this._weekdaysMinRegex;
9096 }
9097 }
9098
9099 function computeWeekdaysParse() {
9100 function cmpLenRev(a, b) {
9101 return b.length - a.length;
9102 }
9103
9104 var minPieces = [],
9105 shortPieces = [],
9106 longPieces = [],
9107 mixedPieces = [],
9108 i,
9109 mom,
9110 minp,
9111 shortp,
9112 longp;
9113
9114 for (i = 0; i < 7; i++) {
9115 // make the regex if we don't have it already
9116 mom = createUTC([2000, 1]).day(i);
9117 minp = this.weekdaysMin(mom, '');
9118 shortp = this.weekdaysShort(mom, '');
9119 longp = this.weekdays(mom, '');
9120 minPieces.push(minp);
9121 shortPieces.push(shortp);
9122 longPieces.push(longp);
9123 mixedPieces.push(minp);
9124 mixedPieces.push(shortp);
9125 mixedPieces.push(longp);
9126 } // Sorting makes sure if one weekday (or abbr) is a prefix of another it
9127 // will match the longer piece.
9128
9129
9130 minPieces.sort(cmpLenRev);
9131 shortPieces.sort(cmpLenRev);
9132 longPieces.sort(cmpLenRev);
9133 mixedPieces.sort(cmpLenRev);
9134
9135 for (i = 0; i < 7; i++) {
9136 shortPieces[i] = regexEscape(shortPieces[i]);
9137 longPieces[i] = regexEscape(longPieces[i]);
9138 mixedPieces[i] = regexEscape(mixedPieces[i]);
9139 }
9140
9141 this._weekdaysRegex = new RegExp('^(' + mixedPieces.join('|') + ')', 'i');
9142 this._weekdaysShortRegex = this._weekdaysRegex;
9143 this._weekdaysMinRegex = this._weekdaysRegex;
9144 this._weekdaysStrictRegex = new RegExp('^(' + longPieces.join('|') + ')', 'i');
9145 this._weekdaysShortStrictRegex = new RegExp('^(' + shortPieces.join('|') + ')', 'i');
9146 this._weekdaysMinStrictRegex = new RegExp('^(' + minPieces.join('|') + ')', 'i');
9147 } // FORMATTING
9148
9149
9150 function hFormat() {
9151 return this.hours() % 12 || 12;
9152 }
9153
9154 function kFormat() {
9155 return this.hours() || 24;
9156 }
9157
9158 addFormatToken('H', ['HH', 2], 0, 'hour');
9159 addFormatToken('h', ['hh', 2], 0, hFormat);
9160 addFormatToken('k', ['kk', 2], 0, kFormat);
9161 addFormatToken('hmm', 0, 0, function () {
9162 return '' + hFormat.apply(this) + zeroFill(this.minutes(), 2);
9163 });
9164 addFormatToken('hmmss', 0, 0, function () {
9165 return '' + hFormat.apply(this) + zeroFill(this.minutes(), 2) + zeroFill(this.seconds(), 2);
9166 });
9167 addFormatToken('Hmm', 0, 0, function () {
9168 return '' + this.hours() + zeroFill(this.minutes(), 2);
9169 });
9170 addFormatToken('Hmmss', 0, 0, function () {
9171 return '' + this.hours() + zeroFill(this.minutes(), 2) + zeroFill(this.seconds(), 2);
9172 });
9173
9174 function meridiem(token, lowercase) {
9175 addFormatToken(token, 0, 0, function () {
9176 return this.localeData().meridiem(this.hours(), this.minutes(), lowercase);
9177 });
9178 }
9179
9180 meridiem('a', true);
9181 meridiem('A', false); // ALIASES
9182
9183 addUnitAlias('hour', 'h'); // PRIORITY
9184
9185 addUnitPriority('hour', 13); // PARSING
9186
9187 function matchMeridiem(isStrict, locale) {
9188 return locale._meridiemParse;
9189 }
9190
9191 addRegexToken('a', matchMeridiem);
9192 addRegexToken('A', matchMeridiem);
9193 addRegexToken('H', match1to2);
9194 addRegexToken('h', match1to2);
9195 addRegexToken('k', match1to2);
9196 addRegexToken('HH', match1to2, match2);
9197 addRegexToken('hh', match1to2, match2);
9198 addRegexToken('kk', match1to2, match2);
9199 addRegexToken('hmm', match3to4);
9200 addRegexToken('hmmss', match5to6);
9201 addRegexToken('Hmm', match3to4);
9202 addRegexToken('Hmmss', match5to6);
9203 addParseToken(['H', 'HH'], HOUR);
9204 addParseToken(['k', 'kk'], function (input, array, config) {
9205 var kInput = toInt(input);
9206 array[HOUR] = kInput === 24 ? 0 : kInput;
9207 });
9208 addParseToken(['a', 'A'], function (input, array, config) {
9209 config._isPm = config._locale.isPM(input);
9210 config._meridiem = input;
9211 });
9212 addParseToken(['h', 'hh'], function (input, array, config) {
9213 array[HOUR] = toInt(input);
9214 getParsingFlags(config).bigHour = true;
9215 });
9216 addParseToken('hmm', function (input, array, config) {
9217 var pos = input.length - 2;
9218 array[HOUR] = toInt(input.substr(0, pos));
9219 array[MINUTE] = toInt(input.substr(pos));
9220 getParsingFlags(config).bigHour = true;
9221 });
9222 addParseToken('hmmss', function (input, array, config) {
9223 var pos1 = input.length - 4;
9224 var pos2 = input.length - 2;
9225 array[HOUR] = toInt(input.substr(0, pos1));
9226 array[MINUTE] = toInt(input.substr(pos1, 2));
9227 array[SECOND] = toInt(input.substr(pos2));
9228 getParsingFlags(config).bigHour = true;
9229 });
9230 addParseToken('Hmm', function (input, array, config) {
9231 var pos = input.length - 2;
9232 array[HOUR] = toInt(input.substr(0, pos));
9233 array[MINUTE] = toInt(input.substr(pos));
9234 });
9235 addParseToken('Hmmss', function (input, array, config) {
9236 var pos1 = input.length - 4;
9237 var pos2 = input.length - 2;
9238 array[HOUR] = toInt(input.substr(0, pos1));
9239 array[MINUTE] = toInt(input.substr(pos1, 2));
9240 array[SECOND] = toInt(input.substr(pos2));
9241 }); // LOCALES
9242
9243 function localeIsPM(input) {
9244 // IE8 Quirks Mode & IE7 Standards Mode do not allow accessing strings like arrays
9245 // Using charAt should be more compatible.
9246 return (input + '').toLowerCase().charAt(0) === 'p';
9247 }
9248
9249 var defaultLocaleMeridiemParse = /[ap]\.?m?\.?/i;
9250
9251 function localeMeridiem(hours, minutes, isLower) {
9252 if (hours > 11) {
9253 return isLower ? 'pm' : 'PM';
9254 } else {
9255 return isLower ? 'am' : 'AM';
9256 }
9257 } // MOMENTS
9258 // Setting the hour should keep the time, because the user explicitly
9259 // specified which hour they want. So trying to maintain the same hour (in
9260 // a new timezone) makes sense. Adding/subtracting hours does not follow
9261 // this rule.
9262
9263
9264 var getSetHour = makeGetSet('Hours', true);
9265 var baseConfig = {
9266 calendar: defaultCalendar,
9267 longDateFormat: defaultLongDateFormat,
9268 invalidDate: defaultInvalidDate,
9269 ordinal: defaultOrdinal,
9270 dayOfMonthOrdinalParse: defaultDayOfMonthOrdinalParse,
9271 relativeTime: defaultRelativeTime,
9272 months: defaultLocaleMonths,
9273 monthsShort: defaultLocaleMonthsShort,
9274 week: defaultLocaleWeek,
9275 weekdays: defaultLocaleWeekdays,
9276 weekdaysMin: defaultLocaleWeekdaysMin,
9277 weekdaysShort: defaultLocaleWeekdaysShort,
9278 meridiemParse: defaultLocaleMeridiemParse
9279 }; // internal storage for locale config files
9280
9281 var locales = {};
9282 var localeFamilies = {};
9283 var globalLocale;
9284
9285 function normalizeLocale(key) {
9286 return key ? key.toLowerCase().replace('_', '-') : key;
9287 } // pick the locale from the array
9288 // try ['en-au', 'en-gb'] as 'en-au', 'en-gb', 'en', as in move through the list trying each
9289 // substring from most specific to least, but move to the next array item if it's a more specific variant than the current root
9290
9291
9292 function chooseLocale(names) {
9293 var i = 0,
9294 j,
9295 next,
9296 locale,
9297 split;
9298
9299 while (i < names.length) {
9300 split = normalizeLocale(names[i]).split('-');
9301 j = split.length;
9302 next = normalizeLocale(names[i + 1]);
9303 next = next ? next.split('-') : null;
9304
9305 while (j > 0) {
9306 locale = loadLocale(split.slice(0, j).join('-'));
9307
9308 if (locale) {
9309 return locale;
9310 }
9311
9312 if (next && next.length >= j && compareArrays(split, next, true) >= j - 1) {
9313 //the next array item is better than a shallower substring of this one
9314 break;
9315 }
9316
9317 j--;
9318 }
9319
9320 i++;
9321 }
9322
9323 return globalLocale;
9324 }
9325
9326 function loadLocale(name) {
9327 var oldLocale = null; // TODO: Find a better way to register and load all the locales in Node
9328
9329 if (!locales[name] && 'object' !== 'undefined' && module && module.exports) {
9330 try {
9331 oldLocale = globalLocale._abbr;
9332 var aliasedRequire = commonjsRequire$2;
9333 aliasedRequire('./locale/' + name);
9334 getSetGlobalLocale(oldLocale);
9335 } catch (e) {}
9336 }
9337
9338 return locales[name];
9339 } // This function will load locale and then set the global locale. If
9340 // no arguments are passed in, it will simply return the current global
9341 // locale key.
9342
9343
9344 function getSetGlobalLocale(key, values) {
9345 var data;
9346
9347 if (key) {
9348 if (isUndefined(values)) {
9349 data = getLocale(key);
9350 } else {
9351 data = defineLocale(key, values);
9352 }
9353
9354 if (data) {
9355 // moment.duration._locale = moment._locale = data;
9356 globalLocale = data;
9357 } else {
9358 if (typeof console !== 'undefined' && console.warn) {
9359 //warn user if arguments are passed but the locale could not be set
9360 console.warn('Locale ' + key + ' not found. Did you forget to load it?');
9361 }
9362 }
9363 }
9364
9365 return globalLocale._abbr;
9366 }
9367
9368 function defineLocale(name, config) {
9369 if (config !== null) {
9370 var locale,
9371 parentConfig = baseConfig;
9372 config.abbr = name;
9373
9374 if (locales[name] != null) {
9375 deprecateSimple('defineLocaleOverride', 'use moment.updateLocale(localeName, config) to change ' + 'an existing locale. moment.defineLocale(localeName, ' + 'config) should only be used for creating a new locale ' + 'See http://momentjs.com/guides/#/warnings/define-locale/ for more info.');
9376 parentConfig = locales[name]._config;
9377 } else if (config.parentLocale != null) {
9378 if (locales[config.parentLocale] != null) {
9379 parentConfig = locales[config.parentLocale]._config;
9380 } else {
9381 locale = loadLocale(config.parentLocale);
9382
9383 if (locale != null) {
9384 parentConfig = locale._config;
9385 } else {
9386 if (!localeFamilies[config.parentLocale]) {
9387 localeFamilies[config.parentLocale] = [];
9388 }
9389
9390 localeFamilies[config.parentLocale].push({
9391 name: name,
9392 config: config
9393 });
9394 return null;
9395 }
9396 }
9397 }
9398
9399 locales[name] = new Locale(mergeConfigs(parentConfig, config));
9400
9401 if (localeFamilies[name]) {
9402 localeFamilies[name].forEach(function (x) {
9403 defineLocale(x.name, x.config);
9404 });
9405 } // backwards compat for now: also set the locale
9406 // make sure we set the locale AFTER all child locales have been
9407 // created, so we won't end up with the child locale set.
9408
9409
9410 getSetGlobalLocale(name);
9411 return locales[name];
9412 } else {
9413 // useful for testing
9414 delete locales[name];
9415 return null;
9416 }
9417 }
9418
9419 function updateLocale(name, config) {
9420 if (config != null) {
9421 var locale,
9422 tmpLocale,
9423 parentConfig = baseConfig; // MERGE
9424
9425 tmpLocale = loadLocale(name);
9426
9427 if (tmpLocale != null) {
9428 parentConfig = tmpLocale._config;
9429 }
9430
9431 config = mergeConfigs(parentConfig, config);
9432 locale = new Locale(config);
9433 locale.parentLocale = locales[name];
9434 locales[name] = locale; // backwards compat for now: also set the locale
9435
9436 getSetGlobalLocale(name);
9437 } else {
9438 // pass null for config to unupdate, useful for tests
9439 if (locales[name] != null) {
9440 if (locales[name].parentLocale != null) {
9441 locales[name] = locales[name].parentLocale;
9442 } else if (locales[name] != null) {
9443 delete locales[name];
9444 }
9445 }
9446 }
9447
9448 return locales[name];
9449 } // returns locale data
9450
9451
9452 function getLocale(key) {
9453 var locale;
9454
9455 if (key && key._locale && key._locale._abbr) {
9456 key = key._locale._abbr;
9457 }
9458
9459 if (!key) {
9460 return globalLocale;
9461 }
9462
9463 if (!isArray(key)) {
9464 //short-circuit everything else
9465 locale = loadLocale(key);
9466
9467 if (locale) {
9468 return locale;
9469 }
9470
9471 key = [key];
9472 }
9473
9474 return chooseLocale(key);
9475 }
9476
9477 function listLocales() {
9478 return keys(locales);
9479 }
9480
9481 function checkOverflow(m) {
9482 var overflow;
9483 var a = m._a;
9484
9485 if (a && getParsingFlags(m).overflow === -2) {
9486 overflow = a[MONTH] < 0 || a[MONTH] > 11 ? MONTH : a[DATE] < 1 || a[DATE] > daysInMonth(a[YEAR], a[MONTH]) ? DATE : a[HOUR] < 0 || a[HOUR] > 24 || a[HOUR] === 24 && (a[MINUTE] !== 0 || a[SECOND] !== 0 || a[MILLISECOND] !== 0) ? HOUR : a[MINUTE] < 0 || a[MINUTE] > 59 ? MINUTE : a[SECOND] < 0 || a[SECOND] > 59 ? SECOND : a[MILLISECOND] < 0 || a[MILLISECOND] > 999 ? MILLISECOND : -1;
9487
9488 if (getParsingFlags(m)._overflowDayOfYear && (overflow < YEAR || overflow > DATE)) {
9489 overflow = DATE;
9490 }
9491
9492 if (getParsingFlags(m)._overflowWeeks && overflow === -1) {
9493 overflow = WEEK;
9494 }
9495
9496 if (getParsingFlags(m)._overflowWeekday && overflow === -1) {
9497 overflow = WEEKDAY;
9498 }
9499
9500 getParsingFlags(m).overflow = overflow;
9501 }
9502
9503 return m;
9504 } // Pick the first defined of two or three arguments.
9505
9506
9507 function defaults(a, b, c) {
9508 if (a != null) {
9509 return a;
9510 }
9511
9512 if (b != null) {
9513 return b;
9514 }
9515
9516 return c;
9517 }
9518
9519 function currentDateArray(config) {
9520 // hooks is actually the exported moment object
9521 var nowValue = new Date(hooks.now());
9522
9523 if (config._useUTC) {
9524 return [nowValue.getUTCFullYear(), nowValue.getUTCMonth(), nowValue.getUTCDate()];
9525 }
9526
9527 return [nowValue.getFullYear(), nowValue.getMonth(), nowValue.getDate()];
9528 } // convert an array to a date.
9529 // the array should mirror the parameters below
9530 // note: all values past the year are optional and will default to the lowest possible value.
9531 // [year, month, day , hour, minute, second, millisecond]
9532
9533
9534 function configFromArray(config) {
9535 var i,
9536 date,
9537 input = [],
9538 currentDate,
9539 expectedWeekday,
9540 yearToUse;
9541
9542 if (config._d) {
9543 return;
9544 }
9545
9546 currentDate = currentDateArray(config); //compute day of the year from weeks and weekdays
9547
9548 if (config._w && config._a[DATE] == null && config._a[MONTH] == null) {
9549 dayOfYearFromWeekInfo(config);
9550 } //if the day of the year is set, figure out what it is
9551
9552
9553 if (config._dayOfYear != null) {
9554 yearToUse = defaults(config._a[YEAR], currentDate[YEAR]);
9555
9556 if (config._dayOfYear > daysInYear(yearToUse) || config._dayOfYear === 0) {
9557 getParsingFlags(config)._overflowDayOfYear = true;
9558 }
9559
9560 date = createUTCDate(yearToUse, 0, config._dayOfYear);
9561 config._a[MONTH] = date.getUTCMonth();
9562 config._a[DATE] = date.getUTCDate();
9563 } // Default to current date.
9564 // * if no year, month, day of month are given, default to today
9565 // * if day of month is given, default month and year
9566 // * if month is given, default only year
9567 // * if year is given, don't default anything
9568
9569
9570 for (i = 0; i < 3 && config._a[i] == null; ++i) {
9571 config._a[i] = input[i] = currentDate[i];
9572 } // Zero out whatever was not defaulted, including time
9573
9574
9575 for (; i < 7; i++) {
9576 config._a[i] = input[i] = config._a[i] == null ? i === 2 ? 1 : 0 : config._a[i];
9577 } // Check for 24:00:00.000
9578
9579
9580 if (config._a[HOUR] === 24 && config._a[MINUTE] === 0 && config._a[SECOND] === 0 && config._a[MILLISECOND] === 0) {
9581 config._nextDay = true;
9582 config._a[HOUR] = 0;
9583 }
9584
9585 config._d = (config._useUTC ? createUTCDate : createDate).apply(null, input);
9586 expectedWeekday = config._useUTC ? config._d.getUTCDay() : config._d.getDay(); // Apply timezone offset from input. The actual utcOffset can be changed
9587 // with parseZone.
9588
9589 if (config._tzm != null) {
9590 config._d.setUTCMinutes(config._d.getUTCMinutes() - config._tzm);
9591 }
9592
9593 if (config._nextDay) {
9594 config._a[HOUR] = 24;
9595 } // check for mismatching day of week
9596
9597
9598 if (config._w && typeof config._w.d !== 'undefined' && config._w.d !== expectedWeekday) {
9599 getParsingFlags(config).weekdayMismatch = true;
9600 }
9601 }
9602
9603 function dayOfYearFromWeekInfo(config) {
9604 var w, weekYear, week, weekday, dow, doy, temp, weekdayOverflow;
9605 w = config._w;
9606
9607 if (w.GG != null || w.W != null || w.E != null) {
9608 dow = 1;
9609 doy = 4; // TODO: We need to take the current isoWeekYear, but that depends on
9610 // how we interpret now (local, utc, fixed offset). So create
9611 // a now version of current config (take local/utc/offset flags, and
9612 // create now).
9613
9614 weekYear = defaults(w.GG, config._a[YEAR], weekOfYear(createLocal(), 1, 4).year);
9615 week = defaults(w.W, 1);
9616 weekday = defaults(w.E, 1);
9617
9618 if (weekday < 1 || weekday > 7) {
9619 weekdayOverflow = true;
9620 }
9621 } else {
9622 dow = config._locale._week.dow;
9623 doy = config._locale._week.doy;
9624 var curWeek = weekOfYear(createLocal(), dow, doy);
9625 weekYear = defaults(w.gg, config._a[YEAR], curWeek.year); // Default to current week.
9626
9627 week = defaults(w.w, curWeek.week);
9628
9629 if (w.d != null) {
9630 // weekday -- low day numbers are considered next week
9631 weekday = w.d;
9632
9633 if (weekday < 0 || weekday > 6) {
9634 weekdayOverflow = true;
9635 }
9636 } else if (w.e != null) {
9637 // local weekday -- counting starts from beginning of week
9638 weekday = w.e + dow;
9639
9640 if (w.e < 0 || w.e > 6) {
9641 weekdayOverflow = true;
9642 }
9643 } else {
9644 // default to beginning of week
9645 weekday = dow;
9646 }
9647 }
9648
9649 if (week < 1 || week > weeksInYear(weekYear, dow, doy)) {
9650 getParsingFlags(config)._overflowWeeks = true;
9651 } else if (weekdayOverflow != null) {
9652 getParsingFlags(config)._overflowWeekday = true;
9653 } else {
9654 temp = dayOfYearFromWeeks(weekYear, week, weekday, dow, doy);
9655 config._a[YEAR] = temp.year;
9656 config._dayOfYear = temp.dayOfYear;
9657 }
9658 } // iso 8601 regex
9659 // 0000-00-00 0000-W00 or 0000-W00-0 + T + 00 or 00:00 or 00:00:00 or 00:00:00.000 + +00:00 or +0000 or +00)
9660
9661
9662 var extendedIsoRegex = /^\s*((?:[+-]\d{6}|\d{4})-(?:\d\d-\d\d|W\d\d-\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?::\d\d(?::\d\d(?:[.,]\d+)?)?)?)([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?$/;
9663 var basicIsoRegex = /^\s*((?:[+-]\d{6}|\d{4})(?:\d\d\d\d|W\d\d\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?:\d\d(?:\d\d(?:[.,]\d+)?)?)?)([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?$/;
9664 var tzRegex = /Z|[+-]\d\d(?::?\d\d)?/;
9665 var isoDates = [['YYYYYY-MM-DD', /[+-]\d{6}-\d\d-\d\d/], ['YYYY-MM-DD', /\d{4}-\d\d-\d\d/], ['GGGG-[W]WW-E', /\d{4}-W\d\d-\d/], ['GGGG-[W]WW', /\d{4}-W\d\d/, false], ['YYYY-DDD', /\d{4}-\d{3}/], ['YYYY-MM', /\d{4}-\d\d/, false], ['YYYYYYMMDD', /[+-]\d{10}/], ['YYYYMMDD', /\d{8}/], // YYYYMM is NOT allowed by the standard
9666 ['GGGG[W]WWE', /\d{4}W\d{3}/], ['GGGG[W]WW', /\d{4}W\d{2}/, false], ['YYYYDDD', /\d{7}/]]; // iso time formats and regexes
9667
9668 var isoTimes = [['HH:mm:ss.SSSS', /\d\d:\d\d:\d\d\.\d+/], ['HH:mm:ss,SSSS', /\d\d:\d\d:\d\d,\d+/], ['HH:mm:ss', /\d\d:\d\d:\d\d/], ['HH:mm', /\d\d:\d\d/], ['HHmmss.SSSS', /\d\d\d\d\d\d\.\d+/], ['HHmmss,SSSS', /\d\d\d\d\d\d,\d+/], ['HHmmss', /\d\d\d\d\d\d/], ['HHmm', /\d\d\d\d/], ['HH', /\d\d/]];
9669 var aspNetJsonRegex = /^\/?Date\((\-?\d+)/i; // date from iso format
9670
9671 function configFromISO(config) {
9672 var i,
9673 l,
9674 string = config._i,
9675 match = extendedIsoRegex.exec(string) || basicIsoRegex.exec(string),
9676 allowTime,
9677 dateFormat,
9678 timeFormat,
9679 tzFormat;
9680
9681 if (match) {
9682 getParsingFlags(config).iso = true;
9683
9684 for (i = 0, l = isoDates.length; i < l; i++) {
9685 if (isoDates[i][1].exec(match[1])) {
9686 dateFormat = isoDates[i][0];
9687 allowTime = isoDates[i][2] !== false;
9688 break;
9689 }
9690 }
9691
9692 if (dateFormat == null) {
9693 config._isValid = false;
9694 return;
9695 }
9696
9697 if (match[3]) {
9698 for (i = 0, l = isoTimes.length; i < l; i++) {
9699 if (isoTimes[i][1].exec(match[3])) {
9700 // match[2] should be 'T' or space
9701 timeFormat = (match[2] || ' ') + isoTimes[i][0];
9702 break;
9703 }
9704 }
9705
9706 if (timeFormat == null) {
9707 config._isValid = false;
9708 return;
9709 }
9710 }
9711
9712 if (!allowTime && timeFormat != null) {
9713 config._isValid = false;
9714 return;
9715 }
9716
9717 if (match[4]) {
9718 if (tzRegex.exec(match[4])) {
9719 tzFormat = 'Z';
9720 } else {
9721 config._isValid = false;
9722 return;
9723 }
9724 }
9725
9726 config._f = dateFormat + (timeFormat || '') + (tzFormat || '');
9727 configFromStringAndFormat(config);
9728 } else {
9729 config._isValid = false;
9730 }
9731 } // RFC 2822 regex: For details see https://tools.ietf.org/html/rfc2822#section-3.3
9732
9733
9734 var rfc2822 = /^(?:(Mon|Tue|Wed|Thu|Fri|Sat|Sun),?\s)?(\d{1,2})\s(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\s(\d{2,4})\s(\d\d):(\d\d)(?::(\d\d))?\s(?:(UT|GMT|[ECMP][SD]T)|([Zz])|([+-]\d{4}))$/;
9735
9736 function extractFromRFC2822Strings(yearStr, monthStr, dayStr, hourStr, minuteStr, secondStr) {
9737 var result = [untruncateYear(yearStr), defaultLocaleMonthsShort.indexOf(monthStr), parseInt(dayStr, 10), parseInt(hourStr, 10), parseInt(minuteStr, 10)];
9738
9739 if (secondStr) {
9740 result.push(parseInt(secondStr, 10));
9741 }
9742
9743 return result;
9744 }
9745
9746 function untruncateYear(yearStr) {
9747 var year = parseInt(yearStr, 10);
9748
9749 if (year <= 49) {
9750 return 2000 + year;
9751 } else if (year <= 999) {
9752 return 1900 + year;
9753 }
9754
9755 return year;
9756 }
9757
9758 function preprocessRFC2822(s) {
9759 // Remove comments and folding whitespace and replace multiple-spaces with a single space
9760 return s.replace(/\([^)]*\)|[\n\t]/g, ' ').replace(/(\s\s+)/g, ' ').replace(/^\s\s*/, '').replace(/\s\s*$/, '');
9761 }
9762
9763 function checkWeekday(weekdayStr, parsedInput, config) {
9764 if (weekdayStr) {
9765 // TODO: Replace the vanilla JS Date object with an indepentent day-of-week check.
9766 var weekdayProvided = defaultLocaleWeekdaysShort.indexOf(weekdayStr),
9767 weekdayActual = new Date(parsedInput[0], parsedInput[1], parsedInput[2]).getDay();
9768
9769 if (weekdayProvided !== weekdayActual) {
9770 getParsingFlags(config).weekdayMismatch = true;
9771 config._isValid = false;
9772 return false;
9773 }
9774 }
9775
9776 return true;
9777 }
9778
9779 var obsOffsets = {
9780 UT: 0,
9781 GMT: 0,
9782 EDT: -4 * 60,
9783 EST: -5 * 60,
9784 CDT: -5 * 60,
9785 CST: -6 * 60,
9786 MDT: -6 * 60,
9787 MST: -7 * 60,
9788 PDT: -7 * 60,
9789 PST: -8 * 60
9790 };
9791
9792 function calculateOffset(obsOffset, militaryOffset, numOffset) {
9793 if (obsOffset) {
9794 return obsOffsets[obsOffset];
9795 } else if (militaryOffset) {
9796 // the only allowed military tz is Z
9797 return 0;
9798 } else {
9799 var hm = parseInt(numOffset, 10);
9800 var m = hm % 100,
9801 h = (hm - m) / 100;
9802 return h * 60 + m;
9803 }
9804 } // date and time from ref 2822 format
9805
9806
9807 function configFromRFC2822(config) {
9808 var match = rfc2822.exec(preprocessRFC2822(config._i));
9809
9810 if (match) {
9811 var parsedArray = extractFromRFC2822Strings(match[4], match[3], match[2], match[5], match[6], match[7]);
9812
9813 if (!checkWeekday(match[1], parsedArray, config)) {
9814 return;
9815 }
9816
9817 config._a = parsedArray;
9818 config._tzm = calculateOffset(match[8], match[9], match[10]);
9819 config._d = createUTCDate.apply(null, config._a);
9820
9821 config._d.setUTCMinutes(config._d.getUTCMinutes() - config._tzm);
9822
9823 getParsingFlags(config).rfc2822 = true;
9824 } else {
9825 config._isValid = false;
9826 }
9827 } // date from iso format or fallback
9828
9829
9830 function configFromString(config) {
9831 var matched = aspNetJsonRegex.exec(config._i);
9832
9833 if (matched !== null) {
9834 config._d = new Date(+matched[1]);
9835 return;
9836 }
9837
9838 configFromISO(config);
9839
9840 if (config._isValid === false) {
9841 delete config._isValid;
9842 } else {
9843 return;
9844 }
9845
9846 configFromRFC2822(config);
9847
9848 if (config._isValid === false) {
9849 delete config._isValid;
9850 } else {
9851 return;
9852 } // Final attempt, use Input Fallback
9853
9854
9855 hooks.createFromInputFallback(config);
9856 }
9857
9858 hooks.createFromInputFallback = deprecate('value provided is not in a recognized RFC2822 or ISO format. moment construction falls back to js Date(), ' + 'which is not reliable across all browsers and versions. Non RFC2822/ISO date formats are ' + 'discouraged and will be removed in an upcoming major release. Please refer to ' + 'http://momentjs.com/guides/#/warnings/js-date/ for more info.', function (config) {
9859 config._d = new Date(config._i + (config._useUTC ? ' UTC' : ''));
9860 }); // constant that refers to the ISO standard
9861
9862 hooks.ISO_8601 = function () {}; // constant that refers to the RFC 2822 form
9863
9864
9865 hooks.RFC_2822 = function () {}; // date from string and format string
9866
9867
9868 function configFromStringAndFormat(config) {
9869 // TODO: Move this to another part of the creation flow to prevent circular deps
9870 if (config._f === hooks.ISO_8601) {
9871 configFromISO(config);
9872 return;
9873 }
9874
9875 if (config._f === hooks.RFC_2822) {
9876 configFromRFC2822(config);
9877 return;
9878 }
9879
9880 config._a = [];
9881 getParsingFlags(config).empty = true; // This array is used to make a Date, either with `new Date` or `Date.UTC`
9882
9883 var string = '' + config._i,
9884 i,
9885 parsedInput,
9886 tokens,
9887 token,
9888 skipped,
9889 stringLength = string.length,
9890 totalParsedInputLength = 0;
9891 tokens = expandFormat(config._f, config._locale).match(formattingTokens) || [];
9892
9893 for (i = 0; i < tokens.length; i++) {
9894 token = tokens[i];
9895 parsedInput = (string.match(getParseRegexForToken(token, config)) || [])[0]; // console.log('token', token, 'parsedInput', parsedInput,
9896 // 'regex', getParseRegexForToken(token, config));
9897
9898 if (parsedInput) {
9899 skipped = string.substr(0, string.indexOf(parsedInput));
9900
9901 if (skipped.length > 0) {
9902 getParsingFlags(config).unusedInput.push(skipped);
9903 }
9904
9905 string = string.slice(string.indexOf(parsedInput) + parsedInput.length);
9906 totalParsedInputLength += parsedInput.length;
9907 } // don't parse if it's not a known token
9908
9909
9910 if (formatTokenFunctions[token]) {
9911 if (parsedInput) {
9912 getParsingFlags(config).empty = false;
9913 } else {
9914 getParsingFlags(config).unusedTokens.push(token);
9915 }
9916
9917 addTimeToArrayFromToken(token, parsedInput, config);
9918 } else if (config._strict && !parsedInput) {
9919 getParsingFlags(config).unusedTokens.push(token);
9920 }
9921 } // add remaining unparsed input length to the string
9922
9923
9924 getParsingFlags(config).charsLeftOver = stringLength - totalParsedInputLength;
9925
9926 if (string.length > 0) {
9927 getParsingFlags(config).unusedInput.push(string);
9928 } // clear _12h flag if hour is <= 12
9929
9930
9931 if (config._a[HOUR] <= 12 && getParsingFlags(config).bigHour === true && config._a[HOUR] > 0) {
9932 getParsingFlags(config).bigHour = undefined;
9933 }
9934
9935 getParsingFlags(config).parsedDateParts = config._a.slice(0);
9936 getParsingFlags(config).meridiem = config._meridiem; // handle meridiem
9937
9938 config._a[HOUR] = meridiemFixWrap(config._locale, config._a[HOUR], config._meridiem);
9939 configFromArray(config);
9940 checkOverflow(config);
9941 }
9942
9943 function meridiemFixWrap(locale, hour, meridiem) {
9944 var isPm;
9945
9946 if (meridiem == null) {
9947 // nothing to do
9948 return hour;
9949 }
9950
9951 if (locale.meridiemHour != null) {
9952 return locale.meridiemHour(hour, meridiem);
9953 } else if (locale.isPM != null) {
9954 // Fallback
9955 isPm = locale.isPM(meridiem);
9956
9957 if (isPm && hour < 12) {
9958 hour += 12;
9959 }
9960
9961 if (!isPm && hour === 12) {
9962 hour = 0;
9963 }
9964
9965 return hour;
9966 } else {
9967 // this is not supposed to happen
9968 return hour;
9969 }
9970 } // date from string and array of format strings
9971
9972
9973 function configFromStringAndArray(config) {
9974 var tempConfig, bestMoment, scoreToBeat, i, currentScore;
9975
9976 if (config._f.length === 0) {
9977 getParsingFlags(config).invalidFormat = true;
9978 config._d = new Date(NaN);
9979 return;
9980 }
9981
9982 for (i = 0; i < config._f.length; i++) {
9983 currentScore = 0;
9984 tempConfig = copyConfig({}, config);
9985
9986 if (config._useUTC != null) {
9987 tempConfig._useUTC = config._useUTC;
9988 }
9989
9990 tempConfig._f = config._f[i];
9991 configFromStringAndFormat(tempConfig);
9992
9993 if (!isValid(tempConfig)) {
9994 continue;
9995 } // if there is any input that was not parsed add a penalty for that format
9996
9997
9998 currentScore += getParsingFlags(tempConfig).charsLeftOver; //or tokens
9999
10000 currentScore += getParsingFlags(tempConfig).unusedTokens.length * 10;
10001 getParsingFlags(tempConfig).score = currentScore;
10002
10003 if (scoreToBeat == null || currentScore < scoreToBeat) {
10004 scoreToBeat = currentScore;
10005 bestMoment = tempConfig;
10006 }
10007 }
10008
10009 extend(config, bestMoment || tempConfig);
10010 }
10011
10012 function configFromObject(config) {
10013 if (config._d) {
10014 return;
10015 }
10016
10017 var i = normalizeObjectUnits(config._i);
10018 config._a = map([i.year, i.month, i.day || i.date, i.hour, i.minute, i.second, i.millisecond], function (obj) {
10019 return obj && parseInt(obj, 10);
10020 });
10021 configFromArray(config);
10022 }
10023
10024 function createFromConfig(config) {
10025 var res = new Moment(checkOverflow(prepareConfig(config)));
10026
10027 if (res._nextDay) {
10028 // Adding is smart enough around DST
10029 res.add(1, 'd');
10030 res._nextDay = undefined;
10031 }
10032
10033 return res;
10034 }
10035
10036 function prepareConfig(config) {
10037 var input = config._i,
10038 format = config._f;
10039 config._locale = config._locale || getLocale(config._l);
10040
10041 if (input === null || format === undefined && input === '') {
10042 return createInvalid({
10043 nullInput: true
10044 });
10045 }
10046
10047 if (typeof input === 'string') {
10048 config._i = input = config._locale.preparse(input);
10049 }
10050
10051 if (isMoment(input)) {
10052 return new Moment(checkOverflow(input));
10053 } else if (isDate(input)) {
10054 config._d = input;
10055 } else if (isArray(format)) {
10056 configFromStringAndArray(config);
10057 } else if (format) {
10058 configFromStringAndFormat(config);
10059 } else {
10060 configFromInput(config);
10061 }
10062
10063 if (!isValid(config)) {
10064 config._d = null;
10065 }
10066
10067 return config;
10068 }
10069
10070 function configFromInput(config) {
10071 var input = config._i;
10072
10073 if (isUndefined(input)) {
10074 config._d = new Date(hooks.now());
10075 } else if (isDate(input)) {
10076 config._d = new Date(input.valueOf());
10077 } else if (typeof input === 'string') {
10078 configFromString(config);
10079 } else if (isArray(input)) {
10080 config._a = map(input.slice(0), function (obj) {
10081 return parseInt(obj, 10);
10082 });
10083 configFromArray(config);
10084 } else if (isObject(input)) {
10085 configFromObject(config);
10086 } else if (isNumber(input)) {
10087 // from milliseconds
10088 config._d = new Date(input);
10089 } else {
10090 hooks.createFromInputFallback(config);
10091 }
10092 }
10093
10094 function createLocalOrUTC(input, format, locale, strict, isUTC) {
10095 var c = {};
10096
10097 if (locale === true || locale === false) {
10098 strict = locale;
10099 locale = undefined;
10100 }
10101
10102 if (isObject(input) && isObjectEmpty(input) || isArray(input) && input.length === 0) {
10103 input = undefined;
10104 } // object construction must be done this way.
10105 // https://github.com/moment/moment/issues/1423
10106
10107
10108 c._isAMomentObject = true;
10109 c._useUTC = c._isUTC = isUTC;
10110 c._l = locale;
10111 c._i = input;
10112 c._f = format;
10113 c._strict = strict;
10114 return createFromConfig(c);
10115 }
10116
10117 function createLocal(input, format, locale, strict) {
10118 return createLocalOrUTC(input, format, locale, strict, false);
10119 }
10120
10121 var prototypeMin = deprecate('moment().min is deprecated, use moment.max instead. http://momentjs.com/guides/#/warnings/min-max/', function () {
10122 var other = createLocal.apply(null, arguments);
10123
10124 if (this.isValid() && other.isValid()) {
10125 return other < this ? this : other;
10126 } else {
10127 return createInvalid();
10128 }
10129 });
10130 var prototypeMax = deprecate('moment().max is deprecated, use moment.min instead. http://momentjs.com/guides/#/warnings/min-max/', function () {
10131 var other = createLocal.apply(null, arguments);
10132
10133 if (this.isValid() && other.isValid()) {
10134 return other > this ? this : other;
10135 } else {
10136 return createInvalid();
10137 }
10138 }); // Pick a moment m from moments so that m[fn](other) is true for all
10139 // other. This relies on the function fn to be transitive.
10140 //
10141 // moments should either be an array of moment objects or an array, whose
10142 // first element is an array of moment objects.
10143
10144 function pickBy(fn, moments) {
10145 var res, i;
10146
10147 if (moments.length === 1 && isArray(moments[0])) {
10148 moments = moments[0];
10149 }
10150
10151 if (!moments.length) {
10152 return createLocal();
10153 }
10154
10155 res = moments[0];
10156
10157 for (i = 1; i < moments.length; ++i) {
10158 if (!moments[i].isValid() || moments[i][fn](res)) {
10159 res = moments[i];
10160 }
10161 }
10162
10163 return res;
10164 } // TODO: Use [].sort instead?
10165
10166
10167 function min() {
10168 var args = [].slice.call(arguments, 0);
10169 return pickBy('isBefore', args);
10170 }
10171
10172 function max() {
10173 var args = [].slice.call(arguments, 0);
10174 return pickBy('isAfter', args);
10175 }
10176
10177 var now = function () {
10178 return Date.now ? Date.now() : +new Date();
10179 };
10180
10181 var ordering = ['year', 'quarter', 'month', 'week', 'day', 'hour', 'minute', 'second', 'millisecond'];
10182
10183 function isDurationValid(m) {
10184 for (var key in m) {
10185 if (!(indexOf.call(ordering, key) !== -1 && (m[key] == null || !isNaN(m[key])))) {
10186 return false;
10187 }
10188 }
10189
10190 var unitHasDecimal = false;
10191
10192 for (var i = 0; i < ordering.length; ++i) {
10193 if (m[ordering[i]]) {
10194 if (unitHasDecimal) {
10195 return false; // only allow non-integers for smallest unit
10196 }
10197
10198 if (parseFloat(m[ordering[i]]) !== toInt(m[ordering[i]])) {
10199 unitHasDecimal = true;
10200 }
10201 }
10202 }
10203
10204 return true;
10205 }
10206
10207 function isValid$1() {
10208 return this._isValid;
10209 }
10210
10211 function createInvalid$1() {
10212 return createDuration(NaN);
10213 }
10214
10215 function Duration(duration) {
10216 var normalizedInput = normalizeObjectUnits(duration),
10217 years = normalizedInput.year || 0,
10218 quarters = normalizedInput.quarter || 0,
10219 months = normalizedInput.month || 0,
10220 weeks = normalizedInput.week || normalizedInput.isoWeek || 0,
10221 days = normalizedInput.day || 0,
10222 hours = normalizedInput.hour || 0,
10223 minutes = normalizedInput.minute || 0,
10224 seconds = normalizedInput.second || 0,
10225 milliseconds = normalizedInput.millisecond || 0;
10226 this._isValid = isDurationValid(normalizedInput); // representation for dateAddRemove
10227
10228 this._milliseconds = +milliseconds + seconds * 1e3 + // 1000
10229 minutes * 6e4 + // 1000 * 60
10230 hours * 1000 * 60 * 60; //using 1000 * 60 * 60 instead of 36e5 to avoid floating point rounding errors https://github.com/moment/moment/issues/2978
10231 // Because of dateAddRemove treats 24 hours as different from a
10232 // day when working around DST, we need to store them separately
10233
10234 this._days = +days + weeks * 7; // It is impossible to translate months into days without knowing
10235 // which months you are are talking about, so we have to store
10236 // it separately.
10237
10238 this._months = +months + quarters * 3 + years * 12;
10239 this._data = {};
10240 this._locale = getLocale();
10241
10242 this._bubble();
10243 }
10244
10245 function isDuration(obj) {
10246 return obj instanceof Duration;
10247 }
10248
10249 function absRound(number) {
10250 if (number < 0) {
10251 return Math.round(-1 * number) * -1;
10252 } else {
10253 return Math.round(number);
10254 }
10255 } // FORMATTING
10256
10257
10258 function offset(token, separator) {
10259 addFormatToken(token, 0, 0, function () {
10260 var offset = this.utcOffset();
10261 var sign = '+';
10262
10263 if (offset < 0) {
10264 offset = -offset;
10265 sign = '-';
10266 }
10267
10268 return sign + zeroFill(~~(offset / 60), 2) + separator + zeroFill(~~offset % 60, 2);
10269 });
10270 }
10271
10272 offset('Z', ':');
10273 offset('ZZ', ''); // PARSING
10274
10275 addRegexToken('Z', matchShortOffset);
10276 addRegexToken('ZZ', matchShortOffset);
10277 addParseToken(['Z', 'ZZ'], function (input, array, config) {
10278 config._useUTC = true;
10279 config._tzm = offsetFromString(matchShortOffset, input);
10280 }); // HELPERS
10281 // timezone chunker
10282 // '+10:00' > ['10', '00']
10283 // '-1530' > ['-15', '30']
10284
10285 var chunkOffset = /([\+\-]|\d\d)/gi;
10286
10287 function offsetFromString(matcher, string) {
10288 var matches = (string || '').match(matcher);
10289
10290 if (matches === null) {
10291 return null;
10292 }
10293
10294 var chunk = matches[matches.length - 1] || [];
10295 var parts = (chunk + '').match(chunkOffset) || ['-', 0, 0];
10296 var minutes = +(parts[1] * 60) + toInt(parts[2]);
10297 return minutes === 0 ? 0 : parts[0] === '+' ? minutes : -minutes;
10298 } // Return a moment from input, that is local/utc/zone equivalent to model.
10299
10300
10301 function cloneWithOffset(input, model) {
10302 var res, diff;
10303
10304 if (model._isUTC) {
10305 res = model.clone();
10306 diff = (isMoment(input) || isDate(input) ? input.valueOf() : createLocal(input).valueOf()) - res.valueOf(); // Use low-level api, because this fn is low-level api.
10307
10308 res._d.setTime(res._d.valueOf() + diff);
10309
10310 hooks.updateOffset(res, false);
10311 return res;
10312 } else {
10313 return createLocal(input).local();
10314 }
10315 }
10316
10317 function getDateOffset(m) {
10318 // On Firefox.24 Date#getTimezoneOffset returns a floating point.
10319 // https://github.com/moment/moment/pull/1871
10320 return -Math.round(m._d.getTimezoneOffset() / 15) * 15;
10321 } // HOOKS
10322 // This function will be called whenever a moment is mutated.
10323 // It is intended to keep the offset in sync with the timezone.
10324
10325
10326 hooks.updateOffset = function () {}; // MOMENTS
10327 // keepLocalTime = true means only change the timezone, without
10328 // affecting the local hour. So 5:31:26 +0300 --[utcOffset(2, true)]-->
10329 // 5:31:26 +0200 It is possible that 5:31:26 doesn't exist with offset
10330 // +0200, so we adjust the time as needed, to be valid.
10331 //
10332 // Keeping the time actually adds/subtracts (one hour)
10333 // from the actual represented time. That is why we call updateOffset
10334 // a second time. In case it wants us to change the offset again
10335 // _changeInProgress == true case, then we have to adjust, because
10336 // there is no such time in the given timezone.
10337
10338
10339 function getSetOffset(input, keepLocalTime, keepMinutes) {
10340 var offset = this._offset || 0,
10341 localAdjust;
10342
10343 if (!this.isValid()) {
10344 return input != null ? this : NaN;
10345 }
10346
10347 if (input != null) {
10348 if (typeof input === 'string') {
10349 input = offsetFromString(matchShortOffset, input);
10350
10351 if (input === null) {
10352 return this;
10353 }
10354 } else if (Math.abs(input) < 16 && !keepMinutes) {
10355 input = input * 60;
10356 }
10357
10358 if (!this._isUTC && keepLocalTime) {
10359 localAdjust = getDateOffset(this);
10360 }
10361
10362 this._offset = input;
10363 this._isUTC = true;
10364
10365 if (localAdjust != null) {
10366 this.add(localAdjust, 'm');
10367 }
10368
10369 if (offset !== input) {
10370 if (!keepLocalTime || this._changeInProgress) {
10371 addSubtract(this, createDuration(input - offset, 'm'), 1, false);
10372 } else if (!this._changeInProgress) {
10373 this._changeInProgress = true;
10374 hooks.updateOffset(this, true);
10375 this._changeInProgress = null;
10376 }
10377 }
10378
10379 return this;
10380 } else {
10381 return this._isUTC ? offset : getDateOffset(this);
10382 }
10383 }
10384
10385 function getSetZone(input, keepLocalTime) {
10386 if (input != null) {
10387 if (typeof input !== 'string') {
10388 input = -input;
10389 }
10390
10391 this.utcOffset(input, keepLocalTime);
10392 return this;
10393 } else {
10394 return -this.utcOffset();
10395 }
10396 }
10397
10398 function setOffsetToUTC(keepLocalTime) {
10399 return this.utcOffset(0, keepLocalTime);
10400 }
10401
10402 function setOffsetToLocal(keepLocalTime) {
10403 if (this._isUTC) {
10404 this.utcOffset(0, keepLocalTime);
10405 this._isUTC = false;
10406
10407 if (keepLocalTime) {
10408 this.subtract(getDateOffset(this), 'm');
10409 }
10410 }
10411
10412 return this;
10413 }
10414
10415 function setOffsetToParsedOffset() {
10416 if (this._tzm != null) {
10417 this.utcOffset(this._tzm, false, true);
10418 } else if (typeof this._i === 'string') {
10419 var tZone = offsetFromString(matchOffset, this._i);
10420
10421 if (tZone != null) {
10422 this.utcOffset(tZone);
10423 } else {
10424 this.utcOffset(0, true);
10425 }
10426 }
10427
10428 return this;
10429 }
10430
10431 function hasAlignedHourOffset(input) {
10432 if (!this.isValid()) {
10433 return false;
10434 }
10435
10436 input = input ? createLocal(input).utcOffset() : 0;
10437 return (this.utcOffset() - input) % 60 === 0;
10438 }
10439
10440 function isDaylightSavingTime() {
10441 return this.utcOffset() > this.clone().month(0).utcOffset() || this.utcOffset() > this.clone().month(5).utcOffset();
10442 }
10443
10444 function isDaylightSavingTimeShifted() {
10445 if (!isUndefined(this._isDSTShifted)) {
10446 return this._isDSTShifted;
10447 }
10448
10449 var c = {};
10450 copyConfig(c, this);
10451 c = prepareConfig(c);
10452
10453 if (c._a) {
10454 var other = c._isUTC ? createUTC(c._a) : createLocal(c._a);
10455 this._isDSTShifted = this.isValid() && compareArrays(c._a, other.toArray()) > 0;
10456 } else {
10457 this._isDSTShifted = false;
10458 }
10459
10460 return this._isDSTShifted;
10461 }
10462
10463 function isLocal() {
10464 return this.isValid() ? !this._isUTC : false;
10465 }
10466
10467 function isUtcOffset() {
10468 return this.isValid() ? this._isUTC : false;
10469 }
10470
10471 function isUtc() {
10472 return this.isValid() ? this._isUTC && this._offset === 0 : false;
10473 } // ASP.NET json date format regex
10474
10475
10476 var aspNetRegex = /^(\-|\+)?(?:(\d*)[. ])?(\d+)\:(\d+)(?:\:(\d+)(\.\d*)?)?$/; // from http://docs.closure-library.googlecode.com/git/closure_goog_date_date.js.source.html
10477 // somewhat more in line with 4.4.3.2 2004 spec, but allows decimal anywhere
10478 // and further modified to allow for strings containing both week and day
10479
10480 var isoRegex = /^(-|\+)?P(?:([-+]?[0-9,.]*)Y)?(?:([-+]?[0-9,.]*)M)?(?:([-+]?[0-9,.]*)W)?(?:([-+]?[0-9,.]*)D)?(?:T(?:([-+]?[0-9,.]*)H)?(?:([-+]?[0-9,.]*)M)?(?:([-+]?[0-9,.]*)S)?)?$/;
10481
10482 function createDuration(input, key) {
10483 var duration = input,
10484 // matching against regexp is expensive, do it on demand
10485 match = null,
10486 sign,
10487 ret,
10488 diffRes;
10489
10490 if (isDuration(input)) {
10491 duration = {
10492 ms: input._milliseconds,
10493 d: input._days,
10494 M: input._months
10495 };
10496 } else if (isNumber(input)) {
10497 duration = {};
10498
10499 if (key) {
10500 duration[key] = input;
10501 } else {
10502 duration.milliseconds = input;
10503 }
10504 } else if (!!(match = aspNetRegex.exec(input))) {
10505 sign = match[1] === '-' ? -1 : 1;
10506 duration = {
10507 y: 0,
10508 d: toInt(match[DATE]) * sign,
10509 h: toInt(match[HOUR]) * sign,
10510 m: toInt(match[MINUTE]) * sign,
10511 s: toInt(match[SECOND]) * sign,
10512 ms: toInt(absRound(match[MILLISECOND] * 1000)) * sign // the millisecond decimal point is included in the match
10513
10514 };
10515 } else if (!!(match = isoRegex.exec(input))) {
10516 sign = match[1] === '-' ? -1 : 1;
10517 duration = {
10518 y: parseIso(match[2], sign),
10519 M: parseIso(match[3], sign),
10520 w: parseIso(match[4], sign),
10521 d: parseIso(match[5], sign),
10522 h: parseIso(match[6], sign),
10523 m: parseIso(match[7], sign),
10524 s: parseIso(match[8], sign)
10525 };
10526 } else if (duration == null) {
10527 // checks for null or undefined
10528 duration = {};
10529 } else if (typeof duration === 'object' && ('from' in duration || 'to' in duration)) {
10530 diffRes = momentsDifference(createLocal(duration.from), createLocal(duration.to));
10531 duration = {};
10532 duration.ms = diffRes.milliseconds;
10533 duration.M = diffRes.months;
10534 }
10535
10536 ret = new Duration(duration);
10537
10538 if (isDuration(input) && hasOwnProp(input, '_locale')) {
10539 ret._locale = input._locale;
10540 }
10541
10542 return ret;
10543 }
10544
10545 createDuration.fn = Duration.prototype;
10546 createDuration.invalid = createInvalid$1;
10547
10548 function parseIso(inp, sign) {
10549 // We'd normally use ~~inp for this, but unfortunately it also
10550 // converts floats to ints.
10551 // inp may be undefined, so careful calling replace on it.
10552 var res = inp && parseFloat(inp.replace(',', '.')); // apply sign while we're at it
10553
10554 return (isNaN(res) ? 0 : res) * sign;
10555 }
10556
10557 function positiveMomentsDifference(base, other) {
10558 var res = {};
10559 res.months = other.month() - base.month() + (other.year() - base.year()) * 12;
10560
10561 if (base.clone().add(res.months, 'M').isAfter(other)) {
10562 --res.months;
10563 }
10564
10565 res.milliseconds = +other - +base.clone().add(res.months, 'M');
10566 return res;
10567 }
10568
10569 function momentsDifference(base, other) {
10570 var res;
10571
10572 if (!(base.isValid() && other.isValid())) {
10573 return {
10574 milliseconds: 0,
10575 months: 0
10576 };
10577 }
10578
10579 other = cloneWithOffset(other, base);
10580
10581 if (base.isBefore(other)) {
10582 res = positiveMomentsDifference(base, other);
10583 } else {
10584 res = positiveMomentsDifference(other, base);
10585 res.milliseconds = -res.milliseconds;
10586 res.months = -res.months;
10587 }
10588
10589 return res;
10590 } // TODO: remove 'name' arg after deprecation is removed
10591
10592
10593 function createAdder(direction, name) {
10594 return function (val, period) {
10595 var dur, tmp; //invert the arguments, but complain about it
10596
10597 if (period !== null && !isNaN(+period)) {
10598 deprecateSimple(name, 'moment().' + name + '(period, number) is deprecated. Please use moment().' + name + '(number, period). ' + 'See http://momentjs.com/guides/#/warnings/add-inverted-param/ for more info.');
10599 tmp = val;
10600 val = period;
10601 period = tmp;
10602 }
10603
10604 val = typeof val === 'string' ? +val : val;
10605 dur = createDuration(val, period);
10606 addSubtract(this, dur, direction);
10607 return this;
10608 };
10609 }
10610
10611 function addSubtract(mom, duration, isAdding, updateOffset) {
10612 var milliseconds = duration._milliseconds,
10613 days = absRound(duration._days),
10614 months = absRound(duration._months);
10615
10616 if (!mom.isValid()) {
10617 // No op
10618 return;
10619 }
10620
10621 updateOffset = updateOffset == null ? true : updateOffset;
10622
10623 if (months) {
10624 setMonth(mom, get(mom, 'Month') + months * isAdding);
10625 }
10626
10627 if (days) {
10628 set$1(mom, 'Date', get(mom, 'Date') + days * isAdding);
10629 }
10630
10631 if (milliseconds) {
10632 mom._d.setTime(mom._d.valueOf() + milliseconds * isAdding);
10633 }
10634
10635 if (updateOffset) {
10636 hooks.updateOffset(mom, days || months);
10637 }
10638 }
10639
10640 var add = createAdder(1, 'add');
10641 var subtract = createAdder(-1, 'subtract');
10642
10643 function getCalendarFormat(myMoment, now) {
10644 var diff = myMoment.diff(now, 'days', true);
10645 return diff < -6 ? 'sameElse' : diff < -1 ? 'lastWeek' : diff < 0 ? 'lastDay' : diff < 1 ? 'sameDay' : diff < 2 ? 'nextDay' : diff < 7 ? 'nextWeek' : 'sameElse';
10646 }
10647
10648 function calendar$1(time, formats) {
10649 // We want to compare the start of today, vs this.
10650 // Getting start-of-today depends on whether we're local/utc/offset or not.
10651 var now = time || createLocal(),
10652 sod = cloneWithOffset(now, this).startOf('day'),
10653 format = hooks.calendarFormat(this, sod) || 'sameElse';
10654 var output = formats && (isFunction(formats[format]) ? formats[format].call(this, now) : formats[format]);
10655 return this.format(output || this.localeData().calendar(format, this, createLocal(now)));
10656 }
10657
10658 function clone() {
10659 return new Moment(this);
10660 }
10661
10662 function isAfter(input, units) {
10663 var localInput = isMoment(input) ? input : createLocal(input);
10664
10665 if (!(this.isValid() && localInput.isValid())) {
10666 return false;
10667 }
10668
10669 units = normalizeUnits(units) || 'millisecond';
10670
10671 if (units === 'millisecond') {
10672 return this.valueOf() > localInput.valueOf();
10673 } else {
10674 return localInput.valueOf() < this.clone().startOf(units).valueOf();
10675 }
10676 }
10677
10678 function isBefore(input, units) {
10679 var localInput = isMoment(input) ? input : createLocal(input);
10680
10681 if (!(this.isValid() && localInput.isValid())) {
10682 return false;
10683 }
10684
10685 units = normalizeUnits(units) || 'millisecond';
10686
10687 if (units === 'millisecond') {
10688 return this.valueOf() < localInput.valueOf();
10689 } else {
10690 return this.clone().endOf(units).valueOf() < localInput.valueOf();
10691 }
10692 }
10693
10694 function isBetween(from, to, units, inclusivity) {
10695 var localFrom = isMoment(from) ? from : createLocal(from),
10696 localTo = isMoment(to) ? to : createLocal(to);
10697
10698 if (!(this.isValid() && localFrom.isValid() && localTo.isValid())) {
10699 return false;
10700 }
10701
10702 inclusivity = inclusivity || '()';
10703 return (inclusivity[0] === '(' ? this.isAfter(localFrom, units) : !this.isBefore(localFrom, units)) && (inclusivity[1] === ')' ? this.isBefore(localTo, units) : !this.isAfter(localTo, units));
10704 }
10705
10706 function isSame(input, units) {
10707 var localInput = isMoment(input) ? input : createLocal(input),
10708 inputMs;
10709
10710 if (!(this.isValid() && localInput.isValid())) {
10711 return false;
10712 }
10713
10714 units = normalizeUnits(units) || 'millisecond';
10715
10716 if (units === 'millisecond') {
10717 return this.valueOf() === localInput.valueOf();
10718 } else {
10719 inputMs = localInput.valueOf();
10720 return this.clone().startOf(units).valueOf() <= inputMs && inputMs <= this.clone().endOf(units).valueOf();
10721 }
10722 }
10723
10724 function isSameOrAfter(input, units) {
10725 return this.isSame(input, units) || this.isAfter(input, units);
10726 }
10727
10728 function isSameOrBefore(input, units) {
10729 return this.isSame(input, units) || this.isBefore(input, units);
10730 }
10731
10732 function diff(input, units, asFloat) {
10733 var that, zoneDelta, output;
10734
10735 if (!this.isValid()) {
10736 return NaN;
10737 }
10738
10739 that = cloneWithOffset(input, this);
10740
10741 if (!that.isValid()) {
10742 return NaN;
10743 }
10744
10745 zoneDelta = (that.utcOffset() - this.utcOffset()) * 6e4;
10746 units = normalizeUnits(units);
10747
10748 switch (units) {
10749 case 'year':
10750 output = monthDiff(this, that) / 12;
10751 break;
10752
10753 case 'month':
10754 output = monthDiff(this, that);
10755 break;
10756
10757 case 'quarter':
10758 output = monthDiff(this, that) / 3;
10759 break;
10760
10761 case 'second':
10762 output = (this - that) / 1e3;
10763 break;
10764 // 1000
10765
10766 case 'minute':
10767 output = (this - that) / 6e4;
10768 break;
10769 // 1000 * 60
10770
10771 case 'hour':
10772 output = (this - that) / 36e5;
10773 break;
10774 // 1000 * 60 * 60
10775
10776 case 'day':
10777 output = (this - that - zoneDelta) / 864e5;
10778 break;
10779 // 1000 * 60 * 60 * 24, negate dst
10780
10781 case 'week':
10782 output = (this - that - zoneDelta) / 6048e5;
10783 break;
10784 // 1000 * 60 * 60 * 24 * 7, negate dst
10785
10786 default:
10787 output = this - that;
10788 }
10789
10790 return asFloat ? output : absFloor(output);
10791 }
10792
10793 function monthDiff(a, b) {
10794 // difference in months
10795 var wholeMonthDiff = (b.year() - a.year()) * 12 + (b.month() - a.month()),
10796 // b is in (anchor - 1 month, anchor + 1 month)
10797 anchor = a.clone().add(wholeMonthDiff, 'months'),
10798 anchor2,
10799 adjust;
10800
10801 if (b - anchor < 0) {
10802 anchor2 = a.clone().add(wholeMonthDiff - 1, 'months'); // linear across the month
10803
10804 adjust = (b - anchor) / (anchor - anchor2);
10805 } else {
10806 anchor2 = a.clone().add(wholeMonthDiff + 1, 'months'); // linear across the month
10807
10808 adjust = (b - anchor) / (anchor2 - anchor);
10809 } //check for negative zero, return zero if negative zero
10810
10811
10812 return -(wholeMonthDiff + adjust) || 0;
10813 }
10814
10815 hooks.defaultFormat = 'YYYY-MM-DDTHH:mm:ssZ';
10816 hooks.defaultFormatUtc = 'YYYY-MM-DDTHH:mm:ss[Z]';
10817
10818 function toString() {
10819 return this.clone().locale('en').format('ddd MMM DD YYYY HH:mm:ss [GMT]ZZ');
10820 }
10821
10822 function toISOString(keepOffset) {
10823 if (!this.isValid()) {
10824 return null;
10825 }
10826
10827 var utc = keepOffset !== true;
10828 var m = utc ? this.clone().utc() : this;
10829
10830 if (m.year() < 0 || m.year() > 9999) {
10831 return formatMoment(m, utc ? 'YYYYYY-MM-DD[T]HH:mm:ss.SSS[Z]' : 'YYYYYY-MM-DD[T]HH:mm:ss.SSSZ');
10832 }
10833
10834 if (isFunction(Date.prototype.toISOString)) {
10835 // native implementation is ~50x faster, use it when we can
10836 if (utc) {
10837 return this.toDate().toISOString();
10838 } else {
10839 return new Date(this.valueOf() + this.utcOffset() * 60 * 1000).toISOString().replace('Z', formatMoment(m, 'Z'));
10840 }
10841 }
10842
10843 return formatMoment(m, utc ? 'YYYY-MM-DD[T]HH:mm:ss.SSS[Z]' : 'YYYY-MM-DD[T]HH:mm:ss.SSSZ');
10844 }
10845 /**
10846 * Return a human readable representation of a moment that can
10847 * also be evaluated to get a new moment which is the same
10848 *
10849 * @link https://nodejs.org/dist/latest/docs/api/util.html#util_custom_inspect_function_on_objects
10850 */
10851
10852
10853 function inspect() {
10854 if (!this.isValid()) {
10855 return 'moment.invalid(/* ' + this._i + ' */)';
10856 }
10857
10858 var func = 'moment';
10859 var zone = '';
10860
10861 if (!this.isLocal()) {
10862 func = this.utcOffset() === 0 ? 'moment.utc' : 'moment.parseZone';
10863 zone = 'Z';
10864 }
10865
10866 var prefix = '[' + func + '("]';
10867 var year = 0 <= this.year() && this.year() <= 9999 ? 'YYYY' : 'YYYYYY';
10868 var datetime = '-MM-DD[T]HH:mm:ss.SSS';
10869 var suffix = zone + '[")]';
10870 return this.format(prefix + year + datetime + suffix);
10871 }
10872
10873 function format(inputString) {
10874 if (!inputString) {
10875 inputString = this.isUtc() ? hooks.defaultFormatUtc : hooks.defaultFormat;
10876 }
10877
10878 var output = formatMoment(this, inputString);
10879 return this.localeData().postformat(output);
10880 }
10881
10882 function from(time, withoutSuffix) {
10883 if (this.isValid() && (isMoment(time) && time.isValid() || createLocal(time).isValid())) {
10884 return createDuration({
10885 to: this,
10886 from: time
10887 }).locale(this.locale()).humanize(!withoutSuffix);
10888 } else {
10889 return this.localeData().invalidDate();
10890 }
10891 }
10892
10893 function fromNow(withoutSuffix) {
10894 return this.from(createLocal(), withoutSuffix);
10895 }
10896
10897 function to(time, withoutSuffix) {
10898 if (this.isValid() && (isMoment(time) && time.isValid() || createLocal(time).isValid())) {
10899 return createDuration({
10900 from: this,
10901 to: time
10902 }).locale(this.locale()).humanize(!withoutSuffix);
10903 } else {
10904 return this.localeData().invalidDate();
10905 }
10906 }
10907
10908 function toNow(withoutSuffix) {
10909 return this.to(createLocal(), withoutSuffix);
10910 } // If passed a locale key, it will set the locale for this
10911 // instance. Otherwise, it will return the locale configuration
10912 // variables for this instance.
10913
10914
10915 function locale(key) {
10916 var newLocaleData;
10917
10918 if (key === undefined) {
10919 return this._locale._abbr;
10920 } else {
10921 newLocaleData = getLocale(key);
10922
10923 if (newLocaleData != null) {
10924 this._locale = newLocaleData;
10925 }
10926
10927 return this;
10928 }
10929 }
10930
10931 var lang = deprecate('moment().lang() is deprecated. Instead, use moment().localeData() to get the language configuration. Use moment().locale() to change languages.', function (key) {
10932 if (key === undefined) {
10933 return this.localeData();
10934 } else {
10935 return this.locale(key);
10936 }
10937 });
10938
10939 function localeData() {
10940 return this._locale;
10941 }
10942
10943 var MS_PER_SECOND = 1000;
10944 var MS_PER_MINUTE = 60 * MS_PER_SECOND;
10945 var MS_PER_HOUR = 60 * MS_PER_MINUTE;
10946 var MS_PER_400_YEARS = (365 * 400 + 97) * 24 * MS_PER_HOUR; // actual modulo - handles negative numbers (for dates before 1970):
10947
10948 function mod$1(dividend, divisor) {
10949 return (dividend % divisor + divisor) % divisor;
10950 }
10951
10952 function localStartOfDate(y, m, d) {
10953 // the date constructor remaps years 0-99 to 1900-1999
10954 if (y < 100 && y >= 0) {
10955 // preserve leap years using a full 400 year cycle, then reset
10956 return new Date(y + 400, m, d) - MS_PER_400_YEARS;
10957 } else {
10958 return new Date(y, m, d).valueOf();
10959 }
10960 }
10961
10962 function utcStartOfDate(y, m, d) {
10963 // Date.UTC remaps years 0-99 to 1900-1999
10964 if (y < 100 && y >= 0) {
10965 // preserve leap years using a full 400 year cycle, then reset
10966 return Date.UTC(y + 400, m, d) - MS_PER_400_YEARS;
10967 } else {
10968 return Date.UTC(y, m, d);
10969 }
10970 }
10971
10972 function startOf(units) {
10973 var time;
10974 units = normalizeUnits(units);
10975
10976 if (units === undefined || units === 'millisecond' || !this.isValid()) {
10977 return this;
10978 }
10979
10980 var startOfDate = this._isUTC ? utcStartOfDate : localStartOfDate;
10981
10982 switch (units) {
10983 case 'year':
10984 time = startOfDate(this.year(), 0, 1);
10985 break;
10986
10987 case 'quarter':
10988 time = startOfDate(this.year(), this.month() - this.month() % 3, 1);
10989 break;
10990
10991 case 'month':
10992 time = startOfDate(this.year(), this.month(), 1);
10993 break;
10994
10995 case 'week':
10996 time = startOfDate(this.year(), this.month(), this.date() - this.weekday());
10997 break;
10998
10999 case 'isoWeek':
11000 time = startOfDate(this.year(), this.month(), this.date() - (this.isoWeekday() - 1));
11001 break;
11002
11003 case 'day':
11004 case 'date':
11005 time = startOfDate(this.year(), this.month(), this.date());
11006 break;
11007
11008 case 'hour':
11009 time = this._d.valueOf();
11010 time -= mod$1(time + (this._isUTC ? 0 : this.utcOffset() * MS_PER_MINUTE), MS_PER_HOUR);
11011 break;
11012
11013 case 'minute':
11014 time = this._d.valueOf();
11015 time -= mod$1(time, MS_PER_MINUTE);
11016 break;
11017
11018 case 'second':
11019 time = this._d.valueOf();
11020 time -= mod$1(time, MS_PER_SECOND);
11021 break;
11022 }
11023
11024 this._d.setTime(time);
11025
11026 hooks.updateOffset(this, true);
11027 return this;
11028 }
11029
11030 function endOf(units) {
11031 var time;
11032 units = normalizeUnits(units);
11033
11034 if (units === undefined || units === 'millisecond' || !this.isValid()) {
11035 return this;
11036 }
11037
11038 var startOfDate = this._isUTC ? utcStartOfDate : localStartOfDate;
11039
11040 switch (units) {
11041 case 'year':
11042 time = startOfDate(this.year() + 1, 0, 1) - 1;
11043 break;
11044
11045 case 'quarter':
11046 time = startOfDate(this.year(), this.month() - this.month() % 3 + 3, 1) - 1;
11047 break;
11048
11049 case 'month':
11050 time = startOfDate(this.year(), this.month() + 1, 1) - 1;
11051 break;
11052
11053 case 'week':
11054 time = startOfDate(this.year(), this.month(), this.date() - this.weekday() + 7) - 1;
11055 break;
11056
11057 case 'isoWeek':
11058 time = startOfDate(this.year(), this.month(), this.date() - (this.isoWeekday() - 1) + 7) - 1;
11059 break;
11060
11061 case 'day':
11062 case 'date':
11063 time = startOfDate(this.year(), this.month(), this.date() + 1) - 1;
11064 break;
11065
11066 case 'hour':
11067 time = this._d.valueOf();
11068 time += MS_PER_HOUR - mod$1(time + (this._isUTC ? 0 : this.utcOffset() * MS_PER_MINUTE), MS_PER_HOUR) - 1;
11069 break;
11070
11071 case 'minute':
11072 time = this._d.valueOf();
11073 time += MS_PER_MINUTE - mod$1(time, MS_PER_MINUTE) - 1;
11074 break;
11075
11076 case 'second':
11077 time = this._d.valueOf();
11078 time += MS_PER_SECOND - mod$1(time, MS_PER_SECOND) - 1;
11079 break;
11080 }
11081
11082 this._d.setTime(time);
11083
11084 hooks.updateOffset(this, true);
11085 return this;
11086 }
11087
11088 function valueOf() {
11089 return this._d.valueOf() - (this._offset || 0) * 60000;
11090 }
11091
11092 function unix() {
11093 return Math.floor(this.valueOf() / 1000);
11094 }
11095
11096 function toDate() {
11097 return new Date(this.valueOf());
11098 }
11099
11100 function toArray() {
11101 var m = this;
11102 return [m.year(), m.month(), m.date(), m.hour(), m.minute(), m.second(), m.millisecond()];
11103 }
11104
11105 function toObject() {
11106 var m = this;
11107 return {
11108 years: m.year(),
11109 months: m.month(),
11110 date: m.date(),
11111 hours: m.hours(),
11112 minutes: m.minutes(),
11113 seconds: m.seconds(),
11114 milliseconds: m.milliseconds()
11115 };
11116 }
11117
11118 function toJSON() {
11119 // new Date(NaN).toJSON() === null
11120 return this.isValid() ? this.toISOString() : null;
11121 }
11122
11123 function isValid$2() {
11124 return isValid(this);
11125 }
11126
11127 function parsingFlags() {
11128 return extend({}, getParsingFlags(this));
11129 }
11130
11131 function invalidAt() {
11132 return getParsingFlags(this).overflow;
11133 }
11134
11135 function creationData() {
11136 return {
11137 input: this._i,
11138 format: this._f,
11139 locale: this._locale,
11140 isUTC: this._isUTC,
11141 strict: this._strict
11142 };
11143 } // FORMATTING
11144
11145
11146 addFormatToken(0, ['gg', 2], 0, function () {
11147 return this.weekYear() % 100;
11148 });
11149 addFormatToken(0, ['GG', 2], 0, function () {
11150 return this.isoWeekYear() % 100;
11151 });
11152
11153 function addWeekYearFormatToken(token, getter) {
11154 addFormatToken(0, [token, token.length], 0, getter);
11155 }
11156
11157 addWeekYearFormatToken('gggg', 'weekYear');
11158 addWeekYearFormatToken('ggggg', 'weekYear');
11159 addWeekYearFormatToken('GGGG', 'isoWeekYear');
11160 addWeekYearFormatToken('GGGGG', 'isoWeekYear'); // ALIASES
11161
11162 addUnitAlias('weekYear', 'gg');
11163 addUnitAlias('isoWeekYear', 'GG'); // PRIORITY
11164
11165 addUnitPriority('weekYear', 1);
11166 addUnitPriority('isoWeekYear', 1); // PARSING
11167
11168 addRegexToken('G', matchSigned);
11169 addRegexToken('g', matchSigned);
11170 addRegexToken('GG', match1to2, match2);
11171 addRegexToken('gg', match1to2, match2);
11172 addRegexToken('GGGG', match1to4, match4);
11173 addRegexToken('gggg', match1to4, match4);
11174 addRegexToken('GGGGG', match1to6, match6);
11175 addRegexToken('ggggg', match1to6, match6);
11176 addWeekParseToken(['gggg', 'ggggg', 'GGGG', 'GGGGG'], function (input, week, config, token) {
11177 week[token.substr(0, 2)] = toInt(input);
11178 });
11179 addWeekParseToken(['gg', 'GG'], function (input, week, config, token) {
11180 week[token] = hooks.parseTwoDigitYear(input);
11181 }); // MOMENTS
11182
11183 function getSetWeekYear(input) {
11184 return getSetWeekYearHelper.call(this, input, this.week(), this.weekday(), this.localeData()._week.dow, this.localeData()._week.doy);
11185 }
11186
11187 function getSetISOWeekYear(input) {
11188 return getSetWeekYearHelper.call(this, input, this.isoWeek(), this.isoWeekday(), 1, 4);
11189 }
11190
11191 function getISOWeeksInYear() {
11192 return weeksInYear(this.year(), 1, 4);
11193 }
11194
11195 function getWeeksInYear() {
11196 var weekInfo = this.localeData()._week;
11197
11198 return weeksInYear(this.year(), weekInfo.dow, weekInfo.doy);
11199 }
11200
11201 function getSetWeekYearHelper(input, week, weekday, dow, doy) {
11202 var weeksTarget;
11203
11204 if (input == null) {
11205 return weekOfYear(this, dow, doy).year;
11206 } else {
11207 weeksTarget = weeksInYear(input, dow, doy);
11208
11209 if (week > weeksTarget) {
11210 week = weeksTarget;
11211 }
11212
11213 return setWeekAll.call(this, input, week, weekday, dow, doy);
11214 }
11215 }
11216
11217 function setWeekAll(weekYear, week, weekday, dow, doy) {
11218 var dayOfYearData = dayOfYearFromWeeks(weekYear, week, weekday, dow, doy),
11219 date = createUTCDate(dayOfYearData.year, 0, dayOfYearData.dayOfYear);
11220 this.year(date.getUTCFullYear());
11221 this.month(date.getUTCMonth());
11222 this.date(date.getUTCDate());
11223 return this;
11224 } // FORMATTING
11225
11226
11227 addFormatToken('Q', 0, 'Qo', 'quarter'); // ALIASES
11228
11229 addUnitAlias('quarter', 'Q'); // PRIORITY
11230
11231 addUnitPriority('quarter', 7); // PARSING
11232
11233 addRegexToken('Q', match1);
11234 addParseToken('Q', function (input, array) {
11235 array[MONTH] = (toInt(input) - 1) * 3;
11236 }); // MOMENTS
11237
11238 function getSetQuarter(input) {
11239 return input == null ? Math.ceil((this.month() + 1) / 3) : this.month((input - 1) * 3 + this.month() % 3);
11240 } // FORMATTING
11241
11242
11243 addFormatToken('D', ['DD', 2], 'Do', 'date'); // ALIASES
11244
11245 addUnitAlias('date', 'D'); // PRIORITY
11246
11247 addUnitPriority('date', 9); // PARSING
11248
11249 addRegexToken('D', match1to2);
11250 addRegexToken('DD', match1to2, match2);
11251 addRegexToken('Do', function (isStrict, locale) {
11252 // TODO: Remove "ordinalParse" fallback in next major release.
11253 return isStrict ? locale._dayOfMonthOrdinalParse || locale._ordinalParse : locale._dayOfMonthOrdinalParseLenient;
11254 });
11255 addParseToken(['D', 'DD'], DATE);
11256 addParseToken('Do', function (input, array) {
11257 array[DATE] = toInt(input.match(match1to2)[0]);
11258 }); // MOMENTS
11259
11260 var getSetDayOfMonth = makeGetSet('Date', true); // FORMATTING
11261
11262 addFormatToken('DDD', ['DDDD', 3], 'DDDo', 'dayOfYear'); // ALIASES
11263
11264 addUnitAlias('dayOfYear', 'DDD'); // PRIORITY
11265
11266 addUnitPriority('dayOfYear', 4); // PARSING
11267
11268 addRegexToken('DDD', match1to3);
11269 addRegexToken('DDDD', match3);
11270 addParseToken(['DDD', 'DDDD'], function (input, array, config) {
11271 config._dayOfYear = toInt(input);
11272 }); // HELPERS
11273 // MOMENTS
11274
11275 function getSetDayOfYear(input) {
11276 var dayOfYear = Math.round((this.clone().startOf('day') - this.clone().startOf('year')) / 864e5) + 1;
11277 return input == null ? dayOfYear : this.add(input - dayOfYear, 'd');
11278 } // FORMATTING
11279
11280
11281 addFormatToken('m', ['mm', 2], 0, 'minute'); // ALIASES
11282
11283 addUnitAlias('minute', 'm'); // PRIORITY
11284
11285 addUnitPriority('minute', 14); // PARSING
11286
11287 addRegexToken('m', match1to2);
11288 addRegexToken('mm', match1to2, match2);
11289 addParseToken(['m', 'mm'], MINUTE); // MOMENTS
11290
11291 var getSetMinute = makeGetSet('Minutes', false); // FORMATTING
11292
11293 addFormatToken('s', ['ss', 2], 0, 'second'); // ALIASES
11294
11295 addUnitAlias('second', 's'); // PRIORITY
11296
11297 addUnitPriority('second', 15); // PARSING
11298
11299 addRegexToken('s', match1to2);
11300 addRegexToken('ss', match1to2, match2);
11301 addParseToken(['s', 'ss'], SECOND); // MOMENTS
11302
11303 var getSetSecond = makeGetSet('Seconds', false); // FORMATTING
11304
11305 addFormatToken('S', 0, 0, function () {
11306 return ~~(this.millisecond() / 100);
11307 });
11308 addFormatToken(0, ['SS', 2], 0, function () {
11309 return ~~(this.millisecond() / 10);
11310 });
11311 addFormatToken(0, ['SSS', 3], 0, 'millisecond');
11312 addFormatToken(0, ['SSSS', 4], 0, function () {
11313 return this.millisecond() * 10;
11314 });
11315 addFormatToken(0, ['SSSSS', 5], 0, function () {
11316 return this.millisecond() * 100;
11317 });
11318 addFormatToken(0, ['SSSSSS', 6], 0, function () {
11319 return this.millisecond() * 1000;
11320 });
11321 addFormatToken(0, ['SSSSSSS', 7], 0, function () {
11322 return this.millisecond() * 10000;
11323 });
11324 addFormatToken(0, ['SSSSSSSS', 8], 0, function () {
11325 return this.millisecond() * 100000;
11326 });
11327 addFormatToken(0, ['SSSSSSSSS', 9], 0, function () {
11328 return this.millisecond() * 1000000;
11329 }); // ALIASES
11330
11331 addUnitAlias('millisecond', 'ms'); // PRIORITY
11332
11333 addUnitPriority('millisecond', 16); // PARSING
11334
11335 addRegexToken('S', match1to3, match1);
11336 addRegexToken('SS', match1to3, match2);
11337 addRegexToken('SSS', match1to3, match3);
11338 var token;
11339
11340 for (token = 'SSSS'; token.length <= 9; token += 'S') {
11341 addRegexToken(token, matchUnsigned);
11342 }
11343
11344 function parseMs(input, array) {
11345 array[MILLISECOND] = toInt(('0.' + input) * 1000);
11346 }
11347
11348 for (token = 'S'; token.length <= 9; token += 'S') {
11349 addParseToken(token, parseMs);
11350 } // MOMENTS
11351
11352
11353 var getSetMillisecond = makeGetSet('Milliseconds', false); // FORMATTING
11354
11355 addFormatToken('z', 0, 0, 'zoneAbbr');
11356 addFormatToken('zz', 0, 0, 'zoneName'); // MOMENTS
11357
11358 function getZoneAbbr() {
11359 return this._isUTC ? 'UTC' : '';
11360 }
11361
11362 function getZoneName() {
11363 return this._isUTC ? 'Coordinated Universal Time' : '';
11364 }
11365
11366 var proto = Moment.prototype;
11367 proto.add = add;
11368 proto.calendar = calendar$1;
11369 proto.clone = clone;
11370 proto.diff = diff;
11371 proto.endOf = endOf;
11372 proto.format = format;
11373 proto.from = from;
11374 proto.fromNow = fromNow;
11375 proto.to = to;
11376 proto.toNow = toNow;
11377 proto.get = stringGet;
11378 proto.invalidAt = invalidAt;
11379 proto.isAfter = isAfter;
11380 proto.isBefore = isBefore;
11381 proto.isBetween = isBetween;
11382 proto.isSame = isSame;
11383 proto.isSameOrAfter = isSameOrAfter;
11384 proto.isSameOrBefore = isSameOrBefore;
11385 proto.isValid = isValid$2;
11386 proto.lang = lang;
11387 proto.locale = locale;
11388 proto.localeData = localeData;
11389 proto.max = prototypeMax;
11390 proto.min = prototypeMin;
11391 proto.parsingFlags = parsingFlags;
11392 proto.set = stringSet;
11393 proto.startOf = startOf;
11394 proto.subtract = subtract;
11395 proto.toArray = toArray;
11396 proto.toObject = toObject;
11397 proto.toDate = toDate;
11398 proto.toISOString = toISOString;
11399 proto.inspect = inspect;
11400 proto.toJSON = toJSON;
11401 proto.toString = toString;
11402 proto.unix = unix;
11403 proto.valueOf = valueOf;
11404 proto.creationData = creationData;
11405 proto.year = getSetYear;
11406 proto.isLeapYear = getIsLeapYear;
11407 proto.weekYear = getSetWeekYear;
11408 proto.isoWeekYear = getSetISOWeekYear;
11409 proto.quarter = proto.quarters = getSetQuarter;
11410 proto.month = getSetMonth;
11411 proto.daysInMonth = getDaysInMonth;
11412 proto.week = proto.weeks = getSetWeek;
11413 proto.isoWeek = proto.isoWeeks = getSetISOWeek;
11414 proto.weeksInYear = getWeeksInYear;
11415 proto.isoWeeksInYear = getISOWeeksInYear;
11416 proto.date = getSetDayOfMonth;
11417 proto.day = proto.days = getSetDayOfWeek;
11418 proto.weekday = getSetLocaleDayOfWeek;
11419 proto.isoWeekday = getSetISODayOfWeek;
11420 proto.dayOfYear = getSetDayOfYear;
11421 proto.hour = proto.hours = getSetHour;
11422 proto.minute = proto.minutes = getSetMinute;
11423 proto.second = proto.seconds = getSetSecond;
11424 proto.millisecond = proto.milliseconds = getSetMillisecond;
11425 proto.utcOffset = getSetOffset;
11426 proto.utc = setOffsetToUTC;
11427 proto.local = setOffsetToLocal;
11428 proto.parseZone = setOffsetToParsedOffset;
11429 proto.hasAlignedHourOffset = hasAlignedHourOffset;
11430 proto.isDST = isDaylightSavingTime;
11431 proto.isLocal = isLocal;
11432 proto.isUtcOffset = isUtcOffset;
11433 proto.isUtc = isUtc;
11434 proto.isUTC = isUtc;
11435 proto.zoneAbbr = getZoneAbbr;
11436 proto.zoneName = getZoneName;
11437 proto.dates = deprecate('dates accessor is deprecated. Use date instead.', getSetDayOfMonth);
11438 proto.months = deprecate('months accessor is deprecated. Use month instead', getSetMonth);
11439 proto.years = deprecate('years accessor is deprecated. Use year instead', getSetYear);
11440 proto.zone = deprecate('moment().zone is deprecated, use moment().utcOffset instead. http://momentjs.com/guides/#/warnings/zone/', getSetZone);
11441 proto.isDSTShifted = deprecate('isDSTShifted is deprecated. See http://momentjs.com/guides/#/warnings/dst-shifted/ for more information', isDaylightSavingTimeShifted);
11442
11443 function createUnix(input) {
11444 return createLocal(input * 1000);
11445 }
11446
11447 function createInZone() {
11448 return createLocal.apply(null, arguments).parseZone();
11449 }
11450
11451 function preParsePostFormat(string) {
11452 return string;
11453 }
11454
11455 var proto$1 = Locale.prototype;
11456 proto$1.calendar = calendar;
11457 proto$1.longDateFormat = longDateFormat;
11458 proto$1.invalidDate = invalidDate;
11459 proto$1.ordinal = ordinal;
11460 proto$1.preparse = preParsePostFormat;
11461 proto$1.postformat = preParsePostFormat;
11462 proto$1.relativeTime = relativeTime;
11463 proto$1.pastFuture = pastFuture;
11464 proto$1.set = set;
11465 proto$1.months = localeMonths;
11466 proto$1.monthsShort = localeMonthsShort;
11467 proto$1.monthsParse = localeMonthsParse;
11468 proto$1.monthsRegex = monthsRegex;
11469 proto$1.monthsShortRegex = monthsShortRegex;
11470 proto$1.week = localeWeek;
11471 proto$1.firstDayOfYear = localeFirstDayOfYear;
11472 proto$1.firstDayOfWeek = localeFirstDayOfWeek;
11473 proto$1.weekdays = localeWeekdays;
11474 proto$1.weekdaysMin = localeWeekdaysMin;
11475 proto$1.weekdaysShort = localeWeekdaysShort;
11476 proto$1.weekdaysParse = localeWeekdaysParse;
11477 proto$1.weekdaysRegex = weekdaysRegex;
11478 proto$1.weekdaysShortRegex = weekdaysShortRegex;
11479 proto$1.weekdaysMinRegex = weekdaysMinRegex;
11480 proto$1.isPM = localeIsPM;
11481 proto$1.meridiem = localeMeridiem;
11482
11483 function get$1(format, index, field, setter) {
11484 var locale = getLocale();
11485 var utc = createUTC().set(setter, index);
11486 return locale[field](utc, format);
11487 }
11488
11489 function listMonthsImpl(format, index, field) {
11490 if (isNumber(format)) {
11491 index = format;
11492 format = undefined;
11493 }
11494
11495 format = format || '';
11496
11497 if (index != null) {
11498 return get$1(format, index, field, 'month');
11499 }
11500
11501 var i;
11502 var out = [];
11503
11504 for (i = 0; i < 12; i++) {
11505 out[i] = get$1(format, i, field, 'month');
11506 }
11507
11508 return out;
11509 } // ()
11510 // (5)
11511 // (fmt, 5)
11512 // (fmt)
11513 // (true)
11514 // (true, 5)
11515 // (true, fmt, 5)
11516 // (true, fmt)
11517
11518
11519 function listWeekdaysImpl(localeSorted, format, index, field) {
11520 if (typeof localeSorted === 'boolean') {
11521 if (isNumber(format)) {
11522 index = format;
11523 format = undefined;
11524 }
11525
11526 format = format || '';
11527 } else {
11528 format = localeSorted;
11529 index = format;
11530 localeSorted = false;
11531
11532 if (isNumber(format)) {
11533 index = format;
11534 format = undefined;
11535 }
11536
11537 format = format || '';
11538 }
11539
11540 var locale = getLocale(),
11541 shift = localeSorted ? locale._week.dow : 0;
11542
11543 if (index != null) {
11544 return get$1(format, (index + shift) % 7, field, 'day');
11545 }
11546
11547 var i;
11548 var out = [];
11549
11550 for (i = 0; i < 7; i++) {
11551 out[i] = get$1(format, (i + shift) % 7, field, 'day');
11552 }
11553
11554 return out;
11555 }
11556
11557 function listMonths(format, index) {
11558 return listMonthsImpl(format, index, 'months');
11559 }
11560
11561 function listMonthsShort(format, index) {
11562 return listMonthsImpl(format, index, 'monthsShort');
11563 }
11564
11565 function listWeekdays(localeSorted, format, index) {
11566 return listWeekdaysImpl(localeSorted, format, index, 'weekdays');
11567 }
11568
11569 function listWeekdaysShort(localeSorted, format, index) {
11570 return listWeekdaysImpl(localeSorted, format, index, 'weekdaysShort');
11571 }
11572
11573 function listWeekdaysMin(localeSorted, format, index) {
11574 return listWeekdaysImpl(localeSorted, format, index, 'weekdaysMin');
11575 }
11576
11577 getSetGlobalLocale('en', {
11578 dayOfMonthOrdinalParse: /\d{1,2}(th|st|nd|rd)/,
11579 ordinal: function (number) {
11580 var b = number % 10,
11581 output = toInt(number % 100 / 10) === 1 ? 'th' : b === 1 ? 'st' : b === 2 ? 'nd' : b === 3 ? 'rd' : 'th';
11582 return number + output;
11583 }
11584 }); // Side effect imports
11585
11586 hooks.lang = deprecate('moment.lang is deprecated. Use moment.locale instead.', getSetGlobalLocale);
11587 hooks.langData = deprecate('moment.langData is deprecated. Use moment.localeData instead.', getLocale);
11588 var mathAbs = Math.abs;
11589
11590 function abs() {
11591 var data = this._data;
11592 this._milliseconds = mathAbs(this._milliseconds);
11593 this._days = mathAbs(this._days);
11594 this._months = mathAbs(this._months);
11595 data.milliseconds = mathAbs(data.milliseconds);
11596 data.seconds = mathAbs(data.seconds);
11597 data.minutes = mathAbs(data.minutes);
11598 data.hours = mathAbs(data.hours);
11599 data.months = mathAbs(data.months);
11600 data.years = mathAbs(data.years);
11601 return this;
11602 }
11603
11604 function addSubtract$1(duration, input, value, direction) {
11605 var other = createDuration(input, value);
11606 duration._milliseconds += direction * other._milliseconds;
11607 duration._days += direction * other._days;
11608 duration._months += direction * other._months;
11609 return duration._bubble();
11610 } // supports only 2.0-style add(1, 's') or add(duration)
11611
11612
11613 function add$1(input, value) {
11614 return addSubtract$1(this, input, value, 1);
11615 } // supports only 2.0-style subtract(1, 's') or subtract(duration)
11616
11617
11618 function subtract$1(input, value) {
11619 return addSubtract$1(this, input, value, -1);
11620 }
11621
11622 function absCeil(number) {
11623 if (number < 0) {
11624 return Math.floor(number);
11625 } else {
11626 return Math.ceil(number);
11627 }
11628 }
11629
11630 function bubble() {
11631 var milliseconds = this._milliseconds;
11632 var days = this._days;
11633 var months = this._months;
11634 var data = this._data;
11635 var seconds, minutes, hours, years, monthsFromDays; // if we have a mix of positive and negative values, bubble down first
11636 // check: https://github.com/moment/moment/issues/2166
11637
11638 if (!(milliseconds >= 0 && days >= 0 && months >= 0 || milliseconds <= 0 && days <= 0 && months <= 0)) {
11639 milliseconds += absCeil(monthsToDays(months) + days) * 864e5;
11640 days = 0;
11641 months = 0;
11642 } // The following code bubbles up values, see the tests for
11643 // examples of what that means.
11644
11645
11646 data.milliseconds = milliseconds % 1000;
11647 seconds = absFloor(milliseconds / 1000);
11648 data.seconds = seconds % 60;
11649 minutes = absFloor(seconds / 60);
11650 data.minutes = minutes % 60;
11651 hours = absFloor(minutes / 60);
11652 data.hours = hours % 24;
11653 days += absFloor(hours / 24); // convert days to months
11654
11655 monthsFromDays = absFloor(daysToMonths(days));
11656 months += monthsFromDays;
11657 days -= absCeil(monthsToDays(monthsFromDays)); // 12 months -> 1 year
11658
11659 years = absFloor(months / 12);
11660 months %= 12;
11661 data.days = days;
11662 data.months = months;
11663 data.years = years;
11664 return this;
11665 }
11666
11667 function daysToMonths(days) {
11668 // 400 years have 146097 days (taking into account leap year rules)
11669 // 400 years have 12 months === 4800
11670 return days * 4800 / 146097;
11671 }
11672
11673 function monthsToDays(months) {
11674 // the reverse of daysToMonths
11675 return months * 146097 / 4800;
11676 }
11677
11678 function as(units) {
11679 if (!this.isValid()) {
11680 return NaN;
11681 }
11682
11683 var days;
11684 var months;
11685 var milliseconds = this._milliseconds;
11686 units = normalizeUnits(units);
11687
11688 if (units === 'month' || units === 'quarter' || units === 'year') {
11689 days = this._days + milliseconds / 864e5;
11690 months = this._months + daysToMonths(days);
11691
11692 switch (units) {
11693 case 'month':
11694 return months;
11695
11696 case 'quarter':
11697 return months / 3;
11698
11699 case 'year':
11700 return months / 12;
11701 }
11702 } else {
11703 // handle milliseconds separately because of floating point math errors (issue #1867)
11704 days = this._days + Math.round(monthsToDays(this._months));
11705
11706 switch (units) {
11707 case 'week':
11708 return days / 7 + milliseconds / 6048e5;
11709
11710 case 'day':
11711 return days + milliseconds / 864e5;
11712
11713 case 'hour':
11714 return days * 24 + milliseconds / 36e5;
11715
11716 case 'minute':
11717 return days * 1440 + milliseconds / 6e4;
11718
11719 case 'second':
11720 return days * 86400 + milliseconds / 1000;
11721 // Math.floor prevents floating point math errors here
11722
11723 case 'millisecond':
11724 return Math.floor(days * 864e5) + milliseconds;
11725
11726 default:
11727 throw new Error('Unknown unit ' + units);
11728 }
11729 }
11730 } // TODO: Use this.as('ms')?
11731
11732
11733 function valueOf$1() {
11734 if (!this.isValid()) {
11735 return NaN;
11736 }
11737
11738 return this._milliseconds + this._days * 864e5 + this._months % 12 * 2592e6 + toInt(this._months / 12) * 31536e6;
11739 }
11740
11741 function makeAs(alias) {
11742 return function () {
11743 return this.as(alias);
11744 };
11745 }
11746
11747 var asMilliseconds = makeAs('ms');
11748 var asSeconds = makeAs('s');
11749 var asMinutes = makeAs('m');
11750 var asHours = makeAs('h');
11751 var asDays = makeAs('d');
11752 var asWeeks = makeAs('w');
11753 var asMonths = makeAs('M');
11754 var asQuarters = makeAs('Q');
11755 var asYears = makeAs('y');
11756
11757 function clone$1() {
11758 return createDuration(this);
11759 }
11760
11761 function get$2(units) {
11762 units = normalizeUnits(units);
11763 return this.isValid() ? this[units + 's']() : NaN;
11764 }
11765
11766 function makeGetter(name) {
11767 return function () {
11768 return this.isValid() ? this._data[name] : NaN;
11769 };
11770 }
11771
11772 var milliseconds = makeGetter('milliseconds');
11773 var seconds = makeGetter('seconds');
11774 var minutes = makeGetter('minutes');
11775 var hours = makeGetter('hours');
11776 var days = makeGetter('days');
11777 var months = makeGetter('months');
11778 var years = makeGetter('years');
11779
11780 function weeks() {
11781 return absFloor(this.days() / 7);
11782 }
11783
11784 var round = Math.round;
11785 var thresholds = {
11786 ss: 44,
11787 // a few seconds to seconds
11788 s: 45,
11789 // seconds to minute
11790 m: 45,
11791 // minutes to hour
11792 h: 22,
11793 // hours to day
11794 d: 26,
11795 // days to month
11796 M: 11 // months to year
11797
11798 }; // helper function for moment.fn.from, moment.fn.fromNow, and moment.duration.fn.humanize
11799
11800 function substituteTimeAgo(string, number, withoutSuffix, isFuture, locale) {
11801 return locale.relativeTime(number || 1, !!withoutSuffix, string, isFuture);
11802 }
11803
11804 function relativeTime$1(posNegDuration, withoutSuffix, locale) {
11805 var duration = createDuration(posNegDuration).abs();
11806 var seconds = round(duration.as('s'));
11807 var minutes = round(duration.as('m'));
11808 var hours = round(duration.as('h'));
11809 var days = round(duration.as('d'));
11810 var months = round(duration.as('M'));
11811 var years = round(duration.as('y'));
11812 var a = seconds <= thresholds.ss && ['s', seconds] || seconds < thresholds.s && ['ss', seconds] || minutes <= 1 && ['m'] || minutes < thresholds.m && ['mm', minutes] || hours <= 1 && ['h'] || hours < thresholds.h && ['hh', hours] || days <= 1 && ['d'] || days < thresholds.d && ['dd', days] || months <= 1 && ['M'] || months < thresholds.M && ['MM', months] || years <= 1 && ['y'] || ['yy', years];
11813 a[2] = withoutSuffix;
11814 a[3] = +posNegDuration > 0;
11815 a[4] = locale;
11816 return substituteTimeAgo.apply(null, a);
11817 } // This function allows you to set the rounding function for relative time strings
11818
11819
11820 function getSetRelativeTimeRounding(roundingFunction) {
11821 if (roundingFunction === undefined) {
11822 return round;
11823 }
11824
11825 if (typeof roundingFunction === 'function') {
11826 round = roundingFunction;
11827 return true;
11828 }
11829
11830 return false;
11831 } // This function allows you to set a threshold for relative time strings
11832
11833
11834 function getSetRelativeTimeThreshold(threshold, limit) {
11835 if (thresholds[threshold] === undefined) {
11836 return false;
11837 }
11838
11839 if (limit === undefined) {
11840 return thresholds[threshold];
11841 }
11842
11843 thresholds[threshold] = limit;
11844
11845 if (threshold === 's') {
11846 thresholds.ss = limit - 1;
11847 }
11848
11849 return true;
11850 }
11851
11852 function humanize(withSuffix) {
11853 if (!this.isValid()) {
11854 return this.localeData().invalidDate();
11855 }
11856
11857 var locale = this.localeData();
11858 var output = relativeTime$1(this, !withSuffix, locale);
11859
11860 if (withSuffix) {
11861 output = locale.pastFuture(+this, output);
11862 }
11863
11864 return locale.postformat(output);
11865 }
11866
11867 var abs$1 = Math.abs;
11868
11869 function sign(x) {
11870 return (x > 0) - (x < 0) || +x;
11871 }
11872
11873 function toISOString$1() {
11874 // for ISO strings we do not use the normal bubbling rules:
11875 // * milliseconds bubble up until they become hours
11876 // * days do not bubble at all
11877 // * months bubble up until they become years
11878 // This is because there is no context-free conversion between hours and days
11879 // (think of clock changes)
11880 // and also not between days and months (28-31 days per month)
11881 if (!this.isValid()) {
11882 return this.localeData().invalidDate();
11883 }
11884
11885 var seconds = abs$1(this._milliseconds) / 1000;
11886 var days = abs$1(this._days);
11887 var months = abs$1(this._months);
11888 var minutes, hours, years; // 3600 seconds -> 60 minutes -> 1 hour
11889
11890 minutes = absFloor(seconds / 60);
11891 hours = absFloor(minutes / 60);
11892 seconds %= 60;
11893 minutes %= 60; // 12 months -> 1 year
11894
11895 years = absFloor(months / 12);
11896 months %= 12; // inspired by https://github.com/dordille/moment-isoduration/blob/master/moment.isoduration.js
11897
11898 var Y = years;
11899 var M = months;
11900 var D = days;
11901 var h = hours;
11902 var m = minutes;
11903 var s = seconds ? seconds.toFixed(3).replace(/\.?0+$/, '') : '';
11904 var total = this.asSeconds();
11905
11906 if (!total) {
11907 // this is the same as C#'s (Noda) and python (isodate)...
11908 // but not other JS (goog.date)
11909 return 'P0D';
11910 }
11911
11912 var totalSign = total < 0 ? '-' : '';
11913 var ymSign = sign(this._months) !== sign(total) ? '-' : '';
11914 var daysSign = sign(this._days) !== sign(total) ? '-' : '';
11915 var hmsSign = sign(this._milliseconds) !== sign(total) ? '-' : '';
11916 return totalSign + 'P' + (Y ? ymSign + Y + 'Y' : '') + (M ? ymSign + M + 'M' : '') + (D ? daysSign + D + 'D' : '') + (h || m || s ? 'T' : '') + (h ? hmsSign + h + 'H' : '') + (m ? hmsSign + m + 'M' : '') + (s ? hmsSign + s + 'S' : '');
11917 }
11918
11919 var proto$2 = Duration.prototype;
11920 proto$2.isValid = isValid$1;
11921 proto$2.abs = abs;
11922 proto$2.add = add$1;
11923 proto$2.subtract = subtract$1;
11924 proto$2.as = as;
11925 proto$2.asMilliseconds = asMilliseconds;
11926 proto$2.asSeconds = asSeconds;
11927 proto$2.asMinutes = asMinutes;
11928 proto$2.asHours = asHours;
11929 proto$2.asDays = asDays;
11930 proto$2.asWeeks = asWeeks;
11931 proto$2.asMonths = asMonths;
11932 proto$2.asQuarters = asQuarters;
11933 proto$2.asYears = asYears;
11934 proto$2.valueOf = valueOf$1;
11935 proto$2._bubble = bubble;
11936 proto$2.clone = clone$1;
11937 proto$2.get = get$2;
11938 proto$2.milliseconds = milliseconds;
11939 proto$2.seconds = seconds;
11940 proto$2.minutes = minutes;
11941 proto$2.hours = hours;
11942 proto$2.days = days;
11943 proto$2.weeks = weeks;
11944 proto$2.months = months;
11945 proto$2.years = years;
11946 proto$2.humanize = humanize;
11947 proto$2.toISOString = toISOString$1;
11948 proto$2.toString = toISOString$1;
11949 proto$2.toJSON = toISOString$1;
11950 proto$2.locale = locale;
11951 proto$2.localeData = localeData;
11952 proto$2.toIsoString = deprecate('toIsoString() is deprecated. Please use toISOString() instead (notice the capitals)', toISOString$1);
11953 proto$2.lang = lang; // Side effect imports
11954 // FORMATTING
11955
11956 addFormatToken('X', 0, 0, 'unix');
11957 addFormatToken('x', 0, 0, 'valueOf'); // PARSING
11958
11959 addRegexToken('x', matchSigned);
11960 addRegexToken('X', matchTimestamp);
11961 addParseToken('X', function (input, array, config) {
11962 config._d = new Date(parseFloat(input, 10) * 1000);
11963 });
11964 addParseToken('x', function (input, array, config) {
11965 config._d = new Date(toInt(input));
11966 }); // Side effect imports
11967
11968 hooks.version = '2.24.0';
11969 setHookCallback(createLocal);
11970 hooks.fn = proto;
11971 hooks.min = min;
11972 hooks.max = max;
11973 hooks.now = now;
11974 hooks.utc = createUTC;
11975 hooks.unix = createUnix;
11976 hooks.months = listMonths;
11977 hooks.isDate = isDate;
11978 hooks.locale = getSetGlobalLocale;
11979 hooks.invalid = createInvalid;
11980 hooks.duration = createDuration;
11981 hooks.isMoment = isMoment;
11982 hooks.weekdays = listWeekdays;
11983 hooks.parseZone = createInZone;
11984 hooks.localeData = getLocale;
11985 hooks.isDuration = isDuration;
11986 hooks.monthsShort = listMonthsShort;
11987 hooks.weekdaysMin = listWeekdaysMin;
11988 hooks.defineLocale = defineLocale;
11989 hooks.updateLocale = updateLocale;
11990 hooks.locales = listLocales;
11991 hooks.weekdaysShort = listWeekdaysShort;
11992 hooks.normalizeUnits = normalizeUnits;
11993 hooks.relativeTimeRounding = getSetRelativeTimeRounding;
11994 hooks.relativeTimeThreshold = getSetRelativeTimeThreshold;
11995 hooks.calendarFormat = getCalendarFormat;
11996 hooks.prototype = proto; // currently HTML5 input type only supports 24-hour formats
11997
11998 hooks.HTML5_FMT = {
11999 DATETIME_LOCAL: 'YYYY-MM-DDTHH:mm',
12000 // <input type="datetime-local" />
12001 DATETIME_LOCAL_SECONDS: 'YYYY-MM-DDTHH:mm:ss',
12002 // <input type="datetime-local" step="1" />
12003 DATETIME_LOCAL_MS: 'YYYY-MM-DDTHH:mm:ss.SSS',
12004 // <input type="datetime-local" step="0.001" />
12005 DATE: 'YYYY-MM-DD',
12006 // <input type="date" />
12007 TIME: 'HH:mm',
12008 // <input type="time" />
12009 TIME_SECONDS: 'HH:mm:ss',
12010 // <input type="time" step="1" />
12011 TIME_MS: 'HH:mm:ss.SSS',
12012 // <input type="time" step="0.001" />
12013 WEEK: 'GGGG-[W]WW',
12014 // <input type="week" />
12015 MONTH: 'YYYY-MM' // <input type="month" />
12016
12017 };
12018 return hooks;
12019 });
12020}); // Maps for number <-> hex string conversion
12021
12022var byteToHex$2$1 = [];
12023
12024for (var i$2$1 = 0; i$2$1 < 256; i$2$1++) {
12025 byteToHex$2$1[i$2$1] = (i$2$1 + 0x100).toString(16).substr(1);
12026}
12027/**
12028 * Generate 16 random bytes to be used as a base for UUID.
12029 *
12030 * @ignore
12031 */
12032
12033
12034var random$1$1 = function () {
12035 if (typeof crypto !== 'undefined' && crypto.getRandomValues) {
12036 // WHATWG crypto-based RNG - http://wiki.whatwg.org/wiki/Crypto
12037 // Moderately fast, high quality
12038 var _rnds8 = new Uint8Array(16);
12039
12040 return function whatwgRNG() {
12041 crypto.getRandomValues(_rnds8);
12042 return _rnds8;
12043 };
12044 } // Math.random()-based (RNG)
12045 //
12046 // If all else fails, use Math.random().
12047 // It's fast, but is of unspecified quality.
12048
12049
12050 var _rnds = new Array(16);
12051
12052 return function () {
12053 for (var i = 0, r; i < 16; i++) {
12054 if ((i & 0x03) === 0) {
12055 r = Math.random() * 0x100000000;
12056 }
12057
12058 _rnds[i] = r >>> ((i & 0x03) << 3) & 0xff;
12059 }
12060
12061 return _rnds;
12062 }; // uuid.js
12063 //
12064 // Copyright (c) 2010-2012 Robert Kieffer
12065 // MIT License - http://opensource.org/licenses/mit-license.php
12066 // Unique ID creation requires a high quality random # generator. We feature
12067 // detect to determine the best RNG source, normalizing to a function that
12068 // returns 128-bits of randomness, since that's what's usually required
12069 // return require('./rng');
12070}();
12071
12072var byteToHex$1$1$1 = [];
12073
12074for (var i$1$1$1 = 0; i$1$1$1 < 256; i$1$1$1++) {
12075 byteToHex$1$1$1[i$1$1$1] = (i$1$1$1 + 0x100).toString(16).substr(1);
12076} // **`v1()` - Generate time-based UUID**
12077//
12078// Inspired by https://github.com/LiosK/UUID.js
12079// and http://docs.python.org/library/uuid.html
12080// random #'s we need to init node and clockseq
12081
12082
12083var seedBytes$1$1 = random$1$1(); // Per 4.5, create and 48-bit node id, (47 random bits + multicast bit = 1)
12084
12085var defaultNodeId$1$1 = [seedBytes$1$1[0] | 0x01, seedBytes$1$1[1], seedBytes$1$1[2], seedBytes$1$1[3], seedBytes$1$1[4], seedBytes$1$1[5]]; // Per 4.2.2, randomize (14 bit) clockseq
12086
12087var defaultClockseq$1$1 = (seedBytes$1$1[6] << 8 | seedBytes$1$1[7]) & 0x3fff; // Previous uuid creation time
12088// for example '/Date(1198908717056)/' or '/Date(1198908717056-0700)/'
12089// code from http://momentjs.com/
12090
12091var ASPDateRegex$1 = /^\/?Date\((-?\d+)/i; // Hex color
12092
12093/**
12094 * Hue, Saturation, Value.
12095 */
12096
12097/**
12098 * Test whether given object is a number
12099 *
12100 * @param value - Input value of unknown type.
12101 *
12102 * @returns True if number, false otherwise.
12103 */
12104
12105function isNumber$1(value) {
12106 return value instanceof Number || typeof value === 'number';
12107}
12108/**
12109 * Test whether given object is a string
12110 *
12111 * @param value - Input value of unknown type.
12112 *
12113 * @returns True if string, false otherwise.
12114 */
12115
12116
12117function isString$1(value) {
12118 return value instanceof String || typeof value === 'string';
12119}
12120/**
12121 * Test whether given object is a Moment date.
12122 * @TODO: This is basically a workaround, if Moment was imported property it wouldn't necessary as moment.isMoment is a TS type guard.
12123 *
12124 * @param value - Input value of unknown type.
12125 *
12126 * @returns True if Moment instance, false otherwise.
12127 */
12128
12129
12130function isMoment$1(value) {
12131 return moment$1.isMoment(value);
12132}
12133/**
12134 * Convert an object into another type
12135 *
12136 * @param object - Value of unknown type.
12137 * @param type - Name of the desired type.
12138 *
12139 * @returns Object in the desired type.
12140 * @throws Error
12141 */
12142
12143
12144function convert$1(object, type) {
12145 var match;
12146
12147 if (object === undefined) {
12148 return undefined;
12149 }
12150
12151 if (object === null) {
12152 return null;
12153 }
12154
12155 if (!type) {
12156 return object;
12157 }
12158
12159 if (!(typeof type === 'string') && !(type instanceof String)) {
12160 throw new Error('Type must be a string');
12161 } //noinspection FallthroughInSwitchStatementJS
12162
12163
12164 switch (type) {
12165 case 'boolean':
12166 case 'Boolean':
12167 return Boolean(object);
12168
12169 case 'number':
12170 case 'Number':
12171 if (isString$1(object) && !isNaN(Date.parse(object))) {
12172 return moment$1(object).valueOf();
12173 } else {
12174 // @TODO: I don't think that Number and String constructors are a good idea.
12175 // This could also fail if the object doesn't have valueOf method or if it's redefined.
12176 // For example: Object.create(null) or { valueOf: 7 }.
12177 return Number(object.valueOf());
12178 }
12179
12180 case 'string':
12181 case 'String':
12182 return String(object);
12183
12184 case 'Date':
12185 if (isNumber$1(object)) {
12186 return new Date(object);
12187 }
12188
12189 if (object instanceof Date) {
12190 return new Date(object.valueOf());
12191 } else if (isMoment$1(object)) {
12192 return new Date(object.valueOf());
12193 }
12194
12195 if (isString$1(object)) {
12196 match = ASPDateRegex$1.exec(object);
12197
12198 if (match) {
12199 // object is an ASP date
12200 return new Date(Number(match[1])); // parse number
12201 } else {
12202 return moment$1(new Date(object)).toDate(); // parse string
12203 }
12204 } else {
12205 throw new Error('Cannot convert object of type ' + getType$1(object) + ' to type Date');
12206 }
12207
12208 case 'Moment':
12209 if (isNumber$1(object)) {
12210 return moment$1(object);
12211 }
12212
12213 if (object instanceof Date) {
12214 return moment$1(object.valueOf());
12215 } else if (isMoment$1(object)) {
12216 return moment$1(object);
12217 }
12218
12219 if (isString$1(object)) {
12220 match = ASPDateRegex$1.exec(object);
12221
12222 if (match) {
12223 // object is an ASP date
12224 return moment$1(Number(match[1])); // parse number
12225 } else {
12226 return moment$1(object); // parse string
12227 }
12228 } else {
12229 throw new Error('Cannot convert object of type ' + getType$1(object) + ' to type Date');
12230 }
12231
12232 case 'ISODate':
12233 if (isNumber$1(object)) {
12234 return new Date(object);
12235 } else if (object instanceof Date) {
12236 return object.toISOString();
12237 } else if (isMoment$1(object)) {
12238 return object.toDate().toISOString();
12239 } else if (isString$1(object)) {
12240 match = ASPDateRegex$1.exec(object);
12241
12242 if (match) {
12243 // object is an ASP date
12244 return new Date(Number(match[1])).toISOString(); // parse number
12245 } else {
12246 return moment$1(object).format(); // ISO 8601
12247 }
12248 } else {
12249 throw new Error('Cannot convert object of type ' + getType$1(object) + ' to type ISODate');
12250 }
12251
12252 case 'ASPDate':
12253 if (isNumber$1(object)) {
12254 return '/Date(' + object + ')/';
12255 } else if (object instanceof Date) {
12256 return '/Date(' + object.valueOf() + ')/';
12257 } else if (isString$1(object)) {
12258 match = ASPDateRegex$1.exec(object);
12259
12260 var _value;
12261
12262 if (match) {
12263 // object is an ASP date
12264 _value = new Date(Number(match[1])).valueOf(); // parse number
12265 } else {
12266 _value = new Date(object).valueOf(); // parse string
12267 }
12268
12269 return '/Date(' + _value + ')/';
12270 } else {
12271 throw new Error('Cannot convert object of type ' + getType$1(object) + ' to type ASPDate');
12272 }
12273
12274 default:
12275 var never = type;
12276 throw new Error("Unknown type ".concat(never));
12277 }
12278}
12279/**
12280 * Get the type of an object, for example exports.getType([]) returns 'Array'
12281 *
12282 * @param object - Input value of unknown type.
12283 *
12284 * @returns Detected type.
12285 */
12286
12287
12288function getType$1(object) {
12289 var type = _typeof(object);
12290
12291 if (type === 'object') {
12292 if (object === null) {
12293 return 'null';
12294 }
12295
12296 if (object instanceof Boolean) {
12297 return 'Boolean';
12298 }
12299
12300 if (object instanceof Number) {
12301 return 'Number';
12302 }
12303
12304 if (object instanceof String) {
12305 return 'String';
12306 }
12307
12308 if (Array.isArray(object)) {
12309 return 'Array';
12310 }
12311
12312 if (object instanceof Date) {
12313 return 'Date';
12314 }
12315
12316 return 'Object';
12317 }
12318
12319 if (type === 'number') {
12320 return 'Number';
12321 }
12322
12323 if (type === 'boolean') {
12324 return 'Boolean';
12325 }
12326
12327 if (type === 'string') {
12328 return 'String';
12329 }
12330
12331 if (type === undefined) {
12332 return 'undefined';
12333 }
12334
12335 return type;
12336}
12337/**
12338 * Determine whether a value can be used as an id.
12339 *
12340 * @param value - Input value of unknown type.
12341 *
12342 * @returns True if the value is valid id, false otherwise.
12343 */
12344
12345
12346function isId(value) {
12347 return typeof value === 'string' || typeof value === 'number';
12348}
12349/**
12350 * A queue.
12351 *
12352 * @typeParam T - The type of method names to be replaced by queued versions.
12353 */
12354
12355
12356var Queue =
12357/*#__PURE__*/
12358function () {
12359 /**
12360 * Construct a new Queue.
12361 *
12362 * @param options - Queue configuration.
12363 */
12364 function Queue(options) {
12365 classCallCheck(this, Queue);
12366 this._queue = [];
12367 this._timeout = null;
12368 this._extended = null; // options
12369
12370 this.delay = null;
12371 this.max = Infinity;
12372 this.setOptions(options);
12373 }
12374 /**
12375 * Update the configuration of the queue.
12376 *
12377 * @param options - Queue configuration.
12378 */
12379
12380
12381 createClass(Queue, [{
12382 key: "setOptions",
12383 value: function setOptions(options) {
12384 if (options && typeof options.delay !== 'undefined') {
12385 this.delay = options.delay;
12386 }
12387
12388 if (options && typeof options.max !== 'undefined') {
12389 this.max = options.max;
12390 }
12391
12392 this._flushIfNeeded();
12393 }
12394 /**
12395 * Extend an object with queuing functionality.
12396 * The object will be extended with a function flush, and the methods provided in options.replace will be replaced with queued ones.
12397 *
12398 * @param object - The object to be extended.
12399 * @param options - Additional options.
12400 *
12401 * @returns The created queue.
12402 */
12403
12404 }, {
12405 key: "destroy",
12406
12407 /**
12408 * Destroy the queue. The queue will first flush all queued actions, and in case it has extended an object, will restore the original object.
12409 */
12410 value: function destroy() {
12411 this.flush();
12412
12413 if (this._extended) {
12414 var object = this._extended.object;
12415 var methods = this._extended.methods;
12416
12417 for (var i = 0; i < methods.length; i++) {
12418 var method = methods[i];
12419
12420 if (method.original) {
12421 object[method.name] = method.original;
12422 } else {
12423 // @TODO: better solution?
12424 delete object[method.name];
12425 }
12426 }
12427
12428 this._extended = null;
12429 }
12430 }
12431 /**
12432 * Replace a method on an object with a queued version.
12433 *
12434 * @param object - Object having the method.
12435 * @param method - The method name.
12436 */
12437
12438 }, {
12439 key: "replace",
12440 value: function replace(object, method) {
12441 var me = this;
12442 var original = object[method];
12443
12444 if (!original) {
12445 throw new Error('Method ' + method + ' undefined');
12446 }
12447
12448 object[method] = function () {
12449 for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
12450 args[_key] = arguments[_key];
12451 } // add this call to the queue
12452
12453
12454 me.queue({
12455 args: args,
12456 fn: original,
12457 context: this
12458 });
12459 };
12460 }
12461 /**
12462 * Queue a call.
12463 *
12464 * @param entry - The function or entry to be queued.
12465 */
12466
12467 }, {
12468 key: "queue",
12469 value: function queue(entry) {
12470 if (typeof entry === 'function') {
12471 this._queue.push({
12472 fn: entry
12473 });
12474 } else {
12475 this._queue.push(entry);
12476 }
12477
12478 this._flushIfNeeded();
12479 }
12480 /**
12481 * Check whether the queue needs to be flushed.
12482 */
12483
12484 }, {
12485 key: "_flushIfNeeded",
12486 value: function _flushIfNeeded() {
12487 var _this = this; // flush when the maximum is exceeded.
12488
12489
12490 if (this._queue.length > this.max) {
12491 this.flush();
12492 } // flush after a period of inactivity when a delay is configured
12493
12494
12495 if (this._timeout != null) {
12496 clearTimeout(this._timeout);
12497 this._timeout = null;
12498 }
12499
12500 if (this.queue.length > 0 && typeof this.delay === 'number') {
12501 this._timeout = setTimeout(function () {
12502 _this.flush();
12503 }, this.delay);
12504 }
12505 }
12506 /**
12507 * Flush all queued calls
12508 */
12509
12510 }, {
12511 key: "flush",
12512 value: function flush() {
12513 this._queue.splice(0).forEach(function (entry) {
12514 entry.fn.apply(entry.context || entry.fn, entry.args || []);
12515 });
12516 }
12517 }], [{
12518 key: "extend",
12519 value: function extend(object, options) {
12520 var queue = new Queue(options);
12521
12522 if (object.flush !== undefined) {
12523 throw new Error('Target object already has a property flush');
12524 }
12525
12526 object.flush = function () {
12527 queue.flush();
12528 };
12529
12530 var methods = [{
12531 name: 'flush',
12532 original: undefined
12533 }];
12534
12535 if (options && options.replace) {
12536 for (var i = 0; i < options.replace.length; i++) {
12537 var name = options.replace[i];
12538 methods.push({
12539 name: name,
12540 // @TODO: better solution?
12541 original: object[name]
12542 }); // @TODO: better solution?
12543
12544 queue.replace(object, name);
12545 }
12546 }
12547
12548 queue._extended = {
12549 object: object,
12550 methods: methods
12551 };
12552 return queue;
12553 }
12554 }]);
12555 return Queue;
12556}();
12557
12558function _arrayWithoutHoles$1(arr) {
12559 if (Array.isArray(arr)) {
12560 for (var i = 0, arr2 = new Array(arr.length); i < arr.length; i++) {
12561 arr2[i] = arr[i];
12562 }
12563
12564 return arr2;
12565 }
12566}
12567
12568var arrayWithoutHoles$1 = _arrayWithoutHoles$1;
12569
12570function _iterableToArray$1(iter) {
12571 if (Symbol.iterator in Object(iter) || Object.prototype.toString.call(iter) === "[object Arguments]") return Array.from(iter);
12572}
12573
12574var iterableToArray$1 = _iterableToArray$1;
12575
12576function _nonIterableSpread$1() {
12577 throw new TypeError("Invalid attempt to spread non-iterable instance");
12578}
12579
12580var nonIterableSpread$1 = _nonIterableSpread$1;
12581
12582function _toConsumableArray$1(arr) {
12583 return arrayWithoutHoles$1(arr) || iterableToArray$1(arr) || nonIterableSpread$1();
12584}
12585
12586var toConsumableArray$1 = _toConsumableArray$1;
12587/**
12588 * [[DataSet]] code that can be reused in [[DataView]] or other similar implementations of [[DataInterface]].
12589 *
12590 * @typeParam Item - Item type that may or may not have an id.
12591 * @typeParam IdProp - Name of the property that contains the id.
12592 */
12593
12594var DataSetPart =
12595/*#__PURE__*/
12596function () {
12597 function DataSetPart() {
12598 classCallCheck(this, DataSetPart);
12599 this._subscribers = {
12600 '*': [],
12601 add: [],
12602 remove: [],
12603 update: []
12604 };
12605 /**
12606 * @deprecated Use on instead (PS: DataView.subscribe === DataView.on).
12607 */
12608
12609 this.subscribe = DataSetPart.prototype.on;
12610 /**
12611 * @deprecated Use off instead (PS: DataView.unsubscribe === DataView.off).
12612 */
12613
12614 this.unsubscribe = DataSetPart.prototype.off;
12615 }
12616 /**
12617 * Trigger an event
12618 *
12619 * @param event - Event name.
12620 * @param payload - Event payload.
12621 * @param senderId - Id of the sender.
12622 */
12623
12624
12625 createClass(DataSetPart, [{
12626 key: "_trigger",
12627 value: function _trigger(event, payload, senderId) {
12628 if (event === '*') {
12629 throw new Error('Cannot trigger event *');
12630 }
12631
12632 var subscribers = [].concat(toConsumableArray$1(this._subscribers[event]), toConsumableArray$1(this._subscribers['*']));
12633
12634 for (var i = 0, len = subscribers.length; i < len; i++) {
12635 var subscriber = subscribers[i];
12636
12637 if (subscriber.callback) {
12638 subscriber.callback(event, payload, senderId != null ? senderId : null);
12639 }
12640 }
12641 }
12642 /**
12643 * Subscribe to an event, add an event listener.
12644 *
12645 * @param event - Event name.
12646 * @param callback - Callback method.
12647 */
12648
12649 }, {
12650 key: "on",
12651 value: function on(event, callback) {
12652 this._subscribers[event].push({
12653 callback: callback
12654 });
12655 }
12656 /**
12657 * Unsubscribe from an event, remove an event listener.
12658 *
12659 * @remarks If the same callback was subscribed more than once **all** occurences will be removed.
12660 *
12661 * @param event - Event name.
12662 * @param callback - Callback method.
12663 */
12664
12665 }, {
12666 key: "off",
12667 value: function off(event, callback) {
12668 this._subscribers[event] = this._subscribers[event].filter(function (listener) {
12669 return listener.callback !== callback;
12670 });
12671 }
12672 }]);
12673 return DataSetPart;
12674}();
12675
12676function ownKeys$1(object, enumerableOnly) {
12677 var keys = Object.keys(object);
12678
12679 if (Object.getOwnPropertySymbols) {
12680 keys.push.apply(keys, Object.getOwnPropertySymbols(object));
12681 }
12682
12683 if (enumerableOnly) keys = keys.filter(function (sym) {
12684 return Object.getOwnPropertyDescriptor(object, sym).enumerable;
12685 });
12686 return keys;
12687}
12688
12689function _objectSpread$1(target) {
12690 for (var i = 1; i < arguments.length; i++) {
12691 var source = arguments[i] != null ? arguments[i] : {};
12692
12693 if (i % 2) {
12694 ownKeys$1(source, true).forEach(function (key) {
12695 defineProperty$1(target, key, source[key]);
12696 });
12697 } else if (Object.getOwnPropertyDescriptors) {
12698 Object.defineProperties(target, Object.getOwnPropertyDescriptors(source));
12699 } else {
12700 ownKeys$1(source).forEach(function (key) {
12701 Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key));
12702 });
12703 }
12704 }
12705
12706 return target;
12707}
12708/**
12709 * # DataSet
12710 *
12711 * Vis.js comes with a flexible DataSet, which can be used to hold and manipulate unstructured data and listen for changes in the data. The DataSet is key/value based. Data items can be added, updated and removed from the DataSet, and one can subscribe to changes in the DataSet. The data in the DataSet can be filtered and ordered, and fields (like dates) can be converted to a specific type. Data can be normalized when appending it to the DataSet as well.
12712 *
12713 * ## Example
12714 *
12715 * The following example shows how to use a DataSet.
12716 *
12717 * ```javascript
12718 * // create a DataSet
12719 * var options = {};
12720 * var data = new vis.DataSet(options);
12721 *
12722 * // add items
12723 * // note that the data items can contain different properties and data formats
12724 * data.add([
12725 * {id: 1, text: 'item 1', date: new Date(2013, 6, 20), group: 1, first: true},
12726 * {id: 2, text: 'item 2', date: '2013-06-23', group: 2},
12727 * {id: 3, text: 'item 3', date: '2013-06-25', group: 2},
12728 * {id: 4, text: 'item 4'}
12729 * ]);
12730 *
12731 * // subscribe to any change in the DataSet
12732 * data.on('*', function (event, properties, senderId) {
12733 * console.log('event', event, properties);
12734 * });
12735 *
12736 * // update an existing item
12737 * data.update({id: 2, group: 1});
12738 *
12739 * // remove an item
12740 * data.remove(4);
12741 *
12742 * // get all ids
12743 * var ids = data.getIds();
12744 * console.log('ids', ids);
12745 *
12746 * // get a specific item
12747 * var item1 = data.get(1);
12748 * console.log('item1', item1);
12749 *
12750 * // retrieve a filtered subset of the data
12751 * var items = data.get({
12752 * filter: function (item) {
12753 * return item.group == 1;
12754 * }
12755 * });
12756 * console.log('filtered items', items);
12757 *
12758 * // retrieve formatted items
12759 * var items = data.get({
12760 * fields: ['id', 'date'],
12761 * type: {
12762 * date: 'ISODate'
12763 * }
12764 * });
12765 * console.log('formatted items', items);
12766 * ```
12767 *
12768 * @typeParam Item - Item type that may or may not have an id.
12769 * @typeParam IdProp - Name of the property that contains the id.
12770 */
12771
12772
12773var DataSet =
12774/*#__PURE__*/
12775function (_DataSetPart) {
12776 inherits(DataSet, _DataSetPart);
12777 /**
12778 * Construct a new DataSet.
12779 *
12780 * @param data - Initial data or options.
12781 * @param options - Options (type error if data is also options).
12782 */
12783
12784 function DataSet(data, options) {
12785 var _this;
12786
12787 classCallCheck(this, DataSet);
12788 _this = possibleConstructorReturn(this, getPrototypeOf(DataSet).call(this)); // correctly read optional arguments
12789
12790 if (data && !Array.isArray(data)) {
12791 options = data;
12792 data = [];
12793 }
12794
12795 _this._options = options || {};
12796 _this._data = Object.create({}); // map with data indexed by id
12797
12798 _this.length = 0; // number of items in the DataSet
12799
12800 _this._idProp = _this._options.fieldId || 'id'; // name of the field containing id
12801
12802 _this._type = {}; // internal field types (NOTE: this can differ from this._options.type)
12803 // all variants of a Date are internally stored as Date, so we can convert
12804 // from everything to everything (also from ISODate to Number for example)
12805
12806 if (_this._options.type) {
12807 var fields = Object.keys(_this._options.type);
12808
12809 for (var i = 0, len = fields.length; i < len; i++) {
12810 var field = fields[i];
12811 var value = _this._options.type[field];
12812
12813 if (value == 'Date' || value == 'ISODate' || value == 'ASPDate') {
12814 _this._type[field] = 'Date';
12815 } else {
12816 _this._type[field] = value;
12817 }
12818 }
12819 } // add initial data when provided
12820
12821
12822 if (data && data.length) {
12823 _this.add(data);
12824 }
12825
12826 _this.setOptions(options);
12827
12828 return _this;
12829 }
12830 /**
12831 * Set new options.
12832 *
12833 * @param options - The new options.
12834 */
12835
12836
12837 createClass(DataSet, [{
12838 key: "setOptions",
12839 value: function setOptions(options) {
12840 if (options && options.queue !== undefined) {
12841 if (options.queue === false) {
12842 // delete queue if loaded
12843 if (this._queue) {
12844 this._queue.destroy();
12845
12846 delete this._queue;
12847 }
12848 } else {
12849 // create queue and update its options
12850 if (!this._queue) {
12851 this._queue = Queue.extend(this, {
12852 replace: ['add', 'update', 'remove']
12853 });
12854 }
12855
12856 if (options.queue && _typeof_1$1(options.queue) === 'object') {
12857 this._queue.setOptions(options.queue);
12858 }
12859 }
12860 }
12861 }
12862 /**
12863 * Add a data item or an array with items.
12864 *
12865 * After the items are added to the DataSet, the DataSet will trigger an event `add`. When a `senderId` is provided, this id will be passed with the triggered event to all subscribers.
12866 *
12867 * ## Example
12868 *
12869 * ```javascript
12870 * // create a DataSet
12871 * const data = new vis.DataSet()
12872 *
12873 * // add items
12874 * const ids = data.add([
12875 * { id: 1, text: 'item 1' },
12876 * { id: 2, text: 'item 2' },
12877 * { text: 'item without an id' }
12878 * ])
12879 *
12880 * console.log(ids) // [1, 2, '<UUIDv4>']
12881 * ```
12882 *
12883 * @param data - Items to be added (ids will be generated if missing).
12884 * @param senderId - Sender id.
12885 *
12886 * @returns addedIds - Array with the ids (generated if not present) of the added items.
12887 *
12888 * @throws When an item with the same id as any of the added items already exists.
12889 */
12890
12891 }, {
12892 key: "add",
12893 value: function add(data, senderId) {
12894 var addedIds = [];
12895 var id;
12896
12897 if (Array.isArray(data)) {
12898 // Array
12899 for (var i = 0, len = data.length; i < len; i++) {
12900 id = this._addItem(data[i]);
12901 addedIds.push(id);
12902 }
12903 } else if (data && _typeof_1$1(data) === 'object') {
12904 // Single item
12905 id = this._addItem(data);
12906 addedIds.push(id);
12907 } else {
12908 throw new Error('Unknown dataType');
12909 }
12910
12911 if (addedIds.length) {
12912 this._trigger('add', {
12913 items: addedIds
12914 }, senderId);
12915 }
12916
12917 return addedIds;
12918 }
12919 /**
12920 * Update existing items. When an item does not exist, it will be created
12921 *
12922 * The provided properties will be merged in the existing item. When an item does not exist, it will be created.
12923 *
12924 * After the items are updated, the DataSet will trigger an event `add` for the added items, and an event `update`. When a `senderId` is provided, this id will be passed with the triggered event to all subscribers.
12925 *
12926 * ## Example
12927 *
12928 * ```javascript
12929 * // create a DataSet
12930 * const data = new vis.DataSet([
12931 * { id: 1, text: 'item 1' },
12932 * { id: 2, text: 'item 2' },
12933 * { id: 3, text: 'item 3' }
12934 * ])
12935 *
12936 * // update items
12937 * const ids = data.update([
12938 * { id: 2, text: 'item 2 (updated)' },
12939 * { id: 4, text: 'item 4 (new)' }
12940 * ])
12941 *
12942 * console.log(ids) // [2, 4]
12943 * ```
12944 *
12945 * @param data - Items to be updated (if the id is already present) or added (if the id is missing).
12946 * @param senderId - Sender id.
12947 *
12948 * @returns updatedIds - The ids of the added (these may be newly generated if there was no id in the item from the data) or updated items.
12949 *
12950 * @throws When the supplied data is neither an item nor an array of items.
12951 */
12952
12953 }, {
12954 key: "update",
12955 value: function update(data, senderId) {
12956 var _this2 = this;
12957
12958 var addedIds = [];
12959 var updatedIds = [];
12960 var oldData = [];
12961 var updatedData = [];
12962 var idProp = this._idProp;
12963
12964 var addOrUpdate = function addOrUpdate(item) {
12965 var origId = item[idProp];
12966
12967 if (origId != null && _this2._data[origId]) {
12968 var fullItem = item; // it has an id, therefore it is a fullitem
12969
12970 var oldItem = Object.assign({}, _this2._data[origId]); // update item
12971
12972 var id = _this2._updateItem(fullItem);
12973
12974 updatedIds.push(id);
12975 updatedData.push(fullItem);
12976 oldData.push(oldItem);
12977 } else {
12978 // add new item
12979 var _id = _this2._addItem(item);
12980
12981 addedIds.push(_id);
12982 }
12983 };
12984
12985 if (Array.isArray(data)) {
12986 // Array
12987 for (var i = 0, len = data.length; i < len; i++) {
12988 if (data[i] && _typeof_1$1(data[i]) === 'object') {
12989 addOrUpdate(data[i]);
12990 } else {
12991 console.warn('Ignoring input item, which is not an object at index ' + i);
12992 }
12993 }
12994 } else if (data && _typeof_1$1(data) === 'object') {
12995 // Single item
12996 addOrUpdate(data);
12997 } else {
12998 throw new Error('Unknown dataType');
12999 }
13000
13001 if (addedIds.length) {
13002 this._trigger('add', {
13003 items: addedIds
13004 }, senderId);
13005 }
13006
13007 if (updatedIds.length) {
13008 var props = {
13009 items: updatedIds,
13010 oldData: oldData,
13011 data: updatedData
13012 }; // TODO: remove deprecated property 'data' some day
13013 //Object.defineProperty(props, 'data', {
13014 // 'get': (function() {
13015 // console.warn('Property data is deprecated. Use DataSet.get(ids) to retrieve the new data, use the oldData property on this object to get the old data');
13016 // return updatedData;
13017 // }).bind(this)
13018 //});
13019
13020 this._trigger('update', props, senderId);
13021 }
13022
13023 return addedIds.concat(updatedIds);
13024 }
13025 /** @inheritdoc */
13026
13027 }, {
13028 key: "get",
13029 value: function get(first, second) {
13030 // @TODO: Woudn't it be better to split this into multiple methods?
13031 // parse the arguments
13032 var id = undefined;
13033 var ids = undefined;
13034 var options = undefined;
13035
13036 if (isId(first)) {
13037 // get(id [, options])
13038 id = first;
13039 options = second;
13040 } else if (Array.isArray(first)) {
13041 // get(ids [, options])
13042 ids = first;
13043 options = second;
13044 } else {
13045 // get([, options])
13046 options = first;
13047 } // determine the return type
13048
13049
13050 var returnType = options && options.returnType === 'Object' ? 'Object' : 'Array'; // @TODO: WTF is this? Or am I missing something?
13051 // var returnType
13052 // if (options && options.returnType) {
13053 // var allowedValues = ['Array', 'Object']
13054 // returnType =
13055 // allowedValues.indexOf(options.returnType) == -1
13056 // ? 'Array'
13057 // : options.returnType
13058 // } else {
13059 // returnType = 'Array'
13060 // }
13061 // build options
13062
13063 var type = options && options.type || this._options.type;
13064 var filter = options && options.filter;
13065 var items = [];
13066 var item = null;
13067 var itemIds = null;
13068 var itemId = null; // convert items
13069
13070 if (id != null) {
13071 // return a single item
13072 item = this._getItem(id, type);
13073
13074 if (item && filter && !filter(item)) {
13075 item = null;
13076 }
13077 } else if (ids != null) {
13078 // return a subset of items
13079 for (var i = 0, len = ids.length; i < len; i++) {
13080 item = this._getItem(ids[i], type);
13081
13082 if (item != null && (!filter || filter(item))) {
13083 items.push(item);
13084 }
13085 }
13086 } else {
13087 // return all items
13088 itemIds = Object.keys(this._data);
13089
13090 for (var _i = 0, _len = itemIds.length; _i < _len; _i++) {
13091 itemId = itemIds[_i];
13092 item = this._getItem(itemId, type);
13093
13094 if (item != null && (!filter || filter(item))) {
13095 items.push(item);
13096 }
13097 }
13098 } // order the results
13099
13100
13101 if (options && options.order && id == undefined) {
13102 this._sort(items, options.order);
13103 } // filter fields of the items
13104
13105
13106 if (options && options.fields) {
13107 var fields = options.fields;
13108
13109 if (id != undefined && item != null) {
13110 item = this._filterFields(item, fields);
13111 } else {
13112 for (var _i2 = 0, _len2 = items.length; _i2 < _len2; _i2++) {
13113 items[_i2] = this._filterFields(items[_i2], fields);
13114 }
13115 }
13116 } // return the results
13117
13118
13119 if (returnType == 'Object') {
13120 var result = {};
13121
13122 for (var _i3 = 0, _len3 = items.length; _i3 < _len3; _i3++) {
13123 var resultant = items[_i3]; // @TODO: Shoudn't this be this._fieldId?
13124 // result[resultant.id] = resultant
13125
13126 var _id2 = resultant[this._idProp];
13127 result[_id2] = resultant;
13128 }
13129
13130 return result;
13131 } else {
13132 if (id != null) {
13133 // a single item
13134 return item;
13135 } else {
13136 // just return our array
13137 return items;
13138 }
13139 }
13140 }
13141 /** @inheritdoc */
13142
13143 }, {
13144 key: "getIds",
13145 value: function getIds(options) {
13146 var data = this._data;
13147 var filter = options && options.filter;
13148 var order = options && options.order;
13149 var type = options && options.type || this._options.type;
13150 var itemIds = Object.keys(data);
13151 var ids = [];
13152 var item;
13153 var items;
13154
13155 if (filter) {
13156 // get filtered items
13157 if (order) {
13158 // create ordered list
13159 items = [];
13160
13161 for (var i = 0, len = itemIds.length; i < len; i++) {
13162 var id = itemIds[i];
13163 item = this._getItem(id, type);
13164
13165 if (filter(item)) {
13166 items.push(item);
13167 }
13168 }
13169
13170 this._sort(items, order);
13171
13172 for (var _i4 = 0, _len4 = items.length; _i4 < _len4; _i4++) {
13173 ids.push(items[_i4][this._idProp]);
13174 }
13175 } else {
13176 // create unordered list
13177 for (var _i5 = 0, _len5 = itemIds.length; _i5 < _len5; _i5++) {
13178 var _id3 = itemIds[_i5];
13179 item = this._getItem(_id3, type);
13180
13181 if (filter(item)) {
13182 ids.push(item[this._idProp]);
13183 }
13184 }
13185 }
13186 } else {
13187 // get all items
13188 if (order) {
13189 // create an ordered list
13190 items = [];
13191
13192 for (var _i6 = 0, _len6 = itemIds.length; _i6 < _len6; _i6++) {
13193 var _id4 = itemIds[_i6];
13194 items.push(data[_id4]);
13195 }
13196
13197 this._sort(items, order);
13198
13199 for (var _i7 = 0, _len7 = items.length; _i7 < _len7; _i7++) {
13200 ids.push(items[_i7][this._idProp]);
13201 }
13202 } else {
13203 // create unordered list
13204 for (var _i8 = 0, _len8 = itemIds.length; _i8 < _len8; _i8++) {
13205 var _id5 = itemIds[_i8];
13206 item = data[_id5];
13207 ids.push(item[this._idProp]);
13208 }
13209 }
13210 }
13211
13212 return ids;
13213 }
13214 /** @inheritdoc */
13215
13216 }, {
13217 key: "getDataSet",
13218 value: function getDataSet() {
13219 return this;
13220 }
13221 /** @inheritdoc */
13222
13223 }, {
13224 key: "forEach",
13225 value: function forEach(callback, options) {
13226 var filter = options && options.filter;
13227 var type = options && options.type || this._options.type;
13228 var data = this._data;
13229 var itemIds = Object.keys(data);
13230
13231 if (options && options.order) {
13232 // execute forEach on ordered list
13233 var items = this.get(options);
13234
13235 for (var i = 0, len = items.length; i < len; i++) {
13236 var item = items[i];
13237 var id = item[this._idProp];
13238 callback(item, id);
13239 }
13240 } else {
13241 // unordered
13242 for (var _i9 = 0, _len9 = itemIds.length; _i9 < _len9; _i9++) {
13243 var _id6 = itemIds[_i9];
13244
13245 var _item = this._getItem(_id6, type);
13246
13247 if (!filter || filter(_item)) {
13248 callback(_item, _id6);
13249 }
13250 }
13251 }
13252 }
13253 /** @inheritdoc */
13254
13255 }, {
13256 key: "map",
13257 value: function map(callback, options) {
13258 var filter = options && options.filter;
13259 var type = options && options.type || this._options.type;
13260 var mappedItems = [];
13261 var data = this._data;
13262 var itemIds = Object.keys(data); // convert and filter items
13263
13264 for (var i = 0, len = itemIds.length; i < len; i++) {
13265 var id = itemIds[i];
13266
13267 var item = this._getItem(id, type);
13268
13269 if (!filter || filter(item)) {
13270 mappedItems.push(callback(item, id));
13271 }
13272 } // order items
13273
13274
13275 if (options && options.order) {
13276 this._sort(mappedItems, options.order);
13277 }
13278
13279 return mappedItems;
13280 }
13281 /**
13282 * Filter the fields of an item.
13283 *
13284 * @param item - The item whose fields should be filtered.
13285 * @param fields - The names of the fields that will be kept.
13286 *
13287 * @typeParam K - Field name type.
13288 *
13289 * @returns The item without any additional fields.
13290 */
13291
13292 }, {
13293 key: "_filterFields",
13294 value: function _filterFields(item, fields) {
13295 if (!item) {
13296 // item is null
13297 return item;
13298 }
13299
13300 return (Array.isArray(fields) ? // Use the supplied array
13301 fields : // Use the keys of the supplied object
13302 Object.keys(fields)).reduce(function (filteredItem, field) {
13303 filteredItem[field] = item[field];
13304 return filteredItem;
13305 }, {});
13306 }
13307 /**
13308 * Sort the provided array with items.
13309 *
13310 * @param items - Items to be sorted in place.
13311 * @param order - A field name or custom sort function.
13312 *
13313 * @typeParam T - The type of the items in the items array.
13314 */
13315
13316 }, {
13317 key: "_sort",
13318 value: function _sort(items, order) {
13319 if (typeof order === 'string') {
13320 // order by provided field name
13321 var name = order; // field name
13322
13323 items.sort(function (a, b) {
13324 // @TODO: How to treat missing properties?
13325 var av = a[name];
13326 var bv = b[name];
13327 return av > bv ? 1 : av < bv ? -1 : 0;
13328 });
13329 } else if (typeof order === 'function') {
13330 // order by sort function
13331 items.sort(order);
13332 } else {
13333 // TODO: extend order by an Object {field:string, direction:string}
13334 // where direction can be 'asc' or 'desc'
13335 throw new TypeError('Order must be a function or a string');
13336 }
13337 }
13338 /**
13339 * Remove an item or multiple items by “reference” (only the id is used) or by id.
13340 *
13341 * The method ignores removal of non-existing items, and returns an array containing the ids of the items which are actually removed from the DataSet.
13342 *
13343 * After the items are removed, the DataSet will trigger an event `remove` for the removed items. When a `senderId` is provided, this id will be passed with the triggered event to all subscribers.
13344 *
13345 * ## Example
13346 * ```javascript
13347 * // create a DataSet
13348 * const data = new vis.DataSet([
13349 * { id: 1, text: 'item 1' },
13350 * { id: 2, text: 'item 2' },
13351 * { id: 3, text: 'item 3' }
13352 * ])
13353 *
13354 * // remove items
13355 * const ids = data.remove([2, { id: 3 }, 4])
13356 *
13357 * console.log(ids) // [2, 3]
13358 * ```
13359 *
13360 * @param id - One or more items or ids of items to be removed.
13361 * @param senderId - Sender id.
13362 *
13363 * @returns The ids of the removed items.
13364 */
13365
13366 }, {
13367 key: "remove",
13368 value: function remove(id, senderId) {
13369 var removedIds = [];
13370 var removedItems = []; // force everything to be an array for simplicity
13371
13372 var ids = Array.isArray(id) ? id : [id];
13373
13374 for (var i = 0, len = ids.length; i < len; i++) {
13375 var item = this._remove(ids[i]);
13376
13377 if (item) {
13378 var itemId = item[this._idProp];
13379
13380 if (itemId != null) {
13381 removedIds.push(itemId);
13382 removedItems.push(item);
13383 }
13384 }
13385 }
13386
13387 if (removedIds.length) {
13388 this._trigger('remove', {
13389 items: removedIds,
13390 oldData: removedItems
13391 }, senderId);
13392 }
13393
13394 return removedIds;
13395 }
13396 /**
13397 * Remove an item by its id or reference.
13398 *
13399 * @param id - Id of an item or the item itself.
13400 *
13401 * @returns The removed item if removed, null otherwise.
13402 */
13403
13404 }, {
13405 key: "_remove",
13406 value: function _remove(id) {
13407 // @TODO: It origianlly returned the item although the docs say id.
13408 // The code expects the item, so probably an error in the docs.
13409 var ident; // confirm the id to use based on the args type
13410
13411 if (isId(id)) {
13412 ident = id;
13413 } else if (id && _typeof_1$1(id) === 'object') {
13414 ident = id[this._idProp]; // look for the identifier field using ._idProp
13415 } // do the removing if the item is found
13416
13417
13418 if (ident != null && this._data[ident]) {
13419 var item = this._data[ident];
13420 delete this._data[ident];
13421 --this.length;
13422 return item;
13423 }
13424
13425 return null;
13426 }
13427 /**
13428 * Clear the entire data set.
13429 *
13430 * After the items are removed, the [[DataSet]] will trigger an event `remove` for all removed items. When a `senderId` is provided, this id will be passed with the triggered event to all subscribers.
13431 *
13432 * @param senderId - Sender id.
13433 *
13434 * @returns removedIds - The ids of all removed items.
13435 */
13436
13437 }, {
13438 key: "clear",
13439 value: function clear(senderId) {
13440 var ids = Object.keys(this._data);
13441 var items = [];
13442
13443 for (var i = 0, len = ids.length; i < len; i++) {
13444 items.push(this._data[ids[i]]);
13445 }
13446
13447 this._data = {};
13448 this.length = 0;
13449
13450 this._trigger('remove', {
13451 items: ids,
13452 oldData: items
13453 }, senderId);
13454
13455 return ids;
13456 }
13457 /**
13458 * Find the item with maximum value of a specified field.
13459 *
13460 * @param field - Name of the property that should be searched for max value.
13461 *
13462 * @returns Item containing max value, or null if no items.
13463 */
13464
13465 }, {
13466 key: "max",
13467 value: function max(field) {
13468 var data = this._data;
13469 var itemIds = Object.keys(data);
13470 var max = null;
13471 var maxField = null;
13472
13473 for (var i = 0, len = itemIds.length; i < len; i++) {
13474 var id = itemIds[i];
13475 var item = data[id];
13476 var itemField = item[field];
13477
13478 if (itemField != null && (maxField == null || itemField > maxField)) {
13479 max = item;
13480 maxField = itemField;
13481 }
13482 }
13483
13484 return max;
13485 }
13486 /**
13487 * Find the item with minimum value of a specified field.
13488 *
13489 * @param field - Name of the property that should be searched for min value.
13490 *
13491 * @returns Item containing min value, or null if no items.
13492 */
13493
13494 }, {
13495 key: "min",
13496 value: function min(field) {
13497 var data = this._data;
13498 var itemIds = Object.keys(data);
13499 var min = null;
13500 var minField = null;
13501
13502 for (var i = 0, len = itemIds.length; i < len; i++) {
13503 var id = itemIds[i];
13504 var item = data[id];
13505 var itemField = item[field];
13506
13507 if (itemField != null && (minField == null || itemField < minField)) {
13508 min = item;
13509 minField = itemField;
13510 }
13511 }
13512
13513 return min;
13514 }
13515 /**
13516 * Find all distinct values of a specified field
13517 *
13518 * @param prop - The property name whose distinct values should be returned.
13519 *
13520 * @returns Unordered array containing all distinct values. Items without specified property are ignored.
13521 */
13522
13523 }, {
13524 key: "distinct",
13525 value: function distinct(prop) {
13526 var data = this._data;
13527 var itemIds = Object.keys(data);
13528 var values = [];
13529 var fieldType = this._options.type && this._options.type[prop] || null;
13530 var count = 0;
13531
13532 for (var i = 0, len = itemIds.length; i < len; i++) {
13533 var id = itemIds[i];
13534 var item = data[id];
13535 var value = item[prop];
13536 var exists = false;
13537
13538 for (var j = 0; j < count; j++) {
13539 if (values[j] == value) {
13540 exists = true;
13541 break;
13542 }
13543 }
13544
13545 if (!exists && value !== undefined) {
13546 values[count] = value;
13547 count++;
13548 }
13549 }
13550
13551 if (fieldType) {
13552 for (var _i10 = 0, _len10 = values.length; _i10 < _len10; _i10++) {
13553 values[_i10] = convert$1(values[_i10], fieldType);
13554 }
13555 }
13556
13557 return values;
13558 }
13559 /**
13560 * Add a single item. Will fail when an item with the same id already exists.
13561 *
13562 * @param item - A new item to be added.
13563 *
13564 * @returns Added item's id. An id is generated when it is not present in the item.
13565 */
13566
13567 }, {
13568 key: "_addItem",
13569 value: function _addItem(item) {
13570 var id = item[this._idProp];
13571
13572 if (id != null) {
13573 // check whether this id is already taken
13574 if (this._data[id]) {
13575 // item already exists
13576 throw new Error('Cannot add item: item with id ' + id + ' already exists');
13577 }
13578 } else {
13579 // generate an id
13580 id = uuid4$1();
13581 item[this._idProp] = id;
13582 }
13583
13584 var d = {};
13585 var fields = Object.keys(item);
13586
13587 for (var i = 0, len = fields.length; i < len; i++) {
13588 var field = fields[i];
13589 var fieldType = this._type[field]; // type may be undefined
13590
13591 d[field] = convert$1(item[field], fieldType);
13592 }
13593
13594 this._data[id] = d;
13595 this.length++;
13596 return id;
13597 }
13598 /**
13599 * Get an item. Fields can be converted to a specific type
13600 *
13601 * @param id - Id of the requested item.
13602 * @param types - Property name to type name object map of type converstions.
13603 *
13604 * @returns The item, optionally after type conversion.
13605 */
13606
13607 }, {
13608 key: "_getItem",
13609 value: function _getItem(id, types) {
13610 // @TODO: I have no idea how to type this.
13611 // get the item from the dataset
13612 var raw = this._data[id];
13613
13614 if (!raw) {
13615 return null;
13616 } // convert the items field types
13617
13618
13619 var converted;
13620 var fields = Object.keys(raw);
13621
13622 if (types) {
13623 converted = {};
13624
13625 for (var i = 0, len = fields.length; i < len; i++) {
13626 var field = fields[i];
13627 var value = raw[field];
13628 converted[field] = convert$1(value, types[field]);
13629 }
13630 } else {
13631 // no field types specified, no converting needed
13632 converted = _objectSpread$1({}, raw);
13633 }
13634
13635 if (converted[this._idProp] == null) {
13636 converted[this._idProp] = raw.id;
13637 }
13638
13639 return converted;
13640 }
13641 /**
13642 * Update a single item: merge with existing item.
13643 * Will fail when the item has no id, or when there does not exist an item with the same id.
13644 *
13645 * @param item - The new item
13646 *
13647 * @returns The id of the updated item.
13648 */
13649
13650 }, {
13651 key: "_updateItem",
13652 value: function _updateItem(item) {
13653 var id = item[this._idProp];
13654
13655 if (id == null) {
13656 throw new Error('Cannot update item: item has no id (item: ' + JSON.stringify(item) + ')');
13657 }
13658
13659 var d = this._data[id];
13660
13661 if (!d) {
13662 // item doesn't exist
13663 throw new Error('Cannot update item: no item with id ' + id + ' found');
13664 } // merge with current item
13665
13666
13667 var fields = Object.keys(item);
13668
13669 for (var i = 0, len = fields.length; i < len; i++) {
13670 var field = fields[i];
13671 var fieldType = this._type[field] // type may be undefined
13672 ;
13673 d[field] = convert$1(item[field], fieldType);
13674 }
13675
13676 return id;
13677 }
13678 }]);
13679 return DataSet;
13680}(DataSetPart);
13681/**
13682 * DataView
13683 *
13684 * A DataView offers a filtered and/or formatted view on a DataSet. One can subscribe to changes in a DataView, and easily get filtered or formatted data without having to specify filters and field types all the time.
13685 *
13686 * ## Example
13687 * ```javascript
13688 * // create a DataSet
13689 * var data = new vis.DataSet();
13690 * data.add([
13691 * {id: 1, text: 'item 1', date: new Date(2013, 6, 20), group: 1, first: true},
13692 * {id: 2, text: 'item 2', date: '2013-06-23', group: 2},
13693 * {id: 3, text: 'item 3', date: '2013-06-25', group: 2},
13694 * {id: 4, text: 'item 4'}
13695 * ]);
13696 *
13697 * // create a DataView
13698 * // the view will only contain items having a property group with value 1,
13699 * // and will only output fields id, text, and date.
13700 * var view = new vis.DataView(data, {
13701 * filter: function (item) {
13702 * return (item.group == 1);
13703 * },
13704 * fields: ['id', 'text', 'date']
13705 * });
13706 *
13707 * // subscribe to any change in the DataView
13708 * view.on('*', function (event, properties, senderId) {
13709 * console.log('event', event, properties);
13710 * });
13711 *
13712 * // update an item in the data set
13713 * data.update({id: 2, group: 1});
13714 *
13715 * // get all ids in the view
13716 * var ids = view.getIds();
13717 * console.log('ids', ids); // will output [1, 2]
13718 *
13719 * // get all items in the view
13720 * var items = view.get();
13721 * ```
13722 *
13723 * @typeParam Item - Item type that may or may not have an id.
13724 * @typeParam IdProp - Name of the property that contains the id.
13725 */
13726
13727
13728var DataView =
13729/*#__PURE__*/
13730function (_DataSetPart) {
13731 inherits(DataView, _DataSetPart);
13732 /**
13733 * Create a DataView.
13734 *
13735 * @param data - The instance containing data (directly or indirectly).
13736 * @param options - Options to configure this data view.
13737 */
13738
13739 function DataView(data, options) {
13740 var _this;
13741
13742 classCallCheck(this, DataView);
13743 _this = possibleConstructorReturn(this, getPrototypeOf(DataView).call(this));
13744 /** @inheritdoc */
13745
13746 _this.length = 0;
13747 _this._ids = {}; // ids of the items currently in memory (just contains a boolean true)
13748
13749 _this._options = options || {};
13750 _this.listener = _this._onEvent.bind(assertThisInitialized(_this));
13751
13752 _this.setData(data);
13753
13754 return _this;
13755 } // TODO: implement a function .config() to dynamically update things like configured filter
13756 // and trigger changes accordingly
13757
13758 /**
13759 * Set a data source for the view.
13760 *
13761 * @param data - The instance containing data (directly or indirectly).
13762 */
13763
13764
13765 createClass(DataView, [{
13766 key: "setData",
13767 value: function setData(data) {
13768 if (this._data) {
13769 // unsubscribe from current dataset
13770 if (this._data.off) {
13771 this._data.off('*', this.listener);
13772 } // trigger a remove of all items in memory
13773
13774
13775 var ids = this._data.getIds({
13776 filter: this._options.filter
13777 });
13778
13779 var items = this._data.get(ids);
13780
13781 this._ids = {};
13782 this.length = 0;
13783
13784 this._trigger('remove', {
13785 items: ids,
13786 oldData: items
13787 });
13788 }
13789
13790 if (data != null) {
13791 this._data = data; // trigger an add of all added items
13792
13793 var _ids = this._data.getIds({
13794 filter: this._options.filter
13795 });
13796
13797 for (var i = 0, len = _ids.length; i < len; i++) {
13798 var id = _ids[i];
13799 this._ids[id] = true;
13800 }
13801
13802 this.length = _ids.length;
13803
13804 this._trigger('add', {
13805 items: _ids
13806 });
13807 } else {
13808 this._data = new DataSet();
13809 } // subscribe to new dataset
13810
13811
13812 if (this._data.on) {
13813 this._data.on('*', this.listener);
13814 }
13815 }
13816 /**
13817 * Refresh the DataView.
13818 * Useful when the DataView has a filter function containing a variable parameter.
13819 */
13820
13821 }, {
13822 key: "refresh",
13823 value: function refresh() {
13824 var ids = this._data.getIds({
13825 filter: this._options.filter
13826 });
13827
13828 var oldIds = Object.keys(this._ids);
13829 var newIds = {};
13830 var addedIds = [];
13831 var removedIds = [];
13832 var removedItems = []; // check for additions
13833
13834 for (var i = 0, len = ids.length; i < len; i++) {
13835 var id = ids[i];
13836 newIds[id] = true;
13837
13838 if (!this._ids[id]) {
13839 addedIds.push(id);
13840 this._ids[id] = true;
13841 }
13842 } // check for removals
13843
13844
13845 for (var _i = 0, _len = oldIds.length; _i < _len; _i++) {
13846 var _id = oldIds[_i];
13847
13848 var item = this._data.get(_id);
13849
13850 if (item == null) {
13851 // @TODO: Investigate.
13852 // Doesn't happen during tests or examples.
13853 // Is it really impossible or could it eventually happen?
13854 // How to handle it if it does? The types guarantee non-nullable items.
13855 console.error('If you see this, report it please.');
13856 } else if (!newIds[_id]) {
13857 removedIds.push(_id);
13858 removedItems.push(item);
13859 delete this._ids[_id];
13860 }
13861 }
13862
13863 this.length += addedIds.length - removedIds.length; // trigger events
13864
13865 if (addedIds.length) {
13866 this._trigger('add', {
13867 items: addedIds
13868 });
13869 }
13870
13871 if (removedIds.length) {
13872 this._trigger('remove', {
13873 items: removedIds,
13874 oldData: removedItems
13875 });
13876 }
13877 }
13878 /** @inheritdoc */
13879
13880 }, {
13881 key: "get",
13882 value: function get(first, second) {
13883 if (this._data == null) {
13884 return null;
13885 } // parse the arguments
13886
13887
13888 var ids = null;
13889 var options;
13890
13891 if (isId(first) || Array.isArray(first)) {
13892 ids = first;
13893 options = second;
13894 } else {
13895 options = first;
13896 } // extend the options with the default options and provided options
13897
13898
13899 var viewOptions = Object.assign({}, this._options, options); // create a combined filter method when needed
13900
13901 var thisFilter = this._options.filter;
13902 var optionsFilter = options && options.filter;
13903
13904 if (thisFilter && optionsFilter) {
13905 viewOptions.filter = function (item) {
13906 return thisFilter(item) && optionsFilter(item);
13907 };
13908 }
13909
13910 if (ids == null) {
13911 return this._data.get(viewOptions);
13912 } else {
13913 return this._data.get(ids, viewOptions);
13914 }
13915 }
13916 /** @inheritdoc */
13917
13918 }, {
13919 key: "getIds",
13920 value: function getIds(options) {
13921 if (this._data.length) {
13922 var defaultFilter = this._options.filter;
13923 var optionsFilter = options != null ? options.filter : null;
13924 var filter;
13925
13926 if (optionsFilter) {
13927 if (defaultFilter) {
13928 filter = function filter(item) {
13929 return defaultFilter(item) && optionsFilter(item);
13930 };
13931 } else {
13932 filter = optionsFilter;
13933 }
13934 } else {
13935 filter = defaultFilter;
13936 }
13937
13938 return this._data.getIds({
13939 filter: filter,
13940 order: options && options.order
13941 });
13942 } else {
13943 return [];
13944 }
13945 }
13946 /** @inheritdoc */
13947
13948 }, {
13949 key: "forEach",
13950 value: function forEach(callback, options) {
13951 if (this._data) {
13952 var defaultFilter = this._options.filter;
13953 var optionsFilter = options && options.filter;
13954 var filter;
13955
13956 if (optionsFilter) {
13957 if (defaultFilter) {
13958 filter = function filter(item) {
13959 return defaultFilter(item) && optionsFilter(item);
13960 };
13961 } else {
13962 filter = optionsFilter;
13963 }
13964 } else {
13965 filter = defaultFilter;
13966 }
13967
13968 this._data.forEach(callback, {
13969 filter: filter,
13970 order: options && options.order
13971 });
13972 }
13973 }
13974 /** @inheritdoc */
13975
13976 }, {
13977 key: "map",
13978 value: function map(callback, options) {
13979 if (this._data) {
13980 var defaultFilter = this._options.filter;
13981 var optionsFilter = options && options.filter;
13982 var filter;
13983
13984 if (optionsFilter) {
13985 if (defaultFilter) {
13986 filter = function filter(item) {
13987 return defaultFilter(item) && optionsFilter(item);
13988 };
13989 } else {
13990 filter = optionsFilter;
13991 }
13992 } else {
13993 filter = defaultFilter;
13994 }
13995
13996 return this._data.map(callback, {
13997 filter: filter,
13998 order: options && options.order
13999 });
14000 } else {
14001 return [];
14002 }
14003 }
14004 /** @inheritdoc */
14005
14006 }, {
14007 key: "getDataSet",
14008 value: function getDataSet() {
14009 return this._data.getDataSet();
14010 }
14011 /**
14012 * Event listener. Will propagate all events from the connected data set to the subscribers of the DataView, but will filter the items and only trigger when there are changes in the filtered data set.
14013 *
14014 * @param event - The name of the event.
14015 * @param params - Parameters of the event.
14016 * @param senderId - Id supplied by the sender.
14017 */
14018
14019 }, {
14020 key: "_onEvent",
14021 value: function _onEvent(event, params, senderId) {
14022 if (!params || !params.items || !this._data) {
14023 return;
14024 }
14025
14026 var ids = params.items;
14027 var addedIds = [];
14028 var updatedIds = [];
14029 var removedIds = [];
14030 var oldItems = [];
14031 var updatedItems = [];
14032 var removedItems = [];
14033
14034 switch (event) {
14035 case 'add':
14036 // filter the ids of the added items
14037 for (var i = 0, len = ids.length; i < len; i++) {
14038 var id = ids[i];
14039 var item = this.get(id);
14040
14041 if (item) {
14042 this._ids[id] = true;
14043 addedIds.push(id);
14044 }
14045 }
14046
14047 break;
14048
14049 case 'update':
14050 // determine the event from the views viewpoint: an updated
14051 // item can be added, updated, or removed from this view.
14052 for (var _i2 = 0, _len2 = ids.length; _i2 < _len2; _i2++) {
14053 var _id2 = ids[_i2];
14054
14055 var _item = this.get(_id2);
14056
14057 if (_item) {
14058 if (this._ids[_id2]) {
14059 updatedIds.push(_id2);
14060 updatedItems.push(params.data[_i2]);
14061 oldItems.push(params.oldData[_i2]);
14062 } else {
14063 this._ids[_id2] = true;
14064 addedIds.push(_id2);
14065 }
14066 } else {
14067 if (this._ids[_id2]) {
14068 delete this._ids[_id2];
14069 removedIds.push(_id2);
14070 removedItems.push(params.oldData[_i2]);
14071 }
14072 }
14073 }
14074
14075 break;
14076
14077 case 'remove':
14078 // filter the ids of the removed items
14079 for (var _i3 = 0, _len3 = ids.length; _i3 < _len3; _i3++) {
14080 var _id3 = ids[_i3];
14081
14082 if (this._ids[_id3]) {
14083 delete this._ids[_id3];
14084 removedIds.push(_id3);
14085 removedItems.push(params.oldData[_i3]);
14086 }
14087 }
14088
14089 break;
14090 }
14091
14092 this.length += addedIds.length - removedIds.length;
14093
14094 if (addedIds.length) {
14095 this._trigger('add', {
14096 items: addedIds
14097 }, senderId);
14098 }
14099
14100 if (updatedIds.length) {
14101 this._trigger('update', {
14102 items: updatedIds,
14103 oldData: oldItems,
14104 data: updatedItems
14105 }, senderId);
14106 }
14107
14108 if (removedIds.length) {
14109 this._trigger('remove', {
14110 items: removedIds,
14111 oldData: removedItems
14112 }, senderId);
14113 }
14114 }
14115 }]);
14116 return DataView;
14117}(DataSetPart);
14118
14119var index = {
14120 DataSet: DataSet,
14121 DataView: DataView,
14122 Queue: Queue
14123};
14124
14125var moment$2 = createCommonjsModule$1(function (module, exports) {
14126
14127 (function (global, factory) {
14128 module.exports = factory() ;
14129 })(commonjsGlobal$1, function () {
14130
14131 var hookCallback;
14132
14133 function hooks() {
14134 return hookCallback.apply(null, arguments);
14135 } // This is done to register the method called with moment()
14136 // without creating circular dependencies.
14137
14138
14139 function setHookCallback(callback) {
14140 hookCallback = callback;
14141 }
14142
14143 function isArray(input) {
14144 return input instanceof Array || Object.prototype.toString.call(input) === '[object Array]';
14145 }
14146
14147 function isObject(input) {
14148 // IE8 will treat undefined and null as object if it wasn't for
14149 // input != null
14150 return input != null && Object.prototype.toString.call(input) === '[object Object]';
14151 }
14152
14153 function isObjectEmpty(obj) {
14154 if (Object.getOwnPropertyNames) {
14155 return Object.getOwnPropertyNames(obj).length === 0;
14156 } else {
14157 var k;
14158
14159 for (k in obj) {
14160 if (obj.hasOwnProperty(k)) {
14161 return false;
14162 }
14163 }
14164
14165 return true;
14166 }
14167 }
14168
14169 function isUndefined(input) {
14170 return input === void 0;
14171 }
14172
14173 function isNumber(input) {
14174 return typeof input === 'number' || Object.prototype.toString.call(input) === '[object Number]';
14175 }
14176
14177 function isDate(input) {
14178 return input instanceof Date || Object.prototype.toString.call(input) === '[object Date]';
14179 }
14180
14181 function map(arr, fn) {
14182 var res = [],
14183 i;
14184
14185 for (i = 0; i < arr.length; ++i) {
14186 res.push(fn(arr[i], i));
14187 }
14188
14189 return res;
14190 }
14191
14192 function hasOwnProp(a, b) {
14193 return Object.prototype.hasOwnProperty.call(a, b);
14194 }
14195
14196 function extend(a, b) {
14197 for (var i in b) {
14198 if (hasOwnProp(b, i)) {
14199 a[i] = b[i];
14200 }
14201 }
14202
14203 if (hasOwnProp(b, 'toString')) {
14204 a.toString = b.toString;
14205 }
14206
14207 if (hasOwnProp(b, 'valueOf')) {
14208 a.valueOf = b.valueOf;
14209 }
14210
14211 return a;
14212 }
14213
14214 function createUTC(input, format, locale, strict) {
14215 return createLocalOrUTC(input, format, locale, strict, true).utc();
14216 }
14217
14218 function defaultParsingFlags() {
14219 // We need to deep clone this object.
14220 return {
14221 empty: false,
14222 unusedTokens: [],
14223 unusedInput: [],
14224 overflow: -2,
14225 charsLeftOver: 0,
14226 nullInput: false,
14227 invalidMonth: null,
14228 invalidFormat: false,
14229 userInvalidated: false,
14230 iso: false,
14231 parsedDateParts: [],
14232 meridiem: null,
14233 rfc2822: false,
14234 weekdayMismatch: false
14235 };
14236 }
14237
14238 function getParsingFlags(m) {
14239 if (m._pf == null) {
14240 m._pf = defaultParsingFlags();
14241 }
14242
14243 return m._pf;
14244 }
14245
14246 var some;
14247
14248 if (Array.prototype.some) {
14249 some = Array.prototype.some;
14250 } else {
14251 some = function (fun) {
14252 var t = Object(this);
14253 var len = t.length >>> 0;
14254
14255 for (var i = 0; i < len; i++) {
14256 if (i in t && fun.call(this, t[i], i, t)) {
14257 return true;
14258 }
14259 }
14260
14261 return false;
14262 };
14263 }
14264
14265 function isValid(m) {
14266 if (m._isValid == null) {
14267 var flags = getParsingFlags(m);
14268 var parsedParts = some.call(flags.parsedDateParts, function (i) {
14269 return i != null;
14270 });
14271 var isNowValid = !isNaN(m._d.getTime()) && flags.overflow < 0 && !flags.empty && !flags.invalidMonth && !flags.invalidWeekday && !flags.weekdayMismatch && !flags.nullInput && !flags.invalidFormat && !flags.userInvalidated && (!flags.meridiem || flags.meridiem && parsedParts);
14272
14273 if (m._strict) {
14274 isNowValid = isNowValid && flags.charsLeftOver === 0 && flags.unusedTokens.length === 0 && flags.bigHour === undefined;
14275 }
14276
14277 if (Object.isFrozen == null || !Object.isFrozen(m)) {
14278 m._isValid = isNowValid;
14279 } else {
14280 return isNowValid;
14281 }
14282 }
14283
14284 return m._isValid;
14285 }
14286
14287 function createInvalid(flags) {
14288 var m = createUTC(NaN);
14289
14290 if (flags != null) {
14291 extend(getParsingFlags(m), flags);
14292 } else {
14293 getParsingFlags(m).userInvalidated = true;
14294 }
14295
14296 return m;
14297 } // Plugins that add properties should also add the key here (null value),
14298 // so we can properly clone ourselves.
14299
14300
14301 var momentProperties = hooks.momentProperties = [];
14302
14303 function copyConfig(to, from) {
14304 var i, prop, val;
14305
14306 if (!isUndefined(from._isAMomentObject)) {
14307 to._isAMomentObject = from._isAMomentObject;
14308 }
14309
14310 if (!isUndefined(from._i)) {
14311 to._i = from._i;
14312 }
14313
14314 if (!isUndefined(from._f)) {
14315 to._f = from._f;
14316 }
14317
14318 if (!isUndefined(from._l)) {
14319 to._l = from._l;
14320 }
14321
14322 if (!isUndefined(from._strict)) {
14323 to._strict = from._strict;
14324 }
14325
14326 if (!isUndefined(from._tzm)) {
14327 to._tzm = from._tzm;
14328 }
14329
14330 if (!isUndefined(from._isUTC)) {
14331 to._isUTC = from._isUTC;
14332 }
14333
14334 if (!isUndefined(from._offset)) {
14335 to._offset = from._offset;
14336 }
14337
14338 if (!isUndefined(from._pf)) {
14339 to._pf = getParsingFlags(from);
14340 }
14341
14342 if (!isUndefined(from._locale)) {
14343 to._locale = from._locale;
14344 }
14345
14346 if (momentProperties.length > 0) {
14347 for (i = 0; i < momentProperties.length; i++) {
14348 prop = momentProperties[i];
14349 val = from[prop];
14350
14351 if (!isUndefined(val)) {
14352 to[prop] = val;
14353 }
14354 }
14355 }
14356
14357 return to;
14358 }
14359
14360 var updateInProgress = false; // Moment prototype object
14361
14362 function Moment(config) {
14363 copyConfig(this, config);
14364 this._d = new Date(config._d != null ? config._d.getTime() : NaN);
14365
14366 if (!this.isValid()) {
14367 this._d = new Date(NaN);
14368 } // Prevent infinite loop in case updateOffset creates new moment
14369 // objects.
14370
14371
14372 if (updateInProgress === false) {
14373 updateInProgress = true;
14374 hooks.updateOffset(this);
14375 updateInProgress = false;
14376 }
14377 }
14378
14379 function isMoment(obj) {
14380 return obj instanceof Moment || obj != null && obj._isAMomentObject != null;
14381 }
14382
14383 function absFloor(number) {
14384 if (number < 0) {
14385 // -0 -> 0
14386 return Math.ceil(number) || 0;
14387 } else {
14388 return Math.floor(number);
14389 }
14390 }
14391
14392 function toInt(argumentForCoercion) {
14393 var coercedNumber = +argumentForCoercion,
14394 value = 0;
14395
14396 if (coercedNumber !== 0 && isFinite(coercedNumber)) {
14397 value = absFloor(coercedNumber);
14398 }
14399
14400 return value;
14401 } // compare two arrays, return the number of differences
14402
14403
14404 function compareArrays(array1, array2, dontConvert) {
14405 var len = Math.min(array1.length, array2.length),
14406 lengthDiff = Math.abs(array1.length - array2.length),
14407 diffs = 0,
14408 i;
14409
14410 for (i = 0; i < len; i++) {
14411 if (dontConvert && array1[i] !== array2[i] || !dontConvert && toInt(array1[i]) !== toInt(array2[i])) {
14412 diffs++;
14413 }
14414 }
14415
14416 return diffs + lengthDiff;
14417 }
14418
14419 function warn(msg) {
14420 if (hooks.suppressDeprecationWarnings === false && typeof console !== 'undefined' && console.warn) {
14421 console.warn('Deprecation warning: ' + msg);
14422 }
14423 }
14424
14425 function deprecate(msg, fn) {
14426 var firstTime = true;
14427 return extend(function () {
14428 if (hooks.deprecationHandler != null) {
14429 hooks.deprecationHandler(null, msg);
14430 }
14431
14432 if (firstTime) {
14433 var args = [];
14434 var arg;
14435
14436 for (var i = 0; i < arguments.length; i++) {
14437 arg = '';
14438
14439 if (typeof arguments[i] === 'object') {
14440 arg += '\n[' + i + '] ';
14441
14442 for (var key in arguments[0]) {
14443 arg += key + ': ' + arguments[0][key] + ', ';
14444 }
14445
14446 arg = arg.slice(0, -2); // Remove trailing comma and space
14447 } else {
14448 arg = arguments[i];
14449 }
14450
14451 args.push(arg);
14452 }
14453
14454 warn(msg + '\nArguments: ' + Array.prototype.slice.call(args).join('') + '\n' + new Error().stack);
14455 firstTime = false;
14456 }
14457
14458 return fn.apply(this, arguments);
14459 }, fn);
14460 }
14461
14462 var deprecations = {};
14463
14464 function deprecateSimple(name, msg) {
14465 if (hooks.deprecationHandler != null) {
14466 hooks.deprecationHandler(name, msg);
14467 }
14468
14469 if (!deprecations[name]) {
14470 warn(msg);
14471 deprecations[name] = true;
14472 }
14473 }
14474
14475 hooks.suppressDeprecationWarnings = false;
14476 hooks.deprecationHandler = null;
14477
14478 function isFunction(input) {
14479 return input instanceof Function || Object.prototype.toString.call(input) === '[object Function]';
14480 }
14481
14482 function set(config) {
14483 var prop, i;
14484
14485 for (i in config) {
14486 prop = config[i];
14487
14488 if (isFunction(prop)) {
14489 this[i] = prop;
14490 } else {
14491 this['_' + i] = prop;
14492 }
14493 }
14494
14495 this._config = config; // Lenient ordinal parsing accepts just a number in addition to
14496 // number + (possibly) stuff coming from _dayOfMonthOrdinalParse.
14497 // TODO: Remove "ordinalParse" fallback in next major release.
14498
14499 this._dayOfMonthOrdinalParseLenient = new RegExp((this._dayOfMonthOrdinalParse.source || this._ordinalParse.source) + '|' + /\d{1,2}/.source);
14500 }
14501
14502 function mergeConfigs(parentConfig, childConfig) {
14503 var res = extend({}, parentConfig),
14504 prop;
14505
14506 for (prop in childConfig) {
14507 if (hasOwnProp(childConfig, prop)) {
14508 if (isObject(parentConfig[prop]) && isObject(childConfig[prop])) {
14509 res[prop] = {};
14510 extend(res[prop], parentConfig[prop]);
14511 extend(res[prop], childConfig[prop]);
14512 } else if (childConfig[prop] != null) {
14513 res[prop] = childConfig[prop];
14514 } else {
14515 delete res[prop];
14516 }
14517 }
14518 }
14519
14520 for (prop in parentConfig) {
14521 if (hasOwnProp(parentConfig, prop) && !hasOwnProp(childConfig, prop) && isObject(parentConfig[prop])) {
14522 // make sure changes to properties don't modify parent config
14523 res[prop] = extend({}, res[prop]);
14524 }
14525 }
14526
14527 return res;
14528 }
14529
14530 function Locale(config) {
14531 if (config != null) {
14532 this.set(config);
14533 }
14534 }
14535
14536 var keys;
14537
14538 if (Object.keys) {
14539 keys = Object.keys;
14540 } else {
14541 keys = function (obj) {
14542 var i,
14543 res = [];
14544
14545 for (i in obj) {
14546 if (hasOwnProp(obj, i)) {
14547 res.push(i);
14548 }
14549 }
14550
14551 return res;
14552 };
14553 }
14554
14555 var defaultCalendar = {
14556 sameDay: '[Today at] LT',
14557 nextDay: '[Tomorrow at] LT',
14558 nextWeek: 'dddd [at] LT',
14559 lastDay: '[Yesterday at] LT',
14560 lastWeek: '[Last] dddd [at] LT',
14561 sameElse: 'L'
14562 };
14563
14564 function calendar(key, mom, now) {
14565 var output = this._calendar[key] || this._calendar['sameElse'];
14566 return isFunction(output) ? output.call(mom, now) : output;
14567 }
14568
14569 var defaultLongDateFormat = {
14570 LTS: 'h:mm:ss A',
14571 LT: 'h:mm A',
14572 L: 'MM/DD/YYYY',
14573 LL: 'MMMM D, YYYY',
14574 LLL: 'MMMM D, YYYY h:mm A',
14575 LLLL: 'dddd, MMMM D, YYYY h:mm A'
14576 };
14577
14578 function longDateFormat(key) {
14579 var format = this._longDateFormat[key],
14580 formatUpper = this._longDateFormat[key.toUpperCase()];
14581
14582 if (format || !formatUpper) {
14583 return format;
14584 }
14585
14586 this._longDateFormat[key] = formatUpper.replace(/MMMM|MM|DD|dddd/g, function (val) {
14587 return val.slice(1);
14588 });
14589 return this._longDateFormat[key];
14590 }
14591
14592 var defaultInvalidDate = 'Invalid date';
14593
14594 function invalidDate() {
14595 return this._invalidDate;
14596 }
14597
14598 var defaultOrdinal = '%d';
14599 var defaultDayOfMonthOrdinalParse = /\d{1,2}/;
14600
14601 function ordinal(number) {
14602 return this._ordinal.replace('%d', number);
14603 }
14604
14605 var defaultRelativeTime = {
14606 future: 'in %s',
14607 past: '%s ago',
14608 s: 'a few seconds',
14609 ss: '%d seconds',
14610 m: 'a minute',
14611 mm: '%d minutes',
14612 h: 'an hour',
14613 hh: '%d hours',
14614 d: 'a day',
14615 dd: '%d days',
14616 M: 'a month',
14617 MM: '%d months',
14618 y: 'a year',
14619 yy: '%d years'
14620 };
14621
14622 function relativeTime(number, withoutSuffix, string, isFuture) {
14623 var output = this._relativeTime[string];
14624 return isFunction(output) ? output(number, withoutSuffix, string, isFuture) : output.replace(/%d/i, number);
14625 }
14626
14627 function pastFuture(diff, output) {
14628 var format = this._relativeTime[diff > 0 ? 'future' : 'past'];
14629 return isFunction(format) ? format(output) : format.replace(/%s/i, output);
14630 }
14631
14632 var aliases = {};
14633
14634 function addUnitAlias(unit, shorthand) {
14635 var lowerCase = unit.toLowerCase();
14636 aliases[lowerCase] = aliases[lowerCase + 's'] = aliases[shorthand] = unit;
14637 }
14638
14639 function normalizeUnits(units) {
14640 return typeof units === 'string' ? aliases[units] || aliases[units.toLowerCase()] : undefined;
14641 }
14642
14643 function normalizeObjectUnits(inputObject) {
14644 var normalizedInput = {},
14645 normalizedProp,
14646 prop;
14647
14648 for (prop in inputObject) {
14649 if (hasOwnProp(inputObject, prop)) {
14650 normalizedProp = normalizeUnits(prop);
14651
14652 if (normalizedProp) {
14653 normalizedInput[normalizedProp] = inputObject[prop];
14654 }
14655 }
14656 }
14657
14658 return normalizedInput;
14659 }
14660
14661 var priorities = {};
14662
14663 function addUnitPriority(unit, priority) {
14664 priorities[unit] = priority;
14665 }
14666
14667 function getPrioritizedUnits(unitsObj) {
14668 var units = [];
14669
14670 for (var u in unitsObj) {
14671 units.push({
14672 unit: u,
14673 priority: priorities[u]
14674 });
14675 }
14676
14677 units.sort(function (a, b) {
14678 return a.priority - b.priority;
14679 });
14680 return units;
14681 }
14682
14683 function zeroFill(number, targetLength, forceSign) {
14684 var absNumber = '' + Math.abs(number),
14685 zerosToFill = targetLength - absNumber.length,
14686 sign = number >= 0;
14687 return (sign ? forceSign ? '+' : '' : '-') + Math.pow(10, Math.max(0, zerosToFill)).toString().substr(1) + absNumber;
14688 }
14689
14690 var formattingTokens = /(\[[^\[]*\])|(\\)?([Hh]mm(ss)?|Mo|MM?M?M?|Do|DDDo|DD?D?D?|ddd?d?|do?|w[o|w]?|W[o|W]?|Qo?|YYYYYY|YYYYY|YYYY|YY|gg(ggg?)?|GG(GGG?)?|e|E|a|A|hh?|HH?|kk?|mm?|ss?|S{1,9}|x|X|zz?|ZZ?|.)/g;
14691 var localFormattingTokens = /(\[[^\[]*\])|(\\)?(LTS|LT|LL?L?L?|l{1,4})/g;
14692 var formatFunctions = {};
14693 var formatTokenFunctions = {}; // token: 'M'
14694 // padded: ['MM', 2]
14695 // ordinal: 'Mo'
14696 // callback: function () { this.month() + 1 }
14697
14698 function addFormatToken(token, padded, ordinal, callback) {
14699 var func = callback;
14700
14701 if (typeof callback === 'string') {
14702 func = function () {
14703 return this[callback]();
14704 };
14705 }
14706
14707 if (token) {
14708 formatTokenFunctions[token] = func;
14709 }
14710
14711 if (padded) {
14712 formatTokenFunctions[padded[0]] = function () {
14713 return zeroFill(func.apply(this, arguments), padded[1], padded[2]);
14714 };
14715 }
14716
14717 if (ordinal) {
14718 formatTokenFunctions[ordinal] = function () {
14719 return this.localeData().ordinal(func.apply(this, arguments), token);
14720 };
14721 }
14722 }
14723
14724 function removeFormattingTokens(input) {
14725 if (input.match(/\[[\s\S]/)) {
14726 return input.replace(/^\[|\]$/g, '');
14727 }
14728
14729 return input.replace(/\\/g, '');
14730 }
14731
14732 function makeFormatFunction(format) {
14733 var array = format.match(formattingTokens),
14734 i,
14735 length;
14736
14737 for (i = 0, length = array.length; i < length; i++) {
14738 if (formatTokenFunctions[array[i]]) {
14739 array[i] = formatTokenFunctions[array[i]];
14740 } else {
14741 array[i] = removeFormattingTokens(array[i]);
14742 }
14743 }
14744
14745 return function (mom) {
14746 var output = '',
14747 i;
14748
14749 for (i = 0; i < length; i++) {
14750 output += isFunction(array[i]) ? array[i].call(mom, format) : array[i];
14751 }
14752
14753 return output;
14754 };
14755 } // format date using native date object
14756
14757
14758 function formatMoment(m, format) {
14759 if (!m.isValid()) {
14760 return m.localeData().invalidDate();
14761 }
14762
14763 format = expandFormat(format, m.localeData());
14764 formatFunctions[format] = formatFunctions[format] || makeFormatFunction(format);
14765 return formatFunctions[format](m);
14766 }
14767
14768 function expandFormat(format, locale) {
14769 var i = 5;
14770
14771 function replaceLongDateFormatTokens(input) {
14772 return locale.longDateFormat(input) || input;
14773 }
14774
14775 localFormattingTokens.lastIndex = 0;
14776
14777 while (i >= 0 && localFormattingTokens.test(format)) {
14778 format = format.replace(localFormattingTokens, replaceLongDateFormatTokens);
14779 localFormattingTokens.lastIndex = 0;
14780 i -= 1;
14781 }
14782
14783 return format;
14784 }
14785
14786 var match1 = /\d/; // 0 - 9
14787
14788 var match2 = /\d\d/; // 00 - 99
14789
14790 var match3 = /\d{3}/; // 000 - 999
14791
14792 var match4 = /\d{4}/; // 0000 - 9999
14793
14794 var match6 = /[+-]?\d{6}/; // -999999 - 999999
14795
14796 var match1to2 = /\d\d?/; // 0 - 99
14797
14798 var match3to4 = /\d\d\d\d?/; // 999 - 9999
14799
14800 var match5to6 = /\d\d\d\d\d\d?/; // 99999 - 999999
14801
14802 var match1to3 = /\d{1,3}/; // 0 - 999
14803
14804 var match1to4 = /\d{1,4}/; // 0 - 9999
14805
14806 var match1to6 = /[+-]?\d{1,6}/; // -999999 - 999999
14807
14808 var matchUnsigned = /\d+/; // 0 - inf
14809
14810 var matchSigned = /[+-]?\d+/; // -inf - inf
14811
14812 var matchOffset = /Z|[+-]\d\d:?\d\d/gi; // +00:00 -00:00 +0000 -0000 or Z
14813
14814 var matchShortOffset = /Z|[+-]\d\d(?::?\d\d)?/gi; // +00 -00 +00:00 -00:00 +0000 -0000 or Z
14815
14816 var matchTimestamp = /[+-]?\d+(\.\d{1,3})?/; // 123456789 123456789.123
14817 // any word (or two) characters or numbers including two/three word month in arabic.
14818 // includes scottish gaelic two word and hyphenated months
14819
14820 var matchWord = /[0-9]{0,256}['a-z\u00A0-\u05FF\u0700-\uD7FF\uF900-\uFDCF\uFDF0-\uFF07\uFF10-\uFFEF]{1,256}|[\u0600-\u06FF\/]{1,256}(\s*?[\u0600-\u06FF]{1,256}){1,2}/i;
14821 var regexes = {};
14822
14823 function addRegexToken(token, regex, strictRegex) {
14824 regexes[token] = isFunction(regex) ? regex : function (isStrict, localeData) {
14825 return isStrict && strictRegex ? strictRegex : regex;
14826 };
14827 }
14828
14829 function getParseRegexForToken(token, config) {
14830 if (!hasOwnProp(regexes, token)) {
14831 return new RegExp(unescapeFormat(token));
14832 }
14833
14834 return regexes[token](config._strict, config._locale);
14835 } // Code from http://stackoverflow.com/questions/3561493/is-there-a-regexp-escape-function-in-javascript
14836
14837
14838 function unescapeFormat(s) {
14839 return regexEscape(s.replace('\\', '').replace(/\\(\[)|\\(\])|\[([^\]\[]*)\]|\\(.)/g, function (matched, p1, p2, p3, p4) {
14840 return p1 || p2 || p3 || p4;
14841 }));
14842 }
14843
14844 function regexEscape(s) {
14845 return s.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
14846 }
14847
14848 var tokens = {};
14849
14850 function addParseToken(token, callback) {
14851 var i,
14852 func = callback;
14853
14854 if (typeof token === 'string') {
14855 token = [token];
14856 }
14857
14858 if (isNumber(callback)) {
14859 func = function (input, array) {
14860 array[callback] = toInt(input);
14861 };
14862 }
14863
14864 for (i = 0; i < token.length; i++) {
14865 tokens[token[i]] = func;
14866 }
14867 }
14868
14869 function addWeekParseToken(token, callback) {
14870 addParseToken(token, function (input, array, config, token) {
14871 config._w = config._w || {};
14872 callback(input, config._w, config, token);
14873 });
14874 }
14875
14876 function addTimeToArrayFromToken(token, input, config) {
14877 if (input != null && hasOwnProp(tokens, token)) {
14878 tokens[token](input, config._a, config, token);
14879 }
14880 }
14881
14882 var YEAR = 0;
14883 var MONTH = 1;
14884 var DATE = 2;
14885 var HOUR = 3;
14886 var MINUTE = 4;
14887 var SECOND = 5;
14888 var MILLISECOND = 6;
14889 var WEEK = 7;
14890 var WEEKDAY = 8; // FORMATTING
14891
14892 addFormatToken('Y', 0, 0, function () {
14893 var y = this.year();
14894 return y <= 9999 ? '' + y : '+' + y;
14895 });
14896 addFormatToken(0, ['YY', 2], 0, function () {
14897 return this.year() % 100;
14898 });
14899 addFormatToken(0, ['YYYY', 4], 0, 'year');
14900 addFormatToken(0, ['YYYYY', 5], 0, 'year');
14901 addFormatToken(0, ['YYYYYY', 6, true], 0, 'year'); // ALIASES
14902
14903 addUnitAlias('year', 'y'); // PRIORITIES
14904
14905 addUnitPriority('year', 1); // PARSING
14906
14907 addRegexToken('Y', matchSigned);
14908 addRegexToken('YY', match1to2, match2);
14909 addRegexToken('YYYY', match1to4, match4);
14910 addRegexToken('YYYYY', match1to6, match6);
14911 addRegexToken('YYYYYY', match1to6, match6);
14912 addParseToken(['YYYYY', 'YYYYYY'], YEAR);
14913 addParseToken('YYYY', function (input, array) {
14914 array[YEAR] = input.length === 2 ? hooks.parseTwoDigitYear(input) : toInt(input);
14915 });
14916 addParseToken('YY', function (input, array) {
14917 array[YEAR] = hooks.parseTwoDigitYear(input);
14918 });
14919 addParseToken('Y', function (input, array) {
14920 array[YEAR] = parseInt(input, 10);
14921 }); // HELPERS
14922
14923 function daysInYear(year) {
14924 return isLeapYear(year) ? 366 : 365;
14925 }
14926
14927 function isLeapYear(year) {
14928 return year % 4 === 0 && year % 100 !== 0 || year % 400 === 0;
14929 } // HOOKS
14930
14931
14932 hooks.parseTwoDigitYear = function (input) {
14933 return toInt(input) + (toInt(input) > 68 ? 1900 : 2000);
14934 }; // MOMENTS
14935
14936
14937 var getSetYear = makeGetSet('FullYear', true);
14938
14939 function getIsLeapYear() {
14940 return isLeapYear(this.year());
14941 }
14942
14943 function makeGetSet(unit, keepTime) {
14944 return function (value) {
14945 if (value != null) {
14946 set$1(this, unit, value);
14947 hooks.updateOffset(this, keepTime);
14948 return this;
14949 } else {
14950 return get(this, unit);
14951 }
14952 };
14953 }
14954
14955 function get(mom, unit) {
14956 return mom.isValid() ? mom._d['get' + (mom._isUTC ? 'UTC' : '') + unit]() : NaN;
14957 }
14958
14959 function set$1(mom, unit, value) {
14960 if (mom.isValid() && !isNaN(value)) {
14961 if (unit === 'FullYear' && isLeapYear(mom.year()) && mom.month() === 1 && mom.date() === 29) {
14962 mom._d['set' + (mom._isUTC ? 'UTC' : '') + unit](value, mom.month(), daysInMonth(value, mom.month()));
14963 } else {
14964 mom._d['set' + (mom._isUTC ? 'UTC' : '') + unit](value);
14965 }
14966 }
14967 } // MOMENTS
14968
14969
14970 function stringGet(units) {
14971 units = normalizeUnits(units);
14972
14973 if (isFunction(this[units])) {
14974 return this[units]();
14975 }
14976
14977 return this;
14978 }
14979
14980 function stringSet(units, value) {
14981 if (typeof units === 'object') {
14982 units = normalizeObjectUnits(units);
14983 var prioritized = getPrioritizedUnits(units);
14984
14985 for (var i = 0; i < prioritized.length; i++) {
14986 this[prioritized[i].unit](units[prioritized[i].unit]);
14987 }
14988 } else {
14989 units = normalizeUnits(units);
14990
14991 if (isFunction(this[units])) {
14992 return this[units](value);
14993 }
14994 }
14995
14996 return this;
14997 }
14998
14999 function mod(n, x) {
15000 return (n % x + x) % x;
15001 }
15002
15003 var indexOf;
15004
15005 if (Array.prototype.indexOf) {
15006 indexOf = Array.prototype.indexOf;
15007 } else {
15008 indexOf = function (o) {
15009 // I know
15010 var i;
15011
15012 for (i = 0; i < this.length; ++i) {
15013 if (this[i] === o) {
15014 return i;
15015 }
15016 }
15017
15018 return -1;
15019 };
15020 }
15021
15022 function daysInMonth(year, month) {
15023 if (isNaN(year) || isNaN(month)) {
15024 return NaN;
15025 }
15026
15027 var modMonth = mod(month, 12);
15028 year += (month - modMonth) / 12;
15029 return modMonth === 1 ? isLeapYear(year) ? 29 : 28 : 31 - modMonth % 7 % 2;
15030 } // FORMATTING
15031
15032
15033 addFormatToken('M', ['MM', 2], 'Mo', function () {
15034 return this.month() + 1;
15035 });
15036 addFormatToken('MMM', 0, 0, function (format) {
15037 return this.localeData().monthsShort(this, format);
15038 });
15039 addFormatToken('MMMM', 0, 0, function (format) {
15040 return this.localeData().months(this, format);
15041 }); // ALIASES
15042
15043 addUnitAlias('month', 'M'); // PRIORITY
15044
15045 addUnitPriority('month', 8); // PARSING
15046
15047 addRegexToken('M', match1to2);
15048 addRegexToken('MM', match1to2, match2);
15049 addRegexToken('MMM', function (isStrict, locale) {
15050 return locale.monthsShortRegex(isStrict);
15051 });
15052 addRegexToken('MMMM', function (isStrict, locale) {
15053 return locale.monthsRegex(isStrict);
15054 });
15055 addParseToken(['M', 'MM'], function (input, array) {
15056 array[MONTH] = toInt(input) - 1;
15057 });
15058 addParseToken(['MMM', 'MMMM'], function (input, array, config, token) {
15059 var month = config._locale.monthsParse(input, token, config._strict); // if we didn't find a month name, mark the date as invalid.
15060
15061
15062 if (month != null) {
15063 array[MONTH] = month;
15064 } else {
15065 getParsingFlags(config).invalidMonth = input;
15066 }
15067 }); // LOCALES
15068
15069 var MONTHS_IN_FORMAT = /D[oD]?(\[[^\[\]]*\]|\s)+MMMM?/;
15070 var defaultLocaleMonths = 'January_February_March_April_May_June_July_August_September_October_November_December'.split('_');
15071
15072 function localeMonths(m, format) {
15073 if (!m) {
15074 return isArray(this._months) ? this._months : this._months['standalone'];
15075 }
15076
15077 return isArray(this._months) ? this._months[m.month()] : this._months[(this._months.isFormat || MONTHS_IN_FORMAT).test(format) ? 'format' : 'standalone'][m.month()];
15078 }
15079
15080 var defaultLocaleMonthsShort = 'Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec'.split('_');
15081
15082 function localeMonthsShort(m, format) {
15083 if (!m) {
15084 return isArray(this._monthsShort) ? this._monthsShort : this._monthsShort['standalone'];
15085 }
15086
15087 return isArray(this._monthsShort) ? this._monthsShort[m.month()] : this._monthsShort[MONTHS_IN_FORMAT.test(format) ? 'format' : 'standalone'][m.month()];
15088 }
15089
15090 function handleStrictParse(monthName, format, strict) {
15091 var i,
15092 ii,
15093 mom,
15094 llc = monthName.toLocaleLowerCase();
15095
15096 if (!this._monthsParse) {
15097 // this is not used
15098 this._monthsParse = [];
15099 this._longMonthsParse = [];
15100 this._shortMonthsParse = [];
15101
15102 for (i = 0; i < 12; ++i) {
15103 mom = createUTC([2000, i]);
15104 this._shortMonthsParse[i] = this.monthsShort(mom, '').toLocaleLowerCase();
15105 this._longMonthsParse[i] = this.months(mom, '').toLocaleLowerCase();
15106 }
15107 }
15108
15109 if (strict) {
15110 if (format === 'MMM') {
15111 ii = indexOf.call(this._shortMonthsParse, llc);
15112 return ii !== -1 ? ii : null;
15113 } else {
15114 ii = indexOf.call(this._longMonthsParse, llc);
15115 return ii !== -1 ? ii : null;
15116 }
15117 } else {
15118 if (format === 'MMM') {
15119 ii = indexOf.call(this._shortMonthsParse, llc);
15120
15121 if (ii !== -1) {
15122 return ii;
15123 }
15124
15125 ii = indexOf.call(this._longMonthsParse, llc);
15126 return ii !== -1 ? ii : null;
15127 } else {
15128 ii = indexOf.call(this._longMonthsParse, llc);
15129
15130 if (ii !== -1) {
15131 return ii;
15132 }
15133
15134 ii = indexOf.call(this._shortMonthsParse, llc);
15135 return ii !== -1 ? ii : null;
15136 }
15137 }
15138 }
15139
15140 function localeMonthsParse(monthName, format, strict) {
15141 var i, mom, regex;
15142
15143 if (this._monthsParseExact) {
15144 return handleStrictParse.call(this, monthName, format, strict);
15145 }
15146
15147 if (!this._monthsParse) {
15148 this._monthsParse = [];
15149 this._longMonthsParse = [];
15150 this._shortMonthsParse = [];
15151 } // TODO: add sorting
15152 // Sorting makes sure if one month (or abbr) is a prefix of another
15153 // see sorting in computeMonthsParse
15154
15155
15156 for (i = 0; i < 12; i++) {
15157 // make the regex if we don't have it already
15158 mom = createUTC([2000, i]);
15159
15160 if (strict && !this._longMonthsParse[i]) {
15161 this._longMonthsParse[i] = new RegExp('^' + this.months(mom, '').replace('.', '') + '$', 'i');
15162 this._shortMonthsParse[i] = new RegExp('^' + this.monthsShort(mom, '').replace('.', '') + '$', 'i');
15163 }
15164
15165 if (!strict && !this._monthsParse[i]) {
15166 regex = '^' + this.months(mom, '') + '|^' + this.monthsShort(mom, '');
15167 this._monthsParse[i] = new RegExp(regex.replace('.', ''), 'i');
15168 } // test the regex
15169
15170
15171 if (strict && format === 'MMMM' && this._longMonthsParse[i].test(monthName)) {
15172 return i;
15173 } else if (strict && format === 'MMM' && this._shortMonthsParse[i].test(monthName)) {
15174 return i;
15175 } else if (!strict && this._monthsParse[i].test(monthName)) {
15176 return i;
15177 }
15178 }
15179 } // MOMENTS
15180
15181
15182 function setMonth(mom, value) {
15183 var dayOfMonth;
15184
15185 if (!mom.isValid()) {
15186 // No op
15187 return mom;
15188 }
15189
15190 if (typeof value === 'string') {
15191 if (/^\d+$/.test(value)) {
15192 value = toInt(value);
15193 } else {
15194 value = mom.localeData().monthsParse(value); // TODO: Another silent failure?
15195
15196 if (!isNumber(value)) {
15197 return mom;
15198 }
15199 }
15200 }
15201
15202 dayOfMonth = Math.min(mom.date(), daysInMonth(mom.year(), value));
15203
15204 mom._d['set' + (mom._isUTC ? 'UTC' : '') + 'Month'](value, dayOfMonth);
15205
15206 return mom;
15207 }
15208
15209 function getSetMonth(value) {
15210 if (value != null) {
15211 setMonth(this, value);
15212 hooks.updateOffset(this, true);
15213 return this;
15214 } else {
15215 return get(this, 'Month');
15216 }
15217 }
15218
15219 function getDaysInMonth() {
15220 return daysInMonth(this.year(), this.month());
15221 }
15222
15223 var defaultMonthsShortRegex = matchWord;
15224
15225 function monthsShortRegex(isStrict) {
15226 if (this._monthsParseExact) {
15227 if (!hasOwnProp(this, '_monthsRegex')) {
15228 computeMonthsParse.call(this);
15229 }
15230
15231 if (isStrict) {
15232 return this._monthsShortStrictRegex;
15233 } else {
15234 return this._monthsShortRegex;
15235 }
15236 } else {
15237 if (!hasOwnProp(this, '_monthsShortRegex')) {
15238 this._monthsShortRegex = defaultMonthsShortRegex;
15239 }
15240
15241 return this._monthsShortStrictRegex && isStrict ? this._monthsShortStrictRegex : this._monthsShortRegex;
15242 }
15243 }
15244
15245 var defaultMonthsRegex = matchWord;
15246
15247 function monthsRegex(isStrict) {
15248 if (this._monthsParseExact) {
15249 if (!hasOwnProp(this, '_monthsRegex')) {
15250 computeMonthsParse.call(this);
15251 }
15252
15253 if (isStrict) {
15254 return this._monthsStrictRegex;
15255 } else {
15256 return this._monthsRegex;
15257 }
15258 } else {
15259 if (!hasOwnProp(this, '_monthsRegex')) {
15260 this._monthsRegex = defaultMonthsRegex;
15261 }
15262
15263 return this._monthsStrictRegex && isStrict ? this._monthsStrictRegex : this._monthsRegex;
15264 }
15265 }
15266
15267 function computeMonthsParse() {
15268 function cmpLenRev(a, b) {
15269 return b.length - a.length;
15270 }
15271
15272 var shortPieces = [],
15273 longPieces = [],
15274 mixedPieces = [],
15275 i,
15276 mom;
15277
15278 for (i = 0; i < 12; i++) {
15279 // make the regex if we don't have it already
15280 mom = createUTC([2000, i]);
15281 shortPieces.push(this.monthsShort(mom, ''));
15282 longPieces.push(this.months(mom, ''));
15283 mixedPieces.push(this.months(mom, ''));
15284 mixedPieces.push(this.monthsShort(mom, ''));
15285 } // Sorting makes sure if one month (or abbr) is a prefix of another it
15286 // will match the longer piece.
15287
15288
15289 shortPieces.sort(cmpLenRev);
15290 longPieces.sort(cmpLenRev);
15291 mixedPieces.sort(cmpLenRev);
15292
15293 for (i = 0; i < 12; i++) {
15294 shortPieces[i] = regexEscape(shortPieces[i]);
15295 longPieces[i] = regexEscape(longPieces[i]);
15296 }
15297
15298 for (i = 0; i < 24; i++) {
15299 mixedPieces[i] = regexEscape(mixedPieces[i]);
15300 }
15301
15302 this._monthsRegex = new RegExp('^(' + mixedPieces.join('|') + ')', 'i');
15303 this._monthsShortRegex = this._monthsRegex;
15304 this._monthsStrictRegex = new RegExp('^(' + longPieces.join('|') + ')', 'i');
15305 this._monthsShortStrictRegex = new RegExp('^(' + shortPieces.join('|') + ')', 'i');
15306 }
15307
15308 function createDate(y, m, d, h, M, s, ms) {
15309 // can't just apply() to create a date:
15310 // https://stackoverflow.com/q/181348
15311 var date; // the date constructor remaps years 0-99 to 1900-1999
15312
15313 if (y < 100 && y >= 0) {
15314 // preserve leap years using a full 400 year cycle, then reset
15315 date = new Date(y + 400, m, d, h, M, s, ms);
15316
15317 if (isFinite(date.getFullYear())) {
15318 date.setFullYear(y);
15319 }
15320 } else {
15321 date = new Date(y, m, d, h, M, s, ms);
15322 }
15323
15324 return date;
15325 }
15326
15327 function createUTCDate(y) {
15328 var date; // the Date.UTC function remaps years 0-99 to 1900-1999
15329
15330 if (y < 100 && y >= 0) {
15331 var args = Array.prototype.slice.call(arguments); // preserve leap years using a full 400 year cycle, then reset
15332
15333 args[0] = y + 400;
15334 date = new Date(Date.UTC.apply(null, args));
15335
15336 if (isFinite(date.getUTCFullYear())) {
15337 date.setUTCFullYear(y);
15338 }
15339 } else {
15340 date = new Date(Date.UTC.apply(null, arguments));
15341 }
15342
15343 return date;
15344 } // start-of-first-week - start-of-year
15345
15346
15347 function firstWeekOffset(year, dow, doy) {
15348 var // first-week day -- which january is always in the first week (4 for iso, 1 for other)
15349 fwd = 7 + dow - doy,
15350 // first-week day local weekday -- which local weekday is fwd
15351 fwdlw = (7 + createUTCDate(year, 0, fwd).getUTCDay() - dow) % 7;
15352 return -fwdlw + fwd - 1;
15353 } // https://en.wikipedia.org/wiki/ISO_week_date#Calculating_a_date_given_the_year.2C_week_number_and_weekday
15354
15355
15356 function dayOfYearFromWeeks(year, week, weekday, dow, doy) {
15357 var localWeekday = (7 + weekday - dow) % 7,
15358 weekOffset = firstWeekOffset(year, dow, doy),
15359 dayOfYear = 1 + 7 * (week - 1) + localWeekday + weekOffset,
15360 resYear,
15361 resDayOfYear;
15362
15363 if (dayOfYear <= 0) {
15364 resYear = year - 1;
15365 resDayOfYear = daysInYear(resYear) + dayOfYear;
15366 } else if (dayOfYear > daysInYear(year)) {
15367 resYear = year + 1;
15368 resDayOfYear = dayOfYear - daysInYear(year);
15369 } else {
15370 resYear = year;
15371 resDayOfYear = dayOfYear;
15372 }
15373
15374 return {
15375 year: resYear,
15376 dayOfYear: resDayOfYear
15377 };
15378 }
15379
15380 function weekOfYear(mom, dow, doy) {
15381 var weekOffset = firstWeekOffset(mom.year(), dow, doy),
15382 week = Math.floor((mom.dayOfYear() - weekOffset - 1) / 7) + 1,
15383 resWeek,
15384 resYear;
15385
15386 if (week < 1) {
15387 resYear = mom.year() - 1;
15388 resWeek = week + weeksInYear(resYear, dow, doy);
15389 } else if (week > weeksInYear(mom.year(), dow, doy)) {
15390 resWeek = week - weeksInYear(mom.year(), dow, doy);
15391 resYear = mom.year() + 1;
15392 } else {
15393 resYear = mom.year();
15394 resWeek = week;
15395 }
15396
15397 return {
15398 week: resWeek,
15399 year: resYear
15400 };
15401 }
15402
15403 function weeksInYear(year, dow, doy) {
15404 var weekOffset = firstWeekOffset(year, dow, doy),
15405 weekOffsetNext = firstWeekOffset(year + 1, dow, doy);
15406 return (daysInYear(year) - weekOffset + weekOffsetNext) / 7;
15407 } // FORMATTING
15408
15409
15410 addFormatToken('w', ['ww', 2], 'wo', 'week');
15411 addFormatToken('W', ['WW', 2], 'Wo', 'isoWeek'); // ALIASES
15412
15413 addUnitAlias('week', 'w');
15414 addUnitAlias('isoWeek', 'W'); // PRIORITIES
15415
15416 addUnitPriority('week', 5);
15417 addUnitPriority('isoWeek', 5); // PARSING
15418
15419 addRegexToken('w', match1to2);
15420 addRegexToken('ww', match1to2, match2);
15421 addRegexToken('W', match1to2);
15422 addRegexToken('WW', match1to2, match2);
15423 addWeekParseToken(['w', 'ww', 'W', 'WW'], function (input, week, config, token) {
15424 week[token.substr(0, 1)] = toInt(input);
15425 }); // HELPERS
15426 // LOCALES
15427
15428 function localeWeek(mom) {
15429 return weekOfYear(mom, this._week.dow, this._week.doy).week;
15430 }
15431
15432 var defaultLocaleWeek = {
15433 dow: 0,
15434 // Sunday is the first day of the week.
15435 doy: 6 // The week that contains Jan 6th is the first week of the year.
15436
15437 };
15438
15439 function localeFirstDayOfWeek() {
15440 return this._week.dow;
15441 }
15442
15443 function localeFirstDayOfYear() {
15444 return this._week.doy;
15445 } // MOMENTS
15446
15447
15448 function getSetWeek(input) {
15449 var week = this.localeData().week(this);
15450 return input == null ? week : this.add((input - week) * 7, 'd');
15451 }
15452
15453 function getSetISOWeek(input) {
15454 var week = weekOfYear(this, 1, 4).week;
15455 return input == null ? week : this.add((input - week) * 7, 'd');
15456 } // FORMATTING
15457
15458
15459 addFormatToken('d', 0, 'do', 'day');
15460 addFormatToken('dd', 0, 0, function (format) {
15461 return this.localeData().weekdaysMin(this, format);
15462 });
15463 addFormatToken('ddd', 0, 0, function (format) {
15464 return this.localeData().weekdaysShort(this, format);
15465 });
15466 addFormatToken('dddd', 0, 0, function (format) {
15467 return this.localeData().weekdays(this, format);
15468 });
15469 addFormatToken('e', 0, 0, 'weekday');
15470 addFormatToken('E', 0, 0, 'isoWeekday'); // ALIASES
15471
15472 addUnitAlias('day', 'd');
15473 addUnitAlias('weekday', 'e');
15474 addUnitAlias('isoWeekday', 'E'); // PRIORITY
15475
15476 addUnitPriority('day', 11);
15477 addUnitPriority('weekday', 11);
15478 addUnitPriority('isoWeekday', 11); // PARSING
15479
15480 addRegexToken('d', match1to2);
15481 addRegexToken('e', match1to2);
15482 addRegexToken('E', match1to2);
15483 addRegexToken('dd', function (isStrict, locale) {
15484 return locale.weekdaysMinRegex(isStrict);
15485 });
15486 addRegexToken('ddd', function (isStrict, locale) {
15487 return locale.weekdaysShortRegex(isStrict);
15488 });
15489 addRegexToken('dddd', function (isStrict, locale) {
15490 return locale.weekdaysRegex(isStrict);
15491 });
15492 addWeekParseToken(['dd', 'ddd', 'dddd'], function (input, week, config, token) {
15493 var weekday = config._locale.weekdaysParse(input, token, config._strict); // if we didn't get a weekday name, mark the date as invalid
15494
15495
15496 if (weekday != null) {
15497 week.d = weekday;
15498 } else {
15499 getParsingFlags(config).invalidWeekday = input;
15500 }
15501 });
15502 addWeekParseToken(['d', 'e', 'E'], function (input, week, config, token) {
15503 week[token] = toInt(input);
15504 }); // HELPERS
15505
15506 function parseWeekday(input, locale) {
15507 if (typeof input !== 'string') {
15508 return input;
15509 }
15510
15511 if (!isNaN(input)) {
15512 return parseInt(input, 10);
15513 }
15514
15515 input = locale.weekdaysParse(input);
15516
15517 if (typeof input === 'number') {
15518 return input;
15519 }
15520
15521 return null;
15522 }
15523
15524 function parseIsoWeekday(input, locale) {
15525 if (typeof input === 'string') {
15526 return locale.weekdaysParse(input) % 7 || 7;
15527 }
15528
15529 return isNaN(input) ? null : input;
15530 } // LOCALES
15531
15532
15533 function shiftWeekdays(ws, n) {
15534 return ws.slice(n, 7).concat(ws.slice(0, n));
15535 }
15536
15537 var defaultLocaleWeekdays = 'Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday'.split('_');
15538
15539 function localeWeekdays(m, format) {
15540 var weekdays = isArray(this._weekdays) ? this._weekdays : this._weekdays[m && m !== true && this._weekdays.isFormat.test(format) ? 'format' : 'standalone'];
15541 return m === true ? shiftWeekdays(weekdays, this._week.dow) : m ? weekdays[m.day()] : weekdays;
15542 }
15543
15544 var defaultLocaleWeekdaysShort = 'Sun_Mon_Tue_Wed_Thu_Fri_Sat'.split('_');
15545
15546 function localeWeekdaysShort(m) {
15547 return m === true ? shiftWeekdays(this._weekdaysShort, this._week.dow) : m ? this._weekdaysShort[m.day()] : this._weekdaysShort;
15548 }
15549
15550 var defaultLocaleWeekdaysMin = 'Su_Mo_Tu_We_Th_Fr_Sa'.split('_');
15551
15552 function localeWeekdaysMin(m) {
15553 return m === true ? shiftWeekdays(this._weekdaysMin, this._week.dow) : m ? this._weekdaysMin[m.day()] : this._weekdaysMin;
15554 }
15555
15556 function handleStrictParse$1(weekdayName, format, strict) {
15557 var i,
15558 ii,
15559 mom,
15560 llc = weekdayName.toLocaleLowerCase();
15561
15562 if (!this._weekdaysParse) {
15563 this._weekdaysParse = [];
15564 this._shortWeekdaysParse = [];
15565 this._minWeekdaysParse = [];
15566
15567 for (i = 0; i < 7; ++i) {
15568 mom = createUTC([2000, 1]).day(i);
15569 this._minWeekdaysParse[i] = this.weekdaysMin(mom, '').toLocaleLowerCase();
15570 this._shortWeekdaysParse[i] = this.weekdaysShort(mom, '').toLocaleLowerCase();
15571 this._weekdaysParse[i] = this.weekdays(mom, '').toLocaleLowerCase();
15572 }
15573 }
15574
15575 if (strict) {
15576 if (format === 'dddd') {
15577 ii = indexOf.call(this._weekdaysParse, llc);
15578 return ii !== -1 ? ii : null;
15579 } else if (format === 'ddd') {
15580 ii = indexOf.call(this._shortWeekdaysParse, llc);
15581 return ii !== -1 ? ii : null;
15582 } else {
15583 ii = indexOf.call(this._minWeekdaysParse, llc);
15584 return ii !== -1 ? ii : null;
15585 }
15586 } else {
15587 if (format === 'dddd') {
15588 ii = indexOf.call(this._weekdaysParse, llc);
15589
15590 if (ii !== -1) {
15591 return ii;
15592 }
15593
15594 ii = indexOf.call(this._shortWeekdaysParse, llc);
15595
15596 if (ii !== -1) {
15597 return ii;
15598 }
15599
15600 ii = indexOf.call(this._minWeekdaysParse, llc);
15601 return ii !== -1 ? ii : null;
15602 } else if (format === 'ddd') {
15603 ii = indexOf.call(this._shortWeekdaysParse, llc);
15604
15605 if (ii !== -1) {
15606 return ii;
15607 }
15608
15609 ii = indexOf.call(this._weekdaysParse, llc);
15610
15611 if (ii !== -1) {
15612 return ii;
15613 }
15614
15615 ii = indexOf.call(this._minWeekdaysParse, llc);
15616 return ii !== -1 ? ii : null;
15617 } else {
15618 ii = indexOf.call(this._minWeekdaysParse, llc);
15619
15620 if (ii !== -1) {
15621 return ii;
15622 }
15623
15624 ii = indexOf.call(this._weekdaysParse, llc);
15625
15626 if (ii !== -1) {
15627 return ii;
15628 }
15629
15630 ii = indexOf.call(this._shortWeekdaysParse, llc);
15631 return ii !== -1 ? ii : null;
15632 }
15633 }
15634 }
15635
15636 function localeWeekdaysParse(weekdayName, format, strict) {
15637 var i, mom, regex;
15638
15639 if (this._weekdaysParseExact) {
15640 return handleStrictParse$1.call(this, weekdayName, format, strict);
15641 }
15642
15643 if (!this._weekdaysParse) {
15644 this._weekdaysParse = [];
15645 this._minWeekdaysParse = [];
15646 this._shortWeekdaysParse = [];
15647 this._fullWeekdaysParse = [];
15648 }
15649
15650 for (i = 0; i < 7; i++) {
15651 // make the regex if we don't have it already
15652 mom = createUTC([2000, 1]).day(i);
15653
15654 if (strict && !this._fullWeekdaysParse[i]) {
15655 this._fullWeekdaysParse[i] = new RegExp('^' + this.weekdays(mom, '').replace('.', '\\.?') + '$', 'i');
15656 this._shortWeekdaysParse[i] = new RegExp('^' + this.weekdaysShort(mom, '').replace('.', '\\.?') + '$', 'i');
15657 this._minWeekdaysParse[i] = new RegExp('^' + this.weekdaysMin(mom, '').replace('.', '\\.?') + '$', 'i');
15658 }
15659
15660 if (!this._weekdaysParse[i]) {
15661 regex = '^' + this.weekdays(mom, '') + '|^' + this.weekdaysShort(mom, '') + '|^' + this.weekdaysMin(mom, '');
15662 this._weekdaysParse[i] = new RegExp(regex.replace('.', ''), 'i');
15663 } // test the regex
15664
15665
15666 if (strict && format === 'dddd' && this._fullWeekdaysParse[i].test(weekdayName)) {
15667 return i;
15668 } else if (strict && format === 'ddd' && this._shortWeekdaysParse[i].test(weekdayName)) {
15669 return i;
15670 } else if (strict && format === 'dd' && this._minWeekdaysParse[i].test(weekdayName)) {
15671 return i;
15672 } else if (!strict && this._weekdaysParse[i].test(weekdayName)) {
15673 return i;
15674 }
15675 }
15676 } // MOMENTS
15677
15678
15679 function getSetDayOfWeek(input) {
15680 if (!this.isValid()) {
15681 return input != null ? this : NaN;
15682 }
15683
15684 var day = this._isUTC ? this._d.getUTCDay() : this._d.getDay();
15685
15686 if (input != null) {
15687 input = parseWeekday(input, this.localeData());
15688 return this.add(input - day, 'd');
15689 } else {
15690 return day;
15691 }
15692 }
15693
15694 function getSetLocaleDayOfWeek(input) {
15695 if (!this.isValid()) {
15696 return input != null ? this : NaN;
15697 }
15698
15699 var weekday = (this.day() + 7 - this.localeData()._week.dow) % 7;
15700 return input == null ? weekday : this.add(input - weekday, 'd');
15701 }
15702
15703 function getSetISODayOfWeek(input) {
15704 if (!this.isValid()) {
15705 return input != null ? this : NaN;
15706 } // behaves the same as moment#day except
15707 // as a getter, returns 7 instead of 0 (1-7 range instead of 0-6)
15708 // as a setter, sunday should belong to the previous week.
15709
15710
15711 if (input != null) {
15712 var weekday = parseIsoWeekday(input, this.localeData());
15713 return this.day(this.day() % 7 ? weekday : weekday - 7);
15714 } else {
15715 return this.day() || 7;
15716 }
15717 }
15718
15719 var defaultWeekdaysRegex = matchWord;
15720
15721 function weekdaysRegex(isStrict) {
15722 if (this._weekdaysParseExact) {
15723 if (!hasOwnProp(this, '_weekdaysRegex')) {
15724 computeWeekdaysParse.call(this);
15725 }
15726
15727 if (isStrict) {
15728 return this._weekdaysStrictRegex;
15729 } else {
15730 return this._weekdaysRegex;
15731 }
15732 } else {
15733 if (!hasOwnProp(this, '_weekdaysRegex')) {
15734 this._weekdaysRegex = defaultWeekdaysRegex;
15735 }
15736
15737 return this._weekdaysStrictRegex && isStrict ? this._weekdaysStrictRegex : this._weekdaysRegex;
15738 }
15739 }
15740
15741 var defaultWeekdaysShortRegex = matchWord;
15742
15743 function weekdaysShortRegex(isStrict) {
15744 if (this._weekdaysParseExact) {
15745 if (!hasOwnProp(this, '_weekdaysRegex')) {
15746 computeWeekdaysParse.call(this);
15747 }
15748
15749 if (isStrict) {
15750 return this._weekdaysShortStrictRegex;
15751 } else {
15752 return this._weekdaysShortRegex;
15753 }
15754 } else {
15755 if (!hasOwnProp(this, '_weekdaysShortRegex')) {
15756 this._weekdaysShortRegex = defaultWeekdaysShortRegex;
15757 }
15758
15759 return this._weekdaysShortStrictRegex && isStrict ? this._weekdaysShortStrictRegex : this._weekdaysShortRegex;
15760 }
15761 }
15762
15763 var defaultWeekdaysMinRegex = matchWord;
15764
15765 function weekdaysMinRegex(isStrict) {
15766 if (this._weekdaysParseExact) {
15767 if (!hasOwnProp(this, '_weekdaysRegex')) {
15768 computeWeekdaysParse.call(this);
15769 }
15770
15771 if (isStrict) {
15772 return this._weekdaysMinStrictRegex;
15773 } else {
15774 return this._weekdaysMinRegex;
15775 }
15776 } else {
15777 if (!hasOwnProp(this, '_weekdaysMinRegex')) {
15778 this._weekdaysMinRegex = defaultWeekdaysMinRegex;
15779 }
15780
15781 return this._weekdaysMinStrictRegex && isStrict ? this._weekdaysMinStrictRegex : this._weekdaysMinRegex;
15782 }
15783 }
15784
15785 function computeWeekdaysParse() {
15786 function cmpLenRev(a, b) {
15787 return b.length - a.length;
15788 }
15789
15790 var minPieces = [],
15791 shortPieces = [],
15792 longPieces = [],
15793 mixedPieces = [],
15794 i,
15795 mom,
15796 minp,
15797 shortp,
15798 longp;
15799
15800 for (i = 0; i < 7; i++) {
15801 // make the regex if we don't have it already
15802 mom = createUTC([2000, 1]).day(i);
15803 minp = this.weekdaysMin(mom, '');
15804 shortp = this.weekdaysShort(mom, '');
15805 longp = this.weekdays(mom, '');
15806 minPieces.push(minp);
15807 shortPieces.push(shortp);
15808 longPieces.push(longp);
15809 mixedPieces.push(minp);
15810 mixedPieces.push(shortp);
15811 mixedPieces.push(longp);
15812 } // Sorting makes sure if one weekday (or abbr) is a prefix of another it
15813 // will match the longer piece.
15814
15815
15816 minPieces.sort(cmpLenRev);
15817 shortPieces.sort(cmpLenRev);
15818 longPieces.sort(cmpLenRev);
15819 mixedPieces.sort(cmpLenRev);
15820
15821 for (i = 0; i < 7; i++) {
15822 shortPieces[i] = regexEscape(shortPieces[i]);
15823 longPieces[i] = regexEscape(longPieces[i]);
15824 mixedPieces[i] = regexEscape(mixedPieces[i]);
15825 }
15826
15827 this._weekdaysRegex = new RegExp('^(' + mixedPieces.join('|') + ')', 'i');
15828 this._weekdaysShortRegex = this._weekdaysRegex;
15829 this._weekdaysMinRegex = this._weekdaysRegex;
15830 this._weekdaysStrictRegex = new RegExp('^(' + longPieces.join('|') + ')', 'i');
15831 this._weekdaysShortStrictRegex = new RegExp('^(' + shortPieces.join('|') + ')', 'i');
15832 this._weekdaysMinStrictRegex = new RegExp('^(' + minPieces.join('|') + ')', 'i');
15833 } // FORMATTING
15834
15835
15836 function hFormat() {
15837 return this.hours() % 12 || 12;
15838 }
15839
15840 function kFormat() {
15841 return this.hours() || 24;
15842 }
15843
15844 addFormatToken('H', ['HH', 2], 0, 'hour');
15845 addFormatToken('h', ['hh', 2], 0, hFormat);
15846 addFormatToken('k', ['kk', 2], 0, kFormat);
15847 addFormatToken('hmm', 0, 0, function () {
15848 return '' + hFormat.apply(this) + zeroFill(this.minutes(), 2);
15849 });
15850 addFormatToken('hmmss', 0, 0, function () {
15851 return '' + hFormat.apply(this) + zeroFill(this.minutes(), 2) + zeroFill(this.seconds(), 2);
15852 });
15853 addFormatToken('Hmm', 0, 0, function () {
15854 return '' + this.hours() + zeroFill(this.minutes(), 2);
15855 });
15856 addFormatToken('Hmmss', 0, 0, function () {
15857 return '' + this.hours() + zeroFill(this.minutes(), 2) + zeroFill(this.seconds(), 2);
15858 });
15859
15860 function meridiem(token, lowercase) {
15861 addFormatToken(token, 0, 0, function () {
15862 return this.localeData().meridiem(this.hours(), this.minutes(), lowercase);
15863 });
15864 }
15865
15866 meridiem('a', true);
15867 meridiem('A', false); // ALIASES
15868
15869 addUnitAlias('hour', 'h'); // PRIORITY
15870
15871 addUnitPriority('hour', 13); // PARSING
15872
15873 function matchMeridiem(isStrict, locale) {
15874 return locale._meridiemParse;
15875 }
15876
15877 addRegexToken('a', matchMeridiem);
15878 addRegexToken('A', matchMeridiem);
15879 addRegexToken('H', match1to2);
15880 addRegexToken('h', match1to2);
15881 addRegexToken('k', match1to2);
15882 addRegexToken('HH', match1to2, match2);
15883 addRegexToken('hh', match1to2, match2);
15884 addRegexToken('kk', match1to2, match2);
15885 addRegexToken('hmm', match3to4);
15886 addRegexToken('hmmss', match5to6);
15887 addRegexToken('Hmm', match3to4);
15888 addRegexToken('Hmmss', match5to6);
15889 addParseToken(['H', 'HH'], HOUR);
15890 addParseToken(['k', 'kk'], function (input, array, config) {
15891 var kInput = toInt(input);
15892 array[HOUR] = kInput === 24 ? 0 : kInput;
15893 });
15894 addParseToken(['a', 'A'], function (input, array, config) {
15895 config._isPm = config._locale.isPM(input);
15896 config._meridiem = input;
15897 });
15898 addParseToken(['h', 'hh'], function (input, array, config) {
15899 array[HOUR] = toInt(input);
15900 getParsingFlags(config).bigHour = true;
15901 });
15902 addParseToken('hmm', function (input, array, config) {
15903 var pos = input.length - 2;
15904 array[HOUR] = toInt(input.substr(0, pos));
15905 array[MINUTE] = toInt(input.substr(pos));
15906 getParsingFlags(config).bigHour = true;
15907 });
15908 addParseToken('hmmss', function (input, array, config) {
15909 var pos1 = input.length - 4;
15910 var pos2 = input.length - 2;
15911 array[HOUR] = toInt(input.substr(0, pos1));
15912 array[MINUTE] = toInt(input.substr(pos1, 2));
15913 array[SECOND] = toInt(input.substr(pos2));
15914 getParsingFlags(config).bigHour = true;
15915 });
15916 addParseToken('Hmm', function (input, array, config) {
15917 var pos = input.length - 2;
15918 array[HOUR] = toInt(input.substr(0, pos));
15919 array[MINUTE] = toInt(input.substr(pos));
15920 });
15921 addParseToken('Hmmss', function (input, array, config) {
15922 var pos1 = input.length - 4;
15923 var pos2 = input.length - 2;
15924 array[HOUR] = toInt(input.substr(0, pos1));
15925 array[MINUTE] = toInt(input.substr(pos1, 2));
15926 array[SECOND] = toInt(input.substr(pos2));
15927 }); // LOCALES
15928
15929 function localeIsPM(input) {
15930 // IE8 Quirks Mode & IE7 Standards Mode do not allow accessing strings like arrays
15931 // Using charAt should be more compatible.
15932 return (input + '').toLowerCase().charAt(0) === 'p';
15933 }
15934
15935 var defaultLocaleMeridiemParse = /[ap]\.?m?\.?/i;
15936
15937 function localeMeridiem(hours, minutes, isLower) {
15938 if (hours > 11) {
15939 return isLower ? 'pm' : 'PM';
15940 } else {
15941 return isLower ? 'am' : 'AM';
15942 }
15943 } // MOMENTS
15944 // Setting the hour should keep the time, because the user explicitly
15945 // specified which hour they want. So trying to maintain the same hour (in
15946 // a new timezone) makes sense. Adding/subtracting hours does not follow
15947 // this rule.
15948
15949
15950 var getSetHour = makeGetSet('Hours', true);
15951 var baseConfig = {
15952 calendar: defaultCalendar,
15953 longDateFormat: defaultLongDateFormat,
15954 invalidDate: defaultInvalidDate,
15955 ordinal: defaultOrdinal,
15956 dayOfMonthOrdinalParse: defaultDayOfMonthOrdinalParse,
15957 relativeTime: defaultRelativeTime,
15958 months: defaultLocaleMonths,
15959 monthsShort: defaultLocaleMonthsShort,
15960 week: defaultLocaleWeek,
15961 weekdays: defaultLocaleWeekdays,
15962 weekdaysMin: defaultLocaleWeekdaysMin,
15963 weekdaysShort: defaultLocaleWeekdaysShort,
15964 meridiemParse: defaultLocaleMeridiemParse
15965 }; // internal storage for locale config files
15966
15967 var locales = {};
15968 var localeFamilies = {};
15969 var globalLocale;
15970
15971 function normalizeLocale(key) {
15972 return key ? key.toLowerCase().replace('_', '-') : key;
15973 } // pick the locale from the array
15974 // try ['en-au', 'en-gb'] as 'en-au', 'en-gb', 'en', as in move through the list trying each
15975 // substring from most specific to least, but move to the next array item if it's a more specific variant than the current root
15976
15977
15978 function chooseLocale(names) {
15979 var i = 0,
15980 j,
15981 next,
15982 locale,
15983 split;
15984
15985 while (i < names.length) {
15986 split = normalizeLocale(names[i]).split('-');
15987 j = split.length;
15988 next = normalizeLocale(names[i + 1]);
15989 next = next ? next.split('-') : null;
15990
15991 while (j > 0) {
15992 locale = loadLocale(split.slice(0, j).join('-'));
15993
15994 if (locale) {
15995 return locale;
15996 }
15997
15998 if (next && next.length >= j && compareArrays(split, next, true) >= j - 1) {
15999 //the next array item is better than a shallower substring of this one
16000 break;
16001 }
16002
16003 j--;
16004 }
16005
16006 i++;
16007 }
16008
16009 return globalLocale;
16010 }
16011
16012 function loadLocale(name) {
16013 var oldLocale = null; // TODO: Find a better way to register and load all the locales in Node
16014
16015 if (!locales[name] && 'object' !== 'undefined' && module && module.exports) {
16016 try {
16017 oldLocale = globalLocale._abbr;
16018 var aliasedRequire = commonjsRequire$1;
16019 aliasedRequire('./locale/' + name);
16020 getSetGlobalLocale(oldLocale);
16021 } catch (e) {}
16022 }
16023
16024 return locales[name];
16025 } // This function will load locale and then set the global locale. If
16026 // no arguments are passed in, it will simply return the current global
16027 // locale key.
16028
16029
16030 function getSetGlobalLocale(key, values) {
16031 var data;
16032
16033 if (key) {
16034 if (isUndefined(values)) {
16035 data = getLocale(key);
16036 } else {
16037 data = defineLocale(key, values);
16038 }
16039
16040 if (data) {
16041 // moment.duration._locale = moment._locale = data;
16042 globalLocale = data;
16043 } else {
16044 if (typeof console !== 'undefined' && console.warn) {
16045 //warn user if arguments are passed but the locale could not be set
16046 console.warn('Locale ' + key + ' not found. Did you forget to load it?');
16047 }
16048 }
16049 }
16050
16051 return globalLocale._abbr;
16052 }
16053
16054 function defineLocale(name, config) {
16055 if (config !== null) {
16056 var locale,
16057 parentConfig = baseConfig;
16058 config.abbr = name;
16059
16060 if (locales[name] != null) {
16061 deprecateSimple('defineLocaleOverride', 'use moment.updateLocale(localeName, config) to change ' + 'an existing locale. moment.defineLocale(localeName, ' + 'config) should only be used for creating a new locale ' + 'See http://momentjs.com/guides/#/warnings/define-locale/ for more info.');
16062 parentConfig = locales[name]._config;
16063 } else if (config.parentLocale != null) {
16064 if (locales[config.parentLocale] != null) {
16065 parentConfig = locales[config.parentLocale]._config;
16066 } else {
16067 locale = loadLocale(config.parentLocale);
16068
16069 if (locale != null) {
16070 parentConfig = locale._config;
16071 } else {
16072 if (!localeFamilies[config.parentLocale]) {
16073 localeFamilies[config.parentLocale] = [];
16074 }
16075
16076 localeFamilies[config.parentLocale].push({
16077 name: name,
16078 config: config
16079 });
16080 return null;
16081 }
16082 }
16083 }
16084
16085 locales[name] = new Locale(mergeConfigs(parentConfig, config));
16086
16087 if (localeFamilies[name]) {
16088 localeFamilies[name].forEach(function (x) {
16089 defineLocale(x.name, x.config);
16090 });
16091 } // backwards compat for now: also set the locale
16092 // make sure we set the locale AFTER all child locales have been
16093 // created, so we won't end up with the child locale set.
16094
16095
16096 getSetGlobalLocale(name);
16097 return locales[name];
16098 } else {
16099 // useful for testing
16100 delete locales[name];
16101 return null;
16102 }
16103 }
16104
16105 function updateLocale(name, config) {
16106 if (config != null) {
16107 var locale,
16108 tmpLocale,
16109 parentConfig = baseConfig; // MERGE
16110
16111 tmpLocale = loadLocale(name);
16112
16113 if (tmpLocale != null) {
16114 parentConfig = tmpLocale._config;
16115 }
16116
16117 config = mergeConfigs(parentConfig, config);
16118 locale = new Locale(config);
16119 locale.parentLocale = locales[name];
16120 locales[name] = locale; // backwards compat for now: also set the locale
16121
16122 getSetGlobalLocale(name);
16123 } else {
16124 // pass null for config to unupdate, useful for tests
16125 if (locales[name] != null) {
16126 if (locales[name].parentLocale != null) {
16127 locales[name] = locales[name].parentLocale;
16128 } else if (locales[name] != null) {
16129 delete locales[name];
16130 }
16131 }
16132 }
16133
16134 return locales[name];
16135 } // returns locale data
16136
16137
16138 function getLocale(key) {
16139 var locale;
16140
16141 if (key && key._locale && key._locale._abbr) {
16142 key = key._locale._abbr;
16143 }
16144
16145 if (!key) {
16146 return globalLocale;
16147 }
16148
16149 if (!isArray(key)) {
16150 //short-circuit everything else
16151 locale = loadLocale(key);
16152
16153 if (locale) {
16154 return locale;
16155 }
16156
16157 key = [key];
16158 }
16159
16160 return chooseLocale(key);
16161 }
16162
16163 function listLocales() {
16164 return keys(locales);
16165 }
16166
16167 function checkOverflow(m) {
16168 var overflow;
16169 var a = m._a;
16170
16171 if (a && getParsingFlags(m).overflow === -2) {
16172 overflow = a[MONTH] < 0 || a[MONTH] > 11 ? MONTH : a[DATE] < 1 || a[DATE] > daysInMonth(a[YEAR], a[MONTH]) ? DATE : a[HOUR] < 0 || a[HOUR] > 24 || a[HOUR] === 24 && (a[MINUTE] !== 0 || a[SECOND] !== 0 || a[MILLISECOND] !== 0) ? HOUR : a[MINUTE] < 0 || a[MINUTE] > 59 ? MINUTE : a[SECOND] < 0 || a[SECOND] > 59 ? SECOND : a[MILLISECOND] < 0 || a[MILLISECOND] > 999 ? MILLISECOND : -1;
16173
16174 if (getParsingFlags(m)._overflowDayOfYear && (overflow < YEAR || overflow > DATE)) {
16175 overflow = DATE;
16176 }
16177
16178 if (getParsingFlags(m)._overflowWeeks && overflow === -1) {
16179 overflow = WEEK;
16180 }
16181
16182 if (getParsingFlags(m)._overflowWeekday && overflow === -1) {
16183 overflow = WEEKDAY;
16184 }
16185
16186 getParsingFlags(m).overflow = overflow;
16187 }
16188
16189 return m;
16190 } // Pick the first defined of two or three arguments.
16191
16192
16193 function defaults(a, b, c) {
16194 if (a != null) {
16195 return a;
16196 }
16197
16198 if (b != null) {
16199 return b;
16200 }
16201
16202 return c;
16203 }
16204
16205 function currentDateArray(config) {
16206 // hooks is actually the exported moment object
16207 var nowValue = new Date(hooks.now());
16208
16209 if (config._useUTC) {
16210 return [nowValue.getUTCFullYear(), nowValue.getUTCMonth(), nowValue.getUTCDate()];
16211 }
16212
16213 return [nowValue.getFullYear(), nowValue.getMonth(), nowValue.getDate()];
16214 } // convert an array to a date.
16215 // the array should mirror the parameters below
16216 // note: all values past the year are optional and will default to the lowest possible value.
16217 // [year, month, day , hour, minute, second, millisecond]
16218
16219
16220 function configFromArray(config) {
16221 var i,
16222 date,
16223 input = [],
16224 currentDate,
16225 expectedWeekday,
16226 yearToUse;
16227
16228 if (config._d) {
16229 return;
16230 }
16231
16232 currentDate = currentDateArray(config); //compute day of the year from weeks and weekdays
16233
16234 if (config._w && config._a[DATE] == null && config._a[MONTH] == null) {
16235 dayOfYearFromWeekInfo(config);
16236 } //if the day of the year is set, figure out what it is
16237
16238
16239 if (config._dayOfYear != null) {
16240 yearToUse = defaults(config._a[YEAR], currentDate[YEAR]);
16241
16242 if (config._dayOfYear > daysInYear(yearToUse) || config._dayOfYear === 0) {
16243 getParsingFlags(config)._overflowDayOfYear = true;
16244 }
16245
16246 date = createUTCDate(yearToUse, 0, config._dayOfYear);
16247 config._a[MONTH] = date.getUTCMonth();
16248 config._a[DATE] = date.getUTCDate();
16249 } // Default to current date.
16250 // * if no year, month, day of month are given, default to today
16251 // * if day of month is given, default month and year
16252 // * if month is given, default only year
16253 // * if year is given, don't default anything
16254
16255
16256 for (i = 0; i < 3 && config._a[i] == null; ++i) {
16257 config._a[i] = input[i] = currentDate[i];
16258 } // Zero out whatever was not defaulted, including time
16259
16260
16261 for (; i < 7; i++) {
16262 config._a[i] = input[i] = config._a[i] == null ? i === 2 ? 1 : 0 : config._a[i];
16263 } // Check for 24:00:00.000
16264
16265
16266 if (config._a[HOUR] === 24 && config._a[MINUTE] === 0 && config._a[SECOND] === 0 && config._a[MILLISECOND] === 0) {
16267 config._nextDay = true;
16268 config._a[HOUR] = 0;
16269 }
16270
16271 config._d = (config._useUTC ? createUTCDate : createDate).apply(null, input);
16272 expectedWeekday = config._useUTC ? config._d.getUTCDay() : config._d.getDay(); // Apply timezone offset from input. The actual utcOffset can be changed
16273 // with parseZone.
16274
16275 if (config._tzm != null) {
16276 config._d.setUTCMinutes(config._d.getUTCMinutes() - config._tzm);
16277 }
16278
16279 if (config._nextDay) {
16280 config._a[HOUR] = 24;
16281 } // check for mismatching day of week
16282
16283
16284 if (config._w && typeof config._w.d !== 'undefined' && config._w.d !== expectedWeekday) {
16285 getParsingFlags(config).weekdayMismatch = true;
16286 }
16287 }
16288
16289 function dayOfYearFromWeekInfo(config) {
16290 var w, weekYear, week, weekday, dow, doy, temp, weekdayOverflow;
16291 w = config._w;
16292
16293 if (w.GG != null || w.W != null || w.E != null) {
16294 dow = 1;
16295 doy = 4; // TODO: We need to take the current isoWeekYear, but that depends on
16296 // how we interpret now (local, utc, fixed offset). So create
16297 // a now version of current config (take local/utc/offset flags, and
16298 // create now).
16299
16300 weekYear = defaults(w.GG, config._a[YEAR], weekOfYear(createLocal(), 1, 4).year);
16301 week = defaults(w.W, 1);
16302 weekday = defaults(w.E, 1);
16303
16304 if (weekday < 1 || weekday > 7) {
16305 weekdayOverflow = true;
16306 }
16307 } else {
16308 dow = config._locale._week.dow;
16309 doy = config._locale._week.doy;
16310 var curWeek = weekOfYear(createLocal(), dow, doy);
16311 weekYear = defaults(w.gg, config._a[YEAR], curWeek.year); // Default to current week.
16312
16313 week = defaults(w.w, curWeek.week);
16314
16315 if (w.d != null) {
16316 // weekday -- low day numbers are considered next week
16317 weekday = w.d;
16318
16319 if (weekday < 0 || weekday > 6) {
16320 weekdayOverflow = true;
16321 }
16322 } else if (w.e != null) {
16323 // local weekday -- counting starts from beginning of week
16324 weekday = w.e + dow;
16325
16326 if (w.e < 0 || w.e > 6) {
16327 weekdayOverflow = true;
16328 }
16329 } else {
16330 // default to beginning of week
16331 weekday = dow;
16332 }
16333 }
16334
16335 if (week < 1 || week > weeksInYear(weekYear, dow, doy)) {
16336 getParsingFlags(config)._overflowWeeks = true;
16337 } else if (weekdayOverflow != null) {
16338 getParsingFlags(config)._overflowWeekday = true;
16339 } else {
16340 temp = dayOfYearFromWeeks(weekYear, week, weekday, dow, doy);
16341 config._a[YEAR] = temp.year;
16342 config._dayOfYear = temp.dayOfYear;
16343 }
16344 } // iso 8601 regex
16345 // 0000-00-00 0000-W00 or 0000-W00-0 + T + 00 or 00:00 or 00:00:00 or 00:00:00.000 + +00:00 or +0000 or +00)
16346
16347
16348 var extendedIsoRegex = /^\s*((?:[+-]\d{6}|\d{4})-(?:\d\d-\d\d|W\d\d-\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?::\d\d(?::\d\d(?:[.,]\d+)?)?)?)([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?$/;
16349 var basicIsoRegex = /^\s*((?:[+-]\d{6}|\d{4})(?:\d\d\d\d|W\d\d\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?:\d\d(?:\d\d(?:[.,]\d+)?)?)?)([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?$/;
16350 var tzRegex = /Z|[+-]\d\d(?::?\d\d)?/;
16351 var isoDates = [['YYYYYY-MM-DD', /[+-]\d{6}-\d\d-\d\d/], ['YYYY-MM-DD', /\d{4}-\d\d-\d\d/], ['GGGG-[W]WW-E', /\d{4}-W\d\d-\d/], ['GGGG-[W]WW', /\d{4}-W\d\d/, false], ['YYYY-DDD', /\d{4}-\d{3}/], ['YYYY-MM', /\d{4}-\d\d/, false], ['YYYYYYMMDD', /[+-]\d{10}/], ['YYYYMMDD', /\d{8}/], // YYYYMM is NOT allowed by the standard
16352 ['GGGG[W]WWE', /\d{4}W\d{3}/], ['GGGG[W]WW', /\d{4}W\d{2}/, false], ['YYYYDDD', /\d{7}/]]; // iso time formats and regexes
16353
16354 var isoTimes = [['HH:mm:ss.SSSS', /\d\d:\d\d:\d\d\.\d+/], ['HH:mm:ss,SSSS', /\d\d:\d\d:\d\d,\d+/], ['HH:mm:ss', /\d\d:\d\d:\d\d/], ['HH:mm', /\d\d:\d\d/], ['HHmmss.SSSS', /\d\d\d\d\d\d\.\d+/], ['HHmmss,SSSS', /\d\d\d\d\d\d,\d+/], ['HHmmss', /\d\d\d\d\d\d/], ['HHmm', /\d\d\d\d/], ['HH', /\d\d/]];
16355 var aspNetJsonRegex = /^\/?Date\((\-?\d+)/i; // date from iso format
16356
16357 function configFromISO(config) {
16358 var i,
16359 l,
16360 string = config._i,
16361 match = extendedIsoRegex.exec(string) || basicIsoRegex.exec(string),
16362 allowTime,
16363 dateFormat,
16364 timeFormat,
16365 tzFormat;
16366
16367 if (match) {
16368 getParsingFlags(config).iso = true;
16369
16370 for (i = 0, l = isoDates.length; i < l; i++) {
16371 if (isoDates[i][1].exec(match[1])) {
16372 dateFormat = isoDates[i][0];
16373 allowTime = isoDates[i][2] !== false;
16374 break;
16375 }
16376 }
16377
16378 if (dateFormat == null) {
16379 config._isValid = false;
16380 return;
16381 }
16382
16383 if (match[3]) {
16384 for (i = 0, l = isoTimes.length; i < l; i++) {
16385 if (isoTimes[i][1].exec(match[3])) {
16386 // match[2] should be 'T' or space
16387 timeFormat = (match[2] || ' ') + isoTimes[i][0];
16388 break;
16389 }
16390 }
16391
16392 if (timeFormat == null) {
16393 config._isValid = false;
16394 return;
16395 }
16396 }
16397
16398 if (!allowTime && timeFormat != null) {
16399 config._isValid = false;
16400 return;
16401 }
16402
16403 if (match[4]) {
16404 if (tzRegex.exec(match[4])) {
16405 tzFormat = 'Z';
16406 } else {
16407 config._isValid = false;
16408 return;
16409 }
16410 }
16411
16412 config._f = dateFormat + (timeFormat || '') + (tzFormat || '');
16413 configFromStringAndFormat(config);
16414 } else {
16415 config._isValid = false;
16416 }
16417 } // RFC 2822 regex: For details see https://tools.ietf.org/html/rfc2822#section-3.3
16418
16419
16420 var rfc2822 = /^(?:(Mon|Tue|Wed|Thu|Fri|Sat|Sun),?\s)?(\d{1,2})\s(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\s(\d{2,4})\s(\d\d):(\d\d)(?::(\d\d))?\s(?:(UT|GMT|[ECMP][SD]T)|([Zz])|([+-]\d{4}))$/;
16421
16422 function extractFromRFC2822Strings(yearStr, monthStr, dayStr, hourStr, minuteStr, secondStr) {
16423 var result = [untruncateYear(yearStr), defaultLocaleMonthsShort.indexOf(monthStr), parseInt(dayStr, 10), parseInt(hourStr, 10), parseInt(minuteStr, 10)];
16424
16425 if (secondStr) {
16426 result.push(parseInt(secondStr, 10));
16427 }
16428
16429 return result;
16430 }
16431
16432 function untruncateYear(yearStr) {
16433 var year = parseInt(yearStr, 10);
16434
16435 if (year <= 49) {
16436 return 2000 + year;
16437 } else if (year <= 999) {
16438 return 1900 + year;
16439 }
16440
16441 return year;
16442 }
16443
16444 function preprocessRFC2822(s) {
16445 // Remove comments and folding whitespace and replace multiple-spaces with a single space
16446 return s.replace(/\([^)]*\)|[\n\t]/g, ' ').replace(/(\s\s+)/g, ' ').replace(/^\s\s*/, '').replace(/\s\s*$/, '');
16447 }
16448
16449 function checkWeekday(weekdayStr, parsedInput, config) {
16450 if (weekdayStr) {
16451 // TODO: Replace the vanilla JS Date object with an indepentent day-of-week check.
16452 var weekdayProvided = defaultLocaleWeekdaysShort.indexOf(weekdayStr),
16453 weekdayActual = new Date(parsedInput[0], parsedInput[1], parsedInput[2]).getDay();
16454
16455 if (weekdayProvided !== weekdayActual) {
16456 getParsingFlags(config).weekdayMismatch = true;
16457 config._isValid = false;
16458 return false;
16459 }
16460 }
16461
16462 return true;
16463 }
16464
16465 var obsOffsets = {
16466 UT: 0,
16467 GMT: 0,
16468 EDT: -4 * 60,
16469 EST: -5 * 60,
16470 CDT: -5 * 60,
16471 CST: -6 * 60,
16472 MDT: -6 * 60,
16473 MST: -7 * 60,
16474 PDT: -7 * 60,
16475 PST: -8 * 60
16476 };
16477
16478 function calculateOffset(obsOffset, militaryOffset, numOffset) {
16479 if (obsOffset) {
16480 return obsOffsets[obsOffset];
16481 } else if (militaryOffset) {
16482 // the only allowed military tz is Z
16483 return 0;
16484 } else {
16485 var hm = parseInt(numOffset, 10);
16486 var m = hm % 100,
16487 h = (hm - m) / 100;
16488 return h * 60 + m;
16489 }
16490 } // date and time from ref 2822 format
16491
16492
16493 function configFromRFC2822(config) {
16494 var match = rfc2822.exec(preprocessRFC2822(config._i));
16495
16496 if (match) {
16497 var parsedArray = extractFromRFC2822Strings(match[4], match[3], match[2], match[5], match[6], match[7]);
16498
16499 if (!checkWeekday(match[1], parsedArray, config)) {
16500 return;
16501 }
16502
16503 config._a = parsedArray;
16504 config._tzm = calculateOffset(match[8], match[9], match[10]);
16505 config._d = createUTCDate.apply(null, config._a);
16506
16507 config._d.setUTCMinutes(config._d.getUTCMinutes() - config._tzm);
16508
16509 getParsingFlags(config).rfc2822 = true;
16510 } else {
16511 config._isValid = false;
16512 }
16513 } // date from iso format or fallback
16514
16515
16516 function configFromString(config) {
16517 var matched = aspNetJsonRegex.exec(config._i);
16518
16519 if (matched !== null) {
16520 config._d = new Date(+matched[1]);
16521 return;
16522 }
16523
16524 configFromISO(config);
16525
16526 if (config._isValid === false) {
16527 delete config._isValid;
16528 } else {
16529 return;
16530 }
16531
16532 configFromRFC2822(config);
16533
16534 if (config._isValid === false) {
16535 delete config._isValid;
16536 } else {
16537 return;
16538 } // Final attempt, use Input Fallback
16539
16540
16541 hooks.createFromInputFallback(config);
16542 }
16543
16544 hooks.createFromInputFallback = deprecate('value provided is not in a recognized RFC2822 or ISO format. moment construction falls back to js Date(), ' + 'which is not reliable across all browsers and versions. Non RFC2822/ISO date formats are ' + 'discouraged and will be removed in an upcoming major release. Please refer to ' + 'http://momentjs.com/guides/#/warnings/js-date/ for more info.', function (config) {
16545 config._d = new Date(config._i + (config._useUTC ? ' UTC' : ''));
16546 }); // constant that refers to the ISO standard
16547
16548 hooks.ISO_8601 = function () {}; // constant that refers to the RFC 2822 form
16549
16550
16551 hooks.RFC_2822 = function () {}; // date from string and format string
16552
16553
16554 function configFromStringAndFormat(config) {
16555 // TODO: Move this to another part of the creation flow to prevent circular deps
16556 if (config._f === hooks.ISO_8601) {
16557 configFromISO(config);
16558 return;
16559 }
16560
16561 if (config._f === hooks.RFC_2822) {
16562 configFromRFC2822(config);
16563 return;
16564 }
16565
16566 config._a = [];
16567 getParsingFlags(config).empty = true; // This array is used to make a Date, either with `new Date` or `Date.UTC`
16568
16569 var string = '' + config._i,
16570 i,
16571 parsedInput,
16572 tokens,
16573 token,
16574 skipped,
16575 stringLength = string.length,
16576 totalParsedInputLength = 0;
16577 tokens = expandFormat(config._f, config._locale).match(formattingTokens) || [];
16578
16579 for (i = 0; i < tokens.length; i++) {
16580 token = tokens[i];
16581 parsedInput = (string.match(getParseRegexForToken(token, config)) || [])[0]; // console.log('token', token, 'parsedInput', parsedInput,
16582 // 'regex', getParseRegexForToken(token, config));
16583
16584 if (parsedInput) {
16585 skipped = string.substr(0, string.indexOf(parsedInput));
16586
16587 if (skipped.length > 0) {
16588 getParsingFlags(config).unusedInput.push(skipped);
16589 }
16590
16591 string = string.slice(string.indexOf(parsedInput) + parsedInput.length);
16592 totalParsedInputLength += parsedInput.length;
16593 } // don't parse if it's not a known token
16594
16595
16596 if (formatTokenFunctions[token]) {
16597 if (parsedInput) {
16598 getParsingFlags(config).empty = false;
16599 } else {
16600 getParsingFlags(config).unusedTokens.push(token);
16601 }
16602
16603 addTimeToArrayFromToken(token, parsedInput, config);
16604 } else if (config._strict && !parsedInput) {
16605 getParsingFlags(config).unusedTokens.push(token);
16606 }
16607 } // add remaining unparsed input length to the string
16608
16609
16610 getParsingFlags(config).charsLeftOver = stringLength - totalParsedInputLength;
16611
16612 if (string.length > 0) {
16613 getParsingFlags(config).unusedInput.push(string);
16614 } // clear _12h flag if hour is <= 12
16615
16616
16617 if (config._a[HOUR] <= 12 && getParsingFlags(config).bigHour === true && config._a[HOUR] > 0) {
16618 getParsingFlags(config).bigHour = undefined;
16619 }
16620
16621 getParsingFlags(config).parsedDateParts = config._a.slice(0);
16622 getParsingFlags(config).meridiem = config._meridiem; // handle meridiem
16623
16624 config._a[HOUR] = meridiemFixWrap(config._locale, config._a[HOUR], config._meridiem);
16625 configFromArray(config);
16626 checkOverflow(config);
16627 }
16628
16629 function meridiemFixWrap(locale, hour, meridiem) {
16630 var isPm;
16631
16632 if (meridiem == null) {
16633 // nothing to do
16634 return hour;
16635 }
16636
16637 if (locale.meridiemHour != null) {
16638 return locale.meridiemHour(hour, meridiem);
16639 } else if (locale.isPM != null) {
16640 // Fallback
16641 isPm = locale.isPM(meridiem);
16642
16643 if (isPm && hour < 12) {
16644 hour += 12;
16645 }
16646
16647 if (!isPm && hour === 12) {
16648 hour = 0;
16649 }
16650
16651 return hour;
16652 } else {
16653 // this is not supposed to happen
16654 return hour;
16655 }
16656 } // date from string and array of format strings
16657
16658
16659 function configFromStringAndArray(config) {
16660 var tempConfig, bestMoment, scoreToBeat, i, currentScore;
16661
16662 if (config._f.length === 0) {
16663 getParsingFlags(config).invalidFormat = true;
16664 config._d = new Date(NaN);
16665 return;
16666 }
16667
16668 for (i = 0; i < config._f.length; i++) {
16669 currentScore = 0;
16670 tempConfig = copyConfig({}, config);
16671
16672 if (config._useUTC != null) {
16673 tempConfig._useUTC = config._useUTC;
16674 }
16675
16676 tempConfig._f = config._f[i];
16677 configFromStringAndFormat(tempConfig);
16678
16679 if (!isValid(tempConfig)) {
16680 continue;
16681 } // if there is any input that was not parsed add a penalty for that format
16682
16683
16684 currentScore += getParsingFlags(tempConfig).charsLeftOver; //or tokens
16685
16686 currentScore += getParsingFlags(tempConfig).unusedTokens.length * 10;
16687 getParsingFlags(tempConfig).score = currentScore;
16688
16689 if (scoreToBeat == null || currentScore < scoreToBeat) {
16690 scoreToBeat = currentScore;
16691 bestMoment = tempConfig;
16692 }
16693 }
16694
16695 extend(config, bestMoment || tempConfig);
16696 }
16697
16698 function configFromObject(config) {
16699 if (config._d) {
16700 return;
16701 }
16702
16703 var i = normalizeObjectUnits(config._i);
16704 config._a = map([i.year, i.month, i.day || i.date, i.hour, i.minute, i.second, i.millisecond], function (obj) {
16705 return obj && parseInt(obj, 10);
16706 });
16707 configFromArray(config);
16708 }
16709
16710 function createFromConfig(config) {
16711 var res = new Moment(checkOverflow(prepareConfig(config)));
16712
16713 if (res._nextDay) {
16714 // Adding is smart enough around DST
16715 res.add(1, 'd');
16716 res._nextDay = undefined;
16717 }
16718
16719 return res;
16720 }
16721
16722 function prepareConfig(config) {
16723 var input = config._i,
16724 format = config._f;
16725 config._locale = config._locale || getLocale(config._l);
16726
16727 if (input === null || format === undefined && input === '') {
16728 return createInvalid({
16729 nullInput: true
16730 });
16731 }
16732
16733 if (typeof input === 'string') {
16734 config._i = input = config._locale.preparse(input);
16735 }
16736
16737 if (isMoment(input)) {
16738 return new Moment(checkOverflow(input));
16739 } else if (isDate(input)) {
16740 config._d = input;
16741 } else if (isArray(format)) {
16742 configFromStringAndArray(config);
16743 } else if (format) {
16744 configFromStringAndFormat(config);
16745 } else {
16746 configFromInput(config);
16747 }
16748
16749 if (!isValid(config)) {
16750 config._d = null;
16751 }
16752
16753 return config;
16754 }
16755
16756 function configFromInput(config) {
16757 var input = config._i;
16758
16759 if (isUndefined(input)) {
16760 config._d = new Date(hooks.now());
16761 } else if (isDate(input)) {
16762 config._d = new Date(input.valueOf());
16763 } else if (typeof input === 'string') {
16764 configFromString(config);
16765 } else if (isArray(input)) {
16766 config._a = map(input.slice(0), function (obj) {
16767 return parseInt(obj, 10);
16768 });
16769 configFromArray(config);
16770 } else if (isObject(input)) {
16771 configFromObject(config);
16772 } else if (isNumber(input)) {
16773 // from milliseconds
16774 config._d = new Date(input);
16775 } else {
16776 hooks.createFromInputFallback(config);
16777 }
16778 }
16779
16780 function createLocalOrUTC(input, format, locale, strict, isUTC) {
16781 var c = {};
16782
16783 if (locale === true || locale === false) {
16784 strict = locale;
16785 locale = undefined;
16786 }
16787
16788 if (isObject(input) && isObjectEmpty(input) || isArray(input) && input.length === 0) {
16789 input = undefined;
16790 } // object construction must be done this way.
16791 // https://github.com/moment/moment/issues/1423
16792
16793
16794 c._isAMomentObject = true;
16795 c._useUTC = c._isUTC = isUTC;
16796 c._l = locale;
16797 c._i = input;
16798 c._f = format;
16799 c._strict = strict;
16800 return createFromConfig(c);
16801 }
16802
16803 function createLocal(input, format, locale, strict) {
16804 return createLocalOrUTC(input, format, locale, strict, false);
16805 }
16806
16807 var prototypeMin = deprecate('moment().min is deprecated, use moment.max instead. http://momentjs.com/guides/#/warnings/min-max/', function () {
16808 var other = createLocal.apply(null, arguments);
16809
16810 if (this.isValid() && other.isValid()) {
16811 return other < this ? this : other;
16812 } else {
16813 return createInvalid();
16814 }
16815 });
16816 var prototypeMax = deprecate('moment().max is deprecated, use moment.min instead. http://momentjs.com/guides/#/warnings/min-max/', function () {
16817 var other = createLocal.apply(null, arguments);
16818
16819 if (this.isValid() && other.isValid()) {
16820 return other > this ? this : other;
16821 } else {
16822 return createInvalid();
16823 }
16824 }); // Pick a moment m from moments so that m[fn](other) is true for all
16825 // other. This relies on the function fn to be transitive.
16826 //
16827 // moments should either be an array of moment objects or an array, whose
16828 // first element is an array of moment objects.
16829
16830 function pickBy(fn, moments) {
16831 var res, i;
16832
16833 if (moments.length === 1 && isArray(moments[0])) {
16834 moments = moments[0];
16835 }
16836
16837 if (!moments.length) {
16838 return createLocal();
16839 }
16840
16841 res = moments[0];
16842
16843 for (i = 1; i < moments.length; ++i) {
16844 if (!moments[i].isValid() || moments[i][fn](res)) {
16845 res = moments[i];
16846 }
16847 }
16848
16849 return res;
16850 } // TODO: Use [].sort instead?
16851
16852
16853 function min() {
16854 var args = [].slice.call(arguments, 0);
16855 return pickBy('isBefore', args);
16856 }
16857
16858 function max() {
16859 var args = [].slice.call(arguments, 0);
16860 return pickBy('isAfter', args);
16861 }
16862
16863 var now = function () {
16864 return Date.now ? Date.now() : +new Date();
16865 };
16866
16867 var ordering = ['year', 'quarter', 'month', 'week', 'day', 'hour', 'minute', 'second', 'millisecond'];
16868
16869 function isDurationValid(m) {
16870 for (var key in m) {
16871 if (!(indexOf.call(ordering, key) !== -1 && (m[key] == null || !isNaN(m[key])))) {
16872 return false;
16873 }
16874 }
16875
16876 var unitHasDecimal = false;
16877
16878 for (var i = 0; i < ordering.length; ++i) {
16879 if (m[ordering[i]]) {
16880 if (unitHasDecimal) {
16881 return false; // only allow non-integers for smallest unit
16882 }
16883
16884 if (parseFloat(m[ordering[i]]) !== toInt(m[ordering[i]])) {
16885 unitHasDecimal = true;
16886 }
16887 }
16888 }
16889
16890 return true;
16891 }
16892
16893 function isValid$1() {
16894 return this._isValid;
16895 }
16896
16897 function createInvalid$1() {
16898 return createDuration(NaN);
16899 }
16900
16901 function Duration(duration) {
16902 var normalizedInput = normalizeObjectUnits(duration),
16903 years = normalizedInput.year || 0,
16904 quarters = normalizedInput.quarter || 0,
16905 months = normalizedInput.month || 0,
16906 weeks = normalizedInput.week || normalizedInput.isoWeek || 0,
16907 days = normalizedInput.day || 0,
16908 hours = normalizedInput.hour || 0,
16909 minutes = normalizedInput.minute || 0,
16910 seconds = normalizedInput.second || 0,
16911 milliseconds = normalizedInput.millisecond || 0;
16912 this._isValid = isDurationValid(normalizedInput); // representation for dateAddRemove
16913
16914 this._milliseconds = +milliseconds + seconds * 1e3 + // 1000
16915 minutes * 6e4 + // 1000 * 60
16916 hours * 1000 * 60 * 60; //using 1000 * 60 * 60 instead of 36e5 to avoid floating point rounding errors https://github.com/moment/moment/issues/2978
16917 // Because of dateAddRemove treats 24 hours as different from a
16918 // day when working around DST, we need to store them separately
16919
16920 this._days = +days + weeks * 7; // It is impossible to translate months into days without knowing
16921 // which months you are are talking about, so we have to store
16922 // it separately.
16923
16924 this._months = +months + quarters * 3 + years * 12;
16925 this._data = {};
16926 this._locale = getLocale();
16927
16928 this._bubble();
16929 }
16930
16931 function isDuration(obj) {
16932 return obj instanceof Duration;
16933 }
16934
16935 function absRound(number) {
16936 if (number < 0) {
16937 return Math.round(-1 * number) * -1;
16938 } else {
16939 return Math.round(number);
16940 }
16941 } // FORMATTING
16942
16943
16944 function offset(token, separator) {
16945 addFormatToken(token, 0, 0, function () {
16946 var offset = this.utcOffset();
16947 var sign = '+';
16948
16949 if (offset < 0) {
16950 offset = -offset;
16951 sign = '-';
16952 }
16953
16954 return sign + zeroFill(~~(offset / 60), 2) + separator + zeroFill(~~offset % 60, 2);
16955 });
16956 }
16957
16958 offset('Z', ':');
16959 offset('ZZ', ''); // PARSING
16960
16961 addRegexToken('Z', matchShortOffset);
16962 addRegexToken('ZZ', matchShortOffset);
16963 addParseToken(['Z', 'ZZ'], function (input, array, config) {
16964 config._useUTC = true;
16965 config._tzm = offsetFromString(matchShortOffset, input);
16966 }); // HELPERS
16967 // timezone chunker
16968 // '+10:00' > ['10', '00']
16969 // '-1530' > ['-15', '30']
16970
16971 var chunkOffset = /([\+\-]|\d\d)/gi;
16972
16973 function offsetFromString(matcher, string) {
16974 var matches = (string || '').match(matcher);
16975
16976 if (matches === null) {
16977 return null;
16978 }
16979
16980 var chunk = matches[matches.length - 1] || [];
16981 var parts = (chunk + '').match(chunkOffset) || ['-', 0, 0];
16982 var minutes = +(parts[1] * 60) + toInt(parts[2]);
16983 return minutes === 0 ? 0 : parts[0] === '+' ? minutes : -minutes;
16984 } // Return a moment from input, that is local/utc/zone equivalent to model.
16985
16986
16987 function cloneWithOffset(input, model) {
16988 var res, diff;
16989
16990 if (model._isUTC) {
16991 res = model.clone();
16992 diff = (isMoment(input) || isDate(input) ? input.valueOf() : createLocal(input).valueOf()) - res.valueOf(); // Use low-level api, because this fn is low-level api.
16993
16994 res._d.setTime(res._d.valueOf() + diff);
16995
16996 hooks.updateOffset(res, false);
16997 return res;
16998 } else {
16999 return createLocal(input).local();
17000 }
17001 }
17002
17003 function getDateOffset(m) {
17004 // On Firefox.24 Date#getTimezoneOffset returns a floating point.
17005 // https://github.com/moment/moment/pull/1871
17006 return -Math.round(m._d.getTimezoneOffset() / 15) * 15;
17007 } // HOOKS
17008 // This function will be called whenever a moment is mutated.
17009 // It is intended to keep the offset in sync with the timezone.
17010
17011
17012 hooks.updateOffset = function () {}; // MOMENTS
17013 // keepLocalTime = true means only change the timezone, without
17014 // affecting the local hour. So 5:31:26 +0300 --[utcOffset(2, true)]-->
17015 // 5:31:26 +0200 It is possible that 5:31:26 doesn't exist with offset
17016 // +0200, so we adjust the time as needed, to be valid.
17017 //
17018 // Keeping the time actually adds/subtracts (one hour)
17019 // from the actual represented time. That is why we call updateOffset
17020 // a second time. In case it wants us to change the offset again
17021 // _changeInProgress == true case, then we have to adjust, because
17022 // there is no such time in the given timezone.
17023
17024
17025 function getSetOffset(input, keepLocalTime, keepMinutes) {
17026 var offset = this._offset || 0,
17027 localAdjust;
17028
17029 if (!this.isValid()) {
17030 return input != null ? this : NaN;
17031 }
17032
17033 if (input != null) {
17034 if (typeof input === 'string') {
17035 input = offsetFromString(matchShortOffset, input);
17036
17037 if (input === null) {
17038 return this;
17039 }
17040 } else if (Math.abs(input) < 16 && !keepMinutes) {
17041 input = input * 60;
17042 }
17043
17044 if (!this._isUTC && keepLocalTime) {
17045 localAdjust = getDateOffset(this);
17046 }
17047
17048 this._offset = input;
17049 this._isUTC = true;
17050
17051 if (localAdjust != null) {
17052 this.add(localAdjust, 'm');
17053 }
17054
17055 if (offset !== input) {
17056 if (!keepLocalTime || this._changeInProgress) {
17057 addSubtract(this, createDuration(input - offset, 'm'), 1, false);
17058 } else if (!this._changeInProgress) {
17059 this._changeInProgress = true;
17060 hooks.updateOffset(this, true);
17061 this._changeInProgress = null;
17062 }
17063 }
17064
17065 return this;
17066 } else {
17067 return this._isUTC ? offset : getDateOffset(this);
17068 }
17069 }
17070
17071 function getSetZone(input, keepLocalTime) {
17072 if (input != null) {
17073 if (typeof input !== 'string') {
17074 input = -input;
17075 }
17076
17077 this.utcOffset(input, keepLocalTime);
17078 return this;
17079 } else {
17080 return -this.utcOffset();
17081 }
17082 }
17083
17084 function setOffsetToUTC(keepLocalTime) {
17085 return this.utcOffset(0, keepLocalTime);
17086 }
17087
17088 function setOffsetToLocal(keepLocalTime) {
17089 if (this._isUTC) {
17090 this.utcOffset(0, keepLocalTime);
17091 this._isUTC = false;
17092
17093 if (keepLocalTime) {
17094 this.subtract(getDateOffset(this), 'm');
17095 }
17096 }
17097
17098 return this;
17099 }
17100
17101 function setOffsetToParsedOffset() {
17102 if (this._tzm != null) {
17103 this.utcOffset(this._tzm, false, true);
17104 } else if (typeof this._i === 'string') {
17105 var tZone = offsetFromString(matchOffset, this._i);
17106
17107 if (tZone != null) {
17108 this.utcOffset(tZone);
17109 } else {
17110 this.utcOffset(0, true);
17111 }
17112 }
17113
17114 return this;
17115 }
17116
17117 function hasAlignedHourOffset(input) {
17118 if (!this.isValid()) {
17119 return false;
17120 }
17121
17122 input = input ? createLocal(input).utcOffset() : 0;
17123 return (this.utcOffset() - input) % 60 === 0;
17124 }
17125
17126 function isDaylightSavingTime() {
17127 return this.utcOffset() > this.clone().month(0).utcOffset() || this.utcOffset() > this.clone().month(5).utcOffset();
17128 }
17129
17130 function isDaylightSavingTimeShifted() {
17131 if (!isUndefined(this._isDSTShifted)) {
17132 return this._isDSTShifted;
17133 }
17134
17135 var c = {};
17136 copyConfig(c, this);
17137 c = prepareConfig(c);
17138
17139 if (c._a) {
17140 var other = c._isUTC ? createUTC(c._a) : createLocal(c._a);
17141 this._isDSTShifted = this.isValid() && compareArrays(c._a, other.toArray()) > 0;
17142 } else {
17143 this._isDSTShifted = false;
17144 }
17145
17146 return this._isDSTShifted;
17147 }
17148
17149 function isLocal() {
17150 return this.isValid() ? !this._isUTC : false;
17151 }
17152
17153 function isUtcOffset() {
17154 return this.isValid() ? this._isUTC : false;
17155 }
17156
17157 function isUtc() {
17158 return this.isValid() ? this._isUTC && this._offset === 0 : false;
17159 } // ASP.NET json date format regex
17160
17161
17162 var aspNetRegex = /^(\-|\+)?(?:(\d*)[. ])?(\d+)\:(\d+)(?:\:(\d+)(\.\d*)?)?$/; // from http://docs.closure-library.googlecode.com/git/closure_goog_date_date.js.source.html
17163 // somewhat more in line with 4.4.3.2 2004 spec, but allows decimal anywhere
17164 // and further modified to allow for strings containing both week and day
17165
17166 var isoRegex = /^(-|\+)?P(?:([-+]?[0-9,.]*)Y)?(?:([-+]?[0-9,.]*)M)?(?:([-+]?[0-9,.]*)W)?(?:([-+]?[0-9,.]*)D)?(?:T(?:([-+]?[0-9,.]*)H)?(?:([-+]?[0-9,.]*)M)?(?:([-+]?[0-9,.]*)S)?)?$/;
17167
17168 function createDuration(input, key) {
17169 var duration = input,
17170 // matching against regexp is expensive, do it on demand
17171 match = null,
17172 sign,
17173 ret,
17174 diffRes;
17175
17176 if (isDuration(input)) {
17177 duration = {
17178 ms: input._milliseconds,
17179 d: input._days,
17180 M: input._months
17181 };
17182 } else if (isNumber(input)) {
17183 duration = {};
17184
17185 if (key) {
17186 duration[key] = input;
17187 } else {
17188 duration.milliseconds = input;
17189 }
17190 } else if (!!(match = aspNetRegex.exec(input))) {
17191 sign = match[1] === '-' ? -1 : 1;
17192 duration = {
17193 y: 0,
17194 d: toInt(match[DATE]) * sign,
17195 h: toInt(match[HOUR]) * sign,
17196 m: toInt(match[MINUTE]) * sign,
17197 s: toInt(match[SECOND]) * sign,
17198 ms: toInt(absRound(match[MILLISECOND] * 1000)) * sign // the millisecond decimal point is included in the match
17199
17200 };
17201 } else if (!!(match = isoRegex.exec(input))) {
17202 sign = match[1] === '-' ? -1 : 1;
17203 duration = {
17204 y: parseIso(match[2], sign),
17205 M: parseIso(match[3], sign),
17206 w: parseIso(match[4], sign),
17207 d: parseIso(match[5], sign),
17208 h: parseIso(match[6], sign),
17209 m: parseIso(match[7], sign),
17210 s: parseIso(match[8], sign)
17211 };
17212 } else if (duration == null) {
17213 // checks for null or undefined
17214 duration = {};
17215 } else if (typeof duration === 'object' && ('from' in duration || 'to' in duration)) {
17216 diffRes = momentsDifference(createLocal(duration.from), createLocal(duration.to));
17217 duration = {};
17218 duration.ms = diffRes.milliseconds;
17219 duration.M = diffRes.months;
17220 }
17221
17222 ret = new Duration(duration);
17223
17224 if (isDuration(input) && hasOwnProp(input, '_locale')) {
17225 ret._locale = input._locale;
17226 }
17227
17228 return ret;
17229 }
17230
17231 createDuration.fn = Duration.prototype;
17232 createDuration.invalid = createInvalid$1;
17233
17234 function parseIso(inp, sign) {
17235 // We'd normally use ~~inp for this, but unfortunately it also
17236 // converts floats to ints.
17237 // inp may be undefined, so careful calling replace on it.
17238 var res = inp && parseFloat(inp.replace(',', '.')); // apply sign while we're at it
17239
17240 return (isNaN(res) ? 0 : res) * sign;
17241 }
17242
17243 function positiveMomentsDifference(base, other) {
17244 var res = {};
17245 res.months = other.month() - base.month() + (other.year() - base.year()) * 12;
17246
17247 if (base.clone().add(res.months, 'M').isAfter(other)) {
17248 --res.months;
17249 }
17250
17251 res.milliseconds = +other - +base.clone().add(res.months, 'M');
17252 return res;
17253 }
17254
17255 function momentsDifference(base, other) {
17256 var res;
17257
17258 if (!(base.isValid() && other.isValid())) {
17259 return {
17260 milliseconds: 0,
17261 months: 0
17262 };
17263 }
17264
17265 other = cloneWithOffset(other, base);
17266
17267 if (base.isBefore(other)) {
17268 res = positiveMomentsDifference(base, other);
17269 } else {
17270 res = positiveMomentsDifference(other, base);
17271 res.milliseconds = -res.milliseconds;
17272 res.months = -res.months;
17273 }
17274
17275 return res;
17276 } // TODO: remove 'name' arg after deprecation is removed
17277
17278
17279 function createAdder(direction, name) {
17280 return function (val, period) {
17281 var dur, tmp; //invert the arguments, but complain about it
17282
17283 if (period !== null && !isNaN(+period)) {
17284 deprecateSimple(name, 'moment().' + name + '(period, number) is deprecated. Please use moment().' + name + '(number, period). ' + 'See http://momentjs.com/guides/#/warnings/add-inverted-param/ for more info.');
17285 tmp = val;
17286 val = period;
17287 period = tmp;
17288 }
17289
17290 val = typeof val === 'string' ? +val : val;
17291 dur = createDuration(val, period);
17292 addSubtract(this, dur, direction);
17293 return this;
17294 };
17295 }
17296
17297 function addSubtract(mom, duration, isAdding, updateOffset) {
17298 var milliseconds = duration._milliseconds,
17299 days = absRound(duration._days),
17300 months = absRound(duration._months);
17301
17302 if (!mom.isValid()) {
17303 // No op
17304 return;
17305 }
17306
17307 updateOffset = updateOffset == null ? true : updateOffset;
17308
17309 if (months) {
17310 setMonth(mom, get(mom, 'Month') + months * isAdding);
17311 }
17312
17313 if (days) {
17314 set$1(mom, 'Date', get(mom, 'Date') + days * isAdding);
17315 }
17316
17317 if (milliseconds) {
17318 mom._d.setTime(mom._d.valueOf() + milliseconds * isAdding);
17319 }
17320
17321 if (updateOffset) {
17322 hooks.updateOffset(mom, days || months);
17323 }
17324 }
17325
17326 var add = createAdder(1, 'add');
17327 var subtract = createAdder(-1, 'subtract');
17328
17329 function getCalendarFormat(myMoment, now) {
17330 var diff = myMoment.diff(now, 'days', true);
17331 return diff < -6 ? 'sameElse' : diff < -1 ? 'lastWeek' : diff < 0 ? 'lastDay' : diff < 1 ? 'sameDay' : diff < 2 ? 'nextDay' : diff < 7 ? 'nextWeek' : 'sameElse';
17332 }
17333
17334 function calendar$1(time, formats) {
17335 // We want to compare the start of today, vs this.
17336 // Getting start-of-today depends on whether we're local/utc/offset or not.
17337 var now = time || createLocal(),
17338 sod = cloneWithOffset(now, this).startOf('day'),
17339 format = hooks.calendarFormat(this, sod) || 'sameElse';
17340 var output = formats && (isFunction(formats[format]) ? formats[format].call(this, now) : formats[format]);
17341 return this.format(output || this.localeData().calendar(format, this, createLocal(now)));
17342 }
17343
17344 function clone() {
17345 return new Moment(this);
17346 }
17347
17348 function isAfter(input, units) {
17349 var localInput = isMoment(input) ? input : createLocal(input);
17350
17351 if (!(this.isValid() && localInput.isValid())) {
17352 return false;
17353 }
17354
17355 units = normalizeUnits(units) || 'millisecond';
17356
17357 if (units === 'millisecond') {
17358 return this.valueOf() > localInput.valueOf();
17359 } else {
17360 return localInput.valueOf() < this.clone().startOf(units).valueOf();
17361 }
17362 }
17363
17364 function isBefore(input, units) {
17365 var localInput = isMoment(input) ? input : createLocal(input);
17366
17367 if (!(this.isValid() && localInput.isValid())) {
17368 return false;
17369 }
17370
17371 units = normalizeUnits(units) || 'millisecond';
17372
17373 if (units === 'millisecond') {
17374 return this.valueOf() < localInput.valueOf();
17375 } else {
17376 return this.clone().endOf(units).valueOf() < localInput.valueOf();
17377 }
17378 }
17379
17380 function isBetween(from, to, units, inclusivity) {
17381 var localFrom = isMoment(from) ? from : createLocal(from),
17382 localTo = isMoment(to) ? to : createLocal(to);
17383
17384 if (!(this.isValid() && localFrom.isValid() && localTo.isValid())) {
17385 return false;
17386 }
17387
17388 inclusivity = inclusivity || '()';
17389 return (inclusivity[0] === '(' ? this.isAfter(localFrom, units) : !this.isBefore(localFrom, units)) && (inclusivity[1] === ')' ? this.isBefore(localTo, units) : !this.isAfter(localTo, units));
17390 }
17391
17392 function isSame(input, units) {
17393 var localInput = isMoment(input) ? input : createLocal(input),
17394 inputMs;
17395
17396 if (!(this.isValid() && localInput.isValid())) {
17397 return false;
17398 }
17399
17400 units = normalizeUnits(units) || 'millisecond';
17401
17402 if (units === 'millisecond') {
17403 return this.valueOf() === localInput.valueOf();
17404 } else {
17405 inputMs = localInput.valueOf();
17406 return this.clone().startOf(units).valueOf() <= inputMs && inputMs <= this.clone().endOf(units).valueOf();
17407 }
17408 }
17409
17410 function isSameOrAfter(input, units) {
17411 return this.isSame(input, units) || this.isAfter(input, units);
17412 }
17413
17414 function isSameOrBefore(input, units) {
17415 return this.isSame(input, units) || this.isBefore(input, units);
17416 }
17417
17418 function diff(input, units, asFloat) {
17419 var that, zoneDelta, output;
17420
17421 if (!this.isValid()) {
17422 return NaN;
17423 }
17424
17425 that = cloneWithOffset(input, this);
17426
17427 if (!that.isValid()) {
17428 return NaN;
17429 }
17430
17431 zoneDelta = (that.utcOffset() - this.utcOffset()) * 6e4;
17432 units = normalizeUnits(units);
17433
17434 switch (units) {
17435 case 'year':
17436 output = monthDiff(this, that) / 12;
17437 break;
17438
17439 case 'month':
17440 output = monthDiff(this, that);
17441 break;
17442
17443 case 'quarter':
17444 output = monthDiff(this, that) / 3;
17445 break;
17446
17447 case 'second':
17448 output = (this - that) / 1e3;
17449 break;
17450 // 1000
17451
17452 case 'minute':
17453 output = (this - that) / 6e4;
17454 break;
17455 // 1000 * 60
17456
17457 case 'hour':
17458 output = (this - that) / 36e5;
17459 break;
17460 // 1000 * 60 * 60
17461
17462 case 'day':
17463 output = (this - that - zoneDelta) / 864e5;
17464 break;
17465 // 1000 * 60 * 60 * 24, negate dst
17466
17467 case 'week':
17468 output = (this - that - zoneDelta) / 6048e5;
17469 break;
17470 // 1000 * 60 * 60 * 24 * 7, negate dst
17471
17472 default:
17473 output = this - that;
17474 }
17475
17476 return asFloat ? output : absFloor(output);
17477 }
17478
17479 function monthDiff(a, b) {
17480 // difference in months
17481 var wholeMonthDiff = (b.year() - a.year()) * 12 + (b.month() - a.month()),
17482 // b is in (anchor - 1 month, anchor + 1 month)
17483 anchor = a.clone().add(wholeMonthDiff, 'months'),
17484 anchor2,
17485 adjust;
17486
17487 if (b - anchor < 0) {
17488 anchor2 = a.clone().add(wholeMonthDiff - 1, 'months'); // linear across the month
17489
17490 adjust = (b - anchor) / (anchor - anchor2);
17491 } else {
17492 anchor2 = a.clone().add(wholeMonthDiff + 1, 'months'); // linear across the month
17493
17494 adjust = (b - anchor) / (anchor2 - anchor);
17495 } //check for negative zero, return zero if negative zero
17496
17497
17498 return -(wholeMonthDiff + adjust) || 0;
17499 }
17500
17501 hooks.defaultFormat = 'YYYY-MM-DDTHH:mm:ssZ';
17502 hooks.defaultFormatUtc = 'YYYY-MM-DDTHH:mm:ss[Z]';
17503
17504 function toString() {
17505 return this.clone().locale('en').format('ddd MMM DD YYYY HH:mm:ss [GMT]ZZ');
17506 }
17507
17508 function toISOString(keepOffset) {
17509 if (!this.isValid()) {
17510 return null;
17511 }
17512
17513 var utc = keepOffset !== true;
17514 var m = utc ? this.clone().utc() : this;
17515
17516 if (m.year() < 0 || m.year() > 9999) {
17517 return formatMoment(m, utc ? 'YYYYYY-MM-DD[T]HH:mm:ss.SSS[Z]' : 'YYYYYY-MM-DD[T]HH:mm:ss.SSSZ');
17518 }
17519
17520 if (isFunction(Date.prototype.toISOString)) {
17521 // native implementation is ~50x faster, use it when we can
17522 if (utc) {
17523 return this.toDate().toISOString();
17524 } else {
17525 return new Date(this.valueOf() + this.utcOffset() * 60 * 1000).toISOString().replace('Z', formatMoment(m, 'Z'));
17526 }
17527 }
17528
17529 return formatMoment(m, utc ? 'YYYY-MM-DD[T]HH:mm:ss.SSS[Z]' : 'YYYY-MM-DD[T]HH:mm:ss.SSSZ');
17530 }
17531 /**
17532 * Return a human readable representation of a moment that can
17533 * also be evaluated to get a new moment which is the same
17534 *
17535 * @link https://nodejs.org/dist/latest/docs/api/util.html#util_custom_inspect_function_on_objects
17536 */
17537
17538
17539 function inspect() {
17540 if (!this.isValid()) {
17541 return 'moment.invalid(/* ' + this._i + ' */)';
17542 }
17543
17544 var func = 'moment';
17545 var zone = '';
17546
17547 if (!this.isLocal()) {
17548 func = this.utcOffset() === 0 ? 'moment.utc' : 'moment.parseZone';
17549 zone = 'Z';
17550 }
17551
17552 var prefix = '[' + func + '("]';
17553 var year = 0 <= this.year() && this.year() <= 9999 ? 'YYYY' : 'YYYYYY';
17554 var datetime = '-MM-DD[T]HH:mm:ss.SSS';
17555 var suffix = zone + '[")]';
17556 return this.format(prefix + year + datetime + suffix);
17557 }
17558
17559 function format(inputString) {
17560 if (!inputString) {
17561 inputString = this.isUtc() ? hooks.defaultFormatUtc : hooks.defaultFormat;
17562 }
17563
17564 var output = formatMoment(this, inputString);
17565 return this.localeData().postformat(output);
17566 }
17567
17568 function from(time, withoutSuffix) {
17569 if (this.isValid() && (isMoment(time) && time.isValid() || createLocal(time).isValid())) {
17570 return createDuration({
17571 to: this,
17572 from: time
17573 }).locale(this.locale()).humanize(!withoutSuffix);
17574 } else {
17575 return this.localeData().invalidDate();
17576 }
17577 }
17578
17579 function fromNow(withoutSuffix) {
17580 return this.from(createLocal(), withoutSuffix);
17581 }
17582
17583 function to(time, withoutSuffix) {
17584 if (this.isValid() && (isMoment(time) && time.isValid() || createLocal(time).isValid())) {
17585 return createDuration({
17586 from: this,
17587 to: time
17588 }).locale(this.locale()).humanize(!withoutSuffix);
17589 } else {
17590 return this.localeData().invalidDate();
17591 }
17592 }
17593
17594 function toNow(withoutSuffix) {
17595 return this.to(createLocal(), withoutSuffix);
17596 } // If passed a locale key, it will set the locale for this
17597 // instance. Otherwise, it will return the locale configuration
17598 // variables for this instance.
17599
17600
17601 function locale(key) {
17602 var newLocaleData;
17603
17604 if (key === undefined) {
17605 return this._locale._abbr;
17606 } else {
17607 newLocaleData = getLocale(key);
17608
17609 if (newLocaleData != null) {
17610 this._locale = newLocaleData;
17611 }
17612
17613 return this;
17614 }
17615 }
17616
17617 var lang = deprecate('moment().lang() is deprecated. Instead, use moment().localeData() to get the language configuration. Use moment().locale() to change languages.', function (key) {
17618 if (key === undefined) {
17619 return this.localeData();
17620 } else {
17621 return this.locale(key);
17622 }
17623 });
17624
17625 function localeData() {
17626 return this._locale;
17627 }
17628
17629 var MS_PER_SECOND = 1000;
17630 var MS_PER_MINUTE = 60 * MS_PER_SECOND;
17631 var MS_PER_HOUR = 60 * MS_PER_MINUTE;
17632 var MS_PER_400_YEARS = (365 * 400 + 97) * 24 * MS_PER_HOUR; // actual modulo - handles negative numbers (for dates before 1970):
17633
17634 function mod$1(dividend, divisor) {
17635 return (dividend % divisor + divisor) % divisor;
17636 }
17637
17638 function localStartOfDate(y, m, d) {
17639 // the date constructor remaps years 0-99 to 1900-1999
17640 if (y < 100 && y >= 0) {
17641 // preserve leap years using a full 400 year cycle, then reset
17642 return new Date(y + 400, m, d) - MS_PER_400_YEARS;
17643 } else {
17644 return new Date(y, m, d).valueOf();
17645 }
17646 }
17647
17648 function utcStartOfDate(y, m, d) {
17649 // Date.UTC remaps years 0-99 to 1900-1999
17650 if (y < 100 && y >= 0) {
17651 // preserve leap years using a full 400 year cycle, then reset
17652 return Date.UTC(y + 400, m, d) - MS_PER_400_YEARS;
17653 } else {
17654 return Date.UTC(y, m, d);
17655 }
17656 }
17657
17658 function startOf(units) {
17659 var time;
17660 units = normalizeUnits(units);
17661
17662 if (units === undefined || units === 'millisecond' || !this.isValid()) {
17663 return this;
17664 }
17665
17666 var startOfDate = this._isUTC ? utcStartOfDate : localStartOfDate;
17667
17668 switch (units) {
17669 case 'year':
17670 time = startOfDate(this.year(), 0, 1);
17671 break;
17672
17673 case 'quarter':
17674 time = startOfDate(this.year(), this.month() - this.month() % 3, 1);
17675 break;
17676
17677 case 'month':
17678 time = startOfDate(this.year(), this.month(), 1);
17679 break;
17680
17681 case 'week':
17682 time = startOfDate(this.year(), this.month(), this.date() - this.weekday());
17683 break;
17684
17685 case 'isoWeek':
17686 time = startOfDate(this.year(), this.month(), this.date() - (this.isoWeekday() - 1));
17687 break;
17688
17689 case 'day':
17690 case 'date':
17691 time = startOfDate(this.year(), this.month(), this.date());
17692 break;
17693
17694 case 'hour':
17695 time = this._d.valueOf();
17696 time -= mod$1(time + (this._isUTC ? 0 : this.utcOffset() * MS_PER_MINUTE), MS_PER_HOUR);
17697 break;
17698
17699 case 'minute':
17700 time = this._d.valueOf();
17701 time -= mod$1(time, MS_PER_MINUTE);
17702 break;
17703
17704 case 'second':
17705 time = this._d.valueOf();
17706 time -= mod$1(time, MS_PER_SECOND);
17707 break;
17708 }
17709
17710 this._d.setTime(time);
17711
17712 hooks.updateOffset(this, true);
17713 return this;
17714 }
17715
17716 function endOf(units) {
17717 var time;
17718 units = normalizeUnits(units);
17719
17720 if (units === undefined || units === 'millisecond' || !this.isValid()) {
17721 return this;
17722 }
17723
17724 var startOfDate = this._isUTC ? utcStartOfDate : localStartOfDate;
17725
17726 switch (units) {
17727 case 'year':
17728 time = startOfDate(this.year() + 1, 0, 1) - 1;
17729 break;
17730
17731 case 'quarter':
17732 time = startOfDate(this.year(), this.month() - this.month() % 3 + 3, 1) - 1;
17733 break;
17734
17735 case 'month':
17736 time = startOfDate(this.year(), this.month() + 1, 1) - 1;
17737 break;
17738
17739 case 'week':
17740 time = startOfDate(this.year(), this.month(), this.date() - this.weekday() + 7) - 1;
17741 break;
17742
17743 case 'isoWeek':
17744 time = startOfDate(this.year(), this.month(), this.date() - (this.isoWeekday() - 1) + 7) - 1;
17745 break;
17746
17747 case 'day':
17748 case 'date':
17749 time = startOfDate(this.year(), this.month(), this.date() + 1) - 1;
17750 break;
17751
17752 case 'hour':
17753 time = this._d.valueOf();
17754 time += MS_PER_HOUR - mod$1(time + (this._isUTC ? 0 : this.utcOffset() * MS_PER_MINUTE), MS_PER_HOUR) - 1;
17755 break;
17756
17757 case 'minute':
17758 time = this._d.valueOf();
17759 time += MS_PER_MINUTE - mod$1(time, MS_PER_MINUTE) - 1;
17760 break;
17761
17762 case 'second':
17763 time = this._d.valueOf();
17764 time += MS_PER_SECOND - mod$1(time, MS_PER_SECOND) - 1;
17765 break;
17766 }
17767
17768 this._d.setTime(time);
17769
17770 hooks.updateOffset(this, true);
17771 return this;
17772 }
17773
17774 function valueOf() {
17775 return this._d.valueOf() - (this._offset || 0) * 60000;
17776 }
17777
17778 function unix() {
17779 return Math.floor(this.valueOf() / 1000);
17780 }
17781
17782 function toDate() {
17783 return new Date(this.valueOf());
17784 }
17785
17786 function toArray() {
17787 var m = this;
17788 return [m.year(), m.month(), m.date(), m.hour(), m.minute(), m.second(), m.millisecond()];
17789 }
17790
17791 function toObject() {
17792 var m = this;
17793 return {
17794 years: m.year(),
17795 months: m.month(),
17796 date: m.date(),
17797 hours: m.hours(),
17798 minutes: m.minutes(),
17799 seconds: m.seconds(),
17800 milliseconds: m.milliseconds()
17801 };
17802 }
17803
17804 function toJSON() {
17805 // new Date(NaN).toJSON() === null
17806 return this.isValid() ? this.toISOString() : null;
17807 }
17808
17809 function isValid$2() {
17810 return isValid(this);
17811 }
17812
17813 function parsingFlags() {
17814 return extend({}, getParsingFlags(this));
17815 }
17816
17817 function invalidAt() {
17818 return getParsingFlags(this).overflow;
17819 }
17820
17821 function creationData() {
17822 return {
17823 input: this._i,
17824 format: this._f,
17825 locale: this._locale,
17826 isUTC: this._isUTC,
17827 strict: this._strict
17828 };
17829 } // FORMATTING
17830
17831
17832 addFormatToken(0, ['gg', 2], 0, function () {
17833 return this.weekYear() % 100;
17834 });
17835 addFormatToken(0, ['GG', 2], 0, function () {
17836 return this.isoWeekYear() % 100;
17837 });
17838
17839 function addWeekYearFormatToken(token, getter) {
17840 addFormatToken(0, [token, token.length], 0, getter);
17841 }
17842
17843 addWeekYearFormatToken('gggg', 'weekYear');
17844 addWeekYearFormatToken('ggggg', 'weekYear');
17845 addWeekYearFormatToken('GGGG', 'isoWeekYear');
17846 addWeekYearFormatToken('GGGGG', 'isoWeekYear'); // ALIASES
17847
17848 addUnitAlias('weekYear', 'gg');
17849 addUnitAlias('isoWeekYear', 'GG'); // PRIORITY
17850
17851 addUnitPriority('weekYear', 1);
17852 addUnitPriority('isoWeekYear', 1); // PARSING
17853
17854 addRegexToken('G', matchSigned);
17855 addRegexToken('g', matchSigned);
17856 addRegexToken('GG', match1to2, match2);
17857 addRegexToken('gg', match1to2, match2);
17858 addRegexToken('GGGG', match1to4, match4);
17859 addRegexToken('gggg', match1to4, match4);
17860 addRegexToken('GGGGG', match1to6, match6);
17861 addRegexToken('ggggg', match1to6, match6);
17862 addWeekParseToken(['gggg', 'ggggg', 'GGGG', 'GGGGG'], function (input, week, config, token) {
17863 week[token.substr(0, 2)] = toInt(input);
17864 });
17865 addWeekParseToken(['gg', 'GG'], function (input, week, config, token) {
17866 week[token] = hooks.parseTwoDigitYear(input);
17867 }); // MOMENTS
17868
17869 function getSetWeekYear(input) {
17870 return getSetWeekYearHelper.call(this, input, this.week(), this.weekday(), this.localeData()._week.dow, this.localeData()._week.doy);
17871 }
17872
17873 function getSetISOWeekYear(input) {
17874 return getSetWeekYearHelper.call(this, input, this.isoWeek(), this.isoWeekday(), 1, 4);
17875 }
17876
17877 function getISOWeeksInYear() {
17878 return weeksInYear(this.year(), 1, 4);
17879 }
17880
17881 function getWeeksInYear() {
17882 var weekInfo = this.localeData()._week;
17883
17884 return weeksInYear(this.year(), weekInfo.dow, weekInfo.doy);
17885 }
17886
17887 function getSetWeekYearHelper(input, week, weekday, dow, doy) {
17888 var weeksTarget;
17889
17890 if (input == null) {
17891 return weekOfYear(this, dow, doy).year;
17892 } else {
17893 weeksTarget = weeksInYear(input, dow, doy);
17894
17895 if (week > weeksTarget) {
17896 week = weeksTarget;
17897 }
17898
17899 return setWeekAll.call(this, input, week, weekday, dow, doy);
17900 }
17901 }
17902
17903 function setWeekAll(weekYear, week, weekday, dow, doy) {
17904 var dayOfYearData = dayOfYearFromWeeks(weekYear, week, weekday, dow, doy),
17905 date = createUTCDate(dayOfYearData.year, 0, dayOfYearData.dayOfYear);
17906 this.year(date.getUTCFullYear());
17907 this.month(date.getUTCMonth());
17908 this.date(date.getUTCDate());
17909 return this;
17910 } // FORMATTING
17911
17912
17913 addFormatToken('Q', 0, 'Qo', 'quarter'); // ALIASES
17914
17915 addUnitAlias('quarter', 'Q'); // PRIORITY
17916
17917 addUnitPriority('quarter', 7); // PARSING
17918
17919 addRegexToken('Q', match1);
17920 addParseToken('Q', function (input, array) {
17921 array[MONTH] = (toInt(input) - 1) * 3;
17922 }); // MOMENTS
17923
17924 function getSetQuarter(input) {
17925 return input == null ? Math.ceil((this.month() + 1) / 3) : this.month((input - 1) * 3 + this.month() % 3);
17926 } // FORMATTING
17927
17928
17929 addFormatToken('D', ['DD', 2], 'Do', 'date'); // ALIASES
17930
17931 addUnitAlias('date', 'D'); // PRIORITY
17932
17933 addUnitPriority('date', 9); // PARSING
17934
17935 addRegexToken('D', match1to2);
17936 addRegexToken('DD', match1to2, match2);
17937 addRegexToken('Do', function (isStrict, locale) {
17938 // TODO: Remove "ordinalParse" fallback in next major release.
17939 return isStrict ? locale._dayOfMonthOrdinalParse || locale._ordinalParse : locale._dayOfMonthOrdinalParseLenient;
17940 });
17941 addParseToken(['D', 'DD'], DATE);
17942 addParseToken('Do', function (input, array) {
17943 array[DATE] = toInt(input.match(match1to2)[0]);
17944 }); // MOMENTS
17945
17946 var getSetDayOfMonth = makeGetSet('Date', true); // FORMATTING
17947
17948 addFormatToken('DDD', ['DDDD', 3], 'DDDo', 'dayOfYear'); // ALIASES
17949
17950 addUnitAlias('dayOfYear', 'DDD'); // PRIORITY
17951
17952 addUnitPriority('dayOfYear', 4); // PARSING
17953
17954 addRegexToken('DDD', match1to3);
17955 addRegexToken('DDDD', match3);
17956 addParseToken(['DDD', 'DDDD'], function (input, array, config) {
17957 config._dayOfYear = toInt(input);
17958 }); // HELPERS
17959 // MOMENTS
17960
17961 function getSetDayOfYear(input) {
17962 var dayOfYear = Math.round((this.clone().startOf('day') - this.clone().startOf('year')) / 864e5) + 1;
17963 return input == null ? dayOfYear : this.add(input - dayOfYear, 'd');
17964 } // FORMATTING
17965
17966
17967 addFormatToken('m', ['mm', 2], 0, 'minute'); // ALIASES
17968
17969 addUnitAlias('minute', 'm'); // PRIORITY
17970
17971 addUnitPriority('minute', 14); // PARSING
17972
17973 addRegexToken('m', match1to2);
17974 addRegexToken('mm', match1to2, match2);
17975 addParseToken(['m', 'mm'], MINUTE); // MOMENTS
17976
17977 var getSetMinute = makeGetSet('Minutes', false); // FORMATTING
17978
17979 addFormatToken('s', ['ss', 2], 0, 'second'); // ALIASES
17980
17981 addUnitAlias('second', 's'); // PRIORITY
17982
17983 addUnitPriority('second', 15); // PARSING
17984
17985 addRegexToken('s', match1to2);
17986 addRegexToken('ss', match1to2, match2);
17987 addParseToken(['s', 'ss'], SECOND); // MOMENTS
17988
17989 var getSetSecond = makeGetSet('Seconds', false); // FORMATTING
17990
17991 addFormatToken('S', 0, 0, function () {
17992 return ~~(this.millisecond() / 100);
17993 });
17994 addFormatToken(0, ['SS', 2], 0, function () {
17995 return ~~(this.millisecond() / 10);
17996 });
17997 addFormatToken(0, ['SSS', 3], 0, 'millisecond');
17998 addFormatToken(0, ['SSSS', 4], 0, function () {
17999 return this.millisecond() * 10;
18000 });
18001 addFormatToken(0, ['SSSSS', 5], 0, function () {
18002 return this.millisecond() * 100;
18003 });
18004 addFormatToken(0, ['SSSSSS', 6], 0, function () {
18005 return this.millisecond() * 1000;
18006 });
18007 addFormatToken(0, ['SSSSSSS', 7], 0, function () {
18008 return this.millisecond() * 10000;
18009 });
18010 addFormatToken(0, ['SSSSSSSS', 8], 0, function () {
18011 return this.millisecond() * 100000;
18012 });
18013 addFormatToken(0, ['SSSSSSSSS', 9], 0, function () {
18014 return this.millisecond() * 1000000;
18015 }); // ALIASES
18016
18017 addUnitAlias('millisecond', 'ms'); // PRIORITY
18018
18019 addUnitPriority('millisecond', 16); // PARSING
18020
18021 addRegexToken('S', match1to3, match1);
18022 addRegexToken('SS', match1to3, match2);
18023 addRegexToken('SSS', match1to3, match3);
18024 var token;
18025
18026 for (token = 'SSSS'; token.length <= 9; token += 'S') {
18027 addRegexToken(token, matchUnsigned);
18028 }
18029
18030 function parseMs(input, array) {
18031 array[MILLISECOND] = toInt(('0.' + input) * 1000);
18032 }
18033
18034 for (token = 'S'; token.length <= 9; token += 'S') {
18035 addParseToken(token, parseMs);
18036 } // MOMENTS
18037
18038
18039 var getSetMillisecond = makeGetSet('Milliseconds', false); // FORMATTING
18040
18041 addFormatToken('z', 0, 0, 'zoneAbbr');
18042 addFormatToken('zz', 0, 0, 'zoneName'); // MOMENTS
18043
18044 function getZoneAbbr() {
18045 return this._isUTC ? 'UTC' : '';
18046 }
18047
18048 function getZoneName() {
18049 return this._isUTC ? 'Coordinated Universal Time' : '';
18050 }
18051
18052 var proto = Moment.prototype;
18053 proto.add = add;
18054 proto.calendar = calendar$1;
18055 proto.clone = clone;
18056 proto.diff = diff;
18057 proto.endOf = endOf;
18058 proto.format = format;
18059 proto.from = from;
18060 proto.fromNow = fromNow;
18061 proto.to = to;
18062 proto.toNow = toNow;
18063 proto.get = stringGet;
18064 proto.invalidAt = invalidAt;
18065 proto.isAfter = isAfter;
18066 proto.isBefore = isBefore;
18067 proto.isBetween = isBetween;
18068 proto.isSame = isSame;
18069 proto.isSameOrAfter = isSameOrAfter;
18070 proto.isSameOrBefore = isSameOrBefore;
18071 proto.isValid = isValid$2;
18072 proto.lang = lang;
18073 proto.locale = locale;
18074 proto.localeData = localeData;
18075 proto.max = prototypeMax;
18076 proto.min = prototypeMin;
18077 proto.parsingFlags = parsingFlags;
18078 proto.set = stringSet;
18079 proto.startOf = startOf;
18080 proto.subtract = subtract;
18081 proto.toArray = toArray;
18082 proto.toObject = toObject;
18083 proto.toDate = toDate;
18084 proto.toISOString = toISOString;
18085 proto.inspect = inspect;
18086 proto.toJSON = toJSON;
18087 proto.toString = toString;
18088 proto.unix = unix;
18089 proto.valueOf = valueOf;
18090 proto.creationData = creationData;
18091 proto.year = getSetYear;
18092 proto.isLeapYear = getIsLeapYear;
18093 proto.weekYear = getSetWeekYear;
18094 proto.isoWeekYear = getSetISOWeekYear;
18095 proto.quarter = proto.quarters = getSetQuarter;
18096 proto.month = getSetMonth;
18097 proto.daysInMonth = getDaysInMonth;
18098 proto.week = proto.weeks = getSetWeek;
18099 proto.isoWeek = proto.isoWeeks = getSetISOWeek;
18100 proto.weeksInYear = getWeeksInYear;
18101 proto.isoWeeksInYear = getISOWeeksInYear;
18102 proto.date = getSetDayOfMonth;
18103 proto.day = proto.days = getSetDayOfWeek;
18104 proto.weekday = getSetLocaleDayOfWeek;
18105 proto.isoWeekday = getSetISODayOfWeek;
18106 proto.dayOfYear = getSetDayOfYear;
18107 proto.hour = proto.hours = getSetHour;
18108 proto.minute = proto.minutes = getSetMinute;
18109 proto.second = proto.seconds = getSetSecond;
18110 proto.millisecond = proto.milliseconds = getSetMillisecond;
18111 proto.utcOffset = getSetOffset;
18112 proto.utc = setOffsetToUTC;
18113 proto.local = setOffsetToLocal;
18114 proto.parseZone = setOffsetToParsedOffset;
18115 proto.hasAlignedHourOffset = hasAlignedHourOffset;
18116 proto.isDST = isDaylightSavingTime;
18117 proto.isLocal = isLocal;
18118 proto.isUtcOffset = isUtcOffset;
18119 proto.isUtc = isUtc;
18120 proto.isUTC = isUtc;
18121 proto.zoneAbbr = getZoneAbbr;
18122 proto.zoneName = getZoneName;
18123 proto.dates = deprecate('dates accessor is deprecated. Use date instead.', getSetDayOfMonth);
18124 proto.months = deprecate('months accessor is deprecated. Use month instead', getSetMonth);
18125 proto.years = deprecate('years accessor is deprecated. Use year instead', getSetYear);
18126 proto.zone = deprecate('moment().zone is deprecated, use moment().utcOffset instead. http://momentjs.com/guides/#/warnings/zone/', getSetZone);
18127 proto.isDSTShifted = deprecate('isDSTShifted is deprecated. See http://momentjs.com/guides/#/warnings/dst-shifted/ for more information', isDaylightSavingTimeShifted);
18128
18129 function createUnix(input) {
18130 return createLocal(input * 1000);
18131 }
18132
18133 function createInZone() {
18134 return createLocal.apply(null, arguments).parseZone();
18135 }
18136
18137 function preParsePostFormat(string) {
18138 return string;
18139 }
18140
18141 var proto$1 = Locale.prototype;
18142 proto$1.calendar = calendar;
18143 proto$1.longDateFormat = longDateFormat;
18144 proto$1.invalidDate = invalidDate;
18145 proto$1.ordinal = ordinal;
18146 proto$1.preparse = preParsePostFormat;
18147 proto$1.postformat = preParsePostFormat;
18148 proto$1.relativeTime = relativeTime;
18149 proto$1.pastFuture = pastFuture;
18150 proto$1.set = set;
18151 proto$1.months = localeMonths;
18152 proto$1.monthsShort = localeMonthsShort;
18153 proto$1.monthsParse = localeMonthsParse;
18154 proto$1.monthsRegex = monthsRegex;
18155 proto$1.monthsShortRegex = monthsShortRegex;
18156 proto$1.week = localeWeek;
18157 proto$1.firstDayOfYear = localeFirstDayOfYear;
18158 proto$1.firstDayOfWeek = localeFirstDayOfWeek;
18159 proto$1.weekdays = localeWeekdays;
18160 proto$1.weekdaysMin = localeWeekdaysMin;
18161 proto$1.weekdaysShort = localeWeekdaysShort;
18162 proto$1.weekdaysParse = localeWeekdaysParse;
18163 proto$1.weekdaysRegex = weekdaysRegex;
18164 proto$1.weekdaysShortRegex = weekdaysShortRegex;
18165 proto$1.weekdaysMinRegex = weekdaysMinRegex;
18166 proto$1.isPM = localeIsPM;
18167 proto$1.meridiem = localeMeridiem;
18168
18169 function get$1(format, index, field, setter) {
18170 var locale = getLocale();
18171 var utc = createUTC().set(setter, index);
18172 return locale[field](utc, format);
18173 }
18174
18175 function listMonthsImpl(format, index, field) {
18176 if (isNumber(format)) {
18177 index = format;
18178 format = undefined;
18179 }
18180
18181 format = format || '';
18182
18183 if (index != null) {
18184 return get$1(format, index, field, 'month');
18185 }
18186
18187 var i;
18188 var out = [];
18189
18190 for (i = 0; i < 12; i++) {
18191 out[i] = get$1(format, i, field, 'month');
18192 }
18193
18194 return out;
18195 } // ()
18196 // (5)
18197 // (fmt, 5)
18198 // (fmt)
18199 // (true)
18200 // (true, 5)
18201 // (true, fmt, 5)
18202 // (true, fmt)
18203
18204
18205 function listWeekdaysImpl(localeSorted, format, index, field) {
18206 if (typeof localeSorted === 'boolean') {
18207 if (isNumber(format)) {
18208 index = format;
18209 format = undefined;
18210 }
18211
18212 format = format || '';
18213 } else {
18214 format = localeSorted;
18215 index = format;
18216 localeSorted = false;
18217
18218 if (isNumber(format)) {
18219 index = format;
18220 format = undefined;
18221 }
18222
18223 format = format || '';
18224 }
18225
18226 var locale = getLocale(),
18227 shift = localeSorted ? locale._week.dow : 0;
18228
18229 if (index != null) {
18230 return get$1(format, (index + shift) % 7, field, 'day');
18231 }
18232
18233 var i;
18234 var out = [];
18235
18236 for (i = 0; i < 7; i++) {
18237 out[i] = get$1(format, (i + shift) % 7, field, 'day');
18238 }
18239
18240 return out;
18241 }
18242
18243 function listMonths(format, index) {
18244 return listMonthsImpl(format, index, 'months');
18245 }
18246
18247 function listMonthsShort(format, index) {
18248 return listMonthsImpl(format, index, 'monthsShort');
18249 }
18250
18251 function listWeekdays(localeSorted, format, index) {
18252 return listWeekdaysImpl(localeSorted, format, index, 'weekdays');
18253 }
18254
18255 function listWeekdaysShort(localeSorted, format, index) {
18256 return listWeekdaysImpl(localeSorted, format, index, 'weekdaysShort');
18257 }
18258
18259 function listWeekdaysMin(localeSorted, format, index) {
18260 return listWeekdaysImpl(localeSorted, format, index, 'weekdaysMin');
18261 }
18262
18263 getSetGlobalLocale('en', {
18264 dayOfMonthOrdinalParse: /\d{1,2}(th|st|nd|rd)/,
18265 ordinal: function (number) {
18266 var b = number % 10,
18267 output = toInt(number % 100 / 10) === 1 ? 'th' : b === 1 ? 'st' : b === 2 ? 'nd' : b === 3 ? 'rd' : 'th';
18268 return number + output;
18269 }
18270 }); // Side effect imports
18271
18272 hooks.lang = deprecate('moment.lang is deprecated. Use moment.locale instead.', getSetGlobalLocale);
18273 hooks.langData = deprecate('moment.langData is deprecated. Use moment.localeData instead.', getLocale);
18274 var mathAbs = Math.abs;
18275
18276 function abs() {
18277 var data = this._data;
18278 this._milliseconds = mathAbs(this._milliseconds);
18279 this._days = mathAbs(this._days);
18280 this._months = mathAbs(this._months);
18281 data.milliseconds = mathAbs(data.milliseconds);
18282 data.seconds = mathAbs(data.seconds);
18283 data.minutes = mathAbs(data.minutes);
18284 data.hours = mathAbs(data.hours);
18285 data.months = mathAbs(data.months);
18286 data.years = mathAbs(data.years);
18287 return this;
18288 }
18289
18290 function addSubtract$1(duration, input, value, direction) {
18291 var other = createDuration(input, value);
18292 duration._milliseconds += direction * other._milliseconds;
18293 duration._days += direction * other._days;
18294 duration._months += direction * other._months;
18295 return duration._bubble();
18296 } // supports only 2.0-style add(1, 's') or add(duration)
18297
18298
18299 function add$1(input, value) {
18300 return addSubtract$1(this, input, value, 1);
18301 } // supports only 2.0-style subtract(1, 's') or subtract(duration)
18302
18303
18304 function subtract$1(input, value) {
18305 return addSubtract$1(this, input, value, -1);
18306 }
18307
18308 function absCeil(number) {
18309 if (number < 0) {
18310 return Math.floor(number);
18311 } else {
18312 return Math.ceil(number);
18313 }
18314 }
18315
18316 function bubble() {
18317 var milliseconds = this._milliseconds;
18318 var days = this._days;
18319 var months = this._months;
18320 var data = this._data;
18321 var seconds, minutes, hours, years, monthsFromDays; // if we have a mix of positive and negative values, bubble down first
18322 // check: https://github.com/moment/moment/issues/2166
18323
18324 if (!(milliseconds >= 0 && days >= 0 && months >= 0 || milliseconds <= 0 && days <= 0 && months <= 0)) {
18325 milliseconds += absCeil(monthsToDays(months) + days) * 864e5;
18326 days = 0;
18327 months = 0;
18328 } // The following code bubbles up values, see the tests for
18329 // examples of what that means.
18330
18331
18332 data.milliseconds = milliseconds % 1000;
18333 seconds = absFloor(milliseconds / 1000);
18334 data.seconds = seconds % 60;
18335 minutes = absFloor(seconds / 60);
18336 data.minutes = minutes % 60;
18337 hours = absFloor(minutes / 60);
18338 data.hours = hours % 24;
18339 days += absFloor(hours / 24); // convert days to months
18340
18341 monthsFromDays = absFloor(daysToMonths(days));
18342 months += monthsFromDays;
18343 days -= absCeil(monthsToDays(monthsFromDays)); // 12 months -> 1 year
18344
18345 years = absFloor(months / 12);
18346 months %= 12;
18347 data.days = days;
18348 data.months = months;
18349 data.years = years;
18350 return this;
18351 }
18352
18353 function daysToMonths(days) {
18354 // 400 years have 146097 days (taking into account leap year rules)
18355 // 400 years have 12 months === 4800
18356 return days * 4800 / 146097;
18357 }
18358
18359 function monthsToDays(months) {
18360 // the reverse of daysToMonths
18361 return months * 146097 / 4800;
18362 }
18363
18364 function as(units) {
18365 if (!this.isValid()) {
18366 return NaN;
18367 }
18368
18369 var days;
18370 var months;
18371 var milliseconds = this._milliseconds;
18372 units = normalizeUnits(units);
18373
18374 if (units === 'month' || units === 'quarter' || units === 'year') {
18375 days = this._days + milliseconds / 864e5;
18376 months = this._months + daysToMonths(days);
18377
18378 switch (units) {
18379 case 'month':
18380 return months;
18381
18382 case 'quarter':
18383 return months / 3;
18384
18385 case 'year':
18386 return months / 12;
18387 }
18388 } else {
18389 // handle milliseconds separately because of floating point math errors (issue #1867)
18390 days = this._days + Math.round(monthsToDays(this._months));
18391
18392 switch (units) {
18393 case 'week':
18394 return days / 7 + milliseconds / 6048e5;
18395
18396 case 'day':
18397 return days + milliseconds / 864e5;
18398
18399 case 'hour':
18400 return days * 24 + milliseconds / 36e5;
18401
18402 case 'minute':
18403 return days * 1440 + milliseconds / 6e4;
18404
18405 case 'second':
18406 return days * 86400 + milliseconds / 1000;
18407 // Math.floor prevents floating point math errors here
18408
18409 case 'millisecond':
18410 return Math.floor(days * 864e5) + milliseconds;
18411
18412 default:
18413 throw new Error('Unknown unit ' + units);
18414 }
18415 }
18416 } // TODO: Use this.as('ms')?
18417
18418
18419 function valueOf$1() {
18420 if (!this.isValid()) {
18421 return NaN;
18422 }
18423
18424 return this._milliseconds + this._days * 864e5 + this._months % 12 * 2592e6 + toInt(this._months / 12) * 31536e6;
18425 }
18426
18427 function makeAs(alias) {
18428 return function () {
18429 return this.as(alias);
18430 };
18431 }
18432
18433 var asMilliseconds = makeAs('ms');
18434 var asSeconds = makeAs('s');
18435 var asMinutes = makeAs('m');
18436 var asHours = makeAs('h');
18437 var asDays = makeAs('d');
18438 var asWeeks = makeAs('w');
18439 var asMonths = makeAs('M');
18440 var asQuarters = makeAs('Q');
18441 var asYears = makeAs('y');
18442
18443 function clone$1() {
18444 return createDuration(this);
18445 }
18446
18447 function get$2(units) {
18448 units = normalizeUnits(units);
18449 return this.isValid() ? this[units + 's']() : NaN;
18450 }
18451
18452 function makeGetter(name) {
18453 return function () {
18454 return this.isValid() ? this._data[name] : NaN;
18455 };
18456 }
18457
18458 var milliseconds = makeGetter('milliseconds');
18459 var seconds = makeGetter('seconds');
18460 var minutes = makeGetter('minutes');
18461 var hours = makeGetter('hours');
18462 var days = makeGetter('days');
18463 var months = makeGetter('months');
18464 var years = makeGetter('years');
18465
18466 function weeks() {
18467 return absFloor(this.days() / 7);
18468 }
18469
18470 var round = Math.round;
18471 var thresholds = {
18472 ss: 44,
18473 // a few seconds to seconds
18474 s: 45,
18475 // seconds to minute
18476 m: 45,
18477 // minutes to hour
18478 h: 22,
18479 // hours to day
18480 d: 26,
18481 // days to month
18482 M: 11 // months to year
18483
18484 }; // helper function for moment.fn.from, moment.fn.fromNow, and moment.duration.fn.humanize
18485
18486 function substituteTimeAgo(string, number, withoutSuffix, isFuture, locale) {
18487 return locale.relativeTime(number || 1, !!withoutSuffix, string, isFuture);
18488 }
18489
18490 function relativeTime$1(posNegDuration, withoutSuffix, locale) {
18491 var duration = createDuration(posNegDuration).abs();
18492 var seconds = round(duration.as('s'));
18493 var minutes = round(duration.as('m'));
18494 var hours = round(duration.as('h'));
18495 var days = round(duration.as('d'));
18496 var months = round(duration.as('M'));
18497 var years = round(duration.as('y'));
18498 var a = seconds <= thresholds.ss && ['s', seconds] || seconds < thresholds.s && ['ss', seconds] || minutes <= 1 && ['m'] || minutes < thresholds.m && ['mm', minutes] || hours <= 1 && ['h'] || hours < thresholds.h && ['hh', hours] || days <= 1 && ['d'] || days < thresholds.d && ['dd', days] || months <= 1 && ['M'] || months < thresholds.M && ['MM', months] || years <= 1 && ['y'] || ['yy', years];
18499 a[2] = withoutSuffix;
18500 a[3] = +posNegDuration > 0;
18501 a[4] = locale;
18502 return substituteTimeAgo.apply(null, a);
18503 } // This function allows you to set the rounding function for relative time strings
18504
18505
18506 function getSetRelativeTimeRounding(roundingFunction) {
18507 if (roundingFunction === undefined) {
18508 return round;
18509 }
18510
18511 if (typeof roundingFunction === 'function') {
18512 round = roundingFunction;
18513 return true;
18514 }
18515
18516 return false;
18517 } // This function allows you to set a threshold for relative time strings
18518
18519
18520 function getSetRelativeTimeThreshold(threshold, limit) {
18521 if (thresholds[threshold] === undefined) {
18522 return false;
18523 }
18524
18525 if (limit === undefined) {
18526 return thresholds[threshold];
18527 }
18528
18529 thresholds[threshold] = limit;
18530
18531 if (threshold === 's') {
18532 thresholds.ss = limit - 1;
18533 }
18534
18535 return true;
18536 }
18537
18538 function humanize(withSuffix) {
18539 if (!this.isValid()) {
18540 return this.localeData().invalidDate();
18541 }
18542
18543 var locale = this.localeData();
18544 var output = relativeTime$1(this, !withSuffix, locale);
18545
18546 if (withSuffix) {
18547 output = locale.pastFuture(+this, output);
18548 }
18549
18550 return locale.postformat(output);
18551 }
18552
18553 var abs$1 = Math.abs;
18554
18555 function sign(x) {
18556 return (x > 0) - (x < 0) || +x;
18557 }
18558
18559 function toISOString$1() {
18560 // for ISO strings we do not use the normal bubbling rules:
18561 // * milliseconds bubble up until they become hours
18562 // * days do not bubble at all
18563 // * months bubble up until they become years
18564 // This is because there is no context-free conversion between hours and days
18565 // (think of clock changes)
18566 // and also not between days and months (28-31 days per month)
18567 if (!this.isValid()) {
18568 return this.localeData().invalidDate();
18569 }
18570
18571 var seconds = abs$1(this._milliseconds) / 1000;
18572 var days = abs$1(this._days);
18573 var months = abs$1(this._months);
18574 var minutes, hours, years; // 3600 seconds -> 60 minutes -> 1 hour
18575
18576 minutes = absFloor(seconds / 60);
18577 hours = absFloor(minutes / 60);
18578 seconds %= 60;
18579 minutes %= 60; // 12 months -> 1 year
18580
18581 years = absFloor(months / 12);
18582 months %= 12; // inspired by https://github.com/dordille/moment-isoduration/blob/master/moment.isoduration.js
18583
18584 var Y = years;
18585 var M = months;
18586 var D = days;
18587 var h = hours;
18588 var m = minutes;
18589 var s = seconds ? seconds.toFixed(3).replace(/\.?0+$/, '') : '';
18590 var total = this.asSeconds();
18591
18592 if (!total) {
18593 // this is the same as C#'s (Noda) and python (isodate)...
18594 // but not other JS (goog.date)
18595 return 'P0D';
18596 }
18597
18598 var totalSign = total < 0 ? '-' : '';
18599 var ymSign = sign(this._months) !== sign(total) ? '-' : '';
18600 var daysSign = sign(this._days) !== sign(total) ? '-' : '';
18601 var hmsSign = sign(this._milliseconds) !== sign(total) ? '-' : '';
18602 return totalSign + 'P' + (Y ? ymSign + Y + 'Y' : '') + (M ? ymSign + M + 'M' : '') + (D ? daysSign + D + 'D' : '') + (h || m || s ? 'T' : '') + (h ? hmsSign + h + 'H' : '') + (m ? hmsSign + m + 'M' : '') + (s ? hmsSign + s + 'S' : '');
18603 }
18604
18605 var proto$2 = Duration.prototype;
18606 proto$2.isValid = isValid$1;
18607 proto$2.abs = abs;
18608 proto$2.add = add$1;
18609 proto$2.subtract = subtract$1;
18610 proto$2.as = as;
18611 proto$2.asMilliseconds = asMilliseconds;
18612 proto$2.asSeconds = asSeconds;
18613 proto$2.asMinutes = asMinutes;
18614 proto$2.asHours = asHours;
18615 proto$2.asDays = asDays;
18616 proto$2.asWeeks = asWeeks;
18617 proto$2.asMonths = asMonths;
18618 proto$2.asQuarters = asQuarters;
18619 proto$2.asYears = asYears;
18620 proto$2.valueOf = valueOf$1;
18621 proto$2._bubble = bubble;
18622 proto$2.clone = clone$1;
18623 proto$2.get = get$2;
18624 proto$2.milliseconds = milliseconds;
18625 proto$2.seconds = seconds;
18626 proto$2.minutes = minutes;
18627 proto$2.hours = hours;
18628 proto$2.days = days;
18629 proto$2.weeks = weeks;
18630 proto$2.months = months;
18631 proto$2.years = years;
18632 proto$2.humanize = humanize;
18633 proto$2.toISOString = toISOString$1;
18634 proto$2.toString = toISOString$1;
18635 proto$2.toJSON = toISOString$1;
18636 proto$2.locale = locale;
18637 proto$2.localeData = localeData;
18638 proto$2.toIsoString = deprecate('toIsoString() is deprecated. Please use toISOString() instead (notice the capitals)', toISOString$1);
18639 proto$2.lang = lang; // Side effect imports
18640 // FORMATTING
18641
18642 addFormatToken('X', 0, 0, 'unix');
18643 addFormatToken('x', 0, 0, 'valueOf'); // PARSING
18644
18645 addRegexToken('x', matchSigned);
18646 addRegexToken('X', matchTimestamp);
18647 addParseToken('X', function (input, array, config) {
18648 config._d = new Date(parseFloat(input, 10) * 1000);
18649 });
18650 addParseToken('x', function (input, array, config) {
18651 config._d = new Date(toInt(input));
18652 }); // Side effect imports
18653
18654 hooks.version = '2.24.0';
18655 setHookCallback(createLocal);
18656 hooks.fn = proto;
18657 hooks.min = min;
18658 hooks.max = max;
18659 hooks.now = now;
18660 hooks.utc = createUTC;
18661 hooks.unix = createUnix;
18662 hooks.months = listMonths;
18663 hooks.isDate = isDate;
18664 hooks.locale = getSetGlobalLocale;
18665 hooks.invalid = createInvalid;
18666 hooks.duration = createDuration;
18667 hooks.isMoment = isMoment;
18668 hooks.weekdays = listWeekdays;
18669 hooks.parseZone = createInZone;
18670 hooks.localeData = getLocale;
18671 hooks.isDuration = isDuration;
18672 hooks.monthsShort = listMonthsShort;
18673 hooks.weekdaysMin = listWeekdaysMin;
18674 hooks.defineLocale = defineLocale;
18675 hooks.updateLocale = updateLocale;
18676 hooks.locales = listLocales;
18677 hooks.weekdaysShort = listWeekdaysShort;
18678 hooks.normalizeUnits = normalizeUnits;
18679 hooks.relativeTimeRounding = getSetRelativeTimeRounding;
18680 hooks.relativeTimeThreshold = getSetRelativeTimeThreshold;
18681 hooks.calendarFormat = getCalendarFormat;
18682 hooks.prototype = proto; // currently HTML5 input type only supports 24-hour formats
18683
18684 hooks.HTML5_FMT = {
18685 DATETIME_LOCAL: 'YYYY-MM-DDTHH:mm',
18686 // <input type="datetime-local" />
18687 DATETIME_LOCAL_SECONDS: 'YYYY-MM-DDTHH:mm:ss',
18688 // <input type="datetime-local" step="1" />
18689 DATETIME_LOCAL_MS: 'YYYY-MM-DDTHH:mm:ss.SSS',
18690 // <input type="datetime-local" step="0.001" />
18691 DATE: 'YYYY-MM-DD',
18692 // <input type="date" />
18693 TIME: 'HH:mm',
18694 // <input type="time" />
18695 TIME_SECONDS: 'HH:mm:ss',
18696 // <input type="time" step="1" />
18697 TIME_MS: 'HH:mm:ss.SSS',
18698 // <input type="time" step="0.001" />
18699 WEEK: 'GGGG-[W]WW',
18700 // <input type="week" />
18701 MONTH: 'YYYY-MM' // <input type="month" />
18702
18703 };
18704 return hooks;
18705 });
18706});
18707
18708// use this instance. Else, load via commonjs.
18709
18710var moment$3 = typeof window !== 'undefined' && window['moment'] || moment$2;
18711
18712function _typeof$1(obj) {
18713 if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") {
18714 _typeof$1 = function (obj) {
18715 return typeof obj;
18716 };
18717 } else {
18718 _typeof$1 = function (obj) {
18719 return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
18720 };
18721 }
18722
18723 return _typeof$1(obj);
18724}
18725
18726function _classCallCheck$1(instance, Constructor) {
18727 if (!(instance instanceof Constructor)) {
18728 throw new TypeError("Cannot call a class as a function");
18729 }
18730}
18731
18732function _defineProperties$1(target, props) {
18733 for (var i = 0; i < props.length; i++) {
18734 var descriptor = props[i];
18735 descriptor.enumerable = descriptor.enumerable || false;
18736 descriptor.configurable = true;
18737 if ("value" in descriptor) descriptor.writable = true;
18738 Object.defineProperty(target, descriptor.key, descriptor);
18739 }
18740}
18741
18742function _createClass$1(Constructor, protoProps, staticProps) {
18743 if (protoProps) _defineProperties$1(Constructor.prototype, protoProps);
18744 if (staticProps) _defineProperties$1(Constructor, staticProps);
18745 return Constructor;
18746}
18747
18748/**
18749 * Prototype for visual components
18750 * @param {{dom: Object, domProps: Object, emitter: Emitter, range: Range}} [body]
18751 * @param {Object} [options]
18752 */
18753
18754function Component(body, options) {
18755 // eslint-disable-line no-unused-vars
18756 this.options = null;
18757 this.props = null;
18758}
18759/**
18760 * Set options for the component. The new options will be merged into the
18761 * current options.
18762 * @param {Object} options
18763 */
18764
18765
18766Component.prototype.setOptions = function (options) {
18767 if (options) {
18768 util.extend(this.options, options);
18769 }
18770};
18771/**
18772 * Repaint the component
18773 * @return {boolean} Returns true if the component is resized
18774 */
18775
18776
18777Component.prototype.redraw = function () {
18778 // should be implemented by the component
18779 return false;
18780};
18781/**
18782 * Destroy the component. Cleanup DOM and event listeners
18783 */
18784
18785
18786Component.prototype.destroy = function () {// should be implemented by the component
18787};
18788/**
18789 * Test whether the component is resized since the last time _isResized() was
18790 * called.
18791 * @return {Boolean} Returns true if the component is resized
18792 * @protected
18793 */
18794
18795
18796Component.prototype._isResized = function () {
18797 var resized = this.props._previousWidth !== this.props.width || this.props._previousHeight !== this.props.height;
18798 this.props._previousWidth = this.props.width;
18799 this.props._previousHeight = this.props.height;
18800 return resized;
18801};
18802
18803var Component_1 = Component;
18804
18805var DateUtil = createCommonjsModule$1(function (module, exports) {
18806 /**
18807 * used in Core to convert the options into a volatile variable
18808 *
18809 * @param {function} moment
18810 * @param {Object} body
18811 * @param {Array | Object} hiddenDates
18812 * @returns {number}
18813 */
18814 exports.convertHiddenOptions = function (moment, body, hiddenDates) {
18815 if (hiddenDates && !Array.isArray(hiddenDates)) {
18816 return exports.convertHiddenOptions(moment, body, [hiddenDates]);
18817 }
18818
18819 body.hiddenDates = [];
18820
18821 if (hiddenDates) {
18822 if (Array.isArray(hiddenDates) == true) {
18823 for (var i = 0; i < hiddenDates.length; i++) {
18824 if (hiddenDates[i].repeat === undefined) {
18825 var dateItem = {};
18826 dateItem.start = moment(hiddenDates[i].start).toDate().valueOf();
18827 dateItem.end = moment(hiddenDates[i].end).toDate().valueOf();
18828 body.hiddenDates.push(dateItem);
18829 }
18830 }
18831
18832 body.hiddenDates.sort(function (a, b) {
18833 return a.start - b.start;
18834 }); // sort by start time
18835 }
18836 }
18837 };
18838 /**
18839 * create new entrees for the repeating hidden dates
18840 *
18841 * @param {function} moment
18842 * @param {Object} body
18843 * @param {Array | Object} hiddenDates
18844 * @returns {null}
18845 */
18846
18847
18848 exports.updateHiddenDates = function (moment, body, hiddenDates) {
18849 if (hiddenDates && !Array.isArray(hiddenDates)) {
18850 return exports.updateHiddenDates(moment, body, [hiddenDates]);
18851 }
18852
18853 if (hiddenDates && body.domProps.centerContainer.width !== undefined) {
18854 exports.convertHiddenOptions(moment, body, hiddenDates);
18855 var start = moment(body.range.start);
18856 var end = moment(body.range.end);
18857 var totalRange = body.range.end - body.range.start;
18858 var pixelTime = totalRange / body.domProps.centerContainer.width;
18859
18860 for (var i = 0; i < hiddenDates.length; i++) {
18861 if (hiddenDates[i].repeat !== undefined) {
18862 var startDate = moment(hiddenDates[i].start);
18863 var endDate = moment(hiddenDates[i].end);
18864
18865 if (startDate._d == "Invalid Date") {
18866 throw new Error("Supplied start date is not valid: " + hiddenDates[i].start);
18867 }
18868
18869 if (endDate._d == "Invalid Date") {
18870 throw new Error("Supplied end date is not valid: " + hiddenDates[i].end);
18871 }
18872
18873 var duration = endDate - startDate;
18874
18875 if (duration >= 4 * pixelTime) {
18876 var offset = 0;
18877 var runUntil = end.clone();
18878
18879 switch (hiddenDates[i].repeat) {
18880 case "daily":
18881 // case of time
18882 if (startDate.day() != endDate.day()) {
18883 offset = 1;
18884 }
18885
18886 startDate.dayOfYear(start.dayOfYear());
18887 startDate.year(start.year());
18888 startDate.subtract(7, 'days');
18889 endDate.dayOfYear(start.dayOfYear());
18890 endDate.year(start.year());
18891 endDate.subtract(7 - offset, 'days');
18892 runUntil.add(1, 'weeks');
18893 break;
18894
18895 case "weekly":
18896 var dayOffset = endDate.diff(startDate, 'days');
18897 var day = startDate.day(); // set the start date to the range.start
18898
18899 startDate.date(start.date());
18900 startDate.month(start.month());
18901 startDate.year(start.year());
18902 endDate = startDate.clone(); // force
18903
18904 startDate.day(day);
18905 endDate.day(day);
18906 endDate.add(dayOffset, 'days');
18907 startDate.subtract(1, 'weeks');
18908 endDate.subtract(1, 'weeks');
18909 runUntil.add(1, 'weeks');
18910 break;
18911
18912 case "monthly":
18913 if (startDate.month() != endDate.month()) {
18914 offset = 1;
18915 }
18916
18917 startDate.month(start.month());
18918 startDate.year(start.year());
18919 startDate.subtract(1, 'months');
18920 endDate.month(start.month());
18921 endDate.year(start.year());
18922 endDate.subtract(1, 'months');
18923 endDate.add(offset, 'months');
18924 runUntil.add(1, 'months');
18925 break;
18926
18927 case "yearly":
18928 if (startDate.year() != endDate.year()) {
18929 offset = 1;
18930 }
18931
18932 startDate.year(start.year());
18933 startDate.subtract(1, 'years');
18934 endDate.year(start.year());
18935 endDate.subtract(1, 'years');
18936 endDate.add(offset, 'years');
18937 runUntil.add(1, 'years');
18938 break;
18939
18940 default:
18941 console.log("Wrong repeat format, allowed are: daily, weekly, monthly, yearly. Given:", hiddenDates[i].repeat);
18942 return;
18943 }
18944
18945 while (startDate < runUntil) {
18946 body.hiddenDates.push({
18947 start: startDate.valueOf(),
18948 end: endDate.valueOf()
18949 });
18950
18951 switch (hiddenDates[i].repeat) {
18952 case "daily":
18953 startDate.add(1, 'days');
18954 endDate.add(1, 'days');
18955 break;
18956
18957 case "weekly":
18958 startDate.add(1, 'weeks');
18959 endDate.add(1, 'weeks');
18960 break;
18961
18962 case "monthly":
18963 startDate.add(1, 'months');
18964 endDate.add(1, 'months');
18965 break;
18966
18967 case "yearly":
18968 startDate.add(1, 'y');
18969 endDate.add(1, 'y');
18970 break;
18971
18972 default:
18973 console.log("Wrong repeat format, allowed are: daily, weekly, monthly, yearly. Given:", hiddenDates[i].repeat);
18974 return;
18975 }
18976 }
18977
18978 body.hiddenDates.push({
18979 start: startDate.valueOf(),
18980 end: endDate.valueOf()
18981 });
18982 }
18983 }
18984 } // remove duplicates, merge where possible
18985
18986
18987 exports.removeDuplicates(body); // ensure the new positions are not on hidden dates
18988
18989 var startHidden = exports.isHidden(body.range.start, body.hiddenDates);
18990 var endHidden = exports.isHidden(body.range.end, body.hiddenDates);
18991 var rangeStart = body.range.start;
18992 var rangeEnd = body.range.end;
18993
18994 if (startHidden.hidden == true) {
18995 rangeStart = body.range.startToFront == true ? startHidden.startDate - 1 : startHidden.endDate + 1;
18996 }
18997
18998 if (endHidden.hidden == true) {
18999 rangeEnd = body.range.endToFront == true ? endHidden.startDate - 1 : endHidden.endDate + 1;
19000 }
19001
19002 if (startHidden.hidden == true || endHidden.hidden == true) {
19003 body.range._applyRange(rangeStart, rangeEnd);
19004 }
19005 }
19006 };
19007 /**
19008 * remove duplicates from the hidden dates list. Duplicates are evil. They mess everything up.
19009 * Scales with N^2
19010 *
19011 * @param {Object} body
19012 */
19013
19014
19015 exports.removeDuplicates = function (body) {
19016 var hiddenDates = body.hiddenDates;
19017 var safeDates = [];
19018
19019 for (var i = 0; i < hiddenDates.length; i++) {
19020 for (var j = 0; j < hiddenDates.length; j++) {
19021 if (i != j && hiddenDates[j].remove != true && hiddenDates[i].remove != true) {
19022 // j inside i
19023 if (hiddenDates[j].start >= hiddenDates[i].start && hiddenDates[j].end <= hiddenDates[i].end) {
19024 hiddenDates[j].remove = true;
19025 } // j start inside i
19026 else if (hiddenDates[j].start >= hiddenDates[i].start && hiddenDates[j].start <= hiddenDates[i].end) {
19027 hiddenDates[i].end = hiddenDates[j].end;
19028 hiddenDates[j].remove = true;
19029 } // j end inside i
19030 else if (hiddenDates[j].end >= hiddenDates[i].start && hiddenDates[j].end <= hiddenDates[i].end) {
19031 hiddenDates[i].start = hiddenDates[j].start;
19032 hiddenDates[j].remove = true;
19033 }
19034 }
19035 }
19036 }
19037
19038 for (i = 0; i < hiddenDates.length; i++) {
19039 if (hiddenDates[i].remove !== true) {
19040 safeDates.push(hiddenDates[i]);
19041 }
19042 }
19043
19044 body.hiddenDates = safeDates;
19045 body.hiddenDates.sort(function (a, b) {
19046 return a.start - b.start;
19047 }); // sort by start time
19048 };
19049
19050 exports.printDates = function (dates) {
19051 for (var i = 0; i < dates.length; i++) {
19052 console.log(i, new Date(dates[i].start), new Date(dates[i].end), dates[i].start, dates[i].end, dates[i].remove);
19053 }
19054 };
19055 /**
19056 * Used in TimeStep to avoid the hidden times.
19057 * @param {function} moment
19058 * @param {TimeStep} timeStep
19059 * @param {Date} previousTime
19060 */
19061
19062
19063 exports.stepOverHiddenDates = function (moment, timeStep, previousTime) {
19064 var stepInHidden = false;
19065 var currentValue = timeStep.current.valueOf();
19066
19067 for (var i = 0; i < timeStep.hiddenDates.length; i++) {
19068 var startDate = timeStep.hiddenDates[i].start;
19069 var endDate = timeStep.hiddenDates[i].end;
19070
19071 if (currentValue >= startDate && currentValue < endDate) {
19072 stepInHidden = true;
19073 break;
19074 }
19075 }
19076
19077 if (stepInHidden == true && currentValue < timeStep._end.valueOf() && currentValue != previousTime) {
19078 var prevValue = moment(previousTime);
19079 var newValue = moment(endDate); //check if the next step should be major
19080
19081 if (prevValue.year() != newValue.year()) {
19082 timeStep.switchedYear = true;
19083 } else if (prevValue.month() != newValue.month()) {
19084 timeStep.switchedMonth = true;
19085 } else if (prevValue.dayOfYear() != newValue.dayOfYear()) {
19086 timeStep.switchedDay = true;
19087 }
19088
19089 timeStep.current = newValue;
19090 }
19091 }; ///**
19092 // * Used in TimeStep to avoid the hidden times.
19093 // * @param timeStep
19094 // * @param previousTime
19095 // */
19096 //exports.checkFirstStep = function(timeStep) {
19097 // var stepInHidden = false;
19098 // var currentValue = timeStep.current.valueOf();
19099 // for (var i = 0; i < timeStep.hiddenDates.length; i++) {
19100 // var startDate = timeStep.hiddenDates[i].start;
19101 // var endDate = timeStep.hiddenDates[i].end;
19102 // if (currentValue >= startDate && currentValue < endDate) {
19103 // stepInHidden = true;
19104 // break;
19105 // }
19106 // }
19107 //
19108 // if (stepInHidden == true && currentValue <= timeStep._end.valueOf()) {
19109 // var newValue = moment(endDate);
19110 // timeStep.current = newValue.toDate();
19111 // }
19112 //};
19113
19114 /**
19115 * replaces the Core toScreen methods
19116 *
19117 * @param {vis.Core} Core
19118 * @param {Date} time
19119 * @param {number} width
19120 * @returns {number}
19121 */
19122
19123
19124 exports.toScreen = function (Core, time, width) {
19125 var conversion;
19126
19127 if (Core.body.hiddenDates.length == 0) {
19128 conversion = Core.range.conversion(width);
19129 return (time.valueOf() - conversion.offset) * conversion.scale;
19130 } else {
19131 var hidden = exports.isHidden(time, Core.body.hiddenDates);
19132
19133 if (hidden.hidden == true) {
19134 time = hidden.startDate;
19135 }
19136
19137 var duration = exports.getHiddenDurationBetween(Core.body.hiddenDates, Core.range.start, Core.range.end);
19138
19139 if (time < Core.range.start) {
19140 conversion = Core.range.conversion(width, duration);
19141 var hiddenBeforeStart = exports.getHiddenDurationBeforeStart(Core.body.hiddenDates, time, conversion.offset);
19142 time = Core.options.moment(time).toDate().valueOf();
19143 time = time + hiddenBeforeStart;
19144 return -(conversion.offset - time.valueOf()) * conversion.scale;
19145 } else if (time > Core.range.end) {
19146 var rangeAfterEnd = {
19147 start: Core.range.start,
19148 end: time
19149 };
19150 time = exports.correctTimeForHidden(Core.options.moment, Core.body.hiddenDates, rangeAfterEnd, time);
19151 conversion = Core.range.conversion(width, duration);
19152 return (time.valueOf() - conversion.offset) * conversion.scale;
19153 } else {
19154 time = exports.correctTimeForHidden(Core.options.moment, Core.body.hiddenDates, Core.range, time);
19155 conversion = Core.range.conversion(width, duration);
19156 return (time.valueOf() - conversion.offset) * conversion.scale;
19157 }
19158 }
19159 };
19160 /**
19161 * Replaces the core toTime methods
19162 *
19163 * @param {vis.Core} Core
19164 * @param {number} x
19165 * @param {number} width
19166 * @returns {Date}
19167 */
19168
19169
19170 exports.toTime = function (Core, x, width) {
19171 if (Core.body.hiddenDates.length == 0) {
19172 var conversion = Core.range.conversion(width);
19173 return new Date(x / conversion.scale + conversion.offset);
19174 } else {
19175 var hiddenDuration = exports.getHiddenDurationBetween(Core.body.hiddenDates, Core.range.start, Core.range.end);
19176 var totalDuration = Core.range.end - Core.range.start - hiddenDuration;
19177 var partialDuration = totalDuration * x / width;
19178 var accumulatedHiddenDuration = exports.getAccumulatedHiddenDuration(Core.body.hiddenDates, Core.range, partialDuration);
19179 return new Date(accumulatedHiddenDuration + partialDuration + Core.range.start);
19180 }
19181 };
19182 /**
19183 * Support function
19184 *
19185 * @param {Array.<{start: Window.start, end: *}>} hiddenDates
19186 * @param {number} start
19187 * @param {number} end
19188 * @returns {number}
19189 */
19190
19191
19192 exports.getHiddenDurationBetween = function (hiddenDates, start, end) {
19193 var duration = 0;
19194
19195 for (var i = 0; i < hiddenDates.length; i++) {
19196 var startDate = hiddenDates[i].start;
19197 var endDate = hiddenDates[i].end; // if time after the cutout, and the
19198
19199 if (startDate >= start && endDate < end) {
19200 duration += endDate - startDate;
19201 }
19202 }
19203
19204 return duration;
19205 };
19206 /**
19207 * Support function
19208 *
19209 * @param {Array.<{start: Window.start, end: *}>} hiddenDates
19210 * @param {number} start
19211 * @param {number} end
19212 * @returns {number}
19213 */
19214
19215
19216 exports.getHiddenDurationBeforeStart = function (hiddenDates, start, end) {
19217 var duration = 0;
19218
19219 for (var i = 0; i < hiddenDates.length; i++) {
19220 var startDate = hiddenDates[i].start;
19221 var endDate = hiddenDates[i].end;
19222
19223 if (startDate >= start && endDate <= end) {
19224 duration += endDate - startDate;
19225 }
19226 }
19227
19228 return duration;
19229 };
19230 /**
19231 * Support function
19232 * @param {function} moment
19233 * @param {Array.<{start: Window.start, end: *}>} hiddenDates
19234 * @param {{start: number, end: number}} range
19235 * @param {Date} time
19236 * @returns {number}
19237 */
19238
19239
19240 exports.correctTimeForHidden = function (moment, hiddenDates, range, time) {
19241 time = moment(time).toDate().valueOf();
19242 time -= exports.getHiddenDurationBefore(moment, hiddenDates, range, time);
19243 return time;
19244 };
19245
19246 exports.getHiddenDurationBefore = function (moment, hiddenDates, range, time) {
19247 var timeOffset = 0;
19248 time = moment(time).toDate().valueOf();
19249
19250 for (var i = 0; i < hiddenDates.length; i++) {
19251 var startDate = hiddenDates[i].start;
19252 var endDate = hiddenDates[i].end; // if time after the cutout, and the
19253
19254 if (startDate >= range.start && endDate < range.end) {
19255 if (time >= endDate) {
19256 timeOffset += endDate - startDate;
19257 }
19258 }
19259 }
19260
19261 return timeOffset;
19262 };
19263 /**
19264 * sum the duration from start to finish, including the hidden duration,
19265 * until the required amount has been reached, return the accumulated hidden duration
19266 * @param {Array.<{start: Window.start, end: *}>} hiddenDates
19267 * @param {{start: number, end: number}} range
19268 * @param {number} [requiredDuration=0]
19269 * @returns {number}
19270 */
19271
19272
19273 exports.getAccumulatedHiddenDuration = function (hiddenDates, range, requiredDuration) {
19274 var hiddenDuration = 0;
19275 var duration = 0;
19276 var previousPoint = range.start; //exports.printDates(hiddenDates)
19277
19278 for (var i = 0; i < hiddenDates.length; i++) {
19279 var startDate = hiddenDates[i].start;
19280 var endDate = hiddenDates[i].end; // if time after the cutout, and the
19281
19282 if (startDate >= range.start && endDate < range.end) {
19283 duration += startDate - previousPoint;
19284 previousPoint = endDate;
19285
19286 if (duration >= requiredDuration) {
19287 break;
19288 } else {
19289 hiddenDuration += endDate - startDate;
19290 }
19291 }
19292 }
19293
19294 return hiddenDuration;
19295 };
19296 /**
19297 * used to step over to either side of a hidden block. Correction is disabled on tablets, might be set to true
19298 * @param {Array.<{start: Window.start, end: *}>} hiddenDates
19299 * @param {Date} time
19300 * @param {number} direction
19301 * @param {boolean} correctionEnabled
19302 * @returns {Date|number}
19303 */
19304
19305
19306 exports.snapAwayFromHidden = function (hiddenDates, time, direction, correctionEnabled) {
19307 var isHidden = exports.isHidden(time, hiddenDates);
19308
19309 if (isHidden.hidden == true) {
19310 if (direction < 0) {
19311 if (correctionEnabled == true) {
19312 return isHidden.startDate - (isHidden.endDate - time) - 1;
19313 } else {
19314 return isHidden.startDate - 1;
19315 }
19316 } else {
19317 if (correctionEnabled == true) {
19318 return isHidden.endDate + (time - isHidden.startDate) + 1;
19319 } else {
19320 return isHidden.endDate + 1;
19321 }
19322 }
19323 } else {
19324 return time;
19325 }
19326 };
19327 /**
19328 * Check if a time is hidden
19329 *
19330 * @param {Date} time
19331 * @param {Array.<{start: Window.start, end: *}>} hiddenDates
19332 * @returns {{hidden: boolean, startDate: Window.start, endDate: *}}
19333 */
19334
19335
19336 exports.isHidden = function (time, hiddenDates) {
19337 for (var i = 0; i < hiddenDates.length; i++) {
19338 var startDate = hiddenDates[i].start;
19339 var endDate = hiddenDates[i].end;
19340
19341 if (time >= startDate && time < endDate) {
19342 // if the start is entering a hidden zone
19343 return {
19344 hidden: true,
19345 startDate: startDate,
19346 endDate: endDate
19347 };
19348 }
19349 }
19350
19351 return {
19352 hidden: false,
19353 startDate: startDate,
19354 endDate: endDate
19355 };
19356 };
19357});
19358var DateUtil_1 = DateUtil.convertHiddenOptions;
19359var DateUtil_2 = DateUtil.updateHiddenDates;
19360var DateUtil_3 = DateUtil.removeDuplicates;
19361var DateUtil_4 = DateUtil.printDates;
19362var DateUtil_5 = DateUtil.stepOverHiddenDates;
19363var DateUtil_6 = DateUtil.toScreen;
19364var DateUtil_7 = DateUtil.toTime;
19365var DateUtil_8 = DateUtil.getHiddenDurationBetween;
19366var DateUtil_9 = DateUtil.getHiddenDurationBeforeStart;
19367var DateUtil_10 = DateUtil.correctTimeForHidden;
19368var DateUtil_11 = DateUtil.getHiddenDurationBefore;
19369var DateUtil_12 = DateUtil.getAccumulatedHiddenDuration;
19370var DateUtil_13 = DateUtil.snapAwayFromHidden;
19371var DateUtil_14 = DateUtil.isHidden;
19372
19373/**
19374 * A Range controls a numeric range with a start and end value.
19375 * The Range adjusts the range based on mouse events or programmatic changes,
19376 * and triggers events when the range is changing or has been changed.
19377 * @param {{dom: Object, domProps: Object, emitter: Emitter}} body
19378 * @param {Object} [options] See description at Range.setOptions
19379 * @constructor Range
19380 * @extends Component
19381 */
19382
19383function Range(body, options) {
19384 var now = moment$3().hours(0).minutes(0).seconds(0).milliseconds(0);
19385 var start = now.clone().add(-3, 'days').valueOf();
19386 var end = now.clone().add(3, 'days').valueOf();
19387 this.millisecondsPerPixelCache = undefined;
19388
19389 if (options === undefined) {
19390 this.start = start;
19391 this.end = end;
19392 } else {
19393 this.start = options.start || start;
19394 this.end = options.end || end;
19395 }
19396
19397 this.rolling = false;
19398 this.body = body;
19399 this.deltaDifference = 0;
19400 this.scaleOffset = 0;
19401 this.startToFront = false;
19402 this.endToFront = true; // default options
19403
19404 this.defaultOptions = {
19405 rtl: false,
19406 start: null,
19407 end: null,
19408 moment: moment$3,
19409 direction: 'horizontal',
19410 // 'horizontal' or 'vertical'
19411 moveable: true,
19412 zoomable: true,
19413 min: null,
19414 max: null,
19415 zoomMin: 10,
19416 // milliseconds
19417 zoomMax: 1000 * 60 * 60 * 24 * 365 * 10000,
19418 // milliseconds
19419 rollingMode: {
19420 follow: false,
19421 offset: 0.5
19422 }
19423 };
19424 this.options = util.extend({}, this.defaultOptions);
19425 this.props = {
19426 touch: {}
19427 };
19428 this.animationTimer = null; // drag listeners for dragging
19429
19430 this.body.emitter.on('panstart', this._onDragStart.bind(this));
19431 this.body.emitter.on('panmove', this._onDrag.bind(this));
19432 this.body.emitter.on('panend', this._onDragEnd.bind(this)); // mouse wheel for zooming
19433
19434 this.body.emitter.on('mousewheel', this._onMouseWheel.bind(this)); // pinch to zoom
19435
19436 this.body.emitter.on('touch', this._onTouch.bind(this));
19437 this.body.emitter.on('pinch', this._onPinch.bind(this)); // on click of rolling mode button
19438
19439 this.body.dom.rollingModeBtn.addEventListener('click', this.startRolling.bind(this));
19440 this.setOptions(options);
19441}
19442
19443Range.prototype = new Component_1();
19444/**
19445 * Set options for the range controller
19446 * @param {Object} options Available options:
19447 * {number | Date | String} start Start date for the range
19448 * {number | Date | String} end End date for the range
19449 * {number} min Minimum value for start
19450 * {number} max Maximum value for end
19451 * {number} zoomMin Set a minimum value for
19452 * (end - start).
19453 * {number} zoomMax Set a maximum value for
19454 * (end - start).
19455 * {boolean} moveable Enable moving of the range
19456 * by dragging. True by default
19457 * {boolean} zoomable Enable zooming of the range
19458 * by pinching/scrolling. True by default
19459 */
19460
19461Range.prototype.setOptions = function (options) {
19462 if (options) {
19463 // copy the options that we know
19464 var fields = ['animation', 'direction', 'min', 'max', 'zoomMin', 'zoomMax', 'moveable', 'zoomable', 'moment', 'activate', 'hiddenDates', 'zoomKey', 'rtl', 'showCurrentTime', 'rollingMode', 'horizontalScroll'];
19465 util.selectiveExtend(fields, this.options, options);
19466
19467 if (options.rollingMode && options.rollingMode.follow) {
19468 this.startRolling();
19469 }
19470
19471 if ('start' in options || 'end' in options) {
19472 // apply a new range. both start and end are optional
19473 this.setRange(options.start, options.end);
19474 }
19475 }
19476};
19477/**
19478 * Test whether direction has a valid value
19479 * @param {string} direction 'horizontal' or 'vertical'
19480 */
19481
19482
19483function validateDirection(direction) {
19484 if (direction != 'horizontal' && direction != 'vertical') {
19485 throw new TypeError('Unknown direction "' + direction + '". ' + 'Choose "horizontal" or "vertical".');
19486 }
19487}
19488/**
19489 * Start auto refreshing the current time bar
19490 */
19491
19492
19493Range.prototype.startRolling = function () {
19494 var me = this;
19495 /**
19496 * Updates the current time.
19497 */
19498
19499 function update() {
19500 me.stopRolling();
19501 me.rolling = true;
19502 var interval = me.end - me.start;
19503 var t = util.convert(new Date(), 'Date').valueOf();
19504 var start = t - interval * me.options.rollingMode.offset;
19505 var end = t + interval * (1 - me.options.rollingMode.offset);
19506 var options = {
19507 animation: false
19508 };
19509 me.setRange(start, end, options); // determine interval to refresh
19510
19511 var scale = me.conversion(me.body.domProps.center.width).scale;
19512 interval = 1 / scale / 10;
19513 if (interval < 30) interval = 30;
19514 if (interval > 1000) interval = 1000;
19515 me.body.dom.rollingModeBtn.style.visibility = "hidden"; // start a renderTimer to adjust for the new time
19516
19517 me.currentTimeTimer = setTimeout(update, interval);
19518 }
19519
19520 update();
19521};
19522/**
19523 * Stop auto refreshing the current time bar
19524 */
19525
19526
19527Range.prototype.stopRolling = function () {
19528 if (this.currentTimeTimer !== undefined) {
19529 clearTimeout(this.currentTimeTimer);
19530 this.rolling = false;
19531 this.body.dom.rollingModeBtn.style.visibility = "visible";
19532 }
19533};
19534/**
19535 * Set a new start and end range
19536 * @param {Date | number | string} [start]
19537 * @param {Date | number | string} [end]
19538 * @param {Object} options Available options:
19539 * {boolean | {duration: number, easingFunction: string}} [animation=false]
19540 * If true, the range is animated
19541 * smoothly to the new window. An object can be
19542 * provided to specify duration and easing function.
19543 * Default duration is 500 ms, and default easing
19544 * function is 'easeInOutQuad'.
19545 * {boolean} [byUser=false]
19546 * {Event} event Mouse event
19547 * @param {Function} callback a callback function to be executed at the end of this function
19548 * @param {Function} frameCallback a callback function executed each frame of the range animation.
19549 * The callback will be passed three parameters:
19550 * {number} easeCoefficient an easing coefficent
19551 * {boolean} willDraw If true the caller will redraw after the callback completes
19552 * {boolean} done If true then animation is ending after the current frame
19553 */
19554
19555
19556Range.prototype.setRange = function (start, end, options, callback, frameCallback) {
19557 if (!options) {
19558 options = {};
19559 }
19560
19561 if (options.byUser !== true) {
19562 options.byUser = false;
19563 }
19564
19565 var me = this;
19566 var finalStart = start != undefined ? util.convert(start, 'Date').valueOf() : null;
19567 var finalEnd = end != undefined ? util.convert(end, 'Date').valueOf() : null;
19568
19569 this._cancelAnimation();
19570
19571 this.millisecondsPerPixelCache = undefined;
19572
19573 if (options.animation) {
19574 // true or an Object
19575 var initStart = this.start;
19576 var initEnd = this.end;
19577 var duration = _typeof$1(options.animation) === 'object' && 'duration' in options.animation ? options.animation.duration : 500;
19578 var easingName = _typeof$1(options.animation) === 'object' && 'easingFunction' in options.animation ? options.animation.easingFunction : 'easeInOutQuad';
19579 var easingFunction = util.easingFunctions[easingName];
19580
19581 if (!easingFunction) {
19582 throw new Error('Unknown easing function ' + JSON.stringify(easingName) + '. ' + 'Choose from: ' + Object.keys(util.easingFunctions).join(', '));
19583 }
19584
19585 var initTime = new Date().valueOf();
19586 var anyChanged = false;
19587
19588 var next = function next() {
19589 if (!me.props.touch.dragging) {
19590 var now = new Date().valueOf();
19591 var time = now - initTime;
19592 var ease = easingFunction(time / duration);
19593 var done = time > duration;
19594 var s = done || finalStart === null ? finalStart : initStart + (finalStart - initStart) * ease;
19595 var e = done || finalEnd === null ? finalEnd : initEnd + (finalEnd - initEnd) * ease;
19596 changed = me._applyRange(s, e);
19597 DateUtil.updateHiddenDates(me.options.moment, me.body, me.options.hiddenDates);
19598 anyChanged = anyChanged || changed;
19599 var params = {
19600 start: new Date(me.start),
19601 end: new Date(me.end),
19602 byUser: options.byUser,
19603 event: options.event
19604 };
19605
19606 if (frameCallback) {
19607 frameCallback(ease, changed, done);
19608 }
19609
19610 if (changed) {
19611 me.body.emitter.emit('rangechange', params);
19612 }
19613
19614 if (done) {
19615 if (anyChanged) {
19616 me.body.emitter.emit('rangechanged', params);
19617
19618 if (callback) {
19619 return callback();
19620 }
19621 }
19622 } else {
19623 // animate with as high as possible frame rate, leave 20 ms in between
19624 // each to prevent the browser from blocking
19625 me.animationTimer = setTimeout(next, 20);
19626 }
19627 }
19628 };
19629
19630 return next();
19631 } else {
19632 var changed = this._applyRange(finalStart, finalEnd);
19633
19634 DateUtil.updateHiddenDates(this.options.moment, this.body, this.options.hiddenDates);
19635
19636 if (changed) {
19637 var params = {
19638 start: new Date(this.start),
19639 end: new Date(this.end),
19640 byUser: options.byUser,
19641 event: options.event
19642 };
19643 this.body.emitter.emit('rangechange', params);
19644 clearTimeout(me.timeoutID);
19645 me.timeoutID = setTimeout(function () {
19646 me.body.emitter.emit('rangechanged', params);
19647 }, 200);
19648
19649 if (callback) {
19650 return callback();
19651 }
19652 }
19653 }
19654};
19655/**
19656 * Get the number of milliseconds per pixel.
19657 *
19658 * @returns {undefined|number}
19659 */
19660
19661
19662Range.prototype.getMillisecondsPerPixel = function () {
19663 if (this.millisecondsPerPixelCache === undefined) {
19664 this.millisecondsPerPixelCache = (this.end - this.start) / this.body.dom.center.clientWidth;
19665 }
19666
19667 return this.millisecondsPerPixelCache;
19668};
19669/**
19670 * Stop an animation
19671 * @private
19672 */
19673
19674
19675Range.prototype._cancelAnimation = function () {
19676 if (this.animationTimer) {
19677 clearTimeout(this.animationTimer);
19678 this.animationTimer = null;
19679 }
19680};
19681/**
19682 * Set a new start and end range. This method is the same as setRange, but
19683 * does not trigger a range change and range changed event, and it returns
19684 * true when the range is changed
19685 * @param {number} [start]
19686 * @param {number} [end]
19687 * @return {boolean} changed
19688 * @private
19689 */
19690
19691
19692Range.prototype._applyRange = function (start, end) {
19693 var newStart = start != null ? util.convert(start, 'Date').valueOf() : this.start,
19694 newEnd = end != null ? util.convert(end, 'Date').valueOf() : this.end,
19695 max = this.options.max != null ? util.convert(this.options.max, 'Date').valueOf() : null,
19696 min = this.options.min != null ? util.convert(this.options.min, 'Date').valueOf() : null,
19697 diff; // check for valid number
19698
19699 if (isNaN(newStart) || newStart === null) {
19700 throw new Error('Invalid start "' + start + '"');
19701 }
19702
19703 if (isNaN(newEnd) || newEnd === null) {
19704 throw new Error('Invalid end "' + end + '"');
19705 } // prevent end < start
19706
19707
19708 if (newEnd < newStart) {
19709 newEnd = newStart;
19710 } // prevent start < min
19711
19712
19713 if (min !== null) {
19714 if (newStart < min) {
19715 diff = min - newStart;
19716 newStart += diff;
19717 newEnd += diff; // prevent end > max
19718
19719 if (max != null) {
19720 if (newEnd > max) {
19721 newEnd = max;
19722 }
19723 }
19724 }
19725 } // prevent end > max
19726
19727
19728 if (max !== null) {
19729 if (newEnd > max) {
19730 diff = newEnd - max;
19731 newStart -= diff;
19732 newEnd -= diff; // prevent start < min
19733
19734 if (min != null) {
19735 if (newStart < min) {
19736 newStart = min;
19737 }
19738 }
19739 }
19740 } // prevent (end-start) < zoomMin
19741
19742
19743 if (this.options.zoomMin !== null) {
19744 var zoomMin = parseFloat(this.options.zoomMin);
19745
19746 if (zoomMin < 0) {
19747 zoomMin = 0;
19748 }
19749
19750 if (newEnd - newStart < zoomMin) {
19751 // compensate for a scale of 0.5 ms
19752 var compensation = 0.5;
19753
19754 if (this.end - this.start === zoomMin && newStart >= this.start - compensation && newEnd <= this.end) {
19755 // ignore this action, we are already zoomed to the minimum
19756 newStart = this.start;
19757 newEnd = this.end;
19758 } else {
19759 // zoom to the minimum
19760 diff = zoomMin - (newEnd - newStart);
19761 newStart -= diff / 2;
19762 newEnd += diff / 2;
19763 }
19764 }
19765 } // prevent (end-start) > zoomMax
19766
19767
19768 if (this.options.zoomMax !== null) {
19769 var zoomMax = parseFloat(this.options.zoomMax);
19770
19771 if (zoomMax < 0) {
19772 zoomMax = 0;
19773 }
19774
19775 if (newEnd - newStart > zoomMax) {
19776 if (this.end - this.start === zoomMax && newStart < this.start && newEnd > this.end) {
19777 // ignore this action, we are already zoomed to the maximum
19778 newStart = this.start;
19779 newEnd = this.end;
19780 } else {
19781 // zoom to the maximum
19782 diff = newEnd - newStart - zoomMax;
19783 newStart += diff / 2;
19784 newEnd -= diff / 2;
19785 }
19786 }
19787 }
19788
19789 var changed = this.start != newStart || this.end != newEnd; // if the new range does NOT overlap with the old range, emit checkRangedItems to avoid not showing ranged items (ranged meaning has end time, not necessarily of type Range)
19790
19791 if (!(newStart >= this.start && newStart <= this.end || newEnd >= this.start && newEnd <= this.end) && !(this.start >= newStart && this.start <= newEnd || this.end >= newStart && this.end <= newEnd)) {
19792 this.body.emitter.emit('checkRangedItems');
19793 }
19794
19795 this.start = newStart;
19796 this.end = newEnd;
19797 return changed;
19798};
19799/**
19800 * Retrieve the current range.
19801 * @return {Object} An object with start and end properties
19802 */
19803
19804
19805Range.prototype.getRange = function () {
19806 return {
19807 start: this.start,
19808 end: this.end
19809 };
19810};
19811/**
19812 * Calculate the conversion offset and scale for current range, based on
19813 * the provided width
19814 * @param {number} width
19815 * @param {number} [totalHidden=0]
19816 * @returns {{offset: number, scale: number}} conversion
19817 */
19818
19819
19820Range.prototype.conversion = function (width, totalHidden) {
19821 return Range.conversion(this.start, this.end, width, totalHidden);
19822};
19823/**
19824 * Static method to calculate the conversion offset and scale for a range,
19825 * based on the provided start, end, and width
19826 * @param {number} start
19827 * @param {number} end
19828 * @param {number} width
19829 * @param {number} [totalHidden=0]
19830 * @returns {{offset: number, scale: number}} conversion
19831 */
19832
19833
19834Range.conversion = function (start, end, width, totalHidden) {
19835 if (totalHidden === undefined) {
19836 totalHidden = 0;
19837 }
19838
19839 if (width != 0 && end - start != 0) {
19840 return {
19841 offset: start,
19842 scale: width / (end - start - totalHidden)
19843 };
19844 } else {
19845 return {
19846 offset: 0,
19847 scale: 1
19848 };
19849 }
19850};
19851/**
19852 * Start dragging horizontally or vertically
19853 * @param {Event} event
19854 * @private
19855 */
19856
19857
19858Range.prototype._onDragStart = function (event) {
19859 this.deltaDifference = 0;
19860 this.previousDelta = 0; // only allow dragging when configured as movable
19861
19862 if (!this.options.moveable) return; // only start dragging when the mouse is inside the current range
19863
19864 if (!this._isInsideRange(event)) return; // refuse to drag when we where pinching to prevent the timeline make a jump
19865 // when releasing the fingers in opposite order from the touch screen
19866
19867 if (!this.props.touch.allowDragging) return;
19868 this.stopRolling();
19869 this.props.touch.start = this.start;
19870 this.props.touch.end = this.end;
19871 this.props.touch.dragging = true;
19872
19873 if (this.body.dom.root) {
19874 this.body.dom.root.style.cursor = 'move';
19875 }
19876};
19877/**
19878 * Perform dragging operation
19879 * @param {Event} event
19880 * @private
19881 */
19882
19883
19884Range.prototype._onDrag = function (event) {
19885 if (!event) return;
19886 if (!this.props.touch.dragging) return; // only allow dragging when configured as movable
19887
19888 if (!this.options.moveable) return; // TODO: this may be redundant in hammerjs2
19889 // refuse to drag when we where pinching to prevent the timeline make a jump
19890 // when releasing the fingers in opposite order from the touch screen
19891
19892 if (!this.props.touch.allowDragging) return;
19893 var direction = this.options.direction;
19894 validateDirection(direction);
19895 var delta = direction == 'horizontal' ? event.deltaX : event.deltaY;
19896 delta -= this.deltaDifference;
19897 var interval = this.props.touch.end - this.props.touch.start; // normalize dragging speed if cutout is in between.
19898
19899 var duration = DateUtil.getHiddenDurationBetween(this.body.hiddenDates, this.start, this.end);
19900 interval -= duration;
19901 var width = direction == 'horizontal' ? this.body.domProps.center.width : this.body.domProps.center.height;
19902 var diffRange;
19903
19904 if (this.options.rtl) {
19905 diffRange = delta / width * interval;
19906 } else {
19907 diffRange = -delta / width * interval;
19908 }
19909
19910 var newStart = this.props.touch.start + diffRange;
19911 var newEnd = this.props.touch.end + diffRange; // snapping times away from hidden zones
19912
19913 var safeStart = DateUtil.snapAwayFromHidden(this.body.hiddenDates, newStart, this.previousDelta - delta, true);
19914 var safeEnd = DateUtil.snapAwayFromHidden(this.body.hiddenDates, newEnd, this.previousDelta - delta, true);
19915
19916 if (safeStart != newStart || safeEnd != newEnd) {
19917 this.deltaDifference += delta;
19918 this.props.touch.start = safeStart;
19919 this.props.touch.end = safeEnd;
19920
19921 this._onDrag(event);
19922
19923 return;
19924 }
19925
19926 this.previousDelta = delta;
19927
19928 this._applyRange(newStart, newEnd);
19929
19930 var startDate = new Date(this.start);
19931 var endDate = new Date(this.end); // fire a rangechange event
19932
19933 this.body.emitter.emit('rangechange', {
19934 start: startDate,
19935 end: endDate,
19936 byUser: true,
19937 event: event
19938 }); // fire a panmove event
19939
19940 this.body.emitter.emit('panmove');
19941};
19942/**
19943 * Stop dragging operation
19944 * @param {event} event
19945 * @private
19946 */
19947
19948
19949Range.prototype._onDragEnd = function (event) {
19950 if (!this.props.touch.dragging) return; // only allow dragging when configured as movable
19951
19952 if (!this.options.moveable) return; // TODO: this may be redundant in hammerjs2
19953 // refuse to drag when we where pinching to prevent the timeline make a jump
19954 // when releasing the fingers in opposite order from the touch screen
19955
19956 if (!this.props.touch.allowDragging) return;
19957 this.props.touch.dragging = false;
19958
19959 if (this.body.dom.root) {
19960 this.body.dom.root.style.cursor = 'auto';
19961 } // fire a rangechanged event
19962
19963
19964 this.body.emitter.emit('rangechanged', {
19965 start: new Date(this.start),
19966 end: new Date(this.end),
19967 byUser: true,
19968 event: event
19969 });
19970};
19971/**
19972 * Event handler for mouse wheel event, used to zoom
19973 * Code from http://adomas.org/javascript-mouse-wheel/
19974 * @param {Event} event
19975 * @private
19976 */
19977
19978
19979Range.prototype._onMouseWheel = function (event) {
19980 // retrieve delta
19981 var delta = 0;
19982
19983 if (event.wheelDelta) {
19984 /* IE/Opera. */
19985 delta = event.wheelDelta / 120;
19986 } else if (event.detail) {
19987 /* Mozilla case. */
19988 // In Mozilla, sign of delta is different than in IE.
19989 // Also, delta is multiple of 3.
19990 delta = -event.detail / 3;
19991 } else if (event.deltaY) {
19992 delta = -event.deltaY / 3;
19993 } // don't allow zoom when the according key is pressed and the zoomKey option or not zoomable but movable
19994
19995
19996 if (this.options.zoomKey && !event[this.options.zoomKey] && this.options.zoomable || !this.options.zoomable && this.options.moveable) {
19997 return;
19998 } // only allow zooming when configured as zoomable and moveable
19999
20000
20001 if (!(this.options.zoomable && this.options.moveable)) return; // only zoom when the mouse is inside the current range
20002
20003 if (!this._isInsideRange(event)) return; // If delta is nonzero, handle it.
20004 // Basically, delta is now positive if wheel was scrolled up,
20005 // and negative, if wheel was scrolled down.
20006
20007 if (delta) {
20008 // perform the zoom action. Delta is normally 1 or -1
20009 // adjust a negative delta such that zooming in with delta 0.1
20010 // equals zooming out with a delta -0.1
20011 var scale;
20012
20013 if (delta < 0) {
20014 scale = 1 - delta / 5;
20015 } else {
20016 scale = 1 / (1 + delta / 5);
20017 } // calculate center, the date to zoom around
20018
20019
20020 var pointerDate;
20021
20022 if (this.rolling) {
20023 pointerDate = this.start + (this.end - this.start) * this.options.rollingMode.offset;
20024 } else {
20025 var pointer = this.getPointer({
20026 x: event.clientX,
20027 y: event.clientY
20028 }, this.body.dom.center);
20029 pointerDate = this._pointerToDate(pointer);
20030 }
20031
20032 this.zoom(scale, pointerDate, delta, event); // Prevent default actions caused by mouse wheel
20033 // (else the page and timeline both scroll)
20034
20035 event.preventDefault();
20036 }
20037};
20038/**
20039 * Start of a touch gesture
20040 * @param {Event} event
20041 * @private
20042 */
20043
20044
20045Range.prototype._onTouch = function (event) {
20046 // eslint-disable-line no-unused-vars
20047 this.props.touch.start = this.start;
20048 this.props.touch.end = this.end;
20049 this.props.touch.allowDragging = true;
20050 this.props.touch.center = null;
20051 this.scaleOffset = 0;
20052 this.deltaDifference = 0; // Disable the browser default handling of this event.
20053
20054 util.preventDefault(event);
20055};
20056/**
20057 * Handle pinch event
20058 * @param {Event} event
20059 * @private
20060 */
20061
20062
20063Range.prototype._onPinch = function (event) {
20064 // only allow zooming when configured as zoomable and moveable
20065 if (!(this.options.zoomable && this.options.moveable)) return; // Disable the browser default handling of this event.
20066
20067 util.preventDefault(event);
20068 this.props.touch.allowDragging = false;
20069
20070 if (!this.props.touch.center) {
20071 this.props.touch.center = this.getPointer(event.center, this.body.dom.center);
20072 }
20073
20074 this.stopRolling();
20075 var scale = 1 / (event.scale + this.scaleOffset);
20076
20077 var centerDate = this._pointerToDate(this.props.touch.center);
20078
20079 var hiddenDuration = DateUtil.getHiddenDurationBetween(this.body.hiddenDates, this.start, this.end);
20080 var hiddenDurationBefore = DateUtil.getHiddenDurationBefore(this.options.moment, this.body.hiddenDates, this, centerDate);
20081 var hiddenDurationAfter = hiddenDuration - hiddenDurationBefore; // calculate new start and end
20082
20083 var newStart = centerDate - hiddenDurationBefore + (this.props.touch.start - (centerDate - hiddenDurationBefore)) * scale;
20084 var newEnd = centerDate + hiddenDurationAfter + (this.props.touch.end - (centerDate + hiddenDurationAfter)) * scale; // snapping times away from hidden zones
20085
20086 this.startToFront = 1 - scale <= 0; // used to do the right auto correction with periodic hidden times
20087
20088 this.endToFront = scale - 1 <= 0; // used to do the right auto correction with periodic hidden times
20089
20090 var safeStart = DateUtil.snapAwayFromHidden(this.body.hiddenDates, newStart, 1 - scale, true);
20091 var safeEnd = DateUtil.snapAwayFromHidden(this.body.hiddenDates, newEnd, scale - 1, true);
20092
20093 if (safeStart != newStart || safeEnd != newEnd) {
20094 this.props.touch.start = safeStart;
20095 this.props.touch.end = safeEnd;
20096 this.scaleOffset = 1 - event.scale;
20097 newStart = safeStart;
20098 newEnd = safeEnd;
20099 }
20100
20101 var options = {
20102 animation: false,
20103 byUser: true,
20104 event: event
20105 };
20106 this.setRange(newStart, newEnd, options);
20107 this.startToFront = false; // revert to default
20108
20109 this.endToFront = true; // revert to default
20110};
20111/**
20112 * Test whether the mouse from a mouse event is inside the visible window,
20113 * between the current start and end date
20114 * @param {Object} event
20115 * @return {boolean} Returns true when inside the visible window
20116 * @private
20117 */
20118
20119
20120Range.prototype._isInsideRange = function (event) {
20121 // calculate the time where the mouse is, check whether inside
20122 // and no scroll action should happen.
20123 var clientX = event.center ? event.center.x : event.clientX;
20124 var x;
20125
20126 if (this.options.rtl) {
20127 x = clientX - util.getAbsoluteLeft(this.body.dom.centerContainer);
20128 } else {
20129 x = util.getAbsoluteRight(this.body.dom.centerContainer) - clientX;
20130 }
20131
20132 var time = this.body.util.toTime(x);
20133 return time >= this.start && time <= this.end;
20134};
20135/**
20136 * Helper function to calculate the center date for zooming
20137 * @param {{x: number, y: number}} pointer
20138 * @return {number} date
20139 * @private
20140 */
20141
20142
20143Range.prototype._pointerToDate = function (pointer) {
20144 var conversion;
20145 var direction = this.options.direction;
20146 validateDirection(direction);
20147
20148 if (direction == 'horizontal') {
20149 return this.body.util.toTime(pointer.x).valueOf();
20150 } else {
20151 var height = this.body.domProps.center.height;
20152 conversion = this.conversion(height);
20153 return pointer.y / conversion.scale + conversion.offset;
20154 }
20155};
20156/**
20157 * Get the pointer location relative to the location of the dom element
20158 * @param {{x: number, y: number}} touch
20159 * @param {Element} element HTML DOM element
20160 * @return {{x: number, y: number}} pointer
20161 * @private
20162 */
20163
20164
20165Range.prototype.getPointer = function (touch, element) {
20166 if (this.options.rtl) {
20167 return {
20168 x: util.getAbsoluteRight(element) - touch.x,
20169 y: touch.y - util.getAbsoluteTop(element)
20170 };
20171 } else {
20172 return {
20173 x: touch.x - util.getAbsoluteLeft(element),
20174 y: touch.y - util.getAbsoluteTop(element)
20175 };
20176 }
20177};
20178/**
20179 * Zoom the range the given scale in or out. Start and end date will
20180 * be adjusted, and the timeline will be redrawn. You can optionally give a
20181 * date around which to zoom.
20182 * For example, try scale = 0.9 or 1.1
20183 * @param {number} scale Scaling factor. Values above 1 will zoom out,
20184 * values below 1 will zoom in.
20185 * @param {number} [center] Value representing a date around which will
20186 * be zoomed.
20187 * @param {number} delta
20188 * @param {Event} event
20189 */
20190
20191
20192Range.prototype.zoom = function (scale, center, delta, event) {
20193 // if centerDate is not provided, take it half between start Date and end Date
20194 if (center == null) {
20195 center = (this.start + this.end) / 2;
20196 }
20197
20198 var hiddenDuration = DateUtil.getHiddenDurationBetween(this.body.hiddenDates, this.start, this.end);
20199 var hiddenDurationBefore = DateUtil.getHiddenDurationBefore(this.options.moment, this.body.hiddenDates, this, center);
20200 var hiddenDurationAfter = hiddenDuration - hiddenDurationBefore; // calculate new start and end
20201
20202 var newStart = center - hiddenDurationBefore + (this.start - (center - hiddenDurationBefore)) * scale;
20203 var newEnd = center + hiddenDurationAfter + (this.end - (center + hiddenDurationAfter)) * scale; // snapping times away from hidden zones
20204
20205 this.startToFront = delta > 0 ? false : true; // used to do the right autocorrection with periodic hidden times
20206
20207 this.endToFront = -delta > 0 ? false : true; // used to do the right autocorrection with periodic hidden times
20208
20209 var safeStart = DateUtil.snapAwayFromHidden(this.body.hiddenDates, newStart, delta, true);
20210 var safeEnd = DateUtil.snapAwayFromHidden(this.body.hiddenDates, newEnd, -delta, true);
20211
20212 if (safeStart != newStart || safeEnd != newEnd) {
20213 newStart = safeStart;
20214 newEnd = safeEnd;
20215 }
20216
20217 var options = {
20218 animation: false,
20219 byUser: true,
20220 event: event
20221 };
20222 this.setRange(newStart, newEnd, options);
20223 this.startToFront = false; // revert to default
20224
20225 this.endToFront = true; // revert to default
20226};
20227/**
20228 * Move the range with a given delta to the left or right. Start and end
20229 * value will be adjusted. For example, try delta = 0.1 or -0.1
20230 * @param {number} delta Moving amount. Positive value will move right,
20231 * negative value will move left
20232 */
20233
20234
20235Range.prototype.move = function (delta) {
20236 // zoom start Date and end Date relative to the centerDate
20237 var diff = this.end - this.start; // apply new values
20238
20239 var newStart = this.start + diff * delta;
20240 var newEnd = this.end + diff * delta; // TODO: reckon with min and max range
20241
20242 this.start = newStart;
20243 this.end = newEnd;
20244};
20245/**
20246 * Move the range to a new center point
20247 * @param {number} moveTo New center point of the range
20248 */
20249
20250
20251Range.prototype.moveTo = function (moveTo) {
20252 var center = (this.start + this.end) / 2;
20253 var diff = center - moveTo; // calculate new start and end
20254
20255 var newStart = this.start - diff;
20256 var newEnd = this.end - diff;
20257 var options = {
20258 animation: false,
20259 byUser: true,
20260 event: null
20261 };
20262 this.setRange(newStart, newEnd, options);
20263};
20264
20265var Range_1 = Range;
20266
20267/**
20268 * Expose `Emitter`.
20269 */
20270var emitterComponent = Emitter;
20271/**
20272 * Initialize a new `Emitter`.
20273 *
20274 * @api public
20275 */
20276
20277function Emitter(obj) {
20278 if (obj) return mixin(obj);
20279}
20280/**
20281 * Mixin the emitter properties.
20282 *
20283 * @param {Object} obj
20284 * @return {Object}
20285 * @api private
20286 */
20287
20288function mixin(obj) {
20289 for (var key in Emitter.prototype) {
20290 obj[key] = Emitter.prototype[key];
20291 }
20292
20293 return obj;
20294}
20295/**
20296 * Listen on the given `event` with `fn`.
20297 *
20298 * @param {String} event
20299 * @param {Function} fn
20300 * @return {Emitter}
20301 * @api public
20302 */
20303
20304
20305Emitter.prototype.on = Emitter.prototype.addEventListener = function (event, fn) {
20306 this._callbacks = this._callbacks || {};
20307 (this._callbacks[event] = this._callbacks[event] || []).push(fn);
20308 return this;
20309};
20310/**
20311 * Adds an `event` listener that will be invoked a single
20312 * time then automatically removed.
20313 *
20314 * @param {String} event
20315 * @param {Function} fn
20316 * @return {Emitter}
20317 * @api public
20318 */
20319
20320
20321Emitter.prototype.once = function (event, fn) {
20322 var self = this;
20323 this._callbacks = this._callbacks || {};
20324
20325 function on() {
20326 self.off(event, on);
20327 fn.apply(this, arguments);
20328 }
20329
20330 on.fn = fn;
20331 this.on(event, on);
20332 return this;
20333};
20334/**
20335 * Remove the given callback for `event` or all
20336 * registered callbacks.
20337 *
20338 * @param {String} event
20339 * @param {Function} fn
20340 * @return {Emitter}
20341 * @api public
20342 */
20343
20344
20345Emitter.prototype.off = Emitter.prototype.removeListener = Emitter.prototype.removeAllListeners = Emitter.prototype.removeEventListener = function (event, fn) {
20346 this._callbacks = this._callbacks || {}; // all
20347
20348 if (0 == arguments.length) {
20349 this._callbacks = {};
20350 return this;
20351 } // specific event
20352
20353
20354 var callbacks = this._callbacks[event];
20355 if (!callbacks) return this; // remove all handlers
20356
20357 if (1 == arguments.length) {
20358 delete this._callbacks[event];
20359 return this;
20360 } // remove specific handler
20361
20362
20363 var cb;
20364
20365 for (var i = 0; i < callbacks.length; i++) {
20366 cb = callbacks[i];
20367
20368 if (cb === fn || cb.fn === fn) {
20369 callbacks.splice(i, 1);
20370 break;
20371 }
20372 }
20373
20374 return this;
20375};
20376/**
20377 * Emit `event` with the given args.
20378 *
20379 * @param {String} event
20380 * @param {Mixed} ...
20381 * @return {Emitter}
20382 */
20383
20384
20385Emitter.prototype.emit = function (event) {
20386 this._callbacks = this._callbacks || {};
20387 var args = [].slice.call(arguments, 1),
20388 callbacks = this._callbacks[event];
20389
20390 if (callbacks) {
20391 callbacks = callbacks.slice(0);
20392
20393 for (var i = 0, len = callbacks.length; i < len; ++i) {
20394 callbacks[i].apply(this, args);
20395 }
20396 }
20397
20398 return this;
20399};
20400/**
20401 * Return array of callbacks for `event`.
20402 *
20403 * @param {String} event
20404 * @return {Array}
20405 * @api public
20406 */
20407
20408
20409Emitter.prototype.listeners = function (event) {
20410 this._callbacks = this._callbacks || {};
20411 return this._callbacks[event] || [];
20412};
20413/**
20414 * Check if this emitter has `event` handlers.
20415 *
20416 * @param {String} event
20417 * @return {Boolean}
20418 * @api public
20419 */
20420
20421
20422Emitter.prototype.hasListeners = function (event) {
20423 return !!this.listeners(event).length;
20424};
20425
20426var propagating = createCommonjsModule$1(function (module, exports) {
20427
20428 (function (factory) {
20429 {
20430 // Node. Does not work with strict CommonJS, but
20431 // only CommonJS-like environments that support module.exports,
20432 // like Node.
20433 module.exports = factory();
20434 }
20435 })(function () {
20436 var _firstTarget = null; // singleton, will contain the target element where the touch event started
20437
20438 /**
20439 * Extend an Hammer.js instance with event propagation.
20440 *
20441 * Features:
20442 * - Events emitted by hammer will propagate in order from child to parent
20443 * elements.
20444 * - Events are extended with a function `event.stopPropagation()` to stop
20445 * propagation to parent elements.
20446 * - An option `preventDefault` to stop all default browser behavior.
20447 *
20448 * Usage:
20449 * var hammer = propagatingHammer(new Hammer(element));
20450 * var hammer = propagatingHammer(new Hammer(element), {preventDefault: true});
20451 *
20452 * @param {Hammer.Manager} hammer An hammer instance.
20453 * @param {Object} [options] Available options:
20454 * - `preventDefault: true | false | 'mouse' | 'touch' | 'pen'`.
20455 * Enforce preventing the default browser behavior.
20456 * Cannot be set to `false`.
20457 * @return {Hammer.Manager} Returns the same hammer instance with extended
20458 * functionality
20459 */
20460
20461 return function propagating(hammer, options) {
20462 var _options = options || {
20463 preventDefault: false
20464 };
20465
20466 if (hammer.Manager) {
20467 // This looks like the Hammer constructor.
20468 // Overload the constructors with our own.
20469 var Hammer = hammer;
20470
20471 var PropagatingHammer = function (element, options) {
20472 var o = Object.create(_options);
20473 if (options) Hammer.assign(o, options);
20474 return propagating(new Hammer(element, o), o);
20475 };
20476
20477 Hammer.assign(PropagatingHammer, Hammer);
20478
20479 PropagatingHammer.Manager = function (element, options) {
20480 var o = Object.create(_options);
20481 if (options) Hammer.assign(o, options);
20482 return propagating(new Hammer.Manager(element, o), o);
20483 };
20484
20485 return PropagatingHammer;
20486 } // create a wrapper object which will override the functions
20487 // `on`, `off`, `destroy`, and `emit` of the hammer instance
20488
20489
20490 var wrapper = Object.create(hammer); // attach to DOM element
20491
20492 var element = hammer.element;
20493 if (!element.hammer) element.hammer = [];
20494 element.hammer.push(wrapper); // register an event to catch the start of a gesture and store the
20495 // target in a singleton
20496
20497 hammer.on('hammer.input', function (event) {
20498 if (_options.preventDefault === true || _options.preventDefault === event.pointerType) {
20499 event.preventDefault();
20500 }
20501
20502 if (event.isFirst) {
20503 _firstTarget = event.target;
20504 }
20505 });
20506 /** @type {Object.<String, Array.<function>>} */
20507
20508 wrapper._handlers = {};
20509 /**
20510 * Register a handler for one or multiple events
20511 * @param {String} events A space separated string with events
20512 * @param {function} handler A callback function, called as handler(event)
20513 * @returns {Hammer.Manager} Returns the hammer instance
20514 */
20515
20516 wrapper.on = function (events, handler) {
20517 // register the handler
20518 split(events).forEach(function (event) {
20519 var _handlers = wrapper._handlers[event];
20520
20521 if (!_handlers) {
20522 wrapper._handlers[event] = _handlers = []; // register the static, propagated handler
20523
20524 hammer.on(event, propagatedHandler);
20525 }
20526
20527 _handlers.push(handler);
20528 });
20529 return wrapper;
20530 };
20531 /**
20532 * Unregister a handler for one or multiple events
20533 * @param {String} events A space separated string with events
20534 * @param {function} [handler] Optional. The registered handler. If not
20535 * provided, all handlers for given events
20536 * are removed.
20537 * @returns {Hammer.Manager} Returns the hammer instance
20538 */
20539
20540
20541 wrapper.off = function (events, handler) {
20542 // unregister the handler
20543 split(events).forEach(function (event) {
20544 var _handlers = wrapper._handlers[event];
20545
20546 if (_handlers) {
20547 _handlers = handler ? _handlers.filter(function (h) {
20548 return h !== handler;
20549 }) : [];
20550
20551 if (_handlers.length > 0) {
20552 wrapper._handlers[event] = _handlers;
20553 } else {
20554 // remove static, propagated handler
20555 hammer.off(event, propagatedHandler);
20556 delete wrapper._handlers[event];
20557 }
20558 }
20559 });
20560 return wrapper;
20561 };
20562 /**
20563 * Emit to the event listeners
20564 * @param {string} eventType
20565 * @param {Event} event
20566 */
20567
20568
20569 wrapper.emit = function (eventType, event) {
20570 _firstTarget = event.target;
20571 hammer.emit(eventType, event);
20572 };
20573
20574 wrapper.destroy = function () {
20575 // Detach from DOM element
20576 var hammers = hammer.element.hammer;
20577 var idx = hammers.indexOf(wrapper);
20578 if (idx !== -1) hammers.splice(idx, 1);
20579 if (!hammers.length) delete hammer.element.hammer; // clear all handlers
20580
20581 wrapper._handlers = {}; // call original hammer destroy
20582
20583 hammer.destroy();
20584 }; // split a string with space separated words
20585
20586
20587 function split(events) {
20588 return events.match(/[^ ]+/g);
20589 }
20590 /**
20591 * A static event handler, applying event propagation.
20592 * @param {Object} event
20593 */
20594
20595
20596 function propagatedHandler(event) {
20597 // let only a single hammer instance handle this event
20598 if (event.type !== 'hammer.input') {
20599 // it is possible that the same srcEvent is used with multiple hammer events,
20600 // we keep track on which events are handled in an object _handled
20601 if (!event.srcEvent._handled) {
20602 event.srcEvent._handled = {};
20603 }
20604
20605 if (event.srcEvent._handled[event.type]) {
20606 return;
20607 } else {
20608 event.srcEvent._handled[event.type] = true;
20609 }
20610 } // attach a stopPropagation function to the event
20611
20612
20613 var stopped = false;
20614
20615 event.stopPropagation = function () {
20616 stopped = true;
20617 }; //wrap the srcEvent's stopPropagation to also stop hammer propagation:
20618
20619
20620 var srcStop = event.srcEvent.stopPropagation.bind(event.srcEvent);
20621
20622 if (typeof srcStop == "function") {
20623 event.srcEvent.stopPropagation = function () {
20624 srcStop();
20625 event.stopPropagation();
20626 };
20627 } // attach firstTarget property to the event
20628
20629
20630 event.firstTarget = _firstTarget; // propagate over all elements (until stopped)
20631
20632 var elem = _firstTarget;
20633
20634 while (elem && !stopped) {
20635 var elemHammer = elem.hammer;
20636
20637 if (elemHammer) {
20638 var _handlers;
20639
20640 for (var k = 0; k < elemHammer.length; k++) {
20641 _handlers = elemHammer[k]._handlers[event.type];
20642 if (_handlers) for (var i = 0; i < _handlers.length && !stopped; i++) {
20643 _handlers[i](event);
20644 }
20645 }
20646 }
20647
20648 elem = elem.parentNode;
20649 }
20650 }
20651
20652 return wrapper;
20653 };
20654 });
20655});
20656
20657var hammer = createCommonjsModule$1(function (module) {
20658 /*! Hammer.JS - v2.0.7 - 2016-04-22
20659 * http://hammerjs.github.io/
20660 *
20661 * Copyright (c) 2016 Jorik Tangelder;
20662 * Licensed under the MIT license */
20663 (function (window, document, exportName, undefined$1) {
20664
20665 var VENDOR_PREFIXES = ['', 'webkit', 'Moz', 'MS', 'ms', 'o'];
20666 var TEST_ELEMENT = document.createElement('div');
20667 var TYPE_FUNCTION = 'function';
20668 var round = Math.round;
20669 var abs = Math.abs;
20670 var now = Date.now;
20671 /**
20672 * set a timeout with a given scope
20673 * @param {Function} fn
20674 * @param {Number} timeout
20675 * @param {Object} context
20676 * @returns {number}
20677 */
20678
20679 function setTimeoutContext(fn, timeout, context) {
20680 return setTimeout(bindFn(fn, context), timeout);
20681 }
20682 /**
20683 * if the argument is an array, we want to execute the fn on each entry
20684 * if it aint an array we don't want to do a thing.
20685 * this is used by all the methods that accept a single and array argument.
20686 * @param {*|Array} arg
20687 * @param {String} fn
20688 * @param {Object} [context]
20689 * @returns {Boolean}
20690 */
20691
20692
20693 function invokeArrayArg(arg, fn, context) {
20694 if (Array.isArray(arg)) {
20695 each(arg, context[fn], context);
20696 return true;
20697 }
20698
20699 return false;
20700 }
20701 /**
20702 * walk objects and arrays
20703 * @param {Object} obj
20704 * @param {Function} iterator
20705 * @param {Object} context
20706 */
20707
20708
20709 function each(obj, iterator, context) {
20710 var i;
20711
20712 if (!obj) {
20713 return;
20714 }
20715
20716 if (obj.forEach) {
20717 obj.forEach(iterator, context);
20718 } else if (obj.length !== undefined$1) {
20719 i = 0;
20720
20721 while (i < obj.length) {
20722 iterator.call(context, obj[i], i, obj);
20723 i++;
20724 }
20725 } else {
20726 for (i in obj) {
20727 obj.hasOwnProperty(i) && iterator.call(context, obj[i], i, obj);
20728 }
20729 }
20730 }
20731 /**
20732 * wrap a method with a deprecation warning and stack trace
20733 * @param {Function} method
20734 * @param {String} name
20735 * @param {String} message
20736 * @returns {Function} A new function wrapping the supplied method.
20737 */
20738
20739
20740 function deprecate(method, name, message) {
20741 var deprecationMessage = 'DEPRECATED METHOD: ' + name + '\n' + message + ' AT \n';
20742 return function () {
20743 var e = new Error('get-stack-trace');
20744 var stack = e && e.stack ? e.stack.replace(/^[^\(]+?[\n$]/gm, '').replace(/^\s+at\s+/gm, '').replace(/^Object.<anonymous>\s*\(/gm, '{anonymous}()@') : 'Unknown Stack Trace';
20745 var log = window.console && (window.console.warn || window.console.log);
20746
20747 if (log) {
20748 log.call(window.console, deprecationMessage, stack);
20749 }
20750
20751 return method.apply(this, arguments);
20752 };
20753 }
20754 /**
20755 * extend object.
20756 * means that properties in dest will be overwritten by the ones in src.
20757 * @param {Object} target
20758 * @param {...Object} objects_to_assign
20759 * @returns {Object} target
20760 */
20761
20762
20763 var assign;
20764
20765 if (typeof Object.assign !== 'function') {
20766 assign = function assign(target) {
20767 if (target === undefined$1 || target === null) {
20768 throw new TypeError('Cannot convert undefined or null to object');
20769 }
20770
20771 var output = Object(target);
20772
20773 for (var index = 1; index < arguments.length; index++) {
20774 var source = arguments[index];
20775
20776 if (source !== undefined$1 && source !== null) {
20777 for (var nextKey in source) {
20778 if (source.hasOwnProperty(nextKey)) {
20779 output[nextKey] = source[nextKey];
20780 }
20781 }
20782 }
20783 }
20784
20785 return output;
20786 };
20787 } else {
20788 assign = Object.assign;
20789 }
20790 /**
20791 * extend object.
20792 * means that properties in dest will be overwritten by the ones in src.
20793 * @param {Object} dest
20794 * @param {Object} src
20795 * @param {Boolean} [merge=false]
20796 * @returns {Object} dest
20797 */
20798
20799
20800 var extend = deprecate(function extend(dest, src, merge) {
20801 var keys = Object.keys(src);
20802 var i = 0;
20803
20804 while (i < keys.length) {
20805 if (!merge || merge && dest[keys[i]] === undefined$1) {
20806 dest[keys[i]] = src[keys[i]];
20807 }
20808
20809 i++;
20810 }
20811
20812 return dest;
20813 }, 'extend', 'Use `assign`.');
20814 /**
20815 * merge the values from src in the dest.
20816 * means that properties that exist in dest will not be overwritten by src
20817 * @param {Object} dest
20818 * @param {Object} src
20819 * @returns {Object} dest
20820 */
20821
20822 var merge = deprecate(function merge(dest, src) {
20823 return extend(dest, src, true);
20824 }, 'merge', 'Use `assign`.');
20825 /**
20826 * simple class inheritance
20827 * @param {Function} child
20828 * @param {Function} base
20829 * @param {Object} [properties]
20830 */
20831
20832 function inherit(child, base, properties) {
20833 var baseP = base.prototype,
20834 childP;
20835 childP = child.prototype = Object.create(baseP);
20836 childP.constructor = child;
20837 childP._super = baseP;
20838
20839 if (properties) {
20840 assign(childP, properties);
20841 }
20842 }
20843 /**
20844 * simple function bind
20845 * @param {Function} fn
20846 * @param {Object} context
20847 * @returns {Function}
20848 */
20849
20850
20851 function bindFn(fn, context) {
20852 return function boundFn() {
20853 return fn.apply(context, arguments);
20854 };
20855 }
20856 /**
20857 * let a boolean value also be a function that must return a boolean
20858 * this first item in args will be used as the context
20859 * @param {Boolean|Function} val
20860 * @param {Array} [args]
20861 * @returns {Boolean}
20862 */
20863
20864
20865 function boolOrFn(val, args) {
20866 if (typeof val == TYPE_FUNCTION) {
20867 return val.apply(args ? args[0] || undefined$1 : undefined$1, args);
20868 }
20869
20870 return val;
20871 }
20872 /**
20873 * use the val2 when val1 is undefined
20874 * @param {*} val1
20875 * @param {*} val2
20876 * @returns {*}
20877 */
20878
20879
20880 function ifUndefined(val1, val2) {
20881 return val1 === undefined$1 ? val2 : val1;
20882 }
20883 /**
20884 * addEventListener with multiple events at once
20885 * @param {EventTarget} target
20886 * @param {String} types
20887 * @param {Function} handler
20888 */
20889
20890
20891 function addEventListeners(target, types, handler) {
20892 each(splitStr(types), function (type) {
20893 target.addEventListener(type, handler, false);
20894 });
20895 }
20896 /**
20897 * removeEventListener with multiple events at once
20898 * @param {EventTarget} target
20899 * @param {String} types
20900 * @param {Function} handler
20901 */
20902
20903
20904 function removeEventListeners(target, types, handler) {
20905 each(splitStr(types), function (type) {
20906 target.removeEventListener(type, handler, false);
20907 });
20908 }
20909 /**
20910 * find if a node is in the given parent
20911 * @method hasParent
20912 * @param {HTMLElement} node
20913 * @param {HTMLElement} parent
20914 * @return {Boolean} found
20915 */
20916
20917
20918 function hasParent(node, parent) {
20919 while (node) {
20920 if (node == parent) {
20921 return true;
20922 }
20923
20924 node = node.parentNode;
20925 }
20926
20927 return false;
20928 }
20929 /**
20930 * small indexOf wrapper
20931 * @param {String} str
20932 * @param {String} find
20933 * @returns {Boolean} found
20934 */
20935
20936
20937 function inStr(str, find) {
20938 return str.indexOf(find) > -1;
20939 }
20940 /**
20941 * split string on whitespace
20942 * @param {String} str
20943 * @returns {Array} words
20944 */
20945
20946
20947 function splitStr(str) {
20948 return str.trim().split(/\s+/g);
20949 }
20950 /**
20951 * find if a array contains the object using indexOf or a simple polyFill
20952 * @param {Array} src
20953 * @param {String} find
20954 * @param {String} [findByKey]
20955 * @return {Boolean|Number} false when not found, or the index
20956 */
20957
20958
20959 function inArray(src, find, findByKey) {
20960 if (src.indexOf && !findByKey) {
20961 return src.indexOf(find);
20962 } else {
20963 var i = 0;
20964
20965 while (i < src.length) {
20966 if (findByKey && src[i][findByKey] == find || !findByKey && src[i] === find) {
20967 return i;
20968 }
20969
20970 i++;
20971 }
20972
20973 return -1;
20974 }
20975 }
20976 /**
20977 * convert array-like objects to real arrays
20978 * @param {Object} obj
20979 * @returns {Array}
20980 */
20981
20982
20983 function toArray(obj) {
20984 return Array.prototype.slice.call(obj, 0);
20985 }
20986 /**
20987 * unique array with objects based on a key (like 'id') or just by the array's value
20988 * @param {Array} src [{id:1},{id:2},{id:1}]
20989 * @param {String} [key]
20990 * @param {Boolean} [sort=False]
20991 * @returns {Array} [{id:1},{id:2}]
20992 */
20993
20994
20995 function uniqueArray(src, key, sort) {
20996 var results = [];
20997 var values = [];
20998 var i = 0;
20999
21000 while (i < src.length) {
21001 var val = key ? src[i][key] : src[i];
21002
21003 if (inArray(values, val) < 0) {
21004 results.push(src[i]);
21005 }
21006
21007 values[i] = val;
21008 i++;
21009 }
21010
21011 if (sort) {
21012 if (!key) {
21013 results = results.sort();
21014 } else {
21015 results = results.sort(function sortUniqueArray(a, b) {
21016 return a[key] > b[key];
21017 });
21018 }
21019 }
21020
21021 return results;
21022 }
21023 /**
21024 * get the prefixed property
21025 * @param {Object} obj
21026 * @param {String} property
21027 * @returns {String|Undefined} prefixed
21028 */
21029
21030
21031 function prefixed(obj, property) {
21032 var prefix, prop;
21033 var camelProp = property[0].toUpperCase() + property.slice(1);
21034 var i = 0;
21035
21036 while (i < VENDOR_PREFIXES.length) {
21037 prefix = VENDOR_PREFIXES[i];
21038 prop = prefix ? prefix + camelProp : property;
21039
21040 if (prop in obj) {
21041 return prop;
21042 }
21043
21044 i++;
21045 }
21046
21047 return undefined$1;
21048 }
21049 /**
21050 * get a unique id
21051 * @returns {number} uniqueId
21052 */
21053
21054
21055 var _uniqueId = 1;
21056
21057 function uniqueId() {
21058 return _uniqueId++;
21059 }
21060 /**
21061 * get the window object of an element
21062 * @param {HTMLElement} element
21063 * @returns {DocumentView|Window}
21064 */
21065
21066
21067 function getWindowForElement(element) {
21068 var doc = element.ownerDocument || element;
21069 return doc.defaultView || doc.parentWindow || window;
21070 }
21071
21072 var MOBILE_REGEX = /mobile|tablet|ip(ad|hone|od)|android/i;
21073 var SUPPORT_TOUCH = 'ontouchstart' in window;
21074 var SUPPORT_POINTER_EVENTS = prefixed(window, 'PointerEvent') !== undefined$1;
21075 var SUPPORT_ONLY_TOUCH = SUPPORT_TOUCH && MOBILE_REGEX.test(navigator.userAgent);
21076 var INPUT_TYPE_TOUCH = 'touch';
21077 var INPUT_TYPE_PEN = 'pen';
21078 var INPUT_TYPE_MOUSE = 'mouse';
21079 var INPUT_TYPE_KINECT = 'kinect';
21080 var COMPUTE_INTERVAL = 25;
21081 var INPUT_START = 1;
21082 var INPUT_MOVE = 2;
21083 var INPUT_END = 4;
21084 var INPUT_CANCEL = 8;
21085 var DIRECTION_NONE = 1;
21086 var DIRECTION_LEFT = 2;
21087 var DIRECTION_RIGHT = 4;
21088 var DIRECTION_UP = 8;
21089 var DIRECTION_DOWN = 16;
21090 var DIRECTION_HORIZONTAL = DIRECTION_LEFT | DIRECTION_RIGHT;
21091 var DIRECTION_VERTICAL = DIRECTION_UP | DIRECTION_DOWN;
21092 var DIRECTION_ALL = DIRECTION_HORIZONTAL | DIRECTION_VERTICAL;
21093 var PROPS_XY = ['x', 'y'];
21094 var PROPS_CLIENT_XY = ['clientX', 'clientY'];
21095 /**
21096 * create new input type manager
21097 * @param {Manager} manager
21098 * @param {Function} callback
21099 * @returns {Input}
21100 * @constructor
21101 */
21102
21103 function Input(manager, callback) {
21104 var self = this;
21105 this.manager = manager;
21106 this.callback = callback;
21107 this.element = manager.element;
21108 this.target = manager.options.inputTarget; // smaller wrapper around the handler, for the scope and the enabled state of the manager,
21109 // so when disabled the input events are completely bypassed.
21110
21111 this.domHandler = function (ev) {
21112 if (boolOrFn(manager.options.enable, [manager])) {
21113 self.handler(ev);
21114 }
21115 };
21116
21117 this.init();
21118 }
21119
21120 Input.prototype = {
21121 /**
21122 * should handle the inputEvent data and trigger the callback
21123 * @virtual
21124 */
21125 handler: function () {},
21126
21127 /**
21128 * bind the events
21129 */
21130 init: function () {
21131 this.evEl && addEventListeners(this.element, this.evEl, this.domHandler);
21132 this.evTarget && addEventListeners(this.target, this.evTarget, this.domHandler);
21133 this.evWin && addEventListeners(getWindowForElement(this.element), this.evWin, this.domHandler);
21134 },
21135
21136 /**
21137 * unbind the events
21138 */
21139 destroy: function () {
21140 this.evEl && removeEventListeners(this.element, this.evEl, this.domHandler);
21141 this.evTarget && removeEventListeners(this.target, this.evTarget, this.domHandler);
21142 this.evWin && removeEventListeners(getWindowForElement(this.element), this.evWin, this.domHandler);
21143 }
21144 };
21145 /**
21146 * create new input type manager
21147 * called by the Manager constructor
21148 * @param {Hammer} manager
21149 * @returns {Input}
21150 */
21151
21152 function createInputInstance(manager) {
21153 var Type;
21154 var inputClass = manager.options.inputClass;
21155
21156 if (inputClass) {
21157 Type = inputClass;
21158 } else if (SUPPORT_POINTER_EVENTS) {
21159 Type = PointerEventInput;
21160 } else if (SUPPORT_ONLY_TOUCH) {
21161 Type = TouchInput;
21162 } else if (!SUPPORT_TOUCH) {
21163 Type = MouseInput;
21164 } else {
21165 Type = TouchMouseInput;
21166 }
21167
21168 return new Type(manager, inputHandler);
21169 }
21170 /**
21171 * handle input events
21172 * @param {Manager} manager
21173 * @param {String} eventType
21174 * @param {Object} input
21175 */
21176
21177
21178 function inputHandler(manager, eventType, input) {
21179 var pointersLen = input.pointers.length;
21180 var changedPointersLen = input.changedPointers.length;
21181 var isFirst = eventType & INPUT_START && pointersLen - changedPointersLen === 0;
21182 var isFinal = eventType & (INPUT_END | INPUT_CANCEL) && pointersLen - changedPointersLen === 0;
21183 input.isFirst = !!isFirst;
21184 input.isFinal = !!isFinal;
21185
21186 if (isFirst) {
21187 manager.session = {};
21188 } // source event is the normalized value of the domEvents
21189 // like 'touchstart, mouseup, pointerdown'
21190
21191
21192 input.eventType = eventType; // compute scale, rotation etc
21193
21194 computeInputData(manager, input); // emit secret event
21195
21196 manager.emit('hammer.input', input);
21197 manager.recognize(input);
21198 manager.session.prevInput = input;
21199 }
21200 /**
21201 * extend the data with some usable properties like scale, rotate, velocity etc
21202 * @param {Object} manager
21203 * @param {Object} input
21204 */
21205
21206
21207 function computeInputData(manager, input) {
21208 var session = manager.session;
21209 var pointers = input.pointers;
21210 var pointersLength = pointers.length; // store the first input to calculate the distance and direction
21211
21212 if (!session.firstInput) {
21213 session.firstInput = simpleCloneInputData(input);
21214 } // to compute scale and rotation we need to store the multiple touches
21215
21216
21217 if (pointersLength > 1 && !session.firstMultiple) {
21218 session.firstMultiple = simpleCloneInputData(input);
21219 } else if (pointersLength === 1) {
21220 session.firstMultiple = false;
21221 }
21222
21223 var firstInput = session.firstInput;
21224 var firstMultiple = session.firstMultiple;
21225 var offsetCenter = firstMultiple ? firstMultiple.center : firstInput.center;
21226 var center = input.center = getCenter(pointers);
21227 input.timeStamp = now();
21228 input.deltaTime = input.timeStamp - firstInput.timeStamp;
21229 input.angle = getAngle(offsetCenter, center);
21230 input.distance = getDistance(offsetCenter, center);
21231 computeDeltaXY(session, input);
21232 input.offsetDirection = getDirection(input.deltaX, input.deltaY);
21233 var overallVelocity = getVelocity(input.deltaTime, input.deltaX, input.deltaY);
21234 input.overallVelocityX = overallVelocity.x;
21235 input.overallVelocityY = overallVelocity.y;
21236 input.overallVelocity = abs(overallVelocity.x) > abs(overallVelocity.y) ? overallVelocity.x : overallVelocity.y;
21237 input.scale = firstMultiple ? getScale(firstMultiple.pointers, pointers) : 1;
21238 input.rotation = firstMultiple ? getRotation(firstMultiple.pointers, pointers) : 0;
21239 input.maxPointers = !session.prevInput ? input.pointers.length : input.pointers.length > session.prevInput.maxPointers ? input.pointers.length : session.prevInput.maxPointers;
21240 computeIntervalInputData(session, input); // find the correct target
21241
21242 var target = manager.element;
21243
21244 if (hasParent(input.srcEvent.target, target)) {
21245 target = input.srcEvent.target;
21246 }
21247
21248 input.target = target;
21249 }
21250
21251 function computeDeltaXY(session, input) {
21252 var center = input.center;
21253 var offset = session.offsetDelta || {};
21254 var prevDelta = session.prevDelta || {};
21255 var prevInput = session.prevInput || {};
21256
21257 if (input.eventType === INPUT_START || prevInput.eventType === INPUT_END) {
21258 prevDelta = session.prevDelta = {
21259 x: prevInput.deltaX || 0,
21260 y: prevInput.deltaY || 0
21261 };
21262 offset = session.offsetDelta = {
21263 x: center.x,
21264 y: center.y
21265 };
21266 }
21267
21268 input.deltaX = prevDelta.x + (center.x - offset.x);
21269 input.deltaY = prevDelta.y + (center.y - offset.y);
21270 }
21271 /**
21272 * velocity is calculated every x ms
21273 * @param {Object} session
21274 * @param {Object} input
21275 */
21276
21277
21278 function computeIntervalInputData(session, input) {
21279 var last = session.lastInterval || input,
21280 deltaTime = input.timeStamp - last.timeStamp,
21281 velocity,
21282 velocityX,
21283 velocityY,
21284 direction;
21285
21286 if (input.eventType != INPUT_CANCEL && (deltaTime > COMPUTE_INTERVAL || last.velocity === undefined$1)) {
21287 var deltaX = input.deltaX - last.deltaX;
21288 var deltaY = input.deltaY - last.deltaY;
21289 var v = getVelocity(deltaTime, deltaX, deltaY);
21290 velocityX = v.x;
21291 velocityY = v.y;
21292 velocity = abs(v.x) > abs(v.y) ? v.x : v.y;
21293 direction = getDirection(deltaX, deltaY);
21294 session.lastInterval = input;
21295 } else {
21296 // use latest velocity info if it doesn't overtake a minimum period
21297 velocity = last.velocity;
21298 velocityX = last.velocityX;
21299 velocityY = last.velocityY;
21300 direction = last.direction;
21301 }
21302
21303 input.velocity = velocity;
21304 input.velocityX = velocityX;
21305 input.velocityY = velocityY;
21306 input.direction = direction;
21307 }
21308 /**
21309 * create a simple clone from the input used for storage of firstInput and firstMultiple
21310 * @param {Object} input
21311 * @returns {Object} clonedInputData
21312 */
21313
21314
21315 function simpleCloneInputData(input) {
21316 // make a simple copy of the pointers because we will get a reference if we don't
21317 // we only need clientXY for the calculations
21318 var pointers = [];
21319 var i = 0;
21320
21321 while (i < input.pointers.length) {
21322 pointers[i] = {
21323 clientX: round(input.pointers[i].clientX),
21324 clientY: round(input.pointers[i].clientY)
21325 };
21326 i++;
21327 }
21328
21329 return {
21330 timeStamp: now(),
21331 pointers: pointers,
21332 center: getCenter(pointers),
21333 deltaX: input.deltaX,
21334 deltaY: input.deltaY
21335 };
21336 }
21337 /**
21338 * get the center of all the pointers
21339 * @param {Array} pointers
21340 * @return {Object} center contains `x` and `y` properties
21341 */
21342
21343
21344 function getCenter(pointers) {
21345 var pointersLength = pointers.length; // no need to loop when only one touch
21346
21347 if (pointersLength === 1) {
21348 return {
21349 x: round(pointers[0].clientX),
21350 y: round(pointers[0].clientY)
21351 };
21352 }
21353
21354 var x = 0,
21355 y = 0,
21356 i = 0;
21357
21358 while (i < pointersLength) {
21359 x += pointers[i].clientX;
21360 y += pointers[i].clientY;
21361 i++;
21362 }
21363
21364 return {
21365 x: round(x / pointersLength),
21366 y: round(y / pointersLength)
21367 };
21368 }
21369 /**
21370 * calculate the velocity between two points. unit is in px per ms.
21371 * @param {Number} deltaTime
21372 * @param {Number} x
21373 * @param {Number} y
21374 * @return {Object} velocity `x` and `y`
21375 */
21376
21377
21378 function getVelocity(deltaTime, x, y) {
21379 return {
21380 x: x / deltaTime || 0,
21381 y: y / deltaTime || 0
21382 };
21383 }
21384 /**
21385 * get the direction between two points
21386 * @param {Number} x
21387 * @param {Number} y
21388 * @return {Number} direction
21389 */
21390
21391
21392 function getDirection(x, y) {
21393 if (x === y) {
21394 return DIRECTION_NONE;
21395 }
21396
21397 if (abs(x) >= abs(y)) {
21398 return x < 0 ? DIRECTION_LEFT : DIRECTION_RIGHT;
21399 }
21400
21401 return y < 0 ? DIRECTION_UP : DIRECTION_DOWN;
21402 }
21403 /**
21404 * calculate the absolute distance between two points
21405 * @param {Object} p1 {x, y}
21406 * @param {Object} p2 {x, y}
21407 * @param {Array} [props] containing x and y keys
21408 * @return {Number} distance
21409 */
21410
21411
21412 function getDistance(p1, p2, props) {
21413 if (!props) {
21414 props = PROPS_XY;
21415 }
21416
21417 var x = p2[props[0]] - p1[props[0]],
21418 y = p2[props[1]] - p1[props[1]];
21419 return Math.sqrt(x * x + y * y);
21420 }
21421 /**
21422 * calculate the angle between two coordinates
21423 * @param {Object} p1
21424 * @param {Object} p2
21425 * @param {Array} [props] containing x and y keys
21426 * @return {Number} angle
21427 */
21428
21429
21430 function getAngle(p1, p2, props) {
21431 if (!props) {
21432 props = PROPS_XY;
21433 }
21434
21435 var x = p2[props[0]] - p1[props[0]],
21436 y = p2[props[1]] - p1[props[1]];
21437 return Math.atan2(y, x) * 180 / Math.PI;
21438 }
21439 /**
21440 * calculate the rotation degrees between two pointersets
21441 * @param {Array} start array of pointers
21442 * @param {Array} end array of pointers
21443 * @return {Number} rotation
21444 */
21445
21446
21447 function getRotation(start, end) {
21448 return getAngle(end[1], end[0], PROPS_CLIENT_XY) + getAngle(start[1], start[0], PROPS_CLIENT_XY);
21449 }
21450 /**
21451 * calculate the scale factor between two pointersets
21452 * no scale is 1, and goes down to 0 when pinched together, and bigger when pinched out
21453 * @param {Array} start array of pointers
21454 * @param {Array} end array of pointers
21455 * @return {Number} scale
21456 */
21457
21458
21459 function getScale(start, end) {
21460 return getDistance(end[0], end[1], PROPS_CLIENT_XY) / getDistance(start[0], start[1], PROPS_CLIENT_XY);
21461 }
21462
21463 var MOUSE_INPUT_MAP = {
21464 mousedown: INPUT_START,
21465 mousemove: INPUT_MOVE,
21466 mouseup: INPUT_END
21467 };
21468 var MOUSE_ELEMENT_EVENTS = 'mousedown';
21469 var MOUSE_WINDOW_EVENTS = 'mousemove mouseup';
21470 /**
21471 * Mouse events input
21472 * @constructor
21473 * @extends Input
21474 */
21475
21476 function MouseInput() {
21477 this.evEl = MOUSE_ELEMENT_EVENTS;
21478 this.evWin = MOUSE_WINDOW_EVENTS;
21479 this.pressed = false; // mousedown state
21480
21481 Input.apply(this, arguments);
21482 }
21483
21484 inherit(MouseInput, Input, {
21485 /**
21486 * handle mouse events
21487 * @param {Object} ev
21488 */
21489 handler: function MEhandler(ev) {
21490 var eventType = MOUSE_INPUT_MAP[ev.type]; // on start we want to have the left mouse button down
21491
21492 if (eventType & INPUT_START && ev.button === 0) {
21493 this.pressed = true;
21494 }
21495
21496 if (eventType & INPUT_MOVE && ev.which !== 1) {
21497 eventType = INPUT_END;
21498 } // mouse must be down
21499
21500
21501 if (!this.pressed) {
21502 return;
21503 }
21504
21505 if (eventType & INPUT_END) {
21506 this.pressed = false;
21507 }
21508
21509 this.callback(this.manager, eventType, {
21510 pointers: [ev],
21511 changedPointers: [ev],
21512 pointerType: INPUT_TYPE_MOUSE,
21513 srcEvent: ev
21514 });
21515 }
21516 });
21517 var POINTER_INPUT_MAP = {
21518 pointerdown: INPUT_START,
21519 pointermove: INPUT_MOVE,
21520 pointerup: INPUT_END,
21521 pointercancel: INPUT_CANCEL,
21522 pointerout: INPUT_CANCEL
21523 }; // in IE10 the pointer types is defined as an enum
21524
21525 var IE10_POINTER_TYPE_ENUM = {
21526 2: INPUT_TYPE_TOUCH,
21527 3: INPUT_TYPE_PEN,
21528 4: INPUT_TYPE_MOUSE,
21529 5: INPUT_TYPE_KINECT // see https://twitter.com/jacobrossi/status/480596438489890816
21530
21531 };
21532 var POINTER_ELEMENT_EVENTS = 'pointerdown';
21533 var POINTER_WINDOW_EVENTS = 'pointermove pointerup pointercancel'; // IE10 has prefixed support, and case-sensitive
21534
21535 if (window.MSPointerEvent && !window.PointerEvent) {
21536 POINTER_ELEMENT_EVENTS = 'MSPointerDown';
21537 POINTER_WINDOW_EVENTS = 'MSPointerMove MSPointerUp MSPointerCancel';
21538 }
21539 /**
21540 * Pointer events input
21541 * @constructor
21542 * @extends Input
21543 */
21544
21545
21546 function PointerEventInput() {
21547 this.evEl = POINTER_ELEMENT_EVENTS;
21548 this.evWin = POINTER_WINDOW_EVENTS;
21549 Input.apply(this, arguments);
21550 this.store = this.manager.session.pointerEvents = [];
21551 }
21552
21553 inherit(PointerEventInput, Input, {
21554 /**
21555 * handle mouse events
21556 * @param {Object} ev
21557 */
21558 handler: function PEhandler(ev) {
21559 var store = this.store;
21560 var removePointer = false;
21561 var eventTypeNormalized = ev.type.toLowerCase().replace('ms', '');
21562 var eventType = POINTER_INPUT_MAP[eventTypeNormalized];
21563 var pointerType = IE10_POINTER_TYPE_ENUM[ev.pointerType] || ev.pointerType;
21564 var isTouch = pointerType == INPUT_TYPE_TOUCH; // get index of the event in the store
21565
21566 var storeIndex = inArray(store, ev.pointerId, 'pointerId'); // start and mouse must be down
21567
21568 if (eventType & INPUT_START && (ev.button === 0 || isTouch)) {
21569 if (storeIndex < 0) {
21570 store.push(ev);
21571 storeIndex = store.length - 1;
21572 }
21573 } else if (eventType & (INPUT_END | INPUT_CANCEL)) {
21574 removePointer = true;
21575 } // it not found, so the pointer hasn't been down (so it's probably a hover)
21576
21577
21578 if (storeIndex < 0) {
21579 return;
21580 } // update the event in the store
21581
21582
21583 store[storeIndex] = ev;
21584 this.callback(this.manager, eventType, {
21585 pointers: store,
21586 changedPointers: [ev],
21587 pointerType: pointerType,
21588 srcEvent: ev
21589 });
21590
21591 if (removePointer) {
21592 // remove from the store
21593 store.splice(storeIndex, 1);
21594 }
21595 }
21596 });
21597 var SINGLE_TOUCH_INPUT_MAP = {
21598 touchstart: INPUT_START,
21599 touchmove: INPUT_MOVE,
21600 touchend: INPUT_END,
21601 touchcancel: INPUT_CANCEL
21602 };
21603 var SINGLE_TOUCH_TARGET_EVENTS = 'touchstart';
21604 var SINGLE_TOUCH_WINDOW_EVENTS = 'touchstart touchmove touchend touchcancel';
21605 /**
21606 * Touch events input
21607 * @constructor
21608 * @extends Input
21609 */
21610
21611 function SingleTouchInput() {
21612 this.evTarget = SINGLE_TOUCH_TARGET_EVENTS;
21613 this.evWin = SINGLE_TOUCH_WINDOW_EVENTS;
21614 this.started = false;
21615 Input.apply(this, arguments);
21616 }
21617
21618 inherit(SingleTouchInput, Input, {
21619 handler: function TEhandler(ev) {
21620 var type = SINGLE_TOUCH_INPUT_MAP[ev.type]; // should we handle the touch events?
21621
21622 if (type === INPUT_START) {
21623 this.started = true;
21624 }
21625
21626 if (!this.started) {
21627 return;
21628 }
21629
21630 var touches = normalizeSingleTouches.call(this, ev, type); // when done, reset the started state
21631
21632 if (type & (INPUT_END | INPUT_CANCEL) && touches[0].length - touches[1].length === 0) {
21633 this.started = false;
21634 }
21635
21636 this.callback(this.manager, type, {
21637 pointers: touches[0],
21638 changedPointers: touches[1],
21639 pointerType: INPUT_TYPE_TOUCH,
21640 srcEvent: ev
21641 });
21642 }
21643 });
21644 /**
21645 * @this {TouchInput}
21646 * @param {Object} ev
21647 * @param {Number} type flag
21648 * @returns {undefined|Array} [all, changed]
21649 */
21650
21651 function normalizeSingleTouches(ev, type) {
21652 var all = toArray(ev.touches);
21653 var changed = toArray(ev.changedTouches);
21654
21655 if (type & (INPUT_END | INPUT_CANCEL)) {
21656 all = uniqueArray(all.concat(changed), 'identifier', true);
21657 }
21658
21659 return [all, changed];
21660 }
21661
21662 var TOUCH_INPUT_MAP = {
21663 touchstart: INPUT_START,
21664 touchmove: INPUT_MOVE,
21665 touchend: INPUT_END,
21666 touchcancel: INPUT_CANCEL
21667 };
21668 var TOUCH_TARGET_EVENTS = 'touchstart touchmove touchend touchcancel';
21669 /**
21670 * Multi-user touch events input
21671 * @constructor
21672 * @extends Input
21673 */
21674
21675 function TouchInput() {
21676 this.evTarget = TOUCH_TARGET_EVENTS;
21677 this.targetIds = {};
21678 Input.apply(this, arguments);
21679 }
21680
21681 inherit(TouchInput, Input, {
21682 handler: function MTEhandler(ev) {
21683 var type = TOUCH_INPUT_MAP[ev.type];
21684 var touches = getTouches.call(this, ev, type);
21685
21686 if (!touches) {
21687 return;
21688 }
21689
21690 this.callback(this.manager, type, {
21691 pointers: touches[0],
21692 changedPointers: touches[1],
21693 pointerType: INPUT_TYPE_TOUCH,
21694 srcEvent: ev
21695 });
21696 }
21697 });
21698 /**
21699 * @this {TouchInput}
21700 * @param {Object} ev
21701 * @param {Number} type flag
21702 * @returns {undefined|Array} [all, changed]
21703 */
21704
21705 function getTouches(ev, type) {
21706 var allTouches = toArray(ev.touches);
21707 var targetIds = this.targetIds; // when there is only one touch, the process can be simplified
21708
21709 if (type & (INPUT_START | INPUT_MOVE) && allTouches.length === 1) {
21710 targetIds[allTouches[0].identifier] = true;
21711 return [allTouches, allTouches];
21712 }
21713
21714 var i,
21715 targetTouches,
21716 changedTouches = toArray(ev.changedTouches),
21717 changedTargetTouches = [],
21718 target = this.target; // get target touches from touches
21719
21720 targetTouches = allTouches.filter(function (touch) {
21721 return hasParent(touch.target, target);
21722 }); // collect touches
21723
21724 if (type === INPUT_START) {
21725 i = 0;
21726
21727 while (i < targetTouches.length) {
21728 targetIds[targetTouches[i].identifier] = true;
21729 i++;
21730 }
21731 } // filter changed touches to only contain touches that exist in the collected target ids
21732
21733
21734 i = 0;
21735
21736 while (i < changedTouches.length) {
21737 if (targetIds[changedTouches[i].identifier]) {
21738 changedTargetTouches.push(changedTouches[i]);
21739 } // cleanup removed touches
21740
21741
21742 if (type & (INPUT_END | INPUT_CANCEL)) {
21743 delete targetIds[changedTouches[i].identifier];
21744 }
21745
21746 i++;
21747 }
21748
21749 if (!changedTargetTouches.length) {
21750 return;
21751 }
21752
21753 return [// merge targetTouches with changedTargetTouches so it contains ALL touches, including 'end' and 'cancel'
21754 uniqueArray(targetTouches.concat(changedTargetTouches), 'identifier', true), changedTargetTouches];
21755 }
21756 /**
21757 * Combined touch and mouse input
21758 *
21759 * Touch has a higher priority then mouse, and while touching no mouse events are allowed.
21760 * This because touch devices also emit mouse events while doing a touch.
21761 *
21762 * @constructor
21763 * @extends Input
21764 */
21765
21766
21767 var DEDUP_TIMEOUT = 2500;
21768 var DEDUP_DISTANCE = 25;
21769
21770 function TouchMouseInput() {
21771 Input.apply(this, arguments);
21772 var handler = bindFn(this.handler, this);
21773 this.touch = new TouchInput(this.manager, handler);
21774 this.mouse = new MouseInput(this.manager, handler);
21775 this.primaryTouch = null;
21776 this.lastTouches = [];
21777 }
21778
21779 inherit(TouchMouseInput, Input, {
21780 /**
21781 * handle mouse and touch events
21782 * @param {Hammer} manager
21783 * @param {String} inputEvent
21784 * @param {Object} inputData
21785 */
21786 handler: function TMEhandler(manager, inputEvent, inputData) {
21787 var isTouch = inputData.pointerType == INPUT_TYPE_TOUCH,
21788 isMouse = inputData.pointerType == INPUT_TYPE_MOUSE;
21789
21790 if (isMouse && inputData.sourceCapabilities && inputData.sourceCapabilities.firesTouchEvents) {
21791 return;
21792 } // when we're in a touch event, record touches to de-dupe synthetic mouse event
21793
21794
21795 if (isTouch) {
21796 recordTouches.call(this, inputEvent, inputData);
21797 } else if (isMouse && isSyntheticEvent.call(this, inputData)) {
21798 return;
21799 }
21800
21801 this.callback(manager, inputEvent, inputData);
21802 },
21803
21804 /**
21805 * remove the event listeners
21806 */
21807 destroy: function destroy() {
21808 this.touch.destroy();
21809 this.mouse.destroy();
21810 }
21811 });
21812
21813 function recordTouches(eventType, eventData) {
21814 if (eventType & INPUT_START) {
21815 this.primaryTouch = eventData.changedPointers[0].identifier;
21816 setLastTouch.call(this, eventData);
21817 } else if (eventType & (INPUT_END | INPUT_CANCEL)) {
21818 setLastTouch.call(this, eventData);
21819 }
21820 }
21821
21822 function setLastTouch(eventData) {
21823 var touch = eventData.changedPointers[0];
21824
21825 if (touch.identifier === this.primaryTouch) {
21826 var lastTouch = {
21827 x: touch.clientX,
21828 y: touch.clientY
21829 };
21830 this.lastTouches.push(lastTouch);
21831 var lts = this.lastTouches;
21832
21833 var removeLastTouch = function () {
21834 var i = lts.indexOf(lastTouch);
21835
21836 if (i > -1) {
21837 lts.splice(i, 1);
21838 }
21839 };
21840
21841 setTimeout(removeLastTouch, DEDUP_TIMEOUT);
21842 }
21843 }
21844
21845 function isSyntheticEvent(eventData) {
21846 var x = eventData.srcEvent.clientX,
21847 y = eventData.srcEvent.clientY;
21848
21849 for (var i = 0; i < this.lastTouches.length; i++) {
21850 var t = this.lastTouches[i];
21851 var dx = Math.abs(x - t.x),
21852 dy = Math.abs(y - t.y);
21853
21854 if (dx <= DEDUP_DISTANCE && dy <= DEDUP_DISTANCE) {
21855 return true;
21856 }
21857 }
21858
21859 return false;
21860 }
21861
21862 var PREFIXED_TOUCH_ACTION = prefixed(TEST_ELEMENT.style, 'touchAction');
21863 var NATIVE_TOUCH_ACTION = PREFIXED_TOUCH_ACTION !== undefined$1; // magical touchAction value
21864
21865 var TOUCH_ACTION_COMPUTE = 'compute';
21866 var TOUCH_ACTION_AUTO = 'auto';
21867 var TOUCH_ACTION_MANIPULATION = 'manipulation'; // not implemented
21868
21869 var TOUCH_ACTION_NONE = 'none';
21870 var TOUCH_ACTION_PAN_X = 'pan-x';
21871 var TOUCH_ACTION_PAN_Y = 'pan-y';
21872 var TOUCH_ACTION_MAP = getTouchActionProps();
21873 /**
21874 * Touch Action
21875 * sets the touchAction property or uses the js alternative
21876 * @param {Manager} manager
21877 * @param {String} value
21878 * @constructor
21879 */
21880
21881 function TouchAction(manager, value) {
21882 this.manager = manager;
21883 this.set(value);
21884 }
21885
21886 TouchAction.prototype = {
21887 /**
21888 * set the touchAction value on the element or enable the polyfill
21889 * @param {String} value
21890 */
21891 set: function (value) {
21892 // find out the touch-action by the event handlers
21893 if (value == TOUCH_ACTION_COMPUTE) {
21894 value = this.compute();
21895 }
21896
21897 if (NATIVE_TOUCH_ACTION && this.manager.element.style && TOUCH_ACTION_MAP[value]) {
21898 this.manager.element.style[PREFIXED_TOUCH_ACTION] = value;
21899 }
21900
21901 this.actions = value.toLowerCase().trim();
21902 },
21903
21904 /**
21905 * just re-set the touchAction value
21906 */
21907 update: function () {
21908 this.set(this.manager.options.touchAction);
21909 },
21910
21911 /**
21912 * compute the value for the touchAction property based on the recognizer's settings
21913 * @returns {String} value
21914 */
21915 compute: function () {
21916 var actions = [];
21917 each(this.manager.recognizers, function (recognizer) {
21918 if (boolOrFn(recognizer.options.enable, [recognizer])) {
21919 actions = actions.concat(recognizer.getTouchAction());
21920 }
21921 });
21922 return cleanTouchActions(actions.join(' '));
21923 },
21924
21925 /**
21926 * this method is called on each input cycle and provides the preventing of the browser behavior
21927 * @param {Object} input
21928 */
21929 preventDefaults: function (input) {
21930 var srcEvent = input.srcEvent;
21931 var direction = input.offsetDirection; // if the touch action did prevented once this session
21932
21933 if (this.manager.session.prevented) {
21934 srcEvent.preventDefault();
21935 return;
21936 }
21937
21938 var actions = this.actions;
21939 var hasNone = inStr(actions, TOUCH_ACTION_NONE) && !TOUCH_ACTION_MAP[TOUCH_ACTION_NONE];
21940 var hasPanY = inStr(actions, TOUCH_ACTION_PAN_Y) && !TOUCH_ACTION_MAP[TOUCH_ACTION_PAN_Y];
21941 var hasPanX = inStr(actions, TOUCH_ACTION_PAN_X) && !TOUCH_ACTION_MAP[TOUCH_ACTION_PAN_X];
21942
21943 if (hasNone) {
21944 //do not prevent defaults if this is a tap gesture
21945 var isTapPointer = input.pointers.length === 1;
21946 var isTapMovement = input.distance < 2;
21947 var isTapTouchTime = input.deltaTime < 250;
21948
21949 if (isTapPointer && isTapMovement && isTapTouchTime) {
21950 return;
21951 }
21952 }
21953
21954 if (hasPanX && hasPanY) {
21955 // `pan-x pan-y` means browser handles all scrolling/panning, do not prevent
21956 return;
21957 }
21958
21959 if (hasNone || hasPanY && direction & DIRECTION_HORIZONTAL || hasPanX && direction & DIRECTION_VERTICAL) {
21960 return this.preventSrc(srcEvent);
21961 }
21962 },
21963
21964 /**
21965 * call preventDefault to prevent the browser's default behavior (scrolling in most cases)
21966 * @param {Object} srcEvent
21967 */
21968 preventSrc: function (srcEvent) {
21969 this.manager.session.prevented = true;
21970 srcEvent.preventDefault();
21971 }
21972 };
21973 /**
21974 * when the touchActions are collected they are not a valid value, so we need to clean things up. *
21975 * @param {String} actions
21976 * @returns {*}
21977 */
21978
21979 function cleanTouchActions(actions) {
21980 // none
21981 if (inStr(actions, TOUCH_ACTION_NONE)) {
21982 return TOUCH_ACTION_NONE;
21983 }
21984
21985 var hasPanX = inStr(actions, TOUCH_ACTION_PAN_X);
21986 var hasPanY = inStr(actions, TOUCH_ACTION_PAN_Y); // if both pan-x and pan-y are set (different recognizers
21987 // for different directions, e.g. horizontal pan but vertical swipe?)
21988 // we need none (as otherwise with pan-x pan-y combined none of these
21989 // recognizers will work, since the browser would handle all panning
21990
21991 if (hasPanX && hasPanY) {
21992 return TOUCH_ACTION_NONE;
21993 } // pan-x OR pan-y
21994
21995
21996 if (hasPanX || hasPanY) {
21997 return hasPanX ? TOUCH_ACTION_PAN_X : TOUCH_ACTION_PAN_Y;
21998 } // manipulation
21999
22000
22001 if (inStr(actions, TOUCH_ACTION_MANIPULATION)) {
22002 return TOUCH_ACTION_MANIPULATION;
22003 }
22004
22005 return TOUCH_ACTION_AUTO;
22006 }
22007
22008 function getTouchActionProps() {
22009 if (!NATIVE_TOUCH_ACTION) {
22010 return false;
22011 }
22012
22013 var touchMap = {};
22014 var cssSupports = window.CSS && window.CSS.supports;
22015 ['auto', 'manipulation', 'pan-y', 'pan-x', 'pan-x pan-y', 'none'].forEach(function (val) {
22016 // If css.supports is not supported but there is native touch-action assume it supports
22017 // all values. This is the case for IE 10 and 11.
22018 touchMap[val] = cssSupports ? window.CSS.supports('touch-action', val) : true;
22019 });
22020 return touchMap;
22021 }
22022 /**
22023 * Recognizer flow explained; *
22024 * All recognizers have the initial state of POSSIBLE when a input session starts.
22025 * The definition of a input session is from the first input until the last input, with all it's movement in it. *
22026 * Example session for mouse-input: mousedown -> mousemove -> mouseup
22027 *
22028 * On each recognizing cycle (see Manager.recognize) the .recognize() method is executed
22029 * which determines with state it should be.
22030 *
22031 * If the recognizer has the state FAILED, CANCELLED or RECOGNIZED (equals ENDED), it is reset to
22032 * POSSIBLE to give it another change on the next cycle.
22033 *
22034 * Possible
22035 * |
22036 * +-----+---------------+
22037 * | |
22038 * +-----+-----+ |
22039 * | | |
22040 * Failed Cancelled |
22041 * +-------+------+
22042 * | |
22043 * Recognized Began
22044 * |
22045 * Changed
22046 * |
22047 * Ended/Recognized
22048 */
22049
22050
22051 var STATE_POSSIBLE = 1;
22052 var STATE_BEGAN = 2;
22053 var STATE_CHANGED = 4;
22054 var STATE_ENDED = 8;
22055 var STATE_RECOGNIZED = STATE_ENDED;
22056 var STATE_CANCELLED = 16;
22057 var STATE_FAILED = 32;
22058 /**
22059 * Recognizer
22060 * Every recognizer needs to extend from this class.
22061 * @constructor
22062 * @param {Object} options
22063 */
22064
22065 function Recognizer(options) {
22066 this.options = assign({}, this.defaults, options || {});
22067 this.id = uniqueId();
22068 this.manager = null; // default is enable true
22069
22070 this.options.enable = ifUndefined(this.options.enable, true);
22071 this.state = STATE_POSSIBLE;
22072 this.simultaneous = {};
22073 this.requireFail = [];
22074 }
22075
22076 Recognizer.prototype = {
22077 /**
22078 * @virtual
22079 * @type {Object}
22080 */
22081 defaults: {},
22082
22083 /**
22084 * set options
22085 * @param {Object} options
22086 * @return {Recognizer}
22087 */
22088 set: function (options) {
22089 assign(this.options, options); // also update the touchAction, in case something changed about the directions/enabled state
22090
22091 this.manager && this.manager.touchAction.update();
22092 return this;
22093 },
22094
22095 /**
22096 * recognize simultaneous with an other recognizer.
22097 * @param {Recognizer} otherRecognizer
22098 * @returns {Recognizer} this
22099 */
22100 recognizeWith: function (otherRecognizer) {
22101 if (invokeArrayArg(otherRecognizer, 'recognizeWith', this)) {
22102 return this;
22103 }
22104
22105 var simultaneous = this.simultaneous;
22106 otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this);
22107
22108 if (!simultaneous[otherRecognizer.id]) {
22109 simultaneous[otherRecognizer.id] = otherRecognizer;
22110 otherRecognizer.recognizeWith(this);
22111 }
22112
22113 return this;
22114 },
22115
22116 /**
22117 * drop the simultaneous link. it doesnt remove the link on the other recognizer.
22118 * @param {Recognizer} otherRecognizer
22119 * @returns {Recognizer} this
22120 */
22121 dropRecognizeWith: function (otherRecognizer) {
22122 if (invokeArrayArg(otherRecognizer, 'dropRecognizeWith', this)) {
22123 return this;
22124 }
22125
22126 otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this);
22127 delete this.simultaneous[otherRecognizer.id];
22128 return this;
22129 },
22130
22131 /**
22132 * recognizer can only run when an other is failing
22133 * @param {Recognizer} otherRecognizer
22134 * @returns {Recognizer} this
22135 */
22136 requireFailure: function (otherRecognizer) {
22137 if (invokeArrayArg(otherRecognizer, 'requireFailure', this)) {
22138 return this;
22139 }
22140
22141 var requireFail = this.requireFail;
22142 otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this);
22143
22144 if (inArray(requireFail, otherRecognizer) === -1) {
22145 requireFail.push(otherRecognizer);
22146 otherRecognizer.requireFailure(this);
22147 }
22148
22149 return this;
22150 },
22151
22152 /**
22153 * drop the requireFailure link. it does not remove the link on the other recognizer.
22154 * @param {Recognizer} otherRecognizer
22155 * @returns {Recognizer} this
22156 */
22157 dropRequireFailure: function (otherRecognizer) {
22158 if (invokeArrayArg(otherRecognizer, 'dropRequireFailure', this)) {
22159 return this;
22160 }
22161
22162 otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this);
22163 var index = inArray(this.requireFail, otherRecognizer);
22164
22165 if (index > -1) {
22166 this.requireFail.splice(index, 1);
22167 }
22168
22169 return this;
22170 },
22171
22172 /**
22173 * has require failures boolean
22174 * @returns {boolean}
22175 */
22176 hasRequireFailures: function () {
22177 return this.requireFail.length > 0;
22178 },
22179
22180 /**
22181 * if the recognizer can recognize simultaneous with an other recognizer
22182 * @param {Recognizer} otherRecognizer
22183 * @returns {Boolean}
22184 */
22185 canRecognizeWith: function (otherRecognizer) {
22186 return !!this.simultaneous[otherRecognizer.id];
22187 },
22188
22189 /**
22190 * You should use `tryEmit` instead of `emit` directly to check
22191 * that all the needed recognizers has failed before emitting.
22192 * @param {Object} input
22193 */
22194 emit: function (input) {
22195 var self = this;
22196 var state = this.state;
22197
22198 function emit(event) {
22199 self.manager.emit(event, input);
22200 } // 'panstart' and 'panmove'
22201
22202
22203 if (state < STATE_ENDED) {
22204 emit(self.options.event + stateStr(state));
22205 }
22206
22207 emit(self.options.event); // simple 'eventName' events
22208
22209 if (input.additionalEvent) {
22210 // additional event(panleft, panright, pinchin, pinchout...)
22211 emit(input.additionalEvent);
22212 } // panend and pancancel
22213
22214
22215 if (state >= STATE_ENDED) {
22216 emit(self.options.event + stateStr(state));
22217 }
22218 },
22219
22220 /**
22221 * Check that all the require failure recognizers has failed,
22222 * if true, it emits a gesture event,
22223 * otherwise, setup the state to FAILED.
22224 * @param {Object} input
22225 */
22226 tryEmit: function (input) {
22227 if (this.canEmit()) {
22228 return this.emit(input);
22229 } // it's failing anyway
22230
22231
22232 this.state = STATE_FAILED;
22233 },
22234
22235 /**
22236 * can we emit?
22237 * @returns {boolean}
22238 */
22239 canEmit: function () {
22240 var i = 0;
22241
22242 while (i < this.requireFail.length) {
22243 if (!(this.requireFail[i].state & (STATE_FAILED | STATE_POSSIBLE))) {
22244 return false;
22245 }
22246
22247 i++;
22248 }
22249
22250 return true;
22251 },
22252
22253 /**
22254 * update the recognizer
22255 * @param {Object} inputData
22256 */
22257 recognize: function (inputData) {
22258 // make a new copy of the inputData
22259 // so we can change the inputData without messing up the other recognizers
22260 var inputDataClone = assign({}, inputData); // is is enabled and allow recognizing?
22261
22262 if (!boolOrFn(this.options.enable, [this, inputDataClone])) {
22263 this.reset();
22264 this.state = STATE_FAILED;
22265 return;
22266 } // reset when we've reached the end
22267
22268
22269 if (this.state & (STATE_RECOGNIZED | STATE_CANCELLED | STATE_FAILED)) {
22270 this.state = STATE_POSSIBLE;
22271 }
22272
22273 this.state = this.process(inputDataClone); // the recognizer has recognized a gesture
22274 // so trigger an event
22275
22276 if (this.state & (STATE_BEGAN | STATE_CHANGED | STATE_ENDED | STATE_CANCELLED)) {
22277 this.tryEmit(inputDataClone);
22278 }
22279 },
22280
22281 /**
22282 * return the state of the recognizer
22283 * the actual recognizing happens in this method
22284 * @virtual
22285 * @param {Object} inputData
22286 * @returns {Const} STATE
22287 */
22288 process: function (inputData) {},
22289 // jshint ignore:line
22290
22291 /**
22292 * return the preferred touch-action
22293 * @virtual
22294 * @returns {Array}
22295 */
22296 getTouchAction: function () {},
22297
22298 /**
22299 * called when the gesture isn't allowed to recognize
22300 * like when another is being recognized or it is disabled
22301 * @virtual
22302 */
22303 reset: function () {}
22304 };
22305 /**
22306 * get a usable string, used as event postfix
22307 * @param {Const} state
22308 * @returns {String} state
22309 */
22310
22311 function stateStr(state) {
22312 if (state & STATE_CANCELLED) {
22313 return 'cancel';
22314 } else if (state & STATE_ENDED) {
22315 return 'end';
22316 } else if (state & STATE_CHANGED) {
22317 return 'move';
22318 } else if (state & STATE_BEGAN) {
22319 return 'start';
22320 }
22321
22322 return '';
22323 }
22324 /**
22325 * direction cons to string
22326 * @param {Const} direction
22327 * @returns {String}
22328 */
22329
22330
22331 function directionStr(direction) {
22332 if (direction == DIRECTION_DOWN) {
22333 return 'down';
22334 } else if (direction == DIRECTION_UP) {
22335 return 'up';
22336 } else if (direction == DIRECTION_LEFT) {
22337 return 'left';
22338 } else if (direction == DIRECTION_RIGHT) {
22339 return 'right';
22340 }
22341
22342 return '';
22343 }
22344 /**
22345 * get a recognizer by name if it is bound to a manager
22346 * @param {Recognizer|String} otherRecognizer
22347 * @param {Recognizer} recognizer
22348 * @returns {Recognizer}
22349 */
22350
22351
22352 function getRecognizerByNameIfManager(otherRecognizer, recognizer) {
22353 var manager = recognizer.manager;
22354
22355 if (manager) {
22356 return manager.get(otherRecognizer);
22357 }
22358
22359 return otherRecognizer;
22360 }
22361 /**
22362 * This recognizer is just used as a base for the simple attribute recognizers.
22363 * @constructor
22364 * @extends Recognizer
22365 */
22366
22367
22368 function AttrRecognizer() {
22369 Recognizer.apply(this, arguments);
22370 }
22371
22372 inherit(AttrRecognizer, Recognizer, {
22373 /**
22374 * @namespace
22375 * @memberof AttrRecognizer
22376 */
22377 defaults: {
22378 /**
22379 * @type {Number}
22380 * @default 1
22381 */
22382 pointers: 1
22383 },
22384
22385 /**
22386 * Used to check if it the recognizer receives valid input, like input.distance > 10.
22387 * @memberof AttrRecognizer
22388 * @param {Object} input
22389 * @returns {Boolean} recognized
22390 */
22391 attrTest: function (input) {
22392 var optionPointers = this.options.pointers;
22393 return optionPointers === 0 || input.pointers.length === optionPointers;
22394 },
22395
22396 /**
22397 * Process the input and return the state for the recognizer
22398 * @memberof AttrRecognizer
22399 * @param {Object} input
22400 * @returns {*} State
22401 */
22402 process: function (input) {
22403 var state = this.state;
22404 var eventType = input.eventType;
22405 var isRecognized = state & (STATE_BEGAN | STATE_CHANGED);
22406 var isValid = this.attrTest(input); // on cancel input and we've recognized before, return STATE_CANCELLED
22407
22408 if (isRecognized && (eventType & INPUT_CANCEL || !isValid)) {
22409 return state | STATE_CANCELLED;
22410 } else if (isRecognized || isValid) {
22411 if (eventType & INPUT_END) {
22412 return state | STATE_ENDED;
22413 } else if (!(state & STATE_BEGAN)) {
22414 return STATE_BEGAN;
22415 }
22416
22417 return state | STATE_CHANGED;
22418 }
22419
22420 return STATE_FAILED;
22421 }
22422 });
22423 /**
22424 * Pan
22425 * Recognized when the pointer is down and moved in the allowed direction.
22426 * @constructor
22427 * @extends AttrRecognizer
22428 */
22429
22430 function PanRecognizer() {
22431 AttrRecognizer.apply(this, arguments);
22432 this.pX = null;
22433 this.pY = null;
22434 }
22435
22436 inherit(PanRecognizer, AttrRecognizer, {
22437 /**
22438 * @namespace
22439 * @memberof PanRecognizer
22440 */
22441 defaults: {
22442 event: 'pan',
22443 threshold: 10,
22444 pointers: 1,
22445 direction: DIRECTION_ALL
22446 },
22447 getTouchAction: function () {
22448 var direction = this.options.direction;
22449 var actions = [];
22450
22451 if (direction & DIRECTION_HORIZONTAL) {
22452 actions.push(TOUCH_ACTION_PAN_Y);
22453 }
22454
22455 if (direction & DIRECTION_VERTICAL) {
22456 actions.push(TOUCH_ACTION_PAN_X);
22457 }
22458
22459 return actions;
22460 },
22461 directionTest: function (input) {
22462 var options = this.options;
22463 var hasMoved = true;
22464 var distance = input.distance;
22465 var direction = input.direction;
22466 var x = input.deltaX;
22467 var y = input.deltaY; // lock to axis?
22468
22469 if (!(direction & options.direction)) {
22470 if (options.direction & DIRECTION_HORIZONTAL) {
22471 direction = x === 0 ? DIRECTION_NONE : x < 0 ? DIRECTION_LEFT : DIRECTION_RIGHT;
22472 hasMoved = x != this.pX;
22473 distance = Math.abs(input.deltaX);
22474 } else {
22475 direction = y === 0 ? DIRECTION_NONE : y < 0 ? DIRECTION_UP : DIRECTION_DOWN;
22476 hasMoved = y != this.pY;
22477 distance = Math.abs(input.deltaY);
22478 }
22479 }
22480
22481 input.direction = direction;
22482 return hasMoved && distance > options.threshold && direction & options.direction;
22483 },
22484 attrTest: function (input) {
22485 return AttrRecognizer.prototype.attrTest.call(this, input) && (this.state & STATE_BEGAN || !(this.state & STATE_BEGAN) && this.directionTest(input));
22486 },
22487 emit: function (input) {
22488 this.pX = input.deltaX;
22489 this.pY = input.deltaY;
22490 var direction = directionStr(input.direction);
22491
22492 if (direction) {
22493 input.additionalEvent = this.options.event + direction;
22494 }
22495
22496 this._super.emit.call(this, input);
22497 }
22498 });
22499 /**
22500 * Pinch
22501 * Recognized when two or more pointers are moving toward (zoom-in) or away from each other (zoom-out).
22502 * @constructor
22503 * @extends AttrRecognizer
22504 */
22505
22506 function PinchRecognizer() {
22507 AttrRecognizer.apply(this, arguments);
22508 }
22509
22510 inherit(PinchRecognizer, AttrRecognizer, {
22511 /**
22512 * @namespace
22513 * @memberof PinchRecognizer
22514 */
22515 defaults: {
22516 event: 'pinch',
22517 threshold: 0,
22518 pointers: 2
22519 },
22520 getTouchAction: function () {
22521 return [TOUCH_ACTION_NONE];
22522 },
22523 attrTest: function (input) {
22524 return this._super.attrTest.call(this, input) && (Math.abs(input.scale - 1) > this.options.threshold || this.state & STATE_BEGAN);
22525 },
22526 emit: function (input) {
22527 if (input.scale !== 1) {
22528 var inOut = input.scale < 1 ? 'in' : 'out';
22529 input.additionalEvent = this.options.event + inOut;
22530 }
22531
22532 this._super.emit.call(this, input);
22533 }
22534 });
22535 /**
22536 * Press
22537 * Recognized when the pointer is down for x ms without any movement.
22538 * @constructor
22539 * @extends Recognizer
22540 */
22541
22542 function PressRecognizer() {
22543 Recognizer.apply(this, arguments);
22544 this._timer = null;
22545 this._input = null;
22546 }
22547
22548 inherit(PressRecognizer, Recognizer, {
22549 /**
22550 * @namespace
22551 * @memberof PressRecognizer
22552 */
22553 defaults: {
22554 event: 'press',
22555 pointers: 1,
22556 time: 251,
22557 // minimal time of the pointer to be pressed
22558 threshold: 9 // a minimal movement is ok, but keep it low
22559
22560 },
22561 getTouchAction: function () {
22562 return [TOUCH_ACTION_AUTO];
22563 },
22564 process: function (input) {
22565 var options = this.options;
22566 var validPointers = input.pointers.length === options.pointers;
22567 var validMovement = input.distance < options.threshold;
22568 var validTime = input.deltaTime > options.time;
22569 this._input = input; // we only allow little movement
22570 // and we've reached an end event, so a tap is possible
22571
22572 if (!validMovement || !validPointers || input.eventType & (INPUT_END | INPUT_CANCEL) && !validTime) {
22573 this.reset();
22574 } else if (input.eventType & INPUT_START) {
22575 this.reset();
22576 this._timer = setTimeoutContext(function () {
22577 this.state = STATE_RECOGNIZED;
22578 this.tryEmit();
22579 }, options.time, this);
22580 } else if (input.eventType & INPUT_END) {
22581 return STATE_RECOGNIZED;
22582 }
22583
22584 return STATE_FAILED;
22585 },
22586 reset: function () {
22587 clearTimeout(this._timer);
22588 },
22589 emit: function (input) {
22590 if (this.state !== STATE_RECOGNIZED) {
22591 return;
22592 }
22593
22594 if (input && input.eventType & INPUT_END) {
22595 this.manager.emit(this.options.event + 'up', input);
22596 } else {
22597 this._input.timeStamp = now();
22598 this.manager.emit(this.options.event, this._input);
22599 }
22600 }
22601 });
22602 /**
22603 * Rotate
22604 * Recognized when two or more pointer are moving in a circular motion.
22605 * @constructor
22606 * @extends AttrRecognizer
22607 */
22608
22609 function RotateRecognizer() {
22610 AttrRecognizer.apply(this, arguments);
22611 }
22612
22613 inherit(RotateRecognizer, AttrRecognizer, {
22614 /**
22615 * @namespace
22616 * @memberof RotateRecognizer
22617 */
22618 defaults: {
22619 event: 'rotate',
22620 threshold: 0,
22621 pointers: 2
22622 },
22623 getTouchAction: function () {
22624 return [TOUCH_ACTION_NONE];
22625 },
22626 attrTest: function (input) {
22627 return this._super.attrTest.call(this, input) && (Math.abs(input.rotation) > this.options.threshold || this.state & STATE_BEGAN);
22628 }
22629 });
22630 /**
22631 * Swipe
22632 * Recognized when the pointer is moving fast (velocity), with enough distance in the allowed direction.
22633 * @constructor
22634 * @extends AttrRecognizer
22635 */
22636
22637 function SwipeRecognizer() {
22638 AttrRecognizer.apply(this, arguments);
22639 }
22640
22641 inherit(SwipeRecognizer, AttrRecognizer, {
22642 /**
22643 * @namespace
22644 * @memberof SwipeRecognizer
22645 */
22646 defaults: {
22647 event: 'swipe',
22648 threshold: 10,
22649 velocity: 0.3,
22650 direction: DIRECTION_HORIZONTAL | DIRECTION_VERTICAL,
22651 pointers: 1
22652 },
22653 getTouchAction: function () {
22654 return PanRecognizer.prototype.getTouchAction.call(this);
22655 },
22656 attrTest: function (input) {
22657 var direction = this.options.direction;
22658 var velocity;
22659
22660 if (direction & (DIRECTION_HORIZONTAL | DIRECTION_VERTICAL)) {
22661 velocity = input.overallVelocity;
22662 } else if (direction & DIRECTION_HORIZONTAL) {
22663 velocity = input.overallVelocityX;
22664 } else if (direction & DIRECTION_VERTICAL) {
22665 velocity = input.overallVelocityY;
22666 }
22667
22668 return this._super.attrTest.call(this, input) && direction & input.offsetDirection && input.distance > this.options.threshold && input.maxPointers == this.options.pointers && abs(velocity) > this.options.velocity && input.eventType & INPUT_END;
22669 },
22670 emit: function (input) {
22671 var direction = directionStr(input.offsetDirection);
22672
22673 if (direction) {
22674 this.manager.emit(this.options.event + direction, input);
22675 }
22676
22677 this.manager.emit(this.options.event, input);
22678 }
22679 });
22680 /**
22681 * A tap is ecognized when the pointer is doing a small tap/click. Multiple taps are recognized if they occur
22682 * between the given interval and position. The delay option can be used to recognize multi-taps without firing
22683 * a single tap.
22684 *
22685 * The eventData from the emitted event contains the property `tapCount`, which contains the amount of
22686 * multi-taps being recognized.
22687 * @constructor
22688 * @extends Recognizer
22689 */
22690
22691 function TapRecognizer() {
22692 Recognizer.apply(this, arguments); // previous time and center,
22693 // used for tap counting
22694
22695 this.pTime = false;
22696 this.pCenter = false;
22697 this._timer = null;
22698 this._input = null;
22699 this.count = 0;
22700 }
22701
22702 inherit(TapRecognizer, Recognizer, {
22703 /**
22704 * @namespace
22705 * @memberof PinchRecognizer
22706 */
22707 defaults: {
22708 event: 'tap',
22709 pointers: 1,
22710 taps: 1,
22711 interval: 300,
22712 // max time between the multi-tap taps
22713 time: 250,
22714 // max time of the pointer to be down (like finger on the screen)
22715 threshold: 9,
22716 // a minimal movement is ok, but keep it low
22717 posThreshold: 10 // a multi-tap can be a bit off the initial position
22718
22719 },
22720 getTouchAction: function () {
22721 return [TOUCH_ACTION_MANIPULATION];
22722 },
22723 process: function (input) {
22724 var options = this.options;
22725 var validPointers = input.pointers.length === options.pointers;
22726 var validMovement = input.distance < options.threshold;
22727 var validTouchTime = input.deltaTime < options.time;
22728 this.reset();
22729
22730 if (input.eventType & INPUT_START && this.count === 0) {
22731 return this.failTimeout();
22732 } // we only allow little movement
22733 // and we've reached an end event, so a tap is possible
22734
22735
22736 if (validMovement && validTouchTime && validPointers) {
22737 if (input.eventType != INPUT_END) {
22738 return this.failTimeout();
22739 }
22740
22741 var validInterval = this.pTime ? input.timeStamp - this.pTime < options.interval : true;
22742 var validMultiTap = !this.pCenter || getDistance(this.pCenter, input.center) < options.posThreshold;
22743 this.pTime = input.timeStamp;
22744 this.pCenter = input.center;
22745
22746 if (!validMultiTap || !validInterval) {
22747 this.count = 1;
22748 } else {
22749 this.count += 1;
22750 }
22751
22752 this._input = input; // if tap count matches we have recognized it,
22753 // else it has began recognizing...
22754
22755 var tapCount = this.count % options.taps;
22756
22757 if (tapCount === 0) {
22758 // no failing requirements, immediately trigger the tap event
22759 // or wait as long as the multitap interval to trigger
22760 if (!this.hasRequireFailures()) {
22761 return STATE_RECOGNIZED;
22762 } else {
22763 this._timer = setTimeoutContext(function () {
22764 this.state = STATE_RECOGNIZED;
22765 this.tryEmit();
22766 }, options.interval, this);
22767 return STATE_BEGAN;
22768 }
22769 }
22770 }
22771
22772 return STATE_FAILED;
22773 },
22774 failTimeout: function () {
22775 this._timer = setTimeoutContext(function () {
22776 this.state = STATE_FAILED;
22777 }, this.options.interval, this);
22778 return STATE_FAILED;
22779 },
22780 reset: function () {
22781 clearTimeout(this._timer);
22782 },
22783 emit: function () {
22784 if (this.state == STATE_RECOGNIZED) {
22785 this._input.tapCount = this.count;
22786 this.manager.emit(this.options.event, this._input);
22787 }
22788 }
22789 });
22790 /**
22791 * Simple way to create a manager with a default set of recognizers.
22792 * @param {HTMLElement} element
22793 * @param {Object} [options]
22794 * @constructor
22795 */
22796
22797 function Hammer(element, options) {
22798 options = options || {};
22799 options.recognizers = ifUndefined(options.recognizers, Hammer.defaults.preset);
22800 return new Manager(element, options);
22801 }
22802 /**
22803 * @const {string}
22804 */
22805
22806
22807 Hammer.VERSION = '2.0.7';
22808 /**
22809 * default settings
22810 * @namespace
22811 */
22812
22813 Hammer.defaults = {
22814 /**
22815 * set if DOM events are being triggered.
22816 * But this is slower and unused by simple implementations, so disabled by default.
22817 * @type {Boolean}
22818 * @default false
22819 */
22820 domEvents: false,
22821
22822 /**
22823 * The value for the touchAction property/fallback.
22824 * When set to `compute` it will magically set the correct value based on the added recognizers.
22825 * @type {String}
22826 * @default compute
22827 */
22828 touchAction: TOUCH_ACTION_COMPUTE,
22829
22830 /**
22831 * @type {Boolean}
22832 * @default true
22833 */
22834 enable: true,
22835
22836 /**
22837 * EXPERIMENTAL FEATURE -- can be removed/changed
22838 * Change the parent input target element.
22839 * If Null, then it is being set the to main element.
22840 * @type {Null|EventTarget}
22841 * @default null
22842 */
22843 inputTarget: null,
22844
22845 /**
22846 * force an input class
22847 * @type {Null|Function}
22848 * @default null
22849 */
22850 inputClass: null,
22851
22852 /**
22853 * Default recognizer setup when calling `Hammer()`
22854 * When creating a new Manager these will be skipped.
22855 * @type {Array}
22856 */
22857 preset: [// RecognizerClass, options, [recognizeWith, ...], [requireFailure, ...]
22858 [RotateRecognizer, {
22859 enable: false
22860 }], [PinchRecognizer, {
22861 enable: false
22862 }, ['rotate']], [SwipeRecognizer, {
22863 direction: DIRECTION_HORIZONTAL
22864 }], [PanRecognizer, {
22865 direction: DIRECTION_HORIZONTAL
22866 }, ['swipe']], [TapRecognizer], [TapRecognizer, {
22867 event: 'doubletap',
22868 taps: 2
22869 }, ['tap']], [PressRecognizer]],
22870
22871 /**
22872 * Some CSS properties can be used to improve the working of Hammer.
22873 * Add them to this method and they will be set when creating a new Manager.
22874 * @namespace
22875 */
22876 cssProps: {
22877 /**
22878 * Disables text selection to improve the dragging gesture. Mainly for desktop browsers.
22879 * @type {String}
22880 * @default 'none'
22881 */
22882 userSelect: 'none',
22883
22884 /**
22885 * Disable the Windows Phone grippers when pressing an element.
22886 * @type {String}
22887 * @default 'none'
22888 */
22889 touchSelect: 'none',
22890
22891 /**
22892 * Disables the default callout shown when you touch and hold a touch target.
22893 * On iOS, when you touch and hold a touch target such as a link, Safari displays
22894 * a callout containing information about the link. This property allows you to disable that callout.
22895 * @type {String}
22896 * @default 'none'
22897 */
22898 touchCallout: 'none',
22899
22900 /**
22901 * Specifies whether zooming is enabled. Used by IE10>
22902 * @type {String}
22903 * @default 'none'
22904 */
22905 contentZooming: 'none',
22906
22907 /**
22908 * Specifies that an entire element should be draggable instead of its contents. Mainly for desktop browsers.
22909 * @type {String}
22910 * @default 'none'
22911 */
22912 userDrag: 'none',
22913
22914 /**
22915 * Overrides the highlight color shown when the user taps a link or a JavaScript
22916 * clickable element in iOS. This property obeys the alpha value, if specified.
22917 * @type {String}
22918 * @default 'rgba(0,0,0,0)'
22919 */
22920 tapHighlightColor: 'rgba(0,0,0,0)'
22921 }
22922 };
22923 var STOP = 1;
22924 var FORCED_STOP = 2;
22925 /**
22926 * Manager
22927 * @param {HTMLElement} element
22928 * @param {Object} [options]
22929 * @constructor
22930 */
22931
22932 function Manager(element, options) {
22933 this.options = assign({}, Hammer.defaults, options || {});
22934 this.options.inputTarget = this.options.inputTarget || element;
22935 this.handlers = {};
22936 this.session = {};
22937 this.recognizers = [];
22938 this.oldCssProps = {};
22939 this.element = element;
22940 this.input = createInputInstance(this);
22941 this.touchAction = new TouchAction(this, this.options.touchAction);
22942 toggleCssProps(this, true);
22943 each(this.options.recognizers, function (item) {
22944 var recognizer = this.add(new item[0](item[1]));
22945 item[2] && recognizer.recognizeWith(item[2]);
22946 item[3] && recognizer.requireFailure(item[3]);
22947 }, this);
22948 }
22949
22950 Manager.prototype = {
22951 /**
22952 * set options
22953 * @param {Object} options
22954 * @returns {Manager}
22955 */
22956 set: function (options) {
22957 assign(this.options, options); // Options that need a little more setup
22958
22959 if (options.touchAction) {
22960 this.touchAction.update();
22961 }
22962
22963 if (options.inputTarget) {
22964 // Clean up existing event listeners and reinitialize
22965 this.input.destroy();
22966 this.input.target = options.inputTarget;
22967 this.input.init();
22968 }
22969
22970 return this;
22971 },
22972
22973 /**
22974 * stop recognizing for this session.
22975 * This session will be discarded, when a new [input]start event is fired.
22976 * When forced, the recognizer cycle is stopped immediately.
22977 * @param {Boolean} [force]
22978 */
22979 stop: function (force) {
22980 this.session.stopped = force ? FORCED_STOP : STOP;
22981 },
22982
22983 /**
22984 * run the recognizers!
22985 * called by the inputHandler function on every movement of the pointers (touches)
22986 * it walks through all the recognizers and tries to detect the gesture that is being made
22987 * @param {Object} inputData
22988 */
22989 recognize: function (inputData) {
22990 var session = this.session;
22991
22992 if (session.stopped) {
22993 return;
22994 } // run the touch-action polyfill
22995
22996
22997 this.touchAction.preventDefaults(inputData);
22998 var recognizer;
22999 var recognizers = this.recognizers; // this holds the recognizer that is being recognized.
23000 // so the recognizer's state needs to be BEGAN, CHANGED, ENDED or RECOGNIZED
23001 // if no recognizer is detecting a thing, it is set to `null`
23002
23003 var curRecognizer = session.curRecognizer; // reset when the last recognizer is recognized
23004 // or when we're in a new session
23005
23006 if (!curRecognizer || curRecognizer && curRecognizer.state & STATE_RECOGNIZED) {
23007 curRecognizer = session.curRecognizer = null;
23008 }
23009
23010 var i = 0;
23011
23012 while (i < recognizers.length) {
23013 recognizer = recognizers[i]; // find out if we are allowed try to recognize the input for this one.
23014 // 1. allow if the session is NOT forced stopped (see the .stop() method)
23015 // 2. allow if we still haven't recognized a gesture in this session, or the this recognizer is the one
23016 // that is being recognized.
23017 // 3. allow if the recognizer is allowed to run simultaneous with the current recognized recognizer.
23018 // this can be setup with the `recognizeWith()` method on the recognizer.
23019
23020 if (session.stopped !== FORCED_STOP && ( // 1
23021 !curRecognizer || recognizer == curRecognizer || // 2
23022 recognizer.canRecognizeWith(curRecognizer))) {
23023 // 3
23024 recognizer.recognize(inputData);
23025 } else {
23026 recognizer.reset();
23027 } // if the recognizer has been recognizing the input as a valid gesture, we want to store this one as the
23028 // current active recognizer. but only if we don't already have an active recognizer
23029
23030
23031 if (!curRecognizer && recognizer.state & (STATE_BEGAN | STATE_CHANGED | STATE_ENDED)) {
23032 curRecognizer = session.curRecognizer = recognizer;
23033 }
23034
23035 i++;
23036 }
23037 },
23038
23039 /**
23040 * get a recognizer by its event name.
23041 * @param {Recognizer|String} recognizer
23042 * @returns {Recognizer|Null}
23043 */
23044 get: function (recognizer) {
23045 if (recognizer instanceof Recognizer) {
23046 return recognizer;
23047 }
23048
23049 var recognizers = this.recognizers;
23050
23051 for (var i = 0; i < recognizers.length; i++) {
23052 if (recognizers[i].options.event == recognizer) {
23053 return recognizers[i];
23054 }
23055 }
23056
23057 return null;
23058 },
23059
23060 /**
23061 * add a recognizer to the manager
23062 * existing recognizers with the same event name will be removed
23063 * @param {Recognizer} recognizer
23064 * @returns {Recognizer|Manager}
23065 */
23066 add: function (recognizer) {
23067 if (invokeArrayArg(recognizer, 'add', this)) {
23068 return this;
23069 } // remove existing
23070
23071
23072 var existing = this.get(recognizer.options.event);
23073
23074 if (existing) {
23075 this.remove(existing);
23076 }
23077
23078 this.recognizers.push(recognizer);
23079 recognizer.manager = this;
23080 this.touchAction.update();
23081 return recognizer;
23082 },
23083
23084 /**
23085 * remove a recognizer by name or instance
23086 * @param {Recognizer|String} recognizer
23087 * @returns {Manager}
23088 */
23089 remove: function (recognizer) {
23090 if (invokeArrayArg(recognizer, 'remove', this)) {
23091 return this;
23092 }
23093
23094 recognizer = this.get(recognizer); // let's make sure this recognizer exists
23095
23096 if (recognizer) {
23097 var recognizers = this.recognizers;
23098 var index = inArray(recognizers, recognizer);
23099
23100 if (index !== -1) {
23101 recognizers.splice(index, 1);
23102 this.touchAction.update();
23103 }
23104 }
23105
23106 return this;
23107 },
23108
23109 /**
23110 * bind event
23111 * @param {String} events
23112 * @param {Function} handler
23113 * @returns {EventEmitter} this
23114 */
23115 on: function (events, handler) {
23116 if (events === undefined$1) {
23117 return;
23118 }
23119
23120 if (handler === undefined$1) {
23121 return;
23122 }
23123
23124 var handlers = this.handlers;
23125 each(splitStr(events), function (event) {
23126 handlers[event] = handlers[event] || [];
23127 handlers[event].push(handler);
23128 });
23129 return this;
23130 },
23131
23132 /**
23133 * unbind event, leave emit blank to remove all handlers
23134 * @param {String} events
23135 * @param {Function} [handler]
23136 * @returns {EventEmitter} this
23137 */
23138 off: function (events, handler) {
23139 if (events === undefined$1) {
23140 return;
23141 }
23142
23143 var handlers = this.handlers;
23144 each(splitStr(events), function (event) {
23145 if (!handler) {
23146 delete handlers[event];
23147 } else {
23148 handlers[event] && handlers[event].splice(inArray(handlers[event], handler), 1);
23149 }
23150 });
23151 return this;
23152 },
23153
23154 /**
23155 * emit event to the listeners
23156 * @param {String} event
23157 * @param {Object} data
23158 */
23159 emit: function (event, data) {
23160 // we also want to trigger dom events
23161 if (this.options.domEvents) {
23162 triggerDomEvent(event, data);
23163 } // no handlers, so skip it all
23164
23165
23166 var handlers = this.handlers[event] && this.handlers[event].slice();
23167
23168 if (!handlers || !handlers.length) {
23169 return;
23170 }
23171
23172 data.type = event;
23173
23174 data.preventDefault = function () {
23175 data.srcEvent.preventDefault();
23176 };
23177
23178 var i = 0;
23179
23180 while (i < handlers.length) {
23181 handlers[i](data);
23182 i++;
23183 }
23184 },
23185
23186 /**
23187 * destroy the manager and unbinds all events
23188 * it doesn't unbind dom events, that is the user own responsibility
23189 */
23190 destroy: function () {
23191 this.element && toggleCssProps(this, false);
23192 this.handlers = {};
23193 this.session = {};
23194 this.input.destroy();
23195 this.element = null;
23196 }
23197 };
23198 /**
23199 * add/remove the css properties as defined in manager.options.cssProps
23200 * @param {Manager} manager
23201 * @param {Boolean} add
23202 */
23203
23204 function toggleCssProps(manager, add) {
23205 var element = manager.element;
23206
23207 if (!element.style) {
23208 return;
23209 }
23210
23211 var prop;
23212 each(manager.options.cssProps, function (value, name) {
23213 prop = prefixed(element.style, name);
23214
23215 if (add) {
23216 manager.oldCssProps[prop] = element.style[prop];
23217 element.style[prop] = value;
23218 } else {
23219 element.style[prop] = manager.oldCssProps[prop] || '';
23220 }
23221 });
23222
23223 if (!add) {
23224 manager.oldCssProps = {};
23225 }
23226 }
23227 /**
23228 * trigger dom event
23229 * @param {String} event
23230 * @param {Object} data
23231 */
23232
23233
23234 function triggerDomEvent(event, data) {
23235 var gestureEvent = document.createEvent('Event');
23236 gestureEvent.initEvent(event, true, true);
23237 gestureEvent.gesture = data;
23238 data.target.dispatchEvent(gestureEvent);
23239 }
23240
23241 assign(Hammer, {
23242 INPUT_START: INPUT_START,
23243 INPUT_MOVE: INPUT_MOVE,
23244 INPUT_END: INPUT_END,
23245 INPUT_CANCEL: INPUT_CANCEL,
23246 STATE_POSSIBLE: STATE_POSSIBLE,
23247 STATE_BEGAN: STATE_BEGAN,
23248 STATE_CHANGED: STATE_CHANGED,
23249 STATE_ENDED: STATE_ENDED,
23250 STATE_RECOGNIZED: STATE_RECOGNIZED,
23251 STATE_CANCELLED: STATE_CANCELLED,
23252 STATE_FAILED: STATE_FAILED,
23253 DIRECTION_NONE: DIRECTION_NONE,
23254 DIRECTION_LEFT: DIRECTION_LEFT,
23255 DIRECTION_RIGHT: DIRECTION_RIGHT,
23256 DIRECTION_UP: DIRECTION_UP,
23257 DIRECTION_DOWN: DIRECTION_DOWN,
23258 DIRECTION_HORIZONTAL: DIRECTION_HORIZONTAL,
23259 DIRECTION_VERTICAL: DIRECTION_VERTICAL,
23260 DIRECTION_ALL: DIRECTION_ALL,
23261 Manager: Manager,
23262 Input: Input,
23263 TouchAction: TouchAction,
23264 TouchInput: TouchInput,
23265 MouseInput: MouseInput,
23266 PointerEventInput: PointerEventInput,
23267 TouchMouseInput: TouchMouseInput,
23268 SingleTouchInput: SingleTouchInput,
23269 Recognizer: Recognizer,
23270 AttrRecognizer: AttrRecognizer,
23271 Tap: TapRecognizer,
23272 Pan: PanRecognizer,
23273 Swipe: SwipeRecognizer,
23274 Pinch: PinchRecognizer,
23275 Rotate: RotateRecognizer,
23276 Press: PressRecognizer,
23277 on: addEventListeners,
23278 off: removeEventListeners,
23279 each: each,
23280 merge: merge,
23281 extend: extend,
23282 assign: assign,
23283 inherit: inherit,
23284 bindFn: bindFn,
23285 prefixed: prefixed
23286 }); // this prevents errors when Hammer is loaded in the presence of an AMD
23287 // style loader but by script tag, not by the loader.
23288
23289 var freeGlobal = typeof window !== 'undefined' ? window : typeof self !== 'undefined' ? self : {}; // jshint ignore:line
23290
23291 freeGlobal.Hammer = Hammer;
23292
23293 if (typeof undefined$1 === 'function' && undefined$1.amd) {
23294 undefined$1(function () {
23295 return Hammer;
23296 });
23297 } else if ( module.exports) {
23298 module.exports = Hammer;
23299 } else {
23300 window[exportName] = Hammer;
23301 }
23302 })(window, document, 'Hammer');
23303});
23304
23305var hammer$1 = createCommonjsModule$1(function (module) {
23306 /**
23307 * Setup a mock hammer.js object, for unit testing.
23308 *
23309 * Inspiration: https://github.com/uber/deck.gl/pull/658
23310 *
23311 * @returns {{on: noop, off: noop, destroy: noop, emit: noop, get: get}}
23312 */
23313 function hammerMock() {
23314 var noop = function noop() {};
23315
23316 return {
23317 on: noop,
23318 off: noop,
23319 destroy: noop,
23320 emit: noop,
23321 get: function get(m) {
23322 //eslint-disable-line no-unused-vars
23323 return {
23324 set: noop
23325 };
23326 }
23327 };
23328 }
23329
23330 if (typeof window !== 'undefined') {
23331 var propagating$1 = propagating;
23332 var Hammer = window['Hammer'] || hammer;
23333 module.exports = propagating$1(Hammer, {
23334 preventDefault: 'mouse'
23335 });
23336 } else {
23337 module.exports = function () {
23338 // hammer.js is only available in a browser, not in node.js. Replacing it with a mock object.
23339 return hammerMock();
23340 };
23341 }
23342});
23343
23344var hammerUtil = createCommonjsModule$1(function (module, exports) {
23345 /**
23346 * Register a touch event, taking place before a gesture
23347 * @param {Hammer} hammer A hammer instance
23348 * @param {function} callback Callback, called as callback(event)
23349 */
23350 exports.onTouch = function (hammer, callback) {
23351 callback.inputHandler = function (event) {
23352 if (event.isFirst) {
23353 callback(event);
23354 }
23355 };
23356
23357 hammer.on('hammer.input', callback.inputHandler);
23358 };
23359 /**
23360 * Register a release event, taking place after a gesture
23361 * @param {Hammer} hammer A hammer instance
23362 * @param {function} callback Callback, called as callback(event)
23363 * @returns {*}
23364 */
23365
23366
23367 exports.onRelease = function (hammer, callback) {
23368 callback.inputHandler = function (event) {
23369 if (event.isFinal) {
23370 callback(event);
23371 }
23372 };
23373
23374 return hammer.on('hammer.input', callback.inputHandler);
23375 };
23376 /**
23377 * Unregister a touch event, taking place before a gesture
23378 * @param {Hammer} hammer A hammer instance
23379 * @param {function} callback Callback, called as callback(event)
23380 */
23381
23382
23383 exports.offTouch = function (hammer, callback) {
23384 hammer.off('hammer.input', callback.inputHandler);
23385 };
23386 /**
23387 * Unregister a release event, taking place before a gesture
23388 * @param {Hammer} hammer A hammer instance
23389 * @param {function} callback Callback, called as callback(event)
23390 */
23391
23392
23393 exports.offRelease = exports.offTouch;
23394 /**
23395 * Hack the PinchRecognizer such that it doesn't prevent default behavior
23396 * for vertical panning.
23397 *
23398 * Yeah ... this is quite a hack ... see https://github.com/hammerjs/hammer.js/issues/932
23399 *
23400 * @param {Hammer.Pinch} pinchRecognizer
23401 * @return {Hammer.Pinch} returns the pinchRecognizer
23402 */
23403
23404 exports.disablePreventDefaultVertically = function (pinchRecognizer) {
23405 var TOUCH_ACTION_PAN_Y = 'pan-y';
23406
23407 pinchRecognizer.getTouchAction = function () {
23408 // default method returns [TOUCH_ACTION_NONE]
23409 return [TOUCH_ACTION_PAN_Y];
23410 };
23411
23412 return pinchRecognizer;
23413 };
23414});
23415var hammerUtil_1 = hammerUtil.onTouch;
23416var hammerUtil_2 = hammerUtil.onRelease;
23417var hammerUtil_3 = hammerUtil.offTouch;
23418var hammerUtil_4 = hammerUtil.offRelease;
23419var hammerUtil_5 = hammerUtil.disablePreventDefaultVertically;
23420
23421/**
23422 * The class TimeStep is an iterator for dates. You provide a start date and an
23423 * end date. The class itself determines the best scale (step size) based on the
23424 * provided start Date, end Date, and minimumStep.
23425 *
23426 * If minimumStep is provided, the step size is chosen as close as possible
23427 * to the minimumStep but larger than minimumStep. If minimumStep is not
23428 * provided, the scale is set to 1 DAY.
23429 * The minimumStep should correspond with the onscreen size of about 6 characters
23430 *
23431 * Alternatively, you can set a scale by hand.
23432 * After creation, you can initialize the class by executing first(). Then you
23433 * can iterate from the start date to the end date via next(). You can check if
23434 * the end date is reached with the function hasNext(). After each step, you can
23435 * retrieve the current date via getCurrent().
23436 * The TimeStep has scales ranging from milliseconds, seconds, minutes, hours,
23437 * days, to years.
23438 *
23439 * Version: 1.2
23440 *
23441 * @param {Date} [start] The start date, for example new Date(2010, 9, 21)
23442 * or new Date(2010, 9, 21, 23, 45, 00)
23443 * @param {Date} [end] The end date
23444 * @param {number} [minimumStep] Optional. Minimum step size in milliseconds
23445 * @param {Date|Array.<Date>} [hiddenDates] Optional.
23446 * @param {{showMajorLabels: boolean}} [options] Optional.
23447 * @constructor TimeStep
23448 */
23449
23450function TimeStep(start, end, minimumStep, hiddenDates, options) {
23451 this.moment = moment$3; // variables
23452
23453 this.current = this.moment();
23454 this._start = this.moment();
23455 this._end = this.moment();
23456 this.autoScale = true;
23457 this.scale = 'day';
23458 this.step = 1; // initialize the range
23459
23460 this.setRange(start, end, minimumStep); // hidden Dates options
23461
23462 this.switchedDay = false;
23463 this.switchedMonth = false;
23464 this.switchedYear = false;
23465
23466 if (Array.isArray(hiddenDates)) {
23467 this.hiddenDates = hiddenDates;
23468 } else if (hiddenDates != undefined) {
23469 this.hiddenDates = [hiddenDates];
23470 } else {
23471 this.hiddenDates = [];
23472 }
23473
23474 this.format = TimeStep.FORMAT; // default formatting
23475
23476 this.options = options ? options : {};
23477} // Time formatting
23478
23479
23480TimeStep.FORMAT = {
23481 minorLabels: {
23482 millisecond: 'SSS',
23483 second: 's',
23484 minute: 'HH:mm',
23485 hour: 'HH:mm',
23486 weekday: 'ddd D',
23487 day: 'D',
23488 week: 'D',
23489 month: 'MMM',
23490 quarter: 'MMM',
23491 year: 'YYYY'
23492 },
23493 majorLabels: {
23494 millisecond: 'HH:mm:ss',
23495 second: 'D MMMM HH:mm',
23496 minute: 'ddd D MMMM',
23497 hour: 'ddd D MMMM',
23498 weekday: 'MMMM YYYY',
23499 day: 'MMMM YYYY',
23500 week: 'MMMM YYYY',
23501 month: 'YYYY',
23502 quarter: 'YYYY',
23503 year: ''
23504 }
23505};
23506/**
23507 * Set custom constructor function for moment. Can be used to set dates
23508 * to UTC or to set a utcOffset.
23509 * @param {function} moment
23510 */
23511
23512TimeStep.prototype.setMoment = function (moment) {
23513 this.moment = moment; // update the date properties, can have a new utcOffset
23514
23515 this.current = this.moment(this.current.valueOf());
23516 this._start = this.moment(this._start.valueOf());
23517 this._end = this.moment(this._end.valueOf());
23518};
23519/**
23520 * Set custom formatting for the minor an major labels of the TimeStep.
23521 * Both `minorLabels` and `majorLabels` are an Object with properties:
23522 * 'millisecond', 'second', 'minute', 'hour', 'weekday', 'day', 'week', 'month', 'quarter', 'year'.
23523 * @param {{minorLabels: Object, majorLabels: Object}} format
23524 */
23525
23526
23527TimeStep.prototype.setFormat = function (format) {
23528 var defaultFormat = util.deepExtend({}, TimeStep.FORMAT);
23529 this.format = util.deepExtend(defaultFormat, format);
23530};
23531/**
23532 * Set a new range
23533 * If minimumStep is provided, the step size is chosen as close as possible
23534 * to the minimumStep but larger than minimumStep. If minimumStep is not
23535 * provided, the scale is set to 1 DAY.
23536 * The minimumStep should correspond with the onscreen size of about 6 characters
23537 * @param {Date} [start] The start date and time.
23538 * @param {Date} [end] The end date and time.
23539 * @param {int} [minimumStep] Optional. Minimum step size in milliseconds
23540 */
23541
23542
23543TimeStep.prototype.setRange = function (start, end, minimumStep) {
23544 if (!(start instanceof Date) || !(end instanceof Date)) {
23545 throw "No legal start or end date in method setRange";
23546 }
23547
23548 this._start = start != undefined ? this.moment(start.valueOf()) : new Date();
23549 this._end = end != undefined ? this.moment(end.valueOf()) : new Date();
23550
23551 if (this.autoScale) {
23552 this.setMinimumStep(minimumStep);
23553 }
23554};
23555/**
23556 * Set the range iterator to the start date.
23557 */
23558
23559
23560TimeStep.prototype.start = function () {
23561 this.current = this._start.clone();
23562 this.roundToMinor();
23563};
23564/**
23565 * Round the current date to the first minor date value
23566 * This must be executed once when the current date is set to start Date
23567 */
23568
23569
23570TimeStep.prototype.roundToMinor = function () {
23571 // round to floor
23572 // to prevent year & month scales rounding down to the first day of week we perform this separately
23573 if (this.scale == 'week') {
23574 this.current.weekday(0);
23575 } // IMPORTANT: we have no breaks in this switch! (this is no bug)
23576 // noinspection FallThroughInSwitchStatementJS
23577
23578
23579 switch (this.scale) {
23580 case 'year':
23581 this.current.year(this.step * Math.floor(this.current.year() / this.step));
23582 this.current.month(0);
23583
23584 case 'quarter':
23585 this.current.month(0);
23586 // eslint-disable-line no-fallthrough
23587
23588 case 'month':
23589 this.current.date(1);
23590 // eslint-disable-line no-fallthrough
23591
23592 case 'week': // eslint-disable-line no-fallthrough
23593
23594 case 'day': // eslint-disable-line no-fallthrough
23595
23596 case 'weekday':
23597 this.current.hours(0);
23598 // eslint-disable-line no-fallthrough
23599
23600 case 'hour':
23601 this.current.minutes(0);
23602 // eslint-disable-line no-fallthrough
23603
23604 case 'minute':
23605 this.current.seconds(0);
23606 // eslint-disable-line no-fallthrough
23607
23608 case 'second':
23609 this.current.milliseconds(0);
23610 // eslint-disable-line no-fallthrough
23611 //case 'millisecond': // nothing to do for milliseconds
23612 }
23613
23614 if (this.step != 1) {
23615 // round down to the first minor value that is a multiple of the current step size
23616 switch (this.scale) {
23617 case 'millisecond':
23618 this.current.subtract(this.current.milliseconds() % this.step, 'milliseconds');
23619 break;
23620
23621 case 'second':
23622 this.current.subtract(this.current.seconds() % this.step, 'seconds');
23623 break;
23624
23625 case 'minute':
23626 this.current.subtract(this.current.minutes() % this.step, 'minutes');
23627 break;
23628
23629 case 'hour':
23630 this.current.subtract(this.current.hours() % this.step, 'hours');
23631 break;
23632
23633 case 'weekday': // intentional fall through
23634
23635 case 'day':
23636 this.current.subtract((this.current.date() - 1) % this.step, 'day');
23637 break;
23638
23639 case 'week':
23640 this.current.subtract(this.current.week() % this.step, 'week');
23641 break;
23642
23643 case 'month':
23644 this.current.subtract(this.current.month() % this.step, 'month');
23645 break;
23646
23647 case 'quarter':
23648 this.current.subtract((this.current.quarter() - 1) % this.step, 'quarter');
23649 break;
23650
23651 case 'year':
23652 this.current.subtract(this.current.year() % this.step, 'year');
23653 break;
23654
23655 default:
23656 break;
23657 }
23658 }
23659};
23660/**
23661 * Check if the there is a next step
23662 * @return {boolean} true if the current date has not passed the end date
23663 */
23664
23665
23666TimeStep.prototype.hasNext = function () {
23667 return this.current.valueOf() <= this._end.valueOf();
23668};
23669/**
23670 * Do the next step
23671 */
23672
23673
23674TimeStep.prototype.next = function () {
23675 var prev = this.current.valueOf(); // Two cases, needed to prevent issues with switching daylight savings
23676 // (end of March and end of October)
23677
23678 switch (this.scale) {
23679 case 'millisecond':
23680 this.current.add(this.step, 'millisecond');
23681 break;
23682
23683 case 'second':
23684 this.current.add(this.step, 'second');
23685 break;
23686
23687 case 'minute':
23688 this.current.add(this.step, 'minute');
23689 break;
23690
23691 case 'hour':
23692 this.current.add(this.step, 'hour');
23693
23694 if (this.current.month() < 6) {
23695 this.current.subtract(this.current.hours() % this.step, 'hour');
23696 } else {
23697 if (this.current.hours() % this.step !== 0) {
23698 this.current.add(this.step - this.current.hours() % this.step, 'hour');
23699 }
23700 }
23701
23702 break;
23703
23704 case 'weekday': // intentional fall through
23705
23706 case 'day':
23707 this.current.add(this.step, 'day');
23708 break;
23709
23710 case 'week':
23711 if (this.current.weekday() !== 0) {
23712 // we had a month break not correlating with a week's start before
23713 this.current.weekday(0); // switch back to week cycles
23714
23715 this.current.add(this.step, 'week');
23716 } else if (this.options.showMajorLabels === false) {
23717 this.current.add(this.step, 'week'); // the default case
23718 } else {
23719 // first day of the week
23720 var nextWeek = this.current.clone();
23721 nextWeek.add(1, 'week');
23722
23723 if (nextWeek.isSame(this.current, 'month')) {
23724 // is the first day of the next week in the same month?
23725 this.current.add(this.step, 'week'); // the default case
23726 } else {
23727 // inject a step at each first day of the month
23728 this.current.add(this.step, 'week');
23729 this.current.date(1);
23730 }
23731 }
23732
23733 break;
23734
23735 case 'month':
23736 this.current.add(this.step, 'month');
23737 break;
23738
23739 case 'quarter':
23740 this.current.add(this.step, 'quarter');
23741 break;
23742
23743 case 'year':
23744 this.current.add(this.step, 'year');
23745 break;
23746
23747 default:
23748 break;
23749 }
23750
23751 if (this.step != 1) {
23752 // round down to the correct major value
23753 switch (this.scale) {
23754 case 'millisecond':
23755 if (this.current.milliseconds() > 0 && this.current.milliseconds() < this.step) this.current.milliseconds(0);
23756 break;
23757
23758 case 'second':
23759 if (this.current.seconds() > 0 && this.current.seconds() < this.step) this.current.seconds(0);
23760 break;
23761
23762 case 'minute':
23763 if (this.current.minutes() > 0 && this.current.minutes() < this.step) this.current.minutes(0);
23764 break;
23765
23766 case 'hour':
23767 if (this.current.hours() > 0 && this.current.hours() < this.step) this.current.hours(0);
23768 break;
23769
23770 case 'weekday': // intentional fall through
23771
23772 case 'day':
23773 if (this.current.date() < this.step + 1) this.current.date(1);
23774 break;
23775
23776 case 'week':
23777 if (this.current.week() < this.step) this.current.week(1);
23778 break;
23779 // week numbering starts at 1, not 0
23780
23781 case 'month':
23782 if (this.current.month() < this.step) this.current.month(0);
23783 break;
23784
23785 case 'quarter':
23786 if (this.current.quarter() < this.step + 1) this.current.quarter(1);
23787 break;
23788
23789 case 'year':
23790 break;
23791 // nothing to do for year
23792
23793 default:
23794 break;
23795 }
23796 } // safety mechanism: if current time is still unchanged, move to the end
23797
23798
23799 if (this.current.valueOf() == prev) {
23800 this.current = this._end.clone();
23801 } // Reset switches for year, month and day. Will get set to true where appropriate in DateUtil.stepOverHiddenDates
23802
23803
23804 this.switchedDay = false;
23805 this.switchedMonth = false;
23806 this.switchedYear = false;
23807 DateUtil.stepOverHiddenDates(this.moment, this, prev);
23808};
23809/**
23810 * Get the current datetime
23811 * @return {Moment} current The current date
23812 */
23813
23814
23815TimeStep.prototype.getCurrent = function () {
23816 return this.current.clone();
23817};
23818/**
23819 * Set a custom scale. Autoscaling will be disabled.
23820 * For example setScale('minute', 5) will result
23821 * in minor steps of 5 minutes, and major steps of an hour.
23822 *
23823 * @param {{scale: string, step: number}} params
23824 * An object containing two properties:
23825 * - A string 'scale'. Choose from 'millisecond', 'second',
23826 * 'minute', 'hour', 'weekday', 'day', 'week', 'month', 'quarter, 'year'.
23827 * - A number 'step'. A step size, by default 1.
23828 * Choose for example 1, 2, 5, or 10.
23829 */
23830
23831
23832TimeStep.prototype.setScale = function (params) {
23833 if (params && typeof params.scale == 'string') {
23834 this.scale = params.scale;
23835 this.step = params.step > 0 ? params.step : 1;
23836 this.autoScale = false;
23837 }
23838};
23839/**
23840 * Enable or disable autoscaling
23841 * @param {boolean} enable If true, autoascaling is set true
23842 */
23843
23844
23845TimeStep.prototype.setAutoScale = function (enable) {
23846 this.autoScale = enable;
23847};
23848/**
23849 * Automatically determine the scale that bests fits the provided minimum step
23850 * @param {number} [minimumStep] The minimum step size in milliseconds
23851 */
23852
23853
23854TimeStep.prototype.setMinimumStep = function (minimumStep) {
23855 if (minimumStep == undefined) {
23856 return;
23857 } //var b = asc + ds;
23858
23859
23860 var stepYear = 1000 * 60 * 60 * 24 * 30 * 12;
23861 var stepQuarter = 1000 * 60 * 60 * 24 * 30 * 3;
23862 var stepMonth = 1000 * 60 * 60 * 24 * 30;
23863 var stepDay = 1000 * 60 * 60 * 24;
23864 var stepHour = 1000 * 60 * 60;
23865 var stepMinute = 1000 * 60;
23866 var stepSecond = 1000;
23867 var stepMillisecond = 1; // find the smallest step that is larger than the provided minimumStep
23868
23869 if (stepYear * 1000 > minimumStep) {
23870 this.scale = 'year';
23871 this.step = 1000;
23872 }
23873
23874 if (stepYear * 500 > minimumStep) {
23875 this.scale = 'year';
23876 this.step = 500;
23877 }
23878
23879 if (stepYear * 100 > minimumStep) {
23880 this.scale = 'year';
23881 this.step = 100;
23882 }
23883
23884 if (stepYear * 50 > minimumStep) {
23885 this.scale = 'year';
23886 this.step = 50;
23887 }
23888
23889 if (stepYear * 10 > minimumStep) {
23890 this.scale = 'year';
23891 this.step = 10;
23892 }
23893
23894 if (stepYear * 5 > minimumStep) {
23895 this.scale = 'year';
23896 this.step = 5;
23897 }
23898
23899 if (stepYear > minimumStep) {
23900 this.scale = 'year';
23901 this.step = 1;
23902 }
23903
23904 if (stepQuarter > minimumStep) {
23905 this.scale = 'quarter';
23906 this.step = 1;
23907 }
23908
23909 if (stepMonth > minimumStep) {
23910 this.scale = 'month';
23911 this.step = 1;
23912 }
23913
23914 if (stepDay * 7 > minimumStep) {
23915 this.scale = 'week';
23916 this.step = 1;
23917 }
23918
23919 if (stepDay * 2 > minimumStep) {
23920 this.scale = 'day';
23921 this.step = 2;
23922 }
23923
23924 if (stepDay > minimumStep) {
23925 this.scale = 'day';
23926 this.step = 1;
23927 }
23928
23929 if (stepDay / 2 > minimumStep) {
23930 this.scale = 'weekday';
23931 this.step = 1;
23932 }
23933
23934 if (stepHour * 4 > minimumStep) {
23935 this.scale = 'hour';
23936 this.step = 4;
23937 }
23938
23939 if (stepHour > minimumStep) {
23940 this.scale = 'hour';
23941 this.step = 1;
23942 }
23943
23944 if (stepMinute * 15 > minimumStep) {
23945 this.scale = 'minute';
23946 this.step = 15;
23947 }
23948
23949 if (stepMinute * 10 > minimumStep) {
23950 this.scale = 'minute';
23951 this.step = 10;
23952 }
23953
23954 if (stepMinute * 5 > minimumStep) {
23955 this.scale = 'minute';
23956 this.step = 5;
23957 }
23958
23959 if (stepMinute > minimumStep) {
23960 this.scale = 'minute';
23961 this.step = 1;
23962 }
23963
23964 if (stepSecond * 15 > minimumStep) {
23965 this.scale = 'second';
23966 this.step = 15;
23967 }
23968
23969 if (stepSecond * 10 > minimumStep) {
23970 this.scale = 'second';
23971 this.step = 10;
23972 }
23973
23974 if (stepSecond * 5 > minimumStep) {
23975 this.scale = 'second';
23976 this.step = 5;
23977 }
23978
23979 if (stepSecond > minimumStep) {
23980 this.scale = 'second';
23981 this.step = 1;
23982 }
23983
23984 if (stepMillisecond * 200 > minimumStep) {
23985 this.scale = 'millisecond';
23986 this.step = 200;
23987 }
23988
23989 if (stepMillisecond * 100 > minimumStep) {
23990 this.scale = 'millisecond';
23991 this.step = 100;
23992 }
23993
23994 if (stepMillisecond * 50 > minimumStep) {
23995 this.scale = 'millisecond';
23996 this.step = 50;
23997 }
23998
23999 if (stepMillisecond * 10 > minimumStep) {
24000 this.scale = 'millisecond';
24001 this.step = 10;
24002 }
24003
24004 if (stepMillisecond * 5 > minimumStep) {
24005 this.scale = 'millisecond';
24006 this.step = 5;
24007 }
24008
24009 if (stepMillisecond > minimumStep) {
24010 this.scale = 'millisecond';
24011 this.step = 1;
24012 }
24013};
24014/**
24015 * Snap a date to a rounded value.
24016 * The snap intervals are dependent on the current scale and step.
24017 * Static function
24018 * @param {Date} date the date to be snapped.
24019 * @param {string} scale Current scale, can be 'millisecond', 'second',
24020 * 'minute', 'hour', 'weekday, 'day', 'week', 'month', 'quarter', 'year'.
24021 * @param {number} step Current step (1, 2, 4, 5, ...
24022 * @return {Date} snappedDate
24023 */
24024
24025
24026TimeStep.snap = function (date, scale, step) {
24027 var clone = moment$3(date);
24028
24029 if (scale == 'year') {
24030 var year = clone.year() + Math.round(clone.month() / 12);
24031 clone.year(Math.round(year / step) * step);
24032 clone.month(0);
24033 clone.date(0);
24034 clone.hours(0);
24035 clone.minutes(0);
24036 clone.seconds(0);
24037 clone.milliseconds(0);
24038 } else if (scale == 'quarter') {
24039 if (clone.month() % 3 == 1 && clone.date() > 15 || clone.month() % 3 == 2) {
24040 clone.date(1);
24041 clone.month(Math.floor(clone.month() / 3) * 3);
24042 clone.add(1, 'quarter'); // important: first set Date to 1, after that change the month and the quarter.
24043 } else {
24044 clone.date(1);
24045 clone.month(Math.floor(clone.month() / 3) * 3);
24046 }
24047
24048 clone.hours(0);
24049 clone.minutes(0);
24050 clone.seconds(0);
24051 clone.milliseconds(0);
24052 } else if (scale == 'month') {
24053 if (clone.date() > 15) {
24054 clone.date(1);
24055 clone.add(1, 'month'); // important: first set Date to 1, after that change the month.
24056 } else {
24057 clone.date(1);
24058 }
24059
24060 clone.hours(0);
24061 clone.minutes(0);
24062 clone.seconds(0);
24063 clone.milliseconds(0);
24064 } else if (scale == 'week') {
24065 if (clone.weekday() > 2) {
24066 // doing it the momentjs locale aware way
24067 clone.weekday(0);
24068 clone.add(1, 'week');
24069 } else {
24070 clone.weekday(0);
24071 }
24072
24073 clone.hours(0);
24074 clone.minutes(0);
24075 clone.seconds(0);
24076 clone.milliseconds(0);
24077 } else if (scale == 'day') {
24078 //noinspection FallthroughInSwitchStatementJS
24079 switch (step) {
24080 case 5:
24081 case 2:
24082 clone.hours(Math.round(clone.hours() / 24) * 24);
24083 break;
24084
24085 default:
24086 clone.hours(Math.round(clone.hours() / 12) * 12);
24087 break;
24088 }
24089
24090 clone.minutes(0);
24091 clone.seconds(0);
24092 clone.milliseconds(0);
24093 } else if (scale == 'weekday') {
24094 //noinspection FallthroughInSwitchStatementJS
24095 switch (step) {
24096 case 5:
24097 case 2:
24098 clone.hours(Math.round(clone.hours() / 12) * 12);
24099 break;
24100
24101 default:
24102 clone.hours(Math.round(clone.hours() / 6) * 6);
24103 break;
24104 }
24105
24106 clone.minutes(0);
24107 clone.seconds(0);
24108 clone.milliseconds(0);
24109 } else if (scale == 'hour') {
24110 switch (step) {
24111 case 4:
24112 clone.minutes(Math.round(clone.minutes() / 60) * 60);
24113 break;
24114
24115 default:
24116 clone.minutes(Math.round(clone.minutes() / 30) * 30);
24117 break;
24118 }
24119
24120 clone.seconds(0);
24121 clone.milliseconds(0);
24122 } else if (scale == 'minute') {
24123 //noinspection FallthroughInSwitchStatementJS
24124 switch (step) {
24125 case 15:
24126 case 10:
24127 clone.minutes(Math.round(clone.minutes() / 5) * 5);
24128 clone.seconds(0);
24129 break;
24130
24131 case 5:
24132 clone.seconds(Math.round(clone.seconds() / 60) * 60);
24133 break;
24134
24135 default:
24136 clone.seconds(Math.round(clone.seconds() / 30) * 30);
24137 break;
24138 }
24139
24140 clone.milliseconds(0);
24141 } else if (scale == 'second') {
24142 //noinspection FallthroughInSwitchStatementJS
24143 switch (step) {
24144 case 15:
24145 case 10:
24146 clone.seconds(Math.round(clone.seconds() / 5) * 5);
24147 clone.milliseconds(0);
24148 break;
24149
24150 case 5:
24151 clone.milliseconds(Math.round(clone.milliseconds() / 1000) * 1000);
24152 break;
24153
24154 default:
24155 clone.milliseconds(Math.round(clone.milliseconds() / 500) * 500);
24156 break;
24157 }
24158 } else if (scale == 'millisecond') {
24159 var _step = step > 5 ? step / 2 : 1;
24160
24161 clone.milliseconds(Math.round(clone.milliseconds() / _step) * _step);
24162 }
24163
24164 return clone;
24165};
24166/**
24167 * Check if the current value is a major value (for example when the step
24168 * is DAY, a major value is each first day of the MONTH)
24169 * @return {boolean} true if current date is major, else false.
24170 */
24171
24172
24173TimeStep.prototype.isMajor = function () {
24174 if (this.switchedYear == true) {
24175 switch (this.scale) {
24176 case 'year':
24177 case 'quarter':
24178 case 'month':
24179 case 'week':
24180 case 'weekday':
24181 case 'day':
24182 case 'hour':
24183 case 'minute':
24184 case 'second':
24185 case 'millisecond':
24186 return true;
24187
24188 default:
24189 return false;
24190 }
24191 } else if (this.switchedMonth == true) {
24192 switch (this.scale) {
24193 case 'week':
24194 case 'weekday':
24195 case 'day':
24196 case 'hour':
24197 case 'minute':
24198 case 'second':
24199 case 'millisecond':
24200 return true;
24201
24202 default:
24203 return false;
24204 }
24205 } else if (this.switchedDay == true) {
24206 switch (this.scale) {
24207 case 'millisecond':
24208 case 'second':
24209 case 'minute':
24210 case 'hour':
24211 return true;
24212
24213 default:
24214 return false;
24215 }
24216 }
24217
24218 var date = this.moment(this.current);
24219
24220 switch (this.scale) {
24221 case 'millisecond':
24222 return date.milliseconds() == 0;
24223
24224 case 'second':
24225 return date.seconds() == 0;
24226
24227 case 'minute':
24228 return date.hours() == 0 && date.minutes() == 0;
24229
24230 case 'hour':
24231 return date.hours() == 0;
24232
24233 case 'weekday': // intentional fall through
24234
24235 case 'day':
24236 return date.date() == 1;
24237
24238 case 'week':
24239 return date.date() == 1;
24240
24241 case 'month':
24242 return date.month() == 0;
24243
24244 case 'quarter':
24245 return date.quarter() == 1;
24246
24247 case 'year':
24248 return false;
24249
24250 default:
24251 return false;
24252 }
24253};
24254/**
24255 * Returns formatted text for the minor axislabel, depending on the current
24256 * date and the scale. For example when scale is MINUTE, the current time is
24257 * formatted as "hh:mm".
24258 * @param {Date} [date=this.current] custom date. if not provided, current date is taken
24259 * @returns {String}
24260 */
24261
24262
24263TimeStep.prototype.getLabelMinor = function (date) {
24264 if (date == undefined) {
24265 date = this.current;
24266 }
24267
24268 if (date instanceof Date) {
24269 date = this.moment(date);
24270 }
24271
24272 if (typeof this.format.minorLabels === "function") {
24273 return this.format.minorLabels(date, this.scale, this.step);
24274 }
24275
24276 var format = this.format.minorLabels[this.scale]; // noinspection FallThroughInSwitchStatementJS
24277
24278 switch (this.scale) {
24279 case 'week':
24280 if (this.isMajor() && date.weekday() !== 0) {
24281 return "";
24282 }
24283
24284 default:
24285 // eslint-disable-line no-fallthrough
24286 return format && format.length > 0 ? this.moment(date).format(format) : '';
24287 }
24288};
24289/**
24290 * Returns formatted text for the major axis label, depending on the current
24291 * date and the scale. For example when scale is MINUTE, the major scale is
24292 * hours, and the hour will be formatted as "hh".
24293 * @param {Date} [date=this.current] custom date. if not provided, current date is taken
24294 * @returns {String}
24295 */
24296
24297
24298TimeStep.prototype.getLabelMajor = function (date) {
24299 if (date == undefined) {
24300 date = this.current;
24301 }
24302
24303 if (date instanceof Date) {
24304 date = this.moment(date);
24305 }
24306
24307 if (typeof this.format.majorLabels === "function") {
24308 return this.format.majorLabels(date, this.scale, this.step);
24309 }
24310
24311 var format = this.format.majorLabels[this.scale];
24312 return format && format.length > 0 ? this.moment(date).format(format) : '';
24313};
24314
24315TimeStep.prototype.getClassName = function () {
24316 var _moment = this.moment;
24317 var m = this.moment(this.current);
24318 var current = m.locale ? m.locale('en') : m.lang('en'); // old versions of moment have .lang() function
24319
24320 var step = this.step;
24321 var classNames = [];
24322 /**
24323 *
24324 * @param {number} value
24325 * @returns {String}
24326 */
24327
24328 function even(value) {
24329 return value / step % 2 == 0 ? ' vis-even' : ' vis-odd';
24330 }
24331 /**
24332 *
24333 * @param {Date} date
24334 * @returns {String}
24335 */
24336
24337
24338 function today(date) {
24339 if (date.isSame(new Date(), 'day')) {
24340 return ' vis-today';
24341 }
24342
24343 if (date.isSame(_moment().add(1, 'day'), 'day')) {
24344 return ' vis-tomorrow';
24345 }
24346
24347 if (date.isSame(_moment().add(-1, 'day'), 'day')) {
24348 return ' vis-yesterday';
24349 }
24350
24351 return '';
24352 }
24353 /**
24354 *
24355 * @param {Date} date
24356 * @returns {String}
24357 */
24358
24359
24360 function currentWeek(date) {
24361 return date.isSame(new Date(), 'week') ? ' vis-current-week' : '';
24362 }
24363 /**
24364 *
24365 * @param {Date} date
24366 * @returns {String}
24367 */
24368
24369
24370 function currentMonth(date) {
24371 return date.isSame(new Date(), 'month') ? ' vis-current-month' : '';
24372 }
24373 /**
24374 *
24375 * @param {Date} date
24376 * @returns {String}
24377 */
24378
24379
24380 function currentQuarter(date) {
24381 return date.isSame(new Date(), 'quarter') ? ' vis-current-quarter' : '';
24382 }
24383 /**
24384 *
24385 * @param {Date} date
24386 * @returns {String}
24387 */
24388
24389
24390 function currentYear(date) {
24391 return date.isSame(new Date(), 'year') ? ' vis-current-year' : '';
24392 }
24393
24394 switch (this.scale) {
24395 case 'millisecond':
24396 classNames.push(today(current));
24397 classNames.push(even(current.milliseconds()));
24398 break;
24399
24400 case 'second':
24401 classNames.push(today(current));
24402 classNames.push(even(current.seconds()));
24403 break;
24404
24405 case 'minute':
24406 classNames.push(today(current));
24407 classNames.push(even(current.minutes()));
24408 break;
24409
24410 case 'hour':
24411 classNames.push('vis-h' + current.hours() + (this.step == 4 ? '-h' + (current.hours() + 4) : ''));
24412 classNames.push(today(current));
24413 classNames.push(even(current.hours()));
24414 break;
24415
24416 case 'weekday':
24417 classNames.push('vis-' + current.format('dddd').toLowerCase());
24418 classNames.push(today(current));
24419 classNames.push(currentWeek(current));
24420 classNames.push(even(current.date()));
24421 break;
24422
24423 case 'day':
24424 classNames.push('vis-day' + current.date());
24425 classNames.push('vis-' + current.format('MMMM').toLowerCase());
24426 classNames.push(today(current));
24427 classNames.push(currentMonth(current));
24428 classNames.push(this.step <= 2 ? today(current) : '');
24429 classNames.push(this.step <= 2 ? 'vis-' + current.format('dddd').toLowerCase() : '');
24430 classNames.push(even(current.date() - 1));
24431 break;
24432
24433 case 'week':
24434 classNames.push('vis-week' + current.format('w'));
24435 classNames.push(currentWeek(current));
24436 classNames.push(even(current.week()));
24437 break;
24438
24439 case 'month':
24440 classNames.push('vis-' + current.format('MMMM').toLowerCase());
24441 classNames.push(currentMonth(current));
24442 classNames.push(even(current.month()));
24443 break;
24444
24445 case 'quarter':
24446 classNames.push('vis-q' + current.quarter());
24447 classNames.push(currentQuarter(current));
24448 classNames.push(even(current.quarter()));
24449 break;
24450
24451 case 'year':
24452 classNames.push('vis-year' + current.year());
24453 classNames.push(currentYear(current));
24454 classNames.push(even(current.year()));
24455 break;
24456 }
24457
24458 return classNames.filter(String).join(" ");
24459};
24460
24461var TimeStep_1 = TimeStep;
24462
24463/**
24464 * A horizontal time axis
24465 * @param {{dom: Object, domProps: Object, emitter: Emitter, range: Range}} body
24466 * @param {Object} [options] See TimeAxis.setOptions for the available
24467 * options.
24468 * @constructor TimeAxis
24469 * @extends Component
24470 */
24471
24472function TimeAxis(body, options) {
24473 this.dom = {
24474 foreground: null,
24475 lines: [],
24476 majorTexts: [],
24477 minorTexts: [],
24478 redundant: {
24479 lines: [],
24480 majorTexts: [],
24481 minorTexts: []
24482 }
24483 };
24484 this.props = {
24485 range: {
24486 start: 0,
24487 end: 0,
24488 minimumStep: 0
24489 },
24490 lineTop: 0
24491 };
24492 this.defaultOptions = {
24493 orientation: {
24494 axis: 'bottom'
24495 },
24496 // axis orientation: 'top' or 'bottom'
24497 showMinorLabels: true,
24498 showMajorLabels: true,
24499 maxMinorChars: 7,
24500 format: TimeStep_1.FORMAT,
24501 moment: moment$3,
24502 timeAxis: null
24503 };
24504 this.options = util.extend({}, this.defaultOptions);
24505 this.body = body; // create the HTML DOM
24506
24507 this._create();
24508
24509 this.setOptions(options);
24510}
24511
24512TimeAxis.prototype = new Component_1();
24513/**
24514 * Set options for the TimeAxis.
24515 * Parameters will be merged in current options.
24516 * @param {Object} options Available options:
24517 * {string} [orientation.axis]
24518 * {boolean} [showMinorLabels]
24519 * {boolean} [showMajorLabels]
24520 */
24521
24522TimeAxis.prototype.setOptions = function (options) {
24523 if (options) {
24524 // copy all options that we know
24525 util.selectiveExtend(['showMinorLabels', 'showMajorLabels', 'maxMinorChars', 'hiddenDates', 'timeAxis', 'moment', 'rtl'], this.options, options); // deep copy the format options
24526
24527 util.selectiveDeepExtend(['format'], this.options, options);
24528
24529 if ('orientation' in options) {
24530 if (typeof options.orientation === 'string') {
24531 this.options.orientation.axis = options.orientation;
24532 } else if (_typeof$1(options.orientation) === 'object' && 'axis' in options.orientation) {
24533 this.options.orientation.axis = options.orientation.axis;
24534 }
24535 } // apply locale to moment.js
24536 // TODO: not so nice, this is applied globally to moment.js
24537
24538
24539 if ('locale' in options) {
24540 if (typeof moment$3.locale === 'function') {
24541 // moment.js 2.8.1+
24542 moment$3.locale(options.locale);
24543 } else {
24544 moment$3.lang(options.locale);
24545 }
24546 }
24547 }
24548};
24549/**
24550 * Create the HTML DOM for the TimeAxis
24551 */
24552
24553
24554TimeAxis.prototype._create = function () {
24555 this.dom.foreground = document.createElement('div');
24556 this.dom.background = document.createElement('div');
24557 this.dom.foreground.className = 'vis-time-axis vis-foreground';
24558 this.dom.background.className = 'vis-time-axis vis-background';
24559};
24560/**
24561 * Destroy the TimeAxis
24562 */
24563
24564
24565TimeAxis.prototype.destroy = function () {
24566 // remove from DOM
24567 if (this.dom.foreground.parentNode) {
24568 this.dom.foreground.parentNode.removeChild(this.dom.foreground);
24569 }
24570
24571 if (this.dom.background.parentNode) {
24572 this.dom.background.parentNode.removeChild(this.dom.background);
24573 }
24574
24575 this.body = null;
24576};
24577/**
24578 * Repaint the component
24579 * @return {boolean} Returns true if the component is resized
24580 */
24581
24582
24583TimeAxis.prototype.redraw = function () {
24584 var props = this.props;
24585 var foreground = this.dom.foreground;
24586 var background = this.dom.background; // determine the correct parent DOM element (depending on option orientation)
24587
24588 var parent = this.options.orientation.axis == 'top' ? this.body.dom.top : this.body.dom.bottom;
24589 var parentChanged = foreground.parentNode !== parent; // calculate character width and height
24590
24591 this._calculateCharSize(); // TODO: recalculate sizes only needed when parent is resized or options is changed
24592
24593
24594 var showMinorLabels = this.options.showMinorLabels && this.options.orientation.axis !== 'none';
24595 var showMajorLabels = this.options.showMajorLabels && this.options.orientation.axis !== 'none'; // determine the width and height of the elemens for the axis
24596
24597 props.minorLabelHeight = showMinorLabels ? props.minorCharHeight : 0;
24598 props.majorLabelHeight = showMajorLabels ? props.majorCharHeight : 0;
24599 props.height = props.minorLabelHeight + props.majorLabelHeight;
24600 props.width = foreground.offsetWidth;
24601 props.minorLineHeight = this.body.domProps.root.height - props.majorLabelHeight - (this.options.orientation.axis == 'top' ? this.body.domProps.bottom.height : this.body.domProps.top.height);
24602 props.minorLineWidth = 1; // TODO: really calculate width
24603
24604 props.majorLineHeight = props.minorLineHeight + props.majorLabelHeight;
24605 props.majorLineWidth = 1; // TODO: really calculate width
24606 // take foreground and background offline while updating (is almost twice as fast)
24607
24608 var foregroundNextSibling = foreground.nextSibling;
24609 var backgroundNextSibling = background.nextSibling;
24610 foreground.parentNode && foreground.parentNode.removeChild(foreground);
24611 background.parentNode && background.parentNode.removeChild(background);
24612 foreground.style.height = this.props.height + 'px';
24613
24614 this._repaintLabels(); // put DOM online again (at the same place)
24615
24616
24617 if (foregroundNextSibling) {
24618 parent.insertBefore(foreground, foregroundNextSibling);
24619 } else {
24620 parent.appendChild(foreground);
24621 }
24622
24623 if (backgroundNextSibling) {
24624 this.body.dom.backgroundVertical.insertBefore(background, backgroundNextSibling);
24625 } else {
24626 this.body.dom.backgroundVertical.appendChild(background);
24627 }
24628
24629 return this._isResized() || parentChanged;
24630};
24631/**
24632 * Repaint major and minor text labels and vertical grid lines
24633 * @private
24634 */
24635
24636
24637TimeAxis.prototype._repaintLabels = function () {
24638 var orientation = this.options.orientation.axis; // calculate range and step (step such that we have space for 7 characters per label)
24639
24640 var start = util.convert(this.body.range.start, 'Number');
24641 var end = util.convert(this.body.range.end, 'Number');
24642 var timeLabelsize = this.body.util.toTime((this.props.minorCharWidth || 10) * this.options.maxMinorChars).valueOf();
24643 var minimumStep = timeLabelsize - DateUtil.getHiddenDurationBefore(this.options.moment, this.body.hiddenDates, this.body.range, timeLabelsize);
24644 minimumStep -= this.body.util.toTime(0).valueOf();
24645 var step = new TimeStep_1(new Date(start), new Date(end), minimumStep, this.body.hiddenDates, this.options);
24646 step.setMoment(this.options.moment);
24647
24648 if (this.options.format) {
24649 step.setFormat(this.options.format);
24650 }
24651
24652 if (this.options.timeAxis) {
24653 step.setScale(this.options.timeAxis);
24654 }
24655
24656 this.step = step; // Move all DOM elements to a "redundant" list, where they
24657 // can be picked for re-use, and clear the lists with lines and texts.
24658 // At the end of the function _repaintLabels, left over elements will be cleaned up
24659
24660 var dom = this.dom;
24661 dom.redundant.lines = dom.lines;
24662 dom.redundant.majorTexts = dom.majorTexts;
24663 dom.redundant.minorTexts = dom.minorTexts;
24664 dom.lines = [];
24665 dom.majorTexts = [];
24666 dom.minorTexts = [];
24667 var current;
24668 var next;
24669 var x;
24670 var xNext;
24671 var isMajor;
24672 var showMinorGrid;
24673 var width = 0,
24674 prevWidth;
24675 var line;
24676 var xFirstMajorLabel = undefined;
24677 var count = 0;
24678 var MAX = 1000;
24679 var className;
24680 step.start();
24681 next = step.getCurrent();
24682 xNext = this.body.util.toScreen(next);
24683
24684 while (step.hasNext() && count < MAX) {
24685 count++;
24686 isMajor = step.isMajor();
24687 className = step.getClassName();
24688 current = next;
24689 x = xNext;
24690 step.next();
24691 next = step.getCurrent();
24692 xNext = this.body.util.toScreen(next);
24693 prevWidth = width;
24694 width = xNext - x;
24695
24696 switch (step.scale) {
24697 case 'week':
24698 showMinorGrid = true;
24699 break;
24700
24701 default:
24702 showMinorGrid = width >= prevWidth * 0.4;
24703 break;
24704 // prevent displaying of the 31th of the month on a scale of 5 days
24705 }
24706
24707 if (this.options.showMinorLabels && showMinorGrid) {
24708 var label = this._repaintMinorText(x, step.getLabelMinor(current), orientation, className);
24709
24710 label.style.width = width + 'px'; // set width to prevent overflow
24711 }
24712
24713 if (isMajor && this.options.showMajorLabels) {
24714 if (x > 0) {
24715 if (xFirstMajorLabel == undefined) {
24716 xFirstMajorLabel = x;
24717 }
24718
24719 label = this._repaintMajorText(x, step.getLabelMajor(current), orientation, className);
24720 }
24721
24722 line = this._repaintMajorLine(x, width, orientation, className);
24723 } else {
24724 // minor line
24725 if (showMinorGrid) {
24726 line = this._repaintMinorLine(x, width, orientation, className);
24727 } else {
24728 if (line) {
24729 // adjust the width of the previous grid
24730 line.style.width = parseInt(line.style.width) + width + 'px';
24731 }
24732 }
24733 }
24734 }
24735
24736 if (count === MAX && !warnedForOverflow) {
24737 console.warn("Something is wrong with the Timeline scale. Limited drawing of grid lines to ".concat(MAX, " lines."));
24738 warnedForOverflow = true;
24739 } // create a major label on the left when needed
24740
24741
24742 if (this.options.showMajorLabels) {
24743 var leftTime = this.body.util.toTime(0),
24744 leftText = step.getLabelMajor(leftTime),
24745 widthText = leftText.length * (this.props.majorCharWidth || 10) + 10; // upper bound estimation
24746
24747 if (xFirstMajorLabel == undefined || widthText < xFirstMajorLabel) {
24748 this._repaintMajorText(0, leftText, orientation, className);
24749 }
24750 } // Cleanup leftover DOM elements from the redundant list
24751
24752
24753 util.forEach(this.dom.redundant, function (arr) {
24754 while (arr.length) {
24755 var elem = arr.pop();
24756
24757 if (elem && elem.parentNode) {
24758 elem.parentNode.removeChild(elem);
24759 }
24760 }
24761 });
24762};
24763/**
24764 * Create a minor label for the axis at position x
24765 * @param {number} x
24766 * @param {string} text
24767 * @param {string} orientation "top" or "bottom" (default)
24768 * @param {string} className
24769 * @return {Element} Returns the HTML element of the created label
24770 * @private
24771 */
24772
24773
24774TimeAxis.prototype._repaintMinorText = function (x, text, orientation, className) {
24775 // reuse redundant label
24776 var label = this.dom.redundant.minorTexts.shift();
24777
24778 if (!label) {
24779 // create new label
24780 var content = document.createTextNode('');
24781 label = document.createElement('div');
24782 label.appendChild(content);
24783 this.dom.foreground.appendChild(label);
24784 }
24785
24786 this.dom.minorTexts.push(label);
24787 label.innerHTML = text;
24788 label.style.top = orientation == 'top' ? this.props.majorLabelHeight + 'px' : '0';
24789
24790 if (this.options.rtl) {
24791 label.style.left = "";
24792 label.style.right = x + 'px';
24793 } else {
24794 label.style.left = x + 'px';
24795 }
24796
24797 label.className = 'vis-text vis-minor ' + className; //label.title = title; // TODO: this is a heavy operation
24798
24799 return label;
24800};
24801/**
24802 * Create a Major label for the axis at position x
24803 * @param {number} x
24804 * @param {string} text
24805 * @param {string} orientation "top" or "bottom" (default)
24806 * @param {string} className
24807 * @return {Element} Returns the HTML element of the created label
24808 * @private
24809 */
24810
24811
24812TimeAxis.prototype._repaintMajorText = function (x, text, orientation, className) {
24813 // reuse redundant label
24814 var label = this.dom.redundant.majorTexts.shift();
24815
24816 if (!label) {
24817 // create label
24818 var content = document.createElement('div');
24819 label = document.createElement('div');
24820 label.appendChild(content);
24821 this.dom.foreground.appendChild(label);
24822 }
24823
24824 label.childNodes[0].innerHTML = text;
24825 label.className = 'vis-text vis-major ' + className; //label.title = title; // TODO: this is a heavy operation
24826
24827 label.style.top = orientation == 'top' ? '0' : this.props.minorLabelHeight + 'px';
24828
24829 if (this.options.rtl) {
24830 label.style.left = "";
24831 label.style.right = x + 'px';
24832 } else {
24833 label.style.left = x + 'px';
24834 }
24835
24836 this.dom.majorTexts.push(label);
24837 return label;
24838};
24839/**
24840 * Create a minor line for the axis at position x
24841 * @param {number} x
24842 * @param {number} width
24843 * @param {string} orientation "top" or "bottom" (default)
24844 * @param {string} className
24845 * @return {Element} Returns the created line
24846 * @private
24847 */
24848
24849
24850TimeAxis.prototype._repaintMinorLine = function (x, width, orientation, className) {
24851 // reuse redundant line
24852 var line = this.dom.redundant.lines.shift();
24853
24854 if (!line) {
24855 // create vertical line
24856 line = document.createElement('div');
24857 this.dom.background.appendChild(line);
24858 }
24859
24860 this.dom.lines.push(line);
24861 var props = this.props;
24862
24863 if (orientation == 'top') {
24864 line.style.top = props.majorLabelHeight + 'px';
24865 } else {
24866 line.style.top = this.body.domProps.top.height + 'px';
24867 }
24868
24869 line.style.height = props.minorLineHeight + 'px';
24870
24871 if (this.options.rtl) {
24872 line.style.left = "";
24873 line.style.right = x - props.minorLineWidth / 2 + 'px';
24874 line.className = 'vis-grid vis-vertical-rtl vis-minor ' + className;
24875 } else {
24876 line.style.left = x - props.minorLineWidth / 2 + 'px';
24877 line.className = 'vis-grid vis-vertical vis-minor ' + className;
24878 }
24879
24880 line.style.width = width + 'px';
24881 return line;
24882};
24883/**
24884 * Create a Major line for the axis at position x
24885 * @param {number} x
24886 * @param {number} width
24887 * @param {string} orientation "top" or "bottom" (default)
24888 * @param {string} className
24889 * @return {Element} Returns the created line
24890 * @private
24891 */
24892
24893
24894TimeAxis.prototype._repaintMajorLine = function (x, width, orientation, className) {
24895 // reuse redundant line
24896 var line = this.dom.redundant.lines.shift();
24897
24898 if (!line) {
24899 // create vertical line
24900 line = document.createElement('div');
24901 this.dom.background.appendChild(line);
24902 }
24903
24904 this.dom.lines.push(line);
24905 var props = this.props;
24906
24907 if (orientation == 'top') {
24908 line.style.top = '0';
24909 } else {
24910 line.style.top = this.body.domProps.top.height + 'px';
24911 }
24912
24913 if (this.options.rtl) {
24914 line.style.left = "";
24915 line.style.right = x - props.majorLineWidth / 2 + 'px';
24916 line.className = 'vis-grid vis-vertical-rtl vis-major ' + className;
24917 } else {
24918 line.style.left = x - props.majorLineWidth / 2 + 'px';
24919 line.className = 'vis-grid vis-vertical vis-major ' + className;
24920 }
24921
24922 line.style.height = props.majorLineHeight + 'px';
24923 line.style.width = width + 'px';
24924 return line;
24925};
24926/**
24927 * Determine the size of text on the axis (both major and minor axis).
24928 * The size is calculated only once and then cached in this.props.
24929 * @private
24930 */
24931
24932
24933TimeAxis.prototype._calculateCharSize = function () {
24934 // Note: We calculate char size with every redraw. Size may change, for
24935 // example when any of the timelines parents had display:none for example.
24936 // determine the char width and height on the minor axis
24937 if (!this.dom.measureCharMinor) {
24938 this.dom.measureCharMinor = document.createElement('DIV');
24939 this.dom.measureCharMinor.className = 'vis-text vis-minor vis-measure';
24940 this.dom.measureCharMinor.style.position = 'absolute';
24941 this.dom.measureCharMinor.appendChild(document.createTextNode('0'));
24942 this.dom.foreground.appendChild(this.dom.measureCharMinor);
24943 }
24944
24945 this.props.minorCharHeight = this.dom.measureCharMinor.clientHeight;
24946 this.props.minorCharWidth = this.dom.measureCharMinor.clientWidth; // determine the char width and height on the major axis
24947
24948 if (!this.dom.measureCharMajor) {
24949 this.dom.measureCharMajor = document.createElement('DIV');
24950 this.dom.measureCharMajor.className = 'vis-text vis-major vis-measure';
24951 this.dom.measureCharMajor.style.position = 'absolute';
24952 this.dom.measureCharMajor.appendChild(document.createTextNode('0'));
24953 this.dom.foreground.appendChild(this.dom.measureCharMajor);
24954 }
24955
24956 this.props.majorCharHeight = this.dom.measureCharMajor.clientHeight;
24957 this.props.majorCharWidth = this.dom.measureCharMajor.clientWidth;
24958};
24959
24960var warnedForOverflow = false;
24961
24962var keycharm = createCommonjsModule$1(function (module, exports) {
24963 /**
24964 * Created by Alex on 11/6/2014.
24965 */
24966 // https://github.com/umdjs/umd/blob/master/returnExports.js#L40-L60
24967 // if the module has no dependencies, the above pattern can be simplified to
24968
24969 (function (root, factory) {
24970 {
24971 // Node. Does not work with strict CommonJS, but
24972 // only CommonJS-like environments that support module.exports,
24973 // like Node.
24974 module.exports = factory();
24975 }
24976 })(commonjsGlobal$1, function () {
24977 function keycharm(options) {
24978 var preventDefault = options && options.preventDefault || false;
24979 var container = options && options.container || window;
24980 var _exportFunctions = {};
24981 var _bound = {
24982 keydown: {},
24983 keyup: {}
24984 };
24985 var _keys = {};
24986 var i; // a - z
24987
24988 for (i = 97; i <= 122; i++) {
24989 _keys[String.fromCharCode(i)] = {
24990 code: 65 + (i - 97),
24991 shift: false
24992 };
24993 } // A - Z
24994
24995
24996 for (i = 65; i <= 90; i++) {
24997 _keys[String.fromCharCode(i)] = {
24998 code: i,
24999 shift: true
25000 };
25001 } // 0 - 9
25002
25003
25004 for (i = 0; i <= 9; i++) {
25005 _keys['' + i] = {
25006 code: 48 + i,
25007 shift: false
25008 };
25009 } // F1 - F12
25010
25011
25012 for (i = 1; i <= 12; i++) {
25013 _keys['F' + i] = {
25014 code: 111 + i,
25015 shift: false
25016 };
25017 } // num0 - num9
25018
25019
25020 for (i = 0; i <= 9; i++) {
25021 _keys['num' + i] = {
25022 code: 96 + i,
25023 shift: false
25024 };
25025 } // numpad misc
25026
25027
25028 _keys['num*'] = {
25029 code: 106,
25030 shift: false
25031 };
25032 _keys['num+'] = {
25033 code: 107,
25034 shift: false
25035 };
25036 _keys['num-'] = {
25037 code: 109,
25038 shift: false
25039 };
25040 _keys['num/'] = {
25041 code: 111,
25042 shift: false
25043 };
25044 _keys['num.'] = {
25045 code: 110,
25046 shift: false
25047 }; // arrows
25048
25049 _keys['left'] = {
25050 code: 37,
25051 shift: false
25052 };
25053 _keys['up'] = {
25054 code: 38,
25055 shift: false
25056 };
25057 _keys['right'] = {
25058 code: 39,
25059 shift: false
25060 };
25061 _keys['down'] = {
25062 code: 40,
25063 shift: false
25064 }; // extra keys
25065
25066 _keys['space'] = {
25067 code: 32,
25068 shift: false
25069 };
25070 _keys['enter'] = {
25071 code: 13,
25072 shift: false
25073 };
25074 _keys['shift'] = {
25075 code: 16,
25076 shift: undefined
25077 };
25078 _keys['esc'] = {
25079 code: 27,
25080 shift: false
25081 };
25082 _keys['backspace'] = {
25083 code: 8,
25084 shift: false
25085 };
25086 _keys['tab'] = {
25087 code: 9,
25088 shift: false
25089 };
25090 _keys['ctrl'] = {
25091 code: 17,
25092 shift: false
25093 };
25094 _keys['alt'] = {
25095 code: 18,
25096 shift: false
25097 };
25098 _keys['delete'] = {
25099 code: 46,
25100 shift: false
25101 };
25102 _keys['pageup'] = {
25103 code: 33,
25104 shift: false
25105 };
25106 _keys['pagedown'] = {
25107 code: 34,
25108 shift: false
25109 }; // symbols
25110
25111 _keys['='] = {
25112 code: 187,
25113 shift: false
25114 };
25115 _keys['-'] = {
25116 code: 189,
25117 shift: false
25118 };
25119 _keys[']'] = {
25120 code: 221,
25121 shift: false
25122 };
25123 _keys['['] = {
25124 code: 219,
25125 shift: false
25126 };
25127
25128 var down = function (event) {
25129 handleEvent(event, 'keydown');
25130 };
25131
25132 var up = function (event) {
25133 handleEvent(event, 'keyup');
25134 }; // handle the actualy bound key with the event
25135
25136
25137 var handleEvent = function (event, type) {
25138 if (_bound[type][event.keyCode] !== undefined) {
25139 var bound = _bound[type][event.keyCode];
25140
25141 for (var i = 0; i < bound.length; i++) {
25142 if (bound[i].shift === undefined) {
25143 bound[i].fn(event);
25144 } else if (bound[i].shift == true && event.shiftKey == true) {
25145 bound[i].fn(event);
25146 } else if (bound[i].shift == false && event.shiftKey == false) {
25147 bound[i].fn(event);
25148 }
25149 }
25150
25151 if (preventDefault == true) {
25152 event.preventDefault();
25153 }
25154 }
25155 }; // bind a key to a callback
25156
25157
25158 _exportFunctions.bind = function (key, callback, type) {
25159 if (type === undefined) {
25160 type = 'keydown';
25161 }
25162
25163 if (_keys[key] === undefined) {
25164 throw new Error("unsupported key: " + key);
25165 }
25166
25167 if (_bound[type][_keys[key].code] === undefined) {
25168 _bound[type][_keys[key].code] = [];
25169 }
25170
25171 _bound[type][_keys[key].code].push({
25172 fn: callback,
25173 shift: _keys[key].shift
25174 });
25175 }; // bind all keys to a call back (demo purposes)
25176
25177
25178 _exportFunctions.bindAll = function (callback, type) {
25179 if (type === undefined) {
25180 type = 'keydown';
25181 }
25182
25183 for (var key in _keys) {
25184 if (_keys.hasOwnProperty(key)) {
25185 _exportFunctions.bind(key, callback, type);
25186 }
25187 }
25188 }; // get the key label from an event
25189
25190
25191 _exportFunctions.getKey = function (event) {
25192 for (var key in _keys) {
25193 if (_keys.hasOwnProperty(key)) {
25194 if (event.shiftKey == true && _keys[key].shift == true && event.keyCode == _keys[key].code) {
25195 return key;
25196 } else if (event.shiftKey == false && _keys[key].shift == false && event.keyCode == _keys[key].code) {
25197 return key;
25198 } else if (event.keyCode == _keys[key].code && key == 'shift') {
25199 return key;
25200 }
25201 }
25202 }
25203
25204 return "unknown key, currently not supported";
25205 }; // unbind either a specific callback from a key or all of them (by leaving callback undefined)
25206
25207
25208 _exportFunctions.unbind = function (key, callback, type) {
25209 if (type === undefined) {
25210 type = 'keydown';
25211 }
25212
25213 if (_keys[key] === undefined) {
25214 throw new Error("unsupported key: " + key);
25215 }
25216
25217 if (callback !== undefined) {
25218 var newBindings = [];
25219 var bound = _bound[type][_keys[key].code];
25220
25221 if (bound !== undefined) {
25222 for (var i = 0; i < bound.length; i++) {
25223 if (!(bound[i].fn == callback && bound[i].shift == _keys[key].shift)) {
25224 newBindings.push(_bound[type][_keys[key].code][i]);
25225 }
25226 }
25227 }
25228
25229 _bound[type][_keys[key].code] = newBindings;
25230 } else {
25231 _bound[type][_keys[key].code] = [];
25232 }
25233 }; // reset all bound variables.
25234
25235
25236 _exportFunctions.reset = function () {
25237 _bound = {
25238 keydown: {},
25239 keyup: {}
25240 };
25241 }; // unbind all listeners and reset all variables.
25242
25243
25244 _exportFunctions.destroy = function () {
25245 _bound = {
25246 keydown: {},
25247 keyup: {}
25248 };
25249 container.removeEventListener('keydown', down, true);
25250 container.removeEventListener('keyup', up, true);
25251 }; // create listeners.
25252
25253
25254 container.addEventListener('keydown', down, true);
25255 container.addEventListener('keyup', up, true); // return the public functions.
25256
25257 return _exportFunctions;
25258 }
25259
25260 return keycharm;
25261 });
25262});
25263
25264/**
25265 * Turn an element into an clickToUse element.
25266 * When not active, the element has a transparent overlay. When the overlay is
25267 * clicked, the mode is changed to active.
25268 * When active, the element is displayed with a blue border around it, and
25269 * the interactive contents of the element can be used. When clicked outside
25270 * the element, the elements mode is changed to inactive.
25271 * @param {Element} container
25272 * @constructor Activator
25273 */
25274
25275function Activator(container) {
25276 this.active = false;
25277 this.dom = {
25278 container: container
25279 };
25280 this.dom.overlay = document.createElement('div');
25281 this.dom.overlay.className = 'vis-overlay';
25282 this.dom.container.appendChild(this.dom.overlay);
25283 this.hammer = hammer$1(this.dom.overlay);
25284 this.hammer.on('tap', this._onTapOverlay.bind(this)); // block all touch events (except tap)
25285
25286 var me = this;
25287 var events = ['tap', 'doubletap', 'press', 'pinch', 'pan', 'panstart', 'panmove', 'panend'];
25288 events.forEach(function (event) {
25289 me.hammer.on(event, function (event) {
25290 event.stopPropagation();
25291 });
25292 }); // attach a click event to the window, in order to deactivate when clicking outside the timeline
25293
25294 if (document && document.body) {
25295 this.onClick = function (event) {
25296 if (!_hasParent(event.target, container)) {
25297 me.deactivate();
25298 }
25299 };
25300
25301 document.body.addEventListener('click', this.onClick);
25302 }
25303
25304 if (this.keycharm !== undefined) {
25305 this.keycharm.destroy();
25306 }
25307
25308 this.keycharm = keycharm(); // keycharm listener only bounded when active)
25309
25310 this.escListener = this.deactivate.bind(this);
25311} // turn into an event emitter
25312
25313
25314emitterComponent(Activator.prototype); // The currently active activator
25315
25316Activator.current = null;
25317/**
25318 * Destroy the activator. Cleans up all created DOM and event listeners
25319 */
25320
25321Activator.prototype.destroy = function () {
25322 this.deactivate(); // remove dom
25323
25324 this.dom.overlay.parentNode.removeChild(this.dom.overlay); // remove global event listener
25325
25326 if (this.onClick) {
25327 document.body.removeEventListener('click', this.onClick);
25328 } // remove keycharm
25329
25330
25331 if (this.keycharm !== undefined) {
25332 this.keycharm.destroy();
25333 }
25334
25335 this.keycharm = null; // cleanup hammer instances
25336
25337 this.hammer.destroy();
25338 this.hammer = null; // FIXME: cleaning up hammer instances doesn't work (Timeline not removed from memory)
25339};
25340/**
25341 * Activate the element
25342 * Overlay is hidden, element is decorated with a blue shadow border
25343 */
25344
25345
25346Activator.prototype.activate = function () {
25347 // we allow only one active activator at a time
25348 if (Activator.current) {
25349 Activator.current.deactivate();
25350 }
25351
25352 Activator.current = this;
25353 this.active = true;
25354 this.dom.overlay.style.display = 'none';
25355 util.addClassName(this.dom.container, 'vis-active');
25356 this.emit('change');
25357 this.emit('activate'); // ugly hack: bind ESC after emitting the events, as the Network rebinds all
25358 // keyboard events on a 'change' event
25359
25360 this.keycharm.bind('esc', this.escListener);
25361};
25362/**
25363 * Deactivate the element
25364 * Overlay is displayed on top of the element
25365 */
25366
25367
25368Activator.prototype.deactivate = function () {
25369 this.active = false;
25370 this.dom.overlay.style.display = '';
25371 util.removeClassName(this.dom.container, 'vis-active');
25372 this.keycharm.unbind('esc', this.escListener);
25373 this.emit('change');
25374 this.emit('deactivate');
25375};
25376/**
25377 * Handle a tap event: activate the container
25378 * @param {Event} event The event
25379 * @private
25380 */
25381
25382
25383Activator.prototype._onTapOverlay = function (event) {
25384 // activate the container
25385 this.activate();
25386 event.stopPropagation();
25387};
25388/**
25389 * Test whether the element has the requested parent element somewhere in
25390 * its chain of parent nodes.
25391 * @param {HTMLElement} element
25392 * @param {HTMLElement} parent
25393 * @returns {boolean} Returns true when the parent is found somewhere in the
25394 * chain of parent nodes.
25395 * @private
25396 */
25397
25398
25399function _hasParent(element, parent) {
25400 while (element) {
25401 if (element === parent) {
25402 return true;
25403 }
25404
25405 element = element.parentNode;
25406 }
25407
25408 return false;
25409}
25410
25411var locales = createCommonjsModule$1(function (module, exports) {
25412 // English
25413 exports['en'] = {
25414 current: 'current',
25415 time: 'time'
25416 };
25417 exports['en_EN'] = exports['en'];
25418 exports['en_US'] = exports['en']; // Italiano
25419
25420 exports['it'] = {
25421 current: 'attuale',
25422 time: 'tempo'
25423 };
25424 exports['it_IT'] = exports['it'];
25425 exports['it_CH'] = exports['it']; // Dutch
25426
25427 exports['nl'] = {
25428 current: 'huidige',
25429 time: 'tijd'
25430 };
25431 exports['nl_NL'] = exports['nl'];
25432 exports['nl_BE'] = exports['nl']; // German
25433
25434 exports['de'] = {
25435 current: 'Aktuelle',
25436 time: 'Zeit'
25437 };
25438 exports['de_DE'] = exports['de']; // French
25439
25440 exports['fr'] = {
25441 current: 'actuel',
25442 time: 'heure'
25443 };
25444 exports['fr_FR'] = exports['fr'];
25445 exports['fr_CA'] = exports['fr'];
25446 exports['fr_BE'] = exports['fr']; // Espanol
25447
25448 exports['es'] = {
25449 current: 'corriente',
25450 time: 'hora'
25451 };
25452 exports['es_ES'] = exports['es']; // Ukrainian
25453
25454 exports['uk'] = {
25455 current: 'поточний',
25456 time: 'час'
25457 };
25458 exports['uk_UA'] = exports['uk']; // Russian
25459
25460 exports['ru'] = {
25461 current: 'текущее',
25462 time: 'время'
25463 };
25464 exports['ru_RU'] = exports['ru'];
25465});
25466
25467/**
25468 * A custom time bar
25469 * @param {{range: Range, dom: Object}} body
25470 * @param {Object} [options] Available parameters:
25471 * {number | string} id
25472 * {string} locales
25473 * {string} locale
25474 * @constructor CustomTime
25475 * @extends Component
25476 */
25477
25478function CustomTime(body, options) {
25479 this.body = body; // default options
25480
25481 this.defaultOptions = {
25482 moment: moment$3,
25483 locales: locales,
25484 locale: 'en',
25485 id: undefined,
25486 title: undefined,
25487 editable: true
25488 };
25489 this.options = util.extend({}, this.defaultOptions);
25490
25491 if (options && options.time) {
25492 this.customTime = options.time;
25493 } else {
25494 this.customTime = new Date();
25495 }
25496
25497 this.eventParams = {}; // stores state parameters while dragging the bar
25498
25499 this.setOptions(options); // create the DOM
25500
25501 this._create();
25502}
25503
25504CustomTime.prototype = new Component_1();
25505/**
25506 * Set options for the component. Options will be merged in current options.
25507 * @param {Object} options Available parameters:
25508 * {number | string} id
25509 * {string} locales
25510 * {string} locale
25511 */
25512
25513CustomTime.prototype.setOptions = function (options) {
25514 if (options) {
25515 // copy all options that we know
25516 util.selectiveExtend(['moment', 'locale', 'locales', 'id', 'editable'], this.options, options);
25517 }
25518};
25519/**
25520 * Create the DOM for the custom time
25521 * @private
25522 */
25523
25524
25525CustomTime.prototype._create = function () {
25526 var bar = document.createElement('div');
25527 bar['custom-time'] = this;
25528 bar.className = 'vis-custom-time ' + (!this.options.editable ? 'disabled ' : '') + (this.options.id || '');
25529 bar.style.position = 'absolute';
25530 bar.style.top = '0px';
25531 bar.style.height = '100%';
25532 this.bar = bar;
25533 var drag = document.createElement('div');
25534 drag.style.position = 'relative';
25535 drag.style.top = '0px';
25536 drag.style.left = '-10px';
25537 drag.style.height = '100%';
25538 drag.style.width = '20px';
25539 /**
25540 *
25541 * @param {WheelEvent} e
25542 */
25543
25544 function onMouseWheel(e) {
25545 this.body.range._onMouseWheel(e);
25546 }
25547
25548 if (drag.addEventListener) {
25549 // IE9, Chrome, Safari, Opera
25550 drag.addEventListener("mousewheel", onMouseWheel.bind(this), false); // Firefox
25551
25552 drag.addEventListener("DOMMouseScroll", onMouseWheel.bind(this), false);
25553 } else {
25554 // IE 6/7/8
25555 drag.attachEvent("onmousewheel", onMouseWheel.bind(this));
25556 }
25557
25558 bar.appendChild(drag); // if bar is editable by the user, attach drag handlers
25559
25560 if (this.options.editable) {
25561 // attach event listeners
25562 this.hammer = new hammer$1(drag);
25563 this.hammer.on('panstart', this._onDragStart.bind(this));
25564 this.hammer.on('panmove', this._onDrag.bind(this));
25565 this.hammer.on('panend', this._onDragEnd.bind(this));
25566 this.hammer.get('pan').set({
25567 threshold: 5,
25568 direction: hammer$1.DIRECTION_HORIZONTAL
25569 });
25570 }
25571};
25572/**
25573 * Destroy the CustomTime bar
25574 */
25575
25576
25577CustomTime.prototype.destroy = function () {
25578 this.hide();
25579 this.hammer.destroy();
25580 this.hammer = null;
25581 this.body = null;
25582};
25583/**
25584 * Repaint the component
25585 * @return {boolean} Returns true if the component is resized
25586 */
25587
25588
25589CustomTime.prototype.redraw = function () {
25590 var parent = this.body.dom.backgroundVertical;
25591
25592 if (this.bar.parentNode != parent) {
25593 // attach to the dom
25594 if (this.bar.parentNode) {
25595 this.bar.parentNode.removeChild(this.bar);
25596 }
25597
25598 parent.appendChild(this.bar);
25599 }
25600
25601 var x = this.body.util.toScreen(this.customTime);
25602 var locale = this.options.locales[this.options.locale];
25603
25604 if (!locale) {
25605 if (!this.warned) {
25606 console.log('WARNING: options.locales[\'' + this.options.locale + '\'] not found. See http://visjs.org/docs/timeline/#Localization');
25607 this.warned = true;
25608 }
25609
25610 locale = this.options.locales['en']; // fall back on english when not available
25611 }
25612
25613 var title = this.options.title; // To hide the title completely use empty string ''.
25614
25615 if (title === undefined) {
25616 title = locale.time + ': ' + this.options.moment(this.customTime).format('dddd, MMMM Do YYYY, H:mm:ss');
25617 title = title.charAt(0).toUpperCase() + title.substring(1);
25618 } else if (typeof title === "function") {
25619 title = title.call(this.customTime);
25620 }
25621
25622 this.bar.style.left = x + 'px';
25623 this.bar.title = title;
25624 return false;
25625};
25626/**
25627 * Remove the CustomTime from the DOM
25628 */
25629
25630
25631CustomTime.prototype.hide = function () {
25632 // remove the line from the DOM
25633 if (this.bar.parentNode) {
25634 this.bar.parentNode.removeChild(this.bar);
25635 }
25636};
25637/**
25638 * Set custom time.
25639 * @param {Date | number | string} time
25640 */
25641
25642
25643CustomTime.prototype.setCustomTime = function (time) {
25644 this.customTime = util.convert(time, 'Date');
25645 this.redraw();
25646};
25647/**
25648 * Retrieve the current custom time.
25649 * @return {Date} customTime
25650 */
25651
25652
25653CustomTime.prototype.getCustomTime = function () {
25654 return new Date(this.customTime.valueOf());
25655};
25656/**
25657 * Set custom title.
25658 * @param {Date | number | string} title
25659 */
25660
25661
25662CustomTime.prototype.setCustomTitle = function (title) {
25663 this.options.title = title;
25664};
25665/**
25666 * Start moving horizontally
25667 * @param {Event} event
25668 * @private
25669 */
25670
25671
25672CustomTime.prototype._onDragStart = function (event) {
25673 this.eventParams.dragging = true;
25674 this.eventParams.customTime = this.customTime;
25675 event.stopPropagation();
25676};
25677/**
25678 * Perform moving operating.
25679 * @param {Event} event
25680 * @private
25681 */
25682
25683
25684CustomTime.prototype._onDrag = function (event) {
25685 if (!this.eventParams.dragging) return;
25686 var x = this.body.util.toScreen(this.eventParams.customTime) + event.deltaX;
25687 var time = this.body.util.toTime(x);
25688 this.setCustomTime(time); // fire a timechange event
25689
25690 this.body.emitter.emit('timechange', {
25691 id: this.options.id,
25692 time: new Date(this.customTime.valueOf()),
25693 event: event
25694 });
25695 event.stopPropagation();
25696};
25697/**
25698 * Stop moving operating.
25699 * @param {Event} event
25700 * @private
25701 */
25702
25703
25704CustomTime.prototype._onDragEnd = function (event) {
25705 if (!this.eventParams.dragging) return; // fire a timechanged event
25706
25707 this.body.emitter.emit('timechanged', {
25708 id: this.options.id,
25709 time: new Date(this.customTime.valueOf()),
25710 event: event
25711 });
25712 event.stopPropagation();
25713};
25714/**
25715 * Find a custom time from an event target:
25716 * searches for the attribute 'custom-time' in the event target's element tree
25717 * @param {Event} event
25718 * @return {CustomTime | null} customTime
25719 */
25720
25721
25722CustomTime.customTimeFromTarget = function (event) {
25723 var target = event.target;
25724
25725 while (target) {
25726 if (target.hasOwnProperty('custom-time')) {
25727 return target['custom-time'];
25728 }
25729
25730 target = target.parentNode;
25731 }
25732
25733 return null;
25734};
25735
25736/**
25737 * Create a timeline visualization
25738 * @constructor Core
25739 */
25740
25741function Core() {} // turn Core into an event emitter
25742
25743
25744emitterComponent(Core.prototype);
25745/**
25746 * Create the main DOM for the Core: a root panel containing left, right,
25747 * top, bottom, content, and background panel.
25748 * @param {Element} container The container element where the Core will
25749 * be attached.
25750 * @protected
25751 */
25752
25753Core.prototype._create = function (container) {
25754 this.dom = {};
25755 this.dom.container = container;
25756 this.dom.container.style.position = 'relative';
25757 this.dom.root = document.createElement('div');
25758 this.dom.background = document.createElement('div');
25759 this.dom.backgroundVertical = document.createElement('div');
25760 this.dom.backgroundHorizontal = document.createElement('div');
25761 this.dom.centerContainer = document.createElement('div');
25762 this.dom.leftContainer = document.createElement('div');
25763 this.dom.rightContainer = document.createElement('div');
25764 this.dom.center = document.createElement('div');
25765 this.dom.left = document.createElement('div');
25766 this.dom.right = document.createElement('div');
25767 this.dom.top = document.createElement('div');
25768 this.dom.bottom = document.createElement('div');
25769 this.dom.shadowTop = document.createElement('div');
25770 this.dom.shadowBottom = document.createElement('div');
25771 this.dom.shadowTopLeft = document.createElement('div');
25772 this.dom.shadowBottomLeft = document.createElement('div');
25773 this.dom.shadowTopRight = document.createElement('div');
25774 this.dom.shadowBottomRight = document.createElement('div');
25775 this.dom.rollingModeBtn = document.createElement('div');
25776 this.dom.loadingScreen = document.createElement('div');
25777 this.dom.root.className = 'vis-timeline';
25778 this.dom.background.className = 'vis-panel vis-background';
25779 this.dom.backgroundVertical.className = 'vis-panel vis-background vis-vertical';
25780 this.dom.backgroundHorizontal.className = 'vis-panel vis-background vis-horizontal';
25781 this.dom.centerContainer.className = 'vis-panel vis-center';
25782 this.dom.leftContainer.className = 'vis-panel vis-left';
25783 this.dom.rightContainer.className = 'vis-panel vis-right';
25784 this.dom.top.className = 'vis-panel vis-top';
25785 this.dom.bottom.className = 'vis-panel vis-bottom';
25786 this.dom.left.className = 'vis-content';
25787 this.dom.center.className = 'vis-content';
25788 this.dom.right.className = 'vis-content';
25789 this.dom.shadowTop.className = 'vis-shadow vis-top';
25790 this.dom.shadowBottom.className = 'vis-shadow vis-bottom';
25791 this.dom.shadowTopLeft.className = 'vis-shadow vis-top';
25792 this.dom.shadowBottomLeft.className = 'vis-shadow vis-bottom';
25793 this.dom.shadowTopRight.className = 'vis-shadow vis-top';
25794 this.dom.shadowBottomRight.className = 'vis-shadow vis-bottom';
25795 this.dom.rollingModeBtn.className = 'vis-rolling-mode-btn';
25796 this.dom.loadingScreen.className = 'vis-loading-screen';
25797 this.dom.root.appendChild(this.dom.background);
25798 this.dom.root.appendChild(this.dom.backgroundVertical);
25799 this.dom.root.appendChild(this.dom.backgroundHorizontal);
25800 this.dom.root.appendChild(this.dom.centerContainer);
25801 this.dom.root.appendChild(this.dom.leftContainer);
25802 this.dom.root.appendChild(this.dom.rightContainer);
25803 this.dom.root.appendChild(this.dom.top);
25804 this.dom.root.appendChild(this.dom.bottom);
25805 this.dom.root.appendChild(this.dom.rollingModeBtn);
25806 this.dom.centerContainer.appendChild(this.dom.center);
25807 this.dom.leftContainer.appendChild(this.dom.left);
25808 this.dom.rightContainer.appendChild(this.dom.right);
25809 this.dom.centerContainer.appendChild(this.dom.shadowTop);
25810 this.dom.centerContainer.appendChild(this.dom.shadowBottom);
25811 this.dom.leftContainer.appendChild(this.dom.shadowTopLeft);
25812 this.dom.leftContainer.appendChild(this.dom.shadowBottomLeft);
25813 this.dom.rightContainer.appendChild(this.dom.shadowTopRight);
25814 this.dom.rightContainer.appendChild(this.dom.shadowBottomRight); // size properties of each of the panels
25815
25816 this.props = {
25817 root: {},
25818 background: {},
25819 centerContainer: {},
25820 leftContainer: {},
25821 rightContainer: {},
25822 center: {},
25823 left: {},
25824 right: {},
25825 top: {},
25826 bottom: {},
25827 border: {},
25828 scrollTop: 0,
25829 scrollTopMin: 0
25830 };
25831 this.on('rangechange', function () {
25832 if (this.initialDrawDone === true) {
25833 this._redraw();
25834 }
25835 }.bind(this));
25836 this.on('rangechanged', function () {
25837 if (!this.initialRangeChangeDone) {
25838 this.initialRangeChangeDone = true;
25839 }
25840 }.bind(this));
25841 this.on('touch', this._onTouch.bind(this));
25842 this.on('panmove', this._onDrag.bind(this));
25843 var me = this;
25844 this._origRedraw = this._redraw.bind(this);
25845 this._redraw = util.throttle(this._origRedraw);
25846 this.on('_change', function (properties) {
25847 if (me.itemSet && me.itemSet.initialItemSetDrawn && properties && properties.queue == true) {
25848 me._redraw();
25849 } else {
25850 me._origRedraw();
25851 }
25852 }); // create event listeners for all interesting events, these events will be
25853 // emitted via emitter
25854
25855 this.hammer = new hammer$1(this.dom.root);
25856 var pinchRecognizer = this.hammer.get('pinch').set({
25857 enable: true
25858 });
25859 pinchRecognizer && hammerUtil.disablePreventDefaultVertically(pinchRecognizer);
25860 this.hammer.get('pan').set({
25861 threshold: 5,
25862 direction: hammer$1.DIRECTION_HORIZONTAL
25863 });
25864 this.listeners = {};
25865 var events = ['tap', 'doubletap', 'press', 'pinch', 'pan', 'panstart', 'panmove', 'panend' // TODO: cleanup
25866 //'touch', 'pinch',
25867 //'tap', 'doubletap', 'hold',
25868 //'dragstart', 'drag', 'dragend',
25869 //'mousewheel', 'DOMMouseScroll' // DOMMouseScroll is needed for Firefox
25870 ];
25871 events.forEach(function (type) {
25872 var listener = function listener(event) {
25873 if (me.isActive()) {
25874 me.emit(type, event);
25875 }
25876 };
25877
25878 me.hammer.on(type, listener);
25879 me.listeners[type] = listener;
25880 }); // emulate a touch event (emitted before the start of a pan, pinch, tap, or press)
25881
25882 hammerUtil.onTouch(this.hammer, function (event) {
25883 me.emit('touch', event);
25884 }.bind(this)); // emulate a release event (emitted after a pan, pinch, tap, or press)
25885
25886 hammerUtil.onRelease(this.hammer, function (event) {
25887 me.emit('release', event);
25888 }.bind(this));
25889 /**
25890 *
25891 * @param {WheelEvent} event
25892 */
25893
25894 function onMouseWheel(event) {
25895 // Reasonable default wheel deltas
25896 var LINE_HEIGHT = 40;
25897 var PAGE_HEIGHT = 800;
25898
25899 if (this.isActive()) {
25900 this.emit('mousewheel', event);
25901 } // deltaX and deltaY normalization from jquery.mousewheel.js
25902
25903
25904 var deltaX = 0;
25905 var deltaY = 0; // Old school scrollwheel delta
25906
25907 if ('detail' in event) {
25908 deltaY = event.detail * -1;
25909 }
25910
25911 if ('wheelDelta' in event) {
25912 deltaY = event.wheelDelta;
25913 }
25914
25915 if ('wheelDeltaY' in event) {
25916 deltaY = event.wheelDeltaY;
25917 }
25918
25919 if ('wheelDeltaX' in event) {
25920 deltaX = event.wheelDeltaX * -1;
25921 } // Firefox < 17 horizontal scrolling related to DOMMouseScroll event
25922
25923
25924 if ('axis' in event && event.axis === event.HORIZONTAL_AXIS) {
25925 deltaX = deltaY * -1;
25926 deltaY = 0;
25927 } // New school wheel delta (wheel event)
25928
25929
25930 if ('deltaY' in event) {
25931 deltaY = event.deltaY * -1;
25932 }
25933
25934 if ('deltaX' in event) {
25935 deltaX = event.deltaX;
25936 } // Normalize deltas
25937
25938
25939 if (event.deltaMode) {
25940 if (event.deltaMode === 1) {
25941 // delta in LINE units
25942 deltaX *= LINE_HEIGHT;
25943 deltaY *= LINE_HEIGHT;
25944 } else {
25945 // delta in PAGE units
25946 deltaX *= LINE_HEIGHT;
25947 deltaY *= PAGE_HEIGHT;
25948 }
25949 } // Prevent scrolling when zooming (no zoom key, or pressing zoom key)
25950
25951
25952 if (!this.options.zoomKey || event[this.options.zoomKey]) return; // Don't preventDefault if you can't scroll
25953
25954 if (!this.options.verticalScroll && !this.options.horizontalScroll) return; // Prevent default actions caused by mouse wheel
25955 // (else the page and timeline both scroll)
25956
25957 event.preventDefault();
25958
25959 if (this.options.verticalScroll && Math.abs(deltaY) >= Math.abs(deltaX)) {
25960 var current = this.props.scrollTop;
25961 var adjusted = current + deltaY;
25962
25963 if (this.isActive()) {
25964 this._setScrollTop(adjusted);
25965
25966 this._redraw();
25967
25968 this.emit('scroll', event);
25969 }
25970 } else if (this.options.horizontalScroll) {
25971 var delta = Math.abs(deltaX) >= Math.abs(deltaY) ? deltaX : deltaY; // calculate a single scroll jump relative to the range scale
25972
25973 var diff = delta / 120 * (this.range.end - this.range.start) / 20; // calculate new start and end
25974
25975 var newStart = this.range.start + diff;
25976 var newEnd = this.range.end + diff;
25977 var options = {
25978 animation: false,
25979 byUser: true,
25980 event: event
25981 };
25982 this.range.setRange(newStart, newEnd, options);
25983 }
25984 } // Add modern wheel event listener
25985
25986
25987 if (this.dom.centerContainer.addEventListener) {
25988 var wheel = "onwheel" in document.createElement("div") ? "wheel" : // Modern browsers support "wheel"
25989 document.onmousewheel !== undefined ? "mousewheel" : // Webkit and IE support at least "mousewheel"
25990 "DOMMouseScroll"; // Older Firefox versions like "DOMMouseScroll"
25991
25992 this.dom.centerContainer.addEventListener(wheel, onMouseWheel.bind(this), false);
25993 } else {
25994 // IE 6/7/8
25995 this.dom.centerContainer.attachEvent("onmousewheel", onMouseWheel.bind(this));
25996 }
25997 /**
25998 *
25999 * @param {scroll} event
26000 */
26001
26002
26003 function onMouseScrollSide(event) {
26004 if (!me.options.verticalScroll) return;
26005 event.preventDefault();
26006
26007 if (me.isActive()) {
26008 var adjusted = -event.target.scrollTop;
26009
26010 me._setScrollTop(adjusted);
26011
26012 me._redraw();
26013
26014 me.emit('scrollSide', event);
26015 }
26016 }
26017
26018 this.dom.left.parentNode.addEventListener('scroll', onMouseScrollSide.bind(this));
26019 this.dom.right.parentNode.addEventListener('scroll', onMouseScrollSide.bind(this));
26020 var itemAddedToTimeline = false;
26021 /**
26022 *
26023 * @param {dragover} event
26024 * @returns {boolean}
26025 */
26026
26027 function handleDragOver(event) {
26028 if (event.preventDefault) {
26029 event.preventDefault(); // Necessary. Allows us to drop.
26030 } // make sure your target is a vis element
26031
26032
26033 if (!event.target.className.indexOf("vis") > -1) return; // make sure only one item is added every time you're over the timeline
26034
26035 if (itemAddedToTimeline) return;
26036 event.dataTransfer.dropEffect = 'move';
26037 itemAddedToTimeline = true;
26038 return false;
26039 }
26040 /**
26041 *
26042 * @param {drop} event
26043 * @returns {boolean}
26044 */
26045
26046
26047 function handleDrop(event) {
26048 // prevent redirect to blank page - Firefox
26049 if (event.preventDefault) {
26050 event.preventDefault();
26051 }
26052
26053 if (event.stopPropagation) {
26054 event.stopPropagation();
26055 } // return when dropping non-vis items
26056
26057
26058 try {
26059 var itemData = JSON.parse(event.dataTransfer.getData("text"));
26060 if (!itemData || !itemData.content) return;
26061 } catch (err) {
26062 return false;
26063 }
26064
26065 itemAddedToTimeline = false;
26066 event.center = {
26067 x: event.clientX,
26068 y: event.clientY
26069 };
26070
26071 if (itemData.target !== 'item') {
26072 me.itemSet._onAddItem(event);
26073 } else {
26074 me.itemSet._onDropObjectOnItem(event);
26075 }
26076
26077 me.emit('drop', me.getEventProperties(event));
26078 return false;
26079 }
26080
26081 this.dom.center.addEventListener('dragover', handleDragOver.bind(this), false);
26082 this.dom.center.addEventListener('drop', handleDrop.bind(this), false);
26083 this.customTimes = []; // store state information needed for touch events
26084
26085 this.touch = {};
26086 this.redrawCount = 0;
26087 this.initialDrawDone = false;
26088 this.initialRangeChangeDone = false; // attach the root panel to the provided container
26089
26090 if (!container) throw new Error('No container provided');
26091 container.appendChild(this.dom.root);
26092 container.appendChild(this.dom.loadingScreen);
26093};
26094/**
26095 * Set options. Options will be passed to all components loaded in the Timeline.
26096 * @param {Object} [options]
26097 * {String} orientation
26098 * Vertical orientation for the Timeline,
26099 * can be 'bottom' (default) or 'top'.
26100 * {string | number} width
26101 * Width for the timeline, a number in pixels or
26102 * a css string like '1000px' or '75%'. '100%' by default.
26103 * {string | number} height
26104 * Fixed height for the Timeline, a number in pixels or
26105 * a css string like '400px' or '75%'. If undefined,
26106 * The Timeline will automatically size such that
26107 * its contents fit.
26108 * {string | number} minHeight
26109 * Minimum height for the Timeline, a number in pixels or
26110 * a css string like '400px' or '75%'.
26111 * {string | number} maxHeight
26112 * Maximum height for the Timeline, a number in pixels or
26113 * a css string like '400px' or '75%'.
26114 * {number | Date | string} start
26115 * Start date for the visible window
26116 * {number | Date | string} end
26117 * End date for the visible window
26118 */
26119
26120
26121Core.prototype.setOptions = function (options) {
26122 if (options) {
26123 // copy the known options
26124 var fields = ['width', 'height', 'minHeight', 'maxHeight', 'autoResize', 'start', 'end', 'clickToUse', 'dataAttributes', 'hiddenDates', 'locale', 'locales', 'moment', 'rtl', 'zoomKey', 'horizontalScroll', 'verticalScroll'];
26125 util.selectiveExtend(fields, this.options, options);
26126 this.dom.rollingModeBtn.style.visibility = 'hidden';
26127
26128 if (this.options.rtl) {
26129 this.dom.container.style.direction = "rtl";
26130 this.dom.backgroundVertical.className = 'vis-panel vis-background vis-vertical-rtl';
26131 }
26132
26133 if (this.options.verticalScroll) {
26134 if (this.options.rtl) {
26135 this.dom.rightContainer.className = 'vis-panel vis-right vis-vertical-scroll';
26136 } else {
26137 this.dom.leftContainer.className = 'vis-panel vis-left vis-vertical-scroll';
26138 }
26139 }
26140
26141 if (_typeof$1(this.options.orientation) !== 'object') {
26142 this.options.orientation = {
26143 item: undefined,
26144 axis: undefined
26145 };
26146 }
26147
26148 if ('orientation' in options) {
26149 if (typeof options.orientation === 'string') {
26150 this.options.orientation = {
26151 item: options.orientation,
26152 axis: options.orientation
26153 };
26154 } else if (_typeof$1(options.orientation) === 'object') {
26155 if ('item' in options.orientation) {
26156 this.options.orientation.item = options.orientation.item;
26157 }
26158
26159 if ('axis' in options.orientation) {
26160 this.options.orientation.axis = options.orientation.axis;
26161 }
26162 }
26163 }
26164
26165 if (this.options.orientation.axis === 'both') {
26166 if (!this.timeAxis2) {
26167 var timeAxis2 = this.timeAxis2 = new TimeAxis(this.body);
26168
26169 timeAxis2.setOptions = function (options) {
26170 var _options = options ? util.extend({}, options) : {};
26171
26172 _options.orientation = 'top'; // override the orientation option, always top
26173
26174 TimeAxis.prototype.setOptions.call(timeAxis2, _options);
26175 };
26176
26177 this.components.push(timeAxis2);
26178 }
26179 } else {
26180 if (this.timeAxis2) {
26181 var index = this.components.indexOf(this.timeAxis2);
26182
26183 if (index !== -1) {
26184 this.components.splice(index, 1);
26185 }
26186
26187 this.timeAxis2.destroy();
26188 this.timeAxis2 = null;
26189 }
26190 } // if the graph2d's drawPoints is a function delegate the callback to the onRender property
26191
26192
26193 if (typeof options.drawPoints == 'function') {
26194 options.drawPoints = {
26195 onRender: options.drawPoints
26196 };
26197 }
26198
26199 if ('hiddenDates' in this.options) {
26200 DateUtil.convertHiddenOptions(this.options.moment, this.body, this.options.hiddenDates);
26201 }
26202
26203 if ('clickToUse' in options) {
26204 if (options.clickToUse) {
26205 if (!this.activator) {
26206 this.activator = new Activator(this.dom.root);
26207 }
26208 } else {
26209 if (this.activator) {
26210 this.activator.destroy();
26211 delete this.activator;
26212 }
26213 }
26214 }
26215
26216 if ('showCustomTime' in options) {
26217 throw new Error('Option `showCustomTime` is deprecated. Create a custom time bar via timeline.addCustomTime(time [, id])');
26218 } // enable/disable autoResize
26219
26220
26221 this._initAutoResize();
26222 } // propagate options to all components
26223
26224
26225 this.components.forEach(function (component) {
26226 return component.setOptions(options);
26227 }); // enable/disable configure
26228
26229 if ('configure' in options) {
26230 if (!this.configurator) {
26231 this.configurator = this._createConfigurator();
26232 }
26233
26234 this.configurator.setOptions(options.configure); // collect the settings of all components, and pass them to the configuration system
26235
26236 var appliedOptions = util.deepExtend({}, this.options);
26237 this.components.forEach(function (component) {
26238 util.deepExtend(appliedOptions, component.options);
26239 });
26240 this.configurator.setModuleOptions({
26241 global: appliedOptions
26242 });
26243 }
26244
26245 this._redraw();
26246};
26247/**
26248 * Returns true when the Timeline is active.
26249 * @returns {boolean}
26250 */
26251
26252
26253Core.prototype.isActive = function () {
26254 return !this.activator || this.activator.active;
26255};
26256/**
26257 * Destroy the Core, clean up all DOM elements and event listeners.
26258 */
26259
26260
26261Core.prototype.destroy = function () {
26262 // unbind datasets
26263 this.setItems(null);
26264 this.setGroups(null); // remove all event listeners
26265
26266 this.off(); // stop checking for changed size
26267
26268 this._stopAutoResize(); // remove from DOM
26269
26270
26271 if (this.dom.root.parentNode) {
26272 this.dom.root.parentNode.removeChild(this.dom.root);
26273 }
26274
26275 this.dom = null; // remove Activator
26276
26277 if (this.activator) {
26278 this.activator.destroy();
26279 delete this.activator;
26280 } // cleanup hammer touch events
26281
26282
26283 for (var event in this.listeners) {
26284 if (this.listeners.hasOwnProperty(event)) {
26285 delete this.listeners[event];
26286 }
26287 }
26288
26289 this.listeners = null;
26290 this.hammer && this.hammer.destroy();
26291 this.hammer = null; // give all components the opportunity to cleanup
26292
26293 this.components.forEach(function (component) {
26294 return component.destroy();
26295 });
26296 this.body = null;
26297};
26298/**
26299 * Set a custom time bar
26300 * @param {Date} time
26301 * @param {number} [id=undefined] Optional id of the custom time bar to be adjusted.
26302 */
26303
26304
26305Core.prototype.setCustomTime = function (time, id) {
26306 var customTimes = this.customTimes.filter(function (component) {
26307 return id === component.options.id;
26308 });
26309
26310 if (customTimes.length === 0) {
26311 throw new Error('No custom time bar found with id ' + JSON.stringify(id));
26312 }
26313
26314 if (customTimes.length > 0) {
26315 customTimes[0].setCustomTime(time);
26316 }
26317};
26318/**
26319 * Retrieve the current custom time.
26320 * @param {number} [id=undefined] Id of the custom time bar.
26321 * @return {Date | undefined} customTime
26322 */
26323
26324
26325Core.prototype.getCustomTime = function (id) {
26326 var customTimes = this.customTimes.filter(function (component) {
26327 return component.options.id === id;
26328 });
26329
26330 if (customTimes.length === 0) {
26331 throw new Error('No custom time bar found with id ' + JSON.stringify(id));
26332 }
26333
26334 return customTimes[0].getCustomTime();
26335};
26336/**
26337 * Set a custom title for the custom time bar.
26338 * @param {string} [title] Custom title
26339 * @param {number} [id=undefined] Id of the custom time bar.
26340 * @returns {*}
26341 */
26342
26343
26344Core.prototype.setCustomTimeTitle = function (title, id) {
26345 var customTimes = this.customTimes.filter(function (component) {
26346 return component.options.id === id;
26347 });
26348
26349 if (customTimes.length === 0) {
26350 throw new Error('No custom time bar found with id ' + JSON.stringify(id));
26351 }
26352
26353 if (customTimes.length > 0) {
26354 return customTimes[0].setCustomTitle(title);
26355 }
26356};
26357/**
26358 * Retrieve meta information from an event.
26359 * Should be overridden by classes extending Core
26360 * @param {Event} event
26361 * @return {Object} An object with related information.
26362 */
26363
26364
26365Core.prototype.getEventProperties = function (event) {
26366 return {
26367 event: event
26368 };
26369};
26370/**
26371 * Add custom vertical bar
26372 * @param {Date | string | number} [time] A Date, unix timestamp, or
26373 * ISO date string. Time point where
26374 * the new bar should be placed.
26375 * If not provided, `new Date()` will
26376 * be used.
26377 * @param {number | string} [id=undefined] Id of the new bar. Optional
26378 * @param {object} [options={}] Control options for the new bar. Supported
26379 * optoins are:
26380 * editable {true, false} determines whether the
26381 * bar can be dragged by the user. Default is true.
26382 * @return {number | string} Returns the id of the new bar
26383 */
26384
26385
26386Core.prototype.addCustomTime = function (time, id, options) {
26387 var timestamp = time !== undefined ? util.convert(time, 'Date').valueOf() : new Date();
26388 var exists = this.customTimes.some(function (customTime) {
26389 return customTime.options.id === id;
26390 });
26391
26392 if (exists) {
26393 throw new Error('A custom time with id ' + JSON.stringify(id) + ' already exists');
26394 }
26395
26396 var customTime = new CustomTime(this.body, util.extend({}, this.options, options, {
26397 time: timestamp,
26398 id: id
26399 }));
26400 this.customTimes.push(customTime);
26401 this.components.push(customTime);
26402
26403 this._redraw();
26404
26405 return id;
26406};
26407/**
26408 * Remove previously added custom bar
26409 * @param {int} id ID of the custom bar to be removed
26410 * [at]returns {boolean} True if the bar exists and is removed, false otherwise
26411 */
26412
26413
26414Core.prototype.removeCustomTime = function (id) {
26415 var customTimes = this.customTimes.filter(function (bar) {
26416 return bar.options.id === id;
26417 });
26418
26419 if (customTimes.length === 0) {
26420 throw new Error('No custom time bar found with id ' + JSON.stringify(id));
26421 }
26422
26423 customTimes.forEach(function (customTime) {
26424 this.customTimes.splice(this.customTimes.indexOf(customTime), 1);
26425 this.components.splice(this.components.indexOf(customTime), 1);
26426 customTime.destroy();
26427 }.bind(this));
26428};
26429/**
26430 * Get the id's of the currently visible items.
26431 * @returns {Array} The ids of the visible items
26432 */
26433
26434
26435Core.prototype.getVisibleItems = function () {
26436 return this.itemSet && this.itemSet.getVisibleItems() || [];
26437};
26438/**
26439 * Get the id's of the currently visible groups.
26440 * @returns {Array} The ids of the visible groups
26441 */
26442
26443
26444Core.prototype.getVisibleGroups = function () {
26445 return this.itemSet && this.itemSet.getVisibleGroups() || [];
26446};
26447/**
26448 * Set Core window such that it fits all items
26449 * @param {Object} [options] Available options:
26450 * `animation: boolean | {duration: number, easingFunction: string}`
26451 * If true (default), the range is animated
26452 * smoothly to the new window. An object can be
26453 * provided to specify duration and easing function.
26454 * Default duration is 500 ms, and default easing
26455 * function is 'easeInOutQuad'.
26456 * @param {function} [callback] a callback funtion to be executed at the end of this function
26457 */
26458
26459
26460Core.prototype.fit = function (options, callback) {
26461 var range = this.getDataRange(); // skip range set if there is no min and max date
26462
26463 if (range.min === null && range.max === null) {
26464 return;
26465 } // apply a margin of 1% left and right of the data
26466
26467
26468 var interval = range.max - range.min;
26469 var min = new Date(range.min.valueOf() - interval * 0.01);
26470 var max = new Date(range.max.valueOf() + interval * 0.01);
26471 var animation = options && options.animation !== undefined ? options.animation : true;
26472 this.range.setRange(min, max, {
26473 animation: animation
26474 }, callback);
26475};
26476/**
26477 * Calculate the data range of the items start and end dates
26478 * [at]returns {{min: [Date], max: [Date]}}
26479 * @protected
26480 */
26481
26482
26483Core.prototype.getDataRange = function () {
26484 // must be implemented by Timeline and Graph2d
26485 throw new Error('Cannot invoke abstract method getDataRange');
26486};
26487/**
26488 * Set the visible window. Both parameters are optional, you can change only
26489 * start or only end. Syntax:
26490 *
26491 * TimeLine.setWindow(start, end)
26492 * TimeLine.setWindow(start, end, options)
26493 * TimeLine.setWindow(range)
26494 *
26495 * Where start and end can be a Date, number, or string, and range is an
26496 * object with properties start and end.
26497 *
26498 * @param {Date | number | string | Object} [start] Start date of visible window
26499 * @param {Date | number | string} [end] End date of visible window
26500 * @param {Object} [options] Available options:
26501 * `animation: boolean | {duration: number, easingFunction: string}`
26502 * If true (default), the range is animated
26503 * smoothly to the new window. An object can be
26504 * provided to specify duration and easing function.
26505 * Default duration is 500 ms, and default easing
26506 * function is 'easeInOutQuad'.
26507 * @param {function} [callback] a callback funtion to be executed at the end of this function
26508 */
26509
26510
26511Core.prototype.setWindow = function (start, end, options, callback) {
26512 if (typeof arguments[2] == "function") {
26513 callback = arguments[2];
26514 options = {};
26515 }
26516
26517 var animation;
26518 var range;
26519
26520 if (arguments.length == 1) {
26521 range = arguments[0];
26522 animation = range.animation !== undefined ? range.animation : true;
26523 this.range.setRange(range.start, range.end, {
26524 animation: animation
26525 });
26526 } else if (arguments.length == 2 && typeof arguments[1] == "function") {
26527 range = arguments[0];
26528 callback = arguments[1];
26529 animation = range.animation !== undefined ? range.animation : true;
26530 this.range.setRange(range.start, range.end, {
26531 animation: animation
26532 }, callback);
26533 } else {
26534 animation = options && options.animation !== undefined ? options.animation : true;
26535 this.range.setRange(start, end, {
26536 animation: animation
26537 }, callback);
26538 }
26539};
26540/**
26541 * Move the window such that given time is centered on screen.
26542 * @param {Date | number | string} time
26543 * @param {Object} [options] Available options:
26544 * `animation: boolean | {duration: number, easingFunction: string}`
26545 * If true (default), the range is animated
26546 * smoothly to the new window. An object can be
26547 * provided to specify duration and easing function.
26548 * Default duration is 500 ms, and default easing
26549 * function is 'easeInOutQuad'.
26550 * @param {function} [callback] a callback funtion to be executed at the end of this function
26551 */
26552
26553
26554Core.prototype.moveTo = function (time, options, callback) {
26555 if (typeof arguments[1] == "function") {
26556 callback = arguments[1];
26557 options = {};
26558 }
26559
26560 var interval = this.range.end - this.range.start;
26561 var t = util.convert(time, 'Date').valueOf();
26562 var start = t - interval / 2;
26563 var end = t + interval / 2;
26564 var animation = options && options.animation !== undefined ? options.animation : true;
26565 this.range.setRange(start, end, {
26566 animation: animation
26567 }, callback);
26568};
26569/**
26570 * Get the visible window
26571 * @return {{start: Date, end: Date}} Visible range
26572 */
26573
26574
26575Core.prototype.getWindow = function () {
26576 var range = this.range.getRange();
26577 return {
26578 start: new Date(range.start),
26579 end: new Date(range.end)
26580 };
26581};
26582/**
26583 * Zoom in the window such that given time is centered on screen.
26584 * @param {number} percentage - must be between [0..1]
26585 * @param {Object} [options] Available options:
26586 * `animation: boolean | {duration: number, easingFunction: string}`
26587 * If true (default), the range is animated
26588 * smoothly to the new window. An object can be
26589 * provided to specify duration and easing function.
26590 * Default duration is 500 ms, and default easing
26591 * function is 'easeInOutQuad'.
26592 * @param {function} [callback] a callback funtion to be executed at the end of this function
26593 */
26594
26595
26596Core.prototype.zoomIn = function (percentage, options, callback) {
26597 if (!percentage || percentage < 0 || percentage > 1) return;
26598
26599 if (typeof arguments[1] == "function") {
26600 callback = arguments[1];
26601 options = {};
26602 }
26603
26604 var range = this.getWindow();
26605 var start = range.start.valueOf();
26606 var end = range.end.valueOf();
26607 var interval = end - start;
26608 var newInterval = interval / (1 + percentage);
26609 var distance = (interval - newInterval) / 2;
26610 var newStart = start + distance;
26611 var newEnd = end - distance;
26612 this.setWindow(newStart, newEnd, options, callback);
26613};
26614/**
26615 * Zoom out the window such that given time is centered on screen.
26616 * @param {number} percentage - must be between [0..1]
26617 * @param {Object} [options] Available options:
26618 * `animation: boolean | {duration: number, easingFunction: string}`
26619 * If true (default), the range is animated
26620 * smoothly to the new window. An object can be
26621 * provided to specify duration and easing function.
26622 * Default duration is 500 ms, and default easing
26623 * function is 'easeInOutQuad'.
26624 * @param {function} [callback] a callback funtion to be executed at the end of this function
26625 */
26626
26627
26628Core.prototype.zoomOut = function (percentage, options, callback) {
26629 if (!percentage || percentage < 0 || percentage > 1) return;
26630
26631 if (typeof arguments[1] == "function") {
26632 callback = arguments[1];
26633 options = {};
26634 }
26635
26636 var range = this.getWindow();
26637 var start = range.start.valueOf();
26638 var end = range.end.valueOf();
26639 var interval = end - start;
26640 var newStart = start - interval * percentage / 2;
26641 var newEnd = end + interval * percentage / 2;
26642 this.setWindow(newStart, newEnd, options, callback);
26643};
26644/**
26645 * Force a redraw. Can be overridden by implementations of Core
26646 *
26647 * Note: this function will be overridden on construction with a trottled version
26648 */
26649
26650
26651Core.prototype.redraw = function () {
26652 this._redraw();
26653};
26654/**
26655 * Redraw for internal use. Redraws all components. See also the public
26656 * method redraw.
26657 * @protected
26658 */
26659
26660
26661Core.prototype._redraw = function () {
26662 this.redrawCount++;
26663 var resized = false;
26664 var options = this.options;
26665 var props = this.props;
26666 var dom = this.dom;
26667 if (!dom || !dom.container || dom.root.offsetWidth == 0) return; // when destroyed, or invisible
26668
26669 DateUtil.updateHiddenDates(this.options.moment, this.body, this.options.hiddenDates); // update class names
26670
26671 if (options.orientation == 'top') {
26672 util.addClassName(dom.root, 'vis-top');
26673 util.removeClassName(dom.root, 'vis-bottom');
26674 } else {
26675 util.removeClassName(dom.root, 'vis-top');
26676 util.addClassName(dom.root, 'vis-bottom');
26677 } // update root width and height options
26678
26679
26680 dom.root.style.maxHeight = util.option.asSize(options.maxHeight, '');
26681 dom.root.style.minHeight = util.option.asSize(options.minHeight, '');
26682 dom.root.style.width = util.option.asSize(options.width, ''); // calculate border widths
26683
26684 props.border.left = (dom.centerContainer.offsetWidth - dom.centerContainer.clientWidth) / 2;
26685 props.border.right = props.border.left;
26686 props.border.top = (dom.centerContainer.offsetHeight - dom.centerContainer.clientHeight) / 2;
26687 props.border.bottom = props.border.top;
26688 props.borderRootHeight = dom.root.offsetHeight - dom.root.clientHeight;
26689 props.borderRootWidth = dom.root.offsetWidth - dom.root.clientWidth; // workaround for a bug in IE: the clientWidth of an element with
26690 // a height:0px and overflow:hidden is not calculated and always has value 0
26691
26692 if (dom.centerContainer.clientHeight === 0) {
26693 props.border.left = props.border.top;
26694 props.border.right = props.border.left;
26695 }
26696
26697 if (dom.root.clientHeight === 0) {
26698 props.borderRootWidth = props.borderRootHeight;
26699 } // calculate the heights. If any of the side panels is empty, we set the height to
26700 // minus the border width, such that the border will be invisible
26701
26702
26703 props.center.height = dom.center.offsetHeight;
26704 props.left.height = dom.left.offsetHeight;
26705 props.right.height = dom.right.offsetHeight;
26706 props.top.height = dom.top.clientHeight || -props.border.top;
26707 props.bottom.height = dom.bottom.clientHeight || -props.border.bottom; // TODO: compensate borders when any of the panels is empty.
26708 // apply auto height
26709 // TODO: only calculate autoHeight when needed (else we cause an extra reflow/repaint of the DOM)
26710
26711 var contentHeight = Math.max(props.left.height, props.center.height, props.right.height);
26712 var autoHeight = props.top.height + contentHeight + props.bottom.height + props.borderRootHeight + props.border.top + props.border.bottom;
26713 dom.root.style.height = util.option.asSize(options.height, autoHeight + 'px'); // calculate heights of the content panels
26714
26715 props.root.height = dom.root.offsetHeight;
26716 props.background.height = props.root.height - props.borderRootHeight;
26717 var containerHeight = props.root.height - props.top.height - props.bottom.height - props.borderRootHeight;
26718 props.centerContainer.height = containerHeight;
26719 props.leftContainer.height = containerHeight;
26720 props.rightContainer.height = props.leftContainer.height; // calculate the widths of the panels
26721
26722 props.root.width = dom.root.offsetWidth;
26723 props.background.width = props.root.width - props.borderRootWidth;
26724
26725 if (!this.initialDrawDone) {
26726 props.scrollbarWidth = util.getScrollBarWidth();
26727 }
26728
26729 if (options.verticalScroll) {
26730 if (options.rtl) {
26731 props.left.width = dom.leftContainer.clientWidth || -props.border.left;
26732 props.right.width = dom.rightContainer.clientWidth + props.scrollbarWidth || -props.border.right;
26733 } else {
26734 props.left.width = dom.leftContainer.clientWidth + props.scrollbarWidth || -props.border.left;
26735 props.right.width = dom.rightContainer.clientWidth || -props.border.right;
26736 }
26737 } else {
26738 props.left.width = dom.leftContainer.clientWidth || -props.border.left;
26739 props.right.width = dom.rightContainer.clientWidth || -props.border.right;
26740 }
26741
26742 this._setDOM(); // update the scrollTop, feasible range for the offset can be changed
26743 // when the height of the Core or of the contents of the center changed
26744
26745
26746 var offset = this._updateScrollTop(); // reposition the scrollable contents
26747
26748
26749 if (options.orientation.item != 'top') {
26750 offset += Math.max(props.centerContainer.height - props.center.height - props.border.top - props.border.bottom, 0);
26751 }
26752
26753 dom.center.style.top = offset + 'px'; // show shadows when vertical scrolling is available
26754
26755 var visibilityTop = props.scrollTop == 0 ? 'hidden' : '';
26756 var visibilityBottom = props.scrollTop == props.scrollTopMin ? 'hidden' : '';
26757 dom.shadowTop.style.visibility = visibilityTop;
26758 dom.shadowBottom.style.visibility = visibilityBottom;
26759 dom.shadowTopLeft.style.visibility = visibilityTop;
26760 dom.shadowBottomLeft.style.visibility = visibilityBottom;
26761 dom.shadowTopRight.style.visibility = visibilityTop;
26762 dom.shadowBottomRight.style.visibility = visibilityBottom;
26763
26764 if (options.verticalScroll) {
26765 dom.rightContainer.className = 'vis-panel vis-right vis-vertical-scroll';
26766 dom.leftContainer.className = 'vis-panel vis-left vis-vertical-scroll';
26767 dom.shadowTopRight.style.visibility = "hidden";
26768 dom.shadowBottomRight.style.visibility = "hidden";
26769 dom.shadowTopLeft.style.visibility = "hidden";
26770 dom.shadowBottomLeft.style.visibility = "hidden";
26771 dom.left.style.top = '0px';
26772 dom.right.style.top = '0px';
26773 }
26774
26775 if (!options.verticalScroll || props.center.height < props.centerContainer.height) {
26776 dom.left.style.top = offset + 'px';
26777 dom.right.style.top = offset + 'px';
26778 dom.rightContainer.className = dom.rightContainer.className.replace(new RegExp('(?:^|\\s)' + 'vis-vertical-scroll' + '(?:\\s|$)'), ' ');
26779 dom.leftContainer.className = dom.leftContainer.className.replace(new RegExp('(?:^|\\s)' + 'vis-vertical-scroll' + '(?:\\s|$)'), ' ');
26780 props.left.width = dom.leftContainer.clientWidth || -props.border.left;
26781 props.right.width = dom.rightContainer.clientWidth || -props.border.right;
26782
26783 this._setDOM();
26784 } // enable/disable vertical panning
26785
26786
26787 var contentsOverflow = props.center.height > props.centerContainer.height;
26788 this.hammer.get('pan').set({
26789 direction: contentsOverflow ? hammer$1.DIRECTION_ALL : hammer$1.DIRECTION_HORIZONTAL
26790 }); // redraw all components
26791
26792 this.components.forEach(function (component) {
26793 resized = component.redraw() || resized;
26794 });
26795 var MAX_REDRAW = 5;
26796
26797 if (resized) {
26798 if (this.redrawCount < MAX_REDRAW) {
26799 this.body.emitter.emit('_change');
26800 return;
26801 } else {
26802 console.log('WARNING: infinite loop in redraw?');
26803 }
26804 } else {
26805 this.redrawCount = 0;
26806 } //Emit public 'changed' event for UI updates, see issue #1592
26807
26808
26809 this.body.emitter.emit("changed");
26810};
26811
26812Core.prototype._setDOM = function () {
26813 var props = this.props;
26814 var dom = this.dom;
26815 props.leftContainer.width = props.left.width;
26816 props.rightContainer.width = props.right.width;
26817 var centerWidth = props.root.width - props.left.width - props.right.width - props.borderRootWidth;
26818 props.center.width = centerWidth;
26819 props.centerContainer.width = centerWidth;
26820 props.top.width = centerWidth;
26821 props.bottom.width = centerWidth; // resize the panels
26822
26823 dom.background.style.height = props.background.height + 'px';
26824 dom.backgroundVertical.style.height = props.background.height + 'px';
26825 dom.backgroundHorizontal.style.height = props.centerContainer.height + 'px';
26826 dom.centerContainer.style.height = props.centerContainer.height + 'px';
26827 dom.leftContainer.style.height = props.leftContainer.height + 'px';
26828 dom.rightContainer.style.height = props.rightContainer.height + 'px';
26829 dom.background.style.width = props.background.width + 'px';
26830 dom.backgroundVertical.style.width = props.centerContainer.width + 'px';
26831 dom.backgroundHorizontal.style.width = props.background.width + 'px';
26832 dom.centerContainer.style.width = props.center.width + 'px';
26833 dom.top.style.width = props.top.width + 'px';
26834 dom.bottom.style.width = props.bottom.width + 'px'; // reposition the panels
26835
26836 dom.background.style.left = '0';
26837 dom.background.style.top = '0';
26838 dom.backgroundVertical.style.left = props.left.width + props.border.left + 'px';
26839 dom.backgroundVertical.style.top = '0';
26840 dom.backgroundHorizontal.style.left = '0';
26841 dom.backgroundHorizontal.style.top = props.top.height + 'px';
26842 dom.centerContainer.style.left = props.left.width + 'px';
26843 dom.centerContainer.style.top = props.top.height + 'px';
26844 dom.leftContainer.style.left = '0';
26845 dom.leftContainer.style.top = props.top.height + 'px';
26846 dom.rightContainer.style.left = props.left.width + props.center.width + 'px';
26847 dom.rightContainer.style.top = props.top.height + 'px';
26848 dom.top.style.left = props.left.width + 'px';
26849 dom.top.style.top = '0';
26850 dom.bottom.style.left = props.left.width + 'px';
26851 dom.bottom.style.top = props.top.height + props.centerContainer.height + 'px';
26852 dom.center.style.left = '0';
26853 dom.left.style.left = '0';
26854 dom.right.style.left = '0';
26855}; // TODO: deprecated since version 1.1.0, remove some day
26856
26857
26858Core.prototype.repaint = function () {
26859 throw new Error('Function repaint is deprecated. Use redraw instead.');
26860};
26861/**
26862 * Set a current time. This can be used for example to ensure that a client's
26863 * time is synchronized with a shared server time.
26864 * Only applicable when option `showCurrentTime` is true.
26865 * @param {Date | string | number} time A Date, unix timestamp, or
26866 * ISO date string.
26867 */
26868
26869
26870Core.prototype.setCurrentTime = function (time) {
26871 if (!this.currentTime) {
26872 throw new Error('Option showCurrentTime must be true');
26873 }
26874
26875 this.currentTime.setCurrentTime(time);
26876};
26877/**
26878 * Get the current time.
26879 * Only applicable when option `showCurrentTime` is true.
26880 * @return {Date} Returns the current time.
26881 */
26882
26883
26884Core.prototype.getCurrentTime = function () {
26885 if (!this.currentTime) {
26886 throw new Error('Option showCurrentTime must be true');
26887 }
26888
26889 return this.currentTime.getCurrentTime();
26890};
26891/**
26892 * Convert a position on screen (pixels) to a datetime
26893 * @param {int} x Position on the screen in pixels
26894 * @return {Date} time The datetime the corresponds with given position x
26895 * @protected
26896 */
26897// TODO: move this function to Range
26898
26899
26900Core.prototype._toTime = function (x) {
26901 return DateUtil.toTime(this, x, this.props.center.width);
26902};
26903/**
26904 * Convert a position on the global screen (pixels) to a datetime
26905 * @param {int} x Position on the screen in pixels
26906 * @return {Date} time The datetime the corresponds with given position x
26907 * @protected
26908 */
26909// TODO: move this function to Range
26910
26911
26912Core.prototype._toGlobalTime = function (x) {
26913 return DateUtil.toTime(this, x, this.props.root.width); //var conversion = this.range.conversion(this.props.root.width);
26914 //return new Date(x / conversion.scale + conversion.offset);
26915};
26916/**
26917 * Convert a datetime (Date object) into a position on the screen
26918 * @param {Date} time A date
26919 * @return {int} x The position on the screen in pixels which corresponds
26920 * with the given date.
26921 * @protected
26922 */
26923// TODO: move this function to Range
26924
26925
26926Core.prototype._toScreen = function (time) {
26927 return DateUtil.toScreen(this, time, this.props.center.width);
26928};
26929/**
26930 * Convert a datetime (Date object) into a position on the root
26931 * This is used to get the pixel density estimate for the screen, not the center panel
26932 * @param {Date} time A date
26933 * @return {int} x The position on root in pixels which corresponds
26934 * with the given date.
26935 * @protected
26936 */
26937// TODO: move this function to Range
26938
26939
26940Core.prototype._toGlobalScreen = function (time) {
26941 return DateUtil.toScreen(this, time, this.props.root.width); //var conversion = this.range.conversion(this.props.root.width);
26942 //return (time.valueOf() - conversion.offset) * conversion.scale;
26943};
26944/**
26945 * Initialize watching when option autoResize is true
26946 * @private
26947 */
26948
26949
26950Core.prototype._initAutoResize = function () {
26951 if (this.options.autoResize == true) {
26952 this._startAutoResize();
26953 } else {
26954 this._stopAutoResize();
26955 }
26956};
26957/**
26958 * Watch for changes in the size of the container. On resize, the Panel will
26959 * automatically redraw itself.
26960 * @private
26961 */
26962
26963
26964Core.prototype._startAutoResize = function () {
26965 var me = this;
26966
26967 this._stopAutoResize();
26968
26969 this._onResize = function () {
26970 if (me.options.autoResize != true) {
26971 // stop watching when the option autoResize is changed to false
26972 me._stopAutoResize();
26973
26974 return;
26975 }
26976
26977 if (me.dom.root) {
26978 // check whether the frame is resized
26979 // Note: we compare offsetWidth here, not clientWidth. For some reason,
26980 // IE does not restore the clientWidth from 0 to the actual width after
26981 // changing the timeline's container display style from none to visible
26982 if (me.dom.root.offsetWidth != me.props.lastWidth || me.dom.root.offsetHeight != me.props.lastHeight) {
26983 me.props.lastWidth = me.dom.root.offsetWidth;
26984 me.props.lastHeight = me.dom.root.offsetHeight;
26985 me.props.scrollbarWidth = util.getScrollBarWidth();
26986 me.body.emitter.emit('_change');
26987 }
26988 }
26989 }; // add event listener to window resize
26990
26991
26992 util.addEventListener(window, 'resize', this._onResize); //Prevent initial unnecessary redraw
26993
26994 if (me.dom.root) {
26995 me.props.lastWidth = me.dom.root.offsetWidth;
26996 me.props.lastHeight = me.dom.root.offsetHeight;
26997 }
26998
26999 this.watchTimer = setInterval(this._onResize, 1000);
27000};
27001/**
27002 * Stop watching for a resize of the frame.
27003 * @private
27004 */
27005
27006
27007Core.prototype._stopAutoResize = function () {
27008 if (this.watchTimer) {
27009 clearInterval(this.watchTimer);
27010 this.watchTimer = undefined;
27011 } // remove event listener on window.resize
27012
27013
27014 if (this._onResize) {
27015 util.removeEventListener(window, 'resize', this._onResize);
27016 this._onResize = null;
27017 }
27018};
27019/**
27020 * Start moving the timeline vertically
27021 * @param {Event} event
27022 * @private
27023 */
27024
27025
27026Core.prototype._onTouch = function (event) {
27027 // eslint-disable-line no-unused-vars
27028 this.touch.allowDragging = true;
27029 this.touch.initialScrollTop = this.props.scrollTop;
27030};
27031/**
27032 * Start moving the timeline vertically
27033 * @param {Event} event
27034 * @private
27035 */
27036
27037
27038Core.prototype._onPinch = function (event) {
27039 // eslint-disable-line no-unused-vars
27040 this.touch.allowDragging = false;
27041};
27042/**
27043 * Move the timeline vertically
27044 * @param {Event} event
27045 * @private
27046 */
27047
27048
27049Core.prototype._onDrag = function (event) {
27050 if (!event) return; // refuse to drag when we where pinching to prevent the timeline make a jump
27051 // when releasing the fingers in opposite order from the touch screen
27052
27053 if (!this.touch.allowDragging) return;
27054 var delta = event.deltaY;
27055
27056 var oldScrollTop = this._getScrollTop();
27057
27058 var newScrollTop = this._setScrollTop(this.touch.initialScrollTop + delta);
27059
27060 if (this.options.verticalScroll) {
27061 this.dom.left.parentNode.scrollTop = -this.props.scrollTop;
27062 this.dom.right.parentNode.scrollTop = -this.props.scrollTop;
27063 }
27064
27065 if (newScrollTop != oldScrollTop) {
27066 this.emit("verticalDrag");
27067 }
27068};
27069/**
27070 * Apply a scrollTop
27071 * @param {number} scrollTop
27072 * @returns {number} scrollTop Returns the applied scrollTop
27073 * @private
27074 */
27075
27076
27077Core.prototype._setScrollTop = function (scrollTop) {
27078 this.props.scrollTop = scrollTop;
27079
27080 this._updateScrollTop();
27081
27082 return this.props.scrollTop;
27083};
27084/**
27085 * Update the current scrollTop when the height of the containers has been changed
27086 * @returns {number} scrollTop Returns the applied scrollTop
27087 * @private
27088 */
27089
27090
27091Core.prototype._updateScrollTop = function () {
27092 // recalculate the scrollTopMin
27093 var scrollTopMin = Math.min(this.props.centerContainer.height - this.props.center.height, 0); // is negative or zero
27094
27095 if (scrollTopMin != this.props.scrollTopMin) {
27096 // in case of bottom orientation, change the scrollTop such that the contents
27097 // do not move relative to the time axis at the bottom
27098 if (this.options.orientation.item != 'top') {
27099 this.props.scrollTop += scrollTopMin - this.props.scrollTopMin;
27100 }
27101
27102 this.props.scrollTopMin = scrollTopMin;
27103 } // limit the scrollTop to the feasible scroll range
27104
27105
27106 if (this.props.scrollTop > 0) this.props.scrollTop = 0;
27107 if (this.props.scrollTop < scrollTopMin) this.props.scrollTop = scrollTopMin;
27108
27109 if (this.options.verticalScroll) {
27110 this.dom.left.parentNode.scrollTop = -this.props.scrollTop;
27111 this.dom.right.parentNode.scrollTop = -this.props.scrollTop;
27112 }
27113
27114 return this.props.scrollTop;
27115};
27116/**
27117 * Get the current scrollTop
27118 * @returns {number} scrollTop
27119 * @private
27120 */
27121
27122
27123Core.prototype._getScrollTop = function () {
27124 return this.props.scrollTop;
27125};
27126/**
27127 * Load a configurator
27128 * [at]returns {Object}
27129 * @private
27130 */
27131
27132
27133Core.prototype._createConfigurator = function () {
27134 throw new Error('Cannot invoke abstract method _createConfigurator');
27135};
27136
27137/**
27138 * A current time bar
27139 * @param {{range: Range, dom: Object, domProps: Object}} body
27140 * @param {Object} [options] Available parameters:
27141 * {Boolean} [showCurrentTime]
27142 * {String} [alignCurrentTime]
27143 * @constructor CurrentTime
27144 * @extends Component
27145 */
27146
27147function CurrentTime(body, options) {
27148 this.body = body; // default options
27149
27150 this.defaultOptions = {
27151 rtl: false,
27152 showCurrentTime: true,
27153 alignCurrentTime: undefined,
27154 moment: moment$3,
27155 locales: locales,
27156 locale: 'en'
27157 };
27158 this.options = util.extend({}, this.defaultOptions);
27159 this.offset = 0;
27160
27161 this._create();
27162
27163 this.setOptions(options);
27164}
27165
27166CurrentTime.prototype = new Component_1();
27167/**
27168 * Create the HTML DOM for the current time bar
27169 * @private
27170 */
27171
27172CurrentTime.prototype._create = function () {
27173 var bar = document.createElement('div');
27174 bar.className = 'vis-current-time';
27175 bar.style.position = 'absolute';
27176 bar.style.top = '0px';
27177 bar.style.height = '100%';
27178 this.bar = bar;
27179};
27180/**
27181 * Destroy the CurrentTime bar
27182 */
27183
27184
27185CurrentTime.prototype.destroy = function () {
27186 this.options.showCurrentTime = false;
27187 this.redraw(); // will remove the bar from the DOM and stop refreshing
27188
27189 this.body = null;
27190};
27191/**
27192 * Set options for the component. Options will be merged in current options.
27193 * @param {Object} options Available parameters:
27194 * {boolean} [showCurrentTime]
27195 * {String} [alignCurrentTime]
27196 */
27197
27198
27199CurrentTime.prototype.setOptions = function (options) {
27200 if (options) {
27201 // copy all options that we know
27202 util.selectiveExtend(['rtl', 'showCurrentTime', 'alignCurrentTime', 'moment', 'locale', 'locales'], this.options, options);
27203 }
27204};
27205/**
27206 * Repaint the component
27207 * @return {boolean} Returns true if the component is resized
27208 */
27209
27210
27211CurrentTime.prototype.redraw = function () {
27212 if (this.options.showCurrentTime) {
27213 var parent = this.body.dom.backgroundVertical;
27214
27215 if (this.bar.parentNode != parent) {
27216 // attach to the dom
27217 if (this.bar.parentNode) {
27218 this.bar.parentNode.removeChild(this.bar);
27219 }
27220
27221 parent.appendChild(this.bar);
27222 this.start();
27223 }
27224
27225 var now = this.options.moment(new Date().valueOf() + this.offset);
27226
27227 if (this.options.alignCurrentTime) {
27228 now = now.startOf(this.options.alignCurrentTime);
27229 }
27230
27231 var x = this.body.util.toScreen(now);
27232 var locale = this.options.locales[this.options.locale];
27233
27234 if (!locale) {
27235 if (!this.warned) {
27236 console.log('WARNING: options.locales[\'' + this.options.locale + '\'] not found. See http://visjs.org/docs/timeline/#Localization');
27237 this.warned = true;
27238 }
27239
27240 locale = this.options.locales['en']; // fall back on english when not available
27241 }
27242
27243 var title = locale.current + ' ' + locale.time + ': ' + now.format('dddd, MMMM Do YYYY, H:mm:ss');
27244 title = title.charAt(0).toUpperCase() + title.substring(1);
27245
27246 if (this.options.rtl) {
27247 this.bar.style.right = x + 'px';
27248 } else {
27249 this.bar.style.left = x + 'px';
27250 }
27251
27252 this.bar.title = title;
27253 } else {
27254 // remove the line from the DOM
27255 if (this.bar.parentNode) {
27256 this.bar.parentNode.removeChild(this.bar);
27257 }
27258
27259 this.stop();
27260 }
27261
27262 return false;
27263};
27264/**
27265 * Start auto refreshing the current time bar
27266 */
27267
27268
27269CurrentTime.prototype.start = function () {
27270 var me = this;
27271 /**
27272 * Updates the current time.
27273 */
27274
27275 function update() {
27276 me.stop(); // determine interval to refresh
27277
27278 var scale = me.body.range.conversion(me.body.domProps.center.width).scale;
27279 var interval = 1 / scale / 10;
27280 if (interval < 30) interval = 30;
27281 if (interval > 1000) interval = 1000;
27282 me.redraw();
27283 me.body.emitter.emit('currentTimeTick'); // start a renderTimer to adjust for the new time
27284
27285 me.currentTimeTimer = setTimeout(update, interval);
27286 }
27287
27288 update();
27289};
27290/**
27291 * Stop auto refreshing the current time bar
27292 */
27293
27294
27295CurrentTime.prototype.stop = function () {
27296 if (this.currentTimeTimer !== undefined) {
27297 clearTimeout(this.currentTimeTimer);
27298 delete this.currentTimeTimer;
27299 }
27300};
27301/**
27302 * Set a current time. This can be used for example to ensure that a client's
27303 * time is synchronized with a shared server time.
27304 * @param {Date | string | number} time A Date, unix timestamp, or
27305 * ISO date string.
27306 */
27307
27308
27309CurrentTime.prototype.setCurrentTime = function (time) {
27310 var t = util.convert(time, 'Date').valueOf();
27311 var now = new Date().valueOf();
27312 this.offset = t - now;
27313 this.redraw();
27314};
27315/**
27316 * Get the current time.
27317 * @return {Date} Returns the current time.
27318 */
27319
27320
27321CurrentTime.prototype.getCurrentTime = function () {
27322 return new Date(new Date().valueOf() + this.offset);
27323};
27324
27325var CurrentTime_1 = CurrentTime;
27326
27327var Stack = createCommonjsModule$1(function (module, exports) {
27328 // Utility functions for ordering and stacking of items
27329 var EPSILON = 0.001; // used when checking collisions, to prevent round-off errors
27330
27331 /**
27332 * Order items by their start data
27333 * @param {Item[]} items
27334 */
27335
27336 exports.orderByStart = function (items) {
27337 items.sort(function (a, b) {
27338 return a.data.start - b.data.start;
27339 });
27340 };
27341 /**
27342 * Order items by their end date. If they have no end date, their start date
27343 * is used.
27344 * @param {Item[]} items
27345 */
27346
27347
27348 exports.orderByEnd = function (items) {
27349 items.sort(function (a, b) {
27350 var aTime = 'end' in a.data ? a.data.end : a.data.start,
27351 bTime = 'end' in b.data ? b.data.end : b.data.start;
27352 return aTime - bTime;
27353 });
27354 };
27355 /**
27356 * Adjust vertical positions of the items such that they don't overlap each
27357 * other.
27358 * @param {Item[]} items
27359 * All visible items
27360 * @param {{item: {horizontal: number, vertical: number}, axis: number}} margin
27361 * Margins between items and between items and the axis.
27362 * @param {boolean} [force=false]
27363 * If true, all items will be repositioned. If false (default), only
27364 * items having a top===null will be re-stacked
27365 * @param {function} shouldBailItemsRedrawFunction
27366 * bailing function
27367 * @return {boolean} shouldBail
27368 */
27369
27370
27371 exports.stack = function (items, margin, force, shouldBailItemsRedrawFunction) {
27372 if (force) {
27373 // reset top position of all items
27374 for (var i = 0; i < items.length; i++) {
27375 items[i].top = null;
27376 }
27377 } // calculate new, non-overlapping positions
27378
27379
27380 for (var i = 0; i < items.length; i++) {
27381 // eslint-disable-line no-redeclare
27382 var item = items[i];
27383
27384 if (item.stack && item.top === null) {
27385 // initialize top position
27386 item.top = margin.axis;
27387 var shouldBail = false;
27388
27389 do {
27390 // TODO: optimize checking for overlap. when there is a gap without items,
27391 // you only need to check for items from the next item on, not from zero
27392 var collidingItem = null;
27393
27394 for (var j = 0, jj = items.length; j < jj; j++) {
27395 var other = items[j];
27396 shouldBail = shouldBailItemsRedrawFunction() || false;
27397
27398 if (shouldBail) {
27399 return true;
27400 }
27401
27402 if (other.top !== null && other !== item && other.stack && exports.collision(item, other, margin.item, other.options.rtl)) {
27403 collidingItem = other;
27404 break;
27405 }
27406 }
27407
27408 if (collidingItem != null) {
27409 // There is a collision. Reposition the items above the colliding element
27410 item.top = collidingItem.top + collidingItem.height + margin.item.vertical;
27411 }
27412 } while (collidingItem);
27413 }
27414 }
27415
27416 return shouldBail;
27417 };
27418 /**
27419 * Adjust vertical positions of the items within a single subgroup such that they
27420 * don't overlap each other.
27421 * @param {Item[]} items
27422 * All items withina subgroup
27423 * @param {{item: {horizontal: number, vertical: number}, axis: number}} margin
27424 * Margins between items and between items and the axis.
27425 * @param {subgroup} subgroup
27426 * The subgroup that is being stacked
27427 */
27428
27429
27430 exports.substack = function (items, margin, subgroup) {
27431 for (var i = 0; i < items.length; i++) {
27432 items[i].top = null;
27433 } // Set the initial height
27434
27435
27436 var subgroupHeight = subgroup.height; // calculate new, non-overlapping positions
27437
27438 for (i = 0; i < items.length; i++) {
27439 var item = items[i];
27440
27441 if (item.stack && item.top === null) {
27442 // initialize top position
27443 item.top = item.baseTop; //margin.axis + item.baseTop;
27444
27445 do {
27446 // TODO: optimize checking for overlap. when there is a gap without items,
27447 // you only need to check for items from the next item on, not from zero
27448 var collidingItem = null;
27449
27450 for (var j = 0, jj = items.length; j < jj; j++) {
27451 var other = items[j];
27452
27453 if (other.top !== null && other !== item
27454 /*&& other.stack*/
27455 && exports.collision(item, other, margin.item, other.options.rtl)) {
27456 collidingItem = other;
27457 break;
27458 }
27459 }
27460
27461 if (collidingItem != null) {
27462 // There is a collision. Reposition the items above the colliding element
27463 item.top = collidingItem.top + collidingItem.height + margin.item.vertical; // + item.baseTop;
27464 }
27465
27466 if (item.top + item.height > subgroupHeight) {
27467 subgroupHeight = item.top + item.height;
27468 }
27469 } while (collidingItem);
27470 }
27471 } // Set the new height
27472
27473
27474 subgroup.height = subgroupHeight - subgroup.top + 0.5 * margin.item.vertical;
27475 };
27476 /**
27477 * Adjust vertical positions of the items without stacking them
27478 * @param {Item[]} items
27479 * All visible items
27480 * @param {{item: {horizontal: number, vertical: number}, axis: number}} margin
27481 * Margins between items and between items and the axis.
27482 * @param {subgroups[]} subgroups
27483 * All subgroups
27484 * @param {boolean} stackSubgroups
27485 */
27486
27487
27488 exports.nostack = function (items, margin, subgroups, stackSubgroups) {
27489 for (var i = 0; i < items.length; i++) {
27490 if (items[i].data.subgroup == undefined) {
27491 items[i].top = margin.item.vertical;
27492 } else if (items[i].data.subgroup !== undefined && stackSubgroups) {
27493 var newTop = 0;
27494
27495 for (var subgroup in subgroups) {
27496 if (subgroups.hasOwnProperty(subgroup)) {
27497 if (subgroups[subgroup].visible == true && subgroups[subgroup].index < subgroups[items[i].data.subgroup].index) {
27498 newTop += subgroups[subgroup].height;
27499 subgroups[items[i].data.subgroup].top = newTop;
27500 }
27501 }
27502 }
27503
27504 items[i].top = newTop + 0.5 * margin.item.vertical;
27505 }
27506 }
27507
27508 if (!stackSubgroups) {
27509 exports.stackSubgroups(items, margin, subgroups);
27510 }
27511 };
27512 /**
27513 * Adjust vertical positions of the subgroups such that they don't overlap each
27514 * other.
27515 * @param {Array.<vis.Item>} items
27516 * @param {{item: {horizontal: number, vertical: number}, axis: number}} margin Margins between items and between items and the axis.
27517 * @param {subgroups[]} subgroups
27518 * All subgroups
27519 */
27520
27521
27522 exports.stackSubgroups = function (items, margin, subgroups) {
27523 for (var subgroup in subgroups) {
27524 if (subgroups.hasOwnProperty(subgroup)) {
27525 subgroups[subgroup].top = 0;
27526
27527 do {
27528 // TODO: optimize checking for overlap. when there is a gap without items,
27529 // you only need to check for items from the next item on, not from zero
27530 var collidingItem = null;
27531
27532 for (var otherSubgroup in subgroups) {
27533 if (subgroups[otherSubgroup].top !== null && otherSubgroup !== subgroup && subgroups[subgroup].index > subgroups[otherSubgroup].index && exports.collisionByTimes(subgroups[subgroup], subgroups[otherSubgroup])) {
27534 collidingItem = subgroups[otherSubgroup];
27535 break;
27536 }
27537 }
27538
27539 if (collidingItem != null) {
27540 // There is a collision. Reposition the subgroups above the colliding element
27541 subgroups[subgroup].top = collidingItem.top + collidingItem.height;
27542 }
27543 } while (collidingItem);
27544 }
27545 }
27546
27547 for (var i = 0; i < items.length; i++) {
27548 if (items[i].data.subgroup !== undefined) {
27549 items[i].top = subgroups[items[i].data.subgroup].top + 0.5 * margin.item.vertical;
27550 }
27551 }
27552 };
27553 /**
27554 * Adjust vertical positions of the subgroups such that they don't overlap each
27555 * other, then stacks the contents of each subgroup individually.
27556 * @param {Item[]} subgroupItems
27557 * All the items in a subgroup
27558 * @param {{item: {horizontal: number, vertical: number}, axis: number}} margin
27559 * Margins between items and between items and the axis.
27560 * @param {subgroups[]} subgroups
27561 * All subgroups
27562 */
27563
27564
27565 exports.stackSubgroupsWithInnerStack = function (subgroupItems, margin, subgroups) {
27566 var doSubStack = false; // Run subgroups in their order (if any)
27567
27568 var subgroupOrder = [];
27569
27570 for (var subgroup in subgroups) {
27571 if (subgroups[subgroup].hasOwnProperty("index")) {
27572 subgroupOrder[subgroups[subgroup].index] = subgroup;
27573 } else {
27574 subgroupOrder.push(subgroup);
27575 }
27576 }
27577
27578 for (var j = 0; j < subgroupOrder.length; j++) {
27579 subgroup = subgroupOrder[j];
27580
27581 if (subgroups.hasOwnProperty(subgroup)) {
27582 doSubStack = doSubStack || subgroups[subgroup].stack;
27583 subgroups[subgroup].top = 0;
27584
27585 for (var otherSubgroup in subgroups) {
27586 if (subgroups[otherSubgroup].visible && subgroups[subgroup].index > subgroups[otherSubgroup].index) {
27587 subgroups[subgroup].top += subgroups[otherSubgroup].height;
27588 }
27589 }
27590
27591 var items = subgroupItems[subgroup];
27592
27593 for (var i = 0; i < items.length; i++) {
27594 if (items[i].data.subgroup !== undefined) {
27595 items[i].top = subgroups[items[i].data.subgroup].top + 0.5 * margin.item.vertical;
27596
27597 if (subgroups[subgroup].stack) {
27598 items[i].baseTop = items[i].top;
27599 }
27600 }
27601 }
27602
27603 if (doSubStack && subgroups[subgroup].stack) {
27604 exports.substack(subgroupItems[subgroup], margin, subgroups[subgroup]);
27605 }
27606 }
27607 }
27608 };
27609 /**
27610 * Test if the two provided items collide
27611 * The items must have parameters left, width, top, and height.
27612 * @param {Item} a The first item
27613 * @param {Item} b The second item
27614 * @param {{horizontal: number, vertical: number}} margin
27615 * An object containing a horizontal and vertical
27616 * minimum required margin.
27617 * @param {boolean} rtl
27618 * @return {boolean} true if a and b collide, else false
27619 */
27620
27621
27622 exports.collision = function (a, b, margin, rtl) {
27623 if (rtl) {
27624 return a.right - margin.horizontal + EPSILON < b.right + b.width && a.right + a.width + margin.horizontal - EPSILON > b.right && a.top - margin.vertical + EPSILON < b.top + b.height && a.top + a.height + margin.vertical - EPSILON > b.top;
27625 } else {
27626 return a.left - margin.horizontal + EPSILON < b.left + b.width && a.left + a.width + margin.horizontal - EPSILON > b.left && a.top - margin.vertical + EPSILON < b.top + b.height && a.top + a.height + margin.vertical - EPSILON > b.top;
27627 }
27628 };
27629 /**
27630 * Test if the two provided objects collide
27631 * The objects must have parameters start, end, top, and height.
27632 * @param {Object} a The first Object
27633 * @param {Object} b The second Object
27634 * @return {boolean} true if a and b collide, else false
27635 */
27636
27637
27638 exports.collisionByTimes = function (a, b) {
27639 return a.start <= b.start && a.end >= b.start && a.top < b.top + b.height && a.top + a.height > b.top || b.start <= a.start && b.end >= a.start && b.top < a.top + a.height && b.top + b.height > a.top;
27640 };
27641});
27642var Stack_1 = Stack.orderByStart;
27643var Stack_2 = Stack.orderByEnd;
27644var Stack_3 = Stack.stack;
27645var Stack_4 = Stack.substack;
27646var Stack_5 = Stack.nostack;
27647var Stack_6 = Stack.stackSubgroups;
27648var Stack_7 = Stack.stackSubgroupsWithInnerStack;
27649var Stack_8 = Stack.collision;
27650var Stack_9 = Stack.collisionByTimes;
27651
27652/**
27653 * @param {number | string} groupId
27654 * @param {Object} data
27655 * @param {ItemSet} itemSet
27656 * @constructor Group
27657 */
27658
27659function Group(groupId, data, itemSet) {
27660 this.groupId = groupId;
27661 this.subgroups = {};
27662 this.subgroupStack = {};
27663 this.subgroupStackAll = false;
27664 this.doInnerStack = false;
27665 this.shouldBailStackItems = false;
27666 this.subgroupIndex = 0;
27667 this.subgroupOrderer = data && data.subgroupOrder;
27668 this.itemSet = itemSet;
27669 this.isVisible = null;
27670 this.stackDirty = true; // if true, items will be restacked on next redraw
27671
27672 if (data && data.nestedGroups) {
27673 this.nestedGroups = data.nestedGroups;
27674
27675 if (data.showNested == false) {
27676 this.showNested = false;
27677 } else {
27678 this.showNested = true;
27679 }
27680 }
27681
27682 if (data && data.subgroupStack) {
27683 if (typeof data.subgroupStack === "boolean") {
27684 this.doInnerStack = data.subgroupStack;
27685 this.subgroupStackAll = data.subgroupStack;
27686 } else {
27687 // We might be doing stacking on specific sub groups, but only
27688 // if at least one is set to do stacking
27689 for (var key in data.subgroupStack) {
27690 this.subgroupStack[key] = data.subgroupStack[key];
27691 this.doInnerStack = this.doInnerStack || data.subgroupStack[key];
27692 }
27693 }
27694 }
27695
27696 if (data && data.heightMode) {
27697 this.heightMode = data.heightMode;
27698 } else {
27699 this.heightMode = itemSet.options.groupHeightMode;
27700 }
27701
27702 this.nestedInGroup = null;
27703 this.dom = {};
27704 this.props = {
27705 label: {
27706 width: 0,
27707 height: 0
27708 }
27709 };
27710 this.className = null;
27711 this.items = {}; // items filtered by groupId of this group
27712
27713 this.visibleItems = []; // items currently visible in window
27714
27715 this.itemsInRange = []; // items currently in range
27716
27717 this.orderedItems = {
27718 byStart: [],
27719 byEnd: []
27720 };
27721 this.checkRangedItems = false; // needed to refresh the ranged items if the window is programatically changed with NO overlap.
27722
27723 var me = this;
27724 this.itemSet.body.emitter.on("checkRangedItems", function () {
27725 me.checkRangedItems = true;
27726 });
27727
27728 this._create();
27729
27730 this.setData(data);
27731}
27732/**
27733 * Create DOM elements for the group
27734 * @private
27735 */
27736
27737
27738Group.prototype._create = function () {
27739 var label = document.createElement('div');
27740
27741 if (this.itemSet.options.groupEditable.order) {
27742 label.className = 'vis-label draggable';
27743 } else {
27744 label.className = 'vis-label';
27745 }
27746
27747 if (this.itemSet.options.groupLabelDirection === 'vertical') {
27748 label.className += ' vertical';
27749 }
27750
27751 this.dom.label = label;
27752 var inner = document.createElement('div');
27753 inner.className = 'vis-inner';
27754 label.appendChild(inner);
27755 this.dom.inner = inner;
27756 var foreground = document.createElement('div');
27757 foreground.className = 'vis-group';
27758 foreground['timeline-group'] = this;
27759 this.dom.foreground = foreground;
27760 this.dom.background = document.createElement('div');
27761 this.dom.background.className = 'vis-group';
27762 this.dom.axis = document.createElement('div');
27763 this.dom.axis.className = 'vis-group'; // create a hidden marker to detect when the Timelines container is attached
27764 // to the DOM, or the style of a parent of the Timeline is changed from
27765 // display:none is changed to visible.
27766
27767 this.dom.marker = document.createElement('div');
27768 this.dom.marker.style.visibility = 'hidden';
27769 this.dom.marker.style.position = 'absolute';
27770 this.dom.marker.innerHTML = '';
27771 this.dom.background.appendChild(this.dom.marker);
27772};
27773/**
27774 * Set the group data for this group
27775 * @param {Object} data Group data, can contain properties content and className
27776 */
27777
27778
27779Group.prototype.setData = function (data) {
27780 // update contents
27781 var content;
27782 var templateFunction;
27783
27784 if (this.itemSet.options && this.itemSet.options.groupTemplate) {
27785 templateFunction = this.itemSet.options.groupTemplate.bind(this);
27786 content = templateFunction(data, this.dom.inner);
27787 } else {
27788 content = data && data.content;
27789 }
27790
27791 if (content instanceof Element) {
27792 while (this.dom.inner.firstChild) {
27793 this.dom.inner.removeChild(this.dom.inner.firstChild);
27794 }
27795
27796 this.dom.inner.appendChild(content);
27797 } else if (content instanceof Object && content.isReactComponent) ; else if (content instanceof Object) {
27798 templateFunction(data, this.dom.inner);
27799 } else if (content !== undefined && content !== null) {
27800 this.dom.inner.innerHTML = content;
27801 } else {
27802 this.dom.inner.innerHTML = this.groupId || ''; // groupId can be null
27803 } // update title
27804
27805
27806 this.dom.label.title = data && data.title || '';
27807
27808 if (!this.dom.inner.firstChild) {
27809 util.addClassName(this.dom.inner, 'vis-hidden');
27810 } else {
27811 util.removeClassName(this.dom.inner, 'vis-hidden');
27812 }
27813
27814 if (data && data.nestedGroups) {
27815 if (!this.nestedGroups || this.nestedGroups != data.nestedGroups) {
27816 this.nestedGroups = data.nestedGroups;
27817 }
27818
27819 if (data.showNested !== undefined || this.showNested === undefined) {
27820 if (data.showNested == false) {
27821 this.showNested = false;
27822 } else {
27823 this.showNested = true;
27824 }
27825 }
27826
27827 util.addClassName(this.dom.label, 'vis-nesting-group');
27828 var collapsedDirClassName = this.itemSet.options.rtl ? 'collapsed-rtl' : 'collapsed';
27829
27830 if (this.showNested) {
27831 util.removeClassName(this.dom.label, collapsedDirClassName);
27832 util.addClassName(this.dom.label, 'expanded');
27833 } else {
27834 util.removeClassName(this.dom.label, 'expanded');
27835 util.addClassName(this.dom.label, collapsedDirClassName);
27836 }
27837 } else if (this.nestedGroups) {
27838 this.nestedGroups = null;
27839 collapsedDirClassName = this.itemSet.options.rtl ? 'collapsed-rtl' : 'collapsed';
27840 util.removeClassName(this.dom.label, collapsedDirClassName);
27841 util.removeClassName(this.dom.label, 'expanded');
27842 util.removeClassName(this.dom.label, 'vis-nesting-group');
27843 }
27844
27845 if (data && data.nestedInGroup) {
27846 util.addClassName(this.dom.label, 'vis-nested-group');
27847
27848 if (this.itemSet.options && this.itemSet.options.rtl) {
27849 this.dom.inner.style.paddingRight = '30px';
27850 } else {
27851 this.dom.inner.style.paddingLeft = '30px';
27852 }
27853 } // update className
27854
27855
27856 var className = data && data.className || null;
27857
27858 if (className != this.className) {
27859 if (this.className) {
27860 util.removeClassName(this.dom.label, this.className);
27861 util.removeClassName(this.dom.foreground, this.className);
27862 util.removeClassName(this.dom.background, this.className);
27863 util.removeClassName(this.dom.axis, this.className);
27864 }
27865
27866 util.addClassName(this.dom.label, className);
27867 util.addClassName(this.dom.foreground, className);
27868 util.addClassName(this.dom.background, className);
27869 util.addClassName(this.dom.axis, className);
27870 this.className = className;
27871 } // update style
27872
27873
27874 if (this.style) {
27875 util.removeCssText(this.dom.label, this.style);
27876 this.style = null;
27877 }
27878
27879 if (data && data.style) {
27880 util.addCssText(this.dom.label, data.style);
27881 this.style = data.style;
27882 }
27883};
27884/**
27885 * Get the width of the group label
27886 * @return {number} width
27887 */
27888
27889
27890Group.prototype.getLabelWidth = function () {
27891 return this.props.label.width;
27892};
27893
27894Group.prototype._didMarkerHeightChange = function () {
27895 var markerHeight = this.dom.marker.clientHeight;
27896
27897 if (markerHeight != this.lastMarkerHeight) {
27898 this.lastMarkerHeight = markerHeight;
27899 var redrawQueue = {};
27900 var redrawQueueLength = 0;
27901 util.forEach(this.items, function (item, key) {
27902 item.dirty = true;
27903
27904 if (item.displayed) {
27905 var returnQueue = true;
27906 redrawQueue[key] = item.redraw(returnQueue);
27907 redrawQueueLength = redrawQueue[key].length;
27908 }
27909 });
27910 var needRedraw = redrawQueueLength > 0;
27911
27912 if (needRedraw) {
27913 // redraw all regular items
27914 for (var i = 0; i < redrawQueueLength; i++) {
27915 util.forEach(redrawQueue, function (fns) {
27916 fns[i]();
27917 });
27918 }
27919 }
27920
27921 return true;
27922 }
27923};
27924
27925Group.prototype._calculateGroupSizeAndPosition = function () {
27926 var offsetTop = this.dom.foreground.offsetTop;
27927 var offsetLeft = this.dom.foreground.offsetLeft;
27928 var offsetWidth = this.dom.foreground.offsetWidth;
27929 this.top = offsetTop;
27930 this.right = offsetLeft;
27931 this.width = offsetWidth;
27932};
27933
27934Group.prototype._shouldBailItemsRedraw = function () {
27935 var me = this;
27936 var timeoutOptions = this.itemSet.options.onTimeout;
27937 var bailOptions = {
27938 relativeBailingTime: this.itemSet.itemsSettingTime,
27939 bailTimeMs: timeoutOptions && timeoutOptions.timeoutMs,
27940 userBailFunction: timeoutOptions && timeoutOptions.callback,
27941 shouldBailStackItems: this.shouldBailStackItems
27942 };
27943 var bail = null;
27944
27945 if (!this.itemSet.initialDrawDone) {
27946 if (bailOptions.shouldBailStackItems) {
27947 return true;
27948 }
27949
27950 if (Math.abs(new Date() - new Date(bailOptions.relativeBailingTime)) > bailOptions.bailTimeMs) {
27951 if (bailOptions.userBailFunction && this.itemSet.userContinueNotBail == null) {
27952 bailOptions.userBailFunction(function (didUserContinue) {
27953 me.itemSet.userContinueNotBail = didUserContinue;
27954 bail = !didUserContinue;
27955 });
27956 } else if (me.itemSet.userContinueNotBail == false) {
27957 bail = true;
27958 } else {
27959 bail = false;
27960 }
27961 }
27962 }
27963
27964 return bail;
27965};
27966
27967Group.prototype._redrawItems = function (forceRestack, lastIsVisible, margin, range) {
27968 var restack = forceRestack || this.stackDirty || this.isVisible && !lastIsVisible; // if restacking, reposition visible items vertically
27969
27970 if (restack) {
27971 var visibleSubgroups = {};
27972 var subgroup = null;
27973
27974 if (typeof this.itemSet.options.order === 'function') {
27975 // a custom order function
27976 // brute force restack of all items
27977 // show all items
27978 var me = this;
27979 var limitSize = false;
27980 var redrawQueue = {};
27981 var redrawQueueLength = 0;
27982 util.forEach(this.items, function (item, key) {
27983 if (!item.displayed && (item.isVisible(range) || !item.dom)) {
27984 var returnQueue = true;
27985 redrawQueue[key] = item.show(returnQueue);
27986 redrawQueueLength = redrawQueue[key].length;
27987 me.visibleItems.push(item);
27988 }
27989 });
27990 var needRedraw = redrawQueueLength > 0;
27991
27992 if (needRedraw) {
27993 // redraw all regular items
27994 for (var i = 0; i < redrawQueueLength; i++) {
27995 util.forEach(redrawQueue, function (fns) {
27996 fns[i]();
27997 });
27998 }
27999 }
28000
28001 util.forEach(this.items, function (item) {
28002 if (item.displayed) {
28003 item.repositionX(limitSize);
28004 }
28005 });
28006
28007 if (this.doInnerStack && this.itemSet.options.stackSubgroups) {
28008 // Order the items within each subgroup
28009 for (subgroup in this.subgroups) {
28010 visibleSubgroups[subgroup] = this.subgroups[subgroup].items.slice().sort(function (a, b) {
28011 return me.itemSet.options.order(a.data, b.data);
28012 });
28013 }
28014
28015 Stack.stackSubgroupsWithInnerStack(visibleSubgroups, margin, this.subgroups);
28016 } else {
28017 // order all items and force a restacking
28018 var customOrderedItems = this.orderedItems.byStart.slice().sort(function (a, b) {
28019 return me.itemSet.options.order(a.data, b.data);
28020 });
28021 this.shouldBailStackItems = Stack.stack(customOrderedItems, margin, true, this._shouldBailItemsRedraw.bind(this));
28022 }
28023
28024 this.visibleItems = this._updateItemsInRange(this.orderedItems, this.visibleItems, range);
28025 } else {
28026 // no custom order function, lazy stacking
28027 this.visibleItems = this._updateItemsInRange(this.orderedItems, this.visibleItems, range);
28028
28029 if (this.itemSet.options.stack) {
28030 if (this.doInnerStack && this.itemSet.options.stackSubgroups) {
28031 for (subgroup in this.subgroups) {
28032 visibleSubgroups[subgroup] = this.subgroups[subgroup].items;
28033 }
28034
28035 Stack.stackSubgroupsWithInnerStack(visibleSubgroups, margin, this.subgroups);
28036 } else {
28037 // TODO: ugly way to access options...
28038 this.shouldBailStackItems = Stack.stack(this.visibleItems, margin, true, this._shouldBailItemsRedraw.bind(this));
28039 }
28040 } else {
28041 // no stacking
28042 Stack.nostack(this.visibleItems, margin, this.subgroups, this.itemSet.options.stackSubgroups);
28043 }
28044 }
28045
28046 if (this.shouldBailStackItems) {
28047 this.itemSet.body.emitter.emit('destroyTimeline');
28048 }
28049
28050 this.stackDirty = false;
28051 }
28052};
28053
28054Group.prototype._didResize = function (resized, height) {
28055 resized = util.updateProperty(this, 'height', height) || resized; // recalculate size of label
28056
28057 var boundingRect = this.dom.inner.getBoundingClientRect();
28058 var labelWidth = boundingRect.width;
28059 var labelHeight = boundingRect.height;
28060 resized = util.updateProperty(this.props.label, 'width', labelWidth) || resized;
28061 resized = util.updateProperty(this.props.label, 'height', labelHeight) || resized;
28062 return resized;
28063};
28064
28065Group.prototype._applyGroupHeight = function (height) {
28066 this.dom.background.style.height = height + 'px';
28067 this.dom.foreground.style.height = height + 'px';
28068 this.dom.label.style.height = height + 'px';
28069}; // update vertical position of items after they are re-stacked and the height of the group is calculated
28070
28071
28072Group.prototype._updateItemsVerticalPosition = function (margin) {
28073 for (var i = 0, ii = this.visibleItems.length; i < ii; i++) {
28074 var item = this.visibleItems[i];
28075 item.repositionY(margin);
28076
28077 if (!this.isVisible && this.groupId != "__background__") {
28078 if (item.displayed) item.hide();
28079 }
28080 }
28081};
28082/**
28083 * Repaint this group
28084 * @param {{start: number, end: number}} range
28085 * @param {{item: {horizontal: number, vertical: number}, axis: number}} margin
28086 * @param {boolean} [forceRestack=false] Force restacking of all items
28087 * @param {boolean} [returnQueue=false] return the queue or if the group resized
28088 * @return {boolean} Returns true if the group is resized or the redraw queue if returnQueue=true
28089 */
28090
28091
28092Group.prototype.redraw = function (range, margin, forceRestack, returnQueue) {
28093 var resized = false;
28094 var lastIsVisible = this.isVisible;
28095 var height;
28096 var queue = [// force recalculation of the height of the items when the marker height changed
28097 // (due to the Timeline being attached to the DOM or changed from display:none to visible)
28098 function () {
28099 forceRestack = this._didMarkerHeightChange.bind(this);
28100 }.bind(this), // recalculate the height of the subgroups
28101 this._updateSubGroupHeights.bind(this, margin), // calculate actual size and position
28102 this._calculateGroupSizeAndPosition.bind(this), // check if group is visible
28103 function () {
28104 this.isVisible = this._isGroupVisible.bind(this)(range, margin);
28105 }.bind(this), // redraw Items if needed
28106 function () {
28107 this._redrawItems.bind(this)(forceRestack, lastIsVisible, margin, range);
28108 }.bind(this), // update subgroups
28109 this._updateSubgroupsSizes.bind(this), // recalculate the height of the group
28110 function () {
28111 height = this._calculateHeight.bind(this)(margin);
28112 }.bind(this), // calculate actual size and position again
28113 this._calculateGroupSizeAndPosition.bind(this), // check if resized
28114 function () {
28115 resized = this._didResize.bind(this)(resized, height);
28116 }.bind(this), // apply group height
28117 function () {
28118 this._applyGroupHeight.bind(this)(height);
28119 }.bind(this), // update vertical position of items after they are re-stacked and the height of the group is calculated
28120 function () {
28121 this._updateItemsVerticalPosition.bind(this)(margin);
28122 }.bind(this), function () {
28123 if (!this.isVisible && this.height) {
28124 resized = false;
28125 }
28126
28127 return resized;
28128 }];
28129
28130 if (returnQueue) {
28131 return queue;
28132 } else {
28133 var result;
28134 queue.forEach(function (fn) {
28135 result = fn();
28136 });
28137 return result;
28138 }
28139};
28140/**
28141 * recalculate the height of the subgroups
28142 *
28143 * @param {{item: vis.Item}} margin
28144 * @private
28145 */
28146
28147
28148Group.prototype._updateSubGroupHeights = function (margin) {
28149 if (Object.keys(this.subgroups).length > 0) {
28150 var me = this;
28151 this.resetSubgroups();
28152 util.forEach(this.visibleItems, function (item) {
28153 if (item.data.subgroup !== undefined) {
28154 me.subgroups[item.data.subgroup].height = Math.max(me.subgroups[item.data.subgroup].height, item.height + margin.item.vertical);
28155 me.subgroups[item.data.subgroup].visible = true;
28156 }
28157 });
28158 }
28159};
28160/**
28161 * check if group is visible
28162 *
28163 * @param {vis.Range} range
28164 * @param {{axis: vis.DataAxis}} margin
28165 * @returns {boolean} is visible
28166 * @private
28167 */
28168
28169
28170Group.prototype._isGroupVisible = function (range, margin) {
28171 return this.top <= range.body.domProps.centerContainer.height - range.body.domProps.scrollTop + margin.axis && this.top + this.height + margin.axis >= -range.body.domProps.scrollTop;
28172};
28173/**
28174 * recalculate the height of the group
28175 * @param {{item: {horizontal: number, vertical: number}, axis: number}} margin
28176 * @returns {number} Returns the height
28177 * @private
28178 */
28179
28180
28181Group.prototype._calculateHeight = function (margin) {
28182 // recalculate the height of the group
28183 var height, items;
28184
28185 if (this.heightMode === 'fixed') {
28186 items = util.toArray(this.items);
28187 } else {
28188 // default or 'auto'
28189 items = this.visibleItems;
28190 }
28191
28192 if (items.length > 0) {
28193 var min = items[0].top;
28194 var max = items[0].top + items[0].height;
28195 util.forEach(items, function (item) {
28196 min = Math.min(min, item.top);
28197 max = Math.max(max, item.top + item.height);
28198 });
28199
28200 if (min > margin.axis) {
28201 // there is an empty gap between the lowest item and the axis
28202 var offset = min - margin.axis;
28203 max -= offset;
28204 util.forEach(items, function (item) {
28205 item.top -= offset;
28206 });
28207 }
28208
28209 height = max + margin.item.vertical / 2;
28210 } else {
28211 height = 0;
28212 }
28213
28214 height = Math.max(height, this.props.label.height);
28215 return height;
28216};
28217/**
28218 * Show this group: attach to the DOM
28219 */
28220
28221
28222Group.prototype.show = function () {
28223 if (!this.dom.label.parentNode) {
28224 this.itemSet.dom.labelSet.appendChild(this.dom.label);
28225 }
28226
28227 if (!this.dom.foreground.parentNode) {
28228 this.itemSet.dom.foreground.appendChild(this.dom.foreground);
28229 }
28230
28231 if (!this.dom.background.parentNode) {
28232 this.itemSet.dom.background.appendChild(this.dom.background);
28233 }
28234
28235 if (!this.dom.axis.parentNode) {
28236 this.itemSet.dom.axis.appendChild(this.dom.axis);
28237 }
28238};
28239/**
28240 * Hide this group: remove from the DOM
28241 */
28242
28243
28244Group.prototype.hide = function () {
28245 var label = this.dom.label;
28246
28247 if (label.parentNode) {
28248 label.parentNode.removeChild(label);
28249 }
28250
28251 var foreground = this.dom.foreground;
28252
28253 if (foreground.parentNode) {
28254 foreground.parentNode.removeChild(foreground);
28255 }
28256
28257 var background = this.dom.background;
28258
28259 if (background.parentNode) {
28260 background.parentNode.removeChild(background);
28261 }
28262
28263 var axis = this.dom.axis;
28264
28265 if (axis.parentNode) {
28266 axis.parentNode.removeChild(axis);
28267 }
28268};
28269/**
28270 * Add an item to the group
28271 * @param {Item} item
28272 */
28273
28274
28275Group.prototype.add = function (item) {
28276 this.items[item.id] = item;
28277 item.setParent(this);
28278 this.stackDirty = true; // add to
28279
28280 if (item.data.subgroup !== undefined) {
28281 this._addToSubgroup(item);
28282
28283 this.orderSubgroups();
28284 }
28285
28286 if (this.visibleItems.indexOf(item) == -1) {
28287 var range = this.itemSet.body.range; // TODO: not nice accessing the range like this
28288
28289 this._checkIfVisible(item, this.visibleItems, range);
28290 }
28291};
28292
28293Group.prototype._addToSubgroup = function (item, subgroupId) {
28294 subgroupId = subgroupId || item.data.subgroup;
28295
28296 if (subgroupId != undefined && this.subgroups[subgroupId] === undefined) {
28297 this.subgroups[subgroupId] = {
28298 height: 0,
28299 top: 0,
28300 start: item.data.start,
28301 end: item.data.end || item.data.start,
28302 visible: false,
28303 index: this.subgroupIndex,
28304 items: [],
28305 stack: this.subgroupStackAll || this.subgroupStack[subgroupId] || false
28306 };
28307 this.subgroupIndex++;
28308 }
28309
28310 if (new Date(item.data.start) < new Date(this.subgroups[subgroupId].start)) {
28311 this.subgroups[subgroupId].start = item.data.start;
28312 }
28313
28314 var itemEnd = item.data.end || item.data.start;
28315
28316 if (new Date(itemEnd) > new Date(this.subgroups[subgroupId].end)) {
28317 this.subgroups[subgroupId].end = itemEnd;
28318 }
28319
28320 this.subgroups[subgroupId].items.push(item);
28321};
28322
28323Group.prototype._updateSubgroupsSizes = function () {
28324 var me = this;
28325
28326 if (me.subgroups) {
28327 for (var subgroup in me.subgroups) {
28328 var initialEnd = me.subgroups[subgroup].items[0].data.end || me.subgroups[subgroup].items[0].data.start;
28329 var newStart = me.subgroups[subgroup].items[0].data.start;
28330 var newEnd = initialEnd - 1;
28331 me.subgroups[subgroup].items.forEach(function (item) {
28332 if (new Date(item.data.start) < new Date(newStart)) {
28333 newStart = item.data.start;
28334 }
28335
28336 var itemEnd = item.data.end || item.data.start;
28337
28338 if (new Date(itemEnd) > new Date(newEnd)) {
28339 newEnd = itemEnd;
28340 }
28341 });
28342 me.subgroups[subgroup].start = newStart;
28343 me.subgroups[subgroup].end = new Date(newEnd - 1); // -1 to compensate for colliding end to start subgroups;
28344 }
28345 }
28346};
28347
28348Group.prototype.orderSubgroups = function () {
28349 if (this.subgroupOrderer !== undefined) {
28350 var sortArray = [];
28351 var subgroup;
28352
28353 if (typeof this.subgroupOrderer == 'string') {
28354 for (subgroup in this.subgroups) {
28355 sortArray.push({
28356 subgroup: subgroup,
28357 sortField: this.subgroups[subgroup].items[0].data[this.subgroupOrderer]
28358 });
28359 }
28360
28361 sortArray.sort(function (a, b) {
28362 return a.sortField - b.sortField;
28363 });
28364 } else if (typeof this.subgroupOrderer == 'function') {
28365 for (subgroup in this.subgroups) {
28366 sortArray.push(this.subgroups[subgroup].items[0].data);
28367 }
28368
28369 sortArray.sort(this.subgroupOrderer);
28370 }
28371
28372 if (sortArray.length > 0) {
28373 for (var i = 0; i < sortArray.length; i++) {
28374 this.subgroups[sortArray[i].subgroup].index = i;
28375 }
28376 }
28377 }
28378};
28379
28380Group.prototype.resetSubgroups = function () {
28381 for (var subgroup in this.subgroups) {
28382 if (this.subgroups.hasOwnProperty(subgroup)) {
28383 this.subgroups[subgroup].visible = false;
28384 this.subgroups[subgroup].height = 0;
28385 }
28386 }
28387};
28388/**
28389 * Remove an item from the group
28390 * @param {Item} item
28391 */
28392
28393
28394Group.prototype.remove = function (item) {
28395 delete this.items[item.id];
28396 item.setParent(null);
28397 this.stackDirty = true; // remove from visible items
28398
28399 var index = this.visibleItems.indexOf(item);
28400 if (index != -1) this.visibleItems.splice(index, 1);
28401
28402 if (item.data.subgroup !== undefined) {
28403 this._removeFromSubgroup(item);
28404
28405 this.orderSubgroups();
28406 }
28407};
28408
28409Group.prototype._removeFromSubgroup = function (item, subgroupId) {
28410 subgroupId = subgroupId || item.data.subgroup;
28411
28412 if (subgroupId != undefined) {
28413 var subgroup = this.subgroups[subgroupId];
28414
28415 if (subgroup) {
28416 var itemIndex = subgroup.items.indexOf(item); // Check the item is actually in this subgroup. How should items not in the group be handled?
28417
28418 if (itemIndex >= 0) {
28419 subgroup.items.splice(itemIndex, 1);
28420
28421 if (!subgroup.items.length) {
28422 delete this.subgroups[subgroupId];
28423 } else {
28424 this._updateSubgroupsSizes();
28425 }
28426 }
28427 }
28428 }
28429};
28430/**
28431 * Remove an item from the corresponding DataSet
28432 * @param {Item} item
28433 */
28434
28435
28436Group.prototype.removeFromDataSet = function (item) {
28437 this.itemSet.removeItem(item.id);
28438};
28439/**
28440 * Reorder the items
28441 */
28442
28443
28444Group.prototype.order = function () {
28445 var array = util.toArray(this.items);
28446 var startArray = [];
28447 var endArray = [];
28448
28449 for (var i = 0; i < array.length; i++) {
28450 if (array[i].data.end !== undefined) {
28451 endArray.push(array[i]);
28452 }
28453
28454 startArray.push(array[i]);
28455 }
28456
28457 this.orderedItems = {
28458 byStart: startArray,
28459 byEnd: endArray
28460 };
28461 Stack.orderByStart(this.orderedItems.byStart);
28462 Stack.orderByEnd(this.orderedItems.byEnd);
28463};
28464/**
28465 * Update the visible items
28466 * @param {{byStart: Item[], byEnd: Item[]}} orderedItems All items ordered by start date and by end date
28467 * @param {Item[]} oldVisibleItems The previously visible items.
28468 * @param {{start: number, end: number}} range Visible range
28469 * @return {Item[]} visibleItems The new visible items.
28470 * @private
28471 */
28472
28473
28474Group.prototype._updateItemsInRange = function (orderedItems, oldVisibleItems, range) {
28475 var visibleItems = [];
28476 var visibleItemsLookup = {}; // we keep this to quickly look up if an item already exists in the list without using indexOf on visibleItems
28477
28478 var interval = (range.end - range.start) / 4;
28479 var lowerBound = range.start - interval;
28480 var upperBound = range.end + interval; // this function is used to do the binary search.
28481
28482 var searchFunction = function searchFunction(value) {
28483 if (value < lowerBound) {
28484 return -1;
28485 } else if (value <= upperBound) {
28486 return 0;
28487 } else {
28488 return 1;
28489 }
28490 }; // first check if the items that were in view previously are still in view.
28491 // IMPORTANT: this handles the case for the items with startdate before the window and enddate after the window!
28492 // also cleans up invisible items.
28493
28494
28495 if (oldVisibleItems.length > 0) {
28496 for (var i = 0; i < oldVisibleItems.length; i++) {
28497 this._checkIfVisibleWithReference(oldVisibleItems[i], visibleItems, visibleItemsLookup, range);
28498 }
28499 } // we do a binary search for the items that have only start values.
28500
28501
28502 var initialPosByStart = util.binarySearchCustom(orderedItems.byStart, searchFunction, 'data', 'start'); // trace the visible items from the inital start pos both ways until an invisible item is found, we only look at the start values.
28503
28504 this._traceVisible(initialPosByStart, orderedItems.byStart, visibleItems, visibleItemsLookup, function (item) {
28505 return item.data.start < lowerBound || item.data.start > upperBound;
28506 }); // if the window has changed programmatically without overlapping the old window, the ranged items with start < lowerBound and end > upperbound are not shown.
28507 // We therefore have to brute force check all items in the byEnd list
28508
28509
28510 if (this.checkRangedItems == true) {
28511 this.checkRangedItems = false;
28512
28513 for (i = 0; i < orderedItems.byEnd.length; i++) {
28514 this._checkIfVisibleWithReference(orderedItems.byEnd[i], visibleItems, visibleItemsLookup, range);
28515 }
28516 } else {
28517 // we do a binary search for the items that have defined end times.
28518 var initialPosByEnd = util.binarySearchCustom(orderedItems.byEnd, searchFunction, 'data', 'end'); // trace the visible items from the inital start pos both ways until an invisible item is found, we only look at the end values.
28519
28520 this._traceVisible(initialPosByEnd, orderedItems.byEnd, visibleItems, visibleItemsLookup, function (item) {
28521 return item.data.end < lowerBound || item.data.end > upperBound;
28522 });
28523 }
28524
28525 var redrawQueue = {};
28526 var redrawQueueLength = 0;
28527
28528 for (i = 0; i < visibleItems.length; i++) {
28529 var item = visibleItems[i];
28530
28531 if (!item.displayed) {
28532 var returnQueue = true;
28533 redrawQueue[i] = item.redraw(returnQueue);
28534 redrawQueueLength = redrawQueue[i].length;
28535 }
28536 }
28537
28538 var needRedraw = redrawQueueLength > 0;
28539
28540 if (needRedraw) {
28541 // redraw all regular items
28542 for (var j = 0; j < redrawQueueLength; j++) {
28543 util.forEach(redrawQueue, function (fns) {
28544 fns[j]();
28545 });
28546 }
28547 }
28548
28549 for (i = 0; i < visibleItems.length; i++) {
28550 visibleItems[i].repositionX();
28551 }
28552
28553 return visibleItems;
28554};
28555
28556Group.prototype._traceVisible = function (initialPos, items, visibleItems, visibleItemsLookup, breakCondition) {
28557 if (initialPos != -1) {
28558 var i, item;
28559
28560 for (i = initialPos; i >= 0; i--) {
28561 item = items[i];
28562
28563 if (breakCondition(item)) {
28564 break;
28565 } else {
28566 if (visibleItemsLookup[item.id] === undefined) {
28567 visibleItemsLookup[item.id] = true;
28568 visibleItems.push(item);
28569 }
28570 }
28571 }
28572
28573 for (i = initialPos + 1; i < items.length; i++) {
28574 item = items[i];
28575
28576 if (breakCondition(item)) {
28577 break;
28578 } else {
28579 if (visibleItemsLookup[item.id] === undefined) {
28580 visibleItemsLookup[item.id] = true;
28581 visibleItems.push(item);
28582 }
28583 }
28584 }
28585 }
28586};
28587/**
28588 * this function is very similar to the _checkIfInvisible() but it does not
28589 * return booleans, hides the item if it should not be seen and always adds to
28590 * the visibleItems.
28591 * this one is for brute forcing and hiding.
28592 *
28593 * @param {Item} item
28594 * @param {Array} visibleItems
28595 * @param {{start:number, end:number}} range
28596 * @private
28597 */
28598
28599
28600Group.prototype._checkIfVisible = function (item, visibleItems, range) {
28601 if (item.isVisible(range)) {
28602 if (!item.displayed) item.show(); // reposition item horizontally
28603
28604 item.repositionX();
28605 visibleItems.push(item);
28606 } else {
28607 if (item.displayed) item.hide();
28608 }
28609};
28610/**
28611 * this function is very similar to the _checkIfInvisible() but it does not
28612 * return booleans, hides the item if it should not be seen and always adds to
28613 * the visibleItems.
28614 * this one is for brute forcing and hiding.
28615 *
28616 * @param {Item} item
28617 * @param {Array.<vis.Item>} visibleItems
28618 * @param {Object<number, boolean>} visibleItemsLookup
28619 * @param {{start:number, end:number}} range
28620 * @private
28621 */
28622
28623
28624Group.prototype._checkIfVisibleWithReference = function (item, visibleItems, visibleItemsLookup, range) {
28625 if (item.isVisible(range)) {
28626 if (visibleItemsLookup[item.id] === undefined) {
28627 visibleItemsLookup[item.id] = true;
28628 visibleItems.push(item);
28629 }
28630 } else {
28631 if (item.displayed) item.hide();
28632 }
28633};
28634
28635Group.prototype.changeSubgroup = function (item, oldSubgroup, newSubgroup) {
28636 this._removeFromSubgroup(item, oldSubgroup);
28637
28638 this._addToSubgroup(item, newSubgroup);
28639
28640 this.orderSubgroups();
28641};
28642
28643var Group_1 = Group;
28644
28645/**
28646 * @constructor BackgroundGroup
28647 * @param {number | string} groupId
28648 * @param {Object} data
28649 * @param {ItemSet} itemSet
28650 * @extends Group
28651 */
28652
28653function BackgroundGroup(groupId, data, itemSet) {
28654 Group_1.call(this, groupId, data, itemSet);
28655 this.width = 0;
28656 this.height = 0;
28657 this.top = 0;
28658 this.left = 0;
28659}
28660
28661BackgroundGroup.prototype = Object.create(Group_1.prototype);
28662/**
28663 * Repaint this group
28664 * @param {{start: number, end: number}} range
28665 * @param {{item: {horizontal: number, vertical: number}, axis: number}} margin
28666 * @param {boolean} [forceRestack=false] Force restacking of all items
28667 * @return {boolean} Returns true if the group is resized
28668 */
28669
28670BackgroundGroup.prototype.redraw = function (range, margin, forceRestack) {
28671 // eslint-disable-line no-unused-vars
28672 var resized = false;
28673 this.visibleItems = this._updateItemsInRange(this.orderedItems, this.visibleItems, range); // calculate actual size
28674
28675 this.width = this.dom.background.offsetWidth; // apply new height (just always zero for BackgroundGroup
28676
28677 this.dom.background.style.height = '0'; // update vertical position of items after they are re-stacked and the height of the group is calculated
28678
28679 for (var i = 0, ii = this.visibleItems.length; i < ii; i++) {
28680 var item = this.visibleItems[i];
28681 item.repositionY(margin);
28682 }
28683
28684 return resized;
28685};
28686/**
28687 * Show this group: attach to the DOM
28688 */
28689
28690
28691BackgroundGroup.prototype.show = function () {
28692 if (!this.dom.background.parentNode) {
28693 this.itemSet.dom.background.appendChild(this.dom.background);
28694 }
28695};
28696
28697var BackgroundGroup_1 = BackgroundGroup;
28698
28699/**
28700 * @constructor Item
28701 * @param {Object} data Object containing (optional) parameters type,
28702 * start, end, content, group, className.
28703 * @param {{toScreen: function, toTime: function}} conversion
28704 * Conversion functions from time to screen and vice versa
28705 * @param {Object} options Configuration options
28706 * // TODO: describe available options
28707 */
28708
28709function Item(data, conversion, options) {
28710 this.id = null;
28711 this.parent = null;
28712 this.data = data;
28713 this.dom = null;
28714 this.conversion = conversion || {};
28715 this.options = options || {};
28716 this.selected = false;
28717 this.displayed = false;
28718 this.groupShowing = true;
28719 this.dirty = true;
28720 this.top = null;
28721 this.right = null;
28722 this.left = null;
28723 this.width = null;
28724 this.height = null;
28725 this.editable = null;
28726
28727 this._updateEditStatus();
28728}
28729
28730Item.prototype.stack = true;
28731/**
28732 * Select current item
28733 */
28734
28735Item.prototype.select = function () {
28736 this.selected = true;
28737 this.dirty = true;
28738 if (this.displayed) this.redraw();
28739};
28740/**
28741 * Unselect current item
28742 */
28743
28744
28745Item.prototype.unselect = function () {
28746 this.selected = false;
28747 this.dirty = true;
28748 if (this.displayed) this.redraw();
28749};
28750/**
28751 * Set data for the item. Existing data will be updated. The id should not
28752 * be changed. When the item is displayed, it will be redrawn immediately.
28753 * @param {Object} data
28754 */
28755
28756
28757Item.prototype.setData = function (data) {
28758 var groupChanged = data.group != undefined && this.data.group != data.group;
28759
28760 if (groupChanged && this.parent != null) {
28761 this.parent.itemSet._moveToGroup(this, data.group);
28762 }
28763
28764 if (this.parent) {
28765 this.parent.stackDirty = true;
28766 }
28767
28768 var subGroupChanged = data.subgroup != undefined && this.data.subgroup != data.subgroup;
28769
28770 if (subGroupChanged && this.parent != null) {
28771 this.parent.changeSubgroup(this, this.data.subgroup, data.subgroup);
28772 }
28773
28774 this.data = data;
28775
28776 this._updateEditStatus();
28777
28778 this.dirty = true;
28779 if (this.displayed) this.redraw();
28780};
28781/**
28782 * Set a parent for the item
28783 * @param {Group} parent
28784 */
28785
28786
28787Item.prototype.setParent = function (parent) {
28788 if (this.displayed) {
28789 this.hide();
28790 this.parent = parent;
28791
28792 if (this.parent) {
28793 this.show();
28794 }
28795 } else {
28796 this.parent = parent;
28797 }
28798};
28799/**
28800 * Check whether this item is visible inside given range
28801 * @param {vis.Range} range with a timestamp for start and end
28802 * @returns {boolean} True if visible
28803 */
28804
28805
28806Item.prototype.isVisible = function (range) {
28807 // eslint-disable-line no-unused-vars
28808 return false;
28809};
28810/**
28811 * Show the Item in the DOM (when not already visible)
28812 * @return {Boolean} changed
28813 */
28814
28815
28816Item.prototype.show = function () {
28817 return false;
28818};
28819/**
28820 * Hide the Item from the DOM (when visible)
28821 * @return {Boolean} changed
28822 */
28823
28824
28825Item.prototype.hide = function () {
28826 return false;
28827};
28828/**
28829 * Repaint the item
28830 */
28831
28832
28833Item.prototype.redraw = function () {// should be implemented by the item
28834};
28835/**
28836 * Reposition the Item horizontally
28837 */
28838
28839
28840Item.prototype.repositionX = function () {// should be implemented by the item
28841};
28842/**
28843 * Reposition the Item vertically
28844 */
28845
28846
28847Item.prototype.repositionY = function () {// should be implemented by the item
28848};
28849/**
28850 * Repaint a drag area on the center of the item when the item is selected
28851 * @protected
28852 */
28853
28854
28855Item.prototype._repaintDragCenter = function () {
28856 if (this.selected && this.options.editable.updateTime && !this.dom.dragCenter) {
28857 var me = this; // create and show drag area
28858
28859 var dragCenter = document.createElement('div');
28860 dragCenter.className = 'vis-drag-center';
28861 dragCenter.dragCenterItem = this;
28862 var hammer = new hammer$1(dragCenter);
28863 hammer.on('tap', function (event) {
28864 me.parent.itemSet.body.emitter.emit('click', {
28865 event: event,
28866 item: me.id
28867 });
28868 });
28869 hammer.on('doubletap', function (event) {
28870 event.stopPropagation();
28871
28872 me.parent.itemSet._onUpdateItem(me);
28873
28874 me.parent.itemSet.body.emitter.emit('doubleClick', {
28875 event: event,
28876 item: me.id
28877 });
28878 });
28879
28880 if (this.dom.box) {
28881 if (this.dom.dragLeft) {
28882 this.dom.box.insertBefore(dragCenter, this.dom.dragLeft);
28883 } else {
28884 this.dom.box.appendChild(dragCenter);
28885 }
28886 } else if (this.dom.point) {
28887 this.dom.point.appendChild(dragCenter);
28888 }
28889
28890 this.dom.dragCenter = dragCenter;
28891 } else if (!this.selected && this.dom.dragCenter) {
28892 // delete drag area
28893 if (this.dom.dragCenter.parentNode) {
28894 this.dom.dragCenter.parentNode.removeChild(this.dom.dragCenter);
28895 }
28896
28897 this.dom.dragCenter = null;
28898 }
28899};
28900/**
28901 * Repaint a delete button on the top right of the item when the item is selected
28902 * @param {HTMLElement} anchor
28903 * @protected
28904 */
28905
28906
28907Item.prototype._repaintDeleteButton = function (anchor) {
28908 var editable = (this.options.editable.overrideItems || this.editable == null) && this.options.editable.remove || !this.options.editable.overrideItems && this.editable != null && this.editable.remove;
28909
28910 if (this.selected && editable && !this.dom.deleteButton) {
28911 // create and show button
28912 var me = this;
28913 var deleteButton = document.createElement('div');
28914
28915 if (this.options.rtl) {
28916 deleteButton.className = 'vis-delete-rtl';
28917 } else {
28918 deleteButton.className = 'vis-delete';
28919 }
28920
28921 deleteButton.title = 'Delete this item'; // TODO: be able to destroy the delete button
28922
28923 new hammer$1(deleteButton).on('tap', function (event) {
28924 event.stopPropagation();
28925 me.parent.removeFromDataSet(me);
28926 });
28927 anchor.appendChild(deleteButton);
28928 this.dom.deleteButton = deleteButton;
28929 } else if (!this.selected && this.dom.deleteButton) {
28930 // remove button
28931 if (this.dom.deleteButton.parentNode) {
28932 this.dom.deleteButton.parentNode.removeChild(this.dom.deleteButton);
28933 }
28934
28935 this.dom.deleteButton = null;
28936 }
28937};
28938/**
28939 * Repaint a onChange tooltip on the top right of the item when the item is selected
28940 * @param {HTMLElement} anchor
28941 * @protected
28942 */
28943
28944
28945Item.prototype._repaintOnItemUpdateTimeTooltip = function (anchor) {
28946 if (!this.options.tooltipOnItemUpdateTime) return;
28947 var editable = (this.options.editable.updateTime || this.data.editable === true) && this.data.editable !== false;
28948
28949 if (this.selected && editable && !this.dom.onItemUpdateTimeTooltip) {
28950 var onItemUpdateTimeTooltip = document.createElement('div');
28951 onItemUpdateTimeTooltip.className = 'vis-onUpdateTime-tooltip';
28952 anchor.appendChild(onItemUpdateTimeTooltip);
28953 this.dom.onItemUpdateTimeTooltip = onItemUpdateTimeTooltip;
28954 } else if (!this.selected && this.dom.onItemUpdateTimeTooltip) {
28955 // remove button
28956 if (this.dom.onItemUpdateTimeTooltip.parentNode) {
28957 this.dom.onItemUpdateTimeTooltip.parentNode.removeChild(this.dom.onItemUpdateTimeTooltip);
28958 }
28959
28960 this.dom.onItemUpdateTimeTooltip = null;
28961 } // position onChange tooltip
28962
28963
28964 if (this.dom.onItemUpdateTimeTooltip) {
28965 // only show when editing
28966 this.dom.onItemUpdateTimeTooltip.style.visibility = this.parent.itemSet.touchParams.itemIsDragging ? 'visible' : 'hidden'; // position relative to item's content
28967
28968 if (this.options.rtl) {
28969 this.dom.onItemUpdateTimeTooltip.style.right = this.dom.content.style.right;
28970 } else {
28971 this.dom.onItemUpdateTimeTooltip.style.left = this.dom.content.style.left;
28972 } // position above or below the item depending on the item's position in the window
28973
28974
28975 var tooltipOffset = 50; // TODO: should be tooltip height (depends on template)
28976
28977 var scrollTop = this.parent.itemSet.body.domProps.scrollTop; // TODO: this.top for orientation:true is actually the items distance from the bottom...
28978 // (should be this.bottom)
28979
28980 var itemDistanceFromTop;
28981
28982 if (this.options.orientation.item == 'top') {
28983 itemDistanceFromTop = this.top;
28984 } else {
28985 itemDistanceFromTop = this.parent.height - this.top - this.height;
28986 }
28987
28988 var isCloseToTop = itemDistanceFromTop + this.parent.top - tooltipOffset < -scrollTop;
28989
28990 if (isCloseToTop) {
28991 this.dom.onItemUpdateTimeTooltip.style.bottom = "";
28992 this.dom.onItemUpdateTimeTooltip.style.top = this.height + 2 + "px";
28993 } else {
28994 this.dom.onItemUpdateTimeTooltip.style.top = "";
28995 this.dom.onItemUpdateTimeTooltip.style.bottom = this.height + 2 + "px";
28996 } // handle tooltip content
28997
28998
28999 var content;
29000 var templateFunction;
29001
29002 if (this.options.tooltipOnItemUpdateTime && this.options.tooltipOnItemUpdateTime.template) {
29003 templateFunction = this.options.tooltipOnItemUpdateTime.template.bind(this);
29004 content = templateFunction(this.data);
29005 } else {
29006 content = 'start: ' + moment$3(this.data.start).format('MM/DD/YYYY hh:mm');
29007
29008 if (this.data.end) {
29009 content += '<br> end: ' + moment$3(this.data.end).format('MM/DD/YYYY hh:mm');
29010 }
29011 }
29012
29013 this.dom.onItemUpdateTimeTooltip.innerHTML = content;
29014 }
29015};
29016/**
29017 * Set HTML contents for the item
29018 * @param {Element} element HTML element to fill with the contents
29019 * @private
29020 */
29021
29022
29023Item.prototype._updateContents = function (element) {
29024 var content;
29025 var changed;
29026 var templateFunction;
29027 var itemVisibleFrameContent;
29028 var visibleFrameTemplateFunction;
29029 var itemData = this.parent.itemSet.itemsData.get(this.id); // get a clone of the data from the dataset
29030
29031 var frameElement = this.dom.box || this.dom.point;
29032 var itemVisibleFrameContentElement = frameElement.getElementsByClassName('vis-item-visible-frame')[0];
29033
29034 if (this.options.visibleFrameTemplate) {
29035 visibleFrameTemplateFunction = this.options.visibleFrameTemplate.bind(this);
29036 itemVisibleFrameContent = visibleFrameTemplateFunction(itemData, frameElement);
29037 } else {
29038 itemVisibleFrameContent = '';
29039 }
29040
29041 if (itemVisibleFrameContentElement) {
29042 if (itemVisibleFrameContent instanceof Object && !(itemVisibleFrameContent instanceof Element)) {
29043 visibleFrameTemplateFunction(itemData, itemVisibleFrameContentElement);
29044 } else {
29045 changed = this._contentToString(this.itemVisibleFrameContent) !== this._contentToString(itemVisibleFrameContent);
29046
29047 if (changed) {
29048 // only replace the content when changed
29049 if (itemVisibleFrameContent instanceof Element) {
29050 itemVisibleFrameContentElement.innerHTML = '';
29051 itemVisibleFrameContentElement.appendChild(itemVisibleFrameContent);
29052 } else if (itemVisibleFrameContent != undefined) {
29053 itemVisibleFrameContentElement.innerHTML = itemVisibleFrameContent;
29054 } else {
29055 if (!(this.data.type == 'background' && this.data.content === undefined)) {
29056 throw new Error('Property "content" missing in item ' + this.id);
29057 }
29058 }
29059
29060 this.itemVisibleFrameContent = itemVisibleFrameContent;
29061 }
29062 }
29063 }
29064
29065 if (this.options.template) {
29066 templateFunction = this.options.template.bind(this);
29067 content = templateFunction(itemData, element, this.data);
29068 } else {
29069 content = this.data.content;
29070 }
29071
29072 if (content instanceof Object && !(content instanceof Element)) {
29073 templateFunction(itemData, element);
29074 } else {
29075 changed = this._contentToString(this.content) !== this._contentToString(content);
29076
29077 if (changed) {
29078 // only replace the content when changed
29079 if (content instanceof Element) {
29080 element.innerHTML = '';
29081 element.appendChild(content);
29082 } else if (content != undefined) {
29083 element.innerHTML = content;
29084 } else {
29085 if (!(this.data.type == 'background' && this.data.content === undefined)) {
29086 throw new Error('Property "content" missing in item ' + this.id);
29087 }
29088 }
29089
29090 this.content = content;
29091 }
29092 }
29093};
29094/**
29095 * Process dataAttributes timeline option and set as data- attributes on dom.content
29096 * @param {Element} element HTML element to which the attributes will be attached
29097 * @private
29098 */
29099
29100
29101Item.prototype._updateDataAttributes = function (element) {
29102 if (this.options.dataAttributes && this.options.dataAttributes.length > 0) {
29103 var attributes = [];
29104
29105 if (Array.isArray(this.options.dataAttributes)) {
29106 attributes = this.options.dataAttributes;
29107 } else if (this.options.dataAttributes == 'all') {
29108 attributes = Object.keys(this.data);
29109 } else {
29110 return;
29111 }
29112
29113 for (var i = 0; i < attributes.length; i++) {
29114 var name = attributes[i];
29115 var value = this.data[name];
29116
29117 if (value != null) {
29118 element.setAttribute('data-' + name, value);
29119 } else {
29120 element.removeAttribute('data-' + name);
29121 }
29122 }
29123 }
29124};
29125/**
29126 * Update custom styles of the element
29127 * @param {Element} element
29128 * @private
29129 */
29130
29131
29132Item.prototype._updateStyle = function (element) {
29133 // remove old styles
29134 if (this.style) {
29135 util.removeCssText(element, this.style);
29136 this.style = null;
29137 } // append new styles
29138
29139
29140 if (this.data.style) {
29141 util.addCssText(element, this.data.style);
29142 this.style = this.data.style;
29143 }
29144};
29145/**
29146 * Stringify the items contents
29147 * @param {string | Element | undefined} content
29148 * @returns {string | undefined}
29149 * @private
29150 */
29151
29152
29153Item.prototype._contentToString = function (content) {
29154 if (typeof content === 'string') return content;
29155 if (content && 'outerHTML' in content) return content.outerHTML;
29156 return content;
29157};
29158/**
29159 * Update the editability of this item.
29160 */
29161
29162
29163Item.prototype._updateEditStatus = function () {
29164 if (this.options) {
29165 if (typeof this.options.editable === 'boolean') {
29166 this.editable = {
29167 updateTime: this.options.editable,
29168 updateGroup: this.options.editable,
29169 remove: this.options.editable
29170 };
29171 } else if (_typeof$1(this.options.editable) === 'object') {
29172 this.editable = {};
29173 util.selectiveExtend(['updateTime', 'updateGroup', 'remove'], this.editable, this.options.editable);
29174 }
29175 } // Item data overrides, except if options.editable.overrideItems is set.
29176
29177
29178 if (!this.options || !this.options.editable || this.options.editable.overrideItems !== true) {
29179 if (this.data) {
29180 if (typeof this.data.editable === 'boolean') {
29181 this.editable = {
29182 updateTime: this.data.editable,
29183 updateGroup: this.data.editable,
29184 remove: this.data.editable
29185 };
29186 } else if (_typeof$1(this.data.editable) === 'object') {
29187 // TODO: in vis.js 5.0, we should change this to not reset options from the timeline configuration.
29188 // Basically just remove the next line...
29189 this.editable = {};
29190 util.selectiveExtend(['updateTime', 'updateGroup', 'remove'], this.editable, this.data.editable);
29191 }
29192 }
29193 }
29194};
29195/**
29196 * Return the width of the item left from its start date
29197 * @return {number}
29198 */
29199
29200
29201Item.prototype.getWidthLeft = function () {
29202 return 0;
29203};
29204/**
29205 * Return the width of the item right from the max of its start and end date
29206 * @return {number}
29207 */
29208
29209
29210Item.prototype.getWidthRight = function () {
29211 return 0;
29212};
29213/**
29214 * Return the title of the item
29215 * @return {string | undefined}
29216 */
29217
29218
29219Item.prototype.getTitle = function () {
29220 return this.data.title;
29221};
29222
29223/**
29224 * @constructor BoxItem
29225 * @extends Item
29226 * @param {Object} data Object containing parameters start
29227 * content, className.
29228 * @param {{toScreen: function, toTime: function}} conversion
29229 * Conversion functions from time to screen and vice versa
29230 * @param {Object} [options] Configuration options
29231 * // TODO: describe available options
29232 */
29233
29234function BoxItem(data, conversion, options) {
29235 this.props = {
29236 dot: {
29237 width: 0,
29238 height: 0
29239 },
29240 line: {
29241 width: 0,
29242 height: 0
29243 }
29244 };
29245 this.options = options; // validate data
29246
29247 if (data) {
29248 if (data.start == undefined) {
29249 throw new Error('Property "start" missing in item ' + data);
29250 }
29251 }
29252
29253 Item.call(this, data, conversion, options);
29254}
29255
29256BoxItem.prototype = new Item(null, null, null);
29257/**
29258 * Check whether this item is visible inside given range
29259 * @param {{start: number, end: number}} range with a timestamp for start and end
29260 * @returns {boolean} True if visible
29261 */
29262
29263BoxItem.prototype.isVisible = function (range) {
29264 // determine visibility
29265 var isVisible;
29266 var align = this.options.align;
29267 var widthInMs = this.width * range.getMillisecondsPerPixel();
29268
29269 if (align == 'right') {
29270 isVisible = this.data.start.getTime() > range.start && this.data.start.getTime() - widthInMs < range.end;
29271 } else if (align == 'left') {
29272 isVisible = this.data.start.getTime() + widthInMs > range.start && this.data.start.getTime() < range.end;
29273 } else {
29274 // default or 'center'
29275 isVisible = this.data.start.getTime() + widthInMs / 2 > range.start && this.data.start.getTime() - widthInMs / 2 < range.end;
29276 }
29277
29278 return isVisible;
29279};
29280
29281BoxItem.prototype._createDomElement = function () {
29282 if (!this.dom) {
29283 // create DOM
29284 this.dom = {}; // create main box
29285
29286 this.dom.box = document.createElement('DIV'); // contents box (inside the background box). used for making margins
29287
29288 this.dom.content = document.createElement('DIV');
29289 this.dom.content.className = 'vis-item-content';
29290 this.dom.box.appendChild(this.dom.content); // line to axis
29291
29292 this.dom.line = document.createElement('DIV');
29293 this.dom.line.className = 'vis-line'; // dot on axis
29294
29295 this.dom.dot = document.createElement('DIV');
29296 this.dom.dot.className = 'vis-dot'; // attach this item as attribute
29297
29298 this.dom.box['timeline-item'] = this;
29299 this.dirty = true;
29300 }
29301};
29302
29303BoxItem.prototype._appendDomElement = function () {
29304 if (!this.parent) {
29305 throw new Error('Cannot redraw item: no parent attached');
29306 }
29307
29308 if (!this.dom.box.parentNode) {
29309 var foreground = this.parent.dom.foreground;
29310 if (!foreground) throw new Error('Cannot redraw item: parent has no foreground container element');
29311 foreground.appendChild(this.dom.box);
29312 }
29313
29314 if (!this.dom.line.parentNode) {
29315 var background = this.parent.dom.background;
29316 if (!background) throw new Error('Cannot redraw item: parent has no background container element');
29317 background.appendChild(this.dom.line);
29318 }
29319
29320 if (!this.dom.dot.parentNode) {
29321 var axis = this.parent.dom.axis;
29322 if (!background) throw new Error('Cannot redraw item: parent has no axis container element');
29323 axis.appendChild(this.dom.dot);
29324 }
29325
29326 this.displayed = true;
29327};
29328
29329BoxItem.prototype._updateDirtyDomComponents = function () {
29330 // An item is marked dirty when:
29331 // - the item is not yet rendered
29332 // - the item's data is changed
29333 // - the item is selected/deselected
29334 if (this.dirty) {
29335 this._updateContents(this.dom.content);
29336
29337 this._updateDataAttributes(this.dom.box);
29338
29339 this._updateStyle(this.dom.box);
29340
29341 var editable = this.editable.updateTime || this.editable.updateGroup; // update class
29342
29343 var className = (this.data.className ? ' ' + this.data.className : '') + (this.selected ? ' vis-selected' : '') + (editable ? ' vis-editable' : ' vis-readonly');
29344 this.dom.box.className = 'vis-item vis-box' + className;
29345 this.dom.line.className = 'vis-item vis-line' + className;
29346 this.dom.dot.className = 'vis-item vis-dot' + className;
29347 }
29348};
29349
29350BoxItem.prototype._getDomComponentsSizes = function () {
29351 return {
29352 previous: {
29353 right: this.dom.box.style.right,
29354 left: this.dom.box.style.left
29355 },
29356 dot: {
29357 height: this.dom.dot.offsetHeight,
29358 width: this.dom.dot.offsetWidth
29359 },
29360 line: {
29361 width: this.dom.line.offsetWidth
29362 },
29363 box: {
29364 width: this.dom.box.offsetWidth,
29365 height: this.dom.box.offsetHeight
29366 }
29367 };
29368};
29369
29370BoxItem.prototype._updateDomComponentsSizes = function (sizes) {
29371 if (this.options.rtl) {
29372 this.dom.box.style.right = "0px";
29373 } else {
29374 this.dom.box.style.left = "0px";
29375 } // recalculate size
29376
29377
29378 this.props.dot.height = sizes.dot.height;
29379 this.props.dot.width = sizes.dot.width;
29380 this.props.line.width = sizes.line.width;
29381 this.width = sizes.box.width;
29382 this.height = sizes.box.height; // restore previous position
29383
29384 if (this.options.rtl) {
29385 this.dom.box.style.right = sizes.previous.right;
29386 } else {
29387 this.dom.box.style.left = sizes.previous.left;
29388 }
29389
29390 this.dirty = false;
29391};
29392
29393BoxItem.prototype._repaintDomAdditionals = function () {
29394 this._repaintOnItemUpdateTimeTooltip(this.dom.box);
29395
29396 this._repaintDragCenter();
29397
29398 this._repaintDeleteButton(this.dom.box);
29399};
29400/**
29401 * Repaint the item
29402 * @param {boolean} [returnQueue=false] return the queue
29403 * @return {boolean} the redraw queue if returnQueue=true
29404 */
29405
29406
29407BoxItem.prototype.redraw = function (returnQueue) {
29408 var sizes;
29409 var queue = [// create item DOM
29410 this._createDomElement.bind(this), // append DOM to parent DOM
29411 this._appendDomElement.bind(this), // update dirty DOM
29412 this._updateDirtyDomComponents.bind(this), function () {
29413 if (this.dirty) {
29414 sizes = this._getDomComponentsSizes();
29415 }
29416 }.bind(this), function () {
29417 if (this.dirty) {
29418 this._updateDomComponentsSizes.bind(this)(sizes);
29419 }
29420 }.bind(this), // repaint DOM additionals
29421 this._repaintDomAdditionals.bind(this)];
29422
29423 if (returnQueue) {
29424 return queue;
29425 } else {
29426 var result;
29427 queue.forEach(function (fn) {
29428 result = fn();
29429 });
29430 return result;
29431 }
29432};
29433/**
29434 * Show the item in the DOM (when not already visible). The items DOM will
29435 * be created when needed.
29436 * @param {boolean} [returnQueue=false] whether to return a queue of functions to execute instead of just executing them
29437 * @return {boolean} the redraw queue if returnQueue=true
29438 */
29439
29440
29441BoxItem.prototype.show = function (returnQueue) {
29442 if (!this.displayed) {
29443 return this.redraw(returnQueue);
29444 }
29445};
29446/**
29447 * Hide the item from the DOM (when visible)
29448 */
29449
29450
29451BoxItem.prototype.hide = function () {
29452 if (this.displayed) {
29453 var dom = this.dom;
29454 if (dom.box.parentNode) dom.box.parentNode.removeChild(dom.box);
29455 if (dom.line.parentNode) dom.line.parentNode.removeChild(dom.line);
29456 if (dom.dot.parentNode) dom.dot.parentNode.removeChild(dom.dot);
29457 this.displayed = false;
29458 }
29459};
29460/**
29461 * Reposition the item horizontally
29462 * @Override
29463 */
29464
29465
29466BoxItem.prototype.repositionX = function () {
29467 var start = this.conversion.toScreen(this.data.start);
29468 var align = this.options.align; // calculate left position of the box
29469
29470 if (align == 'right') {
29471 if (this.options.rtl) {
29472 this.right = start - this.width; // reposition box, line, and dot
29473
29474 this.dom.box.style.right = this.right + 'px';
29475 this.dom.line.style.right = start - this.props.line.width + 'px';
29476 this.dom.dot.style.right = start - this.props.line.width / 2 - this.props.dot.width / 2 + 'px';
29477 } else {
29478 this.left = start - this.width; // reposition box, line, and dot
29479
29480 this.dom.box.style.left = this.left + 'px';
29481 this.dom.line.style.left = start - this.props.line.width + 'px';
29482 this.dom.dot.style.left = start - this.props.line.width / 2 - this.props.dot.width / 2 + 'px';
29483 }
29484 } else if (align == 'left') {
29485 if (this.options.rtl) {
29486 this.right = start; // reposition box, line, and dot
29487
29488 this.dom.box.style.right = this.right + 'px';
29489 this.dom.line.style.right = start + 'px';
29490 this.dom.dot.style.right = start + this.props.line.width / 2 - this.props.dot.width / 2 + 'px';
29491 } else {
29492 this.left = start; // reposition box, line, and dot
29493
29494 this.dom.box.style.left = this.left + 'px';
29495 this.dom.line.style.left = start + 'px';
29496 this.dom.dot.style.left = start + this.props.line.width / 2 - this.props.dot.width / 2 + 'px';
29497 }
29498 } else {
29499 // default or 'center'
29500 if (this.options.rtl) {
29501 this.right = start - this.width / 2; // reposition box, line, and dot
29502
29503 this.dom.box.style.right = this.right + 'px';
29504 this.dom.line.style.right = start - this.props.line.width + 'px';
29505 this.dom.dot.style.right = start - this.props.dot.width / 2 + 'px';
29506 } else {
29507 this.left = start - this.width / 2; // reposition box, line, and dot
29508
29509 this.dom.box.style.left = this.left + 'px';
29510 this.dom.line.style.left = start - this.props.line.width / 2 + 'px';
29511 this.dom.dot.style.left = start - this.props.dot.width / 2 + 'px';
29512 }
29513 }
29514};
29515/**
29516 * Reposition the item vertically
29517 * @Override
29518 */
29519
29520
29521BoxItem.prototype.repositionY = function () {
29522 var orientation = this.options.orientation.item;
29523 var box = this.dom.box;
29524 var line = this.dom.line;
29525 var dot = this.dom.dot;
29526
29527 if (orientation == 'top') {
29528 box.style.top = (this.top || 0) + 'px';
29529 line.style.top = '0';
29530 line.style.height = this.parent.top + this.top + 1 + 'px';
29531 line.style.bottom = '';
29532 } else {
29533 // orientation 'bottom'
29534 var itemSetHeight = this.parent.itemSet.props.height; // TODO: this is nasty
29535
29536 var lineHeight = itemSetHeight - this.parent.top - this.parent.height + this.top;
29537 box.style.top = (this.parent.height - this.top - this.height || 0) + 'px';
29538 line.style.top = itemSetHeight - lineHeight + 'px';
29539 line.style.bottom = '0';
29540 }
29541
29542 dot.style.top = -this.props.dot.height / 2 + 'px';
29543};
29544/**
29545 * Return the width of the item left from its start date
29546 * @return {number}
29547 */
29548
29549
29550BoxItem.prototype.getWidthLeft = function () {
29551 return this.width / 2;
29552};
29553/**
29554 * Return the width of the item right from its start date
29555 * @return {number}
29556 */
29557
29558
29559BoxItem.prototype.getWidthRight = function () {
29560 return this.width / 2;
29561};
29562
29563var BoxItem_1 = BoxItem;
29564
29565/**
29566 * @constructor PointItem
29567 * @extends Item
29568 * @param {Object} data Object containing parameters start
29569 * content, className.
29570 * @param {{toScreen: function, toTime: function}} conversion
29571 * Conversion functions from time to screen and vice versa
29572 * @param {Object} [options] Configuration options
29573 * // TODO: describe available options
29574 */
29575
29576function PointItem(data, conversion, options) {
29577 this.props = {
29578 dot: {
29579 top: 0,
29580 width: 0,
29581 height: 0
29582 },
29583 content: {
29584 height: 0,
29585 marginLeft: 0,
29586 marginRight: 0
29587 }
29588 };
29589 this.options = options; // validate data
29590
29591 if (data) {
29592 if (data.start == undefined) {
29593 throw new Error('Property "start" missing in item ' + data);
29594 }
29595 }
29596
29597 Item.call(this, data, conversion, options);
29598}
29599
29600PointItem.prototype = new Item(null, null, null);
29601/**
29602 * Check whether this item is visible inside given range
29603 * @param {{start: number, end: number}} range with a timestamp for start and end
29604 * @returns {boolean} True if visible
29605 */
29606
29607PointItem.prototype.isVisible = function (range) {
29608 // determine visibility
29609 var widthInMs = this.width * range.getMillisecondsPerPixel();
29610 return this.data.start.getTime() + widthInMs > range.start && this.data.start < range.end;
29611};
29612
29613PointItem.prototype._createDomElement = function () {
29614 if (!this.dom) {
29615 // create DOM
29616 this.dom = {}; // background box
29617
29618 this.dom.point = document.createElement('div'); // className is updated in redraw()
29619 // contents box, right from the dot
29620
29621 this.dom.content = document.createElement('div');
29622 this.dom.content.className = 'vis-item-content';
29623 this.dom.point.appendChild(this.dom.content); // dot at start
29624
29625 this.dom.dot = document.createElement('div');
29626 this.dom.point.appendChild(this.dom.dot); // attach this item as attribute
29627
29628 this.dom.point['timeline-item'] = this;
29629 this.dirty = true;
29630 }
29631};
29632
29633PointItem.prototype._appendDomElement = function () {
29634 if (!this.parent) {
29635 throw new Error('Cannot redraw item: no parent attached');
29636 }
29637
29638 if (!this.dom.point.parentNode) {
29639 var foreground = this.parent.dom.foreground;
29640
29641 if (!foreground) {
29642 throw new Error('Cannot redraw item: parent has no foreground container element');
29643 }
29644
29645 foreground.appendChild(this.dom.point);
29646 }
29647
29648 this.displayed = true;
29649};
29650
29651PointItem.prototype._updateDirtyDomComponents = function () {
29652 // An item is marked dirty when:
29653 // - the item is not yet rendered
29654 // - the item's data is changed
29655 // - the item is selected/deselected
29656 if (this.dirty) {
29657 this._updateContents(this.dom.content);
29658
29659 this._updateDataAttributes(this.dom.point);
29660
29661 this._updateStyle(this.dom.point);
29662
29663 var editable = this.editable.updateTime || this.editable.updateGroup; // update class
29664
29665 var className = (this.data.className ? ' ' + this.data.className : '') + (this.selected ? ' vis-selected' : '') + (editable ? ' vis-editable' : ' vis-readonly');
29666 this.dom.point.className = 'vis-item vis-point' + className;
29667 this.dom.dot.className = 'vis-item vis-dot' + className;
29668 }
29669};
29670
29671PointItem.prototype._getDomComponentsSizes = function () {
29672 return {
29673 dot: {
29674 width: this.dom.dot.offsetWidth,
29675 height: this.dom.dot.offsetHeight
29676 },
29677 content: {
29678 width: this.dom.content.offsetWidth,
29679 height: this.dom.content.offsetHeight
29680 },
29681 point: {
29682 width: this.dom.point.offsetWidth,
29683 height: this.dom.point.offsetHeight
29684 }
29685 };
29686};
29687
29688PointItem.prototype._updateDomComponentsSizes = function (sizes) {
29689 // recalculate size of dot and contents
29690 this.props.dot.width = sizes.dot.width;
29691 this.props.dot.height = sizes.dot.height;
29692 this.props.content.height = sizes.content.height; // resize contents
29693
29694 if (this.options.rtl) {
29695 this.dom.content.style.marginRight = 2 * this.props.dot.width + 'px';
29696 } else {
29697 this.dom.content.style.marginLeft = 2 * this.props.dot.width + 'px';
29698 } //this.dom.content.style.marginRight = ... + 'px'; // TODO: margin right
29699 // recalculate size
29700
29701
29702 this.width = sizes.point.width;
29703 this.height = sizes.point.height; // reposition the dot
29704
29705 this.dom.dot.style.top = (this.height - this.props.dot.height) / 2 + 'px';
29706
29707 if (this.options.rtl) {
29708 this.dom.dot.style.right = this.props.dot.width / 2 + 'px';
29709 } else {
29710 this.dom.dot.style.left = this.props.dot.width / 2 + 'px';
29711 }
29712
29713 this.dirty = false;
29714};
29715
29716PointItem.prototype._repaintDomAdditionals = function () {
29717 this._repaintOnItemUpdateTimeTooltip(this.dom.point);
29718
29719 this._repaintDragCenter();
29720
29721 this._repaintDeleteButton(this.dom.point);
29722};
29723/**
29724 * Repaint the item
29725 * @param {boolean} [returnQueue=false] return the queue
29726 * @return {boolean} the redraw queue if returnQueue=true
29727 */
29728
29729
29730PointItem.prototype.redraw = function (returnQueue) {
29731 var sizes;
29732 var queue = [// create item DOM
29733 this._createDomElement.bind(this), // append DOM to parent DOM
29734 this._appendDomElement.bind(this), // update dirty DOM
29735 this._updateDirtyDomComponents.bind(this), function () {
29736 if (this.dirty) {
29737 sizes = this._getDomComponentsSizes();
29738 }
29739 }.bind(this), function () {
29740 if (this.dirty) {
29741 this._updateDomComponentsSizes.bind(this)(sizes);
29742 }
29743 }.bind(this), // repaint DOM additionals
29744 this._repaintDomAdditionals.bind(this)];
29745
29746 if (returnQueue) {
29747 return queue;
29748 } else {
29749 var result;
29750 queue.forEach(function (fn) {
29751 result = fn();
29752 });
29753 return result;
29754 }
29755};
29756/**
29757 * Show the item in the DOM (when not already visible). The items DOM will
29758 * be created when needed.
29759 * @param {boolean} [returnQueue=false] whether to return a queue of functions to execute instead of just executing them
29760 * @return {boolean} the redraw queue if returnQueue=true
29761 */
29762
29763
29764PointItem.prototype.show = function (returnQueue) {
29765 if (!this.displayed) {
29766 return this.redraw(returnQueue);
29767 }
29768};
29769/**
29770 * Hide the item from the DOM (when visible)
29771 */
29772
29773
29774PointItem.prototype.hide = function () {
29775 if (this.displayed) {
29776 if (this.dom.point.parentNode) {
29777 this.dom.point.parentNode.removeChild(this.dom.point);
29778 }
29779
29780 this.displayed = false;
29781 }
29782};
29783/**
29784 * Reposition the item horizontally
29785 * @Override
29786 */
29787
29788
29789PointItem.prototype.repositionX = function () {
29790 var start = this.conversion.toScreen(this.data.start);
29791
29792 if (this.options.rtl) {
29793 this.right = start - this.props.dot.width; // reposition point
29794
29795 this.dom.point.style.right = this.right + 'px';
29796 } else {
29797 this.left = start - this.props.dot.width; // reposition point
29798
29799 this.dom.point.style.left = this.left + 'px';
29800 }
29801};
29802/**
29803 * Reposition the item vertically
29804 * @Override
29805 */
29806
29807
29808PointItem.prototype.repositionY = function () {
29809 var orientation = this.options.orientation.item;
29810 var point = this.dom.point;
29811
29812 if (orientation == 'top') {
29813 point.style.top = this.top + 'px';
29814 } else {
29815 point.style.top = this.parent.height - this.top - this.height + 'px';
29816 }
29817};
29818/**
29819 * Return the width of the item left from its start date
29820 * @return {number}
29821 */
29822
29823
29824PointItem.prototype.getWidthLeft = function () {
29825 return this.props.dot.width;
29826};
29827/**
29828 * Return the width of the item right from its start date
29829 * @return {number}
29830 */
29831
29832
29833PointItem.prototype.getWidthRight = function () {
29834 return this.props.dot.width;
29835};
29836
29837var PointItem_1 = PointItem;
29838
29839/**
29840 * @constructor RangeItem
29841 * @extends Item
29842 * @param {Object} data Object containing parameters start, end
29843 * content, className.
29844 * @param {{toScreen: function, toTime: function}} conversion
29845 * Conversion functions from time to screen and vice versa
29846 * @param {Object} [options] Configuration options
29847 * // TODO: describe options
29848 */
29849
29850function RangeItem(data, conversion, options) {
29851 this.props = {
29852 content: {
29853 width: 0
29854 }
29855 };
29856 this.overflow = false; // if contents can overflow (css styling), this flag is set to true
29857
29858 this.options = options; // validate data
29859
29860 if (data) {
29861 if (data.start == undefined) {
29862 throw new Error('Property "start" missing in item ' + data.id);
29863 }
29864
29865 if (data.end == undefined) {
29866 throw new Error('Property "end" missing in item ' + data.id);
29867 }
29868 }
29869
29870 Item.call(this, data, conversion, options);
29871}
29872
29873RangeItem.prototype = new Item(null, null, null);
29874RangeItem.prototype.baseClassName = 'vis-item vis-range';
29875/**
29876 * Check whether this item is visible inside given range
29877 *
29878 * @param {vis.Range} range with a timestamp for start and end
29879 * @returns {boolean} True if visible
29880 */
29881
29882RangeItem.prototype.isVisible = function (range) {
29883 // determine visibility
29884 return this.data.start < range.end && this.data.end > range.start;
29885};
29886
29887RangeItem.prototype._createDomElement = function () {
29888 if (!this.dom) {
29889 // create DOM
29890 this.dom = {}; // background box
29891
29892 this.dom.box = document.createElement('div'); // className is updated in redraw()
29893 // frame box (to prevent the item contents from overflowing)
29894
29895 this.dom.frame = document.createElement('div');
29896 this.dom.frame.className = 'vis-item-overflow';
29897 this.dom.box.appendChild(this.dom.frame); // visible frame box (showing the frame that is always visible)
29898
29899 this.dom.visibleFrame = document.createElement('div');
29900 this.dom.visibleFrame.className = 'vis-item-visible-frame';
29901 this.dom.box.appendChild(this.dom.visibleFrame); // contents box
29902
29903 this.dom.content = document.createElement('div');
29904 this.dom.content.className = 'vis-item-content';
29905 this.dom.frame.appendChild(this.dom.content); // attach this item as attribute
29906
29907 this.dom.box['timeline-item'] = this;
29908 this.dirty = true;
29909 }
29910};
29911
29912RangeItem.prototype._appendDomElement = function () {
29913 if (!this.parent) {
29914 throw new Error('Cannot redraw item: no parent attached');
29915 }
29916
29917 if (!this.dom.box.parentNode) {
29918 var foreground = this.parent.dom.foreground;
29919
29920 if (!foreground) {
29921 throw new Error('Cannot redraw item: parent has no foreground container element');
29922 }
29923
29924 foreground.appendChild(this.dom.box);
29925 }
29926
29927 this.displayed = true;
29928};
29929
29930RangeItem.prototype._updateDirtyDomComponents = function () {
29931 // update dirty DOM. An item is marked dirty when:
29932 // - the item is not yet rendered
29933 // - the item's data is changed
29934 // - the item is selected/deselected
29935 if (this.dirty) {
29936 this._updateContents(this.dom.content);
29937
29938 this._updateDataAttributes(this.dom.box);
29939
29940 this._updateStyle(this.dom.box);
29941
29942 var editable = this.editable.updateTime || this.editable.updateGroup; // update class
29943
29944 var className = (this.data.className ? ' ' + this.data.className : '') + (this.selected ? ' vis-selected' : '') + (editable ? ' vis-editable' : ' vis-readonly');
29945 this.dom.box.className = this.baseClassName + className; // turn off max-width to be able to calculate the real width
29946 // this causes an extra browser repaint/reflow, but so be it
29947
29948 this.dom.content.style.maxWidth = 'none';
29949 }
29950};
29951
29952RangeItem.prototype._getDomComponentsSizes = function () {
29953 // determine from css whether this box has overflow
29954 this.overflow = window.getComputedStyle(this.dom.frame).overflow !== 'hidden';
29955 return {
29956 content: {
29957 width: this.dom.content.offsetWidth
29958 },
29959 box: {
29960 height: this.dom.box.offsetHeight
29961 }
29962 };
29963};
29964
29965RangeItem.prototype._updateDomComponentsSizes = function (sizes) {
29966 this.props.content.width = sizes.content.width;
29967 this.height = sizes.box.height;
29968 this.dom.content.style.maxWidth = '';
29969 this.dirty = false;
29970};
29971
29972RangeItem.prototype._repaintDomAdditionals = function () {
29973 this._repaintOnItemUpdateTimeTooltip(this.dom.box);
29974
29975 this._repaintDeleteButton(this.dom.box);
29976
29977 this._repaintDragCenter();
29978
29979 this._repaintDragLeft();
29980
29981 this._repaintDragRight();
29982};
29983/**
29984 * Repaint the item
29985 * @param {boolean} [returnQueue=false] return the queue
29986 * @return {boolean} the redraw queue if returnQueue=true
29987 */
29988
29989
29990RangeItem.prototype.redraw = function (returnQueue) {
29991 var sizes;
29992 var queue = [// create item DOM
29993 this._createDomElement.bind(this), // append DOM to parent DOM
29994 this._appendDomElement.bind(this), // update dirty DOM
29995 this._updateDirtyDomComponents.bind(this), function () {
29996 if (this.dirty) {
29997 sizes = this._getDomComponentsSizes.bind(this)();
29998 }
29999 }.bind(this), function () {
30000 if (this.dirty) {
30001 this._updateDomComponentsSizes.bind(this)(sizes);
30002 }
30003 }.bind(this), // repaint DOM additionals
30004 this._repaintDomAdditionals.bind(this)];
30005
30006 if (returnQueue) {
30007 return queue;
30008 } else {
30009 var result;
30010 queue.forEach(function (fn) {
30011 result = fn();
30012 });
30013 return result;
30014 }
30015};
30016/**
30017 * Show the item in the DOM (when not already visible). The items DOM will
30018 * be created when needed.
30019 * @param {boolean} [returnQueue=false] whether to return a queue of functions to execute instead of just executing them
30020 * @return {boolean} the redraw queue if returnQueue=true
30021 */
30022
30023
30024RangeItem.prototype.show = function (returnQueue) {
30025 if (!this.displayed) {
30026 return this.redraw(returnQueue);
30027 }
30028};
30029/**
30030 * Hide the item from the DOM (when visible)
30031 */
30032
30033
30034RangeItem.prototype.hide = function () {
30035 if (this.displayed) {
30036 var box = this.dom.box;
30037
30038 if (box.parentNode) {
30039 box.parentNode.removeChild(box);
30040 }
30041
30042 this.displayed = false;
30043 }
30044};
30045/**
30046 * Reposition the item horizontally
30047 * @param {boolean} [limitSize=true] If true (default), the width of the range
30048 * item will be limited, as the browser cannot
30049 * display very wide divs. This means though
30050 * that the applied left and width may
30051 * not correspond to the ranges start and end
30052 * @Override
30053 */
30054
30055
30056RangeItem.prototype.repositionX = function (limitSize) {
30057 var parentWidth = this.parent.width;
30058 var start = this.conversion.toScreen(this.data.start);
30059 var end = this.conversion.toScreen(this.data.end);
30060 var align = this.data.align === undefined ? this.options.align : this.data.align;
30061 var contentStartPosition;
30062 var contentWidth; // limit the width of the range, as browsers cannot draw very wide divs
30063 // unless limitSize: false is explicitly set in item data
30064
30065 if (this.data.limitSize !== false && (limitSize === undefined || limitSize === true)) {
30066 if (start < -parentWidth) {
30067 start = -parentWidth;
30068 }
30069
30070 if (end > 2 * parentWidth) {
30071 end = 2 * parentWidth;
30072 }
30073 } // add 0.5 to compensate floating-point values rounding
30074
30075
30076 var boxWidth = Math.max(end - start + 0.5, 1);
30077
30078 if (this.overflow) {
30079 if (this.options.rtl) {
30080 this.right = start;
30081 } else {
30082 this.left = start;
30083 }
30084
30085 this.width = boxWidth + this.props.content.width;
30086 contentWidth = this.props.content.width; // Note: The calculation of width is an optimistic calculation, giving
30087 // a width which will not change when moving the Timeline
30088 // So no re-stacking needed, which is nicer for the eye;
30089 } else {
30090 if (this.options.rtl) {
30091 this.right = start;
30092 } else {
30093 this.left = start;
30094 }
30095
30096 this.width = boxWidth;
30097 contentWidth = Math.min(end - start, this.props.content.width);
30098 }
30099
30100 if (this.options.rtl) {
30101 this.dom.box.style.right = this.right + 'px';
30102 } else {
30103 this.dom.box.style.left = this.left + 'px';
30104 }
30105
30106 this.dom.box.style.width = boxWidth + 'px';
30107
30108 switch (align) {
30109 case 'left':
30110 if (this.options.rtl) {
30111 this.dom.content.style.right = '0';
30112 } else {
30113 this.dom.content.style.left = '0';
30114 }
30115
30116 break;
30117
30118 case 'right':
30119 if (this.options.rtl) {
30120 this.dom.content.style.right = Math.max(boxWidth - contentWidth, 0) + 'px';
30121 } else {
30122 this.dom.content.style.left = Math.max(boxWidth - contentWidth, 0) + 'px';
30123 }
30124
30125 break;
30126
30127 case 'center':
30128 if (this.options.rtl) {
30129 this.dom.content.style.right = Math.max((boxWidth - contentWidth) / 2, 0) + 'px';
30130 } else {
30131 this.dom.content.style.left = Math.max((boxWidth - contentWidth) / 2, 0) + 'px';
30132 }
30133
30134 break;
30135
30136 default:
30137 // 'auto'
30138 // when range exceeds left of the window, position the contents at the left of the visible area
30139 if (this.overflow) {
30140 if (end > 0) {
30141 contentStartPosition = Math.max(-start, 0);
30142 } else {
30143 contentStartPosition = -contentWidth; // ensure it's not visible anymore
30144 }
30145 } else {
30146 if (start < 0) {
30147 contentStartPosition = -start;
30148 } else {
30149 contentStartPosition = 0;
30150 }
30151 }
30152
30153 if (this.options.rtl) {
30154 this.dom.content.style.right = contentStartPosition + 'px';
30155 } else {
30156 this.dom.content.style.left = contentStartPosition + 'px';
30157 this.dom.content.style.width = 'calc(100% - ' + contentStartPosition + 'px)';
30158 }
30159
30160 }
30161};
30162/**
30163 * Reposition the item vertically
30164 * @Override
30165 */
30166
30167
30168RangeItem.prototype.repositionY = function () {
30169 var orientation = this.options.orientation.item;
30170 var box = this.dom.box;
30171
30172 if (orientation == 'top') {
30173 box.style.top = this.top + 'px';
30174 } else {
30175 box.style.top = this.parent.height - this.top - this.height + 'px';
30176 }
30177};
30178/**
30179 * Repaint a drag area on the left side of the range when the range is selected
30180 * @protected
30181 */
30182
30183
30184RangeItem.prototype._repaintDragLeft = function () {
30185 if ((this.selected || this.options.itemsAlwaysDraggable.range) && this.options.editable.updateTime && !this.dom.dragLeft) {
30186 // create and show drag area
30187 var dragLeft = document.createElement('div');
30188 dragLeft.className = 'vis-drag-left';
30189 dragLeft.dragLeftItem = this;
30190 this.dom.box.appendChild(dragLeft);
30191 this.dom.dragLeft = dragLeft;
30192 } else if (!this.selected && !this.options.itemsAlwaysDraggable.range && this.dom.dragLeft) {
30193 // delete drag area
30194 if (this.dom.dragLeft.parentNode) {
30195 this.dom.dragLeft.parentNode.removeChild(this.dom.dragLeft);
30196 }
30197
30198 this.dom.dragLeft = null;
30199 }
30200};
30201/**
30202 * Repaint a drag area on the right side of the range when the range is selected
30203 * @protected
30204 */
30205
30206
30207RangeItem.prototype._repaintDragRight = function () {
30208 if ((this.selected || this.options.itemsAlwaysDraggable.range) && this.options.editable.updateTime && !this.dom.dragRight) {
30209 // create and show drag area
30210 var dragRight = document.createElement('div');
30211 dragRight.className = 'vis-drag-right';
30212 dragRight.dragRightItem = this;
30213 this.dom.box.appendChild(dragRight);
30214 this.dom.dragRight = dragRight;
30215 } else if (!this.selected && !this.options.itemsAlwaysDraggable.range && this.dom.dragRight) {
30216 // delete drag area
30217 if (this.dom.dragRight.parentNode) {
30218 this.dom.dragRight.parentNode.removeChild(this.dom.dragRight);
30219 }
30220
30221 this.dom.dragRight = null;
30222 }
30223};
30224
30225var RangeItem_1 = RangeItem;
30226
30227/**
30228 * @constructor BackgroundItem
30229 * @extends Item
30230 * @param {Object} data Object containing parameters start, end
30231 * content, className.
30232 * @param {{toScreen: function, toTime: function}} conversion
30233 * Conversion functions from time to screen and vice versa
30234 * @param {Object} [options] Configuration options
30235 * // TODO: describe options
30236 * // TODO: implement support for the BackgroundItem just having a start, then being displayed as a sort of an annotation
30237 */
30238
30239function BackgroundItem(data, conversion, options) {
30240 this.props = {
30241 content: {
30242 width: 0
30243 }
30244 };
30245 this.overflow = false; // if contents can overflow (css styling), this flag is set to true
30246 // validate data
30247
30248 if (data) {
30249 if (data.start == undefined) {
30250 throw new Error('Property "start" missing in item ' + data.id);
30251 }
30252
30253 if (data.end == undefined) {
30254 throw new Error('Property "end" missing in item ' + data.id);
30255 }
30256 }
30257
30258 Item.call(this, data, conversion, options);
30259}
30260
30261BackgroundItem.prototype = new Item(null, null, null);
30262BackgroundItem.prototype.baseClassName = 'vis-item vis-background';
30263BackgroundItem.prototype.stack = false;
30264/**
30265 * Check whether this item is visible inside given range
30266 * @param {vis.Range} range with a timestamp for start and end
30267 * @returns {boolean} True if visible
30268 */
30269
30270BackgroundItem.prototype.isVisible = function (range) {
30271 // determine visibility
30272 return this.data.start < range.end && this.data.end > range.start;
30273};
30274
30275BackgroundItem.prototype._createDomElement = function () {
30276 if (!this.dom) {
30277 // create DOM
30278 this.dom = {}; // background box
30279
30280 this.dom.box = document.createElement('div'); // className is updated in redraw()
30281 // frame box (to prevent the item contents from overflowing
30282
30283 this.dom.frame = document.createElement('div');
30284 this.dom.frame.className = 'vis-item-overflow';
30285 this.dom.box.appendChild(this.dom.frame); // contents box
30286
30287 this.dom.content = document.createElement('div');
30288 this.dom.content.className = 'vis-item-content';
30289 this.dom.frame.appendChild(this.dom.content); // Note: we do NOT attach this item as attribute to the DOM,
30290 // such that background items cannot be selected
30291 //this.dom.box['timeline-item'] = this;
30292
30293 this.dirty = true;
30294 }
30295};
30296
30297BackgroundItem.prototype._appendDomElement = function () {
30298 if (!this.parent) {
30299 throw new Error('Cannot redraw item: no parent attached');
30300 }
30301
30302 if (!this.dom.box.parentNode) {
30303 var background = this.parent.dom.background;
30304
30305 if (!background) {
30306 throw new Error('Cannot redraw item: parent has no background container element');
30307 }
30308
30309 background.appendChild(this.dom.box);
30310 }
30311
30312 this.displayed = true;
30313};
30314
30315BackgroundItem.prototype._updateDirtyDomComponents = function () {
30316 // update dirty DOM. An item is marked dirty when:
30317 // - the item is not yet rendered
30318 // - the item's data is changed
30319 // - the item is selected/deselected
30320 if (this.dirty) {
30321 this._updateContents(this.dom.content);
30322
30323 this._updateDataAttributes(this.dom.content);
30324
30325 this._updateStyle(this.dom.box); // update class
30326
30327
30328 var className = (this.data.className ? ' ' + this.data.className : '') + (this.selected ? ' vis-selected' : '');
30329 this.dom.box.className = this.baseClassName + className;
30330 }
30331};
30332
30333BackgroundItem.prototype._getDomComponentsSizes = function () {
30334 // determine from css whether this box has overflow
30335 this.overflow = window.getComputedStyle(this.dom.content).overflow !== 'hidden';
30336 return {
30337 content: {
30338 width: this.dom.content.offsetWidth
30339 }
30340 };
30341};
30342
30343BackgroundItem.prototype._updateDomComponentsSizes = function (sizes) {
30344 // recalculate size
30345 this.props.content.width = sizes.content.width;
30346 this.height = 0; // set height zero, so this item will be ignored when stacking items
30347
30348 this.dirty = false;
30349};
30350
30351BackgroundItem.prototype._repaintDomAdditionals = function () {};
30352/**
30353 * Repaint the item
30354 * @param {boolean} [returnQueue=false] return the queue
30355 * @return {boolean} the redraw result or the redraw queue if returnQueue=true
30356 */
30357
30358
30359BackgroundItem.prototype.redraw = function (returnQueue) {
30360 var sizes;
30361 var queue = [// create item DOM
30362 this._createDomElement.bind(this), // append DOM to parent DOM
30363 this._appendDomElement.bind(this), this._updateDirtyDomComponents.bind(this), function () {
30364 if (this.dirty) {
30365 sizes = this._getDomComponentsSizes.bind(this)();
30366 }
30367 }.bind(this), function () {
30368 if (this.dirty) {
30369 this._updateDomComponentsSizes.bind(this)(sizes);
30370 }
30371 }.bind(this), // repaint DOM additionals
30372 this._repaintDomAdditionals.bind(this)];
30373
30374 if (returnQueue) {
30375 return queue;
30376 } else {
30377 var result;
30378 queue.forEach(function (fn) {
30379 result = fn();
30380 });
30381 return result;
30382 }
30383};
30384/**
30385 * Show the item in the DOM (when not already visible). The items DOM will
30386 * be created when needed.
30387 */
30388
30389
30390BackgroundItem.prototype.show = RangeItem_1.prototype.show;
30391/**
30392 * Hide the item from the DOM (when visible)
30393 * @return {Boolean} changed
30394 */
30395
30396BackgroundItem.prototype.hide = RangeItem_1.prototype.hide;
30397/**
30398 * Reposition the item horizontally
30399 * @Override
30400 */
30401
30402BackgroundItem.prototype.repositionX = RangeItem_1.prototype.repositionX;
30403/**
30404 * Reposition the item vertically
30405 * @Override
30406 */
30407
30408BackgroundItem.prototype.repositionY = function (margin) {
30409 // eslint-disable-line no-unused-vars
30410 var height;
30411 var orientation = this.options.orientation.item; // special positioning for subgroups
30412
30413 if (this.data.subgroup !== undefined) {
30414 // TODO: instead of calculating the top position of the subgroups here for every BackgroundItem, calculate the top of the subgroup once in Itemset
30415 var itemSubgroup = this.data.subgroup;
30416 this.dom.box.style.height = this.parent.subgroups[itemSubgroup].height + 'px';
30417
30418 if (orientation == 'top') {
30419 this.dom.box.style.top = this.parent.top + this.parent.subgroups[itemSubgroup].top + 'px';
30420 } else {
30421 this.dom.box.style.top = this.parent.top + this.parent.height - this.parent.subgroups[itemSubgroup].top - this.parent.subgroups[itemSubgroup].height + 'px';
30422 }
30423
30424 this.dom.box.style.bottom = '';
30425 } // and in the case of no subgroups:
30426 else {
30427 // we want backgrounds with groups to only show in groups.
30428 if (this.parent instanceof BackgroundGroup_1) {
30429 // if the item is not in a group:
30430 height = Math.max(this.parent.height, this.parent.itemSet.body.domProps.center.height, this.parent.itemSet.body.domProps.centerContainer.height);
30431 this.dom.box.style.bottom = orientation == 'bottom' ? '0' : '';
30432 this.dom.box.style.top = orientation == 'top' ? '0' : '';
30433 } else {
30434 height = this.parent.height; // same alignment for items when orientation is top or bottom
30435
30436 this.dom.box.style.top = this.parent.top + 'px';
30437 this.dom.box.style.bottom = '';
30438 }
30439 }
30440
30441 this.dom.box.style.height = height + 'px';
30442};
30443
30444var BackgroundItem_1 = BackgroundItem;
30445
30446/**
30447 * Popup is a class to create a popup window with some text
30448 */
30449
30450var Popup =
30451/*#__PURE__*/
30452function () {
30453 /**
30454 * @param {Element} container The container object.
30455 * @param {string} overflowMethod How the popup should act to overflowing ('flip' or 'cap')
30456 */
30457 function Popup(container, overflowMethod) {
30458 _classCallCheck$1(this, Popup);
30459
30460 this.container = container;
30461 this.overflowMethod = overflowMethod || 'cap';
30462 this.x = 0;
30463 this.y = 0;
30464 this.padding = 5;
30465 this.hidden = false; // create the frame
30466
30467 this.frame = document.createElement('div');
30468 this.frame.className = 'vis-tooltip';
30469 this.container.appendChild(this.frame);
30470 }
30471 /**
30472 * @param {number} x Horizontal position of the popup window
30473 * @param {number} y Vertical position of the popup window
30474 */
30475
30476
30477 _createClass$1(Popup, [{
30478 key: "setPosition",
30479 value: function setPosition(x, y) {
30480 this.x = parseInt(x);
30481 this.y = parseInt(y);
30482 }
30483 /**
30484 * Set the content for the popup window. This can be HTML code or text.
30485 * @param {string | Element} content
30486 */
30487
30488 }, {
30489 key: "setText",
30490 value: function setText(content) {
30491 if (content instanceof Element) {
30492 this.frame.innerHTML = '';
30493 this.frame.appendChild(content);
30494 } else {
30495 this.frame.innerHTML = content; // string containing text or HTML
30496 }
30497 }
30498 /**
30499 * Show the popup window
30500 * @param {boolean} [doShow] Show or hide the window
30501 */
30502
30503 }, {
30504 key: "show",
30505 value: function show(doShow) {
30506 if (doShow === undefined) {
30507 doShow = true;
30508 }
30509
30510 if (doShow === true) {
30511 var height = this.frame.clientHeight;
30512 var width = this.frame.clientWidth;
30513 var maxHeight = this.frame.parentNode.clientHeight;
30514 var maxWidth = this.frame.parentNode.clientWidth;
30515 var left = 0,
30516 top = 0;
30517
30518 if (this.overflowMethod == 'flip') {
30519 var isLeft = false,
30520 isTop = true; // Where around the position it's located
30521
30522 if (this.y - height < this.padding) {
30523 isTop = false;
30524 }
30525
30526 if (this.x + width > maxWidth - this.padding) {
30527 isLeft = true;
30528 }
30529
30530 if (isLeft) {
30531 left = this.x - width;
30532 } else {
30533 left = this.x;
30534 }
30535
30536 if (isTop) {
30537 top = this.y - height;
30538 } else {
30539 top = this.y;
30540 }
30541 } else {
30542 top = this.y - height;
30543
30544 if (top + height + this.padding > maxHeight) {
30545 top = maxHeight - height - this.padding;
30546 }
30547
30548 if (top < this.padding) {
30549 top = this.padding;
30550 }
30551
30552 left = this.x;
30553
30554 if (left + width + this.padding > maxWidth) {
30555 left = maxWidth - width - this.padding;
30556 }
30557
30558 if (left < this.padding) {
30559 left = this.padding;
30560 }
30561 }
30562
30563 this.frame.style.left = left + "px";
30564 this.frame.style.top = top + "px";
30565 this.frame.style.visibility = "visible";
30566 this.hidden = false;
30567 } else {
30568 this.hide();
30569 }
30570 }
30571 /**
30572 * Hide the popup window
30573 */
30574
30575 }, {
30576 key: "hide",
30577 value: function hide() {
30578 this.hidden = true;
30579 this.frame.style.left = "0";
30580 this.frame.style.top = "0";
30581 this.frame.style.visibility = "hidden";
30582 }
30583 /**
30584 * Remove the popup window
30585 */
30586
30587 }, {
30588 key: "destroy",
30589 value: function destroy() {
30590 this.frame.parentNode.removeChild(this.frame); // Remove element from DOM
30591 }
30592 }]);
30593
30594 return Popup;
30595}();
30596
30597var UNGROUPED = '__ungrouped__'; // reserved group id for ungrouped items
30598
30599var BACKGROUND = '__background__'; // reserved group id for background items without group
30600
30601/**
30602 * An ItemSet holds a set of items and ranges which can be displayed in a
30603 * range. The width is determined by the parent of the ItemSet, and the height
30604 * is determined by the size of the items.
30605 * @param {{dom: Object, domProps: Object, emitter: Emitter, range: Range}} body
30606 * @param {Object} [options] See ItemSet.setOptions for the available options.
30607 * @constructor ItemSet
30608 * @extends Component
30609 */
30610
30611function ItemSet(body, options) {
30612 this.body = body;
30613 this.defaultOptions = {
30614 type: null,
30615 // 'box', 'point', 'range', 'background'
30616 orientation: {
30617 item: 'bottom' // item orientation: 'top' or 'bottom'
30618
30619 },
30620 align: 'auto',
30621 // alignment of box items
30622 stack: true,
30623 stackSubgroups: true,
30624 groupOrderSwap: function groupOrderSwap(fromGroup, toGroup, groups) {
30625 // eslint-disable-line no-unused-vars
30626 var targetOrder = toGroup.order;
30627 toGroup.order = fromGroup.order;
30628 fromGroup.order = targetOrder;
30629 },
30630 groupOrder: 'order',
30631 selectable: true,
30632 multiselect: false,
30633 itemsAlwaysDraggable: {
30634 item: false,
30635 range: false
30636 },
30637 editable: {
30638 updateTime: false,
30639 updateGroup: false,
30640 add: false,
30641 remove: false,
30642 overrideItems: false
30643 },
30644 groupEditable: {
30645 order: false,
30646 add: false,
30647 remove: false
30648 },
30649 snap: TimeStep_1.snap,
30650 // Only called when `objectData.target === 'item'.
30651 onDropObjectOnItem: function onDropObjectOnItem(objectData, item, callback) {
30652 callback(item);
30653 },
30654 onAdd: function onAdd(item, callback) {
30655 callback(item);
30656 },
30657 onUpdate: function onUpdate(item, callback) {
30658 callback(item);
30659 },
30660 onMove: function onMove(item, callback) {
30661 callback(item);
30662 },
30663 onRemove: function onRemove(item, callback) {
30664 callback(item);
30665 },
30666 onMoving: function onMoving(item, callback) {
30667 callback(item);
30668 },
30669 onAddGroup: function onAddGroup(item, callback) {
30670 callback(item);
30671 },
30672 onMoveGroup: function onMoveGroup(item, callback) {
30673 callback(item);
30674 },
30675 onRemoveGroup: function onRemoveGroup(item, callback) {
30676 callback(item);
30677 },
30678 margin: {
30679 item: {
30680 horizontal: 10,
30681 vertical: 10
30682 },
30683 axis: 20
30684 },
30685 showTooltips: true,
30686 tooltip: {
30687 followMouse: false,
30688 overflowMethod: 'flip'
30689 },
30690 tooltipOnItemUpdateTime: false
30691 }; // options is shared by this ItemSet and all its items
30692
30693 this.options = util.extend({}, this.defaultOptions);
30694 this.options.rtl = options.rtl;
30695 this.options.onTimeout = options.onTimeout; // options for getting items from the DataSet with the correct type
30696
30697 this.itemOptions = {
30698 type: {
30699 start: 'Date',
30700 end: 'Date'
30701 }
30702 };
30703 this.conversion = {
30704 toScreen: body.util.toScreen,
30705 toTime: body.util.toTime
30706 };
30707 this.dom = {};
30708 this.props = {};
30709 this.hammer = null;
30710 var me = this;
30711 this.itemsData = null; // DataSet
30712
30713 this.groupsData = null; // DataSet
30714
30715 this.itemsSettingTime = null;
30716 this.initialItemSetDrawn = false;
30717 this.userContinueNotBail = null; // listeners for the DataSet of the items
30718
30719 this.itemListeners = {
30720 'add': function add(event, params, senderId) {
30721 // eslint-disable-line no-unused-vars
30722 me._onAdd(params.items);
30723 },
30724 'update': function update(event, params, senderId) {
30725 // eslint-disable-line no-unused-vars
30726 me._onUpdate(params.items);
30727 },
30728 'remove': function remove(event, params, senderId) {
30729 // eslint-disable-line no-unused-vars
30730 me._onRemove(params.items);
30731 }
30732 }; // listeners for the DataSet of the groups
30733
30734 this.groupListeners = {
30735 'add': function add(event, params, senderId) {
30736 // eslint-disable-line no-unused-vars
30737 me._onAddGroups(params.items);
30738
30739 if (me.groupsData && me.groupsData.length > 0) {
30740 var groupsData = me.groupsData.getDataSet();
30741 groupsData.get().forEach(function (groupData) {
30742 if (groupData.nestedGroups) {
30743 if (groupData.showNested != false) {
30744 groupData.showNested = true;
30745 }
30746
30747 var updatedGroups = [];
30748 groupData.nestedGroups.forEach(function (nestedGroupId) {
30749 var updatedNestedGroup = groupsData.get(nestedGroupId);
30750
30751 if (!updatedNestedGroup) {
30752 return;
30753 }
30754
30755 updatedNestedGroup.nestedInGroup = groupData.id;
30756
30757 if (groupData.showNested == false) {
30758 updatedNestedGroup.visible = false;
30759 }
30760
30761 updatedGroups = updatedGroups.concat(updatedNestedGroup);
30762 });
30763 groupsData.update(updatedGroups, senderId);
30764 }
30765 });
30766 }
30767 },
30768 'update': function update(event, params, senderId) {
30769 // eslint-disable-line no-unused-vars
30770 me._onUpdateGroups(params.items);
30771 },
30772 'remove': function remove(event, params, senderId) {
30773 // eslint-disable-line no-unused-vars
30774 me._onRemoveGroups(params.items);
30775 }
30776 };
30777 this.items = {}; // object with an Item for every data item
30778
30779 this.groups = {}; // Group object for every group
30780
30781 this.groupIds = [];
30782 this.selection = []; // list with the ids of all selected nodes
30783
30784 this.popup = null;
30785 this.popupTimer = null;
30786 this.touchParams = {}; // stores properties while dragging
30787
30788 this.groupTouchParams = {}; // create the HTML DOM
30789
30790 this._create();
30791
30792 this.setOptions(options);
30793}
30794
30795ItemSet.prototype = new Component_1(); // available item types will be registered here
30796
30797ItemSet.types = {
30798 background: BackgroundItem_1,
30799 box: BoxItem_1,
30800 range: RangeItem_1,
30801 point: PointItem_1
30802};
30803/**
30804 * Create the HTML DOM for the ItemSet
30805 */
30806
30807ItemSet.prototype._create = function () {
30808 var frame = document.createElement('div');
30809 frame.className = 'vis-itemset';
30810 frame['timeline-itemset'] = this;
30811 this.dom.frame = frame; // create background panel
30812
30813 var background = document.createElement('div');
30814 background.className = 'vis-background';
30815 frame.appendChild(background);
30816 this.dom.background = background; // create foreground panel
30817
30818 var foreground = document.createElement('div');
30819 foreground.className = 'vis-foreground';
30820 frame.appendChild(foreground);
30821 this.dom.foreground = foreground; // create axis panel
30822
30823 var axis = document.createElement('div');
30824 axis.className = 'vis-axis';
30825 this.dom.axis = axis; // create labelset
30826
30827 var labelSet = document.createElement('div');
30828 labelSet.className = 'vis-labelset';
30829 this.dom.labelSet = labelSet; // create ungrouped Group
30830
30831 this._updateUngrouped(); // create background Group
30832
30833
30834 var backgroundGroup = new BackgroundGroup_1(BACKGROUND, null, this);
30835 backgroundGroup.show();
30836 this.groups[BACKGROUND] = backgroundGroup; // attach event listeners
30837 // Note: we bind to the centerContainer for the case where the height
30838 // of the center container is larger than of the ItemSet, so we
30839 // can click in the empty area to create a new item or deselect an item.
30840
30841 this.hammer = new hammer$1(this.body.dom.centerContainer); // drag items when selected
30842
30843 this.hammer.on('hammer.input', function (event) {
30844 if (event.isFirst) {
30845 this._onTouch(event);
30846 }
30847 }.bind(this));
30848 this.hammer.on('panstart', this._onDragStart.bind(this));
30849 this.hammer.on('panmove', this._onDrag.bind(this));
30850 this.hammer.on('panend', this._onDragEnd.bind(this));
30851 this.hammer.get('pan').set({
30852 threshold: 5,
30853 direction: hammer$1.DIRECTION_HORIZONTAL
30854 }); // single select (or unselect) when tapping an item
30855
30856 this.hammer.on('tap', this._onSelectItem.bind(this)); // multi select when holding mouse/touch, or on ctrl+click
30857
30858 this.hammer.on('press', this._onMultiSelectItem.bind(this)); // add item on doubletap
30859
30860 this.hammer.on('doubletap', this._onAddItem.bind(this));
30861
30862 if (this.options.rtl) {
30863 this.groupHammer = new hammer$1(this.body.dom.rightContainer);
30864 } else {
30865 this.groupHammer = new hammer$1(this.body.dom.leftContainer);
30866 }
30867
30868 this.groupHammer.on('tap', this._onGroupClick.bind(this));
30869 this.groupHammer.on('panstart', this._onGroupDragStart.bind(this));
30870 this.groupHammer.on('panmove', this._onGroupDrag.bind(this));
30871 this.groupHammer.on('panend', this._onGroupDragEnd.bind(this));
30872 this.groupHammer.get('pan').set({
30873 threshold: 5,
30874 direction: hammer$1.DIRECTION_VERTICAL
30875 });
30876 this.body.dom.centerContainer.addEventListener('mouseover', this._onMouseOver.bind(this));
30877 this.body.dom.centerContainer.addEventListener('mouseout', this._onMouseOut.bind(this));
30878 this.body.dom.centerContainer.addEventListener('mousemove', this._onMouseMove.bind(this)); // right-click on timeline
30879
30880 this.body.dom.centerContainer.addEventListener('contextmenu', this._onDragEnd.bind(this));
30881 this.body.dom.centerContainer.addEventListener('mousewheel', this._onMouseWheel.bind(this)); // attach to the DOM
30882
30883 this.show();
30884};
30885/**
30886 * Set options for the ItemSet. Existing options will be extended/overwritten.
30887 * @param {Object} [options] The following options are available:
30888 * {string} type
30889 * Default type for the items. Choose from 'box'
30890 * (default), 'point', 'range', or 'background'.
30891 * The default style can be overwritten by
30892 * individual items.
30893 * {string} align
30894 * Alignment for the items, only applicable for
30895 * BoxItem. Choose 'center' (default), 'left', or
30896 * 'right'.
30897 * {string} orientation.item
30898 * Orientation of the item set. Choose 'top' or
30899 * 'bottom' (default).
30900 * {Function} groupOrder
30901 * A sorting function for ordering groups
30902 * {boolean} stack
30903 * If true (default), items will be stacked on
30904 * top of each other.
30905 * {number} margin.axis
30906 * Margin between the axis and the items in pixels.
30907 * Default is 20.
30908 * {number} margin.item.horizontal
30909 * Horizontal margin between items in pixels.
30910 * Default is 10.
30911 * {number} margin.item.vertical
30912 * Vertical Margin between items in pixels.
30913 * Default is 10.
30914 * {number} margin.item
30915 * Margin between items in pixels in both horizontal
30916 * and vertical direction. Default is 10.
30917 * {number} margin
30918 * Set margin for both axis and items in pixels.
30919 * {boolean} selectable
30920 * If true (default), items can be selected.
30921 * {boolean} multiselect
30922 * If true, multiple items can be selected.
30923 * False by default.
30924 * {boolean} editable
30925 * Set all editable options to true or false
30926 * {boolean} editable.updateTime
30927 * Allow dragging an item to an other moment in time
30928 * {boolean} editable.updateGroup
30929 * Allow dragging an item to an other group
30930 * {boolean} editable.add
30931 * Allow creating new items on double tap
30932 * {boolean} editable.remove
30933 * Allow removing items by clicking the delete button
30934 * top right of a selected item.
30935 * {Function(item: Item, callback: Function)} onAdd
30936 * Callback function triggered when an item is about to be added:
30937 * when the user double taps an empty space in the Timeline.
30938 * {Function(item: Item, callback: Function)} onUpdate
30939 * Callback function fired when an item is about to be updated.
30940 * This function typically has to show a dialog where the user
30941 * change the item. If not implemented, nothing happens.
30942 * {Function(item: Item, callback: Function)} onMove
30943 * Fired when an item has been moved. If not implemented,
30944 * the move action will be accepted.
30945 * {Function(item: Item, callback: Function)} onRemove
30946 * Fired when an item is about to be deleted.
30947 * If not implemented, the item will be always removed.
30948 */
30949
30950
30951ItemSet.prototype.setOptions = function (options) {
30952 if (options) {
30953 // copy all options that we know
30954 var fields = ['type', 'rtl', 'align', 'order', 'stack', 'stackSubgroups', 'selectable', 'multiselect', 'multiselectPerGroup', 'groupOrder', 'dataAttributes', 'template', 'groupTemplate', 'visibleFrameTemplate', 'hide', 'snap', 'groupOrderSwap', 'showTooltips', 'tooltip', 'tooltipOnItemUpdateTime', 'groupHeightMode', 'onTimeout', 'groupLabelDirection'];
30955 util.selectiveExtend(fields, this.options, options);
30956
30957 if ('itemsAlwaysDraggable' in options) {
30958 if (typeof options.itemsAlwaysDraggable === 'boolean') {
30959 this.options.itemsAlwaysDraggable.item = options.itemsAlwaysDraggable;
30960 this.options.itemsAlwaysDraggable.range = false;
30961 } else if (_typeof$1(options.itemsAlwaysDraggable) === 'object') {
30962 util.selectiveExtend(['item', 'range'], this.options.itemsAlwaysDraggable, options.itemsAlwaysDraggable); // only allow range always draggable when item is always draggable as well
30963
30964 if (!this.options.itemsAlwaysDraggable.item) {
30965 this.options.itemsAlwaysDraggable.range = false;
30966 }
30967 }
30968 }
30969
30970 if ('orientation' in options) {
30971 if (typeof options.orientation === 'string') {
30972 this.options.orientation.item = options.orientation === 'top' ? 'top' : 'bottom';
30973 } else if (_typeof$1(options.orientation) === 'object' && 'item' in options.orientation) {
30974 this.options.orientation.item = options.orientation.item;
30975 }
30976 }
30977
30978 if ('margin' in options) {
30979 if (typeof options.margin === 'number') {
30980 this.options.margin.axis = options.margin;
30981 this.options.margin.item.horizontal = options.margin;
30982 this.options.margin.item.vertical = options.margin;
30983 } else if (_typeof$1(options.margin) === 'object') {
30984 util.selectiveExtend(['axis'], this.options.margin, options.margin);
30985
30986 if ('item' in options.margin) {
30987 if (typeof options.margin.item === 'number') {
30988 this.options.margin.item.horizontal = options.margin.item;
30989 this.options.margin.item.vertical = options.margin.item;
30990 } else if (_typeof$1(options.margin.item) === 'object') {
30991 util.selectiveExtend(['horizontal', 'vertical'], this.options.margin.item, options.margin.item);
30992 }
30993 }
30994 }
30995 }
30996
30997 if ('editable' in options) {
30998 if (typeof options.editable === 'boolean') {
30999 this.options.editable.updateTime = options.editable;
31000 this.options.editable.updateGroup = options.editable;
31001 this.options.editable.add = options.editable;
31002 this.options.editable.remove = options.editable;
31003 this.options.editable.overrideItems = false;
31004 } else if (_typeof$1(options.editable) === 'object') {
31005 util.selectiveExtend(['updateTime', 'updateGroup', 'add', 'remove', 'overrideItems'], this.options.editable, options.editable);
31006 }
31007 }
31008
31009 if ('groupEditable' in options) {
31010 if (typeof options.groupEditable === 'boolean') {
31011 this.options.groupEditable.order = options.groupEditable;
31012 this.options.groupEditable.add = options.groupEditable;
31013 this.options.groupEditable.remove = options.groupEditable;
31014 } else if (_typeof$1(options.groupEditable) === 'object') {
31015 util.selectiveExtend(['order', 'add', 'remove'], this.options.groupEditable, options.groupEditable);
31016 }
31017 } // callback functions
31018
31019
31020 var addCallback = function (name) {
31021 var fn = options[name];
31022
31023 if (fn) {
31024 if (!(fn instanceof Function)) {
31025 throw new Error('option ' + name + ' must be a function ' + name + '(item, callback)');
31026 }
31027
31028 this.options[name] = fn;
31029 }
31030 }.bind(this);
31031
31032 ['onDropObjectOnItem', 'onAdd', 'onUpdate', 'onRemove', 'onMove', 'onMoving', 'onAddGroup', 'onMoveGroup', 'onRemoveGroup'].forEach(addCallback); // force the itemSet to refresh: options like orientation and margins may be changed
31033
31034 this.markDirty();
31035 }
31036};
31037/**
31038 * Mark the ItemSet dirty so it will refresh everything with next redraw.
31039 * Optionally, all items can be marked as dirty and be refreshed.
31040 * @param {{refreshItems: boolean}} [options]
31041 */
31042
31043
31044ItemSet.prototype.markDirty = function (options) {
31045 this.groupIds = [];
31046
31047 if (options && options.refreshItems) {
31048 util.forEach(this.items, function (item) {
31049 item.dirty = true;
31050 if (item.displayed) item.redraw();
31051 });
31052 }
31053};
31054/**
31055 * Destroy the ItemSet
31056 */
31057
31058
31059ItemSet.prototype.destroy = function () {
31060 this.clearPopupTimer();
31061 this.hide();
31062 this.setItems(null);
31063 this.setGroups(null);
31064 this.hammer && this.hammer.destroy();
31065 this.groupHammer && this.groupHammer.destroy();
31066 this.hammer = null;
31067 this.body = null;
31068 this.conversion = null;
31069};
31070/**
31071 * Hide the component from the DOM
31072 */
31073
31074
31075ItemSet.prototype.hide = function () {
31076 // remove the frame containing the items
31077 if (this.dom.frame.parentNode) {
31078 this.dom.frame.parentNode.removeChild(this.dom.frame);
31079 } // remove the axis with dots
31080
31081
31082 if (this.dom.axis.parentNode) {
31083 this.dom.axis.parentNode.removeChild(this.dom.axis);
31084 } // remove the labelset containing all group labels
31085
31086
31087 if (this.dom.labelSet.parentNode) {
31088 this.dom.labelSet.parentNode.removeChild(this.dom.labelSet);
31089 }
31090};
31091/**
31092 * Show the component in the DOM (when not already visible).
31093 */
31094
31095
31096ItemSet.prototype.show = function () {
31097 // show frame containing the items
31098 if (!this.dom.frame.parentNode) {
31099 this.body.dom.center.appendChild(this.dom.frame);
31100 } // show axis with dots
31101
31102
31103 if (!this.dom.axis.parentNode) {
31104 this.body.dom.backgroundVertical.appendChild(this.dom.axis);
31105 } // show labelset containing labels
31106
31107
31108 if (!this.dom.labelSet.parentNode) {
31109 if (this.options.rtl) {
31110 this.body.dom.right.appendChild(this.dom.labelSet);
31111 } else {
31112 this.body.dom.left.appendChild(this.dom.labelSet);
31113 }
31114 }
31115};
31116/**
31117 * Activates the popup timer to show the given popup after a fixed time.
31118 * @param {any} popup
31119 */
31120
31121
31122ItemSet.prototype.setPopupTimer = function (popup) {
31123 this.clearPopupTimer();
31124
31125 if (popup) {
31126 this.popupTimer = setTimeout(function () {
31127 popup.show();
31128 }, 500);
31129 }
31130};
31131/**
31132 * Clears the popup timer for the tooltip.
31133 */
31134
31135
31136ItemSet.prototype.clearPopupTimer = function () {
31137 if (this.popupTimer != null) {
31138 clearTimeout(this.popupTimer);
31139 this.popupTimer = null;
31140 }
31141};
31142/**
31143 * Set selected items by their id. Replaces the current selection
31144 * Unknown id's are silently ignored.
31145 * @param {string[] | string} [ids] An array with zero or more id's of the items to be
31146 * selected, or a single item id. If ids is undefined
31147 * or an empty array, all items will be unselected.
31148 */
31149
31150
31151ItemSet.prototype.setSelection = function (ids) {
31152 var i, ii, id, item;
31153 if (ids == undefined) ids = [];
31154 if (!Array.isArray(ids)) ids = [ids]; // unselect currently selected items
31155
31156 for (i = 0, ii = this.selection.length; i < ii; i++) {
31157 id = this.selection[i];
31158 item = this.items[id];
31159 if (item) item.unselect();
31160 } // select items
31161
31162
31163 this.selection = [];
31164
31165 for (i = 0, ii = ids.length; i < ii; i++) {
31166 id = ids[i];
31167 item = this.items[id];
31168
31169 if (item) {
31170 this.selection.push(id);
31171 item.select();
31172 }
31173 }
31174};
31175/**
31176 * Get the selected items by their id
31177 * @return {Array} ids The ids of the selected items
31178 */
31179
31180
31181ItemSet.prototype.getSelection = function () {
31182 return this.selection.concat([]);
31183};
31184/**
31185 * Get the id's of the currently visible items.
31186 * @returns {Array} The ids of the visible items
31187 */
31188
31189
31190ItemSet.prototype.getVisibleItems = function () {
31191 var range = this.body.range.getRange();
31192 var right, left;
31193
31194 if (this.options.rtl) {
31195 right = this.body.util.toScreen(range.start);
31196 left = this.body.util.toScreen(range.end);
31197 } else {
31198 left = this.body.util.toScreen(range.start);
31199 right = this.body.util.toScreen(range.end);
31200 }
31201
31202 var ids = [];
31203
31204 for (var groupId in this.groups) {
31205 if (this.groups.hasOwnProperty(groupId)) {
31206 var group = this.groups[groupId];
31207 var rawVisibleItems = group.isVisible ? group.visibleItems : []; // filter the "raw" set with visibleItems into a set which is really
31208 // visible by pixels
31209
31210 for (var i = 0; i < rawVisibleItems.length; i++) {
31211 var item = rawVisibleItems[i]; // TODO: also check whether visible vertically
31212
31213 if (this.options.rtl) {
31214 if (item.right < left && item.right + item.width > right) {
31215 ids.push(item.id);
31216 }
31217 } else {
31218 if (item.left < right && item.left + item.width > left) {
31219 ids.push(item.id);
31220 }
31221 }
31222 }
31223 }
31224 }
31225
31226 return ids;
31227};
31228/**
31229 * Get the id's of the currently visible groups.
31230 * @returns {Array} The ids of the visible groups
31231 */
31232
31233
31234ItemSet.prototype.getVisibleGroups = function () {
31235 var ids = [];
31236
31237 for (var groupId in this.groups) {
31238 if (this.groups.hasOwnProperty(groupId)) {
31239 var group = this.groups[groupId];
31240
31241 if (group.isVisible) {
31242 ids.push(groupId);
31243 }
31244 }
31245 }
31246
31247 return ids;
31248};
31249/**
31250 * Deselect a selected item
31251 * @param {string | number} id
31252 * @private
31253 */
31254
31255
31256ItemSet.prototype._deselect = function (id) {
31257 var selection = this.selection;
31258
31259 for (var i = 0, ii = selection.length; i < ii; i++) {
31260 if (selection[i] == id) {
31261 // non-strict comparison!
31262 selection.splice(i, 1);
31263 break;
31264 }
31265 }
31266};
31267/**
31268 * Repaint the component
31269 * @return {boolean} Returns true if the component is resized
31270 */
31271
31272
31273ItemSet.prototype.redraw = function () {
31274 var margin = this.options.margin,
31275 range = this.body.range,
31276 asSize = util.option.asSize,
31277 options = this.options,
31278 orientation = options.orientation.item,
31279 resized = false,
31280 frame = this.dom.frame; // recalculate absolute position (before redrawing groups)
31281
31282 this.props.top = this.body.domProps.top.height + this.body.domProps.border.top;
31283
31284 if (this.options.rtl) {
31285 this.props.right = this.body.domProps.right.width + this.body.domProps.border.right;
31286 } else {
31287 this.props.left = this.body.domProps.left.width + this.body.domProps.border.left;
31288 } // update class name
31289
31290
31291 frame.className = 'vis-itemset'; // reorder the groups (if needed)
31292
31293 resized = this._orderGroups() || resized; // check whether zoomed (in that case we need to re-stack everything)
31294 // TODO: would be nicer to get this as a trigger from Range
31295
31296 var visibleInterval = range.end - range.start;
31297 var zoomed = visibleInterval != this.lastVisibleInterval || this.props.width != this.props.lastWidth;
31298 var scrolled = range.start != this.lastRangeStart;
31299 var changedStackOption = options.stack != this.lastStack;
31300 var changedStackSubgroupsOption = options.stackSubgroups != this.lastStackSubgroups;
31301 var forceRestack = zoomed || scrolled || changedStackOption || changedStackSubgroupsOption;
31302 this.lastVisibleInterval = visibleInterval;
31303 this.lastRangeStart = range.start;
31304 this.lastStack = options.stack;
31305 this.lastStackSubgroups = options.stackSubgroups;
31306 this.props.lastWidth = this.props.width;
31307
31308 var firstGroup = this._firstGroup();
31309
31310 var firstMargin = {
31311 item: margin.item,
31312 axis: margin.axis
31313 };
31314 var nonFirstMargin = {
31315 item: margin.item,
31316 axis: margin.item.vertical / 2
31317 };
31318 var height = 0;
31319 var minHeight = margin.axis + margin.item.vertical; // redraw the background group
31320
31321 this.groups[BACKGROUND].redraw(range, nonFirstMargin, forceRestack);
31322 var redrawQueue = {};
31323 var redrawQueueLength = 0; // collect redraw functions
31324
31325 util.forEach(this.groups, function (group, key) {
31326 if (key === BACKGROUND) return;
31327 var groupMargin = group == firstGroup ? firstMargin : nonFirstMargin;
31328 var returnQueue = true;
31329 redrawQueue[key] = group.redraw(range, groupMargin, forceRestack, returnQueue);
31330 redrawQueueLength = redrawQueue[key].length;
31331 });
31332 var needRedraw = redrawQueueLength > 0;
31333
31334 if (needRedraw) {
31335 var redrawResults = {};
31336
31337 for (var i = 0; i < redrawQueueLength; i++) {
31338 util.forEach(redrawQueue, function (fns, key) {
31339 redrawResults[key] = fns[i]();
31340 });
31341 } // redraw all regular groups
31342
31343
31344 util.forEach(this.groups, function (group, key) {
31345 if (key === BACKGROUND) return;
31346 var groupResized = redrawResults[key];
31347 resized = groupResized || resized;
31348 height += group.height;
31349 });
31350 height = Math.max(height, minHeight);
31351 }
31352
31353 height = Math.max(height, minHeight); // update frame height
31354
31355 frame.style.height = asSize(height); // calculate actual size
31356
31357 this.props.width = frame.offsetWidth;
31358 this.props.height = height; // reposition axis
31359
31360 this.dom.axis.style.top = asSize(orientation == 'top' ? this.body.domProps.top.height + this.body.domProps.border.top : this.body.domProps.top.height + this.body.domProps.centerContainer.height);
31361
31362 if (this.options.rtl) {
31363 this.dom.axis.style.right = '0';
31364 } else {
31365 this.dom.axis.style.left = '0';
31366 }
31367
31368 this.initialItemSetDrawn = true; // check if this component is resized
31369
31370 resized = this._isResized() || resized;
31371 return resized;
31372};
31373/**
31374 * Get the first group, aligned with the axis
31375 * @return {Group | null} firstGroup
31376 * @private
31377 */
31378
31379
31380ItemSet.prototype._firstGroup = function () {
31381 var firstGroupIndex = this.options.orientation.item == 'top' ? 0 : this.groupIds.length - 1;
31382 var firstGroupId = this.groupIds[firstGroupIndex];
31383 var firstGroup = this.groups[firstGroupId] || this.groups[UNGROUPED];
31384 return firstGroup || null;
31385};
31386/**
31387 * Create or delete the group holding all ungrouped items. This group is used when
31388 * there are no groups specified.
31389 * @protected
31390 */
31391
31392
31393ItemSet.prototype._updateUngrouped = function () {
31394 var ungrouped = this.groups[UNGROUPED];
31395 var item, itemId;
31396
31397 if (this.groupsData) {
31398 // remove the group holding all ungrouped items
31399 if (ungrouped) {
31400 ungrouped.hide();
31401 delete this.groups[UNGROUPED];
31402
31403 for (itemId in this.items) {
31404 if (this.items.hasOwnProperty(itemId)) {
31405 item = this.items[itemId];
31406 item.parent && item.parent.remove(item);
31407
31408 var groupId = this._getGroupId(item.data);
31409
31410 var group = this.groups[groupId];
31411 group && group.add(item) || item.hide();
31412 }
31413 }
31414 }
31415 } else {
31416 // create a group holding all (unfiltered) items
31417 if (!ungrouped) {
31418 var id = null;
31419 var data = null;
31420 ungrouped = new Group_1(id, data, this);
31421 this.groups[UNGROUPED] = ungrouped;
31422
31423 for (itemId in this.items) {
31424 if (this.items.hasOwnProperty(itemId)) {
31425 item = this.items[itemId];
31426 ungrouped.add(item);
31427 }
31428 }
31429
31430 ungrouped.show();
31431 }
31432 }
31433};
31434/**
31435 * Get the element for the labelset
31436 * @return {HTMLElement} labelSet
31437 */
31438
31439
31440ItemSet.prototype.getLabelSet = function () {
31441 return this.dom.labelSet;
31442};
31443/**
31444 * Set items
31445 * @param {vis.DataSet | null} items
31446 */
31447
31448
31449ItemSet.prototype.setItems = function (items) {
31450 this.itemsSettingTime = new Date();
31451 var me = this,
31452 ids,
31453 oldItemsData = this.itemsData; // replace the dataset
31454
31455 if (!items) {
31456 this.itemsData = null;
31457 } else if (items instanceof DataSet || items instanceof DataView) {
31458 this.itemsData = items;
31459 } else {
31460 throw new TypeError('Data must be an instance of DataSet or DataView');
31461 }
31462
31463 if (oldItemsData) {
31464 // unsubscribe from old dataset
31465 util.forEach(this.itemListeners, function (callback, event) {
31466 oldItemsData.off(event, callback);
31467 }); // remove all drawn items
31468
31469 ids = oldItemsData.getIds();
31470
31471 this._onRemove(ids);
31472 }
31473
31474 if (this.itemsData) {
31475 // subscribe to new dataset
31476 var id = this.id;
31477 util.forEach(this.itemListeners, function (callback, event) {
31478 me.itemsData.on(event, callback, id);
31479 }); // add all new items
31480
31481 ids = this.itemsData.getIds();
31482
31483 this._onAdd(ids); // update the group holding all ungrouped items
31484
31485
31486 this._updateUngrouped();
31487 }
31488
31489 this.body.emitter.emit('_change', {
31490 queue: true
31491 });
31492};
31493/**
31494 * Get the current items
31495 * @returns {vis.DataSet | null}
31496 */
31497
31498
31499ItemSet.prototype.getItems = function () {
31500 return this.itemsData;
31501};
31502/**
31503 * Set groups
31504 * @param {vis.DataSet} groups
31505 */
31506
31507
31508ItemSet.prototype.setGroups = function (groups) {
31509 var me = this,
31510 ids; // unsubscribe from current dataset
31511
31512 if (this.groupsData) {
31513 util.forEach(this.groupListeners, function (callback, event) {
31514 me.groupsData.off(event, callback);
31515 }); // remove all drawn groups
31516
31517 ids = this.groupsData.getIds();
31518 this.groupsData = null;
31519
31520 this._onRemoveGroups(ids); // note: this will cause a redraw
31521
31522 } // replace the dataset
31523
31524
31525 if (!groups) {
31526 this.groupsData = null;
31527 } else if (groups instanceof DataSet || groups instanceof DataView) {
31528 this.groupsData = groups;
31529 } else {
31530 throw new TypeError('Data must be an instance of DataSet or DataView');
31531 }
31532
31533 if (this.groupsData) {
31534 // go over all groups nesting
31535 var groupsData = this.groupsData;
31536
31537 if (this.groupsData instanceof DataView) {
31538 groupsData = this.groupsData.getDataSet();
31539 }
31540
31541 groupsData.get().forEach(function (group) {
31542 if (group.nestedGroups) {
31543 group.nestedGroups.forEach(function (nestedGroupId) {
31544 var updatedNestedGroup = groupsData.get(nestedGroupId);
31545 updatedNestedGroup.nestedInGroup = group.id;
31546
31547 if (group.showNested == false) {
31548 updatedNestedGroup.visible = false;
31549 }
31550
31551 groupsData.update(updatedNestedGroup);
31552 });
31553 }
31554 }); // subscribe to new dataset
31555
31556 var id = this.id;
31557 util.forEach(this.groupListeners, function (callback, event) {
31558 me.groupsData.on(event, callback, id);
31559 }); // draw all ms
31560
31561 ids = this.groupsData.getIds();
31562
31563 this._onAddGroups(ids);
31564 } // update the group holding all ungrouped items
31565
31566
31567 this._updateUngrouped(); // update the order of all items in each group
31568
31569
31570 this._order();
31571
31572 this.body.emitter.emit('_change', {
31573 queue: true
31574 });
31575};
31576/**
31577 * Get the current groups
31578 * @returns {vis.DataSet | null} groups
31579 */
31580
31581
31582ItemSet.prototype.getGroups = function () {
31583 return this.groupsData;
31584};
31585/**
31586 * Remove an item by its id
31587 * @param {string | number} id
31588 */
31589
31590
31591ItemSet.prototype.removeItem = function (id) {
31592 var item = this.itemsData.get(id),
31593 dataset = this.itemsData.getDataSet();
31594
31595 if (item) {
31596 // confirm deletion
31597 this.options.onRemove(item, function (item) {
31598 if (item) {
31599 // remove by id here, it is possible that an item has no id defined
31600 // itself, so better not delete by the item itself
31601 dataset.remove(id);
31602 }
31603 });
31604 }
31605};
31606/**
31607 * Get the time of an item based on it's data and options.type
31608 * @param {Object} itemData
31609 * @returns {string} Returns the type
31610 * @private
31611 */
31612
31613
31614ItemSet.prototype._getType = function (itemData) {
31615 return itemData.type || this.options.type || (itemData.end ? 'range' : 'box');
31616};
31617/**
31618 * Get the group id for an item
31619 * @param {Object} itemData
31620 * @returns {string} Returns the groupId
31621 * @private
31622 */
31623
31624
31625ItemSet.prototype._getGroupId = function (itemData) {
31626 var type = this._getType(itemData);
31627
31628 if (type == 'background' && itemData.group == undefined) {
31629 return BACKGROUND;
31630 } else {
31631 return this.groupsData ? itemData.group : UNGROUPED;
31632 }
31633};
31634/**
31635 * Handle updated items
31636 * @param {number[]} ids
31637 * @protected
31638 */
31639
31640
31641ItemSet.prototype._onUpdate = function (ids) {
31642 var me = this;
31643 ids.forEach(function (id) {
31644 var itemData = me.itemsData.get(id, me.itemOptions);
31645 var item = me.items[id];
31646 var type = itemData ? me._getType(itemData) : null;
31647 var constructor = ItemSet.types[type];
31648 var selected;
31649
31650 if (item) {
31651 // update item
31652 if (!constructor || !(item instanceof constructor)) {
31653 // item type has changed, delete the item and recreate it
31654 selected = item.selected; // preserve selection of this item
31655
31656 me._removeItem(item);
31657
31658 item = null;
31659 } else {
31660 me._updateItem(item, itemData);
31661 }
31662 }
31663
31664 if (!item && itemData) {
31665 // create item
31666 if (constructor) {
31667 item = new constructor(itemData, me.conversion, me.options);
31668 item.id = id; // TODO: not so nice setting id afterwards
31669
31670 me._addItem(item);
31671
31672 if (selected) {
31673 this.selection.push(id);
31674 item.select();
31675 }
31676 } else if (type == 'rangeoverflow') {
31677 // TODO: deprecated since version 2.1.0 (or 3.0.0?). cleanup some day
31678 throw new TypeError('Item type "rangeoverflow" is deprecated. Use css styling instead: ' + '.vis-item.vis-range .vis-item-content {overflow: visible;}');
31679 } else {
31680 throw new TypeError('Unknown item type "' + type + '"');
31681 }
31682 }
31683 }.bind(this));
31684
31685 this._order();
31686
31687 this.body.emitter.emit('_change', {
31688 queue: true
31689 });
31690};
31691/**
31692 * Handle added items
31693 * @param {number[]} ids
31694 * @protected
31695 */
31696
31697
31698ItemSet.prototype._onAdd = ItemSet.prototype._onUpdate;
31699/**
31700 * Handle removed items
31701 * @param {number[]} ids
31702 * @protected
31703 */
31704
31705ItemSet.prototype._onRemove = function (ids) {
31706 var count = 0;
31707 var me = this;
31708 ids.forEach(function (id) {
31709 var item = me.items[id];
31710
31711 if (item) {
31712 count++;
31713
31714 me._removeItem(item);
31715 }
31716 });
31717
31718 if (count) {
31719 // update order
31720 this._order();
31721
31722 this.body.emitter.emit('_change', {
31723 queue: true
31724 });
31725 }
31726};
31727/**
31728 * Update the order of item in all groups
31729 * @private
31730 */
31731
31732
31733ItemSet.prototype._order = function () {
31734 // reorder the items in all groups
31735 // TODO: optimization: only reorder groups affected by the changed items
31736 util.forEach(this.groups, function (group) {
31737 group.order();
31738 });
31739};
31740/**
31741 * Handle updated groups
31742 * @param {number[]} ids
31743 * @private
31744 */
31745
31746
31747ItemSet.prototype._onUpdateGroups = function (ids) {
31748 this._onAddGroups(ids);
31749};
31750/**
31751 * Handle changed groups (added or updated)
31752 * @param {number[]} ids
31753 * @private
31754 */
31755
31756
31757ItemSet.prototype._onAddGroups = function (ids) {
31758 var me = this;
31759 ids.forEach(function (id) {
31760 var groupData = me.groupsData.get(id);
31761 var group = me.groups[id];
31762
31763 if (!group) {
31764 // check for reserved ids
31765 if (id == UNGROUPED || id == BACKGROUND) {
31766 throw new Error('Illegal group id. ' + id + ' is a reserved id.');
31767 }
31768
31769 var groupOptions = Object.create(me.options);
31770 util.extend(groupOptions, {
31771 height: null
31772 });
31773 group = new Group_1(id, groupData, me);
31774 me.groups[id] = group; // add items with this groupId to the new group
31775
31776 for (var itemId in me.items) {
31777 if (me.items.hasOwnProperty(itemId)) {
31778 var item = me.items[itemId];
31779
31780 if (item.data.group == id) {
31781 group.add(item);
31782 }
31783 }
31784 }
31785
31786 group.order();
31787 group.show();
31788 } else {
31789 // update group
31790 group.setData(groupData);
31791 }
31792 });
31793 this.body.emitter.emit('_change', {
31794 queue: true
31795 });
31796};
31797/**
31798 * Handle removed groups
31799 * @param {number[]} ids
31800 * @private
31801 */
31802
31803
31804ItemSet.prototype._onRemoveGroups = function (ids) {
31805 var groups = this.groups;
31806 ids.forEach(function (id) {
31807 var group = groups[id];
31808
31809 if (group) {
31810 group.hide();
31811 delete groups[id];
31812 }
31813 });
31814 this.markDirty();
31815 this.body.emitter.emit('_change', {
31816 queue: true
31817 });
31818};
31819/**
31820 * Reorder the groups if needed
31821 * @return {boolean} changed
31822 * @private
31823 */
31824
31825
31826ItemSet.prototype._orderGroups = function () {
31827 if (this.groupsData) {
31828 // reorder the groups
31829 var groupIds = this.groupsData.getIds({
31830 order: this.options.groupOrder
31831 });
31832 groupIds = this._orderNestedGroups(groupIds);
31833 var changed = !util.equalArray(groupIds, this.groupIds);
31834
31835 if (changed) {
31836 // hide all groups, removes them from the DOM
31837 var groups = this.groups;
31838 groupIds.forEach(function (groupId) {
31839 groups[groupId].hide();
31840 }); // show the groups again, attach them to the DOM in correct order
31841
31842 groupIds.forEach(function (groupId) {
31843 groups[groupId].show();
31844 });
31845 this.groupIds = groupIds;
31846 }
31847
31848 return changed;
31849 } else {
31850 return false;
31851 }
31852};
31853/**
31854 * Reorder the nested groups
31855 *
31856 * @param {Array.<number>} groupIds
31857 * @returns {Array.<number>}
31858 * @private
31859 */
31860
31861
31862ItemSet.prototype._orderNestedGroups = function (groupIds) {
31863 var _this = this;
31864
31865 /**
31866 * Recursively order nested groups
31867 *
31868 * @param {ItemSet} t
31869 * @param {Array.<number>} groupIds
31870 * @returns {Array.<number>}
31871 * @private
31872 */
31873 function getOrderedNestedGroups(t, groupIds) {
31874 var result = [];
31875 groupIds.forEach(function (groupId) {
31876 result.push(groupId);
31877 var groupData = t.groupsData.get(groupId);
31878
31879 if (groupData.nestedGroups) {
31880 var nestedGroupIds = t.groupsData.get({
31881 filter: function filter(nestedGroup) {
31882 return nestedGroup.nestedInGroup == groupId;
31883 },
31884 order: t.options.groupOrder
31885 }).map(function (nestedGroup) {
31886 return nestedGroup.id;
31887 });
31888 result = result.concat(getOrderedNestedGroups(t, nestedGroupIds));
31889 }
31890 });
31891 return result;
31892 }
31893
31894 var topGroupIds = groupIds.filter(function (groupId) {
31895 return !_this.groupsData.get(groupId).nestedInGroup;
31896 });
31897 return getOrderedNestedGroups(this, topGroupIds);
31898};
31899/**
31900 * Add a new item
31901 * @param {Item} item
31902 * @private
31903 */
31904
31905
31906ItemSet.prototype._addItem = function (item) {
31907 this.items[item.id] = item; // add to group
31908
31909 var groupId = this._getGroupId(item.data);
31910
31911 var group = this.groups[groupId];
31912
31913 if (!group) {
31914 item.groupShowing = false;
31915 } else if (group && group.data && group.data.showNested) {
31916 item.groupShowing = true;
31917 }
31918
31919 if (group) group.add(item);
31920};
31921/**
31922 * Update an existing item
31923 * @param {Item} item
31924 * @param {Object} itemData
31925 * @private
31926 */
31927
31928
31929ItemSet.prototype._updateItem = function (item, itemData) {
31930 // update the items data (will redraw the item when displayed)
31931 item.setData(itemData);
31932
31933 var groupId = this._getGroupId(item.data);
31934
31935 var group = this.groups[groupId];
31936
31937 if (!group) {
31938 item.groupShowing = false;
31939 } else if (group && group.data && group.data.showNested) {
31940 item.groupShowing = true;
31941 }
31942};
31943/**
31944 * Delete an item from the ItemSet: remove it from the DOM, from the map
31945 * with items, and from the map with visible items, and from the selection
31946 * @param {Item} item
31947 * @private
31948 */
31949
31950
31951ItemSet.prototype._removeItem = function (item) {
31952 // remove from DOM
31953 item.hide(); // remove from items
31954
31955 delete this.items[item.id]; // remove from selection
31956
31957 var index = this.selection.indexOf(item.id);
31958 if (index != -1) this.selection.splice(index, 1); // remove from group
31959
31960 item.parent && item.parent.remove(item);
31961};
31962/**
31963 * Create an array containing all items being a range (having an end date)
31964 * @param {Array.<Object>} array
31965 * @returns {Array}
31966 * @private
31967 */
31968
31969
31970ItemSet.prototype._constructByEndArray = function (array) {
31971 var endArray = [];
31972
31973 for (var i = 0; i < array.length; i++) {
31974 if (array[i] instanceof RangeItem_1) {
31975 endArray.push(array[i]);
31976 }
31977 }
31978
31979 return endArray;
31980};
31981/**
31982 * Register the clicked item on touch, before dragStart is initiated.
31983 *
31984 * dragStart is initiated from a mousemove event, AFTER the mouse/touch is
31985 * already moving. Therefore, the mouse/touch can sometimes be above an other
31986 * DOM element than the item itself.
31987 *
31988 * @param {Event} event
31989 * @private
31990 */
31991
31992
31993ItemSet.prototype._onTouch = function (event) {
31994 // store the touched item, used in _onDragStart
31995 this.touchParams.item = this.itemFromTarget(event);
31996 this.touchParams.dragLeftItem = event.target.dragLeftItem || false;
31997 this.touchParams.dragRightItem = event.target.dragRightItem || false;
31998 this.touchParams.itemProps = null;
31999};
32000/**
32001 * Given an group id, returns the index it has.
32002 *
32003 * @param {number} groupId
32004 * @returns {number} index / groupId
32005 * @private
32006 */
32007
32008
32009ItemSet.prototype._getGroupIndex = function (groupId) {
32010 for (var i = 0; i < this.groupIds.length; i++) {
32011 if (groupId == this.groupIds[i]) return i;
32012 }
32013};
32014/**
32015 * Start dragging the selected events
32016 * @param {Event} event
32017 * @private
32018 */
32019
32020
32021ItemSet.prototype._onDragStart = function (event) {
32022 if (this.touchParams.itemIsDragging) {
32023 return;
32024 }
32025
32026 var item = this.touchParams.item || null;
32027 var me = this;
32028 var props;
32029
32030 if (item && (item.selected || this.options.itemsAlwaysDraggable.item)) {
32031 if (this.options.editable.overrideItems && !this.options.editable.updateTime && !this.options.editable.updateGroup) {
32032 return;
32033 } // override options.editable
32034
32035
32036 if (item.editable != null && !item.editable.updateTime && !item.editable.updateGroup && !this.options.editable.overrideItems) {
32037 return;
32038 }
32039
32040 var dragLeftItem = this.touchParams.dragLeftItem;
32041 var dragRightItem = this.touchParams.dragRightItem;
32042 this.touchParams.itemIsDragging = true;
32043 this.touchParams.selectedItem = item;
32044
32045 if (dragLeftItem) {
32046 props = {
32047 item: dragLeftItem,
32048 initialX: event.center.x,
32049 dragLeft: true,
32050 data: this._cloneItemData(item.data)
32051 };
32052 this.touchParams.itemProps = [props];
32053 } else if (dragRightItem) {
32054 props = {
32055 item: dragRightItem,
32056 initialX: event.center.x,
32057 dragRight: true,
32058 data: this._cloneItemData(item.data)
32059 };
32060 this.touchParams.itemProps = [props];
32061 } else if (this.options.editable.add && (event.srcEvent.ctrlKey || event.srcEvent.metaKey)) {
32062 // create a new range item when dragging with ctrl key down
32063 this._onDragStartAddItem(event);
32064 } else {
32065 if (this.groupIds.length < 1) {
32066 // Mitigates a race condition if _onDragStart() is
32067 // called after markDirty() without redraw() being called between.
32068 this.redraw();
32069 }
32070
32071 var baseGroupIndex = this._getGroupIndex(item.data.group);
32072
32073 var itemsToDrag = this.options.itemsAlwaysDraggable.item && !item.selected ? [item.id] : this.getSelection();
32074 this.touchParams.itemProps = itemsToDrag.map(function (id) {
32075 var item = me.items[id];
32076
32077 var groupIndex = me._getGroupIndex(item.data.group);
32078
32079 return {
32080 item: item,
32081 initialX: event.center.x,
32082 groupOffset: baseGroupIndex - groupIndex,
32083 data: this._cloneItemData(item.data)
32084 };
32085 }.bind(this));
32086 }
32087
32088 event.stopPropagation();
32089 } else if (this.options.editable.add && (event.srcEvent.ctrlKey || event.srcEvent.metaKey)) {
32090 // create a new range item when dragging with ctrl key down
32091 this._onDragStartAddItem(event);
32092 }
32093};
32094/**
32095 * Start creating a new range item by dragging.
32096 * @param {Event} event
32097 * @private
32098 */
32099
32100
32101ItemSet.prototype._onDragStartAddItem = function (event) {
32102 var xAbs;
32103 var x;
32104 var snap = this.options.snap || null;
32105
32106 if (this.options.rtl) {
32107 xAbs = util.getAbsoluteRight(this.dom.frame);
32108 x = xAbs - event.center.x + 10; // plus 10 to compensate for the drag starting as soon as you've moved 10px
32109 } else {
32110 xAbs = util.getAbsoluteLeft(this.dom.frame);
32111 x = event.center.x - xAbs - 10; // minus 10 to compensate for the drag starting as soon as you've moved 10px
32112 }
32113
32114 var time = this.body.util.toTime(x);
32115 var scale = this.body.util.getScale();
32116 var step = this.body.util.getStep();
32117 var start = snap ? snap(time, scale, step) : time;
32118 var end = start;
32119 var itemData = {
32120 type: 'range',
32121 start: start,
32122 end: end,
32123 content: 'new item'
32124 };
32125 var id = util.randomUUID();
32126 itemData[this.itemsData._fieldId] = id;
32127 var group = this.groupFromTarget(event);
32128
32129 if (group) {
32130 itemData.group = group.groupId;
32131 }
32132
32133 var newItem = new RangeItem_1(itemData, this.conversion, this.options);
32134 newItem.id = id; // TODO: not so nice setting id afterwards
32135
32136 newItem.data = this._cloneItemData(itemData);
32137
32138 this._addItem(newItem);
32139
32140 this.touchParams.selectedItem = newItem;
32141 var props = {
32142 item: newItem,
32143 initialX: event.center.x,
32144 data: newItem.data
32145 };
32146
32147 if (this.options.rtl) {
32148 props.dragLeft = true;
32149 } else {
32150 props.dragRight = true;
32151 }
32152
32153 this.touchParams.itemProps = [props];
32154 event.stopPropagation();
32155};
32156/**
32157 * Drag selected items
32158 * @param {Event} event
32159 * @private
32160 */
32161
32162
32163ItemSet.prototype._onDrag = function (event) {
32164 // deactivate tooltip window
32165 this.clearPopupTimer();
32166
32167 if (this.popup != null) {
32168 this.popup.hide();
32169 }
32170
32171 if (this.touchParams.itemProps) {
32172 event.stopPropagation();
32173 var me = this;
32174 var snap = this.options.snap || null;
32175 var xOffset;
32176
32177 if (this.options.rtl) {
32178 xOffset = this.body.dom.root.offsetLeft + this.body.domProps.right.width;
32179 } else {
32180 xOffset = this.body.dom.root.offsetLeft + this.body.domProps.left.width;
32181 }
32182
32183 var scale = this.body.util.getScale();
32184 var step = this.body.util.getStep(); //only calculate the new group for the item that's actually dragged
32185
32186 var selectedItem = this.touchParams.selectedItem;
32187 var updateGroupAllowed = (this.options.editable.overrideItems || selectedItem.editable == null) && this.options.editable.updateGroup || !this.options.editable.overrideItems && selectedItem.editable != null && selectedItem.editable.updateGroup;
32188 var newGroupBase = null;
32189
32190 if (updateGroupAllowed && selectedItem) {
32191 if (selectedItem.data.group != undefined) {
32192 // drag from one group to another
32193 var group = me.groupFromTarget(event);
32194
32195 if (group) {
32196 //we know the offset for all items, so the new group for all items
32197 //will be relative to this one.
32198 newGroupBase = this._getGroupIndex(group.groupId);
32199 }
32200 }
32201 } // move
32202
32203
32204 this.touchParams.itemProps.forEach(function (props) {
32205 var current = me.body.util.toTime(event.center.x - xOffset);
32206 var initial = me.body.util.toTime(props.initialX - xOffset);
32207 var offset;
32208 var initialStart;
32209 var initialEnd;
32210 var start;
32211 var end;
32212
32213 if (this.options.rtl) {
32214 offset = -(current - initial); // ms
32215 } else {
32216 offset = current - initial; // ms
32217 }
32218
32219 var itemData = this._cloneItemData(props.item.data); // clone the data
32220
32221
32222 if (props.item.editable != null && !props.item.editable.updateTime && !props.item.editable.updateGroup && !me.options.editable.overrideItems) {
32223 return;
32224 }
32225
32226 var updateTimeAllowed = (this.options.editable.overrideItems || selectedItem.editable == null) && this.options.editable.updateTime || !this.options.editable.overrideItems && selectedItem.editable != null && selectedItem.editable.updateTime;
32227
32228 if (updateTimeAllowed) {
32229 if (props.dragLeft) {
32230 // drag left side of a range item
32231 if (this.options.rtl) {
32232 if (itemData.end != undefined) {
32233 initialEnd = util.convert(props.data.end, 'Date');
32234 end = new Date(initialEnd.valueOf() + offset); // TODO: pass a Moment instead of a Date to snap(). (Breaking change)
32235
32236 itemData.end = snap ? snap(end, scale, step) : end;
32237 }
32238 } else {
32239 if (itemData.start != undefined) {
32240 initialStart = util.convert(props.data.start, 'Date');
32241 start = new Date(initialStart.valueOf() + offset); // TODO: pass a Moment instead of a Date to snap(). (Breaking change)
32242
32243 itemData.start = snap ? snap(start, scale, step) : start;
32244 }
32245 }
32246 } else if (props.dragRight) {
32247 // drag right side of a range item
32248 if (this.options.rtl) {
32249 if (itemData.start != undefined) {
32250 initialStart = util.convert(props.data.start, 'Date');
32251 start = new Date(initialStart.valueOf() + offset); // TODO: pass a Moment instead of a Date to snap(). (Breaking change)
32252
32253 itemData.start = snap ? snap(start, scale, step) : start;
32254 }
32255 } else {
32256 if (itemData.end != undefined) {
32257 initialEnd = util.convert(props.data.end, 'Date');
32258 end = new Date(initialEnd.valueOf() + offset); // TODO: pass a Moment instead of a Date to snap(). (Breaking change)
32259
32260 itemData.end = snap ? snap(end, scale, step) : end;
32261 }
32262 }
32263 } else {
32264 // drag both start and end
32265 if (itemData.start != undefined) {
32266 initialStart = util.convert(props.data.start, 'Date').valueOf();
32267 start = new Date(initialStart + offset);
32268
32269 if (itemData.end != undefined) {
32270 initialEnd = util.convert(props.data.end, 'Date');
32271 var duration = initialEnd.valueOf() - initialStart.valueOf(); // TODO: pass a Moment instead of a Date to snap(). (Breaking change)
32272
32273 itemData.start = snap ? snap(start, scale, step) : start;
32274 itemData.end = new Date(itemData.start.valueOf() + duration);
32275 } else {
32276 // TODO: pass a Moment instead of a Date to snap(). (Breaking change)
32277 itemData.start = snap ? snap(start, scale, step) : start;
32278 }
32279 }
32280 }
32281 }
32282
32283 if (updateGroupAllowed && !props.dragLeft && !props.dragRight && newGroupBase != null) {
32284 if (itemData.group != undefined) {
32285 var newOffset = newGroupBase - props.groupOffset; //make sure we stay in bounds
32286
32287 newOffset = Math.max(0, newOffset);
32288 newOffset = Math.min(me.groupIds.length - 1, newOffset);
32289 itemData.group = me.groupIds[newOffset];
32290 }
32291 } // confirm moving the item
32292
32293
32294 itemData = this._cloneItemData(itemData); // convert start and end to the correct type
32295
32296 me.options.onMoving(itemData, function (itemData) {
32297 if (itemData) {
32298 props.item.setData(this._cloneItemData(itemData, 'Date'));
32299 }
32300 }.bind(this));
32301 }.bind(this));
32302 this.body.emitter.emit('_change');
32303 }
32304};
32305/**
32306 * Move an item to another group
32307 * @param {Item} item
32308 * @param {string | number} groupId
32309 * @private
32310 */
32311
32312
32313ItemSet.prototype._moveToGroup = function (item, groupId) {
32314 var group = this.groups[groupId];
32315
32316 if (group && group.groupId != item.data.group) {
32317 var oldGroup = item.parent;
32318 oldGroup.remove(item);
32319 oldGroup.order();
32320 item.data.group = group.groupId;
32321 group.add(item);
32322 group.order();
32323 }
32324};
32325/**
32326 * End of dragging selected items
32327 * @param {Event} event
32328 * @private
32329 */
32330
32331
32332ItemSet.prototype._onDragEnd = function (event) {
32333 this.touchParams.itemIsDragging = false;
32334
32335 if (this.touchParams.itemProps) {
32336 event.stopPropagation();
32337 var me = this;
32338 var dataset = this.itemsData.getDataSet();
32339 var itemProps = this.touchParams.itemProps;
32340 this.touchParams.itemProps = null;
32341 itemProps.forEach(function (props) {
32342 var id = props.item.id;
32343 var exists = me.itemsData.get(id, me.itemOptions) != null;
32344
32345 if (!exists) {
32346 // add a new item
32347 me.options.onAdd(props.item.data, function (itemData) {
32348 me._removeItem(props.item); // remove temporary item
32349
32350
32351 if (itemData) {
32352 me.itemsData.getDataSet().add(itemData);
32353 } // force re-stacking of all items next redraw
32354
32355
32356 me.body.emitter.emit('_change');
32357 });
32358 } else {
32359 // update existing item
32360 var itemData = this._cloneItemData(props.item.data); // convert start and end to the correct type
32361
32362
32363 me.options.onMove(itemData, function (itemData) {
32364 if (itemData) {
32365 // apply changes
32366 itemData[dataset._fieldId] = id; // ensure the item contains its id (can be undefined)
32367
32368 dataset.update(itemData);
32369 } else {
32370 // restore original values
32371 props.item.setData(props.data);
32372 me.body.emitter.emit('_change');
32373 }
32374 });
32375 }
32376 }.bind(this));
32377 }
32378};
32379
32380ItemSet.prototype._onGroupClick = function (event) {
32381 var group = this.groupFromTarget(event);
32382 if (!group || !group.nestedGroups) return;
32383 var groupsData = this.groupsData.getDataSet();
32384 var nestingGroup = groupsData.get(group.groupId);
32385
32386 if (nestingGroup.showNested == undefined) {
32387 nestingGroup.showNested = true;
32388 }
32389
32390 nestingGroup.showNested = !nestingGroup.showNested;
32391 var nestedGroups = groupsData.get(group.nestedGroups).map(function (nestedGroup) {
32392 nestedGroup.visible = nestingGroup.showNested;
32393 return nestedGroup;
32394 });
32395 groupsData.update(nestedGroups.concat(nestingGroup));
32396
32397 if (nestingGroup.showNested) {
32398 util.removeClassName(group.dom.label, 'collapsed');
32399 util.addClassName(group.dom.label, 'expanded');
32400 } else {
32401 util.removeClassName(group.dom.label, 'expanded');
32402 var collapsedDirClassName = this.options.rtl ? 'collapsed-rtl' : 'collapsed';
32403 util.addClassName(group.dom.label, collapsedDirClassName);
32404 }
32405};
32406
32407ItemSet.prototype._onGroupDragStart = function (event) {
32408 if (this.options.groupEditable.order) {
32409 this.groupTouchParams.group = this.groupFromTarget(event);
32410
32411 if (this.groupTouchParams.group) {
32412 event.stopPropagation();
32413 this.groupTouchParams.originalOrder = this.groupsData.getIds({
32414 order: this.options.groupOrder
32415 });
32416 }
32417 }
32418};
32419
32420ItemSet.prototype._onGroupDrag = function (event) {
32421 if (this.options.groupEditable.order && this.groupTouchParams.group) {
32422 event.stopPropagation();
32423 var groupsData = this.groupsData;
32424
32425 if (this.groupsData instanceof DataView) {
32426 groupsData = this.groupsData.getDataSet();
32427 } // drag from one group to another
32428
32429
32430 var group = this.groupFromTarget(event); // try to avoid toggling when groups differ in height
32431
32432 if (group && group.height != this.groupTouchParams.group.height) {
32433 var movingUp = group.top < this.groupTouchParams.group.top;
32434 var clientY = event.center ? event.center.y : event.clientY;
32435 var targetGroupTop = util.getAbsoluteTop(group.dom.foreground);
32436 var draggedGroupHeight = this.groupTouchParams.group.height;
32437
32438 if (movingUp) {
32439 // skip swapping the groups when the dragged group is not below clientY afterwards
32440 if (targetGroupTop + draggedGroupHeight < clientY) {
32441 return;
32442 }
32443 } else {
32444 var targetGroupHeight = group.height; // skip swapping the groups when the dragged group is not below clientY afterwards
32445
32446 if (targetGroupTop + targetGroupHeight - draggedGroupHeight > clientY) {
32447 return;
32448 }
32449 }
32450 }
32451
32452 if (group && group != this.groupTouchParams.group) {
32453 var targetGroup = groupsData.get(group.groupId);
32454 var draggedGroup = groupsData.get(this.groupTouchParams.group.groupId); // switch groups
32455
32456 if (draggedGroup && targetGroup) {
32457 this.options.groupOrderSwap(draggedGroup, targetGroup, groupsData);
32458 groupsData.update(draggedGroup);
32459 groupsData.update(targetGroup);
32460 } // fetch current order of groups
32461
32462
32463 var newOrder = groupsData.getIds({
32464 order: this.options.groupOrder
32465 }); // in case of changes since _onGroupDragStart
32466
32467 if (!util.equalArray(newOrder, this.groupTouchParams.originalOrder)) {
32468 var origOrder = this.groupTouchParams.originalOrder;
32469 var draggedId = this.groupTouchParams.group.groupId;
32470 var numGroups = Math.min(origOrder.length, newOrder.length);
32471 var curPos = 0;
32472 var newOffset = 0;
32473 var orgOffset = 0;
32474
32475 while (curPos < numGroups) {
32476 // as long as the groups are where they should be step down along the groups order
32477 while (curPos + newOffset < numGroups && curPos + orgOffset < numGroups && newOrder[curPos + newOffset] == origOrder[curPos + orgOffset]) {
32478 curPos++;
32479 } // all ok
32480
32481
32482 if (curPos + newOffset >= numGroups) {
32483 break;
32484 } // not all ok
32485 // if dragged group was move upwards everything below should have an offset
32486
32487
32488 if (newOrder[curPos + newOffset] == draggedId) {
32489 newOffset = 1;
32490 } // if dragged group was move downwards everything above should have an offset
32491 else if (origOrder[curPos + orgOffset] == draggedId) {
32492 orgOffset = 1;
32493 } // found a group (apart from dragged group) that has the wrong position -> switch with the
32494 // group at the position where other one should be, fix index arrays and continue
32495 else {
32496 var slippedPosition = newOrder.indexOf(origOrder[curPos + orgOffset]);
32497 var switchGroup = groupsData.get(newOrder[curPos + newOffset]);
32498 var shouldBeGroup = groupsData.get(origOrder[curPos + orgOffset]);
32499 this.options.groupOrderSwap(switchGroup, shouldBeGroup, groupsData);
32500 groupsData.update(switchGroup);
32501 groupsData.update(shouldBeGroup);
32502 var switchGroupId = newOrder[curPos + newOffset];
32503 newOrder[curPos + newOffset] = origOrder[curPos + orgOffset];
32504 newOrder[slippedPosition] = switchGroupId;
32505 curPos++;
32506 }
32507 }
32508 }
32509 }
32510 }
32511};
32512
32513ItemSet.prototype._onGroupDragEnd = function (event) {
32514 if (this.options.groupEditable.order && this.groupTouchParams.group) {
32515 event.stopPropagation(); // update existing group
32516
32517 var me = this;
32518 var id = me.groupTouchParams.group.groupId;
32519 var dataset = me.groupsData.getDataSet();
32520 var groupData = util.extend({}, dataset.get(id)); // clone the data
32521
32522 me.options.onMoveGroup(groupData, function (groupData) {
32523 if (groupData) {
32524 // apply changes
32525 groupData[dataset._fieldId] = id; // ensure the group contains its id (can be undefined)
32526
32527 dataset.update(groupData);
32528 } else {
32529 // fetch current order of groups
32530 var newOrder = dataset.getIds({
32531 order: me.options.groupOrder
32532 }); // restore original order
32533
32534 if (!util.equalArray(newOrder, me.groupTouchParams.originalOrder)) {
32535 var origOrder = me.groupTouchParams.originalOrder;
32536 var numGroups = Math.min(origOrder.length, newOrder.length);
32537 var curPos = 0;
32538
32539 while (curPos < numGroups) {
32540 // as long as the groups are where they should be step down along the groups order
32541 while (curPos < numGroups && newOrder[curPos] == origOrder[curPos]) {
32542 curPos++;
32543 } // all ok
32544
32545
32546 if (curPos >= numGroups) {
32547 break;
32548 } // found a group that has the wrong position -> switch with the
32549 // group at the position where other one should be, fix index arrays and continue
32550
32551
32552 var slippedPosition = newOrder.indexOf(origOrder[curPos]);
32553 var switchGroup = dataset.get(newOrder[curPos]);
32554 var shouldBeGroup = dataset.get(origOrder[curPos]);
32555 me.options.groupOrderSwap(switchGroup, shouldBeGroup, dataset);
32556 dataset.update(switchGroup);
32557 dataset.update(shouldBeGroup);
32558 var switchGroupId = newOrder[curPos];
32559 newOrder[curPos] = origOrder[curPos];
32560 newOrder[slippedPosition] = switchGroupId;
32561 curPos++;
32562 }
32563 }
32564 }
32565 });
32566 me.body.emitter.emit('groupDragged', {
32567 groupId: id
32568 });
32569 }
32570};
32571/**
32572 * Handle selecting/deselecting an item when tapping it
32573 * @param {Event} event
32574 * @private
32575 */
32576
32577
32578ItemSet.prototype._onSelectItem = function (event) {
32579 if (!this.options.selectable) return;
32580 var ctrlKey = event.srcEvent && (event.srcEvent.ctrlKey || event.srcEvent.metaKey);
32581 var shiftKey = event.srcEvent && event.srcEvent.shiftKey;
32582
32583 if (ctrlKey || shiftKey) {
32584 this._onMultiSelectItem(event);
32585
32586 return;
32587 }
32588
32589 var oldSelection = this.getSelection();
32590 var item = this.itemFromTarget(event);
32591 var selection = item ? [item.id] : [];
32592 this.setSelection(selection);
32593 var newSelection = this.getSelection(); // emit a select event,
32594 // except when old selection is empty and new selection is still empty
32595
32596 if (newSelection.length > 0 || oldSelection.length > 0) {
32597 this.body.emitter.emit('select', {
32598 items: newSelection,
32599 event: event
32600 });
32601 }
32602};
32603/**
32604 * Handle hovering an item
32605 * @param {Event} event
32606 * @private
32607 */
32608
32609
32610ItemSet.prototype._onMouseOver = function (event) {
32611 var item = this.itemFromTarget(event);
32612 if (!item) return; // Item we just left
32613
32614 var related = this.itemFromRelatedTarget(event);
32615
32616 if (item === related) {
32617 // We haven't changed item, just element in the item
32618 return;
32619 }
32620
32621 var title = item.getTitle();
32622
32623 if (this.options.showTooltips && title) {
32624 if (this.popup == null) {
32625 this.popup = new Popup(this.body.dom.root, this.options.tooltip.overflowMethod || 'flip');
32626 }
32627
32628 this.popup.setText(title);
32629 var container = this.body.dom.centerContainer;
32630 this.popup.setPosition(event.clientX - util.getAbsoluteLeft(container) + container.offsetLeft, event.clientY - util.getAbsoluteTop(container) + container.offsetTop);
32631 this.setPopupTimer(this.popup);
32632 } else {
32633 // Hovering over item without a title, hide popup
32634 // Needed instead of _just_ in _onMouseOut due to #2572
32635 this.clearPopupTimer();
32636
32637 if (this.popup != null) {
32638 this.popup.hide();
32639 }
32640 }
32641
32642 this.body.emitter.emit('itemover', {
32643 item: item.id,
32644 event: event
32645 });
32646};
32647
32648ItemSet.prototype._onMouseOut = function (event) {
32649 var item = this.itemFromTarget(event);
32650 if (!item) return; // Item we are going to
32651
32652 var related = this.itemFromRelatedTarget(event);
32653
32654 if (item === related) {
32655 // We aren't changing item, just element in the item
32656 return;
32657 }
32658
32659 this.clearPopupTimer();
32660
32661 if (this.popup != null) {
32662 this.popup.hide();
32663 }
32664
32665 this.body.emitter.emit('itemout', {
32666 item: item.id,
32667 event: event
32668 });
32669};
32670
32671ItemSet.prototype._onMouseMove = function (event) {
32672 var item = this.itemFromTarget(event);
32673 if (!item) return;
32674
32675 if (this.popupTimer != null) {
32676 // restart timer
32677 this.setPopupTimer(this.popup);
32678 }
32679
32680 if (this.options.showTooltips && this.options.tooltip.followMouse) {
32681 if (this.popup) {
32682 if (!this.popup.hidden) {
32683 var container = this.body.dom.centerContainer;
32684 this.popup.setPosition(event.clientX - util.getAbsoluteLeft(container) + container.offsetLeft, event.clientY - util.getAbsoluteTop(container) + container.offsetTop);
32685 this.popup.show(); // Redraw
32686 }
32687 }
32688 }
32689};
32690/**
32691 * Handle mousewheel
32692 * @param {Event} event The event
32693 * @private
32694 */
32695
32696
32697ItemSet.prototype._onMouseWheel = function (event) {
32698 if (this.touchParams.itemIsDragging) {
32699 this._onDragEnd(event);
32700 }
32701};
32702/**
32703 * Handle updates of an item on double tap
32704 * @param {vis.Item} item The item
32705 * @private
32706 */
32707
32708
32709ItemSet.prototype._onUpdateItem = function (item) {
32710 if (!this.options.selectable) return;
32711 if (!this.options.editable.add) return;
32712 var me = this;
32713
32714 if (item) {
32715 // execute async handler to update the item (or cancel it)
32716 var itemData = me.itemsData.get(item.id); // get a clone of the data from the dataset
32717
32718 this.options.onUpdate(itemData, function (itemData) {
32719 if (itemData) {
32720 me.itemsData.getDataSet().update(itemData);
32721 }
32722 });
32723 }
32724};
32725/**
32726 * Handle drop event of data on item
32727 * Only called when `objectData.target === 'item'.
32728 * @param {Event} event The event
32729 * @private
32730 */
32731
32732
32733ItemSet.prototype._onDropObjectOnItem = function (event) {
32734 var item = this.itemFromTarget(event);
32735 var objectData = JSON.parse(event.dataTransfer.getData("text"));
32736 this.options.onDropObjectOnItem(objectData, item);
32737};
32738/**
32739 * Handle creation of an item on double tap or drop of a drag event
32740 * @param {Event} event The event
32741 * @private
32742 */
32743
32744
32745ItemSet.prototype._onAddItem = function (event) {
32746 if (!this.options.selectable) return;
32747 if (!this.options.editable.add) return;
32748 var me = this;
32749 var snap = this.options.snap || null;
32750 var xAbs;
32751 var x; // add item
32752
32753 if (this.options.rtl) {
32754 xAbs = util.getAbsoluteRight(this.dom.frame);
32755 x = xAbs - event.center.x;
32756 } else {
32757 xAbs = util.getAbsoluteLeft(this.dom.frame);
32758 x = event.center.x - xAbs;
32759 } // var xAbs = util.getAbsoluteLeft(this.dom.frame);
32760 // var x = event.center.x - xAbs;
32761
32762
32763 var start = this.body.util.toTime(x);
32764 var scale = this.body.util.getScale();
32765 var step = this.body.util.getStep();
32766 var end;
32767 var newItemData;
32768
32769 if (event.type == 'drop') {
32770 newItemData = JSON.parse(event.dataTransfer.getData("text"));
32771 newItemData.content = newItemData.content ? newItemData.content : 'new item';
32772 newItemData.start = newItemData.start ? newItemData.start : snap ? snap(start, scale, step) : start;
32773 newItemData.type = newItemData.type || 'box';
32774 newItemData[this.itemsData._fieldId] = newItemData.id || util.randomUUID();
32775
32776 if (newItemData.type == 'range' && !newItemData.end) {
32777 end = this.body.util.toTime(x + this.props.width / 5);
32778 newItemData.end = snap ? snap(end, scale, step) : end;
32779 }
32780 } else {
32781 newItemData = {
32782 start: snap ? snap(start, scale, step) : start,
32783 content: 'new item'
32784 };
32785 newItemData[this.itemsData._fieldId] = util.randomUUID(); // when default type is a range, add a default end date to the new item
32786
32787 if (this.options.type === 'range') {
32788 end = this.body.util.toTime(x + this.props.width / 5);
32789 newItemData.end = snap ? snap(end, scale, step) : end;
32790 }
32791 }
32792
32793 var group = this.groupFromTarget(event);
32794
32795 if (group) {
32796 newItemData.group = group.groupId;
32797 } // execute async handler to customize (or cancel) adding an item
32798
32799
32800 newItemData = this._cloneItemData(newItemData); // convert start and end to the correct type
32801
32802 this.options.onAdd(newItemData, function (item) {
32803 if (item) {
32804 me.itemsData.getDataSet().add(item);
32805
32806 if (event.type == 'drop') {
32807 me.setSelection([item.id]);
32808 } // TODO: need to trigger a redraw?
32809
32810 }
32811 });
32812};
32813/**
32814 * Handle selecting/deselecting multiple items when holding an item
32815 * @param {Event} event
32816 * @private
32817 */
32818
32819
32820ItemSet.prototype._onMultiSelectItem = function (event) {
32821 if (!this.options.selectable) return;
32822 var item = this.itemFromTarget(event);
32823
32824 if (item) {
32825 // multi select items (if allowed)
32826 var selection = this.options.multiselect ? this.getSelection() // take current selection
32827 : []; // deselect current selection
32828
32829 var shiftKey = event.srcEvent && event.srcEvent.shiftKey || false;
32830
32831 if (shiftKey && this.options.multiselect) {
32832 // select all items between the old selection and the tapped item
32833 var itemGroup = this.itemsData.get(item.id).group; // when filtering get the group of the last selected item
32834
32835 var lastSelectedGroup = undefined;
32836
32837 if (this.options.multiselectPerGroup) {
32838 if (selection.length > 0) {
32839 lastSelectedGroup = this.itemsData.get(selection[0]).group;
32840 }
32841 } // determine the selection range
32842
32843
32844 if (!this.options.multiselectPerGroup || lastSelectedGroup == undefined || lastSelectedGroup == itemGroup) {
32845 selection.push(item.id);
32846 }
32847
32848 var range = ItemSet._getItemRange(this.itemsData.get(selection, this.itemOptions));
32849
32850 if (!this.options.multiselectPerGroup || lastSelectedGroup == itemGroup) {
32851 // select all items within the selection range
32852 selection = [];
32853
32854 for (var id in this.items) {
32855 if (this.items.hasOwnProperty(id)) {
32856 var _item = this.items[id];
32857 var start = _item.data.start;
32858 var end = _item.data.end !== undefined ? _item.data.end : start;
32859
32860 if (start >= range.min && end <= range.max && (!this.options.multiselectPerGroup || lastSelectedGroup == this.itemsData.get(_item.id).group) && !(_item instanceof BackgroundItem_1)) {
32861 selection.push(_item.id); // do not use id but item.id, id itself is stringified
32862 }
32863 }
32864 }
32865 }
32866 } else {
32867 // add/remove this item from the current selection
32868 var index = selection.indexOf(item.id);
32869
32870 if (index == -1) {
32871 // item is not yet selected -> select it
32872 selection.push(item.id);
32873 } else {
32874 // item is already selected -> deselect it
32875 selection.splice(index, 1);
32876 }
32877 }
32878
32879 this.setSelection(selection);
32880 this.body.emitter.emit('select', {
32881 items: this.getSelection(),
32882 event: event
32883 });
32884 }
32885};
32886/**
32887 * Calculate the time range of a list of items
32888 * @param {Array.<Object>} itemsData
32889 * @return {{min: Date, max: Date}} Returns the range of the provided items
32890 * @private
32891 */
32892
32893
32894ItemSet._getItemRange = function (itemsData) {
32895 var max = null;
32896 var min = null;
32897 itemsData.forEach(function (data) {
32898 if (min == null || data.start < min) {
32899 min = data.start;
32900 }
32901
32902 if (data.end != undefined) {
32903 if (max == null || data.end > max) {
32904 max = data.end;
32905 }
32906 } else {
32907 if (max == null || data.start > max) {
32908 max = data.start;
32909 }
32910 }
32911 });
32912 return {
32913 min: min,
32914 max: max
32915 };
32916};
32917/**
32918 * Find an item from an element:
32919 * searches for the attribute 'timeline-item' in the element's tree
32920 * @param {HTMLElement} element
32921 * @return {Item | null} item
32922 */
32923
32924
32925ItemSet.prototype.itemFromElement = function (element) {
32926 var cur = element;
32927
32928 while (cur) {
32929 if (cur.hasOwnProperty('timeline-item')) {
32930 return cur['timeline-item'];
32931 }
32932
32933 cur = cur.parentNode;
32934 }
32935
32936 return null;
32937};
32938/**
32939 * Find an item from an event target:
32940 * searches for the attribute 'timeline-item' in the event target's element tree
32941 * @param {Event} event
32942 * @return {Item | null} item
32943 */
32944
32945
32946ItemSet.prototype.itemFromTarget = function (event) {
32947 return this.itemFromElement(event.target);
32948};
32949/**
32950 * Find an item from an event's related target:
32951 * searches for the attribute 'timeline-item' in the related target's element tree
32952 * @param {Event} event
32953 * @return {Item | null} item
32954 */
32955
32956
32957ItemSet.prototype.itemFromRelatedTarget = function (event) {
32958 return this.itemFromElement(event.relatedTarget);
32959};
32960/**
32961 * Find the Group from an event target:
32962 * searches for the attribute 'timeline-group' in the event target's element tree
32963 * @param {Event} event
32964 * @return {Group | null} group
32965 */
32966
32967
32968ItemSet.prototype.groupFromTarget = function (event) {
32969 var clientY = event.center ? event.center.y : event.clientY;
32970 var groupIds = this.groupIds;
32971
32972 if (groupIds.length <= 0 && this.groupsData) {
32973 groupIds = this.groupsData.getIds({
32974 order: this.options.groupOrder
32975 });
32976 }
32977
32978 for (var i = 0; i < groupIds.length; i++) {
32979 var groupId = groupIds[i];
32980 var group = this.groups[groupId];
32981 var foreground = group.dom.foreground;
32982 var top = util.getAbsoluteTop(foreground);
32983
32984 if (clientY >= top && clientY < top + foreground.offsetHeight) {
32985 return group;
32986 }
32987
32988 if (this.options.orientation.item === 'top') {
32989 if (i === this.groupIds.length - 1 && clientY > top) {
32990 return group;
32991 }
32992 } else {
32993 if (i === 0 && clientY < top + foreground.offset) {
32994 return group;
32995 }
32996 }
32997 }
32998
32999 return null;
33000};
33001/**
33002 * Find the ItemSet from an event target:
33003 * searches for the attribute 'timeline-itemset' in the event target's element tree
33004 * @param {Event} event
33005 * @return {ItemSet | null} item
33006 */
33007
33008
33009ItemSet.itemSetFromTarget = function (event) {
33010 var target = event.target;
33011
33012 while (target) {
33013 if (target.hasOwnProperty('timeline-itemset')) {
33014 return target['timeline-itemset'];
33015 }
33016
33017 target = target.parentNode;
33018 }
33019
33020 return null;
33021};
33022/**
33023 * Clone the data of an item, and "normalize" it: convert the start and end date
33024 * to the type (Date, Moment, ...) configured in the DataSet. If not configured,
33025 * start and end are converted to Date.
33026 * @param {Object} itemData, typically `item.data`
33027 * @param {string} [type] Optional Date type. If not provided, the type from the DataSet is taken
33028 * @return {Object} The cloned object
33029 * @private
33030 */
33031
33032
33033ItemSet.prototype._cloneItemData = function (itemData, type) {
33034 var clone = util.extend({}, itemData);
33035
33036 if (!type) {
33037 // convert start and end date to the type (Date, Moment, ...) configured in the DataSet
33038 type = this.itemsData.getDataSet()._options.type;
33039 }
33040
33041 if (clone.start != undefined) {
33042 clone.start = util.convert(clone.start, type && type.start || 'Date');
33043 }
33044
33045 if (clone.end != undefined) {
33046 clone.end = util.convert(clone.end, type && type.end || 'Date');
33047 }
33048
33049 return clone;
33050};
33051
33052var errorFound = false;
33053var allOptions;
33054var printStyle = 'background: #FFeeee; color: #dd0000';
33055/**
33056 * Used to validate options.
33057 */
33058
33059var Validator =
33060/*#__PURE__*/
33061function () {
33062 /**
33063 * @ignore
33064 */
33065 function Validator() {
33066 _classCallCheck$1(this, Validator);
33067 }
33068 /**
33069 * Main function to be called
33070 * @param {Object} options
33071 * @param {Object} referenceOptions
33072 * @param {Object} subObject
33073 * @returns {boolean}
33074 * @static
33075 */
33076
33077
33078 _createClass$1(Validator, null, [{
33079 key: "validate",
33080 value: function validate(options, referenceOptions, subObject) {
33081 errorFound = false;
33082 allOptions = referenceOptions;
33083 var usedOptions = referenceOptions;
33084
33085 if (subObject !== undefined) {
33086 usedOptions = referenceOptions[subObject];
33087 }
33088
33089 Validator.parse(options, usedOptions, []);
33090 return errorFound;
33091 }
33092 /**
33093 * Will traverse an object recursively and check every value
33094 * @param {Object} options
33095 * @param {Object} referenceOptions
33096 * @param {array} path | where to look for the actual option
33097 * @static
33098 */
33099
33100 }, {
33101 key: "parse",
33102 value: function parse(options, referenceOptions, path) {
33103 for (var option in options) {
33104 if (options.hasOwnProperty(option)) {
33105 Validator.check(option, options, referenceOptions, path);
33106 }
33107 }
33108 }
33109 /**
33110 * Check every value. If the value is an object, call the parse function on that object.
33111 * @param {string} option
33112 * @param {Object} options
33113 * @param {Object} referenceOptions
33114 * @param {array} path | where to look for the actual option
33115 * @static
33116 */
33117
33118 }, {
33119 key: "check",
33120 value: function check(option, options, referenceOptions, path) {
33121 if (referenceOptions[option] === undefined && referenceOptions.__any__ === undefined) {
33122 Validator.getSuggestion(option, referenceOptions, path);
33123 return;
33124 }
33125
33126 var referenceOption = option;
33127 var is_object = true;
33128
33129 if (referenceOptions[option] === undefined && referenceOptions.__any__ !== undefined) {
33130 // NOTE: This only triggers if the __any__ is in the top level of the options object.
33131 // THAT'S A REALLY BAD PLACE TO ALLOW IT!!!!
33132 // TODO: Examine if needed, remove if possible
33133 // __any__ is a wildcard. Any value is accepted and will be further analysed by reference.
33134 referenceOption = '__any__'; // if the any-subgroup is not a predefined object in the configurator,
33135 // we do not look deeper into the object.
33136
33137 is_object = Validator.getType(options[option]) === 'object';
33138 }
33139
33140 var refOptionObj = referenceOptions[referenceOption];
33141
33142 if (is_object && refOptionObj.__type__ !== undefined) {
33143 refOptionObj = refOptionObj.__type__;
33144 }
33145
33146 Validator.checkFields(option, options, referenceOptions, referenceOption, refOptionObj, path);
33147 }
33148 /**
33149 *
33150 * @param {string} option | the option property
33151 * @param {Object} options | The supplied options object
33152 * @param {Object} referenceOptions | The reference options containing all options and their allowed formats
33153 * @param {string} referenceOption | Usually this is the same as option, except when handling an __any__ tag.
33154 * @param {string} refOptionObj | This is the type object from the reference options
33155 * @param {Array} path | where in the object is the option
33156 * @static
33157 */
33158
33159 }, {
33160 key: "checkFields",
33161 value: function checkFields(option, options, referenceOptions, referenceOption, refOptionObj, path) {
33162 var log = function log(message) {
33163 console.log('%c' + message + Validator.printLocation(path, option), printStyle);
33164 };
33165
33166 var optionType = Validator.getType(options[option]);
33167 var refOptionType = refOptionObj[optionType];
33168
33169 if (refOptionType !== undefined) {
33170 // if the type is correct, we check if it is supposed to be one of a few select values
33171 if (Validator.getType(refOptionType) === 'array' && refOptionType.indexOf(options[option]) === -1) {
33172 log('Invalid option detected in "' + option + '".' + ' Allowed values are:' + Validator.print(refOptionType) + ' not "' + options[option] + '". ');
33173 errorFound = true;
33174 } else if (optionType === 'object' && referenceOption !== "__any__") {
33175 path = util.copyAndExtendArray(path, option);
33176 Validator.parse(options[option], referenceOptions[referenceOption], path);
33177 }
33178 } else if (refOptionObj['any'] === undefined) {
33179 // type of the field is incorrect and the field cannot be any
33180 log('Invalid type received for "' + option + '". Expected: ' + Validator.print(Object.keys(refOptionObj)) + '. Received [' + optionType + '] "' + options[option] + '"');
33181 errorFound = true;
33182 }
33183 }
33184 /**
33185 *
33186 * @param {Object|boolean|number|string|Array.<number>|Date|Node|Moment|undefined|null} object
33187 * @returns {string}
33188 * @static
33189 */
33190
33191 }, {
33192 key: "getType",
33193 value: function getType(object) {
33194 var type = _typeof$1(object);
33195
33196 if (type === 'object') {
33197 if (object === null) {
33198 return 'null';
33199 }
33200
33201 if (object instanceof Boolean) {
33202 return 'boolean';
33203 }
33204
33205 if (object instanceof Number) {
33206 return 'number';
33207 }
33208
33209 if (object instanceof String) {
33210 return 'string';
33211 }
33212
33213 if (Array.isArray(object)) {
33214 return 'array';
33215 }
33216
33217 if (object instanceof Date) {
33218 return 'date';
33219 }
33220
33221 if (object.nodeType !== undefined) {
33222 return 'dom';
33223 }
33224
33225 if (object._isAMomentObject === true) {
33226 return 'moment';
33227 }
33228
33229 return 'object';
33230 } else if (type === 'number') {
33231 return 'number';
33232 } else if (type === 'boolean') {
33233 return 'boolean';
33234 } else if (type === 'string') {
33235 return 'string';
33236 } else if (type === undefined) {
33237 return 'undefined';
33238 }
33239
33240 return type;
33241 }
33242 /**
33243 * @param {string} option
33244 * @param {Object} options
33245 * @param {Array.<string>} path
33246 * @static
33247 */
33248
33249 }, {
33250 key: "getSuggestion",
33251 value: function getSuggestion(option, options, path) {
33252 var localSearch = Validator.findInOptions(option, options, path, false);
33253 var globalSearch = Validator.findInOptions(option, allOptions, [], true);
33254 var localSearchThreshold = 8;
33255 var globalSearchThreshold = 4;
33256 var msg;
33257
33258 if (localSearch.indexMatch !== undefined) {
33259 msg = ' in ' + Validator.printLocation(localSearch.path, option, '') + 'Perhaps it was incomplete? Did you mean: "' + localSearch.indexMatch + '"?\n\n';
33260 } else if (globalSearch.distance <= globalSearchThreshold && localSearch.distance > globalSearch.distance) {
33261 msg = ' in ' + Validator.printLocation(localSearch.path, option, '') + 'Perhaps it was misplaced? Matching option found at: ' + Validator.printLocation(globalSearch.path, globalSearch.closestMatch, '');
33262 } else if (localSearch.distance <= localSearchThreshold) {
33263 msg = '. Did you mean "' + localSearch.closestMatch + '"?' + Validator.printLocation(localSearch.path, option);
33264 } else {
33265 msg = '. Did you mean one of these: ' + Validator.print(Object.keys(options)) + Validator.printLocation(path, option);
33266 }
33267
33268 console.log('%cUnknown option detected: "' + option + '"' + msg, printStyle);
33269 errorFound = true;
33270 }
33271 /**
33272 * traverse the options in search for a match.
33273 * @param {string} option
33274 * @param {Object} options
33275 * @param {Array} path | where to look for the actual option
33276 * @param {boolean} [recursive=false]
33277 * @returns {{closestMatch: string, path: Array, distance: number}}
33278 * @static
33279 */
33280
33281 }, {
33282 key: "findInOptions",
33283 value: function findInOptions(option, options, path) {
33284 var recursive = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false;
33285 var min = 1e9;
33286 var closestMatch = '';
33287 var closestMatchPath = [];
33288 var lowerCaseOption = option.toLowerCase();
33289 var indexMatch = undefined;
33290
33291 for (var op in options) {
33292 // eslint-disable-line guard-for-in
33293 var distance = void 0;
33294
33295 if (options[op].__type__ !== undefined && recursive === true) {
33296 var result = Validator.findInOptions(option, options[op], util.copyAndExtendArray(path, op));
33297
33298 if (min > result.distance) {
33299 closestMatch = result.closestMatch;
33300 closestMatchPath = result.path;
33301 min = result.distance;
33302 indexMatch = result.indexMatch;
33303 }
33304 } else {
33305 if (op.toLowerCase().indexOf(lowerCaseOption) !== -1) {
33306 indexMatch = op;
33307 }
33308
33309 distance = Validator.levenshteinDistance(option, op);
33310
33311 if (min > distance) {
33312 closestMatch = op;
33313 closestMatchPath = util.copyArray(path);
33314 min = distance;
33315 }
33316 }
33317 }
33318
33319 return {
33320 closestMatch: closestMatch,
33321 path: closestMatchPath,
33322 distance: min,
33323 indexMatch: indexMatch
33324 };
33325 }
33326 /**
33327 * @param {Array.<string>} path
33328 * @param {Object} option
33329 * @param {string} prefix
33330 * @returns {String}
33331 * @static
33332 */
33333
33334 }, {
33335 key: "printLocation",
33336 value: function printLocation(path, option) {
33337 var prefix = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 'Problem value found at: \n';
33338 var str = '\n\n' + prefix + 'options = {\n';
33339
33340 for (var i = 0; i < path.length; i++) {
33341 for (var j = 0; j < i + 1; j++) {
33342 str += ' ';
33343 }
33344
33345 str += path[i] + ': {\n';
33346 }
33347
33348 for (var _j = 0; _j < path.length + 1; _j++) {
33349 str += ' ';
33350 }
33351
33352 str += option + '\n';
33353
33354 for (var _i = 0; _i < path.length + 1; _i++) {
33355 for (var _j2 = 0; _j2 < path.length - _i; _j2++) {
33356 str += ' ';
33357 }
33358
33359 str += '}\n';
33360 }
33361
33362 return str + '\n\n';
33363 }
33364 /**
33365 * @param {Object} options
33366 * @returns {String}
33367 * @static
33368 */
33369
33370 }, {
33371 key: "print",
33372 value: function print(options) {
33373 return JSON.stringify(options).replace(/(\")|(\[)|(\])|(,"__type__")/g, "").replace(/(\,)/g, ', ');
33374 }
33375 /**
33376 * Compute the edit distance between the two given strings
33377 * http://en.wikibooks.org/wiki/Algorithm_Implementation/Strings/Levenshtein_distance#JavaScript
33378 *
33379 * Copyright (c) 2011 Andrei Mackenzie
33380 *
33381 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
33382 *
33383 * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
33384 *
33385 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
33386 *
33387 * @param {string} a
33388 * @param {string} b
33389 * @returns {Array.<Array.<number>>}}
33390 * @static
33391 */
33392
33393 }, {
33394 key: "levenshteinDistance",
33395 value: function levenshteinDistance(a, b) {
33396 if (a.length === 0) return b.length;
33397 if (b.length === 0) return a.length;
33398 var matrix = []; // increment along the first column of each row
33399
33400 var i;
33401
33402 for (i = 0; i <= b.length; i++) {
33403 matrix[i] = [i];
33404 } // increment each column in the first row
33405
33406
33407 var j;
33408
33409 for (j = 0; j <= a.length; j++) {
33410 matrix[0][j] = j;
33411 } // Fill in the rest of the matrix
33412
33413
33414 for (i = 1; i <= b.length; i++) {
33415 for (j = 1; j <= a.length; j++) {
33416 if (b.charAt(i - 1) == a.charAt(j - 1)) {
33417 matrix[i][j] = matrix[i - 1][j - 1];
33418 } else {
33419 matrix[i][j] = Math.min(matrix[i - 1][j - 1] + 1, // substitution
33420 Math.min(matrix[i][j - 1] + 1, // insertion
33421 matrix[i - 1][j] + 1)); // deletion
33422 }
33423 }
33424 }
33425
33426 return matrix[b.length][a.length];
33427 }
33428 }]);
33429
33430 return Validator;
33431}();
33432
33433var Validator$1 = /*#__PURE__*/Object.freeze({
33434 Validator: Validator,
33435 printStyle: printStyle
33436});
33437
33438/**
33439 * This object contains all possible options. It will check if the types are correct, if required if the option is one
33440 * of the allowed values.
33441 *
33442 * __any__ means that the name of the property does not matter.
33443 * __type__ is a required field for all objects and contains the allowed types of all objects
33444 */
33445var string = 'string';
33446var bool = 'boolean';
33447var number = 'number';
33448var array = 'array';
33449var date = 'date';
33450var object = 'object'; // should only be in a __type__ property
33451
33452var dom = 'dom';
33453var moment$4 = 'moment';
33454var any = 'any';
33455var allOptions$1 = {
33456 configure: {
33457 enabled: {
33458 'boolean': bool
33459 },
33460 filter: {
33461 'boolean': bool,
33462 'function': 'function'
33463 },
33464 container: {
33465 dom: dom
33466 },
33467 __type__: {
33468 object: object,
33469 'boolean': bool,
33470 'function': 'function'
33471 }
33472 },
33473 //globals :
33474 align: {
33475 string: string
33476 },
33477 alignCurrentTime: {
33478 string: string,
33479 'undefined': 'undefined'
33480 },
33481 rtl: {
33482 'boolean': bool,
33483 'undefined': 'undefined'
33484 },
33485 rollingMode: {
33486 follow: {
33487 'boolean': bool
33488 },
33489 offset: {
33490 number: number,
33491 'undefined': 'undefined'
33492 },
33493 __type__: {
33494 object: object
33495 }
33496 },
33497 onTimeout: {
33498 timeoutMs: {
33499 number: number
33500 },
33501 callback: {
33502 'function': 'function'
33503 },
33504 __type__: {
33505 object: object
33506 }
33507 },
33508 verticalScroll: {
33509 'boolean': bool,
33510 'undefined': 'undefined'
33511 },
33512 horizontalScroll: {
33513 'boolean': bool,
33514 'undefined': 'undefined'
33515 },
33516 autoResize: {
33517 'boolean': bool
33518 },
33519 throttleRedraw: {
33520 number: number
33521 },
33522 // TODO: DEPRICATED see https://github.com/almende/vis/issues/2511
33523 clickToUse: {
33524 'boolean': bool
33525 },
33526 dataAttributes: {
33527 string: string,
33528 array: array
33529 },
33530 editable: {
33531 add: {
33532 'boolean': bool,
33533 'undefined': 'undefined'
33534 },
33535 remove: {
33536 'boolean': bool,
33537 'undefined': 'undefined'
33538 },
33539 updateGroup: {
33540 'boolean': bool,
33541 'undefined': 'undefined'
33542 },
33543 updateTime: {
33544 'boolean': bool,
33545 'undefined': 'undefined'
33546 },
33547 overrideItems: {
33548 'boolean': bool,
33549 'undefined': 'undefined'
33550 },
33551 __type__: {
33552 'boolean': bool,
33553 object: object
33554 }
33555 },
33556 end: {
33557 number: number,
33558 date: date,
33559 string: string,
33560 moment: moment$4
33561 },
33562 format: {
33563 minorLabels: {
33564 millisecond: {
33565 string: string,
33566 'undefined': 'undefined'
33567 },
33568 second: {
33569 string: string,
33570 'undefined': 'undefined'
33571 },
33572 minute: {
33573 string: string,
33574 'undefined': 'undefined'
33575 },
33576 hour: {
33577 string: string,
33578 'undefined': 'undefined'
33579 },
33580 weekday: {
33581 string: string,
33582 'undefined': 'undefined'
33583 },
33584 day: {
33585 string: string,
33586 'undefined': 'undefined'
33587 },
33588 week: {
33589 string: string,
33590 'undefined': 'undefined'
33591 },
33592 month: {
33593 string: string,
33594 'undefined': 'undefined'
33595 },
33596 quarter: {
33597 string: string,
33598 'undefined': 'undefined'
33599 },
33600 year: {
33601 string: string,
33602 'undefined': 'undefined'
33603 },
33604 __type__: {
33605 object: object,
33606 'function': 'function'
33607 }
33608 },
33609 majorLabels: {
33610 millisecond: {
33611 string: string,
33612 'undefined': 'undefined'
33613 },
33614 second: {
33615 string: string,
33616 'undefined': 'undefined'
33617 },
33618 minute: {
33619 string: string,
33620 'undefined': 'undefined'
33621 },
33622 hour: {
33623 string: string,
33624 'undefined': 'undefined'
33625 },
33626 weekday: {
33627 string: string,
33628 'undefined': 'undefined'
33629 },
33630 day: {
33631 string: string,
33632 'undefined': 'undefined'
33633 },
33634 week: {
33635 string: string,
33636 'undefined': 'undefined'
33637 },
33638 month: {
33639 string: string,
33640 'undefined': 'undefined'
33641 },
33642 quarter: {
33643 string: string,
33644 'undefined': 'undefined'
33645 },
33646 year: {
33647 string: string,
33648 'undefined': 'undefined'
33649 },
33650 __type__: {
33651 object: object,
33652 'function': 'function'
33653 }
33654 },
33655 __type__: {
33656 object: object
33657 }
33658 },
33659 moment: {
33660 'function': 'function'
33661 },
33662 groupHeightMode: {
33663 string: string
33664 },
33665 groupLabelDirection: {
33666 string: string
33667 },
33668 groupOrder: {
33669 string: string,
33670 'function': 'function'
33671 },
33672 groupEditable: {
33673 add: {
33674 'boolean': bool,
33675 'undefined': 'undefined'
33676 },
33677 remove: {
33678 'boolean': bool,
33679 'undefined': 'undefined'
33680 },
33681 order: {
33682 'boolean': bool,
33683 'undefined': 'undefined'
33684 },
33685 __type__: {
33686 'boolean': bool,
33687 object: object
33688 }
33689 },
33690 groupOrderSwap: {
33691 'function': 'function'
33692 },
33693 height: {
33694 string: string,
33695 number: number
33696 },
33697 hiddenDates: {
33698 start: {
33699 date: date,
33700 number: number,
33701 string: string,
33702 moment: moment$4
33703 },
33704 end: {
33705 date: date,
33706 number: number,
33707 string: string,
33708 moment: moment$4
33709 },
33710 repeat: {
33711 string: string
33712 },
33713 __type__: {
33714 object: object,
33715 array: array
33716 }
33717 },
33718 itemsAlwaysDraggable: {
33719 item: {
33720 'boolean': bool,
33721 'undefined': 'undefined'
33722 },
33723 range: {
33724 'boolean': bool,
33725 'undefined': 'undefined'
33726 },
33727 __type__: {
33728 'boolean': bool,
33729 object: object
33730 }
33731 },
33732 limitSize: {
33733 'boolean': bool
33734 },
33735 locale: {
33736 string: string
33737 },
33738 locales: {
33739 __any__: {
33740 any: any
33741 },
33742 __type__: {
33743 object: object
33744 }
33745 },
33746 margin: {
33747 axis: {
33748 number: number
33749 },
33750 item: {
33751 horizontal: {
33752 number: number,
33753 'undefined': 'undefined'
33754 },
33755 vertical: {
33756 number: number,
33757 'undefined': 'undefined'
33758 },
33759 __type__: {
33760 object: object,
33761 number: number
33762 }
33763 },
33764 __type__: {
33765 object: object,
33766 number: number
33767 }
33768 },
33769 max: {
33770 date: date,
33771 number: number,
33772 string: string,
33773 moment: moment$4
33774 },
33775 maxHeight: {
33776 number: number,
33777 string: string
33778 },
33779 maxMinorChars: {
33780 number: number
33781 },
33782 min: {
33783 date: date,
33784 number: number,
33785 string: string,
33786 moment: moment$4
33787 },
33788 minHeight: {
33789 number: number,
33790 string: string
33791 },
33792 moveable: {
33793 'boolean': bool
33794 },
33795 multiselect: {
33796 'boolean': bool
33797 },
33798 multiselectPerGroup: {
33799 'boolean': bool
33800 },
33801 onAdd: {
33802 'function': 'function'
33803 },
33804 onDropObjectOnItem: {
33805 'function': 'function'
33806 },
33807 onUpdate: {
33808 'function': 'function'
33809 },
33810 onMove: {
33811 'function': 'function'
33812 },
33813 onMoving: {
33814 'function': 'function'
33815 },
33816 onRemove: {
33817 'function': 'function'
33818 },
33819 onAddGroup: {
33820 'function': 'function'
33821 },
33822 onMoveGroup: {
33823 'function': 'function'
33824 },
33825 onRemoveGroup: {
33826 'function': 'function'
33827 },
33828 onInitialDrawComplete: {
33829 'function': 'function'
33830 },
33831 order: {
33832 'function': 'function'
33833 },
33834 orientation: {
33835 axis: {
33836 string: string,
33837 'undefined': 'undefined'
33838 },
33839 item: {
33840 string: string,
33841 'undefined': 'undefined'
33842 },
33843 __type__: {
33844 string: string,
33845 object: object
33846 }
33847 },
33848 selectable: {
33849 'boolean': bool
33850 },
33851 showCurrentTime: {
33852 'boolean': bool
33853 },
33854 showMajorLabels: {
33855 'boolean': bool
33856 },
33857 showMinorLabels: {
33858 'boolean': bool
33859 },
33860 stack: {
33861 'boolean': bool
33862 },
33863 stackSubgroups: {
33864 'boolean': bool
33865 },
33866 snap: {
33867 'function': 'function',
33868 'null': 'null'
33869 },
33870 start: {
33871 date: date,
33872 number: number,
33873 string: string,
33874 moment: moment$4
33875 },
33876 template: {
33877 'function': 'function'
33878 },
33879 loadingScreenTemplate: {
33880 'function': 'function'
33881 },
33882 groupTemplate: {
33883 'function': 'function'
33884 },
33885 visibleFrameTemplate: {
33886 string: string,
33887 'function': 'function'
33888 },
33889 showTooltips: {
33890 'boolean': bool
33891 },
33892 tooltip: {
33893 followMouse: {
33894 'boolean': bool
33895 },
33896 overflowMethod: {
33897 'string': ['cap', 'flip']
33898 },
33899 __type__: {
33900 object: object
33901 }
33902 },
33903 tooltipOnItemUpdateTime: {
33904 template: {
33905 'function': 'function'
33906 },
33907 __type__: {
33908 'boolean': bool,
33909 object: object
33910 }
33911 },
33912 timeAxis: {
33913 scale: {
33914 string: string,
33915 'undefined': 'undefined'
33916 },
33917 step: {
33918 number: number,
33919 'undefined': 'undefined'
33920 },
33921 __type__: {
33922 object: object
33923 }
33924 },
33925 type: {
33926 string: string
33927 },
33928 width: {
33929 string: string,
33930 number: number
33931 },
33932 zoomable: {
33933 'boolean': bool
33934 },
33935 zoomKey: {
33936 string: ['ctrlKey', 'altKey', 'metaKey', '']
33937 },
33938 zoomMax: {
33939 number: number
33940 },
33941 zoomMin: {
33942 number: number
33943 },
33944 __type__: {
33945 object: object
33946 }
33947};
33948var configureOptions = {
33949 global: {
33950 align: ['center', 'left', 'right'],
33951 alignCurrentTime: ['none', 'year', 'month', 'quarter', 'week', 'isoWeek', 'day', 'date', 'hour', 'minute', 'second'],
33952 direction: false,
33953 autoResize: true,
33954 clickToUse: false,
33955 // dataAttributes: ['all'], // FIXME: can be 'all' or string[]
33956 editable: {
33957 add: false,
33958 remove: false,
33959 updateGroup: false,
33960 updateTime: false
33961 },
33962 end: '',
33963 format: {
33964 minorLabels: {
33965 millisecond: 'SSS',
33966 second: 's',
33967 minute: 'HH:mm',
33968 hour: 'HH:mm',
33969 weekday: 'ddd D',
33970 day: 'D',
33971 week: 'w',
33972 month: 'MMM',
33973 quarter: '[Q]Q',
33974 year: 'YYYY'
33975 },
33976 majorLabels: {
33977 millisecond: 'HH:mm:ss',
33978 second: 'D MMMM HH:mm',
33979 minute: 'ddd D MMMM',
33980 hour: 'ddd D MMMM',
33981 weekday: 'MMMM YYYY',
33982 day: 'MMMM YYYY',
33983 week: 'MMMM YYYY',
33984 month: 'YYYY',
33985 quarter: 'YYYY',
33986 year: ''
33987 }
33988 },
33989 groupHeightMode: ['auto', 'fixed'],
33990 groupLabelDirection: ['horizontal', 'vertical'],
33991 //groupOrder: {string, 'function': 'function'},
33992 groupsDraggable: false,
33993 height: '',
33994 //hiddenDates: {object, array},
33995 locale: '',
33996 margin: {
33997 axis: [20, 0, 100, 1],
33998 item: {
33999 horizontal: [10, 0, 100, 1],
34000 vertical: [10, 0, 100, 1]
34001 }
34002 },
34003 max: '',
34004 maxHeight: '',
34005 maxMinorChars: [7, 0, 20, 1],
34006 min: '',
34007 minHeight: '',
34008 moveable: false,
34009 multiselect: false,
34010 multiselectPerGroup: false,
34011 //onAdd: {'function': 'function'},
34012 //onUpdate: {'function': 'function'},
34013 //onMove: {'function': 'function'},
34014 //onMoving: {'function': 'function'},
34015 //onRename: {'function': 'function'},
34016 //order: {'function': 'function'},
34017 orientation: {
34018 axis: ['both', 'bottom', 'top'],
34019 item: ['bottom', 'top']
34020 },
34021 selectable: true,
34022 showCurrentTime: false,
34023 showMajorLabels: true,
34024 showMinorLabels: true,
34025 stack: true,
34026 stackSubgroups: true,
34027 //snap: {'function': 'function', nada},
34028 start: '',
34029 //template: {'function': 'function'},
34030 //timeAxis: {
34031 // scale: ['millisecond', 'second', 'minute', 'hour', 'weekday', 'day', 'week', 'month', 'quarter', 'year'],
34032 // step: [1, 1, 10, 1]
34033 //},
34034 showTooltips: true,
34035 tooltip: {
34036 followMouse: false,
34037 overflowMethod: 'flip'
34038 },
34039 tooltipOnItemUpdateTime: false,
34040 type: ['box', 'point', 'range', 'background'],
34041 width: '100%',
34042 zoomable: true,
34043 zoomKey: ['ctrlKey', 'altKey', 'metaKey', ''],
34044 zoomMax: [315360000000000, 10, 315360000000000, 1],
34045 zoomMin: [10, 10, 315360000000000, 1]
34046 }
34047};
34048
34049var optionsTimeline = /*#__PURE__*/Object.freeze({
34050 allOptions: allOptions$1,
34051 configureOptions: configureOptions
34052});
34053
34054var htmlColors = {
34055 black: '#000000',
34056 navy: '#000080',
34057 darkblue: '#00008B',
34058 mediumblue: '#0000CD',
34059 blue: '#0000FF',
34060 darkgreen: '#006400',
34061 green: '#008000',
34062 teal: '#008080',
34063 darkcyan: '#008B8B',
34064 deepskyblue: '#00BFFF',
34065 darkturquoise: '#00CED1',
34066 mediumspringgreen: '#00FA9A',
34067 lime: '#00FF00',
34068 springgreen: '#00FF7F',
34069 aqua: '#00FFFF',
34070 cyan: '#00FFFF',
34071 midnightblue: '#191970',
34072 dodgerblue: '#1E90FF',
34073 lightseagreen: '#20B2AA',
34074 forestgreen: '#228B22',
34075 seagreen: '#2E8B57',
34076 darkslategray: '#2F4F4F',
34077 limegreen: '#32CD32',
34078 mediumseagreen: '#3CB371',
34079 turquoise: '#40E0D0',
34080 royalblue: '#4169E1',
34081 steelblue: '#4682B4',
34082 darkslateblue: '#483D8B',
34083 mediumturquoise: '#48D1CC',
34084 indigo: '#4B0082',
34085 darkolivegreen: '#556B2F',
34086 cadetblue: '#5F9EA0',
34087 cornflowerblue: '#6495ED',
34088 mediumaquamarine: '#66CDAA',
34089 dimgray: '#696969',
34090 slateblue: '#6A5ACD',
34091 olivedrab: '#6B8E23',
34092 slategray: '#708090',
34093 lightslategray: '#778899',
34094 mediumslateblue: '#7B68EE',
34095 lawngreen: '#7CFC00',
34096 chartreuse: '#7FFF00',
34097 aquamarine: '#7FFFD4',
34098 maroon: '#800000',
34099 purple: '#800080',
34100 olive: '#808000',
34101 gray: '#808080',
34102 skyblue: '#87CEEB',
34103 lightskyblue: '#87CEFA',
34104 blueviolet: '#8A2BE2',
34105 darkred: '#8B0000',
34106 darkmagenta: '#8B008B',
34107 saddlebrown: '#8B4513',
34108 darkseagreen: '#8FBC8F',
34109 lightgreen: '#90EE90',
34110 mediumpurple: '#9370D8',
34111 darkviolet: '#9400D3',
34112 palegreen: '#98FB98',
34113 darkorchid: '#9932CC',
34114 yellowgreen: '#9ACD32',
34115 sienna: '#A0522D',
34116 brown: '#A52A2A',
34117 darkgray: '#A9A9A9',
34118 lightblue: '#ADD8E6',
34119 greenyellow: '#ADFF2F',
34120 paleturquoise: '#AFEEEE',
34121 lightsteelblue: '#B0C4DE',
34122 powderblue: '#B0E0E6',
34123 firebrick: '#B22222',
34124 darkgoldenrod: '#B8860B',
34125 mediumorchid: '#BA55D3',
34126 rosybrown: '#BC8F8F',
34127 darkkhaki: '#BDB76B',
34128 silver: '#C0C0C0',
34129 mediumvioletred: '#C71585',
34130 indianred: '#CD5C5C',
34131 peru: '#CD853F',
34132 chocolate: '#D2691E',
34133 tan: '#D2B48C',
34134 lightgrey: '#D3D3D3',
34135 palevioletred: '#D87093',
34136 thistle: '#D8BFD8',
34137 orchid: '#DA70D6',
34138 goldenrod: '#DAA520',
34139 crimson: '#DC143C',
34140 gainsboro: '#DCDCDC',
34141 plum: '#DDA0DD',
34142 burlywood: '#DEB887',
34143 lightcyan: '#E0FFFF',
34144 lavender: '#E6E6FA',
34145 darksalmon: '#E9967A',
34146 violet: '#EE82EE',
34147 palegoldenrod: '#EEE8AA',
34148 lightcoral: '#F08080',
34149 khaki: '#F0E68C',
34150 aliceblue: '#F0F8FF',
34151 honeydew: '#F0FFF0',
34152 azure: '#F0FFFF',
34153 sandybrown: '#F4A460',
34154 wheat: '#F5DEB3',
34155 beige: '#F5F5DC',
34156 whitesmoke: '#F5F5F5',
34157 mintcream: '#F5FFFA',
34158 ghostwhite: '#F8F8FF',
34159 salmon: '#FA8072',
34160 antiquewhite: '#FAEBD7',
34161 linen: '#FAF0E6',
34162 lightgoldenrodyellow: '#FAFAD2',
34163 oldlace: '#FDF5E6',
34164 red: '#FF0000',
34165 fuchsia: '#FF00FF',
34166 magenta: '#FF00FF',
34167 deeppink: '#FF1493',
34168 orangered: '#FF4500',
34169 tomato: '#FF6347',
34170 hotpink: '#FF69B4',
34171 coral: '#FF7F50',
34172 darkorange: '#FF8C00',
34173 lightsalmon: '#FFA07A',
34174 orange: '#FFA500',
34175 lightpink: '#FFB6C1',
34176 pink: '#FFC0CB',
34177 gold: '#FFD700',
34178 peachpuff: '#FFDAB9',
34179 navajowhite: '#FFDEAD',
34180 moccasin: '#FFE4B5',
34181 bisque: '#FFE4C4',
34182 mistyrose: '#FFE4E1',
34183 blanchedalmond: '#FFEBCD',
34184 papayawhip: '#FFEFD5',
34185 lavenderblush: '#FFF0F5',
34186 seashell: '#FFF5EE',
34187 cornsilk: '#FFF8DC',
34188 lemonchiffon: '#FFFACD',
34189 floralwhite: '#FFFAF0',
34190 snow: '#FFFAFA',
34191 yellow: '#FFFF00',
34192 lightyellow: '#FFFFE0',
34193 ivory: '#FFFFF0',
34194 white: '#FFFFFF'
34195};
34196/**
34197 * @param {number} [pixelRatio=1]
34198 */
34199
34200var ColorPicker =
34201/*#__PURE__*/
34202function () {
34203 /**
34204 * @param {number} [pixelRatio=1]
34205 */
34206 function ColorPicker() {
34207 var pixelRatio = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 1;
34208
34209 _classCallCheck$1(this, ColorPicker);
34210
34211 this.pixelRatio = pixelRatio;
34212 this.generated = false;
34213 this.centerCoordinates = {
34214 x: 289 / 2,
34215 y: 289 / 2
34216 };
34217 this.r = 289 * 0.49;
34218 this.color = {
34219 r: 255,
34220 g: 255,
34221 b: 255,
34222 a: 1.0
34223 };
34224 this.hueCircle = undefined;
34225 this.initialColor = {
34226 r: 255,
34227 g: 255,
34228 b: 255,
34229 a: 1.0
34230 };
34231 this.previousColor = undefined;
34232 this.applied = false; // bound by
34233
34234 this.updateCallback = function () {};
34235
34236 this.closeCallback = function () {}; // create all DOM elements
34237
34238
34239 this._create();
34240 }
34241 /**
34242 * this inserts the colorPicker into a div from the DOM
34243 * @param {Element} container
34244 */
34245
34246
34247 _createClass$1(ColorPicker, [{
34248 key: "insertTo",
34249 value: function insertTo(container) {
34250 if (this.hammer !== undefined) {
34251 this.hammer.destroy();
34252 this.hammer = undefined;
34253 }
34254
34255 this.container = container;
34256 this.container.appendChild(this.frame);
34257
34258 this._bindHammer();
34259
34260 this._setSize();
34261 }
34262 /**
34263 * the callback is executed on apply and save. Bind it to the application
34264 * @param {function} callback
34265 */
34266
34267 }, {
34268 key: "setUpdateCallback",
34269 value: function setUpdateCallback(callback) {
34270 if (typeof callback === 'function') {
34271 this.updateCallback = callback;
34272 } else {
34273 throw new Error("Function attempted to set as colorPicker update callback is not a function.");
34274 }
34275 }
34276 /**
34277 * the callback is executed on apply and save. Bind it to the application
34278 * @param {function} callback
34279 */
34280
34281 }, {
34282 key: "setCloseCallback",
34283 value: function setCloseCallback(callback) {
34284 if (typeof callback === 'function') {
34285 this.closeCallback = callback;
34286 } else {
34287 throw new Error("Function attempted to set as colorPicker closing callback is not a function.");
34288 }
34289 }
34290 /**
34291 *
34292 * @param {string} color
34293 * @returns {String}
34294 * @private
34295 */
34296
34297 }, {
34298 key: "_isColorString",
34299 value: function _isColorString(color) {
34300 if (typeof color === 'string') {
34301 return htmlColors[color];
34302 }
34303 }
34304 /**
34305 * Set the color of the colorPicker
34306 * Supported formats:
34307 * 'red' --> HTML color string
34308 * '#ffffff' --> hex string
34309 * 'rgb(255,255,255)' --> rgb string
34310 * 'rgba(255,255,255,1.0)' --> rgba string
34311 * {r:255,g:255,b:255} --> rgb object
34312 * {r:255,g:255,b:255,a:1.0} --> rgba object
34313 * @param {string|Object} color
34314 * @param {boolean} [setInitial=true]
34315 */
34316
34317 }, {
34318 key: "setColor",
34319 value: function setColor(color) {
34320 var setInitial = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
34321
34322 if (color === 'none') {
34323 return;
34324 }
34325
34326 var rgba; // if a html color shorthand is used, convert to hex
34327
34328 var htmlColor = this._isColorString(color);
34329
34330 if (htmlColor !== undefined) {
34331 color = htmlColor;
34332 } // check format
34333
34334
34335 if (util.isString(color) === true) {
34336 if (util.isValidRGB(color) === true) {
34337 var rgbaArray = color.substr(4).substr(0, color.length - 5).split(',');
34338 rgba = {
34339 r: rgbaArray[0],
34340 g: rgbaArray[1],
34341 b: rgbaArray[2],
34342 a: 1.0
34343 };
34344 } else if (util.isValidRGBA(color) === true) {
34345 var _rgbaArray = color.substr(5).substr(0, color.length - 6).split(',');
34346
34347 rgba = {
34348 r: _rgbaArray[0],
34349 g: _rgbaArray[1],
34350 b: _rgbaArray[2],
34351 a: _rgbaArray[3]
34352 };
34353 } else if (util.isValidHex(color) === true) {
34354 var rgbObj = util.hexToRGB(color);
34355 rgba = {
34356 r: rgbObj.r,
34357 g: rgbObj.g,
34358 b: rgbObj.b,
34359 a: 1.0
34360 };
34361 }
34362 } else {
34363 if (color instanceof Object) {
34364 if (color.r !== undefined && color.g !== undefined && color.b !== undefined) {
34365 var alpha = color.a !== undefined ? color.a : '1.0';
34366 rgba = {
34367 r: color.r,
34368 g: color.g,
34369 b: color.b,
34370 a: alpha
34371 };
34372 }
34373 }
34374 } // set color
34375
34376
34377 if (rgba === undefined) {
34378 throw new Error("Unknown color passed to the colorPicker. Supported are strings: rgb, hex, rgba. Object: rgb ({r:r,g:g,b:b,[a:a]}). Supplied: " + JSON.stringify(color));
34379 } else {
34380 this._setColor(rgba, setInitial);
34381 }
34382 }
34383 /**
34384 * this shows the color picker.
34385 * The hue circle is constructed once and stored.
34386 */
34387
34388 }, {
34389 key: "show",
34390 value: function show() {
34391 if (this.closeCallback !== undefined) {
34392 this.closeCallback();
34393 this.closeCallback = undefined;
34394 }
34395
34396 this.applied = false;
34397 this.frame.style.display = 'block';
34398
34399 this._generateHueCircle();
34400 } // ------------------------------------------ PRIVATE ----------------------------- //
34401
34402 /**
34403 * Hide the picker. Is called by the cancel button.
34404 * Optional boolean to store the previous color for easy access later on.
34405 * @param {boolean} [storePrevious=true]
34406 * @private
34407 */
34408
34409 }, {
34410 key: "_hide",
34411 value: function _hide() {
34412 var _this = this;
34413
34414 var storePrevious = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
34415
34416 // store the previous color for next time;
34417 if (storePrevious === true) {
34418 this.previousColor = util.extend({}, this.color);
34419 }
34420
34421 if (this.applied === true) {
34422 this.updateCallback(this.initialColor);
34423 }
34424
34425 this.frame.style.display = 'none'; // call the closing callback, restoring the onclick method.
34426 // this is in a setTimeout because it will trigger the show again before the click is done.
34427
34428 setTimeout(function () {
34429 if (_this.closeCallback !== undefined) {
34430 _this.closeCallback();
34431
34432 _this.closeCallback = undefined;
34433 }
34434 }, 0);
34435 }
34436 /**
34437 * bound to the save button. Saves and hides.
34438 * @private
34439 */
34440
34441 }, {
34442 key: "_save",
34443 value: function _save() {
34444 this.updateCallback(this.color);
34445 this.applied = false;
34446
34447 this._hide();
34448 }
34449 /**
34450 * Bound to apply button. Saves but does not close. Is undone by the cancel button.
34451 * @private
34452 */
34453
34454 }, {
34455 key: "_apply",
34456 value: function _apply() {
34457 this.applied = true;
34458 this.updateCallback(this.color);
34459
34460 this._updatePicker(this.color);
34461 }
34462 /**
34463 * load the color from the previous session.
34464 * @private
34465 */
34466
34467 }, {
34468 key: "_loadLast",
34469 value: function _loadLast() {
34470 if (this.previousColor !== undefined) {
34471 this.setColor(this.previousColor, false);
34472 } else {
34473 alert("There is no last color to load...");
34474 }
34475 }
34476 /**
34477 * set the color, place the picker
34478 * @param {Object} rgba
34479 * @param {boolean} [setInitial=true]
34480 * @private
34481 */
34482
34483 }, {
34484 key: "_setColor",
34485 value: function _setColor(rgba) {
34486 var setInitial = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
34487
34488 // store the initial color
34489 if (setInitial === true) {
34490 this.initialColor = util.extend({}, rgba);
34491 }
34492
34493 this.color = rgba;
34494 var hsv = util.RGBToHSV(rgba.r, rgba.g, rgba.b);
34495 var angleConvert = 2 * Math.PI;
34496 var radius = this.r * hsv.s;
34497 var x = this.centerCoordinates.x + radius * Math.sin(angleConvert * hsv.h);
34498 var y = this.centerCoordinates.y + radius * Math.cos(angleConvert * hsv.h);
34499 this.colorPickerSelector.style.left = x - 0.5 * this.colorPickerSelector.clientWidth + 'px';
34500 this.colorPickerSelector.style.top = y - 0.5 * this.colorPickerSelector.clientHeight + 'px';
34501
34502 this._updatePicker(rgba);
34503 }
34504 /**
34505 * bound to opacity control
34506 * @param {number} value
34507 * @private
34508 */
34509
34510 }, {
34511 key: "_setOpacity",
34512 value: function _setOpacity(value) {
34513 this.color.a = value / 100;
34514
34515 this._updatePicker(this.color);
34516 }
34517 /**
34518 * bound to brightness control
34519 * @param {number} value
34520 * @private
34521 */
34522
34523 }, {
34524 key: "_setBrightness",
34525 value: function _setBrightness(value) {
34526 var hsv = util.RGBToHSV(this.color.r, this.color.g, this.color.b);
34527 hsv.v = value / 100;
34528 var rgba = util.HSVToRGB(hsv.h, hsv.s, hsv.v);
34529 rgba['a'] = this.color.a;
34530 this.color = rgba;
34531
34532 this._updatePicker();
34533 }
34534 /**
34535 * update the color picker. A black circle overlays the hue circle to mimic the brightness decreasing.
34536 * @param {Object} rgba
34537 * @private
34538 */
34539
34540 }, {
34541 key: "_updatePicker",
34542 value: function _updatePicker() {
34543 var rgba = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : this.color;
34544 var hsv = util.RGBToHSV(rgba.r, rgba.g, rgba.b);
34545 var ctx = this.colorPickerCanvas.getContext('2d');
34546
34547 if (this.pixelRation === undefined) {
34548 this.pixelRatio = (window.devicePixelRatio || 1) / (ctx.webkitBackingStorePixelRatio || ctx.mozBackingStorePixelRatio || ctx.msBackingStorePixelRatio || ctx.oBackingStorePixelRatio || ctx.backingStorePixelRatio || 1);
34549 }
34550
34551 ctx.setTransform(this.pixelRatio, 0, 0, this.pixelRatio, 0, 0); // clear the canvas
34552
34553 var w = this.colorPickerCanvas.clientWidth;
34554 var h = this.colorPickerCanvas.clientHeight;
34555 ctx.clearRect(0, 0, w, h);
34556 ctx.putImageData(this.hueCircle, 0, 0);
34557 ctx.fillStyle = 'rgba(0,0,0,' + (1 - hsv.v) + ')';
34558 ctx.circle(this.centerCoordinates.x, this.centerCoordinates.y, this.r);
34559 ctx.fill();
34560 this.brightnessRange.value = 100 * hsv.v;
34561 this.opacityRange.value = 100 * rgba.a;
34562 this.initialColorDiv.style.backgroundColor = 'rgba(' + this.initialColor.r + ',' + this.initialColor.g + ',' + this.initialColor.b + ',' + this.initialColor.a + ')';
34563 this.newColorDiv.style.backgroundColor = 'rgba(' + this.color.r + ',' + this.color.g + ',' + this.color.b + ',' + this.color.a + ')';
34564 }
34565 /**
34566 * used by create to set the size of the canvas.
34567 * @private
34568 */
34569
34570 }, {
34571 key: "_setSize",
34572 value: function _setSize() {
34573 this.colorPickerCanvas.style.width = '100%';
34574 this.colorPickerCanvas.style.height = '100%';
34575 this.colorPickerCanvas.width = 289 * this.pixelRatio;
34576 this.colorPickerCanvas.height = 289 * this.pixelRatio;
34577 }
34578 /**
34579 * create all dom elements
34580 * TODO: cleanup, lots of similar dom elements
34581 * @private
34582 */
34583
34584 }, {
34585 key: "_create",
34586 value: function _create() {
34587 this.frame = document.createElement('div');
34588 this.frame.className = 'vis-color-picker';
34589 this.colorPickerDiv = document.createElement('div');
34590 this.colorPickerSelector = document.createElement('div');
34591 this.colorPickerSelector.className = 'vis-selector';
34592 this.colorPickerDiv.appendChild(this.colorPickerSelector);
34593 this.colorPickerCanvas = document.createElement('canvas');
34594 this.colorPickerDiv.appendChild(this.colorPickerCanvas);
34595
34596 if (!this.colorPickerCanvas.getContext) {
34597 var noCanvas = document.createElement('DIV');
34598 noCanvas.style.color = 'red';
34599 noCanvas.style.fontWeight = 'bold';
34600 noCanvas.style.padding = '10px';
34601 noCanvas.innerHTML = 'Error: your browser does not support HTML canvas';
34602 this.colorPickerCanvas.appendChild(noCanvas);
34603 } else {
34604 var ctx = this.colorPickerCanvas.getContext("2d");
34605 this.pixelRatio = (window.devicePixelRatio || 1) / (ctx.webkitBackingStorePixelRatio || ctx.mozBackingStorePixelRatio || ctx.msBackingStorePixelRatio || ctx.oBackingStorePixelRatio || ctx.backingStorePixelRatio || 1);
34606 this.colorPickerCanvas.getContext("2d").setTransform(this.pixelRatio, 0, 0, this.pixelRatio, 0, 0);
34607 }
34608
34609 this.colorPickerDiv.className = 'vis-color';
34610 this.opacityDiv = document.createElement('div');
34611 this.opacityDiv.className = 'vis-opacity';
34612 this.brightnessDiv = document.createElement('div');
34613 this.brightnessDiv.className = 'vis-brightness';
34614 this.arrowDiv = document.createElement('div');
34615 this.arrowDiv.className = 'vis-arrow';
34616 this.opacityRange = document.createElement('input');
34617
34618 try {
34619 this.opacityRange.type = 'range'; // Not supported on IE9
34620
34621 this.opacityRange.min = '0';
34622 this.opacityRange.max = '100';
34623 } // TODO: Add some error handling and remove this lint exception
34624 catch (err) {} // eslint-disable-line no-empty
34625
34626
34627 this.opacityRange.value = '100';
34628 this.opacityRange.className = 'vis-range';
34629 this.brightnessRange = document.createElement('input');
34630
34631 try {
34632 this.brightnessRange.type = 'range'; // Not supported on IE9
34633
34634 this.brightnessRange.min = '0';
34635 this.brightnessRange.max = '100';
34636 } // TODO: Add some error handling and remove this lint exception
34637 catch (err) {} // eslint-disable-line no-empty
34638
34639
34640 this.brightnessRange.value = '100';
34641 this.brightnessRange.className = 'vis-range';
34642 this.opacityDiv.appendChild(this.opacityRange);
34643 this.brightnessDiv.appendChild(this.brightnessRange);
34644 var me = this;
34645
34646 this.opacityRange.onchange = function () {
34647 me._setOpacity(this.value);
34648 };
34649
34650 this.opacityRange.oninput = function () {
34651 me._setOpacity(this.value);
34652 };
34653
34654 this.brightnessRange.onchange = function () {
34655 me._setBrightness(this.value);
34656 };
34657
34658 this.brightnessRange.oninput = function () {
34659 me._setBrightness(this.value);
34660 };
34661
34662 this.brightnessLabel = document.createElement("div");
34663 this.brightnessLabel.className = "vis-label vis-brightness";
34664 this.brightnessLabel.innerHTML = 'brightness:';
34665 this.opacityLabel = document.createElement("div");
34666 this.opacityLabel.className = "vis-label vis-opacity";
34667 this.opacityLabel.innerHTML = 'opacity:';
34668 this.newColorDiv = document.createElement("div");
34669 this.newColorDiv.className = "vis-new-color";
34670 this.newColorDiv.innerHTML = 'new';
34671 this.initialColorDiv = document.createElement("div");
34672 this.initialColorDiv.className = "vis-initial-color";
34673 this.initialColorDiv.innerHTML = 'initial';
34674 this.cancelButton = document.createElement("div");
34675 this.cancelButton.className = "vis-button vis-cancel";
34676 this.cancelButton.innerHTML = 'cancel';
34677 this.cancelButton.onclick = this._hide.bind(this, false);
34678 this.applyButton = document.createElement("div");
34679 this.applyButton.className = "vis-button vis-apply";
34680 this.applyButton.innerHTML = 'apply';
34681 this.applyButton.onclick = this._apply.bind(this);
34682 this.saveButton = document.createElement("div");
34683 this.saveButton.className = "vis-button vis-save";
34684 this.saveButton.innerHTML = 'save';
34685 this.saveButton.onclick = this._save.bind(this);
34686 this.loadButton = document.createElement("div");
34687 this.loadButton.className = "vis-button vis-load";
34688 this.loadButton.innerHTML = 'load last';
34689 this.loadButton.onclick = this._loadLast.bind(this);
34690 this.frame.appendChild(this.colorPickerDiv);
34691 this.frame.appendChild(this.arrowDiv);
34692 this.frame.appendChild(this.brightnessLabel);
34693 this.frame.appendChild(this.brightnessDiv);
34694 this.frame.appendChild(this.opacityLabel);
34695 this.frame.appendChild(this.opacityDiv);
34696 this.frame.appendChild(this.newColorDiv);
34697 this.frame.appendChild(this.initialColorDiv);
34698 this.frame.appendChild(this.cancelButton);
34699 this.frame.appendChild(this.applyButton);
34700 this.frame.appendChild(this.saveButton);
34701 this.frame.appendChild(this.loadButton);
34702 }
34703 /**
34704 * bind hammer to the color picker
34705 * @private
34706 */
34707
34708 }, {
34709 key: "_bindHammer",
34710 value: function _bindHammer() {
34711 var _this2 = this;
34712
34713 this.drag = {};
34714 this.pinch = {};
34715 this.hammer = new hammer$1(this.colorPickerCanvas);
34716 this.hammer.get('pinch').set({
34717 enable: true
34718 });
34719 hammerUtil.onTouch(this.hammer, function (event) {
34720 _this2._moveSelector(event);
34721 });
34722 this.hammer.on('tap', function (event) {
34723 _this2._moveSelector(event);
34724 });
34725 this.hammer.on('panstart', function (event) {
34726 _this2._moveSelector(event);
34727 });
34728 this.hammer.on('panmove', function (event) {
34729 _this2._moveSelector(event);
34730 });
34731 this.hammer.on('panend', function (event) {
34732 _this2._moveSelector(event);
34733 });
34734 }
34735 /**
34736 * generate the hue circle. This is relatively heavy (200ms) and is done only once on the first time it is shown.
34737 * @private
34738 */
34739
34740 }, {
34741 key: "_generateHueCircle",
34742 value: function _generateHueCircle() {
34743 if (this.generated === false) {
34744 var ctx = this.colorPickerCanvas.getContext('2d');
34745
34746 if (this.pixelRation === undefined) {
34747 this.pixelRatio = (window.devicePixelRatio || 1) / (ctx.webkitBackingStorePixelRatio || ctx.mozBackingStorePixelRatio || ctx.msBackingStorePixelRatio || ctx.oBackingStorePixelRatio || ctx.backingStorePixelRatio || 1);
34748 }
34749
34750 ctx.setTransform(this.pixelRatio, 0, 0, this.pixelRatio, 0, 0); // clear the canvas
34751
34752 var w = this.colorPickerCanvas.clientWidth;
34753 var h = this.colorPickerCanvas.clientHeight;
34754 ctx.clearRect(0, 0, w, h); // draw hue circle
34755
34756 var x, y, hue, sat;
34757 this.centerCoordinates = {
34758 x: w * 0.5,
34759 y: h * 0.5
34760 };
34761 this.r = 0.49 * w;
34762 var angleConvert = 2 * Math.PI / 360;
34763 var hfac = 1 / 360;
34764 var sfac = 1 / this.r;
34765 var rgb;
34766
34767 for (hue = 0; hue < 360; hue++) {
34768 for (sat = 0; sat < this.r; sat++) {
34769 x = this.centerCoordinates.x + sat * Math.sin(angleConvert * hue);
34770 y = this.centerCoordinates.y + sat * Math.cos(angleConvert * hue);
34771 rgb = util.HSVToRGB(hue * hfac, sat * sfac, 1);
34772 ctx.fillStyle = 'rgb(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ')';
34773 ctx.fillRect(x - 0.5, y - 0.5, 2, 2);
34774 }
34775 }
34776
34777 ctx.strokeStyle = 'rgba(0,0,0,1)';
34778 ctx.circle(this.centerCoordinates.x, this.centerCoordinates.y, this.r);
34779 ctx.stroke();
34780 this.hueCircle = ctx.getImageData(0, 0, w, h);
34781 }
34782
34783 this.generated = true;
34784 }
34785 /**
34786 * move the selector. This is called by hammer functions.
34787 *
34788 * @param {Event} event The event
34789 * @private
34790 */
34791
34792 }, {
34793 key: "_moveSelector",
34794 value: function _moveSelector(event) {
34795 var rect = this.colorPickerDiv.getBoundingClientRect();
34796 var left = event.center.x - rect.left;
34797 var top = event.center.y - rect.top;
34798 var centerY = 0.5 * this.colorPickerDiv.clientHeight;
34799 var centerX = 0.5 * this.colorPickerDiv.clientWidth;
34800 var x = left - centerX;
34801 var y = top - centerY;
34802 var angle = Math.atan2(x, y);
34803 var radius = 0.98 * Math.min(Math.sqrt(x * x + y * y), centerX);
34804 var newTop = Math.cos(angle) * radius + centerY;
34805 var newLeft = Math.sin(angle) * radius + centerX;
34806 this.colorPickerSelector.style.top = newTop - 0.5 * this.colorPickerSelector.clientHeight + 'px';
34807 this.colorPickerSelector.style.left = newLeft - 0.5 * this.colorPickerSelector.clientWidth + 'px'; // set color
34808
34809 var h = angle / (2 * Math.PI);
34810 h = h < 0 ? h + 1 : h;
34811 var s = radius / this.r;
34812 var hsv = util.RGBToHSV(this.color.r, this.color.g, this.color.b);
34813 hsv.h = h;
34814 hsv.s = s;
34815 var rgba = util.HSVToRGB(hsv.h, hsv.s, hsv.v);
34816 rgba['a'] = this.color.a;
34817 this.color = rgba; // update previews
34818
34819 this.initialColorDiv.style.backgroundColor = 'rgba(' + this.initialColor.r + ',' + this.initialColor.g + ',' + this.initialColor.b + ',' + this.initialColor.a + ')';
34820 this.newColorDiv.style.backgroundColor = 'rgba(' + this.color.r + ',' + this.color.g + ',' + this.color.b + ',' + this.color.a + ')';
34821 }
34822 }]);
34823
34824 return ColorPicker;
34825}();
34826
34827/**
34828 * The way this works is for all properties of this.possible options, you can supply the property name in any form to list the options.
34829 * Boolean options are recognised as Boolean
34830 * Number options should be written as array: [default value, min value, max value, stepsize]
34831 * Colors should be written as array: ['color', '#ffffff']
34832 * Strings with should be written as array: [option1, option2, option3, ..]
34833 *
34834 * The options are matched with their counterparts in each of the modules and the values used in the configuration are
34835 */
34836
34837var Configurator =
34838/*#__PURE__*/
34839function () {
34840 /**
34841 * @param {Object} parentModule | the location where parentModule.setOptions() can be called
34842 * @param {Object} defaultContainer | the default container of the module
34843 * @param {Object} configureOptions | the fully configured and predefined options set found in allOptions.js
34844 * @param {number} pixelRatio | canvas pixel ratio
34845 */
34846 function Configurator(parentModule, defaultContainer, configureOptions) {
34847 var pixelRatio = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 1;
34848
34849 _classCallCheck$1(this, Configurator);
34850
34851 this.parent = parentModule;
34852 this.changedOptions = [];
34853 this.container = defaultContainer;
34854 this.allowCreation = false;
34855 this.options = {};
34856 this.initialized = false;
34857 this.popupCounter = 0;
34858 this.defaultOptions = {
34859 enabled: false,
34860 filter: true,
34861 container: undefined,
34862 showButton: true
34863 };
34864 util.extend(this.options, this.defaultOptions);
34865 this.configureOptions = configureOptions;
34866 this.moduleOptions = {};
34867 this.domElements = [];
34868 this.popupDiv = {};
34869 this.popupLimit = 5;
34870 this.popupHistory = {};
34871 this.colorPicker = new ColorPicker(pixelRatio);
34872 this.wrapper = undefined;
34873 }
34874 /**
34875 * refresh all options.
34876 * Because all modules parse their options by themselves, we just use their options. We copy them here.
34877 *
34878 * @param {Object} options
34879 */
34880
34881
34882 _createClass$1(Configurator, [{
34883 key: "setOptions",
34884 value: function setOptions(options) {
34885 if (options !== undefined) {
34886 // reset the popup history because the indices may have been changed.
34887 this.popupHistory = {};
34888
34889 this._removePopup();
34890
34891 var enabled = true;
34892
34893 if (typeof options === 'string') {
34894 this.options.filter = options;
34895 } else if (options instanceof Array) {
34896 this.options.filter = options.join();
34897 } else if (_typeof$1(options) === 'object') {
34898 if (options == null) {
34899 throw new TypeError('options cannot be null');
34900 }
34901
34902 if (options.container !== undefined) {
34903 this.options.container = options.container;
34904 }
34905
34906 if (options.filter !== undefined) {
34907 this.options.filter = options.filter;
34908 }
34909
34910 if (options.showButton !== undefined) {
34911 this.options.showButton = options.showButton;
34912 }
34913
34914 if (options.enabled !== undefined) {
34915 enabled = options.enabled;
34916 }
34917 } else if (typeof options === 'boolean') {
34918 this.options.filter = true;
34919 enabled = options;
34920 } else if (typeof options === 'function') {
34921 this.options.filter = options;
34922 enabled = true;
34923 }
34924
34925 if (this.options.filter === false) {
34926 enabled = false;
34927 }
34928
34929 this.options.enabled = enabled;
34930 }
34931
34932 this._clean();
34933 }
34934 /**
34935 *
34936 * @param {Object} moduleOptions
34937 */
34938
34939 }, {
34940 key: "setModuleOptions",
34941 value: function setModuleOptions(moduleOptions) {
34942 this.moduleOptions = moduleOptions;
34943
34944 if (this.options.enabled === true) {
34945 this._clean();
34946
34947 if (this.options.container !== undefined) {
34948 this.container = this.options.container;
34949 }
34950
34951 this._create();
34952 }
34953 }
34954 /**
34955 * Create all DOM elements
34956 * @private
34957 */
34958
34959 }, {
34960 key: "_create",
34961 value: function _create() {
34962 this._clean();
34963
34964 this.changedOptions = [];
34965 var filter = this.options.filter;
34966 var counter = 0;
34967 var show = false;
34968
34969 for (var option in this.configureOptions) {
34970 if (this.configureOptions.hasOwnProperty(option)) {
34971 this.allowCreation = false;
34972 show = false;
34973
34974 if (typeof filter === 'function') {
34975 show = filter(option, []);
34976 show = show || this._handleObject(this.configureOptions[option], [option], true);
34977 } else if (filter === true || filter.indexOf(option) !== -1) {
34978 show = true;
34979 }
34980
34981 if (show !== false) {
34982 this.allowCreation = true; // linebreak between categories
34983
34984 if (counter > 0) {
34985 this._makeItem([]);
34986 } // a header for the category
34987
34988
34989 this._makeHeader(option); // get the sub options
34990
34991
34992 this._handleObject(this.configureOptions[option], [option]);
34993 }
34994
34995 counter++;
34996 }
34997 }
34998
34999 this._makeButton();
35000
35001 this._push(); //~ this.colorPicker.insertTo(this.container);
35002
35003 }
35004 /**
35005 * draw all DOM elements on the screen
35006 * @private
35007 */
35008
35009 }, {
35010 key: "_push",
35011 value: function _push() {
35012 this.wrapper = document.createElement('div');
35013 this.wrapper.className = 'vis-configuration-wrapper';
35014 this.container.appendChild(this.wrapper);
35015
35016 for (var i = 0; i < this.domElements.length; i++) {
35017 this.wrapper.appendChild(this.domElements[i]);
35018 }
35019
35020 this._showPopupIfNeeded();
35021 }
35022 /**
35023 * delete all DOM elements
35024 * @private
35025 */
35026
35027 }, {
35028 key: "_clean",
35029 value: function _clean() {
35030 for (var i = 0; i < this.domElements.length; i++) {
35031 this.wrapper.removeChild(this.domElements[i]);
35032 }
35033
35034 if (this.wrapper !== undefined) {
35035 this.container.removeChild(this.wrapper);
35036 this.wrapper = undefined;
35037 }
35038
35039 this.domElements = [];
35040
35041 this._removePopup();
35042 }
35043 /**
35044 * get the value from the actualOptions if it exists
35045 * @param {array} path | where to look for the actual option
35046 * @returns {*}
35047 * @private
35048 */
35049
35050 }, {
35051 key: "_getValue",
35052 value: function _getValue(path) {
35053 var base = this.moduleOptions;
35054
35055 for (var i = 0; i < path.length; i++) {
35056 if (base[path[i]] !== undefined) {
35057 base = base[path[i]];
35058 } else {
35059 base = undefined;
35060 break;
35061 }
35062 }
35063
35064 return base;
35065 }
35066 /**
35067 * all option elements are wrapped in an item
35068 * @param {Array} path | where to look for the actual option
35069 * @param {Array.<Element>} domElements
35070 * @returns {number}
35071 * @private
35072 */
35073
35074 }, {
35075 key: "_makeItem",
35076 value: function _makeItem(path) {
35077 if (this.allowCreation === true) {
35078 var item = document.createElement('div');
35079 item.className = 'vis-configuration vis-config-item vis-config-s' + path.length;
35080
35081 for (var _len = arguments.length, domElements = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
35082 domElements[_key - 1] = arguments[_key];
35083 }
35084
35085 domElements.forEach(function (element) {
35086 item.appendChild(element);
35087 });
35088 this.domElements.push(item);
35089 return this.domElements.length;
35090 }
35091
35092 return 0;
35093 }
35094 /**
35095 * header for major subjects
35096 * @param {string} name
35097 * @private
35098 */
35099
35100 }, {
35101 key: "_makeHeader",
35102 value: function _makeHeader(name) {
35103 var div = document.createElement('div');
35104 div.className = 'vis-configuration vis-config-header';
35105 div.innerHTML = name;
35106
35107 this._makeItem([], div);
35108 }
35109 /**
35110 * make a label, if it is an object label, it gets different styling.
35111 * @param {string} name
35112 * @param {array} path | where to look for the actual option
35113 * @param {string} objectLabel
35114 * @returns {HTMLElement}
35115 * @private
35116 */
35117
35118 }, {
35119 key: "_makeLabel",
35120 value: function _makeLabel(name, path) {
35121 var objectLabel = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
35122 var div = document.createElement('div');
35123 div.className = 'vis-configuration vis-config-label vis-config-s' + path.length;
35124
35125 if (objectLabel === true) {
35126 div.innerHTML = '<i><b>' + name + ':</b></i>';
35127 } else {
35128 div.innerHTML = name + ':';
35129 }
35130
35131 return div;
35132 }
35133 /**
35134 * make a dropdown list for multiple possible string optoins
35135 * @param {Array.<number>} arr
35136 * @param {number} value
35137 * @param {array} path | where to look for the actual option
35138 * @private
35139 */
35140
35141 }, {
35142 key: "_makeDropdown",
35143 value: function _makeDropdown(arr, value, path) {
35144 var select = document.createElement('select');
35145 select.className = 'vis-configuration vis-config-select';
35146 var selectedValue = 0;
35147
35148 if (value !== undefined) {
35149 if (arr.indexOf(value) !== -1) {
35150 selectedValue = arr.indexOf(value);
35151 }
35152 }
35153
35154 for (var i = 0; i < arr.length; i++) {
35155 var option = document.createElement('option');
35156 option.value = arr[i];
35157
35158 if (i === selectedValue) {
35159 option.selected = 'selected';
35160 }
35161
35162 option.innerHTML = arr[i];
35163 select.appendChild(option);
35164 }
35165
35166 var me = this;
35167
35168 select.onchange = function () {
35169 me._update(this.value, path);
35170 };
35171
35172 var label = this._makeLabel(path[path.length - 1], path);
35173
35174 this._makeItem(path, label, select);
35175 }
35176 /**
35177 * make a range object for numeric options
35178 * @param {Array.<number>} arr
35179 * @param {number} value
35180 * @param {array} path | where to look for the actual option
35181 * @private
35182 */
35183
35184 }, {
35185 key: "_makeRange",
35186 value: function _makeRange(arr, value, path) {
35187 var defaultValue = arr[0];
35188 var min = arr[1];
35189 var max = arr[2];
35190 var step = arr[3];
35191 var range = document.createElement('input');
35192 range.className = 'vis-configuration vis-config-range';
35193
35194 try {
35195 range.type = 'range'; // not supported on IE9
35196
35197 range.min = min;
35198 range.max = max;
35199 } // TODO: Add some error handling and remove this lint exception
35200 catch (err) {} // eslint-disable-line no-empty
35201
35202
35203 range.step = step; // set up the popup settings in case they are needed.
35204
35205 var popupString = '';
35206 var popupValue = 0;
35207
35208 if (value !== undefined) {
35209 var factor = 1.20;
35210
35211 if (value < 0 && value * factor < min) {
35212 range.min = Math.ceil(value * factor);
35213 popupValue = range.min;
35214 popupString = 'range increased';
35215 } else if (value / factor < min) {
35216 range.min = Math.ceil(value / factor);
35217 popupValue = range.min;
35218 popupString = 'range increased';
35219 }
35220
35221 if (value * factor > max && max !== 1) {
35222 range.max = Math.ceil(value * factor);
35223 popupValue = range.max;
35224 popupString = 'range increased';
35225 }
35226
35227 range.value = value;
35228 } else {
35229 range.value = defaultValue;
35230 }
35231
35232 var input = document.createElement('input');
35233 input.className = 'vis-configuration vis-config-rangeinput';
35234 input.value = range.value;
35235 var me = this;
35236
35237 range.onchange = function () {
35238 input.value = this.value;
35239
35240 me._update(Number(this.value), path);
35241 };
35242
35243 range.oninput = function () {
35244 input.value = this.value;
35245 };
35246
35247 var label = this._makeLabel(path[path.length - 1], path);
35248
35249 var itemIndex = this._makeItem(path, label, range, input); // if a popup is needed AND it has not been shown for this value, show it.
35250
35251
35252 if (popupString !== '' && this.popupHistory[itemIndex] !== popupValue) {
35253 this.popupHistory[itemIndex] = popupValue;
35254
35255 this._setupPopup(popupString, itemIndex);
35256 }
35257 }
35258 /**
35259 * make a button object
35260 * @private
35261 */
35262
35263 }, {
35264 key: "_makeButton",
35265 value: function _makeButton() {
35266 var _this = this;
35267
35268 if (this.options.showButton === true) {
35269 var generateButton = document.createElement('div');
35270 generateButton.className = 'vis-configuration vis-config-button';
35271 generateButton.innerHTML = 'generate options';
35272
35273 generateButton.onclick = function () {
35274 _this._printOptions();
35275 };
35276
35277 generateButton.onmouseover = function () {
35278 generateButton.className = 'vis-configuration vis-config-button hover';
35279 };
35280
35281 generateButton.onmouseout = function () {
35282 generateButton.className = 'vis-configuration vis-config-button';
35283 };
35284
35285 this.optionsContainer = document.createElement('div');
35286 this.optionsContainer.className = 'vis-configuration vis-config-option-container';
35287 this.domElements.push(this.optionsContainer);
35288 this.domElements.push(generateButton);
35289 }
35290 }
35291 /**
35292 * prepare the popup
35293 * @param {string} string
35294 * @param {number} index
35295 * @private
35296 */
35297
35298 }, {
35299 key: "_setupPopup",
35300 value: function _setupPopup(string, index) {
35301 var _this2 = this;
35302
35303 if (this.initialized === true && this.allowCreation === true && this.popupCounter < this.popupLimit) {
35304 var div = document.createElement("div");
35305 div.id = "vis-configuration-popup";
35306 div.className = "vis-configuration-popup";
35307 div.innerHTML = string;
35308
35309 div.onclick = function () {
35310 _this2._removePopup();
35311 };
35312
35313 this.popupCounter += 1;
35314 this.popupDiv = {
35315 html: div,
35316 index: index
35317 };
35318 }
35319 }
35320 /**
35321 * remove the popup from the dom
35322 * @private
35323 */
35324
35325 }, {
35326 key: "_removePopup",
35327 value: function _removePopup() {
35328 if (this.popupDiv.html !== undefined) {
35329 this.popupDiv.html.parentNode.removeChild(this.popupDiv.html);
35330 clearTimeout(this.popupDiv.hideTimeout);
35331 clearTimeout(this.popupDiv.deleteTimeout);
35332 this.popupDiv = {};
35333 }
35334 }
35335 /**
35336 * Show the popup if it is needed.
35337 * @private
35338 */
35339
35340 }, {
35341 key: "_showPopupIfNeeded",
35342 value: function _showPopupIfNeeded() {
35343 var _this3 = this;
35344
35345 if (this.popupDiv.html !== undefined) {
35346 var correspondingElement = this.domElements[this.popupDiv.index];
35347 var rect = correspondingElement.getBoundingClientRect();
35348 this.popupDiv.html.style.left = rect.left + "px";
35349 this.popupDiv.html.style.top = rect.top - 30 + "px"; // 30 is the height;
35350
35351 document.body.appendChild(this.popupDiv.html);
35352 this.popupDiv.hideTimeout = setTimeout(function () {
35353 _this3.popupDiv.html.style.opacity = 0;
35354 }, 1500);
35355 this.popupDiv.deleteTimeout = setTimeout(function () {
35356 _this3._removePopup();
35357 }, 1800);
35358 }
35359 }
35360 /**
35361 * make a checkbox for boolean options.
35362 * @param {number} defaultValue
35363 * @param {number} value
35364 * @param {array} path | where to look for the actual option
35365 * @private
35366 */
35367
35368 }, {
35369 key: "_makeCheckbox",
35370 value: function _makeCheckbox(defaultValue, value, path) {
35371 var checkbox = document.createElement('input');
35372 checkbox.type = 'checkbox';
35373 checkbox.className = 'vis-configuration vis-config-checkbox';
35374 checkbox.checked = defaultValue;
35375
35376 if (value !== undefined) {
35377 checkbox.checked = value;
35378
35379 if (value !== defaultValue) {
35380 if (_typeof$1(defaultValue) === 'object') {
35381 if (value !== defaultValue.enabled) {
35382 this.changedOptions.push({
35383 path: path,
35384 value: value
35385 });
35386 }
35387 } else {
35388 this.changedOptions.push({
35389 path: path,
35390 value: value
35391 });
35392 }
35393 }
35394 }
35395
35396 var me = this;
35397
35398 checkbox.onchange = function () {
35399 me._update(this.checked, path);
35400 };
35401
35402 var label = this._makeLabel(path[path.length - 1], path);
35403
35404 this._makeItem(path, label, checkbox);
35405 }
35406 /**
35407 * make a text input field for string options.
35408 * @param {number} defaultValue
35409 * @param {number} value
35410 * @param {array} path | where to look for the actual option
35411 * @private
35412 */
35413
35414 }, {
35415 key: "_makeTextInput",
35416 value: function _makeTextInput(defaultValue, value, path) {
35417 var checkbox = document.createElement('input');
35418 checkbox.type = 'text';
35419 checkbox.className = 'vis-configuration vis-config-text';
35420 checkbox.value = value;
35421
35422 if (value !== defaultValue) {
35423 this.changedOptions.push({
35424 path: path,
35425 value: value
35426 });
35427 }
35428
35429 var me = this;
35430
35431 checkbox.onchange = function () {
35432 me._update(this.value, path);
35433 };
35434
35435 var label = this._makeLabel(path[path.length - 1], path);
35436
35437 this._makeItem(path, label, checkbox);
35438 }
35439 /**
35440 * make a color field with a color picker for color fields
35441 * @param {Array.<number>} arr
35442 * @param {number} value
35443 * @param {array} path | where to look for the actual option
35444 * @private
35445 */
35446
35447 }, {
35448 key: "_makeColorField",
35449 value: function _makeColorField(arr, value, path) {
35450 var _this4 = this;
35451
35452 var defaultColor = arr[1];
35453 var div = document.createElement('div');
35454 value = value === undefined ? defaultColor : value;
35455
35456 if (value !== 'none') {
35457 div.className = 'vis-configuration vis-config-colorBlock';
35458 div.style.backgroundColor = value;
35459 } else {
35460 div.className = 'vis-configuration vis-config-colorBlock none';
35461 }
35462
35463 value = value === undefined ? defaultColor : value;
35464
35465 div.onclick = function () {
35466 _this4._showColorPicker(value, div, path);
35467 };
35468
35469 var label = this._makeLabel(path[path.length - 1], path);
35470
35471 this._makeItem(path, label, div);
35472 }
35473 /**
35474 * used by the color buttons to call the color picker.
35475 * @param {number} value
35476 * @param {HTMLElement} div
35477 * @param {array} path | where to look for the actual option
35478 * @private
35479 */
35480
35481 }, {
35482 key: "_showColorPicker",
35483 value: function _showColorPicker(value, div, path) {
35484 var _this5 = this;
35485
35486 // clear the callback from this div
35487 div.onclick = function () {};
35488
35489 this.colorPicker.insertTo(div);
35490 this.colorPicker.show();
35491 this.colorPicker.setColor(value);
35492 this.colorPicker.setUpdateCallback(function (color) {
35493 var colorString = 'rgba(' + color.r + ',' + color.g + ',' + color.b + ',' + color.a + ')';
35494 div.style.backgroundColor = colorString;
35495
35496 _this5._update(colorString, path);
35497 }); // on close of the colorpicker, restore the callback.
35498
35499 this.colorPicker.setCloseCallback(function () {
35500 div.onclick = function () {
35501 _this5._showColorPicker(value, div, path);
35502 };
35503 });
35504 }
35505 /**
35506 * parse an object and draw the correct items
35507 * @param {Object} obj
35508 * @param {array} [path=[]] | where to look for the actual option
35509 * @param {boolean} [checkOnly=false]
35510 * @returns {boolean}
35511 * @private
35512 */
35513
35514 }, {
35515 key: "_handleObject",
35516 value: function _handleObject(obj) {
35517 var path = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];
35518 var checkOnly = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
35519 var show = false;
35520 var filter = this.options.filter;
35521 var visibleInSet = false;
35522
35523 for (var subObj in obj) {
35524 if (obj.hasOwnProperty(subObj)) {
35525 show = true;
35526 var item = obj[subObj];
35527 var newPath = util.copyAndExtendArray(path, subObj);
35528
35529 if (typeof filter === 'function') {
35530 show = filter(subObj, path); // if needed we must go deeper into the object.
35531
35532 if (show === false) {
35533 if (!(item instanceof Array) && typeof item !== 'string' && typeof item !== 'boolean' && item instanceof Object) {
35534 this.allowCreation = false;
35535 show = this._handleObject(item, newPath, true);
35536 this.allowCreation = checkOnly === false;
35537 }
35538 }
35539 }
35540
35541 if (show !== false) {
35542 visibleInSet = true;
35543
35544 var value = this._getValue(newPath);
35545
35546 if (item instanceof Array) {
35547 this._handleArray(item, value, newPath);
35548 } else if (typeof item === 'string') {
35549 this._makeTextInput(item, value, newPath);
35550 } else if (typeof item === 'boolean') {
35551 this._makeCheckbox(item, value, newPath);
35552 } else if (item instanceof Object) {
35553 // collapse the physics options that are not enabled
35554 var draw = true;
35555
35556 if (path.indexOf('physics') !== -1) {
35557 if (this.moduleOptions.physics.solver !== subObj) {
35558 draw = false;
35559 }
35560 }
35561
35562 if (draw === true) {
35563 // initially collapse options with an disabled enabled option.
35564 if (item.enabled !== undefined) {
35565 var enabledPath = util.copyAndExtendArray(newPath, 'enabled');
35566
35567 var enabledValue = this._getValue(enabledPath);
35568
35569 if (enabledValue === true) {
35570 var label = this._makeLabel(subObj, newPath, true);
35571
35572 this._makeItem(newPath, label);
35573
35574 visibleInSet = this._handleObject(item, newPath) || visibleInSet;
35575 } else {
35576 this._makeCheckbox(item, enabledValue, newPath);
35577 }
35578 } else {
35579 var _label = this._makeLabel(subObj, newPath, true);
35580
35581 this._makeItem(newPath, _label);
35582
35583 visibleInSet = this._handleObject(item, newPath) || visibleInSet;
35584 }
35585 }
35586 } else {
35587 console.error('dont know how to handle', item, subObj, newPath);
35588 }
35589 }
35590 }
35591 }
35592
35593 return visibleInSet;
35594 }
35595 /**
35596 * handle the array type of option
35597 * @param {Array.<number>} arr
35598 * @param {number} value
35599 * @param {array} path | where to look for the actual option
35600 * @private
35601 */
35602
35603 }, {
35604 key: "_handleArray",
35605 value: function _handleArray(arr, value, path) {
35606 if (typeof arr[0] === 'string' && arr[0] === 'color') {
35607 this._makeColorField(arr, value, path);
35608
35609 if (arr[1] !== value) {
35610 this.changedOptions.push({
35611 path: path,
35612 value: value
35613 });
35614 }
35615 } else if (typeof arr[0] === 'string') {
35616 this._makeDropdown(arr, value, path);
35617
35618 if (arr[0] !== value) {
35619 this.changedOptions.push({
35620 path: path,
35621 value: value
35622 });
35623 }
35624 } else if (typeof arr[0] === 'number') {
35625 this._makeRange(arr, value, path);
35626
35627 if (arr[0] !== value) {
35628 this.changedOptions.push({
35629 path: path,
35630 value: Number(value)
35631 });
35632 }
35633 }
35634 }
35635 /**
35636 * called to update the network with the new settings.
35637 * @param {number} value
35638 * @param {array} path | where to look for the actual option
35639 * @private
35640 */
35641
35642 }, {
35643 key: "_update",
35644 value: function _update(value, path) {
35645 var options = this._constructOptions(value, path);
35646
35647 if (this.parent.body && this.parent.body.emitter && this.parent.body.emitter.emit) {
35648 this.parent.body.emitter.emit("configChange", options);
35649 }
35650
35651 this.initialized = true;
35652 this.parent.setOptions(options);
35653 }
35654 /**
35655 *
35656 * @param {string|Boolean} value
35657 * @param {Array.<string>} path
35658 * @param {{}} optionsObj
35659 * @returns {{}}
35660 * @private
35661 */
35662
35663 }, {
35664 key: "_constructOptions",
35665 value: function _constructOptions(value, path) {
35666 var optionsObj = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
35667 var pointer = optionsObj; // when dropdown boxes can be string or boolean, we typecast it into correct types
35668
35669 value = value === 'true' ? true : value;
35670 value = value === 'false' ? false : value;
35671
35672 for (var i = 0; i < path.length; i++) {
35673 if (path[i] !== 'global') {
35674 if (pointer[path[i]] === undefined) {
35675 pointer[path[i]] = {};
35676 }
35677
35678 if (i !== path.length - 1) {
35679 pointer = pointer[path[i]];
35680 } else {
35681 pointer[path[i]] = value;
35682 }
35683 }
35684 }
35685
35686 return optionsObj;
35687 }
35688 /**
35689 * @private
35690 */
35691
35692 }, {
35693 key: "_printOptions",
35694 value: function _printOptions() {
35695 var options = this.getOptions();
35696 this.optionsContainer.innerHTML = '<pre>var options = ' + JSON.stringify(options, null, 2) + '</pre>';
35697 }
35698 /**
35699 *
35700 * @returns {{}} options
35701 */
35702
35703 }, {
35704 key: "getOptions",
35705 value: function getOptions() {
35706 var options = {};
35707
35708 for (var i = 0; i < this.changedOptions.length; i++) {
35709 this._constructOptions(this.changedOptions[i].value, this.changedOptions[i].path, options);
35710 }
35711
35712 return options;
35713 }
35714 }]);
35715
35716 return Configurator;
35717}();
35718
35719var DataSet$1 = index.DataSet;
35720var DataView$1 = index.DataView;
35721var Validator$2 = Validator$1.Validator;
35722var printStyle$1 = Validator$1.printStyle;
35723var allOptions$2 = optionsTimeline.allOptions;
35724var configureOptions$1 = optionsTimeline.configureOptions;
35725var Configurator$1 = Configurator.default;
35726/**
35727 * Create a timeline visualization
35728 * @param {HTMLElement} container
35729 * @param {vis.DataSet | vis.DataView | Array} [items]
35730 * @param {vis.DataSet | vis.DataView | Array} [groups]
35731 * @param {Object} [options] See Timeline.setOptions for the available options.
35732 * @constructor Timeline
35733 * @extends Core
35734 */
35735
35736function Timeline(container, items, groups, options) {
35737 this.initTime = new Date();
35738 this.itemsDone = false;
35739
35740 if (!(this instanceof Timeline)) {
35741 throw new SyntaxError('Constructor must be called with the new operator');
35742 } // if the third element is options, the forth is groups (optionally);
35743
35744
35745 if (!(Array.isArray(groups) || groups instanceof DataSet$1 || groups instanceof DataView$1) && groups instanceof Object) {
35746 var forthArgument = options;
35747 options = groups;
35748 groups = forthArgument;
35749 } // TODO: REMOVE THIS in the next MAJOR release
35750 // see https://github.com/almende/vis/issues/2511
35751
35752
35753 if (options && options.throttleRedraw) {
35754 console.warn("Timeline option \"throttleRedraw\" is DEPRICATED and no longer supported. It will be removed in the next MAJOR release.");
35755 }
35756
35757 var me = this;
35758 this.defaultOptions = {
35759 start: null,
35760 end: null,
35761 autoResize: true,
35762 orientation: {
35763 axis: 'bottom',
35764 // axis orientation: 'bottom', 'top', or 'both'
35765 item: 'bottom' // not relevant
35766
35767 },
35768 moment: moment$3,
35769 width: null,
35770 height: null,
35771 maxHeight: null,
35772 minHeight: null
35773 };
35774 this.options = util.deepExtend({}, this.defaultOptions); // Create the DOM, props, and emitter
35775
35776 this._create(container);
35777
35778 if (!options || options && typeof options.rtl == "undefined") {
35779 this.dom.root.style.visibility = 'hidden';
35780 var directionFromDom,
35781 domNode = this.dom.root;
35782
35783 while (!directionFromDom && domNode) {
35784 directionFromDom = window.getComputedStyle(domNode, null).direction;
35785 domNode = domNode.parentElement;
35786 }
35787
35788 this.options.rtl = directionFromDom && directionFromDom.toLowerCase() == "rtl";
35789 } else {
35790 this.options.rtl = options.rtl;
35791 }
35792
35793 this.options.rollingMode = options && options.rollingMode;
35794 this.options.onInitialDrawComplete = options && options.onInitialDrawComplete;
35795 this.options.onTimeout = options && options.onTimeout;
35796 this.options.loadingScreenTemplate = options && options.loadingScreenTemplate; // Prepare loading screen
35797
35798 var loadingScreenFragment = document.createElement('div');
35799
35800 if (this.options.loadingScreenTemplate) {
35801 var templateFunction = this.options.loadingScreenTemplate.bind(this);
35802 var loadingScreen = templateFunction(this.dom.loadingScreen);
35803
35804 if (loadingScreen instanceof Object && !(loadingScreen instanceof Element)) {
35805 templateFunction(loadingScreenFragment);
35806 } else {
35807 if (loadingScreen instanceof Element) {
35808 loadingScreenFragment.innerHTML = '';
35809 loadingScreenFragment.appendChild(loadingScreen);
35810 } else if (loadingScreen != undefined) {
35811 loadingScreenFragment.innerHTML = loadingScreen;
35812 }
35813 }
35814 }
35815
35816 this.dom.loadingScreen.appendChild(loadingScreenFragment); // all components listed here will be repainted automatically
35817
35818 this.components = [];
35819 this.body = {
35820 dom: this.dom,
35821 domProps: this.props,
35822 emitter: {
35823 on: this.on.bind(this),
35824 off: this.off.bind(this),
35825 emit: this.emit.bind(this)
35826 },
35827 hiddenDates: [],
35828 util: {
35829 getScale: function getScale() {
35830 return me.timeAxis.step.scale;
35831 },
35832 getStep: function getStep() {
35833 return me.timeAxis.step.step;
35834 },
35835 toScreen: me._toScreen.bind(me),
35836 toGlobalScreen: me._toGlobalScreen.bind(me),
35837 // this refers to the root.width
35838 toTime: me._toTime.bind(me),
35839 toGlobalTime: me._toGlobalTime.bind(me)
35840 }
35841 }; // range
35842
35843 this.range = new Range_1(this.body, this.options);
35844 this.components.push(this.range);
35845 this.body.range = this.range; // time axis
35846
35847 this.timeAxis = new TimeAxis(this.body, this.options);
35848 this.timeAxis2 = null; // used in case of orientation option 'both'
35849
35850 this.components.push(this.timeAxis); // current time bar
35851
35852 this.currentTime = new CurrentTime_1(this.body, this.options);
35853 this.components.push(this.currentTime); // item set
35854
35855 this.itemSet = new ItemSet(this.body, this.options);
35856 this.components.push(this.itemSet);
35857 this.itemsData = null; // DataSet
35858
35859 this.groupsData = null; // DataSet
35860
35861 this.dom.root.onclick = function (event) {
35862 me.emit('click', me.getEventProperties(event));
35863 };
35864
35865 this.dom.root.ondblclick = function (event) {
35866 me.emit('doubleClick', me.getEventProperties(event));
35867 };
35868
35869 this.dom.root.oncontextmenu = function (event) {
35870 me.emit('contextmenu', me.getEventProperties(event));
35871 };
35872
35873 this.dom.root.onmouseover = function (event) {
35874 me.emit('mouseOver', me.getEventProperties(event));
35875 };
35876
35877 if (window.PointerEvent) {
35878 this.dom.root.onpointerdown = function (event) {
35879 me.emit('mouseDown', me.getEventProperties(event));
35880 };
35881
35882 this.dom.root.onpointermove = function (event) {
35883 me.emit('mouseMove', me.getEventProperties(event));
35884 };
35885
35886 this.dom.root.onpointerup = function (event) {
35887 me.emit('mouseUp', me.getEventProperties(event));
35888 };
35889 } else {
35890 this.dom.root.onmousemove = function (event) {
35891 me.emit('mouseMove', me.getEventProperties(event));
35892 };
35893
35894 this.dom.root.onmousedown = function (event) {
35895 me.emit('mouseDown', me.getEventProperties(event));
35896 };
35897
35898 this.dom.root.onmouseup = function (event) {
35899 me.emit('mouseUp', me.getEventProperties(event));
35900 };
35901 } //Single time autoscale/fit
35902
35903
35904 this.initialFitDone = false;
35905 this.on('changed', function () {
35906 if (me.itemsData == null) return;
35907
35908 if (!me.initialFitDone && !me.options.rollingMode) {
35909 me.initialFitDone = true;
35910
35911 if (me.options.start != undefined || me.options.end != undefined) {
35912 if (me.options.start == undefined || me.options.end == undefined) {
35913 var range = me.getItemRange();
35914 }
35915
35916 var start = me.options.start != undefined ? me.options.start : range.min;
35917 var end = me.options.end != undefined ? me.options.end : range.max;
35918 me.setWindow(start, end, {
35919 animation: false
35920 });
35921 } else {
35922 me.fit({
35923 animation: false
35924 });
35925 }
35926 }
35927
35928 if (!me.initialDrawDone && (me.initialRangeChangeDone || !me.options.start && !me.options.end || me.options.rollingMode)) {
35929 me.initialDrawDone = true;
35930 me.itemSet.initialDrawDone = true;
35931 me.dom.root.style.visibility = 'visible';
35932 me.dom.loadingScreen.parentNode.removeChild(me.dom.loadingScreen);
35933
35934 if (me.options.onInitialDrawComplete) {
35935 setTimeout(function () {
35936 return me.options.onInitialDrawComplete();
35937 }, 0);
35938 }
35939 }
35940 });
35941 this.on('destroyTimeline', function () {
35942 me.destroy();
35943 }); // apply options
35944
35945 if (options) {
35946 this.setOptions(options);
35947 } // IMPORTANT: THIS HAPPENS BEFORE SET ITEMS!
35948
35949
35950 if (groups) {
35951 this.setGroups(groups);
35952 } // create itemset
35953
35954
35955 if (items) {
35956 this.setItems(items);
35957 } // draw for the first time
35958
35959
35960 this._redraw();
35961} // Extend the functionality from Core
35962
35963
35964Timeline.prototype = new Core();
35965/**
35966 * Load a configurator
35967 * @return {Object}
35968 * @private
35969 */
35970
35971Timeline.prototype._createConfigurator = function () {
35972 return new Configurator$1(this, this.dom.container, configureOptions$1);
35973};
35974/**
35975 * Force a redraw. The size of all items will be recalculated.
35976 * Can be useful to manually redraw when option autoResize=false and the window
35977 * has been resized, or when the items CSS has been changed.
35978 *
35979 * Note: this function will be overridden on construction with a trottled version
35980 */
35981
35982
35983Timeline.prototype.redraw = function () {
35984 this.itemSet && this.itemSet.markDirty({
35985 refreshItems: true
35986 });
35987
35988 this._redraw();
35989};
35990
35991Timeline.prototype.setOptions = function (options) {
35992 // validate options
35993 var errorFound = Validator$2.validate(options, allOptions$2);
35994
35995 if (errorFound === true) {
35996 console.log('%cErrors have been found in the supplied options object.', printStyle$1);
35997 }
35998
35999 Core.prototype.setOptions.call(this, options);
36000
36001 if ('type' in options) {
36002 if (options.type !== this.options.type) {
36003 this.options.type = options.type; // force recreation of all items
36004
36005 var itemsData = this.itemsData;
36006
36007 if (itemsData) {
36008 var selection = this.getSelection();
36009 this.setItems(null); // remove all
36010
36011 this.setItems(itemsData); // add all
36012
36013 this.setSelection(selection); // restore selection
36014 }
36015 }
36016 }
36017};
36018/**
36019 * Set items
36020 * @param {vis.DataSet | Array | null} items
36021 */
36022
36023
36024Timeline.prototype.setItems = function (items) {
36025 this.itemsDone = false; // convert to type DataSet when needed
36026
36027 var newDataSet;
36028
36029 if (!items) {
36030 newDataSet = null;
36031 } else if (items instanceof DataSet$1 || items instanceof DataView$1) {
36032 newDataSet = items;
36033 } else {
36034 // turn an array into a dataset
36035 newDataSet = new DataSet$1(items, {
36036 type: {
36037 start: 'Date',
36038 end: 'Date'
36039 }
36040 });
36041 } // set items
36042
36043
36044 this.itemsData = newDataSet;
36045 this.itemSet && this.itemSet.setItems(newDataSet);
36046};
36047/**
36048 * Set groups
36049 * @param {vis.DataSet | Array} groups
36050 */
36051
36052
36053Timeline.prototype.setGroups = function (groups) {
36054 // convert to type DataSet when needed
36055 var newDataSet;
36056
36057 if (!groups) {
36058 newDataSet = null;
36059 } else {
36060 var filter = function filter(group) {
36061 return group.visible !== false;
36062 };
36063
36064 if (groups instanceof DataSet$1 || groups instanceof DataView$1) {
36065 newDataSet = new DataView$1(groups, {
36066 filter: filter
36067 });
36068 } else {
36069 // turn an array into a dataset
36070 newDataSet = new DataSet$1(groups.filter(filter));
36071 }
36072 }
36073
36074 this.groupsData = newDataSet;
36075 this.itemSet.setGroups(newDataSet);
36076};
36077/**
36078 * Set both items and groups in one go
36079 * @param {{items: (Array | vis.DataSet), groups: (Array | vis.DataSet)}} data
36080 */
36081
36082
36083Timeline.prototype.setData = function (data) {
36084 if (data && data.groups) {
36085 this.setGroups(data.groups);
36086 }
36087
36088 if (data && data.items) {
36089 this.setItems(data.items);
36090 }
36091};
36092/**
36093 * Set selected items by their id. Replaces the current selection
36094 * Unknown id's are silently ignored.
36095 * @param {string[] | string} [ids] An array with zero or more id's of the items to be
36096 * selected. If ids is an empty array, all items will be
36097 * unselected.
36098 * @param {Object} [options] Available options:
36099 * `focus: boolean`
36100 * If true, focus will be set to the selected item(s)
36101 * `animation: boolean | {duration: number, easingFunction: string}`
36102 * If true (default), the range is animated
36103 * smoothly to the new window. An object can be
36104 * provided to specify duration and easing function.
36105 * Default duration is 500 ms, and default easing
36106 * function is 'easeInOutQuad'.
36107 * Only applicable when option focus is true.
36108 */
36109
36110
36111Timeline.prototype.setSelection = function (ids, options) {
36112 this.itemSet && this.itemSet.setSelection(ids);
36113
36114 if (options && options.focus) {
36115 this.focus(ids, options);
36116 }
36117};
36118/**
36119 * Get the selected items by their id
36120 * @return {Array} ids The ids of the selected items
36121 */
36122
36123
36124Timeline.prototype.getSelection = function () {
36125 return this.itemSet && this.itemSet.getSelection() || [];
36126};
36127/**
36128 * Adjust the visible window such that the selected item (or multiple items)
36129 * are centered on screen.
36130 * @param {string | String[]} id An item id or array with item ids
36131 * @param {Object} [options] Available options:
36132 * `animation: boolean | {duration: number, easingFunction: string}`
36133 * If true (default), the range is animated
36134 * smoothly to the new window. An object can be
36135 * provided to specify duration and easing function.
36136 * Default duration is 500 ms, and default easing
36137 * function is 'easeInOutQuad'.
36138 */
36139
36140
36141Timeline.prototype.focus = function (id, options) {
36142 if (!this.itemsData || id == undefined) return;
36143 var ids = Array.isArray(id) ? id : [id]; // get the specified item(s)
36144
36145 var itemsData = this.itemsData.getDataSet().get(ids, {
36146 type: {
36147 start: 'Date',
36148 end: 'Date'
36149 }
36150 }); // calculate minimum start and maximum end of specified items
36151
36152 var start = null;
36153 var end = null;
36154 itemsData.forEach(function (itemData) {
36155 var s = itemData.start.valueOf();
36156 var e = 'end' in itemData ? itemData.end.valueOf() : itemData.start.valueOf();
36157
36158 if (start === null || s < start) {
36159 start = s;
36160 }
36161
36162 if (end === null || e > end) {
36163 end = e;
36164 }
36165 });
36166
36167 if (start !== null && end !== null) {
36168 var me = this; // Use the first item for the vertical focus
36169
36170 var item = this.itemSet.items[ids[0]];
36171 var startPos = this._getScrollTop() * -1;
36172 var initialVerticalScroll = null; // Setup a handler for each frame of the vertical scroll
36173
36174 var verticalAnimationFrame = function verticalAnimationFrame(ease, willDraw, done) {
36175 var verticalScroll = getItemVerticalScroll(me, item);
36176
36177 if (verticalScroll === false) {
36178 return; // We don't need to scroll, so do nothing
36179 }
36180
36181 if (!initialVerticalScroll) {
36182 initialVerticalScroll = verticalScroll;
36183 }
36184
36185 if (initialVerticalScroll.itemTop == verticalScroll.itemTop && !initialVerticalScroll.shouldScroll) {
36186 return; // We don't need to scroll, so do nothing
36187 } else if (initialVerticalScroll.itemTop != verticalScroll.itemTop && verticalScroll.shouldScroll) {
36188 // The redraw shifted elements, so reset the animation to correct
36189 initialVerticalScroll = verticalScroll;
36190 startPos = me._getScrollTop() * -1;
36191 }
36192
36193 var from = startPos;
36194 var to = initialVerticalScroll.scrollOffset;
36195 var scrollTop = done ? to : from + (to - from) * ease;
36196
36197 me._setScrollTop(-scrollTop);
36198
36199 if (!willDraw) {
36200 me._redraw();
36201 }
36202 }; // Enforces the final vertical scroll position
36203
36204
36205 var setFinalVerticalPosition = function setFinalVerticalPosition() {
36206 var finalVerticalScroll = getItemVerticalScroll(me, item);
36207
36208 if (finalVerticalScroll.shouldScroll && finalVerticalScroll.itemTop != initialVerticalScroll.itemTop) {
36209 me._setScrollTop(-finalVerticalScroll.scrollOffset);
36210
36211 me._redraw();
36212 }
36213 }; // Perform one last check at the end to make sure the final vertical
36214 // position is correct
36215
36216
36217 var finalVerticalCallback = function finalVerticalCallback() {
36218 // Double check we ended at the proper scroll position
36219 setFinalVerticalPosition(); // Let the redraw settle and finalize the position.
36220
36221 setTimeout(setFinalVerticalPosition, 100);
36222 }; // calculate the new middle and interval for the window
36223
36224
36225 var middle = (start + end) / 2;
36226 var interval = Math.max(this.range.end - this.range.start, (end - start) * 1.1);
36227 var animation = options && options.animation !== undefined ? options.animation : true;
36228
36229 if (!animation) {
36230 // We aren't animating so set a default so that the final callback forces the vertical location
36231 initialVerticalScroll = {
36232 shouldScroll: false,
36233 scrollOffset: -1,
36234 itemTop: -1
36235 };
36236 }
36237
36238 this.range.setRange(middle - interval / 2, middle + interval / 2, {
36239 animation: animation
36240 }, finalVerticalCallback, verticalAnimationFrame);
36241 }
36242};
36243/**
36244 * Set Timeline window such that it fits all items
36245 * @param {Object} [options] Available options:
36246 * `animation: boolean | {duration: number, easingFunction: string}`
36247 * If true (default), the range is animated
36248 * smoothly to the new window. An object can be
36249 * provided to specify duration and easing function.
36250 * Default duration is 500 ms, and default easing
36251 * function is 'easeInOutQuad'.
36252 * @param {function} [callback]
36253 */
36254
36255
36256Timeline.prototype.fit = function (options, callback) {
36257 var animation = options && options.animation !== undefined ? options.animation : true;
36258 var range;
36259 var dataset = this.itemsData && this.itemsData.getDataSet();
36260
36261 if (dataset.length === 1 && dataset.get()[0].end === undefined) {
36262 // a single item -> don't fit, just show a range around the item from -4 to +3 days
36263 range = this.getDataRange();
36264 this.moveTo(range.min.valueOf(), {
36265 animation: animation
36266 }, callback);
36267 } else {
36268 // exactly fit the items (plus a small margin)
36269 range = this.getItemRange();
36270 this.range.setRange(range.min, range.max, {
36271 animation: animation
36272 }, callback);
36273 }
36274};
36275/**
36276 *
36277 * @param {vis.Item} item
36278 * @returns {number}
36279 */
36280
36281
36282function getStart(item) {
36283 return util.convert(item.data.start, 'Date').valueOf();
36284}
36285/**
36286 *
36287 * @param {vis.Item} item
36288 * @returns {number}
36289 */
36290
36291
36292function getEnd(item) {
36293 var end = item.data.end != undefined ? item.data.end : item.data.start;
36294 return util.convert(end, 'Date').valueOf();
36295}
36296/**
36297 * @param {vis.Timeline} timeline
36298 * @param {vis.Item} item
36299 * @return {{shouldScroll: bool, scrollOffset: number, itemTop: number}}
36300 */
36301
36302
36303function getItemVerticalScroll(timeline, item) {
36304 if (!item.parent) {
36305 // The item no longer exists, so ignore this focus.
36306 return false;
36307 }
36308
36309 var leftHeight = timeline.props.leftContainer.height;
36310 var contentHeight = timeline.props.left.height;
36311 var group = item.parent;
36312 var offset = group.top;
36313 var shouldScroll = true;
36314 var orientation = timeline.timeAxis.options.orientation.axis;
36315
36316 var itemTop = function itemTop() {
36317 if (orientation == "bottom") {
36318 return group.height - item.top - item.height;
36319 } else {
36320 return item.top;
36321 }
36322 };
36323
36324 var currentScrollHeight = timeline._getScrollTop() * -1;
36325 var targetOffset = offset + itemTop();
36326 var height = item.height;
36327
36328 if (targetOffset < currentScrollHeight) {
36329 if (offset + leftHeight <= offset + itemTop() + height) {
36330 offset += itemTop() - timeline.itemSet.options.margin.item.vertical;
36331 }
36332 } else if (targetOffset + height > currentScrollHeight + leftHeight) {
36333 offset += itemTop() + height - leftHeight + timeline.itemSet.options.margin.item.vertical;
36334 } else {
36335 shouldScroll = false;
36336 }
36337
36338 offset = Math.min(offset, contentHeight - leftHeight);
36339 return {
36340 shouldScroll: shouldScroll,
36341 scrollOffset: offset,
36342 itemTop: targetOffset
36343 };
36344}
36345/**
36346 * Determine the range of the items, taking into account their actual width
36347 * and a margin of 10 pixels on both sides.
36348 *
36349 * @returns {{min: Date, max: Date}}
36350 */
36351
36352
36353Timeline.prototype.getItemRange = function () {
36354 // get a rough approximation for the range based on the items start and end dates
36355 var range = this.getDataRange();
36356 var min = range.min !== null ? range.min.valueOf() : null;
36357 var max = range.max !== null ? range.max.valueOf() : null;
36358 var minItem = null;
36359 var maxItem = null;
36360
36361 if (min != null && max != null) {
36362 var interval = max - min; // ms
36363
36364 if (interval <= 0) {
36365 interval = 10;
36366 }
36367
36368 var factor = interval / this.props.center.width;
36369 var redrawQueue = {};
36370 var redrawQueueLength = 0; // collect redraw functions
36371
36372 util.forEach(this.itemSet.items, function (item, key) {
36373 if (item.groupShowing) {
36374 var returnQueue = true;
36375 redrawQueue[key] = item.redraw(returnQueue);
36376 redrawQueueLength = redrawQueue[key].length;
36377 }
36378 });
36379 var needRedraw = redrawQueueLength > 0;
36380
36381 if (needRedraw) {
36382 // redraw all regular items
36383 for (var i = 0; i < redrawQueueLength; i++) {
36384 util.forEach(redrawQueue, function (fns) {
36385 fns[i]();
36386 });
36387 }
36388 } // calculate the date of the left side and right side of the items given
36389
36390
36391 util.forEach(this.itemSet.items, function (item) {
36392 var start = getStart(item);
36393 var end = getEnd(item);
36394 var startSide;
36395 var endSide;
36396
36397 if (this.options.rtl) {
36398 startSide = start - (item.getWidthRight() + 10) * factor;
36399 endSide = end + (item.getWidthLeft() + 10) * factor;
36400 } else {
36401 startSide = start - (item.getWidthLeft() + 10) * factor;
36402 endSide = end + (item.getWidthRight() + 10) * factor;
36403 }
36404
36405 if (startSide < min) {
36406 min = startSide;
36407 minItem = item;
36408 }
36409
36410 if (endSide > max) {
36411 max = endSide;
36412 maxItem = item;
36413 }
36414 }.bind(this));
36415
36416 if (minItem && maxItem) {
36417 var lhs = minItem.getWidthLeft() + 10;
36418 var rhs = maxItem.getWidthRight() + 10;
36419 var delta = this.props.center.width - lhs - rhs; // px
36420
36421 if (delta > 0) {
36422 if (this.options.rtl) {
36423 min = getStart(minItem) - rhs * interval / delta; // ms
36424
36425 max = getEnd(maxItem) + lhs * interval / delta; // ms
36426 } else {
36427 min = getStart(minItem) - lhs * interval / delta; // ms
36428
36429 max = getEnd(maxItem) + rhs * interval / delta; // ms
36430 }
36431 }
36432 }
36433 }
36434
36435 return {
36436 min: min != null ? new Date(min) : null,
36437 max: max != null ? new Date(max) : null
36438 };
36439};
36440/**
36441 * Calculate the data range of the items start and end dates
36442 * @returns {{min: Date, max: Date}}
36443 */
36444
36445
36446Timeline.prototype.getDataRange = function () {
36447 var min = null;
36448 var max = null;
36449 var dataset = this.itemsData && this.itemsData.getDataSet();
36450
36451 if (dataset) {
36452 dataset.forEach(function (item) {
36453 var start = util.convert(item.start, 'Date').valueOf();
36454 var end = util.convert(item.end != undefined ? item.end : item.start, 'Date').valueOf();
36455
36456 if (min === null || start < min) {
36457 min = start;
36458 }
36459
36460 if (max === null || end > max) {
36461 max = end;
36462 }
36463 });
36464 }
36465
36466 return {
36467 min: min != null ? new Date(min) : null,
36468 max: max != null ? new Date(max) : null
36469 };
36470};
36471/**
36472 * Generate Timeline related information from an event
36473 * @param {Event} event
36474 * @return {Object} An object with related information, like on which area
36475 * The event happened, whether clicked on an item, etc.
36476 */
36477
36478
36479Timeline.prototype.getEventProperties = function (event) {
36480 var clientX = event.center ? event.center.x : event.clientX;
36481 var clientY = event.center ? event.center.y : event.clientY;
36482 var x;
36483
36484 if (this.options.rtl) {
36485 x = util.getAbsoluteRight(this.dom.centerContainer) - clientX;
36486 } else {
36487 x = clientX - util.getAbsoluteLeft(this.dom.centerContainer);
36488 }
36489
36490 var y = clientY - util.getAbsoluteTop(this.dom.centerContainer);
36491 var item = this.itemSet.itemFromTarget(event);
36492 var group = this.itemSet.groupFromTarget(event);
36493 var customTime = CustomTime.customTimeFromTarget(event);
36494 var snap = this.itemSet.options.snap || null;
36495 var scale = this.body.util.getScale();
36496 var step = this.body.util.getStep();
36497
36498 var time = this._toTime(x);
36499
36500 var snappedTime = snap ? snap(time, scale, step) : time;
36501 var element = util.getTarget(event);
36502 var what = null;
36503
36504 if (item != null) {
36505 what = 'item';
36506 } else if (customTime != null) {
36507 what = 'custom-time';
36508 } else if (util.hasParent(element, this.timeAxis.dom.foreground)) {
36509 what = 'axis';
36510 } else if (this.timeAxis2 && util.hasParent(element, this.timeAxis2.dom.foreground)) {
36511 what = 'axis';
36512 } else if (util.hasParent(element, this.itemSet.dom.labelSet)) {
36513 what = 'group-label';
36514 } else if (util.hasParent(element, this.currentTime.bar)) {
36515 what = 'current-time';
36516 } else if (util.hasParent(element, this.dom.center)) {
36517 what = 'background';
36518 }
36519
36520 return {
36521 event: event,
36522 item: item ? item.id : null,
36523 group: group ? group.groupId : null,
36524 what: what,
36525 pageX: event.srcEvent ? event.srcEvent.pageX : event.pageX,
36526 pageY: event.srcEvent ? event.srcEvent.pageY : event.pageY,
36527 x: x,
36528 y: y,
36529 time: time,
36530 snappedTime: snappedTime
36531 };
36532};
36533/**
36534 * Toggle Timeline rolling mode
36535 */
36536
36537
36538Timeline.prototype.toggleRollingMode = function () {
36539 if (this.range.rolling) {
36540 this.range.stopRolling();
36541 } else {
36542 if (this.options.rollingMode == undefined) {
36543 this.setOptions(this.options);
36544 }
36545
36546 this.range.startRolling();
36547 }
36548};
36549
36550var Timeline_1 = Timeline;
36551
36552/**
36553 *
36554 * @param {number} start
36555 * @param {number} end
36556 * @param {boolean} autoScaleStart
36557 * @param {boolean} autoScaleEnd
36558 * @param {number} containerHeight
36559 * @param {number} majorCharHeight
36560 * @param {boolean} zeroAlign
36561 * @param {function} formattingFunction
36562 * @constructor DataScale
36563 */
36564function DataScale(start, end, autoScaleStart, autoScaleEnd, containerHeight, majorCharHeight) {
36565 var zeroAlign = arguments.length > 6 && arguments[6] !== undefined ? arguments[6] : false;
36566 var formattingFunction = arguments.length > 7 && arguments[7] !== undefined ? arguments[7] : false;
36567 this.majorSteps = [1, 2, 5, 10];
36568 this.minorSteps = [0.25, 0.5, 1, 2];
36569 this.customLines = null;
36570 this.containerHeight = containerHeight;
36571 this.majorCharHeight = majorCharHeight;
36572 this._start = start;
36573 this._end = end;
36574 this.scale = 1;
36575 this.minorStepIdx = -1;
36576 this.magnitudefactor = 1;
36577 this.determineScale();
36578 this.zeroAlign = zeroAlign;
36579 this.autoScaleStart = autoScaleStart;
36580 this.autoScaleEnd = autoScaleEnd;
36581 this.formattingFunction = formattingFunction;
36582
36583 if (autoScaleStart || autoScaleEnd) {
36584 var me = this;
36585
36586 var roundToMinor = function roundToMinor(value) {
36587 var rounded = value - value % (me.magnitudefactor * me.minorSteps[me.minorStepIdx]);
36588
36589 if (value % (me.magnitudefactor * me.minorSteps[me.minorStepIdx]) > 0.5 * (me.magnitudefactor * me.minorSteps[me.minorStepIdx])) {
36590 return rounded + me.magnitudefactor * me.minorSteps[me.minorStepIdx];
36591 } else {
36592 return rounded;
36593 }
36594 };
36595
36596 if (autoScaleStart) {
36597 this._start -= this.magnitudefactor * 2 * this.minorSteps[this.minorStepIdx];
36598 this._start = roundToMinor(this._start);
36599 }
36600
36601 if (autoScaleEnd) {
36602 this._end += this.magnitudefactor * this.minorSteps[this.minorStepIdx];
36603 this._end = roundToMinor(this._end);
36604 }
36605
36606 this.determineScale();
36607 }
36608}
36609
36610DataScale.prototype.setCharHeight = function (majorCharHeight) {
36611 this.majorCharHeight = majorCharHeight;
36612};
36613
36614DataScale.prototype.setHeight = function (containerHeight) {
36615 this.containerHeight = containerHeight;
36616};
36617
36618DataScale.prototype.determineScale = function () {
36619 var range = this._end - this._start;
36620 this.scale = this.containerHeight / range;
36621 var minimumStepValue = this.majorCharHeight / this.scale;
36622 var orderOfMagnitude = range > 0 ? Math.round(Math.log(range) / Math.LN10) : 0;
36623 this.minorStepIdx = -1;
36624 this.magnitudefactor = Math.pow(10, orderOfMagnitude);
36625 var start = 0;
36626
36627 if (orderOfMagnitude < 0) {
36628 start = orderOfMagnitude;
36629 }
36630
36631 var solutionFound = false;
36632
36633 for (var l = start; Math.abs(l) <= Math.abs(orderOfMagnitude); l++) {
36634 this.magnitudefactor = Math.pow(10, l);
36635
36636 for (var j = 0; j < this.minorSteps.length; j++) {
36637 var stepSize = this.magnitudefactor * this.minorSteps[j];
36638
36639 if (stepSize >= minimumStepValue) {
36640 solutionFound = true;
36641 this.minorStepIdx = j;
36642 break;
36643 }
36644 }
36645
36646 if (solutionFound === true) {
36647 break;
36648 }
36649 }
36650};
36651
36652DataScale.prototype.is_major = function (value) {
36653 return value % (this.magnitudefactor * this.majorSteps[this.minorStepIdx]) === 0;
36654};
36655
36656DataScale.prototype.getStep = function () {
36657 return this.magnitudefactor * this.minorSteps[this.minorStepIdx];
36658};
36659
36660DataScale.prototype.getFirstMajor = function () {
36661 var majorStep = this.magnitudefactor * this.majorSteps[this.minorStepIdx];
36662 return this.convertValue(this._start + (majorStep - this._start % majorStep) % majorStep);
36663};
36664
36665DataScale.prototype.formatValue = function (current) {
36666 var returnValue = current.toPrecision(5);
36667
36668 if (typeof this.formattingFunction === 'function') {
36669 returnValue = this.formattingFunction(current);
36670 }
36671
36672 if (typeof returnValue === 'number') {
36673 return '' + returnValue;
36674 } else if (typeof returnValue === 'string') {
36675 return returnValue;
36676 } else {
36677 return current.toPrecision(5);
36678 }
36679};
36680
36681DataScale.prototype.getLines = function () {
36682 var lines = [];
36683 var step = this.getStep();
36684 var bottomOffset = (step - this._start % step) % step;
36685
36686 for (var i = this._start + bottomOffset; this._end - i > 0.00001; i += step) {
36687 if (i != this._start) {
36688 //Skip the bottom line
36689 lines.push({
36690 major: this.is_major(i),
36691 y: this.convertValue(i),
36692 val: this.formatValue(i)
36693 });
36694 }
36695 }
36696
36697 return lines;
36698};
36699
36700DataScale.prototype.followScale = function (other) {
36701 var oldStepIdx = this.minorStepIdx;
36702 var oldStart = this._start;
36703 var oldEnd = this._end;
36704 var me = this;
36705
36706 var increaseMagnitude = function increaseMagnitude() {
36707 me.magnitudefactor *= 2;
36708 };
36709
36710 var decreaseMagnitude = function decreaseMagnitude() {
36711 me.magnitudefactor /= 2;
36712 };
36713
36714 if (other.minorStepIdx <= 1 && this.minorStepIdx <= 1 || other.minorStepIdx > 1 && this.minorStepIdx > 1) ; else if (other.minorStepIdx < this.minorStepIdx) {
36715 //I'm 5, they are 4 per major.
36716 this.minorStepIdx = 1;
36717
36718 if (oldStepIdx == 2) {
36719 increaseMagnitude();
36720 } else {
36721 increaseMagnitude();
36722 increaseMagnitude();
36723 }
36724 } else {
36725 //I'm 4, they are 5 per major
36726 this.minorStepIdx = 2;
36727
36728 if (oldStepIdx == 1) {
36729 decreaseMagnitude();
36730 } else {
36731 decreaseMagnitude();
36732 decreaseMagnitude();
36733 }
36734 } //Get masters stats:
36735
36736
36737 var otherZero = other.convertValue(0);
36738 var otherStep = other.getStep() * other.scale;
36739 var done = false;
36740 var count = 0; //Loop until magnitude is correct for given constrains.
36741
36742 while (!done && count++ < 5) {
36743 //Get my stats:
36744 this.scale = otherStep / (this.minorSteps[this.minorStepIdx] * this.magnitudefactor);
36745 var newRange = this.containerHeight / this.scale; //For the case the magnitudefactor has changed:
36746
36747 this._start = oldStart;
36748 this._end = this._start + newRange;
36749 var myOriginalZero = this._end * this.scale;
36750 var majorStep = this.magnitudefactor * this.majorSteps[this.minorStepIdx];
36751 var majorOffset = this.getFirstMajor() - other.getFirstMajor();
36752
36753 if (this.zeroAlign) {
36754 var zeroOffset = otherZero - myOriginalZero;
36755 this._end += zeroOffset / this.scale;
36756 this._start = this._end - newRange;
36757 } else {
36758 if (!this.autoScaleStart) {
36759 this._start += majorStep - majorOffset / this.scale;
36760 this._end = this._start + newRange;
36761 } else {
36762 this._start -= majorOffset / this.scale;
36763 this._end = this._start + newRange;
36764 }
36765 }
36766
36767 if (!this.autoScaleEnd && this._end > oldEnd + 0.00001) {
36768 //Need to decrease magnitude to prevent scale overshoot! (end)
36769 decreaseMagnitude();
36770 done = false;
36771 continue;
36772 }
36773
36774 if (!this.autoScaleStart && this._start < oldStart - 0.00001) {
36775 if (this.zeroAlign && oldStart >= 0) {
36776 console.warn("Can't adhere to given 'min' range, due to zeroalign");
36777 } else {
36778 //Need to decrease magnitude to prevent scale overshoot! (start)
36779 decreaseMagnitude();
36780 done = false;
36781 continue;
36782 }
36783 }
36784
36785 if (this.autoScaleStart && this.autoScaleEnd && newRange < oldEnd - oldStart) {
36786 increaseMagnitude();
36787 done = false;
36788 continue;
36789 }
36790
36791 done = true;
36792 }
36793};
36794
36795DataScale.prototype.convertValue = function (value) {
36796 return this.containerHeight - (value - this._start) * this.scale;
36797};
36798
36799DataScale.prototype.screenToValue = function (pixels) {
36800 return (this.containerHeight - pixels) / this.scale + this._start;
36801};
36802
36803var DataScale_1 = DataScale;
36804
36805/**
36806 * A horizontal time axis
36807 * @param {Object} body
36808 * @param {Object} [options] See DataAxis.setOptions for the available
36809 * options.
36810 * @param {SVGElement} svg
36811 * @param {vis.LineGraph.options} linegraphOptions
36812 * @constructor DataAxis
36813 * @extends Component
36814 */
36815
36816function DataAxis(body, options, svg, linegraphOptions) {
36817 this.id = util.randomUUID();
36818 this.body = body;
36819 this.defaultOptions = {
36820 orientation: 'left',
36821 // supported: 'left', 'right'
36822 showMinorLabels: true,
36823 showMajorLabels: true,
36824 icons: false,
36825 majorLinesOffset: 7,
36826 minorLinesOffset: 4,
36827 labelOffsetX: 10,
36828 labelOffsetY: 2,
36829 iconWidth: 20,
36830 width: '40px',
36831 visible: true,
36832 alignZeros: true,
36833 left: {
36834 range: {
36835 min: undefined,
36836 max: undefined
36837 },
36838 format: function format(value) {
36839 return '' + parseFloat(value.toPrecision(3));
36840 },
36841 title: {
36842 text: undefined,
36843 style: undefined
36844 }
36845 },
36846 right: {
36847 range: {
36848 min: undefined,
36849 max: undefined
36850 },
36851 format: function format(value) {
36852 return '' + parseFloat(value.toPrecision(3));
36853 },
36854 title: {
36855 text: undefined,
36856 style: undefined
36857 }
36858 }
36859 };
36860 this.linegraphOptions = linegraphOptions;
36861 this.linegraphSVG = svg;
36862 this.props = {};
36863 this.DOMelements = {
36864 // dynamic elements
36865 lines: {},
36866 labels: {},
36867 title: {}
36868 };
36869 this.dom = {};
36870 this.scale = undefined;
36871 this.range = {
36872 start: 0,
36873 end: 0
36874 };
36875 this.options = util.extend({}, this.defaultOptions);
36876 this.conversionFactor = 1;
36877 this.setOptions(options);
36878 this.width = Number(('' + this.options.width).replace("px", ""));
36879 this.minWidth = this.width;
36880 this.height = this.linegraphSVG.getBoundingClientRect().height;
36881 this.hidden = false;
36882 this.stepPixels = 25;
36883 this.zeroCrossing = -1;
36884 this.amountOfSteps = -1;
36885 this.lineOffset = 0;
36886 this.master = true;
36887 this.masterAxis = null;
36888 this.svgElements = {};
36889 this.iconsRemoved = false;
36890 this.groups = {};
36891 this.amountOfGroups = 0; // create the HTML DOM
36892
36893 this._create();
36894
36895 if (this.scale == undefined) {
36896 this._redrawLabels();
36897 }
36898
36899 this.framework = {
36900 svg: this.svg,
36901 svgElements: this.svgElements,
36902 options: this.options,
36903 groups: this.groups
36904 };
36905 var me = this;
36906 this.body.emitter.on("verticalDrag", function () {
36907 me.dom.lineContainer.style.top = me.body.domProps.scrollTop + 'px';
36908 });
36909}
36910
36911DataAxis.prototype = new Component_1();
36912
36913DataAxis.prototype.addGroup = function (label, graphOptions) {
36914 if (!this.groups.hasOwnProperty(label)) {
36915 this.groups[label] = graphOptions;
36916 }
36917
36918 this.amountOfGroups += 1;
36919};
36920
36921DataAxis.prototype.updateGroup = function (label, graphOptions) {
36922 if (!this.groups.hasOwnProperty(label)) {
36923 this.amountOfGroups += 1;
36924 }
36925
36926 this.groups[label] = graphOptions;
36927};
36928
36929DataAxis.prototype.removeGroup = function (label) {
36930 if (this.groups.hasOwnProperty(label)) {
36931 delete this.groups[label];
36932 this.amountOfGroups -= 1;
36933 }
36934};
36935
36936DataAxis.prototype.setOptions = function (options) {
36937 if (options) {
36938 var redraw = false;
36939
36940 if (this.options.orientation != options.orientation && options.orientation !== undefined) {
36941 redraw = true;
36942 }
36943
36944 var fields = ['orientation', 'showMinorLabels', 'showMajorLabels', 'icons', 'majorLinesOffset', 'minorLinesOffset', 'labelOffsetX', 'labelOffsetY', 'iconWidth', 'width', 'visible', 'left', 'right', 'alignZeros'];
36945 util.selectiveDeepExtend(fields, this.options, options);
36946 this.minWidth = Number(('' + this.options.width).replace("px", ""));
36947
36948 if (redraw === true && this.dom.frame) {
36949 this.hide();
36950 this.show();
36951 }
36952 }
36953};
36954/**
36955 * Create the HTML DOM for the DataAxis
36956 */
36957
36958
36959DataAxis.prototype._create = function () {
36960 this.dom.frame = document.createElement('div');
36961 this.dom.frame.style.width = this.options.width;
36962 this.dom.frame.style.height = this.height;
36963 this.dom.lineContainer = document.createElement('div');
36964 this.dom.lineContainer.style.width = '100%';
36965 this.dom.lineContainer.style.height = this.height;
36966 this.dom.lineContainer.style.position = 'relative';
36967 this.dom.lineContainer.style.visibility = 'visible';
36968 this.dom.lineContainer.style.display = 'block'; // create svg element for graph drawing.
36969
36970 this.svg = document.createElementNS('http://www.w3.org/2000/svg', "svg");
36971 this.svg.style.position = "absolute";
36972 this.svg.style.top = '0px';
36973 this.svg.style.height = '100%';
36974 this.svg.style.width = '100%';
36975 this.svg.style.display = "block";
36976 this.dom.frame.appendChild(this.svg);
36977};
36978
36979DataAxis.prototype._redrawGroupIcons = function () {
36980 DOMutil.prepareElements(this.svgElements);
36981 var x;
36982 var iconWidth = this.options.iconWidth;
36983 var iconHeight = 15;
36984 var iconOffset = 4;
36985 var y = iconOffset + 0.5 * iconHeight;
36986
36987 if (this.options.orientation === 'left') {
36988 x = iconOffset;
36989 } else {
36990 x = this.width - iconWidth - iconOffset;
36991 }
36992
36993 var groupArray = Object.keys(this.groups);
36994 groupArray.sort(function (a, b) {
36995 return a < b ? -1 : 1;
36996 });
36997
36998 for (var i = 0; i < groupArray.length; i++) {
36999 var groupId = groupArray[i];
37000
37001 if (this.groups[groupId].visible === true && (this.linegraphOptions.visibility[groupId] === undefined || this.linegraphOptions.visibility[groupId] === true)) {
37002 this.groups[groupId].getLegend(iconWidth, iconHeight, this.framework, x, y);
37003 y += iconHeight + iconOffset;
37004 }
37005 }
37006
37007 DOMutil.cleanupElements(this.svgElements);
37008 this.iconsRemoved = false;
37009};
37010
37011DataAxis.prototype._cleanupIcons = function () {
37012 if (this.iconsRemoved === false) {
37013 DOMutil.prepareElements(this.svgElements);
37014 DOMutil.cleanupElements(this.svgElements);
37015 this.iconsRemoved = true;
37016 }
37017};
37018/**
37019 * Create the HTML DOM for the DataAxis
37020 */
37021
37022
37023DataAxis.prototype.show = function () {
37024 this.hidden = false;
37025
37026 if (!this.dom.frame.parentNode) {
37027 if (this.options.orientation === 'left') {
37028 this.body.dom.left.appendChild(this.dom.frame);
37029 } else {
37030 this.body.dom.right.appendChild(this.dom.frame);
37031 }
37032 }
37033
37034 if (!this.dom.lineContainer.parentNode) {
37035 this.body.dom.backgroundHorizontal.appendChild(this.dom.lineContainer);
37036 }
37037
37038 this.dom.lineContainer.style.display = 'block';
37039};
37040/**
37041 * Create the HTML DOM for the DataAxis
37042 */
37043
37044
37045DataAxis.prototype.hide = function () {
37046 this.hidden = true;
37047
37048 if (this.dom.frame.parentNode) {
37049 this.dom.frame.parentNode.removeChild(this.dom.frame);
37050 }
37051
37052 this.dom.lineContainer.style.display = 'none';
37053};
37054/**
37055 * Set a range (start and end)
37056 * @param {number} start
37057 * @param {number} end
37058 */
37059
37060
37061DataAxis.prototype.setRange = function (start, end) {
37062 this.range.start = start;
37063 this.range.end = end;
37064};
37065/**
37066 * Repaint the component
37067 * @return {boolean} Returns true if the component is resized
37068 */
37069
37070
37071DataAxis.prototype.redraw = function () {
37072 var resized = false;
37073 var activeGroups = 0; // Make sure the line container adheres to the vertical scrolling.
37074
37075 this.dom.lineContainer.style.top = this.body.domProps.scrollTop + 'px';
37076
37077 for (var groupId in this.groups) {
37078 if (this.groups.hasOwnProperty(groupId)) {
37079 if (this.groups[groupId].visible === true && (this.linegraphOptions.visibility[groupId] === undefined || this.linegraphOptions.visibility[groupId] === true)) {
37080 activeGroups++;
37081 }
37082 }
37083 }
37084
37085 if (this.amountOfGroups === 0 || activeGroups === 0) {
37086 this.hide();
37087 } else {
37088 this.show();
37089 this.height = Number(this.linegraphSVG.style.height.replace("px", "")); // svg offsetheight did not work in firefox and explorer...
37090
37091 this.dom.lineContainer.style.height = this.height + 'px';
37092 this.width = this.options.visible === true ? Number(('' + this.options.width).replace("px", "")) : 0;
37093 var props = this.props;
37094 var frame = this.dom.frame; // update classname
37095
37096 frame.className = 'vis-data-axis'; // calculate character width and height
37097
37098 this._calculateCharSize();
37099
37100 var orientation = this.options.orientation;
37101 var showMinorLabels = this.options.showMinorLabels;
37102 var showMajorLabels = this.options.showMajorLabels; // determine the width and height of the elements for the axis
37103
37104 props.minorLabelHeight = showMinorLabels ? props.minorCharHeight : 0;
37105 props.majorLabelHeight = showMajorLabels ? props.majorCharHeight : 0;
37106 props.minorLineWidth = this.body.dom.backgroundHorizontal.offsetWidth - this.lineOffset - this.width + 2 * this.options.minorLinesOffset;
37107 props.minorLineHeight = 1;
37108 props.majorLineWidth = this.body.dom.backgroundHorizontal.offsetWidth - this.lineOffset - this.width + 2 * this.options.majorLinesOffset;
37109 props.majorLineHeight = 1; // take frame offline while updating (is almost twice as fast)
37110
37111 if (orientation === 'left') {
37112 frame.style.top = '0';
37113 frame.style.left = '0';
37114 frame.style.bottom = '';
37115 frame.style.width = this.width + 'px';
37116 frame.style.height = this.height + "px";
37117 this.props.width = this.body.domProps.left.width;
37118 this.props.height = this.body.domProps.left.height;
37119 } else {
37120 // right
37121 frame.style.top = '';
37122 frame.style.bottom = '0';
37123 frame.style.left = '0';
37124 frame.style.width = this.width + 'px';
37125 frame.style.height = this.height + "px";
37126 this.props.width = this.body.domProps.right.width;
37127 this.props.height = this.body.domProps.right.height;
37128 }
37129
37130 resized = this._redrawLabels();
37131 resized = this._isResized() || resized;
37132
37133 if (this.options.icons === true) {
37134 this._redrawGroupIcons();
37135 } else {
37136 this._cleanupIcons();
37137 }
37138
37139 this._redrawTitle(orientation);
37140 }
37141
37142 return resized;
37143};
37144/**
37145 * Repaint major and minor text labels and vertical grid lines
37146 *
37147 * @returns {boolean}
37148 * @private
37149 */
37150
37151
37152DataAxis.prototype._redrawLabels = function () {
37153 var _this = this;
37154
37155 var resized = false;
37156 DOMutil.prepareElements(this.DOMelements.lines);
37157 DOMutil.prepareElements(this.DOMelements.labels);
37158 var orientation = this.options['orientation'];
37159 var customRange = this.options[orientation].range != undefined ? this.options[orientation].range : {}; //Override range with manual options:
37160
37161 var autoScaleEnd = true;
37162
37163 if (customRange.max != undefined) {
37164 this.range.end = customRange.max;
37165 autoScaleEnd = false;
37166 }
37167
37168 var autoScaleStart = true;
37169
37170 if (customRange.min != undefined) {
37171 this.range.start = customRange.min;
37172 autoScaleStart = false;
37173 }
37174
37175 this.scale = new DataScale_1(this.range.start, this.range.end, autoScaleStart, autoScaleEnd, this.dom.frame.offsetHeight, this.props.majorCharHeight, this.options.alignZeros, this.options[orientation].format);
37176
37177 if (this.master === false && this.masterAxis != undefined) {
37178 this.scale.followScale(this.masterAxis.scale);
37179 this.dom.lineContainer.style.display = 'none';
37180 } else {
37181 this.dom.lineContainer.style.display = 'block';
37182 } //Is updated in side-effect of _redrawLabel():
37183
37184
37185 this.maxLabelSize = 0;
37186 var lines = this.scale.getLines();
37187 lines.forEach(function (line) {
37188 var y = line.y;
37189 var isMajor = line.major;
37190
37191 if (_this.options['showMinorLabels'] && isMajor === false) {
37192 _this._redrawLabel(y - 2, line.val, orientation, 'vis-y-axis vis-minor', _this.props.minorCharHeight);
37193 }
37194
37195 if (isMajor) {
37196 if (y >= 0) {
37197 _this._redrawLabel(y - 2, line.val, orientation, 'vis-y-axis vis-major', _this.props.majorCharHeight);
37198 }
37199 }
37200
37201 if (_this.master === true) {
37202 if (isMajor) {
37203 _this._redrawLine(y, orientation, 'vis-grid vis-horizontal vis-major', _this.options.majorLinesOffset, _this.props.majorLineWidth);
37204 } else {
37205 _this._redrawLine(y, orientation, 'vis-grid vis-horizontal vis-minor', _this.options.minorLinesOffset, _this.props.minorLineWidth);
37206 }
37207 }
37208 }); // Note that title is rotated, so we're using the height, not width!
37209
37210 var titleWidth = 0;
37211
37212 if (this.options[orientation].title !== undefined && this.options[orientation].title.text !== undefined) {
37213 titleWidth = this.props.titleCharHeight;
37214 }
37215
37216 var offset = this.options.icons === true ? Math.max(this.options.iconWidth, titleWidth) + this.options.labelOffsetX + 15 : titleWidth + this.options.labelOffsetX + 15; // this will resize the yAxis to accommodate the labels.
37217
37218 if (this.maxLabelSize > this.width - offset && this.options.visible === true) {
37219 this.width = this.maxLabelSize + offset;
37220 this.options.width = this.width + "px";
37221 DOMutil.cleanupElements(this.DOMelements.lines);
37222 DOMutil.cleanupElements(this.DOMelements.labels);
37223 this.redraw();
37224 resized = true;
37225 } // this will resize the yAxis if it is too big for the labels.
37226 else if (this.maxLabelSize < this.width - offset && this.options.visible === true && this.width > this.minWidth) {
37227 this.width = Math.max(this.minWidth, this.maxLabelSize + offset);
37228 this.options.width = this.width + "px";
37229 DOMutil.cleanupElements(this.DOMelements.lines);
37230 DOMutil.cleanupElements(this.DOMelements.labels);
37231 this.redraw();
37232 resized = true;
37233 } else {
37234 DOMutil.cleanupElements(this.DOMelements.lines);
37235 DOMutil.cleanupElements(this.DOMelements.labels);
37236 resized = false;
37237 }
37238
37239 return resized;
37240};
37241
37242DataAxis.prototype.convertValue = function (value) {
37243 return this.scale.convertValue(value);
37244};
37245
37246DataAxis.prototype.screenToValue = function (x) {
37247 return this.scale.screenToValue(x);
37248};
37249/**
37250 * Create a label for the axis at position x
37251 *
37252 * @param {number} y
37253 * @param {string} text
37254 * @param {'top'|'right'|'bottom'|'left'} orientation
37255 * @param {string} className
37256 * @param {number} characterHeight
37257 * @private
37258 */
37259
37260
37261DataAxis.prototype._redrawLabel = function (y, text, orientation, className, characterHeight) {
37262 // reuse redundant label
37263 var label = DOMutil.getDOMElement('div', this.DOMelements.labels, this.dom.frame); //this.dom.redundant.labels.shift();
37264
37265 label.className = className;
37266 label.innerHTML = text;
37267
37268 if (orientation === 'left') {
37269 label.style.left = '-' + this.options.labelOffsetX + 'px';
37270 label.style.textAlign = "right";
37271 } else {
37272 label.style.right = '-' + this.options.labelOffsetX + 'px';
37273 label.style.textAlign = "left";
37274 }
37275
37276 label.style.top = y - 0.5 * characterHeight + this.options.labelOffsetY + 'px';
37277 text += '';
37278 var largestWidth = Math.max(this.props.majorCharWidth, this.props.minorCharWidth);
37279
37280 if (this.maxLabelSize < text.length * largestWidth) {
37281 this.maxLabelSize = text.length * largestWidth;
37282 }
37283};
37284/**
37285 * Create a minor line for the axis at position y
37286 * @param {number} y
37287 * @param {'top'|'right'|'bottom'|'left'} orientation
37288 * @param {string} className
37289 * @param {number} offset
37290 * @param {number} width
37291 */
37292
37293
37294DataAxis.prototype._redrawLine = function (y, orientation, className, offset, width) {
37295 if (this.master === true) {
37296 var line = DOMutil.getDOMElement('div', this.DOMelements.lines, this.dom.lineContainer); //this.dom.redundant.lines.shift();
37297
37298 line.className = className;
37299 line.innerHTML = '';
37300
37301 if (orientation === 'left') {
37302 line.style.left = this.width - offset + 'px';
37303 } else {
37304 line.style.right = this.width - offset + 'px';
37305 }
37306
37307 line.style.width = width + 'px';
37308 line.style.top = y + 'px';
37309 }
37310};
37311/**
37312 * Create a title for the axis
37313 * @private
37314 * @param {'top'|'right'|'bottom'|'left'} orientation
37315 */
37316
37317
37318DataAxis.prototype._redrawTitle = function (orientation) {
37319 DOMutil.prepareElements(this.DOMelements.title); // Check if the title is defined for this axes
37320
37321 if (this.options[orientation].title !== undefined && this.options[orientation].title.text !== undefined) {
37322 var title = DOMutil.getDOMElement('div', this.DOMelements.title, this.dom.frame);
37323 title.className = 'vis-y-axis vis-title vis-' + orientation;
37324 title.innerHTML = this.options[orientation].title.text; // Add style - if provided
37325
37326 if (this.options[orientation].title.style !== undefined) {
37327 util.addCssText(title, this.options[orientation].title.style);
37328 }
37329
37330 if (orientation === 'left') {
37331 title.style.left = this.props.titleCharHeight + 'px';
37332 } else {
37333 title.style.right = this.props.titleCharHeight + 'px';
37334 }
37335
37336 title.style.width = this.height + 'px';
37337 } // we need to clean up in case we did not use all elements.
37338
37339
37340 DOMutil.cleanupElements(this.DOMelements.title);
37341};
37342/**
37343 * Determine the size of text on the axis (both major and minor axis).
37344 * The size is calculated only once and then cached in this.props.
37345 * @private
37346 */
37347
37348
37349DataAxis.prototype._calculateCharSize = function () {
37350 // determine the char width and height on the minor axis
37351 if (!('minorCharHeight' in this.props)) {
37352 var textMinor = document.createTextNode('0');
37353 var measureCharMinor = document.createElement('div');
37354 measureCharMinor.className = 'vis-y-axis vis-minor vis-measure';
37355 measureCharMinor.appendChild(textMinor);
37356 this.dom.frame.appendChild(measureCharMinor);
37357 this.props.minorCharHeight = measureCharMinor.clientHeight;
37358 this.props.minorCharWidth = measureCharMinor.clientWidth;
37359 this.dom.frame.removeChild(measureCharMinor);
37360 }
37361
37362 if (!('majorCharHeight' in this.props)) {
37363 var textMajor = document.createTextNode('0');
37364 var measureCharMajor = document.createElement('div');
37365 measureCharMajor.className = 'vis-y-axis vis-major vis-measure';
37366 measureCharMajor.appendChild(textMajor);
37367 this.dom.frame.appendChild(measureCharMajor);
37368 this.props.majorCharHeight = measureCharMajor.clientHeight;
37369 this.props.majorCharWidth = measureCharMajor.clientWidth;
37370 this.dom.frame.removeChild(measureCharMajor);
37371 }
37372
37373 if (!('titleCharHeight' in this.props)) {
37374 var textTitle = document.createTextNode('0');
37375 var measureCharTitle = document.createElement('div');
37376 measureCharTitle.className = 'vis-y-axis vis-title vis-measure';
37377 measureCharTitle.appendChild(textTitle);
37378 this.dom.frame.appendChild(measureCharTitle);
37379 this.props.titleCharHeight = measureCharTitle.clientHeight;
37380 this.props.titleCharWidth = measureCharTitle.clientWidth;
37381 this.dom.frame.removeChild(measureCharTitle);
37382 }
37383};
37384
37385/**
37386 *
37387 * @param {number | string} groupId
37388 * @param {Object} options // TODO: Describe options
37389 *
37390 * @constructor Points
37391 */
37392
37393function Points(groupId, options) {} // eslint-disable-line no-unused-vars
37394
37395/**
37396 * draw the data points
37397 *
37398 * @param {Array} dataset
37399 * @param {GraphGroup} group
37400 * @param {Object} framework | SVG DOM element
37401 * @param {number} [offset]
37402 */
37403
37404
37405Points.draw = function (dataset, group, framework, offset) {
37406 offset = offset || 0;
37407 var callback = getCallback(framework, group);
37408
37409 for (var i = 0; i < dataset.length; i++) {
37410 if (!callback) {
37411 // draw the point the simple way.
37412 DOMutil.drawPoint(dataset[i].screen_x + offset, dataset[i].screen_y, getGroupTemplate(group), framework.svgElements, framework.svg, dataset[i].label);
37413 } else {
37414 var callbackResult = callback(dataset[i], group); // result might be true, false or an object
37415
37416 if (callbackResult === true || _typeof$1(callbackResult) === 'object') {
37417 DOMutil.drawPoint(dataset[i].screen_x + offset, dataset[i].screen_y, getGroupTemplate(group, callbackResult), framework.svgElements, framework.svg, dataset[i].label);
37418 }
37419 }
37420 }
37421};
37422
37423Points.drawIcon = function (group, x, y, iconWidth, iconHeight, framework) {
37424 var fillHeight = iconHeight * 0.5;
37425 var outline = DOMutil.getSVGElement("rect", framework.svgElements, framework.svg);
37426 outline.setAttributeNS(null, "x", x);
37427 outline.setAttributeNS(null, "y", y - fillHeight);
37428 outline.setAttributeNS(null, "width", iconWidth);
37429 outline.setAttributeNS(null, "height", 2 * fillHeight);
37430 outline.setAttributeNS(null, "class", "vis-outline"); //Don't call callback on icon
37431
37432 DOMutil.drawPoint(x + 0.5 * iconWidth, y, getGroupTemplate(group), framework.svgElements, framework.svg);
37433};
37434/**
37435 *
37436 * @param {vis.Group} group
37437 * @param {any} callbackResult
37438 * @returns {{style: *, styles: (*|string), size: *, className: *}}
37439 */
37440
37441
37442function getGroupTemplate(group, callbackResult) {
37443 callbackResult = typeof callbackResult === 'undefined' ? {} : callbackResult;
37444 return {
37445 style: callbackResult.style || group.options.drawPoints.style,
37446 styles: callbackResult.styles || group.options.drawPoints.styles,
37447 size: callbackResult.size || group.options.drawPoints.size,
37448 className: callbackResult.className || group.className
37449 };
37450}
37451/**
37452 *
37453 * @param {Object} framework | SVG DOM element
37454 * @param {vis.Group} group
37455 * @returns {function}
37456 */
37457
37458
37459function getCallback(framework, group) {
37460 var callback = undefined; // check for the graph2d onRender
37461
37462 if (framework.options && framework.options.drawPoints && framework.options.drawPoints.onRender && typeof framework.options.drawPoints.onRender == 'function') {
37463 callback = framework.options.drawPoints.onRender;
37464 } // override it with the group onRender if defined
37465
37466
37467 if (group.group.options && group.group.options.drawPoints && group.group.options.drawPoints.onRender && typeof group.group.options.drawPoints.onRender == 'function') {
37468 callback = group.group.options.drawPoints.onRender;
37469 }
37470
37471 return callback;
37472}
37473
37474var points = Points;
37475
37476/**
37477 *
37478 * @param {vis.GraphGroup.id} groupId
37479 * @param {Object} options // TODO: Describe options
37480 * @constructor Bargraph
37481 */
37482
37483function Bargraph(groupId, options) {// eslint-disable-line no-unused-vars
37484}
37485
37486Bargraph.drawIcon = function (group, x, y, iconWidth, iconHeight, framework) {
37487 var fillHeight = iconHeight * 0.5;
37488 var outline = DOMutil.getSVGElement("rect", framework.svgElements, framework.svg);
37489 outline.setAttributeNS(null, "x", x);
37490 outline.setAttributeNS(null, "y", y - fillHeight);
37491 outline.setAttributeNS(null, "width", iconWidth);
37492 outline.setAttributeNS(null, "height", 2 * fillHeight);
37493 outline.setAttributeNS(null, "class", "vis-outline");
37494 var barWidth = Math.round(0.3 * iconWidth);
37495 var originalWidth = group.options.barChart.width;
37496 var scale = originalWidth / barWidth;
37497 var bar1Height = Math.round(0.4 * iconHeight);
37498 var bar2Height = Math.round(0.75 * iconHeight);
37499 var offset = Math.round((iconWidth - 2 * barWidth) / 3);
37500 DOMutil.drawBar(x + 0.5 * barWidth + offset, y + fillHeight - bar1Height - 1, barWidth, bar1Height, group.className + ' vis-bar', framework.svgElements, framework.svg, group.style);
37501 DOMutil.drawBar(x + 1.5 * barWidth + offset + 2, y + fillHeight - bar2Height - 1, barWidth, bar2Height, group.className + ' vis-bar', framework.svgElements, framework.svg, group.style);
37502
37503 if (group.options.drawPoints.enabled == true) {
37504 var groupTemplate = {
37505 style: group.options.drawPoints.style,
37506 styles: group.options.drawPoints.styles,
37507 size: group.options.drawPoints.size / scale,
37508 className: group.className
37509 };
37510 DOMutil.drawPoint(x + 0.5 * barWidth + offset, y + fillHeight - bar1Height - 1, groupTemplate, framework.svgElements, framework.svg);
37511 DOMutil.drawPoint(x + 1.5 * barWidth + offset + 2, y + fillHeight - bar2Height - 1, groupTemplate, framework.svgElements, framework.svg);
37512 }
37513};
37514/**
37515 * draw a bar graph
37516 *
37517 * @param {Array.<vis.GraphGroup.id>} groupIds
37518 * @param {Object} processedGroupData
37519 * @param {{svg: Object, svgElements: Array.<Object>, options: Object, groups: Array.<vis.Group>}} framework
37520 */
37521
37522
37523Bargraph.draw = function (groupIds, processedGroupData, framework) {
37524 var combinedData = [];
37525 var intersections = {};
37526 var coreDistance;
37527 var key, drawData;
37528 var group;
37529 var i, j;
37530 var barPoints = 0; // combine all barchart data
37531
37532 for (i = 0; i < groupIds.length; i++) {
37533 group = framework.groups[groupIds[i]];
37534
37535 if (group.options.style === 'bar') {
37536 if (group.visible === true && (framework.options.groups.visibility[groupIds[i]] === undefined || framework.options.groups.visibility[groupIds[i]] === true)) {
37537 for (j = 0; j < processedGroupData[groupIds[i]].length; j++) {
37538 combinedData.push({
37539 screen_x: processedGroupData[groupIds[i]][j].screen_x,
37540 screen_end: processedGroupData[groupIds[i]][j].screen_end,
37541 screen_y: processedGroupData[groupIds[i]][j].screen_y,
37542 x: processedGroupData[groupIds[i]][j].x,
37543 end: processedGroupData[groupIds[i]][j].end,
37544 y: processedGroupData[groupIds[i]][j].y,
37545 groupId: groupIds[i],
37546 label: processedGroupData[groupIds[i]][j].label
37547 });
37548 barPoints += 1;
37549 }
37550 }
37551 }
37552 }
37553
37554 if (barPoints === 0) {
37555 return;
37556 } // sort by time and by group
37557
37558
37559 combinedData.sort(function (a, b) {
37560 if (a.screen_x === b.screen_x) {
37561 return a.groupId < b.groupId ? -1 : 1;
37562 } else {
37563 return a.screen_x - b.screen_x;
37564 }
37565 }); // get intersections
37566
37567 Bargraph._getDataIntersections(intersections, combinedData); // plot barchart
37568
37569
37570 for (i = 0; i < combinedData.length; i++) {
37571 group = framework.groups[combinedData[i].groupId];
37572 var minWidth = group.options.barChart.minWidth != undefined ? group.options.barChart.minWidth : 0.1 * group.options.barChart.width;
37573 key = combinedData[i].screen_x;
37574 var heightOffset = 0;
37575
37576 if (intersections[key] === undefined) {
37577 if (i + 1 < combinedData.length) {
37578 coreDistance = Math.abs(combinedData[i + 1].screen_x - key);
37579 }
37580
37581 drawData = Bargraph._getSafeDrawData(coreDistance, group, minWidth);
37582 } else {
37583 var nextKey = i + (intersections[key].amount - intersections[key].resolved);
37584
37585 if (nextKey < combinedData.length) {
37586 coreDistance = Math.abs(combinedData[nextKey].screen_x - key);
37587 }
37588
37589 drawData = Bargraph._getSafeDrawData(coreDistance, group, minWidth);
37590 intersections[key].resolved += 1;
37591
37592 if (group.options.stack === true && group.options.excludeFromStacking !== true) {
37593 if (combinedData[i].screen_y < group.zeroPosition) {
37594 heightOffset = intersections[key].accumulatedNegative;
37595 intersections[key].accumulatedNegative += group.zeroPosition - combinedData[i].screen_y;
37596 } else {
37597 heightOffset = intersections[key].accumulatedPositive;
37598 intersections[key].accumulatedPositive += group.zeroPosition - combinedData[i].screen_y;
37599 }
37600 } else if (group.options.barChart.sideBySide === true) {
37601 drawData.width = drawData.width / intersections[key].amount;
37602 drawData.offset += intersections[key].resolved * drawData.width - 0.5 * drawData.width * (intersections[key].amount + 1);
37603 }
37604 }
37605
37606 var dataWidth = drawData.width;
37607 var start = combinedData[i].screen_x; // are we drawing explicit boxes? (we supplied an end value)
37608
37609 if (combinedData[i].screen_end != undefined) {
37610 dataWidth = combinedData[i].screen_end - combinedData[i].screen_x;
37611 start += dataWidth * 0.5;
37612 } else {
37613 start += drawData.offset;
37614 }
37615
37616 DOMutil.drawBar(start, combinedData[i].screen_y - heightOffset, dataWidth, group.zeroPosition - combinedData[i].screen_y, group.className + ' vis-bar', framework.svgElements, framework.svg, group.style); // draw points
37617
37618 if (group.options.drawPoints.enabled === true) {
37619 var pointData = {
37620 screen_x: combinedData[i].screen_x,
37621 screen_y: combinedData[i].screen_y - heightOffset,
37622 x: combinedData[i].x,
37623 y: combinedData[i].y,
37624 groupId: combinedData[i].groupId,
37625 label: combinedData[i].label
37626 };
37627 points.draw([pointData], group, framework, drawData.offset); //DOMutil.drawPoint(combinedData[i].x + drawData.offset, combinedData[i].y, group, framework.svgElements, framework.svg);
37628 }
37629 }
37630};
37631/**
37632 * Fill the intersections object with counters of how many datapoints share the same x coordinates
37633 * @param {Object} intersections
37634 * @param {Array.<Object>} combinedData
37635 * @private
37636 */
37637
37638
37639Bargraph._getDataIntersections = function (intersections, combinedData) {
37640 // get intersections
37641 var coreDistance;
37642
37643 for (var i = 0; i < combinedData.length; i++) {
37644 if (i + 1 < combinedData.length) {
37645 coreDistance = Math.abs(combinedData[i + 1].screen_x - combinedData[i].screen_x);
37646 }
37647
37648 if (i > 0) {
37649 coreDistance = Math.min(coreDistance, Math.abs(combinedData[i - 1].screen_x - combinedData[i].screen_x));
37650 }
37651
37652 if (coreDistance === 0) {
37653 if (intersections[combinedData[i].screen_x] === undefined) {
37654 intersections[combinedData[i].screen_x] = {
37655 amount: 0,
37656 resolved: 0,
37657 accumulatedPositive: 0,
37658 accumulatedNegative: 0
37659 };
37660 }
37661
37662 intersections[combinedData[i].screen_x].amount += 1;
37663 }
37664 }
37665};
37666/**
37667 * Get the width and offset for bargraphs based on the coredistance between datapoints
37668 *
37669 * @param {number} coreDistance
37670 * @param {vis.Group} group
37671 * @param {number} minWidth
37672 * @returns {{width: number, offset: number}}
37673 * @private
37674 */
37675
37676
37677Bargraph._getSafeDrawData = function (coreDistance, group, minWidth) {
37678 var width, offset;
37679
37680 if (coreDistance < group.options.barChart.width && coreDistance > 0) {
37681 width = coreDistance < minWidth ? minWidth : coreDistance;
37682 offset = 0; // recalculate offset with the new width;
37683
37684 if (group.options.barChart.align === 'left') {
37685 offset -= 0.5 * coreDistance;
37686 } else if (group.options.barChart.align === 'right') {
37687 offset += 0.5 * coreDistance;
37688 }
37689 } else {
37690 // default settings
37691 width = group.options.barChart.width;
37692 offset = 0;
37693
37694 if (group.options.barChart.align === 'left') {
37695 offset -= 0.5 * group.options.barChart.width;
37696 } else if (group.options.barChart.align === 'right') {
37697 offset += 0.5 * group.options.barChart.width;
37698 }
37699 }
37700
37701 return {
37702 width: width,
37703 offset: offset
37704 };
37705};
37706
37707Bargraph.getStackedYRange = function (combinedData, groupRanges, groupIds, groupLabel, orientation) {
37708 if (combinedData.length > 0) {
37709 // sort by time and by group
37710 combinedData.sort(function (a, b) {
37711 if (a.screen_x === b.screen_x) {
37712 return a.groupId < b.groupId ? -1 : 1;
37713 } else {
37714 return a.screen_x - b.screen_x;
37715 }
37716 });
37717 var intersections = {};
37718
37719 Bargraph._getDataIntersections(intersections, combinedData);
37720
37721 groupRanges[groupLabel] = Bargraph._getStackedYRange(intersections, combinedData);
37722 groupRanges[groupLabel].yAxisOrientation = orientation;
37723 groupIds.push(groupLabel);
37724 }
37725};
37726
37727Bargraph._getStackedYRange = function (intersections, combinedData) {
37728 var key;
37729 var yMin = combinedData[0].screen_y;
37730 var yMax = combinedData[0].screen_y;
37731
37732 for (var i = 0; i < combinedData.length; i++) {
37733 key = combinedData[i].screen_x;
37734
37735 if (intersections[key] === undefined) {
37736 yMin = yMin > combinedData[i].screen_y ? combinedData[i].screen_y : yMin;
37737 yMax = yMax < combinedData[i].screen_y ? combinedData[i].screen_y : yMax;
37738 } else {
37739 if (combinedData[i].screen_y < 0) {
37740 intersections[key].accumulatedNegative += combinedData[i].screen_y;
37741 } else {
37742 intersections[key].accumulatedPositive += combinedData[i].screen_y;
37743 }
37744 }
37745 }
37746
37747 for (var xpos in intersections) {
37748 if (intersections.hasOwnProperty(xpos)) {
37749 yMin = yMin > intersections[xpos].accumulatedNegative ? intersections[xpos].accumulatedNegative : yMin;
37750 yMin = yMin > intersections[xpos].accumulatedPositive ? intersections[xpos].accumulatedPositive : yMin;
37751 yMax = yMax < intersections[xpos].accumulatedNegative ? intersections[xpos].accumulatedNegative : yMax;
37752 yMax = yMax < intersections[xpos].accumulatedPositive ? intersections[xpos].accumulatedPositive : yMax;
37753 }
37754 }
37755
37756 return {
37757 min: yMin,
37758 max: yMax
37759 };
37760};
37761
37762var bar = Bargraph;
37763
37764/**
37765 *
37766 * @param {vis.GraphGroup.id} groupId
37767 * @param {Object} options // TODO: Describe options
37768 * @constructor Line
37769 */
37770
37771function Line(groupId, options) {// eslint-disable-line no-unused-vars
37772}
37773
37774Line.calcPath = function (dataset, group) {
37775 if (dataset != null) {
37776 if (dataset.length > 0) {
37777 var d = []; // construct path from dataset
37778
37779 if (group.options.interpolation.enabled == true) {
37780 d = Line._catmullRom(dataset, group);
37781 } else {
37782 d = Line._linear(dataset);
37783 }
37784
37785 return d;
37786 }
37787 }
37788};
37789
37790Line.drawIcon = function (group, x, y, iconWidth, iconHeight, framework) {
37791 var fillHeight = iconHeight * 0.5;
37792 var path, fillPath;
37793 var outline = DOMutil.getSVGElement("rect", framework.svgElements, framework.svg);
37794 outline.setAttributeNS(null, "x", x);
37795 outline.setAttributeNS(null, "y", y - fillHeight);
37796 outline.setAttributeNS(null, "width", iconWidth);
37797 outline.setAttributeNS(null, "height", 2 * fillHeight);
37798 outline.setAttributeNS(null, "class", "vis-outline");
37799 path = DOMutil.getSVGElement("path", framework.svgElements, framework.svg);
37800 path.setAttributeNS(null, "class", group.className);
37801
37802 if (group.style !== undefined) {
37803 path.setAttributeNS(null, "style", group.style);
37804 }
37805
37806 path.setAttributeNS(null, "d", "M" + x + "," + y + " L" + (x + iconWidth) + "," + y + "");
37807
37808 if (group.options.shaded.enabled == true) {
37809 fillPath = DOMutil.getSVGElement("path", framework.svgElements, framework.svg);
37810
37811 if (group.options.shaded.orientation == 'top') {
37812 fillPath.setAttributeNS(null, "d", "M" + x + ", " + (y - fillHeight) + "L" + x + "," + y + " L" + (x + iconWidth) + "," + y + " L" + (x + iconWidth) + "," + (y - fillHeight));
37813 } else {
37814 fillPath.setAttributeNS(null, "d", "M" + x + "," + y + " " + "L" + x + "," + (y + fillHeight) + " " + "L" + (x + iconWidth) + "," + (y + fillHeight) + "L" + (x + iconWidth) + "," + y);
37815 }
37816
37817 fillPath.setAttributeNS(null, "class", group.className + " vis-icon-fill");
37818
37819 if (group.options.shaded.style !== undefined && group.options.shaded.style !== "") {
37820 fillPath.setAttributeNS(null, "style", group.options.shaded.style);
37821 }
37822 }
37823
37824 if (group.options.drawPoints.enabled == true) {
37825 var groupTemplate = {
37826 style: group.options.drawPoints.style,
37827 styles: group.options.drawPoints.styles,
37828 size: group.options.drawPoints.size,
37829 className: group.className
37830 };
37831 DOMutil.drawPoint(x + 0.5 * iconWidth, y, groupTemplate, framework.svgElements, framework.svg);
37832 }
37833};
37834
37835Line.drawShading = function (pathArray, group, subPathArray, framework) {
37836 // append shading to the path
37837 if (group.options.shaded.enabled == true) {
37838 var svgHeight = Number(framework.svg.style.height.replace('px', ''));
37839 var fillPath = DOMutil.getSVGElement('path', framework.svgElements, framework.svg);
37840 var type = "L";
37841
37842 if (group.options.interpolation.enabled == true) {
37843 type = "C";
37844 }
37845
37846 var dFill;
37847 var zero = 0;
37848
37849 if (group.options.shaded.orientation == 'top') {
37850 zero = 0;
37851 } else if (group.options.shaded.orientation == 'bottom') {
37852 zero = svgHeight;
37853 } else {
37854 zero = Math.min(Math.max(0, group.zeroPosition), svgHeight);
37855 }
37856
37857 if (group.options.shaded.orientation == 'group' && subPathArray != null && subPathArray != undefined) {
37858 dFill = 'M' + pathArray[0][0] + "," + pathArray[0][1] + " " + this.serializePath(pathArray, type, false) + ' L' + subPathArray[subPathArray.length - 1][0] + "," + subPathArray[subPathArray.length - 1][1] + " " + this.serializePath(subPathArray, type, true) + subPathArray[0][0] + "," + subPathArray[0][1] + " Z";
37859 } else {
37860 dFill = 'M' + pathArray[0][0] + "," + pathArray[0][1] + " " + this.serializePath(pathArray, type, false) + ' V' + zero + ' H' + pathArray[0][0] + " Z";
37861 }
37862
37863 fillPath.setAttributeNS(null, 'class', group.className + ' vis-fill');
37864
37865 if (group.options.shaded.style !== undefined) {
37866 fillPath.setAttributeNS(null, 'style', group.options.shaded.style);
37867 }
37868
37869 fillPath.setAttributeNS(null, 'd', dFill);
37870 }
37871};
37872/**
37873 * draw a line graph
37874 *
37875 * @param {Array.<Object>} pathArray
37876 * @param {vis.Group} group
37877 * @param {{svg: Object, svgElements: Array.<Object>, options: Object, groups: Array.<vis.Group>}} framework
37878 */
37879
37880
37881Line.draw = function (pathArray, group, framework) {
37882 if (pathArray != null && pathArray != undefined) {
37883 var path = DOMutil.getSVGElement('path', framework.svgElements, framework.svg);
37884 path.setAttributeNS(null, "class", group.className);
37885
37886 if (group.style !== undefined) {
37887 path.setAttributeNS(null, "style", group.style);
37888 }
37889
37890 var type = "L";
37891
37892 if (group.options.interpolation.enabled == true) {
37893 type = "C";
37894 } // copy properties to path for drawing.
37895
37896
37897 path.setAttributeNS(null, 'd', 'M' + pathArray[0][0] + "," + pathArray[0][1] + " " + this.serializePath(pathArray, type, false));
37898 }
37899};
37900
37901Line.serializePath = function (pathArray, type, inverse) {
37902 if (pathArray.length < 2) {
37903 //Too little data to create a path.
37904 return "";
37905 }
37906
37907 var d = type;
37908 var i;
37909
37910 if (inverse) {
37911 for (i = pathArray.length - 2; i > 0; i--) {
37912 d += pathArray[i][0] + "," + pathArray[i][1] + " ";
37913 }
37914 } else {
37915 for (i = 1; i < pathArray.length; i++) {
37916 d += pathArray[i][0] + "," + pathArray[i][1] + " ";
37917 }
37918 }
37919
37920 return d;
37921};
37922/**
37923 * This uses an uniform parametrization of the interpolation algorithm:
37924 * 'On the Parameterization of Catmull-Rom Curves' by Cem Yuksel et al.
37925 * @param {Array.<Object>} data
37926 * @returns {string}
37927 * @private
37928 */
37929
37930
37931Line._catmullRomUniform = function (data) {
37932 // catmull rom
37933 var p0, p1, p2, p3, bp1, bp2;
37934 var d = [];
37935 d.push([Math.round(data[0].screen_x), Math.round(data[0].screen_y)]);
37936 var normalization = 1 / 6;
37937 var length = data.length;
37938
37939 for (var i = 0; i < length - 1; i++) {
37940 p0 = i == 0 ? data[0] : data[i - 1];
37941 p1 = data[i];
37942 p2 = data[i + 1];
37943 p3 = i + 2 < length ? data[i + 2] : p2; // Catmull-Rom to Cubic Bezier conversion matrix
37944 // 0 1 0 0
37945 // -1/6 1 1/6 0
37946 // 0 1/6 1 -1/6
37947 // 0 0 1 0
37948 // bp0 = { x: p1.x, y: p1.y };
37949
37950 bp1 = {
37951 screen_x: (-p0.screen_x + 6 * p1.screen_x + p2.screen_x) * normalization,
37952 screen_y: (-p0.screen_y + 6 * p1.screen_y + p2.screen_y) * normalization
37953 };
37954 bp2 = {
37955 screen_x: (p1.screen_x + 6 * p2.screen_x - p3.screen_x) * normalization,
37956 screen_y: (p1.screen_y + 6 * p2.screen_y - p3.screen_y) * normalization
37957 }; // bp0 = { x: p2.x, y: p2.y };
37958
37959 d.push([bp1.screen_x, bp1.screen_y]);
37960 d.push([bp2.screen_x, bp2.screen_y]);
37961 d.push([p2.screen_x, p2.screen_y]);
37962 }
37963
37964 return d;
37965};
37966/**
37967 * This uses either the chordal or centripetal parameterization of the catmull-rom algorithm.
37968 * By default, the centripetal parameterization is used because this gives the nicest results.
37969 * These parameterizations are relatively heavy because the distance between 4 points have to be calculated.
37970 *
37971 * One optimization can be used to reuse distances since this is a sliding window approach.
37972 * @param {Array.<Object>} data
37973 * @param {vis.GraphGroup} group
37974 * @returns {string}
37975 * @private
37976 */
37977
37978
37979Line._catmullRom = function (data, group) {
37980 var alpha = group.options.interpolation.alpha;
37981
37982 if (alpha == 0 || alpha === undefined) {
37983 return this._catmullRomUniform(data);
37984 } else {
37985 var p0, p1, p2, p3, bp1, bp2, d1, d2, d3, A, B, N, M;
37986 var d3powA, d2powA, d3pow2A, d2pow2A, d1pow2A, d1powA;
37987 var d = [];
37988 d.push([Math.round(data[0].screen_x), Math.round(data[0].screen_y)]);
37989 var length = data.length;
37990
37991 for (var i = 0; i < length - 1; i++) {
37992 p0 = i == 0 ? data[0] : data[i - 1];
37993 p1 = data[i];
37994 p2 = data[i + 1];
37995 p3 = i + 2 < length ? data[i + 2] : p2;
37996 d1 = Math.sqrt(Math.pow(p0.screen_x - p1.screen_x, 2) + Math.pow(p0.screen_y - p1.screen_y, 2));
37997 d2 = Math.sqrt(Math.pow(p1.screen_x - p2.screen_x, 2) + Math.pow(p1.screen_y - p2.screen_y, 2));
37998 d3 = Math.sqrt(Math.pow(p2.screen_x - p3.screen_x, 2) + Math.pow(p2.screen_y - p3.screen_y, 2)); // Catmull-Rom to Cubic Bezier conversion matrix
37999 // A = 2d1^2a + 3d1^a * d2^a + d3^2a
38000 // B = 2d3^2a + 3d3^a * d2^a + d2^2a
38001 // [ 0 1 0 0 ]
38002 // [ -d2^2a /N A/N d1^2a /N 0 ]
38003 // [ 0 d3^2a /M B/M -d2^2a /M ]
38004 // [ 0 0 1 0 ]
38005
38006 d3powA = Math.pow(d3, alpha);
38007 d3pow2A = Math.pow(d3, 2 * alpha);
38008 d2powA = Math.pow(d2, alpha);
38009 d2pow2A = Math.pow(d2, 2 * alpha);
38010 d1powA = Math.pow(d1, alpha);
38011 d1pow2A = Math.pow(d1, 2 * alpha);
38012 A = 2 * d1pow2A + 3 * d1powA * d2powA + d2pow2A;
38013 B = 2 * d3pow2A + 3 * d3powA * d2powA + d2pow2A;
38014 N = 3 * d1powA * (d1powA + d2powA);
38015
38016 if (N > 0) {
38017 N = 1 / N;
38018 }
38019
38020 M = 3 * d3powA * (d3powA + d2powA);
38021
38022 if (M > 0) {
38023 M = 1 / M;
38024 }
38025
38026 bp1 = {
38027 screen_x: (-d2pow2A * p0.screen_x + A * p1.screen_x + d1pow2A * p2.screen_x) * N,
38028 screen_y: (-d2pow2A * p0.screen_y + A * p1.screen_y + d1pow2A * p2.screen_y) * N
38029 };
38030 bp2 = {
38031 screen_x: (d3pow2A * p1.screen_x + B * p2.screen_x - d2pow2A * p3.screen_x) * M,
38032 screen_y: (d3pow2A * p1.screen_y + B * p2.screen_y - d2pow2A * p3.screen_y) * M
38033 };
38034
38035 if (bp1.screen_x == 0 && bp1.screen_y == 0) {
38036 bp1 = p1;
38037 }
38038
38039 if (bp2.screen_x == 0 && bp2.screen_y == 0) {
38040 bp2 = p2;
38041 }
38042
38043 d.push([bp1.screen_x, bp1.screen_y]);
38044 d.push([bp2.screen_x, bp2.screen_y]);
38045 d.push([p2.screen_x, p2.screen_y]);
38046 }
38047
38048 return d;
38049 }
38050};
38051/**
38052 * this generates the SVG path for a linear drawing between datapoints.
38053 * @param {Array.<Object>} data
38054 * @returns {string}
38055 * @private
38056 */
38057
38058
38059Line._linear = function (data) {
38060 // linear
38061 var d = [];
38062
38063 for (var i = 0; i < data.length; i++) {
38064 d.push([data[i].screen_x, data[i].screen_y]);
38065 }
38066
38067 return d;
38068};
38069
38070var line = Line;
38071
38072/**
38073 * /**
38074 * @param {object} group | the object of the group from the dataset
38075 * @param {string} groupId | ID of the group
38076 * @param {object} options | the default options
38077 * @param {array} groupsUsingDefaultStyles | this array has one entree.
38078 * It is passed as an array so it is passed by reference.
38079 * It enumerates through the default styles
38080 * @constructor GraphGroup
38081 */
38082
38083function GraphGroup(group, groupId, options, groupsUsingDefaultStyles) {
38084 this.id = groupId;
38085 var fields = ['sampling', 'style', 'sort', 'yAxisOrientation', 'barChart', 'drawPoints', 'shaded', 'interpolation', 'zIndex', 'excludeFromStacking', 'excludeFromLegend'];
38086 this.options = util.selectiveBridgeObject(fields, options);
38087 this.usingDefaultStyle = group.className === undefined;
38088 this.groupsUsingDefaultStyles = groupsUsingDefaultStyles;
38089 this.zeroPosition = 0;
38090 this.update(group);
38091
38092 if (this.usingDefaultStyle == true) {
38093 this.groupsUsingDefaultStyles[0] += 1;
38094 }
38095
38096 this.itemsData = [];
38097 this.visible = group.visible === undefined ? true : group.visible;
38098}
38099/**
38100 * this loads a reference to all items in this group into this group.
38101 * @param {array} items
38102 */
38103
38104
38105GraphGroup.prototype.setItems = function (items) {
38106 if (items != null) {
38107 this.itemsData = items;
38108
38109 if (this.options.sort == true) {
38110 util.insertSort(this.itemsData, function (a, b) {
38111 return a.x > b.x ? 1 : -1;
38112 });
38113 }
38114 } else {
38115 this.itemsData = [];
38116 }
38117};
38118
38119GraphGroup.prototype.getItems = function () {
38120 return this.itemsData;
38121};
38122/**
38123 * this is used for barcharts and shading, this way, we only have to calculate it once.
38124 * @param {number} pos
38125 */
38126
38127
38128GraphGroup.prototype.setZeroPosition = function (pos) {
38129 this.zeroPosition = pos;
38130};
38131/**
38132 * set the options of the graph group over the default options.
38133 * @param {Object} options
38134 */
38135
38136
38137GraphGroup.prototype.setOptions = function (options) {
38138 if (options !== undefined) {
38139 var fields = ['sampling', 'style', 'sort', 'yAxisOrientation', 'barChart', 'zIndex', 'excludeFromStacking', 'excludeFromLegend'];
38140 util.selectiveDeepExtend(fields, this.options, options); // if the group's drawPoints is a function delegate the callback to the onRender property
38141
38142 if (typeof options.drawPoints == 'function') {
38143 options.drawPoints = {
38144 onRender: options.drawPoints
38145 };
38146 }
38147
38148 util.mergeOptions(this.options, options, 'interpolation');
38149 util.mergeOptions(this.options, options, 'drawPoints');
38150 util.mergeOptions(this.options, options, 'shaded');
38151
38152 if (options.interpolation) {
38153 if (_typeof$1(options.interpolation) == 'object') {
38154 if (options.interpolation.parametrization) {
38155 if (options.interpolation.parametrization == 'uniform') {
38156 this.options.interpolation.alpha = 0;
38157 } else if (options.interpolation.parametrization == 'chordal') {
38158 this.options.interpolation.alpha = 1.0;
38159 } else {
38160 this.options.interpolation.parametrization = 'centripetal';
38161 this.options.interpolation.alpha = 0.5;
38162 }
38163 }
38164 }
38165 }
38166 }
38167};
38168/**
38169 * this updates the current group class with the latest group dataset entree, used in _updateGroup in linegraph
38170 * @param {vis.Group} group
38171 */
38172
38173
38174GraphGroup.prototype.update = function (group) {
38175 this.group = group;
38176 this.content = group.content || 'graph';
38177 this.className = group.className || this.className || 'vis-graph-group' + this.groupsUsingDefaultStyles[0] % 10;
38178 this.visible = group.visible === undefined ? true : group.visible;
38179 this.style = group.style;
38180 this.setOptions(group.options);
38181};
38182/**
38183 * return the legend entree for this group.
38184 *
38185 * @param {number} iconWidth
38186 * @param {number} iconHeight
38187 * @param {{svg: (*|Element), svgElements: Object, options: Object, groups: Array.<Object>}} framework
38188 * @param {number} x
38189 * @param {number} y
38190 * @returns {{icon: (*|Element), label: (*|string), orientation: *}}
38191 */
38192
38193
38194GraphGroup.prototype.getLegend = function (iconWidth, iconHeight, framework, x, y) {
38195 if (framework == undefined || framework == null) {
38196 var svg = document.createElementNS('http://www.w3.org/2000/svg', "svg");
38197 framework = {
38198 svg: svg,
38199 svgElements: {},
38200 options: this.options,
38201 groups: [this]
38202 };
38203 }
38204
38205 if (x == undefined || x == null) {
38206 x = 0;
38207 }
38208
38209 if (y == undefined || y == null) {
38210 y = 0.5 * iconHeight;
38211 }
38212
38213 switch (this.options.style) {
38214 case "line":
38215 line.drawIcon(this, x, y, iconWidth, iconHeight, framework);
38216 break;
38217
38218 case "points": //explicit no break
38219
38220 case "point":
38221 points.drawIcon(this, x, y, iconWidth, iconHeight, framework);
38222 break;
38223
38224 case "bar":
38225 bar.drawIcon(this, x, y, iconWidth, iconHeight, framework);
38226 break;
38227 }
38228
38229 return {
38230 icon: framework.svg,
38231 label: this.content,
38232 orientation: this.options.yAxisOrientation
38233 };
38234};
38235
38236GraphGroup.prototype.getYRange = function (groupData) {
38237 var yMin = groupData[0].y;
38238 var yMax = groupData[0].y;
38239
38240 for (var j = 0; j < groupData.length; j++) {
38241 yMin = yMin > groupData[j].y ? groupData[j].y : yMin;
38242 yMax = yMax < groupData[j].y ? groupData[j].y : yMax;
38243 }
38244
38245 return {
38246 min: yMin,
38247 max: yMax,
38248 yAxisOrientation: this.options.yAxisOrientation
38249 };
38250};
38251
38252var GraphGroup_1 = GraphGroup;
38253
38254/**
38255 * Legend for Graph2d
38256 *
38257 * @param {vis.Graph2d.body} body
38258 * @param {vis.Graph2d.options} options
38259 * @param {number} side
38260 * @param {vis.LineGraph.options} linegraphOptions
38261 * @constructor Legend
38262 * @extends Component
38263 */
38264
38265function Legend(body, options, side, linegraphOptions) {
38266 this.body = body;
38267 this.defaultOptions = {
38268 enabled: false,
38269 icons: true,
38270 iconSize: 20,
38271 iconSpacing: 6,
38272 left: {
38273 visible: true,
38274 position: 'top-left' // top/bottom - left,center,right
38275
38276 },
38277 right: {
38278 visible: true,
38279 position: 'top-right' // top/bottom - left,center,right
38280
38281 }
38282 };
38283 this.side = side;
38284 this.options = util.extend({}, this.defaultOptions);
38285 this.linegraphOptions = linegraphOptions;
38286 this.svgElements = {};
38287 this.dom = {};
38288 this.groups = {};
38289 this.amountOfGroups = 0;
38290
38291 this._create();
38292
38293 this.framework = {
38294 svg: this.svg,
38295 svgElements: this.svgElements,
38296 options: this.options,
38297 groups: this.groups
38298 };
38299 this.setOptions(options);
38300}
38301
38302Legend.prototype = new Component_1();
38303
38304Legend.prototype.clear = function () {
38305 this.groups = {};
38306 this.amountOfGroups = 0;
38307};
38308
38309Legend.prototype.addGroup = function (label, graphOptions) {
38310 // Include a group only if the group option 'excludeFromLegend: false' is not set.
38311 if (graphOptions.options.excludeFromLegend != true) {
38312 if (!this.groups.hasOwnProperty(label)) {
38313 this.groups[label] = graphOptions;
38314 }
38315
38316 this.amountOfGroups += 1;
38317 }
38318};
38319
38320Legend.prototype.updateGroup = function (label, graphOptions) {
38321 this.groups[label] = graphOptions;
38322};
38323
38324Legend.prototype.removeGroup = function (label) {
38325 if (this.groups.hasOwnProperty(label)) {
38326 delete this.groups[label];
38327 this.amountOfGroups -= 1;
38328 }
38329};
38330
38331Legend.prototype._create = function () {
38332 this.dom.frame = document.createElement('div');
38333 this.dom.frame.className = 'vis-legend';
38334 this.dom.frame.style.position = "absolute";
38335 this.dom.frame.style.top = "10px";
38336 this.dom.frame.style.display = "block";
38337 this.dom.textArea = document.createElement('div');
38338 this.dom.textArea.className = 'vis-legend-text';
38339 this.dom.textArea.style.position = "relative";
38340 this.dom.textArea.style.top = "0px";
38341 this.svg = document.createElementNS('http://www.w3.org/2000/svg', "svg");
38342 this.svg.style.position = 'absolute';
38343 this.svg.style.top = 0 + 'px';
38344 this.svg.style.width = this.options.iconSize + 5 + 'px';
38345 this.svg.style.height = '100%';
38346 this.dom.frame.appendChild(this.svg);
38347 this.dom.frame.appendChild(this.dom.textArea);
38348};
38349/**
38350 * Hide the component from the DOM
38351 */
38352
38353
38354Legend.prototype.hide = function () {
38355 // remove the frame containing the items
38356 if (this.dom.frame.parentNode) {
38357 this.dom.frame.parentNode.removeChild(this.dom.frame);
38358 }
38359};
38360/**
38361 * Show the component in the DOM (when not already visible).
38362 */
38363
38364
38365Legend.prototype.show = function () {
38366 // show frame containing the items
38367 if (!this.dom.frame.parentNode) {
38368 this.body.dom.center.appendChild(this.dom.frame);
38369 }
38370};
38371
38372Legend.prototype.setOptions = function (options) {
38373 var fields = ['enabled', 'orientation', 'icons', 'left', 'right'];
38374 util.selectiveDeepExtend(fields, this.options, options);
38375};
38376
38377Legend.prototype.redraw = function () {
38378 var activeGroups = 0;
38379 var groupArray = Object.keys(this.groups);
38380 groupArray.sort(function (a, b) {
38381 return a < b ? -1 : 1;
38382 });
38383
38384 for (var i = 0; i < groupArray.length; i++) {
38385 var groupId = groupArray[i];
38386
38387 if (this.groups[groupId].visible == true && (this.linegraphOptions.visibility[groupId] === undefined || this.linegraphOptions.visibility[groupId] == true)) {
38388 activeGroups++;
38389 }
38390 }
38391
38392 if (this.options[this.side].visible == false || this.amountOfGroups == 0 || this.options.enabled == false || activeGroups == 0) {
38393 this.hide();
38394 } else {
38395 this.show();
38396
38397 if (this.options[this.side].position == 'top-left' || this.options[this.side].position == 'bottom-left') {
38398 this.dom.frame.style.left = '4px';
38399 this.dom.frame.style.textAlign = "left";
38400 this.dom.textArea.style.textAlign = "left";
38401 this.dom.textArea.style.left = this.options.iconSize + 15 + 'px';
38402 this.dom.textArea.style.right = '';
38403 this.svg.style.left = 0 + 'px';
38404 this.svg.style.right = '';
38405 } else {
38406 this.dom.frame.style.right = '4px';
38407 this.dom.frame.style.textAlign = "right";
38408 this.dom.textArea.style.textAlign = "right";
38409 this.dom.textArea.style.right = this.options.iconSize + 15 + 'px';
38410 this.dom.textArea.style.left = '';
38411 this.svg.style.right = 0 + 'px';
38412 this.svg.style.left = '';
38413 }
38414
38415 if (this.options[this.side].position == 'top-left' || this.options[this.side].position == 'top-right') {
38416 this.dom.frame.style.top = 4 - Number(this.body.dom.center.style.top.replace("px", "")) + 'px';
38417 this.dom.frame.style.bottom = '';
38418 } else {
38419 var scrollableHeight = this.body.domProps.center.height - this.body.domProps.centerContainer.height;
38420 this.dom.frame.style.bottom = 4 + scrollableHeight + Number(this.body.dom.center.style.top.replace("px", "")) + 'px';
38421 this.dom.frame.style.top = '';
38422 }
38423
38424 if (this.options.icons == false) {
38425 this.dom.frame.style.width = this.dom.textArea.offsetWidth + 10 + 'px';
38426 this.dom.textArea.style.right = '';
38427 this.dom.textArea.style.left = '';
38428 this.svg.style.width = '0px';
38429 } else {
38430 this.dom.frame.style.width = this.options.iconSize + 15 + this.dom.textArea.offsetWidth + 10 + 'px';
38431 this.drawLegendIcons();
38432 }
38433
38434 var content = '';
38435
38436 for (i = 0; i < groupArray.length; i++) {
38437 groupId = groupArray[i];
38438
38439 if (this.groups[groupId].visible == true && (this.linegraphOptions.visibility[groupId] === undefined || this.linegraphOptions.visibility[groupId] == true)) {
38440 content += this.groups[groupId].content + '<br />';
38441 }
38442 }
38443
38444 this.dom.textArea.innerHTML = content;
38445 this.dom.textArea.style.lineHeight = 0.75 * this.options.iconSize + this.options.iconSpacing + 'px';
38446 }
38447};
38448
38449Legend.prototype.drawLegendIcons = function () {
38450 if (this.dom.frame.parentNode) {
38451 var groupArray = Object.keys(this.groups);
38452 groupArray.sort(function (a, b) {
38453 return a < b ? -1 : 1;
38454 }); // this resets the elements so the order is maintained
38455
38456 DOMutil.resetElements(this.svgElements);
38457 var padding = window.getComputedStyle(this.dom.frame).paddingTop;
38458 var iconOffset = Number(padding.replace('px', ''));
38459 var x = iconOffset;
38460 var iconWidth = this.options.iconSize;
38461 var iconHeight = 0.75 * this.options.iconSize;
38462 var y = iconOffset + 0.5 * iconHeight + 3;
38463 this.svg.style.width = iconWidth + 5 + iconOffset + 'px';
38464
38465 for (var i = 0; i < groupArray.length; i++) {
38466 var groupId = groupArray[i];
38467
38468 if (this.groups[groupId].visible == true && (this.linegraphOptions.visibility[groupId] === undefined || this.linegraphOptions.visibility[groupId] == true)) {
38469 this.groups[groupId].getLegend(iconWidth, iconHeight, this.framework, x, y);
38470 y += iconHeight + this.options.iconSpacing;
38471 }
38472 }
38473 }
38474};
38475
38476var Legend_1 = Legend;
38477
38478var DataSet$2 = index.DataSet;
38479var DataView$2 = index.DataView;
38480var UNGROUPED$1 = '__ungrouped__'; // reserved group id for ungrouped items
38481
38482/**
38483 * This is the constructor of the LineGraph. It requires a Timeline body and options.
38484 *
38485 * @param {vis.Timeline.body} body
38486 * @param {Object} options
38487 * @constructor LineGraph
38488 * @extends Component
38489 */
38490
38491function LineGraph(body, options) {
38492 this.id = util.randomUUID();
38493 this.body = body;
38494 this.defaultOptions = {
38495 yAxisOrientation: 'left',
38496 defaultGroup: 'default',
38497 sort: true,
38498 sampling: true,
38499 stack: false,
38500 graphHeight: '400px',
38501 shaded: {
38502 enabled: false,
38503 orientation: 'bottom' // top, bottom, zero
38504
38505 },
38506 style: 'line',
38507 // line, bar
38508 barChart: {
38509 width: 50,
38510 sideBySide: false,
38511 align: 'center' // left, center, right
38512
38513 },
38514 interpolation: {
38515 enabled: true,
38516 parametrization: 'centripetal',
38517 // uniform (alpha = 0.0), chordal (alpha = 1.0), centripetal (alpha = 0.5)
38518 alpha: 0.5
38519 },
38520 drawPoints: {
38521 enabled: true,
38522 size: 6,
38523 style: 'square' // square, circle
38524
38525 },
38526 dataAxis: {},
38527 //Defaults are done on DataAxis level
38528 legend: {},
38529 //Defaults are done on Legend level
38530 groups: {
38531 visibility: {}
38532 }
38533 }; // options is shared by this lineGraph and all its items
38534
38535 this.options = util.extend({}, this.defaultOptions);
38536 this.dom = {};
38537 this.props = {};
38538 this.hammer = null;
38539 this.groups = {};
38540 this.abortedGraphUpdate = false;
38541 this.updateSVGheight = false;
38542 this.updateSVGheightOnResize = false;
38543 this.forceGraphUpdate = true;
38544 var me = this;
38545 this.itemsData = null; // DataSet
38546
38547 this.groupsData = null; // DataSet
38548 // listeners for the DataSet of the items
38549
38550 this.itemListeners = {
38551 'add': function add(event, params, senderId) {
38552 // eslint-disable-line no-unused-vars
38553 me._onAdd(params.items);
38554 },
38555 'update': function update(event, params, senderId) {
38556 // eslint-disable-line no-unused-vars
38557 me._onUpdate(params.items);
38558 },
38559 'remove': function remove(event, params, senderId) {
38560 // eslint-disable-line no-unused-vars
38561 me._onRemove(params.items);
38562 }
38563 }; // listeners for the DataSet of the groups
38564
38565 this.groupListeners = {
38566 'add': function add(event, params, senderId) {
38567 // eslint-disable-line no-unused-vars
38568 me._onAddGroups(params.items);
38569 },
38570 'update': function update(event, params, senderId) {
38571 // eslint-disable-line no-unused-vars
38572 me._onUpdateGroups(params.items);
38573 },
38574 'remove': function remove(event, params, senderId) {
38575 // eslint-disable-line no-unused-vars
38576 me._onRemoveGroups(params.items);
38577 }
38578 };
38579 this.items = {}; // object with an Item for every data item
38580
38581 this.selection = []; // list with the ids of all selected nodes
38582
38583 this.lastStart = this.body.range.start;
38584 this.touchParams = {}; // stores properties while dragging
38585
38586 this.svgElements = {};
38587 this.setOptions(options);
38588 this.groupsUsingDefaultStyles = [0];
38589 this.body.emitter.on('rangechanged', function () {
38590 me.lastStart = me.body.range.start;
38591 me.svg.style.left = util.option.asSize(-me.props.width);
38592 me.forceGraphUpdate = true; //Is this local redraw necessary? (Core also does a change event!)
38593
38594 me.redraw.call(me);
38595 }); // create the HTML DOM
38596
38597 this._create();
38598
38599 this.framework = {
38600 svg: this.svg,
38601 svgElements: this.svgElements,
38602 options: this.options,
38603 groups: this.groups
38604 };
38605}
38606
38607LineGraph.prototype = new Component_1();
38608/**
38609 * Create the HTML DOM for the ItemSet
38610 */
38611
38612LineGraph.prototype._create = function () {
38613 var frame = document.createElement('div');
38614 frame.className = 'vis-line-graph';
38615 this.dom.frame = frame; // create svg element for graph drawing.
38616
38617 this.svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
38618 this.svg.style.position = 'relative';
38619 this.svg.style.height = ('' + this.options.graphHeight).replace('px', '') + 'px';
38620 this.svg.style.display = 'block';
38621 frame.appendChild(this.svg); // data axis
38622
38623 this.options.dataAxis.orientation = 'left';
38624 this.yAxisLeft = new DataAxis(this.body, this.options.dataAxis, this.svg, this.options.groups);
38625 this.options.dataAxis.orientation = 'right';
38626 this.yAxisRight = new DataAxis(this.body, this.options.dataAxis, this.svg, this.options.groups);
38627 delete this.options.dataAxis.orientation; // legends
38628
38629 this.legendLeft = new Legend_1(this.body, this.options.legend, 'left', this.options.groups);
38630 this.legendRight = new Legend_1(this.body, this.options.legend, 'right', this.options.groups);
38631 this.show();
38632};
38633/**
38634 * set the options of the LineGraph. the mergeOptions is used for subObjects that have an enabled element.
38635 * @param {object} options
38636 */
38637
38638
38639LineGraph.prototype.setOptions = function (options) {
38640 if (options) {
38641 var fields = ['sampling', 'defaultGroup', 'stack', 'height', 'graphHeight', 'yAxisOrientation', 'style', 'barChart', 'dataAxis', 'sort', 'groups'];
38642
38643 if (options.graphHeight === undefined && options.height !== undefined) {
38644 this.updateSVGheight = true;
38645 this.updateSVGheightOnResize = true;
38646 } else if (this.body.domProps.centerContainer.height !== undefined && options.graphHeight !== undefined) {
38647 if (parseInt((options.graphHeight + '').replace("px", '')) < this.body.domProps.centerContainer.height) {
38648 this.updateSVGheight = true;
38649 }
38650 }
38651
38652 util.selectiveDeepExtend(fields, this.options, options);
38653 util.mergeOptions(this.options, options, 'interpolation');
38654 util.mergeOptions(this.options, options, 'drawPoints');
38655 util.mergeOptions(this.options, options, 'shaded');
38656 util.mergeOptions(this.options, options, 'legend');
38657
38658 if (options.interpolation) {
38659 if (_typeof$1(options.interpolation) == 'object') {
38660 if (options.interpolation.parametrization) {
38661 if (options.interpolation.parametrization == 'uniform') {
38662 this.options.interpolation.alpha = 0;
38663 } else if (options.interpolation.parametrization == 'chordal') {
38664 this.options.interpolation.alpha = 1.0;
38665 } else {
38666 this.options.interpolation.parametrization = 'centripetal';
38667 this.options.interpolation.alpha = 0.5;
38668 }
38669 }
38670 }
38671 }
38672
38673 if (this.yAxisLeft) {
38674 if (options.dataAxis !== undefined) {
38675 this.yAxisLeft.setOptions(this.options.dataAxis);
38676 this.yAxisRight.setOptions(this.options.dataAxis);
38677 }
38678 }
38679
38680 if (this.legendLeft) {
38681 if (options.legend !== undefined) {
38682 this.legendLeft.setOptions(this.options.legend);
38683 this.legendRight.setOptions(this.options.legend);
38684 }
38685 }
38686
38687 if (this.groups.hasOwnProperty(UNGROUPED$1)) {
38688 this.groups[UNGROUPED$1].setOptions(options);
38689 }
38690 } // this is used to redraw the graph if the visibility of the groups is changed.
38691
38692
38693 if (this.dom.frame) {
38694 //not on initial run?
38695 this.forceGraphUpdate = true;
38696 this.body.emitter.emit("_change", {
38697 queue: true
38698 });
38699 }
38700};
38701/**
38702 * Hide the component from the DOM
38703 */
38704
38705
38706LineGraph.prototype.hide = function () {
38707 // remove the frame containing the items
38708 if (this.dom.frame.parentNode) {
38709 this.dom.frame.parentNode.removeChild(this.dom.frame);
38710 }
38711};
38712/**
38713 * Show the component in the DOM (when not already visible).
38714 */
38715
38716
38717LineGraph.prototype.show = function () {
38718 // show frame containing the items
38719 if (!this.dom.frame.parentNode) {
38720 this.body.dom.center.appendChild(this.dom.frame);
38721 }
38722};
38723/**
38724 * Set items
38725 * @param {vis.DataSet | null} items
38726 */
38727
38728
38729LineGraph.prototype.setItems = function (items) {
38730 var me = this,
38731 ids,
38732 oldItemsData = this.itemsData; // replace the dataset
38733
38734 if (!items) {
38735 this.itemsData = null;
38736 } else if (items instanceof DataSet$2 || items instanceof DataView$2) {
38737 this.itemsData = items;
38738 } else {
38739 throw new TypeError('Data must be an instance of DataSet or DataView');
38740 }
38741
38742 if (oldItemsData) {
38743 // unsubscribe from old dataset
38744 util.forEach(this.itemListeners, function (callback, event) {
38745 oldItemsData.off(event, callback);
38746 }); // remove all drawn items
38747
38748 ids = oldItemsData.getIds();
38749
38750 this._onRemove(ids);
38751 }
38752
38753 if (this.itemsData) {
38754 // subscribe to new dataset
38755 var id = this.id;
38756 util.forEach(this.itemListeners, function (callback, event) {
38757 me.itemsData.on(event, callback, id);
38758 }); // add all new items
38759
38760 ids = this.itemsData.getIds();
38761
38762 this._onAdd(ids);
38763 }
38764};
38765/**
38766 * Set groups
38767 * @param {vis.DataSet} groups
38768 */
38769
38770
38771LineGraph.prototype.setGroups = function (groups) {
38772 var me = this;
38773 var ids; // unsubscribe from current dataset
38774
38775 if (this.groupsData) {
38776 util.forEach(this.groupListeners, function (callback, event) {
38777 me.groupsData.off(event, callback);
38778 }); // remove all drawn groups
38779
38780 ids = this.groupsData.getIds();
38781 this.groupsData = null;
38782
38783 for (var i = 0; i < ids.length; i++) {
38784 this._removeGroup(ids[i]);
38785 }
38786 } // replace the dataset
38787
38788
38789 if (!groups) {
38790 this.groupsData = null;
38791 } else if (groups instanceof DataSet$2 || groups instanceof DataView$2) {
38792 this.groupsData = groups;
38793 } else {
38794 throw new TypeError('Data must be an instance of DataSet or DataView');
38795 }
38796
38797 if (this.groupsData) {
38798 // subscribe to new dataset
38799 var id = this.id;
38800 util.forEach(this.groupListeners, function (callback, event) {
38801 me.groupsData.on(event, callback, id);
38802 }); // draw all ms
38803
38804 ids = this.groupsData.getIds();
38805
38806 this._onAddGroups(ids);
38807 }
38808};
38809
38810LineGraph.prototype._onUpdate = function (ids) {
38811 this._updateAllGroupData(ids);
38812};
38813
38814LineGraph.prototype._onAdd = function (ids) {
38815 this._onUpdate(ids);
38816};
38817
38818LineGraph.prototype._onRemove = function (ids) {
38819 this._onUpdate(ids);
38820};
38821
38822LineGraph.prototype._onUpdateGroups = function (groupIds) {
38823 this._updateAllGroupData(null, groupIds);
38824};
38825
38826LineGraph.prototype._onAddGroups = function (groupIds) {
38827 this._onUpdateGroups(groupIds);
38828};
38829/**
38830 * this cleans the group out off the legends and the dataaxis, updates the ungrouped and updates the graph
38831 * @param {Array} groupIds
38832 * @private
38833 */
38834
38835
38836LineGraph.prototype._onRemoveGroups = function (groupIds) {
38837 for (var i = 0; i < groupIds.length; i++) {
38838 this._removeGroup(groupIds[i]);
38839 }
38840
38841 this.forceGraphUpdate = true;
38842 this.body.emitter.emit("_change", {
38843 queue: true
38844 });
38845};
38846/**
38847 * this cleans the group out off the legends and the dataaxis
38848 * @param {vis.GraphGroup.id} groupId
38849 * @private
38850 */
38851
38852
38853LineGraph.prototype._removeGroup = function (groupId) {
38854 if (this.groups.hasOwnProperty(groupId)) {
38855 if (this.groups[groupId].options.yAxisOrientation == 'right') {
38856 this.yAxisRight.removeGroup(groupId);
38857 this.legendRight.removeGroup(groupId);
38858 this.legendRight.redraw();
38859 } else {
38860 this.yAxisLeft.removeGroup(groupId);
38861 this.legendLeft.removeGroup(groupId);
38862 this.legendLeft.redraw();
38863 }
38864
38865 delete this.groups[groupId];
38866 }
38867};
38868/**
38869 * update a group object with the group dataset entree
38870 *
38871 * @param {vis.GraphGroup} group
38872 * @param {vis.GraphGroup.id} groupId
38873 * @private
38874 */
38875
38876
38877LineGraph.prototype._updateGroup = function (group, groupId) {
38878 if (!this.groups.hasOwnProperty(groupId)) {
38879 this.groups[groupId] = new GraphGroup_1(group, groupId, this.options, this.groupsUsingDefaultStyles);
38880
38881 if (this.groups[groupId].options.yAxisOrientation == 'right') {
38882 this.yAxisRight.addGroup(groupId, this.groups[groupId]);
38883 this.legendRight.addGroup(groupId, this.groups[groupId]);
38884 } else {
38885 this.yAxisLeft.addGroup(groupId, this.groups[groupId]);
38886 this.legendLeft.addGroup(groupId, this.groups[groupId]);
38887 }
38888 } else {
38889 this.groups[groupId].update(group);
38890
38891 if (this.groups[groupId].options.yAxisOrientation == 'right') {
38892 this.yAxisRight.updateGroup(groupId, this.groups[groupId]);
38893 this.legendRight.updateGroup(groupId, this.groups[groupId]); //If yAxisOrientation changed, clean out the group from the other axis.
38894
38895 this.yAxisLeft.removeGroup(groupId);
38896 this.legendLeft.removeGroup(groupId);
38897 } else {
38898 this.yAxisLeft.updateGroup(groupId, this.groups[groupId]);
38899 this.legendLeft.updateGroup(groupId, this.groups[groupId]); //If yAxisOrientation changed, clean out the group from the other axis.
38900
38901 this.yAxisRight.removeGroup(groupId);
38902 this.legendRight.removeGroup(groupId);
38903 }
38904 }
38905
38906 this.legendLeft.redraw();
38907 this.legendRight.redraw();
38908};
38909/**
38910 * this updates all groups, it is used when there is an update the the itemset.
38911 *
38912 * @param {Array} ids
38913 * @param {Array} groupIds
38914 * @private
38915 */
38916
38917
38918LineGraph.prototype._updateAllGroupData = function (ids, groupIds) {
38919 if (this.itemsData != null) {
38920 var groupsContent = {};
38921 var items = this.itemsData.get();
38922 var fieldId = this.itemsData._fieldId;
38923 var idMap = {};
38924
38925 if (ids) {
38926 ids.map(function (id) {
38927 idMap[id] = id;
38928 });
38929 } //pre-Determine array sizes, for more efficient memory claim
38930
38931
38932 var groupCounts = {};
38933
38934 for (var i = 0; i < items.length; i++) {
38935 var item = items[i];
38936 var groupId = item.group;
38937
38938 if (groupId === null || groupId === undefined) {
38939 groupId = UNGROUPED$1;
38940 }
38941
38942 groupCounts.hasOwnProperty(groupId) ? groupCounts[groupId]++ : groupCounts[groupId] = 1;
38943 } //Pre-load arrays from existing groups if items are not changed (not in ids)
38944
38945
38946 var existingItemsMap = {};
38947
38948 if (!groupIds && ids) {
38949 for (groupId in this.groups) {
38950 if (this.groups.hasOwnProperty(groupId)) {
38951 group = this.groups[groupId];
38952 var existing_items = group.getItems();
38953 groupsContent[groupId] = existing_items.filter(function (item) {
38954 existingItemsMap[item[fieldId]] = item[fieldId];
38955 return item[fieldId] !== idMap[item[fieldId]];
38956 });
38957 var newLength = groupCounts[groupId];
38958 groupCounts[groupId] -= groupsContent[groupId].length;
38959
38960 if (groupsContent[groupId].length < newLength) {
38961 groupsContent[groupId][newLength - 1] = {};
38962 }
38963 }
38964 }
38965 } //Now insert data into the arrays.
38966
38967
38968 for (i = 0; i < items.length; i++) {
38969 item = items[i];
38970 groupId = item.group;
38971
38972 if (groupId === null || groupId === undefined) {
38973 groupId = UNGROUPED$1;
38974 }
38975
38976 if (!groupIds && ids && item[fieldId] !== idMap[item[fieldId]] && existingItemsMap.hasOwnProperty(item[fieldId])) {
38977 continue;
38978 }
38979
38980 if (!groupsContent.hasOwnProperty(groupId)) {
38981 groupsContent[groupId] = new Array(groupCounts[groupId]);
38982 } //Copy data (because of unmodifiable DataView input.
38983
38984
38985 var extended = util.bridgeObject(item);
38986 extended.x = util.convert(item.x, 'Date');
38987 extended.end = util.convert(item.end, 'Date');
38988 extended.orginalY = item.y; //real Y
38989
38990 extended.y = Number(item.y);
38991 extended[fieldId] = item[fieldId];
38992 var index = groupsContent[groupId].length - groupCounts[groupId]--;
38993 groupsContent[groupId][index] = extended;
38994 } //Make sure all groups are present, to allow removal of old groups
38995
38996
38997 for (groupId in this.groups) {
38998 if (this.groups.hasOwnProperty(groupId)) {
38999 if (!groupsContent.hasOwnProperty(groupId)) {
39000 groupsContent[groupId] = new Array(0);
39001 }
39002 }
39003 } //Update legendas, style and axis
39004
39005
39006 for (groupId in groupsContent) {
39007 if (groupsContent.hasOwnProperty(groupId)) {
39008 if (groupsContent[groupId].length == 0) {
39009 if (this.groups.hasOwnProperty(groupId)) {
39010 this._removeGroup(groupId);
39011 }
39012 } else {
39013 var group = undefined;
39014
39015 if (this.groupsData != undefined) {
39016 group = this.groupsData.get(groupId);
39017 }
39018
39019 if (group == undefined) {
39020 group = {
39021 id: groupId,
39022 content: this.options.defaultGroup + groupId
39023 };
39024 }
39025
39026 this._updateGroup(group, groupId);
39027
39028 this.groups[groupId].setItems(groupsContent[groupId]);
39029 }
39030 }
39031 }
39032
39033 this.forceGraphUpdate = true;
39034 this.body.emitter.emit("_change", {
39035 queue: true
39036 });
39037 }
39038};
39039/**
39040 * Redraw the component, mandatory function
39041 * @return {boolean} Returns true if the component is resized
39042 */
39043
39044
39045LineGraph.prototype.redraw = function () {
39046 var resized = false; // calculate actual size and position
39047
39048 this.props.width = this.dom.frame.offsetWidth;
39049 this.props.height = this.body.domProps.centerContainer.height - this.body.domProps.border.top - this.body.domProps.border.bottom; // check if this component is resized
39050
39051 resized = this._isResized() || resized; // check whether zoomed (in that case we need to re-stack everything)
39052
39053 var visibleInterval = this.body.range.end - this.body.range.start;
39054 var zoomed = visibleInterval != this.lastVisibleInterval;
39055 this.lastVisibleInterval = visibleInterval; // the svg element is three times as big as the width, this allows for fully dragging left and right
39056 // without reloading the graph. the controls for this are bound to events in the constructor
39057
39058 if (resized == true) {
39059 this.svg.style.width = util.option.asSize(3 * this.props.width);
39060 this.svg.style.left = util.option.asSize(-this.props.width); // if the height of the graph is set as proportional, change the height of the svg
39061
39062 if ((this.options.height + '').indexOf("%") != -1 || this.updateSVGheightOnResize == true) {
39063 this.updateSVGheight = true;
39064 }
39065 } // update the height of the graph on each redraw of the graph.
39066
39067
39068 if (this.updateSVGheight == true) {
39069 if (this.options.graphHeight != this.props.height + 'px') {
39070 this.options.graphHeight = this.props.height + 'px';
39071 this.svg.style.height = this.props.height + 'px';
39072 }
39073
39074 this.updateSVGheight = false;
39075 } else {
39076 this.svg.style.height = ('' + this.options.graphHeight).replace('px', '') + 'px';
39077 } // zoomed is here to ensure that animations are shown correctly.
39078
39079
39080 if (resized == true || zoomed == true || this.abortedGraphUpdate == true || this.forceGraphUpdate == true) {
39081 resized = this._updateGraph() || resized;
39082 this.forceGraphUpdate = false;
39083 } else {
39084 // move the whole svg while dragging
39085 if (this.lastStart != 0) {
39086 var offset = this.body.range.start - this.lastStart;
39087 var range = this.body.range.end - this.body.range.start;
39088
39089 if (this.props.width != 0) {
39090 var rangePerPixelInv = this.props.width / range;
39091 var xOffset = offset * rangePerPixelInv;
39092 this.svg.style.left = -this.props.width - xOffset + 'px';
39093 }
39094 }
39095 }
39096
39097 this.legendLeft.redraw();
39098 this.legendRight.redraw();
39099 return resized;
39100};
39101
39102LineGraph.prototype._getSortedGroupIds = function () {
39103 // getting group Ids
39104 var grouplist = [];
39105
39106 for (var groupId in this.groups) {
39107 if (this.groups.hasOwnProperty(groupId)) {
39108 var group = this.groups[groupId];
39109
39110 if (group.visible == true && (this.options.groups.visibility[groupId] === undefined || this.options.groups.visibility[groupId] == true)) {
39111 grouplist.push({
39112 id: groupId,
39113 zIndex: group.options.zIndex
39114 });
39115 }
39116 }
39117 }
39118
39119 util.insertSort(grouplist, function (a, b) {
39120 var az = a.zIndex;
39121 var bz = b.zIndex;
39122 if (az === undefined) az = 0;
39123 if (bz === undefined) bz = 0;
39124 return az == bz ? 0 : az < bz ? -1 : 1;
39125 });
39126 var groupIds = new Array(grouplist.length);
39127
39128 for (var i = 0; i < grouplist.length; i++) {
39129 groupIds[i] = grouplist[i].id;
39130 }
39131
39132 return groupIds;
39133};
39134/**
39135 * Update and redraw the graph.
39136 *
39137 * @returns {boolean}
39138 * @private
39139 */
39140
39141
39142LineGraph.prototype._updateGraph = function () {
39143 // reset the svg elements
39144 DOMutil.prepareElements(this.svgElements);
39145
39146 if (this.props.width != 0 && this.itemsData != null) {
39147 var group, i;
39148 var groupRanges = {};
39149 var changeCalled = false; // this is the range of the SVG canvas
39150
39151 var minDate = this.body.util.toGlobalTime(-this.body.domProps.root.width);
39152 var maxDate = this.body.util.toGlobalTime(2 * this.body.domProps.root.width); // getting group Ids
39153
39154 var groupIds = this._getSortedGroupIds();
39155
39156 if (groupIds.length > 0) {
39157 var groupsData = {}; // fill groups data, this only loads the data we require based on the timewindow
39158
39159 this._getRelevantData(groupIds, groupsData, minDate, maxDate); // apply sampling, if disabled, it will pass through this function.
39160
39161
39162 this._applySampling(groupIds, groupsData); // we transform the X coordinates to detect collisions
39163
39164
39165 for (i = 0; i < groupIds.length; i++) {
39166 this._convertXcoordinates(groupsData[groupIds[i]]);
39167 } // now all needed data has been collected we start the processing.
39168
39169
39170 this._getYRanges(groupIds, groupsData, groupRanges); // update the Y axis first, we use this data to draw at the correct Y points
39171
39172
39173 changeCalled = this._updateYAxis(groupIds, groupRanges); // at changeCalled, abort this update cycle as the graph needs another update with new Width input from the Redraw container.
39174 // Cleanup SVG elements on abort.
39175
39176 if (changeCalled == true) {
39177 DOMutil.cleanupElements(this.svgElements);
39178 this.abortedGraphUpdate = true;
39179 return true;
39180 }
39181
39182 this.abortedGraphUpdate = false; // With the yAxis scaled correctly, use this to get the Y values of the points.
39183
39184 var below = undefined;
39185
39186 for (i = 0; i < groupIds.length; i++) {
39187 group = this.groups[groupIds[i]];
39188
39189 if (this.options.stack === true && this.options.style === 'line') {
39190 if (group.options.excludeFromStacking == undefined || !group.options.excludeFromStacking) {
39191 if (below != undefined) {
39192 this._stack(groupsData[group.id], groupsData[below.id]);
39193
39194 if (group.options.shaded.enabled == true && group.options.shaded.orientation !== "group") {
39195 if (group.options.shaded.orientation == "top" && below.options.shaded.orientation !== "group") {
39196 below.options.shaded.orientation = "group";
39197 below.options.shaded.groupId = group.id;
39198 } else {
39199 group.options.shaded.orientation = "group";
39200 group.options.shaded.groupId = below.id;
39201 }
39202 }
39203 }
39204
39205 below = group;
39206 }
39207 }
39208
39209 this._convertYcoordinates(groupsData[groupIds[i]], group);
39210 } //Precalculate paths and draw shading if appropriate. This will make sure the shading is always behind any lines.
39211
39212
39213 var paths = {};
39214
39215 for (i = 0; i < groupIds.length; i++) {
39216 group = this.groups[groupIds[i]];
39217
39218 if (group.options.style === 'line' && group.options.shaded.enabled == true) {
39219 var dataset = groupsData[groupIds[i]];
39220
39221 if (dataset == null || dataset.length == 0) {
39222 continue;
39223 }
39224
39225 if (!paths.hasOwnProperty(groupIds[i])) {
39226 paths[groupIds[i]] = line.calcPath(dataset, group);
39227 }
39228
39229 if (group.options.shaded.orientation === "group") {
39230 var subGroupId = group.options.shaded.groupId;
39231
39232 if (groupIds.indexOf(subGroupId) === -1) {
39233 console.log(group.id + ": Unknown shading group target given:" + subGroupId);
39234 continue;
39235 }
39236
39237 if (!paths.hasOwnProperty(subGroupId)) {
39238 paths[subGroupId] = line.calcPath(groupsData[subGroupId], this.groups[subGroupId]);
39239 }
39240
39241 line.drawShading(paths[groupIds[i]], group, paths[subGroupId], this.framework);
39242 } else {
39243 line.drawShading(paths[groupIds[i]], group, undefined, this.framework);
39244 }
39245 }
39246 } // draw the groups, calculating paths if still necessary.
39247
39248
39249 bar.draw(groupIds, groupsData, this.framework);
39250
39251 for (i = 0; i < groupIds.length; i++) {
39252 group = this.groups[groupIds[i]];
39253
39254 if (groupsData[groupIds[i]].length > 0) {
39255 switch (group.options.style) {
39256 case "line":
39257 if (!paths.hasOwnProperty(groupIds[i])) {
39258 paths[groupIds[i]] = line.calcPath(groupsData[groupIds[i]], group);
39259 }
39260
39261 line.draw(paths[groupIds[i]], group, this.framework);
39262 // eslint-disable-line no-fallthrough
39263
39264 case "point": // eslint-disable-line no-fallthrough
39265
39266 case "points":
39267 if (group.options.style == "point" || group.options.style == "points" || group.options.drawPoints.enabled == true) {
39268 points.draw(groupsData[groupIds[i]], group, this.framework);
39269 }
39270
39271 break;
39272
39273 case "bar": // bar needs to be drawn enmasse
39274 // eslint-disable-line no-fallthrough
39275
39276 default: //do nothing...
39277
39278 }
39279 }
39280 }
39281 }
39282 } // cleanup unused svg elements
39283
39284
39285 DOMutil.cleanupElements(this.svgElements);
39286 return false;
39287};
39288
39289LineGraph.prototype._stack = function (data, subData) {
39290 var index, dx, dy, subPrevPoint, subNextPoint;
39291 index = 0; // for each data point we look for a matching on in the set below
39292
39293 for (var j = 0; j < data.length; j++) {
39294 subPrevPoint = undefined;
39295 subNextPoint = undefined; // we look for time matches or a before-after point
39296
39297 for (var k = index; k < subData.length; k++) {
39298 // if times match exactly
39299 if (subData[k].x === data[j].x) {
39300 subPrevPoint = subData[k];
39301 subNextPoint = subData[k];
39302 index = k;
39303 break;
39304 } else if (subData[k].x > data[j].x) {
39305 // overshoot
39306 subNextPoint = subData[k];
39307
39308 if (k == 0) {
39309 subPrevPoint = subNextPoint;
39310 } else {
39311 subPrevPoint = subData[k - 1];
39312 }
39313
39314 index = k;
39315 break;
39316 }
39317 } // in case the last data point has been used, we assume it stays like this.
39318
39319
39320 if (subNextPoint === undefined) {
39321 subPrevPoint = subData[subData.length - 1];
39322 subNextPoint = subData[subData.length - 1];
39323 } // linear interpolation
39324
39325
39326 dx = subNextPoint.x - subPrevPoint.x;
39327 dy = subNextPoint.y - subPrevPoint.y;
39328
39329 if (dx == 0) {
39330 data[j].y = data[j].orginalY + subNextPoint.y;
39331 } else {
39332 data[j].y = data[j].orginalY + dy / dx * (data[j].x - subPrevPoint.x) + subPrevPoint.y; // ax + b where b is data[j].y
39333 }
39334 }
39335};
39336/**
39337 * first select and preprocess the data from the datasets.
39338 * the groups have their preselection of data, we now loop over this data to see
39339 * what data we need to draw. Sorted data is much faster.
39340 * more optimization is possible by doing the sampling before and using the binary search
39341 * to find the end date to determine the increment.
39342 *
39343 * @param {array} groupIds
39344 * @param {object} groupsData
39345 * @param {date} minDate
39346 * @param {date} maxDate
39347 * @private
39348 */
39349
39350
39351LineGraph.prototype._getRelevantData = function (groupIds, groupsData, minDate, maxDate) {
39352 var group, i, j, item;
39353
39354 if (groupIds.length > 0) {
39355 for (i = 0; i < groupIds.length; i++) {
39356 group = this.groups[groupIds[i]];
39357 var itemsData = group.getItems(); // optimization for sorted data
39358
39359 if (group.options.sort == true) {
39360 var dateComparator = function dateComparator(a, b) {
39361 return a.getTime() == b.getTime() ? 0 : a < b ? -1 : 1;
39362 };
39363
39364 var first = Math.max(0, util.binarySearchValue(itemsData, minDate, 'x', 'before', dateComparator));
39365 var last = Math.min(itemsData.length, util.binarySearchValue(itemsData, maxDate, 'x', 'after', dateComparator) + 1);
39366
39367 if (last <= 0) {
39368 last = itemsData.length;
39369 }
39370
39371 var dataContainer = new Array(last - first);
39372
39373 for (j = first; j < last; j++) {
39374 item = group.itemsData[j];
39375 dataContainer[j - first] = item;
39376 }
39377
39378 groupsData[groupIds[i]] = dataContainer;
39379 } else {
39380 // If unsorted data, all data is relevant, just returning entire structure
39381 groupsData[groupIds[i]] = group.itemsData;
39382 }
39383 }
39384 }
39385};
39386/**
39387 *
39388 * @param {Array.<vis.GraphGroup.id>} groupIds
39389 * @param {vis.DataSet} groupsData
39390 * @private
39391 */
39392
39393
39394LineGraph.prototype._applySampling = function (groupIds, groupsData) {
39395 var group;
39396
39397 if (groupIds.length > 0) {
39398 for (var i = 0; i < groupIds.length; i++) {
39399 group = this.groups[groupIds[i]];
39400
39401 if (group.options.sampling == true) {
39402 var dataContainer = groupsData[groupIds[i]];
39403
39404 if (dataContainer.length > 0) {
39405 var increment = 1;
39406 var amountOfPoints = dataContainer.length; // the global screen is used because changing the width of the yAxis may affect the increment, resulting in an endless loop
39407 // of width changing of the yAxis.
39408 //TODO: This assumes sorted data, but that's not guaranteed!
39409
39410 var xDistance = this.body.util.toGlobalScreen(dataContainer[dataContainer.length - 1].x) - this.body.util.toGlobalScreen(dataContainer[0].x);
39411 var pointsPerPixel = amountOfPoints / xDistance;
39412 increment = Math.min(Math.ceil(0.2 * amountOfPoints), Math.max(1, Math.round(pointsPerPixel)));
39413 var sampledData = new Array(amountOfPoints);
39414
39415 for (var j = 0; j < amountOfPoints; j += increment) {
39416 var idx = Math.round(j / increment);
39417 sampledData[idx] = dataContainer[j];
39418 }
39419
39420 groupsData[groupIds[i]] = sampledData.splice(0, Math.round(amountOfPoints / increment));
39421 }
39422 }
39423 }
39424 }
39425};
39426/**
39427 *
39428 * @param {Array.<vis.GraphGroup.id>} groupIds
39429 * @param {vis.DataSet} groupsData
39430 * @param {object} groupRanges | this is being filled here
39431 * @private
39432 */
39433
39434
39435LineGraph.prototype._getYRanges = function (groupIds, groupsData, groupRanges) {
39436 var groupData, group, i;
39437 var combinedDataLeft = [];
39438 var combinedDataRight = [];
39439 var options;
39440
39441 if (groupIds.length > 0) {
39442 for (i = 0; i < groupIds.length; i++) {
39443 groupData = groupsData[groupIds[i]];
39444 options = this.groups[groupIds[i]].options;
39445
39446 if (groupData.length > 0) {
39447 group = this.groups[groupIds[i]]; // if bar graphs are stacked, their range need to be handled differently and accumulated over all groups.
39448
39449 if (options.stack === true && options.style === 'bar') {
39450 if (options.yAxisOrientation === 'left') {
39451 combinedDataLeft = combinedDataLeft.concat(groupData);
39452 } else {
39453 combinedDataRight = combinedDataRight.concat(groupData);
39454 }
39455 } else {
39456 groupRanges[groupIds[i]] = group.getYRange(groupData, groupIds[i]);
39457 }
39458 }
39459 } // if bar graphs are stacked, their range need to be handled differently and accumulated over all groups.
39460
39461
39462 bar.getStackedYRange(combinedDataLeft, groupRanges, groupIds, '__barStackLeft', 'left');
39463 bar.getStackedYRange(combinedDataRight, groupRanges, groupIds, '__barStackRight', 'right');
39464 }
39465};
39466/**
39467 * this sets the Y ranges for the Y axis. It also determines which of the axis should be shown or hidden.
39468 * @param {Array.<vis.GraphGroup.id>} groupIds
39469 * @param {Object} groupRanges
39470 * @returns {boolean} resized
39471 * @private
39472 */
39473
39474
39475LineGraph.prototype._updateYAxis = function (groupIds, groupRanges) {
39476 var resized = false;
39477 var yAxisLeftUsed = false;
39478 var yAxisRightUsed = false;
39479 var minLeft = 1e9,
39480 minRight = 1e9,
39481 maxLeft = -1e9,
39482 maxRight = -1e9,
39483 minVal,
39484 maxVal; // if groups are present
39485
39486 if (groupIds.length > 0) {
39487 // this is here to make sure that if there are no items in the axis but there are groups, that there is no infinite draw/redraw loop.
39488 for (var i = 0; i < groupIds.length; i++) {
39489 var group = this.groups[groupIds[i]];
39490
39491 if (group && group.options.yAxisOrientation != 'right') {
39492 yAxisLeftUsed = true;
39493 minLeft = 1e9;
39494 maxLeft = -1e9;
39495 } else if (group && group.options.yAxisOrientation) {
39496 yAxisRightUsed = true;
39497 minRight = 1e9;
39498 maxRight = -1e9;
39499 }
39500 } // if there are items:
39501
39502
39503 for (i = 0; i < groupIds.length; i++) {
39504 if (groupRanges.hasOwnProperty(groupIds[i])) {
39505 if (groupRanges[groupIds[i]].ignore !== true) {
39506 minVal = groupRanges[groupIds[i]].min;
39507 maxVal = groupRanges[groupIds[i]].max;
39508
39509 if (groupRanges[groupIds[i]].yAxisOrientation != 'right') {
39510 yAxisLeftUsed = true;
39511 minLeft = minLeft > minVal ? minVal : minLeft;
39512 maxLeft = maxLeft < maxVal ? maxVal : maxLeft;
39513 } else {
39514 yAxisRightUsed = true;
39515 minRight = minRight > minVal ? minVal : minRight;
39516 maxRight = maxRight < maxVal ? maxVal : maxRight;
39517 }
39518 }
39519 }
39520 }
39521
39522 if (yAxisLeftUsed == true) {
39523 this.yAxisLeft.setRange(minLeft, maxLeft);
39524 }
39525
39526 if (yAxisRightUsed == true) {
39527 this.yAxisRight.setRange(minRight, maxRight);
39528 }
39529 }
39530
39531 resized = this._toggleAxisVisiblity(yAxisLeftUsed, this.yAxisLeft) || resized;
39532 resized = this._toggleAxisVisiblity(yAxisRightUsed, this.yAxisRight) || resized;
39533
39534 if (yAxisRightUsed == true && yAxisLeftUsed == true) {
39535 this.yAxisLeft.drawIcons = true;
39536 this.yAxisRight.drawIcons = true;
39537 } else {
39538 this.yAxisLeft.drawIcons = false;
39539 this.yAxisRight.drawIcons = false;
39540 }
39541
39542 this.yAxisRight.master = !yAxisLeftUsed;
39543 this.yAxisRight.masterAxis = this.yAxisLeft;
39544
39545 if (this.yAxisRight.master == false) {
39546 if (yAxisRightUsed == true) {
39547 this.yAxisLeft.lineOffset = this.yAxisRight.width;
39548 } else {
39549 this.yAxisLeft.lineOffset = 0;
39550 }
39551
39552 resized = this.yAxisLeft.redraw() || resized;
39553 resized = this.yAxisRight.redraw() || resized;
39554 } else {
39555 resized = this.yAxisRight.redraw() || resized;
39556 } // clean the accumulated lists
39557
39558
39559 var tempGroups = ['__barStackLeft', '__barStackRight', '__lineStackLeft', '__lineStackRight'];
39560
39561 for (i = 0; i < tempGroups.length; i++) {
39562 if (groupIds.indexOf(tempGroups[i]) != -1) {
39563 groupIds.splice(groupIds.indexOf(tempGroups[i]), 1);
39564 }
39565 }
39566
39567 return resized;
39568};
39569/**
39570 * This shows or hides the Y axis if needed. If there is a change, the changed event is emitted by the updateYAxis function
39571 *
39572 * @param {boolean} axisUsed
39573 * @param {vis.DataAxis} axis
39574 * @returns {boolean}
39575 * @private
39576 */
39577
39578
39579LineGraph.prototype._toggleAxisVisiblity = function (axisUsed, axis) {
39580 var changed = false;
39581
39582 if (axisUsed == false) {
39583 if (axis.dom.frame.parentNode && axis.hidden == false) {
39584 axis.hide();
39585 changed = true;
39586 }
39587 } else {
39588 if (!axis.dom.frame.parentNode && axis.hidden == true) {
39589 axis.show();
39590 changed = true;
39591 }
39592 }
39593
39594 return changed;
39595};
39596/**
39597 * This uses the DataAxis object to generate the correct X coordinate on the SVG window. It uses the
39598 * util function toScreen to get the x coordinate from the timestamp. It also pre-filters the data and get the minMax ranges for
39599 * the yAxis.
39600 *
39601 * @param {Array.<Object>} datapoints
39602 * @private
39603 */
39604
39605
39606LineGraph.prototype._convertXcoordinates = function (datapoints) {
39607 var toScreen = this.body.util.toScreen;
39608
39609 for (var i = 0; i < datapoints.length; i++) {
39610 datapoints[i].screen_x = toScreen(datapoints[i].x) + this.props.width;
39611 datapoints[i].screen_y = datapoints[i].y; //starting point for range calculations
39612
39613 if (datapoints[i].end != undefined) {
39614 datapoints[i].screen_end = toScreen(datapoints[i].end) + this.props.width;
39615 } else {
39616 datapoints[i].screen_end = undefined;
39617 }
39618 }
39619};
39620/**
39621 * This uses the DataAxis object to generate the correct X coordinate on the SVG window. It uses the
39622 * util function toScreen to get the x coordinate from the timestamp. It also pre-filters the data and get the minMax ranges for
39623 * the yAxis.
39624 *
39625 * @param {Array.<Object>} datapoints
39626 * @param {vis.GraphGroup} group
39627 * @private
39628 */
39629
39630
39631LineGraph.prototype._convertYcoordinates = function (datapoints, group) {
39632 var axis = this.yAxisLeft;
39633 var svgHeight = Number(this.svg.style.height.replace('px', ''));
39634
39635 if (group.options.yAxisOrientation == 'right') {
39636 axis = this.yAxisRight;
39637 }
39638
39639 for (var i = 0; i < datapoints.length; i++) {
39640 datapoints[i].screen_y = Math.round(axis.convertValue(datapoints[i].y));
39641 }
39642
39643 group.setZeroPosition(Math.min(svgHeight, axis.convertValue(0)));
39644};
39645
39646var LineGraph_1 = LineGraph;
39647
39648/**
39649 * This object contains all possible options. It will check if the types are correct, if required if the option is one
39650 * of the allowed values.
39651 *
39652 * __any__ means that the name of the property does not matter.
39653 * __type__ is a required field for all objects and contains the allowed types of all objects
39654 */
39655var string$1 = 'string';
39656var bool$1 = 'boolean';
39657var number$1 = 'number';
39658var array$1 = 'array';
39659var date$1 = 'date';
39660var object$1 = 'object'; // should only be in a __type__ property
39661
39662var dom$1 = 'dom';
39663var moment$5 = 'moment';
39664var any$1 = 'any';
39665var allOptions$3 = {
39666 configure: {
39667 enabled: {
39668 'boolean': bool$1
39669 },
39670 filter: {
39671 'boolean': bool$1,
39672 'function': 'function'
39673 },
39674 container: {
39675 dom: dom$1
39676 },
39677 __type__: {
39678 object: object$1,
39679 'boolean': bool$1,
39680 'function': 'function'
39681 }
39682 },
39683 //globals :
39684 alignCurrentTime: {
39685 string: string$1,
39686 'undefined': 'undefined'
39687 },
39688 yAxisOrientation: {
39689 string: ['left', 'right']
39690 },
39691 defaultGroup: {
39692 string: string$1
39693 },
39694 sort: {
39695 'boolean': bool$1
39696 },
39697 sampling: {
39698 'boolean': bool$1
39699 },
39700 stack: {
39701 'boolean': bool$1
39702 },
39703 graphHeight: {
39704 string: string$1,
39705 number: number$1
39706 },
39707 shaded: {
39708 enabled: {
39709 'boolean': bool$1
39710 },
39711 orientation: {
39712 string: ['bottom', 'top', 'zero', 'group']
39713 },
39714 // top, bottom, zero, group
39715 groupId: {
39716 object: object$1
39717 },
39718 __type__: {
39719 'boolean': bool$1,
39720 object: object$1
39721 }
39722 },
39723 style: {
39724 string: ['line', 'bar', 'points']
39725 },
39726 // line, bar
39727 barChart: {
39728 width: {
39729 number: number$1
39730 },
39731 minWidth: {
39732 number: number$1
39733 },
39734 sideBySide: {
39735 'boolean': bool$1
39736 },
39737 align: {
39738 string: ['left', 'center', 'right']
39739 },
39740 __type__: {
39741 object: object$1
39742 }
39743 },
39744 interpolation: {
39745 enabled: {
39746 'boolean': bool$1
39747 },
39748 parametrization: {
39749 string: ['centripetal', 'chordal', 'uniform']
39750 },
39751 // uniform (alpha = 0.0), chordal (alpha = 1.0), centripetal (alpha = 0.5)
39752 alpha: {
39753 number: number$1
39754 },
39755 __type__: {
39756 object: object$1,
39757 'boolean': bool$1
39758 }
39759 },
39760 drawPoints: {
39761 enabled: {
39762 'boolean': bool$1
39763 },
39764 onRender: {
39765 'function': 'function'
39766 },
39767 size: {
39768 number: number$1
39769 },
39770 style: {
39771 string: ['square', 'circle']
39772 },
39773 // square, circle
39774 __type__: {
39775 object: object$1,
39776 'boolean': bool$1,
39777 'function': 'function'
39778 }
39779 },
39780 dataAxis: {
39781 showMinorLabels: {
39782 'boolean': bool$1
39783 },
39784 showMajorLabels: {
39785 'boolean': bool$1
39786 },
39787 icons: {
39788 'boolean': bool$1
39789 },
39790 width: {
39791 string: string$1,
39792 number: number$1
39793 },
39794 visible: {
39795 'boolean': bool$1
39796 },
39797 alignZeros: {
39798 'boolean': bool$1
39799 },
39800 left: {
39801 range: {
39802 min: {
39803 number: number$1,
39804 'undefined': 'undefined'
39805 },
39806 max: {
39807 number: number$1,
39808 'undefined': 'undefined'
39809 },
39810 __type__: {
39811 object: object$1
39812 }
39813 },
39814 format: {
39815 'function': 'function'
39816 },
39817 title: {
39818 text: {
39819 string: string$1,
39820 number: number$1,
39821 'undefined': 'undefined'
39822 },
39823 style: {
39824 string: string$1,
39825 'undefined': 'undefined'
39826 },
39827 __type__: {
39828 object: object$1
39829 }
39830 },
39831 __type__: {
39832 object: object$1
39833 }
39834 },
39835 right: {
39836 range: {
39837 min: {
39838 number: number$1,
39839 'undefined': 'undefined'
39840 },
39841 max: {
39842 number: number$1,
39843 'undefined': 'undefined'
39844 },
39845 __type__: {
39846 object: object$1
39847 }
39848 },
39849 format: {
39850 'function': 'function'
39851 },
39852 title: {
39853 text: {
39854 string: string$1,
39855 number: number$1,
39856 'undefined': 'undefined'
39857 },
39858 style: {
39859 string: string$1,
39860 'undefined': 'undefined'
39861 },
39862 __type__: {
39863 object: object$1
39864 }
39865 },
39866 __type__: {
39867 object: object$1
39868 }
39869 },
39870 __type__: {
39871 object: object$1
39872 }
39873 },
39874 legend: {
39875 enabled: {
39876 'boolean': bool$1
39877 },
39878 icons: {
39879 'boolean': bool$1
39880 },
39881 left: {
39882 visible: {
39883 'boolean': bool$1
39884 },
39885 position: {
39886 string: ['top-right', 'bottom-right', 'top-left', 'bottom-left']
39887 },
39888 __type__: {
39889 object: object$1
39890 }
39891 },
39892 right: {
39893 visible: {
39894 'boolean': bool$1
39895 },
39896 position: {
39897 string: ['top-right', 'bottom-right', 'top-left', 'bottom-left']
39898 },
39899 __type__: {
39900 object: object$1
39901 }
39902 },
39903 __type__: {
39904 object: object$1,
39905 'boolean': bool$1
39906 }
39907 },
39908 groups: {
39909 visibility: {
39910 any: any$1
39911 },
39912 __type__: {
39913 object: object$1
39914 }
39915 },
39916 autoResize: {
39917 'boolean': bool$1
39918 },
39919 throttleRedraw: {
39920 number: number$1
39921 },
39922 // TODO: DEPRICATED see https://github.com/almende/vis/issues/2511
39923 clickToUse: {
39924 'boolean': bool$1
39925 },
39926 end: {
39927 number: number$1,
39928 date: date$1,
39929 string: string$1,
39930 moment: moment$5
39931 },
39932 format: {
39933 minorLabels: {
39934 millisecond: {
39935 string: string$1,
39936 'undefined': 'undefined'
39937 },
39938 second: {
39939 string: string$1,
39940 'undefined': 'undefined'
39941 },
39942 minute: {
39943 string: string$1,
39944 'undefined': 'undefined'
39945 },
39946 hour: {
39947 string: string$1,
39948 'undefined': 'undefined'
39949 },
39950 weekday: {
39951 string: string$1,
39952 'undefined': 'undefined'
39953 },
39954 day: {
39955 string: string$1,
39956 'undefined': 'undefined'
39957 },
39958 month: {
39959 string: string$1,
39960 'undefined': 'undefined'
39961 },
39962 quarter: {
39963 string: string$1,
39964 'undefined': 'undefined'
39965 },
39966 year: {
39967 string: string$1,
39968 'undefined': 'undefined'
39969 },
39970 __type__: {
39971 object: object$1
39972 }
39973 },
39974 majorLabels: {
39975 millisecond: {
39976 string: string$1,
39977 'undefined': 'undefined'
39978 },
39979 second: {
39980 string: string$1,
39981 'undefined': 'undefined'
39982 },
39983 minute: {
39984 string: string$1,
39985 'undefined': 'undefined'
39986 },
39987 hour: {
39988 string: string$1,
39989 'undefined': 'undefined'
39990 },
39991 weekday: {
39992 string: string$1,
39993 'undefined': 'undefined'
39994 },
39995 day: {
39996 string: string$1,
39997 'undefined': 'undefined'
39998 },
39999 month: {
40000 string: string$1,
40001 'undefined': 'undefined'
40002 },
40003 quarter: {
40004 string: string$1,
40005 'undefined': 'undefined'
40006 },
40007 year: {
40008 string: string$1,
40009 'undefined': 'undefined'
40010 },
40011 __type__: {
40012 object: object$1
40013 }
40014 },
40015 __type__: {
40016 object: object$1
40017 }
40018 },
40019 moment: {
40020 'function': 'function'
40021 },
40022 height: {
40023 string: string$1,
40024 number: number$1
40025 },
40026 hiddenDates: {
40027 start: {
40028 date: date$1,
40029 number: number$1,
40030 string: string$1,
40031 moment: moment$5
40032 },
40033 end: {
40034 date: date$1,
40035 number: number$1,
40036 string: string$1,
40037 moment: moment$5
40038 },
40039 repeat: {
40040 string: string$1
40041 },
40042 __type__: {
40043 object: object$1,
40044 array: array$1
40045 }
40046 },
40047 locale: {
40048 string: string$1
40049 },
40050 locales: {
40051 __any__: {
40052 any: any$1
40053 },
40054 __type__: {
40055 object: object$1
40056 }
40057 },
40058 max: {
40059 date: date$1,
40060 number: number$1,
40061 string: string$1,
40062 moment: moment$5
40063 },
40064 maxHeight: {
40065 number: number$1,
40066 string: string$1
40067 },
40068 maxMinorChars: {
40069 number: number$1
40070 },
40071 min: {
40072 date: date$1,
40073 number: number$1,
40074 string: string$1,
40075 moment: moment$5
40076 },
40077 minHeight: {
40078 number: number$1,
40079 string: string$1
40080 },
40081 moveable: {
40082 'boolean': bool$1
40083 },
40084 multiselect: {
40085 'boolean': bool$1
40086 },
40087 orientation: {
40088 string: string$1
40089 },
40090 showCurrentTime: {
40091 'boolean': bool$1
40092 },
40093 showMajorLabels: {
40094 'boolean': bool$1
40095 },
40096 showMinorLabels: {
40097 'boolean': bool$1
40098 },
40099 start: {
40100 date: date$1,
40101 number: number$1,
40102 string: string$1,
40103 moment: moment$5
40104 },
40105 timeAxis: {
40106 scale: {
40107 string: string$1,
40108 'undefined': 'undefined'
40109 },
40110 step: {
40111 number: number$1,
40112 'undefined': 'undefined'
40113 },
40114 __type__: {
40115 object: object$1
40116 }
40117 },
40118 width: {
40119 string: string$1,
40120 number: number$1
40121 },
40122 zoomable: {
40123 'boolean': bool$1
40124 },
40125 zoomKey: {
40126 string: ['ctrlKey', 'altKey', 'metaKey', '']
40127 },
40128 zoomMax: {
40129 number: number$1
40130 },
40131 zoomMin: {
40132 number: number$1
40133 },
40134 zIndex: {
40135 number: number$1
40136 },
40137 __type__: {
40138 object: object$1
40139 }
40140};
40141var configureOptions$2 = {
40142 global: {
40143 alignCurrentTime: ['none', 'year', 'month', 'quarter', 'week', 'isoWeek', 'day', 'date', 'hour', 'minute', 'second'],
40144 //yAxisOrientation: ['left','right'], // TDOO: enable as soon as Grahp2d doesn't crash when changing this on the fly
40145 sort: true,
40146 sampling: true,
40147 stack: false,
40148 shaded: {
40149 enabled: false,
40150 orientation: ['zero', 'top', 'bottom', 'group'] // zero, top, bottom
40151
40152 },
40153 style: ['line', 'bar', 'points'],
40154 // line, bar
40155 barChart: {
40156 width: [50, 5, 100, 5],
40157 minWidth: [50, 5, 100, 5],
40158 sideBySide: false,
40159 align: ['left', 'center', 'right'] // left, center, right
40160
40161 },
40162 interpolation: {
40163 enabled: true,
40164 parametrization: ['centripetal', 'chordal', 'uniform'] // uniform (alpha = 0.0), chordal (alpha = 1.0), centripetal (alpha = 0.5)
40165
40166 },
40167 drawPoints: {
40168 enabled: true,
40169 size: [6, 2, 30, 1],
40170 style: ['square', 'circle'] // square, circle
40171
40172 },
40173 dataAxis: {
40174 showMinorLabels: true,
40175 showMajorLabels: true,
40176 icons: false,
40177 width: [40, 0, 200, 1],
40178 visible: true,
40179 alignZeros: true,
40180 left: {
40181 //range: {min:'undefined': 'undefined'ined,max:'undefined': 'undefined'ined},
40182 //format: function (value) {return value;},
40183 title: {
40184 text: '',
40185 style: ''
40186 }
40187 },
40188 right: {
40189 //range: {min:'undefined': 'undefined'ined,max:'undefined': 'undefined'ined},
40190 //format: function (value) {return value;},
40191 title: {
40192 text: '',
40193 style: ''
40194 }
40195 }
40196 },
40197 legend: {
40198 enabled: false,
40199 icons: true,
40200 left: {
40201 visible: true,
40202 position: ['top-right', 'bottom-right', 'top-left', 'bottom-left'] // top/bottom - left,right
40203
40204 },
40205 right: {
40206 visible: true,
40207 position: ['top-right', 'bottom-right', 'top-left', 'bottom-left'] // top/bottom - left,right
40208
40209 }
40210 },
40211 autoResize: true,
40212 clickToUse: false,
40213 end: '',
40214 format: {
40215 minorLabels: {
40216 millisecond: 'SSS',
40217 second: 's',
40218 minute: 'HH:mm',
40219 hour: 'HH:mm',
40220 weekday: 'ddd D',
40221 day: 'D',
40222 month: 'MMM',
40223 quarter: '[Q]Q',
40224 year: 'YYYY'
40225 },
40226 majorLabels: {
40227 millisecond: 'HH:mm:ss',
40228 second: 'D MMMM HH:mm',
40229 minute: 'ddd D MMMM',
40230 hour: 'ddd D MMMM',
40231 weekday: 'MMMM YYYY',
40232 day: 'MMMM YYYY',
40233 month: 'YYYY',
40234 quarter: 'YYYY',
40235 year: ''
40236 }
40237 },
40238 height: '',
40239 locale: '',
40240 max: '',
40241 maxHeight: '',
40242 maxMinorChars: [7, 0, 20, 1],
40243 min: '',
40244 minHeight: '',
40245 moveable: true,
40246 orientation: ['both', 'bottom', 'top'],
40247 showCurrentTime: false,
40248 showMajorLabels: true,
40249 showMinorLabels: true,
40250 start: '',
40251 width: '100%',
40252 zoomable: true,
40253 zoomKey: ['ctrlKey', 'altKey', 'metaKey', ''],
40254 zoomMax: [315360000000000, 10, 315360000000000, 1],
40255 zoomMin: [10, 10, 315360000000000, 1],
40256 zIndex: 0
40257 }
40258};
40259
40260var optionsGraph2d = /*#__PURE__*/Object.freeze({
40261 allOptions: allOptions$3,
40262 configureOptions: configureOptions$2
40263});
40264
40265var DataSet$3 = index.DataSet;
40266var DataView$3 = index.DataView;
40267var Validator$3 = Validator$1.Validator;
40268var printStyle$2 = Validator$1.printStyle;
40269var allOptions$4 = optionsGraph2d.allOptions;
40270var configureOptions$3 = optionsGraph2d.configureOptions;
40271var Configurator$2 = Configurator.default;
40272/**
40273 * Create a timeline visualization
40274 * @param {HTMLElement} container
40275 * @param {vis.DataSet | Array} [items]
40276 * @param {vis.DataSet | Array | vis.DataView | Object} [groups]
40277 * @param {Object} [options] See Graph2d.setOptions for the available options.
40278 * @constructor Graph2d
40279 * @extends Core
40280 */
40281
40282function Graph2d(container, items, groups, options) {
40283 // if the third element is options, the forth is groups (optionally);
40284 if (!(Array.isArray(groups) || groups instanceof DataSet$3 || groups instanceof DataView$3) && groups instanceof Object) {
40285 var forthArgument = options;
40286 options = groups;
40287 groups = forthArgument;
40288 } // TODO: REMOVE THIS in the next MAJOR release
40289 // see https://github.com/almende/vis/issues/2511
40290
40291
40292 if (options && options.throttleRedraw) {
40293 console.warn("Graph2d option \"throttleRedraw\" is DEPRICATED and no longer supported. It will be removed in the next MAJOR release.");
40294 }
40295
40296 var me = this;
40297 this.defaultOptions = {
40298 start: null,
40299 end: null,
40300 autoResize: true,
40301 orientation: {
40302 axis: 'bottom',
40303 // axis orientation: 'bottom', 'top', or 'both'
40304 item: 'bottom' // not relevant for Graph2d
40305
40306 },
40307 moment: moment$3,
40308 width: null,
40309 height: null,
40310 maxHeight: null,
40311 minHeight: null
40312 };
40313 this.options = util.deepExtend({}, this.defaultOptions); // Create the DOM, props, and emitter
40314
40315 this._create(container); // all components listed here will be repainted automatically
40316
40317
40318 this.components = [];
40319 this.body = {
40320 dom: this.dom,
40321 domProps: this.props,
40322 emitter: {
40323 on: this.on.bind(this),
40324 off: this.off.bind(this),
40325 emit: this.emit.bind(this)
40326 },
40327 hiddenDates: [],
40328 util: {
40329 toScreen: me._toScreen.bind(me),
40330 toGlobalScreen: me._toGlobalScreen.bind(me),
40331 // this refers to the root.width
40332 toTime: me._toTime.bind(me),
40333 toGlobalTime: me._toGlobalTime.bind(me)
40334 }
40335 }; // range
40336
40337 this.range = new Range_1(this.body);
40338 this.components.push(this.range);
40339 this.body.range = this.range; // time axis
40340
40341 this.timeAxis = new TimeAxis(this.body);
40342 this.components.push(this.timeAxis); //this.body.util.snap = this.timeAxis.snap.bind(this.timeAxis);
40343 // current time bar
40344
40345 this.currentTime = new CurrentTime_1(this.body);
40346 this.components.push(this.currentTime); // item set
40347
40348 this.linegraph = new LineGraph_1(this.body);
40349 this.components.push(this.linegraph);
40350 this.itemsData = null; // DataSet
40351
40352 this.groupsData = null; // DataSet
40353
40354 this.on('tap', function (event) {
40355 me.emit('click', me.getEventProperties(event));
40356 });
40357 this.on('doubletap', function (event) {
40358 me.emit('doubleClick', me.getEventProperties(event));
40359 });
40360
40361 this.dom.root.oncontextmenu = function (event) {
40362 me.emit('contextmenu', me.getEventProperties(event));
40363 }; //Single time autoscale/fit
40364
40365
40366 this.initialFitDone = false;
40367 this.on('changed', function () {
40368 if (me.itemsData == null) return;
40369
40370 if (!me.initialFitDone && !me.options.rollingMode) {
40371 me.initialFitDone = true;
40372
40373 if (me.options.start != undefined || me.options.end != undefined) {
40374 if (me.options.start == undefined || me.options.end == undefined) {
40375 var range = me.getItemRange();
40376 }
40377
40378 var start = me.options.start != undefined ? me.options.start : range.min;
40379 var end = me.options.end != undefined ? me.options.end : range.max;
40380 me.setWindow(start, end, {
40381 animation: false
40382 });
40383 } else {
40384 me.fit({
40385 animation: false
40386 });
40387 }
40388 }
40389
40390 if (!me.initialDrawDone && (me.initialRangeChangeDone || !me.options.start && !me.options.end || me.options.rollingMode)) {
40391 me.initialDrawDone = true;
40392 me.itemSet.initialDrawDone = true;
40393 me.dom.root.style.visibility = 'visible';
40394 me.dom.loadingScreen.parentNode.removeChild(me.dom.loadingScreen);
40395
40396 if (me.options.onInitialDrawComplete) {
40397 setTimeout(function () {
40398 return me.options.onInitialDrawComplete();
40399 }, 0);
40400 }
40401 }
40402 }); // apply options
40403
40404 if (options) {
40405 this.setOptions(options);
40406 } // IMPORTANT: THIS HAPPENS BEFORE SET ITEMS!
40407
40408
40409 if (groups) {
40410 this.setGroups(groups);
40411 } // create itemset
40412
40413
40414 if (items) {
40415 this.setItems(items);
40416 } // draw for the first time
40417
40418
40419 this._redraw();
40420} // Extend the functionality from Core
40421
40422
40423Graph2d.prototype = new Core();
40424
40425Graph2d.prototype.setOptions = function (options) {
40426 // validate options
40427 var errorFound = Validator$3.validate(options, allOptions$4);
40428
40429 if (errorFound === true) {
40430 console.log('%cErrors have been found in the supplied options object.', printStyle$2);
40431 }
40432
40433 Core.prototype.setOptions.call(this, options);
40434};
40435/**
40436 * Set items
40437 * @param {vis.DataSet | Array | null} items
40438 */
40439
40440
40441Graph2d.prototype.setItems = function (items) {
40442 var initialLoad = this.itemsData == null; // convert to type DataSet when needed
40443
40444 var newDataSet;
40445
40446 if (!items) {
40447 newDataSet = null;
40448 } else if (items instanceof DataSet$3 || items instanceof DataView$3) {
40449 newDataSet = items;
40450 } else {
40451 // turn an array into a dataset
40452 newDataSet = new DataSet$3(items, {
40453 type: {
40454 start: 'Date',
40455 end: 'Date'
40456 }
40457 });
40458 } // set items
40459
40460
40461 this.itemsData = newDataSet;
40462 this.linegraph && this.linegraph.setItems(newDataSet);
40463
40464 if (initialLoad) {
40465 if (this.options.start != undefined || this.options.end != undefined) {
40466 var start = this.options.start != undefined ? this.options.start : null;
40467 var end = this.options.end != undefined ? this.options.end : null;
40468 this.setWindow(start, end, {
40469 animation: false
40470 });
40471 } else {
40472 this.fit({
40473 animation: false
40474 });
40475 }
40476 }
40477};
40478/**
40479 * Set groups
40480 * @param {vis.DataSet | Array} groups
40481 */
40482
40483
40484Graph2d.prototype.setGroups = function (groups) {
40485 // convert to type DataSet when needed
40486 var newDataSet;
40487
40488 if (!groups) {
40489 newDataSet = null;
40490 } else if (groups instanceof DataSet$3 || groups instanceof DataView$3) {
40491 newDataSet = groups;
40492 } else {
40493 // turn an array into a dataset
40494 newDataSet = new DataSet$3(groups);
40495 }
40496
40497 this.groupsData = newDataSet;
40498 this.linegraph.setGroups(newDataSet);
40499};
40500/**
40501 * Returns an object containing an SVG element with the icon of the group (size determined by iconWidth and iconHeight), the label of the group (content) and the yAxisOrientation of the group (left or right).
40502 * @param {vis.GraphGroup.id} groupId
40503 * @param {number} width
40504 * @param {number} height
40505 * @returns {{icon: SVGElement, label: string, orientation: string}|string}
40506 */
40507
40508
40509Graph2d.prototype.getLegend = function (groupId, width, height) {
40510 if (width === undefined) {
40511 width = 15;
40512 }
40513
40514 if (height === undefined) {
40515 height = 15;
40516 }
40517
40518 if (this.linegraph.groups[groupId] !== undefined) {
40519 return this.linegraph.groups[groupId].getLegend(width, height);
40520 } else {
40521 return "cannot find group:'" + groupId + "'";
40522 }
40523};
40524/**
40525 * This checks if the visible option of the supplied group (by ID) is true or false.
40526 * @param {vis.GraphGroup.id} groupId
40527 * @returns {boolean}
40528 */
40529
40530
40531Graph2d.prototype.isGroupVisible = function (groupId) {
40532 if (this.linegraph.groups[groupId] !== undefined) {
40533 return this.linegraph.groups[groupId].visible && (this.linegraph.options.groups.visibility[groupId] === undefined || this.linegraph.options.groups.visibility[groupId] == true);
40534 } else {
40535 return false;
40536 }
40537};
40538/**
40539 * Get the data range of the item set.
40540 * @returns {{min: Date, max: Date}} range A range with a start and end Date.
40541 * When no minimum is found, min==null
40542 * When no maximum is found, max==null
40543 */
40544
40545
40546Graph2d.prototype.getDataRange = function () {
40547 var min = null;
40548 var max = null; // calculate min from start filed
40549
40550 for (var groupId in this.linegraph.groups) {
40551 if (this.linegraph.groups.hasOwnProperty(groupId)) {
40552 if (this.linegraph.groups[groupId].visible == true) {
40553 for (var i = 0; i < this.linegraph.groups[groupId].itemsData.length; i++) {
40554 var item = this.linegraph.groups[groupId].itemsData[i];
40555 var value = util.convert(item.x, 'Date').valueOf();
40556 min = min == null ? value : min > value ? value : min;
40557 max = max == null ? value : max < value ? value : max;
40558 }
40559 }
40560 }
40561 }
40562
40563 return {
40564 min: min != null ? new Date(min) : null,
40565 max: max != null ? new Date(max) : null
40566 };
40567};
40568/**
40569 * Generate Timeline related information from an event
40570 * @param {Event} event
40571 * @return {Object} An object with related information, like on which area
40572 * The event happened, whether clicked on an item, etc.
40573 */
40574
40575
40576Graph2d.prototype.getEventProperties = function (event) {
40577 var clientX = event.center ? event.center.x : event.clientX;
40578 var clientY = event.center ? event.center.y : event.clientY;
40579 var x = clientX - util.getAbsoluteLeft(this.dom.centerContainer);
40580 var y = clientY - util.getAbsoluteTop(this.dom.centerContainer);
40581
40582 var time = this._toTime(x);
40583
40584 var customTime = CustomTime.customTimeFromTarget(event);
40585 var element = util.getTarget(event);
40586 var what = null;
40587
40588 if (util.hasParent(element, this.timeAxis.dom.foreground)) {
40589 what = 'axis';
40590 } else if (this.timeAxis2 && util.hasParent(element, this.timeAxis2.dom.foreground)) {
40591 what = 'axis';
40592 } else if (util.hasParent(element, this.linegraph.yAxisLeft.dom.frame)) {
40593 what = 'data-axis';
40594 } else if (util.hasParent(element, this.linegraph.yAxisRight.dom.frame)) {
40595 what = 'data-axis';
40596 } else if (util.hasParent(element, this.linegraph.legendLeft.dom.frame)) {
40597 what = 'legend';
40598 } else if (util.hasParent(element, this.linegraph.legendRight.dom.frame)) {
40599 what = 'legend';
40600 } else if (customTime != null) {
40601 what = 'custom-time';
40602 } else if (util.hasParent(element, this.currentTime.bar)) {
40603 what = 'current-time';
40604 } else if (util.hasParent(element, this.dom.center)) {
40605 what = 'background';
40606 }
40607
40608 var value = [];
40609 var yAxisLeft = this.linegraph.yAxisLeft;
40610 var yAxisRight = this.linegraph.yAxisRight;
40611
40612 if (!yAxisLeft.hidden && this.itemsData.length > 0) {
40613 value.push(yAxisLeft.screenToValue(y));
40614 }
40615
40616 if (!yAxisRight.hidden && this.itemsData.length > 0) {
40617 value.push(yAxisRight.screenToValue(y));
40618 }
40619
40620 return {
40621 event: event,
40622 what: what,
40623 pageX: event.srcEvent ? event.srcEvent.pageX : event.pageX,
40624 pageY: event.srcEvent ? event.srcEvent.pageY : event.pageY,
40625 x: x,
40626 y: y,
40627 time: time,
40628 value: value
40629 };
40630};
40631/**
40632 * Load a configurator
40633 * @return {Object}
40634 * @private
40635 */
40636
40637
40638Graph2d.prototype._createConfigurator = function () {
40639 return new Configurator$2(this, this.dom.container, configureOptions$3);
40640};
40641
40642var Graph2d_1 = Graph2d;
40643
40644var util_1 = util;
40645var DOMutil$1 = DOMutil; // data
40646
40647var DataSet$4 = index.DataSet,
40648 DataView$4 = index.DataView,
40649 Queue$1 = index.Queue;
40650var DataSet_1 = DataSet$4;
40651var DataView_1 = DataView$4;
40652var Queue_1 = Queue$1; // Timeline
40653
40654var Timeline$1 = Timeline_1;
40655var Graph2d$1 = Graph2d_1;
40656var timeline = {
40657 Core: Core,
40658 DateUtil: DateUtil,
40659 Range: Range_1,
40660 stack: Stack,
40661 TimeStep: TimeStep_1,
40662 components: {
40663 items: {
40664 Item: Item,
40665 BackgroundItem: BackgroundItem_1,
40666 BoxItem: BoxItem_1,
40667 PointItem: PointItem_1,
40668 RangeItem: RangeItem_1
40669 },
40670 BackgroundGroup: BackgroundGroup_1,
40671 Component: Component_1,
40672 CurrentTime: CurrentTime_1,
40673 CustomTime: CustomTime,
40674 DataAxis: DataAxis,
40675 DataScale: DataScale_1,
40676 GraphGroup: GraphGroup_1,
40677 Group: Group_1,
40678 ItemSet: ItemSet,
40679 Legend: Legend_1,
40680 LineGraph: LineGraph_1,
40681 TimeAxis: TimeAxis
40682 }
40683}; // bundled external libraries
40684
40685var moment$6 = moment$3;
40686var Hammer = hammer$1;
40687var keycharm$1 = keycharm;
40688var visTimeline = {
40689 util: util_1,
40690 DOMutil: DOMutil$1,
40691 DataSet: DataSet_1,
40692 DataView: DataView_1,
40693 Queue: Queue_1,
40694 Timeline: Timeline$1,
40695 Graph2d: Graph2d$1,
40696 timeline: timeline,
40697 moment: moment$6,
40698 Hammer: Hammer,
40699 keycharm: keycharm$1
40700};
40701
40702export default visTimeline;
40703export { DOMutil$1 as DOMutil, DataSet_1 as DataSet, DataView_1 as DataView, Graph2d$1 as Graph2d, Hammer, Queue_1 as Queue, Timeline$1 as Timeline, keycharm$1 as keycharm, moment$6 as moment, timeline, util_1 as util };
40704//# sourceMappingURL=vis-timeline-graph2d.esm.js.map