PageSourceSearch

https://ebooks.edu.gr/ebooks/static/js/vis/dist/vis.js?v1.1

js ebooks.edu.gr collected 2026-09-24 09:16:55 UTC 1,846,028 bytes, 59,936 lines download raw bytes

1/**
2 * vis.js
3 * https://github.com/almende/vis
4 *
5 * A dynamic, browser-based visualization library.
6 *
7 * @version 4.21.0
8 * @date    2017-10-12
9 *
10 * @license
11 * Copyright (C) 2011-2017 Almende B.V, http://almende.com
12 *
13 * Vis.js is dual licensed under both
14 *
15 * * The Apache 2.0 License
16 *   http://www.apache.org/licenses/LICENSE-2.0
17 *
18 * and
19 *
20 * * The MIT License
21 *   http://opensource.org/licenses/MIT
22 *
23 * Vis.js may be distributed under either license .
24 */
25
26"use strict";
27
28(function webpackUniversalModuleDefinition(root, factory) {
29	if(typeof exports === 'object' && typeof module === 'object')
30		module.exports = factory();
31	else if(typeof define === 'function' && define.amd)
32		define([], factory);
33	else if(typeof exports === 'object')
34		exports["vis"] = factory();
35	else
36		root["vis"] = factory();
37})(this, function() {
38return /******/ (function(modules) { // webpackBootstrap
39/******/ 	// The module cache
40/******/ 	var installedModules = {};
41/******/
42/******/ 	// The require function
43/******/ 	function __webpack_require__(moduleId) {
44/******/
45/******/ 		// Check if module is in cache
46/******/ 		if(installedModules[moduleId]) {
47/******/ 			return installedModules[moduleId].exports;
48/******/ 		}
49/******/ 		// Create a new module (and put it into the cache)
50/******/ 		var module = installedModules[moduleId] = {
51/******/ 			i: moduleId,
52/******/ 			l: false,
53/******/ 			exports: {}
54/******/ 		};
55/******/
56/******/ 		// Execute the module function
57/******/ 		modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
58/******/
59/******/ 		// Flag the module as loaded
60/******/ 		module.l = true;
61/******/
62/******/ 		// Return the exports of the module
63/******/ 		return module.exports;
64/******/ 	}
65/******/
66/******/
67/******/ 	// expose the modules object (__webpack_modules__)
68/******/ 	__webpack_require__.m = modules;
69/******/
70/******/ 	// expose the module cache
71/******/ 	__webpack_require__.c = installedModules;
72/******/
73/******/ 	// define getter function for harmony exports
74/******/ 	__webpack_require__.d = function(exports, name, getter) {
75/******/ 		if(!__webpack_require__.o(exports, name)) {
76/******/ 			Object.defineProperty(exports, name, {
77/******/ 				configurable: false,
78/******/ 				enumerable: true,
79/******/ 				get: getter
80/******/ 			});
81/******/ 		}
82/******/ 	};
83/******/
84/******/ 	// getDefaultExport function for compatibility with non-harmony modules
85/******/ 	__webpack_require__.n = function(module) {
86/******/ 		var getter = module && module.__esModule ?
87/******/ 			function getDefault() { return module['default']; } :
88/******/ 			function getModuleExports() { return module; };
89/******/ 		__webpack_require__.d(getter, 'a', getter);
90/******/ 		return getter;
91/******/ 	};
92/******/
93/******/ 	// Object.prototype.hasOwnProperty.call
94/******/ 	__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
95/******/
96/******/ 	// __webpack_public_path__
97/******/ 	__webpack_require__.p = "";
98/******/
99/******/ 	// Load entry module and return exports
100/******/ 	return __webpack_require__(__webpack_require__.s = 123);
101/******/ })
102/************************************************************************/
103/******/ ([
104/* 0 */
105/***/ (function(module, exports, __webpack_require__) {
106
107"use strict";
108
109
110exports.__esModule = true;
111
112exports.default = function (instance, Constructor) {
113  if (!(instance instanceof Constructor)) {
114    throw new TypeError("Cannot call a class as a function");
115  }
116};
117
118/***/ }),
119/* 1 */
120/***/ (function(module, exports, __webpack_require__) {
121
122"use strict";
123
124
125exports.__esModule = true;
126
127var _defineProperty = __webpack_require__(169);
128
129var _defineProperty2 = _interopRequireDefault(_defineProperty);
130
131function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
132
133exports.default = function () {
134  function defineProperties(target, props) {
135    for (var i = 0; i < props.length; i++) {
136      var descriptor = props[i];
137      descriptor.enumerable = descriptor.enumerable || false;
138      descriptor.configurable = true;
139      if ("value" in descriptor) descriptor.writable = true;
140      (0, _defineProperty2.default)(target, descriptor.key, descriptor);
141    }
142  }
143
144  return function (Constructor, protoProps, staticProps) {
145    if (protoProps) defineProperties(Constructor.prototype, protoProps);
146    if (staticProps) defineProperties(Constructor, staticProps);
147    return Constructor;
148  };
149}();
150
151/***/ }),
152/* 2 */
153/***/ (function(module, exports, __webpack_require__) {
154
155"use strict";
156
157
158var _getIterator2 = __webpack_require__(77);
159
160var _getIterator3 = _interopRequireDefault(_getIterator2);
161
162var _create = __webpack_require__(29);
163
164var _create2 = _interopRequireDefault(_create);
165
166var _keys = __webpack_require__(8);
167
168var _keys2 = _interopRequireDefault(_keys);
169
170var _typeof2 = __webpack_require__(6);
171
172var _typeof3 = _interopRequireDefault(_typeof2);
173
174function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
175
176// utility functions
177
178// first check if moment.js is already loaded in the browser window, if so,
179// use this instance. Else, load via commonjs.
180
181
182var moment = __webpack_require__(9);
183var uuid = __webpack_require__(157);
184
185/**
186 * Test whether given object is a number
187 * @param {*} object
188 * @return {Boolean} isNumber
189 */
190exports.isNumber = function (object) {
191  return object instanceof Number || typeof object == 'number';
192};
193
194/**
195 * Remove everything in the DOM object
196 * @param {Element} DOMobject
197 */
198exports.recursiveDOMDelete = function (DOMobject) {
199  if (DOMobject) {
200    while (DOMobject.hasChildNodes() === true) {
201      exports.recursiveDOMDelete(DOMobject.firstChild);
202      DOMobject.removeChild(DOMobject.firstChild);
203    }
204  }
205};
206
207/**
208 * this function gives you a range between 0 and 1 based on the min and max values in the set, the total sum of all values and the current value.
209 *
210 * @param {number} min
211 * @param {number} max
212 * @param {number} total
213 * @param {number} value
214 * @returns {number}
215 */
216exports.giveRange = function (min, max, total, value) {
217  if (max == min) {
218    return 0.5;
219  } else {
220    var scale = 1 / (max - min);
221    return Math.max(0, (value - min) * scale);
222  }
223};
224
225/**
226 * Test whether given object is a string
227 * @param {*} object
228 * @return {Boolean} isString
229 */
230exports.isString = function (object) {
231  return object instanceof String || typeof object == 'string';
232};
233
234/**
235 * Test whether given object is a Date, or a String containing a Date
236 * @param {Date | String} object
237 * @return {Boolean} isDate
238 */
239exports.isDate = function (object) {
240  if (object instanceof Date) {
241    return true;
242  } else if (exports.isString(object)) {
243    // test whether this string contains a date
244    var match = ASPDateRegex.exec(object);
245    if (match) {
246      return true;
247    } else if (!isNaN(Date.parse(object))) {
248      return true;
249    }
250  }
251
252  return false;
253};
254
255/**
256 * Create a semi UUID
257 * source: http://stackoverflow.com/a/105074/1262753
258 * @return {string} uuid
259 */
260exports.randomUUID = function () {
261  return uuid.v4();
262};
263
264/**
265 * assign all keys of an object that are not nested objects to a certain value (used for color objects).
266 * @param {object} obj
267 * @param {number} value
268 */
269exports.assignAllKeys = function (obj, value) {
270  for (var prop in obj) {
271    if (obj.hasOwnProperty(prop)) {
272      if ((0, _typeof3['default'])(obj[prop]) !== 'object') {
273        obj[prop] = value;
274      }
275    }
276  }
277};
278
279/**
280 * Copy property from b to a if property present in a.
281 * If property in b explicitly set to null, delete it if `allowDeletion` set.
282 *
283 * Internal helper routine, should not be exported. Not added to `exports` for that reason.
284 *
285 * @param {object} a  target object
286 * @param {object} b  source object
287 * @param {string} prop  name of property to copy to a
288 * @param {boolean} allowDeletion  if true, delete property in a if explicitly set to null in b 
289 * @private
290 */
291function copyOrDelete(a, b, prop, allowDeletion) {
292  var doDeletion = false;
293  if (allowDeletion === true) {
294    doDeletion = b[prop] === null && a[prop] !== undefined;
295  }
296
297  if (doDeletion) {
298    delete a[prop];
299  } else {
300    a[prop] = b[prop]; // Remember, this is a reference copy!
301  }
302}
303
304/**
305 * Fill an object with a possibly partially defined other object.
306 *
307 * Only copies values for the properties already present in a.
308 * That means an object is not created on a property if only the b object has it.
309 *
310 * @param {object} a
311 * @param {object} b
312 * @param {boolean} [allowDeletion=false]  if true, delete properties in a that are explicitly set to null in b 
313 */
314exports.fillIfDefined = function (a, b) {
315  var allowDeletion = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
316
317  // NOTE: iteration of properties of a
318  // NOTE: prototype properties iterated over as well
319  for (var prop in a) {
320    if (b[prop] !== undefined) {
321      if (b[prop] === null || (0, _typeof3['default'])(b[prop]) !== 'object') {
322        // Note: typeof null === 'object'
323        copyOrDelete(a, b, prop, allowDeletion);
324      } else {
325        if ((0, _typeof3['default'])(a[prop]) === 'object') {
326          exports.fillIfDefined(a[prop], b[prop], allowDeletion);
327        }
328      }
329    }
330  }
331};
332
333/**
334 * Extend object a with the properties of object b or a series of objects
335 * Only properties with defined values are copied
336 * @param {Object} a
337 * @param {...Object} b
338 * @return {Object} a
339 */
340exports.extend = function (a, b) {
341  // eslint-disable-line no-unused-vars
342  for (var i = 1; i < arguments.length; i++) {
343    var other = arguments[i];
344    for (var prop in other) {
345      if (other.hasOwnProperty(prop)) {
346        a[prop] = other[prop];
347      }
348    }
349  }
350  return a;
351};
352
353/**
354 * Extend object a with selected properties of object b or a series of objects
355 * Only properties with defined values are copied
356 * @param {Array.<string>} props
357 * @param {Object} a
358 * @param {Object} b
359 * @return {Object} a
360 */
361exports.selectiveExtend = function (props, a, b) {
362  // eslint-disable-line no-unused-vars
363  if (!Array.isArray(props)) {
364    throw new Error('Array with property names expected as first argument');
365  }
366
367  for (var i = 2; i < arguments.length; i++) {
368    var other = arguments[i];
369
370    for (var p = 0; p < props.length; p++) {
371      var prop = props[p];
372      if (other && other.hasOwnProperty(prop)) {
373        a[prop] = other[prop];
374      }
375    }
376  }
377  return a;
378};
379
380/**
381 * Extend object a with selected properties of object b.
382 * Only properties with defined values are copied.
383 *
384 * **Note:** Previous version of this routine implied that multiple source objects
385 *           could be used; however, the implementation was **wrong**.
386 *           Since multiple (>1) sources weren't used anywhere in the `vis.js` code,
387 *           this has been removed
388 *
389 * @param {Array.<string>} props names of first-level properties to copy over
390 * @param {object} a  target object
391 * @param {object} b  source object
392 * @param {boolean} [allowDeletion=false]  if true, delete property in a if explicitly set to null in b 
393 * @returns {Object} a
394 */
395exports.selectiveDeepExtend = function (props, a, b) {
396  var allowDeletion = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false;
397
398  // TODO: add support for Arrays to deepExtend
399  if (Array.isArray(b)) {
400    throw new TypeError('Arrays are not supported by deepExtend');
401  }
402
403  for (var p = 0; p < props.length; p++) {
404    var prop = props[p];
405    if (b.hasOwnProperty(prop)) {
406      if (b[prop] && b[prop].constructor === Object) {
407        if (a[prop] === undefined) {
408          a[prop] = {};
409        }
410        if (a[prop].constructor === Object) {
411          exports.deepExtend(a[prop], b[prop], false, allowDeletion);
412        } else {
413          copyOrDelete(a, b, prop, allowDeletion);
414        }
415      } else if (Array.isArray(b[prop])) {
416        throw new TypeError('Arrays are not supported by deepExtend');
417      } else {
418        copyOrDelete(a, b, prop, allowDeletion);
419      }
420    }
421  }
422  return a;
423};
424
425/**
426 * Extend object `a` with properties of object `b`, ignoring properties which are explicitly 
427 * specified to be excluded.
428 * 
429 * The properties of `b` are considered for copying.
430 * Properties which are themselves objects are are also extended.
431 * Only properties with defined values are copied
432 *
433 * @param {Array.<string>} propsToExclude  names of properties which should *not* be copied
434 * @param {Object}                      a  object to extend
435 * @param {Object}                      b  object to take properties from for extension
436 * @param {boolean} [allowDeletion=false]  if true, delete properties in a that are explicitly set to null in b 
437 * @return {Object} a
438 */
439exports.selectiveNotDeepExtend = function (propsToExclude, a, b) {
440  var allowDeletion = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false;
441
442  // TODO: add support for Arrays to deepExtend
443  // NOTE: array properties have an else-below; apparently, there is a problem here. 
444  if (Array.isArray(b)) {
445    throw new TypeError('Arrays are not supported by deepExtend');
446  }
447
448  for (var prop in b) {
449    if (!b.hasOwnProperty(prop)) continue; // Handle local properties only 
450    if (propsToExclude.indexOf(prop) !== -1) continue; // In exclusion list, skip
451
452    if (b[prop] && b[prop].constructor === Object) {
453      if (a[prop] === undefined) {
454        a[prop] = {};
455      }
456      if (a[prop].constructor === Object) {
457        exports.deepExtend(a[prop], b[prop]); // NOTE: allowDeletion not propagated!
458      } else {
459        copyOrDelete(a, b, prop, allowDeletion);
460      }
461    } else if (Array.isArray(b[prop])) {
462      a[prop] = [];
463      for (var i = 0; i < b[prop].length; i++) {
464        a[prop].push(b[prop][i]);
465      }
466    } else {
467      copyOrDelete(a, b, prop, allowDeletion);
468    }
469  }
470
471  return a;
472};
473
474/**
475 * Deep extend an object a with the properties of object b
476 *
477 * @param {Object} a
478 * @param {Object} b
479 * @param {boolean} [protoExtend=false]  If true, the prototype values will also be extended.
480 *                          (ie. the options objects that inherit from others will also get the inherited options)
481 * @param {boolean} [allowDeletion=false] If true, the values of fields that are null will be deleted
482 * @returns {Object}
483 */
484exports.deepExtend = function (a, b) {
485  var protoExtend = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
486  var allowDeletion = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false;
487
488  for (var prop in b) {
489    if (b.hasOwnProperty(prop) || protoExtend === true) {
490      if (b[prop] && b[prop].constructor === Object) {
491        if (a[prop] === undefined) {
492          a[prop] = {};
493        }
494        if (a[prop].constructor === Object) {
495          exports.deepExtend(a[prop], b[prop], protoExtend); // NOTE: allowDeletion not propagated!
496        } else {
497          copyOrDelete(a, b, prop, allowDeletion);
498        }
499      } else if (Array.isArray(b[prop])) {
500        a[prop] = [];
501        for (var i = 0; i < b[prop].length; i++) {
502          a[prop].push(b[prop][i]);
503        }
504      } else {
505        copyOrDelete(a, b, prop, allowDeletion);
506      }
507    }
508  }
509  return a;
510};
511
512/**
513 * Test whether all elements in two arrays are equal.
514 * @param {Array} a
515 * @param {Array} b
516 * @return {boolean} Returns true if both arrays have the same length and same
517 *                   elements.
518 */
519exports.equalArray = function (a, b) {
520  if (a.length != b.length) return false;
521
522  for (var i = 0, len = a.length; i < len; i++) {
523    if (a[i] != b[i]) return false;
524  }
525
526  return true;
527};
528
529/**
530 * Convert an object to another type
531 * @param {boolean | number | string | Date | Moment | Null | undefined} object
532 * @param {string | undefined} type   Name of the type. Available types:
533 *                                    'Boolean', 'Number', 'String',
534 *                                    'Date', 'Moment', ISODate', 'ASPDate'.
535 * @return {*} object
536 * @throws Error
537 */
538exports.convert = function (object, type) {
539  var match;
540
541  if (object === undefined) {
542    return undefined;
543  }
544  if (object === null) {
545    return null;
546  }
547
548  if (!type) {
549    return object;
550  }
551  if (!(typeof type === 'string') && !(type instanceof String)) {
552    throw new Error('Type must be a string');
553  }
554
555  //noinspection FallthroughInSwitchStatementJS
556  switch (type) {
557    case 'boolean':
558    case 'Boolean':
559      return Boolean(object);
560
561    case 'number':
562    case 'Number':
563      if (exports.isString(object) && !isNaN(Date.parse(object))) {
564        return moment(object).valueOf();
565      } else {
566        return Number(object.valueOf());
567      }
568    case 'string':
569    case 'String':
570      return String(object);
571
572    case 'Date':
573      if (exports.isNumber(object)) {
574        return new Date(object);
575      }
576      if (object instanceof Date) {
577        return new Date(object.valueOf());
578      } else if (moment.isMoment(object)) {
579        return new Date(object.valueOf());
580      }
581      if (exports.isString(object)) {
582        match = ASPDateRegex.exec(object);
583        if (match) {
584          // object is an ASP date
585          return new Date(Number(match[1])); // parse number
586        } else {
587          return moment(new Date(object)).toDate(); // parse string
588        }
589      } else {
590        throw new Error('Cannot convert object of type ' + exports.getType(object) + ' to type Date');
591      }
592
593    case 'Moment':
594      if (exports.isNumber(object)) {
595        return moment(object);
596      }
597      if (object instanceof Date) {
598        return moment(object.valueOf());
599      } else if (moment.isMoment(object)) {
600        return moment(object);
601      }
602      if (exports.isString(object)) {
603        match = ASPDateRegex.exec(object);
604        if (match) {
605          // object is an ASP date
606          return moment(Number(match[1])); // parse number
607        } else {
608          return moment(object); // parse string
609        }
610      } else {
611        throw new Error('Cannot convert object of type ' + exports.getType(object) + ' to type Date');
612      }
613
614    case 'ISODate':
615      if (exports.isNumber(object)) {
616        return new Date(object);
617      } else if (object instanceof Date) {
618        return object.toISOString();
619      } else if (moment.isMoment(object)) {
620        return object.toDate().toISOString();
621      } else if (exports.isString(object)) {
622        match = ASPDateRegex.exec(object);
623        if (match) {
624          // object is an ASP date
625          return new Date(Number(match[1])).toISOString(); // parse number
626        } else {
627          return moment(object).format(); // ISO 8601
628        }
629      } else {
630        throw new Error('Cannot convert object of type ' + exports.getType(object) + ' to type ISODate');
631      }
632
633    case 'ASPDate':
634      if (exports.isNumber(object)) {
635        return '/Date(' + object + ')/';
636      } else if (object instanceof Date) {
637        return '/Date(' + object.valueOf() + ')/';
638      } else if (exports.isString(object)) {
639        match = ASPDateRegex.exec(object);
640        var value;
641        if (match) {
642          // object is an ASP date
643          value = new Date(Number(match[1])).valueOf(); // parse number
644        } else {
645          value = new Date(object).valueOf(); // parse string
646        }
647        return '/Date(' + value + ')/';
648      } else {
649        throw new Error('Cannot convert object of type ' + exports.getType(object) + ' to type ASPDate');
650      }
651
652    default:
653      throw new Error('Unknown type "' + type + '"');
654  }
655};
656
657// parse ASP.Net Date pattern,
658// for example '/Date(1198908717056)/' or '/Date(1198908717056-0700)/'
659// code from http://momentjs.com/
660var ASPDateRegex = /^\/?Date\((\-?\d+)/i;
661
662/**
663 * Get the type of an object, for example exports.getType([]) returns 'Array'
664 * @param {*} object
665 * @return {string} type
666 */
667exports.getType = function (object) {
668  var type = typeof object === 'undefined' ? 'undefined' : (0, _typeof3['default'])(object);
669
670  if (type == 'object') {
671    if (object === null) {
672      return 'null';
673    }
674    if (object instanceof Boolean) {
675      return 'Boolean';
676    }
677    if (object instanceof Number) {
678      return 'Number';
679    }
680    if (object instanceof String) {
681      return 'String';
682    }
683    if (Array.isArray(object)) {
684      return 'Array';
685    }
686    if (object instanceof Date) {
687      return 'Date';
688    }
689    return 'Object';
690  } else if (type == 'number') {
691    return 'Number';
692  } else if (type == 'boolean') {
693    return 'Boolean';
694  } else if (type == 'string') {
695    return 'String';
696  } else if (type === undefined) {
697    return 'undefined';
698  }
699
700  return type;
701};
702
703/**
704 * Used to extend an array and copy it. This is used to propagate paths recursively.
705 *
706 * @param {Array} arr
707 * @param {*} newValue
708 * @returns {Array}
709 */
710exports.copyAndExtendArray = function (arr, newValue) {
711  var newArr = [];
712  for (var i = 0; i < arr.length; i++) {
713    newArr.push(arr[i]);
714  }
715  newArr.push(newValue);
716  return newArr;
717};
718
719/**
720 * Used to extend an array and copy it. This is used to propagate paths recursively.
721 *
722 * @param {Array} arr
723 * @returns {Array}
724 */
725exports.copyArray = function (arr) {
726  var newArr = [];
727  for (var i = 0; i < arr.length; i++) {
728    newArr.push(arr[i]);
729  }
730  return newArr;
731};
732
733/**
734 * Retrieve the absolute left value of a DOM element
735 * @param {Element} elem        A dom element, for example a div
736 * @return {number} left        The absolute left position of this element
737 *                              in the browser page.
738 */
739exports.getAbsoluteLeft = function (elem) {
740  return elem.getBoundingClientRect().left;
741};
742
743exports.getAbsoluteRight = function (elem) {
744  return elem.getBoundingClientRect().right;
745};
746
747/**
748 * Retrieve the absolute top value of a DOM element
749 * @param {Element} elem        A dom element, for example a div
750 * @return {number} top        The absolute top position of this element
751 *                              in the browser page.
752 */
753exports.getAbsoluteTop = function (elem) {
754  return elem.getBoundingClientRect().top;
755};
756
757/**
758 * add a className to the given elements style
759 * @param {Element} elem
760 * @param {string} classNames
761 */
762exports.addClassName = function (elem, classNames) {
763  var classes = elem.className.split(' ');
764  var newClasses = classNames.split(' ');
765  classes = classes.concat(newClasses.filter(function (className) {
766    return classes.indexOf(className) < 0;
767  }));
768  elem.className = classes.join(' ');
769};
770
771/**
772 * add a className to the given elements style
773 * @param {Element} elem
774 * @param {string} classNames
775 */
776exports.removeClassName = function (elem, classNames) {
777  var classes = elem.className.split(' ');
778  var oldClasses = classNames.split(' ');
779  classes = classes.filter(function (className) {
780    return oldClasses.indexOf(className) < 0;
781  });
782  elem.className = classes.join(' ');
783};
784
785/**
786 * For each method for both arrays and objects.
787 * In case of an array, the built-in Array.forEach() is applied. (**No, it's not!**)
788 * In case of an Object, the method loops over all properties of the object.
789 * @param {Object | Array} object   An Object or Array
790 * @param {function} callback       Callback method, called for each item in
791 *                                  the object or array with three parameters:
792 *                                  callback(value, index, object)
793 */
794exports.forEach = function (object, callback) {
795  var i, len;
796  if (Array.isArray(object)) {
797    // array
798    for (i = 0, len = object.length; i < len; i++) {
799      callback(object[i], i, object);
800    }
801  } else {
802    // object
803    for (i in object) {
804      if (object.hasOwnProperty(i)) {
805        callback(object[i], i, object);
806      }
807    }
808  }
809};
810
811/**
812 * Convert an object into an array: all objects properties are put into the
813 * array. The resulting array is unordered.
814 * @param {Object} object
815 * @returns {Array} array
816 */
817exports.toArray = function (object) {
818  var array = [];
819
820  for (var prop in object) {
821    if (object.hasOwnProperty(prop)) array.push(object[prop]);
822  }
823
824  return array;
825};
826
827/**
828 * Update a property in an object
829 * @param {Object} object
830 * @param {string} key
831 * @param {*} value
832 * @return {Boolean} changed
833 */
834exports.updateProperty = function (object, key, value) {
835  if (object[key] !== value) {
836    object[key] = value;
837    return true;
838  } else {
839    return false;
840  }
841};
842
843/**
844 * Throttle the given function to be only executed once per animation frame
845 * @param {function} fn
846 * @returns {function} Returns the throttled function
847 */
848exports.throttle = function (fn) {
849  var scheduled = false;
850
851  return function throttled() {
852    if (!scheduled) {
853      scheduled = true;
854      requestAnimationFrame(function () {
855        scheduled = false;
856        fn();
857      });
858    }
859  };
860};
861
862/**
863 * Add and event listener. Works for all browsers
864 * @param {Element}     element    An html element
865 * @param {string}      action     The action, for example "click",
866 *                                 without the prefix "on"
867 * @param {function}    listener   The callback function to be executed
868 * @param {boolean}     [useCapture]
869 */
870exports.addEventListener = function (element, action, listener, useCapture) {
871  if (element.addEventListener) {
872    if (useCapture === undefined) useCapture = false;
873
874    if (action === "mousewheel" && navigator.userAgent.indexOf("Firefox") >= 0) {
875      action = "DOMMouseScroll"; // For Firefox
876    }
877
878    element.addEventListener(action, listener, useCapture);
879  } else {
880    element.attachEvent("on" + action, listener); // IE browsers
881  }
882};
883
884/**
885 * Remove an event listener from an element
886 * @param {Element}     element         An html dom element
887 * @param {string}      action          The name of the event, for example "mousedown"
888 * @param {function}    listener        The listener function
889 * @param {boolean}     [useCapture]
890 */
891exports.removeEventListener = function (element, action, listener, useCapture) {
892  if (element.removeEventListener) {
893    // non-IE browsers
894    if (useCapture === undefined) useCapture = false;
895
896    if (action === "mousewheel" && navigator.userAgent.indexOf("Firefox") >= 0) {
897      action = "DOMMouseScroll"; // For Firefox
898    }
899
900    element.removeEventListener(action, listener, useCapture);
901  } else {
902    // IE browsers
903    element.detachEvent("on" + action, listener);
904  }
905};
906
907/**
908 * Cancels the event if it is cancelable, without stopping further propagation of the event.
909 * @param {Event} event
910 */
911exports.preventDefault = function (event) {
912  if (!event) event = window.event;
913
914  if (event.preventDefault) {
915    event.preventDefault(); // non-IE browsers
916  } else {
917    event.returnValue = false; // IE browsers
918  }
919};
920
921/**
922 * Get HTML element which is the target of the event
923 * @param {Event} event
924 * @return {Element} target element
925 */
926exports.getTarget = function (event) {
927  // code from http://www.quirksmode.org/js/events_properties.html
928  if (!event) {
929    event = window.event;
930  }
931
932  var target;
933
934  if (event.target) {
935    target = event.target;
936  } else if (event.srcElement) {
937    target = event.srcElement;
938  }
939
940  if (target.nodeType != undefined && target.nodeType == 3) {
941    // defeat Safari bug
942    target = target.parentNode;
943  }
944
945  return target;
946};
947
948/**
949 * Check if given element contains given parent somewhere in the DOM tree
950 * @param {Element} element
951 * @param {Element} parent
952 * @returns {boolean}
953 */
954exports.hasParent = function (element, parent) {
955  var e = element;
956
957  while (e) {
958    if (e === parent) {
959      return true;
960    }
961    e = e.parentNode;
962  }
963
964  return false;
965};
966
967exports.option = {};
968
969/**
970 * Convert a value into a boolean
971 * @param {Boolean | function | undefined} value
972 * @param {boolean} [defaultValue]
973 * @returns {Boolean} bool
974 */
975exports.option.asBoolean = function (value, defaultValue) {
976  if (typeof value == 'function') {
977    value = value();
978  }
979
980  if (value != null) {
981    return value != false;
982  }
983
984  return defaultValue || null;
985};
986
987/**
988 * Convert a value into a number
989 * @param {Boolean | function | undefined} value
990 * @param {number} [defaultValue]
991 * @returns {number} number
992 */
993exports.option.asNumber = function (value, defaultValue) {
994  if (typeof value == 'function') {
995    value = value();
996  }
997
998  if (value != null) {
999    return Number(value) || defaultValue || null;
1000  }
1001
1002  return defaultValue || null;
1003};
1004
1005/**
1006 * Convert a value into a string
1007 * @param {string | function | undefined} value
1008 * @param {string} [defaultValue]
1009 * @returns {String} str
1010 */
1011exports.option.asString = function (value, defaultValue) {
1012  if (typeof value == 'function') {
1013    value = value();
1014  }
1015
1016  if (value != null) {
1017    return String(value);
1018  }
1019
1020  return defaultValue || null;
1021};
1022
1023/**
1024 * Convert a size or location into a string with pixels or a percentage
1025 * @param {string | number | function | undefined} value
1026 * @param {string} [defaultValue]
1027 * @returns {String} size
1028 */
1029exports.option.asSize = function (value, defaultValue) {
1030  if (typeof value == 'function') {
1031    value = value();
1032  }
1033
1034  if (exports.isString(value)) {
1035    return value;
1036  } else if (exports.isNumber(value)) {
1037    return value + 'px';
1038  } else {
1039    return defaultValue || null;
1040  }
1041};
1042
1043/**
1044 * Convert a value into a DOM element
1045 * @param {HTMLElement | function | undefined} value
1046 * @param {HTMLElement} [defaultValue]
1047 * @returns {HTMLElement | null} dom
1048 */
1049exports.option.asElement = function (value, defaultValue) {
1050  if (typeof value == 'function') {
1051    value = value();
1052  }
1053
1054  return value || defaultValue || null;
1055};
1056
1057/**
1058 * http://stackoverflow.com/questions/5623838/rgb-to-hex-and-hex-to-rgb
1059 *
1060 * @param {string} hex
1061 * @returns {{r: *, g: *, b: *}} | 255 range
1062 */
1063exports.hexToRGB = function (hex) {
1064  // Expand shorthand form (e.g. "03F") to full form (e.g. "0033FF")
1065  var shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i;
1066  hex = hex.replace(shorthandRegex, function (m, r, g, b) {
1067    return r + r + g + g + b + b;
1068  });
1069  var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
1070  return result ? {
1071    r: parseInt(result[1], 16),
1072    g: parseInt(result[2], 16),
1073    b: parseInt(result[3], 16)
1074  } : null;
1075};
1076
1077/**
1078 * This function takes color in hex format or rgb() or rgba() format and overrides the opacity. Returns rgba() string.
1079 * @param {string} color
1080 * @param {number} opacity
1081 * @returns {String}
1082 */
1083exports.overrideOpacity = function (color, opacity) {
1084  var rgb;
1085  if (color.indexOf("rgba") != -1) {
1086    return color;
1087  } else if (color.indexOf("rgb") != -1) {
1088    rgb = color.substr(color.indexOf("(") + 1).replace(")", "").split(",");
1089    return "rgba(" + rgb[0] + "," + rgb[1] + "," + rgb[2] + "," + opacity + ")";
1090  } else {
1091    rgb = exports.hexToRGB(color);
1092    if (rgb == null) {
1093      return color;
1094    } else {
1095      return "rgba(" + rgb.r + "," + rgb.g + "," + rgb.b + "," + opacity + ")";
1096    }
1097  }
1098};
1099
1100/**
1101 *
1102 * @param {number} red     0 -- 255
1103 * @param {number} green   0 -- 255
1104 * @param {number} blue    0 -- 255
1105 * @returns {String}
1106 * @constructor
1107 */
1108exports.RGBToHex = function (red, green, blue) {
1109  return "#" + ((1 << 24) + (red << 16) + (green << 8) + blue).toString(16).slice(1);
1110};
1111
1112/**
1113 * Parse a color property into an object with border, background, and
1114 * highlight colors
1115 * @param {Object | String} color
1116 * @return {Object} colorObject
1117 */
1118exports.parseColor = function (color) {
1119  var c;
1120  if (exports.isString(color) === true) {
1121    if (exports.isValidRGB(color) === true) {
1122      var rgb = color.substr(4).substr(0, color.length - 5).split(',').map(function (value) {
1123        return parseInt(value);
1124      });
1125      color = exports.RGBToHex(rgb[0], rgb[1], rgb[2]);
1126    }
1127    if (exports.isValidHex(color) === true) {
1128      var hsv = exports.hexToHSV(color);
1129      var lighterColorHSV = { h: hsv.h, s: hsv.s * 0.8, v: Math.min(1, hsv.v * 1.02) };
1130      var darkerColorHSV = { h: hsv.h, s: Math.min(1, hsv.s * 1.25), v: hsv.v * 0.8 };
1131      var darkerColorHex = exports.HSVToHex(darkerColorHSV.h, darkerColorHSV.s, darkerColorHSV.v);
1132      var lighterColorHex = exports.HSVToHex(lighterColorHSV.h, lighterColorHSV.s, lighterColorHSV.v);
1133      c = {
1134        background: color,
1135        border: darkerColorHex,
1136        highlight: {
1137          background: lighterColorHex,
1138          border: darkerColorHex
1139        },
1140        hover: {
1141          background: lighterColorHex,
1142          border: darkerColorHex
1143        }
1144      };
1145    } else {
1146      c = {
1147        background: color,
1148        border: color,
1149        highlight: {
1150          background: color,
1151          border: color
1152        },
1153        hover: {
1154          background: color,
1155          border: color
1156        }
1157      };
1158    }
1159  } else {
1160    c = {};
1161    c.background = color.background || undefined;
1162    c.border = color.border || undefined;
1163
1164    if (exports.isString(color.highlight)) {
1165      c.highlight = {
1166        border: color.highlight,
1167        background: color.highlight
1168      };
1169    } else {
1170      c.highlight = {};
1171      c.highlight.background = color.highlight && color.highlight.background || undefined;
1172      c.highlight.border = color.highlight && color.highlight.border || undefined;
1173    }
1174
1175    if (exports.isString(color.hover)) {
1176      c.hover = {
1177        border: color.hover,
1178        background: color.hover
1179      };
1180    } else {
1181      c.hover = {};
1182      c.hover.background = color.hover && color.hover.background || undefined;
1183      c.hover.border = color.hover && color.hover.border || undefined;
1184    }
1185  }
1186
1187  return c;
1188};
1189
1190/**
1191 * http://www.javascripter.net/faq/rgb2hsv.htm
1192 *
1193 * @param {number} red
1194 * @param {number} green
1195 * @param {number} blue
1196 * @returns {{h: number, s: number, v: number}}
1197 * @constructor
1198 */
1199exports.RGBToHSV = function (red, green, blue) {
1200  red = red / 255;
1200green = green / 255;blue = blue / 255;
1201  var minRGB = Math.min(red, Math.min(green, blue));
1202  var maxRGB = Math.max(red, Math.max(green, blue));
1203
1204  // Black-gray-white
1205  if (minRGB == maxRGB) {
1206    return { h: 0, s: 0, v: minRGB };
1207  }
1208
1209  // Colors other than black-gray-white:
1210  var d = red == minRGB ? green - blue : blue == minRGB ? red - green : blue - red;
1211  var h = red == minRGB ? 3 : blue == minRGB ? 1 : 5;
1212  var hue = 60 * (h - d / (maxRGB - minRGB)) / 360;
1213  var saturation = (maxRGB - minRGB) / maxRGB;
1214  var value = maxRGB;
1215  return { h: hue, s: saturation, v: value };
1216};
1217
1218var cssUtil = {
1219  // split a string with css styles into an object with key/values
1220  split: function split(cssText) {
1221    var styles = {};
1222
1223    cssText.split(';').forEach(function (style) {
1224      if (style.trim() != '') {
1225        var parts = style.split(':');
1226        var key = parts[0].trim();
1227        var value = parts[1].trim();
1228        styles[key] = value;
1229      }
1230    });
1231
1232    return styles;
1233  },
1234
1235  // build a css text string from an object with key/values
1236  join: function join(styles) {
1237    return (0, _keys2['default'])(styles).map(function (key) {
1238      return key + ': ' + styles[key];
1239    }).join('; ');
1240  }
1241};
1242
1243/**
1244 * Append a string with css styles to an element
1245 * @param {Element} element
1246 * @param {string} cssText
1247 */
1248exports.addCssText = function (element, cssText) {
1249  var currentStyles = cssUtil.split(element.style.cssText);
1250  var newStyles = cssUtil.split(cssText);
1251  var styles = exports.extend(currentStyles, newStyles);
1252
1253  element.style.cssText = cssUtil.join(styles);
1254};
1255
1256/**
1257 * Remove a string with css styles from an element
1258 * @param {Element} element
1259 * @param {string} cssText
1260 */
1261exports.removeCssText = function (element, cssText) {
1262  var styles = cssUtil.split(element.style.cssText);
1263  var removeStyles = cssUtil.split(cssText);
1264
1265  for (var key in removeStyles) {
1266    if (removeStyles.hasOwnProperty(key)) {
1267      delete styles[key];
1268    }
1269  }
1270
1271  element.style.cssText = cssUtil.join(styles);
1272};
1273
1274/**
1275 * https://gist.github.com/mjijackson/5311256
1276 * @param {number} h
1277 * @param {number} s
1278 * @param {number} v
1279 * @returns {{r: number, g: number, b: number}}
1280 * @constructor
1281 */
1282exports.HSVToRGB = function (h, s, v) {
1283  var r, g, b;
1284
1285  var i = Math.floor(h * 6);
1286  var f = h * 6 - i;
1287  var p = v * (1 - s);
1288  var q = v * (1 - f * s);
1289  var t = v * (1 - (1 - f) * s);
1290
1291  switch (i % 6) {
1292    case 0:
1293      r = v, g = t, b = p;break;
1294    case 1:
1295      r = q, g = v, b = p;break;
1296    case 2:
1297      r = p, g = v, b = t;break;
1298    case 3:
1299      r = p, g = q, b = v;break;
1300    case 4:
1301      r = t, g = p, b = v;break;
1302    case 5:
1303      r = v, g = p, b = q;break;
1304  }
1305
1306  return { r: Math.floor(r * 255), g: Math.floor(g * 255), b: Math.floor(b * 255) };
1307};
1308
1309exports.HSVToHex = function (h, s, v) {
1310  var rgb = exports.HSVToRGB(h, s, v);
1311  return exports.RGBToHex(rgb.r, rgb.g, rgb.b);
1312};
1313
1314exports.hexToHSV = function (hex) {
1315  var rgb = exports.hexToRGB(hex);
1316  return exports.RGBToHSV(rgb.r, rgb.g, rgb.b);
1317};
1318
1319exports.isValidHex = function (hex) {
1320  var isOk = /(^#[0-9A-F]{6}$)|(^#[0-9A-F]{3}$)/i.test(hex);
1321  return isOk;
1322};
1323
1324exports.isValidRGB = function (rgb) {
1325  rgb = rgb.replace(" ", "");
1326  var isOk = /rgb\((\d{1,3}),(\d{1,3}),(\d{1,3})\)/i.test(rgb);
1327  return isOk;
1328};
1329exports.isValidRGBA = function (rgba) {
1330  rgba = rgba.replace(" ", "");
1331  var isOk = /rgba\((\d{1,3}),(\d{1,3}),(\d{1,3}),(.{1,3})\)/i.test(rgba);
1332  return isOk;
1333};
1334
1335/**
1336 * This recursively redirects the prototype of JSON objects to the referenceObject
1337 * This is used for default options.
1338 *
1339 * @param {Array.<string>} fields
1340 * @param {Object} referenceObject
1341 * @returns {*}
1342 */
1343exports.selectiveBridgeObject = function (fields, referenceObject) {
1344  if (referenceObject !== null && (typeof referenceObject === 'undefined' ? 'undefined' : (0, _typeof3['default'])(referenceObject)) === "object") {
1345    // !!! typeof null === 'object'
1346    var objectTo = (0, _create2['default'])(referenceObject);
1347    for (var i = 0; i < fields.length; i++) {
1348      if (referenceObject.hasOwnProperty(fields[i])) {
1349        if ((0, _typeof3['default'])(referenceObject[fields[i]]) == "object") {
1350          objectTo[fields[i]] = exports.bridgeObject(referenceObject[fields[i]]);
1351        }
1352      }
1353    }
1354    return objectTo;
1355  } else {
1356    return null;
1357  }
1358};
1359
1360/**
1361 * This recursively redirects the prototype of JSON objects to the referenceObject
1362 * This is used for default options.
1363 *
1364 * @param {Object} referenceObject
1365 * @returns {*}
1366 */
1367exports.bridgeObject = function (referenceObject) {
1368  if (referenceObject !== null && (typeof referenceObject === 'undefined' ? 'undefined' : (0, _typeof3['default'])(referenceObject)) === "object") {
1369    // !!! typeof null === 'object'
1370    var objectTo = (0, _create2['default'])(referenceObject);
1371    if (referenceObject instanceof Element) {
1372      // Avoid bridging DOM objects
1373      objectTo = referenceObject;
1374    } else {
1375      objectTo = (0, _create2['default'])(referenceObject);
1376      for (var i in referenceObject) {
1377        if (referenceObject.hasOwnProperty(i)) {
1378          if ((0, _typeof3['default'])(referenceObject[i]) == "object") {
1379            objectTo[i] = exports.bridgeObject(referenceObject[i]);
1380          }
1381        }
1382      }
1383    }
1384    return objectTo;
1385  } else {
1386    return null;
1387  }
1388};
1389
1390/**
1391 * This method provides a stable sort implementation, very fast for presorted data
1392 *
1393 * @param {Array} a the array
1394 * @param {function} compare an order comparator
1395 * @returns {Array}
1396 */
1397exports.insertSort = function (a, compare) {
1398  for (var i = 0; i < a.length; i++) {
1399    var k = a[i];
1400    for (var j = i; j > 0 && compare(k, a[j - 1]) < 0; j--) {
1401      a[j] = a[j - 1];
1402    }
1403    a[j] = k;
1404  }
1405  return a;
1406};
1407
1408/**
1409 * This is used to set the options of subobjects in the options object.
1410 *
1411 * A requirement of these subobjects is that they have an 'enabled' element
1412 * which is optional for the user but mandatory for the program.
1413 *
1414 * The added value here of the merge is that option 'enabled' is set as required.
1415 *
1416 *
1417 * @param {object} mergeTarget   | either this.options or the options used for the groups.
1418 * @param {object} options       | options
1419 * @param {string} option        | option key in the options argument
1420 * @param {object} globalOptions | global options, passed in to determine value of option 'enabled'
1421 */
1422exports.mergeOptions = function (mergeTarget, options, option) {
1423  var globalOptions = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {};
1424
1425  // Local helpers
1426  var isPresent = function isPresent(obj) {
1427    return obj !== null && obj !== undefined;
1428  };
1429
1430  var isObject = function isObject(obj) {
1431    return obj !== null && (typeof obj === 'undefined' ? 'undefined' : (0, _typeof3['default'])(obj)) === 'object';
1432  };
1433
1434  // https://stackoverflow.com/a/34491287/1223531
1435  var isEmpty = function isEmpty(obj) {
1436    for (var x in obj) {
1437      if (obj.hasOwnProperty(x)) return false;
1438    }
1439    return true;
1440  };
1441
1442  // Guards
1443  if (!isObject(mergeTarget)) {
1444    throw new Error('Parameter mergeTarget must be an object');
1445  }
1446
1447  if (!isObject(options)) {
1448    throw new Error('Parameter options must be an object');
1449  }
1450
1451  if (!isPresent(option)) {
1452    throw new Error('Parameter option must have a value');
1453  }
1454
1455  if (!isObject(globalOptions)) {
1456    throw new Error('Parameter globalOptions must be an object');
1457  }
1458
1459  //
1460  // Actual merge routine, separated from main logic
1461  // Only a single level of options is merged. Deeper levels are ref'd. This may actually be an issue.
1462  //
1463  var doMerge = function doMerge(target, options, option) {
1464    if (!isObject(target[option])) {
1465      target[option] = {};
1466    }
1467
1468    var src = options[option];
1469    var dst = target[option];
1470    for (var prop in src) {
1471      if (src.hasOwnProperty(prop)) {
1472        dst[prop] = src[prop];
1473      }
1474    }
1475  };
1476
1477  // Local initialization
1478  var srcOption = options[option];
1479  var globalPassed = isObject(globalOptions) && !isEmpty(globalOptions);
1480  var globalOption = globalPassed ? globalOptions[option] : undefined;
1481  var globalEnabled = globalOption ? globalOption.enabled : undefined;
1482
1483  /////////////////////////////////////////
1484  // Main routine
1485  /////////////////////////////////////////
1486  if (srcOption === undefined) {
1487    return; // Nothing to do
1488  }
1489
1490  if (typeof srcOption === 'boolean') {
1491    if (!isObject(mergeTarget[option])) {
1492      mergeTarget[option] = {};
1493    }
1494
1495    mergeTarget[option].enabled = srcOption;
1496    return;
1497  }
1498
1499  if (srcOption === null && !isObject(mergeTarget[option])) {
1500    // If possible, explicit copy from globals
1501    if (isPresent(globalOption)) {
1502      mergeTarget[option] = (0, _create2['default'])(globalOption);
1503    } else {
1504      return; // Nothing to do
1505    }
1506  }
1507
1508  if (!isObject(srcOption)) {
1509    return;
1510  }
1511
1512  //
1513  // Ensure that 'enabled' is properly set. It is required internally
1514  // Note that the value from options will always overwrite the existing value
1515  //
1516  var enabled = true; // default value
1517
1518  if (srcOption.enabled !== undefined) {
1519    enabled = srcOption.enabled;
1520  } else {
1521    // Take from globals, if present
1522    if (globalEnabled !== undefined) {
1523      enabled = globalOption.enabled;
1524    }
1525  }
1526
1527  doMerge(mergeTarget, options, option);
1528  mergeTarget[option].enabled = enabled;
1529};
1530
1531/**
1532 * This function does a binary search for a visible item in a sorted list. If we find a visible item, the code that uses
1533 * this function will then iterate in both directions over this sorted list to find all visible items.
1534 *
1535 * @param {Item[]} orderedItems       | Items ordered by start
1536 * @param {function} comparator       | -1 is lower, 0 is equal, 1 is higher
1537 * @param {string} field
1538 * @param {string} field2
1539 * @returns {number}
1540 * @private
1541 */
1542exports.binarySearchCustom = function (orderedItems, comparator, field, field2) {
1543  var maxIterations = 10000;
1544  var iteration = 0;
1545  var low = 0;
1546  var high = orderedItems.length - 1;
1547
1548  while (low <= high && iteration < maxIterations) {
1549    var middle = Math.floor((low + high) / 2);
1550
1551    var item = orderedItems[middle];
1552    var value = field2 === undefined ? item[field] : item[field][field2];
1553
1554    var searchResult = comparator(value);
1555    if (searchResult == 0) {
1556      // jihaa, found a visible item!
1557      return middle;
1558    } else if (searchResult == -1) {
1559      // it is too small --> increase low
1560      low = middle + 1;
1561    } else {
1562      // it is too big --> decrease high
1563      high = middle - 1;
1564    }
1565
1566    iteration++;
1567  }
1568
1569  return -1;
1570};
1571
1572/**
1573 * This function does a binary search for a specific value in a sorted array. If it does not exist but is in between of
1574 * two values, we return either the one before or the one after, depending on user input
1575 * If it is found, we return the index, else -1.
1576 *
1577 * @param {Array} orderedItems
1578 * @param {{start: number, end: number}} target
1579 * @param {string} field
1580 * @param {string} sidePreference   'before' or 'after'
1581 * @param {function} comparator an optional comparator, returning -1,0,1 for <,==,>.
1582 * @returns {number}
1583 * @private
1584 */
1585exports.binarySearchValue = function (orderedItems, target, field, sidePreference, comparator) {
1586  var maxIterations = 10000;
1587  var iteration = 0;
1588  var low = 0;
1589  var high = orderedItems.length - 1;
1590  var prevValue, value, nextValue, middle;
1591
1592  comparator = comparator != undefined ? comparator : function (a, b) {
1593    return a == b ? 0 : a < b ? -1 : 1;
1594  };
1595
1596  while (low <= high && iteration < maxIterations) {
1597    // get a new guess
1598    middle = Math.floor(0.5 * (high + low));
1599    prevValue = orderedItems[Math.max(0, middle - 1)][field];
1600    value = orderedItems[middle][field];
1601    nextValue = orderedItems[Math.min(orderedItems.length - 1, middle + 1)][field];
1602
1603    if (comparator(value, target) == 0) {
1604      // we found the target
1605      return middle;
1606    } else if (comparator(prevValue, target) < 0 && comparator(value, target) > 0) {
1607      // target is in between of the previous and the current
1608      return sidePreference == 'before' ? Math.max(0, middle - 1) : middle;
1609    } else if (comparator(value, target) < 0 && comparator(nextValue, target) > 0) {
1610      // target is in between of the current and the next
1611      return sidePreference == 'before' ? middle : Math.min(orderedItems.length - 1, middle + 1);
1612    } else {
1613      // didnt find the target, we need to change our boundaries.
1614      if (comparator(value, target) < 0) {
1615        // it is too small --> increase low
1616        low = middle + 1;
1617      } else {
1618        // it is too big --> decrease high
1619        high = middle - 1;
1620      }
1621    }
1622    iteration++;
1623  }
1624
1625  // didnt find anything. Return -1.
1626  return -1;
1627};
1628
1629/*
1630 * Easing Functions - inspired from http://gizma.com/easing/
1631 * only considering the t value for the range [0, 1] => [0, 1]
1632 * https://gist.github.com/gre/1650294
1633 */
1634exports.easingFunctions = {
1635  // no easing, no acceleration
1636  linear: function linear(t) {
1637    return t;
1638  },
1639  // accelerating from zero velocity
1640  easeInQuad: function easeInQuad(t) {
1641    return t * t;
1642  },
1643  // decelerating to zero velocity
1644  easeOutQuad: function easeOutQuad(t) {
1645    return t * (2 - t);
1646  },
1647  // acceleration until halfway, then deceleration
1648  easeInOutQuad: function easeInOutQuad(t) {
1649    return t < .5 ? 2 * t * t : -1 + (4 - 2 * t) * t;
1650  },
1651  // accelerating from zero velocity
1652  easeInCubic: function easeInCubic(t) {
1653    return t * t * t;
1654  },
1655  // decelerating to zero velocity
1656  easeOutCubic: function easeOutCubic(t) {
1657    return --t * t * t + 1;
1658  },
1659  // acceleration until halfway, then deceleration
1660  easeInOutCubic: function easeInOutCubic(t) {
1661    return t < .5 ? 4 * t * t * t : (t - 1) * (2 * t - 2) * (2 * t - 2) + 1;
1662  },
1663  // accelerating from zero velocity
1664  easeInQuart: function easeInQuart(t) {
1665    return t * t * t * t;
1666  },
1667  // decelerating to zero velocity
1668  easeOutQuart: function easeOutQuart(t) {
1669    return 1 - --t * t * t * t;
1670  },
1671  // acceleration until halfway, then deceleration
1672  easeInOutQuart: function easeInOutQuart(t) {
1673    return t < .5 ? 8 * t * t * t * t : 1 - 8 * --t * t * t * t;
1674  },
1675  // accelerating from zero velocity
1676  easeInQuint: function easeInQuint(t) {
1677    return t * t * t * t * t;
1678  },
1679  // decelerating to zero velocity
1680  easeOutQuint: function easeOutQuint(t) {
1681    return 1 + --t * t * t * t * t;
1682  },
1683  // acceleration until halfway, then deceleration
1684  easeInOutQuint: function easeInOutQuint(t) {
1685    return t < .5 ? 16 * t * t * t * t * t : 1 + 16 * --t * t * t * t * t;
1686  }
1687};
1688
1689exports.getScrollBarWidth = function () {
1690  var inner = document.createElement('p');
1691  inner.style.width = "100%";
1692  inner.style.height = "200px";
1693
1694  var outer = document.createElement('div');
1695  outer.style.position = "absolute";
1696  outer.style.top = "0px";
1697  outer.style.left = "0px";
1698  outer.style.visibility = "hidden";
1699  outer.style.width = "200px";
1700  outer.style.height = "150px";
1701  outer.style.overflow = "hidden";
1702  outer.appendChild(inner);
1703
1704  document.body.appendChild(outer);
1705  var w1 = inner.offsetWidth;
1706  outer.style.overflow = 'scroll';
1707  var w2 = inner.offsetWidth;
1708  if (w1 == w2) w2 = outer.clientWidth;
1709
1710  document.body.removeChild(outer);
1711
1712  return w1 - w2;
1713};
1714
1715exports.topMost = function (pile, accessors) {
1716  var candidate = void 0;
1717  if (!Array.isArray(accessors)) {
1718    accessors = [accessors];
1719  }
1720  var _iteratorNormalCompletion = true;
1721  var _didIteratorError = false;
1722  var _iteratorError = undefined;
1723
1724  try {
1725    for (var _iterator = (0, _getIterator3['default'])(pile), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
1726      var member = _step.value;
1727
1728      if (member) {
1729        candidate = member[accessors[0]];
1730        for (var i = 1; i < accessors.length; i++) {
1731          if (candidate) {
1732            candidate = candidate[accessors[i]];
1733          } else {
1734            continue;
1735          }
1736        }
1737        if (typeof candidate != 'undefined') {
1738          break;
1739        }
1740      }
1741    }
1742  } catch (err) {
1743    _didIteratorError = true;
1744    _iteratorError = err;
1745  } finally {
1746    try {
1747      if (!_iteratorNormalCompletion && _iterator['return']) {
1748        _iterator['return']();
1749      }
1750    } finally {
1751      if (_didIteratorError) {
1752        throw _iteratorError;
1753      }
1754    }
1755  }
1756
1757  return candidate;
1758};
1759
1760/***/ }),
1761/* 3 */
1762/***/ (function(module, exports, __webpack_require__) {
1763
1764module.exports = { "default": __webpack_require__(194), __esModule: true };
1765
1766/***/ }),
1767/* 4 */
1768/***/ (function(module, exports, __webpack_require__) {
1769
1770"use strict";
1771
1772
1773exports.__esModule = true;
1774
1775var _typeof2 = __webpack_require__(6);
1776
1777var _typeof3 = _interopRequireDefault(_typeof2);
1778
1779function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
1780
1781exports.default = function (self, call) {
1782  if (!self) {
1783    throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
1784  }
1785
1786  return call && ((typeof call === "undefined" ? "undefined" : (0, _typeof3.default)(call)) === "object" || typeof call === "function") ? call : self;
1787};
1788
1789/***/ }),
1790/* 5 */
1791/***/ (function(module, exports, __webpack_require__) {
1792
1793"use strict";
1794
1795
1796exports.__esModule = true;
1797
1798var _setPrototypeOf = __webpack_require__(196);
1799
1800var _setPrototypeOf2 = _interopRequireDefault(_setPrototypeOf);
1801
1802var _create = __webpack_require__(29);
1803
1804var _create2 = _interopRequireDefault(_create);
1805
1806var _typeof2 = __webpack_require__(6);
1807
1808var _typeof3 = _interopRequireDefault(_typeof2);
1809
1810function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
1811
1812exports.default = function (subClass, superClass) {
1813  if (typeof superClass !== "function" && superClass !== null) {
1814    throw new TypeError("Super expression must either be null or a function, not " + (typeof superClass === "undefined" ? "undefined" : (0, _typeof3.default)(superClass)));
1815  }
1816
1817  subClass.prototype = (0, _create2.default)(superClass && superClass.prototype, {
1818    constructor: {
1819      value: subClass,
1820      enumerable: false,
1821      writable: true,
1822      configurable: true
1823    }
1824  });
1825  if (superClass) _setPrototypeOf2.default ? (0, _setPrototypeOf2.default)(subClass, superClass) : subClass.__proto__ = superClass;
1826};
1827
1828/***/ }),
1829/* 6 */
1830/***/ (function(module, exports, __webpack_require__) {
1831
1832"use strict";
1833
1834
1835exports.__esModule = true;
1836
1837var _iterator = __webpack_require__(142);
1838
1839var _iterator2 = _interopRequireDefault(_iterator);
1840
1841var _symbol = __webpack_require__(144);
1842
1843var _symbol2 = _interopRequireDefault(_symbol);
1844
1845var _typeof = typeof _symbol2.default === "function" && typeof _iterator2.default === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof _symbol2.default === "function" && obj.constructor === _symbol2.default && obj !== _symbol2.default.prototype ? "symbol" : typeof obj; };
1846
1847function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
1848
1849exports.default = typeof _symbol2.default === "function" && _typeof(_iterator2.default) === "symbol" ? function (obj) {
1850  return typeof obj === "undefined" ? "undefined" : _typeof(obj);
1851} : function (obj) {
1852  return obj && typeof _symbol2.default === "function" && obj.constructor === _symbol2.default && obj !== _symbol2.default.prototype ? "symbol" : typeof obj === "undefined" ? "undefined" : _typeof(obj);
1853};
1854
1855/***/ }),
1856/* 7 */
1857/***/ (function(module, exports) {
1858
1859var core = module.exports = { version: '2.5.1' };
1860if (typeof __e == 'number') __e = core; // eslint-disable-line no-undef
1861
1862
1863/***/ }),
1864/* 8 */
1865/***/ (function(module, exports, __webpack_require__) {
1866
1867module.exports = { "default": __webpack_require__(140), __esModule: true };
1868
1869/***/ }),
1870/* 9 */
1871/***/ (function(module, exports, __webpack_require__) {
1872
1873"use strict";
1874
1875
1876// first check if moment.js is already loaded in the browser window, if so,
1877// use this instance. Else, load via commonjs.
1878module.exports = typeof window !== 'undefined' && window['moment'] || __webpack_require__(154);
1879
1880/***/ }),
1881/* 10 */
1882/***/ (function(module, exports, __webpack_require__) {
1883
1884"use strict";
1885
1886
1887/**
1888 * Setup a mock hammer.js object, for unit testing.
1889 *
1890 * Inspiration: https://github.com/uber/deck.gl/pull/658
1891 *
1892 * @returns {{on: noop, off: noop, destroy: noop, emit: noop, get: get}}
1893 */
1894function hammerMock() {
1895  var noop = function noop() {};
1896
1897  return {
1898    on: noop,
1899    off: noop,
1900    destroy: noop,
1901    emit: noop,
1902
1903    get: function get(m) {
1904      //eslint-disable-line no-unused-vars
1905      return {
1906        set: noop
1907      };
1908    }
1909  };
1910}
1911
1912if (typeof window !== 'undefined') {
1913  var propagating = __webpack_require__(175);
1914  var Hammer = window['Hammer'] || __webpack_require__(176);
1915  module.exports = propagating(Hammer, {
1916    preventDefault: 'mouse'
1917  });
1918} else {
1919  module.exports = function () {
1920    // hammer.js is only available in a browser, not in node.js. Replacing it with a mock object.
1921    return hammerMock();
1922  };
1923}
1924
1925/***/ }),
1926/* 11 */
1927/***/ (function(module, exports, __webpack_require__) {
1928
1929"use strict";
1930
1931
1932var _stringify = __webpack_require__(19);
1933
1934var _stringify2 = _interopRequireDefault(_stringify);
1935
1936var _typeof2 = __webpack_require__(6);
1937
1938var _typeof3 = _interopRequireDefault(_typeof2);
1939
1940var _keys = __webpack_require__(8);
1941
1942var _keys2 = _interopRequireDefault(_keys);
1943
1944function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
1945
1946var util = __webpack_require__(2);
1947var Queue = __webpack_require__(43);
1948
1949/**
1950 * DataSet
1951 * // TODO: add a DataSet constructor DataSet(data, options)
1952 *
1953 * Usage:
1954 *     var dataSet = new DataSet({
1955 *         fieldId: '_id',
1956 *         type: {
1957 *             // ...
1958 *         }
1959 *     });
1960 *
1961 *     dataSet.add(item);
1962 *     dataSet.add(data);
1963 *     dataSet.update(item);
1964 *     dataSet.update(data);
1965 *     dataSet.remove(id);
1966 *     dataSet.remove(ids);
1967 *     var data = dataSet.get();
1968 *     var data = dataSet.get(id);
1969 *     var data = dataSet.get(ids);
1970 *     var data = dataSet.get(ids, options, data);
1971 *     dataSet.clear();
1972 *
1973 * A data set can:
1974 * - add/remove/update data
1975 * - gives triggers upon changes in the data
1976 * - can  import/export data in various data formats
1977 *
1978 * @param {Array} [data]    Optional array with initial data
1979 * @param {Object} [options]   Available options:
1980 *                             {string} fieldId Field name of the id in the
1981 *                                              items, 'id' by default.
1982 *                             {Object.<string, string} type
1983 *                                              A map with field names as key,
1984 *                                              and the field type as value.
1985 *                             {Object} queue   Queue changes to the DataSet,
1986 *                                              flush them all at once.
1987 *                                              Queue options:
1988 *                                              - {number} delay  Delay in ms, null by default
1989 *                                              - {number} max    Maximum number of entries in the queue, Infinity by default
1990 * @constructor DataSet
1991 */
1992function DataSet(data, options) {
1993  // correctly read optional arguments
1994  if (data && !Array.isArray(data)) {
1995    options = data;
1996    data = null;
1997  }
1998
1999  this._options = options || {};
2000  this._data = {}; // map with data indexed by id
2001  this.length = 0; // number of items in the DataSet
2002  this._fieldId = this._options.fieldId || 'id'; // name of the field containing id
2003  this._type = {}; // internal field types (NOTE: this can differ from this._options.type)
2004
2005  // all variants of a Date are internally stored as Date, so we can convert
2006  // from everything to everything (also from ISODate to Number for example)
2007  if (this._options.type) {
2008    var fields = (0, _keys2['default'])(this._options.type);
2009    for (var i = 0, len = fields.length; i < len; i++) {
2010      var field = fields[i];
2011      var value = this._options.type[field];
2012      if (value == 'Date' || value == 'ISODate' || value == 'ASPDate') {
2013        this._type[field] = 'Date';
2014      } else {
2015        this._type[field] = value;
2016      }
2017    }
2018  }
2019
2020  this._subscribers = {}; // event subscribers
2021
2022  // add initial data when provided
2023  if (data) {
2024    this.add(data);
2025  }
2026
2027  this.setOptions(options);
2028}
2029
2030/**
2031 * @param {Object} options   Available options:
2032 *                             {Object} queue   Queue changes to the DataSet,
2033 *                                              flush them all at once.
2034 *                                              Queue options:
2035 *                                              - {number}
2035 delay  Delay in ms, null by default
2036 *                                              - {number} max    Maximum number of entries in the queue, Infinity by default
2037 */
2038DataSet.prototype.setOptions = function (options) {
2039  if (options && options.queue !== undefined) {
2040    if (options.queue === false) {
2041      // delete queue if loaded
2042      if (this._queue) {
2043        this._queue.destroy();
2044        delete this._queue;
2045      }
2046    } else {
2047      // create queue and update its options
2048      if (!this._queue) {
2049        this._queue = Queue.extend(this, {
2050          replace: ['add', 'update', 'remove']
2051        });
2052      }
2053
2054      if ((0, _typeof3['default'])(options.queue) === 'object') {
2055        this._queue.setOptions(options.queue);
2056      }
2057    }
2058  }
2059};
2060
2061/**
2062 * Subscribe to an event, add an event listener
2063 * @param {string} event        Event name. Available events: 'add', 'update',
2064 *                              'remove'
2065 * @param {function} callback   Callback method. Called with three parameters:
2066 *                                  {string} event
2067 *                                  {Object | null} params
2068 *                                  {string | number} senderId
2069 */
2070DataSet.prototype.on = function (event, callback) {
2071  var subscribers = this._subscribers[event];
2072  if (!subscribers) {
2073    subscribers = [];
2074    this._subscribers[event] = subscribers;
2075  }
2076
2077  subscribers.push({
2078    callback: callback
2079  });
2080};
2081
2082/**
2083 * Unsubscribe from an event, remove an event listener
2084 * @param {string} event
2085 * @param {function} callback
2086 */
2087DataSet.prototype.off = function (event, callback) {
2088  var subscribers = this._subscribers[event];
2089  if (subscribers) {
2090    this._subscribers[event] = subscribers.filter(function (listener) {
2091      return listener.callback != callback;
2092    });
2093  }
2094};
2095
2096/**
2097 * Trigger an event
2098 * @param {string} event
2099 * @param {Object | null} params
2100 * @param {string} [senderId]       Optional id of the sender.
2101 * @private
2102 */
2103DataSet.prototype._trigger = function (event, params, senderId) {
2104  if (event == '*') {
2105    throw new Error('Cannot trigger event *');
2106  }
2107
2108  var subscribers = [];
2109  if (event in this._subscribers) {
2110    subscribers = subscribers.concat(this._subscribers[event]);
2111  }
2112  if ('*' in this._subscribers) {
2113    subscribers = subscribers.concat(this._subscribers['*']);
2114  }
2115
2116  for (var i = 0, len = subscribers.length; i < len; i++) {
2117    var subscriber = subscribers[i];
2118    if (subscriber.callback) {
2119      subscriber.callback(event, params, senderId || null);
2120    }
2121  }
2122};
2123
2124/**
2125 * Add data.
2126 * Adding an item will fail when there already is an item with the same id.
2127 * @param {Object | Array} data
2128 * @param {string} [senderId] Optional sender id
2129 * @return {Array.<string|number>} addedIds      Array with the ids of the added items
2130 */
2131DataSet.prototype.add = function (data, senderId) {
2132  var addedIds = [],
2133      id,
2134      me = this;
2135
2136  if (Array.isArray(data)) {
2137    // Array
2138    for (var i = 0, len = data.length; i < len; i++) {
2139      id = me._addItem(data[i]);
2140      addedIds.push(id);
2141    }
2142  } else if (data && (typeof data === 'undefined' ? 'undefined' : (0, _typeof3['default'])(data)) === 'object') {
2143    // Single item
2144    id = me._addItem(data);
2145    addedIds.push(id);
2146  } else {
2147    throw new Error('Unknown dataType');
2148  }
2149
2150  if (addedIds.length) {
2151    this._trigger('add', { items: addedIds }, senderId);
2152  }
2153
2154  return addedIds;
2155};
2156
2157/**
2158 * Update existing items. When an item does not exist, it will be created
2159 * @param {Object | Array} data
2160 * @param {string} [senderId] Optional sender id
2161 * @return {Array.<string|number>} updatedIds     The ids of the added or updated items
2162 * @throws {Error} Unknown Datatype
2163 */
2164DataSet.prototype.update = function (data, senderId) {
2165  var addedIds = [];
2166  var updatedIds = [];
2167  var oldData = [];
2168  var updatedData = [];
2169  var me = this;
2170  var fieldId = me._fieldId;
2171
2172  var addOrUpdate = function addOrUpdate(item) {
2173    var id = item[fieldId];
2174    if (me._data[id]) {
2175      var oldItem = util.extend({}, me._data[id]);
2176      // update item
2177      id = me._updateItem(item);
2178      updatedIds.push(id);
2179      updatedData.push(item);
2180      oldData.push(oldItem);
2181    } else {
2182      // add new item
2183      id = me._addItem(item);
2184      addedIds.push(id);
2185    }
2186  };
2187
2188  if (Array.isArray(data)) {
2189    // Array
2190    for (var i = 0, len = data.length; i < len; i++) {
2191      if (data[i] && (0, _typeof3['default'])(data[i]) === 'object') {
2192        addOrUpdate(data[i]);
2193      } else {
2194        console.warn('Ignoring input item, which is not an object at index ' + i);
2195      }
2196    }
2197  } else if (data && (typeof data === 'undefined' ? 'undefined' : (0, _typeof3['default'])(data)) === 'object') {
2198    // Single item
2199    addOrUpdate(data);
2200  } else {
2201    throw new Error('Unknown dataType');
2202  }
2203
2204  if (addedIds.length) {
2205    this._trigger('add', { items: addedIds }, senderId);
2206  }
2207  if (updatedIds.length) {
2208    var props = { items: updatedIds, oldData: oldData, data: updatedData };
2209    // TODO: remove deprecated property 'data' some day
2210    //Object.defineProperty(props, 'data', {
2211    //  'get': (function() {
2212    //    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');
2213    //    return updatedData;
2214    //  }).bind(this)
2215    //});
2216    this._trigger('update', props, senderId);
2217  }
2218
2219  return addedIds.concat(updatedIds);
2220};
2221
2222/**
2223 * Get a data item or multiple items.
2224 *
2225 * Usage:
2226 *
2227 *     get()
2228 *     get(options: Object)
2229 *
2230 *     get(id: number | string)
2231 *     get(id: number | string, options: Object)
2232 *
2233 *     get(ids: number[] | string[])
2234 *     get(ids: number[] | string[], options: Object)
2235 *
2236 * Where:
2237 *
2238 * {number | string} id         The id of an item
2239 * {number[] | string{}} ids    An array with ids of items
2240 * {Object} options             An Object with options. Available options:
2241 * {string} [returnType]        Type of data to be returned.
2242 *                              Can be 'Array' (default) or 'Object'.
2243 * {Object.<string, string>} [type]
2244 * {string[]} [fields]          field names to be returned
2245 * {function} [filter]          filter items
2246 * {string | function} [order]  Order the items by a field name or custom sort function.
2247 * @param {Array} args
2248 * @returns {DataSet}
2249 * @throws Error
2250 */
2251DataSet.prototype.get = function (args) {
2252  // eslint-disable-line no-unused-vars
2253  var me = this;
2254
2255  // parse the arguments
2256  var id, ids, options;
2257  var firstType = util.getType(arguments[0]);
2258  if (firstType == 'String' || firstType == 'Number') {
2259    // get(id [, options])
2260    id = arguments[0];
2261    options = arguments[1];
2262  } else if (firstType == 'Array') {
2263    // get(ids [, options])
2264    ids = arguments[0];
2265    options = arguments[1];
2266  } else {
2267    // get([, options])
2268    options = arguments[0];
2269  }
2270
2271  // determine the return type
2272  var returnType;
2273  if (options && options.returnType) {
2274    var allowedValues = ['Array', 'Object'];
2275    returnType = allowedValues.indexOf(options.returnType) == -1 ? 'Array' : options.returnType;
2276  } else {
2277    returnType = 'Array';
2278  }
2279
2280  // build options
2281  var type = options && options.type || this._options.type;
2282  var filter = options && options.filter;
2283  var items = [],
2284      item,
2285      itemIds,
2286      itemId,
2287      i,
2288      len;
2289
2290  // convert items
2291  if (id != undefined) {
2292    // return a single item
2293    item = me._getItem(id, type);
2294    if (item && filter && !filter(item)) {
2295      item = null;
2296    }
2297  } else if (ids != undefined) {
2298    // return a subset of items
2299    for (i = 0, len = ids.length; i < len; i++) {
2300      item = me._getItem(ids[i], type);
2301      if (!filter || filter(item)) {
2302        items.push(item);
2303      }
2304    }
2305  } else {
2306    // return all items
2307    itemIds = (0, _keys2['default'])(this._data);
2308    for (i = 0, len = itemIds.length; i < len; i++) {
2309      itemId = itemIds[i];
2310      item = me._getItem(itemId, type);
2311      if (!filter || filter(item)) {
2312        items.push(item);
2313      }
2314    }
2315  }
2316
2317  // order the results
2318  if (options && options.order && id == undefined) {
2319    this._sort(items, options.order);
2320  }
2321
2322  // filter fields of the items
2323  if (options && options.fields) {
2324    var fields = options.fields;
2325    if (id != undefined) {
2326      item = this._filterFields(item, fields);
2327    } else {
2328      for (i = 0, len = items.length; i < len; i++) {
2329        items[i] = this._filterFields(items[i], fields);
2330      }
2331    }
2332  }
2333
2334  // return the results
2335  if (returnType == 'Object') {
2336    var result = {},
2337        resultant;
2338    for (i = 0, len = items.length; i < len; i++) {
2339      resultant = items[i];
2340      result[resultant.id] = resultant;
2341    }
2342    return result;
2343  } else {
2344    if (id != undefined) {
2345      // a single item
2346      return item;
2347    } else {
2348      // just return our array
2349      return items;
2350    }
2351  }
2352};
2353
2354/**
2355 * Get ids of all items or from a filtered set of items.
2356 * @param {Object} [options]    An Object with options. Available options:
2357 *                              {function} [filter] filter items
2358 *                              {string | function} [order] Order the items by
2359 *                                  a field name or custom sort function.
2360 * @return {Array.<string|number>} ids
2361 */
2362DataSet.prototype.getIds = function (options) {
2363  var data = this._data,
2364      filter = options && options.filter,
2365      order = options && options.order,
2366      type = options && options.type || this._options.type,
2367      itemIds = (0, _keys2['default'])(data),
2368      i,
2369      len,
2370      id,
2371      item,
2372      items,
2373      ids = [];
2374
2375  if (filter) {
2376    // get filtered items
2377    if (order) {
2378      // create ordered list
2379      items = [];
2380      for (i = 0, len = itemIds.length; i < len; i++) {
2381        id = itemIds[i];
2382        item = this._getItem(id, type);
2383        if (filter(item)) {
2384          items.push(item);
2385        }
2386      }
2387
2388      this._sort(items, order);
2389
2390      for (i = 0, len = items.length; i < len; i++) {
2391        ids.push(items[i][this._fieldId]);
2392      }
2393    } else {
2394      // create unordered list
2395      for (i = 0, len = itemIds.length; i < len; i++) {
2396        id = itemIds[i];
2397        item = this._getItem(id, type);
2398        if (filter(item)) {
2399          ids.push(item[this._fieldId]);
2400        }
2401      }
2402    }
2403  } else {
2404    // get all items
2405    if (order) {
2406      // create an ordered list
2407      items = [];
2408      for (i = 0, len = itemIds.length; i < len; i++) {
2409        id = itemIds[i];
2410        items.push(data[id]);
2411      }
2412
2413      this._sort(items, order);
2414
2415      for (i = 0, len = items.length; i < len; i++) {
2416        ids.push(items[i][this._fieldId]);
2417      }
2418    } else {
2419      // create unordered list
2420      for (i = 0, len = itemIds.length; i < len; i++) {
2421        id = itemIds[i];
2422        item = data[id];
2423        ids.push(item[this._fieldId]);
2424      }
2425    }
2426  }
2427
2428  return ids;
2429};
2430
2431/**
2432 * Returns the DataSet itself. Is overwritten for example by the DataView,
2433 * which returns the DataSet it is connected to instead.
2434 * @returns {DataSet}
2435 */
2436DataSet.prototype.getDataSet = function () {
2437  return this;
2438};
2439
2440/**
2441 * Execute a callback function for every item in the dataset.
2442 * @param {function} callback
2443 * @param {Object} [options]    Available options:
2444 *                              {Object.<string, string>} [type]
2445 *                              {string[]} [fields] filter fields
2446 *                              {function} [filter] filter items
2447 *                              {string | function} [order] Order the items by
2448 *                                  a field name or custom sort function.
2449 */
2450DataSet.prototype.forEach = function (callback, options) {
2451  var filter = options && options.filter,
2452      type = options && options.type || this._options.type,
2453      data = this._data,
2454      itemIds = (0, _keys2['default'])(data),
2455      i,
2456      len,
2457      item,
2458      id;
2459
2460  if (options && options.order) {
2461    // execute forEach on ordered list
2462    var items = this.get(options);
2463
2464    for (i = 0, len = items.length; i < len; i++) {
2465      item = items[i];
2466      id = item[this._fieldId];
2467      callback(item, id);
2468    }
2469  } else {
2470    // unordered
2471    for (i = 0, len = itemIds.length; i < len; i++) {
2472      id = itemIds[i];
2473      item = this._getItem(id, type);
2474      if (!filter || filter(item)) {
2475        callback(item, id);
2476      }
2477    }
2478  }
2479};
2480
2481/**
2482 * Map every item in the dataset.
2483 * @param {function} callback
2484 * @param {Object} [options]    Available options:
2485 *                              {Object.<string, string>} [type]
2486 *                              {string[]} [fields] filter fields
2487 *                              {function} [filter] filter items
2488 *                              {string | function} [order] Order the items by
2489 *                                  a field name or custom sort function.
2490 * @return {Object[]} mappedItems
2491 */
2492DataSet.prototype.map = function (callback, options) {
2493  var filter = options && options.filter,
2494      type = options && options.type || this._options.type,
2495      mappedItems = [],
2496      data = this._data,
2497      itemIds = (0, _keys2['default'])(data),
2498      i,
2499      len,
2500      id,
2501      item;
2502
2503  // convert and filter items
2504  for (i = 0, len = itemIds.length; i < len; i++) {
2505    id = itemIds[i];
2506    item = this._getItem(id, type);
2507    if (!filter || filter(item)) {
2508      mappedItems.push(callback(item, id));
2509    }
2510  }
2511
2512  // order items
2513  if (options && options.order) {
2514    this._sort(mappedItems, options.order);
2515  }
2516
2517  return mappedItems;
2518};
2519
2520/**
2521 * Filter the fields of an item
2522 * @param {Object | null} item
2523 * @param {string[]} fields     Field names
2524 * @return {Object | null} filteredItem or null if no item is provided
2525 * @private
2526 */
2527DataSet.prototype._filterFields = function (item, fields) {
2528  if (!item) {
2529    // item is null
2530    return item;
2531  }
2532
2533  var filteredItem = {},
2534      itemFields = (0, _keys2['default'])(item),
2535      len = itemFields.length,
2536      i,
2537      field;
2538
2539  if (Array.isArray(fields)) {
2540    for (i = 0; i < len; i++) {
2541      field = itemFields[i];
2542      if (fields.indexOf(field) != -1) {
2543        filteredItem[field] = item[field];
2544      }
2545    }
2546  } else {
2547    for (i = 0; i < len; i++) {
2548      field = itemFields[i];
2549      if (fields.hasOwnProperty(field)) {
2550        filteredItem[fields[field]] = item[field];
2551      }
2552    }
2553  }
2554
2555  return filteredItem;
2556};
2557
2558/**
2559 * Sort the provided array with items
2560 * @param {Object[]} items
2561 * @param {string | function} order      A field name or custom sort function.
2562 * @private
2563 */
2564DataSet.prototype._sort = function (items, order) {
2565  if (util.isString(order)) {
2566    // order by provided field name
2567    var name = order; // field name
2568    items.sort(function (a, b) {
2569      var av = a[name];
2570      var bv = b[name];
2571      return av > bv ? 1 : av < bv ? -1 : 0;
2572    });
2573  } else if (typeof order === 'function') {
2574    // order by sort function
2575    items.sort(order);
2576  }
2577  // TODO: extend order by an Object {field:string, direction:string}
2578  //       where direction can be 'asc' or 'desc'
2579  else {
2580      throw new TypeError('Order must be a function or a string');
2581    }
2582};
2583
2584/**
2585 * Remove an object by pointer or by id
2586 * @param {string | number | Object | Array.<string|number>} id Object or id, or an array with
2587 *                                              objects or ids to be removed
2588 * @param {string} [senderId] Optional sender id
2589 * @return {Array.<string|number>} removedIds
2590 */
2591DataSet.prototype.remove = function (id, senderId) {
2592  var removedIds = [],
2593      removedItems = [],
2594      ids = [],
2595      i,
2596      len,
2597      itemId,
2598      item;
2599
2600  // force everything to be an array for simplicity
2601  ids = Array.isArray(id) ? id : [id];
2602
2603  for (i = 0, len = ids.length; i < len; i++) {
2604    item = this._remove(ids[i]);
2605    if (item) {
2606      itemId = item[this._fieldId];
2607      if (itemId != undefined) {
2608        removedIds.push(itemId);
2609        removedItems.push(item);
2610      }
2611    }
2612  }
2613
2614  if (removedIds.length) {
2615    this._trigger('remove', { items: removedIds, oldData: removedItems }, senderId);
2616  }
2617
2618  return removedIds;
2619};
2620
2621/**
2622 * Remove an item by its id
2623 * @param {number | string | Object} id   id or item
2624 * @returns {number | string | null} id
2625 * @private
2626 */
2627DataSet.prototype._remove = function (id) {
2628  var item, ident;
2629
2630  // confirm the id to use based on the args type
2631  if (util.isNumber(id) || util.isString(id)) {
2632    ident = id;
2633  } else if (id && (typeof id === 'undefined' ? 'undefined' : (0, _typeof3['default'])(id)) === 'object') {
2634    ident = id[this._fieldId]; // look for the identifier field using _fieldId
2635  }
2636
2637  // do the remove if the item is found
2638  if (ident !== undefined && this._data[ident]) {
2639    item = this._data[ident];
2640    delete this._data[ident];
2641    this.length--;
2642    return item;
2643  }
2644  return null;
2645};
2646
2647/**
2648 * Clear the data
2649 * @param {string} [senderId] Optional sender id
2650 * @return {Array.<string|number>} removedIds    The ids of all removed items
2651 */
2652DataSet.prototype.clear = function (senderId) {
2653  var i, len;
2654  var ids = (0, _keys2['default'])(this._data);
2655  var items = [];
2656
2657  for (i = 0, len = ids.length; i < len; i++) {
2658    items.push(this._data[ids[i]]);
2659  }
2660
2661  this._data = {};
2662  this.length = 0;
2663
2664  this._trigger('remove', { items: ids, oldData: items }, senderId);
2665
2666  return ids;
2667};
2668
2669/**
2670 * Find the item with maximum value of a specified field
2671 * @param {string} field
2672 * @return {Object | null} item  Item containing max value, or null if no items
2673 */
2674DataSet.prototype.max = function (field) {
2675  var data = this._data,
2676      itemIds = (0, _keys2['default'])(data),
2677      max = null,
2678      maxField = null,
2679      i,
2680      len;
2681
2682  for (i = 0, len = itemIds.length; i < len; i++) {
2683    var id = itemIds[i];
2684    var item = data[id];
2685    var itemField = item[field];
2686    if (itemField != null && (!max || itemField > maxField)) {
2687      max = item;
2688      maxField = itemField;
2689    }
2690  }
2691
2692  return max;
2693};
2694
2695/**
2696 * Find the item with minimum value of a specified field
2697 * @param {string} field
2698 * @return {Object | null} item  Item containing max value, or null if no items
2699 */
2700DataSet.prototype.min = function (field) {
2701  var data = this._data,
2702      itemIds = (0, _keys2['default'])(data),
2703      min = null,
2704      minField = null,
2705      i,
2706      len;
2707
2708  for (i = 0, len = itemIds.length; i < len; i++) {
2709    var id = itemIds[i];
2710    var item = data[id];
2711    var itemField = item[field];
2712    if (itemField != null && (!min || itemField < minField)) {
2713      min = item;
2714      minField = itemField;
2715    }
2716  }
2717
2718  return min;
2719};
2720
2721/**
2722 * Find all distinct values of a specified field
2723 * @param {string} field
2724 * @return {Array} values  Array containing all distinct values. If data items
2725 *                         do not contain the specified field are ignored.
2726 *                         The returned array is unordered.
2727 */
2728DataSet.prototype.distinct = function (field) {
2729  var data = this._data;
2730  var itemIds = (0, _keys2['default'])(data);
2731  var values = [];
2732  var fieldType = this._options.type && this._options.type[field] || null;
2733  var count = 0;
2734  var i, j, len;
2735
2736  for (i = 0, len = itemIds.length; i < len; i++) {
2737    var id = itemIds[i];
2738    var item = data[id];
2739    var value = item[field];
2740    var exists = false;
2741    for (j = 0; j < count; j++) {
2742      if (values[j] == value) {
2743        exists = true;
2744        break;
2745      }
2746    }
2747    if (!exists && value !== undefined) {
2748      values[count] = value;
2749      count++;
2750    }
2751  }
2752
2753  if (fieldType) {
2754    for (i = 0, len = values.length; i < len; i++) {
2755      values[i] = util.convert(values[i], fieldType);
2756    }
2757  }
2758
2759  return values;
2760};
2761
2762/**
2763 * Add a single item. Will fail when an item with the same id already exists.
2764 * @param {Object} item
2765 * @return {string} id
2766 * @private
2767 */
2768DataSet.prototype._addItem = function (item) {
2769  var id = item[this._fieldId];
2770
2771  if (id != undefined) {
2772    // check whether this id is already taken
2773    if (this._data[id]) {
2774      // item already exists
2775      throw new Error('Cannot add item: item with id ' + id + ' already exists');
2776    }
2777  } else {
2778    // generate an id
2779    id = util.randomUUID();
2780    item[this._fieldId] = id;
2781  }
2782
2783  var d = {},
2784      fields = (0, _keys2['default'])(item),
2785      i,
2786      len;
2787  for (i = 0, len = fields.length; i < len; i++) {
2788    var field = fields[i];
2789    var fieldType = this._type[field]; // type may be undefined
2790    d[field] = util.convert(item[field], fieldType);
2791  }
2792  this._data[id] = d;
2793  this.length++;
2794
2795  return id;
2796};
2797
2798/**
2799 * Get an item. Fields can be converted to a specific type
2800 * @param {string} id
2801 * @param {Object.<string, string>} [types]  field types to convert
2802 * @return {Object | null} item
2803 * @private
2804 */
2805DataSet.prototype._getItem = function (id, types) {
2806  var field, value, i, len;
2807
2808  // get the item from the dataset
2809  var raw = this._data[id];
2810  if (!raw) {
2811    return null;
2812  }
2813
2814  // convert the items field types
2815  var converted = {},
2816      fields = (0, _keys2['default'])(raw);
2817
2818  if (types) {
2819    for (i = 0, len = fields.length; i < len; i++) {
2820      field = fields[i];
2821      value = raw[field];
2822      converted[field] = util.convert(value, types[field]);
2823    }
2824  } else {
2825    // no field types specified, no converting needed
2826    for (i = 0, len = fields.length; i < len; i++) {
2827      field = fields[i];
2828      value = raw[field];
2829      converted[field] = value;
2830    }
2831  }
2832
2833  if (!converted[this._fieldId]) {
2834    converted[this._fieldId] = raw.id;
2835  }
2836
2837  return converted;
2838};
2839
2840/**
2841 * Update a single item: merge with existing item.
2842 * Will fail when the item has no id, or when there does not exist an item
2843 * with the same id.
2844 * @param {Object} item
2845 * @return {string} id
2846 * @private
2847 */
2848DataSet.prototype._updateItem = function (item) {
2849  var id = item[this._fieldId];
2850  if (id == undefined) {
2851    throw new Error('Cannot update item: item has no id (item: ' + (0, _stringify2['default'])(item) + ')');
2852  }
2853  var d = this._data[id];
2854  if (!d) {
2855    // item doesn't exist
2856    throw new Error('Cannot update item: no item with id ' + id + ' found');
2857  }
2858
2859  // merge with current item
2860  var fields = (0, _keys2['default'])(item);
2861  for (var i = 0, len = fields.length; i < len; i++) {
2862    var field = fields[i];
2863    var fieldType = this._type[field]; // type may be undefined
2864    d[field] = util.convert(item[field], fieldType);
2865  }
2866
2867  return id;
2868};
2869
2870module.exports = DataSet;
2871
2872/***/ }),
2873/* 12 */
2874/***/ (function(module, exports, __webpack_require__) {
2875
2876"use strict";
2877
2878
2879var _keys = __webpack_require__(8);
2880
2881var _keys2 = _interopRequireDefault(_keys);
2882
2883function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
2884
2885var util = __webpack_require__(2);
2886var DataSet = __webpack_require__(11);
2887
2888/**
2889 * DataView
2890 *
2891 * a dataview offers a filtered view on a dataset or an other dataview.
2892 *
2893 * @param {DataSet | DataView} data
2894 * @param {Object} [options]   Available options: see method get
2895 *
2896 * @constructor DataView
2897 */
2898function DataView(data, options) {
2899  this._data = null;
2900  this._ids = {}; // ids of the items currently in memory (just contains a boolean true)
2901  this.length = 0; // number of items in the DataView
2902  this._options = options || {};
2903  this._fieldId = 'id'; // name of the field containing id
2904  this._subscribers = {}; // event subscribers
2905
2906  var me = this;
2907  this.listener = function () {
2908    me._onEvent.apply(me, arguments);
2909  };
2910
2911  this.setData(data);
2912}
2913
2914// TODO: implement a function .config() to dynamically update things like configured filter
2915// and trigger changes accordingly
2916
2917/**
2918 * Set a data source for the view
2919 * @param {DataSet | DataView} data
2920 */
2921DataView.prototype.setData = function (data) {
2922  var ids, id, i, len, items;
2923
2924  if (this._data) {
2925    // unsubscribe from current dataset
2926    if (this._data.off) {
2927      this._data.off('*', this.listener);
2928    }
2929
2930    // trigger a remove of all items in memory
2931    ids = this._data.getIds({ filter: this._options && this._options.filter });
2932    items = [];
2933
2934    for (i = 0, len = ids.length; i < len; i++) {
2935      items.push(this._data._data[ids[i]]);
2936    }
2937
2938    this._ids = {};
2939    this.length = 0;
2940    this._trigger('remove', { items: ids, oldData: items });
2941  }
2942
2943  this._data = data;
2944
2945  if (this._data) {
2946    // update fieldId
2947    this._fieldId = this._options.fieldId || this._data && this._data.options && this._data.options.fieldId || 'id';
2948
2949    // trigger an add of all added items
2950    ids = this._data.getIds({ filter: this._options && this._options.filter });
2951    for (i = 0, len = ids.length; i < len; i++) {
2952      id = ids[i];
2953      this._ids[id] = true;
2954    }
2955    this.length = ids.length;
2956    this._trigger('add', { items: ids });
2957
2958    // subscribe to new dataset
2959    if (this._data.on) {
2960      this._data.on('*', this.listener);
2961    }
2962  }
2963};
2964
2965/**
2966 * Refresh the DataView. Useful when the DataView has a filter function
2967 * containing a variable parameter.
2968 */
2969DataView.prototype.refresh = function () {
2970  var id, i, len;
2971  var ids = this._data.getIds({ filter: this._options && this._options.filter }),
2972      oldIds = (0, _keys2['default'])(this._ids),
2973      newIds = {},
2974      addedIds = [],
2975      removedIds = [],
2976      removedItems = [];
2977
2978  // check for additions
2979  for (i = 0, len = ids.length; i < len; i++) {
2980    id = ids[i];
2981    newIds[id] = true;
2982    if (!this._ids[id]) {
2983      addedIds.push(id);
2984      this._ids[id] = true;
2985    }
2986  }
2987
2988  // check for removals
2989  for (i = 0, len = oldIds.length; i < len; i++) {
2990    id = oldIds[i];
2991    if (!newIds[id]) {
2992      removedIds.push(id);
2993      removedItems.push(this._data._data[id]);
2994      delete this._ids[id];
2995    }
2996  }
2997
2998  this.length += addedIds.length - removedIds.length;
2999
3000  // trigger events
3001  if (addedIds.length) {
3002    this._trigger('add', { items: addedIds });
3003  }
3004  if (removedIds.length) {
3005    this._trigger('remove', { items: removedIds, oldData: removedItems });
3006  }
3007};
3008
3009/**
3010 * Get data from the data view
3011 *
3012 * Usage:
3013 *
3014 *     get()
3015 *     get(options: Object)
3016 *     get(options: Object, data: Array | DataTable)
3017 *
3018 *     get(id: Number)
3019 *     get(id: Number, options: Object)
3020 *     get(id: Number, options: Object, data: Array | DataTable)
3021 *
3022 *     get(ids: Number[])
3023 *     get(ids: Number[], options: Object)
3024 *     get(ids: Number[], options: Object, data: Array | DataTable)
3025 *
3026 * Where:
3027 *
3028 * {number | string} id         The id of an item
3029 * {number[] | string{}} ids    An array with ids of items
3030 * {Object} options             An Object with options. Available options:
3031 *                              {string} [type] Type of data to be returned. Can
3032 *                                              be 'DataTable' or 'Array' (default)
3033 *                              {Object.<string, string>} [convert]
3034 *                              {string[]} [fields] field names to be returned
3035 *                              {function} [filter] filter items
3036 *                              {string | function} [order] Order the items by
3037 *                                  a field name or custom sort function.
3038 * {Array | DataTable} [data]   If provided, items will be appended to this
3039 *                              array or table. Required in case of Google
3040 *                              DataTable.
3041 * @param {Array} args
3042 * @return {DataSet|DataView}
3043 */
3044DataView.prototype.get = function (args) {
3045  // eslint-disable-line no-unused-vars
3046  var me = this;
3047
3048  // parse the arguments
3049  var ids, options, data;
3050  var firstType = util.getType(arguments[0]);
3051  if (firstType == 'String' || firstType == 'Number' || firstType == 'Array') {
3052    // get(id(s) [, options] [, data])
3053    ids = arguments[0]; // can be a single id or an array with ids
3054    options = arguments[1];
3055    data = arguments[2];
3056  } else {
3057    // get([, options] [, data])
3058    options = arguments[0];
3059    data = arguments[1];
3060  }
3061
3062  // extend the options with the default options and provided options
3063  var viewOptions = util.extend({}, this._options, options);
3064
3065  // create a combined filter method when needed
3066  if (this._options.filter && options && options.filter) {
3067    viewOptions.filter = function (item) {
3068      return me._options.filter(item) && options.filter(item);
3069    };
3070  }
3071
3072  // build up the call to the linked data set
3073  var getArguments = [];
3074  if (ids != undefined) {
3075    getArguments.push(ids);
3076  }
3077  getArguments.push(viewOptions);
3078  getArguments.push(data);
3079
3080  return this._data && this._data.get.apply(this._data, getArguments);
3081};
3082
3083/**
3084 * Get ids of all items or from a filtered set of items.
3085 * @param {Object} [options]    An Object with options. Available options:
3086 *                              {function} [filter] filter items
3087 *                              {string | function} [order] Order the items by
3088 *                                  a field name or custom sort function.
3089 * @return {Array.<string|number>} ids
3090 */
3091DataView.prototype.getIds = function (options) {
3092  var ids;
3093
3094  if (this._data) {
3095    var defaultFilter = this._options.filter;
3096    var filter;
3097
3098    if (options && options.filter) {
3099      if (defaultFilter) {
3100        filter = function filter(item) {
3101          return defaultFilter(item) && options.filter(item);
3102        };
3103      } else {
3104        filter = options.filter;
3105      }
3106    } else {
3107      filter = defaultFilter;
3108    }
3109
3110    ids = this._data.getIds({
3111      filter: filter,
3112      order: options && options.order
3113    });
3114  } else {
3115    ids = [];
3116  }
3117
3118  return ids;
3119};
3120
3121/**
3122 * Map every item in the dataset.
3123 * @param {function} callback
3124 * @param {Object} [options]    Available options:
3125 *                              {Object.<string, string>} [type]
3126 *                              {string[]} [fields] filter fields
3127 *                              {function} [filter] filter items
3128 *                              {string | function} [order] Order the items by
3129 *                                  a field name or custom sort function.
3130 * @return {Object[]} mappedItems
3131 */
3132DataView.prototype.map = function (callback, options) {
3133  var mappedItems = [];
3134  if (this._data) {
3135    var defaultFilter = this._options.filter;
3136    var filter;
3137
3138    if (options && options.filter) {
3139      if (defaultFilter) {
3140        filter = function filter(item) {
3141          return defaultFilter(item) && options.filter(item);
3142        };
3143      } else {
3144        filter = options.filter;
3145      }
3146    } else {
3147      filter = defaultFilter;
3148    }
3149
3150    mappedItems = this._data.map(callback, {
3151      filter: filter,
3152      order: options && options.order
3153    });
3154  } else {
3155    mappedItems = [];
3156  }
3157
3158  return mappedItems;
3159};
3160
3161/**
3162 * Get the DataSet to which this DataView is connected. In case there is a chain
3163 * of multiple DataViews, the root DataSet of this chain is returned.
3164 * @return {DataSet} dataSet
3165 */
3166DataView.prototype.getDataSet = function () {
3167  var dataSet = this;
3168  while (dataSet instanceof DataView) {
3169    dataSet = dataSet._data;
3170  }
3171  return dataSet || null;
3172};
3173
3174/**
3175 * Event listener. Will propagate all events from the connected data set to
3176 * the subscribers of the DataView, but will filter the items and only trigger
3177 * when there are changes in the filtered data set.
3178 * @param {string} event
3179 * @param {Object | null} params
3180 * @param {string} senderId
3181 * @private
3182 */
3183DataView.prototype._onEvent = function (event, params, senderId) {
3184  var i, len, id, item;
3185  var ids = params && params.items;
3186  var addedIds = [],
3187      updatedIds = [],
3188      removedIds = [],
3189      oldItems = [],
3190      updatedItems = [],
3191      removedItems = [];
3192
3193  if (ids && this._data) {
3194    switch (event) {
3195      case 'add':
3196        // filter the ids of the added items
3197        for (i = 0, len = ids.length; i < len; i++) {
3198          id = ids[i];
3199          item = this.get(id);
3200          if (item) {
3201            this._ids[id] = true;
3202            addedIds.push(id);
3203          }
3204        }
3205
3206        break;
3207
3208      case 'update':
3209        // determine the event from the views viewpoint: an updated
3210        // item can be added, updated, or removed from this view.
3211        for (i = 0, len = ids.length; i < len; i++) {
3212          id = ids[i];
3213          item = this.get(id);
3214
3215          if (item) {
3216            if (this._ids[id]) {
3217              updatedIds.push(id);
3218              updatedItems.push(params.data[i]);
3219              oldItems.push(params.oldData[i]);
3220            } else {
3221              this._ids[id] = true;
3222              addedIds.push(id);
3223            }
3224          } else {
3225            if (this._ids[id]) {
3226              delete this._ids[id];
3227              removedIds.push(id);
3228              removedItems.push(params.oldData[i]);
3229            } else {
3230              // nothing interesting for me :-(
3231            }
3232          }
3233        }
3234
3235        break;
3236
3237      case 'remove':
3238        // filter the ids of the removed items
3239        for (i = 0, len = ids.length; i < len; i++) {
3240          id = ids[i];
3241          if (this._ids[id]) {
3242            delete this._ids[id];
3243            removedIds.push(id);
3244            removedItems.push(params.oldData[i]);
3245          }
3246        }
3247
3248        break;
3249    }
3250
3251    this.length += addedIds.length - removedIds.length;
3252
3253    if (addedIds.length) {
3254      this._trigger('add', { items: addedIds }, senderId);
3255    }
3256    if (updatedIds.length) {
3257      this._trigger('update', { items: updatedIds, oldData: oldItems, data: updatedItems }, senderId);
3258    }
3259    if (removedIds.length) {
3260      this._trigger('remove', { items: removedIds, oldData: removedItems }, senderId);
3261    }
3262  }
3263};
3264
3265// copy subscription functionality from DataSet
3266DataView.prototype.on = DataSet.prototype.on;
3267DataView.prototype.off = DataSet.prototype.off;
3268DataView.prototype._trigger = DataSet.prototype._trigger;
3269
3270// TODO: make these functions deprecated (replaced with `on` and `off` since version 0.5)
3271DataView.prototype.subscribe = DataView.prototype.on;
3272DataView.prototype.unsubscribe = DataView.prototype.off;
3273
3274module.exports = DataView;
3275
3276/***/ }),
3277/* 13 */
3278/***/ (function(module, exports, __webpack_require__) {
3279
3280var store = __webpack_require__(57)('wks');
3281var uid = __webpack_require__(40);
3282var Symbol = __webpack_require__(18).Symbol;
3283var USE_SYMBOL = typeof Symbol == 'function';
3284
3285var $exports = module.exports = function (name) {
3286  return store[name] || (store[name] =
3287    USE_SYMBOL && Symbol[name] || (USE_SYMBOL ? Symbol : uid)('Symbol.' + name));
3288};
3289
3290$exports.store = store;
3291
3292
3293/***/ }),
3294/* 14 */
3295/***/ (function(module, exports, __webpack_require__) {
3296
3297"use strict";
3298
3299
3300// DOM utility methods
3301
3302/**
3303 * this prepares the JSON container for allocating SVG elements
3304 * @param {Object} JSONcontainer
3305 * @private
3306 */
3307exports.prepareElements = function (JSONcontainer) {
3308  // cleanup the redundant svgElements;
3309  for (var elementType in JSONcontainer) {
3310    if (JSONcontainer.hasOwnProperty(elementType)) {
3311      JSONcontainer[elementType].redundant = JSONcontainer[elementType].used;
3312      JSONcontainer[elementType].used = [];
3313    }
3314  }
3315};
3316
3317/**
3318 * this cleans up all the unused SVG elements. By asking for the parentNode, we only need to supply the JSON container from
3319 * which to remove the redundant elements.
3320 *
3321 * @param {Object} JSONcontainer
3322 * @private
3323 */
3324exports.cleanupElements = function (JSONcontainer) {
3325  // cleanup the redundant svgElements;
3326  for (var elementType in JSONcontainer) {
3327    if (JSONcontainer.hasOwnProperty(elementType)) {
3328      if (JSONcontainer[elementType].redundant) {
3329        for (var i = 0; i < JSONcontainer[elementType].redundant.length; i++) {
3330          JSONcontainer[elementType].redundant[i].parentNode.removeChild(JSONcontainer[elementType].redundant[i]);
3331        }
3332        JSONcontainer[elementType].redundant = [];
3333      }
3334    }
3335  }
3336};
3337
3338/**
3339 * Ensures that all elements are removed first up so they can be recreated cleanly
3340 * @param {Object} JSONcontainer
3341 */
3342exports.resetElements = function (JSONcontainer) {
3343  exports.prepareElements(JSONcontainer);
3344  exports.cleanupElements(JSONcontainer);
3345  exports.prepareElements(JSONcontainer);
3346};
3347
3348/**
3349 * Allocate or generate an SVG element if needed. Store a reference to it in the JSON container and draw it in the svgContainer
3350 * the JSON container and the SVG container have to be supplied so other svg containers (like the legend) can use this.
3351 *
3352 * @param {string} elementType
3353 * @param {Object} JSONcontainer
3354 * @param {Object} svgContainer
3355 * @returns {Element}
3356 * @private
3357 */
3358exports.getSVGElement = function (elementType, JSONcontainer, svgContainer) {
3359  var element;
3360  // allocate SVG element, if it doesnt yet exist, create one.
3361  if (JSONcontainer.hasOwnProperty(elementType)) {
3362    // this element has been created before
3363    // check if there is an redundant element
3364    if (JSONcontainer[elementType].redundant.length > 0) {
3365      element = JSONcontainer[elementType].redundant[0];
3366      JSONcontainer[elementType].redundant.shift();
3367    } else {
3368      // create a new element and add it to the SVG
3369      element = document.createElementNS('http://www.w3.org/2000/svg', elementType);
3370      svgContainer.appendChild(element);
3371    }
3372  } else {
3373    // create a new element and add it to the SVG, also create a new object in the svgElements to keep track of it.
3374    element = document.createElementNS('http://www.w3.org/2000/svg', elementType);
3375    JSONcontainer[elementType] = { used: [], redundant: [] };
3376    svgContainer.appendChild(element);
3377  }
3378  JSONcontainer[elementType].used.push(element);
3379  return element;
3380};
3381
3382/**
3383 * Allocate or generate an SVG element if needed. Store a reference to it in the JSON container and draw it in the svgContainer
3384 * the JSON container and the SVG container have to be supplied so other svg containers (like the legend) can use this.
3385 *
3386 * @param {string} elementType
3387 * @param {Object} JSONcontainer
3388 * @param {Element} DOMContainer
3389 * @param {Element} insertBefore
3390 * @returns {*}
3391 */
3392exports.getDOMElement = function (elementType, JSONcontainer, DOMContainer, insertBefore) {
3393  var element;
3394  // allocate DOM element, if it doesnt yet exist, create one.
3395  if (JSONcontainer.hasOwnProperty(elementType)) {
3396    // this element has been created before
3397    // check if there is an redundant element
3398    if (JSONcontainer[elementType].redundant.length > 0) {
3399      element = JSONcontainer[elementType].redundant[0];
3400      JSONcontainer[elementType].redundant.shift();
3401    } else {
3402      // create a new element and add it to the SVG
3403      element = document.createElement(elementType);
3404      if (insertBefore !== undefined) {
3405        DOMContainer.insertBefore(element, insertBefore);
3406      } else {
3407        DOMContainer.appendChild(element);
3408      }
3409    }
3410  } else {
3411    // create a new element and add it to the SVG, also create a new object in the svgElements to keep track of it.
3412    element = document.createElement(elementType);
3413    JSONcontainer[elementType] = { used: [], redundant: [] };
3414    if (insertBefore !== undefined) {
3415      DOMContainer.insertBefore(element, insertBefore);
3416    } else {
3417      DOMContainer.appendChild(element);
3418    }
3419  }
3420  JSONcontainer[elementType].used.push(element);
3421  return element;
3422};
3423
3424/**
3425 * Draw a point object. This is a separate function because it can also be called by the legend.
3426 * The reason the JSONcontainer and the target SVG svgContainer have to be supplied is so the legend can use these functions
3427 * as well.
3428 *
3429 * @param {number} x
3430 * @param {number} y
3431 * @param {Object} groupTemplate: A template containing the necessary information to draw the datapoint e.g., {style: 'circle', size: 5, className: 'className' }
3432 * @param {Object} JSONcontainer
3433 * @param {Object} svgContainer
3434 * @param {Object} labelObj
3435 * @returns {vis.PointItem}
3436 */
3437exports.drawPoint = function (x, y, groupTemplate, JSONcontainer, svgContainer, labelObj) {
3438  var point;
3439  if (groupTemplate.style == 'circle') {
3440    point = exports.getSVGElement('circle', JSONcontainer, svgContainer);
3441    point.setAttributeNS(null, "cx", x);
3442    point.setAttributeNS(null, "cy", y);
3443    point.setAttributeNS(null, "r", 0.5 * groupTemplate.size);
3444  } else {
3445    point = exports.getSVGElement('rect', JSONcontainer, svgContainer);
3446    point.setAttributeNS(null, "x", x - 0.5 * groupTemplate.size);
3447    point.setAttributeNS(null, "y", y - 0.5 * groupTemplate.size);
3448    point.setAttributeNS(null, "width", groupTemplate.size);
3449    point.setAttributeNS(null, "height", groupTemplate.size);
3450  }
3451
3452  if (groupTemplate.styles !== undefined) {
3453    point.setAttributeNS(null, "style", groupTemplate.styles);
3454  }
3455  point.setAttributeNS(null, "class", groupTemplate.className + " vis-point");
3456  //handle label
3457
3458
3459  if (labelObj) {
3460    var label = exports.getSVGElement('text', JSONcontainer, svgContainer);
3461    if (labelObj.xOffset) {
3462      x = x + labelObj.xOffset;
3463    }
3464
3465    if (labelObj.yOffset) {
3466      y = y + labelObj.yOffset;
3467    }
3468    if (labelObj.content) {
3469      label.textContent = labelObj.content;
3470    }
3471
3472    if (labelObj.className) {
3473      label.setAttributeNS(null, "class", labelObj.className + " vis-label");
3474    }
3475    label.setAttributeNS(null, "x", x);
3476    label.setAttributeNS(null, "y", y);
3477  }
3478
3479  return point;
3480};
3481
3482/**
3483 * draw a bar SVG element centered on the X coordinate
3484 *
3485 * @param {number} x
3486 * @param {number} y
3487 * @param {number} width
3488 * @param {number} height
3489 * @param {string} className
3490 * @param {Object} JSONcontainer
3491 * @param {Object} svgContainer
3492 * @param {string} style
3493 */
3494exports.drawBar = function (x, y, width, height, className, JSONcontainer, svgContainer, style) {
3495  if (height != 0) {
3496    if (height < 0) {
3497      height *= -1;
3498      y -= height;
3499    }
3500    var rect = exports.getSVGElement('rect', JSONcontainer, svgContainer);
3501    rect.setAttributeNS(null, "x", x - 0.5 * width);
3502    rect.setAttributeNS(null, "y", y);
3503    rect.setAttributeNS(null, "width", width);
3504    rect.setAttributeNS(null, "height", height);
3505    rect.setAttributeNS(null, "class", className);
3506    if (style) {
3507      rect.setAttributeNS(null, "style", style);
3508    }
3509  }
3510};
3511
3512/***/ }),
3513/* 15 */
3514/***/ (function(module, exports, __webpack_require__) {
3515
3516"use strict";
3517
3518
3519Object.defineProperty(exports, "__esModule", {
3520  value: true
3521});
3522exports.printStyle = undefined;
3523
3524var _stringify = __webpack_require__(19);
3525
3526var _stringify2 = _interopRequireDefault(_stringify);
3527
3528var _typeof2 = __webpack_require__(6);
3529
3530var _typeof3 = _interopRequireDefault(_typeof2);
3531
3532var _keys = __webpack_require__(8);
3533
3534var _keys2 = _interopRequireDefault(_keys);
3535
3536var _classCallCheck2 = __webpack_require__(0);
3537
3538var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
3539
3540var _createClass2 = __webpack_require__(1);
3541
3542var _createClass3 = _interopRequireDefault(_createClass2);
3543
3544function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
3545
3546var util = __webpack_require__(2);
3547
3548var errorFound = false;
3549var allOptions = void 0;
3550var printStyle = 'background: #FFeeee; color: #dd0000';
3551/**
3552 *  Used to validate options.
3553 */
3554
3555var Validator = function () {
3556  /**
3557   * @ignore
3558   */
3559  function Validator() {
3560    (0, _classCallCheck3['default'])(this, Validator);
3561  }
3562
3563  /**
3564   * Main function to be called
3565   * @param {Object} options
3566   * @param {Object} referenceOptions
3567   * @param {Object} subObject
3568   * @returns {boolean}
3569   * @static
3570   */
3571
3572
3573  (0, _createClass3['default'])(Validator, null, [{
3574    key: 'validate',
3575    value: function validate(options, referenceOptions, subObject) {
3576      errorFound = false;
3577      allOptions = referenceOptions;
3578      var usedOptions = referenceOptions;
3579      if (subObject !== undefined) {
3580        usedOptions = referenceOptions[subObject];
3581      }
3582      Validator.parse(options, usedOptions, []);
3583      return errorFound;
3584    }
3585
3586    /**
3587     * Will traverse an object recursively and check every value
3588     * @param {Object} options
3589     * @param {Object} referenceOptions
3590     * @param {array} path    | where to look for the actual option
3591     * @static
3592     */
3593
3594  }, {
3595    key: 'parse',
3596    value: function parse(options, referenceOptions, path) {
3597      for (var option in options) {
3598        if (options.hasOwnProperty(option)) {
3599          Validator.check(option, options, referenceOptions, path);
3600        }
3601      }
3602    }
3603
3604    /**
3605     * Check every value. If the value is an object, call the parse function on that object.
3606     * @param {string} option
3607     * @param {Object} options
3608     * @param {Object} referenceOptions
3609     * @param {array} path    | where to look for the actual option
3610     * @static
3611     */
3612
3613  }, {
3614    key: 'check',
3615    value: function check(option, options, referenceOptions, path) {
3616      if (referenceOptions[option] === undefined && referenceOptions.__any__ === undefined) {
3617        Validator.getSuggestion(option, referenceOptions, path);
3618        return;
3619      }
3620
3621      var referenceOption = option;
3622      var is_object = true;
3623
3624      if (referenceOptions[option] === undefined && referenceOptions.__any__ !== undefined) {
3625        // NOTE: This only triggers if the __any__ is in the top level of the options object.
3626        //       THAT'S A REALLY BAD PLACE TO ALLOW IT!!!!
3627        // TODO: Examine if needed, remove if possible
3628
3629        // __any__ is a wildcard. Any value is accepted and will be further analysed by reference.
3630        referenceOption = '__any__';
3631
3632        // if the any-subgroup is not a predefined object in the configurator,
3633        // we do not look deeper into the object.
3634        is_object = Validator.getType(options[option]) === 'object';
3635      } else {
3636        // Since all options in the reference are objects, we can check whether 
3637        // they are supposed to be the object to look for the __type__ field.
3638        // if this is an object, we check if the correct type has been supplied to account for shorthand options.
3639      }
3640
3641      var refOptionObj = referenceOptions[referenceOption];
3642      if (is_object && refOptionObj.__type__ !== undefined) {
3643        refOptionObj = refOptionObj.__type__;
3644      }
3645
3646      Validator.checkFields(option, options, referenceOptions, referenceOption, refOptionObj, path);
3647    }
3648
3649    /**
3650     *
3651     * @param {string}  option           | the option property
3652     * @param {Object}  options          | The supplied options object
3653     * @param {Object}  referenceOptions | The reference options containing all options and their allowed formats
3654     * @param {string}  referenceOption  | Usually this is the same as option, except when handling an __any__ tag.
3655     * @param {string}  refOptionObj     | This is the type object from the reference options
3656     * @param {Array}   path             | where in the object is the option
3657     * @static
3658     */
3659
3660  }, {
3661    key: 'checkFields',
3662    value: function checkFields(option, options, referenceOptions, referenceOption, refOptionObj, path) {
3663      var log = function log(message) {
3664        console.log('%c' + message + Validator.printLocation(path, option), printStyle);
3665      };
3666
3667      var optionType = Validator.getType(options[option]);
3668      var refOptionType = refOptionObj[optionType];
3669
3670      if (refOptionType !== undefined) {
3671        // if the type is correct, we check if it is supposed to be one of a few select values
3672        if (Validator.getType(refOptionType) === 'array' && refOptionType.indexOf(options[option]) === -1) {
3673          log('Invalid option detected in "' + option + '".' + ' Allowed values are:' + Validator.print(refOptionType) + ' not "' + options[option] + '". ');
3674          errorFound = true;
3675        } else if (optionType === 'object' && referenceOption !== "__any__") {
3676          path = util.copyAndExtendArray(path, option);
3677          Validator.parse(options[option], referenceOptions[referenceOption], path);
3678        }
3679      } else if (refOptionObj['any'] === undefined) {
3680        // type of the field is incorrect and the field cannot be any
3681        log('Invalid type received for "' + option + '". Expected: ' + Validator.print((0, _keys2['default'])(refOptionObj)) + '. Received [' + optionType + '] "' + options[option] + '"');
3682        errorFound = true;
3683      }
3684    }
3685
3686    /**
3687     *
3688     * @param {Object|boolean|number|string|Array.<number>|Date|Node|Moment|undefined|null} object
3689     * @returns {string}
3690     * @static
3691     */
3692
3693  }, {
3694    key: 'getType',
3695    value: function getType(object) {
3696      var type = typeof object === 'undefined' ? 'undefined' : (0, _typeof3['default'])(object);
3697
3698      if (type === 'object') {
3699        if (object === null) {
3700          return 'null';
3701        }
3702        if (object instanceof Boolean) {
3703          return 'boolean';
3704        }
3705        if (object instanceof Number) {
3706          return 'number';
3707        }
3708        if (object instanceof String) {
3709          return 'string';
3710        }
3711        if (Array.isArray(object)) {
3712          return 'array';
3713        }
3714        if (object instanceof Date) {
3715          return 'date';
3716        }
3717        if (object.nodeType !== undefined) {
3718          return 'dom';
3719        }
3720        if (object._isAMomentObject === true) {
3721          return 'moment';
3722        }
3723        return 'object';
3724      } else if (type === 'number') {
3725        return 'number';
3726      } else if (type === 'boolean') {
3727        return 'boolean';
3728      } else if (type === 'string') {
3729        return 'string';
3730      } else if (type === undefined) {
3731        return 'undefined';
3732      }
3733      return type;
3734    }
3735
3736    /**
3737     * @param {string} option
3738     * @param {Object} options
3739     * @param {Array.<string>} path
3740     * @static
3741     */
3742
3743  }, {
3744    key: 'getSuggestion',
3745    value: function getSuggestion(option, options, path) {
3746      var localSearch = Validator.findInOptions(option, options, path, false);
3747      var globalSearch = Validator.findInOptions(option, allOptions, [], true);
3748
3749      var localSearchThreshold = 8;
3750      var globalSearchThreshold = 4;
3751
3752      var msg = void 0;
3753      if (localSearch.indexMatch !== undefined) {
3754        msg = ' in ' + Validator.printLocation(localSearch.path, option, '') + 'Perhaps it was incomplete? Did you mean: "' + localSearch.indexMatch + '"?\n\n';
3755      } else if (globalSearch.distance <= globalSearchThreshold && localSearch.distance > globalSearch.distance) {
3756        msg = ' in ' + Validator.printLocation(localSearch.path, option, '') + 'Perhaps it was misplaced? Matching option found at: ' + Validator.printLocation(globalSearch.path, globalSearch.closestMatch, '');
3757      } else if (localSearch.distance <= localSearchThreshold) {
3758        msg = '. Did you mean "' + localSearch.closestMatch + '"?' + Validator.printLocation(localSearch.path, option);
3759      } else {
3760        msg = '. Did you mean one of these: ' + Validator.print((0, _keys2['default'])(options)) + Validator.printLocation(path, option);
3761      }
3762
3763      console.log('%cUnknown option detected: "' + option + '"' + msg, printStyle);
3764      errorFound = true;
3765    }
3766
3767    /**
3768     * traverse the options in search for a match.
3769     * @param {string} option
3770     * @param {Object} options
3771     * @param {Array} path    | where to look for the actual option
3772     * @param {boolean} [recursive=false]
3773     * @returns {{closestMatch: string, path: Array, distance: number}}
3774     * @static
3775     */
3776
3777  }, {
3778    key: 'findInOptions',
3779    value: function findInOptions(option, options, path) {
3780      var recursive = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false;
3781
3782      var min = 1e9;
3783      var closestMatch = '';
3784      var closestMatchPath = [];
3785      var lowerCaseOption = option.toLowerCase();
3786      var indexMatch = undefined;
3787      for (var op in options) {
3788        // eslint-disable-line guard-for-in
3789        var distance = void 0;
3790        if (options[op].__type__ !== undefined && recursive === true) {
3791          var result = Validator.findInOptions(option, options[op], util.copyAndExtendArray(path, op));
3792          if (min > result.distance) {
3793            closestMatch = result.closestMatch;
3794            closestMatchPath = result.path;
3795            min = result.distance;
3796            indexMatch = result.indexMatch;
3797          }
3798        } else {
3799          if (op.toLowerCase().indexOf(lowerCaseOption) !== -1) {
3800            indexMatch = op;
3801          }
3802          distance = Validator.levenshteinDistance(option, op);
3803          if (min > distance) {
3804            closestMatch = op;
3805            closestMatchPath = util.copyArray(path);
3806            min = distance;
3807          }
3808        }
3809      }
3810      return { closestMatch: closestMatch, path: closestMatchPath, distance: min, indexMatch: indexMatch };
3811    }
3812
3813    /**
3814     * @param {Array.<string>} path
3815     * @param {Object} option
3816     * @param {string} prefix
3817     * @returns {String}
3818     * @static
3819     */
3820
3821  }, {
3822    key: 'printLocation',
3823    value: function printLocation(path, option) {
3824      var prefix = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 'Problem value found at: \n';
3825
3826      var str = '\n\n' + prefix + 'options = {\n';
3827      for (var i = 0; i < path.length; i++) {
3828        for (var j = 0; j < i + 1; j++) {
3829          str += '  ';
3830        }
3831        str += path[i] + ': {\n';
3832      }
3833      for (var _j = 0; _j < path.length + 1; _j++) {
3834        str += '  ';
3835      }
3836      str += option + '\n';
3837      for (var _i = 0; _i < path.length + 1; _i++) {
3838        for (var _j2 = 0; _j2 < path.length - _i; _j2++) {
3839          str += '  ';
3840        }
3841        str += '}\n';
3842      }
3843      return str + '\n\n';
3844    }
3845
3846    /**
3847     * @param {Object} options
3848     * @returns {String}
3849     * @static
3850     */
3851
3852  }, {
3853    key: 'print',
3854    value: function print(options) {
3855      return (0, _stringify2['default'])(options).replace(/(\")|(\[)|(\])|(,"__type__")/g, "").replace(/(\,)/g, ', ');
3856    }
3857
3858    /**
3859     *  Compute the edit distance between the two given strings
3860     * http://en.wikibooks.org/wiki/Algorithm_Implementation/Strings/Levenshtein_distance#JavaScript
3861     *
3862     * Copyright (c) 2011 Andrei Mackenzie
3863     *
3864     * 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:
3865     *
3866     * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
3867     *
3868     * 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.
3869     *
3870     * @param {string} a
3871     * @param {string} b
3872     * @returns {Array.<Array.<number>>}}
3873     * @static
3874     */
3875
3876  }, {
3877    key: 'levenshteinDistance',
3878    value: function levenshteinDistance(a, b) {
3879      if (a.length === 0) return b.length;
3880      if (b.length === 0) return a.length;
3881
3882      var matrix = [];
3883
3884      // increment along the first column of each row
3885      var i;
3886      for (i = 0; i <= b.length; i++) {
3887        matrix[i] = [i];
3888      }
3889
3890      // increment each column in the first row
3891      var j;
3892      for (j = 0; j <= a.length; j++) {
3893        matrix[0][j] = j;
3894      }
3895
3896      // Fill in the rest of the matrix
3897      for (i = 1; i <= b.length; i++) {
3898        for (j = 1; j <= a.length; j++) {
3899          if (b.charAt(i - 1) == a.charAt(j - 1)) {
3900            matrix[i][j] = matrix[i - 1][j - 1];
3901          } else {
3902            matrix[i][j] = Math.min(matrix[i - 1][j - 1] + 1, // substitution
3903            Math.min(matrix[i][j - 1] + 1, // insertion
3904            matrix[i - 1][j] + 1)); // deletion
3905          }
3906        }
3907      }
3908
3909      return matrix[b.length][a.length];
3910    }
3911  }]);
3912  return Validator;
3913}();
3914
3915exports['default'] = Validator;
3916exports.printStyle = printStyle;
3917
3918/***/ }),
3919/* 16 */
3920/***/ (function(module, exports, __webpack_require__) {
3921
3922"use strict";
3923
3924
3925var util = __webpack_require__(2);
3926
3927/**
3928 * Prototype for visual components
3929 * @param {{dom: Object, domProps: Object, emitter: Emitter, range: Range}} [body]
3930 * @param {Object} [options]
3931 */
3932function Component(body, options) {
3933  // eslint-disable-line no-unused-vars
3934  this.options = null;
3935  this.props = null;
3936}
3937
3938/**
3939 * Set options for the component. The new options will be merged into the
3940 * current options.
3941 * @param {Object} options
3942 */
3943Component.prototype.setOptions = function (options) {
3944  if (options) {
3945    util.extend(this.options, options);
3946  }
3947};
3948
3949/**
3950 * Repaint the component
3951 * @return {boolean} Returns true if the component is resized
3952 */
3953Component.prototype.redraw = function () {
3954  // should be implemented by the component
3955  return false;
3956};
3957
3958/**
3959 * Destroy the component. Cleanup DOM and event listeners
3960 */
3961Component.prototype.destroy = function () {
3962  // should be implemented by the component
3963};
3964
3965/**
3966 * Test whether the component is resized since the last time _isResized() was
3967 * called.
3968 * @return {Boolean} Returns true if the component is resized
3969 * @protected
3970 */
3971Component.prototype._isResized = function () {
3972  var resized = this.props._previousWidth !== this.props.width || this.props._previousHeight !== this.props.height;
3973
3974  this.props._previousWidth = this.props.width;
3975  this.props._previousHeight = this.props.height;
3976
3977  return resized;
3978};
3979
3980module.exports = Component;
3981
3982/***/ }),
3983/* 17 */
3984/***/ (function(module, exports, __webpack_require__) {
3985
3986var global = __webpack_require__(18);
3987var core = __webpack_require__(7);
3988var ctx = __webpack_require__(80);
3989var hide = __webpack_require__(26);
3990var PROTOTYPE = 'prototype';
3991
3992var $export = function (type, name, source) {
3993  var IS_FORCED = type & $export.F;
3994  var IS_GLOBAL = type & $export.G;
3995  var IS_STATIC = type & $export.S;
3996  var IS_PROTO = type & $export.P;
3997  var IS_BIND = type & $export.B;
3998  var IS_WRAP = type & $export.W;
3999  var exports = IS_GLOBAL ? core : core[name] || (core[name] = {});
4000  var expProto = exports[PROTOTYPE];
4001  var target = IS_GLOBAL ? global : IS_STATIC ? global[name] : (global[name] || {})[PROTOTYPE];
4002  var key, own, out;
4003  if (IS_GLOBAL) source = name;
4004  for (key in source) {
4005    // contains in native
4006    own = !IS_FORCED && target && target[key] !== undefined;
4007    if (own && key in exports) continue;
4008    // export native or passed
4009    out = own ? target[key] : source[key];
4010    // prevent global pollution for namespaces
4011    exports[key] = IS_GLOBAL && typeof target[key] != 'function' ? source[key]
4012    // bind timers to global for call from export context
4013    : IS_BIND && own ? ctx(out, global)
4014    // wrap global constructors for prevent change them in library
4015    : IS_WRAP && target[key] == out ? (function (C) {
4016      var F = function (a, b, c) {
4017        if (this instanceof C) {
4018          switch (arguments.length) {
4019            case 0: return new C();
4020            case 1: return new C(a);
4021            case 2: return new C(a, b);
4022          } return new C(a, b, c);
4023        } return C.apply(this, arguments);
4024      };
4025      F[PROTOTYPE] = C[PROTOTYPE];
4026      return F;
4027    // make static versions for prototype methods
4028    })(out) : IS_PROTO && typeof out == 'function' ? ctx(Function.call, out) : out;
4029    // export proto methods to core.%CONSTRUCTOR%.methods.%NAME%
4030    if (IS_PROTO) {
4031      (exports.virtual || (exports.virtual = {}))[key] = out;
4032      // export proto methods to core.%CONSTRUCTOR%.prototype.%NAME%
4033      if (type & $export.R && expProto && !expProto[key]) hide(expProto, key, out);
4034    }
4035  }
4036};
4037// type bitmap
4038$export.F = 1;   // forced
4039$export.G = 2;   // global
4040$export.S = 4;   // static
4041$export.P = 8;   // proto
4042$export.B = 16;  // bind
4043$export.W = 32;  // wrap
4044$export.U = 64;  // safe
4045$export.R = 128; // real proto method for `library`
4046module.exports = $export;
4047
4048
4049/***/ }),
4050/* 18 */
4051/***/ (function(module, exports) {
4052
4053// https://github.com/zloirock/core-js/issues/86#issuecomment-115759028
4054var global = module.exports = typeof window != 'undefined' && window.Math == Math
4055  ? window : typeof self != 'undefined' && self.Math == Math ? self
4056  // eslint-disable-next-line no-new-func
4057  : Function('return this')();
4058if (typeof __g == 'number') __g = global; // eslint-disable-line no-undef
4059
4060
4061/***/ }),
4062/* 19 */
4063/***/ (function(module, exports, __webpack_require__) {
4064
4065module.exports = { "default": __webpack_require__(160), __esModule: true };
4066
4067/***/ }),
4068/* 20 */
4069/***/ (function(module, exports, __webpack_require__) {
4070
4071var anObject = __webpack_require__(27);
4072var IE8_DOM_DEFINE = __webpack_require__(81);
4073var toPrimitive = __webpack_require__(53);
4074var dP = Object.defineProperty;
4075
4076exports.f = __webpack_require__(21) ? Object.defineProperty : function defineProperty(O, P, Attributes) {
4077  anObject(O);
4078  P = toPrimitive(P, true);
4079  anObject(Attributes);
4080  if (IE8_DOM_DEFINE) try {
4081    return dP(O, P, Attributes);
4082  } catch (e) { /* empty */ }
4083  if ('get' in Attributes || 'set' in Attributes) throw TypeError('Accessors not supported!');
4084  if ('value' in Attributes) O[P] = Attributes.value;
4085  return O;
4086};
4087
4088
4089/***/ }),
4090/* 21 */
4091/***/ (function(module, exports, __webpack_require__) {
4092
4093// Thank's IE8 for his funny defineProperty
4094module.exports = !__webpack_require__(28)(function () {
4095  return Object.defineProperty({}, 'a', { get: function () { return 7; } }).a != 7;
4096});
4097
4098
4099/***/ }),
4100/* 22 */
4101/***/ (function(module, exports) {
4102
4103var hasOwnProperty = {}.hasOwnProperty;
4104module.exports = function (it, key) {
4105  return hasOwnProperty.call(it, key);
4106};
4107
4108
4109/***/ }),
4110/* 23 */
4111/***/ (function(module, exports, __webpack_require__) {
4112
4113"use strict";
4114
4115
4116Object.defineProperty(exports, "__esModule", {
4117  value: true
4118});
4119
4120var _typeof2 = __webpack_require__(6);
4121
4122var _typeof3 = _interopRequireDefault(_typeof2);
4123
4124var _classCallCheck2 = __webpack_require__(0);
4125
4126var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
4127
4128var _createClass2 = __webpack_require__(1);
4129
4130var _createClass3 = _interopRequireDefault(_createClass2);
4131
4132function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
4133
4134/**
4135 * The Base class for all Nodes.
4136 */
4137var NodeBase = function () {
4138  /**
4139   * @param {Object} options
4140   * @param {Object} body
4141   * @param {Label} labelModule
4142   */
4143  function NodeBase(options, body, labelModule) {
4144    (0, _classCallCheck3['default'])(this, NodeBase);
4145
4146    this.body = body;
4147    this.labelModule = labelModule;
4148    this.setOptions(options);
4149    this.top = undefined;
4150    this.left = undefined;
4151    this.height = undefined;
4152    this.width = undefined;
4153    this.radius = undefined;
4154    this.margin = undefined;
4155    this.refreshNeeded = true;
4156    this.boundingBox = { top: 0, left: 0, right: 0, bottom: 0 };
4157  }
4158
4159  /**
4160   *
4161   * @param {Object} options
4162   */
4163
4164
4165  (0, _createClass3['default'])(NodeBase, [{
4166    key: 'setOptions',
4167    value: function setOptions(options) {
4168      this.options = options;
4169    }
4170
4171    /**
4172     *
4173     * @param {Label} labelModule
4174     * @private
4175     */
4176
4177  }, {
4178    key: '_setMargins',
4179    value: function _setMargins(labelModule) {
4180      this.margin = {};
4181      if (this.options.margin) {
4182        if ((0, _typeof3['default'])(this.options.margin) == 'object') {
4183          this.margin.top = this.options.margin.top;
4184          this.margin.right = this.options.margin.right;
4185          this.margin.bottom = this.options.margin.bottom;
4186          this.margin.left = this.options.margin.left;
4187        } else {
4188          this.margin.top = this.options.margin;
4189          this.margin.right = this.options.margin;
4190          this.margin.bottom = this.options.margin;
4191          this.margin.left = this.options.margin;
4192        }
4193      }
4194      labelModule.adjustSizes(this.margin);
4195    }
4196
4197    /**
4198     *
4199     * @param {CanvasRenderingContext2D} ctx
4200     * @param {number} angle
4201     * @returns {number}
4202     * @private
4203     */
4204
4205  }, {
4206    key: '_distanceToBorder',
4207    value: function _distanceToBorder(ctx, angle) {
4208      var borderWidth = this.options.borderWidth;
4209      this.resize(ctx);
4210      return Math.min(Math.abs(this.width / 2 / Math.cos(angle)), Math.abs(this.height / 2 / Math.sin(angle))) + borderWidth;
4211    }
4212
4213    /**
4214     *
4215     * @param {CanvasRenderingContext2D} ctx
4216     * @param {{toArrow: boolean, toArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), toArrowType: *, middleArrow: boolean, middleArrowScale: (number|allOptions.edges.arrows.middle.scaleFactor|{number}|Array), middleArrowType: (allOptions.edges.arrows.middle.type|{string}|string|*), fromArrow: boolean, fromArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), fromArrowType: *, arrowStrikethrough: (*|boolean|allOptions.edges.arrowStrikethrough|{boolean}), color: undefined, inheritsColor: (string|string|string|allOptions.edges.color.inherit|{string, boolean}|Array|*), opacity: *, hidden: *, length: *, shadow: *, shadowColor: *, shadowSize: *, shadowX: *, shadowY: *, dashes: (*|boolean|Array|allOptions.edges.dashes|{boolean, array}), width: *}} values
4217     */
4218
4219  }, {
4220    key: 'enableShadow',
4221    value: function enableShadow(ctx, values) {
4222      if (values.shadow) {
4223        ctx.shadowColor = values.shadowColor;
4224        ctx.shadowBlur = values.shadowSize;
4225        ctx.shadowOffsetX = values.shadowX;
4226        ctx.shadowOffsetY = values.shadowY;
4227      }
4228    }
4229
4230    /**
4231     *
4232     * @param {CanvasRenderingContext2D} ctx
4233     * @param {{toArrow: boolean, toArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), toArrowType: *, middleArrow: boolean, middleArrowScale: (number|allOptions.edges.arrows.middle.scaleFactor|{number}|Array), middleArrowType: (allOptions.edges.arrows.middle.type|{string}|string|*), fromArrow: boolean, fromArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), fromArrowType: *, arrowStrikethrough: (*|boolean|allOptions.edges.arrowStrikethrough|{boolean}), color: undefined, inheritsColor: (string|string|string|allOptions.edges.color.inherit|{string, boolean}|Array|*), opacity: *, hidden: *, length: *, shadow: *, shadowColor: *, shadowSize: *, shadowX: *, shadowY: *, dashes: (*|boolean|Array|allOptions.edges.dashes|{boolean, array}), width: *}} values
4234     */
4235
4236  }, {
4237    key: 'disableShadow',
4238    value: function disableShadow(ctx, values) {
4239      if (values.shadow) {
4240        ctx.shadowColor = 'rgba(0,0,0,0)';
4241        ctx.shadowBlur = 0;
4242        ctx.shadowOffsetX = 0;
4243        ctx.shadowOffsetY = 0;
4244      }
4245    }
4246
4247    /**
4248     *
4249     * @param {CanvasRenderingContext2D} ctx
4250     * @param {{toArrow: boolean, toArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), toArrowType: *, middleArrow: boolean, middleArrowScale: (number|allOptions.edges.arrows.middle.scaleFactor|{number}|Array), middleArrowType: (allOptions.edges.arrows.middle.type|{string}|string|*), fromArrow: boolean, fromArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), fromArrowType: *, arrowStrikethrough: (*|boolean|allOptions.edges.arrowStrikethrough|{boolean}), color: undefined, inheritsColor: (string|string|string|allOptions.edges.color.inherit|{string, boolean}|Array|*), opacity: *, hidden: *, length: *, shadow: *, shadowColor: *, shadowSize: *, shadowX: *, shadowY: *, dashes: (*|boolean|Array|allOptions.edges.dashes|{boolean, array}), width: *}} values
4251     */
4252
4253  }, {
4254    key: 'enableBorderDashes',
4255    value: function enableBorderDashes(ctx, values) {
4256      if (values.borderDashes !== false) {
4257        if (ctx.setLineDash !== undefined) {
4258          var dashes = values.borderDashes;
4259          if (dashes === true) {
4260            dashes = [5, 15];
4261          }
4262          ctx.setLineDash(dashes);
4263        } else {
4264          console.warn("setLineDash is not supported in this browser. The dashed borders cannot be used.");
4265          this.options.shapeProperties.borderDashes = false;
4266          values.borderDashes = false;
4267        }
4268      }
4269    }
4270
4271    /**
4272     *
4273     * @param {CanvasRenderingContext2D} ctx
4274     * @param {{toArrow: boolean, toArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), toArrowType: *, middleArrow: boolean, middleArrowScale: (number|allOptions.edges.arrows.middle.scaleFactor|{number}|Array), middleArrowType: (allOptions.edges.arrows.middle.type|{string}|string|*), fromArrow: boolean, fromArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), fromArrowType: *, arrowStrikethrough: (*|boolean|allOptions.edges.arrowStrikethrough|{boolean}), color: undefined, inheritsColor: (string|string|string|allOptions.edges.color.inherit|{string, boolean}|Array|*), opacity: *, hidden: *, length: *, shadow: *, shadowColor: *, shadowSize: *, shadowX: *, shadowY: *, dashes: (*|boolean|Array|allOptions.edges.dashes|{boolean, array}), width: *}} values
4275     */
4276
4277  }, {
4278    key: 'disableBorderDashes',
4279    value: function disableBorderDashes(ctx, values) {
4280      if (values.borderDashes !== false) {
4281        if (ctx.setLineDash !== undefined) {
4282          ctx.setLineDash([0]);
4283        } else {
4284          console.warn("setLineDash is not supported in this browser. The dashed borders cannot be used.");
4285          this.options.shapeProperties.borderDashes = false;
4286          values.borderDashes = false;
4287        }
4288      }
4289    }
4290
4291    /**
4292     * Determine if the shape of a node needs to be recalculated.
4293     *
4294     * @param {boolean} selected
4295     * @param {boolean} hover
4296     * @returns {boolean}
4297     * @protected
4298     */
4299
4300  }, {
4301    key: 'needsRefresh',
4302    value: function needsRefresh(selected, hover) {
4303      if (this.refreshNeeded === true) {
4304        // This is probably not the best location to reset this member.
4305        // However, in the current logic, it is the most convenient one.
4306        this.refreshNeeded = false;
4307        return true;
4308      }
4309
4310      return this.width === undefined || this.labelModule.differentState(selected, hover);
4311    }
4312
4313    /**
4314     *
4315     * @param {CanvasRenderingContext2D} ctx
4316     * @param {{toArrow: boolean, toArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), toArrowType: *, middleArrow: boolean, middleArrowScale: (number|allOptions.edges.arrows.middle.scaleFactor|{number}|Array), middleArrowType: (allOptions.edges.arrows.middle.type|{string}|string|*), fromArrow: boolean, fromArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), fromArrowType: *, arrowStrikethrough: (*|boolean|allOptions.edges.arrowStrikethrough|{boolean}), color: undefined, inheritsColor: (string|string|string|allOptions.edges.color.inherit|{string, boolean}|Array|*), opacity: *, hidden: *, length: *, shadow: *, shadowColor: *, shadowSize: *, shadowX: *, shadowY: *, dashes: (*|boolean|Array|allOptions.edges.dashes|{boolean, array}), width: *}} values
4317     */
4318
4319  }, {
4320    key: 'initContextForDraw',
4321    value: function initContextForDraw(ctx, values) {
4322      var borderWidth = values.borderWidth / this.body.view.scale;
4323
4324      ctx.lineWidth = Math.min(this.width, borderWidth);
4325      ctx.strokeStyle = values.borderColor;
4326      ctx.fillStyle = values.color;
4327    }
4328
4329    /**
4330     *
4331     * @param {CanvasRenderingContext2D} ctx
4332     * @param {{toArrow: boolean, toArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), toArrowType: *, middleArrow: boolean, middleArrowScale: (number|allOptions.edges.arrows.middle.scaleFactor|{number}|Array), middleArrowType: (allOptions.edges.arrows.middle.type|{string}|string|*), fromArrow: boolean, fromArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), fromArrowType: *, arrowStrikethrough: (*|boolean|allOptions.edges.arrowStrikethrough|{boolean}), color: undefined, inheritsColor: (string|string|string|allOptions.edges.color.inherit|{string, boolean}|Array|*), opacity: *, hidden: *, length: *, shadow: *, shadowColor: *, shadowSize: *, shadowX: *, shadowY: *, dashes: (*|boolean|Array|allOptions.edges.dashes|{boolean, array}), width: *}} values
4333     */
4334
4335  }, {
4336    key: 'performStroke',
4337    value: function performStroke(ctx, values) {
4338      var borderWidth = values.borderWidth / this.body.view.scale;
4339
4340      //draw dashed border if enabled, save and restore is required for firefox not to crash on unix.
4341      ctx.save();
4342      // if borders are zero width, they will be drawn with width 1 by default. This prevents that
4343      if (borderWidth > 0) {
4344        this.enableBorderDashes(ctx, values);
4345        //draw the border
4346        ctx.stroke();
4347        //disable dashed border for other elements
4348        this.disableBorderDashes(ctx, values);
4349      }
4350      ctx.restore();
4351    }
4352
4353    /**
4354     *
4355     * @param {CanvasRenderingContext2D} ctx
4356     * @param {{toArrow: boolean, toArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), toArrowType: *, middleArrow: boolean, middleArrowScale: (number|allOptions.edges.arrows.middle.scaleFactor|{number}|Array), middleArrowType: (allOptions.edges.arrows.middle.type|{string}|string|*), fromArrow: boolean, fromArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), fromArrowType: *, arrowStrikethrough: (*|boolean|allOptions.edges.arrowStrikethrough|{boolean}), color: undefined, inheritsColor: (string|string|string|allOptions.edges.color.inherit|{string, boolean}|Array|*), opacity: *, hidden: *, length: *, shadow: *, shadowColor: *, shadowSize: *, shadowX: *, shadowY: *, dashes: (*|boolean|Array|allOptions.edges.dashes|{boolean, array}), width: *}} values
4357     */
4358
4359  }, {
4360    key: 'performFill',
4361    value: function performFill(ctx, values) {
4362      // draw shadow if enabled
4363      this.enableShadow(ctx, values);
4364      // draw the background
4365      ctx.fill();
4366      // disable shadows for other elements.
4367      this.disableShadow(ctx, values);
4368
4369      this.performStroke(ctx, values);
4370    }
4371
4372    /**
4373     *
4374     * @param {number} margin
4375     * @private
4376     */
4377
4378  }, {
4379    key: '_addBoundingBoxMargin',
4380    value: function _addBoundingBoxMargin(margin) {
4381      this.boundingBox.left -= margin;
4382      this.boundingBox.top -= margin;
4383      this.boundingBox.bottom += margin;
4384      this.boundingBox.right += margin;
4385    }
4386
4387    /**
4388     * Actual implementation of this method call.
4389     *
4390     * Doing it like this makes it easier to override
4391     * in the child classes.
4392     *
4393     * @param {number} x width
4394     * @param {number} y height
4395     * @param {CanvasRenderingContext2D} ctx
4396     * @param {boolean} selected
4397     * @param {boolean} hover
4398     * @private
4399     */
4400
4401  }, {
4402    key: '_updateBoundingBox',
4403    value: function _updateBoundingBox(x, y, ctx, selected, hover) {
4404      if (ctx !== undefined) {
4405        this.resize(ctx, selected, hover);
4406      }
4407
4408      this.left = x - this.width / 2;
4409      this.top = y - this.height / 2;
4410
4411      this.boundingBox.left = this.left;
4412      this.boundingBox.top = this.top;
4413      this.boundingBox.bottom = this.top + this.height;
4414      this.boundingBox.right = this.left + this.width;
4415    }
4416
4417    /**
4418     * Default implementation of this method call.
4419     * This acts as a stub which can be overridden.
4420     *
4421     * @param {number} x width
4422     * @param {number} y height
4423     * @param {CanvasRenderingContext2D} ctx
4424     * @param {boolean} selected
4425     * @param {boolean} hover
4426     */
4427
4428  }, {
4429    key: 'updateBoundingBox',
4430    value: function updateBoundingBox(x, y, ctx, selected, hover) {
4431      this._updateBoundingBox(x, y, ctx, selected, hover);
4432    }
4433
4434    /**
4435     * Determine the dimensions to use for nodes with an internal label
4436     *
4437     * Currently, these are: Circle, Ellipse, Database, Box
4438     * The other nodes have external labels, and will not call this method
4439     *
4440     * If there is no label, decent default values are supplied.
4441     *
4442     * @param {CanvasRenderingContext2D} ctx
4443     * @param {boolean} [selected]
4444     * @param {boolean} [hover]
4445     * @returns {{width:number, height:number}}
4446     */
4447
4448  }, {
4449    key: 'getDimensionsFromLabel',
4450    value: function getDimensionsFromLabel(ctx, selected, hover) {
4451      // NOTE: previously 'textSize' was not put in 'this' for Ellipse
4452      // TODO: examine the consequences.
4453      this.textSize = this.labelModule.getTextSize(ctx, selected, hover);
4454      var width = this.textSize.width;
4455      var height = this.textSize.height;
4456
4457      var DEFAULT_SIZE = 14;
4458      if (width === 0) {
4459        // This happens when there is no label text set
4460        width = DEFAULT_SIZE; // use a decent default
4461        height = DEFAULT_SIZE; // if width zero, then height also always zero
4462      }
4463
4464      return { width: width, height: height };
4465    }
4466  }]);
4467  return NodeBase;
4468}();
4469
4470exports['default'] = NodeBase;
4471
4472/***/ }),
4473/* 24 */
4474/***/ (function(module, exports, __webpack_require__) {
4475
4476"use strict";
4477
4478
4479Object.defineProperty(exports, "__esModule", {
4480  value: true
4481});
4482
4483var _getPrototypeOf = __webpack_require__(3);
4484
4485var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf);
4486
4487var _classCallCheck2 = __webpack_require__(0);
4488
4489var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
4490
4491var _createClass2 = __webpack_require__(1);
4492
4493var _createClass3 = _interopRequireDefault(_createClass2);
4494
4495var _possibleConstructorReturn2 = __webpack_require__(4);
4496
4497var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2);
4498
4499var _inherits2 = __webpack_require__(5);
4500
4501var _inherits3 = _interopRequireDefault(_inherits2);
4502
4503var _NodeBase2 = __webpack_require__(23);
4504
4505var _NodeBase3 = _interopRequireDefault(_NodeBase2);
4506
4507function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
4508
4509/**
4510 * Base class for constructing Node/Cluster Shapes.
4511 *
4512 * @extends NodeBase
4513 */
4514var ShapeBase = function (_NodeBase) {
4515  (0, _inherits3['default'])(ShapeBase, _NodeBase);
4516
4517  /**
4518   * @param {Object} options
4519   * @param {Object} body
4520   * @param {Label} labelModule
4521   */
4522  function ShapeBase(options, body, labelModule) {
4523    (0, _classCallCheck3['default'])(this, ShapeBase);
4524    return (0, _possibleConstructorReturn3['default'])(this, (ShapeBase.__proto__ || (0, _getPrototypeOf2['default'])(ShapeBase)).call(this, options, body, labelModule));
4525  }
4526
4527  /**
4528   *
4529   * @param {CanvasRenderingContext2D} ctx
4530   * @param {boolean} [selected]
4531   * @param {boolean} [hover]
4532   * @param {Object} [values={size: this.options.size}]
4533   */
4534
4535
4536  (0, _createClass3['default'])(ShapeBase, [{
4537    key: 'resize',
4538    value: function resize(ctx) {
4539      var selected = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : this.selected;
4540      var hover = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : this.hover;
4541      var values = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : { size: this.options.size };
4542
4543      if (this.needsRefresh(selected, hover)) {
4544        this.labelModule.getTextSize(ctx, selected, hover);
4545        var size = 2 * values.size;
4546        this.width = size;
4547        this.height = size;
4548        this.radius = 0.5 * this.width;
4549      }
4550    }
4551
4552    /**
4553     *
4554     * @param {CanvasRenderingContext2D} ctx
4555     * @param {string} shape
4556     * @param {number} sizeMultiplier - Unused! TODO: Remove next major release
4557     * @param {number} x
4558     * @param {number} y
4559     * @param {boolean} selected
4560     * @param {boolean} hover
4561     * @param {{toArrow: boolean, toArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), toArrowType: *, middleArrow: boolean, middleArrowScale: (number|allOptions.edges.arrows.middle.scaleFactor|{number}|Array), middleArrowType: (allOptions.edges.arrows.middle.type|{string}|string|*), fromArrow: boolean, fromArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), fromArrowType: *, arrowStrikethrough: (*|boolean|allOptions.edges.arrowStrikethrough|{boolean}), color: undefined, inheritsColor: (string|string|string|allOptions.edges.color.inherit|{string, boolean}|Array|*), opacity: *, hidden: *, length: *, shadow: *, shadowColor: *, shadowSize: *, shadowX: *, shadowY: *, dashes: (*|boolean|Array|allOptions.edges.dashes|{boolean, array}), width: *}} values
4562     * @private
4563     */
4564
4565  }, {
4566    key: '_drawShape',
4567    value: function _drawShape(ctx, shape, sizeMultiplier, x, y, selected, hover, values) {
4568      this.resize(ctx, selected, hover, values);
4569      this.left = x - this.width / 2;
4570      this.top = y - this.height / 2;
4571
4572      this.initContextForDraw(ctx, values);
4573      ctx[shape](x, y, values.size);
4574      this.performFill(ctx, values);
4575
4576      if (this.options.label !== undefined) {
4577        // Need to call following here in order to ensure value for `this.labelModule.size.height`
4578        this.labelModule.calculateLabelSize(ctx, selected, hover, x, y, 'hanging');
4579        var yLabel = y + 0.5 * this.height + 0.5 * this.labelModule.size.height;
4580        this.labelModule.draw(ctx, x, yLabel, selected, hover, 'hanging');
4581      }
4582
4583      this.updateBoundingBox(x, y);
4584    }
4585
4586    /**
4587     *
4588     * @param {number} x
4589     * @param {number} y
4590     */
4591
4592  }, {
4593    key: 'updateBoundingBox',
4594    value: function updateBoundingBox(x, y) {
4595      this.boundingBox.top = y - this.options.size;
4596      this.boundingBox.left = x - this.options.size;
4597      this.boundingBox.right = x + this.options.size;
4598      this.boundingBox.bottom = y + this.options.size;
4599
4600      if (this.options.label !== undefined && this.labelModule.size.width > 0) {
4601        this.boundingBox.left = Math.min(this.boundingBox.left, this.labelModule.size.left);
4602        this.boundingBox.right = Math.max(this.boundingBox.right, this.labelModule.size.left + this.labelModule.size.width);
4603        this.boundingBox.bottom = Math.max(this.boundingBox.bottom, this.boundingBox.bottom + this.labelModule.size.height);
4604      }
4605    }
4606  }]);
4607  return ShapeBase;
4608}(_NodeBase3['default']);
4609
4610exports['default'] = ShapeBase;
4611
4612/***/ }),
4613/* 25 */
4614/***/ (function(module, exports, __webpack_require__) {
4615
4616// to indexed object, toObject with fallback for non-array-like ES3 strings
4617var IObject = __webpack_require__(78);
4618var defined = __webpack_require__(51);
4619module.exports = function (it) {
4620  return IObject(defined(it));
4621};
4622
4623
4624/***/ }),
4625/* 26 */
4626/***/ (function(module, exports, __webpack_require__) {
4627
4628var dP = __webpack_require__(20);
4629var createDesc = __webpack_require__(39);
4630module.exports = __webpack_require__(21) ? function (object, key, value) {
4631  return dP.f(object, key, createDesc(1, value));
4632} : function (object, key, value) {
4633  object[key] = value;
4634  return object;
4635};
4636
4637
4638/***/ }),
4639/* 27 */
4640/***/ (function(module, exports, __webpack_require__) {
4641
4642var isObject = __webpack_require__(32);
4643module.exports = function (it) {
4644  if (!isObject(it)) throw TypeError(it + ' is not an object!');
4645  return it;
4646};
4647
4648
4649/***/ }),
4650/* 28 */
4651/***/ (function(module, exports) {
4652
4653module.exports = function (exec) {
4654  try {
4655    return !!exec();
4656  } catch (e) {
4657    return true;
4658  }
4659};
4660
4661
4662/***/ }),
4663/* 29 */
4664/***/ (function(module, exports, __webpack_require__) {
4665
4666module.exports = { "default": __webpack_require__(138), __esModule: true };
4667
4668/***/ }),
4669/* 30 */
4670/***/ (function(module, exports, __webpack_require__) {
4671
4672"use strict";
4673
4674
4675exports.__esModule = true;
4676
4677var _isIterable2 = __webpack_require__(188);
4678
4679var _isIterable3 = _interopRequireDefault(_isIterable2);
4680
4681var _getIterator2 = __webpack_require__(77);
4682
4683var _getIterator3 = _interopRequireDefault(_getIterator2);
4684
4685function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
4686
4687exports.default = function () {
4688  function sliceIterator(arr, i) {
4689    var _arr = [];
4690    var _n = true;
4691    var _d = false;
4692    var _e = undefined;
4693
4694    try {
4695      for (var _i = (0, _getIterator3.default)(arr), _s; !(_n = (_s = _i.next()).done); _n = true) {
4696        _arr.push(_s.value);
4697
4698        if (i && _arr.length === i) break;
4699      }
4700    } catch (err) {
4701      _d = true;
4702      _e = err;
4703    } finally {
4704      try {
4705        if (!_n && _i["return"]) _i["return"]();
4706      } finally {
4707        if (_d) throw _e;
4708      }
4709    }
4710
4711    return _arr;
4712  }
4713
4714  return function (arr, i) {
4715    if (Array.isArray(arr)) {
4716      return arr;
4717    } else if ((0, _isIterable3.default)(Object(arr))) {
4718      return sliceIterator(arr, i);
4719    } else {
4720      throw new TypeError("Invalid attempt to destructure non-iterable instance");
4721    }
4722  };
4723}();
4724
4725/***/ }),
4726/* 31 */
4727/***/ (function(module, exports) {
4728
4729module.exports = {};
4730
4731
4732/***/ }),
4733/* 32 */
4734/***/ (function(module, exports) {
4735
4736module.exports = function (it) {
4737  return typeof it === 'object' ? it !== null : typeof it === 'function';
4738};
4739
4740
4741/***/ }),
4742/* 33 */
4743/***/ (function(module, exports, __webpack_require__) {
4744
4745// 19.1.2.14 / 15.2.3.14 Object.keys(O)
4746var $keys = __webpack_require__(84);
4747var enumBugKeys = __webpack_require__(58);
4748
4749module.exports = Object.keys || function keys(O) {
4750  return $keys(O, enumBugKeys);
4751};
4752
4753
4754/***/ }),
4755/* 34 */
4756/***/ (function(module, exports, __webpack_require__) {
4757
4758"use strict";
4759
4760
4761/**
4762 * @prototype Point3d
4763 * @param {number} [x]
4764 * @param {number} [y]
4765 * @param {number} [z]
4766 */
4767function Point3d(x, y, z) {
4768  this.x = x !== undefined ? x : 0;
4769  this.y = y !== undefined ? y : 0;
4770  this.z = z !== undefined ? z : 0;
4771}
4772
4773/**
4774 * Subtract the two provided points, returns a-b
4775 * @param {Point3d} a
4776 * @param {Point3d} b
4777 * @return {Point3d} a-b
4778 */
4779Point3d.subtract = function (a, b) {
4780  var sub = new Point3d();
4781  sub.x = a.x - b.x;
4782  sub.y = a.y - b.y;
4783  sub.z = a.z - b.z;
4784  return sub;
4785};
4786
4787/**
4788 * Add the two provided points, returns a+b
4789 * @param {Point3d} a
4790 * @param {Point3d} b
4791 * @return {Point3d} a+b
4792 */
4793Point3d.add = function (a, b) {
4794  var sum = new Point3d();
4795  sum.x = a.x + b.x;
4796  sum.y = a.y + b.y;
4797  sum.z = a.z + b.z;
4798  return sum;
4799};
4800
4801/**
4802 * Calculate the average of two 3d points
4803 * @param {Point3d} a
4804 * @param {Point3d} b
4805 * @return {Point3d} The average, (a+b)/2
4806 */
4807Point3d.avg = function (a, b) {
4808  return new Point3d((a.x + b.x) / 2, (a.y + b.y) / 2, (a.z + b.z) / 2);
4809};
4810
4811/**
4812 * Calculate the cross product of the two provided points, returns axb
4813 * Documentation: http://en.wikipedia.org/wiki/Cross_product
4814 * @param {Point3d} a
4815 * @param {Point3d} b
4816 * @return {Point3d} cross product axb
4817 */
4818Point3d.crossProduct = function (a, b) {
4819  var crossproduct = new Point3d();
4820
4821  crossproduct.x = a.y * b.z - a.z * b.y;
4822  crossproduct.y = a.z * b.x - a.x * b.z;
4823  crossproduct.z = a.x * b.y - a.y * b.x;
4824
4825  return crossproduct;
4826};
4827
4828/**
4829 * Rtrieve the length of the vector (or the distance from this point to the origin
4830 * @return {number}  length
4831 */
4832Point3d.prototype.length = function () {
4833  return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z);
4834};
4835
4836module.exports = Point3d;
4837
4838/***/ }),
4839/* 35 */
4840/***/ (function(module, exports, __webpack_require__) {
4841
4842"use strict";
4843var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;
4844/**
4845 * Created by Alex on 11/6/2014.
4846 */
4847
4848// https://github.com/umdjs/umd/blob/master/returnExports.js#L40-L60
vendor: 5,697 bytes, lines 4849-5003
4849// if the module has no dependencies, the above pattern can be simplified to
4850(function (root, factory) {
4851  if (true) {
4852    // AMD. Register as an anonymous module.
4853    !(__WEBPACK_AMD_DEFINE_ARRAY__ = [], __WEBPACK_AMD_DEFINE_FACTORY__ = (factory),
4854				__WEBPACK_AMD_DEFINE_RESULT__ = (typeof __WEBPACK_AMD_DEFINE_FACTORY__ === 'function' ?
4855				(__WEBPACK_AMD_DEFINE_FACTORY__.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__)) : __WEBPACK_AMD_DEFINE_FACTORY__),
4856				__WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));
4857  } else if (typeof exports === 'object') {
4858    // Node. Does not work with strict CommonJS, but
4859    // only CommonJS-like environments that support module.exports,
4860    // like Node.
4861    module.exports = factory();
4862  } else {
4863    // Browser globals (root is window)
4864    root.keycharm = factory();
4865  }
4866}(this, function () {
4867
4868  function keycharm(options) {
4869    var preventDefault = options && options.preventDefault || false;
4870
4871    var container = options && options.container || window;
4872
4873    var _exportFunctions = {};
4874    var _bound = {keydown:{}, keyup:{}};
4875    var _keys = {};
4876    var i;
4877
4878    // a - z
4879    for (i = 97; i <= 122; i++) {_keys[String.fromCharCode(i)] = {code:65 + (i - 97), shift: false};}
4880    // A - Z
4881    for (i = 65; i <= 90; i++) {_keys[String.fromCharCode(i)] = {code:i, shift: true};}
4882    // 0 - 9
4883    for (i = 0;  i <= 9;   i++) {_keys['' + i] = {code:48 + i, shift: false};}
4884    // F1 - F12
4885    for (i = 1;  i <= 12;   i++) {_keys['F' + i] = {code:111 + i, shift: false};}
4886    // num0 - num9
4887    for (i = 0;  i <= 9;   i++) {_keys['num' + i] = {code:96 + i, shift: false};}
4888
4889    // numpad misc
4890    _keys['num*'] = {code:106, shift: false};
4891    _keys['num+'] = {code:107, shift: false};
4892    _keys['num-'] = {code:109, shift: false};
4893    _keys['num/'] = {code:111, shift: false};
4894    _keys['num.'] = {code:110, shift: false};
4895    // arrows
4896    _keys['left']  = {code:37, shift: false};
4897    _keys['up']    = {code:38, shift: false};
4898    _keys['right'] = {code:39, shift: false};
4899    _keys['down']  = {code:40, shift: false};
4900    // extra keys
4901    _keys['space'] = {code:32, shift: false};
4902    _keys['enter'] = {code:13, shift: false};
4903    _keys['shift'] = {code:16, shift: undefined};
4904    _keys['esc']   = {code:27, shift: false};
4905    _keys['backspace'] = {code:8, shift: false};
4906    _keys['tab']       = {code:9, shift: false};
4907    _keys['ctrl']      = {code:17, shift: false};
4908    _keys['alt']       = {code:18, shift: false};
4909    _keys['delete']    = {code:46, shift: false};
4910    _keys['pageup']    = {code:33, shift: false};
4911    _keys['pagedown']  = {code:34, shift: false};
4912    // symbols
4913    _keys['=']     = {code:187, shift: false};
4914    _keys['-']     = {code:189, shift: false};
4915    _keys[']']     = {code:221, shift: false};
4916    _keys['[']     = {code:219, shift: false};
4917
4918
4919
4920    var down = function(event) {handleEvent(event,'keydown');};
4921    var up = function(event) {handleEvent(event,'keyup');};
4922
4923    // handle the actualy bound key with the event
4924    var handleEvent = function(event,type) {
4925      if (_bound[type][event.keyCode] !== undefined) {
4926        var bound = _bound[type][event.keyCode];
4927        for (var i = 0; i < bound.length; i++) {
4928          if (bound[i].shift === undefined) {
4929            bound[i].fn(event);
4930          }
4931          else if (bound[i].shift == true && event.shiftKey == true) {
4932            bound[i].fn(event);
4933          }
4934          else if (bound[i].shift == false && event.shiftKey == false) {
4935            bound[i].fn(event);
4936          }
4937        }
4938
4939        if (preventDefault == true) {
4940          event.preventDefault();
4941        }
4942      }
4943    };
4944
4945    // bind a key to a callback
4946    _exportFunctions.bind = function(key, callback, type) {
4947      if (type === undefined) {
4948        type = 'keydown';
4949      }
4950      if (_keys[key] === undefined) {
4951        throw new Error("unsupported key: " + key);
4952      }
4953      if (_bound[type][_keys[key].code] === undefined) {
4954        _bound[type][_keys[key].code] = [];
4955      }
4956      _bound[type][_keys[key].code].push({fn:callback, shift:_keys[key].shift});
4957    };
4958
4959
4960    // bind all keys to a call back (demo purposes)
4961    _exportFunctions.bindAll = function(callback, type) {
4962      if (type === undefined) {
4963        type = 'keydown';
4964      }
4965      for (var key in _keys) {
4966        if (_keys.hasOwnProperty(key)) {
4967          _exportFunctions.bind(key,callback,type);
4968        }
4969      }
4970    };
4971
4972    // get the key label from an event
4973    _exportFunctions.getKey = function(event) {
4974      for (var key in _keys) {
4975        if (_keys.hasOwnProperty(key)) {
4976          if (event.shiftKey == true && _keys[key].shift == true && event.keyCode == _keys[key].code) {
4977            return key;
4978          }
4979          else if (event.shiftKey == false && _keys[key].shift == false && event.keyCode == _keys[key].code) {
4980            return key;
4981          }
4982          else if (event.keyCode == _keys[key].code && key == 'shift') {
4983            return key;
4984          }
4985        }
4986      }
4987      return "unknown key, currently not supported";
4988    };
4989
4990    // unbind either a specific callback from a key or all of them (by leaving callback undefined)
4991    _exportFunctions.unbind = function(key, callback, type) {
4992      if (type === undefined) {
4993        type = 'keydown';
4994      }
4995      if (_keys[key] === undefined) {
4996        throw new Error("unsupported key: " + key);
4997      }
4998      if (callback !== undefined) {
4999        var newBindings = [];
5000        var bound = _bound[type][_keys[key].code];
5001        if (bound !== undefined) {
5002          for (var i = 0; i < bound.length; i++) {
5003            if (!(bound[i].fn == callback && bound[i].shift == _keys[key].shift)) {
5004              newBindings.push(_bound[type][_keys[key].code][i]);
5005            }
5006          }
5007        }
5008        _bound[type][_keys[key].code] = newBindings;
5009      }
5010      else {
5011        _bound[type][_keys[key].code] = [];
5012      }
5013    };
5014
5015    // reset all bound variables.
5016    _exportFunctions.reset = function() {
5017      _bound = {keydown:{}, keyup:{}};
5018    };
5019
5020    // unbind all listeners and reset all variables.
5021    _exportFunctions.destroy = function() {
5022      _bound = {keydown:{}, keyup:{}};
5023      container.removeEventListener('keydown', down, true);
5024      container.removeEventListener('keyup', up, true);
5025    };
5026
5027    // create listeners.
5028    container.addEventListener('keydown',down,true);
5029    container.addEventListener('keyup',up,true);
5030
5031    // return the public functions.
5032    return _exportFunctions;
5033  }
5034
5035  return keycharm;
5036}));
5037
5038
5039
5040
5041/***/ }),
5042/* 36 */
5043/***/ (function(module, exports, __webpack_require__) {
5044
5045"use strict";
5046
5047
5048/**
5049 * used in Core to convert the options into a volatile variable
5050 * 
5051 * @param {function} moment
5052 * @param {Object} body
5053 * @param {Array | Object} hiddenDates
5054 * @returns {number}
5055 */
5056exports.convertHiddenOptions = function (moment, body, hiddenDates) {
5057  if (hiddenDates && !Array.isArray(hiddenDates)) {
5058    return exports.convertHiddenOptions(moment, body, [hiddenDates]);
5059  }
5060
5061  body.hiddenDates = [];
5062  if (hiddenDates) {
5063    if (Array.isArray(hiddenDates) == true) {
5064      for (var i = 0; i < hiddenDates.length; i++) {
5065        if (hiddenDates[i].repeat === undefined) {
5066          var dateItem = {};
5067          dateItem.start = moment(hiddenDates[i].start).toDate().valueOf();
5068          dateItem.end = moment(hiddenDates[i].end).toDate().valueOf();
5069          body.hiddenDates.push(dateItem);
5070        }
5071      }
5072      body.hiddenDates.sort(function (a, b) {
5073        return a.start - b.start;
5074      }); // sort by start time
5075    }
5076  }
5077};
5078
5079/**
5080 * create new entrees for the repeating hidden dates
5081 *
5082 * @param {function} moment
5083 * @param {Object} body
5084 * @param {Array | Object} hiddenDates
5085 * @returns {null}
5086 */
5087exports.updateHiddenDates = function (moment, body, hiddenDates) {
5088  if (hiddenDates && !Array.isArray(hiddenDates)) {
5089    return exports.updateHiddenDates(moment, body, [hiddenDates]);
5090  }
5091
5092  if (hiddenDates && body.domProps.centerContainer.width !== undefined) {
5093    exports.convertHiddenOptions(moment, body, hiddenDates);
5094
5095    var start = moment(body.range.start);
5096    var end = moment(body.range.end);
5097
5098    var totalRange = body.range.end - body.range.start;
5099    var pixelTime = totalRange / body.domProps.centerContainer.width;
5100
5101    for (var i = 0; i < hiddenDates.length; i++) {
5102      if (hiddenDates[i].repeat !== undefined) {
5103        var startDate = moment(hiddenDates[i].start);
5104        var endDate = moment(hiddenDates[i].end);
5105
5106        if (startDate._d == "Invalid Date") {
5107          throw new Error("Supplied start date is not valid: " + hiddenDates[i].start);
5108        }
5109        if (endDate._d == "Invalid Date") {
5110          throw new Error("Supplied end date is not valid: " + hiddenDates[i].end);
5111        }
5112
5113        var duration = endDate - startDate;
5114        if (duration >= 4 * pixelTime) {
5115
5116          var offset = 0;
5117          var runUntil = end.clone();
5118          switch (hiddenDates[i].repeat) {
5119            case "daily":
5120              // case of time
5121              if (startDate.day() != endDate.day()) {
5122                offset = 1;
5123              }
5124              startDate.dayOfYear(start.dayOfYear());
5125              startDate.year(start.year());
5126              startDate.subtract(7, 'days');
5127
5128              endDate.dayOfYear(start.dayOfYear());
5129              endDate.year(start.year());
5130              endDate.subtract(7 - offset, 'days');
5131
5132              runUntil.add(1, 'weeks');
5133              break;
5134            case "weekly":
5135              var dayOffset = endDate.diff(startDate, 'days');
5136              var day = startDate.day();
5137
5138              // set the start date to the range.start
5139              startDate.date(start.date());
5140              startDate.month(start.month());
5141              startDate.year(start.year());
5142              endDate = startDate.clone();
5143
5144              // force
5145              startDate.day(day);
5146              endDate.day(day);
5147              endDate.add(dayOffset, 'days');
5148
5149              startDate.subtract(1, 'weeks');
5150              endDate.subtract(1, 'weeks');
5151
5152              runUntil.add(1, 'weeks');
5153              break;
5154            case "monthly":
5155              if (startDate.month() != endDate.month()) {
5156                offset = 1;
5157              }
5158              startDate.month(start.month());
5159              startDate.year(start.year());
5160              startDate.subtract(1, 'months');
5161
5162              endDate.month(start.month());
5163              endDate.year(start.year());
5164              endDate.subtract(1, 'months');
5165              endDate.add(offset, 'months');
5166
5167              runUntil.add(1, 'months');
5168              break;
5169            case "yearly":
5170              if (startDate.year() != endDate.year()) {
5171                offset = 1;
5172              }
5173              startDate.year(start.year());
5174              startDate.subtract(1, 'years');
5175              endDate.year(start.year());
5176              endDate.subtract(1, 'years');
5177              endDate.add(offset, 'years');
5178
5179              runUntil.add(1, 'years');
5180              break;
5181            default:
5182              console.log("Wrong repeat format, allowed are: daily, weekly, monthly, yearly. Given:", hiddenDates[i].repeat);
5183              return;
5184          }
5185          while (startDate < runUntil) {
5186            body.hiddenDates.push({ start: startDate.valueOf(), end: endDate.valueOf() });
5187            switch (hiddenDates[i].repeat) {
5188              case "daily":
5189                startDate.add(1, 'days');
5190                endDate.add(1, 'days');
5191                break;
5192              case "weekly":
5193                startDate.add(1, 'weeks');
5194                endDate.add(1, 'weeks');
5195                break;
5196              case "monthly":
5197                startDate.add(1, 'months');
5198                endDate.add(1, 'months');
5199                break;
5200              case "yearly":
5201                startDate.add(1, 'y');
5202                endDate.add(1, 'y');
5203                break;
5204              default:
5205                console.log("Wrong repeat format, allowed are: daily, weekly, monthly, yearly. Given:", hiddenDates[i].repeat);
5206                return;
5207            }
5208          }
5209          body.hiddenDates.push({ start: startDate.valueOf(), end: endDate.valueOf() });
5210        }
5211      }
5212    }
5213    // remove duplicates, merge where possible
5214    exports.removeDuplicates(body);
5215    // ensure the new positions are not on hidden dates
5216    var startHidden = exports.isHidden(body.range.start, body.hiddenDates);
5217    var endHidden = exports.isHidden(body.range.end, body.hiddenDates);
5218    var rangeStart = body.range.start;
5219    var rangeEnd = body.range.end;
5220    if (startHidden.hidden == true) {
5221      rangeStart = body.range.startToFront == true ? startHidden.startDate - 1 : startHidden.endDate + 1;
5222    }
5223    if (endHidden.hidden == true) {
5224      rangeEnd = body.range.endToFront == true ? endHidden.startDate - 1 : endHidden.endDate + 1;
5225    }
5226    if (startHidden.hidden == true || endHidden.hidden == true) {
5227      body.range._applyRange(rangeStart, rangeEnd);
5228    }
5229  }
5230};
5231
5232/**
5233 * remove duplicates from the hidden dates list. Duplicates are evil. They mess everything up.
5234 * Scales with N^2
5235 *
5236 * @param {Object} body
5237 */
5238exports.removeDuplicates = function (body) {
5239  var hiddenDates = body.hiddenDates;
5240  var safeDates = [];
5241  for (var i = 0; i < hiddenDates.length; i++) {
5242    for (var j = 0; j < hiddenDates.length; j++) {
5243      if (i != j && hiddenDates[j].remove != true && hiddenDates[i].remove != true) {
5244        // j inside i
5245        if (hiddenDates[j].start >= hiddenDates[i].start && hiddenDates[j].end <= hiddenDates[i].end) {
5246          hiddenDates[j].remove = true;
5247        }
5248        // j start inside i
5249        else if (hiddenDates[j].start >= hiddenDates[i].start && hiddenDates[j].start <= hiddenDates[i].end) {
5250            hiddenDates[i].end = hiddenDates[j].end;
5251            hiddenDates[j].remove = true;
5252          }
5253          // j end inside i
5254          else if (hiddenDates[j].end >= hiddenDates[i].start && hiddenDates[j].end <= hiddenDates[i].end) {
5255              hiddenDates[i].start = hiddenDates[j].start;
5256              hiddenDates[j].remove = true;
5257            }
5258      }
5259    }
5260  }
5261
5262  for (i = 0; i < hiddenDates.length; i++) {
5263    if (hiddenDates[i].remove !== true) {
5264      safeDates.push(hiddenDates[i]);
5265    }
5266  }
5267
5268  body.hiddenDates = safeDates;
5269  body.hiddenDates.sort(function (a, b) {
5270    return a.start - b.start;
5271  }); // sort by start time
5272};
5273
5274exports.printDates = function (dates) {
5275  for (var i = 0; i < dates.length; i++) {
5276    console.log(i, new Date(dates[i].start), new Date(dates[i].end), dates[i].start, dates[i].end, dates[i].remove);
5277  }
5278};
5279
5280/**
5281 * Used in TimeStep to avoid the hidden times.
5282 * @param {function} moment
5283 * @param {TimeStep} timeStep
5284 * @param {Date} previousTime
5285 */
5286exports.stepOverHiddenDates = function (moment, timeStep, previousTime) {
5287  var stepInHidden = false;
5288  var currentValue = timeStep.current.valueOf();
5289  for (var i = 0; i < timeStep.hiddenDates.length; i++) {
5290    var startDate = timeStep.hiddenDates[i].start;
5291    var endDate = timeStep.hiddenDates[i].end;
5292    if (currentValue >= startDate && currentValue < endDate) {
5293      stepInHidden = true;
5294      break;
5295    }
5296  }
5297
5298  if (stepInHidden == true && currentValue < timeStep._end.valueOf() && currentValue != previousTime) {
5299    var prevValue = moment(previousTime);
5300    var newValue = moment(endDate);
5301    //check if the next step should be major
5302    if (prevValue.year() != newValue.year()) {
5303      timeStep.switchedYear = true;
5304    } else if (prevValue.month() != newValue.month()) {
5305      timeStep.switchedMonth = true;
5306    } else if (prevValue.dayOfYear() != newValue.dayOfYear()) {
5307      timeStep.switchedDay = true;
5308    }
5309
5310    timeStep.current = newValue;
5311  }
5312};
5313
5314///**
5315// * Used in TimeStep to avoid the hidden times.
5316// * @param timeStep
5317// * @param previousTime
5318// */
5319//exports.checkFirstStep = function(timeStep) {
5320//  var stepInHidden = false;
5321//  var currentValue = timeStep.current.valueOf();
5322//  for (var i = 0; i < timeStep.hiddenDates.length; i++) {
5323//    var startDate = timeStep.hiddenDates[i].start;
5324//    var endDate = timeStep.hiddenDates[i].end;
5325//    if (currentValue >= startDate && currentValue < endDate) {
5326//      stepInHidden = true;
5327//      break;
5328//    }
5329//  }
5330//
5331//  if (stepInHidden == true && currentValue <= timeStep._end.valueOf()) {
5332//    var newValue = moment(endDate);
5333//    timeStep.current = newValue.toDate();
5334//  }
5335//};
5336
5337/**
5338 * replaces the Core toScreen methods
5339 *
5340 * @param {vis.Core} Core
5341 * @param {Date} time
5342 * @param {number} width
5343 * @returns {number}
5344 */
5345exports.toScreen = function (Core, time, width) {
5346  var conversion;
5347  if (Core.body.hiddenDates.length == 0) {
5348    conversion = Core.range.conversion(width);
5349    return (time.valueOf() - conversion.offset) * conversion.scale;
5350  } else {
5351    var hidden = exports.isHidden(time, Core.body.hiddenDates);
5352    if (hidden.hidden == true) {
5353      time = hidden.startDate;
5354    }
5355
5356    var duration = exports.getHiddenDurationBetween(Core.body.hiddenDates, Core.range.start, Core.range.end);
5357    if (time < Core.range.start) {
5358      conversion = Core.range.conversion(width, duration);
5359      var hiddenBeforeStart = exports.getHiddenDurationBeforeStart(Core.body.hiddenDates, time, conversion.offset);
5360      time = Core.options.moment(time).toDate().valueOf();
5361      time = time + hiddenBeforeStart;
5362      return -(conversion.offset - time.valueOf()) * conversion.scale;
5363    } else if (time > Core.range.end) {
5364      var rangeAfterEnd = { start: Core.range.start, end: time };
5365      time = exports.correctTimeForHidden(Core.options.moment, Core.body.hiddenDates, rangeAfterEnd, time);
5366      conversion = Core.range.conversion(width, duration);
5367      return (time.valueOf() - conversion.offset) * conversion.scale;
5368    } else {
5369      time = exports.correctTimeForHidden(Core.options.moment, Core.body.hiddenDates, Core.range, time);
5370      conversion = Core.range.conversion(width, duration);
5371      return (time.valueOf() - conversion.offset) * conversion.scale;
5372    }
5373  }
5374};
5375
5376/**
5377 * Replaces the core toTime methods
5378 *
5379 * @param {vis.Core} Core
5380 * @param {number} x
5381 * @param {number} width
5382 * @returns {Date}
5383 */
5384exports.toTime = function (Core, x, width) {
5385  if (Core.body.hiddenDates.length == 0) {
5386    var conversion = Core.range.conversion(width);
5387    return new Date(x / conversion.scale + conversion.offset);
5388  } else {
5389    var hiddenDuration = exports.getHiddenDurationBetween(Core.body.hiddenDates, Core.range.start, Core.range.end);
5390    var totalDuration = Core.range.end - Core.range.start - hiddenDuration;
5391    var partialDuration = totalDuration * x / width;
5392    var accumulatedHiddenDuration = exports.getAccumulatedHiddenDuration(Core.body.hiddenDates, Core.range, partialDuration);
5393
5394    return new Date(accumulatedHiddenDuration + partialDuration + Core.range.start);
5395  }
5396};
5397
5398/**
5399 * Support function
5400 *
5401 * @param {Array.<{start: Window.start, end: *}>} hiddenDates
5402 * @param {number} start
5403 * @param {number} end
5404 * @returns {number}
5405 */
5406exports.getHiddenDurationBetween = function (hiddenDates, start, end) {
5407  var duration = 0;
5408  for (var i = 0; i < hiddenDates.length; i++) {
5409    var startDate = hiddenDates[i].start;
5410    var endDate = hiddenDates[i].end;
5411    // if time after the cutout, and the
5412    if (startDate >= start && endDate < end) {
5413      duration += endDate - startDate;
5414    }
5415  }
5416  return duration;
5417};
5418
5419/**
5420 * Support function
5421 *
5422 * @param {Array.<{start: Window.start, end: *}>} hiddenDates
5423 * @param {number} start
5424 * @param {number} end
5425 * @returns {number}
5426 */
5427exports.getHiddenDurationBeforeStart = function (hiddenDates, start, end) {
5428  var duration = 0;
5429  for (var i = 0; i < hiddenDates.length; i++) {
5430    var startDate = hiddenDates[i].start;
5431    var endDate = hiddenDates[i].end;
5432
5433    if (startDate >= start && endDate <= end) {
5434      duration += endDate - startDate;
5435    }
5436  }
5437  return duration;
5438};
5439
5440/**
5441 * Support function
5442 * @param {function} moment
5443 * @param {Array.<{start: Window.start, end: *}>} hiddenDates
5444 * @param {{start: number, end: number}} range
5445 * @param {Date} time
5446 * @returns {number}
5447 */
5448exports.correctTimeForHidden = function (moment, hiddenDates, range, time) {
5449  time = moment(time).toDate().valueOf();
5450  time -= exports.getHiddenDurationBefore(moment, hiddenDates, range, time);
5451  return time;
5452};
5453
5454exports.getHiddenDurationBefore = function (moment, hiddenDates, range, time) {
5455  var timeOffset = 0;
5456  time = moment(time).toDate().valueOf();
5457
5458  for (var i = 0; i < hiddenDates.length; i++) {
5459    var startDate = hiddenDates[i].start;
5460    var endDate = hiddenDates[i].end;
5461    // if time after the cutout, and the
5462    if (startDate >= range.start && endDate < range.end) {
5463      if (time >
5463= endDate) {
5464        timeOffset += endDate - startDate;
5465      }
5466    }
5467  }
5468  return timeOffset;
5469};
5470
5471/**
5472 * sum the duration from start to finish, including the hidden duration,
5473 * until the required amount has been reached, return the accumulated hidden duration
5474 * @param {Array.<{start: Window.start, end: *}>} hiddenDates
5475 * @param {{start: number, end: number}} range
5476 * @param {number} [requiredDuration=0]
5477 * @returns {number}
5478 */
5479exports.getAccumulatedHiddenDuration = function (hiddenDates, range, requiredDuration) {
5480  var hiddenDuration = 0;
5481  var duration = 0;
5482  var previousPoint = range.start;
5483  //exports.printDates(hiddenDates)
5484  for (var i = 0; i < hiddenDates.length; i++) {
5485    var startDate = hiddenDates[i].start;
5486    var endDate = hiddenDates[i].end;
5487    // if time after the cutout, and the
5488    if (startDate >= range.start && endDate < range.end) {
5489      duration += startDate - previousPoint;
5490      previousPoint = endDate;
5491      if (duration >= requiredDuration) {
5492        break;
5493      } else {
5494        hiddenDuration += endDate - startDate;
5495      }
5496    }
5497  }
5498
5499  return hiddenDuration;
5500};
5501
5502/**
5503 * used to step over to either side of a hidden block. Correction is disabled on tablets, might be set to true
5504 * @param {Array.<{start: Window.start, end: *}>} hiddenDates
5505 * @param {Date} time
5506 * @param {number} direction
5507 * @param {boolean} correctionEnabled
5508 * @returns {Date|number}
5509 */
5510exports.snapAwayFromHidden = function (hiddenDates, time, direction, correctionEnabled) {
5511  var isHidden = exports.isHidden(time, hiddenDates);
5512  if (isHidden.hidden == true) {
5513    if (direction < 0) {
5514      if (correctionEnabled == true) {
5515        return isHidden.startDate - (isHidden.endDate - time) - 1;
5516      } else {
5517        return isHidden.startDate - 1;
5518      }
5519    } else {
5520      if (correctionEnabled == true) {
5521        return isHidden.endDate + (time - isHidden.startDate) + 1;
5522      } else {
5523        return isHidden.endDate + 1;
5524      }
5525    }
5526  } else {
5527    return time;
5528  }
5529};
5530
5531/**
5532 * Check if a time is hidden
5533 *
5534 * @param {Date} time
5535 * @param {Array.<{start: Window.start, end: *}>} hiddenDates
5536 * @returns {{hidden: boolean, startDate: Window.start, endDate: *}}
5537 */
5538exports.isHidden = function (time, hiddenDates) {
5539  for (var i = 0; i < hiddenDates.length; i++) {
5540    var startDate = hiddenDates[i].start;
5541    var endDate = hiddenDates[i].end;
5542
5543    if (time >= startDate && time < endDate) {
5544      // if the start is entering a hidden zone
5545      return { hidden: true, startDate: startDate, endDate: endDate };
5546    }
5547  }
5548  return { hidden: false, startDate: startDate, endDate: endDate };
5549};
5550
5551/***/ }),
5552/* 37 */
5553/***/ (function(module, exports, __webpack_require__) {
5554
5555"use strict";
5556
5557
5558/**
5559 * Register a touch event, taking place before a gesture
5560 * @param {Hammer} hammer       A hammer instance
5561 * @param {function} callback   Callback, called as callback(event)
5562 */
5563exports.onTouch = function (hammer, callback) {
5564  callback.inputHandler = function (event) {
5565    if (event.isFirst) {
5566      callback(event);
5567    }
5568  };
5569
5570  hammer.on('hammer.input', callback.inputHandler);
5571};
5572
5573/**
5574 * Register a release event, taking place after a gesture
5575 * @param {Hammer} hammer       A hammer instance
5576 * @param {function} callback   Callback, called as callback(event)
5577 * @returns {*}
5578 */
5579exports.onRelease = function (hammer, callback) {
5580  callback.inputHandler = function (event) {
5581    if (event.isFinal) {
5582      callback(event);
5583    }
5584  };
5585
5586  return hammer.on('hammer.input', callback.inputHandler);
5587};
5588
5589/**
5590 * Unregister a touch event, taking place before a gesture
5591 * @param {Hammer} hammer       A hammer instance
5592 * @param {function} callback   Callback, called as callback(event)
5593 */
5594exports.offTouch = function (hammer, callback) {
5595  hammer.off('hammer.input', callback.inputHandler);
5596};
5597
5598/**
5599 * Unregister a release event, taking place before a gesture
5600 * @param {Hammer} hammer       A hammer instance
5601 * @param {function} callback   Callback, called as callback(event)
5602 */
5603exports.offRelease = exports.offTouch;
5604
5605/**
5606 * Hack the PinchRecognizer such that it doesn't prevent default behavior
5607 * for vertical panning.
5608 *
5609 * Yeah ... this is quite a hack ... see https://github.com/hammerjs/hammer.js/issues/932
5610 *
5611 * @param {Hammer.Pinch} pinchRecognizer
5612 * @return {Hammer.Pinch} returns the pinchRecognizer
5613 */
5614exports.disablePreventDefaultVertically = function (pinchRecognizer) {
5615  var TOUCH_ACTION_PAN_Y = 'pan-y';
5616
5617  pinchRecognizer.getTouchAction = function () {
5618    // default method returns [TOUCH_ACTION_NONE]
5619    return [TOUCH_ACTION_PAN_Y];
5620  };
5621
5622  return pinchRecognizer;
5623};
5624
5625/***/ }),
5626/* 38 */
5627/***/ (function(module, exports, __webpack_require__) {
5628
5629"use strict";
5630
5631
5632var _typeof2 = __webpack_require__(6);
5633
5634var _typeof3 = _interopRequireDefault(_typeof2);
5635
5636var _keys = __webpack_require__(8);
5637
5638var _keys2 = _interopRequireDefault(_keys);
5639
5640function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
5641
5642var Hammer = __webpack_require__(10);
5643var util = __webpack_require__(2);
5644var moment = __webpack_require__(9);
5645
5646/**
5647 * @constructor Item
5648 * @param {Object} data             Object containing (optional) parameters type,
5649 *                                  start, end, content, group, className.
5650 * @param {{toScreen: function, toTime: function}} conversion
5651 *                                  Conversion functions from time to screen and vice versa
5652 * @param {Object} options          Configuration options
5653 *                                  // TODO: describe available options
5654 */
5655function Item(data, conversion, options) {
5656  this.id = null;
5657  this.parent = null;
5658  this.data = data;
5659  this.dom = null;
5660  this.conversion = conversion || {};
5661  this.options = options || {};
5662  this.selected = false;
5663  this.displayed = false;
5664  this.groupShowing = true;
5665  this.dirty = true;
5666
5667  this.top = null;
5668  this.right = null;
5669  this.left = null;
5670  this.width = null;
5671  this.height = null;
5672
5673  this.editable = null;
5674  this._updateEditStatus();
5675}
5676
5677Item.prototype.stack = true;
5678
5679/**
5680 * Select current item
5681 */
5682Item.prototype.select = function () {
5683  this.selected = true;
5684  this.dirty = true;
5685  if (this.displayed) this.redraw();
5686};
5687
5688/**
5689 * Unselect current item
5690 */
5691Item.prototype.unselect = function () {
5692  this.selected = false;
5693  this.dirty = true;
5694  if (this.displayed) this.redraw();
5695};
5696
5697/**
5698 * Set data for the item. Existing data will be updated. The id should not
5699 * be changed. When the item is displayed, it will be redrawn immediately.
5700 * @param {Object} data
5701 */
5702Item.prototype.setData = function (data) {
5703  var groupChanged = data.group != undefined && this.data.group != data.group;
5704  if (groupChanged && this.parent != null) {
5705    this.parent.itemSet._moveToGroup(this, data.group);
5706  }
5707
5708  if (this.parent) {
5709    this.parent.stackDirty = true;
5710  }
5711
5712  var subGroupChanged = data.subgroup != undefined && this.data.subgroup != data.subgroup;
5713  if (subGroupChanged && this.parent != null) {
5714    this.parent.changeSubgroup(this, this.data.subgroup, data.subgroup);
5715  }
5716
5717  this.data = data;
5718  this._updateEditStatus();
5719  this.dirty = true;
5720  if (this.displayed) this.redraw();
5721};
5722
5723/**
5724 * Set a parent for the item
5725 * @param {Group} parent
5726 */
5727Item.prototype.setParent = function (parent) {
5728  if (this.displayed) {
5729    this.hide();
5730    this.parent = parent;
5731    if (this.parent) {
5732      this.show();
5733    }
5734  } else {
5735    this.parent = parent;
5736  }
5737};
5738
5739/**
5740 * Check whether this item is visible inside given range
5741 * @param {vis.Range} range with a timestamp for start and end
5742 * @returns {boolean} True if visible
5743 */
5744Item.prototype.isVisible = function (range) {
5745  // eslint-disable-line no-unused-vars
5746  return false;
5747};
5748
5749/**
5750 * Show the Item in the DOM (when not already visible)
5751 * @return {Boolean} changed
5752 */
5753Item.prototype.show = function () {
5754  return false;
5755};
5756
5757/**
5758 * Hide the Item from the DOM (when visible)
5759 * @return {Boolean} changed
5760 */
5761Item.prototype.hide = function () {
5762  return false;
5763};
5764
5765/**
5766 * Repaint the item
5767 */
5768Item.prototype.redraw = function () {
5769  // should be implemented by the item
5770};
5771
5772/**
5773 * Reposition the Item horizontally
5774 */
5775Item.prototype.repositionX = function () {
5776  // should be implemented by the item
5777};
5778
5779/**
5780 * Reposition the Item vertically
5781 */
5782Item.prototype.repositionY = function () {
5783  // should be implemented by the item
5784};
5785
5786/**
5787 * Repaint a drag area on the center of the item when the item is selected
5788 * @protected
5789 */
5790Item.prototype._repaintDragCenter = function () {
5791  if (this.selected && this.options.editable.updateTime && !this.dom.dragCenter) {
5792    var me = this;
5793    // create and show drag area
5794    var dragCenter = document.createElement('div');
5795    dragCenter.className = 'vis-drag-center';
5796    dragCenter.dragCenterItem = this;
5797    var hammer = new Hammer(dragCenter);
5798
5799    hammer.on('tap', function (event) {
5800      me.parent.itemSet.body.emitter.emit('click', {
5801        event: event,
5802        item: me.id
5803      });
5804    });
5805    hammer.on('doubletap', function (event) {
5806      event.stopPropagation();
5807      me.parent.itemSet._onUpdateItem(me);
5808      me.parent.itemSet.body.emitter.emit('doubleClick', {
5809        event: event,
5810        item: me.id
5811      });
5812    });
5813
5814    if (this.dom.box) {
5815      if (this.dom.dragLeft) {
5816        this.dom.box.insertBefore(dragCenter, this.dom.dragLeft);
5817      } else {
5818        this.dom.box.appendChild(dragCenter);
5819      }
5820    } else if (this.dom.point) {
5821      this.dom.point.appendChild(dragCenter);
5822    }
5823
5824    this.dom.dragCenter = dragCenter;
5825  } else if (!this.selected && this.dom.dragCenter) {
5826    // delete drag area
5827    if (this.dom.dragCenter.parentNode) {
5828      this.dom.dragCenter.parentNode.removeChild(this.dom.dragCenter);
5829    }
5830    this.dom.dragCenter = null;
5831  }
5832};
5833
5834/**
5835 * Repaint a delete button on the top right of the item when the item is selected
5836 * @param {HTMLElement} anchor
5837 * @protected
5838 */
5839Item.prototype._repaintDeleteButton = function (anchor) {
5840  var editable = (this.options.editable.overrideItems || this.editable == null) && this.options.editable.remove || !this.options.editable.overrideItems && this.editable != null && this.editable.remove;
5841
5842  if (this.selected && editable && !this.dom.deleteButton) {
5843    // create and show button
5844    var me = this;
5845
5846    var deleteButton = document.createElement('div');
5847
5848    if (this.options.rtl) {
5849      deleteButton.className = 'vis-delete-rtl';
5850    } else {
5851      deleteButton.className = 'vis-delete';
5852    }
5853    deleteButton.title = 'Delete this item';
5854
5855    // TODO: be able to destroy the delete button
5856    new Hammer(deleteButton).on('tap', function (event) {
5857      event.stopPropagation();
5858      me.parent.removeFromDataSet(me);
5859    });
5860
5861    anchor.appendChild(deleteButton);
5862    this.dom.deleteButton = deleteButton;
5863  } else if (!this.selected && this.dom.deleteButton) {
5864    // remove button
5865    if (this.dom.deleteButton.parentNode) {
5866      this.dom.deleteButton.parentNode.removeChild(this.dom.deleteButton);
5867    }
5868    this.dom.deleteButton = null;
5869  }
5870};
5871
5872/**
5873 * Repaint a onChange tooltip on the top right of the item when the item is selected
5874 * @param {HTMLElement} anchor
5875 * @protected
5876 */
5877Item.prototype._repaintOnItemUpdateTimeTooltip = function (anchor) {
5878  if (!this.options.tooltipOnItemUpdateTime) return;
5879
5880  var editable = (this.options.editable.updateTime || this.data.editable === true) && this.data.editable !== false;
5881
5882  if (this.selected && editable && !this.dom.onItemUpdateTimeTooltip) {
5883    var onItemUpdateTimeTooltip = document.createElement('div');
5884
5885    onItemUpdateTimeTooltip.className = 'vis-onUpdateTime-tooltip';
5886    anchor.appendChild(onItemUpdateTimeTooltip);
5887    this.dom.onItemUpdateTimeTooltip = onItemUpdateTimeTooltip;
5888  } else if (!this.selected && this.dom.onItemUpdateTimeTooltip) {
5889    // remove button
5890    if (this.dom.onItemUpdateTimeTooltip.parentNode) {
5891      this.dom.onItemUpdateTimeTooltip.parentNode.removeChild(this.dom.onItemUpdateTimeTooltip);
5892    }
5893    this.dom.onItemUpdateTimeTooltip = null;
5894  }
5895
5896  // position onChange tooltip
5897  if (this.dom.onItemUpdateTimeTooltip) {
5898
5899    // only show when editing
5900    this.dom.onItemUpdateTimeTooltip.style.visibility = this.parent.itemSet.touchParams.itemIsDragging ? 'visible' : 'hidden';
5901
5902    // position relative to item's content
5903    if (this.options.rtl) {
5904      this.dom.onItemUpdateTimeTooltip.style.right = this.dom.content.style.right;
5905    } else {
5906      this.dom.onItemUpdateTimeTooltip.style.left = this.dom.content.style.left;
5907    }
5908
5909    // position above or below the item depending on the item's position in the window
5910    var tooltipOffset = 50; // TODO: should be tooltip height (depends on template)
5911    var scrollTop = this.parent.itemSet.body.domProps.scrollTop;
5912
5913    // TODO: this.top for orientation:true is actually the items distance from the bottom... 
5914    // (should be this.bottom)
5915    var itemDistanceFromTop;
5916    if (this.options.orientation.item == 'top') {
5917      itemDistanceFromTop = this.top;
5918    } else {
5919      itemDistanceFromTop = this.parent.height - this.top - this.height;
5920    }
5921    var isCloseToTop = itemDistanceFromTop + this.parent.top - tooltipOffset < -scrollTop;
5922
5923    if (isCloseToTop) {
5924      this.dom.onItemUpdateTimeTooltip.style.bottom = "";
5925      this.dom.onItemUpdateTimeTooltip.style.top = this.height + 2 + "px";
5926    } else {
5927      this.dom.onItemUpdateTimeTooltip.style.top = "";
5928      this.dom.onItemUpdateTimeTooltip.style.bottom = this.height + 2 + "px";
5929    }
5930
5931    // handle tooltip content
5932    var content;
5933    var templateFunction;
5934
5935    if (this.options.tooltipOnItemUpdateTime && this.options.tooltipOnItemUpdateTime.template) {
5936      templateFunction = this.options.tooltipOnItemUpdateTime.template.bind(this);
5937      content = templateFunction(this.data);
5938    } else {
5939      content = 'start: ' + moment(this.data.start).format('MM/DD/YYYY hh:mm');
5940      if (this.data.end) {
5941        content += '<br> end: ' + moment(this.data.end).format('MM/DD/YYYY hh:mm');
5942      }
5943    }
5944    this.dom.onItemUpdateTimeTooltip.innerHTML = content;
5945  }
5946};
5947
5948/**
5949 * Set HTML contents for the item
5950 * @param {Element} element   HTML element to fill with the contents
5951 * @private
5952 */
5953Item.prototype._updateContents = function (element) {
5954  var content;
5955  var changed;
5956  var templateFunction;
5957  var itemVisibleFrameContent;
5958  var visibleFrameTemplateFunction;
5959  var itemData = this.parent.itemSet.itemsData.get(this.id); // get a clone of the data from the dataset
5960
5961  var frameElement = this.dom.box || this.dom.point;
5962  var itemVisibleFrameContentElement = frameElement.getElementsByClassName('vis-item-visible-frame')[0];
5963
5964  if (this.options.visibleFrameTemplate) {
5965    visibleFrameTemplateFunction = this.options.visibleFrameTemplate.bind(this);
5966    itemVisibleFrameContent = visibleFrameTemplateFunction(itemData, frameElement);
5967  } else {
5968    itemVisibleFrameContent = '';
5969  }
5970
5971  if (itemVisibleFrameContentElement) {
5972    if (itemVisibleFrameContent instanceof Object && !(itemVisibleFrameContent instanceof Element)) {
5973      visibleFrameTemplateFunction(itemData, itemVisibleFrameContentElement);
5974    } else {
5975      changed = this._contentToString(this.itemVisibleFrameContent) !== this._contentToString(itemVisibleFrameContent);
5976      if (changed) {
5977        // only replace the content when changed
5978        if (itemVisibleFrameContent instanceof Element) {
5979          itemVisibleFrameContentElement.innerHTML = '';
5980          itemVisibleFrameContentElement.appendChild(itemVisibleFrameContent);
5981        } else if (itemVisibleFrameContent != undefined) {
5982          itemVisibleFrameContentElement.innerHTML = itemVisibleFrameContent;
5983        } else {
5984          if (!(this.data.type == 'background' && this.data.content === undefined)) {
5985            throw new Error('Property "content" missing in item ' + this.id);
5986          }
5987        }
5988
5989        this.itemVisibleFrameContent = itemVisibleFrameContent;
5990      }
5991    }
5992  }
5993
5994  if (this.options.template) {
5995    templateFunction = this.options.template.bind(this);
5996    content = templateFunction(itemData, element, this.data);
5997  } else {
5998    content = this.data.content;
5999  }
6000
6001  if (content instanceof Object && !(content instanceof Element)) {
6002    templateFunction(itemData, element);
6003  } else {
6004    changed = this._contentToString(this.content) !== this._contentToString(content);
6005    if (changed) {
6006      // only replace the content when changed
6007      if (content instanceof Element) {
6008        element.innerHTML = '';
6009        element.appendChild(content);
6010      } else if (content != undefined) {
6011        element.innerHTML = content;
6012      } else {
6013        if (!(this.data.type == 'background' && this.data.content === undefined)) {
6014          throw new Error('Property "content" missing in item ' + this.id);
6015        }
6016      }
6017      this.content = content;
6018    }
6019  }
6020};
6021
6022/**
6023 * Process dataAttributes timeline option and set as data- attributes on dom.content
6024 * @param {Element} element   HTML element to which the attributes will be attached
6025 * @private
6026 */
6027Item.prototype._updateDataAttributes = function (element) {
6028  if (this.options.dataAttributes && this.options.dataAttributes.length > 0) {
6029    var attributes = [];
6030
6031    if (Array.isArray(this.options.dataAttributes)) {
6032      attributes = this.options.dataAttributes;
6033    } else if (this.options.dataAttributes == 'all') {
6034      attributes = (0, _keys2['default'])(this.data);
6035    } else {
6036      return;
6037    }
6038
6039    for (var i = 0; i < attributes.length; i++) {
6040      var name = attributes[i];
6041      var value = this.data[name];
6042
6043      if (value != null) {
6044        element.setAttribute('data-' + name, value);
6045      } else {
6046        element.removeAttribute('data-' + name);
6047      }
6048    }
6049  }
6050};
6051
6052/**
6053 * Update custom styles of the element
6054 * @param {Element} element
6055 * @private
6056 */
6057Item.prototype._updateStyle = function (element) {
6058  // remove old styles
6059  if (this.style) {
6060    util.removeCssText(element, this.style);
6061    this.style = null;
6062  }
6063
6064  // append new styles
6065  if (this.data.style) {
6066    util.addCssText(element, this.data.style);
6067    this.style = this.data.style;
6068  }
6069};
6070
6071/**
6072 * Stringify the items contents
6073 * @param {string | Element | undefined} content
6074 * @returns {string | undefined}
6075 * @private
6076 */
6077Item.prototype._contentToString = function (content) {
6078  if (typeof content === 'string') return content;
6079  if (content && 'outerHTML' in content) return content.outerHTML;
6080  return content;
6081};
6082
6083/**
6084 * Update the editability of this item.
6085 */
6086Item.prototype._updateEditStatus = function () {
6087  if (this.options) {
6088    if (typeof this.options.editable === 'boolean') {
6089      this.editable = {
6090        updateTime: this.options.editable,
6091        updateGroup: this.options.editable,
6092        remove: this.options.editable
6093      };
6094    } else if ((0, _typeof3['default'])(this.options.editable) === 'object') {
6095      this.editable = {};
6096      util.selectiveExtend(['updateTime', 'updateGroup', 'remove'], this.editable, this.options.editable);
6097    }
6098  }
6099  // Item data overrides, except if options.editable.overrideItems is set.
6100  if (!this.options || !this.options.editable || this.options.editable.overrideItems !== true) {
6101    if (this.data) {
6102      if (typeof this.data.editable === 'boolean') {
6103        this.editable = {
6104          updateTime: this.data.editable,
6105          updateGroup: this.data.editable,
6106          remove: this.data.editable
6107        };
6108      } else if ((0, _typeof3['default'])(this.data.editable) === 'object') {
6109        // TODO: in vis.js 5.0, we should change this to not reset options from the timeline configuration.
6110        // Basically just remove the next line...
6111        this.editable = {};
6112        util.selectiveExtend(['updateTime', 'updateGroup', 'remove'], this.editable, this.data.editable);
6113      }
6114    }
6115  }
6116};
6117
6118/**
6119 * Return the width of the item left from its start date
6120 * @return {number}
6121 */
6122Item.prototype.getWidthLeft = function () {
6123  return 0;
6124};
6125
6126/**
6127 * Return the width of the item right from the max of its start and end date
6128 * @return {number}
6129 */
6130Item.prototype.getWidthRight = function () {
6131  return 0;
6132};
6133
6134/**
6135 * Return the title of the item
6136 * @return {string | undefined}
6137 */
6138Item.prototype.getTitle = function () {
6139  return this.data.title;
6140};
6141
6142module.exports = Item;
6143
6144/***/ }),
6145/* 39 */
6146/***/ (function(module, exports) {
6147
6148module.exports = function (bitmap, value) {
6149  return {
6150    enumerable: !(bitmap & 1),
6151    configurable: !(bitmap & 2),
6152    writable: !(bitmap & 4),
6153    value: value
6154  };
6155};
6156
6157
6158/***/ }),
6159/* 40 */
6160/***/ (function(module, exports) {
6161
6162var id = 0;
6163var px = Math.random();
6164module.exports = function (key) {
6165  return 'Symbol('.concat(key === undefined ? '' : key, ')_', (++id + px).toString(36));
6166};
6167
6168
6169/***/ }),
6170/* 41 */
6171/***/ (function(module, exports, __webpack_require__) {
6172
6173// 7.1.13 ToObject(argument)
6174var defined = __webpack_require__(51);
6175module.exports = function (it) {
6176  return Object(defined(it));
6177};
6178
6179
6180/***/ }),
6181/* 42 */
6182/***/ (function(module, exports) {
6183
6184exports.f = {}.propertyIsEnumerable;
6185
6186
6187/***/ }),
6188/* 43 */
6189/***/ (function(module, exports, __webpack_require__) {
6190
6191"use strict";
6192
6193
6194/**
6195 * A queue
6196 * @param {Object} options
6197 *            Available options:
6198 *            - delay: number    When provided, the queue will be flushed
6199 *                               automatically after an inactivity of this delay
6200 *                               in milliseconds.
6201 *                               Default value is null.
6202 *            - max: number      When the queue exceeds the given maximum number
6203 *                               of entries, the queue is flushed automatically.
6204 *                               Default value of max is Infinity.
6205 * @constructor Queue
6206 */
6207function Queue(options) {
6208  // options
6209  this.delay = null;
6210  this.max = Infinity;
6211
6212  // properties
6213  this._queue = [];
6214  this._timeout = null;
6215  this._extended = null;
6216
6217  this.setOptions(options);
6218}
6219
6220/**
6221 * Update the configuration of the queue
6222 * @param {Object} options
6223 *            Available options:
6224 *            - delay: number    When provided, the queue will be flushed
6225 *                               automatically after an inactivity of this delay
6226 *                               in milliseconds.
6227 *                               Default value is null.
6228 *            - max: number      When the queue exceeds the given maximum number
6229 *                               of entries, the queue is flushed automatically.
6230 *                               Default value of max is Infinity.
6231 */
6232Queue.prototype.setOptions = function (options) {
6233  if (options && typeof options.delay !== 'undefined') {
6234    this.delay = options.delay;
6235  }
6236  if (options && typeof options.max !== 'undefined') {
6237    this.max = options.max;
6238  }
6239
6240  this._flushIfNeeded();
6241};
6242
6243/**
6244 * Extend an object with queuing functionality.
6245 * The object will be extended with a function flush, and the methods provided
6246 * in options.replace will be replaced with queued ones.
6247 * @param {Object} object
6248 * @param {Object} options
6249 *            Available options:
6250 *            - replace: Array.<string>
6251 *                               A list with method names of the methods
6252 *                               on the object to be replaced with queued ones.
6253 *            - delay: number    When provided, the queue will be flushed
6254 *                               automatically after an inactivity of this delay
6255 *                               in milliseconds.
6256 *                               Default value is null.
6257 *            - max: number      When the queue exceeds the given maximum number
6258 *                               of entries, the queue is flushed automatically.
6259 *                               Default value of max is Infinity.
6260 * @return {Queue} Returns the created queue
6261 */
6262Queue.extend = function (object, options) {
6263  var queue = new Queue(options);
6264
6265  if (object.flush !== undefined) {
6266    throw new Error('Target object already has a property flush');
6267  }
6268  object.flush = function () {
6269    queue.flush();
6270  };
6271
6272  var methods = [{
6273    name: 'flush',
6274    original: undefined
6275  }];
6276
6277  if (options && options.replace) {
6278    for (var i = 0; i < options.replace.length; i++) {
6279      var name = options.replace[i];
6280      methods.push({
6281        name: name,
6282        original: object[name]
6283      });
6284      queue.replace(object, name);
6285    }
6286  }
6287
6288  queue._extended = {
6289    object: object,
6290    methods: methods
6291  };
6292
6293  return queue;
6294};
6295
6296/**
6297 * Destroy the queue. The queue will first flush all queued actions, and in
6298 * case it has extended an object, will restore the original object.
6299 */
6300Queue.prototype.destroy = function () {
6301  this.flush();
6302
6303  if (this._extended) {
6304    var object = this._extended.object;
6305    var methods = this._extended.methods;
6306    for (var i = 0; i < methods.length; i++) {
6307      var method = methods[i];
6308      if (method.original) {
6309        object[method.name] = method.original;
6310      } else {
6311        delete object[method.name];
6312      }
6313    }
6314    this._extended = null;
6315  }
6316};
6317
6318/**
6319 * Replace a method on an object with a queued version
6320 * @param {Object} object   Object having the method
6321 * @param {string} method   The method name
6322 */
6323Queue.prototype.replace = function (object, method) {
6324  var me = this;
6325  var original = object[method];
6326  if (!original) {
6327    throw new Error('Method ' + method + ' undefined');
6328  }
6329
6330  object[method] = function () {
6331    // create an Array with the arguments
6332    var args = [];
6333    for (var i = 0; i < arguments.length; i++) {
6334      args[i] = arguments[i];
6335    }
6336
6337    // add this call to the queue
6338    me.queue({
6339      args: args,
6340      fn: original,
6341      context: this
6342    });
6343  };
6344};
6345
6346/**
6347 * Queue a call
6348 * @param {function | {fn: function, args: Array} | {fn: function, args: Array, context: Object}} entry
6349 */
6350Queue.prototype.queue = function (entry) {
6351  if (typeof entry === 'function') {
6352    this._queue.push({ fn: entry });
6353  } else {
6354    this._queue.push(entry);
6355  }
6356
6357  this._flushIfNeeded();
6358};
6359
6360/**
6361 * Check whether the queue needs to be flushed
6362 * @private
6363 */
6364Queue.prototype._flushIfNeeded = function () {
6365  // flush when the maximum is exceeded.
6366  if (this._queue.length > this.max) {
6367    this.flush();
6368  }
6369
6370  // flush after a period of inactivity when a delay is configured
6371  clearTimeout(this._timeout);
6372  if (this.queue.length > 0 && typeof this.delay === 'number') {
6373    var me = this;
6374    this._timeout = setTimeout(function () {
6375      me.flush();
6376    }, this.delay);
6377  }
6378};
6379
6380/**
6381 * Flush all queued calls
6382 */
6383Queue.prototype.flush = function () {
6384  while (this._queue.length > 0) {
6385    var entry = this._queue.shift();
6386    entry.fn.apply(entry.context || entry.fn, entry.args || []);
6387  }
6388};
6389
6390module.exports = Queue;
6391
6392/***/ }),
6393/* 44 */
6394/***/ (function(module, exports) {
6395
6396
6397/**
6398 * Expose `Emitter`.
6399 */
6400
6401module.exports = Emitter;
6402
6403/**
6404 * Initialize a new `Emitter`.
6405 *
6406 * @api public
6407 */
6408
6409function Emitter(obj) {
6410  if (obj) return mixin(obj);
6411};
6412
6413/**
6414 * Mixin the emitter properties.
6415 *
6416 * @param {Object} obj
6417 * @return {Object}
6418 * @api private
6419 */
6420
6421function mixin(obj) {
6422  for (var key in Emitter.prototype) {
6423    obj[key] = Emitter.prototype[key];
6424  }
6425  return obj;
6426}
6427
6428/**
6429 * Listen on the given `event` with `fn`.
6430 *
6431 * @param {String} event
6432 * @param {Function} fn
6433 * @return {Emitter}
6434 * @api public
6435 */
6436
6437Emitter.prototype.on =
6438Emitter.prototype.addEventListener = function(event, fn){
6439  this._callbacks = this._callbacks || {};
6440  (this._callbacks[event] = this._callbacks[event] || [])
6441    .push(fn);
6442  return this;
6443};
6444
6445/**
6446 * Adds an `event` listener that will be invoked a single
6447 * time then automatically removed.
6448 *
6449 * @param {String} event
6450 * @param {Function} fn
6451 * @return {Emitter}
6452 * @api public
6453 */
6454
6455Emitter.prototype.once = function(event, fn){
6456  var self = this;
6457  this._callbacks = this._callbacks || {};
6458
6459  function on() {
6460    self.off(event, on);
6461    fn.apply(this, arguments);
6462  }
6463
6464  on.fn = fn;
6465  this.on(event, on);
6466  return this;
6467};
6468
6469/**
6470 * Remove the given callback for `event` or all
6471 * registered callbacks.
6472 *
6473 * @param {String} event
6474 * @param {Function} fn
6475 * @return {Emitter}
6476 * @api public
6477 */
6478
6479Emitter.prototype.off =
6480Emitter.prototype.removeListener =
6481Emitter.prototype.removeAllListeners =
6482Emitter.prototype.removeEventListener = function(event, fn){
6483  this._callbacks = this._callbacks || {};
6484
6485  // all
6486  if (0 == arguments.length) {
6487    this._callbacks = {};
6488    return this;
6489  }
6490
6491  // specific event
6492  var callbacks = this._callbacks[event];
6493  if (!callbacks) return this;
6494
6495  // remove all handlers
6496  if (1 == arguments.length) {
6497    delete this._callbacks[event];
6498    return this;
6499  }
6500
6501  // remove specific handler
6502  var cb;
6503  for (var i = 0; i < callbacks.length; i++) {
6504    cb = callbacks[i];
6505    if (cb === fn || cb.fn === fn) {
6506      callbacks.splice(i, 1);
6507      break;
6508    }
6509  }
6510  return this;
6511};
6512
6513/**
6514 * Emit `event` with the given args.
6515 *
6516 * @param {String} event
6517 * @param {Mixed} ...
6518 * @return {Emitter}
6519 */
6520
6521Emitter.prototype.emit = function(event){
6522  this._callbacks = this._callbacks || {};
6523  var args = [].slice.call(arguments, 1)
6524    , callbacks = this._callbacks[event];
6525
6526  if (callbacks) {
6527    callbacks = callbacks.slice(0);
6528    for (var i = 0, len = callbacks.length; i < len; ++i) {
6529      callbacks[i].apply(this, args);
6530    }
6531  }
6532
6533  return this;
6534};
6535
6536/**
6537 * Return array of callbacks for `event`.
6538 *
6539 * @param {String} event
6540 * @return {Array}
6541 * @api public
6542 */
6543
6544Emitter.prototype.listeners = function(event){
6545  this._callbacks = this._callbacks || {};
6546  return this._callbacks[event] || [];
6547};
6548
6549/**
6550 * Check if this emitter has `event` handlers.
6551 *
6552 * @param {String} event
6553 * @return {Boolean}
6554 * @api public
6555 */
6556
6557Emitter.prototype.hasListeners = function(event){
6558  return !! this.listeners(event).length;
6559};
6560
6561
6562/***/ }),
6563/* 45 */
6564/***/ (function(module, exports, __webpack_require__) {
6565
6566"use strict";
6567
6568
6569var _typeof2 = __webpack_require__(6);
6570
6571var _typeof3 = _interopRequireDefault(_typeof2);
6572
6573function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
6574
6575var util = __webpack_require__(2);
6576var Component = __webpack_require__(16);
6577var TimeStep = __webpack_require__(66);
6578var DateUtil = __webpack_require__(36);
6579var moment = __webpack_require__(9);
6580
6581/**
6582 * A horizontal time axis
6583 * @param {{dom: Object, domProps: Object, emitter: Emitter, range: Range}} body
6584 * @param {Object} [options]        See TimeAxis.setOptions for the available
6585 *                                  options.
6586 * @constructor TimeAxis
6587 * @extends Component
6588 */
6589function TimeAxis(body, options) {
6590  this.dom = {
6591    foreground: null,
6592    lines: [],
6593    majorTexts: [],
6594    minorTexts: [],
6595    redundant: {
6596      lines: [],
6597      majorTexts: [],
6598      minorTexts: []
6599    }
6600  };
6601  this.props = {
6602    range: {
6603      start: 0,
6604      end: 0,
6605      minimumStep: 0
6606    },
6607    lineTop: 0
6608  };
6609
6610  this.defaultOptions = {
6611    orientation: {
6612      axis: 'bottom'
6613    }, // axis orientation: 'top' or 'bottom'
6614    showMinorLabels: true,
6615    showMajorLabels: true,
6616    maxMinorChars: 7,
6617    format: TimeStep.FORMAT,
6618    moment: moment,
6619    timeAxis: null
6620  };
6621  this.options = util.extend({}, this.defaultOptions);
6622
6623  this.body = body;
6624
6625  // create the HTML DOM
6626  this._create();
6627
6628  this.setOptions(options);
6629}
6630
6631TimeAxis.prototype = new Component();
6632
6633/**
6634 * Set options for the TimeAxis.
6635 * Parameters will be merged in current options.
6636 * @param {Object} options  Available options:
6637 *                          {string} [orientation.axis]
6638 *                          {boolean} [showMinorLabels]
6639 *                          {boolean} [showMajorLabels]
6640 */
6641TimeAxis.prototype.setOptions = function (options) {
6642  if (options) {
6643    // copy all options that we know
6644    util.selectiveExtend(['showMinorLabels', 'showMajorLabels', 'maxMinorChars', 'hiddenDates', 'timeAxis', 'moment', 'rtl'], this.options, options);
6645
6646    // deep copy the format options
6647    util.selectiveDeepExtend(['format'], this.options, options);
6648
6649    if ('orientation' in options) {
6650      if (typeof options.orientation === 'string') {
6651        this.options.orientation.axis = options.orientation;
6652      } else if ((0, _typeof3['default'])(options.orientation) === 'object' && 'axis' in options.orientation) {
6653        this.options.orientation.axis = options.orientation.axis;
6654      }
6655    }
6656
6657    // apply locale to moment.js
6658    // TODO: not so nice, this is applied globally to moment.js
6659    if ('locale' in options) {
6660      if (typeof moment.locale === 'function') {
6661        // moment.js 2.8.1+
6662        moment.locale(options.locale);
6663      } else {
6664        moment.lang(options.locale);
6665      }
6666    }
6667  }
6668};
6669
6670/**
6671 * Create the HTML DOM for the TimeAxis
6672 */
6673TimeAxis.prototype._create = function () {
6674  this.dom.foreground = document.createElement('div');
6675  this.dom.background = document.createElement('div');
6676
6677  this.dom.foreground.className = 'vis-time-axis vis-foreground';
6678  this.dom.background.className = 'vis-time-axis vis-background';
6679};
6680
6681/**
6682 * Destroy the TimeAxis
6683 */
6684TimeAxis.prototype.destroy = function () {
6685  // remove from DOM
6686  if (this.dom.foreground.parentNode) {
6687    this.dom.foreground.parentNode.removeChild(this.dom.foreground);
6688  }
6689  if (this.dom.background.parentNode) {
6690    this.dom.background.parentNode.removeChild(this.dom.background);
6691  }
6692
6693  this.body = null;
6694};
6695
6696/**
6697 * Repaint the component
6698 * @return {boolean} Returns true if the component is resized
6699 */
6700TimeAxis.prototype.redraw = function () {
6701  var props = this.props;
6702  var foreground = this.dom.foreground;
6703  var background = this.dom.background;
6704
6705  // determine the correct parent DOM element (depending on option orientation)
6706  var parent = this.options.orientation.axis == 'top' ? this.body.dom.top : this.body.dom.bottom;
6707  var parentChanged = foreground.parentNode !== parent;
6708
6709  // calculate character width and height
6710  this._calculateCharSize();
6711
6712  // TODO: recalculate sizes only needed when parent is resized or options is changed
6713  var showMinorLabels = this.options.showMinorLabels && this.options.orientation.axis !== 'none';
6714  var showMajorLabels = this.options.showMajorLabels && this.options.orientation.axis !== 'none';
6715
6716  // determine the width and height of the elemens for the axis
6717  props.minorLabelHeight = showMinorLabels ? props.minorCharHeight : 0;
6718  props.majorLabelHeight = showMajorLabels ? props.majorCharHeight : 0;
6719  props.height = props.minorLabelHeight + props.majorLabelHeight;
6720  props.width = foreground.offsetWidth;
6721
6722  props.minorLineHeight = this.body.domProps.root.height - props.majorLabelHeight - (this.options.orientation.axis == 'top' ? this.body.domProps.bottom.height : this.body.domProps.top.height);
6723  props.minorLineWidth = 1; // TODO: really calculate width
6724  props.majorLineHeight = props.minorLineHeight + props.majorLabelHeight;
6725  props.majorLineWidth = 1; // TODO: really calculate width
6726
6727  //  take foreground and background offline while updating (is almost twice as fast)
6728  var foregroundNextSibling = foreground.nextSibling;
6729  var backgroundNextSibling = background.nextSibling;
6730  foreground.parentNode && foreground.parentNode.removeChild(foreground);
6731  background.parentNode && background.parentNode.removeChild(background);
6732
6733  foreground.style.height = this.props.height + 'px';
6734
6735  this._repaintLabels();
6736
6737  // put DOM online again (at the same place)
6738  if (foregroundNextSibling) {
6739    parent.insertBefore(foreground, foregroundNextSibling);
6740  } else {
6741    parent.appendChild(foreground);
6742  }
6743  if (backgroundNextSibling) {
6744    this.body.dom.backgroundVertical.insertBefore(background, backgroundNextSibling);
6745  } else {
6746    this.body.dom.backgroundVertical.appendChild(background);
6747  }
6748  return this._isResized() || parentChanged;
6749};
6750
6751/**
6752 * Repaint major and minor text labels and vertical grid lines
6753 * @private
6754 */
6755TimeAxis.prototype._repaintLabels = function () {
6756  var orientation = this.options.orientation.axis;
6757
6758  // calculate range and step (step such that we have space for 7 characters per label)
6759  var start = util.convert(this.body.range.start, 'Number');
6760  var end = util.convert(this.body.range.end, 'Number');
6761  var timeLabelsize = this.body.util.toTime((this.props.minorCharWidth || 10) * this.options.maxMinorChars).valueOf();
6762  var minimumStep = timeLabelsize - DateUtil.getHiddenDurationBefore(this.options.moment, this.body.hiddenDates, this.body.range, timeLabelsize);
6763  minimumStep -= this.body.util.toTime(0).valueOf();
6764
6765  var step = new TimeStep(new Date(start), new Date(end), minimumStep, this.body.hiddenDates, this.options);
6766  step.setMoment(this.options.moment);
6767  if (this.options.format) {
6768    step.setFormat(this.options.format);
6769  }
6770  if (this.options.timeAxis) {
6771    step.setScale(this.options.timeAxis);
6772  }
6773  this.step = step;
6774
6775  // Move all DOM elements to a "redundant" list, where they
6776  // can be picked for re-use, and clear the lists with lines and texts.
6777  // At the end of the function _repaintLabels, left over elements will be cleaned up
6778  var dom = this.dom;
6779  dom.redundant.lines = dom.lines;
6780  dom.redundant.majorTexts = dom.majorTexts;
6781  dom.redundant.minorTexts = dom.minorTexts;
6782  dom.lines = [];
6783  dom.majorTexts = [];
6784  dom.minorTexts = [];
6785
6786  var current; // eslint-disable-line no-unused-vars
6787  var next;
6788  var x;
6789  var xNext;
6790  var isMajor;
6791  var nextIsMajor; // eslint-disable-line no-unused-vars
6792  var showMinorGrid;
6793  var width = 0,
6794      prevWidth;
6795  var line;
6796  var labelMinor;
6797  var xFirstMajorLabel = undefined;
6798  var count = 0;
6799  var MAX = 1000;
6800  var className;
6801
6802  step.start();
6803  next = step.getCurrent();
6804  xNext = this.body.util.toScreen(next);
6805  while (step.hasNext() && count < MAX) {
6806    count++;
6807
6808    isMajor = step.isMajor();
6809    className = step.getClassName();
6810    labelMinor = step.getLabelMinor();
6811
6812    current = next;
6813    x = xNext;
6814
6815    step.next();
6816    next = step.getCurrent();
6817    nextIsMajor = step.isMajor();
6818    xNext = this.body.util.toScreen(next);
6819
6820    prevWidth = width;
6821    width = xNext - x;
6822    switch (step.scale) {
6823      case 'week':
6824        showMinorGrid = true;break;
6825      default:
6826        showMinorGrid = width >= prevWidth * 0.4;break; // prevent displaying of the 31th of the month on a scale of 5 days
6827    }
6828
6829    if (this.options.showMinorLabels && showMinorGrid) {
6830      var label = this._repaintMinorText(x, labelMinor, orientation, className);
6831      label.style.width = width + 'px'; // set width to prevent overflow
6832    }
6833
6834    if (isMajor && this.options.showMajorLabels) {
6835      if (x > 0) {
6836        if (xFirstMajorLabel == undefined) {
6837          xFirstMajorLabel = x;
6838        }
6839        label = this._repaintMajorText(x, step.getLabelMajor(), orientation, className);
6840      }
6841      line = this._repaintMajorLine(x, width, orientation, className);
6842    } else {
6843      // minor line
6844      if (showMinorGrid) {
6845        line = this._repaintMinorLine(x, width, orientation, className);
6846      } else {
6847        if (line) {
6848          // adjust the width of the previous grid
6849          line.style.width = parseInt(line.style.width) + width + 'px';
6850        }
6851      }
6852    }
6853  }
6854
6855  if (count === MAX && !warnedForOverflow) {
6856    console.warn('Something is wrong with the Timeline scale. Limited drawing of grid lines to ' + MAX + ' lines.');
6857    warnedForOverflow = true;
6858  }
6859
6860  // create a major label on the left when needed
6861  if (this.options.showMajorLabels) {
6862    var leftTime = this.body.util.toTime(0),
6863        leftText = step.getLabelMajor(leftTime),
6864        widthText = leftText.length * (this.props.majorCharWidth || 10) + 10; // upper bound estimation
6865
6866    if (xFirstMajorLabel == undefined || widthText < xFirstMajorLabel) {
6867      this._repaintMajorText(0, leftText, orientation, className);
6868    }
6869  }
6870
6871  // Cleanup leftover DOM elements from the redundant list
6872  util.forEach(this.dom.redundant, function (arr) {
6873    while (arr.length) {
6874      var elem = arr.pop();
6875      if (elem && elem.parentNode) {
6876        elem.parentNode.removeChild(elem);
6877      }
6878    }
6879  });
6880};
6881
6882/**
6883 * Create a minor label for the axis at position x
6884 * @param {number} x
6885 * @param {string} text
6886 * @param {string} orientation   "top" or "bottom" (default)
6887 * @param {string} className
6888 * @return {Element} Returns the HTML element of the created label
6889 * @private
6890 */
6891TimeAxis.prototype._repaintMinorText = function (x, text, orientation, className) {
6892  // reuse redundant label
6893  var label = this.dom.redundant.minorTexts.shift();
6894
6895  if (!label) {
6896    // create new label
6897    var content = document.createTextNode('');
6898    label = document.createElement('div');
6899    label.appendChild(content);
6900    this.dom.foreground.appendChild(label);
6901  }
6902  this.dom.minorTexts.push(label);
6903  label.innerHTML = text;
6904
6905  label.style.top = orientation == 'top' ? this.props.majorLabelHeight + 'px' : '0';
6906
6907  if (this.options.rtl) {
6908    label.style.left = "";
6909    label.style.right = x + 'px';
6910  } else {
6911    label.style.left = x + 'px';
6912  }
6913  label.className = 'vis-text vis-minor ' + className;
6914  //label.title = title;  // TODO: this is a heavy operation
6915
6916  return label;
6917};
6918
6919/**
6920 * Create a Major label for the axis at position x
6921 * @param {number} x
6922 * @param {string} text
6923 * @param {string} orientation   "top" or "bottom" (default)
6924 * @param {string} className
6925 * @return {Element} Returns the HTML element of the created label
6926 * @private
6927 */
6928TimeAxis.prototype._repaintMajorText = function (x, text, orientation, className) {
6929  // reuse redundant 
6929label
6930  var label = this.dom.redundant.majorTexts.shift();
6931
6932  if (!label) {
6933    // create label
6934    var content = document.createElement('div');
6935    label = document.createElement('div');
6936    label.appendChild(content);
6937    this.dom.foreground.appendChild(label);
6938  }
6939
6940  label.childNodes[0].innerHTML = text;
6941  label.className = 'vis-text vis-major ' + className;
6942  //label.title = title; // TODO: this is a heavy operation
6943
6944  label.style.top = orientation == 'top' ? '0' : this.props.minorLabelHeight + 'px';
6945  if (this.options.rtl) {
6946    label.style.left = "";
6947    label.style.right = x + 'px';
6948  } else {
6949    label.style.left = x + 'px';
6950  }
6951
6952  this.dom.majorTexts.push(label);
6953  return label;
6954};
6955
6956/**
6957 * Create a minor line for the axis at position x
6958 * @param {number} x
6959 * @param {number} width
6960 * @param {string} orientation   "top" or "bottom" (default)
6961 * @param {string} className
6962 * @return {Element} Returns the created line
6963 * @private
6964 */
6965TimeAxis.prototype._repaintMinorLine = function (x, width, orientation, className) {
6966  // reuse redundant line
6967  var line = this.dom.redundant.lines.shift();
6968  if (!line) {
6969    // create vertical line
6970    line = document.createElement('div');
6971    this.dom.background.appendChild(line);
6972  }
6973  this.dom.lines.push(line);
6974
6975  var props = this.props;
6976  if (orientation == 'top') {
6977    line.style.top = props.majorLabelHeight + 'px';
6978  } else {
6979    line.style.top = this.body.domProps.top.height + 'px';
6980  }
6981  line.style.height = props.minorLineHeight + 'px';
6982  if (this.options.rtl) {
6983    line.style.left = "";
6984    line.style.right = x - props.minorLineWidth / 2 + 'px';
6985    line.className = 'vis-grid vis-vertical-rtl vis-minor ' + className;
6986  } else {
6987    line.style.left = x - props.minorLineWidth / 2 + 'px';
6988    line.className = 'vis-grid vis-vertical vis-minor ' + className;
6989  }
6990  line.style.width = width + 'px';
6991
6992  return line;
6993};
6994
6995/**
6996 * Create a Major line for the axis at position x
6997 * @param {number} x
6998 * @param {number} width
6999 * @param {string} orientation   "top" or "bottom" (default)
7000 * @param {string} className
7001 * @return {Element} Returns the created line
7002 * @private
7003 */
7004TimeAxis.prototype._repaintMajorLine = function (x, width, orientation, className) {
7005  // reuse redundant line
7006  var line = this.dom.redundant.lines.shift();
7007  if (!line) {
7008    // create vertical line
7009    line = document.createElement('div');
7010    this.dom.background.appendChild(line);
7011  }
7012  this.dom.lines.push(line);
7013
7014  var props = this.props;
7015  if (orientation == 'top') {
7016    line.style.top = '0';
7017  } else {
7018    line.style.top = this.body.domProps.top.height + 'px';
7019  }
7020
7021  if (this.options.rtl) {
7022    line.style.left = "";
7023    line.style.right = x - props.majorLineWidth / 2 + 'px';
7024    line.className = 'vis-grid vis-vertical-rtl vis-major ' + className;
7025  } else {
7026    line.style.left = x - props.majorLineWidth / 2 + 'px';
7027    line.className = 'vis-grid vis-vertical vis-major ' + className;
7028  }
7029
7030  line.style.height = props.majorLineHeight + 'px';
7031  line.style.width = width + 'px';
7032
7033  return line;
7034};
7035
7036/**
7037 * Determine the size of text on the axis (both major and minor axis).
7038 * The size is calculated only once and then cached in this.props.
7039 * @private
7040 */
7041TimeAxis.prototype._calculateCharSize = function () {
7042  // Note: We calculate char size with every redraw. Size may change, for
7043  // example when any of the timelines parents had display:none for example.
7044
7045  // determine the char width and height on the minor axis
7046  if (!this.dom.measureCharMinor) {
7047    this.dom.measureCharMinor = document.createElement('DIV');
7048    this.dom.measureCharMinor.className = 'vis-text vis-minor vis-measure';
7049    this.dom.measureCharMinor.style.position = 'absolute';
7050
7051    this.dom.measureCharMinor.appendChild(document.createTextNode('0'));
7052    this.dom.foreground.appendChild(this.dom.measureCharMinor);
7053  }
7054  this.props.minorCharHeight = this.dom.measureCharMinor.clientHeight;
7055  this.props.minorCharWidth = this.dom.measureCharMinor.clientWidth;
7056
7057  // determine the char width and height on the major axis
7058  if (!this.dom.measureCharMajor) {
7059    this.dom.measureCharMajor = document.createElement('DIV');
7060    this.dom.measureCharMajor.className = 'vis-text vis-major vis-measure';
7061    this.dom.measureCharMajor.style.position = 'absolute';
7062
7063    this.dom.measureCharMajor.appendChild(document.createTextNode('0'));
7064    this.dom.foreground.appendChild(this.dom.measureCharMajor);
7065  }
7066  this.props.majorCharHeight = this.dom.measureCharMajor.clientHeight;
7067  this.props.majorCharWidth = this.dom.measureCharMajor.clientWidth;
7068};
7069
7070var warnedForOverflow = false;
7071
7072module.exports = TimeAxis;
7073
7074/***/ }),
7075/* 46 */
7076/***/ (function(module, exports, __webpack_require__) {
7077
7078"use strict";
7079
7080
7081var Hammer = __webpack_require__(10);
7082var util = __webpack_require__(2);
7083var Component = __webpack_require__(16);
7084var moment = __webpack_require__(9);
7085var locales = __webpack_require__(98);
7086
7087/**
7088 * A custom time bar
7089 * @param {{range: Range, dom: Object}} body
7090 * @param {Object} [options]        Available parameters:
7091 *                                  {number | string} id
7092 *                                  {string} locales
7093 *                                  {string} locale
7094 * @constructor CustomTime
7095 * @extends Component
7096 */
7097function CustomTime(body, options) {
7098  this.body = body;
7099
7100  // default options
7101  this.defaultOptions = {
7102    moment: moment,
7103    locales: locales,
7104    locale: 'en',
7105    id: undefined,
7106    title: undefined
7107  };
7108  this.options = util.extend({}, this.defaultOptions);
7109
7110  if (options && options.time) {
7111    this.customTime = options.time;
7112  } else {
7113    this.customTime = new Date();
7114  }
7115
7116  this.eventParams = {}; // stores state parameters while dragging the bar
7117
7118  this.setOptions(options);
7119
7120  // create the DOM
7121  this._create();
7122}
7123
7124CustomTime.prototype = new Component();
7125
7126/**
7127 * Set options for the component. Options will be merged in current options.
7128 * @param {Object} options  Available parameters:
7129 *                                  {number | string} id
7130 *                                  {string} locales
7131 *                                  {string} locale
7132 */
7133CustomTime.prototype.setOptions = function (options) {
7134  if (options) {
7135    // copy all options that we know
7136    util.selectiveExtend(['moment', 'locale', 'locales', 'id'], this.options, options);
7137  }
7138};
7139
7140/**
7141 * Create the DOM for the custom time
7142 * @private
7143 */
7144CustomTime.prototype._create = function () {
7145  var bar = document.createElement('div');
7146  bar['custom-time'] = this;
7147  bar.className = 'vis-custom-time ' + (this.options.id || '');
7148  bar.style.position = 'absolute';
7149  bar.style.top = '0px';
7150  bar.style.height = '100%';
7151  this.bar = bar;
7152
7153  var drag = document.createElement('div');
7154  drag.style.position = 'relative';
7155  drag.style.top = '0px';
7156  drag.style.left = '-10px';
7157  drag.style.height = '100%';
7158  drag.style.width = '20px';
7159
7160  /**
7161   *
7162   * @param {WheelEvent} e
7163   */
7164  function onMouseWheel(e) {
7165    this.body.range._onMouseWheel(e);
7166  }
7167
7168  if (drag.addEventListener) {
7169    // IE9, Chrome, Safari, Opera
7170    drag.addEventListener("mousewheel", onMouseWheel.bind(this), false);
7171    // Firefox
7172    drag.addEventListener("DOMMouseScroll", onMouseWheel.bind(this), false);
7173  } else {
7174    // IE 6/7/8
7175    drag.attachEvent("onmousewheel", onMouseWheel.bind(this));
7176  }
7177
7178  bar.appendChild(drag);
7179  // attach event listeners
7180  this.hammer = new Hammer(drag);
7181  this.hammer.on('panstart', this._onDragStart.bind(this));
7182  this.hammer.on('panmove', this._onDrag.bind(this));
7183  this.hammer.on('panend', this._onDragEnd.bind(this));
7184  this.hammer.get('pan').set({ threshold: 5, direction: Hammer.DIRECTION_HORIZONTAL });
7185};
7186
7187/**
7188 * Destroy the CustomTime bar
7189 */
7190CustomTime.prototype.destroy = function () {
7191  this.hide();
7192
7193  this.hammer.destroy();
7194  this.hammer = null;
7195
7196  this.body = null;
7197};
7198
7199/**
7200 * Repaint the component
7201 * @return {boolean} Returns true if the component is resized
7202 */
7203CustomTime.prototype.redraw = function () {
7204  var parent = this.body.dom.backgroundVertical;
7205  if (this.bar.parentNode != parent) {
7206    // attach to the dom
7207    if (this.bar.parentNode) {
7208      this.bar.parentNode.removeChild(this.bar);
7209    }
7210    parent.appendChild(this.bar);
7211  }
7212
7213  var x = this.body.util.toScreen(this.customTime);
7214
7215  var locale = this.options.locales[this.options.locale];
7216  if (!locale) {
7217    if (!this.warned) {
7218      console.log('WARNING: options.locales[\'' + this.options.locale + '\'] not found. See http://visjs.org/docs/timeline/#Localization');
7219      this.warned = true;
7220    }
7221    locale = this.options.locales['en']; // fall back on english when not available
7222  }
7223
7224  var title = this.options.title;
7225  // To hide the title completely use empty string ''.
7226  if (title === undefined) {
7227    title = locale.time + ': ' + this.options.moment(this.customTime).format('dddd, MMMM Do YYYY, H:mm:ss');
7228    title = title.charAt(0).toUpperCase() + title.substring(1);
7229  } else if (typeof title === "function") {
7230    title = title.call(this.customTime);
7231  }
7232
7233  this.bar.style.left = x + 'px';
7234  this.bar.title = title;
7235
7236  return false;
7237};
7238
7239/**
7240 * Remove the CustomTime from the DOM
7241 */
7242CustomTime.prototype.hide = function () {
7243  // remove the line from the DOM
7244  if (this.bar.parentNode) {
7245    this.bar.parentNode.removeChild(this.bar);
7246  }
7247};
7248
7249/**
7250 * Set custom time.
7251 * @param {Date | number | string} time
7252 */
7253CustomTime.prototype.setCustomTime = function (time) {
7254  this.customTime = util.convert(time, 'Date');
7255  this.redraw();
7256};
7257
7258/**
7259 * Retrieve the current custom time.
7260 * @return {Date} customTime
7261 */
7262CustomTime.prototype.getCustomTime = function () {
7263  return new Date(this.customTime.valueOf());
7264};
7265
7266/**
7267  * Set custom title.
7268  * @param {Date | number | string} title
7269  */
7270CustomTime.prototype.setCustomTitle = function (title) {
7271  this.options.title = title;
7272};
7273
7274/**
7275 * Start moving horizontally
7276 * @param {Event} event
7277 * @private
7278 */
7279CustomTime.prototype._onDragStart = function (event) {
7280  this.eventParams.dragging = true;
7281  this.eventParams.customTime = this.customTime;
7282
7283  event.stopPropagation();
7284};
7285
7286/**
7287 * Perform moving operating.
7288 * @param {Event} event
7289 * @private
7290 */
7291CustomTime.prototype._onDrag = function (event) {
7292  if (!this.eventParams.dragging) return;
7293
7294  var x = this.body.util.toScreen(this.eventParams.customTime) + event.deltaX;
7295  var time = this.body.util.toTime(x);
7296
7297  this.setCustomTime(time);
7298
7299  // fire a timechange event
7300  this.body.emitter.emit('timechange', {
7301    id: this.options.id,
7302    time: new Date(this.customTime.valueOf()),
7303    event: event
7304  });
7305
7306  event.stopPropagation();
7307};
7308
7309/**
7310 * Stop moving operating.
7311 * @param {Event} event
7312 * @private
7313 */
7314CustomTime.prototype._onDragEnd = function (event) {
7315  if (!this.eventParams.dragging) return;
7316
7317  // fire a timechanged event
7318  this.body.emitter.emit('timechanged', {
7319    id: this.options.id,
7320    time: new Date(this.customTime.valueOf()),
7321    event: event
7322  });
7323
7324  event.stopPropagation();
7325};
7326
7327/**
7328 * Find a custom time from an event target:
7329 * searches for the attribute 'custom-time' in the event target's element tree
7330 * @param {Event} event
7331 * @return {CustomTime | null} customTime
7332 */
7333CustomTime.customTimeFromTarget = function (event) {
7334  var target = event.target;
7335  while (target) {
7336    if (target.hasOwnProperty('custom-time')) {
7337      return target['custom-time'];
7338    }
7339    target = target.parentNode;
7340  }
7341
7342  return null;
7343};
7344
7345module.exports = CustomTime;
7346
7347/***/ }),
7348/* 47 */
7349/***/ (function(module, exports, __webpack_require__) {
7350
7351"use strict";
7352
7353
7354Object.defineProperty(exports, "__esModule", {
7355  value: true
7356});
7357
7358var _classCallCheck2 = __webpack_require__(0);
7359
7360var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
7361
7362var _createClass2 = __webpack_require__(1);
7363
7364var _createClass3 = _interopRequireDefault(_createClass2);
7365
7366function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
7367
7368var util = __webpack_require__(2);
7369
7370var Label = __webpack_require__(117)['default'];
7371var ComponentUtil = __webpack_require__(48)['default'];
7372var Box = __webpack_require__(193)['default'];
7373var Circle = __webpack_require__(200)['default'];
7374var CircularImage = __webpack_require__(201)['default'];
7375var Database = __webpack_require__(202)['default'];
7376var Diamond = __webpack_require__(203)['default'];
7377var Dot = __webpack_require__(204)['default'];
7378var Ellipse = __webpack_require__(205)['default'];
7379var Icon = __webpack_require__(206)['default'];
7380var Image = __webpack_require__(207)['default'];
7381var Square = __webpack_require__(208)['default'];
7382var Hexagon = __webpack_require__(209)['default'];
7383var Star = __webpack_require__(210)['default'];
7384var Text = __webpack_require__(211)['default'];
7385var Triangle = __webpack_require__(212)['default'];
7386var TriangleDown = __webpack_require__(213)['default'];
7387
7388var _require = __webpack_require__(15),
7389    printStyle = _require.printStyle;
7390
7391/**
7392 * A node. A node can be connected to other nodes via one or multiple edges.
7393 */
7394
7395
7396var Node = function () {
7397  /**
7398   *
7399   * @param {object} options An object containing options for the node. All
7400   *                            options are optional, except for the id.
7401   *                              {number} id     Id of the node. Required
7402   *                              {string} label  Text label for the node
7403   *                              {number} x      Horizontal position of the node
7404   *                              {number} y      Vertical position of the node
7405   *                              {string} shape  Node shape
7406   *                              {string} image  An image url
7407   *                              {string} title  A title text, can be HTML
7408   *                              {anytype} group A group name or number
7409   *
7410   * @param {Object} body               Shared state of current network instance
7411   * @param {Network.Images} imagelist  A list with images. Only needed when the node has an image
7412   * @param {Groups} grouplist          A list with groups. Needed for retrieving group options
7413   * @param {Object} globalOptions      Current global node options; these serve as defaults for the node instance
7414   * @param {Object} defaultOptions     Global default options for nodes; note that this is also the prototype
7415   *                                    for parameter `globalOptions`.
7416   */
7417  function Node(options, body, imagelist, grouplist, globalOptions, defaultOptions) {
7418    (0, _classCallCheck3['default'])(this, Node);
7419
7420    this.options = util.bridgeObject(globalOptions);
7421    this.globalOptions = globalOptions;
7422    this.defaultOptions = defaultOptions;
7423    this.body = body;
7424
7425    this.edges = []; // all edges connected to this node
7426
7427    // set defaults for the options
7428    this.id = undefined;
7429    this.imagelist = imagelist;
7430    this.grouplist = grouplist;
7431
7432    // state options
7433    this.x = undefined;
7434    this.y = undefined;
7435    this.baseSize = this.options.size;
7436    this.baseFontSize = this.options.font.size;
7437    this.predefinedPosition = false; // used to check if initial fit should just take the range or approximate
7438    this.selected = false;
7439    this.hover = false;
7440
7441    this.labelModule = new Label(this.body, this.options, false /* Not edge label */);
7442    this.setOptions(options);
7443  }
7444
7445  /**
7446   * Attach a edge to the node
7447   * @param {Edge} edge
7448   */
7449
7450
7451  (0, _createClass3['default'])(Node, [{
7452    key: 'attachEdge',
7453    value: function attachEdge(edge) {
7454      if (this.edges.indexOf(edge) === -1) {
7455        this.edges.push(edge);
7456      }
7457    }
7458
7459    /**
7460     * Detach a edge from the node
7461     *
7462     * @param {Edge} edge
7463     */
7464
7465  }, {
7466    key: 'detachEdge',
7467    value: function detachEdge(edge) {
7468      var index = this.edges.indexOf(edge);
7469      if (index != -1) {
7470        this.edges.splice(index, 1);
7471      }
7472    }
7473
7474    /**
7475     * Set or overwrite options for the node
7476     *
7477     * @param {Object} options an object with options
7478     * @returns {null|boolean}
7479     */
7480
7481  }, {
7482    key: 'setOptions',
7483    value: function setOptions(options) {
7484      var currentShape = this.options.shape;
7485      if (!options) {
7486        return; // Note that the return value will be 'undefined'! This is OK.
7487      }
7488
7489      // basic options
7490      if (options.id !== undefined) {
7491        this.id = options.id;
7492      }
7493
7494      if (this.id === undefined) {
7495        throw new Error("Node must have an id");
7496      }
7497
7498      Node.checkMass(options, this.id);
7499
7500      // set these options locally
7501      // clear x and y positions
7502      if (options.x !== undefined) {
7503        if (options.x === null) {
7504          this.x = undefined;this.predefinedPosition = false;
7505        } else {
7506          this.x = parseInt(options.x);this.predefinedPosition = true;
7507        }
7508      }
7509      if (options.y !== undefined) {
7510        if (options.y === null) {
7511          this.y = undefined;this.predefinedPosition = false;
7512        } else {
7513          this.y = parseInt(options.y);this.predefinedPosition = true;
7514        }
7515      }
7516      if (options.size !== undefined) {
7517        this.baseSize = options.size;
7518      }
7519      if (options.value !== undefined) {
7520        options.value = parseFloat(options.value);
7521      }
7522
7523      // this transforms all shorthands into fully defined options
7524      Node.parseOptions(this.options, options, true, this.globalOptions, this.grouplist);
7525
7526      var pile = [options, this.options, this.defaultOptions];
7527      this.chooser = ComponentUtil.choosify('node', pile);
7528
7529      this._load_images();
7530      this.updateLabelModule(options);
7531      this.updateShape(currentShape);
7532
7533      return options.hidden !== undefined || options.physics !== undefined;
7534    }
7535
7536    /**
7537     * Load the images from the options, for the nodes that need them.
7538     *
7539     * TODO: The imageObj members should be moved to CircularImageBase.
7540     *       It's the only place where they are required.
7541     *
7542     * @private
7543     */
7544
7545  }, {
7546    key: '_load_images',
7547    value: function _load_images() {
7548      // Don't bother loading for nodes without images
7549      if (this.options.shape !== 'circularImage' && this.options.shape !== 'image') {
7550        return;
7551      }
7552
7553      if (this.options.image === undefined) {
7554        throw new Error("Option image must be defined for node type '" + this.options.shape + "'");
7555      }
7556
7557      if (this.imagelist === undefined) {
7558        throw new Error("Internal Error: No images provided");
7559      }
7560
7561      if (typeof this.options.image === 'string') {
7562        this.imageObj = this.imagelist.load(this.options.image, this.options.brokenImage, this.id);
7563      } else {
7564        if (this.options.image.unselected === undefined) {
7565          throw new Error("No unselected image provided");
7566        }
7567
7568        this.imageObj = this.imagelist.load(this.options.image.unselected, this.options.brokenImage, this.id);
7569
7570        if (this.options.image.selected !== undefined) {
7571          this.imageObjAlt = this.imagelist.load(this.options.image.selected, this.options.brokenImage, this.id);
7572        } else {
7573          this.imageObjAlt = undefined;
7574        }
7575      }
7576    }
7577
7578    /**
7579     * Copy group option values into the node options.
7580     *
7581     * The group options override the global node options, so the copy of group options
7582     *  must happen *after* the global node options have been set.
7583     *
7584     * This method must also be called also if the global node options have changed and the group options did not.
7585     *
7586     * @param {Object} parentOptions
7587     * @param {Object} newOptions  new values for the options, currently only passed in for check
7588     * @param {Object} groupList
7589     */
7590
7591  }, {
7592    key: 'getFormattingValues',
7593
7594
7595    /**
7596     *
7597     * @returns {{color: *, borderWidth: *, borderColor: *, size: *, borderDashes: (boolean|Array|allOptions.nodes.shapeProperties.borderDashes|{boolean, array}), borderRadius: (number|allOptions.nodes.shapeProperties.borderRadius|{number}|Array), shadow: *, shadowColor: *, shadowSize: *, shadowX: *, shadowY: *}}
7598     */
7599    value: function getFormattingValues() {
7600      var values = {
7601        color: this.options.color.background,
7602        borderWidth: this.options.borderWidth,
7603        borderColor: this.options.color.border,
7604        size: this.options.size,
7605        borderDashes: this.options.shapeProperties.borderDashes,
7606        borderRadius: this.options.shapeProperties.borderRadius,
7607        shadow: this.options.shadow.enabled,
7608        shadowColor: this.options.shadow.color,
7609        shadowSize: this.options.shadow.size,
7610        shadowX: this.options.shadow.x,
7611        shadowY: this.options.shadow.y
7612      };
7613      if (this.selected || this.hover) {
7614        if (this.chooser === true) {
7615          if (this.selected) {
7616            values.borderWidth *= 2;
7617            values.color = this.options.color.highlight.background;
7618            values.borderColor = this.options.color.highlight.border;
7619            values.shadow = this.options.shadow.enabled;
7620          } else if (this.hover) {
7621            values.color = this.options.color.hover.background;
7622            values.borderColor = this.options.color.hover.border;
7623            values.shadow = this.options.shadow.enabled;
7624          }
7625        } else if (typeof this.chooser === 'function') {
7626          this.chooser(values, this.options.id, this.selected, this.hover);
7627          if (values.shadow === false) {
7628            if (values.shadowColor !== this.options.shadow.color || values.shadowSize !== this.options.shadow.size || values.shadowX !== this.options.shadow.x || values.shadowY !== this.options.shadow.y) {
7629              values.shadow = true;
7630            }
7631          }
7632        }
7633      } else {
7634        values.shadow = this.options.shadow.enabled;
7635      }
7636      return values;
7637    }
7638
7639    /**
7640     *
7641     * @param {Object} options
7642     */
7643
7644  }, {
7645    key: 'updateLabelModule',
7646    value: function updateLabelModule(options) {
7647      if (this.options.label === undefined || this.options.label === null) {
7648        this.options.label = '';
7649      }
7650
7651      Node.updateGroupOptions(this.options, options, this.grouplist);
7652
7653      //
7654      // Note:The prototype chain for this.options is:
7655      //
7656      // this.options ->    NodesHandler.options    -> NodesHandler.defaultOptions
7657      //                 (also: this.globalOptions)
7658      //
7659      // Note that the prototypes are mentioned explicitly in the pile list below;
7660      // WE DON'T WANT THE ORDER OF THE PROTOTYPES!!!! At least, not for font handling of labels.
7661      // This is a good indication that the prototype usage of options is deficient.
7662      //
7663      var currentGroup = this.grouplist.get(this.options.group, false);
7664      var pile = [options, // new options
7665      this.options, // current node options, see comment above for prototype
7666      currentGroup, // group options, if any
7667      this.globalOptions, // Currently set global node options
7668      this.defaultOptions // Default global node options
7669      ];
7670      this.labelModule.update(this.options, pile);
7671
7672      if (this.labelModule.baseSize !== undefined) {
7673        this.baseFontSize = this.labelModule.baseSize;
7674      }
7675    }
7676
7677    /**
7678     *
7679     * @param {string} currentShape
7680     */
7681
7682  }, {
7683    key: 'updateShape',
7684    value: function updateShape(currentShape) {
7685      if (currentShape === this.options.shape && this.shape) {
7686        this.shape.setOptions(this.options, this.imageObj, this.imageObjAlt);
7687      } else {
7688        // choose draw method depending on the shape
7689        switch (this.options.shape) {
7690          case 'box':
7691            this.shape = new Box(this.options, this.body, this.labelModule);
7692            break;
7693          case 'circle':
7694            this.shape = new Circle(this.options, this.body, this.labelModule);
7695            break;
7696          case 'circularImage':
7697            this.shape = new CircularImage(this.options, this.body, this.labelModule, this.imageObj, this.imageObjAlt);
7698            break;
7699          case 'database':
7700            this.shape = new Database(this.options, this.body, this.labelModule);
7701            break;
7702          case 'diamond':
7703            this.shape = new Diamond(this.options, this.body, this.labelModule);
7704            break;
7705          case 'dot':
7706            this.shape = new Dot(this.options, this.body, this.labelModule);
7707            break;
7708          case 'ellipse':
7709            this.shape = new Ellipse(this.options, this.body, this.labelModule);
7710            break;
7711          case 'icon':
7712            this.shape = new Icon(this.options, this.body, this.labelModule);
7713            break;
7714          case 'image':
7715            this.shape = new Image(this.options, this.body, this.labelModule, this.imageObj, this.imageObjAlt);
7716            break;
7717          case 'square':
7718            this.shape = new Square(this.options, this.body, this.labelModule);
7719            break;
7720          case 'hexagon':
7721            this.shape = new Hexagon(this.options, this.body, this.labelModule);
7722            break;
7723          case 'star':
7724            this.shape = new Star(this.options, this.body, this.labelModule);
7725            break;
7726          case 'text':
7727            this.shape = new Text(this.options, this.body, this.labelModule);
7728            break;
7729          case 'triangle':
7730            this.shape = new Triangle(this.options, this.body, this.labelModule);
7731            break;
7732          case 'triangleDown':
7733            this.shape = new TriangleDown(this.options, this.body, this.labelModule);
7734            break;
7735          default:
7736            this.shape = new Ellipse(this.options, this.body, this.labelModule);
7737            break;
7738        }
7739      }
7740      this.needsRefresh();
7741    }
7742
7743    /**
7744     * select this node
7745     */
7746
7747  }, {
7748    key: 'select',
7749    value: function select() {
7750      this.selected = true;
7751      this.needsRefresh();
7752    }
7753
7754    /**
7755     * unselect this node
7756     */
7757
7758  }, {
7759    key: 'unselect',
7760    value: function unselect() {
7761      this.selected = false;
7762      this.needsRefresh();
7763    }
7764
7765    /**
7766     * Reset the calculated size of the node, forces it to recalculate its size
7767     */
7768
7769  }, {
7770    key: 'needsRefresh',
7771    value: function needsRefresh() {
7772      this.shape.refreshNeeded = true;
7773    }
7774
7775    /**
7776     * get the title of this node.
7777     * @return {string} title    The title of the node, or undefined when no title
7778     *                           has been set.
7779     */
7780
7781  }, {
7782    key: 'getTitle',
7783    value: function getTitle() {
7784      return this.options.title;
7785    }
7786
7787    /**
7788     * Calculate the distance to the border of the Node
7789     * @param {CanvasRenderingContext2D}   ctx
7790     * @param {number} angle        Angle in radians
7791     * @returns {number} distance   Distance to the border in pixels
7792     */
7793
7794  }, {
7795    key: 'distanceToBorder',
7796    value: function distanceToBorder(ctx, angle) {
7797      return this.shape.distanceToBorder(ctx, angle);
7798    }
7799
7800    /**
7801     * Check if this node has a fixed x and y position
7802     * @return {boolean}      true if fixed, false if not
7803     */
7804
7805  }, {
7806    key: 'isFixed',
7807    value: function isFixed() {
7808      return this.options.fixed.x && this.options.fixed.y;
7809    }
7810
7811    /**
7812     * check if this node is selecte
7813     * @return {boolean} selected   True if node is selected, else false
7814     */
7815
7816  }, {
7817    key: 'isSelected',
7818    value: function isSelected() {
7819      return this.selected;
7820    }
7821
7822    /**
7823     * Retrieve the value of the node. Can be undefined
7824     * @return {number} value
7825     */
7826
7827  }, {
7828    key: 'getValue',
7829    value: function getValue() {
7830      return this.options.value;
7831    }
7832
7833    /**
7834     * Get the current dimensions of the label
7835     *
7836     * @return {rect}
7837     */
7838
7839  }, {
7840    key: 'getLabelSize',
7841    value: function getLabelSize() {
7842      return this.labelModule.size();
7843    }
7844
7845    /**
7846     * Adjust the value range of the node. The node will adjust it's size
7847     * based on its value.
7848     * @param {number} min
7849     * @param {number} max
7850     * @param {number} total
7851     */
7852
7853  }, {
7854    key: 'setValueRange',
7855    value: function setValueRange(min, max, total) {
7856      if (this.options.value !== undefined) {
7857        var scale = this.options.scaling.customScalingFunction(min, max, total, this.options.value);
7858        var sizeDiff = this.options.scaling.max - this.options.scaling.min;
7859        if (this.options.scaling.label.enabled === true) {
7860          var fontDiff = this.options.scaling.label.max - this.options.scaling.label.min;
7861          this.options.font.size = this.options.scaling.label.min + scale * fontDiff;
7862        }
7863        this.options.size = this.options.scaling.min + scale * sizeDiff;
7864      } else {
7865        this.options.size = this.baseSize;
7866        this.options.font.size = this.baseFontSize;
7867      }
7868
7869      this.updateLabelModule();
7870    }
7871
7872    /**
7873     * Draw this node in the given canvas
7874     * The 2d context of a HTML canvas can be retrieved by canvas.getContext("2d");
7875     * @param {CanvasRenderingContext2D}   ctx
7876     */
7877
7878  }, {
7879    key: 'draw',
7880    value: function draw(ctx) {
7881      var values = this.getFormattingValues();
7882      this.shape.draw(ctx, this.x, this.y, this.selected, this.hover, values);
7883    }
7884
7885    /**
7886     * Update the bounding box of the shape
7887     * @param {CanvasRenderingContext2D}   ctx
7888     */
7889
7890  }, {
7891    key: 'updateBoundingBox',
7892    value: function updateBoundingBox(ctx) {
7893      this.shape.updateBoundingBox(this.x, this.y, ctx);
7894    }
7895
7896    /**
7897     * Recalculate the size of this node in the given canvas
7898     * The 2d context of a HTML canvas can be retrieved by canvas.getContext("2d");
7899     * @param {CanvasRenderingContext2D}   ctx
7900     */
7901
7902  }, {
7903    key: 'resize',
7904    value: function resize(ctx) {
7905      var values = this.getFormattingValues();
7906      this.shape.resize(ctx, this.selected, this.hover, values);
7907    }
7908
7909    /**
7910     * Determine all visual elements of this node instance, in which the given
7911     * point falls within the bounding shape.
7912     *
7913     * @param {point} point
7914     * @returns {Array.<nodeClickItem|nodeLabelClickItem>} list with the items which are on the point
7915     */
7916
7917  }, {
7918    key: 'getItemsOnPoint',
7919    value: function getItemsOnPoint(point) {
7920      var ret = [];
7921
7922      if (this.labelModule.visible()) {
7923        if (ComponentUtil.pointInRect(this.labelModule.getSize(), point)) {
7924          ret.push({ nodeId: this.id, labelId: 0 });
7925        }
7926      }
7927
7928      if (ComponentUtil.pointInRect(this.shape.boundingBox, point)) {
7929        ret.push({ nodeId: this.id });
7930      }
7931
7932      return ret;
7933    }
7934
7935    /**
7936     * Check if this object is overlapping with the provided object
7937     * @param {Object} obj   an object with parameters left, top, right, bottom
7938     * @return {boolean}     True if location is located on node
7939     */
7940
7941  }, {
7942    key: 'isOverlappingWith',
7943    value: function isOverlappingWith(obj) {
7944      return this.shape.left < obj.right && this.shape.left + this.shape.width > obj.left && this.shape.top < obj.bottom && this.shape.top + this.shape.height > obj.top;
7945    }
7946
7947    /**
7948     * Check if this object is overlapping with the provided object
7949     * @param {Object} obj   an object with parameters left, top, right, bottom
7950     * @return {boolean}     True if location is located on node
7951     */
7952
7953  }, {
7954    key: 'isBoundingBoxOverlappingWith',
7955    value: function isBoundingBoxOverlappingWith(obj) {
7956      return this.shape.boundingBox.left < obj.right && this.shape.boundingBox.right > obj.left && this.shape.boundingBox.top < obj.bottom && this.shape.boundingBox.bottom > obj.top;
7957    }
7958
7959    /**
7960    * Check valid values for mass
7961    *
7962    * The mass may not be negative or zero. If it is, reset to 1
7963    *
7964    * @param {object} options
7965    * @param {Node.id} id
7966     * @static
7967    */
7968
7969  }], [{
7970    key: 'updateGroupOptions',
7971    value: function updateGroupOptions(parentOptions, newOptions, groupList) {
7972      if (groupList === undefined) return; // No groups, nothing to do
7973
7974      var group = parentOptions.group;
7975
7976      // paranoia: the selected group is already merged into node options, check.
7977      if (newOptions !== undefined && newOptions.group !== undefined && group !== newOptions.group) {
7978        throw new Error("updateGroupOptions: group values in options don't match.");
7979      }
7980
7981      var hasGroup = typeof group === 'number' || typeof group === 'string' && group != '';
7982      if (!hasGroup) return; // current node has no group, no need to merge
7983
7984      var groupObj = groupList.get(group);
7985
7986      // Skip merging of group font options into parent; these are required to be distinct for labels
7987      // TODO: It might not be a good idea either to merge the rest of the options, investigate this. 
7988      util.selectiveNotDeepExtend(['font'], parentOptions, groupObj);
7989
7990      // the color object needs to be completely defined.
7991      // Since groups can partially overwrite the colors, we parse it again, just in case.
7992      parentOptions.color = util.parseColor(parentOptions.color);
7993    }
7994
7995    /**
7996     * This process all possible shorthands in the new options and makes sure that the parentOptions are fully defined.
7997     * Static so it can also be used by the handler.
7998     *
7999     * @param {Object} parentOptions
8000     * @param {Object} newOptions
8001     * @param {boolean} [allowDeletion=false]
8002     * @param {Object} [globalOptions={}]
8003     * @param {Object} [groupList]
8004     * @static
8005     */
8006
8007  }, {
8008    key: 'parseOptions',
8009    value: function parseOptions(parentOptions, newOptions) {
8010      var allowDeletion = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
8011      var globalOptions = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {};
8012      var groupList = arguments[4];
8013
8014
8015      var fields = ['color', 'fixed', 'shadow'];
8016      util.selectiveNotDeepExtend(fields, parentOptions, newOptions, allowDeletion);
8017
8018      Node.checkMass(newOptions);
8019
8020      // merge the shadow options into the parent.
8021      util.mergeOptions(parentOptions, newOptions, 'shadow', globalOptions);
8022
8023      // individual shape newOptions
8024      if (newOptions.color !== undefined && newOptions.color !== null) {
8025        var parsedColor = util.parseColor(newOptions.color);
8026        util.fillIfDefined(parentOptions.color, parsedColor);
8027      } else if (allowDeletion === true && newOptions.color === null) {
8028        parentOptions.color = util.bridgeObject(globalOptions.color); // set the object back to the global options
8029      }
8030
8031      // handle the fixed options
8032      if (newOptions.fixed !== undefined && newOptions.fixed !== null) {
8033        if (typeof newOptions.fixed === 'boolean') {
8034          parentOptions.fixed.x = newOptions.fixed;
8035          parentOptions.fixed.y = newOptions.fixed;
8036        } else {
8037          if (newOptions.fixed.x !== undefined && typeof newOptions.fixed.x === 'boolean') {
8038            parentOptions.fixed.x = newOptions.fixed.x;
8039          }
8040          if (newOptions.fixed.y !== undefined && typeof newOptions.fixed.y === 'boolean') {
8041            parentOptions.fixed.y = newOptions.fixed.y;
8042          }
8043        }
8044      }
8045
8046      if (allowDeletion === true && newOptions.font === null) {
8047        parentOptions.font = util.bridgeObject(globalOptions.font); // set the object back to the global options
8048      }
8049
8050      Node.updateGroupOptions(parentOptions, newOptions, groupList);
8051
8052      // handle the scaling options, specifically the label part
8053      if (newOptions.scaling !== undefined) {
8054        util.mergeOptions(parentOptions.scaling, newOptions.scaling, 'label', globalOptions.scaling);
8055      }
8056    }
8057  }, {
8058    key: 'checkMass',
8059    value: function checkMass(options, id) {
8060      if (options.mass !== undefined && options.mass <= 0) {
8061        var strId = '';
8062        if (id !== undefined) {
8063          strId = ' in node id: ' + id;
8064        }
8065        console.log('%cNegative or zero mass disallowed' + strId + ', setting mass to 1.', printStyle);
8066        options.mass = 1;
8067      }
8068    }
8069  }]);
8070  return Node;
8071}();
8072
8073exports['default'] = Node;
8074
8075/***/ }),
8076/* 48 */
8077/***/ (function(module, exports, __webpack_require__) {
8078
8079"use strict";
8080
8081
8082Object.defineProperty(exports, "__esModule", {
8083  value: true
8084});
8085
8086var _typeof2 = __webpack_require__(6);
8087
8088var _typeof3 = _interopRequireDefault(_typeof2);
8089
8090var _classCallCheck2 = __webpack_require__(0);
8091
8092var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
8093
8094var _createClass2 = __webpack_require__(1);
8095
8096var _createClass3 = _interopRequireDefault(_createClass2);
8097
8098function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
8099
8100/**
8101 * Definitions for param's in jsdoc.
8102 * These are more or less global within Network. Putting them here until I can figure out
8103 * where to really put them
8104 * 
8105 * @typedef {string|number} Id
8106 * @typedef {Id} NodeId
8107 * @typedef {Id} EdgeId
8108 * @typedef {Id} LabelId
8109 *
8110 * @typedef {{x: number, y: number}} point
8111 * @typedef {{left: number, top: number, width: number, height: number}} rect
8112 * @typedef {{x: number, y:number, angle: number}} rotationPoint
8113 *   - point to rotate around and the angle in radians to rotate. angle == 0 means no rotation
8114 * @typedef {{nodeId:NodeId}} nodeClickItem
8115 * @typedef {{nodeId:NodeId, labelId:LabelId}} nodeLabelClickItem
8116 * @typedef {{edgeId:EdgeId}} edgeClickItem
8117 * @typedef {{edgeId:EdgeId, labelId:LabelId}} edgeLabelClickItem
8118 */
8119
8120var util = __webpack_require__(2);
8121
8122/**
8123 * Helper functions for components
8124 * @class
8125 */
8126
8127var ComponentUtil = function () {
8128  function ComponentUtil() {
8129    (0, _classCallCheck3['default'])(this, ComponentUtil);
8130  }
8131
8132  (0, _createClass3['default'])(ComponentUtil, null, [{
8133    key: 'choosify',
8134
8135    /**
8136     * Determine values to use for (sub)options of 'chosen'.
8137     *
8138     * This option is either a boolean or an object whose values should be examined further.
8139     * The relevant structures are:
8140     *
8141     * - chosen: <boolean value>
8142     * - chosen: { subOption: <boolean or function> }
8143     *
8144     * Where subOption is 'node', 'edge' or 'label'.
8145     *
8146     * The intention of this method appears to be to set a specific priority to the options;
8147     * Since most properties are either bridged or merged into the local options objects, there
8148     * is not much point in handling them separately.
8149     * TODO: examine if 'most' in previous sentence can be replaced with 'all'. In that case, we
8150     *       should be able to get rid of this method.
8151     *
8152     * @param {string}  subOption  option within object 'chosen' to consider; either 'node', 'edge' or 'label'
8153     * @param {Object}  pile       array of options objects to consider
8154     * 
8155     * @return {boolean|function}  value for passed subOption of 'chosen' to use
8156     */
8157    value: function choosify(subOption, pile) {
8158      // allowed values for subOption
8159      var allowed = ['node', 'edge', 'label'];
8160      var value = true;
8161
8162      var chosen = util.topMost(pile, 'chosen');
8163      if (typeof chosen === 'boolean') {
8164        value = chosen;
8165      } else if ((typeof chosen === 'undefined' ? 'undefined' : (0, _typeof3['default'])(chosen)) === 'object') {
8166        if (allowed.indexOf(subOption) === -1) {
8167          throw new Error('choosify: subOption \'' + subOption + '\' should be one of ' + "'" + allowed.join("', '") + "'");
8168        }
8169
8170        var chosenEdge = util.topMost(pile, ['chosen', subOption]);
8171        if (typeof chosenEdge === 'boolean' || typeof chosenEdge === 'function') {
8172          value = chosenEdge;
8173        }
8174      }
8175
8176      return value;
8177    }
8178
8179    /**
8180     * Check if the point falls within the given rectangle.
8181     *
8182     * @param {rect} rect
8183     * @param {point} point
8184     * @param {rotationPoint} [rotationPoint] if specified, the rotation that applies to the rectangle.
8185     * @returns {boolean}  true if point within rectangle, false otherwise
8186     * @static
8187     */
8188
8189  }, {
8190    key: 'pointInRect',
8191    value: function pointInRect(rect, point, rotationPoint) {
8192      if (rect.width <= 0 || rect.height <= 0) {
8193        return false; // early out
8194      }
8195
8196      if (rotationPoint !== undefined) {
8197        // Rotate the point the same amount as the rectangle
8198        var tmp = {
8199          x: point.x - rotationPoint.x,
8200          y: point.y - rotationPoint.y
8201        };
8202
8203        if (rotationPoint.angle !== 0) {
8204          // In order to get the coordinates the same, you need to 
8205          // rotate in the reverse direction
8206          var angle = -rotationPoint.angle;
8207
8208          var tmp2 = {
8209            x: Math.cos(angle) * tmp.x - Math.sin(angle) * tmp.y,
vendor: 4,980 bytes, lines 8210-8368
8210            y: Math.sin(angle) * tmp.x + Math.cos(angle) * tmp.y
8211          };
8212          point = tmp2;
8213        } else {
8214          point = tmp;
8215        }
8216
8217        // Note that if a rotation is specified, the rectangle coordinates
8218        // are **not* the full canvas coordinates. They are relative to the
8219        // rotationPoint. Hence, the point coordinates need not be translated
8220        // back in this case.
8221      }
8222
8223      var right = rect.x + rect.width;
8224      var bottom = rect.y + rect.width;
8225
8226      return rect.left < point.x && right > point.x && rect.top < point.y && bottom > point.y;
8227    }
8228
8229    /**
8230     * Check if given value is acceptable as a label text.
8231     *
8232     * @param {*} text value to check; can be anything at this point
8233     * @returns {boolean} true if valid label value, false otherwise
8234     */
8235
8236  }, {
8237    key: 'isValidLabel',
8238    value: function isValidLabel(text) {
8239      // Note that this is quite strict: types that *might* be converted to string are disallowed
8240      return typeof text === 'string' && text !== '';
8241    }
8242  }]);
8243  return ComponentUtil;
8244}();
8245
8246exports['default'] = ComponentUtil;
8247
8248/***/ }),
8249/* 49 */
8250/***/ (function(module, exports, __webpack_require__) {
8251
8252__webpack_require__(125);
8253var global = __webpack_require__(18);
8254var hide = __webpack_require__(26);
8255var Iterators = __webpack_require__(31);
8256var TO_STRING_TAG = __webpack_require__(13)('toStringTag');
8257
8258var DOMIterables = ('CSSRuleList,CSSStyleDeclaration,CSSValueList,ClientRectList,DOMRectList,DOMStringList,' +
8259  'DOMTokenList,DataTransferItemList,FileList,HTMLAllCollection,HTMLCollection,HTMLFormElement,HTMLSelectElement,' +
8260  'MediaList,MimeTypeArray,NamedNodeMap,NodeList,PaintRequestList,Plugin,PluginArray,SVGLengthList,SVGNumberList,' +
8261  'SVGPathSegList,SVGPointList,SVGStringList,SVGTransformList,SourceBufferList,StyleSheetList,TextTrackCueList,' +
8262  'TextTrackList,TouchList').split(',');
8263
8264for (var i = 0; i < DOMIterables.length; i++) {
8265  var NAME = DOMIterables[i];
8266  var Collection = global[NAME];
8267  var proto = Collection && Collection.prototype;
8268  if (proto && !proto[TO_STRING_TAG]) hide(proto, TO_STRING_TAG, NAME);
8269  Iterators[NAME] = Iterators.Array;
8270}
8271
8272
8273/***/ }),
8274/* 50 */
8275/***/ (function(module, exports) {
8276
8277var toString = {}.toString;
8278
8279module.exports = function (it) {
8280  return toString.call(it).slice(8, -1);
8281};
8282
8283
8284/***/ }),
8285/* 51 */
8286/***/ (function(module, exports) {
8287
8288// 7.2.1 RequireObjectCoercible(argument)
8289module.exports = function (it) {
8290  if (it == undefined) throw TypeError("Can't call method on  " + it);
8291  return it;
8292};
8293
8294
8295/***/ }),
8296/* 52 */
8297/***/ (function(module, exports) {
8298
8299module.exports = true;
8300
8301
8302/***/ }),
8303/* 53 */
8304/***/ (function(module, exports, __webpack_require__) {
8305
8306// 7.1.1 ToPrimitive(input [, PreferredType])
8307var isObject = __webpack_require__(32);
8308// instead of the ES6 spec version, we didn't implement @@toPrimitive case
8309// and the second argument - flag - preferred type is a string
8310module.exports = function (it, S) {
8311  if (!isObject(it)) return it;
8312  var fn, val;
8313  if (S && typeof (fn = it.toString) == 'function' && !isObject(val = fn.call(it))) return val;
8314  if (typeof (fn = it.valueOf) == 'function' && !isObject(val = fn.call(it))) return val;
8315  if (!S && typeof (fn = it.toString) == 'function' && !isObject(val = fn.call(it))) return val;
8316  throw TypeError("Can't convert object to primitive value");
8317};
8318
8319
8320/***/ }),
8321/* 54 */
8322/***/ (function(module, exports, __webpack_require__) {
8323
8324// 19.1.2.2 / 15.2.3.5 Object.create(O [, Properties])
8325var anObject = __webpack_require__(27);
8326var dPs = __webpack_require__(130);
8327var enumBugKeys = __webpack_require__(58);
8328var IE_PROTO = __webpack_require__(56)('IE_PROTO');
8329var Empty = function () { /* empty */ };
8330var PROTOTYPE = 'prototype';
8331
8332// Create object with fake `null` prototype: use iframe Object with cleared prototype
8333var createDict = function () {
8334  // Thrash, waste and sodomy: IE GC bug
8335  var iframe = __webpack_require__(82)('iframe');
8336  var i = enumBugKeys.length;
8337  var lt = '<';
8338  var gt = '>';
8339  var iframeDocument;
8340  iframe.style.display = 'none';
8341  __webpack_require__(134).appendChild(iframe);
8342  iframe.src = 'javascript:'; // eslint-disable-line no-script-url
8343  // createDict = iframe.contentWindow.Object;
8344  // html.removeChild(iframe);
8345  iframeDocument = iframe.contentWindow.document;
8346  iframeDocument.open();
8347  iframeDocument.write(lt + 'script' + gt + 'document.F=Object' + lt + '/script' + gt);
8348  iframeDocument.close();
8349  createDict = iframeDocument.F;
8350  while (i--) delete createDict[PROTOTYPE][enumBugKeys[i]];
8351  return createDict();
8352};
8353
8354module.exports = Object.create || function create(O, Properties) {
8355  var result;
8356  if (O !== null) {
8357    Empty[PROTOTYPE] = anObject(O);
8358    result = new Empty();
8359    Empty[PROTOTYPE] = null;
8360    // add "__proto__" for Object.getPrototypeOf polyfill
8361    result[IE_PROTO] = O;
8362  } else result = createDict();
8363  return Properties === undefined ? result : dPs(result, Properties);
8364};
8365
8366
8367/***/ }),
8368/* 55 */
8369/***/ (function(module, exports) {
8370
8371// 7.1.4 ToInteger
8372var ceil = Math.ceil;
8373var floor = Math.floor;
8374module.exports = function (it) {
8375  return isNaN(it = +it) ? 0 : (it > 0 ? floor : ceil)(it);
8376};
8377
8378
8379/***/ }),
8380/* 56 */
8381/***/ (function(module, exports, __webpack_require__) {
8382
8383var shared = __webpack_require__(57)('keys');
8384var uid = __webpack_require__(40);
8385module.exports = function (key) {
8386  return shared[key] || (shared[key] = uid(key));
8387};
8388
8389
8390/***/ }),
8391/* 57 */
8392/***/ (function(module, exports, __webpack_require__) {
8393
8394var global = __webpack_require__(18);
8395var SHARED = '__core-js_shared__';
8396var store = global[SHARED] || (global[SHARED] = {});
8397module.exports = function (key) {
8398  return store[key] || (store[key] = {});
8399};
8400
8401
8402/***/ }),
8403/* 58 */
8404/***/ (function(module, exports) {
8405
8406// IE 8- don't enum bug keys
8407module.exports = (
8408  'constructor,hasOwnProperty,isPrototypeOf,propertyIsEnumerable,toLocaleString,toString,valueOf'
8409).split(',');
8410
8411
8412/***/ }),
8413/* 59 */
8414/***/ (function(module, exports, __webpack_require__) {
8415
8416var def = __webpack_require__(20).f;
8417var has = __webpack_require__(22);
8418var TAG = __webpack_require__(13)('toStringTag');
8419
8420module.exports = function (it, tag, stat) {
8421  if (it && !has(it = stat ? it : it.prototype, TAG)) def(it, TAG, { configurable: true, value: tag });
8422};
8423
8424
8425/***/ }),
8426/* 60 */
8427/***/ (function(module, exports, __webpack_require__) {
8428
8429"use strict";
8430
8431var $at = __webpack_require__(135)(true);
8432
8433// 21.1.3.27 String.prototype[@@iterator]()
8434__webpack_require__(79)(String, 'String', function (iterated) {
8435  this._t = String(iterated); // target
8436  this._i = 0;                // next index
8437// 21.1.5.2.1 %StringIteratorPrototype%.next()
8438}, function () {
8439  var O = this._t;
8440  var index = this._i;
8441  var point;
8442  if (index >= O.length) return { value: undefined, done: true };
8443  point = $at(O, index);
8444  this._i += point.length;
8445  return { value: point, done: false };
8446});
8447
8448
8449/***/ }),
8450/* 61 */
8451/***/ (function(module, exports, __webpack_require__) {
8452
8453exports.f = __webpack_require__(13);
8454
8455
8456/***/ }),
8457/* 62 */
8458/***/ (function(module, exports, __webpack_require__) {
8459
8460var global = __webpack_require__(18);
8461var core = __webpack_require__(7);
8462var LIBRARY = __webpack_require__(52);
8463var wksExt = __webpack_require__(61);
8464var defineProperty = __webpack_require__(20).f;
8465module.exports = function (name) {
8466  var $Symbol = core.Symbol || (core.Symbol = LIBRARY ? {} : global.Symbol || {});
8467  if (name.charAt(0) != '_' && !(name in $Symbol)) defineProperty($Symbol, name, { value: wksExt.f(name) });
8468};
8469
8470
8471/***/ }),
8472/* 63 */
8473/***/ (function(module, exports) {
8474
8475exports.f = Object.getOwnPropertySymbols;
8476
8477
8478/***/ }),
8479/* 64 */
8480/***/ (function(module, exports, __webpack_require__) {
8481
8482"use strict";
8483
8484
8485var _keys = __webpack_require__(8);
8486
8487var _keys2 = _interopRequireDefault(_keys);
8488
8489var _stringify = __webpack_require__(19);
8490
8491var _stringify2 = _interopRequireDefault(_stringify);
8492
8493var _typeof2 = __webpack_require__(6);
8494
8495var _typeof3 = _interopRequireDefault(_typeof2);
8496
8497function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
8498
8499var util = __webpack_require__(2);
8500var moment = __webpack_require__(9);
8501var Component = __webpack_require__(16);
8502var DateUtil = __webpack_require__(36);
8503
8504/**
8505 * A Range controls a numeric range with a start and end value.
8506 * The Range adjusts the range based on mouse events or programmatic changes,
8507 * and triggers events when the range is changing or has been changed.
8508 * @param {{dom: Object, domProps: Object, emitter: Emitter}} body
8509 * @param {Object} [options]    See description at Range.setOptions
8510 * @constructor Range
8511 * @extends Component
8512 */
8513function Range(body, options) {
8514  var now = moment().hours(0).minutes(0).seconds(0).milliseconds(0);
8515  var start = now.clone().add(-3, 'days').valueOf();
8516  var end = now.clone().add(3, 'days').valueOf();
8517  this.millisecondsPerPixelCache = undefined;
8518
8519  if (options === undefined) {
8520    this.start = start;
8521    this.end = end;
8522  } else {
8523    this.start = options.start || start;
8524    this.end = options.end || end;
8525  }
8526
8527  this.rolling = false;
8528
8529  this.body = body;
8530  this.deltaDifference = 0;
8531  this.scaleOffset = 0;
8532  this.startToFront = false;
8533  this.endToFront = true;
8534
8535  // default options
8536  this.defaultOptions = {
8537    rtl: false,
8538    start: null,
8539    end: null,
8540    moment: moment,
8541    direction: 'horizontal', // 'horizontal' or 'vertical'
8542    moveable: true,
8543    zoomable: true,
8544    min: null,
8545    max: null,
8546    zoomMin: 10, // milliseconds
8547    zoomMax: 1000 * 60 * 60 * 24 * 365 * 10000, // milliseconds
8548    rollingMode: {
8549      follow: false,
8550      offset: 0.5
8551    }
8552  };
8553  this.options = util.extend({}, this.defaultOptions);
8554  this.props = {
8555    touch: {}
8556  };
8557  this.animationTimer = null;
8558
8559  // drag listeners for dragging
8560  this.body.emitter.on('panstart', this._onDragStart.bind(this));
8561  this.body.emitter.on('panmove', this._onDrag.bind(this));
8562  this.body.emitter.on('panend', this._onDragEnd.bind(this));
8563
8564  // mouse wheel for zooming
8565  this.body.emitter.on('mousewheel', this._onMouseWheel.bind(this));
8566
8567  // pinch to zoom
8568  this.body.emitter.on('touch', this._onTouch.bind(this));
8569  this.body.emitter.on('pinch', this._onPinch.bind(this));
8570
8571  // on click of rolling mode button
8572  this.body.dom.rollingModeBtn.addEventListener('click', this.startRolling.bind(this));
8573
8574  this.setOptions(options);
8575}
8576
8577Range.prototype = new Component();
8578
8579/**
8580 * Set options for the range controller
8581 * @param {Object} options      Available options:
8582 *                              {number | Date | String} start  Start date for the range
8583 *                              {number | Date | String} end    End date for the range
8584 *                              {number} min    Minimum value for start
8585 *                              {number} max    Maximum value for end
8586 *                              {number} zoomMin    Set a minimum value for
8587 *                                                  (end - start).
8588 *                              {number} zoomMax    Set a maximum value for
8589 *                                                  (end - start).
8590 *                              {boolean} moveable Enable moving of the range
8591 *                                                 by dragging. True by default
8592 *                              {boolean} zoomable Enable zooming of the range
8593 *                                                 by pinching/scrolling. True by default
8594 */
8595Range.prototype.setOptions = function (options) {
8596  if (options) {
8597    // copy the options that we know
8598    var fields = ['animation', 'direction', 'min', 'max', 'zoomMin', 'zoomMax', 'moveable', 'zoomable', 'moment', 'activate', 'hiddenDates', 'zoomKey', 'rtl', 'showCurrentTime', 'rollingMode', 'horizontalScroll'];
8599    util.selectiveExtend(fields, this.options, options);
8600
8601    if (options.rollingMode && options.rollingMode.follow) {
8602      this.startRolling();
8603    }
8604    if ('start' in options || 'end' in options) {
8605      // apply a new range. both start and end are optional
8606      this.setRange(options.start, options.end);
8607    }
8608  }
8609};
8610
8611/**
8612 * Test whether direction has a valid value
8613 * @param {string} direction    'horizontal' or 'vertical'
8614 */
8615function validateDirection(direction) {
8616  if (direction != 'horizontal' && direction != 'vertical') {
8617    throw new TypeError('Unknown direction "' + direction + '". ' + 'Choose "horizontal" or "vertical".');
8618  }
8619}
8620
8621/**
8622 * Start auto refreshing the current time bar
8623 */
8624Range.prototype.startRolling = function () {
8625  var me = this;
8626
8627  /**
8628   *  Updates the current time.
8629   */
8630  function update() {
8631    me.stopRolling();
8632    me.rolling = true;
8633
8634    var interval = me.end - me.start;
8635    var t = util.convert(new Date(), 'Date').valueOf();
8636
8637    var start = t - interval * me.options.rollingMode.offset;
8638    var end = t + interval * (1 - me.options.rollingMode.offset);
8639
8640    var options = {
8641      animation: false
8642    };
8643    me.setRange(start, end, options);
8644
8645    // determine interval to refresh
8646    var scale = me.conversion(me.body.domProps.center.width).scale;
8647    interval = 1 / scale / 10;
8648    if (interval < 30) interval = 30;
8649    if (interval > 1000) interval = 1000;
8650
8651    me.body.dom.rollingModeBtn.style.visibility = "hidden";
8652    // start a renderTimer to adjust for the new time
8653    me.currentTimeTimer = setTimeout(update, interval);
8654  }
8655
8656  update();
8657};
8658
8659/**
8660 * Stop auto refreshing the current time bar
8661 */
8662Range.prototype.stopRolling = function () {
8663  if (this.currentTimeTimer !== undefined) {
8664    clearTimeout(this.currentTimeTimer);
8665    this.rolling = false;
8666    this.body.dom.rollingModeBtn.style.visibility = "visible";
8667  }
8668};
8669
8670/**
8671 * Set a new start and end range
8672 * @param {Date | number | string} [start]
8673 * @param {Date | number | string} [end]
8674 * @param {Object} options      Available options:
8675 *                              {boolean | {duration: number, easingFunction: string}} [animation=false]
8676 *                                    If true, the range is animated
8677 *                                    smoothly to the new window. An object can be
8678 *                                    provided to specify duration and easing function.
8679 *                                    Default duration is 500 ms, and default easing
8680 *                                    function is 'easeInOutQuad'.
8681 *                              {boolean} [byUser=false]
8682 *                              {Event}  event  Mouse event
8683 * @param {Function} callback     a callback function to be executed at the end of this function  
8684 * @param {Function} frameCallback    a callback function executed each frame of the range animation.
8685 *                                    The callback will be passed three parameters:
8686 *                                    {number} easeCoefficient    an easing coefficent
8687 *                                    {boolean} willDraw          If true the caller will redraw after the callback completes
8688 *                                    {boolean} done              If true then animation is ending after the current frame
8689 */
8690
8691Range.prototype.setRange = function (start, end, options, callback, frameCallback) {
8692  if (!options) {
8693    options = {};
8694  }
8695  if (options.byUser !== true) {
8696    options.byUser = false;
8697  }
8698  var me = this;
8699  var finalStart = start != undefined ? util.convert(start, 'Date').valueOf() : null;
8700  var finalEnd = end != undefined ? util.convert(end, 'Date').valueOf() : null;
8701  this._cancelAnimation();
8702  this.millisecondsPerPixelCache = undefined;
8703
8704  if (options.animation) {
8705    // true or an Object
8706    var initStart = this.start;
8707    var initEnd = this.end;
8708    var duration = (0, _typeof3['default'])(options.animation) === 'object' && 'duration' in options.animation ? options.animation.duration : 500;
8709    var easingName = (0, _typeof3['default'])(options.animation) === 'object' && 'easingFunction' in options.animation ? options.animation.easingFunction : 'easeInOutQuad';
8710    var easingFunction = util.easingFunctions[easingName];
8711    if (!easingFunction) {
8712      throw new Error('Unknown easing function ' + (0, _stringify2['default'])(easingName) + '. ' + 'Choose from: ' + (0, _keys2['default'])(util.easingFunctions).join(', '));
8713    }
8714
8715    var initTime = new Date().valueOf();
8716    var anyChanged = false;
8717
8718    var next = function next() {
8719      if (!me.props.touch.dragging) {
8720        var now = new Date().valueOf();
8721        var time = now - initTime;
8722        var ease = easingFunction(time / duration);
8723        var done = time > duration;
8724        var s = done || finalStart === null ? finalStart : initStart + (finalStart - initStart) * ease;
8725        var e = done || finalEnd === null ? finalEnd : initEnd + (finalEnd - initEnd) * ease;
8726
8727        changed = me._applyRange(s, e);
8728        DateUtil.updateHiddenDates(me.options.moment, me.body, me.options.hiddenDates);
8729        anyChanged = anyChanged || changed;
8730
8731        var params = {
8732          start: new Date(me.start),
8733          end: new Date(me.end),
8734          byUser: options.byUser,
8735          event: options.event
8736        };
8737
8738        if (frameCallback) {
8739          frameCallback(ease, changed, done);
8740        }
8741
8742        if (changed) {
8743          me.body.emitter.emit('rangechange', params);
8744        }
8745
8746        if (done) {
8747          if (anyChanged) {
8748            me.body.emitter.emit('rangechanged', params);
8749            if (callback) {
8750              return callback();
8751            }
8752          }
8753        } else {
8754          // animate with as high as possible frame rate, leave 20 ms in between
8755          // each to prevent the browser from blocking
8756          me.animationTimer = setTimeout(next, 20);
8757        }
8758      }
8759    };
8760
8761    return next();
8762  } else {
8763    var changed = this._applyRange(finalStart, finalEnd);
8764    DateUtil.updateHiddenDates(this.options.moment, this.body, this.options.hiddenDates);
8765    if (changed) {
8766      var params = {
8767        start: new Date(this.start),
8768        end: new Date(this.end),
8769        byUser: options.byUser,
8770        event: options.event
8771      };
8772
8773      this.body.emitter.emit('rangechange', params);
8774      clearTimeout(me.timeoutID);
8775      me.timeoutID = setTimeout(function () {
8776        me.body.emitter.emit('rangechanged', params);
8777      }, 200);
8778      if (callback) {
8779        return callback();
8780      }
8781    }
8782  }
8783};
8784
8785/**
8786 * Get the number of milliseconds per pixel.
8787 *
8788 * @returns {undefined|number}
8789 */
8790Range.prototype.getMillisecondsPerPixel = function () {
8791  if (this.millisecondsPerPixelCache === undefined) {
8792    this.millisecondsPerPixelCache = (this.end - this.start) / this.body.dom.center.clientWidth;
8793  }
8794  return this.millisecondsPerPixelCache;
8795};
8796
8797/**
8798 * Stop an animation
8799 * @private
8800 */
8801Range.prototype._cancelAnimation = function () {
8802  if (this.animationTimer) {
8803    clearTimeout(this.animationTimer);
8804    this.animationTimer = null;
8805  }
8806};
8807
8808/**
8809 * Set a new start and end range. This method is the same as setRange, but
8810 * does not trigger a range change and range changed event, and it returns
8811 * true when the range is changed
8812 * @param {number} [start]
8813 * @param {number} [end]
8814 * @return {boolean} changed
8815 * @private
8816 */
8817Range.prototype._applyRange = function (start, end) {
8818  var newStart = start != null ? util.convert(start, 'Date').valueOf() : this.start,
8819      newEnd = end != null ? util.convert(end, 'Date').valueOf() : this.end,
8820      max = this.options.max != null ? util.convert(this.options.max, 'Date').valueOf() : null,
8821      min = this.options.min != null ? util.convert(this.options.min, 'Date').valueOf() : null,
8822      diff;
8823
8824  // check for valid number
8825  if (isNaN(newStart) || newStart === null) {
8826    throw new Error('Invalid start "' + start + '"');
8827  }
8828  if (isNaN(newEnd) || newEnd === null) {
8829    throw new Error('Invalid end "' + end + '"');
8830  }
8831
8832  // prevent end < start
8833  if (newEnd < newStart) {
8834    newEnd = newStart;
8835  }
8836
8837  // prevent start < min
8838  if (min !== null) {
8839    if (newStart < min) {
8840      diff = min - newStart;
8841      newStart += diff;
8842      newEnd += diff;
8843
8844      // prevent end > max
8845      if (max != null) {
8846        if (newEnd > max) {
8847          newEnd = max;
8848        }
8849      }
8850    }
8851  }
8852
8853  // prevent end > max
8854  if (max !== null) {
8855    if (newEnd > max) {
8856      diff = newEnd - max;
8857      newStart -= diff;
8858      newEnd -= diff;
8859
8860      // prevent start < min
8861      if (min != null) {
8862        if (newStart < min) {
8863          newStart = min;
8864        }
8865      }
8866    }
8867  }
8868
8869  // prevent (end-start) < zoomMin
8870  if (this.options.zoomMin !== null) {
8871    var zoomMin = parseFloat(this.options.zoomMin);
8872    if (zoomMin < 0) {
8873      zoomMin = 0;
8874    }
8875    if (newEnd - newStart < zoomMin) {
8876      // compensate for a scale of 0.5 ms
8877      var compensation = 0.5;
8878      if (this.end - this.start === zoomMin && newStart >= this.start - compensation && newEnd <= this.end) {
8879        // ignore this action, we are already zoomed to the minimum
8880        newStart = this.start;
8881        newEnd = this.end;
8882      } else {
8883        // zoom to the minimum
8884        diff = zoomMin - (newEnd - newStart);
8885        newStart -= diff / 2;
8886        newEnd += diff / 2;
8887      }
8888    }
8889  }
8890
8891  // prevent (end-start) > zoomMax
8892  if (this.options.zoomMax !== null) {
8893    var zoomMax = parseFloat(this.options.zoomMax);
8894    if (zoomMax < 0) {
8895      zoomMax = 0;
8896    }
8897
8898    if (newEnd - newStart > zoomMax) {
8899      if (this.end - this.start === zoomMax && newStart < this.start && newEnd > this.end) {
8900        // ignore this action, we are already zoomed to the maximum
8901        newStart = this.start;
8902        newEnd = this.end;
8903      } else {
8904        // zoom to the maximum
8905        diff = newEnd - newStart - zoomMax;
8906        newStart += diff / 2;
8907        newEnd -= diff / 2;
8908      }
8909    }
8910  }
8911
8912  var changed = this.start != newStart || this.end != newEnd;
8913
8914  // 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)
8915  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)) {
8916    this.body.emitter.emit('checkRangedItems');
8917  }
8918
8919  this.start = newStart;
8920  this.end = newEnd;
8921  return changed;
8922};
8923
8924/**
8925 * Retrieve the current range.
8926 * @return {Object} An object with start and end properties
8927 */
8928Range.prototype.getRange = function () {
8929  return {
8930    start: this.start,
8931    end: this.end
8932  };
8933};
8934
8935/**
8936 * Calculate the conversion offset and scale for current range, based on
8937 * the provided width
8938 * @param {number} width
8939 * @param {number} [totalHidden=0]
8940 * @returns {{offset: number, scale: number}} conversion
8941 */
8942Range.prototype.conversion = function (width, totalHidden) {
8943  return Range.conversion(this.start, this.end, width, totalHidden);
8944};
8945
8946/**
8947 * Static method to calculate the conversion offset and scale for a range,
8948 * based on the provided start, end, and width
8949 * @param {number} start
8950 * @param {number} end
8951 * @param {number} width
8952 * @param {number} [totalHidden=0]
8953 * @returns {{offset: number, scale: number}} conversion
8954 */
8955Range.conversion = function (start, end, width, totalHidden) {
8956  if (totalHidden === undefined) {
8957    totalHidden = 0;
8958  }
8959  if (width != 0 && end - start != 0) {
8960    return {
8961      offset: start,
8962      scale: width / (end - start - totalHidden)
8963    };
8964  } else {
8965    return {
8966      offset: 0,
8967      scale: 1
8968    };
8969  }
8970};
8971
8972/**
8973 * Start dragging horizontally or vertically
8974 * @param {Event} event
8975 * @private
8976 */
8977Range.prototype._onDragStart = function (event) {
8978  this.deltaDifference = 0;
8979  this.previousDelta = 0;
8980
8981  // only allow dragging when configured as movable
8982  if (!this.options.moveable) return;
8983
8984  // only start dragging when the mouse is inside the current range
8985  if (!this._isInsideRange(event)) return;
8986
8987  // refuse to drag when we where pinching to prevent the timeline make a jump
8988  // when releasing the fingers in opposite order from the touch screen
8989  if (!this.props.touch.allowDragging) return;
8990
8991  this.stopRolling();
8992
8993  this.props.touch.start = this.start;
8994  this.props.touch.end = this.end;
8995  this.props.touch.dragging = true;
8996
8997  if (this.body.dom.root) {
8998    this.body.dom.root.style.cursor = 'move';
8999  }
9000};
9001
9002/**
9003 * Perform dragging operation
9004 * @param {Event} event
9005 * @private
9006 */
9007Range.prototype._onDrag = function (event) {
9008  if (!event) return;
9009
9010  if (!this.props.touch.dragging) return;
9011
9012  // only allow dragging when configured as movable
9013  if (!this.options.moveable) return;
9014
9015  // TODO: this may be redundant in hammerjs2
9016  // refuse to drag when we where pinching to prevent the timeline make a jump
9017  // when releasing the fingers in opposite order from the touch screen
9018  if (!this.props.touch.allowDragging) return;
9019
9020  var direction = this.options.direction;
9021  validateDirection(direction);
9022  var delta = direction == 'horizontal' ? event.deltaX : event.deltaY;
9023  delta -= this.deltaDifference;
9024  var interval = this.props.touch.end - this.props.touch.start;
9025
9026  // normalize dragging speed if cutout is in between.
9027  var duration = DateUtil.getHiddenDurationBetween(this.body.hiddenDates, this.start, this.end);
9028  interval -= duration;
9029
9030  var width = direction == 'horizontal' ? this.body.domProps.center.width : this.body.domProps.center.height;
9031  var diffRange;
9032  if (this.options.rtl) {
9033    diffRange = delta / width * interval;
9034  } else {
9035    diffRange = -delta / width * interval;
9036  }
9037
9038  var newStart = this.props.touch.start + diffRange;
9039  var newEnd = this.props.touch.end + diffRange;
9040
9041  // snapping times away from hidden zones
9042  var safeStart = DateUtil.snapAwayFromHidden(this.body.hiddenDates, newStart, this.previousDelta - delta, true);
9043  var safeEnd = DateUtil.snapAwayFromHidden(this.body.hiddenDates, newEnd, this.previousDelta - delta, true);
9044  if (safeStart != newStart || safeEnd != newEnd) {
9045    this.deltaDifference += delta;
9046    this.props.touch.start = safeStart;
9047    this.props.touch.end = safeEnd;
9048    this._onDrag(event);
9049    return;
9050  }
9051
9052  this.previousDelta = delta;
9053  this._applyRange(newStart, newEnd);
9054
9055  var startDate = new Date(this.start);
9056  var endDate = new Date(this.end);
9057
9058  // fire a rangechange event
9059  this.body.emitter.emit('rangechange', {
9060    start: startDate,
9061    end: endDate,
9062    byUser: true,
9063    event: event
9064  });
9065
9066  // fire a panmove event
9067  this.body.emitter.emit('panmove');
9068};
9069
9070/**
9071 * Stop dragging operation
9072 * @param {event} event
9073 * @private
9074 */
9075Range.prototype._onDragEnd = function (event) {
9076  if (!this.props.touch.dragging) return;
9077
9078  // only allow dragging when configured as movable
9079  if (!this.options.moveable) return;
9080
9081  // TODO: this may be redundant in hammerjs2
9082  // refuse to drag when we where pinching to prevent the timeline make a jump
9083  // when releasing the fingers in opposite order from the touch screen
9084  if (!this.props.touch.allowDragging) return;
9085
9086  this.props.touch.dragging = false;
9087  if (this.body.dom.root) {
9088    this.body.dom.root.style.cursor = 'auto';
9089  }
9090
9091  // fire a rangechanged event
9092  this.body.emitter.emit('rangechanged', {
9093    start: new Date(this.start),
9094    end: new Date(this.end),
9095    byUser: true,
9096    event: event
9097  });
9098};
9099
9100/**
9101 * Event handler for mouse wheel event, used to zoom
9102 * Code from http://adomas.org/javascript-mouse-wheel/
9103 * @param {Event} event
9104 * @private
9105 */
9106Range.prototype._onMouseWheel = function (event) {
9107  // retrieve delta
9108  var delta = 0;
9109  if (event.wheelDelta) {
9110    /* IE/Opera. */
9111    delta = event.wheelDelta / 120;
9112  } else if (event.detail) {
9113    /* Mozilla case. */
9114    // In Mozilla, sign of delta is different than in IE.
9115    // Also, delta is multiple of 3.
9116    delta = -event.detail / 3;
9117  }
9118
9119  // don't allow zoom when the according key is pressed and the zoomKey option or not zoomable but movable
9120  if (this.options.zoomKey && !event[this.options.zoomKey] && this.options.zoomable || !this.options.zoomable && this.options.moveable) {
9121    return;
9122  }
9123
9124  // only allow zooming when configured as zoomable and moveable
9125  if (!(this.options.zoomable && this.options.moveable)) return;
9126
9127  // only zoom when the mouse is inside the current range
9128  if (!this._isInsideRange(event)) return;
9129
9130  // If delta is nonzero, handle it.
9131  // Basically, delta is now positive if wheel was scrolled up,
9132  // and negative, if wheel was scrolled down.
9133  if (delta) {
9134    // perform the zoom action. Delta is normally 1 or -1
9135
9136    // adjust a negative delta such that zooming in with delta 0.1
9137    // equals zooming out with a delta -0.1
9138    var scale;
9139    if (delta < 0) {
9140      scale = 1 - delta / 5;
9141    } else {
9142      scale = 1 / (1 + delta / 5);
9143    }
9144
9145    // calculate center, the date to zoom around
9146    var pointerDate;
9147    if (this.rolling) {
9148      pointerDate = this.start + (this.end - this.start) * this.options.rollingMode.offset;
9149    } else {
9150      var pointer = this.getPointer({ x: event.clientX, y: event.clientY }, this.body.dom.center);
9151      pointerDate = this._pointerToDate(pointer);
9152    }
9153    this.zoom(scale, pointerDate, delta, event);
9154
9155    // Prevent default actions caused by mouse wheel
9156    // (else the page and timeline both scroll)
9157    event.preventDefault();
9158  }
9159};
9160
9161/**
9162 * Start of a touch gesture
9163 * @param {Event} event
9164 * @private
9165 */
9166Range.prototype._onTouch = function (event) {
9167  // eslint-disable-line no-unused-vars
9168  this.props.touch.start = this.start;
9169  this.props.touch.end = this.end;
9170  this.props.touch.allowDragging = true;
9171  this.props.touch.center = null;
9172  this.scaleOffset = 0;
9173  this.deltaDifference = 0;
9174  // Disable the browser default handling of this event.
9175  util.preventDefault(event);
9176};
9177
9178/**
9179 * Handle pinch event
9180 * @param {Event} event
9181 * @private
9182 */
9183Range.prototype._onPinch = function (event) {
9184  // only allow zooming when configured as zoomable and moveable
9185  if (!(this.options.zoomable && this.options.moveable)) return;
9186
9187  // Disable the browser default handling of this event.
9188  util.preventDefault(event);
9189
9190  this.props.touch.allowDragging = false;
9191
9192  if (!this.props.touch.center) {
9193    this.props.touch.center = this.getPointer(event.center, this.body.dom.center);
9194  }
9195
9196  this.stopRolling();
9197
9198  var scale = 1 / (event.scale + this.scaleOffset);
9199  var centerDate = this._pointerToDate(this.props.touch.center);
9200
9201  var hiddenDuration = DateUtil.getHiddenDurationBetween(this.body.hiddenDates, this.start, this.end);
9202  var hiddenDurationBefore = DateUtil.getHiddenDurationBefore(this.options.moment, this.body.hiddenDates, this, centerDate);
9203  var hiddenDurationAfter = hiddenDuration - hiddenDurationBefore;
9204
9205  // calculate new start and end
9206  var newStart = centerDate - hiddenDurationBefore + (this.props.touch.start - (centerDate - hiddenDurationBefore)) * scale;
9207  var newEnd = centerDate + hiddenDurationAfter + (this.props.touch.end - (centerDate + hiddenDurationAfter)) * scale;
9208
9209  // snapping times away from hidden zones
9210  this.startToFront = 1 - scale <= 0; // used to do the right auto correction with periodic hidden times
9211  this.endToFront = scale - 1 <= 0; // used to do the right auto correction with periodic hidden times
9212
9213  var safeStart = DateUtil.snapAwayFromHidden(this.body.hiddenDates, newStart, 1 - scale, true);
9214  var safeEnd = DateUtil.snapAwayFromHidden(this.body.hiddenDates, newEnd, scale - 1, true);
9215  if (safeStart != newStart || safeEnd != newEnd) {
9216    this.props.touch.start = safeStart;
9217    this.props.touch.end = safeEnd;
9218    this.scaleOffset = 1 - event.scale;
9219    newStart = safeStart;
9220    newEnd = safeEnd;
9221  }
9222
9223  var options = {
9224    animation: false,
9225    byUser: true,
9226    event: event
9227  };
9228  this.setRange(newStart, newEnd, options);
9229
9230  this.startToFront = false; // revert to default
9231  this.endToFront = true; // revert to default
9232};
9233
9234/**
9235 * Test whether the mouse from a mouse event is inside the visible window,
9236 * between the current start and end date
9237 * @param {Object} event
9238 * @return {boolean} Returns true when inside the visible window
9239 * @private
9240 */
9241Range.prototype._isInsideRange = function (event) {
9242  // calculate the time where the mouse is, check whether inside
9243  // and no scroll action should happen.
9244  var clientX = event.center ? event.center.x : event.clientX;
9245  var x;
9246  if (this.options.rtl) {
9247    x = clientX - util.getAbsoluteLeft(this.body.dom.centerContainer);
9248  } else {
9249    x = util.getAbsoluteRight(this.body.dom.centerContainer) - clientX;
9250  }
9251  var time = this.body.util.toTime(x);
9252
9253  return time >= this.start && time <= this.end;
9254};
9255
9256/**
9257 * Helper function to calculate the center date for zooming
9258 * @param {{x: number, y: number}} pointer
9259 * @return {number} date
9260 * @private
9261 */
9262Range.prototype._pointerToDate = function (pointer) {
9263  var conversion;
9264  var direction = this.options.direction;
9265
9266  validateDirection(direction);
9267
9268  if (direction == 'horizontal') {
9269    return this.body.util.toTime(pointer.x).valueOf();
9270  } else {
9271    var height = this.body.domProps.center.height;
9272    conversion = this.conversion(height);
9273    return pointer.y / conversion.scale + conversion.offset;
9274  }
9275};
9276
9277/**
9278 * Get the pointer location relative to the location of the dom element
9279 * @param {{x: number, y: number}} touch
9280 * @param {Element} element   HTML DOM element
9281 * @return {{x: number, y: number}} pointer
9282 * @private
9283 */
9284Range.prototype.getPointer = function (touch, element) {
9285  if (this.options.rtl) {
9286    return {
9287      x: util.getAbsoluteRight(element) - touch.x,
9288      y: touch.y - util.getAbsoluteTop(element)
9289    };
9290  } else {
9291    return {
9292      x: touch.x - util.getAbsoluteLeft(element),
9293      y: touch.y - util.getAbsoluteTop(element)
9294    };
9295  }
9296};
9297
9298/**
9299 * Zoom the range the given scale in or out. Start and end date will
9300 * be adjusted, and the timeline will be redrawn. You can optionally give a
9301 * date around which to zoom.
9302 * For example, try scale = 0.9 or 1.1
9303 * @param {number} scale      Scaling factor. Values above 1 will zoom out,
9304 *                            values below 1 will zoom in.
9305 * @param {number} [center]   Value representing a date around which will
9306 *                            be zoomed.
9307 * @param {number} delta
9308 * @param {Event} event
9309 */
9310Range.prototype.zoom = function (scale, center, delta, event) {
9311  // if centerDate is not provided, take it half between start Date and end Date
9312  if (center == null) {
9313    center = (this.start + this.end) / 2;
9314  }
9315
9316  var hiddenDuration = DateUtil.getHiddenDurationBetween(this.body.hiddenDates, this.start, this.end);
9317  var hiddenDurationBefore = DateUtil.getHiddenDurationBefore(this.options.moment, this.body.hiddenDates, this, center);
9318  var hiddenDurationAfter = hiddenDuration - hiddenDurationBefore;
9319
9320  // calculate new start and end
9321  var newStart = center - hiddenDurationBefore + (this.start - (center - hiddenDurationBefore)) * scale;
9322  var newEnd = center + hiddenDurationAfter + (this.end - (center + hiddenDurationAfter)) * scale;
9323
9324  // snapping times away from hidden zones
9325  this.startToFront = delta > 0 ? false : true; // used to do the right autocorrection with periodic hidden times
9326  this.endToFront = -delta > 0 ? false : true; // used to do the right autocorrection with periodic hidden times
9327  var safeStart = DateUtil.snapAwayFromHidden(this.body.hiddenDates, newStart, delta, true);
9328  var safeEnd = DateUtil.snapAwayFromHidden(this.body.hiddenDates, newEnd, -delta, true);
9329  if (safeStart != newStart || safeEnd != newEnd) {
9330    newStart = safeStart;
9331    newEnd = safeEnd;
9332  }
9333
9334  var options = {
9335    animation: false,
9336    byUser: true,
9337    event: event
9338  };
9339  this.setRange(newStart, newEnd, options);
9340
9341  this.startToFront = false; // revert to default
9342  this.endToFront = true; // revert to default
9343};
9344
9345/**
9346 * Move the range with a given delta to the left or right. Start and end
9347 * value will be adjusted. For example, try delta = 0.1 or -0.1
9348 * @param {number}  delta     Moving amount. Positive value will move right,
9349 *                            negative value will move left
9350 */
9351Range.prototype.move = function (delta) {
9352  // zoom start Date and end Date relative to the centerDate
9353  var diff = this.end - this.start;
9354
9355  // apply new values
9356  var newStart = this.start + diff * delta;
9357  var newEnd = this.end + diff * delta;
9358
9359  // TODO: reckon with min and max range
9360
9361  this.start = newStart;
9362  this.end = newEnd;
9363};
9364
9365/**
9366 * Move the range to a new center point
9367 * @param {number} moveTo      New center point of the range
9368 */
9369Range.prototype.moveTo = function (moveTo) {
9370  var center = (this.start + this.end) / 2;
9371
9372  var diff = center - moveTo;
9373
9374  // calculate new start and end
9375  var newStart = this.start - diff;
9376  var newEnd = this.end - diff;
9377
9378  var options = {
9379    animation: false,
9380    byUser: true,
9381    event: null
9382  };
9383  this.setRange(newStart, newEnd, options);
9384};
9385
9386module.exports = Range;
9387
9388/***/ }),
9389/* 65 */
9390/***/ (function(module, exports, __webpack_require__) {
9391
9392"use strict";
9393
9394
9395var _stringify = __webpack_require__(19);
9396
9397var _stringify2 = _interopRequireDefault(_stringify);
9398
9399var _typeof2 = __webpack_require__(6);
9400
9401var _typeof3 = _interopRequireDefault(_typeof2);
9402
9403function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
9404
9405var Emitter = __webpack_require__(44);
9406var Hammer = __webpack_require__(10);
9407var hammerUtil = __webpack_require__(37);
9408var util = __webpack_require__(2);
9409var TimeAxis = __webpack_require__(45);
9410var Activator = __webpack_require__(97);
9411var DateUtil = __webpack_require__(36);
9412var CustomTime = __webpack_require__(46);
9413
9414/**
9415 * Create a timeline visualization
9416 * @constructor Core
9417 */
9418function Core() {}
9419
9420// turn Core into an event emitter
9421Emitter(Core.prototype);
9422
9423/**
9424 * Create the main DOM for the Core: a root panel containing left, right,
9425 * top, bottom, content, and background panel.
9426 * @param {Element} container  The container element where the Core will
9427 *                             be attached.
9428 * @protected
9429 */
9430Core.prototype._create = function (container) {
9431  this.dom = {};
9432
9433  this.dom.container = container;
9434
9435  this.dom.root = document.createElement('div');
9436  this.dom.background = document.createElement('div');
9437  this.dom.backgroundVertical = document.createElement('div');
9438  this.dom.backgroundHorizontal = document.createElement('div');
9439  this.dom.centerContainer = document.createElement('div');
9440  this.dom.leftContainer = document.createElement('div');
9441  this.dom.rightContainer = document.createElement('div');
9442  this.dom.center = document.createElement('div');
9443  this.dom.left = document.createElement('div');
9444  this.dom.right = document.createElement('div');
9445  this.dom.top = document.createElement('div');
9446  this.dom.bottom = document.createElement('div');
9447  this.dom.shadowTop = document.createElement('div');
9448  this.dom.shadowBottom = document.createElement('div');
9449  this.dom.shadowTopLeft = document.createElement('div');
9450  this.dom.shadowBottomLeft = document.createElement('div');
9451  this.dom.shadowTopRight = document.createElement('div');
9452  this.dom.shadowBottomRight = document.createElement('div');
9453  this.dom.rollingModeBtn = document.createElement('div');
9454
9455  this.dom.root.className = 'vis-timeline';
9456  this.dom.background.className = 'vis-panel vis-background';
9457  this.dom.backgroundVertical.className = 'vis-panel vis-background vis-vertical';
9458  this.dom.backgroundHorizontal.className = 'vis-panel vis-background vis-horizontal';
9459  this.dom.centerContainer.className = 'vis-panel vis-center';
9460  this.dom.leftContainer.className = 'vis-panel vis-left';
9461  this.dom.rightContainer.className = 'vis-panel vis-right';
9462  this.dom.top.className = 'vis-panel vis-top';
9463  this.dom.bottom.className = 'vis-panel vis-bottom';
9464  this.dom.left.className = 'vis-content';
9465  this.dom.center.className = 'vis-content';
9466  this.dom.right.className = 'vis-content';
9467  this.dom.shadowTop.className = 'vis-shadow vis-top';
9468  this.dom.shadowBottom.className = 'vis-shadow vis-bottom';
9469  this.dom.shadowTopLeft.className = 'vis-shadow vis-top';
9470  this.dom.shadowBottomLeft.className = 'vis-shadow vis-bottom';
9471  this.dom.shadowTopRight.className = 'vis-shadow vis-top';
9472  this.dom.shadowBottomRight.className = 'vis-shadow vis-bottom';
9473  this.dom.rollingModeBtn.className = 'vis-rolling-mode-btn';
9474
9475  this.dom.root.appendChild(this.dom.background);
9476  this.dom.root.appendChild(this.dom.backgroundVertical);
9477  this.dom.root.appendChild(this.dom.backgroundHorizontal);
9478  this.dom.root.appendChild(this.dom.centerContainer);
9479  this.dom.root.appendChild(this.dom.leftContainer);
9480  this.dom.root.appendChild(this.dom.rightContainer);
9481  this.dom.root.appendChild(this.dom.top);
9482  this.dom.root.appendChild(this.dom.bottom);
9483  this.dom.root.appendChild(this.dom.bottom);
9484  this.dom.root.appendChild(this.dom.rollingModeBtn);
9485
9486  this.dom.centerContainer.appendChild(this.dom.center);
9487  this.dom.leftContainer.appendChild(this.dom.left);
9488  this.dom.rightContainer.appendChild(this.dom.right);
9489  this.dom.centerContainer.appendChild(this.dom.shadowTop);
9490  this.dom.centerContainer.appendChild(this.dom.shadowBottom);
9491  this.dom.leftContainer.appendChild(this.dom.shadowTopLeft);
9492  this.dom.leftContainer.appendChild(this.dom.shadowBottomLeft);
9493  this.dom.rightContainer.appendChild(this.dom.shadowTopRight);
9494  this.dom.rightContainer.appendChild(this.dom.shadowBottomRight);
9495
9496  // size properties of each of the panels
9497  this.props = {
9498    root: {},
9499    background: {},
9500    centerContainer: {},
9501    leftContainer: {},
9502    rightContainer: {},
9503    center: {},
9504    left: {},
9505    right: {},
9506    top: {},
9507    bottom: {},
9508    border: {},
9509    scrollTop: 0,
9510    scrollTopMin: 0
9511  };
9512
9513  this.on('rangechange', function () {
9514    if (this.initialDrawDone === true) {
9515      this._redraw();
9516    }
9517  }.bind(this));
9518  this.on('rangechanged', function () {
9519    if (!this.initialRangeChangeDone) {
9520      this.initialRangeChangeDone = true;
9521    }
9522  }.bind(this));
9523  this.on('touch', this._onTouch.bind(this));
9524  this.on('panmove', this._onDrag.bind(this));
9525
9526  var me = this;
9527  this._origRedraw = this._redraw.bind(this);
9528  this._redraw = util.throttle(this._origRedraw);
9529
9530  this.on('_change', function (properties) {
9531    if (me.itemSet && me.itemSet.initialItemSetDrawn && properties && properties.queue == true) {
9532      me._redraw();
9533    } else {
9534      me._origRedraw();
9535    }
9536  });
9537
9538  // create event listeners for all interesting events, these events will be
9539  // emitted via emitter
9540  this.hammer = new Hammer(this.dom.root);
9541  var pinchRecognizer = this.hammer.get('pinch').set({ enable: true });
9542  hammerUtil.disablePreventDefaultVertically(pinchRecognizer);
9543  this.hammer.get('pan').set({ threshold: 5, direction: Hammer.DIRECTION_HORIZONTAL });
9544  this.listeners = {};
9545
9546  var events = ['tap', 'doubletap', 'press', 'pinch', 'pan', 'panstart', 'panmove', 'panend'
9547  // TODO: cleanup
9548  //'touch', 'pinch',
9549  //'tap', 'doubletap', 'hold',
9550  //'dragstart', 'drag', 'dragend',
9551  //'mousewheel', 'DOMMouseScroll' // DOMMouseScroll is needed for Firefox
9552  ];
9553  events.forEach(function (type) {
9554    var listener = function listener(event) {
9555      if (me.isActive()) {
9556        me.emit(type, event);
9557      }
9558    };
9559    me.hammer.on(type, listener);
9560    me.listeners[type] = listener;
9561  });
9562
9563  // emulate a touch event (emitted before the start of a pan, pinch, tap, or press)
9564  hammerUtil.onTouch(this.hammer, function (event) {
9565    me.emit('touch', event);
9566  }.bind(this));
9567
9568  // emulate a release event (emitted after a pan, pinch, tap, or press)
9569  hammerUtil.onRelease(this.hammer, function (event) {
9570    me.emit('release', event);
9571  }.bind(this));
9572
9573  /**
9574   *
9575   * @param {WheelEvent} event
9576   */
9577  function onMouseWheel(event) {
9578    if (this.isActive()) {
9579      this.emit('mousewheel', event);
9580    }
9581
9582    // deltaX and deltaY normalization from jquery.mousewheel.js
9583    var deltaX = 0;
9584    var deltaY = 0;
9585
9586    // Old school scrollwheel delta
9587    if ('detail' in event) {
9588      deltaY = event.detail * -1;
9589    }
9590    if ('wheelDelta' in event) {
9591      deltaY = event.wheelDelta;
9592    }
9593    if ('wheelDeltaY' in event) {
9594      deltaY = event.wheelDeltaY;
9595    }
9596    if ('wheelDeltaX' in event) {
9597      deltaX = event.wheelDeltaX * -1;
9598    }
9599
9600    // Firefox < 17 horizontal scrolling related to DOMMouseScroll event
9601    if ('axis' in event && event.axis === event.HORIZONTAL_AXIS) {
9602      deltaX = deltaY * -1;
9603      deltaY = 0;
9604    }
9605
9606    // New school wheel delta (wheel event)
9607    if ('deltaY' in event) {
9608      deltaY = event.deltaY * -1;
9609    }
9610    if ('deltaX' in event) {
9611      deltaX = event.deltaX;
9612    }
9613
9614    // prevent scrolling when zoomKey defined or activated
9615    if (!this.options.zoomKey || event[this.options.zoomKey]) return;
9616
9617    // Prevent default actions caused by mouse wheel
9618    // (else the page and timeline both scroll)
9619    event.preventDefault();
9620
9621    if (this.options.verticalScroll && Math.abs(deltaY) >= Math.abs(deltaX)) {
9622      var current = this.props.scrollTop;
9623      var adjusted = current + deltaY;
9624
9625      if (this.isActive()) {
9626        this._setScrollTop(adjusted);
9627        this._redraw();
9628        this.emit('scroll', event);
9629      }
9630    } else if (this.options.horizontalScroll) {
9631      var delta = Math.abs(deltaX) >= Math.abs(deltaY) ? deltaX : deltaY;
9632
9633      // calculate a single scroll jump relative to the range scale
9634      var diff = delta / 120 * (this.range.end - this.range.start) / 20;
9635      // calculate new start and end
9636      var newStart = this.range.start + diff;
9637      var newEnd = this.range.end + diff;
9638
9639      var options = {
9640        animation: false,
9641        byUser: true,
9642        event: event
9643      };
9644      this.range.setRange(newStart, newEnd, options);
9645    }
9646  }
9647
9648  if (this.dom.centerContainer.addEventListener) {
9649    // IE9, Chrome, Safari, Opera
9650    this.dom.centerContainer.addEventListener("mousewheel", onMouseWheel.bind(this), false);
9651    // Firefox
9652    this.dom.centerContainer.addEventListener("DOMMouseScroll", onMouseWheel.bind(this), false);
9653  } else {
9654    // IE 6/7/8
9655    this.dom.centerContainer.attachEvent("onmousewheel", onMouseWheel.bind(this));
9656  }
9657
9658  /**
9659   *
9660   * @param {scroll} event
9661   */
9662  function onMouseScrollSide(event) {
9663    if (!me.options.verticalScroll) return;
9664    event.preventDefault();
9665    if (me.isActive()) {
9666      var adjusted = -event.target.scrollTop;
9667      me._setScrollTop(adjusted);
9668      me._redraw();
9669      me.emit('scrollSide', event);
9670    }
9671  }
9672
9673  this.dom.left.parentNode.addEventListener('scroll', onMouseScrollSide.bind(this));
9674  this.dom.right.parentNode.addEventListener('scroll', onMouseScrollSide.bind(this));
9675
9676  var itemAddedToTimeline = false;
9677
9678  /**
9679   *
9680   * @param {dragover} event
9681   * @returns {boolean}
9682   */
9683  function handleDragOver(event) {
9684    if (event.preventDefault) {
9685      event.preventDefault(); // Necessary. Allows us to drop.
9686    }
9687
9688    // make sure your target is a vis element
9689    if (!event.target.className.indexOf("vis") > -1) return;
9690
9691    // make sure only one item is added every time you're over the timeline
9692    if (itemAddedToTimeline) return;
9693
9694    event.dataTransfer.dropEffect = 'move';
9695    itemAddedToTimeline = true;
9696    return false;
9697  }
9698
9699  /**
9700   *
9701   * @param {drop} event
9702   * @returns {boolean}
9703   */
9704  function handleDrop(event) {
9705    // prevent redirect to blank page - Firefox
9706    if (event.preventDefault) {
9707      event.preventDefault();
9708    }
9709    if (event.stopPropagation) {
9710      event.stopPropagation();
9711    }
9712    // return when dropping non-vis items
9713    try {
9714      var itemData = JSON.parse(event.dataTransfer.getData("text"));
9715      if (!itemData || !itemData.content) return;
9716    } catch (err) {
9717      return false;
9718    }
9719
9720    itemAddedToTimeline = false;
9721    event.center = {
9722      x: event.clientX,
9723      y: event.clientY
9724    };
9725
9726    if (itemData.target !== 'item') {
9727      me.itemSet._onAddItem(event);
9728    } else {
9729      me.itemSet._onDropObjectOnItem(event);
9730    }
9731    me.emit('drop', me.getEventProperties(event));
9732    return false;
9733  }
9734
9735  this.dom.center.addEventListener('dragover', handleDragOver.bind(this), false);
9736  this.dom.center.addEventListener('drop', handleDrop.bind(this), false);
9737
9738  this.customTimes = [];
9739
9740  // store state information needed for touch events
9741  this.touch = {};
9742
9743  this.redrawCount = 0;
9744  this.initialDrawDone = false;
9745  this.initialRangeChangeDone = false;
9746
9747  // attach the root panel to the provided container
9748  if (!container) throw new Error('No container provided');
9749  container.appendChild(this.dom.root);
9750};
9751
9752/**
9753 * Set options. Options will be passed to all components loaded in the Timeline.
9754 * @param {Object} [options]
9755 *                           {String} orientation
9756 *                              Vertical orientation for the Timeline,
9757 *                              can be 'bottom' (default) or 'top'.
9758 *                           {string | number} width
9759 *                              Width for the timeline, a number in pixels or
9760 *                              a css string like '1000px' or '75%'. '100%' by default.
9761 *                           {string | number} height
9762 *                              Fixed height for the Timeline, a number in pixels or
9763 *                              a css string like '400px' or '75%'. If undefined,
9764 *                              The Timeline will automatically size such that
9765 *                              its contents fit.
9766 *                           {string | number} minHeight
9767 *                              Minimum height for the Timeline, a number in pixels or
9768 *                              a css string like '400px' or '75%'.
9769 *                           {string | number} maxHeight
9770 *                              Maximum height for the Timeline, a number in pixels or
9771 *                              a css string like '400px' or '75%'.
9772 *                           {number | Date | string} start
9773 *                              Start date for the visible window
9774 *                           {number | Date | string} end
9775 *                              End date for the visible window
9776 */
9777Core.prototype.setOptions = function (options) {
9778  if (options) {
9779    // copy the known options
9780    var fields = ['width', 'height', 'minHeight', 'maxHeight', 'autoResize', 'start', 'end', 'clickToUse', 'dataAttributes', 'hiddenDates', 'locale', 'locales', 'moment', 'rtl', 'zoomKey', 'horizontalScroll', 'verticalScroll'];
9781    util.selectiveExtend(fields, this.options, options);
9782
9783    this.dom.rollingModeBtn.style.visibility = 'hidden';
9784
9785    if (this.options.rtl) {
9786      this.dom.container.style.direction = "rtl";
9787      this.dom.backgroundVertical.className = 'vis-panel vis-background vis-vertical-rtl';
9788    }
9789
9790    if (this.options.verticalScroll) {
9791      if (this.options.rtl) {
9792        this.dom.rightContainer.className = 'vis-panel vis-right vis-vertical-scroll';
9793      } else {
9794        this.dom.leftContainer.className = 'vis-panel vis-left vis-vertical-scroll';
9795      }
9796    }
9797
9798    if ((0, _typeof3['default'])(this.options.orientation) !== 'object') {
9799      this.options.orientation = { item: undefined, axis: undefined };
9800    }
9801    if ('orientation' in options) {
9802      if (typeof options.orientation === 'string') {
9803        this.options.orientation = {
9804          item: options.orientation,
9805          axis: options.orientation
9806        };
9807      } else if ((0, _typeof3['default'])(options.orientation) === 'object') {
9808        if ('item' in options.orientation) {
9809          this.options.orientation.item = options.orientation.item;
9810        }
9811        if ('axis' in options.orientation) {
9812          this.options.orientation.axis = options.orientation.axis;
9813        }
9814      }
9815    }
9816
9817    if (this.options.orientation.axis === 'both') {
9818      if (!this.timeAxis2) {
9819        var timeAxis2 = this.timeAxis2 = new TimeAxis(this.body);
9820        timeAxis2.setOptions = function (options) {
9821          var _options = options ? util.extend({}, options) : {};
9822          _options.orientation = 'top'; // override the orientation option, always top
9823          TimeAxis.prototype.setOptions.call(timeAxis2, _options);
9824        };
9825        this.components.push(timeAxis2);
9826      }
9827    } else {
9828      if (this.timeAxis2) {
9829        var index = this.components.indexOf(this.timeAxis2);
9830        if (index !== -1) {
9831          this.components.splice(index, 1);
9832        }
9833        this.timeAxis2.destroy();
9834        this.timeAxis2 = null;
9835      }
9836    }
9837
9838    // if the graph2d's drawPoints is a function delegate the callback to the onRender property
9839    if (typeof options.drawPoints == 'function') {
9840      options.drawPoints = {
9841        onRender: options.drawPoints
9842      };
9843    }
9844
9845    if ('hiddenDates' in this.options) {
9846      DateUtil.convertHiddenOptions(this.options.moment, this.body, this.options.hiddenDates);
9847    }
9848
9849    if ('clickToUse' in options) {
9850      if (options.clickToUse) {
9851        if (!this.activator) {
9852          this.activator = new Activator(this.dom.root);
9853        }
9854      } else {
9855        if (this.activator) {
9856          this.activator.destroy();
9857          delete this.activator;
9858        }
9859      }
9860    }
9861
9862    if ('showCustomTime' in options) {
9863      throw new Error('Option `showCustomTime` is deprecated. Create a custom time bar via timeline.addCustomTime(time [, id])');
9864    }
9865
9866    // enable/disable autoResize
9867    this._initAutoResize();
9868  }
9869
9870  // propagate options to all components
9871  this.components.forEach(function (component) {
9872    return component.setOptions(options);
9873  });
9874
9875  // enable/disable configure
9876  if ('configure' in options) {
9877    if (!this.configurator) {
9878      this.configurator = this._createConfigurator();
9879    }
9880
9881    this.configurator.setOptions(options.configure);
9882
9883    // collect the settings of all components, and pass them to the configuration system
9884    var appliedOptions = util.deepExtend({}, this.options);
9885    this.components.forEach(function (component) {
9886      util.deepExtend(appliedOptions, component.options);
9887    });
9888    this.configurator.setModuleOptions({ global: appliedOptions });
9889  }
9890
9891  this._redraw();
9892};
9893
9894/**
9895 * Returns true when the Timeline is active.
9896 * @returns {boolean}
9897 */
9898Core.prototype.isActive = function () {
9899  return !this.activator || this.activator.active;
9900};
9901
9902/**
9903 * Destroy the Core, clean up all DOM elements and event listeners.
9904 */
9905Core.prototype.destroy = function () {
9906  // unbind datasets
9907  this.setItems(null);
9908  this.setGroups(null);
9909
9910  // remove all event listeners
9911  this.off();
9912
9913  // stop checking for changed size
9914  this._stopAutoResize();
9915
9916  // remove from DOM
9917  if (this.dom.root.parentNode) {
9918    this.dom.root.parentNode.removeChild(this.dom.root);
9919  }
9920  this.dom = null;
9921
9922  // remove Activator
9923  if (this.activator) {
9924    this.activator.destroy();
9925    delete this.activator;
9926  }
9927
9928  // cleanup hammer touch events
9929  for (var event in this.listeners) {
9930    if (this.listeners.hasOwnProperty(event)) {
9931      delete this.listeners[event];
9932    }
9933  }
9934  this.listeners = null;
9935  this.hammer = null;
9936
9937  // give all components the opportunity to cleanup
9938  this.components.forEach(function (component) {
9939    return component.destroy();
9940  });
9941
9942  this.body = null;
9943};
9944
9945/**
9946 * Set a custom time bar
9947 * @param {Date} time
9948 * @param {number} [id=undefined] Optional id of the custom time bar to be adjusted.
9949 */
9950Core.prototype.setCustomTime = function (time, id) {
9951  var customTimes = this.customTimes.filter(function (component) {
9952    return id === component.options.id;
9953  });
9954
9955  if (customTimes.length === 0) {
9956    throw new Error('No custom time bar found with id ' + (0, _stringify2['default'])(id));
9957  }
9958
9959  if (customTimes.length > 0) {
9960    customTimes[0].setCustomTime(time);
9961  }
9962};
9963
9964/**
9965 * Retrieve the current custom time.
9966 * @param {number} [id=undefined]    Id of the custom time bar.
9967 * @return {Date | undefined} customTime
9968 */
9969Core.prototype.getCustomTime = function (id) {
9970  var customTimes = this.customTimes.filter(function (component) {
9971    return component.options.id === id;
9972  });
9973
9974  if (customTimes.length === 0) {
9975    throw new Error('No custom time bar found with id ' + (0, _stringify2['default'])(id));
9976  }
9977  return customTimes[0].getCustomTime();
9978};
9979
9980/**
9981 * Set a custom title for the custom time bar.
9982 * @param {string} [title] Custom title
9983 * @param {number} [id=undefined]    Id of the custom time bar.
9984 * @returns {*}
9985 */
9986Core.prototype.setCustomTimeTitle = function (title, id) {
9987  var customTimes = this.customTimes.filter(function (component) {
9988    return component.options.id === id;
9989  });
9990
9991  if (customTimes.length === 0) {
9992    throw new Error('No custom time bar found with id ' + (0, _stringify2['default'])(id));
9993  }
9994  if (customTimes.length > 0) {
9995    return customTimes[0].setCustomTitle(title);
9996  }
9997};
9998
9999/**
10000 * Retrieve meta information from an event.
10001 * Should be overridden by classes extending Core
10002 * @param {Event} event
10003 * @return {Object} An object with related information.
10004 */
10005Core.prototype.getEventProperties = function (event) {
10006  return { event: event };
10007};
10008
10009/**
10010 * Add custom vertical bar
10011 * @param {Date | string | number} [time]  A Date, unix timestamp, or
10012 *                                         ISO date string. Time point where
10013 *                                         the new bar should be placed.
10014 *                                         If not provided, `new Date()` will
10015 *                                         be used.
10016 * @param {number | string} [id=undefined] Id of the new bar. Optional
10017 * @return {number | string}               Returns the id of the new bar
10018 */
10019Core.prototype.addCustomTime = function (time, id) {
10020  var timestamp = time !== undefined ? util.convert(time, 'Date').valueOf() : new Date();
10021
10022  var exists = this.customTimes.some(function (customTime) {
10023    return customTime.options.id === id;
10024  });
10025  if (exists) {
10026    throw new Error('A custom time with id ' + (0, _stringify2['default'])(id) + ' already exists');
10027  }
10028
10029  var customTime = new CustomTime(this.body, util.extend({}, this.options, {
10030    time: timestamp,
10031    id: id
10032  }));
10033
10034  this.customTimes.push(customTime);
10035  this.components.push(customTime);
10036  this._redraw();
10037
10038  return id;
10039};
10040
10041/**
10042 * Remove previously added custom bar
10043 * @param {int} id ID of the custom bar to be removed
10044 * [at]returns {boolean} True if the bar exists and is removed, false otherwise
10045 */
10046Core.prototype.removeCustomTime = function (id) {
10047  var customTimes = this.customTimes.filter(function (bar) {
10048    return bar.options.id === id;
10049  });
10050
10051  if (customTimes.length === 0) {
10052    throw new Error('No custom time bar found with id ' + (0, _stringify2['default'])(id));
10053  }
10054
10055  customTimes.forEach(function (customTime) {
10056    this.customTimes.splice(this.customTimes.indexOf(customTime), 1);
10057    this.components.splice(this.components.indexOf(customTime), 1);
10058    customTime.destroy();
10059  }.bind(this));
10060};
10061
10062/**
10063 * Get the id's of the currently visible items.
10064 * @returns {Array} The ids of the visible items
10065 */
10066Core.prototype.getVisibleItems = function () {
10067  return this.itemSet && this.itemSet.getVisibleItems() || [];
10068};
10069
10070/**
10071 * Set Core window such that it fits all items
10072 * @param {Object} [options]  Available options:
10073 *                                `animation: boolean | {duration: number, easingFunction: string}`
10074 *                                    If true (default), the range is animated
10075 *                                    smoothly to the new window. An object can be
10076 *                                    provided to specify duration and easing function.
10077 *                                    Default duration is 500 ms, and default easing
10078 *                                    function is 'easeInOutQuad'.
10079 * @param {function} [callback] a callback funtion to be executed at the end of this function
10080 */
10081Core.prototype.fit = function (options, callback) {
10082  var range = this.getDataRange();
10083
10084  // skip range set if there is no min and max date
10085  if (range.min === null && range.max === null) {
10086    return;
10087  }
10088
10089  // apply a margin of 1% left and right of the data
10090  var interval = range.max - range.min;
10091  var min = new Date(range.min.valueOf() - interval * 0.01);
10092  var max = new Date(range.max.valueOf() + interval * 0.01);
10093  var animation = options && options.animation !== undefined ? options.animation : true;
10094  this.range.setRange(min, max, { animation: animation }, callback);
10095};
10096
10097/**
10098 * Calculate the data range of the items start and end dates
10099 * [at]returns {{min: [Date], max: [Date]}}
10100 * @protected
10101 */
10102Core.prototype.getDataRange = function () {
10103  // must be implemented by Timeline and Graph2d
10104  throw new Error('Cannot invoke abstract method getDataRange');
10105};
10106
10107/**
10108 * Set the visible window. Both parameters are optional, you can change only
10109 * start or only end. Syntax:
10110 *
10111 *     TimeLine.setWindow(start, end)
10112 *     TimeLine.setWindow(start, end, options)
10113 *     TimeLine.setWindow(range)
10114 *
10115 * Where start and end can be a Date, number, or string, and range is an
10116 * object with properties start and end.
10117 *
10118 * @param {Date | number | string | Object} [start] Start date of visible window
10119 * @param {Date | number | string} [end]            End date of visible window
10120 * @param {Object} [options]  Available options:
10121 *                                `animation: boolean | {duration: number, easingFunction: string}`
10122 *                                    If true (default), the range is animated
10123 *                                    smoothly to the new window. An object can be
10124 *                                    provided to specify duration and easing function.
10125 *                                    Default duration is 500 ms, and default easing
10126 *                                    function is 'easeInOutQuad'.
10127 * @param {function} [callback] a callback funtion to be executed at the end of this function
10128 */
10129Core.prototype.setWindow = function (start, end, options, callback) {
10130  if (typeof arguments[2] == "function") {
10131    callback = arguments[2];
10132    options = {};
10133  }
10134  var animation;
10135  var range;
10136  if (arguments.length == 1) {
10137    range = arguments[0];
10138    animation = range.animation !== undefined ? range.animation : true;
10139    this.range.setRange(range.start, range.end, { animation: animation });
10140  } else if (arguments.length == 2 && typeof arguments[1] == "function") {
10141    range = arguments[0];
10142    callback = arguments[1];
10143    animation = range.animation !== undefined ? range.animation : true;
10144    this.range.setRange(range.start, range.end, { animation: animation }, callback);
10145  } else {
10146    animation = options && options.animation !== undefined ? options.animation : true;
10147    this.range.setRange(start, end, { animation: animation }, callback);
10148  }
10149};
10150
10151/**
10152 * Move the window such that given time is centered on screen.
10153 * @param {Date | number | string} time
10154 * @param {Object} [options]  Available options:
10155 *                                `animation: boolean | {duration: number, easingFunction: string}`
10156 *                                    If true (default), the range is animated
10157 *                                    smoothly to the new window. An object can be
10158 *                                    provided to specify duration and easing function.
10159 *                                    Default duration is 500 ms, and default easing
10160 *                                    function is 'easeInOutQuad'.
10161 * @param {function} [callback] a callback funtion to be executed at the end of this function
10162 */
10163Core.prototype.moveTo = function (time, options, callback) {
10164  if (typeof arguments[1] == "function") {
10165    callback = arguments[1];
10166    options = {};
10167  }
10168  var interval = this.range.end - this.range.start;
10169  var t = util.convert(time, 'Date').valueOf();
10170
10171  var start = t - interval / 2;
10172  var end = t + interval / 2;
10173  var animation = options && options.animation !== undefined ? options.animation : true;
10174
10175  this.range.setRange(start, end, { animation: animation }, callback);
10176};
10177
10178/**
10179 * Get the visible window
10180 * @return {{start: Date, end: Date}}   Visible range
10181 */
10182Core.prototype.getWindow = function () {
10183  var range = this.range.getRange();
10184  return {
10185    start: new Date(range.start),
10186    end: new Date(range.end)
10187  };
10188};
10189
10190/**
10191 * Zoom in the window such that given time is centered on screen.
10192 * @param {number} percentage - must be between [0..1]
10193 * @param {Object} [options]  Available options:
10194 *                                `animation: boolean | {duration: number, easingFunction: string}`
10195 *                                    If true (default), the range is animated
10196 *                                    smoothly to the new window. An object can be
10197 *                                    provided to specify duration and easing function.
10198 *                                    Default duration is 500 ms, and default easing
10199 *                                    function is 'easeInOutQuad'.
10200 * @param {function} [callback] a callback funtion to be executed at the end of this function
10201 */
10202Core.prototype.zoomIn = function (percentage, options, callback) {
10203  if (!percentage || percentage < 0 || percentage > 1) return;
10204  if (typeof arguments[1] == "function") {
10205    callback = arguments[1];
10206    options = {};
10207  }
10208  var range = this.getWindow();
10209  var start = range.start.valueOf();
10210  var end = range.end.valueOf();
10211  var interval = end - start;
10212  var newInterval = interval / (1 + percentage);
10213  var distance = (interval - newInterval) / 2;
10214  var newStart = start + distance;
10215  var newEnd = end - distance;
10216
10217  this.setWindow(newStart, newEnd, options, callback);
10218};
10219
10220/**
10221 * Zoom out the window such that given time is centered on screen.
10222 * @param {number} percentage - must be between [0..1]
10223 * @param {Object} [options]  Available options:
10224 *                                `animation: boolean | {duration: number, easingFunction: string}`
10225 *                                    If true (default), the range is animated
10226 *                                    smoothly to the new window. An object can be
10227 *                                    provided to specify duration and easing function.
10228 *                                    Default duration is 500 ms, and default easing
10229 *                                    function is 'easeInOutQuad'.
10230 * @param {function} [callback] a callback funtion to be executed at the end of this function
10231 */
10232Core.prototype.zoomOut = function (percentage, options, callback) {
10233  if (!percentage || percentage < 0 || percentage > 1) return;
10234  if (typeof arguments[1] == "function") {
10235    callback = arguments[1];
10236    options = {};
10237  }
10238  var range = this.getWindow();
10239  var start = range.start.valueOf();
10240  var end = range.end.valueOf();
10241  var interval = end - start;
10242  var newStart = start - interval * percentage / 2;
10243  var newEnd = end + interval * percentage / 2;
10244
10245  this.setWindow(newStart, newEnd, options, callback);
10246};
10247
10248/**
10249 * Force a redraw. Can be overridden by implementations of Core
10250 *
10251 * Note: this function will be overridden on construction with a trottled version
10252 */
10253Core.prototype.redraw = function () {
10254  this._redraw();
10255};
10256
10257/**
10258 * Redraw for internal use. Redraws all components. See also the public
10259 * method redraw.
10260 * @protected
10261 */
10262Core.prototype._redraw = function () {
10263  this.redrawCount++;
10264  var resized = false;
10265  var options = this.options;
10266  var props = this.props;
10267  var dom = this.dom;
10268
10269  if (!dom || !dom.container || dom.root.offsetWidth == 0) return; // when destroyed, or invisible
10270
10271  DateUtil.updateHiddenDates(this.options.moment, this.body, this.options.hiddenDates);
10272
10273  // update class names
10274  if (options.orientation == 'top') {
10275    util.addClassName(dom.root, 'vis-top');
10276    util.removeClassName(dom.root, 'vis-bottom');
10277  } else {
10278    util.removeClassName(dom.root, 'vis-top');
10279    util.addClassName(dom.root, 'vis-bottom');
10280  }
10281
10282  // update root width and height options
10283  dom.root.style.maxHeight = util.option.asSize(options.maxHeight, '');
10284  dom.root.style.minHeight = util.option.asSize(options.minHeight, '');
10285  dom.root.style.width = util.option.asSize(options.width, '');
10286
10287  // calculate border widths
10288  props.border.left = (dom.centerContainer.offsetWidth - dom.centerContainer.clientWidth) / 2;
10289  props.border.right = props.border.left;
10290  props.border.top = (dom.centerContainer.offsetHeight - dom.centerContainer.clientHeight) / 2;
10291  props.border.bottom = props.border.top;
10292  props.borderRootHeight = dom.root.offsetHeight - dom.root.clientHeight;
10293  props.borderRootWidth = dom.root.offsetWidth - dom.root.clientWidth;
10294
10295  // workaround for a bug in IE: the clientWidth of an element with
10296  // a height:0px and overflow:hidden is not calculated and always has value 0
10297  if (dom.centerContainer.clientHeight === 0) {
10298    props.border.left = props.border.top;
10299    props.border.right = props.border.left;
10300  }
10301  if (dom.root.clientHeight === 0) {
10302    props.borderRootWidth = props.borderRootHeight;
10303  }
10304
10305  // calculate the heights. If any of the side panels is empty, we set the height to
10306  // minus the border width, such that the border will be invisible
10307  props.center.height = dom.center.offsetHeight;
10308  props.left.height = dom.left.offsetHeight;
10309  props.right.height = dom.right.offsetHeight;
10310  props.top.height = dom.top.clientHeight || -props.border.top;
10311  props.bottom.height = dom.bottom.clientHeight || -props.border.bottom;
10312
10313  // TODO: compensate borders when any of the panels is empty.
10314
10315  // apply auto height
10316  // TODO: only calculate autoHeight when needed (else we cause an extra reflow/repaint of the DOM)
10317  var contentHeight = Math.max(props.left.height, props.center.height, props.right.height);
10318  var autoHeight = props.top.height + contentHeight + props.bottom.height + props.borderRootHeight + props.border.top + props.border.bottom;
10319  dom.root.style.height = util.option.asSize(options.height, autoHeight + 'px');
10320
10321  // calculate heights of the content panels
10322  props.root.height = dom.root.offsetHeight;
10323  props.background.height = props.root.height - props.borderRootHeight;
10324  var containerHeight = props.root.height - props.top.height - props.bottom.height - props.borderRootHeight;
10325  props.centerContainer.height = containerHeight;
10326  props.leftContainer.height = containerHeight;
10327  props.rightContainer.height = props.leftContainer.height;
10328
10329  // calculate the widths of the panels
10330  props.root.width = dom.root.offsetWidth;
10331  props.background.width = props.root.width - props.borderRootWidth;
10332
10333  if (!this.initialDrawDone) {
10334    props.scrollbarWidth = util.getScrollBarWidth();
10335  }
10336
10337  if (options.verticalScroll) {
10338    if (options.rtl) {
10339      props.left.width = dom.leftContainer.clientWidth || -props.border.left;
10340      props.right.width = dom.rightContainer.clientWidth + props.scrollbarWidth || -props.border.right;
10341    } else {
10342      props.left.width = dom.leftContainer.clientWidth + props.scrollbarWidth || -props.border.left;
10343      props.right.width = dom.rightContainer.clientWidth || -props.border.right;
10344    }
10345  } else {
10346    props.left.width = dom.leftContainer.clientWidth || -props.border.left;
10347    props.right.width = dom.rightContainer.clientWidth || -props.border.right;
10348  }
10349
10350  this._setDOM();
10351
10352  // update the scrollTop, feasible range for the offset can be changed
10353  // when the height of the Core or of the contents of the center changed
10354  var offset = this._updateScrollTop();
10355
10356  // reposition the scrollable contents
10357  if (options.orientation.item != 'top') {
10358    offset += Math.max(props.centerContainer.height - props.center.height - props.border.top - props.border.bottom, 0);
10359  }
10360  dom.center.style.top = offset + 'px';
10361
10362  // show shadows when vertical scrolling is available
10363  var visibilityTop = props.scrollTop == 0 ? 'hidden' : '';
10364  var visibilityBottom = props.scrollTop == props.scrollTopMin ? 'hidden' : '';
10365  dom.shadowTop.style.visibility = visibilityTop;
10366  dom.shadowBottom.style.visibility = visibilityBottom;
10367  dom.shadowTopLeft.style.visibility = visibilityTop;
10368  dom.shadowBottomLeft.style.visibility = visibilityBottom;
10369  dom.shadowTopRight.style.visibility = visibilityTop;
10370  dom.shadowBottomRight.style.visibility = visibilityBottom;
10371
10372  if (options.verticalScroll) {
10373    dom.rightContainer.className = 'vis-panel vis-right vis-vertical-scroll';
10374    dom.leftContainer.className = 'vis-panel vis-left vis-vertical-scroll';
10375
10376    dom.shadowTopRight.style.visibility = "hidden";
10377    dom.shadowBottomRight.style.visibility = "hidden";
10378    dom.shadowTopLeft.style.visibility = "hidden";
10379    dom.shadowBottomLeft.style.visibility = "hidden";
10380
10381    dom.left.style.top = '0px';
10382    dom.right.style.top = '0px';
10383  }
10384
10385  if (!options.verticalScroll || props.center.height < props.centerContainer.height) {
10386    dom.left.style.top = offset + 'px';
10387    dom.right.style.top = offset + 'px';
10388    dom.rightContainer.className = dom.rightContainer.className.replace(new RegExp('(?:^|\\s)' + 'vis-vertical-scroll' + '(?:\\s|$)'), ' ');
10389    dom.leftContainer.className = dom.leftContainer.className.replace(new RegExp('(?:^|\\s)' + 'vis-vertical-scroll' + '(?:\\s|$)'), ' ');
10390    props.left.width = dom.leftContainer.clientWidth || -props.border.left;
10391    props.right.width = dom.rightContainer.clientWidth || -props.border.right;
10392    this._setDOM();
10393  }
10394
10395  // enable/disable vertical panning
10396  var contentsOverflow = props.center.height > props.centerContainer.height;
10397  this.hammer.get('pan').set({
10398    direction: contentsOverflow ? Hammer.DIRECTION_ALL : Hammer.DIRECTION_HORIZONTAL
10399  });
10400
10401  // redraw all components
10402  this.components.forEach(function (component) {
10403    resized = component.redraw() || resized;
10404  });
10405  var MAX_REDRAW = 5;
10406  if (resized) {
10407    if (this.redrawCount < MAX_REDRAW) {
10408      this.body.emitter.emit('_change');
10409      return;
10410    } else {
10411      console.log('WARNING: infinite loop in redraw?');
10412    }
10413  } else {
10414    this.redrawCount = 0;
10415  }
10416
10417  //Emit public 'changed' event for UI updates, see issue #1592
10418  this.body.emitter.emit("changed");
10419};
10420
10421Core.prototype._setDOM = function () {
10422  var props = this.props;
10423  var dom = this.dom;
10424
10425  props.leftContainer.width = props.left.width;
10426  props.rightContainer.width = props.right.width;
10427  var centerWidth = props.root.width - props.left.width - props.right.width - props.borderRootWidth;
10428  props.center.width = centerWidth;
10429  props.centerContainer.width = centerWidth;
10430  props.top.width = centerWidth;
10431  props.bottom.width = centerWidth;
10432
10433  // resize the panels
10434  dom.background.style.height = props.background.height + 'px';
10435  dom.backgroundVertical.style.height = props.background.height + 'px';
10436  dom.backgroundHorizontal.style.height = props.centerContainer.height + 'px';
10437  dom.centerContainer.style.height = props.centerContainer.height + 'px';
10438  dom.leftContainer.style.height = props.leftContainer.height + 'px';
10439  dom.rightContainer.style.height = props.rightContainer.height + 'px';
10440
10441  dom.background.style.width = props.background.width + 'px';
10442  dom.backgroundVertical.style.width = props.centerContainer.width + 'px';
10443  dom.backgroundHorizontal.style.width = props.background.width + 'px';
10444  dom.centerContainer.style.width = props.center.width + 'px';
10445  dom.top.style.width = props.top.width + 'px';
10446  dom.bottom.style.width = props.bottom.width + 'px';
10447
10448  // reposition the panels
10449  dom.background.style.left = '0';
10450  dom.background.style.top = '0';
10451  dom.backgroundVertical.style.left = props.left.width + props.border.left + 'px';
10452  dom.backgroundVertical.style.top = '0';
10453  dom.backgroundHorizontal.style.left = '0';
10454  dom.backgroundHorizontal.style.top = props.top.height + 'px';
10455  dom.centerContainer.style.left = props.left.width + 'px';
10456  dom.centerContainer.style.top = props.top.height + 'px';
10457  dom.leftContainer.style.left = '0';
10458  dom.leftContainer.style.top = props.top.height + 'px';
10459  dom.rightContainer.style.left = props.left.width + props.center.width + 'px';
10460  dom.rightContainer.style.top = props.top.height + 'px';
10461  dom.top.style.left = props.left.width + 'px';
10462  dom.top.style.top = '0';
10463  dom.bottom.style.left = props.left.width + 'px';
10464  dom.bottom.style.top = props.top.height + props.centerContainer.height + 'px';
10465  dom.center.style.left = '0';
10466  dom.left.style.left = '0';
10467  dom.right.style.left = '0';
10468};
10469
10470// TODO: deprecated since version 1.1.0, remove some day
10471Core.prototype.repaint = function () {
10472  throw new Error('Function repaint is deprecated. Use redraw instead.');
10473};
10474
10475/**
10476 * Set a current time. This can be used for example to ensure that a client's
10477 * time is synchronized with a shared server time.
10478 * Only applicable when option `showCurrentTime` is true.
10479 * @param {Date | string | number} time     A Date, unix timestamp, or
10480 *                                          ISO date string.
10481 */
10482Core.prototype.setCurrentTime = function (time) {
10483  if (!this.currentTime) {
10484    throw new Error('Option showCurrentTime must be true');
10485  }
10486
10487  this.currentTime.setCurrentTime(time);
10488};
10489
10490/**
10491 * Get the current time.
10492 * Only applicable when option `showCurrentTime` is true.
10493 * @return {Date} Returns the current time.
10494 */
10495Core.prototype.getCurrentTime = function () {
10496  if (!this.currentTime) {
10497    throw new Error('Option showCurrentTime must be true');
10498  }
10499
10500  return this.currentTime.getCurrentTime();
10501};
10502
10503/**
10504 * Convert a position on screen (pixels) to a datetime
10505 * @param {int}     x    Position on the screen in pixels
10506 * @return {Date}   time The datetime the corresponds with given position x
10507 * @protected
10508 */
10509// TODO: move this function to Range
10510Core.prototype._toTime = function (x) {
10511  return DateUtil.toTime(this, x, this.props.center.width);
10512};
10513
10514/**
10515 * Convert a position on the global screen (pixels) to a datetime
10516 * @param {int}     x    Position on the screen in pixels
10517 * @return {Date}   time The datetime the corresponds with given position x
10518 * @protected
10519 */
10520// TODO: move this function to Range
10521Core.prototype._toGlobalTime = function (x) {
10522  return DateUtil.toTime(this, x, this.props.root.width);
10523  //var conversion = this.range.conversion(this.props.root.width);
10524  //return new Date(x / conversion.scale + conversion.offset);
10525};
10526
10527/**
10528 * Convert a datetime (Date object) into a position on the screen
10529 * @param {Date}   time A date
10530 * @return {int}   x    The position on the screen in pixels which corresponds
10531 *                      with the given date.
10532 * @protected
10533 */
10534// TODO: move this function to Range
10535Core.prototype._toScreen = function (time) {
10536  return DateUtil.toScreen(this, time, this.props.center.width);
10537};
10538
10539/**
10540 * Convert a datetime (Date object) into a position on the root
10541 * This is used to get the pixel density estimate for the screen, not the center panel
10542 * @param {Date}   time A date
10543 * @return {int}   x    The position on root in pixels which corresponds
10544 *                      with the given date.
10545 * @protected
10546 */
10547// TODO: move this function to Range
10548Core.prototype._toGlobalScreen = function (time) {
10549  return DateUtil.toScreen(this, time, this.props.root.width);
10550  //var conversion = this.range.conversion(this.props.root.width);
10551  //return (time.valueOf() - conversion.offset) * conversion.scale;
10552};
10553
10554/**
10555 * Initialize watching when option autoResize is true
10556 * @private
10557 */
10558Core.prototype._initAutoResize = function () {
10559  if (this.options.autoResize == true) {
10560    this._startAutoResize();
10561  } else {
10562    this._stopAutoResize();
10563  }
10564};
10565
10566/**
10567 * Watch for changes in the size of the container. On resize, the Panel will
10568 * automatically redraw itself.
10569 * @private
10570 */
10571Core.prototype._startAutoResize = function () {
10572  var me = this;
10573
10574  this._stopAutoResize();
10575
10576  this._onResize = function () {
10577    if (me.options.autoResize != true) {
10578      // stop watching when the option autoResize is changed to fal
10578se
10579      me._stopAutoResize();
10580      return;
10581    }
10582
10583    if (me.dom.root) {
10584      // check whether the frame is resized
10585      // Note: we compare offsetWidth here, not clientWidth. For some reason,
10586      // IE does not restore the clientWidth from 0 to the actual width after
10587      // changing the timeline's container display style from none to visible
10588      if (me.dom.root.offsetWidth != me.props.lastWidth || me.dom.root.offsetHeight != me.props.lastHeight) {
10589        me.props.lastWidth = me.dom.root.offsetWidth;
10590        me.props.lastHeight = me.dom.root.offsetHeight;
10591        me.props.scrollbarWidth = util.getScrollBarWidth();
10592
10593        me.body.emitter.emit('_change');
10594      }
10595    }
10596  };
10597
10598  // add event listener to window resize
10599  util.addEventListener(window, 'resize', this._onResize);
10600
10601  //Prevent initial unnecessary redraw
10602  if (me.dom.root) {
10603    me.props.lastWidth = me.dom.root.offsetWidth;
10604    me.props.lastHeight = me.dom.root.offsetHeight;
10605  }
10606
10607  this.watchTimer = setInterval(this._onResize, 1000);
10608};
10609
10610/**
10611 * Stop watching for a resize of the frame.
10612 * @private
10613 */
10614Core.prototype._stopAutoResize = function () {
10615  if (this.watchTimer) {
10616    clearInterval(this.watchTimer);
10617    this.watchTimer = undefined;
10618  }
10619
10620  // remove event listener on window.resize
10621  if (this._onResize) {
10622    util.removeEventListener(window, 'resize', this._onResize);
10623    this._onResize = null;
10624  }
10625};
10626
10627/**
10628 * Start moving the timeline vertically
10629 * @param {Event} event
10630 * @private
10631 */
10632Core.prototype._onTouch = function (event) {
10633  // eslint-disable-line no-unused-vars
10634  this.touch.allowDragging = true;
10635  this.touch.initialScrollTop = this.props.scrollTop;
10636};
10637
10638/**
10639 * Start moving the timeline vertically
10640 * @param {Event} event
10641 * @private
10642 */
10643Core.prototype._onPinch = function (event) {
10644  // eslint-disable-line no-unused-vars
10645  this.touch.allowDragging = false;
10646};
10647
10648/**
10649 * Move the timeline vertically
10650 * @param {Event} event
10651 * @private
10652 */
10653Core.prototype._onDrag = function (event) {
10654  if (!event) return;
10655  // refuse to drag when we where pinching to prevent the timeline make a jump
10656  // when releasing the fingers in opposite order from the touch screen
10657  if (!this.touch.allowDragging) return;
10658
10659  var delta = event.deltaY;
10660
10661  var oldScrollTop = this._getScrollTop();
10662  var newScrollTop = this._setScrollTop(this.touch.initialScrollTop + delta);
10663
10664  if (this.options.verticalScroll) {
10665    this.dom.left.parentNode.scrollTop = -this.props.scrollTop;
10666    this.dom.right.parentNode.scrollTop = -this.props.scrollTop;
10667  }
10668
10669  if (newScrollTop != oldScrollTop) {
10670    this.emit("verticalDrag");
10671  }
10672};
10673
10674/**
10675 * Apply a scrollTop
10676 * @param {number} scrollTop
10677 * @returns {number} scrollTop  Returns the applied scrollTop
10678 * @private
10679 */
10680Core.prototype._setScrollTop = function (scrollTop) {
10681  this.props.scrollTop = scrollTop;
10682  this._updateScrollTop();
10683  return this.props.scrollTop;
10684};
10685
10686/**
10687 * Update the current scrollTop when the height of  the containers has been changed
10688 * @returns {number} scrollTop  Returns the applied scrollTop
10689 * @private
10690 */
10691Core.prototype._updateScrollTop = function () {
10692  // recalculate the scrollTopMin
10693  var scrollTopMin = Math.min(this.props.centerContainer.height - this.props.center.height, 0); // is negative or zero
10694  if (scrollTopMin != this.props.scrollTopMin) {
10695    // in case of bottom orientation, change the scrollTop such that the contents
10696    // do not move relative to the time axis at the bottom
10697    if (this.options.orientation.item != 'top') {
10698      this.props.scrollTop += scrollTopMin - this.props.scrollTopMin;
10699    }
10700    this.props.scrollTopMin = scrollTopMin;
10701  }
10702
10703  // limit the scrollTop to the feasible scroll range
10704  if (this.props.scrollTop > 0) this.props.scrollTop = 0;
10705  if (this.props.scrollTop < scrollTopMin) this.props.scrollTop = scrollTopMin;
10706
10707  if (this.options.verticalScroll) {
10708    this.dom.left.parentNode.scrollTop = -this.props.scrollTop;
10709    this.dom.right.parentNode.scrollTop = -this.props.scrollTop;
10710  }
10711
10712  return this.props.scrollTop;
10713};
10714
10715/**
10716 * Get the current scrollTop
10717 * @returns {number} scrollTop
10718 * @private
10719 */
10720Core.prototype._getScrollTop = function () {
10721  return this.props.scrollTop;
10722};
10723
10724/**
10725 * Load a configurator
10726 * [at]returns {Object}
10727 * @private
10728 */
10729Core.prototype._createConfigurator = function () {
10730  throw new Error('Cannot invoke abstract method _createConfigurator');
10731};
10732
10733module.exports = Core;
10734
10735/***/ }),
10736/* 66 */
10737/***/ (function(module, exports, __webpack_require__) {
10738
10739"use strict";
10740
10741
10742var moment = __webpack_require__(9);
10743var DateUtil = __webpack_require__(36);
10744var util = __webpack_require__(2);
10745
10746/**
10747 * The class TimeStep is an iterator for dates. You provide a start date and an
10748 * end date. The class itself determines the best scale (step size) based on the
10749 * provided start Date, end Date, and minimumStep.
10750 *
10751 * If minimumStep is provided, the step size is chosen as close as possible
10752 * to the minimumStep but larger than minimumStep. If minimumStep is not
10753 * provided, the scale is set to 1 DAY.
10754 * The minimumStep should correspond with the onscreen size of about 6 characters
10755 *
10756 * Alternatively, you can set a scale by hand.
10757 * After creation, you can initialize the class by executing first(). Then you
10758 * can iterate from the start date to the end date via next(). You can check if
10759 * the end date is reached with the function hasNext(). After each step, you can
10760 * retrieve the current date via getCurrent().
10761 * The TimeStep has scales ranging from milliseconds, seconds, minutes, hours,
10762 * days, to years.
10763 *
10764 * Version: 1.2
10765 *
10766 * @param {Date} [start]         The start date, for example new Date(2010, 9, 21)
10767 *                               or new Date(2010, 9, 21, 23, 45, 00)
10768 * @param {Date} [end]           The end date
10769 * @param {number} [minimumStep] Optional. Minimum step size in milliseconds
10770 * @param {Date|Array.<Date>} [hiddenDates] Optional.
10771 * @param {{showMajorLabels: boolean}} [options] Optional.
10772 * @constructor  TimeStep
10773 */
10774function TimeStep(start, end, minimumStep, hiddenDates, options) {
10775  this.moment = moment;
10776
10777  // variables
10778  this.current = this.moment();
10779  this._start = this.moment();
10780  this._end = this.moment();
10781
10782  this.autoScale = true;
10783  this.scale = 'day';
10784  this.step = 1;
10785
10786  // initialize the range
10787  this.setRange(start, end, minimumStep);
10788
10789  // hidden Dates options
10790  this.switchedDay = false;
10791  this.switchedMonth = false;
10792  this.switchedYear = false;
10793  if (Array.isArray(hiddenDates)) {
10794    this.hiddenDates = hiddenDates;
10795  } else if (hiddenDates != undefined) {
10796    this.hiddenDates = [hiddenDates];
10797  } else {
10798    this.hiddenDates = [];
10799  }
10800
10801  this.format = TimeStep.FORMAT; // default formatting
10802  this.options = options ? options : {};
10803}
10804
10805// Time formatting
10806TimeStep.FORMAT = {
10807  minorLabels: {
10808    millisecond: 'SSS',
10809    second: 's',
10810    minute: 'HH:mm',
10811    hour: 'HH:mm',
10812    weekday: 'ddd D',
10813    day: 'D',
10814    week: 'w',
10815    month: 'MMM',
10816    year: 'YYYY'
10817  },
10818  majorLabels: {
10819    millisecond: 'HH:mm:ss',
10820    second: 'D MMMM HH:mm',
10821    minute: 'ddd D MMMM',
10822    hour: 'ddd D MMMM',
10823    weekday: 'MMMM YYYY',
10824    day: 'MMMM YYYY',
10825    week: 'MMMM YYYY',
10826    month: 'YYYY',
10827    year: ''
10828  }
10829};
10830
10831/**
10832 * Set custom constructor function for moment. Can be used to set dates
10833 * to UTC or to set a utcOffset.
10834 * @param {function} moment
10835 */
10836TimeStep.prototype.setMoment = function (moment) {
10837  this.moment = moment;
10838
10839  // update the date properties, can have a new utcOffset
10840  this.current = this.moment(this.current.valueOf());
10841  this._start = this.moment(this._start.valueOf());
10842  this._end = this.moment(this._end.valueOf());
10843};
10844
10845/**
10846 * Set custom formatting for the minor an major labels of the TimeStep.
10847 * Both `minorLabels` and `majorLabels` are an Object with properties:
10848 * 'millisecond', 'second', 'minute', 'hour', 'weekday', 'day', 'week', 'month', 'year'.
10849 * @param {{minorLabels: Object, majorLabels: Object}} format
10850 */
10851TimeStep.prototype.setFormat = function (format) {
10852  var defaultFormat = util.deepExtend({}, TimeStep.FORMAT);
10853  this.format = util.deepExtend(defaultFormat, format);
10854};
10855
10856/**
10857 * Set a new range
10858 * If minimumStep is provided, the step size is chosen as close as possible
10859 * to the minimumStep but larger than minimumStep. If minimumStep is not
10860 * provided, the scale is set to 1 DAY.
10861 * The minimumStep should correspond with the onscreen size of about 6 characters
10862 * @param {Date} [start]      The start date and time.
10863 * @param {Date} [end]        The end date and time.
10864 * @param {int} [minimumStep] Optional. Minimum step size in milliseconds
10865 */
10866TimeStep.prototype.setRange = function (start, end, minimumStep) {
10867  if (!(start instanceof Date) || !(end instanceof Date)) {
10868    throw "No legal start or end date in method setRange";
10869  }
10870
10871  this._start = start != undefined ? this.moment(start.valueOf()) : new Date();
10872  this._end = end != undefined ? this.moment(end.valueOf()) : new Date();
10873
10874  if (this.autoScale) {
10875    this.setMinimumStep(minimumStep);
10876  }
10877};
10878
10879/**
10880 * Set the range iterator to the start date.
10881 */
10882TimeStep.prototype.start = function () {
10883  this.current = this._start.clone();
10884  this.roundToMinor();
10885};
10886
10887/**
10888 * Round the current date to the first minor date value
10889 * This must be executed once when the current date is set to start Date
10890 */
10891TimeStep.prototype.roundToMinor = function () {
10892  // round to floor
10893  // to prevent year & month scales rounding down to the first day of week we perform this separately
10894  if (this.scale == 'week') {
10895    this.current.weekday(0);
10896  }
10897  // IMPORTANT: we have no breaks in this switch! (this is no bug)
10898  // noinspection FallThroughInSwitchStatementJS
10899  switch (this.scale) {
10900    case 'year':
10901      this.current.year(this.step * Math.floor(this.current.year() / this.step));
10902      this.current.month(0);
10903    case 'month':
10904      this.current.date(1); // eslint-disable-line no-fallthrough
10905    case 'week': // eslint-disable-line no-fallthrough
10906    case 'day': // eslint-disable-line no-fallthrough
10907    case 'weekday':
10908      this.current.hours(0); // eslint-disable-line no-fallthrough
10909    case 'hour':
10910      this.current.minutes(0); // eslint-disable-line no-fallthrough
10911    case 'minute':
10912      this.current.seconds(0); // eslint-disable-line no-fallthrough
10913    case 'second':
10914      this.current.milliseconds(0); // eslint-disable-line no-fallthrough
10915    //case 'millisecond': // nothing to do for milliseconds
10916  }
10917
10918  if (this.step != 1) {
10919    // round down to the first minor value that is a multiple of the current step size
10920    switch (this.scale) {
10921      case 'millisecond':
10922        this.current.subtract(this.current.milliseconds() % this.step, 'milliseconds');break;
10923      case 'second':
10924        this.current.subtract(this.current.seconds() % this.step, 'seconds');break;
10925      case 'minute':
10926        this.current.subtract(this.current.minutes() % this.step, 'minutes');break;
10927      case 'hour':
10928        this.current.subtract(this.current.hours() % this.step, 'hours');break;
10929      case 'weekday': // intentional fall through
10930      case 'day':
10931        this.current.subtract((this.current.date() - 1) % this.step, 'day');break;
10932      case 'week':
10933        this.current.subtract(this.current.week() % this.step, 'week');break;
10934      case 'month':
10935        this.current.subtract(this.current.month() % this.step, 'month');break;
10936      case 'year':
10937        this.current.subtract(this.current.year() % this.step, 'year');break;
10938      default:
10939        break;
10940    }
10941  }
10942};
10943
10944/**
10945 * Check if the there is a next step
10946 * @return {boolean}  true if the current date has not passed the end date
10947 */
10948TimeStep.prototype.hasNext = function () {
10949  return this.current.valueOf() <= this._end.valueOf();
10950};
10951
10952/**
10953 * Do the next step
10954 */
10955TimeStep.prototype.next = function () {
10956  var prev = this.current.valueOf();
10957
10958  // Two cases, needed to prevent issues with switching daylight savings
10959  // (end of March and end of October)
10960  switch (this.scale) {
10961    case 'millisecond':
10962      this.current.add(this.step, 'millisecond');break;
10963    case 'second':
10964      this.current.add(this.step, 'second');break;
10965    case 'minute':
10966      this.current.add(this.step, 'minute');break;
10967    case 'hour':
10968      this.current.add(this.step, 'hour');
10969
10970      if (this.current.month() < 6) {
10971        this.current.subtract(this.current.hours() % this.step, 'hour');
10972      } else {
10973        if (this.current.hours() % this.step !== 0) {
10974          this.current.add(this.step - this.current.hours() % this.step, 'hour');
10975        }
10976      }
10977      break;
10978    case 'weekday': // intentional fall through
10979    case 'day':
10980      this.current.add(this.step, 'day');break;
10981    case 'week':
10982      if (this.current.weekday() !== 0) {
10983        // we had a month break not correlating with a week's start before
10984        this.current.weekday(0); // switch back to week cycles
10985        this.current.add(this.step, 'week');
10986      } else if (this.options.showMajorLabels === false) {
10987        this.current.add(this.step, 'week'); // the default case
10988      } else {
10989        // first day of the week
10990        var nextWeek = this.current.clone();
10991        nextWeek.add(1, 'week');
10992        if (nextWeek.isSame(this.current, 'month')) {
10993          // is the first day of the next week in the same month?
10994          this.current.add(this.step, 'week'); // the default case
10995        } else {
10996          // inject a step at each first day of the month
10997          this.current.add(this.step, 'week');
10998          this.current.date(1);
10999        }
11000      }
11001      break;
11002    case 'month':
11003      this.current.add(this.step, 'month');break;
11004    case 'year':
11005      this.current.add(this.step, 'year');break;
11006    default:
11007      break;
11008  }
11009
11010  if (this.step != 1) {
11011    // round down to the correct major value
11012    switch (this.scale) {
11013      case 'millisecond':
11014        if (this.current.milliseconds() > 0 && this.current.milliseconds() < this.step) this.current.milliseconds(0);break;
11015      case 'second':
11016        if (this.current.seconds() > 0 && this.current.seconds() < this.step) this.current.seconds(0);break;
11017      case 'minute':
11018        if (this.current.minutes() > 0 && this.current.minutes() < this.step) this.current.minutes(0);break;
11019      case 'hour':
11020        if (this.current.hours() > 0 && this.current.hours() < this.step) this.current.hours(0);break;
11021      case 'weekday': // intentional fall through
11022      case 'day':
11023        if (this.current.date() < this.step + 1) this.current.date(1);break;
11024      case 'week':
11025        if (this.current.week() < this.step) this.current.week(1);break; // week numbering starts at 1, not 0
11026      case 'month':
11027        if (this.current.month() < this.step) this.current.month(0);break;
11028      case 'year':
11029        break; // nothing to do for year
11030      default:
11031        break;
11032    }
11033  }
11034
11035  // safety mechanism: if current time is still unchanged, move to the end
11036  if (this.current.valueOf() == prev) {
11037    this.current = this._end.clone();
11038  }
11039
11040  // Reset switches for year, month and day. Will get set to true where appropriate in DateUtil.stepOverHiddenDates
11041  this.switchedDay = false;
11042  this.switchedMonth = false;
11043  this.switchedYear = false;
11044
11045  DateUtil.stepOverHiddenDates(this.moment, this, prev);
11046};
11047
11048/**
11049 * Get the current datetime
11050 * @return {Moment}  current The current date
11051 */
11052TimeStep.prototype.getCurrent = function () {
11053  return this.current;
11054};
11055
11056/**
11057 * Set a custom scale. Autoscaling will be disabled.
11058 * For example setScale('minute', 5) will result
11059 * in minor steps of 5 minutes, and major steps of an hour.
11060 *
11061 * @param {{scale: string, step: number}} params
11062 *                               An object containing two properties:
11063 *                               - A string 'scale'. Choose from 'millisecond', 'second',
11064 *                                 'minute', 'hour', 'weekday', 'day', 'week', 'month', 'year'.
11065 *                               - A number 'step'. A step size, by default 1.
11066 *                                 Choose for example 1, 2, 5, or 10.
11067 */
11068TimeStep.prototype.setScale = function (params) {
11069  if (params && typeof params.scale == 'string') {
11070    this.scale = params.scale;
11071    this.step = params.step > 0 ? params.step : 1;
11072    this.autoScale = false;
11073  }
11074};
11075
11076/**
11077 * Enable or disable autoscaling
11078 * @param {boolean} enable  If true, autoascaling is set true
11079 */
11080TimeStep.prototype.setAutoScale = function (enable) {
11081  this.autoScale = enable;
11082};
11083
11084/**
11085 * Automatically determine the scale that bests fits the provided minimum step
11086 * @param {number} [minimumStep]  The minimum step size in milliseconds
11087 */
11088TimeStep.prototype.setMinimumStep = function (minimumStep) {
11089  if (minimumStep == undefined) {
11090    return;
11091  }
11092
11093  //var b = asc + ds;
11094
11095  var stepYear = 1000 * 60 * 60 * 24 * 30 * 12;
11096  var stepMonth = 1000 * 60 * 60 * 24 * 30;
11097  var stepDay = 1000 * 60 * 60 * 24;
11098  var stepHour = 1000 * 60 * 60;
11099  var stepMinute = 1000 * 60;
11100  var stepSecond = 1000;
11101  var stepMillisecond = 1;
11102
11103  // find the smallest step that is larger than the provided minimumStep
11104  if (stepYear * 1000 > minimumStep) {
11105    this.scale = 'year';this.step = 1000;
11106  }
11107  if (stepYear * 500 > minimumStep) {
11108    this.scale = 'year';this.step = 500;
11109  }
11110  if (stepYear * 100 > minimumStep) {
11111    this.scale = 'year';this.step = 100;
11112  }
11113  if (stepYear * 50 > minimumStep) {
11114    this.scale = 'year';this.step = 50;
11115  }
11116  if (stepYear * 10 > minimumStep) {
11117    this.scale = 'year';this.step = 10;
11118  }
11119  if (stepYear * 5 > minimumStep) {
11120    this.scale = 'year';this.step = 5;
11121  }
11122  if (stepYear > minimumStep) {
11123    this.scale = 'year';this.step = 1;
11124  }
11125  if (stepMonth * 3 > minimumStep) {
11126    this.scale = 'month';this.step = 3;
11127  }
11128  if (stepMonth > minimumStep) {
11129    this.scale = 'month';this.step = 1;
11130  }
11131  if (stepDay * 5 > minimumStep) {
11132    this.scale = 'day';this.step = 5;
11133  }
11134  if (stepDay * 2 > minimumStep) {
11135    this.scale = 'day';this.step = 2;
11136  }
11137  if (stepDay > minimumStep) {
11138    this.scale = 'day';this.step = 1;
11139  }
11140  if (stepDay / 2 > minimumStep) {
11141    this.scale = 'weekday';this.step = 1;
11142  }
11143  if (stepHour * 4 > minimumStep) {
11144    this.scale = 'hour';this.step = 4;
11145  }
11146  if (stepHour > minimumStep) {
11147    this.scale = 'hour';this.step = 1;
11148  }
11149  if (stepMinute * 15 > minimumStep) {
11150    this.scale = 'minute';this.step = 15;
11151  }
11152  if (stepMinute * 10 > minimumStep) {
11153    this.scale = 'minute';this.step = 10;
11154  }
11155  if (stepMinute * 5 > minimumStep) {
11156    this.scale = 'minute';this.step = 5;
11157  }
11158  if (stepMinute > minimumStep) {
11159    this.scale = 'minute';this.step = 1;
11160  }
11161  if (stepSecond * 15 > minimumStep) {
11162    this.scale = 'second';this.step = 15;
11163  }
11164  if (stepSecond * 10 > minimumStep) {
11165    this.scale = 'second';this.step = 10;
11166  }
11167  if (stepSecond * 5 > minimumStep) {
11168    this.scale = 'second';this.step = 5;
11169  }
11170  if (stepSecond > minimumStep) {
11171    this.scale = 'second';this.step = 1;
11172  }
11173  if (stepMillisecond * 200 > minimumStep) {
11174    this.scale = 'millisecond';this.step = 200;
11175  }
11176  if (stepMillisecond * 100 > minimumStep) {
11177    this.scale = 'millisecond';this.step = 100;
11178  }
11179  if (stepMillisecond * 50 > minimumStep) {
11180    this.scale = 'millisecond';this.step = 50;
11181  }
11182  if (stepMillisecond * 10 > minimumStep) {
11183    this.scale = 'millisecond';this.step = 10;
11184  }
11185  if (stepMillisecond * 5 > minimumStep) {
11186    this.scale = 'millisecond';this.step = 5;
11187  }
11188  if (stepMillisecond > minimumStep) {
11189    this.scale = 'millisecond';this.step = 1;
11190  }
11191};
11192
11193/**
11194 * Snap a date to a rounded value.
11195 * The snap intervals are dependent on the current scale and step.
11196 * Static function
11197 * @param {Date} date    the date to be snapped.
11198 * @param {string}
11198 scale Current scale, can be 'millisecond', 'second',
11199 *                       'minute', 'hour', 'weekday, 'day', 'week', 'month', 'year'.
11200 * @param {number} step  Current step (1, 2, 4, 5, ...
11201 * @return {Date} snappedDate
11202 */
11203TimeStep.snap = function (date, scale, step) {
11204  var clone = moment(date);
11205
11206  if (scale == 'year') {
11207    var year = clone.year() + Math.round(clone.month() / 12);
11208    clone.year(Math.round(year / step) * step);
11209    clone.month(0);
11210    clone.date(0);
11211    clone.hours(0);
11212    clone.minutes(0);
11213    clone.seconds(0);
11214    clone.milliseconds(0);
11215  } else if (scale == 'month') {
11216    if (clone.date() > 15) {
11217      clone.date(1);
11218      clone.add(1, 'month');
11219      // important: first set Date to 1, after that change the month.
11220    } else {
11221      clone.date(1);
11222    }
11223
11224    clone.hours(0);
11225    clone.minutes(0);
11226    clone.seconds(0);
11227    clone.milliseconds(0);
11228  } else if (scale == 'week') {
11229    if (clone.weekday() > 2) {
11230      // doing it the momentjs locale aware way
11231      clone.weekday(0);
11232      clone.add(1, 'week');
11233    } else {
11234      clone.weekday(0);
11235    }
11236
11237    clone.hours(0);
11238    clone.minutes(0);
11239    clone.seconds(0);
11240    clone.milliseconds(0);
11241  } else if (scale == 'day') {
11242    //noinspection FallthroughInSwitchStatementJS
11243    switch (step) {
11244      case 5:
11245      case 2:
11246        clone.hours(Math.round(clone.hours() / 24) * 24);break;
11247      default:
11248        clone.hours(Math.round(clone.hours() / 12) * 12);break;
11249    }
11250    clone.minutes(0);
11251    clone.seconds(0);
11252    clone.milliseconds(0);
11253  } else if (scale == 'weekday') {
11254    //noinspection FallthroughInSwitchStatementJS
11255    switch (step) {
11256      case 5:
11257      case 2:
11258        clone.hours(Math.round(clone.hours() / 12) * 12);break;
11259      default:
11260        clone.hours(Math.round(clone.hours() / 6) * 6);break;
11261    }
11262    clone.minutes(0);
11263    clone.seconds(0);
11264    clone.milliseconds(0);
11265  } else if (scale == 'hour') {
11266    switch (step) {
11267      case 4:
11268        clone.minutes(Math.round(clone.minutes() / 60) * 60);break;
11269      default:
11270        clone.minutes(Math.round(clone.minutes() / 30) * 30);break;
11271    }
11272    clone.seconds(0);
11273    clone.milliseconds(0);
11274  } else if (scale == 'minute') {
11275    //noinspection FallthroughInSwitchStatementJS
11276    switch (step) {
11277      case 15:
11278      case 10:
11279        clone.minutes(Math.round(clone.minutes() / 5) * 5);
11280        clone.seconds(0);
11281        break;
11282      case 5:
11283        clone.seconds(Math.round(clone.seconds() / 60) * 60);break;
11284      default:
11285        clone.seconds(Math.round(clone.seconds() / 30) * 30);break;
11286    }
11287    clone.milliseconds(0);
11288  } else if (scale == 'second') {
11289    //noinspection FallthroughInSwitchStatementJS
11290    switch (step) {
11291      case 15:
11292      case 10:
11293        clone.seconds(Math.round(clone.seconds() / 5) * 5);
11294        clone.milliseconds(0);
11295        break;
11296      case 5:
11297        clone.milliseconds(Math.round(clone.milliseconds() / 1000) * 1000);break;
11298      default:
11299        clone.milliseconds(Math.round(clone.milliseconds() / 500) * 500);break;
11300    }
11301  } else if (scale == 'millisecond') {
11302    var _step = step > 5 ? step / 2 : 1;
11303    clone.milliseconds(Math.round(clone.milliseconds() / _step) * _step);
11304  }
11305
11306  return clone;
11307};
11308
11309/**
11310 * Check if the current value is a major value (for example when the step
11311 * is DAY, a major value is each first day of the MONTH)
11312 * @return {boolean} true if current date is major, else false.
11313 */
11314TimeStep.prototype.isMajor = function () {
11315  if (this.switchedYear == true) {
11316    switch (this.scale) {
11317      case 'year':
11318      case 'month':
11319      case 'week':
11320      case 'weekday':
11321      case 'day':
11322      case 'hour':
11323      case 'minute':
11324      case 'second':
11325      case 'millisecond':
11326        return true;
11327      default:
11328        return false;
11329    }
11330  } else if (this.switchedMonth == true) {
11331    switch (this.scale) {
11332      case 'week':
11333      case 'weekday':
11334      case 'day':
11335      case 'hour':
11336      case 'minute':
11337      case 'second':
11338      case 'millisecond':
11339        return true;
11340      default:
11341        return false;
11342    }
11343  } else if (this.switchedDay == true) {
11344    switch (this.scale) {
11345      case 'millisecond':
11346      case 'second':
11347      case 'minute':
11348      case 'hour':
11349        return true;
11350      default:
11351        return false;
11352    }
11353  }
11354
11355  var date = this.moment(this.current);
11356  switch (this.scale) {
11357    case 'millisecond':
11358      return date.milliseconds() == 0;
11359    case 'second':
11360      return date.seconds() == 0;
11361    case 'minute':
11362      return date.hours() == 0 && date.minutes() == 0;
11363    case 'hour':
11364      return date.hours() == 0;
11365    case 'weekday': // intentional fall through
11366    case 'day':
11367      return date.date() == 1;
11368    case 'week':
11369      return date.date() == 1;
11370    case 'month':
11371      return date.month() == 0;
11372    case 'year':
11373      return false;
11374    default:
11375      return false;
11376  }
11377};
11378
11379/**
11380 * Returns formatted text for the minor axislabel, depending on the current
11381 * date and the scale. For example when scale is MINUTE, the current time is
11382 * formatted as "hh:mm".
11383 * @param {Date} [date=this.current] custom date. if not provided, current date is taken
11384 * @returns {String}
11385 */
11386TimeStep.prototype.getLabelMinor = function (date) {
11387  if (date == undefined) {
11388    date = this.current;
11389  }
11390  if (date instanceof Date) {
11391    date = this.moment(date);
11392  }
11393
11394  if (typeof this.format.minorLabels === "function") {
11395    return this.format.minorLabels(date, this.scale, this.step);
11396  }
11397
11398  var format = this.format.minorLabels[this.scale];
11399  // noinspection FallThroughInSwitchStatementJS
11400  switch (this.scale) {
11401    case 'week':
11402      if (this.isMajor() && date.weekday() !== 0) {
11403        return "";
11404      }
11405    default:
11406      // eslint-disable-line no-fallthrough
11407      return format && format.length > 0 ? this.moment(date).format(format) : '';
11408  }
11409};
11410
11411/**
11412 * Returns formatted text for the major axis label, depending on the current
11413 * date and the scale. For example when scale is MINUTE, the major scale is
11414 * hours, and the hour will be formatted as "hh".
11415 * @param {Date} [date=this.current] custom date. if not provided, current date is taken
11416 * @returns {String}
11417 */
11418TimeStep.prototype.getLabelMajor = function (date) {
11419  if (date == undefined) {
11420    date = this.current;
11421  }
11422  if (date instanceof Date) {
11423    date = this.moment(date);
11424  }
11425
11426  if (typeof this.format.majorLabels === "function") {
11427    return this.format.majorLabels(date, this.scale, this.step);
11428  }
11429
11430  var format = this.format.majorLabels[this.scale];
11431  return format && format.length > 0 ? this.moment(date).format(format) : '';
11432};
11433
11434TimeStep.prototype.getClassName = function () {
11435  var _moment = this.moment;
11436  var m = this.moment(this.current);
11437  var current = m.locale ? m.locale('en') : m.lang('en'); // old versions of moment have .lang() function
11438  var step = this.step;
11439  var classNames = [];
11440
11441  /**
11442   *
11443   * @param {number} value
11444   * @returns {String}
11445   */
11446  function even(value) {
11447    return value / step % 2 == 0 ? ' vis-even' : ' vis-odd';
11448  }
11449
11450  /**
11451   *
11452   * @param {Date} date
11453   * @returns {String}
11454   */
11455  function today(date) {
11456    if (date.isSame(new Date(), 'day')) {
11457      return ' vis-today';
11458    }
11459    if (date.isSame(_moment().add(1, 'day'), 'day')) {
11460      return ' vis-tomorrow';
11461    }
11462    if (date.isSame(_moment().add(-1, 'day'), 'day')) {
11463      return ' vis-yesterday';
11464    }
11465    return '';
11466  }
11467
11468  /**
11469   *
11470   * @param {Date} date
11471   * @returns {String}
11472   */
11473  function currentWeek(date) {
11474    return date.isSame(new Date(), 'week') ? ' vis-current-week' : '';
11475  }
11476
11477  /**
11478   *
11479   * @param {Date} date
11480   * @returns {String}
11481   */
11482  function currentMonth(date) {
11483    return date.isSame(new Date(), 'month') ? ' vis-current-month' : '';
11484  }
11485
11486  /**
11487   *
11488   * @param {Date} date
11489   * @returns {String}
11490   */
11491  function currentYear(date) {
11492    return date.isSame(new Date(), 'year') ? ' vis-current-year' : '';
11493  }
11494
11495  switch (this.scale) {
11496    case 'millisecond':
11497      classNames.push(today(current));
11498      classNames.push(even(current.milliseconds()));
11499      break;
11500    case 'second':
11501      classNames.push(today(current));
11502      classNames.push(even(current.seconds()));
11503      break;
11504    case 'minute':
11505      classNames.push(today(current));
11506      classNames.push(even(current.minutes()));
11507      break;
11508    case 'hour':
11509      classNames.push('vis-h' + current.hours() + (this.step == 4 ? '-h' + (current.hours() + 4) : ''));
11510      classNames.push(today(current));
11511      classNames.push(even(current.hours()));
11512      break;
11513    case 'weekday':
11514      classNames.push('vis-' + current.format('dddd').toLowerCase());
11515      classNames.push(today(current));
11516      classNames.push(currentWeek(current));
11517      classNames.push(even(current.date()));
11518      break;
11519    case 'day':
11520      classNames.push('vis-day' + current.date());
11521      classNames.push('vis-' + current.format('MMMM').toLowerCase());
11522      classNames.push(today(current));
11523      classNames.push(currentMonth(current));
11524      classNames.push(this.step <= 2 ? today(current) : '');
11525      classNames.push(this.step <= 2 ? 'vis-' + current.format('dddd').toLowerCase() : '');
11526      classNames.push(even(current.date() - 1));
11527      break;
11528    case 'week':
11529      classNames.push('vis-week' + current.format('w'));
11530      classNames.push(currentWeek(current));
11531      classNames.push(even(current.week()));
11532      break;
11533    case 'month':
11534      classNames.push('vis-' + current.format('MMMM').toLowerCase());
11535      classNames.push(currentMonth(current));
11536      classNames.push(even(current.month()));
11537      break;
11538    case 'year':
11539      classNames.push('vis-year' + current.year());
11540      classNames.push(currentYear(current));
11541      classNames.push(even(current.year()));
11542      break;
11543  }
11544  return classNames.filter(String).join(" ");
11545};
11546
11547module.exports = TimeStep;
11548
11549/***/ }),
11550/* 67 */
11551/***/ (function(module, exports, __webpack_require__) {
11552
11553"use strict";
11554
11555
11556var util = __webpack_require__(2);
11557var Component = __webpack_require__(16);
11558var moment = __webpack_require__(9);
11559var locales = __webpack_require__(98);
11560
11561/**
11562 * A current time bar
11563 * @param {{range: Range, dom: Object, domProps: Object}} body
11564 * @param {Object} [options]        Available parameters:
11565 *                                  {Boolean} [showCurrentTime]
11566 * @constructor CurrentTime
11567 * @extends Component
11568 */
11569function CurrentTime(body, options) {
11570  this.body = body;
11571
11572  // default options
11573  this.defaultOptions = {
11574    rtl: false,
11575    showCurrentTime: true,
11576
11577    moment: moment,
11578    locales: locales,
11579    locale: 'en'
11580  };
11581  this.options = util.extend({}, this.defaultOptions);
11582  this.offset = 0;
11583
11584  this._create();
11585
11586  this.setOptions(options);
11587}
11588
11589CurrentTime.prototype = new Component();
11590
11591/**
11592 * Create the HTML DOM for the current time bar
11593 * @private
11594 */
11595CurrentTime.prototype._create = function () {
11596  var bar = document.createElement('div');
11597  bar.className = 'vis-current-time';
11598  bar.style.position = 'absolute';
11599  bar.style.top = '0px';
11600  bar.style.height = '100%';
11601
11602  this.bar = bar;
11603};
11604
11605/**
11606 * Destroy the CurrentTime bar
11607 */
11608CurrentTime.prototype.destroy = function () {
11609  this.options.showCurrentTime = false;
11610  this.redraw(); // will remove the bar from the DOM and stop refreshing
11611
11612  this.body = null;
11613};
11614
11615/**
11616 * Set options for the component. Options will be merged in current options.
11617 * @param {Object} options  Available parameters:
11618 *                          {boolean} [showCurrentTime]
11619 */
11620CurrentTime.prototype.setOptions = function (options) {
11621  if (options) {
11622    // copy all options that we know
11623    util.selectiveExtend(['rtl', 'showCurrentTime', 'moment', 'locale', 'locales'], this.options, options);
11624  }
11625};
11626
11627/**
11628 * Repaint the component
11629 * @return {boolean} Returns true if the component is resized
11630 */
11631CurrentTime.prototype.redraw = function () {
11632  if (this.options.showCurrentTime) {
11633    var parent = this.body.dom.backgroundVertical;
11634    if (this.bar.parentNode != parent) {
11635      // attach to the dom
11636      if (this.bar.parentNode) {
11637        this.bar.parentNode.removeChild(this.bar);
11638      }
11639      parent.appendChild(this.bar);
11640
11641      this.start();
11642    }
11643
11644    var now = this.options.moment(new Date().valueOf() + this.offset);
11645    var x = this.body.util.toScreen(now);
11646
11647    var locale = this.options.locales[this.options.locale];
11648    if (!locale) {
11649      if (!this.warned) {
11650        console.log('WARNING: options.locales[\'' + this.options.locale + '\'] not found. See http://visjs.org/docs/timeline/#Localization');
11651        this.warned = true;
11652      }
11653      locale = this.options.locales['en']; // fall back on english when not available
11654    }
11655    var title = locale.current + ' ' + locale.time + ': ' + now.format('dddd, MMMM Do YYYY, H:mm:ss');
11656    title = title.charAt(0).toUpperCase() + title.substring(1);
11657
11658    if (this.options.rtl) {
11659      this.bar.style.right = x + 'px';
11660    } else {
11661      this.bar.style.left = x + 'px';
11662    }
11663    this.bar.title = title;
11664  } else {
11665    // remove the line from the DOM
11666    if (this.bar.parentNode) {
11667      this.bar.parentNode.removeChild(this.bar);
11668    }
11669    this.stop();
11670  }
11671
11672  return false;
11673};
11674
11675/**
11676 * Start auto refreshing the current time bar
11677 */
11678CurrentTime.prototype.start = function () {
11679  var me = this;
11680
11681  /**
11682   *  Updates the current time.
11683   */
11684  function update() {
11685    me.stop();
11686
11687    // determine interval to refresh
11688    var scale = me.body.range.conversion(me.body.domProps.center.width).scale;
11689    var interval = 1 / scale / 10;
11690    if (interval < 30) interval = 30;
11691    if (interval > 1000) interval = 1000;
11692
11693    me.redraw();
11694    me.body.emitter.emit('currentTimeTick');
11695
11696    // start a renderTimer to adjust for the new time
11697    me.currentTimeTimer = setTimeout(update, interval);
11698  }
11699
11700  update();
11701};
11702
11703/**
11704 * Stop auto refreshing the current time bar
11705 */
11706CurrentTime.prototype.stop = function () {
11707  if (this.currentTimeTimer !== undefined) {
11708    clearTimeout(this.currentTimeTimer);
11709    delete this.currentTimeTimer;
11710  }
11711};
11712
11713/**
11714 * Set a current time. This can be used for example to ensure that a client's
11715 * time is synchronized with a shared server time.
11716 * @param {Date | string | number} time     A Date, unix timestamp, or
11717 *                                          ISO date string.
11718 */
11719CurrentTime.prototype.setCurrentTime = function (time) {
11720  var t = util.convert(time, 'Date').valueOf();
11721  var now = new Date().valueOf();
11722  this.offset = t - now;
11723  this.redraw();
11724};
11725
11726/**
11727 * Get the current time.
11728 * @return {Date} Returns the current time.
11729 */
11730CurrentTime.prototype.getCurrentTime = function () {
11731  return new Date(new Date().valueOf() + this.offset);
11732};
11733
11734module.exports = CurrentTime;
11735
11736/***/ }),
11737/* 68 */
11738/***/ (function(module, exports, __webpack_require__) {
11739
11740"use strict";
11741
11742
11743var _keys = __webpack_require__(8);
11744
11745var _keys2 = _interopRequireDefault(_keys);
11746
11747function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
11748
11749var util = __webpack_require__(2);
11750var stack = __webpack_require__(100);
11751
11752/**
11753 * @param {number | string} groupId
11754 * @param {Object} data
11755 * @param {ItemSet} itemSet
11756 * @constructor Group
11757 */
11758function Group(groupId, data, itemSet) {
11759  this.groupId = groupId;
11760  this.subgroups = {};
11761  this.subgroupStack = {};
11762  this.subgroupStackAll = false;
11763  this.doInnerStack = false;
11764  this.subgroupIndex = 0;
11765  this.subgroupOrderer = data && data.subgroupOrder;
11766  this.itemSet = itemSet;
11767  this.isVisible = null;
11768  this.stackDirty = true; // if true, items will be restacked on next redraw
11769
11770  if (data && data.nestedGroups) {
11771    this.nestedGroups = data.nestedGroups;
11772    if (data.showNested == false) {
11773      this.showNested = false;
11774    } else {
11775      this.showNested = true;
11776    }
11777  }
11778
11779  if (data && data.subgroupStack) {
11780    if (typeof data.subgroupStack === "boolean") {
11781      this.doInnerStack = data.subgroupStack;
11782      this.subgroupStackAll = data.subgroupStack;
11783    } else {
11784      // We might be doing stacking on specific sub groups, but only
11785      // if at least one is set to do stacking
11786      for (var key in data.subgroupStack) {
11787        this.subgroupStack[key] = data.subgroupStack[key];
11788        this.doInnerStack = this.doInnerStack || data.subgroupStack[key];
11789      }
11790    }
11791  }
11792
11793  this.nestedInGroup = null;
11794
11795  this.dom = {};
11796  this.props = {
11797    label: {
11798      width: 0,
11799      height: 0
11800    }
11801  };
11802  this.className = null;
11803
11804  this.items = {}; // items filtered by groupId of this group
11805  this.visibleItems = []; // items currently visible in window
11806  this.itemsInRange = []; // items currently in range
11807  this.orderedItems = {
11808    byStart: [],
11809    byEnd: []
11810  };
11811  this.checkRangedItems = false; // needed to refresh the ranged items if the window is programatically changed with NO overlap.
11812  var me = this;
11813  this.itemSet.body.emitter.on("checkRangedItems", function () {
11814    me.checkRangedItems = true;
11815  });
11816
11817  this._create();
11818
11819  this.setData(data);
11820}
11821
11822/**
11823 * Create DOM elements for the group
11824 * @private
11825 */
11826Group.prototype._create = function () {
11827  var label = document.createElement('div');
11828  if (this.itemSet.options.groupEditable.order) {
11829    label.className = 'vis-label draggable';
11830  } else {
11831    label.className = 'vis-label';
11832  }
11833  this.dom.label = label;
11834
11835  var inner = document.createElement('div');
11836  inner.className = 'vis-inner';
11837  label.appendChild(inner);
11838  this.dom.inner = inner;
11839
11840  var foreground = document.createElement('div');
11841  foreground.className = 'vis-group';
11842  foreground['timeline-group'] = this;
11843  this.dom.foreground = foreground;
11844
11845  this.dom.background = document.createElement('div');
11846  this.dom.background.className = 'vis-group';
11847
11848  this.dom.axis = document.createElement('div');
11849  this.dom.axis.className = 'vis-group';
11850
11851  // create a hidden marker to detect when the Timelines container is attached
11852  // to the DOM, or the style of a parent of the Timeline is changed from
11853  // display:none is changed to visible.
11854  this.dom.marker = document.createElement('div');
11855  this.dom.marker.style.visibility = 'hidden';
11856  this.dom.marker.style.position = 'absolute';
11857  this.dom.marker.innerHTML = '';
11858  this.dom.background.appendChild(this.dom.marker);
11859};
11860
11861/**
11862 * Set the group data for this group
11863 * @param {Object} data   Group data, can contain properties content and className
11864 */
11865Group.prototype.setData = function (data) {
11866  // update contents
11867  var content;
11868  var templateFunction;
11869
11870  if (this.itemSet.options && this.itemSet.options.groupTemplate) {
11871    templateFunction = this.itemSet.options.groupTemplate.bind(this);
11872    content = templateFunction(data, this.dom.inner);
11873  } else {
11874    content = data && data.content;
11875  }
11876
11877  if (content instanceof Element) {
11878    this.dom.inner.appendChild(content);
11879    while (this.dom.inner.firstChild) {
11880      this.dom.inner.removeChild(this.dom.inner.firstChild);
11881    }
11882    this.dom.inner.appendChild(content);
11883  } else if (content instanceof Object) {
11884    templateFunction(data, this.dom.inner);
11885  } else if (content !== undefined && content !== null) {
11886    this.dom.inner.innerHTML = content;
11887  } else {
11888    this.dom.inner.innerHTML = this.groupId || ''; // groupId can be null
11889  }
11890
11891  // update title
11892  this.dom.label.title = data && data.title || '';
11893  if (!this.dom.inner.firstChild) {
11894    util.addClassName(this.dom.inner, 'vis-hidden');
11895  } else {
11896    util.removeClassName(this.dom.inner, 'vis-hidden');
11897  }
11898
11899  if (data && data.nestedGroups) {
11900    if (!this.nestedGroups || this.nestedGroups != data.nestedGroups) {
11901      this.nestedGroups = data.nestedGroups;
11902    }
11903
11904    if (data.showNested !== undefined || this.showNested === undefined) {
11905      if (data.showNested == false) {
11906        this.showNested = false;
11907      } else {
11908        this.showNested = true;
11909      }
11910    }
11911
11912    util.addClassName(this.dom.label, 'vis-nesting-group');
11913    var collapsedDirClassName = this.itemSet.options.rtl ? 'collapsed-rtl' : 'collapsed';
11914    if (this.showNested) {
11915      util.removeClassName(this.dom.label, collapsedDirClassName);
11916      util.addClassName(this.dom.label, 'expanded');
11917    } else {
11918      util.removeClassName(this.dom.label, 'expanded');
11919      util.addClassName(this.dom.label, collapsedDirClassName);
11920    }
11921  } else if (this.nestedGroups) {
11922    this.nestedGroups = null;
11923    collapsedDirClassName = this.itemSet.options.rtl ? 'collapsed-rtl' : 'collapsed';
11924    util.removeClassName(this.dom.label, collapsedDirClassName);
11925    util.removeClassName(this.dom.label, 'expanded');
11926    util.removeClassName(this.dom.label, 'vis-nesting-group');
11927  }
11928
11929  if (data && data.nestedInGroup) {
11930    util.addClassName(this.dom.label, 'vis-nested-group');
11931    if (this.itemSet.options && this.itemSet.options.rtl) {
11932      this.dom.inner.style.paddingRight = '30px';
11933    } else {
11934      this.dom.inner.style.paddingLeft = '30px';
11935    }
11936  }
11937
11938  // update className
11939  var className = data && data.className || null;
11940  if (className != this.className) {
11941    if (this.className) {
11942      util.removeClassName(this.dom.label, this.className);
11943      util.removeClassName(this.dom.foreground, this.className);
11944      util.removeClassName(this.dom.background, this.className);
11945      util.removeClassName(this.dom.axis, this.className);
11946    }
11947    util.addClassName(this.dom.label, className);
11948    util.addClassName(this.dom.foreground, className);
11949    util.addClassName(this.dom.background, className);
11950    util.addClassName(this.dom.axis, className);
11951    this.className = className;
11952  }
11953
11954  // update style
11955  if (this.style) {
11956    util.removeCssText(this.dom.label, this.style);
11957    this.style = null;
11958  }
11959  if (data && data.style) {
11960    util.addCssText(this.dom.label, data.style);
11961    this.style = data.style;
11962  }
11963};
11964
11965/**
11966 * Get the width of the group label
11967 * @return {number} width
11968 */
11969Group.prototype.getLabelWidth = function () {
11970  return this.props.label.width;
11971};
11972
11973Group.prototype._didMarkerHeightChange = function () {
11974  var markerHeight = this.dom.marker.clientHeight;
11975  if (markerHeight != this.lastMarkerHeight) {
11976    this.lastMarkerHeight = markerHeight;
11977    var redrawQueue = {};
11978    var redrawQueueLength = 0;
11979
11980    util.forEach(this.items, function (item, key) {
11981      item.dirty = true;
11982      if (item.displayed) {
11983        var returnQueue = true;
11984        redrawQueue[key] = item.redraw(returnQueue);
11985        redrawQueueLength = redrawQueue[key].length;
11986      }
11987    });
11988
11989    var needRedraw = redrawQueueLength > 0;
11990    if (needRedraw) {
11991      // redraw all regular items
11992      for (var i = 0; i < redrawQueueLength; i++) {
11993        util.forEach(redrawQueue, function (fns) {
11994          fns[i]();
11995        });
11996      }
11997    }
11998    return true;
11999  }
12000};
12001
12002Group.prototype._calculateGroupSizeAndPosition = function () {
12003  var offsetTop = this.dom.foreground.offsetTop;
12004  var offsetLeft = this.dom.foreground.offsetLeft;
12005  var offsetWidth = this.dom.foreground.offsetWidth;
12006  this.top = offsetTop;
12007  this.right = offsetLeft;
12008  this.width = offsetWidth;
12009};
12010
12011Group.prototype._redrawItems = function (forceRestack, lastIsVisible, margin, range) {
12012  var restack = forceRestack || this.stackDirty || this.isVisible && !lastIsVisible;
12013
12014  // if restacking, reposition visible items vertically
12015  if (restack) {
12016    var visibleSubgroups = {};
12017    var subgroup = null;
12018
12019    if (typeof this.itemSet.options.order === 'function') {
12020      // a custom order function
12021      // brute force restack of all items
12022
12023      // show all items
12024      var me = this;
12025      var limitSize = false;
12026
12027      var redrawQueue = {};
12028      var redrawQueueLength = 0;
12029
12030      util.forEach(this.items, function (item, key) {
12031        if (!item.displayed) {
12032          var returnQueue = true;
12033          redrawQueue[key] = item.redraw(returnQueue);
12034          redrawQueueLength = redrawQueue[key].length;
12035          me.visibleItems.push(item);
12036        }
12037      });
12038
12039      var needRedraw = redrawQueueLength > 0;
12040      if (needRedraw) {
12041        // redraw all regular items
12042        for (var i = 0; i < redrawQueueLength; i++) {
12043          util.forEach(redrawQueue, function (fns) {
12044            fns[i]();
12045          });
12046        }
12047      }
12048
12049      util.forEach(this.items, function (item) {
12050        item.repositionX(limitSize);
12051      });
12052
12053      if (this.doInnerStack && this.itemSet.options.stackSubgroups) {
12054        // Order the items within each subgroup
12055        for (subgroup in this.subgroups) {
12056          visibleSubgroups[subgroup] = this.subgroups[subgroup].items.slice().sort(function (a, b) {
12057            return me.itemSet.options.order(a.data, b.data);
12058          });
12059        }
12060
12061        stack.stackSubgroupsWithInnerStack(visibleSubgroups, margin, this.subgroups);
12062      } else {
12063        // order all items and force a restacking
12064        var customOrderedItems = this.orderedItems.byStart.slice().sort(function (a, b) {
12065          return me.itemSet.options.order(a.data, b.data);
12066        });
12067        stack.stack(customOrderedItems, margin, true /* restack=true */);
12068      }
12069
12070      this.visibleItems = this._updateItemsInRange(this.orderedItems, this.visibleItems, range);
12071    } else {
12072      // no custom order function, lazy stacking
12073      this.visibleItems = this._updateItemsInRange(this.orderedItems, this.visibleItems, range);
12074
12075      if (this.itemSet.options.stack) {
12076        if (this.doInnerStack && this.itemSet.options.stackSubgroups) {
12077          for (subgroup in this.subgroups) {
12078            visibleSubgroups[subgroup] = this.subgroups[subgroup].items;
12079          }
12080
12081          stack.stackSubgroupsWithInnerStack(visibleSubgroups, margin, this.subgroups);
12082        } else {
12083          // TODO: ugly way to access options...
12084          stack.stack(this.visibleItems, margin, true /* restack=true */);
12085        }
12086      } else {
12087        // no stacking
12088        stack.nostack(this.visibleItems, margin, this.subgroups, this.itemSet.options.stackSubgroups);
12089      }
12090    }
12091
12092    this.stackDirty = false;
12093  }
12094};
12095
12096Group.prototype._didResize = function (resized, height) {
12097  resized = util.updateProperty(this, 'height', height) || resized;
12098  // recalculate size of label
12099  var labelWidth = this.dom.inner.clientWidth;
12100  var labelHeight = this.dom.inner.clientHeight;
12101  resized = util.updateProperty(this.props.label, 'width', labelWidth) || resized;
12102  resized = util.updateProperty(this.props.label, 'height', labelHeight) || resized;
12103  return resized;
12104};
12105
12106Group.prototype._applyGroupHeight = function (height) {
12107  this.dom.background.style.height = height + 'px';
12108  this.dom.foreground.style.height = height + 'px';
12109  this.dom.label.style.height = height + 'px';
12110};
12111
12112// update vertical position of items after they are re-stacked and the height of the group is calculated
12113Group.prototype._updateItemsVerticalPosition = function (margin) {
12114  for (var i = 0, ii = this.visibleItems.length; i < ii; i++) {
12115    var item = this.visibleItems[i];
12116    item.repositionY(margin);
12117    if (!this.isVisible && this.groupId != "__background__") {
12118      if (item.displayed) item.hide();
12119    }
12120  }
12121};
12122
12123/**
12124 * Repaint this group
12125 * @param {{start: number, end: number}} range
12126 * @param {{item: {horizontal: number, vertical: number}, axis: number}} margin
12127 * @param {boolean} [forceRestack=false]  Force restacking of all items
12128 * @param {boolean} [returnQueue=false]  return the queue or if the group resized
12129 * @return {boolean} Returns true if the group is resized or the redraw queue if returnQueue=true
12130 */
12131Group.prototype.redraw = function (range, margin, forceRestack, returnQueue) {
12132  var resized = false;
12133  var lastIsVisible = this.isVisible;
12134  var height;
12135
12136  var queue = [
12137  // force recalculation of the height of the items when the marker height changed
12138  // (due to the Timeline being attached to the DOM or changed from display:none to visible)
12139  function () {
12140    forceRestack = this._didMarkerHeightChange.bind(this);
12141  }.bind(this),
12142
12143  // recalculate the height of the subgroups
12144  this._updateSubGroupHeights.bind(this, margin),
12145
12146  // calculate actual size and position
12147  this._calculateGroupSizeAndPosition.bind(this),
12148
12149  // check if group is visible
12150  function () {
12151    this.isVisible = this._isGroupVisible.bind(this)(range, margin);
12152  }.bind(this),
12153
12154  // redraw Items if needed
12155  function () {
12156    this._redrawItems.bind(this)(forceRestack, lastIsVisible, margin, range);
12157  }.bind(this),
12158
12159  // update subgroups
12160  this._updateSubgroupsSizes.bind(this),
12161
12162  // recalculate the height of the group
12163  function () {
12164    height = this._calculateHeight.bind(this)(margin);
12165  }.bind(this),
12166
12167  // calculate actual size and position again
12168  this._calculateGroupSizeAndPosition.bind(this),
12169
12170  // check if resized
12171  function () {
12172    resized = this._didResize.bind(this)(resized, height);
12173  }.bind(this),
12174
12175  // apply group height
12176  function () {
12177    this._applyGroupHeight.bind(this)(height);
12178  }.bind(this),
12179
12180  // update vertical position of items after they are re-stacked and the height of the group is calculated
12181  function () {
12182    this._updateItemsVerticalPosition.bind(this)(margin);
12183  }.bind(this), function () {
12184    if (!this.isVisible && this.height) {
12185      resized = false;
12186    }
12187    return resized;
12188  }];
12189
12190  if (returnQueue) {
12191    return queue;
12192  } else {
12193    var result;
12194    queue.forEach(function (fn) {
12195      result = fn();
12196    });
12197    return result;
12198  }
12199};
12200
12201/**
12202 * recalculate the height of the subgroups
12203 *
12204 * @param {{item: vis.Item}} margin
12205 * @private
12206 */
12207Group.prototype._updateSubGroupHeights = function (margin) {
12208  if ((0, _keys2['default'])(this.subgroups).length > 0) {
12209    var me = this;
12210
12211    this.resetSubgroups();
12212
12213    util.forEach(this.visibleItems, function (item) {
12214      if (item.data.subgroup !== undefined) {
12215        me.subgroups[item.data.subgroup].height = Math.max(me.subgroups[item.data.subgroup].height, item.height + margin.item.vertical);
12216        me.subgroups[item.data.subgroup].visible = true;
12217      }
12218    });
12219  }
12220};
12221
12222/**
12223 * check if group is visible
12224 *
12225 * @param {vis.Range} range
12226 * @param {{axis: vis.DataAxis}} margin
12227 * @returns {boolean} is visible
12228 * @private
12229 */
12230Group.prototype._isGroupVisible = function (range, margin) {
12231  return this.top <= range.body.domProps.centerContainer.height - range.body.domProps.scrollTop + margin.axis && this.top + this.height + margin.axis >= -range.body.domProps.scrollTop;
12232};
12233
12234/**
12235 * recalculate the height of the group
12236 * @param {{item: {horizontal: number, vertical: number}, axis: number}} margin
12237 * @returns {number} Returns the height
12238 * @private
12239 */
12240Group.prototype._calculateHeight = function (margin) {
12241  // recalculate the height of the group
12242  var height;
12243  var itemsInRange = this.visibleItems;
12244  if (itemsInRange.length > 0) {
12245    var min = itemsInRange[0].top;
12246    var max = itemsInRange[0].top + itemsInRange[0].height;
12247    util.forEach(itemsInRange, function (item) {
12248      min = Math.min(min, item.top);
12249      max = Math.max(max, item.top + item.height);
12250    });
12251    if (min > margin.axis) {
12252      // there is an empty gap between the lowest item and the axis
12253      var offset = min - margin.axis;
12254      max -= offset;
12255      util.forEach(itemsInRange, function (item) {
12256        item.top -= offset;
12257      });
12258    }
12259    height = max + margin.item.vertical / 2;
12260  } else {
12261    height = 0;
12262  }
12263  height = Math.max(height, this.props.label.height);
12264
12265  return height;
12266};
12267
12268/**
12269 * Show this group: attach to the DOM
12270 */
12271Group.prototype.show = function () {
12272  if (!this.dom.label.parentNode) {
12273    this.itemSet.dom.labelSet.appendChild(this.dom.label);
12274  }
12275
12276  if (!this.dom.foreground.parentNode) {
12277    this.itemSet.dom.foreground.appendChild(this.dom.foreground);
12278  }
12279
12280  if (!this.dom.background.parentNode) {
12281    this.itemSet.dom.background.appendChild(this.dom.background);
12282  }
12283
12284  if (!this.dom.axis.parentNode) {
12285    this.itemSet.dom.axis.appendChild(this.dom.axis);
12286  }
12287};
12288
12289/**
12290 * Hide this group: remove from the DOM
12291 */
12292Group.prototype.hide = function () {
12293  var label = this.dom.label;
12294  if (label.parentNode) {
12295    label.parentNode.removeChild(label);
12296  }
12297
12298  var foreground = this.dom.foreground;
12299  if (foreground.parentNode) {
12300    foreground.parentNode.removeChild(foreground);
12301  }
12302
12303  var background = this.dom.background;
12304  if (background.parentNode) {
12305    background.parentNode.removeChild(background);
12306  }
12307
12308  var axis = this.dom.axis;
12309  if (axis.parentNode) {
12310    axis.parentNode.removeChild(axis);
12311  }
12312};
12313
12314/**
12315 * Add an item to the group
12316 * @param {Item} item
12317 */
12318Group.prototype.add = function (item) {
12319  this.items[item.id] = item;
12320  item.setParent(this);
12321  this.stackDirty = true;
12322  // add to
12323  if (item.data.subgroup !== undefined) {
12324    this._addToSubgroup(item);
12325    this.orderSubgroups();
12326  }
12327
12328  if (this.visibleItems.indexOf(item) == -1) {
12329    var range = this.itemSet.body.range; // TODO: not nice accessing the range like this
12330    this._checkIfVisible(item, this.visibleItems, range);
12331  }
12332};
12333
12334Group.prototype._addToSubgroup = function (item, subgroupId) {
12335  subgroupId = subgroupId || item.data.subgroup;
12336  if (subgroupId != undefined && this.subgroups[subgroupId] === undefined) {
12337    this.subgroups[subgroupId] = {
12338      height: 0,
12339      top: 0,
12340      start: item.data.start,
12341      end: item.data.end || item.data.start,
12342      visible: false,
12343      index: this.subgroupIndex,
12344      items: [],
12345      stack: this.subgroupStackAll || this.subgroupStack[subgroupId] || false
12346    };
12347    this.subgroupIndex++;
12348  }
12349
12350  if (new Date(item.data.start) < new Date(this.subgroups[subgroupId].start)) {
12351    this.subgroups[subgroupId].start = item.data.start;
12352  }
12353
12354  var itemEnd = item.data.end || item.data.start;
12355  if (new Date(itemEnd) > new Date(this.subgroups[subgroupId].end)) {
12356    this.subgroups[subgroupId].end = itemEnd;
12357  }
12358
12359  this.subgroups[subgroupId].items.push(item);
12360};
12361
12362Group.prototype._updateSubgroupsSizes = function () {
12363  var me = this;
12364  if (me.subgroups) {
12365    for (var subgroup in me.subgroups) {
12366      var initialEnd = me.subgroups[subgroup].items[0].data.end || me.subgroups[subgroup].items[0].data.start;
12367      var newStart = me.subgroups[subgroup].items[0].data.start;
12368      var newEnd = initialEnd - 1;
12369
12370      me.subgroups[subgroup].items.forEach(function (item) {
12371        if (new Date(item.data.start) < new Date(newStart)) {
12372          newStart = item.data.start;
12373        }
12374
12375        var itemEnd = item.data.end || item.data.start;
12376        if (new Date(itemEnd) > new Date(newEnd)) {
12377          newEnd = itemEnd;
12378        }
12379      });
12380
12381      me.subgroups[subgroup].start = newStart;
12382      me.subgroups[subgroup].end = new Date(newEnd - 1); // -1 to compensate for colliding end to start subgroups;
12383    }
12384  }
12385};
12386
12387Group.prototype.orderSubgroups = function () {
12388  if (this.subgroupOrderer !== undefined) {
12389    var sortArray = [];
12390    var subgroup;
12391    if (typeof this.subgroupOrderer == 'string') {
12392      for (subgroup in this.subgroups) {
12393        sortArray.push({ subgroup: subgroup, sortField: this.subgroups[subgroup].items[0].data[this.subgroupOrderer] });
12394      }
12395      sortArray.sort(function (a, b) {
12396        return a.sortField - b.sortField;
12397      });
12398    } else if (typeof this.subgroupOrderer == 'function') {
12399      for (subgroup in this.subgroups) {
12400        sortArray.push(this.subgroups[subgroup].items[0].data);
12401      }
12402      sortArray.sort(this.subgroupOrderer);
12403    }
12404
12405    if (sortArray.length > 0) {
12406      for (var i = 0; i < sortArray.length; i++) {
12407        this.subgroups[sortArray[i].subgroup].index = i;
12408      }
12409    }
12410  }
12411};
12412
12413Group.prototype.resetSubgroups = function () {
12414  for (var subgroup in this.subgroups) {
12415    if (this.subgroups.hasOwnProperty(subgroup)) {
12416      this.subgroups[subgroup].visible = false;
12417      this.subgroups[subgroup].height = 0;
12418    }
12419  }
12420};
12421
12422/**
12423 * Remove an item from the group
12424 * @param {Item} item
12425 */
12426Group.prototype.remove = function (item) {
12427  delete this.items[item.id];
12428  item.setParent(null);
12429  this.stackDirty = true;
12430
12431  // remove from visible items
12432  var index = this.visibleItems.indexOf(item);
12433  if (index != -1) this.visibleItems.splice(index, 1);
12434
12435  if (item.data.subgroup !== undefined) {
12436    this._removeFromSubgroup(item);
12437    this.orderSubgroups();
12438  }
12439};
12440
12441Group.prototype._removeFromSubgroup = function (item, subgroupId) {
12442  subgroupId = subgroupId || item.data.subgroup;
12443  if (subgroupId != undefined) {
12444    var subgroup = this.subgroups[subgroupId];
12445    if (subgroup) {
12446      var itemIndex = subgroup.items.indexOf(item);
12447      //  Check the item is actually in this subgroup. How should items not in the group be handled?
12448      if (itemIndex >= 0) {
12449        subgroup.items.splice(itemIndex, 1);
12450        if (!subgroup.items.length) {
12451          delete this.subgroups[subgroupId];
12452        } else {
12453          this._updateSubgroupsSizes();
12454        }
12455      }
12456    }
12457  }
12458};
12459
12460/**
12461 * Remove an item from the corresponding DataSet
12462 * @param {Item} item
12463 */
12464Group.prototype.removeFromDataSet = function (item) {
12465  this.itemSet.removeItem(item.id);
12466};
12467
12468/**
12469 * Reorder the items
12470 */
12471Group.prototype.order = function () {
12472  var array = util.toArray(this.items);
12473  var startArray = [];
12474  var endArray = [];
12475
12476  for (var i = 0; i < array.length; i++) {
12477    if (array[i].data.end !== undefined) {
12478      endArray.push(array[i]);
12479    }
12480    startArray.push(array[i]);
12481  }
12482  this.orderedItems = {
12483    byStart: startArray,
12484    byEnd: endArray
12485  };
12486
12487  stack.orderByStart(this.orderedItems.byStart);
12488  stack.orderByEnd(this.orderedItems.byEnd);
12489};
12490
12491/**
12492 * Update the visible items
12493 * @param {{byStart: Item[], byEnd: Item[]}} orderedItems   All items ordered by start date and by end date
12494 * @param {Item[]} oldVisibleItems                          The previously visible items.
12495 * @param {{start: number, end: number}} range              Visible range
12496 * @return {Item[]} visibleItems                            The new visible items.
12497 * @private
12498 */
12499Group.prototype._updateItemsInRange = function (orderedItems, oldVisibleItems, range) {
12500  var visibleItems = [];
12501  var visibleItemsLookup = {}; // we keep this to quickly look up if an item already exists in the list without using indexOf on visibleItems
12502
12503  var interval = (range.end - range.start) / 4;
12504  var lowerBound = range.start - interval;
12505  var upperBound = range.end + interval;
12506
12507  // this function is used to do the binary search.
12508  var searchFunction = function searchFunction(value) {
12509    if (value < lowerBound) {
12510      return -1;
12511    } else if (value <= upperBound) {
12512      return 0;
12513    } else {
12514      return 1;
12515    }
12516  };
12517
12518  // first check if the items that were in view previously are still in view.
12519  // IMPORTANT: this handles the case for the items with startdate before the window and enddate after the window!
12520  // also cleans up invisible items.
12521  if (oldVisibleItems.length > 0) {
12522    for (var i = 0; i < oldVisibleItems.length; i++) {
12523      this._checkIfVisibleWithReference(oldVisibleItems[i], visibleItems, visibleItemsLookup, range);
12524    }
12525  }
12526
12527  // we do a binary search for the items that have only start values.
12528  var initialPosByStart = util.binarySearchCustom(orderedItems.byStart, searchFunction, 'data', 'start');
12529
12530  // trace the visible items from the inital start pos both ways until an invisible item is found, we only look at the start values.
12531  this._traceVisible(initialPosByStart, orderedItems.byStart, visibleItems, visibleItemsLookup, function (item) {
12532    return item.data.start < lowerBound || item.data.start > upperBound;
12533  });
12534
12535  // if the window has changed programmatically without overlapping the old window, the ranged items with start < lowerBound and end > upperbound are not shown.
12536  // We therefore have to brute force check all items in the byEnd list
12537  if (this.checkRangedItems == true) {
12538    this.checkRangedItems = false;
12539    for (i = 0; i < orderedItems.byEnd.length; i++) {
12540      this._checkIfVisibleWithReference(orderedItems.byEnd[i], visibleItems, visibleItemsLookup, range);
12541    }
12542  } else {
12543    // we do a binary search for the items that have defined end times.
12544    var initialPosByEnd = util.binarySearchCustom(orderedItems.byEnd, searchFunction, 'data', 'end');
12545
12546    // trace the visible items from the inital start pos both ways until an invisible item is found, we only look at the end values.
12547    this._traceVisible(initialPosByEnd, orderedItems.byEnd, visibleItems, visibleItemsLookup, function (item) {
12548      return item.data.end < lowerBound || item.data.end > upperBound;
12549    });
12550  }
12551
12552  var redrawQueue = {};
12553  var redrawQueueLength = 0;
12554
12555  for (i = 0; i < visibleItems.length; i++) {
12556    var item = visibleItems[i];
12557    if (!item.displayed) {
12558      var returnQueue = true;
12559      redrawQueue[i] = item.redraw(returnQueue);
12560      redrawQueueLength = redrawQueue[i].length;
12561    }
12562  }
12563
12564  var needRedraw = redrawQueueLength > 0;
12565  if (needRedraw) {
12566    // redraw all regular items
12567    for (var j = 0; j < redrawQueueLength; j++) {
12568      util.forEach(redrawQueue, function (fns) {
12569        fns[j]();
12570      });
12571    }
12572  }
12573
12574  for (i = 0; i < visibleItems.length; i++) {
12575    visibleItems[i].repositionX();
12576  }
12577  return visibleItems;
12578};
12579
12580Group.prototype._traceVisible = function (initialPos, items, visibleItems, visibleItemsLookup, breakCondition) {
12581  if (initialPos != -1) {
12582    var i, item;
12583    for (i = initialPos; i >= 0; i--) {
12584      item = items[i];
12585      if (breakCondition(item)) {
12586        break;
12587      } else {
12588        if (visibleItemsLookup[item.id] === undefined) {
12589          visibleItemsLookup[item.id] = true;
12590          visibleItems.push(item);
12591        }
12592      }
12593    }
12594
12595    for (i = initialPos + 1; i < items.length; i++) {
12596      item = items[i];
12597      if (breakCondition(item)) {
12598        break;
12599      } else {
12600        if (visibleItemsLookup[item.id] === undefined) {
12601          visibleItemsLookup[item.id] = true;
12602          visibleItems.push(item);
12603        }
12604      }
12605    }
12606  }
12607};
12608
12609/**
12610 * this function is very similar to the _checkIfInvisible() but it does not
12611 * return booleans, hides the item if it should not be seen and always adds to
12612 * the visibleItems.
12613 * this one is for brute forcing and hiding.
12614 *
12615 * @param {Item} item
12616 * @param {Array} visibleItems
12617 * @param {{start:number, end:number}} range
12618 * @private
12619 */
12620Group.prototype._checkIfVisible = function (item, visibleItems, range) {
12621  if (item.isVisible(range)) {
12622    if (!item.displayed) item.show();
12623    // reposition item horizontally
12624    item.repositionX();
12625    visibleItems.push(item);
12626  } else {
12627    if (item.displayed) item.hide();
12628  }
12629};
12630
12631/**
12632 * this function is very similar to the _checkIfInvisible() but it does not
12633 * return booleans, hides the item if it should not be seen and always adds to
12634 * the visibleItems.
12635 * this one is for brute forcing and hiding.
12636 *
12637 * @param {Item} item
12638 * @param {Array.<vis.Item>} visibleItems
12639 * @param {Object<number, boolean>} visibleItemsLookup
12640 * @param {{start:number, end:number}} range
12641 * @private
12642 */
12643Group.prototype._checkIfVisibleWithReference = function (item, visibleItems, visibleItemsLookup, range) {
12644  if (item.isVisible(range)) {
12645    if (visibleItemsLookup[item.id] === undefined) {
12646      visibleItemsLookup[item.id] = true;
12647      visibleItems.push(item);
12648    }
12649  } else {
12650    if (item.displayed) item.hide();
12651  }
12652};
12653
12654Group.prototype.changeSubgroup = function (item, oldSubgroup, newSubgroup) {
12655  this._removeFromSubgroup(item, oldSubgroup);
12656  this._addToSubgroup(item, newSubgroup);
12657  this.orderSubgroups();
12658};
12659
12660module.exports = Group;
12661
12662/***/ }),
12663/* 69 */
12664/***/ (function(module, exports, __webpack_require__) {
12665
12666"use strict";
12667
12668
12669var _create = __webpack_require__(29);
12670
12671var _create2 = _interopRequireDefault(_create);
12672
12673function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
12674
12675var Group = __webpack_require__(68);
12676
12677/**
12678 * @constructor BackgroundGroup
12679 * @param {number | string} groupId
12680 * @param {Object} data
12681 * @param {ItemSet} itemSet
12682 * @extends Group
12683 */
12684function BackgroundGroup(groupId, data, itemSet) {
12685  Group.call(this, groupId, data, itemSet);
12686
12687  this.width = 0;
12688  this.height = 0;
12689  this.top = 0;
12690  this.left = 0;
12691}
12692
12693BackgroundGroup.prototype = (0, _create2['default'])(Group.prototype);
12694
12695/**
12696 * Repaint this group
12697 * @param {{start: number, end: number}} range
12698 * @param {{item: {horizontal: number, vertical: number}, axis: number}} margin
12699 * @param {boolean} [forceRestack=false]  Force restacking of all items
12700 * @return {boolean} Returns true if the group is resized
12701 */
12702BackgroundGroup.prototype.redraw = function (range, margin, forceRestack) {
12703  // eslint-disable-line no-unused-vars
12704  var resized = false;
12705
12706  this.visibleItems = this._updateItemsInRange(this.orderedItems, this.visibleItems, range);
12707
12708  // calculate actual size
12709  this.width = this.dom.background.offsetWidth;
12710
12711  // apply new height (just always zero for BackgroundGroup
12712  this.dom.background.style.height = '0';
12713
12714  // update vertical position of items after they are re-stacked and the height of the group is calculated
12715  for (var i = 0, ii = this.visibleItems.length; i < ii; i++) {
12716    var item = this.visibleItems[i];
12717    item.repositionY(margin);
12718  }
12719
12720  return resized;
12721};
12722
12723/**
12724 * Show this group: attach to the DOM
12725 */
12726BackgroundGroup.prototype.show = function () {
12727  if (!this.dom.background.parentNode) {
12728    this.itemSet.dom.background.appendChild(this.dom.background);
12729  }
12730};
12731
12732module.exports = BackgroundGroup;
12733
12734/***/ }),
12735/* 70 */
12736/***/ (function(module, exports, __webpack_require__) {
12737
12738"use strict";
12739
12740
12741var Item = __webpack_require__(38);
12742
12743/**
12744 * @constructor RangeItem
12745 * @extends Item
12746 * @param {Object} data             Object containing parameters start, end
12747 *                                  content, className.
12748 * @param {{toScreen: function, toTime: function}} conversion
12749 *                                  Conversion functions from time to screen and vice versa
12750 * @param {Object} [options]        Configuration options
12751 *                                  // TODO: describe options
12752 */
12753function RangeItem(data, conversion, options) {
12754  this.props = {
12755    content: {
12756      width: 0
12757    }
12758  };
12759  this.overflow = false; // if contents can overflow (css styling), this flag is set to true
12760  this.options = options;
12761  // validate data
12762  if (data) {
12763    if (data.start == undefined) {
12764      throw new Error('Property "start" missing in item ' + data.id);
12765    }
12766    if (data.end == undefined) {
12767      throw new Error('Property "end" missing in item ' + data.id);
12768    }
12769  }
12770
12771  Item.call(this, data, conversion, options);
12772}
12773
12774RangeItem.prototype = new Item(null, null, null);
12775
12776RangeItem.prototype.baseClassName = 'vis-item vis-range';
12777
12778/**
12779 * Check whether this item is visible inside given range
12780 *
12781 * @param {vis.Range} range with a timestamp for start and end
12782 * @returns {boolean} True if visible
12783 */
12784RangeItem.prototype.isVisible = function (range) {
12785  // determine visibility
12786  return this.data.start < range.end && this.data.end > range.start;
12787};
12788
12789RangeItem.prototype._createDomElement = function () {
12790  if (!this.dom) {
12791    // create DOM
12792    this.dom = {};
12793
12794    // background box
12795    this.dom.box = document.createElement('div');
12796    // className is updated in redraw()
12797
12798    // frame box (to prevent the item contents from overflowing)
12799    this.dom.frame = document.createElement('div');
12800    this.dom.frame.className = 'vis-item-overflow';
12801    this.dom.box.appendChild(this.dom.frame);
12802
12803    // visible frame box (showing the frame that is always visible)
12804    this.dom.visibleFrame = document.createElement('div');
12805    this.dom.visibleFrame.className = 'vis-item-visible-frame';
12806    this.dom.box.appendChild(this.dom.visibleFrame);
12807
12808    // contents box
12809    this.dom.content = document.createElement('div');
12810    this.dom.content.className = 'vis-item-content';
12811    this.dom.frame.appendChild(this.dom.content);
12812
12813    // attach this item as attribute
12814    this.dom.box['timeline-item'] = this;
12815
12816    this.dirty = true;
12817  }
12818};
12819
12820RangeItem.prototype._appendDomElement = function () {
12821  if (!this.parent) {
12822    throw new Error('Cannot redraw item: no parent attached');
12823  }
12824  if (!this.dom.box.parentNode) {
12825    var foreground = this.parent.dom.foreground;
12826    if (!foreground) {
12827      throw new Error('Cannot redraw item: parent has no foreground container element');
12828    }
12829    foreground.appendChild(this.dom.box);
12830  }
12831  this.displayed = true;
12832};
12833
12834RangeItem.prototype._updateDirtyDomComponents = function () {
12835  // update dirty DOM. An item is marked dirty when:
12836  // - the item is not yet rendered
12837  // - the item's data is changed
12838  // - the item is selected/deselected
12839  if (this.dirty) {
12840    this._updateContents(this.dom.content);
12841    this._updateDataAttributes(this.dom.box);
12842    this._updateStyle(this.dom.box);
12843
12844    var editable = this.editable.updateTime || this.editable.updateGroup;
12845
12846    // update class
12847    var className = (this.data.className ? ' ' + this.data.className : '') + (this.selected ? ' vis-selected' : '') + (editable ? ' vis-editable' : ' vis-readonly');
12848    this.dom.box.className = this.baseClassName + className;
12849
12850    // turn off max-width to be able to calculate the real width
12851    // this causes an extra browser repaint/reflow, but so be it
12852    this.dom.content.style.maxWidth = 'none';
12853  }
12854};
12855
12856RangeItem.prototype._getDomComponentsSizes = function () {
12857  // determine from css whether this box has overflow
12858  this.overflow = window.getComputedStyle(this.dom.frame).overflow !== 'hidden';
12859  return {
12860    content: {
12861      width: this.dom.content.offsetWidth
12862    },
12863    box: {
12864      height: this.dom.box.offsetHeight
12865    }
12866  };
12867};
12868
12869RangeItem.prototype._updateDomComponentsSizes = function (sizes) {
12870  this.props.content.width = sizes.content.width;
12871  this.height = sizes.box.height;
12872  this.dom.content.style.maxWidth = '';
12873  this.dirty = false;
12874};
12875
12876RangeItem.prototype._repaintDomAdditionals = function () {
12877  this._repaintOnItemUpdateTimeTooltip(this.dom.box);
12878  this._repaintDeleteButton(this.dom.box);
12879  this._repaintDragCenter();
12880  this._repaintDragLeft();
12881  this._repaintDragRight();
12882};
12883
12884/**
12885 * Repaint the item
12886 * @param {boolean} [returnQueue=false]  return the queue
12887 * @return {boolean} the redraw queue if returnQueue=true
12888 */
12889RangeItem.prototype.redraw = function (returnQueue) {
12890  var sizes;
12891  var queue = [
12892  // create item DOM
12893  this._createDomElement.bind(this),
12894
12895  // append DOM to parent DOM
12896  this._appendDomElement.bind(this),
12897
12898  // update dirty DOM 
12899  this._updateDirtyDomComponents.bind(this), function () {
12900    if (this.dirty) {
12901      sizes = this._getDomComponentsSizes.bind(this)();
12902    }
12903  }.bind(this), function () {
12904    if (this.dirty) {
12905      this._updateDomComponentsSizes.bind(this)(sizes);
12906    }
12907  }.bind(this),
12908
12909  // repaint DOM additionals
12910  this._repaintDomAdditionals.bind(this)];
12911
12912  if (returnQueue) {
12913    return queue;
12914  } else {
12915    var result;
12916    queue.forEach(function (fn) {
12917      result = fn();
12918    });
12919    return result;
12920  }
12921};
12922
12923/**
12924 * Show the item in the DOM (when not already visible). The items DOM will
12925 * be created when needed.
12926 */
12927RangeItem.prototype.show = function () {
12928  if (!this.displayed) {
12929    this.redraw();
12930  }
12931};
12932
12933/**
12934 * Hide the item from the DOM (when visible)
12935 */
12936RangeItem.prototype.hide = function () {
12937  if (this.displayed) {
12938    var box = this.dom.box;
12939
12940    if (box.parentNode) {
12941      box.parentNode.removeChild(box);
12942    }
12943
12944    this.displayed = false;
12945  }
12946};
12947
12948/**
12949 * Reposition the item horizontally
12950 * @param {boolean} [limitSize=true] If true (default), the width of the range
12951 *                                   item will be limited, as the browser cannot
12952 *                                   display very wide divs. This means though
12953 *                                   that the applied left and width may
12954 *                                   not correspond to the ranges start and end
12955 * @Override
12956 */
12957RangeItem.prototype.repositionX = function (limitSize) {
12958  var parentWidth = this.parent.width;
12959  var start = this.conversion.toScreen(this.data.start);
12960  var end = this.conversion.toScreen(this.data.end);
12961  var align = this.data.align === undefined ? this.options.align : this.data.align;
12962  var contentStartPosition;
12963  var contentWidth;
12964
12965  // limit the width of the range, as browsers cannot draw very wide divs
12966  // unless limitSize: false is explicitly set in item data
12967  if (this.data.limitSize !== false && (limitSize === undefined || limitSize === true)) {
12968    if (start < -parentWidth) {
12969      start = -parentWidth;
12970    }
12971    if (end > 2 * parentWidth) {
12972      end = 2 * parentWidth;
12973    }
12974  }
12975
12976  // add 0.5 to compensate floating-point values rounding
12977  var boxWidth = Math.max(end - start + 0.5, 1);
12978
12979  if (this.overflow) {
12980    if (this.options.rtl) {
12981      this.right = start;
12982    } else {
12983      this.left = start;
12984    }
12985    this.width = boxWidth + this.props.content.width;
12986    contentWidth = this.props.content.width;
12987
12988    // Note: The calculation of width is an optimistic calculation, giving
12989    //       a width which will not change when moving the Timeline
12990    //       So no re-stacking needed, which is nicer for the eye;
12991  } else {
12992    if (this.options.rtl) {
12993      this.right = start;
12994    } else {
12995      this.left = start;
12996    }
12997    this.width = boxWidth;
12998    contentWidth = Math.min(end - start, this.props.content.width);
12999  }
13000
13001  if (this.options.rtl) {
13002    this.dom.box.style.right = this.right + 'px';
13003  } else {
13004    this.dom.box.style.left = this.left + 'px';
13005  }
13006  this.dom.box.style.width = boxWidth + 'px';
13007
13008  switch (align) {
13009    case 'left':
13010      if (this.options.rtl) {
13011        this.dom.content.style.right = '0';
13012      } else {
13013        this.dom.content.style.left = '0';
13014      }
13015      break;
13016
13017    case 'right':
13018      if (this.options.rtl) {
13019        this.dom.content.style.right = Math.max(boxWidth - contentWidth, 0) + 'px';
13020      } else {
13021        this.dom.content.style.left = Math.max(boxWidth - contentWidth, 0) + 'px';
13022      }
13023      break;
13024
13025    case 'center':
13026      if (this.options.rtl) {
13027        this.dom.content.style.right = Math.max((boxWidth - contentWidth) / 2, 0) + 'px';
13028      } else {
13029        this.dom.content.style.left = Math.max((boxWidth - contentWidth) / 2, 0) + 'px';
13030      }
13031
13032      break;
13033
13034    default:
13035      // 'auto'
13036      // when range exceeds left of the window, position the contents at the left of the visible area
13037      if (this.overflow) {
13038        if (end > 0) {
13039          contentStartPosition = Math.max(-start, 0);
13040        } else {
13041          contentStartPosition = -contentWidth; // ensure it's not visible anymore
13042        }
13043      } else {
13044        if (start < 0) {
13045          contentStartPosition = -start;
13046        } else {
13047          contentStartPosition = 0;
13048        }
13049      }
13050      if (this.options.rtl) {
13051        this.dom.content.style.right = contentStartPosition + 'px';
13052      } else {
13053        this.dom.content.style.left = contentStartPosition + 'px';
13054        this.dom.content.style.width = 'calc(100% - ' + contentStartPosition + 'px)';
13055      }
13056  }
13057};
13058
13059/**
13060 * Reposition the item vertically
13061 * @Override
13062 */
13063RangeItem.prototype.repositionY = function () {
13064  var orientation = this.options.orientation.item;
13065  var box = this.dom.box;
13066
13067  if (orientation == 'top') {
13068    box.style.top = this.top + 'px';
13069  } else {
13070    box.style.top = this.parent.height - this.top - this.height + 'px';
13071  }
13072};
13073
13074/**
13075 * Repaint a drag area on the left side of the range when the range is selected
13076 * @protected
13077 */
13078RangeItem.prototype._repaintDragLeft = function () {
13079  if ((this.selected || this.options.itemsAlwaysDraggable.range) && this.options.editable.updateTime && !this.dom.dragLeft) {
13080    // create and show drag area
13081    var dragLeft = document.createElement('div');
13082    dragLeft.className = 'vis-drag-left';
13083    dragLeft.dragLeftItem = this;
13084
13085    this.dom.box.appendChild(dragLeft);
13086    this.dom.dragLeft = dragLeft;
13087  } else if (!this.selected && !this.options.itemsAlwaysDraggable.range && this.dom.dragLeft) {
13088    // delete drag area
13089    if (this.dom.dragLeft.parentNode) {
13090      this.dom.dragLeft.parentNode.removeChild(this.dom.dragLeft);
13091    }
13092    this.dom.dragLeft = null;
13093  }
13094};
13095
13096/**
13097 * Repaint a drag area on the right side of the range when the range is selected
13098 * @protected
13099 */
13100RangeItem.prototype._repaintDragRight = function () {
13101  if ((this.selected || this.options.itemsAlwaysDraggable.range) && this.options.editable.updateTime && !this.dom.dragRight) {
13102    // create and show drag area
13103    var dragRight = document.createElement('div');
13104    dragRight.className = 'vis-drag-right';
13105    dragRight.dragRightItem = this;
13106
13107    this.dom.box.appendChild(dragRight);
13108    this.dom.dragRight = dragRight;
13109  } else if (!this.selected && !this.options.itemsAlwaysDraggable.range && this.dom.dragRight) {
13110    // delete drag area
13111    if (this.dom.dragRight.parentNode) {
13112      this.dom.dragRight.parentNode.removeChild(this.dom.dragRight);
13113    }
13114    this.dom.dragRight = null;
13115  }
13116};
13117
13118module.exports = RangeItem;
13119
13120/***/ }),
13121/* 71 */
13122/***/ (function(module, exports, __webpack_require__) {
13123
13124"use strict";
13125
13126
13127Object.defineProperty(exports, "__esModule", {
13128  value: true
13129});
13130
13131var _stringify = __webpack_require__(19);
13132
13133var _stringify2 = _interopRequireDefault(_stringify);
13134
13135var _typeof2 = __webpack_require__(6);
13136
13137var _typeof3 = _interopRequireDefault(_typeof2);
13138
13139var _classCallCheck2 = __webpack_require__(0);
13140
13141var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
13142
13143var _createClass2 = __webpack_require__(1);
13144
13145var _createClass3 = _interopRequireDefault(_createClass2);
13146
13147function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
13148
13149var util = __webpack_require__(2);
13150
13151var ColorPicker = __webpack_require__(179)['default'];
13152
13153/**
13154 * 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.
13155 * Boolean options are recognised as Boolean
13156 * Number options should be written as array: [default value, min value, max value, stepsize]
13157 * Colors should be written as array: ['color', '#ffffff']
13158 * Strings with should be written as array: [option1, option2, option3, ..]
13159 *
13160 * The options are matched with their counterparts in each of the modules and the values used in the configuration are
13161 */
13162
13163var Configurator = function () {
13164  /**
13165   * @param {Object} parentModule        | the location where parentModule.setOptions() can be called
13166   * @param {Object} defaultContainer    | the default container of the module
13167   * @param {Object} configureOptions    | the fully configured and predefined options set found in allOptions.js
13168   * @param {number} pixelRatio          | canvas pixel ratio
13169   */
13170  function Configurator(parentModule, defaultContainer, configureOptions) {
13171    var pixelRatio = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 1;
13172    (0, _classCallCheck3['default'])(this, Configurator);
13173
13174    this.parent = parentModule;
13175    this.changedOptions = [];
13176    this.container = defaultContainer;
13177    this.allowCreation = false;
13178
13179    this.options = {};
13180    this.initialized = false;
13181    this.popupCounter = 0;
13182    this.defaultOptions = {
13183      enabled: false,
13184      filter: true,
13185      container: undefined,
13186      showButton: true
13187    };
13188    util.extend(this.options, this.defaultOptions);
13189
13190    this.configureOptions = configureOptions;
13191    this.moduleOptions = {};
13192    this.domElements = [];
13193    this.popupDiv = {};
13194    this.popupLimit = 5;
13195    this.popupHistory = {};
13196    this.colorPicker = new ColorPicker(pixelRatio);
13197    this.wrapper = undefined;
13198  }
13199
13200  /**
13201   * refresh all options.
13202   * Because all modules parse their options by themselves, we just use their options. We copy them here.
13203   *
13204   * @param {Object} options
13205   */
13206
13207
13208  (0, _createClass3['default'])(Configurator, [{
13209    key: 'setOptions',
13210    value: function setOptions(options) {
13211      if (options !== undefined) {
13212        // reset the popup history because the indices may have been changed.
13213        this.popupHistory = {};
13214        this._removePopup();
13215
13216        var enabled = true;
13217        if (typeof options === 'string') {
13218          this.options.filter = options;
13219        } else if (options instanceof Array) {
13220          this.options.filter = options.join();
13221        } else if ((typeof options === 'undefined' ? 'undefined' : (0, _typeof3['default'])(options)) === 'object') {
13222          if (options.container !== undefined) {
13223            this.options.container = options.container;
13224          }
13225          if (options.filter !== undefined) {
13226            this.options.filter = options.filter;
13227          }
13228          if (options.showButton !== undefined) {
13229            this.options.showButton = options.showButton;
13230          }
13231          if (options.enabled !== undefined) {
13232            enabled = options.enabled;
13233          }
13234        } else if (typeof options === 'boolean') {
13235          this.options.filter = true;
13236          enabled = options;
13237        } else if (typeof options === 'function') {
13238          this.options.filter = options;
13239          enabled = true;
13240        }
13241        if (this.options.filter === false) {
13242          enabled = false;
13243        }
13244
13245        this.options.enabled = enabled;
13246      }
13247      this._clean();
13248    }
13249
13250    /**
13251     *
13252     * @param {Object} moduleOptions
13253     */
13254
13255  }, {
13256    key: 'setModuleOptions',
13257    value: function setModuleOptions(moduleOptions) {
13258      this.moduleOptions = moduleOptions;
13259      if (this.options.enabled === true) {
13260        this._clean();
13261        if (this.options.container !== undefined) {
13262          this.container = this.options.container;
13263        }
13264        this._create();
13265      }
13266    }
13267
13268    /**
13269     * Create all DOM elements
13270     * @private
13271     */
13272
13273  }, {
13274    key: '_create',
13275    value: function _create() {
13276      var _this = this;
13277
13278      this._clean();
13279      this.changedOptions = [];
13280
13281      var filter = this.options.filter;
13282      var counter = 0;
13283      var show = false;
13284      for (var option in this.configureOptions) {
13285        if (this.configureOptions.hasOwnProperty(option)) {
13286          this.allowCreation = false;
13287          show = false;
13288          if (typeof filter === 'function') {
13289            show = filter(option, []);
13290            show = show || this._handleObject(this.configureOptions[option], [option], true);
13291          } else if (filter === true || filter.indexOf(option) !== -1) {
13292            show = true;
13293          }
13294
13295          if (show !== false) {
13296            this.allowCreation = true;
13297
13298            // linebreak between categories
13299            if (counter > 0) {
13300              this._makeItem([]);
13301            }
13302            // a header for the category
13303            this._makeHeader(option);
13304
13305            // get the sub options
13306            this._handleObject(this.configureOptions[option], [option]);
13307          }
13308          counter++;
13309        }
13310      }
13311
13312      if (this.options.showButton === true) {
13313        var generateButton = document.createElement('div');
13314        generateButton.className = 'vis-configuration vis-config-button';
13315        generateButton.innerHTML = 'generate options';
13316        generateButton.onclick = function () {
13317          _this._printOptions();
13318        };
13319        generateButton.onmouseover = function () {
13320          generateButton.className = 'vis-configuration vis-config-button hover';
13321        };
13322        generateButton.onmouseout = function () {
13323          generateButton.className = 'vis-configuration vis-config-button';
13324        };
13325
13326        this.optionsContainer = document.createElement('div');
13327        this.optionsContainer.className = 'vis-configuration vis-config-option-container';
13328
13329        this.domElements.push(this.optionsContainer);
13330        this.domElements.push(generateButton);
13331      }
13332
13333      this._push();
13334      //~ this.colorPicker.insertTo(this.container);
13335    }
13336
13337    /**
13338     * draw all DOM elements on the screen
13339     * @private
13340     */
13341
13342  }, {
13343    key: '_push',
13344    value: function _push() {
13345      this.wrapper = document.createElement('div');
13346      this.wrapper.className = 'vis-configuration-wrapper';
13347      this.container.appendChild(this.wrapper);
13348      for (var i = 0; i < this.domElements.length; i++) {
13349        this.wrapper.appendChild(this.domElements[i]);
13350      }
13351
13352      this._showPopupIfNeeded();
13353    }
13354
13355    /**
13356     * delete all DOM elements
13357     * @private
13358     */
13359
13360  }, {
13361    key: '_clean',
13362    value: function _clean() {
13363      for (var i = 0; i < this.domElements.length; i++) {
13364        this.wrapper.removeChild(this.domElements[i]);
13365      }
13366
13367      if (this.wrapper !== undefined) {
13368        this.container.removeChild(this.wrapper);
13369        this.wrapper = undefined;
13370      }
13371      this.domElements = [];
13372
13373      this._removePopup();
13374    }
13375
13376    /**
13377     * get the value from the actualOptions if it exists
13378     * @param {array} path    | where to look for the actual option
13379     * @returns {*}
13380     * @private
13381     */
13382
13383  }, {
13384    key: '_getValue',
13385    value: function _getValue(path) {
13386      var base = this.moduleOptions;
13387      for (var i = 0; i < path.length; i++) {
13388        if (base[path[i]] !== undefined) {
13389          base = base[path[i]];
13390        } else {
13391          base = undefined;
13392          break;
13393        }
13394      }
13395      return base;
13396    }
13397
13398    /**
13399     * all option elements are wrapped in an item
13400     * @param {Array} path    | where to look for the actual option
13401     * @param {Array.<Element>} domElements
13402     * @returns {number}
13403     * @private
13404     */
13405
13406  }, {
13407    key: '_makeItem',
13408    value: function _makeItem(path) {
13409      if (this.allowCreation === true) {
13410        var item = document.createElement('div');
13411        item.className = 'vis-configuration vis-config-item vis-config-s' + path.length;
13412
13413        for (var _len = arguments.length, domElements = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
13414          domElements[_key - 1] = arguments[_key];
13415        }
13416
13417        domElements.forEach(function (element) {
13418          item.appendChild(element);
13419        });
13420        this.domElements.push(item);
13421        return this.domElements.length;
13422      }
13423      return 0;
13424    }
13425
13426    /**
13427     * header for major subjects
13428     * @param {string} name
13429     * @private
13430     */
13431
13432  }, {
13433    key: '_makeHeader',
13434    value: function _makeHeader(name) {
13435      var div = document.createElement('div');
13436      div.className = 'vis-configuration vis-config-header';
13437      div.innerHTML = name;
13438      this._makeItem([], div);
13439    }
13440
13441    /**
13442     * make a label, if it is an object label, it gets different styling.
13443     * @param {string} name
13444     * @param {array} path    | where to look for the actual option
13445     * @param {string} objectLabel
13446     * @returns {HTMLElement}
13447     * @private
13448     */
13449
13450  }, {
13451    key: '_makeLabel',
13452    value: function _makeLabel(name, path) {
13453      var objectLabel = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
13454
13455      var div = document.createElement('div');
13456      div.className = 'vis-configuration vis-config-label vis-config-s' + path.length;
13457      if (objectLabel === true) {
13458        div.innerHTML = '<i><b>' + name + ':</b></i>';
13459      } else {
13460        div.innerHTML = name + ':';
13461      }
13462      return div;
13463    }
13464
13465    /**
13466     * make a dropdown list for multiple possible string optoins
13467     * @param {Array.<number>} arr
13468     * @param {number} value
13469     * @param {array} path    | where to look for the actual option
13470     * @private
13471     */
13472
13473  }, {
13474    key: '_makeDropdown',
13475    value: function _makeDropdown(arr, value, path) {
13476      var select = document.createElement('select');
13477      select.className = 'vis-configuration vis-config-select';
13478      var selectedValue = 0;
13479      if (value !== undefined) {
13480        if (arr.indexOf(value) !== -1) {
13481          selectedValue = arr.indexOf(value);
13482        }
13483      }
13484
13485      for (var i = 0; i < arr.length; i++) {
13486        var option = document.createElement('option');
13487        option.value = arr[i];
13488        if (i === selectedValue) {
13489          option.selected = 'selected';
13490        }
13491        option.innerHTML = arr[i];
13492        select.appendChild(option);
13493      }
13494
13495      var me = this;
13496      select.onchange = function () {
13497        me._update(this.value, path);
13498      };
13499
13500      var label = this._makeLabel(path[path.length - 1], path);
13501      this._makeItem(path, label, select);
13502    }
13503
13504    /**
13505     * make a range object for numeric options
13506     * @param {Array.<number>} arr
13507     * @param {number} value
13508     * @param {array} path    | where to look for the actual option
13509     * @private
13510     */
13511
13512  }, {
13513    key: '_makeRange',
13514    value: function _makeRange(arr, value, path) {
13515      var defaultValue = arr[0];
13516      var min = arr[1];
13517      var max = arr[2];
13518      var step = arr[3];
13519      var range = document.createElement('input');
13520      range.className = 'vis-configuration vis-config-range';
13521      try {
13522        range.type = 'range'; // not supported on IE9
13523        range.min = min;
13524        range.max = max;
13525      }
13526      // TODO: Add some error handling and remove this lint exception
13527      catch (err) {} // eslint-disable-line no-empty
13528      range.step = step;
13529
13530      // set up the popup settings in case they are needed.
13531      var popupString = '';
13532      var popupValue = 0;
13533
13534      if (value !== undefined) {
13535        var factor = 1.20;
13536        if (value < 0 && value * factor < min) {
13537          range.min = Math.ceil(value * factor);
13538          popupValue = range.min;
13539          popupString = 'range increased';
13540        } else if (value / factor < min) {
13541          range.min = Math.ceil(value / factor);
13542          popupValue = range.min;
13543          popupString = 'range increased';
13544        }
13545        if (value * factor > max && max !== 1) {
13546          range.max = Math.ceil(value * factor);
13547          popupValue = range.max;
13548          popupString = 'range increased';
13549        }
13550        range.value = value;
13551      } else {
13552        range.value = defaultValue;
13553      }
13554
13555      var input = document.createElement('input');
13556      input.className = 'vis-configuration vis-config-rangeinput';
13557      input.value = range.value;
13558
13559      var me = this;
13560      range.onchange = function () {
13561        input.value = this.value;me._update(Number(this.value), path);
13562      };
13563      range.oninput = function () {
13564        input.value = this.value;
13565      };
13566
13567      var label = this._makeLabel(path[path.length - 1], path);
13568      var itemIndex = this._makeItem(path, label, range, input);
13569
13570      // if a popup is needed AND it has not been shown for this value, show it.
13571      if (popupString !== '' && this.popupHistory[itemIndex] !== popupValue) {
13572        this.popupHistory[itemIndex] = popupValue;
13573        this._setupPopup(popupString, itemIndex);
13574      }
13575    }
13576
13577    /**
13578     * prepare the popup
13579     * @param {string} string
13580     * @param {number} index
13581     * @private
13582     */
13583
13584  }, {
13585    key: '_setupPopup',
13586    value: function _setupPopup(string, index) {
13587      var _this2 = this;
13588
13589      if (this.initialized === true && this.allowCreation === true && this.popupCounter < this.popupLimit) {
13590        var div = document.createElement("div");
13591        div.id = "vis-configuration-popup";
13592        div.className = "vis-configuration-popup";
13593        div.innerHTML = string;
13594        div.onclick = function () {
13595          _this2._removePopup();
13596        };
13597        this.popupCounter += 1;
13598        this.popupDiv = { html: div, index: index };
13599      }
13600    }
13601
13602    /**
13603     * remove the popup from the dom
13604     * @private
13605     */
13606
13607  }, {
13608    key: '_removePopup',
13609    value: function _removePopup() {
13610      if (this.popupDiv.html !== undefined) {
13611        this.popupDiv.html.parentNode.removeChild(this.popupDiv.html);
13612        clearTimeout(this.popupDiv.hideTimeout);
13613        clearTimeout(this.popupDiv.deleteTimeout);
13614        this.popupDiv = {};
13615      }
13616    }
13617
13618    /**
13619     * Show the popup if it is needed.
13620     * @private
13621     */
13622
13623  }, {
13624    key: '_showPopupIfNeeded',
13625    value: function _showPopupIfNeeded() {
13626      var _this3 = this;
13627
13628      if (this.popupDiv.html !== undefined) {
13629        var correspondingElement = this.domElements[this.popupDiv.index];
13630        var rect = correspondingElement.getBoundingClientRect();
13631        this.popupDiv.html.style.left = rect.left + "px";
13632        this.popupDiv.html.style.top = rect.top - 30 + "px"; // 30 is the height;
13633        document.body.appendChild(this.popupDiv.html);
13634        this.popupDiv.hideTimeout = setTimeout(function () {
13635          _this3.popupDiv.html.style.opacity = 0;
13636        }, 1500);
13637        this.popupDiv.deleteTimeout = setTimeout(function () {
13638          _this3._removePopup();
13639        }, 1800);
13640      }
13641    }
13642
13643    /**
13644     * make a checkbox for boolean options.
13645     * @param {number} defaultValue
13646     * @param {number} value
13647     * @param {array} path    | where to look for the actual option
13648     * @private
13649     */
13650
13651  }, {
13652    key: '_makeCheckbox',
13653    value: function _makeCheckbox(defaultValue, value, path) {
13654      var checkbox = document.createElement('input');
13655      checkbox.type = 'checkbox';
13656      checkbox.className = 'vis-configuration vis-config-checkbox';
13657      checkbox.checked = defaultValue;
13658      if (value !== undefined) {
13659        checkbox.checked = value;
13660        if (value !== defaultValue) {
13661          if ((typeof defaultValue === 'undefined' ? 'undefined' : (0, _typeof3['default'])(defaultValue)) === 'object') {
13662            if (value !== defaultValue.enabled) {
13663              this.changedOptions.push({ path: path, value: value });
13664            }
13665          } else {
13666            this.changedOptions.push({ path: path, value: value });
13667          }
13668        }
13669      }
13670
13671      var me = this;
13672      checkbox.onchange = function () {
13673        me._update(this.checked, path);
13674      };
13675
13676      var label = this._makeLabel(path[path.length - 1], path);
13677      this._makeItem(path, label, checkbox);
13678    }
13679
13680    /**
13681     * make a text input field for string options.
13682     * @param {number} defaultValue
13683     * @param {number} value
13684     * @param {array} path    | where to look for the actual option
13685     * @private
13686     */
13687
13688  }, {
13689    key: '_makeTextInput',
13690    value: function _makeTextInput(defaultValue, value, path) {
13691      var checkbox = document.createElement('input');
13692      checkbox.type = 'text';
13693      checkbox.className = 'vis-configuration vis-config-text';
13694      checkbox.value = value;
13695      if (value !== defaultValue) {
13696        this.changedOptions.push({ path: path, value: value });
13697      }
13698
13699      var me = this;
13700      checkbox.onchange = function () {
13701        me._update(this.value, path);
13702      };
13703
13704      var label = this._makeLabel(path[path.length - 1], path);
13705      this._makeItem(path, label, checkbox);
13706    }
13707
13708    /**
13709     * make a color field with a color picker for color fields
13710     * @param {Array.<number>} arr
13711     * @param {number} value
13712     * @param {array} path    | where to look for the actual option
13713     * @private
13714     */
13715
13716  }, {
13717    key: '_makeColorField',
13718    value: function _makeColorField(arr, value, path) {
13719      var _this4 = this;
13720
13721      var defaultColor = arr[1];
13722      var div = document.createElement('div');
13723      value = value === undefined ? defaultColor : value;
13724
13725      if (value !== 'none') {
13726        div.className = 'vis-configuration vis-config-colorBlock';
13727        div.style.backgroundColor = value;
13728      } else {
13729        div.className = 'vis-configuration vis-config-colorBlock none';
13730      }
13731
13732      value = value === undefined ? defaultColor : value;
13733      div.onclick = function () {
13734        _this4._showColorPicker(value, div, path);
13735      };
13736
13737      var label = this._makeLabel(path[path.length - 1], path);
13738      this._makeItem(path, label, div);
13739    }
13740
13741    /**
13742     * used by the color buttons to call the color picker.
13743     * @param {number} value
13744     * @param {HTMLElement} div
13745     * @param {array} path    | where to look for the actual option
13746     * @private
13747     */
13748
13749  }, {
13750    key: '_showColorPicker',
13751    value: function _showColorPicker(value, div, path) {
13752      var _this5 = this;
13753
13754      // clear the callback from this div
13755      div.onclick = function () {};
13756
13757      this.colorPicker.insertTo(div);
13758      this.colorPicker.show();
13759
13760      this.colorPicker.setColor(value);
13761      this.colorPicker.setUpdateCallback(function (color) {
13762        var colorString = 'rgba(' + color.r + ',' + color.g + ',' + color.b + ',' + color.a + ')';
13763        div.style.backgroundColor = colorString;
13764        _this5._update(colorString, path);
13765      });
13766
13767      // on close of the colorpicker, restore the callback.
13768      this.colorPicker.setCloseCallback(function () {
13769        div.onclick = function () {
13770          _this5._showColorPicker(value, div, path);
13771        };
13772      });
13773    }
13774
13775    /**
13776     * parse an object and draw the correct items
13777     * @param {Object} obj
13778     * @param {array} [path=[]]    | where to look for the actual option
13779     * @param {boolean} [checkOnly=false]
13780     * @returns {boolean}
13781     * @private
13782     */
13783
13784  }, {
13785    key: '_handleObject',
13786    value: function _handleObject(obj) {
13787      var path = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];
13788      var checkOnly = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
13789
13790      var show = false;
13791      var filter = this.options.filter;
13792      var visibleInSet = false;
13793      for (var subObj in obj) {
13794        if (obj.hasOwnProperty(subObj)) {
13795          show = true;
13796          var item = obj[subObj];
13797          var newPath = util.copyAndExtendArray(path, subObj);
13798          if (typeof filter === 'function') {
13799            show = filter(subObj, path);
13800
13801            // if needed we must go deeper into the object.
13802            if (show === false) {
13803              if (!(item instanceof Array) && typeof item !== 'string' && typeof item !== 'boolean' && item instanceof Object) {
13804                this.allowCreation = false;
13805                show = this._handleObject(item, newPath, true);
13806                this.allowCreation = checkOnly === false;
13807              }
13808            }
13809          }
13810
13811          if (show !== false) {
13812            visibleInSet = true;
13813            var value = this._getValue(newPath);
13814
13815            if (item instanceof Array) {
13816              this._handleArray(item, value, newPath);
13817            } else if (typeof item === 'string') {
13818              this._makeTextInput(item, value, newPath);
13819            } else if (typeof item === 'boolean') {
13820              this._makeCheckbox(item, value, newPath);
13821            } else if (item instanceof Object) {
13822              // collapse the physics options that are not enabled
13823              var draw = true;
13824              if (path.indexOf('physics') !== -1) {
13825                if (this.moduleOptions.physics.solver !== subObj) {
13826                  draw = false;
13827                }
13828              }
13829
13830              if (draw === true) {
13831                // initially collapse options with an disabled enabled option.
13832                if (item.enabled !== undefined) {
13833                  var enabledPath = util.copyAndExtendArray(newPath, 'enabled');
13834                  var enabledValue = this._getValue(enabledPath);
13835                  if (enabledValue === true) {
13836                    var label = this._makeLabel(subObj, newPath, true);
13837                    this._makeItem(newPath, label);
13838                    visibleInSet = this._handleObject(item, newPath) || visibleInSet;
13839                  } else {
13840                    this._makeCheckbox(item, enabledValue, newPath);
13841                  }
13842                } else {
13843                  var _label = this._makeLabel(subObj, newPath, true);
13844                  this._makeItem(newPath, _label);
13845                  visibleInSet = this._handleObject(item, newPath) || visibleInSet;
13846                }
13847              }
13848            } else {
13849              console.error('dont know how to handle', item, subObj, newPath);
13850            }
13851          }
13852        }
13853      }
13854      return visibleInSet;
13855    }
13856
13857    /**
13858     * handle the array type of option
13859     * @param {Array.<number>} arr
13860     * @param {number} value
13861     * @param {array} path    | where to look for the actual option
13862     * @private
13863     */
13864
13865  }, {
13866    key: '_handleArray',
13867    value: function _handleArray(arr, value, path) {
13868      if (typeof arr[0] === 'string' && arr[0] === 'color') {
13869        this._makeColorField(arr, value, path);
13870        if (arr[1] !== value) {
13871          this.changedOptions.push({ path: path, value: value });
13872        }
13873      } else if (typeof arr[0] === 'string') {
13874        this._makeDropdown(arr, value, path);
13875        if (arr[0] !== value) {
13876          this.changedOptions.push({ path: path, value: value });
13877        }
13878      } else if (typeof arr[0] === 'number') {
13879        this._makeRange(arr, value, path);
13880        if (arr[0] !== value) {
13881          this.changedOptions.push({ path: path, value: Number(value) });
13882        }
13883      }
13884    }
13885
13886    /**
13887     * called to update the network with the new settings.
13888     * @param {number} value
13889     * @param {array} path    | where to look for the actual option
13890     * @private
13891     */
13892
13893  }, {
13894    key: '_update',
13895    value: function _update(value, path) {
13896      var options = this._constructOptions(value, path);
13897
13898      if (this.parent.body && this.parent.body.emitter && this.parent.body.emitter.emit) {
13899        this.parent.body.emitter.emit("configChange", options);
13900      }
13901      this.initialized = true;
13902      this.parent.setOptions(options);
13903    }
13904
13905    /**
13906     *
13907     * @param {string|Boolean} value
13908     * @param {Array.<string>} path
13909     * @param {{}} optionsObj
13910     * @returns {{}}
13911     * @private
13912     */
13913
13914  }, {
13915    key: '_constructOptions',
13916    value: function _constructOptions(value, path) {
13917      var optionsObj = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
13918
13919      var pointer = optionsObj;
13920
13921      // when dropdown boxes can be string or boolean, we typecast it into correct types
13922      value = value === 'true' ? true : value;
13923      value = value === 'false' ? false : value;
13924
13925      for (var i = 0; i < path.length; i++) {
13926        if (path[i] !== 'global') {
13927          if (pointer[path[i]] === undefined) {
13928            pointer[path[i]] = {};
13929          }
13930          if (i !== path.length - 1) {
13931            pointer = pointer[path[i]];
13932          } else {
13933            pointer[path[i]] = value;
13934          }
13935        }
13936      }
13937      return optionsObj;
13938    }
13939
13940    /**
13941     * @private
13942     */
13943
13944  }, {
13945    key: '_printOptions',
13946    value: function _printOptions() {
13947      var options = this.getOptions();
13948      this.optionsContainer.innerHTML = '<pre>var options = ' + (0, _stringify2['default'])(options, null, 2) + '</pre>';
13949    }
13950
13951    /**
13952     *
13953     * @returns {{}} options
13954     */
13955
13956  }, {
13957    key: 'getOptions',
13958    value: function getOptions() {
13959      var options = {};
13960      for (var i = 0; i < this.changedOptions.length; i++) {
13961        this._constructOptions(this.changedOptions[i].value, this.changedOptions[i].path, options);
13962      }
13963      return options;
13964    }
13965  }]);
13966  return Configurator;
13967}();
13968
13969exports['default'] = Configurator;
13970
13971/***/ }),
13972/* 72 */
13973/***/ (function(module, exports, __webpack_require__) {
13974
13975"use strict";
13976
13977
13978var _typeof2 = __webpack_require__(6);
13979
13980var _typeof3 = _interopRequireDefault(_typeof2);
13981
13982function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
13983
13984var DOMutil = __webpack_require__(14);
13985
13986/**
13987 *
13988 * @param {number | string} groupId
13989 * @param {Object} options   // TODO: Describe options
13990 *
13991 * @constructor Points
13992 */
13993function Points(groupId, options) {} // eslint-disable-line no-unused-vars
13994
13995
13996/**
13997 * draw the data points
13998 *
13999 * @param {Array} dataset
14000 * @param {GraphGroup} group
14001 * @param {Object} framework            | SVG DOM element
14002 * @param {number} [offset]
14003 */
14004Points.draw = function (dataset, group, framework, offset) {
14005  offset = offset || 0;
14006  var callback = getCallback(framework, group);
14007
14008  for (var i = 0; i < dataset.length; i++) {
14009    if (!callback) {
14010      // draw the point the simple way.
14011      DOMutil.drawPoint(dataset[i].screen_x + offset, dataset[i].screen_y, getGroupTemplate(group), framework.svgElements, framework.svg, dataset[i].label);
14012    } else {
14013      var callbackResult = callback(dataset[i], group); // result might be true, false or an object
14014      if (callbackResult === true || (typeof callbackResult === 'undefined' ? 'undefined' : (0, _typeof3['default'])(callbackResult)) === 'object') {
14015        DOMutil.drawPoint(dataset[i].screen_x + offset, dataset[i].screen_y, getGroupTemplate(group, callbackResult), framework.svgElements, framework.svg, dataset[i].label);
14016      }
14017    }
14018  }
14019};
14020
14021Points.drawIcon = function (group, x, y, iconWidth, iconHeight, framework) {
14022  var fillHeight = iconHeight * 0.5;
14023
14024  var outline = DOMutil.getSVGElement("rect", framework.svgElements, framework.svg);
14025  outline.setAttributeNS(null, "x", x);
14026  outline.setAttributeNS(null, "y", y - fillHeight);
14027  outline.setAttributeNS(null, "width", iconWidth);
14028  outline.setAttributeNS(null, "height", 2 * fillHeight);
14029  outline.setAttributeNS(null, "class", "vis-outline");
14030
14031  //Don't call callback on icon
14032  DOMutil.drawPoint(x + 0.5 * iconWidth, y, getGroupTemplate(group), framework.svgElements, framework.svg);
14033};
14034
14035/**
14036 *
14037 * @param {vis.Group} group
14038 * @param {any} callbackResult
14039 * @returns {{style: *, styles: (*|string), size: *, className: *}}
14040 */
14041function getGroupTemplate(group, callbackResult) {
14042  callbackResult = typeof callbackResult === 'undefined' ? {} : callbackResult;
14043  return {
14044    style: callbackResult.style || group.options.drawPoints.style,
14045    styles: callbackResult.styles || group.options.drawPoints.styles,
14046    size: callbackResult.size || group.options.drawPoints.size,
14047    className: callbackResult.className || group.className
14048  };
14049}
14050
14051/**
14052 *
14053 * @param {Object} framework            | SVG DOM element
14054 * @param {vis.Group} group
14055 * @returns {function}
14056 */
14057function getCallback(framework, group) {
14058  var callback = undefined;
14059  // check for the graph2d onRender
14060  if (framework.options && framework.options.drawPoints && framework.options.drawPoints.onRender && typeof framework.options.drawPoints.onRender == 'function') {
14061    callback = framework.options.drawPoints.onRender;
14062  }
14063
14064  // override it with the group onRender if defined
14065  if (group.group.options && group.group.options.drawPoints && group.group.options.drawPoints.onRender && typeof group.group.options.drawPoints.onRender == 'function') {
14066    callback = group.group.options.drawPoints.onRender;
14067  }
14068  return callback;
14069}
14070
14071module.exports = Points;
14072
14073/***/ }),
14074/* 73 */
14075/***/ (function(module, exports, __webpack_require__) {
14076
14077"use strict";
14078
14079
14080Object.defineProperty(exports, "__esModule", {
14081  value: true
14082});
14083
14084var _getPrototypeOf = __webpack_require__(3);
14085
14086var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf);
14087
14088var _classCallCheck2 = __webpack_require__(0);
14089
14090var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
14091
14092var _createClass2 = __webpack_require__(1);
14093
14094var _createClass3 = _interopRequireDefault(_createClass2);
14095
14096var _possibleConstructorReturn2 = __webpack_require__(4);
14097
14098var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2);
14099
14100var _inherits2 = __webpack_require__(5);
14101
14102var _inherits3 = _interopRequireDefault(_inherits2);
14103
14104var _NodeBase2 = __webpack_require__(23);
14105
14106var _NodeBase3 = _interopRequireDefault(_NodeBase2);
14107
14108function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
14109
14110/**
14111 * NOTE: This is a bad base class
14112 *
14113 * Child classes are:
14114 *
14115 *   Image       - uses *only* image methods
14116 *   Circle      - uses *only* _drawRawCircle
14117 *   CircleImage - uses all
14118 *
14119 * TODO: Refactor, move _drawRawCircle to different module, derive Circle from NodeBase
14120 *       Rename this to ImageBase
14121 *       Consolidate common code in Image and CircleImage to base class
14122 *
14123 * @extends NodeBase
14124 */
14125var CircleImageBase = function (_NodeBase) {
14126  (0, _inherits3['default'])(CircleImageBase, _NodeBase);
14127
14128  /**
14129   * @param {Object} options
14130   * @param {Object} body
14131   * @param {Label} labelModule
14132   */
14133  function CircleImageBase(options, body, labelModule) {
14134    (0, _classCallCheck3['default'])(this, CircleImageBase);
14135
14136    var _this = (0, _possibleConstructorReturn3['default'])(this, (CircleImageBase.__proto__ || (0, _getPrototypeOf2['default'])(CircleImageBase)).call(this, options, body, labelModule));
14137
14138    _this.labelOffset = 0;
14139    _this.selected = false;
14140    return _this;
14141  }
14142
14143  /**
14144   *
14145   * @param {Object} options
14146   * @param {Object} [imageObj]
14147   * @param {Object} [imageObjAlt]
14148   */
14149
14150
14151  (0, _createClass3['default'])(CircleImageBase, [{
14152    key: 'setOptions',
14153    value: function setOptions(options, imageObj, imageObjAlt) {
14154      this.options = options;
14155
14156      if (!(imageObj === undefined && imageObjAlt === undefined)) {
14157        this.setImages(imageObj, imageObjAlt);
14158      }
14159    }
14160
14161    /**
14162     * Set the images for this node.
14163     *
14164     * The images can be updated after the initial setting of options;
14165     * therefore, this method needs to be reentrant. 
14166     *
14167     * For correct working in error cases, it is necessary to properly set
14168     * field 'nodes.brokenImage' in the options.
14169     *
14170     * @param {Image} imageObj  required; main image to show for this node
14171     * @param {Image|undefined} imageObjAlt optional; image to show when node is selected
14172     */
14173
14174  }, {
14175    key: 'setImages',
14176    value: function setImages(imageObj, imageObjAlt) {
14177      if (imageObjAlt && this.selected) {
14178        this.imageObj = imageObjAlt;
14179        this.imageObjAlt = imageObj;
14180      } else {
14181        this.imageObj = imageObj;
14182        this.imageObjAlt = imageObjAlt;
14183      }
14184    }
14185
14186    /**
14187     * Set selection and switch between the base and the selected image.
14188     *
14189     * Do the switch only if imageObjAlt exists.
14190     *
14191     * @param {boolean} selected value of new selected state for current node
14192     */
14193
14194  }, {
14195    key: 'switchImages',
14196    value: function switchImages(selected) {
14197      var selection_changed = selected && !this.selected || !selected && this.selected;
14198      this.selected = selected; // Remember new selection
14199
14200      if (this.imageObjAlt !== undefined && selection_changed) {
14201        var imageTmp = this.imageObj;
14202        this.imageObj = this.imageObjAlt;
14203        this.imageObjAlt = imageTmp;
14204      }
14205    }
14206
14207    /**
14208     * Adjust the node dimensions for a loaded image.
14209     *
14210     * Pre: this.imageObj is valid
14211     */
14212
14213  }, {
14214    key: '_resizeImage',
14215    value: function _resizeImage() {
14216      var width, height;
14217
14218      if (this.options.shapeProperties.useImageSize === false) {
14219        // Use the size property
14220        var ratio_width = 1;
14221        var ratio_height = 1;
14222
14223        // Only calculate the proper ratio if both width and height not zero
14224        if (this.imageObj.width && this.imageObj.height) {
14225          if (this.imageObj.width > this.imageObj.height) {
14226            ratio_width = this.imageObj.width / this.imageObj.height;
14227          } else {
14228            ratio_height = this.imageObj.height / this.imageObj.width;
14229          }
14230        }
14231
14232        width = this.options.size * 2 * ratio_width;
14233        height = this.options.size * 2 * ratio_height;
14234      } else {
14235        // Use the image size
14236        width = this.imageObj.width;
14237        height = this.imageObj.height;
14238      }
14239
14240      this.width = width;
14241      this.height = height;
14242      this.radius = 0.5 * this.width;
14243    }
14244
14245    /**
14246     *
14247     * @param {CanvasRenderingContext2D} ctx
14248     * @param {number} x width
14249     * @param {number} y height
14250     * @param {{toArrow: boolean, toArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), toArrowType: *, middleArrow: boolean, middleArrowScale: (number|allOptions.edges.arrows.middle.scaleFactor|{number}|Array), middleArrowType: (allOptions.edges.arrows.middle.type|{string}|string|*), fromArrow: boolean, fromArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), fromArrowType: *, arrowStrikethrough: (*|boolean|allOptions.edges.arrowStrikethrough|{boolean}), color: undefined, inheritsColor: (string|string|string|allOptions.edges.color.inherit|{string, boolean}|Array|*), opacity: *, hidden: *, length: *, shadow: *, shadowColor: *, shadowSize: *, shadowX: *, shadowY: *, dashes: (*|boolean|Array|allOptions.edges.dashes|{boolean, array}), width: *}} values
14251     * @private
14252     */
14253
14254  }, {
14255    key: '_drawRawCircle',
14256    value: function _drawRawCircle(ctx, x, y, values) {
14257      this.initContextForDraw(ctx, values);
14258      ctx.circle(x, y, values.size);
14259      this.performFill(ctx, values);
14260    }
14261
14262    /**
14263     *
14264     * @param {CanvasRenderingContext2D} ctx
14265     * @param {{toArrow: boolean, toArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), toArrowType: *, middleArrow: boolean, middleArrowScale: (number|allOptions.edges.arrows.middle.scaleFactor|{number}|Array), middleArrowType: (allOptions.edges.arrows.middle.type|{string}|string|*), fromArrow: boolean, fromArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), fromArrowType: *, arrowStrikethrough: (*|boolean|allOptions.edges.arrowStrikethrough|{boolean}), color: undefined, inheritsColor: (string|string|string|allOptions.edges.color.inherit|{string, boolean}|Array|*), opacity: *, hidden: *, length: *, shadow: *, shadowColor: *, shadowSize: *, shadowX: *, shadowY: *, dashes: (*|boolean|Array|allOptions.edges.dashes|{boolean, array}), width: *}} values
14266     * @private
14267     */
14268
14269  }, {
14270    key: '_drawImageAtPosition',
14271    value: function _drawImageAtPosition(ctx, values) {
14272      if (this.imageObj.width != 0) {
14273        // draw the image
14274        ctx.globalAlpha = 1.0;
14275
14276        // draw shadow if enabled
14277        this.enableShadow(ctx, values);
14278
14279        var factor = 1;
14280        if (this.options.shapeProperties.interpolation === true) {
14281          factor = this.imageObj.width / this.width / this.body.view.scale;
14282        }
14283
14284        this.imageObj.drawImageAtPosition(ctx, factor, this.left, this.top, this.width, this.height);
14285
14286        // disable shadows for other elements.
14287        this.disableShadow(ctx, values);
14288      }
14289    }
14290
14291    /**
14292     *
14293     * @param {CanvasRenderingContext2D} ctx
14294     * @param {number} x width
14295     * @param {number} y height
14296     * @param {boolean} selected
14297     * @param {boolean} hover
14298     * @private
14299     */
14300
14301  }, {
14302    key: '_drawImageLabel',
14303    value: function _drawImageLabel(ctx, x, y, selected, hover) {
14304      var yLabel;
14305      var offset = 0;
14306
14307      if (this.height !== undefined) {
14308        offset = this.height * 0.5;
14309        var labelDimensions = this.labelModule.getTextSize(ctx, selected, hover);
14310        if (labelDimensions.lineCount >= 1) {
14311          offset += labelDimensions.height / 2;
14312        }
14313      }
14314
14315      yLabel = y + offset;
14316
14317      if (this.options.label) {
14318        this.labelOffset = offset;
14319      }
14320      this.labelModule.draw(ctx, x, yLabel, selected, hover, 'hanging');
14321    }
14322  }]);
14323  return CircleImageBase;
14324}(_NodeBase3['default']);
14325
14326exports['default'] = CircleImageBase;
14327
14328/***/ }),
14329/* 74 */
14330/***/ (function(module, exports, __webpack_require__) {
14331
14332"use strict";
14333
14334
14335Object.defineProperty(exports, "__esModule", {
14336  value: true
14337});
14338
14339var _stringify = __webpack_require__(19);
14340
14341var _stringify2 = _interopRequireDefault(_stringify);
14342
14343var _typeof2 = __webpack_require__(6);
14344
14345var _typeof3 = _interopRequireDefault(_typeof2);
14346
14347var _create = __webpack_require__(29);
14348
14349var _create2 = _interopRequireDefault(_create);
14350
14351var _classCallCheck2 = __webpack_require__(0);
14352
14353var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
14354
14355var _createClass2 = __webpack_require__(1);
14356
14357var _createClass3 = _interopRequireDefault(_createClass2);
14358
14359function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
14360
14361var util = __webpack_require__(2);
14362var Label = __webpack_require__(117)['default'];
14363var ComponentUtil = __webpack_require__(48)['default'];
14364var CubicBezierEdge = __webpack_require__(215)['default'];
14365var BezierEdgeDynamic = __webpack_require__(217)['default'];
14366var BezierEdgeStatic = __webpack_require__(218)['default'];
14367var StraightEdge = __webpack_require__(219)['default'];
14368
14369/**
14370 * An edge connects two nodes and has a specific direction.
14371 */
14372
14373var Edge = function () {
14374  /**
14375   * @param {Object} options        values specific to this edge, must contain at least 'from' and 'to'
14376   * @param {Object} body           shared state from Network instance
14377   * @param {Object} globalOptions  options from the EdgesHandler instance
14378   * @param {Object} defaultOptions default options from the EdgeHandler instance. Value and reference are constant
14379   */
14380  function Edge(options, body, globalOptions, defaultOptions) {
14381    (0, _classCallCheck3['default'])(this, Edge);
14382
14383    if (body === undefined) {
14384      throw new Error("No body provided");
14385    }
14386
14387    // Since globalOptions is constant in values as well as reference,
14388    // Following needs to be done only once.
14389
14390    this.options = util.bridgeObject(globalOptions);
14391    this.globalOptions = globalOptions;
14392    this.defaultOptions = defaultOptions;
14393    this.body = body;
14394
14395    // initialize variables
14396    this.id = undefined;
14397    this.fromId = undefined;
14398    this.toId = undefined;
14399    this.selected = false;
14400    this.hover = false;
14401    this.labelDirty = true;
14402
14403    this.baseWidth = this.options.width;
14404    this.baseFontSize = this.options.font.size;
14405
14406    this.from = undefined; // a node
14407    this.to = undefined; // a node
14408
14409    this.edgeType = undefined;
14410
14411    this.connected = false;
14412
14413    this.labelModule = new Label(this.body, this.options, true /* It's an edge label */);
14414    this.setOptions(options);
14415  }
14416
14417  /**
14418   * Set or overwrite options for the edge
14419   * @param {Object} options  an object with options
14420   * @returns {null|boolean} null if no options, boolean if date changed
14421   */
14422
14423
14424  (0, _createClass3['default'])(Edge, [{
14425    key: 'setOptions',
14426    value: function setOptions(options) {
14427      if (!options) {
14428        return;
14429      }
14430
14431      Edge.parseOptions(this.options, options, true, this.globalOptions);
14432
14433      if (options.id !== undefined) {
14434        this.id = options.id;
14435      }
14436      if (options.from !== undefined) {
14437        this.fromId = options.from;
14438      }
14439      if (options.to !== undefined) {
14440        this.toId = options.to;
14441      }
14442      if (options.title !== undefined) {
14443        this.title = options.title;
14444      }
14445      if (options.value !== undefined) {
14446        options.value = parseFloat(options.value);
14447      }
14448
14449      var pile = [options, this.options, this.defaultOptions];
14450      this.chooser = ComponentUtil.choosify('edge', pile);
14451
14452      // update label Module
14453      this.updateLabelModule(options);
14454
14455      var dataChanged = this.updateEdgeType();
14456
14457      // if anything has been updates, reset the selection width and the hover width
14458      this._setInteractionWidths();
14459
14460      // A node is connected when it has a from and to node that both exist in the network.body.nodes.
14461      this.connect();
14462
14463      if (options.hidden !== undefined || options.physics !== undefined) {
14464        dataChanged = true;
14465      }
14466
14467      return dataChanged;
14468    }
14469
14470    /**
14471     *
14472     * @param {Object} parentOptions
14473     * @param {Object} newOptions
14474     * @param {boolean} [allowDeletion=false]
14475     * @param {Object} [globalOptions={}]
14476     * @param {boolean} [copyFromGlobals=false]
14477     */
14478
14479  }, {
14480    key: 'getFormattingValues',
14481
14482
14483    /**
14484     *
14485     * @returns {{toArrow: boolean, toArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), toArrowType: *, middleArrow: boolean, middleArrowScale: (number|allOptions.edges.arrows.middle.scaleFactor|{number}|Array), middleArrowType: (allOptions.edges.arrows.middle.type|{string}|string|*), fromArrow: boolean, fromArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), fromArrowType: *, arrowStrikethrough: (*|boolean|allOptions.edges.arrowStrikethrough|{boolean}), color: undefined, inheritsColor: (string|string|string|allOptions.edges.color.inherit|{string, boolean}|Array|*), opacity: *, hidden: *, length: *, shadow: *, shadowColor: *, shadowSize: *, shadowX: *, shadowY: *, dashes: (*|boolean|Array|allOptions.edges.dashes|{boolean, array}), width: *}}
14486     */
14487    value: function getFormattingValues() {
14488      var toArrow = this.options.arrows.to === true || this.options.arrows.to.enabled === true;
14489      var fromArrow = this.options.arrows.from === true || this.options.arrows.from.enabled === true;
14490      var middleArrow = this.options.arrows.middle === true || this.options.arrows.middle.enabled === true;
14491      var inheritsColor = this.options.color.inherit;
14492      var values = {
14493        toArrow: toArrow,
14494        toArrowScale: this.options.arrows.to.scaleFactor,
14495        toArrowType: this.options.arrows.to.type,
14496        middleArrow: middleArrow,
14497        middleArrowScale: this.options.arrows.middle.scaleFactor,
14498        middleArrowType: this.options.arrows.middle.type,
14499        fromArrow: fromArrow,
14500        fromArrowScale: this.options.arrows.from.scaleFactor,
14501        fromArrowType: this.options.arrows.from.type,
14502        arrowStrikethrough: this.options.arrowStrikethrough,
14503        color: inheritsColor ? undefined : this.options.color.color,
14504        inheritsColor: inheritsColor,
14505        opacity: this.options.color.opacity,
14506        hidden: this.options.hidden,
14507        length: this.options.length,
14508        shadow: this.options.shadow.enabled,
14509        shadowColor: this.options.shadow.color,
14510        shadowSize: this.options.shadow.size,
14511        shadowX: this.options.shadow.x,
14512        shadowY: this.options.shadow.y,
14513        dashes: this.options.dashes,
14514        width: this.options.width
14515      };
14516      if (this.selected || this.hover) {
14517        if (this.chooser === true) {
14518          if (this.selected) {
14519            var selectedWidth = this.options.selectionWidth;
14520            if (typeof selectedWidth === 'function') {
14521              values.width = selectedWidth(values.width);
14522            } else if (typeof selectedWidth === 'number') {
14523              values.width += selectedWidth;
14524            }
14525            values.width = Math.max(values.width, 0.3 / this.body.view.scale);
14526            values.color = this.options.color.highlight;
14527            values.shadow = this.options.shadow.enabled;
14528          } else if (this.hover) {
14529            var hoverWidth = this.options.hoverWidth;
14530            if (typeof hoverWidth === 'function') {
14531              values.width = hoverWidth(values.width);
14532            } else if (typeof hoverWidth === 'number') {
14533              values.width += hoverWidth;
14534            }
14535            values.width = Math.max(values.width, 0.3 / this.body.view.scale);
14536            values.color = this.options.color.hover;
14537            values.shadow = this.options.shadow.enabled;
14538          }
14539        } else if (typeof this.chooser === 'function') {
14540          this.chooser(values, this.options.id, this.selected, this.hover);
14541          if (values.color !== undefined) {
14542            values.inheritsColor = false;
14543          }
14544          if (values.shadow === false) {
14545            if (values.shadowColor !== this.options.shadow.color || values.shadowSize !== this.options.shadow.size || values.shadowX !== this.options.shadow.x || values.shadowY !== this.options.shadow.y) {
14546              values.shadow = true;
14547            }
14548          }
14549        }
14550      } else {
14551        values.shadow = this.options.shadow.enabled;
14552        values.width = Math.max(values.width, 0.3 / this.body.view.scale);
14553      }
14554      return values;
14555    }
14556
14557    /**
14558     * update the options in the label module
14559     *
14560     * @param {Object} options
14561     */
14562
14563  }, {
14564    key: 'updateLabelModule',
14565    value: function updateLabelModule(options) {
14566      var pile = [options, this.options, this.globalOptions, // Currently set global edge options
14567      this.defaultOptions];
14568
14569      this.labelModule.update(this.options, pile);
14570
14571      if (this.labelModule.baseSize !== undefined) {
14572        this.baseFontSize = this.labelModule.baseSize;
14573      }
14574    }
14575
14576    /**
14577     * update the edge type, set the options
14578     * @returns {boolean}
14579     */
14580
14581  }, {
14582    key: 'updateEdgeType',
14583    value: function updateEdgeType() {
14584      var smooth = this.options.smooth;
14585      var dataChanged = false;
14586      var changeInType = true;
14587      if (this.edgeType !== undefined) {
14588        if (this.edgeType instanceof BezierEdgeDynamic && smooth.enabled === true && smooth.type === 'dynamic' || this.edgeType instanceof CubicBezierEdge && smooth.enabled === true && smooth.type === 'cubicBezier' || this.edgeType instanceof BezierEdgeStatic && smooth.enabled === true && smooth.type !== 'dynamic' && smooth.type !== 'cubicBezier' || this.edgeType instanceof StraightEdge && smooth.type.enabled === false) {
14589          changeInType = false;
14590        }
14591        if (changeInType === true) {
14592          dataChanged = this.cleanup();
14593        }
14594      }
14595      if (changeInType === true) {
14596        if (smooth.enabled === true) {
14597          if (smooth.type === 'dynamic') {
14598            dataChanged = true;
14599            this.edgeType = new BezierEdgeDynamic(this.options, this.body, this.labelModule);
14600          } else if (smooth.type === 'cubicBezier') {
14601            this.edgeType = new CubicBezierEdge(this.options, this.body, this.labelModule);
14602          } else {
14603            this.edgeType = new BezierEdgeStatic(this.options, this.body, this.labelModule);
14604          }
14605        } else {
14606          this.edgeType = new StraightEdge(this.options, this.body, this.labelModule);
14607        }
14608      } else {
14609        // if nothing changes, we just set the options.
14610        this.edgeType.setOptions(this.options);
14611      }
14612      return dataChanged;
14613    }
14614
14615    /**
14616     * Connect an edge to its nodes
14617     */
14618
14619  }, {
14620    key: 'connect',
14621    value: function connect() {
14622      this.disconnect();
14623
14624      this.from = this.body.nodes[this.fromId] || undefined;
14625      this.to = this.body.nodes[this.toId] || undefined;
14626      this.connected = this.from !== undefined && this.to !== undefined;
14627
14628      if (this.connected === true) {
14629        this.from.attachEdge(this);
14630        this.to.attachEdge(this);
14631      } else {
14632        if (this.from) {
14633          this.from.detachEdge(this);
14634        }
14635        if (this.to) {
14636          this.to.detachEdge(this);
14637        }
14638      }
14639
14640      this.edgeType.connect();
14641    }
14642
14643    /**
14644     * Disconnect an edge from its nodes
14645     */
14646
14647  }, {
14648    key: 'disconnect',
14649    value: function disconnect() {
14650      if (this.from) {
14651        this.from.detachEdge(this);
14652        this.from = undefined;
14653      }
14654      if (this.to) {
14655        this.to.detachEdge(this);
14656        this.to = undefined;
14657      }
14658
14659      this.connected = false;
14660    }
14661
14662    /**
14663     * get the title of this edge.
14664     * @return {string} title    The title of the edge, or undefined when no title
14665     *                           has been set.
14666     */
14667
14668  }, {
14669    key: 'getTitle',
14670    value: function getTitle() {
14671      return this.title;
14672    }
14673
14674    /**
14675     * check if this node is selecte
14676     * @return {boolean} selected   True if node is selected, else false
14677     */
14678
14679  }, {
14680    key: 'isSelected',
14681    value: function isSelected() {
14682      return this.selected;
14683    }
14684
14685    /**
14686     * Retrieve the value of the edge. Can be undefined
14687     * @return {number} value
14688     */
14689
14690  }, {
14691    key: 'getValue',
14692    value: function getValue() {
14693      return this.options.value;
14694    }
14695
14696    /**
14697     * Adjust the value range of the edge. The edge will adjust it's width
14698     * based on its value.
14699     * @param {number} min
14700     * @param {number} max
14701     * @param {number} total
14702     */
14703
14704  }, {
14705    key: 'setValueRange',
14706    value: function setValueRange(min, max, total) {
14707      if (this.options.value !== undefined) {
14708        var scale = this.options.scaling.customScalingFunction(min, max, total, this.options.value);
14709        var widthDiff = this.options.scaling.max - this.options.scaling.min;
14710        if (this.options.scaling.label.enabled === true) {
14711          var fontDiff = this.options.scaling.label.max - this.options.scaling.label.min;
14712          this.options.font.size = this.options.scaling.label.min + scale * fontDiff;
14713        }
14714        this.options.width = this.options.scaling.min + scale * widthDiff;
14715      } else {
14716        this.options.width = this.baseWidth;
14717        this.options.font.size = this.baseFontSize;
14718      }
14719
14720      this._setInteractionWidths();
14721      this.updateLabelModule();
14722    }
14723
14724    /**
14725     *
14726     * @private
14727     */
14728
14729  }, {
14730    key: '_setInteractionWidths',
14731    value: function _setInteractionWidths() {
14732      if (typeof this.options.hoverWidth === 'function') {
14733        this.edgeType.hoverWidth = this.options.hoverWidth(this.options.width);
14734      } else {
14735        this.edgeType.hoverWidth = this.options.hoverWidth + this.options.width;
14736      }
14737      if (typeof this.options.selectionWidth === 'function') {
14738        this.edgeType.selectionWidth = this.options.selectionWidth(this.options.width);
14739      } else {
14740        this.edgeType.selectionWidth = this.options.selectionWidth + this.options.width;
14741      }
14742    }
14743
14744    /**
14745     * Redraw a edge
14746     * Draw this edge in the given canvas
14747     * The 2d context of a HTML canvas can be retrieved by canvas.getContext("2d");
14748     * @param {CanvasRenderingContext2D}   ctx
14749     */
14750
14751  }, {
14752    key: 'draw',
14753    value: function draw(ctx) {
14754      var values = this.getFormattingValues();
14755      if (values.hidden) {
14756        return;
14757      }
14758
14759      // get the via node from the edge type
14760      var viaNode = this.edgeType.getViaNode();
14761      var arrowData = {};
14762
14763      // restore edge targets to defaults
14764      this.edgeType.fromPoint = this.edgeType.from;
14765      this.edgeType.toPoint = this.edgeType.to;
14766
14767      // from and to arrows give a different end point for edges. we set them here
14768      if (values.fromArrow) {
14769        arrowData.from = this.edgeType.getArrowData(ctx, 'from', viaNode, this.selected, this.hover, values);
14770        if (values.arrowStrikethrough === false) this.edgeType.fromPoint = arrowData.from.core;
14771      }
14772      if (values.toArrow) {
14773        arrowData.to = this.edgeType.getArrowData(ctx, 'to', viaNode, this.selected, this.hover, values);
14774        if (values.arrowStrikethrough === false) this.edgeType.toPoint = arrowData.to.core;
14775      }
14776
14777      // the middle arrow depends on the line, which can depend on the to and from arrows so we do this one lastly.
14778      if (values.middleArrow) {
14779        arrowData.middle = this.edgeType.getArrowData(ctx, 'middle', viaNode, this.selected, this.hover, values);
14780      }
14781
14782      // draw everything
14783      this.edgeType.drawLine(ctx, values, this.selected, this.hover, viaNode);
14784      this.drawArrows(ctx, arrowData, values);
14785      this.drawLabel(ctx, viaNode);
14786    }
14787
14788    /**
14789     *
14790     * @param {CanvasRenderingContext2D} ctx
14791     * @param {Object} arrowData
14792     * @param {{toArrow: boolean, toArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), toArrowType: *, middleArrow: boolean, middleArrowScale: (number|allOptions.edges.arrows.middle.scaleFactor|{number}|Array), middleArrowType: (allOptions.edges.arrows.middle.type|{string}|string|*), fromArrow: boolean, fromArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), fromArrowType: *, arrowStrikethrough: (*|boolean|allOptions.edges.arrowStrikethrough|{boolean}), color: undefined, inheritsColor: (string|string|string|allOptions.edges.color.inherit|{string, boolean}|Array|*), opacity: *, hidden: *, length: *, shadow: *, shadowColor: *, shadowSize: *, shadowX: *, shadowY: *, dashes: (*|boolean|Array|allOptions.edges.dashes|{boolean, array}), width: *}} values
14793     */
14794
14795  }, {
14796    key: 'drawArrows',
14797    value: function drawArrows(ctx, arrowData, values) {
14798      if (values.fromArrow) {
14799        this.edgeType.drawArrowHead(ctx, values, this.selected, this.hover, arrowData.from);
14800      }
14801      if (values.middleArrow) {
14802        this.edgeType.drawArrowHead(ctx, values, this.selected, this.hover, arrowData.middle);
14803      }
14804      if (values.toArrow) {
14805        this.edgeType.drawArrowHead(ctx, values, this.selected, this.hover, arrowData.to);
14806      }
14807    }
14808
14809    /**
14810     *
14811     * @param {CanvasRenderingContext2D} ctx
14812     * @param {Node} viaNode
14813     */
14814
14815  }, {
14816    key: 'drawLabel',
14817    value: function drawLabel(ctx, viaNode) {
14818      if (this.options.label !== undefined) {
14819        // set style
14820        var node1 = this.from;
14821        var node2 = this.to;
14822
14823        if (this.labelModule.differentState(this.selected, this.hover)) {
14824          this.labelModule.getTextSize(ctx, this.selected, this.hover);
14825        }
14826
14827        if (node1.id != node2.id) {
14828          this.labelModule.pointToSelf = false;
14829          var point = this.edgeType.getPoint(0.5, viaNode);
14830          ctx.save();
14831
14832          var rotationPoint = this._getRotation(ctx);
14833          if (rotationPoint.angle != 0) {
14834            ctx.translate(rotationPoint.x, rotationPoint.y);
14835            ctx.rotate(rotationPoint.angle);
14836          }
14837
14838          // draw the label
14839          this.labelModule.draw(ctx, point.x, point.y, this.selected, this.hover);
14840
14841          /*
14842                  // Useful debug code: draw a border around the label
14843                  // This should **not** be enabled in production!
14844                  var size = this.labelModule.getSize();; // ;; intentional so lint catches it
14845                  ctx.strokeStyle = "#ff0000";
14846                  ctx.strokeRect(size.left, size.top, size.width, size.height);
14847                  // End  debug code
14848          */
14849
14850          ctx.restore();
14851        } else {
14852          // Ignore the orientations.
14853          this.labelModule.pointToSelf = true;
14854          var x, y;
14855          var radius = this.options.selfReferenceSize;
14856          if (node1.shape.width > node1.shape.height) {
14857            x = node1.x + node1.shape.width * 0.5;
14858            y = node1.y - radius;
14859          } else {
14860            x = node1.x + radius;
14861            y = node1.y - node1.shape.height * 0.5;
14862          }
14863          point = this._pointOnCircle(x, y, radius, 0.125);
14864          this.labelModule.draw(ctx, point.x, point.y, this.selected, this.hover);
14865        }
14866      }
14867    }
14868
14869    /**
14870     * Determine all visual elements of this edge instance, in which the given
14871     * point falls within the bounding shape.
14872     *
14873     * @param {point} point
14874     * @returns {Array.<edgeClickItem|edgeLabelClickItem>} list with the items which are on the point
14875     */
14876
14877  }, {
14878    key: 'getItemsOnPoint',
14879    value: function getItemsOnPoint(point) {
14880      var ret = [];
14881
14882      if (this.labelModule.visible()) {
14883        var rotationPoint = this._getRotation();
14884        if (ComponentUtil.pointInRect(this.labelModule.getSize(), point, rotationPoint)) {
14885          ret.push({ edgeId: this.id, labelId: 0 });
14886        }
14887      }
14888
14889      var obj = {
14890        left: point.x,
14891        top: point.y
14892      };
14893
14894      if (this.isOverlappingWith(obj)) {
14895        ret.push({ edgeId: this.id });
14896      }
14897
14898      return ret;
14899    }
14900
14901    /**
14902     * Check if this object is overlapping with the provided object
14903     * @param {Object} obj   an object with parameters left, top
14904     * @return {boolean}     True if location is located on the edge
14905     */
14906
14907  }, {
14908    key: 'isOverlappingWith',
14909    value: function isOverlappingWith(obj) {
14910      if (this.connected) {
14911        var distMax = 10;
14912        var xFrom = this.from.x;
14913        var yFrom = this.from.y;
14914        var xTo = this.to.x;
14915        var yTo = this.to.y;
14916        var xObj = obj.left;
14917        var yObj = obj.top;
14918
14919        var dist = this.edgeType.getDistanceToEdge(xFrom, yFrom, xTo, yTo, xObj, yObj);
14920
14921        return dist < distMax;
14922      } else {
14923        return false;
14924      }
14925    }
14926
14927    /** 
14928     * Determine the rotation point, if any.
14929     *
14930     * @param {CanvasRenderingContext2D} [ctx] if passed, do a recalculation of the label size
14931     * @returns {rotationPoint} the point to rotate around and the angle in radians to rotate
14932     * @private
14933     */
14934
14935  }, {
14936    key: '_getRotation',
14937    value: function _getRotation(ctx) {
14938      var viaNode = this.edgeType.getViaNode();
14939      var point = this.edgeType.getPoint(0.5, viaNode);
14940
14941      if (ctx !== undefined) {
14942        this.labelModule.calculateLabelSize(ctx, this.selected, this.hover, point.x, point.y);
14943      }
14944
14945      var ret = {
14946        x: point.x,
14947        y: this.labelModule.size.yLine,
14948        angle: 0
14949      };
14950
14951      if (!this.labelModule.visible()) {
14952        return ret; // Don't even bother doing the atan2, there's nothing to draw
14953      }
14954
14955      if (this.options.font.align === "horizontal") {
14956        return ret; // No need to calculate angle
14957      }
14958
14959      var dy = this.from.y - this.to.y;
14960      var dx = this.from.x - this.to.x;
14961      var angle = Math.atan2(dy, dx); // radians
14962
14963      // rotate so that label is readable
14964      if (angle < -1 && dx < 0 || angle > 0 && dx < 0) {
14965        angle += Math.PI;
14966      }
14967      ret.angle = angle;
14968
14969      return ret;
14970    }
14971
14972    /**
14973     * Get a point on a circle
14974     * @param {number} x
14975     * @param {number} y
14976     * @param {number} radius
14977     * @param {number} percentage Value between 0 (line start) and 1 (line end)
14978     * @return {Object} point
14979     * @private
14980     */
14981
14982  }, {
14983    key: '_pointOnCircle',
14984    value: function _pointOnCircle(x, y, radius, percentage) {
14985      var angle = percentage * 2 * Math.PI;
14986      return {
14987        x: x + radius * Math.cos(angle),
14988        y: y - radius * Math.sin(angle)
14989      };
14990    }
14991
14992    /**
14993     * Sets selected state to true
14994     */
14995
14996  }, {
14997    key: 'select',
14998    value: function select() {
14999      this.selected = true;
15000    }
15001
15002    /**
15003     * Sets selected state to false
15004     */
15005
15006  }, {
15007    key: 'unselect',
15008    value: function unselect() {
15009      this.selected = false;
15010    }
15011
15012    /**
15013     * cleans all required things on delete
15014     * @returns {*}
15015     */
15016
15017  }, {
15018    key: 'cleanup',
15019    value: function cleanup() {
15020      return this.edgeType.cleanup();
15021    }
15022
15023    /**
15024     * Remove edge from the list and perform necessary cleanup.
15025     */
15026
15027  }, {
15028    key: 'remove',
15029    value: function remove() {
15030      this.cleanup();
15031      this.disconnect();
15032      delete this.body.edges[this.id];
15033    }
15034
15035    /**
15036     * Check if both connecting nodes exist
15037     * @returns {boolean}
15038     */
15039
15040  }, {
15041    key: 'endPointsValid',
15042    value: function endPointsValid() {
15043      return this.body.nodes[this.fromId] !== undefined && this.body.nodes[this.toId] !== undefined;
15044    }
15045  }], [{
15046    key: 'parseOptions',
15047    value: function parseOptions(parentOptions, newOptions) {
15048      var allowDeletion = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
15049      var globalOptions = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {};
15050      var copyFromGlobals = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : false;
15051
15052      var fields = ['arrowStrikethrough', 'id', 'from', 'hidden', 'hoverWidth', 'labelHighlightBold', 'length', 'line', 'opacity', 'physics', 'scaling', 'selectionWidth', 'selfReferenceSize', 'to', 'title', 'value', 'width', 'font', 'chosen', 'widthConstraint'];
15053
15054      // only deep extend the items in the field array. These do not have shorthand.
15055      util.selectiveDeepExtend(fields, parentOptions, newOptions, allowDeletion);
15056
15057      // Only copy label if it's a legal value.
15058      if (ComponentUtil.isValidLabel(newOptions.label)) {
15059        parentOptions.label = newOptions.label;
15060      } else {
15061        parentOptions.label = undefined;
15062      }
15063
15064      util.mergeOptions(parentOptions, newOptions, 'smooth', globalOptions);
15065      util.mergeOptions(parentOptions, newOptions, 'shadow', globalOptions);
15066
15067      if (newOptions.dashes !== undefined && newOptions.dashes !== null) {
15068        parentOptions.dashes = newOptions.dashes;
15069      } else if (allowDeletion === true && newOptions.dashes === null) {
15070        parentOptions.dashes = (0, _create2['default'])(globalOptions.dashes); // this sets the pointer of the option back to the global option.
15071      }
15072
15073      // set the scaling newOptions
15074      if (newOptions.scaling !== undefined && newOptions.scaling !== null) {
15075        if (newOptions.scaling.min !== undefined) {
15076          parentOptions.scaling.min = newOptions.scaling.min;
15077        }
15078        if (newOptions.scaling.max !== undefined) {
15079          parentOptions.scaling.max = newOptions.scaling.max;
15080        }
15081        util.mergeOptions(parentOptions.scaling, newOptions.scaling, 'label', globalOptions.scaling);
15082      } else if (allowDeletion === true && newOptions.scaling === null) {
15083        parentOptions.scaling = (0, _create2['default'])(globalOptions.scaling); // this sets the pointer of the option back to the global option.
15084      }
15085
15086      // handle multiple input cases for arrows
15087      if (newOptions.arrows !== undefined && newOptions.arrows !== null) {
15088        if (typeof newOptions.arrows === 'string') {
15089          var arrows = newOptions.arrows.toLowerCase();
15090          parentOptions.arrows.to.enabled = arrows.indexOf("to") != -1;
15091          parentOptions.arrows.middle.enabled = arrows.indexOf("middle") != -1;
15092          parentOptions.arrows.from.enabled = arrows.indexOf("from") != -1;
15093        } else if ((0, _typeof3['default'])(newOptions.arrows) === 'object') {
15094          util.mergeOptions(parentOptions.arrows, newOptions.arrows, 'to', globalOptions.arrows);
15095          util.mergeOptions(parentOptions.arrows, newOptions.arrows, 'middle', globalOptions.arrows);
15096          util.mergeOptions(parentOptions.arrows, newOptions.arrows, 'from', globalOptions.arrows);
15097        } else {
15098          throw new Error("The arrow newOptions can only be an object or a string. Refer to the documentation. You used:" + (0, _stringify2['default'])(newOptions.arrows));
15099        }
15100      } else if (allowDeletion === true && newOptions.arrows === null) {
15101        parentOptions.arrows = (0, _create2['default'])(globalOptions.arrows); // this sets the pointer of the option back to the global option.
15102      }
15103
15104      // handle multiple input cases for color
15105      if (newOptions.color !== undefined && newOptions.color !== null) {
15106        var fromColor = newOptions.color;
15107        var toColor = parentOptions.color;
15108
15109        // If passed, fill in values from default options - required in the case of no prototype bridging
15110        if (copyFromGlobals) {
15111          util.deepExtend(toColor, globalOptions.color, false, allowDeletion);
15112        } else {
15113          // Clear local properties - need to do it like this in order to retain prototype bridges
15114          for (var i in toColor) {
15115            if (toColor.hasOwnProperty(i)) {
15116              delete toColor[i];
15117            }
15118          }
15119        }
15120
15121        if (util.isString(toColor)) {
15122          toColor.color = toColor;
15123          toColor.highlight = toColor;
15124          toColor.hover = toColor;
15125          toColor.inherit = false;
15126          if (fromColor.opacity === undefined) {
15127            toColor.opacity = 1.0; // set default
15128          }
15129        } else {
15130          var colorsDefined = false;
15131          if (fromColor.color !== undefined) {
15132            toColor.color = fromColor.color;colorsDefined = true;
15133          }
15134          if (fromColor.highlight !== undefined) {
15135            toColor.highlight = fromColor.highlight;colorsDefined = true;
15136          }
15137          if (fromColor.hover !== undefined) {
15138            toColor.hover = fromColor.hover;colorsDefined = true;
15139          }
15140          if (fromColor.inherit !== undefined) {
15141            toColor.inherit = fromColor.inherit;
15142          }
15143          if (fromColor.opacity !== undefined) {
15144            toColor.opacity = Math.min(1, Math.max(0, fromColor.opacity));
15145          }
15146
15147          if (colorsDefined === true) {
15148            toColor.inherit = false;
15149          } else {
15150            if (toColor.inherit === undefined) {
15151              toColor.inherit = 'from'; // Set default
15152            }
15153          }
15154        }
15155      } else if (allowDeletion === true && newOptions.color === null) {
15156        parentOptions.color = util.bridgeObject(globalOptions.color); // set the object back to the global options
15157      }
15158
15159      if (allowDeletion === true && newOptions.font === null) {
15160        parentOptions.font = util.bridgeObject(globalOptions.font); // set the object back to the global options
15161      }
15162    }
15163  }]);
15164  return Edge;
15165}();
15166
15167exports['default'] = Edge;
15168
15169/***/ }),
15170/* 75 */
15171/***/ (function(module, exports, __webpack_require__) {
15172
15173"use strict";
15174
15175
15176Object.defineProperty(exports, "__esModule", {
15177  value: true
15178});
15179
15180var _getPrototypeOf = __webpack_require__(3);
15181
15182var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf);
15183
15184var _classCallCheck2 = __webpack_require__(0);
15185
15186var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
15187
15188var _createClass2 = __webpack_require__(1);
15189
15190var _createClass3 = _interopRequireDefault(_createClass2);
15191
15192var _possibleConstructorReturn2 = __webpack_require__(4);
15193
15194var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2);
15195
15196var _inherits2 = __webpack_require__(5);
15197
15198var _inherits3 = _interopRequireDefault(_inherits2);
15199
15200var _EdgeBase2 = __webpack_require__(118);
15201
15202var _EdgeBase3 = _interopRequireDefault(_EdgeBase2);
15203
15204function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
15205
15206/**
15207 * The Base Class for all Bezier edges. Bezier curves are used to model smooth
15208 * gradual curves in paths between nodes.
15209 *
15210 * @extends EdgeBase
15211 */
15212var BezierEdgeBase = function (_EdgeBase) {
15213  (0, _inherits3['default'])(BezierEdgeBase, _EdgeBase);
15214
15215  /**
15216   * @param {Object} options
15217   * @param {Object} body
15218   * @param {Label} labelModule
15219   */
15220  function BezierEdgeBase(options, body, labelModule) {
15221    (0, _classCallCheck3['default'])(this, BezierEdgeBase);
15222    return (0, _possibleConstructorReturn3['default'])(this, (BezierEdgeBase.__proto__ || (0, _getPrototypeOf2['default'])(BezierEdgeBase)).call(this, options, body, labelModule));
15223  }
15224
15225  /**
15226   * This function uses binary search to look for the point where the bezier curve crosses the border of the node.
15227   *
15228   * @param {Node} nearNode
15229   * @param {CanvasRenderingContext2D} ctx
15230   * @param {Node} viaNode
15231   * @returns {*}
15232   * @private
15233   */
15234
15235
15236  (0, _createClass3['default'])(BezierEdgeBase, [{
15237    key: '_findBorderPositionBezier',
15238    value: function _findBorderPositionBezier(nearNode, ctx) {
15239      var viaNode = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : this._getViaCoordinates();
15240
15241      var maxIterations = 10;
15242      var iteration = 0;
15243      var low = 0;
15244      var high = 1;
15245      var pos, angle, distanceToBorder, distanceToPoint, difference;
15246      var threshold = 0.2;
15247      var node = this.to;
15248      var from = false;
15249      if (nearNode.id === this.from.id) {
15250        node = this.from;
15251        from = true;
15252      }
15253
15254      while (low <= high && iteration < maxIterations) {
15255        var middle = (low + high) * 0.5;
15256
15257        pos = this.getPoint(middle, viaNode);
15258        angle = Math.atan2(node.y - pos.y, node.x - pos.x);
15259        distanceToBorder = node.distanceToBorder(ctx, angle);
15260        distanceToPoint = Math.sqrt(Math.pow(pos.x - node.x, 2) + Math.pow(pos.y - node.y, 2));
15261        difference = distanceToBorder - distanceToPoint;
15262        if (Math.abs(difference) < threshold) {
15263          break; // found
15264        } else if (difference < 0) {
15265          // distance to nodes is larger than distance to border --> t needs to be bigger if we're looking at the to node.
15266          if (from === false) {
15267            low = middle;
15268          } else {
15269            high = middle;
15270          }
15271        } else {
15272          if (from === false) {
15273            high = middle;
15274          } else {
15275            low = middle;
15276          }
15277        }
15278
15279        iteration++;
15280      }
15281      pos.t = middle;
15282
15283      return pos;
15284    }
15285
15286    /**
15287     * Calculate the distance between a point (x3,y3) and a line segment from
15288     * (x1,y1) to (x2,y2).
15289     * http://stackoverflow.com/questions/849211/shortest-distancae-between-a-point-and-a-line-segment
15290     * @param {number} x1 from x
15291     * @param {number} y1 from y
15292     * @param {number} x2 to x
15293     * @param {number} y2 to y
15294     * @param {number} x3 point to check x
15295     * @param {number} y3 point to check y
15296     * @param {Node} via
15297     * @returns {number}
15298     * @private
15299     */
15300
15301  }, {
15302    key: '_getDistanceToBezierEdge',
15303    value: function _getDistanceToBezierEdge(x1, y1, x2, y2, x3, y3, via) {
15304      // x3,y3 is the point
15305      var minDistance = 1e9;
15306      var distance = void 0;
15307      var i = void 0,
15308          t = void 0,
15309          x = void 0,
15310          y = void 0;
15311      var lastX = x1;
15312      var lastY = y1;
15313      for (i = 1; i < 10; i++) {
15314        t = 0.1 * i;
15315        x = Math.pow(1 - t, 2) * x1 + 2 * t * (1 - t) * via.x + Math.pow(t, 2) * x2;
15316        y = Math.pow(1 - t, 2) * y1 + 2 * t * (1 - t) * via.y + Math.pow(t, 2) * y2;
15317        if (i > 0) {
15318          distance = this._getDistanceToLine(lastX, lastY, x, y, x3, y3);
15319          minDistance = distance < minDistance ? distance : minDistance;
15320        }
15321        lastX = x;
15322        lastY = y;
15323      }
15324
15325      return minDistance;
15326    }
15327
15328    /**
15329     * Draw a bezier curve between two nodes
15330     *
15331     * The method accepts zero, one or two control points.
15332     * Passing zero control points just draws a straight line
15333     *
15334     * @param {CanvasRenderingContext2D} ctx
15335     * @param {Object}           values   | options for shadow drawing
15336     * @param {Object|undefined} viaNode1 | first control point for curve drawing
15337     * @param {Object|undefined} viaNode2 | second control point for curve drawing
15338     *
15339     * @protected
15340     */
15341
15342  }, {
15343    key: '_bezierCurve',
15344    value: function _bezierCurve(ctx, values, viaNode1, viaNode2) {
15345      var hasNode1 = viaNode1 !== undefined && viaNode1.x !== undefined;
15346      var hasNode2 = viaNode2 !== undefined && viaNode2.x !== undefined;
15347
15348      ctx.beginPath();
15349      ctx.moveTo(this.fromPoint.x, this.fromPoint.y);
15350
15351      if (hasNode1 && hasNode2) {
15352        ctx.bezierCurveTo(viaNode1.x, viaNode1.y, viaNode2.x, viaNode2.y, this.toPoint.x, this.toPoint.y);
15353      } else if (hasNode1) {
15354        ctx.quadraticCurveTo(viaNode1.x, viaNode1.y, this.toPoint.x, this.toPoint.y);
15355      } else {
15356        // fallback to normal straight edge
15357        ctx.lineTo(this.toPoint.x, this.toPoint.y);
15358      }
15359
15360      // draw shadow if enabled
15361      this.enableShadow(ctx, values);
15362      ctx.stroke();
15363      this.disableShadow(ctx, values);
15364    }
15365
15366    /**
15367     *
15368     * @returns {*|{x, y}|{x: undefined, y: undefined}}
15369     */
15370
15371  }, {
15372    key: 'getViaNode',
15373    value: function getViaNode() {
15374      return this._getViaCoordinates();
15375    }
15376  }]);
15377  return BezierEdgeBase;
15378}(_EdgeBase3['default']);
15379
15380exports['default'] = BezierEdgeBase;
15381
15382/***/ }),
15383/* 76 */
15384/***/ (function(module, exports, __webpack_require__) {
15385
15386"use strict";
15387
15388
15389Object.defineProperty(exports, "__esModule", {
15390  value: true
15391});
15392
15393var _classCallCheck2 = __webpack_require__(0);
15394
15395var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
15396
15397var _createClass2 = __webpack_require__(1);
15398
15399var _createClass3 = _interopRequireDefault(_createClass2);
15400
15401function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
15402
15403var util = __webpack_require__(2);
15404
15405/**
15406 * Utility Class
15407 */
15408
15409var NetworkUtil = function () {
15410  /**
15411   * @ignore
15412   */
15413  function NetworkUtil() {
15414    (0, _classCallCheck3["default"])(this, NetworkUtil);
15415  }
15416
15417  /**
15418   * Find the center position of the network considering the bounding boxes
15419   *
15420   * @param {Array.<Node>} allNodes
15421   * @param {Array.<Node>} [specificNodes=[]]
15422   * @returns {{minX: number, maxX: number, minY: number, maxY: number}}
15423   * @static
15424   */
15425
15426
15427  (0, _createClass3["default"])(NetworkUtil, null, [{
15428    key: "getRange",
15429    value: function getRange(allNodes) {
15430      var specificNodes = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];
15431
15432      var minY = 1e9,
15433          maxY = -1e9,
15434          minX = 1e9,
15435          maxX = -1e9,
15436          node;
15437      if (specificNodes.length > 0) {
15438        for (var i = 0; i < specificNodes.length; i++) {
15439          node = allNodes[specificNodes[i]];
15440          if (minX > node.shape.boundingBox.left) {
15441            minX = node.shape.boundingBox.left;
15442          }
15443          if (maxX < node.shape.boundingBox.right) {
15444            maxX = node.shape.boundingBox.right;
15445          }
15446          if (minY > node.shape.boundingBox.top) {
15447            minY = node.shape.boundingBox.top;
15448          } // top is negative, bottom is positive
15449          if (maxY < node.shape.boundingBox.bottom) {
15450            maxY = node.shape.boundingBox.bottom;
15451          } // top is negative, bottom is positive
15452        }
15453      }
15454
15455      if (minX === 1e9 && maxX === -1e9 && minY === 1e9 && maxY === -1e9) {
15456        minY = 0, maxY = 0, minX = 0, maxX = 0;
15457      }
15458      return { minX: minX, maxX: maxX, minY: minY, maxY: maxY };
15459    }
15460
15461    /**
15462     * Find the center position of the network
15463     *
15464     * @param {Array.<Node>} allNodes
15465     * @param {Array.<Node>} [specificNodes=[]]
15466     * @returns {{minX: number, maxX: number, minY: number, maxY: number}}
15467     * @static
15468     */
15469
15470  }, {
15471    key: "getRangeCore",
15472    value: function getRangeCore(allNodes) {
15473      var specificNodes = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];
15474
15475      var minY = 1e9,
15476          maxY = -1e9,
15477          minX = 1e9,
15478          maxX = -1e9,
15479          node;
15480      if (specificNodes.length > 0) {
15481        for (var i = 0; i < specificNodes.length; i++) {
15482          node = allNodes[specificNodes[i]];
15483          if (minX > node.x) {
15484            minX = node.x;
15485          }
15486          if (maxX < node.x) {
15487            maxX = node.x;
15488          }
15489          if (minY > node.y) {
15490            minY = node.y;
15491          } // top is negative, bottom is positive
15492          if (maxY < node.y) {
15493            maxY = node.y;
15494          } // top is negative, bottom is positive
15495        }
15496      }
15497
15498      if (minX === 1e9 && maxX === -1e9 && minY === 1e9 && maxY === -1e9) {
15499        minY = 0, maxY = 0, minX = 0, maxX = 0;
15500      }
15501      return { minX: minX, maxX: maxX, minY: minY, maxY: maxY };
15502    }
15503
15504    /**
15505     * @param {object} range = {minX: minX, maxX: maxX, minY: minY, maxY: maxY};
15506     * @returns {{x: number, y: number}}
15507     * @static
15508     */
15509
15510  }, {
15511    key: "findCenter",
15512    value: function findCenter(range) {
15513      return { x: 0.5 * (range.maxX + range.minX),
15514        y: 0.5 * (range.maxY + range.minY) };
15515    }
15516
15517    /**
15518     * This returns a clone of the options or options of the edge or node to be used for c
vendor: 4,197 bytes, lines 15518-15643
15518onstruction of new edges or check functions for new nodes.
15519     * @param {vis.Item} item
15520     * @param {'node'|undefined} type
15521     * @returns {{}}
15522     * @static
15523     */
15524
15525  }, {
15526    key: "cloneOptions",
15527    value: function cloneOptions(item, type) {
15528      var clonedOptions = {};
15529      if (type === undefined || type === 'node') {
15530        util.deepExtend(clonedOptions, item.options, true);
15531        clonedOptions.x = item.x;
15532        clonedOptions.y = item.y;
15533        clonedOptions.amountOfConnections = item.edges.length;
15534      } else {
15535        util.deepExtend(clonedOptions, item.options, true);
15536      }
15537      return clonedOptions;
15538    }
15539  }]);
15540  return NetworkUtil;
15541}();
15542
15543exports["default"] = NetworkUtil;
15544
15545/***/ }),
15546/* 77 */
15547/***/ (function(module, exports, __webpack_require__) {
15548
15549module.exports = { "default": __webpack_require__(124), __esModule: true };
15550
15551/***/ }),
15552/* 78 */
15553/***/ (function(module, exports, __webpack_require__) {
15554
15555// fallback for non-array-like ES3 and non-enumerable old V8 strings
15556var cof = __webpack_require__(50);
15557// eslint-disable-next-line no-prototype-builtins
15558module.exports = Object('z').propertyIsEnumerable(0) ? Object : function (it) {
15559  return cof(it) == 'String' ? it.split('') : Object(it);
15560};
15561
15562
15563/***/ }),
15564/* 79 */
15565/***/ (function(module, exports, __webpack_require__) {
15566
15567"use strict";
15568
15569var LIBRARY = __webpack_require__(52);
15570var $export = __webpack_require__(17);
15571var redefine = __webpack_require__(83);
15572var hide = __webpack_require__(26);
15573var has = __webpack_require__(22);
15574var Iterators = __webpack_require__(31);
15575var $iterCreate = __webpack_require__(129);
15576var setToStringTag = __webpack_require__(59);
15577var getPrototypeOf = __webpack_require__(85);
15578var ITERATOR = __webpack_require__(13)('iterator');
15579var BUGGY = !([].keys && 'next' in [].keys()); // Safari has buggy iterators w/o `next`
15580var FF_ITERATOR = '@@iterator';
15581var KEYS = 'keys';
15582var VALUES = 'values';
15583
15584var returnThis = function () { return this; };
15585
15586module.exports = function (Base, NAME, Constructor, next, DEFAULT, IS_SET, FORCED) {
15587  $iterCreate(Constructor, NAME, next);
15588  var getMethod = function (kind) {
15589    if (!BUGGY && kind in proto) return proto[kind];
15590    switch (kind) {
15591      case KEYS: return function keys() { return new Constructor(this, kind); };
15592      case VALUES: return function values() { return new Constructor(this, kind); };
15593    } return function entries() { return new Constructor(this, kind); };
15594  };
15595  var TAG = NAME + ' Iterator';
15596  var DEF_VALUES = DEFAULT == VALUES;
15597  var VALUES_BUG = false;
15598  var proto = Base.prototype;
15599  var $native = proto[ITERATOR] || proto[FF_ITERATOR] || DEFAULT && proto[DEFAULT];
15600  var $default = $native || getMethod(DEFAULT);
15601  var $entries = DEFAULT ? !DEF_VALUES ? $default : getMethod('entries') : undefined;
15602  var $anyNative = NAME == 'Array' ? proto.entries || $native : $native;
15603  var methods, key, IteratorPrototype;
15604  // Fix native
15605  if ($anyNative) {
15606    IteratorPrototype = getPrototypeOf($anyNative.call(new Base()));
15607    if (IteratorPrototype !== Object.prototype && IteratorPrototype.next) {
15608      // Set @@toStringTag to native iterators
15609      setToStringTag(IteratorPrototype, TAG, true);
15610      // fix for some old engines
15611      if (!LIBRARY && !has(IteratorPrototype, ITERATOR)) hide(IteratorPrototype, ITERATOR, returnThis);
15612    }
15613  }
15614  // fix Array#{values, @@iterator}.name in V8 / FF
15615  if (DEF_VALUES && $native && $native.name !== VALUES) {
15616    VALUES_BUG = true;
15617    $default = function values() { return $native.call(this); };
15618  }
15619  // Define iterator
15620  if ((!LIBRARY || FORCED) && (BUGGY || VALUES_BUG || !proto[ITERATOR])) {
15621    hide(proto, ITERATOR, $default);
15622  }
15623  // Plug for library
15624  Iterators[NAME] = $default;
15625  Iterators[TAG] = returnThis;
15626  if (DEFAULT) {
15627    methods = {
15628      values: DEF_VALUES ? $default : getMethod(VALUES),
15629      keys: IS_SET ? $default : getMethod(KEYS),
15630      entries: $entries
15631    };
15632    if (FORCED) for (key in methods) {
15633      if (!(key in proto)) redefine(proto, key, methods[key]);
15634    } else $export($export.P + $export.F * (BUGGY || VALUES_BUG), NAME, methods);
15635  }
15636  return methods;
15637};
15638
15639
15640/***/ }),
15641/* 80 */
15642/***/ (function(module, exports, __webpack_require__) {
15643
15644// optional / simple context binding
15645var aFunction = __webpack_require__(128);
15646module.exports = function (fn, that, length) {
15647  aFunction(fn);
15648  if (that === undefined) return fn;
15649  switch (length) {
15650    case 1: return function (a) {
15651      return fn.call(that, a);
15652    };
15653    case 2: return function (a, b) {
15654      return fn.call(that, a, b);
15655    };
15656    case 3: return function (a, b, c) {
15657      return fn.call(that, a, b, c);
15658    };
15659  }
15660  return function (/* ...args */) {
15661    return fn.apply(that, arguments);
15662  };
15663};
15664
15665
15666/***/ }),
15667/* 81 */
15668/***/ (function(module, exports, __webpack_require__) {
15669
15670module.exports = !__webpack_require__(21) && !__webpack_require__(28)(function () {
15671  return Object.defineProperty(__webpack_require__(82)('div'), 'a', { get: function () { return 7; } }).a != 7;
15672});
15673
15674
15675/***/ }),
15676/* 82 */
15677/***/ (function(module, exports, __webpack_require__) {
15678
15679var isObject = __webpack_require__(32);
15680var document = __webpack_require__(18).document;
15681// typeof document.createElement is 'object' in old IE
15682var is = isObject(document) && isObject(document.createElement);
15683module.exports = function (it) {
15684  return is ? document.createElement(it) : {};
15685};
15686
15687
15688/***/ }),
15689/* 83 */
15690/***/ (function(module, exports, __webpack_require__) {
15691
15692module.exports = __webpack_require__(26);
15693
15694
15695/***/ }),
15696/* 84 */
15697/***/ (function(module, exports, __webpack_require__) {
15698
15699var has = __webpack_require__(22);
15700var toIObject = __webpack_require__(25);
15701var arrayIndexOf = __webpack_require__(131)(false);
15702var IE_PROTO = __webpack_require__(56)('IE_PROTO');
15703
15704module.exports = function (object, names) {
15705  var O = toIObject(object);
15706  var i = 0;
15707  var result = [];
15708  var key;
15709  for (key in O) if (key != IE_PROTO) has(O, key) && result.push(key);
15710  // Don't enum bug & hidden keys
15711  while (names.length > i) if (has(O, key = names[i++])) {
15712    ~arrayIndexOf(result, key) || result.push(key);
15713  }
15714  return result;
15715};
15716
15717
15718/***/ }),
15719/* 85 */
15720/***/ (function(module, exports, __webpack_require__) {
15721
15722// 19.1.2.9 / 15.2.3.2 Object.getPrototypeOf(O)
15723var has = __webpack_require__(22);
15724var toObject = __webpack_require__(41);
15725var IE_PROTO = __webpack_require__(56)('IE_PROTO');
15726var ObjectProto = Object.prototype;
15727
15728module.exports = Object.getPrototypeOf || function (O) {
15729  O = toObject(O);
15730  if (has(O, IE_PROTO)) return O[IE_PROTO];
15731  if (typeof O.constructor == 'function' && O instanceof O.constructor) {
15732    return O.constructor.prototype;
15733  } return O instanceof Object ? ObjectProto : null;
15734};
15735
15736
15737/***/ }),
15738/* 86 */
15739/***/ (function(module, exports, __webpack_require__) {
15740
15741// getting tag from 19.1.3.6 Object.prototype.toString()
15742var cof = __webpack_require__(50);
15743var TAG = __webpack_require__(13)('toStringTag');
15744// ES3 wrong here
15745var ARG = cof(function () { return arguments; }()) == 'Arguments';
15746
15747// fallback for IE11 Script Access Denied error
15748var tryGet = function (it, key) {
15749  try {
15750    return it[key];
15751  } catch (e) { /* empty */ }
15752};
15753
15754module.exports = function (it) {
15755  var O, T, B;
15756  return it === undefined ? 'Undefined' : it === null ? 'Null'
15757    // @@toStringTag case
15758    : typeof (T = tryGet(O = Object(it), TAG)) == 'string' ? T
15759    // builtinTag case
15760    : ARG ? cof(O)
15761    // ES3 arguments fallback
15762    : (B = cof(O)) == 'Object' && typeof O.callee == 'function' ? 'Arguments' : B;
15763};
15764
15765
15766/***/ }),
15767/* 87 */
15768/***/ (function(module, exports, __webpack_require__) {
15769
15770// most Object methods by ES6 should accept primitives
15771var $export = __webpack_require__(17);
15772var core = __webpack_require__(7);
15773var fails = __webpack_require__(28);
15774module.exports = function (KEY, exec) {
15775  var fn = (core.Object || {})[KEY] || Object[KEY];
15776  var exp = {};
15777  exp[KEY] = exec(fn);
15778  $export($export.S + $export.F * fails(function () { fn(1); }), 'Object', exp);
15779};
15780
15781
15782/***/ }),
15783/* 88 */
15784/***/ (function(module, exports, __webpack_require__) {
15785
15786// 19.1.2.7 / 15.2.3.4 Object.getOwnPropertyNames(O)
15787var $keys = __webpack_require__(84);
15788var hiddenKeys = __webpack_require__(58).concat('length', 'prototype');
15789
15790exports.f = Object.getOwnPropertyNames || function getOwnPropertyNames(O) {
15791  return $keys(O, hiddenKeys);
15792};
15793
15794
15795/***/ }),
15796/* 89 */
15797/***/ (function(module, exports, __webpack_require__) {
15798
15799var pIE = __webpack_require__(42);
15800var createDesc = __webpack_require__(39);
15801var toIObject = __webpack_require__(25);
15802var toPrimitive = __webpack_require__(53);
15803var has = __webpack_require__(22);
15804var IE8_DOM_DEFINE = __webpack_require__(81);
15805var gOPD = Object.getOwnPropertyDescriptor;
15806
15807exports.f = __webpack_require__(21) ? gOPD : function getOwnPropertyDescriptor(O, P) {
15808  O = toIObject(O);
15809  P = toPrimitive(P, true);
15810  if (IE8_DOM_DEFINE) try {
15811    return gOPD(O, P);
15812  } catch (e) { /* empty */ }
15813  if (has(O, P)) return createDesc(!pIE.f.call(O, P), O[P]);
15814};
15815
15816
15817/***/ }),
15818/* 90 */
15819/***/ (function(module, exports, __webpack_require__) {
15820
15821module.exports = { "default": __webpack_require__(162), __esModule: true };
15822
15823/***/ }),
15824/* 91 */
15825/***/ (function(module, exports, __webpack_require__) {
15826
15827"use strict";
15828
15829
15830/**
15831 * @prototype Point2d
15832 * @param {number} [x]
15833 * @param {number} [y]
15834 */
15835function Point2d(x, y) {
15836  this.x = x !== undefined ? x : 0;
15837  this.y = y !== undefined ? y : 0;
15838}
15839
15840module.exports = Point2d;
15841
15842/***/ }),
15843/* 92 */
15844/***/ (function(module, exports, __webpack_require__) {
15845
15846"use strict";
15847
15848
15849var util = __webpack_require__(2);
15850
15851/**
15852 * An html slider control with start/stop/prev/next buttons
15853 *
15854 * @constructor Slider
15855 * @param {Element} container  The element where the slider will be created
15856 * @param {Object} options   Available options:
15857 *                 {boolean} visible   If true (default) the
15858 *                           slider is visible.
15859 */
15860function Slider(container, options) {
15861  if (container === undefined) {
15862    throw new Error('No container element defined');
15863  }
15864  this.container = container;
15865  this.visible = options && options.visible != undefined ? options.visible : true;
15866
15867  if (this.visible) {
15868    this.frame = document.createElement('DIV');
15869    //this.frame.style.backgroundColor = '#E5E5E5';
15870    this.frame.style.width = '100%';
15871    this.frame.style.position = 'relative';
15872    this.container.appendChild(this.frame);
15873
15874    this.frame.prev = document.createElement('INPUT');
15875    this.frame.prev.type = 'BUTTON';
15876    this.frame.prev.value = 'Prev';
15877    this.frame.appendChild(this.frame.prev);
15878
15879    this.frame.play = document.createElement('INPUT');
15880    this.frame.play.type = 'BUTTON';
15881    this.frame.play.value = 'Play';
15882    this.frame.appendChild(this.frame.play);
15883
15884    this.frame.next = document.createElement('INPUT');
15885    this.frame.next.type = 'BUTTON';
15886    this.frame.next.value = 'Next';
15887    this.frame.appendChild(this.frame.next);
15888
15889    this.frame.bar = document.createElement('INPUT');
15890    this.frame.bar.type = 'BUTTON';
15891    this.frame.bar.style.position = 'absolute';
15892    this.frame.bar.style.border = '1px solid red';
15893    this.frame.bar.style.width = '100px';
15894    this.frame.bar.style.height = '6px';
15895    this.frame.bar.style.borderRadius = '2px';
15896    this.frame.bar.style.MozBorderRadius = '2px';
15897    this.frame.bar.style.border = '1px solid #7F7F7F';
15898    this.frame.bar.style.backgroundColor = '#E5E5E5';
15899    this.frame.appendChild(this.frame.bar);
15900
15901    this.frame.slide = document.createElement('INPUT');
15902    this.frame.slide.type = 'BUTTON';
15903    this.frame.slide.style.margin = '0px';
15904    this.frame.slide.value = ' ';
15905    this.frame.slide.style.position = 'relative';
15906    this.frame.slide.style.left = '-100px';
15907    this.frame.appendChild(this.frame.slide);
15908
15909    // create events
15910    var me = this;
15911    this.frame.slide.onmousedown = function (event) {
15912      me._onMouseDown(event);
15913    };
15914    this.frame.prev.onclick = function (event) {
15915      me.prev(event);
15916    };
15917    this.frame.play.onclick = function (event) {
15918      me.togglePlay(event);
15919    };
15920    this.frame.next.onclick = function (event) {
15921      me.next(event);
15922    };
15923  }
15924
15925  this.onChangeCallback = undefined;
15926
15927  this.values = [];
15928  this.index = undefined;
15929
15930  this.playTimeout = undefined;
15931  this.playInterval = 1000; // milliseconds
15932  this.playLoop = true;
15933}
15934
15935/**
15936 * Select the previous index
15937 */
15938Slider.prototype.prev = function () {
15939  var index = this.getIndex();
15940  if (index > 0) {
15941    index--;
15942    this.setIndex(index);
15943  }
15944};
15945
15946/**
15947 * Select the next index
15948 */
15949Slider.prototype.next = function () {
15950  var index = this.getIndex();
15951  if (index < this.values.length - 1) {
15952    index++;
15953    this.setIndex(index);
15954  }
15955};
15956
15957/**
15958 * Select the next index
15959 */
15960Slider.prototype.playNext = function () {
15961  var start = new Date();
15962
15963  var index = this.getIndex();
15964  if (index < this.values.length - 1) {
15965    index++;
15966    this.setIndex(index);
15967  } else if (this.playLoop) {
15968    // jump to the start
15969    index = 0;
15970    this.setIndex(index);
15971  }
15972
15973  var end = new Date();
15974  var diff = end - start;
15975
15976  // calculate how much time it to to set the index and to execute the callback
15977  // function.
15978  var interval = Math.max(this.playInterval - diff, 0);
15979  // document.title = diff // TODO: cleanup
15980
15981  var me = this;
15982  this.playTimeout = setTimeout(function () {
15983    me.playNext();
15984  }, interval);
15985};
15986
15987/**
15988 * Toggle start or stop playing
15989 */
15990Slider.prototype.togglePlay = function () {
15991  if (this.playTimeout === undefined) {
15992    this.play();
15993  } else {
15994    this.stop();
15995  }
15996};
15997
15998/**
15999 * Start playing
16000 */
16001Slider.prototype.play = function () {
16002  // Test whether already playing
16003  if (this.playTimeout) return;
16004
16005  this.playNext();
16006
16007  if (this.frame) {
16008    this.frame.play.value = 'Stop';
16009  }
16010};
16011
16012/**
16013 * Stop playing
16014 */
16015Slider.prototype.stop = function () {
16016  clearInterval(this.playTimeout);
16017  this.playTimeout = undefined;
16018
16019  if (this.frame) {
16020    this.frame.play.value = 'Play';
16021  }
16022};
16023
16024/**
16025 * Set a callback function which will be triggered when the value of the
16026 * slider bar has changed.
16027 *
16028 * @param {function} callback
16029 */
16030Slider.prototype.setOnChangeCallback = function (callback) {
16031  this.onChangeCallback = callback;
16032};
16033
16034/**
16035 * Set the interval for playing the list
16036 * @param {number} interval   The interval in milliseconds
16037 */
16038Slider.prototype.setPlayInterval = function (interval) {
16039  this.playInterval = interval;
16040};
16041
16042/**
16043 * Retrieve the current play interval
16044 * @return {number} interval   The interval in milliseconds
16045 */
16046Slider.prototype.getPlayInterval = function () {
16047  return this.playInterval;
16048};
16049
16050/**
16051 * Set looping on or off
16052 * @param {boolean} doLoop  If true, the slider will jump to the start when
16053 *               the end is passed, and will jump to the end
16054 *               when the start is passed.
16055 *
16056 */
16057Slider.prototype.setPlayLoop = function (doLoop) {
16058  this.playLoop = doLoop;
16059};
16060
16061/**
16062 * Execute the onchange callback function
16063 */
16064Slider.prototype.onChange = function () {
16065  if (this.onChangeCallback !== undefined) {
16066    this.onChangeCallback();
16067  }
16068};
16069
16070/**
16071 * redraw the slider on the correct place
16072 */
16073Slider.prototype.redraw = function () {
16074  if (this.frame) {
16075    // resize the bar
16076    this.frame.bar.style.top = this.frame.clientHeight / 2 - this.frame.bar.offsetHeight / 2 + 'px';
16077    this.frame.bar.style.width = this.frame.clientWidth - this.frame.prev.clientWidth - this.frame.play.clientWidth - this.frame.next.clientWidth - 30 + 'px';
16078
16079    // position the slider button
16080    var left = this.indexToLeft(this.index);
16081    this.frame.slide.style.left = left + 'px';
16082  }
16083};
16084
16085/**
16086 * Set the list with values for the slider
16087 * @param {Array} values   A javascript array with values (any type)
16088 */
16089Slider.prototype.setValues = function (values) {
16090  this.values = values;
16091
16092  if (this.values.length > 0) this.setIndex(0);else this.index = undefined;
16093};
16094
16095/**
16096 * Select a value by its index
16097 * @param {number} index
16098 */
16099Slider.prototype.setIndex = function (index) {
16100  if (index < this.values.length) {
16101    this.index = index;
16102
16103    this.redraw();
16104    this.onChange();
16105  } else {
16106    throw new Error('Index out of range');
16107  }
16108};
16109
16110/**
16111 * retrieve the index of the currently selected vaue
16112 * @return {number} index
16113 */
16114Slider.prototype.getIndex = function () {
16115  return this.index;
16116};
16117
16118/**
16119 * retrieve the currently selected value
16120 * @return {*} value
16121 */
16122Slider.prototype.get = function () {
16123  return this.values[this.index];
16124};
16125
16126Slider.prototype._onMouseDown = function (event) {
16127  // only react on left mouse button down
16128  var leftButtonDown = event.which ? event.which === 1 : event.button === 1;
16129  if (!leftButtonDown) return;
16130
16131  this.startClientX = event.clientX;
16132  this.startSlideX = parseFloat(this.frame.slide.style.left);
16133
16134  this.frame.style.cursor = 'move';
16135
16136  // add event listeners to handle moving the contents
16137  // we store the function onmousemove and onmouseup in the graph, so we can
16138  // remove the eventlisteners lateron in the function mouseUp()
16139  var me = this;
16140  this.onmousemove = function (event) {
16141    me._onMouseMove(event);
16142  };
16143  this.onmouseup = function (event) {
16144    me._onMouseUp(event);
16145  };
16146  util.addEventListener(document, 'mousemove', this.onmousemove);
16147  util.addEventListener(document, 'mouseup', this.onmouseup);
16148  util.preventDefault(event);
16149};
16150
16151Slider.prototype.leftToIndex = function (left) {
16152  var width = parseFloat(this.frame.bar.style.width) - this.frame.slide.clientWidth - 10;
16153  var x = left - 3;
16154
16155  var index = Math.round(x / width * (this.values.length - 1));
16156  if (index < 0) index = 0;
16157  if (index > this.values.length - 1) index = this.values.length - 1;
16158
16159  return index;
16160};
16161
16162Slider.prototype.indexToLeft = function (index) {
16163  var width = parseFloat(this.frame.bar.style.width) - this.frame.slide.clientWidth - 10;
16164
16165  var x = index / (this.values.length - 1) * width;
16166  var left = x + 3;
16167
16168  return left;
16169};
16170
16171Slider.prototype._onMouseMove = function (event) {
16172  var diff = event.clientX - this.startClientX;
16173  var x = this.startSlideX + diff;
16174
16175  var index = this.leftToIndex(x);
16176
16177  this.setIndex(index);
16178
16179  util.preventDefault();
16180};
16181
16182Slider.prototype._onMouseUp = function (event) {
16183  // eslint-disable-line no-unused-vars
16184  this.frame.style.cursor = 'auto';
16185
16186  // remove event listeners
16187  util.removeEventListener(document, 'mousemove', this.onmousemove);
16188  util.removeEventListener(document, 'mouseup', this.onmouseup);
16189
16190  util.preventDefault();
16191};
16192
16193module.exports = Slider;
16194
16195/***/ }),
16196/* 93 */
16197/***/ (function(module, exports, __webpack_require__) {
16198
16199"use strict";
16200
16201
16202/**
16203 * @prototype StepNumber
16204 * The class StepNumber is an iterator for Numbers. You provide a start and end
16205 * value, and a best step size. StepNumber itself rounds to fixed values and
16206 * a finds the step that best fits the provided step.
16207 *
16208 * If prettyStep is true, the step size is chosen as close as possible to the
16209 * provided step, but being a round value like 1, 2, 5, 10, 20, 50, ....
16210 *
16211 * Example usage:
16212 *   var step = new StepNumber(0, 10, 2.5, true);
16213 *   step.start();
16214 *   while (!step.end()) {
16215 *   alert(step.getCurrent());
16216 *   step.next();
16217 *   }
16218 *
16219 * Version: 1.0
16220 *
16221 * @param {number} start     The start value
16222 * @param {number} end     The end value
16223 * @param {number} step    Optional. Step size. Must be a positive value.
16224 * @param {boolean} prettyStep Optional. If true, the step size is rounded
16225 *               To a pretty step size (like 1, 2, 5, 10, 20, 50, ...)
16226 */
16227function StepNumber(start, end, step, prettyStep) {
16228  // set default values
16229  this._start = 0;
16230  this._end = 0;
16231  this._step = 1;
16232  this.prettyStep = true;
16233  this.precision = 5;
16234
16235  this._current = 0;
16236  this.setRange(start, end, step, prettyStep);
16237}
16238
16239/**
16240 * Check for input values, to prevent disasters from happening
16241 *
16242 * Source: http://stackoverflow.com/a/1830844
16243 *
16244 * @param {string} n
16245 * @returns {boolean}
16246 */
16247StepNumber.prototype.isNumeric = function (n) {
16248  return !isNaN(parseFloat(n)) && isFinite(n);
16249};
16250
16251/**
16252 * Set a new range: start, end and step.
16253 *
16254 * @param {number} start     The start value
16255 * @param {number} end     The end value
16256 * @param {number} step    Optional. Step size. Must be a positive value.
16257 * @param {boolean} prettyStep Optional. If true, the step size is rounded
16258 *               To a pretty step size (like 1, 2, 5, 10, 20, 50, ...)
16259 */
16260StepNumber.prototype.setRange = function (start, end, step, prettyStep) {
16261  if (!this.isNumeric(start)) {
16262    throw new Error('Parameter \'start\' is not numeric; value: ' + start);
16263  }
16264  if (!this.isNumeric(end)) {
16265    throw new Error('Parameter \'end\' is not numeric; value: ' + start);
16266  }
16267  if (!this.isNumeric(step)) {
16268    throw new Error('Parameter \'step\' is not numeric; value: ' + start);
16269  }
16270
16271  this._start = start ? start : 0;
16272  this._end = end ? end : 0;
16273
16274  this.setStep(step, prettyStep);
16275};
16276
16277/**
16278 * Set a new step size
16279 * @param {number} step    New step size. Must be a positive value
16280 * @param {boolean} prettyStep Optional. If true, the provided step is rounded
16281 *               to a pretty step size (like 1, 2, 5, 10, 20, 50, ...)
16282 */
16283StepNumber.prototype.setStep = function (step, prettyStep) {
16284  if (step === undefined || step <= 0) return;
16285
16286  if (prettyStep !== undefined) this.prettyStep = prettyStep;
16287
16288  if (this.prettyStep === true) this._step = StepNumber.calculatePrettyStep(step);else this._step = step;
16289};
16290
16291/**
16292 * Calculate a nice step size, closest to the desired step size.
16293 * Returns a value in one of the ranges 1*10^n, 2*10^n, or 5*10^n, where n is an
16294 * integer Number. For example 1, 2, 5, 10, 20, 50, etc...
16295 * @param {number}  step  Desired step size
16296 * @return {number}     Nice step size
16297 */
16298StepNumber.calculatePrettyStep = function (step) {
16299  var log10 = function log10(x) {
16300    return Math.log(x) / Math.LN10;
16301  };
16302
16303  // try three steps (multiple of 1, 2, or 5
16304  var step1 = Math.pow(10, Math.round(log10(step))),
16305      step2 = 2 * Math.pow(10, Math.round(log10(step / 2))),
16306      step5 = 5 * Math.pow(10, Math.round(log10(step / 5)));
16307
16308  // choose the best step (closest to minimum step)
16309  var prettyStep = step1;
16310  if (Math.abs(step2 - step) <= Math.abs(prettyStep - step)) prettyStep = step2;
16311  if (Math.abs(step5 - step) <= Math.abs(prettyStep - step)) prettyStep = step5;
16312
16313  // for safety
16314  if (prettyStep <= 0) {
16315    prettyStep = 1;
16316  }
16317
16318  return prettyStep;
16319};
16320
16321/**
16322 * returns the current value of the step
16323 * @return {number} current value
16324 */
16325StepNumber.prototype.getCurrent = function () {
16326  return parseFloat(this._current.toPrecision(this.precision));
16327};
16328
16329/**
16330 * returns the current step size
16331 * @return {number} current step size
16332 */
16333StepNumber.prototype.getStep = function () {
16334  return this._step;
16335};
16336
16337/**
16338 * Set the current to its starting value.
16339 *
16340 * By default, this will be the largest value smaller than start, which
16341 * is a multiple of the step size.
16342 *
16343 * Parameters checkFirst is optional, default false.
16344 * If set to true, move the current value one step if smaller than start.
16345 *
16346 * @param {boolean} [checkFirst=false]
16347 */
16348StepNumber.prototype.start = function (checkFirst) {
16349  if (checkFirst === undefined) {
16350    checkFirst = false;
16351  }
16352
16353  this._current = this._start - this._start % this._step;
16354
16355  if (checkFirst) {
16356    if (this.getCurrent() < this._start) {
16357      this.next();
16358    }
16359  }
16360};
16361
16362/**
16363 * Do a step, add the step size to the current value
16364 */
16365StepNumber.prototype.next = function () {
16366  this._current += this._step;
16367};
16368
16369/**
16370 * Returns true whether the end is reached
16371 * @return {boolean}  True if the current value has passed the end value.
16372 */
16373StepNumber.prototype.end = function () {
16374  return this._current > this._end;
16375};
16376
16377module.exports = StepNumber;
16378
16379/***/ }),
16380/* 94 */
16381/***/ (function(module, exports, __webpack_require__) {
16382
16383"use strict";
16384
16385
16386var _typeof2 = __webpack_require__(6);
16387
16388var _typeof3 = _interopRequireDefault(_typeof2);
16389
16390function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
16391
16392////////////////////////////////////////////////////////////////////////////////
16393// This modules handles the options for Graph3d.
16394//
16395////////////////////////////////////////////////////////////////////////////////
16396var util = __webpack_require__(2);
16397var Camera = __webpack_require__(95);
16398var Point3d = __webpack_require__(34);
16399
16400// enumerate the available styles
16401var STYLE = {
16402  BAR: 0,
16403  BARCOLOR: 1,
16404  BARSIZE: 2,
16405  DOT: 3,
16406  DOTLINE: 4,
16407  DOTCOLOR: 5,
16408  DOTSIZE: 6,
16409  GRID: 7,
16410  LINE: 8,
16411  SURFACE: 9
16412};
16413
16414// The string representations of the styles
16415var STYLENAME = {
16416  'dot': STYLE.DOT,
16417  'dot-line': STYLE.DOTLINE,
16418  'dot-color': STYLE.DOTCOLOR,
16419  'dot-size': STYLE.DOTSIZE,
16420  'line': STYLE.LINE,
16421  'grid': STYLE.GRID,
16422  'surface': STYLE.SURFACE,
16423  'bar': STYLE.BAR,
16424  'bar-color': STYLE.BARCOLOR,
16425  'bar-size': STYLE.BARSIZE
16426};
16427
16428/**
16429 * Field names in the options hash which are of relevance to the user.
16430 *
16431 * Specifically, these are the fields which require no special handling,
16432 * and can be directly copied over.
16433 */
16434var OPTIONKEYS = ['width', 'height', 'filterLabel', 'legendLabel', 'xLabel', 'yLabel', 'zLabel', 'xValueLabel', 'yValueLabel', 'zValueLabel', 'showXAxis', 'showYAxis', 'showZAxis', 'showGrid', 'showPerspective', 'showShadow', 'keepAspectRatio', 'verticalRatio', 'dotSizeRatio', 'dotSizeMinFraction', 'dotSizeMaxFraction', 'showAnimationControls', 'animationInterval', 'animationPreload', 'animationAutoStart', 'axisColor', 'gridColor', 'xCenter', 'yCenter'];
16435
16436/**
16437 * Field names in the options hash which are of relevance to the user.
16438 *
16439 * Same as OPTIONKEYS, but internally these fields are stored with 
16440 * prefix 'default' in the name.
16441 */
16442var PREFIXEDOPTIONKEYS = ['xBarWidth', 'yBarWidth', 'valueMin', 'valueMax', 'xMin', 'xMax', 'xStep', 'yMin', 'yMax', 'yStep', 'zMin', 'zMax', 'zStep'];
16443
16444// Placeholder for DEFAULTS reference
16445var DEFAULTS = undefined;
16446
16447/**
16448 * Check if given hash is empty.
16449 *
16450 * Source: http://stackoverflow.com/a/679937
16451 *
16452 * @param {object} obj
16453 * @returns {boolean}
16454 */
16455function isEmpty(obj) {
16456  for (var prop in obj) {
16457    if (obj.hasOwnProperty(prop)) return false;
16458  }
16459
16460  return true;
16461}
16462
16463/**
16464 * Make first letter of parameter upper case.
16465 *
16466 * Source: http://stackoverflow.com/a/1026087
16467 *
16468 * @param {string} str
16469 * @returns {string}
16470 */
16471function capitalize(str) {
16472  if (str === undefined || str === "" || typeof str != "string") {
16473    return str;
16474  }
16475
16476  return str.charAt(0).toUpperCase() + str.slice(1);
16477}
16478
16479/**
16480 * Add a prefix to a field name, taking style guide into account
16481 *
16482 * @param {string} prefix
16483 * @param {string} fieldName
16484 * @returns {string}
16485 */
16486function prefixFieldName(prefix, fieldName) {
16487  if (prefix === undefined || prefix === "") {
16488    return fieldName;
16489  }
16490
16491  return prefix + capitalize(fieldName);
16492}
16493
16494/**
16495 * Forcibly copy fields from src to dst in a controlled manner.
16496 *
16497 * A given field in dst will always be overwitten. If this field
16498 * is undefined or not present in src, the field in dst will 
16499 * be explicitly set to undefined.
16500 * 
16501 * The intention here is to be able to reset all option fields.
16502 * 
16503 * Only the fields mentioned in array 'fields' will be handled.
16504 *
16505 * @param {object} src
16506 * @param {object} dst
16507 * @param {array<string>} fields array with names of fields to copy
16508 * @param {string} [prefix] prefix to use for the target fields.
16509 */
16510function forceCopy(src, dst, fields, prefix) {
16511  var srcKey;
16512  var dstKey;
16513
16514  for (var i = 0; i < fields.length; ++i) {
16515    srcKey = fields[i];
16516    dstKey = prefixFieldName(prefix, srcKey);
16517
16518    dst[dstKey] = src[srcKey];
16519  }
16520}
16521
16522/**
16523 * Copy fields from src to dst in a safe and controlled manner.
16524 *
16525 * Only the fields mentioned in array 'fields' will be copied over,
16526 * and only if these are actually defined.
16527 *
16528 * @param {object} src
16529 * @param {object} dst
16530 * @param {array<string>}
16530 fields array with names of fields to copy
16531 * @param {string} [prefix] prefix to use for the target fields.
16532 */
16533function safeCopy(src, dst, fields, prefix) {
16534  var srcKey;
16535  var dstKey;
16536
16537  for (var i = 0; i < fields.length; ++i) {
16538    srcKey = fields[i];
16539    if (src[srcKey] === undefined) continue;
16540
16541    dstKey = prefixFieldName(prefix, srcKey);
16542
16543    dst[dstKey] = src[srcKey];
16544  }
16545}
16546
16547/**
16548 * Initialize dst with the values in src.
16549 *
16550 * src is the hash with the default values. 
16551 * A reference DEFAULTS to this hash is stored locally for 
16552 * further handling.
16553 *
16554 * For now, dst is assumed to be a Graph3d instance.
16555 * @param {object} src
16556 * @param {object} dst
16557 */
16558function setDefaults(src, dst) {
16559  if (src === undefined || isEmpty(src)) {
16560    throw new Error('No DEFAULTS passed');
16561  }
16562  if (dst === undefined) {
16563    throw new Error('No dst passed');
16564  }
16565
16566  // Remember defaults for future reference
16567  DEFAULTS = src;
16568
16569  // Handle the defaults which can be simply copied over
16570  forceCopy(src, dst, OPTIONKEYS);
16571  forceCopy(src, dst, PREFIXEDOPTIONKEYS, 'default');
16572
16573  // Handle the more complex ('special') fields
16574  setSpecialSettings(src, dst);
16575
16576  // Following are internal fields, not part of the user settings
16577  dst.margin = 10; // px
16578  dst.showGrayBottom = false; // TODO: this does not work correctly
16579  dst.showTooltip = false;
16580  dst.onclick_callback = null;
16581  dst.eye = new Point3d(0, 0, -1); // TODO: set eye.z about 3/4 of the width of the window?
16582}
16583
16584/**
16585 *
16586 * @param {object} options
16587 * @param {object} dst
16588 */
16589function setOptions(options, dst) {
16590  if (options === undefined) {
16591    return;
16592  }
16593  if (dst === undefined) {
16594    throw new Error('No dst passed');
16595  }
16596
16597  if (DEFAULTS === undefined || isEmpty(DEFAULTS)) {
16598    throw new Error('DEFAULTS not set for module Settings');
16599  }
16600
16601  // Handle the parameters which can be simply copied over
16602  safeCopy(options, dst, OPTIONKEYS);
16603  safeCopy(options, dst, PREFIXEDOPTIONKEYS, 'default');
16604
16605  // Handle the more complex ('special') fields
16606  setSpecialSettings(options, dst);
16607}
16608
16609/**
16610 * Special handling for certain parameters
16611 *
16612 * 'Special' here means: setting requires more than a simple copy
16613 *
16614 * @param {object} src
16615 * @param {object} dst
16616 */
16617function setSpecialSettings(src, dst) {
16618  if (src.backgroundColor !== undefined) {
16619    setBackgroundColor(src.backgroundColor, dst);
16620  }
16621
16622  setDataColor(src.dataColor, dst);
16623  setStyle(src.style, dst);
16624  setShowLegend(src.showLegend, dst);
16625  setCameraPosition(src.cameraPosition, dst);
16626
16627  // As special fields go, this is an easy one; just a translation of the name.
16628  // Can't use this.tooltip directly, because that field exists internally
16629  if (src.tooltip !== undefined) {
16630    dst.showTooltip = src.tooltip;
16631  }
16632  if (src.onclick != undefined) {
16633    dst.onclick_callback = src.onclick;
16634  }
16635
16636  if (src.tooltipStyle !== undefined) {
16637    util.selectiveDeepExtend(['tooltipStyle'], dst, src);
16638  }
16639}
16640
16641/**
16642 * Set the value of setting 'showLegend'
16643 *
16644 * This depends on the value of the style fields, so it must be called
16645 * after the style field has been initialized.
16646 *
16647 * @param {boolean} showLegend
16648 * @param {object} dst
16649 */
16650function setShowLegend(showLegend, dst) {
16651  if (showLegend === undefined) {
16652    // If the default was auto, make a choice for this field
16653    var isAutoByDefault = DEFAULTS.showLegend === undefined;
16654
16655    if (isAutoByDefault) {
16656      // these styles default to having legends
16657      var isLegendGraphStyle = dst.style === STYLE.DOTCOLOR || dst.style === STYLE.DOTSIZE;
16658
16659      dst.showLegend = isLegendGraphStyle;
16660    } else {
16661      // Leave current value as is
16662    }
16663  } else {
16664    dst.showLegend = showLegend;
16665  }
16666}
16667
16668/**
16669 * Retrieve the style index from given styleName
16670 * @param {string} styleName  Style name such as 'dot', 'grid', 'dot-line'
16671 * @return {number} styleNumber Enumeration value representing the style, or -1
16672 *                when not found
16673 */
16674function getStyleNumberByName(styleName) {
16675  var number = STYLENAME[styleName];
16676
16677  if (number === undefined) {
16678    return -1;
16679  }
16680
16681  return number;
16682}
16683
16684/**
16685 * Check if given number is a valid style number.
16686 *
16687 * @param {string | number} style
16688 * @return {boolean} true if valid, false otherwise
16689 */
16690function checkStyleNumber(style) {
16691  var valid = false;
16692
16693  for (var n in STYLE) {
16694    if (STYLE[n] === style) {
16695      valid = true;
16696      break;
16697    }
16698  }
16699
16700  return valid;
16701}
16702
16703/**
16704 *
16705 * @param {string | number} style
16706 * @param {Object} dst
16707 */
16708function setStyle(style, dst) {
16709  if (style === undefined) {
16710    return; // Nothing to do
16711  }
16712
16713  var styleNumber;
16714
16715  if (typeof style === 'string') {
16716    styleNumber = getStyleNumberByName(style);
16717
16718    if (styleNumber === -1) {
16719      throw new Error('Style \'' + style + '\' is invalid');
16720    }
16721  } else {
16722    // Do a pedantic check on style number value
16723    if (!checkStyleNumber(style)) {
16724      throw new Error('Style \'' + style + '\' is invalid');
16725    }
16726
16727    styleNumber = style;
16728  }
16729
16730  dst.style = styleNumber;
16731}
16732
16733/**
16734 * Set the background styling for the graph
16735 * @param {string | {fill: string, stroke: string, strokeWidth: string}} backgroundColor
16736 * @param {Object} dst
16737 */
16738function setBackgroundColor(backgroundColor, dst) {
16739  var fill = 'white';
16740  var stroke = 'gray';
16741  var strokeWidth = 1;
16742
16743  if (typeof backgroundColor === 'string') {
16744    fill = backgroundColor;
16745    stroke = 'none';
16746    strokeWidth = 0;
16747  } else if ((typeof backgroundColor === 'undefined' ? 'undefined' : (0, _typeof3['default'])(backgroundColor)) === 'object') {
16748    if (backgroundColor.fill !== undefined) fill = backgroundColor.fill;
16749    if (backgroundColor.stroke !== undefined) stroke = backgroundColor.stroke;
16750    if (backgroundColor.strokeWidth !== undefined) strokeWidth = backgroundColor.strokeWidth;
16751  } else {
16752    throw new Error('Unsupported type of backgroundColor');
16753  }
16754
16755  dst.frame.style.backgroundColor = fill;
16756  dst.frame.style.borderColor = stroke;
16757  dst.frame.style.borderWidth = strokeWidth + 'px';
16758  dst.frame.style.borderStyle = 'solid';
16759}
16760
16761/**
16762 *
16763 * @param {string | Object} dataColor
16764 * @param {Object} dst
16765 */
16766function setDataColor(dataColor, dst) {
16767  if (dataColor === undefined) {
16768    return; // Nothing to do
16769  }
16770
16771  if (dst.dataColor === undefined) {
16772    dst.dataColor = {};
16773  }
16774
16775  if (typeof dataColor === 'string') {
16776    dst.dataColor.fill = dataColor;
16777    dst.dataColor.stroke = dataColor;
16778  } else {
16779    if (dataColor.fill) {
16780      dst.dataColor.fill = dataColor.fill;
16781    }
16782    if (dataColor.stroke) {
16783      dst.dataColor.stroke = dataColor.stroke;
16784    }
16785    if (dataColor.strokeWidth !== undefined) {
16786      dst.dataColor.strokeWidth = dataColor.strokeWidth;
16787    }
16788  }
16789}
16790
16791/**
16792 *
16793 * @param {Object} cameraPosition
16794 * @param {Object} dst
16795 */
16796function setCameraPosition(cameraPosition, dst) {
16797  var camPos = cameraPosition;
16798  if (camPos === undefined) {
16799    return;
16800  }
16801
16802  if (dst.camera === undefined) {
16803    dst.camera = new Camera();
16804  }
16805
16806  dst.camera.setArmRotation(camPos.horizontal, camPos.vertical);
16807  dst.camera.setArmLength(camPos.distance);
16808}
16809
16810module.exports.STYLE = STYLE;
16811module.exports.setDefaults = setDefaults;
16812module.exports.setOptions = setOptions;
16813module.exports.setCameraPosition = setCameraPosition;
16814
16815/***/ }),
16816/* 95 */
16817/***/ (function(module, exports, __webpack_require__) {
16818
16819"use strict";
16820
16821
16822var _sign = __webpack_require__(165);
16823
16824var _sign2 = _interopRequireDefault(_sign);
16825
16826function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
16827
16828var Point3d = __webpack_require__(34);
16829
16830/**
16831 * The camera is mounted on a (virtual) camera arm. The camera arm can rotate
16832 * The camera is always looking in the direction of the origin of the arm.
16833 * This way, the camera always rotates around one fixed point, the location
16834 * of the camera arm.
16835 *
16836 * Documentation:
16837 *   http://en.wikipedia.org/wiki/3D_projection
16838 * @class Camera
16839 */
16840function Camera() {
16841  this.armLocation = new Point3d();
16842  this.armRotation = {};
16843  this.armRotation.horizontal = 0;
16844  this.armRotation.vertical = 0;
16845  this.armLength = 1.7;
16846  this.cameraOffset = new Point3d();
16847  this.offsetMultiplier = 0.6;
16848
16849  this.cameraLocation = new Point3d();
16850  this.cameraRotation = new Point3d(0.5 * Math.PI, 0, 0);
16851
16852  this.calculateCameraOrientation();
16853}
16854
16855/**
16856 * Set offset camera in camera coordinates
16857 * @param {number} x offset by camera horisontal
16858 * @param {number} y offset by camera vertical
16859 */
16860Camera.prototype.setOffset = function (x, y) {
16861  var abs = Math.abs,
16862      sign = _sign2['default'],
16863      mul = this.offsetMultiplier,
16864      border = this.armLength * mul;
16865
16866  if (abs(x) > border) {
16867    x = sign(x) * border;
16868  }
16869  if (abs(y) > border) {
16870    y = sign(y) * border;
16871  }
16872  this.cameraOffset.x = x;
16873  this.cameraOffset.y = y;
16874  this.calculateCameraOrientation();
16875};
16876
16877/**
16878 * Get camera offset by horizontal and vertical
16879 * @returns {number}
16880 */
16881Camera.prototype.getOffset = function () {
16882  return this.cameraOffset;
16883};
16884
16885/**
16886 * Set the location (origin) of the arm
16887 * @param {number} x  Normalized value of x
16888 * @param {number} y  Normalized value of y
16889 * @param {number} z  Normalized value of z
16890 */
16891Camera.prototype.setArmLocation = function (x, y, z) {
16892  this.armLocation.x = x;
16893  this.armLocation.y = y;
16894  this.armLocation.z = z;
16895
16896  this.calculateCameraOrientation();
16897};
16898
16899/**
16900 * Set the rotation of the camera arm
16901 * @param {number} horizontal   The horizontal rotation, between 0 and 2*PI.
16902 *                Optional, can be left undefined.
16903 * @param {number} vertical   The vertical rotation, between 0 and 0.5*PI
16904 *                if vertical=0.5*PI, the graph is shown from the
16905 *                top. Optional, can be left undefined.
16906 */
16907Camera.prototype.setArmRotation = function (horizontal, vertical) {
16908  if (horizontal !== undefined) {
16909    this.armRotation.horizontal = horizontal;
16910  }
16911
16912  if (vertical !== undefined) {
16913    this.armRotation.vertical = vertical;
16914    if (this.armRotation.vertical < 0) this.armRotation.vertical = 0;
16915    if (this.armRotation.vertical > 0.5 * Math.PI) this.armRotation.vertical = 0.5 * Math.PI;
16916  }
16917
16918  if (horizontal !== undefined || vertical !== undefined) {
16919    this.calculateCameraOrientation();
16920  }
16921};
16922
16923/**
16924 * Retrieve the current arm rotation
16925 * @return {object}   An object with parameters horizontal and vertical
16926 */
16927Camera.prototype.getArmRotation = function () {
16928  var rot = {};
16929  rot.horizontal = this.armRotation.horizontal;
16930  rot.vertical = this.armRotation.vertical;
16931
16932  return rot;
16933};
16934
16935/**
16936 * Set the (normalized) length of the camera arm.
16937 * @param {number} length A length between 0.71 and 5.0
16938 */
16939Camera.prototype.setArmLength = function (length) {
16940  if (length === undefined) return;
16941
16942  this.armLength = length;
16943
16944  // Radius must be larger than the corner of the graph,
16945  // which has a distance of sqrt(0.5^2+0.5^2) = 0.71 from the center of the
16946  // graph
16947  if (this.armLength < 0.71) this.armLength = 0.71;
16948  if (this.armLength > 5.0) this.armLength = 5.0;
16949
16950  this.setOffset(this.cameraOffset.x, this.cameraOffset.y);
16951  this.calculateCameraOrientation();
16952};
16953
16954/**
16955 * Retrieve the arm length
16956 * @return {number} length
16957 */
16958Camera.prototype.getArmLength = function () {
16959  return this.armLength;
16960};
16961
16962/**
16963 * Retrieve the camera location
16964 * @return {Point3d} cameraLocation
16965 */
16966Camera.prototype.getCameraLocation = function () {
16967  return this.cameraLocation;
16968};
16969
16970/**
16971 * Retrieve the camera rotation
16972 * @return {Point3d} cameraRotation
16973 */
16974Camera.prototype.getCameraRotation = function () {
16975  return this.cameraRotation;
16976};
16977
16978/**
16979 * Calculate the location and rotation of the camera based on the
16980 * position and orientation of the camera arm
16981 */
16982Camera.prototype.calculateCameraOrientation = function () {
16983  // calculate location of the camera
16984  this.cameraLocation.x = this.armLocation.x - this.armLength * Math.sin(this.armRotation.horizontal) * Math.cos(this.armRotation.vertical);
16985  this.cameraLocation.y = this.armLocation.y - this.armLength * Math.cos(this.armRotation.horizontal) * Math.cos(this.armRotation.vertical);
16986  this.cameraLocation.z = this.armLocation.z + this.armLength * Math.sin(this.armRotation.vertical);
16987
16988  // calculate rotation of the camera
16989  this.cameraRotation.x = Math.PI / 2 - this.armRotation.vertical;
16990  this.cameraRotation.y = 0;
16991  this.cameraRotation.z = -this.armRotation.horizontal;
16992
16993  var xa = this.cameraRotation.x;
16994  var za = this.cameraRotation.z;
16995  var dx = this.cameraOffset.x;
16996  var dy = this.cameraOffset.y;
16997  var sin = Math.sin,
16998      cos = Math.cos;
16999
17000  this.cameraLocation.x = this.cameraLocation.x + dx * cos(za) + dy * -sin(za) * cos(xa);
17001  this.cameraLocation.y = this.cameraLocation.y + dx * sin(za) + dy * cos(za) * cos(xa);
17002  this.cameraLocation.z = this.cameraLocation.z + dy * sin(xa);
17003};
17004
17005module.exports = Camera;
17006
17007/***/ }),
17008/* 96 */
17009/***/ (function(module, exports, __webpack_require__) {
17010
17011"use strict";
17012
17013
17014var DataView = __webpack_require__(12);
17015
17016/**
17017 * @class Filter
17018 *
17019 * @param {DataGroup} dataGroup the data group 
17020 * @param {number}  column             The index of the column to be filtered
17021 * @param {Graph3d} graph              The graph
17022 */
17023function Filter(dataGroup, column, graph) {
17024  this.dataGroup = dataGroup;
17025  this.column = column;
17026  this.graph = graph; // the parent graph
17027
17028  this.index = undefined;
17029  this.value = undefined;
17030
17031  // read all distinct values and select the first one
17032  this.values = dataGroup.getDistinctValues(this.column);
17033
17034  if (this.values.length > 0) {
17035    this.selectValue(0);
17036  }
17037
17038  // create an array with the filtered datapoints. this will be loaded afterwards
17039  this.dataPoints = [];
17040
17041  this.loaded = false;
17042  this.onLoadCallback = undefined;
17043
17044  if (graph.animationPreload) {
17045    this.loaded = false;
17046    this.loadInBackground();
17047  } else {
17048    this.loaded = true;
17049  }
17050}
17051
17052/**
17053 * Return the label
17054 * @return {string} label
17055 */
17056Filter.prototype.isLoaded = function () {
17057  return this.loaded;
17058};
17059
17060/**
17061 * Return the loaded progress
17062 * @return {number} percentage between 0 and 100
17063 */
17064Filter.prototype.getLoadedProgress = function () {
17065  var len = this.values.length;
17066
17067  var i = 0;
17068  while (this.dataPoints[i]) {
17069    i++;
17070  }
17071
17072  return Math.round(i / len * 100);
17073};
17074
17075/**
17076 * Return the label
17077 * @return {string} label
17078 */
17079Filter.prototype.getLabel = function () {
17080  return this.graph.filterLabel;
17081};
17082
17083/**
17084 * Return the columnIndex of the filter
17085 * @return {number} columnIndex
17086 */
17087Filter.prototype.getColumn = function () {
17088  return this.column;
17089};
17090
17091/**
17092 * Return the currently selected value. Returns undefined if there is no selection
17093 * @return {*} value
17094 */
17095Filter.prototype.getSelectedValue = function () {
17096  if (this.index === undefined) return undefined;
17097
17098  return this.values[this.index];
17099};
17100
17101/**
17102 * Retrieve all values of the filter
17103 * @return {Array} values
17104 */
17105Filter.prototype.getValues = function () {
17106  return this.values;
17107};
17108
17109/**
17110 * Retrieve one value of the filter
17111 * @param {number}  index
17112 * @return {*} value
17113 */
17114Filter.prototype.getValue = function (index) {
17115  if (index >= this.values.length) throw new Error('Index out of range');
17116
17117  return this.values[index];
17118};
17119
17120/**
17121 * Retrieve the (filtered) dataPoints for the currently selected filter index
17122 * @param {number} [index] (optional)
17123 * @return {Array} dataPoints
17124 */
17125Filter.prototype._getDataPoints = function (index) {
17126  if (index === undefined) index = this.index;
17127
17128  if (index === undefined) return [];
17129
17130  var dataPoints;
17131  if (this.dataPoints[index]) {
17132    dataPoints = this.dataPoints[index];
17133  } else {
17134    var f = {};
17135    f.column = this.column;
17136    f.value = this.values[index];
17137
17138    var dataView = new DataView(this.dataGroup.getDataSet(), { filter: function filter(item) {
17139        return item[f.column] == f.value;
17140      } }).get();
17141    dataPoints = this.dataGroup._getDataPoints(dataView);
17142
17143    this.dataPoints[index] = dataPoints;
17144  }
17145
17146  return dataPoints;
17147};
17148
17149/**
17150 * Set a callback function when the filter is fully loaded.
17151 *
17152 * @param {function} callback
17153 */
17154Filter.prototype.setOnLoadCallback = function (callback) {
17155  this.onLoadCallback = callback;
17156};
17157
17158/**
17159 * Add a value to the list with available values for this filter
17160 * No double entries will be created.
17161 * @param {number} index
17162 */
17163Filter.prototype.selectValue = function (index) {
17164  if (index >= this.values.length) throw new Error('Index out of range');
17165
17166  this.index = index;
17167  this.value = this.values[index];
17168};
17169
17170/**
17171 * Load all filtered rows in the background one by one
17172 * Start this method without providing an index!
17173 *
17174 * @param {number} [index=0]
17175 */
17176Filter.prototype.loadInBackground = function (index) {
17177  if (index === undefined) index = 0;
17178
17179  var frame = this.graph.frame;
17180
17181  if (index < this.values.length) {
17182    // create a progress box
17183    if (frame.progress === undefined) {
17184      frame.progress = document.createElement('DIV');
17185      frame.progress.style.position = 'absolute';
17186      frame.progress.style.color = 'gray';
17187      frame.appendChild(frame.progress);
17188    }
17189    var progress = this.getLoadedProgress();
17190    frame.progress.innerHTML = 'Loading animation... ' + progress + '%';
17191    // TODO: this is no nice solution...
17192    frame.progress.style.bottom = 60 + 'px'; // TODO: use height of slider
17193    frame.progress.style.left = 10 + 'px';
17194
17195    var me = this;
17196    setTimeout(function () {
17197      me.loadInBackground(index + 1);
17198    }, 10);
17199    this.loaded = false;
17200  } else {
17201    this.loaded = true;
17202
17203    // remove the progress box
17204    if (frame.progress !== undefined) {
17205      frame.removeChild(frame.progress);
17206      frame.progress = undefined;
17207    }
17208
17209    if (this.onLoadCallback) this.onLoadCallback();
17210  }
17211};
17212
17213module.exports = Filter;
17214
17215/***/ }),
17216/* 97 */
17217/***/ (function(module, exports, __webpack_require__) {
17218
17219"use strict";
17220
17221
17222var keycharm = __webpack_require__(35);
17223var Emitter = __webpack_require__(44);
17224var Hammer = __webpack_require__(10);
17225var util = __webpack_require__(2);
17226
17227/**
17228 * Turn an element into an clickToUse element.
17229 * When not active, the element has a transparent overlay. When the overlay is
17230 * clicked, the mode is changed to active.
17231 * When active, the element is displayed with a blue border around it, and
17232 * the interactive contents of the element can be used. When clicked outside
17233 * the element, the elements mode is changed to inactive.
17234 * @param {Element} container
17235 * @constructor Activator
17236 */
17237function Activator(container) {
17238  this.active = false;
17239
17240  this.dom = {
17241    container: container
17242  };
17243
17244  this.dom.overlay = document.createElement('div');
17245  this.dom.overlay.className = 'vis-overlay';
17246
17247  this.dom.container.appendChild(this.dom.overlay);
17248
17249  this.hammer = Hammer(this.dom.overlay);
17250  this.hammer.on('tap', this._onTapOverlay.bind(this));
17251
17252  // block all touch events (except tap)
17253  var me = this;
17254  var events = ['tap', 'doubletap', 'press', 'pinch', 'pan', 'panstart', 'panmove', 'panend'];
17255  events.forEach(function (event) {
17256    me.hammer.on(event, function (event) {
17257      event.stopPropagation();
17258    });
17259  });
17260
17261  // attach a click event to the window, in order to deactivate when clicking outside the timeline
17262  if (document && document.body) {
17263    this.onClick = function (event) {
17264      if (!_hasParent(event.target, container)) {
17265        me.deactivate();
17266      }
17267    };
17268    document.body.addEventListener('click', this.onClick);
17269  }
17270
17271  if (this.keycharm !== undefined) {
17272    this.keycharm.destroy();
17273  }
17274  this.keycharm = keycharm();
17275
17276  // keycharm listener only bounded when active)
17277  this.escListener = this.deactivate.bind(this);
17278}
17279
17280// turn into an event emitter
17281Emitter(Activator.prototype);
17282
17283// The currently active activator
17284Activator.current = null;
17285
17286/**
17287 * Destroy the activator. Cleans up all created DOM and event listeners
17288 */
17289Activator.prototype.destroy = function () {
17290  this.deactivate();
17291
17292  // remove dom
17293  this.dom.overlay.parentNode.removeChild(this.dom.overlay);
17294
17295  // remove global event listener
17296  if (this.onClick) {
17297    document.body.removeEventListener('click', this.onClick);
17298  }
17299
17300  // cleanup hammer instances
17301  this.hammer.destroy();
17302  this.hammer = null;
17303  // FIXME: cleaning up hammer instances doesn't work (Timeline not removed from memory)
17304};
17305
17306/**
17307 * Activate the element
17308 * Overlay is hidden, element is decorated with a blue shadow border
17309 */
17310Activator.prototype.activate = function () {
17311  // we allow only one active activator at a time
17312  if (Activator.current) {
17313    Activator.current.deactivate();
17314  }
17315  Activator.current = this;
17316
17317  this.active = true;
17318  this.dom.overlay.style.display = 'none';
17319  util.addClassName(this.dom.container, 'vis-active');
17320
17321  this.emit('change');
17322  this.emit('activate');
17323
17324  // ugly hack: bind ESC after emitting the events, as the Network rebinds all
17325  // keyboard events on a 'change' event
17326  this.keycharm.bind('esc', this.escListener);
17327};
17328
17329/**
17330 * Deactivate the element
17331 * Overlay is displayed on top of the element
17332 */
17333Activator.prototype.deactivate = function () {
17334  this.active = false;
17335  this.dom.overlay.style.display = '';
17336  util.removeClassName(this.dom.container, 'vis-active');
17337  this.keycharm.unbind('esc', this.escListener);
17338
17339  this.emit('change');
17340  this.emit('deactivate');
17341};
17342
17343/**
17344 * Handle a tap event: activate the container
17345 * @param {Event}  event   The event
17346 * @private
17347 */
17348Activator.prototype._onTapOverlay = function (event) {
17349  // activate the container
17350  this.activate();
17351  event.stopPropagation();
17352};
17353
17354/**
17355 * Test whether the element has the requested parent element somewhere in
17356 * its chain of parent nodes.
17357 * @param {HTMLElement} element
17358 * @param {HTMLElement} parent
17359 * @returns {boolean} Returns true when the parent is found somewhere in the
17360 *                    chain of parent nodes.
17361 * @private
17362 */
17363function _hasParent(element, parent) {
17364  while (element) {
17365    if (element === parent) {
17366      return true;
17367    }
17368    element = element.parentNode;
17369  }
17370  return false;
17371}
17372
17373module.exports = Activator;
17374
17375/***/ }),
17376/* 98 */
17377/***/ (function(module, exports, __webpack_require__) {
17378
17379"use strict";
17380
17381
17382// English
17383exports['en'] = {
17384  current: 'current',
17385  time: 'time'
17386};
17387exports['en_EN'] = exports['en'];
17388exports['en_US'] = exports['en'];
17389
17390// Italiano
17391exports['it'] = {
17392  current: 'attuale',
17393  time: 'tempo'
17394};
17395exports['it_IT'] = exports['it'];
17396exports['it_CH'] = exports['it'];
17397
17398// Dutch
17399exports['nl'] = {
17400  current: 'huidige',
17401  time: 'tijd'
17402};
17403exports['nl_NL'] = exports['nl'];
17404exports['nl_BE'] = exports['nl'];
17405
17406// German
17407exports['de'] = {
17408  current: 'Aktuelle',
17409  time: 'Zeit'
17410};
17411exports['de_DE'] = exports['de'];
17412
17413// French
17414exports['fr'] = {
17415  current: 'actuel',
17416  time: 'heure'
17417};
17418exports['fr_FR'] = exports['fr'];
17419exports['fr_CA'] = exports['fr'];
17420exports['fr_BE'] = exports['fr'];
17421
17422// Espanol
17423exports['es'] = {
17424  current: 'corriente',
17425  time: 'hora'
17426};
17427exports['es_ES'] = exports['es'];
17428
17429/***/ }),
17430/* 99 */
17431/***/ (function(module, exports, __webpack_require__) {
17432
17433"use strict";
17434
17435
17436var _create = __webpack_require__(29);
17437
17438var _create2 = _interopRequireDefault(_create);
17439
17440var _typeof2 = __webpack_require__(6);
17441
17442var _typeof3 = _interopRequireDefault(_typeof2);
17443
17444function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
17445
17446var Hammer = __webpack_require__(10);
17447var util = __webpack_require__(2);
17448var DataSet = __webpack_require__(11);
17449var DataView = __webpack_require__(12);
17450var TimeStep = __webpack_require__(66);
17451var Component = __webpack_require__(16);
17452var Group = __webpack_require__(68);
17453var BackgroundGroup = __webpack_require__(69);
17454var BoxItem = __webpack_require__(101);
17455var PointItem = __webpack_require__(102);
17456var RangeItem = __webpack_require__(70);
17457var BackgroundItem = __webpack_require__(103);
17458var Popup = __webpack_require__(104)['default'];
17459
17460var UNGROUPED = '__ungrouped__'; // reserved group id for ungrouped items
17461var BACKGROUND = '__background__'; // reserved group id for background items without group
17462
17463/**
17464 * An ItemSet holds a set of items and ranges which can be displayed in a
17465 * range. The width is determined by the parent of the ItemSet, and the height
17466 * is determined by the size of the items.
17467 * @param {{dom: Object, domProps: Object, emitter: Emitter, range: Range}} body
17468 * @param {Object} [options]      See ItemSet.setOptions for the available options.
17469 * @constructor ItemSet
17470 * @extends Component
17471 */
17472function ItemSet(body, options) {
17473  this.body = body;
17474  this.defaultOptions = {
17475    type: null, // 'box', 'point', 'range', 'background'
17476    orientation: {
17477      item: 'bottom' // item orientation: 'top' or 'bottom'
17478    },
17479    align: 'auto', // alignment of box items
17480    stack: true,
17481    stackSubgroups: true,
17482    groupOrderSwap: function groupOrderSwap(fromGroup, toGroup, groups) {
17483      // eslint-disable-line no-unused-vars
17484      var targetOrder = toGroup.order;
17485      toGroup.order = fromGroup.order;
17486      fromGroup.order = targetOrder;
17487    },
17488    groupOrder: 'order',
17489
17490    selectable: true,
17491    multiselect: false,
17492    itemsAlwaysDraggable: {
17493      item: false,
17494      range: false
17495    },
17496
17497    editable: {
17498      updateTime: false,
17499      updateGroup: false,
17500      add: false,
17501      remove: false,
17502      overrideItems: false
17503    },
17504
17505    groupEditable: {
17506      order: false,
17507      add: false,
17508      remove: false
17509    },
17510
17511    snap: TimeStep.snap,
17512
17513    // Only called when `objectData.target === 'item'.
17514    onDropObjectOnItem: function onDropObjectOnItem(objectData, item, callback) {
17515      callback(item);
17516    },
17517    onAdd: function onAdd(item, callback) {
17518      callback(item);
17519    },
17520    onUpdate: function onUpdate(item, callback) {
17521      callback(item);
17522    },
17523    onMove: function onMove(item, callback) {
17524      callback(item);
17525    },
17526    onRemove: function onRemove(item, callback) {
17527      callback(item);
17528    },
17529    onMoving: function onMoving(item, callback) {
17530      callback(item);
17531    },
17532    onAddGroup: function onAddGroup(item, callback) {
17533      callback(item);
17534    },
17535    onMoveGroup: function onMoveGroup(item, callback) {
17536      callback(item);
17537    },
17538    onRemoveGroup: function onRemoveGroup(item, callback) {
17539      callback(item);
17540    },
17541
17542    margin: {
17543      item: {
17544        horizontal: 10,
17545        vertical: 10
17546      },
17547      axis: 20
17548    },
17549
17550    showTooltips: true,
17551
17552    tooltip: {
17553      followMouse: false,
17554      overflowMethod: 'flip'
17555    },
17556
17557    tooltipOnItemUpdateTime: false
17558  };
17559
17560  // options is shared by this ItemSet and all its items
17561  this.options = util.extend({}, this.defaultOptions);
17562  this.options.rtl = options.rtl;
17563
17564  // options for getting items from the DataSet with the correct type
17565  this.itemOptions = {
17566    type: { start: 'Date', end: 'Date' }
17567  };
17568
17569  this.conversion = {
17570    toScreen: body.util.toScreen,
17571    toTime: body.util.toTime
17572  };
17573  this.dom = {};
17574  this.props = {};
17575  this.hammer = null;
17576
17577  var me = this;
17578  this.itemsData = null; // DataSet
17579  this.groupsData = null; // DataSet
17580
17581  // listeners for the DataSet of the items
17582  this.itemListeners = {
17583    'add': function add(event, params, senderId) {
17584      // eslint-disable-line no-unused-vars
17585      me._onAdd(params.items);
17586    },
17587    'update': function update(event, params, senderId) {
17588      // eslint-disable-line no-unused-vars
17589      me._onUpdate(params.items);
17590    },
17591    'remove': function remove(event, params, senderId) {
17592      // eslint-disable-line no-unused-vars
17593      me._onRemove(params.items);
17594    }
17595  };
17596
17597  // listeners for the DataSet of the groups
17598  this.groupListeners = {
17599    'add': function add(event, params, senderId) {
17600      // eslint-disable-line no-unused-vars
17601      me._onAddGroups(params.items);
17602
17603      if (me.groupsData && me.groupsData.length > 0) {
17604        var groupsData = me.groupsData.getDataSet();
17605        groupsData.get().forEach(function (groupData) {
17606          if (groupData.nestedGroups) {
17607            if (groupData.showNested != false) {
17608              groupData.showNested = true;
17609            }
17610            var updatedGroups = [];
17611            groupData.nestedGroups.forEach(function (nestedGroupId) {
17612              var updatedNestedGroup = groupsData.get(nestedGroupId);
17613              if (!updatedNestedGroup) {
17614                return;
17615              }
17616              updatedNestedGroup.nestedInGroup = groupData.id;
17617              if (groupData.showNested == false) {
17618                updatedNestedGroup.visible = false;
17619              }
17620              updatedGroups = updatedGroups.concat(updatedNestedGroup);
17621            });
17622            groupsData.update(updatedGroups, senderId);
17623          }
17624        });
17625      }
17626    },
17627    'update': function update(event, params, senderId) {
17628      // eslint-disable-line no-unused-vars
17629      me._onUpdateGroups(params.items);
17630    },
17631    'remove': function remove(event, params, senderId) {
17632      // eslint-disable-line no-unused-vars
17633      me._onRemoveGroups(params.items);
17634    }
17635  };
17636
17637  this.items = {}; // object with an Item for every data item
17638  this.groups = {}; // Group object for every group
17639  this.groupIds = [];
17640
17641  this.selection = []; // list with the ids of all selected nodes
17642
17643  this.popup = null;
17644
17645  this.touchParams = {}; // stores properties while dragging
17646  this.groupTouchParams = {};
17647  // create the HTML DOM
17648
17649  this._create();
17650
17651  this.setOptions(options);
17652}
17653
17654ItemSet.prototype = new Component();
17655
17656// available item types will be registered here
17657ItemSet.types = {
17658  background: BackgroundItem,
17659  box: BoxItem,
17660  range: RangeItem,
17661  point: PointItem
17662};
17663
17664/**
17665 * Create the HTML DOM for the ItemSet
17666 */
17667ItemSet.prototype._create = function () {
17668  var frame = document.createElement('div');
17669  frame.className = 'vis-itemset';
17670  frame['timeline-itemset'] = this;
17671  this.dom.frame = frame;
17672
17673  // create background panel
17674  var background = document.createElement('div');
17675  background.className = 'vis-background';
17676  frame.appendChild(background);
17677  this.dom.background = background;
17678
17679  // create foreground panel
17680  var foreground = document.createElement('div');
17681  foreground.className = 'vis-foreground';
17682  frame.appendChild(foreground);
17683  this.dom.foreground = foreground;
17684
17685  // create axis panel
17686  var axis = document.createElement('div');
17687  axis.className = 'vis-axis';
17688  this.dom.axis = axis;
17689
17690  // create labelset
17691  var labelSet = document.createElement('div');
17692  labelSet.className = 'vis-labelset';
17693  this.dom.labelSet = labelSet;
17694
17695  // create ungrouped Group
17696  this._updateUngrouped();
17697
17698  // create background Group
17699  var backgroundGroup = new BackgroundGroup(BACKGROUND, null, this);
17700  backgroundGroup.show();
17701  this.groups[BACKGROUND] = backgroundGroup;
17702
17703  // attach event listeners
17704  // Note: we bind to the centerContainer for the case where the height
17705  //       of the center container is larger than of the ItemSet, so we
17706  //       can click in the empty area to create a new item or deselect an item.
17707  this.hammer = new Hammer(this.body.dom.centerContainer);
17708
17709  // drag items when selected
17710  this.hammer.on('hammer.input', function (event) {
17711    if (event.isFirst) {
17712      this._onTouch(event);
17713    }
17714  }.bind(this));
17715  this.hammer.on('panstart', this._onDragStart.bind(this));
17716  this.hammer.on('panmove', this._onDrag.bind(this));
17717  this.hammer.on('panend', this._onDragEnd.bind(this));
17718  this.hammer.get('pan').set({ threshold: 5, direction: Hammer.DIRECTION_HORIZONTAL });
17719
17720  // single select (or unselect) when tapping an item
17721  this.hammer.on('tap', this._onSelectItem.bind(this));
17722
17723  // multi select when holding mouse/touch, or on ctrl+click
17724  this.hammer.on('press', this._onMultiSelectItem.bind(this));
17725
17726  // add item on doubletap
17727  this.hammer.on('doubletap', this._onAddItem.bind(this));
17728
17729  if (this.options.rtl) {
17730    this.groupHammer = new Hammer(this.body.dom.rightContainer);
17731  } else {
17732    this.groupHammer = new Hammer(this.body.dom.leftContainer);
17733  }
17734
17735  this.groupHammer.on('tap', this._onGroupClick.bind(this));
17736  this.groupHammer.on('panstart', this._onGroupDragStart.bind(this));
17737  this.groupHammer.on('panmove', this._onGroupDrag.bind(this));
17738  this.groupHammer.on('panend', this._onGroupDragEnd.bind(this));
17739  this.groupHammer.get('pan').set({ threshold: 5, direction: Hammer.DIRECTION_VERTICAL });
17740
17741  this.body.dom.centerContainer.addEventListener('mouseover', this._onMouseOver.bind(this));
17742  this.body.dom.centerContainer.addEventListener('mouseout', this._onMouseOut.bind(this));
17743  this.body.dom.centerContainer.addEventListener('mousemove', this._onMouseMove.bind(this));
17744  // right-click on timeline 
17745  this.body.dom.centerContainer.addEventListener('contextmenu', this._onDragEnd.bind(this));
17746
17747  this.body.dom.centerContainer.addEventListener('mousewheel', this._onMouseWheel.bind(this));
17748
17749  // attach to the DOM
17750  this.show();
17751};
17752
17753/**
17754 * Set options for the ItemSet. Existing options will be extended/overwritten.
17755 * @param {Object} [options] The following options are available:
17756 *                           {string} type
17757 *                              Default type for the items. Choose from 'box'
17758 *                              (default), 'point', 'range', or 'background'.
17759 *                              The default style can be overwritten by
17760 *                              individual items.
17761 *                           {string} align
17762 *                              Alignment for the items, only applicable for
17763 *                              BoxItem. Choose 'center' (default), 'left', or
17764 *                              'right'.
17765 *                           {string} orientation.item
17766 *                              Orientation of the item set. Choose 'top' or
17767 *                              'bottom' (default).
17768 *                           {Function} groupOrder
17769 *                              A sorting function for ordering groups
17770 *                           {boolean} stack
17771 *                              If true (default), items will be stacked on
17772 *                              top of each other.
17773 *                           {number} margin.axis
17774 *                              Margin between the axis and the items in pixels.
17775 *                              Default is 20.
17776 *                           {number} margin.item.horizontal
17777 *                              Horizontal margin between items in pixels.
17778 *                              Default is 10.
17779 *                           {number} margin.item.vertical
17780 *                              Vertical Margin between items in pixels.
17781 *                              Default is 10.
17782 *                           {number} margin.item
17783 *                              Margin between items in pixels in both horizontal
17784 *                              and vertical direction. Default is 10.
17785 *                           {number} margin
17786 *                              Set margin for both axis and items in pixels.
17787 *                           {boolean} selectable
17788 *                              If true (default), items can be selected.
17789 *                           {boolean} multiselect
17790 *                              If true, multiple items can be selected.
17791 *                              False by default.
17792 *                           {boolean} editable
17793 *                              Set all editable options to true or false
17794 *                           {boolean} editable.updateTime
17795 *                              Allow dragging an item to an other moment in time
17796 *                           {boolean} editable.updateGroup
17797 *                              Allow dragging an item to an other group
17798 *                           {boolean} editable.add
17799 *                              Allow creating new items on double tap
17800 *                           {boolean} editable.remove
17801 *                              Allow removing items by clicking the delete button
17802 *                              top right of a selected item.
17803 *                           {Function(item: Item, callback: Function)} onAdd
17804 *                              Callback function triggered when an item is about to be added:
17805 *                              when the user double taps an empty space in the Timeline.
17806 *                           {Function(item: Item, callback: Function)} onUpdate
17807 *                              Callback function fired when an item is about to be updated.
17808 *                              This function typically has to show a dialog where the user
17809 *                              change the item. If not implemented, nothing happens.
17810 *                           {Function(item: Item, callback: Function)} onMove
17811 *                              Fired when an item has been moved. If not implemented,
17812 *                              the move action will be accepted.
17813 *                           {Function(item: Item, callback: Function)} onRemove
17814 *                              Fired when an item is about to be deleted.
17815 *                              If not implemented, the item will be always removed.
17816 */
17817ItemSet.prototype.setOptions = function (options) {
17818  if (options) {
17819    // copy all options that we know
17820    var fields = ['type', 'rtl', 'align', 'order', 'stack', 'stackSubgroups', 'selectable', 'multiselect', 'multiselectPerGroup', 'groupOrder', 'dataAttributes', 'template', 'groupTemplate', 'visibleFrameTemplate', 'hide', 'snap', 'groupOrderSwap', 'showTooltips', 'tooltip', 'tooltipOnItemUpdateTime'];
17821    util.selectiveExtend(fields, this.options, options);
17822
17823    if ('itemsAlwaysDraggable' in options) {
17824      if (typeof options.itemsAlwaysDraggable === 'boolean') {
17825        this.options.itemsAlwaysDraggable.item = options.itemsAlwaysDraggable;
17826        this.options.itemsAlwaysDraggable.range = false;
17827      } else if ((0, _typeof3['default'])(options.itemsAlwaysDraggable) === 'object') {
17828        util.selectiveExtend(['item', 'range'], this.options.itemsAlwaysDraggable, options.itemsAlwaysDraggable);
17829        // only allow range always draggable when item is always draggable as well
17830        if (!this.options.itemsAlwaysDraggable.item) {
17831          this.options.itemsAlwaysDraggable.range = false;
17832        }
17833      }
17834    }
17835
17836    if ('orientation' in options) {
17837      if (typeof options.orientation === 'string') {
17838        this.options.orientation.item = options.orientation === 'top' ? 'top' : 'bottom';
17839      } else if ((0, _typeof3['default'])(options.orientation) === 'object' && 'item' in options.orientation) {
17840        this.options.orientation.item = options.orientation.item;
17841      }
17842    }
17843
17844    if ('margin' in options) {
17845      if (typeof options.margin === 'number') {
17846        this.options.margin.axis = options.margin;
17847        this.options.margin.item.horizontal = options.margin;
17848        this.options.margin.item.vertical = options.margin;
17849      } else if ((0, _typeof3['default'])(options.margin) === 'object') {
17850        util.selectiveExtend(['axis'], this.options.margin, options.margin);
17851        if ('item' in options.margin) {
17852          if (typeof options.margin.item === 'number') {
17853            this.options.margin.item.horizontal = options.margin.item;
17854            this.options.margin.item.vertical = options.margin.item;
17855          } else if ((0, _typeof3['default'])(options.margin.item) === 'object') {
17856            util.selectiveExtend(['horizontal', 'vertical'], this.options.margin.item, options.margin.item);
17857          }
17858        }
17859      }
17860    }
17861
17862    if ('editable' in options) {
17863      if (typeof options.editable === 'boolean') {
17864        this.options.editable.updateTime = options.editable;
17865        this.options.editable.updateGroup = options.editable;
17866        this.options.editable.add = options.editable;
17867        this.options.editable.remove = options.editable;
17868        this.options.editable.overrideItems = false;
17869      } else if ((0, _typeof3['default'])(options.editable) === 'object') {
17870        util.selectiveExtend(['updateTime', 'updateGroup', 'add', 'remove', 'overrideItems'], this.options.editable, options.editable);
17871      }
17872    }
17873
17874    if ('groupEditable' in options) {
17875      if (typeof options.groupEditable === 'boolean') {
17876        this.options.groupEditable.order = options.groupEditable;
17877        this.options.groupEditable.add = options.groupEditable;
17878        this.options.groupEditable.remove = options.groupEditable;
17879      } else if ((0, _typeof3['default'])(options.groupEditable) === 'object') {
17880        util.selectiveExtend(['order', 'add', 'remove'], this.options.groupEditable, options.groupEditable);
17881      }
17882    }
17883
17884    // callback functions
17885    var addCallback = function (name) {
17886      var fn = options[name];
17887      if (fn) {
17888        if (!(fn instanceof Function)) {
17889          throw new Error('option ' + name + ' must be a function ' + n
17889ame + '(item, callback)');
17890        }
17891        this.options[name] = fn;
17892      }
17893    }.bind(this);
17894    ['onDropObjectOnItem', 'onAdd', 'onUpdate', 'onRemove', 'onMove', 'onMoving', 'onAddGroup', 'onMoveGroup', 'onRemoveGroup'].forEach(addCallback);
17895
17896    // force the itemSet to refresh: options like orientation and margins may be changed
17897    this.markDirty();
17898  }
17899};
17900
17901/**
17902 * Mark the ItemSet dirty so it will refresh everything with next redraw.
17903 * Optionally, all items can be marked as dirty and be refreshed.
17904 * @param {{refreshItems: boolean}} [options]
17905 */
17906ItemSet.prototype.markDirty = function (options) {
17907  this.groupIds = [];
17908
17909  if (options && options.refreshItems) {
17910    util.forEach(this.items, function (item) {
17911      item.dirty = true;
17912      if (item.displayed) item.redraw();
17913    });
17914  }
17915};
17916
17917/**
17918 * Destroy the ItemSet
17919 */
17920ItemSet.prototype.destroy = function () {
17921  this.hide();
17922  this.setItems(null);
17923  this.setGroups(null);
17924
17925  this.hammer = null;
17926
17927  this.body = null;
17928  this.conversion = null;
17929};
17930
17931/**
17932 * Hide the component from the DOM
17933 */
17934ItemSet.prototype.hide = function () {
17935  // remove the frame containing the items
17936  if (this.dom.frame.parentNode) {
17937    this.dom.frame.parentNode.removeChild(this.dom.frame);
17938  }
17939
17940  // remove the axis with dots
17941  if (this.dom.axis.parentNode) {
17942    this.dom.axis.parentNode.removeChild(this.dom.axis);
17943  }
17944
17945  // remove the labelset containing all group labels
17946  if (this.dom.labelSet.parentNode) {
17947    this.dom.labelSet.parentNode.removeChild(this.dom.labelSet);
17948  }
17949};
17950
17951/**
17952 * Show the component in the DOM (when not already visible).
17953 */
17954ItemSet.prototype.show = function () {
17955  // show frame containing the items
17956  if (!this.dom.frame.parentNode) {
17957    this.body.dom.center.appendChild(this.dom.frame);
17958  }
17959
17960  // show axis with dots
17961  if (!this.dom.axis.parentNode) {
17962    this.body.dom.backgroundVertical.appendChild(this.dom.axis);
17963  }
17964
17965  // show labelset containing labels
17966  if (!this.dom.labelSet.parentNode) {
17967    if (this.options.rtl) {
17968      this.body.dom.right.appendChild(this.dom.labelSet);
17969    } else {
17970      this.body.dom.left.appendChild(this.dom.labelSet);
17971    }
17972  }
17973};
17974
17975/**
17976 * Set selected items by their id. Replaces the current selection
17977 * Unknown id's are silently ignored.
17978 * @param {string[] | string} [ids] An array with zero or more id's of the items to be
17979 *                                  selected, or a single item id. If ids is undefined
17980 *                                  or an empty array, all items will be unselected.
17981 */
17982ItemSet.prototype.setSelection = function (ids) {
17983  var i, ii, id, item;
17984
17985  if (ids == undefined) ids = [];
17986  if (!Array.isArray(ids)) ids = [ids];
17987
17988  // unselect currently selected items
17989  for (i = 0, ii = this.selection.length; i < ii; i++) {
17990    id = this.selection[i];
17991    item = this.items[id];
17992    if (item) item.unselect();
17993  }
17994
17995  // select items
17996  this.selection = [];
17997  for (i = 0, ii = ids.length; i < ii; i++) {
17998    id = ids[i];
17999    item = this.items[id];
18000    if (item) {
18001      this.selection.push(id);
18002      item.select();
18003    }
18004  }
18005};
18006
18007/**
18008 * Get the selected items by their id
18009 * @return {Array} ids  The ids of the selected items
18010 */
18011ItemSet.prototype.getSelection = function () {
18012  return this.selection.concat([]);
18013};
18014
18015/**
18016 * Get the id's of the currently visible items.
18017 * @returns {Array} The ids of the visible items
18018 */
18019ItemSet.prototype.getVisibleItems = function () {
18020  var range = this.body.range.getRange();
18021  var right, left;
18022
18023  if (this.options.rtl) {
18024    right = this.body.util.toScreen(range.start);
18025    left = this.body.util.toScreen(range.end);
18026  } else {
18027    left = this.body.util.toScreen(range.start);
18028    right = this.body.util.toScreen(range.end);
18029  }
18030
18031  var ids = [];
18032  for (var groupId in this.groups) {
18033    if (this.groups.hasOwnProperty(groupId)) {
18034      var group = this.groups[groupId];
18035      var rawVisibleItems = group.isVisible ? group.visibleItems : [];
18036
18037      // filter the "raw" set with visibleItems into a set which is really
18038      // visible by pixels
18039      for (var i = 0; i < rawVisibleItems.length; i++) {
18040        var item = rawVisibleItems[i];
18041        // TODO: also check whether visible vertically
18042        if (this.options.rtl) {
18043          if (item.right < left && item.right + item.width > right) {
18044            ids.push(item.id);
18045          }
18046        } else {
18047          if (item.left < right && item.left + item.width > left) {
18048            ids.push(item.id);
18049          }
18050        }
18051      }
18052    }
18053  }
18054
18055  return ids;
18056};
18057
18058/**
18059 * Deselect a selected item
18060 * @param {string | number} id
18061 * @private
18062 */
18063ItemSet.prototype._deselect = function (id) {
18064  var selection = this.selection;
18065  for (var i = 0, ii = selection.length; i < ii; i++) {
18066    if (selection[i] == id) {
18067      // non-strict comparison!
18068      selection.splice(i, 1);
18069      break;
18070    }
18071  }
18072};
18073
18074/**
18075 * Repaint the component
18076 * @return {boolean} Returns true if the component is resized
18077 */
18078ItemSet.prototype.redraw = function () {
18079  var margin = this.options.margin,
18080      range = this.body.range,
18081      asSize = util.option.asSize,
18082      options = this.options,
18083      orientation = options.orientation.item,
18084      resized = false,
18085      frame = this.dom.frame;
18086
18087  // recalculate absolute position (before redrawing groups)
18088  this.props.top = this.body.domProps.top.height + this.body.domProps.border.top;
18089
18090  if (this.options.rtl) {
18091    this.props.right = this.body.domProps.right.width + this.body.domProps.border.right;
18092  } else {
18093    this.props.left = this.body.domProps.left.width + this.body.domProps.border.left;
18094  }
18095
18096  // update class name
18097  frame.className = 'vis-itemset';
18098
18099  // reorder the groups (if needed)
18100  resized = this._orderGroups() || resized;
18101
18102  // check whether zoomed (in that case we need to re-stack everything)
18103  // TODO: would be nicer to get this as a trigger from Range
18104  var visibleInterval = range.end - range.start;
18105  var zoomed = visibleInterval != this.lastVisibleInterval || this.props.width != this.props.lastWidth;
18106  var scrolled = range.start != this.lastRangeStart;
18107  var changedStackOption = options.stack != this.lastStack;
18108  var changedStackSubgroupsOption = options.stackSubgroups != this.lastStackSubgroups;
18109  var forceRestack = zoomed || scrolled || changedStackOption || changedStackSubgroupsOption;
18110  this.lastVisibleInterval = visibleInterval;
18111  this.lastRangeStart = range.start;
18112  this.lastStack = options.stack;
18113  this.lastStackSubgroups = options.stackSubgroups;
18114
18115  this.props.lastWidth = this.props.width;
18116
18117  var firstGroup = this._firstGroup();
18118  var firstMargin = {
18119    item: margin.item,
18120    axis: margin.axis
18121  };
18122  var nonFirstMargin = {
18123    item: margin.item,
18124    axis: margin.item.vertical / 2
18125  };
18126  var height = 0;
18127  var minHeight = margin.axis + margin.item.vertical;
18128
18129  // redraw the background group
18130  this.groups[BACKGROUND].redraw(range, nonFirstMargin, forceRestack);
18131
18132  var redrawQueue = {};
18133  var redrawQueueLength = 0;
18134
18135  // collect redraw functions
18136  util.forEach(this.groups, function (group, key) {
18137    if (key === BACKGROUND) return;
18138    var groupMargin = group == firstGroup ? firstMargin : nonFirstMargin;
18139    var returnQueue = true;
18140    redrawQueue[key] = group.redraw(range, groupMargin, forceRestack, returnQueue);
18141    redrawQueueLength = redrawQueue[key].length;
18142  });
18143
18144  var needRedraw = redrawQueueLength > 0;
18145  if (needRedraw) {
18146    var redrawResults = {};
18147
18148    for (var i = 0; i < redrawQueueLength; i++) {
18149      util.forEach(redrawQueue, function (fns, key) {
18150        redrawResults[key] = fns[i]();
18151      });
18152    }
18153
18154    // redraw all regular groups
18155    util.forEach(this.groups, function (group, key) {
18156      if (key === BACKGROUND) return;
18157      var groupResized = redrawResults[key];
18158      resized = groupResized || resized;
18159      height += group.height;
18160    });
18161    height = Math.max(height, minHeight);
18162  }
18163
18164  height = Math.max(height, minHeight);
18165
18166  // update frame height
18167  frame.style.height = asSize(height);
18168
18169  // calculate actual size
18170  this.props.width = frame.offsetWidth;
18171  this.props.height = height;
18172
18173  // reposition axis
18174  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);
18175  if (this.options.rtl) {
18176    this.dom.axis.style.right = '0';
18177  } else {
18178    this.dom.axis.style.left = '0';
18179  }
18180
18181  this.initialItemSetDrawn = true;
18182  // check if this component is resized
18183  resized = this._isResized() || resized;
18184
18185  return resized;
18186};
18187
18188/**
18189 * Get the first group, aligned with the axis
18190 * @return {Group | null} firstGroup
18191 * @private
18192 */
18193ItemSet.prototype._firstGroup = function () {
18194  var firstGroupIndex = this.options.orientation.item == 'top' ? 0 : this.groupIds.length - 1;
18195  var firstGroupId = this.groupIds[firstGroupIndex];
18196  var firstGroup = this.groups[firstGroupId] || this.groups[UNGROUPED];
18197
18198  return firstGroup || null;
18199};
18200
18201/**
18202 * Create or delete the group holding all ungrouped items. This group is used when
18203 * there are no groups specified.
18204 * @protected
18205 */
18206ItemSet.prototype._updateUngrouped = function () {
18207  var ungrouped = this.groups[UNGROUPED];
18208  var item, itemId;
18209
18210  if (this.groupsData) {
18211    // remove the group holding all ungrouped items
18212    if (ungrouped) {
18213      ungrouped.hide();
18214      delete this.groups[UNGROUPED];
18215
18216      for (itemId in this.items) {
18217        if (this.items.hasOwnProperty(itemId)) {
18218          item = this.items[itemId];
18219          item.parent && item.parent.remove(item);
18220          var groupId = this._getGroupId(item.data);
18221          var group = this.groups[groupId];
18222          group && group.add(item) || item.hide();
18223        }
18224      }
18225    }
18226  } else {
18227    // create a group holding all (unfiltered) items
18228    if (!ungrouped) {
18229      var id = null;
18230      var data = null;
18231      ungrouped = new Group(id, data, this);
18232      this.groups[UNGROUPED] = ungrouped;
18233
18234      for (itemId in this.items) {
18235        if (this.items.hasOwnProperty(itemId)) {
18236          item = this.items[itemId];
18237          ungrouped.add(item);
18238        }
18239      }
18240
18241      ungrouped.show();
18242    }
18243  }
18244};
18245
18246/**
18247 * Get the element for the labelset
18248 * @return {HTMLElement} labelSet
18249 */
18250ItemSet.prototype.getLabelSet = function () {
18251  return this.dom.labelSet;
18252};
18253
18254/**
18255 * Set items
18256 * @param {vis.DataSet | null} items
18257 */
18258ItemSet.prototype.setItems = function (items) {
18259  var me = this,
18260      ids,
18261      oldItemsData = this.itemsData;
18262
18263  // replace the dataset
18264  if (!items) {
18265    this.itemsData = null;
18266  } else if (items instanceof DataSet || items instanceof DataView) {
18267    this.itemsData = items;
18268  } else {
18269    throw new TypeError('Data must be an instance of DataSet or DataView');
18270  }
18271
18272  if (oldItemsData) {
18273    // unsubscribe from old dataset
18274    util.forEach(this.itemListeners, function (callback, event) {
18275      oldItemsData.off(event, callback);
18276    });
18277
18278    // remove all drawn items
18279    ids = oldItemsData.getIds();
18280    this._onRemove(ids);
18281  }
18282
18283  if (this.itemsData) {
18284    // subscribe to new dataset
18285    var id = this.id;
18286    util.forEach(this.itemListeners, function (callback, event) {
18287      me.itemsData.on(event, callback, id);
18288    });
18289
18290    // add all new items
18291    ids = this.itemsData.getIds();
18292    this._onAdd(ids);
18293
18294    // update the group holding all ungrouped items
18295    this._updateUngrouped();
18296  }
18297
18298  this.body.emitter.emit('_change', { queue: true });
18299};
18300
18301/**
18302 * Get the current items
18303 * @returns {vis.DataSet | null}
18304 */
18305ItemSet.prototype.getItems = function () {
18306  return this.itemsData;
18307};
18308
18309/**
18310 * Set groups
18311 * @param {vis.DataSet} groups
18312 */
18313ItemSet.prototype.setGroups = function (groups) {
18314  var me = this,
18315      ids;
18316
18317  // unsubscribe from current dataset
18318  if (this.groupsData) {
18319    util.forEach(this.groupListeners, function (callback, event) {
18320      me.groupsData.off(event, callback);
18321    });
18322
18323    // remove all drawn groups
18324    ids = this.groupsData.getIds();
18325    this.groupsData = null;
18326    this._onRemoveGroups(ids); // note: this will cause a redraw
18327  }
18328
18329  // replace the dataset
18330  if (!groups) {
18331    this.groupsData = null;
18332  } else if (groups instanceof DataSet || groups instanceof DataView) {
18333    this.groupsData = groups;
18334  } else {
18335    throw new TypeError('Data must be an instance of DataSet or DataView');
18336  }
18337
18338  if (this.groupsData) {
18339    // go over all groups nesting
18340    var groupsData = this.groupsData;
18341    if (this.groupsData instanceof DataView) {
18342      groupsData = this.groupsData.getDataSet();
18343    }
18344
18345    groupsData.get().forEach(function (group) {
18346      if (group.nestedGroups) {
18347        group.nestedGroups.forEach(function (nestedGroupId) {
18348          var updatedNestedGroup = groupsData.get(nestedGroupId);
18349          updatedNestedGroup.nestedInGroup = group.id;
18350          if (group.showNested == false) {
18351            updatedNestedGroup.visible = false;
18352          }
18353          groupsData.update(updatedNestedGroup);
18354        });
18355      }
18356    });
18357
18358    // subscribe to new dataset
18359    var id = this.id;
18360    util.forEach(this.groupListeners, function (callback, event) {
18361      me.groupsData.on(event, callback, id);
18362    });
18363
18364    // draw all ms
18365    ids = this.groupsData.getIds();
18366    this._onAddGroups(ids);
18367  }
18368
18369  // update the group holding all ungrouped items
18370  this._updateUngrouped();
18371
18372  // update the order of all items in each group
18373  this._order();
18374
18375  this.body.emitter.emit('_change', { queue: true });
18376};
18377
18378/**
18379 * Get the current groups
18380 * @returns {vis.DataSet | null} groups
18381 */
18382ItemSet.prototype.getGroups = function () {
18383  return this.groupsData;
18384};
18385
18386/**
18387 * Remove an item by its id
18388 * @param {string | number} id
18389 */
18390ItemSet.prototype.removeItem = function (id) {
18391  var item = this.itemsData.get(id),
18392      dataset = this.itemsData.getDataSet();
18393
18394  if (item) {
18395    // confirm deletion
18396    this.options.onRemove(item, function (item) {
18397      if (item) {
18398        // remove by id here, it is possible that an item has no id defined
18399        // itself, so better not delete by the item itself
18400        dataset.remove(id);
18401      }
18402    });
18403  }
18404};
18405
18406/**
18407 * Get the time of an item based on it's data and options.type
18408 * @param {Object} itemData
18409 * @returns {string} Returns the type
18410 * @private
18411 */
18412ItemSet.prototype._getType = function (itemData) {
18413  return itemData.type || this.options.type || (itemData.end ? 'range' : 'box');
18414};
18415
18416/**
18417 * Get the group id for an item
18418 * @param {Object} itemData
18419 * @returns {string} Returns the groupId
18420 * @private
18421 */
18422ItemSet.prototype._getGroupId = function (itemData) {
18423  var type = this._getType(itemData);
18424  if (type == 'background' && itemData.group == undefined) {
18425    return BACKGROUND;
18426  } else {
18427    return this.groupsData ? itemData.group : UNGROUPED;
18428  }
18429};
18430
18431/**
18432 * Handle updated items
18433 * @param {number[]} ids
18434 * @protected
18435 */
18436ItemSet.prototype._onUpdate = function (ids) {
18437  var me = this;
18438
18439  ids.forEach(function (id) {
18440    var itemData = me.itemsData.get(id, me.itemOptions);
18441    var item = me.items[id];
18442    var type = itemData ? me._getType(itemData) : null;
18443
18444    var constructor = ItemSet.types[type];
18445    var selected;
18446
18447    if (item) {
18448      // update item   	
18449      if (!constructor || !(item instanceof constructor)) {
18450        // item type has changed, delete the item and recreate it
18451        selected = item.selected; // preserve selection of this item
18452        me._removeItem(item);
18453        item = null;
18454      } else {
18455        me._updateItem(item, itemData);
18456      }
18457    }
18458
18459    if (!item && itemData) {
18460      // create item
18461      if (constructor) {
18462        item = new constructor(itemData, me.conversion, me.options);
18463        item.id = id; // TODO: not so nice setting id afterwards
18464
18465        me._addItem(item);
18466        if (selected) {
18467          this.selection.push(id);
18468          item.select();
18469        }
18470      } else if (type == 'rangeoverflow') {
18471        // TODO: deprecated since version 2.1.0 (or 3.0.0?). cleanup some day
18472        throw new TypeError('Item type "rangeoverflow" is deprecated. Use css styling instead: ' + '.vis-item.vis-range .vis-item-content {overflow: visible;}');
18473      } else {
18474        throw new TypeError('Unknown item type "' + type + '"');
18475      }
18476    }
18477  }.bind(this));
18478
18479  this._order();
18480  this.body.emitter.emit('_change', { queue: true });
18481};
18482
18483/**
18484 * Handle added items
18485 * @param {number[]} ids
18486 * @protected
18487 */
18488ItemSet.prototype._onAdd = ItemSet.prototype._onUpdate;
18489
18490/**
18491 * Handle removed items
18492 * @param {number[]} ids
18493 * @protected
18494 */
18495ItemSet.prototype._onRemove = function (ids) {
18496  var count = 0;
18497  var me = this;
18498  ids.forEach(function (id) {
18499    var item = me.items[id];
18500    if (item) {
18501      count++;
18502      me._removeItem(item);
18503    }
18504  });
18505
18506  if (count) {
18507    // update order
18508    this._order();
18509    this.body.emitter.emit('_change', { queue: true });
18510  }
18511};
18512
18513/**
18514 * Update the order of item in all groups
18515 * @private
18516 */
18517ItemSet.prototype._order = function () {
18518  // reorder the items in all groups
18519  // TODO: optimization: only reorder groups affected by the changed items
18520  util.forEach(this.groups, function (group) {
18521    group.order();
18522  });
18523};
18524
18525/**
18526 * Handle updated groups
18527 * @param {number[]} ids
18528 * @private
18529 */
18530ItemSet.prototype._onUpdateGroups = function (ids) {
18531  this._onAddGroups(ids);
18532};
18533
18534/**
18535 * Handle changed groups (added or updated)
18536 * @param {number[]} ids
18537 * @private
18538 */
18539ItemSet.prototype._onAddGroups = function (ids) {
18540  var me = this;
18541
18542  ids.forEach(function (id) {
18543    var groupData = me.groupsData.get(id);
18544    var group = me.groups[id];
18545
18546    if (!group) {
18547      // check for reserved ids
18548      if (id == UNGROUPED || id == BACKGROUND) {
18549        throw new Error('Illegal group id. ' + id + ' is a reserved id.');
18550      }
18551
18552      var groupOptions = (0, _create2['default'])(me.options);
18553      util.extend(groupOptions, {
18554        height: null
18555      });
18556
18557      group = new Group(id, groupData, me);
18558      me.groups[id] = group;
18559
18560      // add items with this groupId to the new group
18561      for (var itemId in me.items) {
18562        if (me.items.hasOwnProperty(itemId)) {
18563          var item = me.items[itemId];
18564          if (item.data.group == id) {
18565            group.add(item);
18566          }
18567        }
18568      }
18569
18570      group.order();
18571      group.show();
18572    } else {
18573      // update group
18574      group.setData(groupData);
18575    }
18576  });
18577
18578  this.body.emitter.emit('_change', { queue: true });
18579};
18580
18581/**
18582 * Handle removed groups
18583 * @param {number[]} ids
18584 * @private
18585 */
18586ItemSet.prototype._onRemoveGroups = function (ids) {
18587  var groups = this.groups;
18588  ids.forEach(function (id) {
18589    var group = groups[id];
18590
18591    if (group) {
18592      group.hide();
18593      delete groups[id];
18594    }
18595  });
18596
18597  this.markDirty();
18598
18599  this.body.emitter.emit('_change', { queue: true });
18600};
18601
18602/**
18603 * Reorder the groups if needed
18604 * @return {boolean} changed
18605 * @private
18606 */
18607ItemSet.prototype._orderGroups = function () {
18608  if (this.groupsData) {
18609    // reorder the groups
18610    var groupIds = this.groupsData.getIds({
18611      order: this.options.groupOrder
18612    });
18613
18614    groupIds = this._orderNestedGroups(groupIds);
18615
18616    var changed = !util.equalArray(groupIds, this.groupIds);
18617    if (changed) {
18618      // hide all groups, removes them from the DOM
18619      var groups = this.groups;
18620      groupIds.forEach(function (groupId) {
18621        groups[groupId].hide();
18622      });
18623
18624      // show the groups again, attach them to the DOM in correct order
18625      groupIds.forEach(function (groupId) {
18626        groups[groupId].show();
18627      });
18628
18629      this.groupIds = groupIds;
18630    }
18631
18632    return changed;
18633  } else {
18634    return false;
18635  }
18636};
18637
18638/**
18639 * Reorder the nested groups
18640 *
18641 * @param {Array.<number>} groupIds
18642 * @returns {Array.<number>}
18643 * @private
18644 */
18645ItemSet.prototype._orderNestedGroups = function (groupIds) {
18646  var newGroupIdsOrder = [];
18647
18648  groupIds.forEach(function (groupId) {
18649    var groupData = this.groupsData.get(groupId);
18650    if (!groupData.nestedInGroup) {
18651      newGroupIdsOrder.push(groupId);
18652    }
18653    if (groupData.nestedGroups) {
18654      var nestedGroups = this.groupsData.get({
18655        filter: function filter(nestedGroup) {
18656          return nestedGroup.nestedInGroup == groupId;
18657        },
18658        order: this.options.groupOrder
18659      });
18660      var nestedGroupIds = nestedGroups.map(function (nestedGroup) {
18661        return nestedGroup.id;
18662      });
18663      newGroupIdsOrder = newGroupIdsOrder.concat(nestedGroupIds);
18664    }
18665  }, this);
18666  return newGroupIdsOrder;
18667};
18668
18669/**
18670 * Add a new item
18671 * @param {Item} item
18672 * @private
18673 */
18674ItemSet.prototype._addItem = function (item) {
18675  this.items[item.id] = item;
18676
18677  // add to group
18678  var groupId = this._getGroupId(item.data);
18679  var group = this.groups[groupId];
18680
18681  if (!group) {
18682    item.groupShowing = false;
18683  } else if (group && group.data && group.data.showNested) {
18684    item.groupShowing = true;
18685  }
18686
18687  if (group) group.add(item);
18688};
18689
18690/**
18691 * Update an existing item
18692 * @param {Item} item
18693 * @param {Object} itemData
18694 * @private
18695 */
18696ItemSet.prototype._updateItem = function (item, itemData) {
18697  // update the items data (will redraw the item when displayed)
18698  item.setData(itemData);
18699
18700  var groupId = this._getGroupId(item.data);
18701  var group = this.groups[groupId];
18702  if (!group) {
18703    item.groupShowing = false;
18704  } else if (group && group.data && group.data.showNested) {
18705    item.groupShowing = true;
18706  }
18707};
18708
18709/**
18710 * Delete an item from the ItemSet: remove it from the DOM, from the map
18711 * with items, and from the map with visible items, and from the selection
18712 * @param {Item} item
18713 * @private
18714 */
18715ItemSet.prototype._removeItem = function (item) {
18716  // remove from DOM
18717  item.hide();
18718
18719  // remove from items
18720  delete this.items[item.id];
18721
18722  // remove from selection
18723  var index = this.selection.indexOf(item.id);
18724  if (index != -1) this.selection.splice(index, 1);
18725
18726  // remove from group
18727  item.parent && item.parent.remove(item);
18728};
18729
18730/**
18731 * Create an array containing all items being a range (having an end date)
18732 * @param {Array.<Object>} array
18733 * @returns {Array}
18734 * @private
18735 */
18736ItemSet.prototype._constructByEndArray = function (array) {
18737  var endArray = [];
18738
18739  for (var i = 0; i < array.length; i++) {
18740    if (array[i] instanceof RangeItem) {
18741      endArray.push(array[i]);
18742    }
18743  }
18744  return endArray;
18745};
18746
18747/**
18748 * Register the clicked item on touch, before dragStart is initiated.
18749 *
18750 * dragStart is initiated from a mousemove event, AFTER the mouse/touch is
18751 * already moving. Therefore, the mouse/touch can sometimes be above an other
18752 * DOM element than the item itself.
18753 *
18754 * @param {Event} event
18755 * @private
18756 */
18757ItemSet.prototype._onTouch = function (event) {
18758  // store the touched item, used in _onDragStart
18759  this.touchParams.item = this.itemFromTarget(event);
18760  this.touchParams.dragLeftItem = event.target.dragLeftItem || false;
18761  this.touchParams.dragRightItem = event.target.dragRightItem || false;
18762  this.touchParams.itemProps = null;
18763};
18764
18765/**
18766 * Given an group id, returns the index it has.
18767 *
18768 * @param {number} groupId
18769 * @returns {number} index / groupId
18770 * @private
18771 */
18772ItemSet.prototype._getGroupIndex = function (groupId) {
18773  for (var i = 0; i < this.groupIds.length; i++) {
18774    if (groupId == this.groupIds[i]) return i;
18775  }
18776};
18777
18778/**
18779 * Start dragging the selected events
18780 * @param {Event} event
18781 * @private
18782 */
18783ItemSet.prototype._onDragStart = function (event) {
18784  if (this.touchParams.itemIsDragging) {
18785    return;
18786  }
18787  var item = this.touchParams.item || null;
18788  var me = this;
18789  var props;
18790
18791  if (item && (item.selected || this.options.itemsAlwaysDraggable.item)) {
18792
18793    if (this.options.editable.overrideItems && !this.options.editable.updateTime && !this.options.editable.updateGroup) {
18794      return;
18795    }
18796
18797    // override options.editable
18798    if (item.editable != null && !item.editable.updateTime && !item.editable.updateGroup && !this.options.editable.overrideItems) {
18799      return;
18800    }
18801
18802    var dragLeftItem = this.touchParams.dragLeftItem;
18803    var dragRightItem = this.touchParams.dragRightItem;
18804    this.touchParams.itemIsDragging = true;
18805    this.touchParams.selectedItem = item;
18806
18807    if (dragLeftItem) {
18808      props = {
18809        item: dragLeftItem,
18810        initialX: event.center.x,
18811        dragLeft: true,
18812        data: this._cloneItemData(item.data)
18813      };
18814
18815      this.touchParams.itemProps = [props];
18816    } else if (dragRightItem) {
18817      props = {
18818        item: dragRightItem,
18819        initialX: event.center.x,
18820        dragRight: true,
18821        data: this._cloneItemData(item.data)
18822      };
18823
18824      this.touchParams.itemProps = [props];
18825    } else if (this.options.editable.add && (event.srcEvent.ctrlKey || event.srcEvent.metaKey)) {
18826      // create a new range item when dragging with ctrl key down
18827      this._onDragStartAddItem(event);
18828    } else {
18829      if (this.groupIds.length < 1) {
18830        // Mitigates a race condition if _onDragStart() is
18831        // called after markDirty() without redraw() being called between.
18832        this.redraw();
18833      }
18834
18835      var baseGroupIndex = this._getGroupIndex(item.data.group);
18836
18837      var itemsToDrag = this.options.itemsAlwaysDraggable.item && !item.selected ? [item.id] : this.get
18837Selection();
18838
18839      this.touchParams.itemProps = itemsToDrag.map(function (id) {
18840        var item = me.items[id];
18841        var groupIndex = me._getGroupIndex(item.data.group);
18842        return {
18843          item: item,
18844          initialX: event.center.x,
18845          groupOffset: baseGroupIndex - groupIndex,
18846          data: this._cloneItemData(item.data)
18847        };
18848      }.bind(this));
18849    }
18850
18851    event.stopPropagation();
18852  } else if (this.options.editable.add && (event.srcEvent.ctrlKey || event.srcEvent.metaKey)) {
18853    // create a new range item when dragging with ctrl key down
18854    this._onDragStartAddItem(event);
18855  }
18856};
18857
18858/**
18859 * Start creating a new range item by dragging.
18860 * @param {Event} event
18861 * @private
18862 */
18863ItemSet.prototype._onDragStartAddItem = function (event) {
18864  var xAbs;
18865  var x;
18866  var snap = this.options.snap || null;
18867
18868  if (this.options.rtl) {
18869    xAbs = util.getAbsoluteRight(this.dom.frame);
18870    x = xAbs - event.center.x + 10; // plus 10 to compensate for the drag starting as soon as you've moved 10px
18871  } else {
18872    xAbs = util.getAbsoluteLeft(this.dom.frame);
18873    x = event.center.x - xAbs - 10; // minus 10 to compensate for the drag starting as soon as you've moved 10px
18874  }
18875
18876  var time = this.body.util.toTime(x);
18877  var scale = this.body.util.getScale();
18878  var step = this.body.util.getStep();
18879  var start = snap ? snap(time, scale, step) : time;
18880  var end = start;
18881
18882  var itemData = {
18883    type: 'range',
18884    start: start,
18885    end: end,
18886    content: 'new item'
18887  };
18888
18889  var id = util.randomUUID();
18890  itemData[this.itemsData._fieldId] = id;
18891
18892  var group = this.groupFromTarget(event);
18893  if (group) {
18894    itemData.group = group.groupId;
18895  }
18896  var newItem = new RangeItem(itemData, this.conversion, this.options);
18897  newItem.id = id; // TODO: not so nice setting id afterwards
18898  newItem.data = this._cloneItemData(itemData);
18899  this._addItem(newItem);
18900  this.touchParams.selectedItem = newItem;
18901
18902  var props = {
18903    item: newItem,
18904    initialX: event.center.x,
18905    data: newItem.data
18906  };
18907
18908  if (this.options.rtl) {
18909    props.dragLeft = true;
18910  } else {
18911    props.dragRight = true;
18912  }
18913  this.touchParams.itemProps = [props];
18914
18915  event.stopPropagation();
18916};
18917
18918/**
18919 * Drag selected items
18920 * @param {Event} event
18921 * @private
18922 */
18923ItemSet.prototype._onDrag = function (event) {
18924  if (this.touchParams.itemProps) {
18925    event.stopPropagation();
18926
18927    var me = this;
18928    var snap = this.options.snap || null;
18929    var xOffset;
18930
18931    if (this.options.rtl) {
18932      xOffset = this.body.dom.root.offsetLeft + this.body.domProps.right.width;
18933    } else {
18934      xOffset = this.body.dom.root.offsetLeft + this.body.domProps.left.width;
18935    }
18936
18937    var scale = this.body.util.getScale();
18938    var step = this.body.util.getStep();
18939
18940    //only calculate the new group for the item that's actually dragged
18941    var selectedItem = this.touchParams.selectedItem;
18942    var updateGroupAllowed = (this.options.editable.overrideItems || selectedItem.editable == null) && this.options.editable.updateGroup || !this.options.editable.overrideItems && selectedItem.editable != null && selectedItem.editable.updateGroup;
18943    var newGroupBase = null;
18944    if (updateGroupAllowed && selectedItem) {
18945      if (selectedItem.data.group != undefined) {
18946        // drag from one group to another
18947        var group = me.groupFromTarget(event);
18948        if (group) {
18949          //we know the offset for all items, so the new group for all items
18950          //will be relative to this one.
18951          newGroupBase = this._getGroupIndex(group.groupId);
18952        }
18953      }
18954    }
18955
18956    // move
18957    this.touchParams.itemProps.forEach(function (props) {
18958      var current = me.body.util.toTime(event.center.x - xOffset);
18959      var initial = me.body.util.toTime(props.initialX - xOffset);
18960      var offset;
18961      var initialStart;
18962      var initialEnd;
18963      var start;
18964      var end;
18965
18966      if (this.options.rtl) {
18967        offset = -(current - initial); // ms
18968      } else {
18969        offset = current - initial; // ms
18970      }
18971
18972      var itemData = this._cloneItemData(props.item.data); // clone the data
18973      if (props.item.editable != null && !props.item.editable.updateTime && !props.item.editable.updateGroup && !me.options.editable.overrideItems) {
18974        return;
18975      }
18976
18977      var updateTimeAllowed = (this.options.editable.overrideItems || selectedItem.editable == null) && this.options.editable.updateTime || !this.options.editable.overrideItems && selectedItem.editable != null && selectedItem.editable.updateTime;
18978      if (updateTimeAllowed) {
18979        if (props.dragLeft) {
18980          // drag left side of a range item
18981          if (this.options.rtl) {
18982            if (itemData.end != undefined) {
18983              initialEnd = util.convert(props.data.end, 'Date');
18984              end = new Date(initialEnd.valueOf() + offset);
18985              // TODO: pass a Moment instead of a Date to snap(). (Breaking change)
18986              itemData.end = snap ? snap(end, scale, step) : end;
18987            }
18988          } else {
18989            if (itemData.start != undefined) {
18990              initialStart = util.convert(props.data.start, 'Date');
18991              start = new Date(initialStart.valueOf() + offset);
18992              // TODO: pass a Moment instead of a Date to snap(). (Breaking change)
18993              itemData.start = snap ? snap(start, scale, step) : start;
18994            }
18995          }
18996        } else if (props.dragRight) {
18997          // drag right side of a range item
18998          if (this.options.rtl) {
18999            if (itemData.start != undefined) {
19000              initialStart = util.convert(props.data.start, 'Date');
19001              start = new Date(initialStart.valueOf() + offset);
19002              // TODO: pass a Moment instead of a Date to snap(). (Breaking change)
19003              itemData.start = snap ? snap(start, scale, step) : start;
19004            }
19005          } else {
19006            if (itemData.end != undefined) {
19007              initialEnd = util.convert(props.data.end, 'Date');
19008              end = new Date(initialEnd.valueOf() + offset);
19009              // TODO: pass a Moment instead of a Date to snap(). (Breaking change)
19010              itemData.end = snap ? snap(end, scale, step) : end;
19011            }
19012          }
19013        } else {
19014          // drag both start and end
19015          if (itemData.start != undefined) {
19016
19017            initialStart = util.convert(props.data.start, 'Date').valueOf();
19018            start = new Date(initialStart + offset);
19019
19020            if (itemData.end != undefined) {
19021              initialEnd = util.convert(props.data.end, 'Date');
19022              var duration = initialEnd.valueOf() - initialStart.valueOf();
19023
19024              // TODO: pass a Moment instead of a Date to snap(). (Breaking change)
19025              itemData.start = snap ? snap(start, scale, step) : start;
19026              itemData.end = new Date(itemData.start.valueOf() + duration);
19027            } else {
19028              // TODO: pass a Moment instead of a Date to snap(). (Breaking change)
19029              itemData.start = snap ? snap(start, scale, step) : start;
19030            }
19031          }
19032        }
19033      }
19034
19035      if (updateGroupAllowed && !props.dragLeft && !props.dragRight && newGroupBase != null) {
19036        if (itemData.group != undefined) {
19037          var newOffset = newGroupBase - props.groupOffset;
19038
19039          //make sure we stay in bounds
19040          newOffset = Math.max(0, newOffset);
19041          newOffset = Math.min(me.groupIds.length - 1, newOffset);
19042          itemData.group = me.groupIds[newOffset];
19043        }
19044      }
19045
19046      // confirm moving the item
19047      itemData = this._cloneItemData(itemData); // convert start and end to the correct type
19048      me.options.onMoving(itemData, function (itemData) {
19049        if (itemData) {
19050          props.item.setData(this._cloneItemData(itemData, 'Date'));
19051        }
19052      }.bind(this));
19053    }.bind(this));
19054
19055    this.body.emitter.emit('_change');
19056  }
19057};
19058
19059/**
19060 * Move an item to another group
19061 * @param {Item} item
19062 * @param {string | number} groupId
19063 * @private
19064 */
19065ItemSet.prototype._moveToGroup = function (item, groupId) {
19066  var group = this.groups[groupId];
19067  if (group && group.groupId != item.data.group) {
19068    var oldGroup = item.parent;
19069    oldGroup.remove(item);
19070    oldGroup.order();
19071
19072    item.data.group = group.groupId;
19073
19074    group.add(item);
19075    group.order();
19076  }
19077};
19078
19079/**
19080 * End of dragging selected items
19081 * @param {Event} event
19082 * @private
19083 */
19084ItemSet.prototype._onDragEnd = function (event) {
19085  this.touchParams.itemIsDragging = false;
19086  if (this.touchParams.itemProps) {
19087    event.stopPropagation();
19088
19089    var me = this;
19090    var dataset = this.itemsData.getDataSet();
19091    var itemProps = this.touchParams.itemProps;
19092    this.touchParams.itemProps = null;
19093
19094    itemProps.forEach(function (props) {
19095      var id = props.item.id;
19096      var exists = me.itemsData.get(id, me.itemOptions) != null;
19097
19098      if (!exists) {
19099        // add a new item
19100        me.options.onAdd(props.item.data, function (itemData) {
19101          me._removeItem(props.item); // remove temporary item
19102          if (itemData) {
19103            me.itemsData.getDataSet().add(itemData);
19104          }
19105
19106          // force re-stacking of all items next redraw
19107          me.body.emitter.emit('_change');
19108        });
19109      } else {
19110        // update existing item
19111        var itemData = this._cloneItemData(props.item.data); // convert start and end to the correct type
19112        me.options.onMove(itemData, function (itemData) {
19113          if (itemData) {
19114            // apply changes
19115            itemData[dataset._fieldId] = id; // ensure the item contains its id (can be undefined)
19116            dataset.update(itemData);
19117          } else {
19118            // restore original values
19119            props.item.setData(props.data);
19120
19121            me.body.emitter.emit('_change');
19122          }
19123        });
19124      }
19125    }.bind(this));
19126  }
19127};
19128
19129ItemSet.prototype._onGroupClick = function (event) {
19130  var group = this.groupFromTarget(event);
19131
19132  if (!group || !group.nestedGroups) return;
19133
19134  var groupsData = this.groupsData.getDataSet();
19135
19136  var nestingGroup = groupsData.get(group.groupId);
19137  if (nestingGroup.showNested == undefined) {
19138    nestingGroup.showNested = true;
19139  }
19140  nestingGroup.showNested = !nestingGroup.showNested;
19141
19142  var nestedGroups = groupsData.get(group.nestedGroups).map(function (nestedGroup) {
19143    nestedGroup.visible = nestingGroup.showNested;
19144    return nestedGroup;
19145  });
19146
19147  groupsData.update(nestedGroups.concat(nestingGroup));
19148
19149  if (nestingGroup.showNested) {
19150    util.removeClassName(group.dom.label, 'collapsed');
19151    util.addClassName(group.dom.label, 'expanded');
19152  } else {
19153    util.removeClassName(group.dom.label, 'expanded');
19154    var collapsedDirClassName = this.options.rtl ? 'collapsed-rtl' : 'collapsed';
19155    util.addClassName(group.dom.label, collapsedDirClassName);
19156  }
19157};
19158
19159ItemSet.prototype._onGroupDragStart = function (event) {
19160  if (this.options.groupEditable.order) {
19161    this.groupTouchParams.group = this.groupFromTarget(event);
19162
19163    if (this.groupTouchParams.group) {
19164      event.stopPropagation();
19165
19166      this.groupTouchParams.originalOrder = this.groupsData.getIds({
19167        order: this.options.groupOrder
19168      });
19169    }
19170  }
19171};
19172
19173ItemSet.prototype._onGroupDrag = function (event) {
19174  if (this.options.groupEditable.order && this.groupTouchParams.group) {
19175    event.stopPropagation();
19176
19177    var groupsData = this.groupsData;
19178    if (this.groupsData instanceof DataView) {
19179      groupsData = this.groupsData.getDataSet();
19180    }
19181    // drag from one group to another
19182    var group = this.groupFromTarget(event);
19183
19184    // try to avoid toggling when groups differ in height
19185    if (group && group.height != this.groupTouchParams.group.height) {
19186      var movingUp = group.top < this.groupTouchParams.group.top;
19187      var clientY = event.center ? event.center.y : event.clientY;
19188      var targetGroupTop = util.getAbsoluteTop(group.dom.foreground);
19189      var draggedGroupHeight = this.groupTouchParams.group.height;
19190      if (movingUp) {
19191        // skip swapping the groups when the dragged group is not below clientY afterwards
19192        if (targetGroupTop + draggedGroupHeight < clientY) {
19193          return;
19194        }
19195      } else {
19196        var targetGroupHeight = group.height;
19197        // skip swapping the groups when the dragged group is not below clientY afterwards
19198        if (targetGroupTop + targetGroupHeight - draggedGroupHeight > clientY) {
19199          return;
19200        }
19201      }
19202    }
19203
19204    if (group && group != this.groupTouchParams.group) {
19205      var targetGroup = groupsData.get(group.groupId);
19206      var draggedGroup = groupsData.get(this.groupTouchParams.group.groupId);
19207
19208      // switch groups
19209      if (draggedGroup && targetGroup) {
19210        this.options.groupOrderSwap(draggedGroup, targetGroup, groupsData);
19211        groupsData.update(draggedGroup);
19212        groupsData.update(targetGroup);
19213      }
19214
19215      // fetch current order of groups
19216      var newOrder = groupsData.getIds({
19217        order: this.options.groupOrder
19218      });
19219
19220      // in case of changes since _onGroupDragStart
19221      if (!util.equalArray(newOrder, this.groupTouchParams.originalOrder)) {
19222        var origOrder = this.groupTouchParams.originalOrder;
19223        var draggedId = this.groupTouchParams.group.groupId;
19224        var numGroups = Math.min(origOrder.length, newOrder.length);
19225        var curPos = 0;
19226        var newOffset = 0;
19227        var orgOffset = 0;
19228        while (curPos < numGroups) {
19229          // as long as the groups are where they should be step down along the groups order
19230          while (curPos + newOffset < numGroups && curPos + orgOffset < numGroups && newOrder[curPos + newOffset] == origOrder[curPos + orgOffset]) {
19231            curPos++;
19232          }
19233
19234          // all ok
19235          if (curPos + newOffset >= numGroups) {
19236            break;
19237          }
19238
19239          // not all ok
19240          // if dragged group was move upwards everything below should have an offset
19241          if (newOrder[curPos + newOffset] == draggedId) {
19242            newOffset = 1;
19243          }
19244          // if dragged group was move downwards everything above should have an offset
19245          else if (origOrder[curPos + orgOffset] == draggedId) {
19246              orgOffset = 1;
19247            }
19248            // found a group (apart from dragged group) that has the wrong position -> switch with the 
19249            // group at the position where other one should be, fix index arrays and continue
19250            else {
19251                var slippedPosition = newOrder.indexOf(origOrder[curPos + orgOffset]);
19252                var switchGroup = groupsData.get(newOrder[curPos + newOffset]);
19253                var shouldBeGroup = groupsData.get(origOrder[curPos + orgOffset]);
19254                this.options.groupOrderSwap(switchGroup, shouldBeGroup, groupsData);
19255                groupsData.update(switchGroup);
19256                groupsData.update(shouldBeGroup);
19257
19258                var switchGroupId = newOrder[curPos + newOffset];
19259                newOrder[curPos + newOffset] = origOrder[curPos + orgOffset];
19260                newOrder[slippedPosition] = switchGroupId;
19261
19262                curPos++;
19263              }
19264        }
19265      }
19266    }
19267  }
19268};
19269
19270ItemSet.prototype._onGroupDragEnd = function (event) {
19271  if (this.options.groupEditable.order && this.groupTouchParams.group) {
19272    event.stopPropagation();
19273
19274    // update existing group
19275    var me = this;
19276    var id = me.groupTouchParams.group.groupId;
19277    var dataset = me.groupsData.getDataSet();
19278    var groupData = util.extend({}, dataset.get(id)); // clone the data
19279    me.options.onMoveGroup(groupData, function (groupData) {
19280      if (groupData) {
19281        // apply changes
19282        groupData[dataset._fieldId] = id; // ensure the group contains its id (can be undefined)
19283        dataset.update(groupData);
19284      } else {
19285
19286        // fetch current order of groups
19287        var newOrder = dataset.getIds({
19288          order: me.options.groupOrder
19289        });
19290
19291        // restore original order
19292        if (!util.equalArray(newOrder, me.groupTouchParams.originalOrder)) {
19293          var origOrder = me.groupTouchParams.originalOrder;
19294          var numGroups = Math.min(origOrder.length, newOrder.length);
19295          var curPos = 0;
19296          while (curPos < numGroups) {
19297            // as long as the groups are where they should be step down along the groups order
19298            while (curPos < numGroups && newOrder[curPos] == origOrder[curPos]) {
19299              curPos++;
19300            }
19301
19302            // all ok
19303            if (curPos >= numGroups) {
19304              break;
19305            }
19306
19307            // found a group that has the wrong position -> switch with the
19308            // group at the position where other one should be, fix index arrays and continue
19309            var slippedPosition = newOrder.indexOf(origOrder[curPos]);
19310            var switchGroup = dataset.get(newOrder[curPos]);
19311            var shouldBeGroup = dataset.get(origOrder[curPos]);
19312            me.options.groupOrderSwap(switchGroup, shouldBeGroup, dataset);
19313            dataset.update(switchGroup);
19314            dataset.update(shouldBeGroup);
19315
19316            var switchGroupId = newOrder[curPos];
19317            newOrder[curPos] = origOrder[curPos];
19318            newOrder[slippedPosition] = switchGroupId;
19319
19320            curPos++;
19321          }
19322        }
19323      }
19324    });
19325
19326    me.body.emitter.emit('groupDragged', { groupId: id });
19327  }
19328};
19329
19330/**
19331 * Handle selecting/deselecting an item when tapping it
19332 * @param {Event} event
19333 * @private
19334 */
19335ItemSet.prototype._onSelectItem = function (event) {
19336  if (!this.options.selectable) return;
19337
19338  var ctrlKey = event.srcEvent && (event.srcEvent.ctrlKey || event.srcEvent.metaKey);
19339  var shiftKey = event.srcEvent && event.srcEvent.shiftKey;
19340  if (ctrlKey || shiftKey) {
19341    this._onMultiSelectItem(event);
19342    return;
19343  }
19344
19345  var oldSelection = this.getSelection();
19346
19347  var item = this.itemFromTarget(event);
19348  var selection = item ? [item.id] : [];
19349  this.setSelection(selection);
19350
19351  var newSelection = this.getSelection();
19352
19353  // emit a select event,
19354  // except when old selection is empty and new selection is still empty
19355  if (newSelection.length > 0 || oldSelection.length > 0) {
19356    this.body.emitter.emit('select', {
19357      items: newSelection,
19358      event: event
19359    });
19360  }
19361};
19362
19363/**
19364 * Handle hovering an item
19365 * @param {Event} event
19366 * @private
19367 */
19368ItemSet.prototype._onMouseOver = function (event) {
19369  var item = this.itemFromTarget(event);
19370  if (!item) return;
19371
19372  // Item we just left
19373  var related = this.itemFromRelatedTarget(event);
19374  if (item === related) {
19375    // We haven't changed item, just element in the item
19376    return;
19377  }
19378
19379  var title = item.getTitle();
19380  if (this.options.showTooltips && title) {
19381    if (this.popup == null) {
19382      this.popup = new Popup(this.body.dom.root, this.options.tooltip.overflowMethod || 'flip');
19383    }
19384
19385    this.popup.setText(title);
19386    var container = this.body.dom.centerContainer;
19387    this.popup.setPosition(event.clientX - util.getAbsoluteLeft(container) + container.offsetLeft, event.clientY - util.getAbsoluteTop(container) + container.offsetTop);
19388    this.popup.show();
19389  } else {
19390    // Hovering over item without a title, hide popup
19391    // Needed instead of _just_ in _onMouseOut due to #2572
19392    if (this.popup != null) {
19393      this.popup.hide();
19394    }
19395  }
19396
19397  this.body.emitter.emit('itemover', {
19398    item: item.id,
19399    event: event
19400  });
19401};
19402ItemSet.prototype._onMouseOut = function (event) {
19403  var item = this.itemFromTarget(event);
19404  if (!item) return;
19405
19406  // Item we are going to
19407  var related = this.itemFromRelatedTarget(event);
19408  if (item === related) {
19409    // We aren't changing item, just element in the item
19410    return;
19411  }
19412
19413  if (this.popup != null) {
19414    this.popup.hide();
19415  }
19416
19417  this.body.emitter.emit('itemout', {
19418    item: item.id,
19419    event: event
19420  });
19421};
19422ItemSet.prototype._onMouseMove = function (event) {
19423  var item = this.itemFromTarget(event);
19424  if (!item) return;
19425
19426  if (this.options.showTooltips && this.options.tooltip.followMouse) {
19427    if (this.popup) {
19428      if (!this.popup.hidden) {
19429        var container = this.body.dom.centerContainer;
19430        this.popup.setPosition(event.clientX - util.getAbsoluteLeft(container) + container.offsetLeft, event.clientY - util.getAbsoluteTop(container) + container.offsetTop);
19431        this.popup.show(); // Redraw
19432      }
19433    }
19434  }
19435};
19436
19437/**
19438 * Handle mousewheel
19439 * @param {Event}  event   The event
19440 * @private
19441 */
19442ItemSet.prototype._onMouseWheel = function (event) {
19443  if (this.touchParams.itemIsDragging) {
19444    this._onDragEnd(event);
19445  }
19446};
19447
19448/**
19449 * Handle updates of an item on double tap
19450 * @param {vis.Item}  item   The item
19451 * @private
19452 */
19453ItemSet.prototype._onUpdateItem = function (item) {
19454  if (!this.options.selectable) return;
19455  if (!this.options.editable.add) return;
19456
19457  var me = this;
19458
19459  if (item) {
19460    // execute async handler to update the item (or cancel it)
19461    var itemData = me.itemsData.get(item.id); // get a clone of the data from the dataset
19462    this.options.onUpdate(itemData, function (itemData) {
19463      if (itemData) {
19464        me.itemsData.getDataSet().update(itemData);
19465      }
19466    });
19467  }
19468};
19469
19470/**
19471 * Handle drop event of data on item
19472 * Only called when `objectData.target === 'item'.
19473 * @param {Event} event The event 
19474 * @private
19475 */
19476ItemSet.prototype._onDropObjectOnItem = function (event) {
19477  var item = this.itemFromTarget(event);
19478  var objectData = JSON.parse(event.dataTransfer.getData("text"));
19479  this.options.onDropObjectOnItem(objectData, item);
19480};
19481
19482/**
19483 * Handle creation of an item on double tap or drop of a drag event
19484 * @param {Event} event   The event
19485 * @private
19486 */
19487ItemSet.prototype._onAddItem = function (event) {
19488  if (!this.options.selectable) return;
19489  if (!this.options.editable.add) return;
19490
19491  var me = this;
19492  var snap = this.options.snap || null;
19493  var xAbs;
19494  var x;
19495  // add item
19496  if (this.options.rtl) {
19497    xAbs = util.getAbsoluteRight(this.dom.frame);
19498    x = xAbs - event.center.x;
19499  } else {
19500    xAbs = util.getAbsoluteLeft(this.dom.frame);
19501    x = event.center.x - xAbs;
19502  }
19503  // var xAbs = util.getAbsoluteLeft(this.dom.frame);
19504  // var x = event.center.x - xAbs;
19505  var start = this.body.util.toTime(x);
19506  var scale = this.body.util.getScale();
19507  var step = this.body.util.getStep();
19508  var end;
19509
19510  var newItemData;
19511  if (event.type == 'drop') {
19512    newItemData = JSON.parse(event.dataTransfer.getData("text"));
19513    newItemData.content = newItemData.content ? newItemData.content : 'new item';
19514    newItemData.start = newItemData.start ? newItemData.start : snap ? snap(start, scale, step) : start;
19515    newItemData.type = newItemData.type || 'box';
19516    newItemData[this.itemsData._fieldId] = newItemData.id || util.randomUUID();
19517
19518    if (newItemData.type == 'range' && !newItemData.end) {
19519      end = this.body.util.toTime(x + this.props.width / 5);
19520      newItemData.end = snap ? snap(end, scale, step) : end;
19521    }
19522  } else {
19523    newItemData = {
19524      start: snap ? snap(start, scale, step) : start,
19525      content: 'new item'
19526    };
19527    newItemData[this.itemsData._fieldId] = util.randomUUID();
19528
19529    // when default type is a range, add a default end date to the new item
19530    if (this.options.type === 'range') {
19531      end = this.body.util.toTime(x + this.props.width / 5);
19532      newItemData.end = snap ? snap(end, scale, step) : end;
19533    }
19534  }
19535
19536  var group = this.groupFromTarget(event);
19537  if (group) {
19538    newItemData.group = group.groupId;
19539  }
19540
19541  // execute async handler to customize (or cancel) adding an item
19542  newItemData = this._cloneItemData(newItemData); // convert start and end to the correct type
19543  this.options.onAdd(newItemData, function (item) {
19544    if (item) {
19545      me.itemsData.getDataSet().add(item);
19546      if (event.type == 'drop') {
19547        me.setSelection([item.id]);
19548      }
19549      // TODO: need to trigger a redraw?
19550    }
19551  });
19552};
19553
19554/**
19555 * Handle selecting/deselecting multiple items when holding an item
19556 * @param {Event} event
19557 * @private
19558 */
19559ItemSet.prototype._onMultiSelectItem = function (event) {
19560  if (!this.options.selectable) return;
19561
19562  var item = this.itemFromTarget(event);
19563
19564  if (item) {
19565    // multi select items (if allowed)
19566
19567    var selection = this.options.multiselect ? this.getSelection() // take current selection
19568    : []; // deselect current selection
19569
19570    var shiftKey = event.srcEvent && event.srcEvent.shiftKey || false;
19571
19572    if (shiftKey && this.options.multiselect) {
19573      // select all items between the old selection and the tapped item
19574      var itemGroup = this.itemsData.get(item.id).group;
19575
19576      // when filtering get the group of the last selected item
19577      var lastSelectedGroup = undefined;
19578      if (this.options.multiselectPerGroup) {
19579        if (selection.length > 0) {
19580          lastSelectedGroup = this.itemsData.get(selection[0]).group;
19581        }
19582      }
19583
19584      // determine the selection range
19585      if (!this.options.multiselectPerGroup || lastSelectedGroup == undefined || lastSelectedGroup == itemGroup) {
19586        selection.push(item.id);
19587      }
19588      var range = ItemSet._getItemRange(this.itemsData.get(selection, this.itemOptions));
19589
19590      if (!this.options.multiselectPerGroup || lastSelectedGroup == itemGroup) {
19591        // select all items within the selection range
19592        selection = [];
19593        for (var id in this.items) {
19594          if (this.items.hasOwnProperty(id)) {
19595            var _item = this.items[id];
19596            var start = _item.data.start;
19597            var end = _item.data.end !== undefined ? _item.data.end : start;
19598
19599            if (start >= range.min && end <= range.max && (!this.options.multiselectPerGroup || lastSelectedGroup == this.itemsData.get(_item.id).group) && !(_item instanceof BackgroundItem)) {
19600              selection.push(_item.id); // do not use id but item.id, id itself is stringified
19601            }
19602          }
19603        }
19604      }
19605    } else {
19606      // add/remove this item from the current selection
19607      var index = selection.indexOf(item.id);
19608      if (index == -1) {
19609        // item is not yet selected -> select it
19610        selection.push(item.id);
19611      } else {
19612        // item is already selected -> deselect it
19613        selection.splice(index, 1);
19614      }
19615    }
19616
19617    this.setSelection(selection);
19618
19619    this.body.emitter.emit('select', {
19620      items: this.getSelection(),
19621      event: event
19622    });
19623  }
19624};
19625
19626/**
19627 * Calculate the time range of a list of items
19628 * @param {Array.<Object>} itemsData
19629 * @return {{min: Date, max: Date}} Returns the range of the provided items
19630 * @private
19631 */
19632ItemSet._getItemRange = function (itemsData) {
19633  var max = null;
19634  var min = null;
19635
19636  itemsData.forEach(function (data) {
19637    if (min == null || data.start < min) {
19638      min = data.start;
19639    }
19640
19641    if (data.end != undefined) {
19642      if (max == null || data.end > max) {
19643        max = data.end;
19644      }
19645    } else {
19646      if (max == null || data.start > max) {
19647        max = data.start;
19648      }
19649    }
19650  });
19651
19652  return {
19653    min: min,
19654    max: max
19655  };
19656};
19657
19658/**
19659 * Find an item from an element:
19660 * searches for the attribute 'timeline-item' in the element's tree
19661 * @param {HTMLElement} element
19662 * @return {Item | null} item
19663 */
19664ItemSet.prototype.itemFromElement = function (element) {
19665  var cur = element;
19666  while (cur) {
19667    if (cur.hasOwnProperty('timeline-item')) {
19668      return cur['timeline-item'];
19669    }
19670    cur = cur.parentNode;
19671  }
19672
19673  return null;
19674};
19675
19676/**
19677 * Find an item from an event target:
19678 * searches for the attribute 'timeline-item' in the event target's element tree
19679 * @param {Event} event
19680 * @return {Item | null} item
19681 */
19682ItemSet.prototype.itemFromTarget = function (event) {
19683  return this.itemFromElement(event.target);
19684};
19685
19686/**
19687 * Find an item from an event's related target:
19688 * searches for the attribute 'timeline-item' in the related target's element tree
19689 * @param {Event} event
19690 * @return {Item | null} item
19691 */
19692ItemSet.prototype.itemFromRelatedTarget = function (event) {
19693  return this.itemFromElement(event.relatedTarget);
19694};
19695
19696/**
19697 * Find the Group from an event target:
19698 * searches for the attribute 'timeline-group' in the event target's element tree
19699 * @param {Event} event
19700 * @return {Group | null} group
19701 */
19702ItemSet.prototype.groupFromTarget = function (event) {
19703  var clientY = event.center ? event.center.y : event.clientY;
19704  var groupIds = this.groupIds;
19705
19706  if (groupIds.length <= 0 && this.groupsData) {
19707    groupIds = this.groupsData.getIds({
19708      order: this.options.groupOrder
19709    });
19710  }
19711
19712  for (var i = 0; i < groupIds.length; i++) {
19713    var groupId = groupIds[i];
19714    var group = this.groups[groupId];
19715    var foreground = group.dom.foreground;
19716    var top = util.getAbsoluteTop(foreground);
19717    if (clientY > top && clientY < top + foreground.offsetHeight) {
19718      return group;
19719    }
19720
19721    if (this.options.orientation.item === 'top') {
19722      if (i === this.groupIds.length - 1 && clientY > top) {
19723        return group;
19724      }
19725    } else {
19726      if (i === 0 && clientY < top + foreground.offset) {
19727        return group;
19728      }
19729    }
19730  }
19731
19732  return null;
19733};
19734
19735/**
19736 * Find the ItemSet from an event target:
19737 * searches for the attribute 'timeline-itemset' in the event target's element tree
19738 * @param {Event} event
19739 * @return {ItemSet | null} item
19740 */
19741ItemSet.itemSetFromTarget = function (event) {
19742  var target = event.target;
19743  while (target) {
19744    if (target.hasOwnProperty('timeline-itemset')) {
19745      return target['timeline-itemset'];
19746    }
19747    target = target.parentNode;
19748  }
19749
19750  return null;
19751};
19752
19753/**
19754 * Clone the data of an item, and "normalize" it: convert the start and end date
19755 * to the type (Date, Moment, ...) configured in the DataSet. If not configured,
19756 * start and end are converted to Date.
19757 * @param {Object} itemData, typically `item.data`
19758 * @param {string} [type]  Optional Date type. If not provided, the type from the DataSet is taken
19759 * @return {Object} The cloned object
19760 * @private
19761 */
19762ItemSet.prototype._cloneItemData = function (itemData, type) {
19763  var clone = util.extend({}, itemData);
19764
19765  if (!type) {
19766    // convert start and end date to the type (Date, Moment, ...) configured in the DataSet
19767    type = this.itemsData.getDataSet()._options.type;
19768  }
19769
19770  if (clone.start != undefined) {
19771    clone.start = util.convert(clone.start, type && type.start || 'Date');
19772  }
19773  if (clone.end != undefined) {
19774    clone.end = util.convert(clone.end, type && type.end || 'Date');
19775  }
19776
19777  return clone;
19778};
19779
19780module.exports = ItemSet;
19781
19782/***/ }),
19783/* 100 */
19784/***/ (function(module, exports, __webpack_require__) {
19785
19786"use strict";
19787
19788
19789// Utility functions for ordering and stacking of items
19790var EPSILON = 0.001;
19790 // used when checking collisions, to prevent round-off errors
19791
19792/**
19793 * Order items by their start data
19794 * @param {Item[]} items
19795 */
19796exports.orderByStart = function (items) {
19797  items.sort(function (a, b) {
19798    return a.data.start - b.data.start;
19799  });
19800};
19801
19802/**
19803 * Order items by their end date. If they have no end date, their start date
19804 * is used.
19805 * @param {Item[]} items
19806 */
19807exports.orderByEnd = function (items) {
19808  items.sort(function (a, b) {
19809    var aTime = 'end' in a.data ? a.data.end : a.data.start,
19810        bTime = 'end' in b.data ? b.data.end : b.data.start;
19811
19812    return aTime - bTime;
19813  });
19814};
19815
19816/**
19817 * Adjust vertical positions of the items such that they don't overlap each
19818 * other.
19819 * @param {Item[]} items
19820 *            All visible items
19821 * @param {{item: {horizontal: number, vertical: number}, axis: number}} margin
19822 *            Margins between items and between items and the axis.
19823 * @param {boolean} [force=false]
19824 *            If true, all items will be repositioned. If false (default), only
19825 *            items having a top===null will be re-stacked
19826 */
19827exports.stack = function (items, margin, force) {
19828  if (force) {
19829    // reset top position of all items
19830    for (var i = 0; i < items.length; i++) {
19831      items[i].top = null;
19832    }
19833  }
19834
19835  // calculate new, non-overlapping positions
19836  for (var i = 0; i < items.length; i++) {
19837    // eslint-disable-line no-redeclare
19838    var item = items[i];
19839    if (item.stack && item.top === null) {
19840      // initialize top position
19841      item.top = margin.axis;
19842
19843      do {
19844        // TODO: optimize checking for overlap. when there is a gap without items,
19845        //       you only need to check for items from the next item on, not from zero
19846        var collidingItem = null;
19847        for (var j = 0, jj = items.length; j < jj; j++) {
19848          var other = items[j];
19849          if (other.top !== null && other !== item && other.stack && exports.collision(item, other, margin.item, other.options.rtl)) {
19850            collidingItem = other;
19851            break;
19852          }
19853        }
19854
19855        if (collidingItem != null) {
19856          // There is a collision. Reposition the items above the colliding element
19857          item.top = collidingItem.top + collidingItem.height + margin.item.vertical;
19858        }
19859      } while (collidingItem);
19860    }
19861  }
19862};
19863
19864/**
19865 * Adjust vertical positions of the items within a single subgroup such that they 
19866 * don't overlap each other.
19867 * @param {Item[]} items
19868 *            All items withina subgroup
19869 * @param {{item: {horizontal: number, vertical: number}, axis: number}} margin
19870 *            Margins between items and between items and the axis.
19871 * @param {subgroup} subgroup
19872 *            The subgroup that is being stacked 
19873 */
19874exports.substack = function (items, margin, subgroup) {
19875  for (var i = 0; i < items.length; i++) {
19876    items[i].top = null;
19877  }
19878
19879  // Set the initial height
19880  var subgroupHeight = subgroup.height;
19881
19882  // calculate new, non-overlapping positions
19883  for (i = 0; i < items.length; i++) {
19884    var item = items[i];
19885
19886    if (item.stack && item.top === null) {
19887      // initialize top position
19888      item.top = item.baseTop; //margin.axis + item.baseTop;
19889
19890      do {
19891        // TODO: optimize checking for overlap. when there is a gap without items,
19892        //       you only need to check for items from the next item on, not from zero
19893        var collidingItem = null;
19894        for (var j = 0, jj = items.length; j < jj; j++) {
19895          var other = items[j];
19896          if (other.top !== null && other !== item /*&& other.stack*/ && exports.collision(item, other, margin.item, other.options.rtl)) {
19897            collidingItem = other;
19898            break;
19899          }
19900        }
19901
19902        if (collidingItem != null) {
19903          // There is a collision. Reposition the items above the colliding element
19904          item.top = collidingItem.top + collidingItem.height + margin.item.vertical; // + item.baseTop;
19905        }
19906
19907        if (item.top + item.height > subgroupHeight) {
19908          subgroupHeight = item.top + item.height;
19909        }
19910      } while (collidingItem);
19911    }
19912  }
19913
19914  // Set the new height
19915  subgroup.height = subgroupHeight - subgroup.top + 0.5 * margin.item.vertical;
19916};
19917
19918/**
19919 * Adjust vertical positions of the items without stacking them
19920 * @param {Item[]} items
19921 *            All visible items
19922 * @param {{item: {horizontal: number, vertical: number}, axis: number}} margin
19923 *            Margins between items and between items and the axis.
19924 * @param {subgroups[]} subgroups
19925 *            All subgroups
19926 * @param {boolean} stackSubgroups
19927 */
19928exports.nostack = function (items, margin, subgroups, stackSubgroups) {
19929  for (var i = 0; i < items.length; i++) {
19930    if (items[i].data.subgroup == undefined) {
19931      items[i].top = margin.item.vertical;
19932    } else if (items[i].data.subgroup !== undefined && stackSubgroups) {
19933      var newTop = 0;
19934      for (var subgroup in subgroups) {
19935        if (subgroups.hasOwnProperty(subgroup)) {
19936          if (subgroups[subgroup].visible == true && subgroups[subgroup].index < subgroups[items[i].data.subgroup].index) {
19937            newTop += subgroups[subgroup].height;
19938            subgroups[items[i].data.subgroup].top = newTop;
19939          }
19940        }
19941      }
19942      items[i].top = newTop + 0.5 * margin.item.vertical;
19943    }
19944  }
19945  if (!stackSubgroups) {
19946    exports.stackSubgroups(items, margin, subgroups);
19947  }
19948};
19949
19950/**
19951 * Adjust vertical positions of the subgroups such that they don't overlap each
19952 * other.
19953 * @param {Array.<vis.Item>} items
19954 * @param {{item: {horizontal: number, vertical: number}, axis: number}} margin Margins between items and between items and the axis.
19955 * @param {subgroups[]} subgroups
19956 *            All subgroups
19957 */
19958exports.stackSubgroups = function (items, margin, subgroups) {
19959  for (var subgroup in subgroups) {
19960    if (subgroups.hasOwnProperty(subgroup)) {
19961
19962      subgroups[subgroup].top = 0;
19963      do {
19964        // TODO: optimize checking for overlap. when there is a gap without items,
19965        //       you only need to check for items from the next item on, not from zero
19966        var collidingItem = null;
19967        for (var otherSubgroup in subgroups) {
19968          if (subgroups[otherSubgroup].top !== null && otherSubgroup !== subgroup && subgroups[subgroup].index > subgroups[otherSubgroup].index && exports.collisionByTimes(subgroups[subgroup], subgroups[otherSubgroup])) {
19969            collidingItem = subgroups[otherSubgroup];
19970            break;
19971          }
19972        }
19973
19974        if (collidingItem != null) {
19975          // There is a collision. Reposition the subgroups above the colliding element
19976          subgroups[subgroup].top = collidingItem.top + collidingItem.height;
19977        }
19978      } while (collidingItem);
19979    }
19980  }
19981  for (var i = 0; i < items.length; i++) {
19982    if (items[i].data.subgroup !== undefined) {
19983      items[i].top = subgroups[items[i].data.subgroup].top + 0.5 * margin.item.vertical;
19984    }
19985  }
19986};
19987
19988/**
19989 * Adjust vertical positions of the subgroups such that they don't overlap each
19990 * other, then stacks the contents of each subgroup individually.
19991 * @param {Item[]} subgroupItems
19992 *            All the items in a subgroup
19993 * @param {{item: {horizontal: number, vertical: number}, axis: number}} margin
19994 *            Margins between items and between items and the axis.
19995 * @param {subgroups[]} subgroups
19996 *            All subgroups 
19997 */
19998exports.stackSubgroupsWithInnerStack = function (subgroupItems, margin, subgroups) {
19999  var doSubStack = false;
20000
20001  // Run subgroups in their order (if any)
20002  var subgroupOrder = [];
20003
20004  for (var subgroup in subgroups) {
20005    if (subgroups[subgroup].hasOwnProperty("index")) {
20006      subgroupOrder[subgroups[subgroup].index] = subgroup;
20007    } else {
20008      subgroupOrder.push(subgroup);
20009    }
20010  }
20011
20012  for (var j = 0; j < subgroupOrder.length; j++) {
20013    subgroup = subgroupOrder[j];
20014    if (subgroups.hasOwnProperty(subgroup)) {
20015
20016      doSubStack = doSubStack || subgroups[subgroup].stack;
20017      subgroups[subgroup].top = 0;
20018
20019      for (var otherSubgroup in subgroups) {
20020        if (subgroups[otherSubgroup].visible && subgroups[subgroup].index > subgroups[otherSubgroup].index) {
20021          subgroups[subgroup].top += subgroups[otherSubgroup].height;
20022        }
20023      }
20024
20025      var items = subgroupItems[subgroup];
20026      for (var i = 0; i < items.length; i++) {
20027        if (items[i].data.subgroup !== undefined) {
20028          items[i].top = subgroups[items[i].data.subgroup].top + 0.5 * margin.item.vertical;
20029
20030          if (subgroups[subgroup].stack) {
20031            items[i].baseTop = items[i].top;
20032          }
20033        }
20034      }
20035
20036      if (doSubStack && subgroups[subgroup].stack) {
20037        exports.substack(subgroupItems[subgroup], margin, subgroups[subgroup]);
20038      }
20039    }
20040  }
20041};
20042
20043/**
20044 * Test if the two provided items collide
20045 * The items must have parameters left, width, top, and height.
20046 * @param {Item} a          The first item
20047 * @param {Item} b          The second item
20048 * @param {{horizontal: number, vertical: number}} margin
20049 *                          An object containing a horizontal and vertical
20050 *                          minimum required margin.
20051 * @param {boolean} rtl
20052 * @return {boolean}        true if a and b collide, else false
20053 */
20054exports.collision = function (a, b, margin, rtl) {
20055  if (rtl) {
20056    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;
20057  } else {
20058    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;
20059  }
20060};
20061
20062/**
20063 * Test if the two provided objects collide
20064 * The objects must have parameters start, end, top, and height.
20065 * @param {Object} a          The first Object
20066 * @param {Object} b          The second Object
20067 * @return {boolean}        true if a and b collide, else false
20068 */
20069exports.collisionByTimes = function (a, b) {
20070  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;
20071};
20072
20073/***/ }),
20074/* 101 */
20075/***/ (function(module, exports, __webpack_require__) {
20076
20077"use strict";
20078
20079
20080var Item = __webpack_require__(38);
20081
20082/**
20083 * @constructor BoxItem
20084 * @extends Item
20085 * @param {Object} data             Object containing parameters start
20086 *                                  content, className.
20087 * @param {{toScreen: function, toTime: function}} conversion
20088 *                                  Conversion functions from time to screen and vice versa
20089 * @param {Object} [options]        Configuration options
20090 *                                  // TODO: describe available options
20091 */
20092function BoxItem(data, conversion, options) {
20093  this.props = {
20094    dot: {
20095      width: 0,
20096      height: 0
20097    },
20098    line: {
20099      width: 0,
20100      height: 0
20101    }
20102  };
20103  this.options = options;
20104  // validate data
20105  if (data) {
20106    if (data.start == undefined) {
20107      throw new Error('Property "start" missing in item ' + data);
20108    }
20109  }
20110
20111  Item.call(this, data, conversion, options);
20112}
20113
20114BoxItem.prototype = new Item(null, null, null);
20115
20116/**
20117 * Check whether this item is visible inside given range
20118 * @param {{start: number, end: number}} range with a timestamp for start and end
20119 * @returns {boolean} True if visible
20120 */
20121BoxItem.prototype.isVisible = function (range) {
20122  // determine visibility
20123  var isVisible;
20124  var align = this.options.align;
20125  var widthInMs = this.width * range.getMillisecondsPerPixel();
20126
20127  if (align == 'right') {
20128    isVisible = this.data.start.getTime() > range.start && this.data.start.getTime() - widthInMs < range.end;
20129  } else if (align == 'left') {
20130    isVisible = this.data.start.getTime() + widthInMs > range.start && this.data.start.getTime() < range.end;
20131  } else {
20132    // default or 'center'
20133    isVisible = this.data.start.getTime() + widthInMs / 2 > range.start && this.data.start.getTime() - widthInMs / 2 < range.end;
20134  }
20135  return isVisible;
20136};
20137
20138BoxItem.prototype._createDomElement = function () {
20139  if (!this.dom) {
20140    // create DOM
20141    this.dom = {};
20142
20143    // create main box
20144    this.dom.box = document.createElement('DIV');
20145
20146    // contents box (inside the background box). used for making margins
20147    this.dom.content = document.createElement('DIV');
20148    this.dom.content.className = 'vis-item-content';
20149    this.dom.box.appendChild(this.dom.content);
20150
20151    // line to axis
20152    this.dom.line = document.createElement('DIV');
20153    this.dom.line.className = 'vis-line';
20154
20155    // dot on axis
20156    this.dom.dot = document.createElement('DIV');
20157    this.dom.dot.className = 'vis-dot';
20158
20159    // attach this item as attribute
20160    this.dom.box['timeline-item'] = this;
20161
20162    this.dirty = true;
20163  }
20164};
20165
20166BoxItem.prototype._appendDomElement = function () {
20167  if (!this.parent) {
20168    throw new Error('Cannot redraw item: no parent attached');
20169  }
20170  if (!this.dom.box.parentNode) {
20171    var foreground = this.parent.dom.foreground;
20172    if (!foreground) throw new Error('Cannot redraw item: parent has no foreground container element');
20173    foreground.appendChild(this.dom.box);
20174  }
20175  if (!this.dom.line.parentNode) {
20176    var background = this.parent.dom.background;
20177    if (!background) throw new Error('Cannot redraw item: parent has no background container element');
20178    background.appendChild(this.dom.line);
20179  }
20180  if (!this.dom.dot.parentNode) {
20181    var axis = this.parent.dom.axis;
20182    if (!background) throw new Error('Cannot redraw item: parent has no axis container element');
20183    axis.appendChild(this.dom.dot);
20184  }
20185  this.displayed = true;
20186};
20187
20188BoxItem.prototype._updateDirtyDomComponents = function () {
20189  // An item is marked dirty when:
20190  // - the item is not yet rendered
20191  // - the item's data is changed
20192  // - the item is selected/deselected
20193  if (this.dirty) {
20194    this._updateContents(this.dom.content);
20195    this._updateDataAttributes(this.dom.box);
20196    this._updateStyle(this.dom.box);
20197
20198    var editable = this.editable.updateTime || this.editable.updateGroup;
20199
20200    // update class
20201    var className = (this.data.className ? ' ' + this.data.className : '') + (this.selected ? ' vis-selected' : '') + (editable ? ' vis-editable' : ' vis-readonly');
20202    this.dom.box.className = 'vis-item vis-box' + className;
20203    this.dom.line.className = 'vis-item vis-line' + className;
20204    this.dom.dot.className = 'vis-item vis-dot' + className;
20205  }
20206};
20207
20208BoxItem.prototype._getDomComponentsSizes = function () {
20209  return {
20210    previous: {
20211      right: this.dom.box.style.right,
20212      left: this.dom.box.style.left
20213    },
20214    dot: {
20215      height: this.dom.dot.offsetHeight,
20216      width: this.dom.dot.offsetWidth
20217    },
20218    line: {
20219      width: this.dom.line.offsetWidth
20220    },
20221    box: {
20222      width: this.dom.box.offsetWidth,
20223      height: this.dom.box.offsetHeight
20224    }
20225  };
20226};
20227
20228BoxItem.prototype._updateDomComponentsSizes = function (sizes) {
20229  if (this.options.rtl) {
20230    this.dom.box.style.right = "0px";
20231  } else {
20232    this.dom.box.style.left = "0px";
20233  }
20234
20235  // recalculate size
20236  this.props.dot.height = sizes.dot.height;
20237  this.props.dot.width = sizes.dot.width;
20238  this.props.line.width = sizes.line.width;
20239  this.width = sizes.box.width;
20240  this.height = sizes.box.height;
20241
20242  // restore previous position
20243  if (this.options.rtl) {
20244    this.dom.box.style.right = sizes.previous.right;
20245  } else {
20246    this.dom.box.style.left = sizes.previous.left;
20247  }
20248
20249  this.dirty = false;
20250};
20251
20252BoxItem.prototype._repaintDomAdditionals = function () {
20253  this._repaintOnItemUpdateTimeTooltip(this.dom.box);
20254  this._repaintDragCenter();
20255  this._repaintDeleteButton(this.dom.box);
20256};
20257
20258/**
20259 * Repaint the item
20260 * @param {boolean} [returnQueue=false]  return the queue
20261 * @return {boolean} the redraw queue if returnQueue=true
20262 */
20263BoxItem.prototype.redraw = function (returnQueue) {
20264  var sizes;
20265  var queue = [
20266  // create item DOM
20267  this._createDomElement.bind(this),
20268
20269  // append DOM to parent DOM
20270  this._appendDomElement.bind(this),
20271
20272  // update dirty DOM
20273  this._updateDirtyDomComponents.bind(this), function () {
20274    if (this.dirty) {
20275      sizes = this._getDomComponentsSizes();
20276    }
20277  }.bind(this), function () {
20278    if (this.dirty) {
20279      this._updateDomComponentsSizes.bind(this)(sizes);
20280    }
20281  }.bind(this),
20282
20283  // repaint DOM additionals
20284  this._repaintDomAdditionals.bind(this)];
20285
20286  if (returnQueue) {
20287    return queue;
20288  } else {
20289    var result;
20290    queue.forEach(function (fn) {
20291      result = fn();
20292    });
20293    return result;
20294  }
20295};
20296
20297/**
20298 * Show the item in the DOM (when not already displayed). The items DOM will
20299 * be created when needed.
20300 */
20301BoxItem.prototype.show = function () {
20302  if (!this.displayed) {
20303    this.redraw();
20304  }
20305};
20306
20307/**
20308 * Hide the item from the DOM (when visible)
20309 */
20310BoxItem.prototype.hide = function () {
20311  if (this.displayed) {
20312    var dom = this.dom;
20313
20314    if (dom.box.parentNode) dom.box.parentNode.removeChild(dom.box);
20315    if (dom.line.parentNode) dom.line.parentNode.removeChild(dom.line);
20316    if (dom.dot.parentNode) dom.dot.parentNode.removeChild(dom.dot);
20317
20318    this.displayed = false;
20319  }
20320};
20321
20322/**
20323 * Reposition the item horizontally
20324 * @Override
20325 */
20326BoxItem.prototype.repositionX = function () {
20327  var start = this.conversion.toScreen(this.data.start);
20328  var align = this.options.align;
20329
20330  // calculate left position of the box
20331  if (align == 'right') {
20332    if (this.options.rtl) {
20333      this.right = start - this.width;
20334
20335      // reposition box, line, and dot
20336      this.dom.box.style.right = this.right + 'px';
20337      this.dom.line.style.right = start - this.props.line.width + 'px';
20338      this.dom.dot.style.right = start - this.props.line.width / 2 - this.props.dot.width / 2 + 'px';
20339    } else {
20340      this.left = start - this.width;
20341
20342      // reposition box, line, and dot
20343      this.dom.box.style.left = this.left + 'px';
20344      this.dom.line.style.left = start - this.props.line.width + 'px';
20345      this.dom.dot.style.left = start - this.props.line.width / 2 - this.props.dot.width / 2 + 'px';
20346    }
20347  } else if (align == 'left') {
20348    if (this.options.rtl) {
20349      this.right = start;
20350
20351      // reposition box, line, and dot
20352      this.dom.box.style.right = this.right + 'px';
20353      this.dom.line.style.right = start + 'px';
20354      this.dom.dot.style.right = start + this.props.line.width / 2 - this.props.dot.width / 2 + 'px';
20355    } else {
20356      this.left = start;
20357
20358      // reposition box, line, and dot
20359      this.dom.box.style.left = this.left + 'px';
20360      this.dom.line.style.left = start + 'px';
20361      this.dom.dot.style.left = start + this.props.line.width / 2 - this.props.dot.width / 2 + 'px';
20362    }
20363  } else {
20364    // default or 'center'
20365    if (this.options.rtl) {
20366      this.right = start - this.width / 2;
20367
20368      // reposition box, line, and dot
20369      this.dom.box.style.right = this.right + 'px';
20370      this.dom.line.style.right = start - this.props.line.width + 'px';
20371      this.dom.dot.style.right = start - this.props.dot.width / 2 + 'px';
20372    } else {
20373      this.left = start - this.width / 2;
20374
20375      // reposition box, line, and dot
20376      this.dom.box.style.left = this.left + 'px';
20377      this.dom.line.style.left = start - this.props.line.width / 2 + 'px';
20378      this.dom.dot.style.left = start - this.props.dot.width / 2 + 'px';
20379    }
20380  }
20381};
20382
20383/**
20384 * Reposition the item vertically
20385 * @Override
20386 */
20387BoxItem.prototype.repositionY = function () {
20388  var orientation = this.options.orientation.item;
20389  var box = this.dom.box;
20390  var line = this.dom.line;
20391  var dot = this.dom.dot;
20392
20393  if (orientation == 'top') {
20394    box.style.top = (this.top || 0) + 'px';
20395
20396    line.style.top = '0';
20397    line.style.height = this.parent.top + this.top + 1 + 'px';
20398    line.style.bottom = '';
20399  } else {
20400    // orientation 'bottom'
20401    var itemSetHeight = this.parent.itemSet.props.height; // TODO: this is nasty
20402    var lineHeight = itemSetHeight - this.parent.top - this.parent.height + this.top;
20403
20404    box.style.top = (this.parent.height - this.top - this.height || 0
20404) + 'px';
20405    line.style.top = itemSetHeight - lineHeight + 'px';
20406    line.style.bottom = '0';
20407  }
20408
20409  dot.style.top = -this.props.dot.height / 2 + 'px';
20410};
20411
20412/**
20413 * Return the width of the item left from its start date
20414 * @return {number}
20415 */
20416BoxItem.prototype.getWidthLeft = function () {
20417  return this.width / 2;
20418};
20419
20420/**
20421 * Return the width of the item right from its start date
20422 * @return {number}
20423 */
20424BoxItem.prototype.getWidthRight = function () {
20425  return this.width / 2;
20426};
20427
20428module.exports = BoxItem;
20429
20430/***/ }),
20431/* 102 */
20432/***/ (function(module, exports, __webpack_require__) {
20433
20434"use strict";
20435
20436
20437var Item = __webpack_require__(38);
20438
20439/**
20440 * @constructor PointItem
20441 * @extends Item
20442 * @param {Object} data             Object containing parameters start
20443 *                                  content, className.
20444 * @param {{toScreen: function, toTime: function}} conversion
20445 *                                  Conversion functions from time to screen and vice versa
20446 * @param {Object} [options]        Configuration options
20447 *                                  // TODO: describe available options
20448 */
20449function PointItem(data, conversion, options) {
20450  this.props = {
20451    dot: {
20452      top: 0,
20453      width: 0,
20454      height: 0
20455    },
20456    content: {
20457      height: 0,
20458      marginLeft: 0,
20459      marginRight: 0
20460    }
20461  };
20462  this.options = options;
20463  // validate data
20464  if (data) {
20465    if (data.start == undefined) {
20466      throw new Error('Property "start" missing in item ' + data);
20467    }
20468  }
20469
20470  Item.call(this, data, conversion, options);
20471}
20472
20473PointItem.prototype = new Item(null, null, null);
20474
20475/**
20476 * Check whether this item is visible inside given range
20477 * @param {{start: number, end: number}} range with a timestamp for start and end
20478 * @returns {boolean} True if visible
20479 */
20480PointItem.prototype.isVisible = function (range) {
20481  // determine visibility
20482  var widthInMs = this.width * range.getMillisecondsPerPixel();
20483
20484  return this.data.start.getTime() + widthInMs > range.start && this.data.start < range.end;
20485};
20486
20487PointItem.prototype._createDomElement = function () {
20488  if (!this.dom) {
20489    // create DOM
20490    this.dom = {};
20491
20492    // background box
20493    this.dom.point = document.createElement('div');
20494    // className is updated in redraw()
20495
20496    // contents box, right from the dot
20497    this.dom.content = document.createElement('div');
20498    this.dom.content.className = 'vis-item-content';
20499    this.dom.point.appendChild(this.dom.content);
20500
20501    // dot at start
20502    this.dom.dot = document.createElement('div');
20503    this.dom.point.appendChild(this.dom.dot);
20504
20505    // attach this item as attribute
20506    this.dom.point['timeline-item'] = this;
20507
20508    this.dirty = true;
20509  }
20510};
20511
20512PointItem.prototype._appendDomElement = function () {
20513  if (!this.parent) {
20514    throw new Error('Cannot redraw item: no parent attached');
20515  }
20516  if (!this.dom.point.parentNode) {
20517    var foreground = this.parent.dom.foreground;
20518    if (!foreground) {
20519      throw new Error('Cannot redraw item: parent has no foreground container element');
20520    }
20521    foreground.appendChild(this.dom.point);
20522  }
20523  this.displayed = true;
20524};
20525
20526PointItem.prototype._updateDirtyDomComponents = function () {
20527  // An item is marked dirty when:
20528  // - the item is not yet rendered
20529  // - the item's data is changed
20530  // - the item is selected/deselected
20531  if (this.dirty) {
20532    this._updateContents(this.dom.content);
20533    this._updateDataAttributes(this.dom.point);
20534    this._updateStyle(this.dom.point);
20535
20536    var editable = this.editable.updateTime || this.editable.updateGroup;
20537    // update class
20538    var className = (this.data.className ? ' ' + this.data.className : '') + (this.selected ? ' vis-selected' : '') + (editable ? ' vis-editable' : ' vis-readonly');
20539    this.dom.point.className = 'vis-item vis-point' + className;
20540    this.dom.dot.className = 'vis-item vis-dot' + className;
20541  }
20542};
20543
20544PointItem.prototype._getDomComponentsSizes = function () {
20545  return {
20546    dot: {
20547      width: this.dom.dot.offsetWidth,
20548      height: this.dom.dot.offsetHeight
20549    },
20550    content: {
20551      width: this.dom.content.offsetWidth,
20552      height: this.dom.content.offsetHeight
20553    },
20554    point: {
20555      width: this.dom.point.offsetWidth,
20556      height: this.dom.point.offsetHeight
20557    }
20558  };
20559};
20560
20561PointItem.prototype._updateDomComponentsSizes = function (sizes) {
20562  // recalculate size of dot and contents
20563  this.props.dot.width = sizes.dot.width;
20564  this.props.dot.height = sizes.dot.height;
20565  this.props.content.height = sizes.content.height;
20566
20567  // resize contents
20568  if (this.options.rtl) {
20569    this.dom.content.style.marginRight = 2 * this.props.dot.width + 'px';
20570  } else {
20571    this.dom.content.style.marginLeft = 2 * this.props.dot.width + 'px';
20572  }
20573  //this.dom.content.style.marginRight = ... + 'px'; // TODO: margin right
20574
20575  // recalculate size
20576  this.width = sizes.point.width;
20577  this.height = sizes.point.height;
20578
20579  // reposition the dot
20580  this.dom.dot.style.top = (this.height - this.props.dot.height) / 2 + 'px';
20581  if (this.options.rtl) {
20582    this.dom.dot.style.right = this.props.dot.width / 2 + 'px';
20583  } else {
20584    this.dom.dot.style.left = this.props.dot.width / 2 + 'px';
20585  }
20586
20587  this.dirty = false;
20588};
20589
20590PointItem.prototype._repaintDomAdditionals = function () {
20591  this._repaintOnItemUpdateTimeTooltip(this.dom.point);
20592  this._repaintDragCenter();
20593  this._repaintDeleteButton(this.dom.point);
20594};
20595
20596/**
20597 * Repaint the item
20598 * @param {boolean} [returnQueue=false]  return the queue
20599 * @return {boolean} the redraw queue if returnQueue=true
20600 */
20601PointItem.prototype.redraw = function (returnQueue) {
20602  var sizes;
20603  var queue = [
20604  // create item DOM
20605  this._createDomElement.bind(this),
20606
20607  // append DOM to parent DOM
20608  this._appendDomElement.bind(this),
20609
20610  // update dirty DOM
20611  this._updateDirtyDomComponents.bind(this), function () {
20612    if (this.dirty) {
20613      sizes = this._getDomComponentsSizes();
20614    }
20615  }.bind(this), function () {
20616    if (this.dirty) {
20617      this._updateDomComponentsSizes.bind(this)(sizes);
20618    }
20619  }.bind(this),
20620
20621  // repaint DOM additionals
20622  this._repaintDomAdditionals.bind(this)];
20623
20624  if (returnQueue) {
20625    return queue;
20626  } else {
20627    var result;
20628    queue.forEach(function (fn) {
20629      result = fn();
20630    });
20631    return result;
20632  }
20633};
20634
20635/**
20636 * Show the item in the DOM (when not already visible). The items DOM will
20637 * be created when needed.
20638 */
20639PointItem.prototype.show = function () {
20640  if (!this.displayed) {
20641    this.redraw();
20642  }
20643};
20644
20645/**
20646 * Hide the item from the DOM (when visible)
20647 */
20648PointItem.prototype.hide = function () {
20649  if (this.displayed) {
20650    if (this.dom.point.parentNode) {
20651      this.dom.point.parentNode.removeChild(this.dom.point);
20652    }
20653
20654    this.displayed = false;
20655  }
20656};
20657
20658/**
20659 * Reposition the item horizontally
20660 * @Override
20661 */
20662PointItem.prototype.repositionX = function () {
20663  var start = this.conversion.toScreen(this.data.start);
20664
20665  if (this.options.rtl) {
20666    this.right = start - this.props.dot.width;
20667
20668    // reposition point
20669    this.dom.point.style.right = this.right + 'px';
20670  } else {
20671    this.left = start - this.props.dot.width;
20672
20673    // reposition point
20674    this.dom.point.style.left = this.left + 'px';
20675  }
20676};
20677
20678/**
20679 * Reposition the item vertically
20680 * @Override
20681 */
20682PointItem.prototype.repositionY = function () {
20683  var orientation = this.options.orientation.item;
20684  var point = this.dom.point;
20685  if (orientation == 'top') {
20686    point.style.top = this.top + 'px';
20687  } else {
20688    point.style.top = this.parent.height - this.top - this.height + 'px';
20689  }
20690};
20691
20692/**
20693 * Return the width of the item left from its start date
20694 * @return {number}
20695 */
20696PointItem.prototype.getWidthLeft = function () {
20697  return this.props.dot.width;
20698};
20699
20700/**
20701 * Return the width of the item right from  its start date
20702 * @return {number}
20703 */
20704PointItem.prototype.getWidthRight = function () {
20705  return this.props.dot.width;
20706};
20707
20708module.exports = PointItem;
20709
20710/***/ }),
20711/* 103 */
20712/***/ (function(module, exports, __webpack_require__) {
20713
20714"use strict";
20715
20716
20717var Item = __webpack_require__(38);
20718var BackgroundGroup = __webpack_require__(69);
20719var RangeItem = __webpack_require__(70);
20720
20721/**
20722 * @constructor BackgroundItem
20723 * @extends Item
20724 * @param {Object} data             Object containing parameters start, end
20725 *                                  content, className.
20726 * @param {{toScreen: function, toTime: function}} conversion
20727 *                                  Conversion functions from time to screen and vice versa
20728 * @param {Object} [options]        Configuration options
20729 *                                  // TODO: describe options
20730 * // TODO: implement support for the BackgroundItem just having a start, then being displayed as a sort of an annotation
20731 */
20732function BackgroundItem(data, conversion, options) {
20733  this.props = {
20734    content: {
20735      width: 0
20736    }
20737  };
20738  this.overflow = false; // if contents can overflow (css styling), this flag is set to true
20739
20740  // validate data
20741  if (data) {
20742    if (data.start == undefined) {
20743      throw new Error('Property "start" missing in item ' + data.id);
20744    }
20745    if (data.end == undefined) {
20746      throw new Error('Property "end" missing in item ' + data.id);
20747    }
20748  }
20749
20750  Item.call(this, data, conversion, options);
20751}
20752
20753BackgroundItem.prototype = new Item(null, null, null);
20754
20755BackgroundItem.prototype.baseClassName = 'vis-item vis-background';
20756
20757BackgroundItem.prototype.stack = false;
20758
20759/**
20760 * Check whether this item is visible inside given range
20761 * @param {vis.Range} range with a timestamp for start and end
20762 * @returns {boolean} True if visible
20763 */
20764BackgroundItem.prototype.isVisible = function (range) {
20765  // determine visibility
20766  return this.data.start < range.end && this.data.end > range.start;
20767};
20768
20769BackgroundItem.prototype._createDomElement = function () {
20770  if (!this.dom) {
20771    // create DOM
20772    this.dom = {};
20773
20774    // background box
20775    this.dom.box = document.createElement('div');
20776    // className is updated in redraw()
20777
20778    // frame box (to prevent the item contents from overflowing
20779    this.dom.frame = document.createElement('div');
20780    this.dom.frame.className = 'vis-item-overflow';
20781    this.dom.box.appendChild(this.dom.frame);
20782
20783    // contents box
20784    this.dom.content = document.createElement('div');
20785    this.dom.content.className = 'vis-item-content';
20786    this.dom.frame.appendChild(this.dom.content);
20787
20788    // Note: we do NOT attach this item as attribute to the DOM,
20789    //       such that background items cannot be selected
20790    //this.dom.box['timeline-item'] = this;
20791
20792    this.dirty = true;
20793  }
20794};
20795
20796BackgroundItem.prototype._appendDomElement = function () {
20797  if (!this.parent) {
20798    throw new Error('Cannot redraw item: no parent attached');
20799  }
20800  if (!this.dom.box.parentNode) {
20801    var background = this.parent.dom.background;
20802    if (!background) {
20803      throw new Error('Cannot redraw item: parent has no background container element');
20804    }
20805    background.appendChild(this.dom.box);
20806  }
20807  this.displayed = true;
20808};
20809
20810BackgroundItem.prototype._updateDirtyDomComponents = function () {
20811  // update dirty DOM. An item is marked dirty when:
20812  // - the item is not yet rendered
20813  // - the item's data is changed
20814  // - the item is selected/deselected
20815  if (this.dirty) {
20816    this._updateContents(this.dom.content);
20817    this._updateDataAttributes(this.dom.content);
20818    this._updateStyle(this.dom.box);
20819
20820    // update class
20821    var className = (this.data.className ? ' ' + this.data.className : '') + (this.selected ? ' vis-selected' : '');
20822    this.dom.box.className = this.baseClassName + className;
20823  }
20824};
20825
20826BackgroundItem.prototype._getDomComponentsSizes = function () {
20827  // determine from css whether this box has overflow
20828  this.overflow = window.getComputedStyle(this.dom.content).overflow !== 'hidden';
20829  return {
20830    content: {
20831      width: this.dom.content.offsetWidth
20832    }
20833  };
20834};
20835
20836BackgroundItem.prototype._updateDomComponentsSizes = function (sizes) {
20837  // recalculate size
20838  this.props.content.width = sizes.content.width;
20839  this.height = 0; // set height zero, so this item will be ignored when stacking items
20840
20841  this.dirty = false;
20842};
20843
20844BackgroundItem.prototype._repaintDomAdditionals = function () {};
20845
20846/**
20847 * Repaint the item
20848 * @param {boolean} [returnQueue=false]  return the queue
20849 * @return {boolean} the redraw result or the redraw queue if returnQueue=true
20850 */
20851BackgroundItem.prototype.redraw = function (returnQueue) {
20852  var sizes;
20853  var queue = [
20854  // create item DOM
20855  this._createDomElement.bind(this),
20856
20857  // append DOM to parent DOM
20858  this._appendDomElement.bind(this), this._updateDirtyDomComponents.bind(this), function () {
20859    if (this.dirty) {
20860      sizes = this._getDomComponentsSizes.bind(this)();
20861    }
20862  }.bind(this), function () {
20863    if (this.dirty) {
20864      this._updateDomComponentsSizes.bind(this)(sizes);
20865    }
20866  }.bind(this),
20867
20868  // repaint DOM additionals
20869  this._repaintDomAdditionals.bind(this)];
20870
20871  if (returnQueue) {
20872    return queue;
20873  } else {
20874    var result;
20875    queue.forEach(function (fn) {
20876      result = fn();
20877    });
20878    return result;
20879  }
20880};
20881
20882/**
20883 * Show the item in the DOM (when not already visible). The items DOM will
20884 * be created when needed.
20885 */
20886BackgroundItem.prototype.show = RangeItem.prototype.show;
20887
20888/**
20889 * Hide the item from the DOM (when visible)
20890 * @return {Boolean} changed
20891 */
20892BackgroundItem.prototype.hide = RangeItem.prototype.hide;
20893
20894/**
20895 * Reposition the item horizontally
20896 * @Override
20897 */
20898BackgroundItem.prototype.repositionX = RangeItem.prototype.repositionX;
20899
20900/**
20901 * Reposition the item vertically
20902 * @Override
20903 */
20904BackgroundItem.prototype.repositionY = function (margin) {
20905  // eslint-disable-line no-unused-vars
20906  var height;
20907  var orientation = this.options.orientation.item;
20908
20909  // special positioning for subgroups
20910  if (this.data.subgroup !== undefined) {
20911    // TODO: instead of calculating the top position of the subgroups here for every BackgroundItem, calculate the top of the subgroup once in Itemset
20912    var itemSubgroup = this.data.subgroup;
20913
20914    this.dom.box.style.height = this.parent.subgroups[itemSubgroup].height + 'px';
20915
20916    if (orientation == 'top') {
20917      this.dom.box.style.top = this.parent.top + this.parent.subgroups[itemSubgroup].top + 'px';
20918    } else {
20919      this.dom.box.style.top = this.parent.top + this.parent.height - this.parent.subgroups[itemSubgroup].top - this.parent.subgroups[itemSubgroup].height + 'px';
20920    }
20921    this.dom.box.style.bottom = '';
20922  }
20923  // and in the case of no subgroups:
20924  else {
20925      // we want backgrounds with groups to only show in groups.
20926      if (this.parent instanceof BackgroundGroup) {
20927        // if the item is not in a group:
20928        height = Math.max(this.parent.height, this.parent.itemSet.body.domProps.center.height, this.parent.itemSet.body.domProps.centerContainer.height);
20929        this.dom.box.style.bottom = orientation == 'bottom' ? '0' : '';
20930        this.dom.box.style.top = orientation == 'top' ? '0' : '';
20931      } else {
20932        height = this.parent.height;
20933        // same alignment for items when orientation is top or bottom
20934        this.dom.box.style.top = this.parent.top + 'px';
20935        this.dom.box.style.bottom = '';
20936      }
20937    }
20938  this.dom.box.style.height = height + 'px';
20939};
20940
20941module.exports = BackgroundItem;
20942
20943/***/ }),
20944/* 104 */
20945/***/ (function(module, exports, __webpack_require__) {
20946
20947"use strict";
20948
20949
20950Object.defineProperty(exports, "__esModule", {
20951  value: true
20952});
20953
20954var _classCallCheck2 = __webpack_require__(0);
20955
20956var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
20957
20958var _createClass2 = __webpack_require__(1);
20959
20960var _createClass3 = _interopRequireDefault(_createClass2);
20961
20962function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
20963
20964/**
20965 * Popup is a class to create a popup window with some text
20966 */
20967var Popup = function () {
20968  /**
20969   * @param {Element} container       The container object.
20970   * @param {string}  overflowMethod  How the popup should act to overflowing ('flip' or 'cap')
20971   */
20972  function Popup(container, overflowMethod) {
20973    (0, _classCallCheck3['default'])(this, Popup);
20974
20975    this.container = container;
20976    this.overflowMethod = overflowMethod || 'cap';
20977
20978    this.x = 0;
20979    this.y = 0;
20980    this.padding = 5;
20981    this.hidden = false;
20982
20983    // create the frame
20984    this.frame = document.createElement('div');
20985    this.frame.className = 'vis-tooltip';
20986    this.container.appendChild(this.frame);
20987  }
20988
20989  /**
20990   * @param {number} x   Horizontal position of the popup window
20991   * @param {number} y   Vertical position of the popup window
20992   */
20993
20994
20995  (0, _createClass3['default'])(Popup, [{
20996    key: 'setPosition',
20997    value: function setPosition(x, y) {
20998      this.x = parseInt(x);
20999      this.y = parseInt(y);
21000    }
21001
21002    /**
21003     * Set the content for the popup window. This can be HTML code or text.
21004     * @param {string | Element} content
21005     */
21006
21007  }, {
21008    key: 'setText',
21009    value: function setText(content) {
21010      if (content instanceof Element) {
21011        this.frame.innerHTML = '';
21012        this.frame.appendChild(content);
21013      } else {
21014        this.frame.innerHTML = content; // string containing text or HTML
21015      }
21016    }
21017
21018    /**
21019     * Show the popup window
21020     * @param {boolean} [doShow]    Show or hide the window
21021     */
21022
21023  }, {
21024    key: 'show',
21025    value: function show(doShow) {
21026      if (doShow === undefined) {
21027        doShow = true;
21028      }
21029
21030      if (doShow === true) {
21031        var height = this.frame.clientHeight;
21032        var width = this.frame.clientWidth;
21033        var maxHeight = this.frame.parentNode.clientHeight;
21034        var maxWidth = this.frame.parentNode.clientWidth;
21035
21036        var left = 0,
21037            top = 0;
21038
21039        if (this.overflowMethod == 'flip') {
21040          var isLeft = false,
21041              isTop = true; // Where around the position it's located
21042
21043          if (this.y - height < this.padding) {
21044            isTop = false;
21045          }
21046
21047          if (this.x + width > maxWidth - this.padding) {
21048            isLeft = true;
21049          }
21050
21051          if (isLeft) {
21052            left = this.x - width;
21053          } else {
21054            left = this.x;
21055          }
21056
21057          if (isTop) {
21058            top = this.y - height;
21059          } else {
21060            top = this.y;
21061          }
21062        } else {
21063          top = this.y - height;
21064          if (top + height + this.padding > maxHeight) {
21065            top = maxHeight - height - this.padding;
21066          }
21067          if (top < this.padding) {
21068            top = this.padding;
21069          }
21070
21071          left = this.x;
21072          if (left + width + this.padding > maxWidth) {
21073            left = maxWidth - width - this.padding;
21074          }
21075          if (left < this.padding) {
21076            left = this.padding;
21077          }
21078        }
21079
21080        this.frame.style.left = left + "px";
21081        this.frame.style.top = top + "px";
21082        this.frame.style.visibility = "visible";
21083        this.hidden = false;
21084      } else {
21085        this.hide();
21086      }
21087    }
21088
21089    /**
21090     * Hide the popup window
21091     */
21092
21093  }, {
21094    key: 'hide',
21095    value: function hide() {
21096      this.hidden = true;
21097      this.frame.style.left = "0";
21098      this.frame.style.top = "0";
21099      this.frame.style.visibility = "hidden";
21100    }
21101
21102    /**
21103     * Remove the popup window
21104     */
21105
21106  }, {
21107    key: 'destroy',
21108    value: function destroy() {
21109      this.frame.parentNode.removeChild(this.frame); // Remove element from DOM
21110    }
21111  }]);
21112  return Popup;
21113}();
21114
21115exports['default'] = Popup;
21116
21117/***/ }),
21118/* 105 */
21119/***/ (function(module, exports, __webpack_require__) {
21120
21121"use strict";
21122
21123
21124Object.defineProperty(exports, "__esModule", {
21125  value: true
21126});
21127/**
21128 * This object contains all possible options. It will check if the types are correct, if required if the option is one
21129 * of the allowed values.
21130 *
21131 * __any__ means that the name of the property does not matter.
21132 * __type__ is a required field for all objects and contains the allowed types of all objects
21133 */
21134var string = 'string';
21135var bool = 'boolean';
21136var number = 'number';
21137var array = 'array';
21138var date = 'date';
21139var object = 'object'; // should only be in a __type__ property
21140var dom = 'dom';
21141var moment = 'moment';
21142var any = 'any';
21143
21144var allOptions = {
21145  configure: {
21146    enabled: { 'boolean': bool },
21147    filter: { 'boolean': bool, 'function': 'function' },
21148    container: { dom: dom },
21149    __type__: { object: object, 'boolean': bool, 'function': 'function' }
21150  },
21151
21152  //globals :
21153  align: { string: string },
21154  rtl: { 'boolean': bool, 'undefined': 'undefined' },
21155  rollingMode: {
21156    follow: { 'boolean': bool },
21157    offset: { number: number, 'undefined': 'undefined' },
21158    __type__: { object: object }
21159  },
21160  verticalScroll: { 'boolean': bool, 'undefined': 'undefined' },
21161  horizontalScroll: { 'boolean': bool, 'undefined': 'undefined' },
21162  autoResize: { 'boolean': bool },
21163  throttleRedraw: { number: number }, // TODO: DEPRICATED see https://github.com/almende/vis/issues/2511
21164  clickToUse: { 'boolean': bool },
21165  dataAttributes: { string: string, array: array },
21166  editable: {
21167    add: { 'boolean': bool, 'undefined': 'undefined' },
21168    remove: { 'boolean': bool, 'undefined': 'undefined' },
21169    updateGroup: { 'boolean': bool, 'undefined': 'undefined' },
21170    updateTime: { 'boolean': bool, 'undefined': 'undefined' },
21171    overrideItems: { 'boolean': bool, 'undefined': 'undefined' },
21172    __type__: { 'boolean': bool, object: object }
21173  },
21174  end: { number: number, date: date, string: string, moment: moment },
21175  format: {
21176    minorLabels: {
21177      millisecond: { string: string, 'undefined': 'undefined' },
21178      second: { string: string, 'undefined': 'undefined' },
21179      minute: { string: string, 'undefined': 'undefined' },
21180      hour: { string: string, 'undefined': 'undefined' },
21181      weekday: { string: string, 'undefined': 'undefined' },
21182      day: { string: string, 'undefined': 'undefined' },
21183      week: { string: string, 'undefined': 'undefined' },
21184      month: { string: string, 'undefined': 'undefined' },
21185      year: { string: string, 'undefined': 'undefined' },
21186      __type__: { object: object, 'function': 'function' }
21187    },
21188    majorLabels: {
21189      millisecond: { string: string, 'undefined': 'undefined' },
21190      second: { string: string, 'undefined': 'undefined' },
21191      minute: { string: string, 'undefined': 'undefined' },
21192      hour: { string: string, 'undefined': 'undefined' },
21193      weekday: { string: string, 'undefined': 'undefined' },
21194      day: { string: string, 'undefined': 'undefined' },
21195      week: { string: string, 'undefined': 'undefined' },
21196      month: { string: string, 'undefined': 'undefined' },
21197      year: { string: string, 'undefined': 'undefined' },
21198      __type__: { object: object, 'function': 'function' }
21199    },
21200    __type__: { object: object }
21201  },
21202  moment: { 'function': 'function' },
21203  groupOrder: { string: string, 'function': 'function' },
21204  groupEditable: {
21205    add: { 'boolean': bool, 'undefined': 'undefined' },
21206    remove: { 'boolean': bool, 'undefined': 'undefined' },
21207    order: { 'boolean': bool, 'undefined': 'undefined' },
21208    __type__: { 'boolean': bool, object: object }
21209  },
21210  groupOrderSwap: { 'function': 'function' },
21211  height: { string: string, number: number },
21212  hiddenDates: {
21213    start: { date: date, number: number, string: string, moment: moment },
21214    end: { date: date, number: number, string: string, moment: moment },
21215    repeat: { string: string },
21216    __type__: { object: object, array: array }
21217  },
21218  itemsAlwaysDraggable: {
21219    item: { 'boolean': bool, 'undefined': 'undefined' },
21220    range: { 'boolean': bool, 'undefined': 'undefined' },
21221    __type__: { 'boolean': bool, object: object }
21222  },
21223  limitSize: { 'boolean': bool },
21224  locale: { string: string },
21225  locales: {
21226    __any__: { any: any },
21227    __type__: { object: object }
21228  },
21229  margin: {
21230    axis: { number: number },
21231    item: {
21232      horizontal: { number: number, 'undefined': 'undefined' },
21233      vertical: { number: number, 'undefined': 'undefined' },
21234      __type__: { object: object, number: number }
21235    },
21236    __type__: { object: object, number: number }
21237  },
21238  max: { date: date, number: number, string: string, moment: moment },
21239  maxHeight: { number: number, string: string },
21240  maxMinorChars: { number: number },
21241  min: { date: date, number: number, string: string, moment: moment },
21242  minHeight: { number: number, string: string },
21243  moveable: { 'boolean': bool },
21244  multiselect: { 'boolean': bool },
21245  multiselectPerGroup: { 'boolean': bool },
21246  onAdd: { 'function': 'function' },
21247  onDropObjectOnItem: { 'function': 'function' },
21248  onUpdate: { 'function': 'function' },
21249  onMove: { 'function': 'function' },
21250  onMoving: { 'function': 'function' },
21251  onRemove: { 'function': 'function' },
21252  onAddGroup: { 'function': 'function' },
21253  onMoveGroup: { 'function': 'function' },
21254  onRemoveGroup: { 'function': 'function' },
21255  onInitialDrawComplete: { 'function': 'function' },
21256  order: { 'function': 'function' },
21257  orientation: {
21258    axis: { string: string, 'undefined': 'undefined' },
21259    item: { string: string, 'undefined': 'undefined' },
21260    __type__: { string: string, object: object }
21261  },
21262  selectable: { 'boolean': bool },
21263  showCurrentTime: { 'boolean': bool },
21264  showMajorLabels: { 'boolean': bool },
21265  showMinorLabels: { 'boolean': bool },
21266  stack: { 'boolean': bool },
21267  stackSubgroups: { 'boolean': bool },
21268  snap: { 'function': 'function', 'null': 'null' },
21269  start: { date: date, number: number, string: string, moment: moment },
21270  template: { 'function': 'function' },
21271  groupTemplate: { 'function': 'function' },
21272  visibleFrameTemplate: { string: string, 'function': 'function' },
21273  showTooltips: { 'boolean': bool },
21274  tooltip: {
21275    followMouse: { 'boolean': bool },
21276    overflowMethod: { 'string': ['cap', 'flip'] },
21277    __type__: { object: object }
21278  },
21279  tooltipOnItemUpdateTime: {
21280    template: { 'function': 'function' },
21281    __type__: { 'boolean': bool, object: object }
21282  },
21283  timeAxis: {
21284    scale: { string: string, 'undefined': 'undefined' },
21285    step: { number: number, 'undefined': 'undefined' },
21286    __type__: { object: object }
21287  },
21288  type: { string: string },
21289  width: { string: string, number: number },
21290  zoomable: { 'boolean': bool },
21291  zoomKey: { string: ['ctrlKey', 'altKey', 'metaKey', ''] },
21292  zoomMax: { number: number },
21293  zoomMin: { number: number },
21294
21295  __type__: { object: object }
21296};
21297
21298var configureOptions = {
21299  global: {
21300    align: ['center', 'left', 'right'],
21301    direction: false,
21302    autoResize: true,
21303    clickToUse: false,
21304    // dataAttributes: ['all'], // FIXME: can be 'all' or string[]
21305    editable: {
21306      add: false,
21307      remove: false,
21308      updateGroup: false,
21309      updateTime: false
21310    },
21311    end: '',
21312    format: {
21313      minorLabels: {
21314        millisecond: 'SSS',
21315        second: 's',
21316        minute: 'HH:mm',
21317        hour: 'HH:mm',
21318        weekday: 'ddd D',
21319        day: 'D',
21320        week: 'w',
21321        month: 'MMM',
21322        year: 'YYYY'
21323      },
21324      majorLabels: {
21325        millisecond: 'HH:mm:ss',
21326        second: 'D MMMM HH:mm',
21327        minute: 'ddd D MMMM',
21328        hour: 'ddd D MMMM',
21329        weekday: 'MMMM YYYY',
21330        day: 'MMMM YYYY',
21331        week: 'MMMM YYYY',
21332        month: 'YYYY',
21333        year: ''
21334      }
21335    },
21336
21337    //groupOrder: {string, 'function': 'function'},
21338    groupsDraggable: false,
21339    height: '',
21340    //hiddenDates: {object, array},
21341    locale: '',
21342    margin: {
21343      axis: [20, 0, 100, 1],
21344      item: {
21345        horizontal: [10, 0, 100, 1],
21346        vertical: [10, 0, 100, 1]
21347      }
21348    },
21349    max: '',
21350    maxHeight: '',
21351    maxMinorChars: [7, 0, 20, 1],
21352    min: '',
21353    minHeight: '',
21354    moveable: false,
21355    multiselect: false,
21356    multiselectPerGroup: false,
21357    //onAdd: {'function': 'function'},
21358    //onUpdate: {'function': 'function'},
21359    //onMove: {'function': 'function'},
21360    //onMoving: {'function': 'function'},
21361    //onRename: {'function': 'function'},
21362    //order: {'function': 'function'},
21363    orientation: {
21364      axis: ['both', 'bottom', 'top'],
21365      item: ['bottom', 'top']
21366    },
21367    selectable: true,
21368    showCurrentTime: false,
21369    showMajorLabels: true,
21370    showMinorLabels: true,
21371    stack: true,
21372    stackSubgroups: true,
21373    //snap: {'function': 'function', nada},
21374    start: '',
21375    //template: {'function': 'function'},
21376    //timeAxis: {
21377    //  scale: ['millisecond', 'second', 'minute', 'hour', 'weekday', 'day', 'week', 'month', 'year'],
21378    //  step: [1, 1, 10, 1]
21379    //},
21380    showTooltips: true,
21381    tooltip: {
21382      followMouse: false,
21383      overflowMethod: 'flip'
21384    },
21385    tooltipOnItemUpdateTime: false,
21386    type: ['box', 'point', 'range', 'background'],
21387    width: '100%',
21388    zoomable: true,
21389    zoomKey: ['ctrlKey', 'altKey', 'metaKey', ''],
21390    zoomMax: [315360000000000, 10, 315360000000000, 1],
21391    zoomMin: [10, 10, 315360000000000, 1]
21392  }
21393};
21394
21395exports.allOptions = allOptions;
21396exports.configureOptions = configureOptions;
21397
21398/***/ }),
21399/* 106 */
21400/***/ (function(module, exports, __webpack_require__) {
21401
21402"use strict";
21403
21404
21405var _typeof2 = __webpack_require__(6);
21406
21407var _typeof3 = _interopRequireDefault(_typeof2);
21408
21409function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
21410
21411var util = __webpack_require__(2);
21412var DOMutil = __webpack_require__(14);
21413var DataSet = __webpack_require__(11);
21414var DataView = __webpack_require__(12);
21415var Component = __webpack_require__(16);
21416var DataAxis = __webpack_require__(107);
21417var GraphGroup = __webpack_require__(109);
21418var Legend = __webpack_require__(112);
21419var Bars = __webpack_require__(110);
21420var Lines = __webpack_require__(111);
21421var Points = __webpack_require__(72);
21422
21423var UNGROUPED = '__ungrouped__'; // reserved group id for ungrouped items
21424
21425/**
21426 * This is the constructor of the LineGraph. It requires a Timeline body and options.
21427 *
21428 * @param {vis.Timeline.body} body
21429 * @param {Object} options
21430 * @constructor LineGraph
21431 * @extends Component
21432 */
21433function LineGraph(body, options) {
21434  this.id = util.randomUUID();
21435  this.body = body;
21436
21437  this.defaultOptions = {
21438    yAxisOrientation: 'left',
21439    defaultGroup: 'default',
21440    sort: true,
21441    sampling: true,
21442    stack: false,
21443    graphHeight: '400px',
21444    shaded: {
21445      enabled: false,
21446      orientation: 'bottom' // top, bottom, zero
21447    },
21448    style: 'line', // line, bar
21449    barChart: {
21450      width: 50,
21451      sideBySide: false,
21452      align: 'center' // left, center, right
21453    },
21454    interpolation: {
21455      enabled: true,
21456      parametrization: 'centripetal', // uniform (alpha = 0.0), chordal (alpha = 1.0), centripetal (alpha = 0.5)
21457      alpha: 0.5
21458    },
21459    drawPoints: {
21460      enabled: true,
21461      size: 6,
21462      style: 'square' // square, circle
21463    },
21464    dataAxis: {}, //Defaults are done on DataAxis level
21465    legend: {}, //Defaults are done on Legend level
21466    groups: {
21467      visibility: {}
21468    }
21469  };
21470
21471  // options is shared by this lineGraph and all its items
21472  this.options = util.extend({}, this.defaultOptions);
21473  this.dom = {};
21474  this.props = {};
21475  this.hammer = null;
21476  this.groups = {};
21477  this.abortedGraphUpdate = false;
vendor: 5,170 bytes, lines 21478-21621
21478  this.updateSVGheight = false;
21479  this.updateSVGheightOnResize = false;
21480  this.forceGraphUpdate = true;
21481
21482  var me = this;
21483  this.itemsData = null; // DataSet
21484  this.groupsData = null; // DataSet
21485
21486  // listeners for the DataSet of the items
21487  this.itemListeners = {
21488    'add': function add(event, params, senderId) {
21489      // eslint-disable-line no-unused-vars
21490      me._onAdd(params.items);
21491    },
21492    'update': function update(event, params, senderId) {
21493      // eslint-disable-line no-unused-vars
21494      me._onUpdate(params.items);
21495    },
21496    'remove': function remove(event, params, senderId) {
21497      // eslint-disable-line no-unused-vars
21498      me._onRemove(params.items);
21499    }
21500  };
21501
21502  // listeners for the DataSet of the groups
21503  this.groupListeners = {
21504    'add': function add(event, params, senderId) {
21505      // eslint-disable-line no-unused-vars
21506      me._onAddGroups(params.items);
21507    },
21508    'update': function update(event, params, senderId) {
21509      // eslint-disable-line no-unused-vars
21510      me._onUpdateGroups(params.items);
21511    },
21512    'remove': function remove(event, params, senderId) {
21513      // eslint-disable-line no-unused-vars
21514      me._onRemoveGroups(params.items);
21515    }
21516  };
21517
21518  this.items = {}; // object with an Item for every data item
21519  this.selection = []; // list with the ids of all selected nodes
21520  this.lastStart = this.body.range.start;
21521  this.touchParams = {}; // stores properties while dragging
21522
21523  this.svgElements = {};
21524  this.setOptions(options);
21525  this.groupsUsingDefaultStyles = [0];
21526  this.body.emitter.on('rangechanged', function () {
21527    me.lastStart = me.body.range.start;
21528    me.svg.style.left = util.option.asSize(-me.props.width);
21529
21530    me.forceGraphUpdate = true;
21531    //Is this local redraw necessary? (Core also does a change event!)
21532    me.redraw.call(me);
21533  });
21534
21535  // create the HTML DOM
21536  this._create();
21537  this.framework = { svg: this.svg, svgElements: this.svgElements, options: this.options, groups: this.groups };
21538}
21539
21540LineGraph.prototype = new Component();
21541
21542/**
21543 * Create the HTML DOM for the ItemSet
21544 */
21545LineGraph.prototype._create = function () {
21546  var frame = document.createElement('div');
21547  frame.className = 'vis-line-graph';
21548  this.dom.frame = frame;
21549
21550  // create svg element for graph drawing.
21551  this.svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
21552  this.svg.style.position = 'relative';
21553  this.svg.style.height = ('' + this.options.graphHeight).replace('px', '') + 'px';
21554  this.svg.style.display = 'block';
21555  frame.appendChild(this.svg);
21556
21557  // data axis
21558  this.options.dataAxis.orientation = 'left';
21559  this.yAxisLeft = new DataAxis(this.body, this.options.dataAxis, this.svg, this.options.groups);
21560
21561  this.options.dataAxis.orientation = 'right';
21562  this.yAxisRight = new DataAxis(this.body, this.options.dataAxis, this.svg, this.options.groups);
21563  delete this.options.dataAxis.orientation;
21564
21565  // legends
21566  this.legendLeft = new Legend(this.body, this.options.legend, 'left', this.options.groups);
21567  this.legendRight = new Legend(this.body, this.options.legend, 'right', this.options.groups);
21568
21569  this.show();
21570};
21571
21572/**
21573 * set the options of the LineGraph. the mergeOptions is used for subObjects that have an enabled element.
21574 * @param {object} options
21575 */
21576LineGraph.prototype.setOptions = function (options) {
21577  if (options) {
21578    var fields = ['sampling', 'defaultGroup', 'stack', 'height', 'graphHeight', 'yAxisOrientation', 'style', 'barChart', 'dataAxis', 'sort', 'groups'];
21579    if (options.graphHeight === undefined && options.height !== undefined) {
21580      this.updateSVGheight = true;
21581      this.updateSVGheightOnResize = true;
21582    } else if (this.body.domProps.centerContainer.height !== undefined && options.graphHeight !== undefined) {
21583      if (parseInt((options.graphHeight + '').replace("px", '')) < this.body.domProps.centerContainer.height) {
21584        this.updateSVGheight = true;
21585      }
21586    }
21587    util.selectiveDeepExtend(fields, this.options, options);
21588    util.mergeOptions(this.options, options, 'interpolation');
21589    util.mergeOptions(this.options, options, 'drawPoints');
21590    util.mergeOptions(this.options, options, 'shaded');
21591    util.mergeOptions(this.options, options, 'legend');
21592
21593    if (options.interpolation) {
21594      if ((0, _typeof3['default'])(options.interpolation) == 'object') {
21595        if (options.interpolation.parametrization) {
21596          if (options.interpolation.parametrization == 'uniform') {
21597            this.options.interpolation.alpha = 0;
21598          } else if (options.interpolation.parametrization == 'chordal') {
21599            this.options.interpolation.alpha = 1.0;
21600          } else {
21601            this.options.interpolation.parametrization = 'centripetal';
21602            this.options.interpolation.alpha = 0.5;
21603          }
21604        }
21605      }
21606    }
21607
21608    if (this.yAxisLeft) {
21609      if (options.dataAxis !== undefined) {
21610        this.yAxisLeft.setOptions(this.options.dataAxis);
21611        this.yAxisRight.setOptions(this.options.dataAxis);
21612      }
21613    }
21614
21615    if (this.legendLeft) {
21616      if (options.legend !== undefined) {
21617        this.legendLeft.setOptions(this.options.legend);
21618        this.legendRight.setOptions(this.options.legend);
21619      }
21620    }
21621
21622    if (this.groups.hasOwnProperty(UNGROUPED)) {
21623      this.groups[UNGROUPED].setOptions(options);
21624    }
21625  }
21626
21627  // this is used to redraw the graph if the visibility of the groups is changed.
21628  if (this.dom.frame) {
21629    //not on initial run?
21630    this.forceGraphUpdate = true;
21631    this.body.emitter.emit("_change", { queue: true });
21632  }
21633};
21634
21635/**
21636 * Hide the component from the DOM
21637 */
21638LineGraph.prototype.hide = function () {
21639  // remove the frame containing the items
21640  if (this.dom.frame.parentNode) {
21641    this.dom.frame.parentNode.removeChild(this.dom.frame);
21642  }
21643};
21644
21645/**
21646 * Show the component in the DOM (when not already visible).
21647 */
21648LineGraph.prototype.show = function () {
21649  // show frame containing the items
21650  if (!this.dom.frame.parentNode) {
21651    this.body.dom.center.appendChild(this.dom.frame);
21652  }
21653};
21654
21655/**
21656 * Set items
21657 * @param {vis.DataSet | null} items
21658 */
21659LineGraph.prototype.setItems = function (items) {
21660  var me = this,
21661      ids,
21662      oldItemsData = this.itemsData;
21663
21664  // replace the dataset
21665  if (!items) {
21666    this.itemsData = null;
21667  } else if (items instanceof DataSet || items instanceof DataView) {
21668    this.itemsData = items;
21669  } else {
21670    throw new TypeError('Data must be an instance of DataSet or DataView');
21671  }
21672
21673  if (oldItemsData) {
21674    // unsubscribe from old dataset
21675    util.forEach(this.itemListeners, function (callback, event) {
21676      oldItemsData.off(event, callback);
21677    });
21678
21679    // remove all drawn items
21680    ids = oldItemsData.getIds();
21681    this._onRemove(ids);
21682  }
21683
21684  if (this.itemsData) {
21685    // subscribe to new dataset
21686    var id = this.id;
21687    util.forEach(this.itemListeners, function (callback, event) {
21688      me.itemsData.on(event, callback, id);
21689    });
21690
21691    // add all new items
21692    ids = this.itemsData.getIds();
21693    this._onAdd(ids);
21694  }
21695};
21696
21697/**
21698 * Set groups
21699 * @param {vis.DataSet} groups
21700 */
21701LineGraph.prototype.setGroups = function (groups) {
21702  var me = this;
21703  var ids;
21704
21705  // unsubscribe from current dataset
21706  if (this.groupsData) {
21707    util.forEach(this.groupListeners, function (callback, event) {
21708      me.groupsData.off(event, callback);
21709    });
21710
21711    // remove all drawn groups
21712    ids = this.groupsData.getIds();
21713    this.groupsData = null;
21714    for (var i = 0; i < ids.length; i++) {
21715      this._removeGroup(ids[i]);
21716    }
21717  }
21718
21719  // replace the dataset
21720  if (!groups) {
21721    this.groupsData = null;
21722  } else if (groups instanceof DataSet || groups instanceof DataView) {
21723    this.groupsData = groups;
21724  } else {
21725    throw new TypeError('Data must be an instance of DataSet or DataView');
21726  }
21727
21728  if (this.groupsData) {
21729    // subscribe to new dataset
21730    var id = this.id;
21731    util.forEach(this.groupListeners, function (callback, event) {
21732      me.groupsData.on(event, callback, id);
21733    });
21734
21735    // draw all ms
21736    ids = this.groupsData.getIds();
21737    this._onAddGroups(ids);
21738  }
21739};
21740
21741LineGraph.prototype._onUpdate = function (ids) {
21742  this._updateAllGroupData(ids);
21743};
21744LineGraph.prototype._onAdd = function (ids) {
21745  this._onUpdate(ids);
21746};
21747LineGraph.prototype._onRemove = function (ids) {
21748  this._onUpdate(ids);
21749};
21750LineGraph.prototype._onUpdateGroups = function (groupIds) {
21751  this._updateAllGroupData(null, groupIds);
21752};
21753LineGraph.prototype._onAddGroups = function (groupIds) {
21754  this._onUpdateGroups(groupIds);
21755};
21756
21757/**
21758 * this cleans the group out off the legends and the dataaxis, updates the ungrouped and updates the graph
21759 * @param {Array} groupIds
21760 * @private
21761 */
21762LineGraph.prototype._onRemoveGroups = function (groupIds) {
21763  for (var i = 0; i < groupIds.length; i++) {
21764    this._removeGroup(groupIds[i]);
21765  }
21766  this.forceGraphUpdate = true;
21767  this.body.emitter.emit("_change", { queue: true });
21768};
21769
21770/**
21771 * this cleans the group out off the legends and the dataaxis
21772 * @param {vis.GraphGroup.id} groupId
21773 * @private
21774 */
21775LineGraph.prototype._removeGroup = function (groupId) {
21776  if (this.groups.hasOwnProperty(groupId)) {
21777    if (this.groups[groupId].options.yAxisOrientation == 'right') {
21778      this.yAxisRight.removeGroup(groupId);
21779      this.legendRight.removeGroup(groupId);
21780      this.legendRight.redraw();
21781    } else {
21782      this.yAxisLeft.removeGroup(groupId);
21783      this.legendLeft.removeGroup(groupId);
21784      this.legendLeft.redraw();
21785    }
21786    delete this.groups[groupId];
21787  }
21788};
21789
21790/**
21791 * update a group object with the group dataset entree
21792 *
21793 * @param {vis.GraphGroup} group
21794 * @param {vis.GraphGroup.id} groupId
21795 * @private
21796 */
21797LineGraph.prototype._updateGroup = function (group, groupId) {
21798  if (!this.groups.hasOwnProperty(groupId)) {
21799    this.groups[groupId] = new GraphGroup(group, groupId, this.options, this.groupsUsingDefaultStyles);
21800    if (this.groups[groupId].options.yAxisOrientation == 'right') {
21801      this.yAxisRight.addGroup(groupId, this.groups[groupId]);
21802      this.legendRight.addGroup(groupId, this.groups[groupId]);
21803    } else {
21804      this.yAxisLeft.addGroup(groupId, this.groups[groupId]);
21805      this.legendLeft.addGroup(groupId, this.groups[groupId]);
21806    }
21807  } else {
21808    this.groups[groupId].update(group);
21809    if (this.groups[groupId].options.yAxisOrientation == 'right') {
21810      this.yAxisRight.updateGroup(groupId, this.groups[groupId]);
21811      this.legendRight.updateGroup(groupId, this.groups[groupId]);
21812      //If yAxisOrientation changed, clean out the group from the other axis.
21813      this.yAxisLeft.removeGroup(groupId);
21814      this.legendLeft.removeGroup(groupId);
21815    } else {
21816      this.yAxisLeft.updateGroup(groupId, this.groups[groupId]);
21817      this.legendLeft.updateGroup(groupId, this.groups[groupId]);
21818      //If yAxisOrientation changed, clean out the group from the other axis.
21819      this.yAxisRight.removeGroup(groupId);
21820      this.legendRight.removeGroup(groupId);
21821    }
21822  }
21823  this.legendLeft.redraw();
21824  this.legendRight.redraw();
21825};
21826
21827/**
21828 * this updates all groups, it is used when there is an update the the itemset.
21829 *
21830 * @param  {Array} ids
21831 * @param  {Array} groupIds
21832 * @private
21833 */
21834LineGraph.prototype._updateAllGroupData = function (ids, groupIds) {
21835  if (this.itemsData != null) {
21836    var groupsContent = {};
21837    var items = this.itemsData.get();
21838    var fieldId = this.itemsData._fieldId;
21839    var idMap = {};
21840    if (ids) {
21841      ids.map(function (id) {
21842        idMap[id] = id;
21843      });
21844    }
21845
21846    //pre-Determine array sizes, for more efficient memory claim
21847    var groupCounts = {};
21848    for (var i = 0; i < items.length; i++) {
21849      var item = items[i];
21850      var groupId = item.group;
21851      if (groupId === null || groupId === undefined) {
21852        groupId = UNGROUPED;
21853      }
21854      groupCounts.hasOwnProperty(groupId) ? groupCounts[groupId]++ : groupCounts[groupId] = 1;
21855    }
21856
21857    //Pre-load arrays from existing groups if items are not changed (not in ids)
21858    var existingItemsMap = {};
21859    if (!groupIds && ids) {
21860      for (groupId in this.groups) {
21861        if (this.groups.hasOwnProperty(groupId)) {
21862          group = this.groups[groupId];
21863          var existing_items = group.getItems();
21864
21865          groupsContent[groupId] = existing_items.filter(function (item) {
21866            existingItemsMap[item[fieldId]] = item[fieldId];
21867            return item[fieldId] !== idMap[item[fieldId]];
21868          });
21869          var newLength = groupCounts[groupId];
21870          groupCounts[groupId] -= groupsContent[groupId].length;
21871          if (groupsContent[groupId].length < newLength) {
21872            groupsContent[groupId][newLength - 1] = {};
21873          }
21874        }
21875      }
21876    }
21877
21878    //Now insert data into the arrays.
21879    for (i = 0; i < items.length; i++) {
21880      item = items[i];
21881      groupId = item.group;
21882      if (groupId === null || groupId === undefined) {
21883        groupId = UNGROUPED;
21884      }
21885      if (!groupIds && ids && item[fieldId] !== idMap[item[fieldId]] && existingItemsMap.hasOwnProperty(item[fieldId])) {
21886        continue;
21887      }
21888      if (!groupsContent.hasOwnProperty(groupId)) {
21889        groupsContent[groupId] = new Array(groupCounts[groupId]);
21890      }
21891      //Copy data (because of unmodifiable DataView input.
21892      var extended = util.bridgeObject(item);
21893      extended.x = util.convert(item.x, 'Date');
21894      extended.end = util.convert(item.end, 'Date');
21895      extended.orginalY = item.y; //real Y
21896      extended.y = Number(item.y);
21897      extended[fieldId] = item[fieldId];
21898
21899      var index = groupsContent[groupId].length - groupCounts[groupId]--;
21900      groupsContent[groupId][index] = extended;
21901    }
21902
21903    //Make sure all groups are present, to allow removal of old groups
21904    for (groupId in this.groups) {
21905      if (this.groups.hasOwnProperty(groupId)) {
21906        if (!groupsContent.hasOwnProperty(groupId)) {
21907          groupsContent[groupId] = new Array(0);
21908        }
21909      }
21910    }
21911
21912    //Update legendas, style and axis
21913    for (groupId in groupsContent) {
21914      if (groupsContent.hasOwnProperty(groupId)) {
21915        if (groupsContent[groupId].length == 0) {
21916          if (this.groups.hasOwnProperty(groupId)) {
21917            this._removeGroup(groupId);
21918          }
21919        } else {
21920          var group = undefined;
21921          if (this.groupsData != undefined) {
21922            group = this.groupsData.get(groupId);
21923          }
21924          if (group == undefined) {
21925            group = { id: groupId, content: this.options.defaultGroup + groupId };
21926          }
21927          this._updateGroup(group, groupId);
21928          this.groups[groupId].setItems(groupsContent[groupId]);
21929        }
21930      }
21931    }
21932    this.forceGraphUpdate = true;
21933    this.body.emitter.emit("_change", { queue: true });
21934  }
21935};
21936
21937/**
21938 * Redraw the component, mandatory function
21939 * @return {boolean} Returns true if the component is resized
21940 */
21941LineGraph.prototype.redraw = function () {
21942  var resized = false;
vendor: 19,626 bytes, lines 21943-22472
21943
21944  // calculate actual size and position
21945  this.props.width = this.dom.frame.offsetWidth;
21946  this.props.height = this.body.domProps.centerContainer.height - this.body.domProps.border.top - this.body.domProps.border.bottom;
21947
21948  // check if this component is resized
21949  resized = this._isResized() || resized;
21950
21951  // check whether zoomed (in that case we need to re-stack everything)
21952  var visibleInterval = this.body.range.end - this.body.range.start;
21953  var zoomed = visibleInterval != this.lastVisibleInterval;
21954  this.lastVisibleInterval = visibleInterval;
21955
21956  // the svg element is three times as big as the width, this allows for fully dragging left and right
21957  // without reloading the graph. the controls for this are bound to events in the constructor
21958  if (resized == true) {
21959    this.svg.style.width = util.option.asSize(3 * this.props.width);
21960    this.svg.style.left = util.option.asSize(-this.props.width);
21961
21962    // if the height of the graph is set as proportional, change the height of the svg
21963    if ((this.options.height + '').indexOf("%") != -1 || this.updateSVGheightOnResize == true) {
21964      this.updateSVGheight = true;
21965    }
21966  }
21967
21968  // update the height of the graph on each redraw of the graph.
21969  if (this.updateSVGheight == true) {
21970    if (this.options.graphHeight != this.props.height + 'px') {
21971      this.options.graphHeight = this.props.height + 'px';
21972      this.svg.style.height = this.props.height + 'px';
21973    }
21974    this.updateSVGheight = false;
21975  } else {
21976    this.svg.style.height = ('' + this.options.graphHeight).replace('px', '') + 'px';
21977  }
21978
21979  // zoomed is here to ensure that animations are shown correctly.
21980  if (resized == true || zoomed == true || this.abortedGraphUpdate == true || this.forceGraphUpdate == true) {
21981    resized = this._updateGraph() || resized;
21982    this.forceGraphUpdate = false;
21983  } else {
21984    // move the whole svg while dragging
21985    if (this.lastStart != 0) {
21986      var offset = this.body.range.start - this.lastStart;
21987      var range = this.body.range.end - this.body.range.start;
21988      if (this.props.width != 0) {
21989        var rangePerPixelInv = this.props.width / range;
21990        var xOffset = offset * rangePerPixelInv;
21991        this.svg.style.left = -this.props.width - xOffset + 'px';
21992      }
21993    }
21994  }
21995  this.legendLeft.redraw();
21996  this.legendRight.redraw();
21997  return resized;
21998};
21999
22000LineGraph.prototype._getSortedGroupIds = function () {
22001  // getting group Ids
22002  var grouplist = [];
22003  for (var groupId in this.groups) {
22004    if (this.groups.hasOwnProperty(groupId)) {
22005      var group = this.groups[groupId];
22006      if (group.visible == true && (this.options.groups.visibility[groupId] === undefined || this.options.groups.visibility[groupId] == true)) {
22007        grouplist.push({ id: groupId, zIndex: group.options.zIndex });
22008      }
22009    }
22010  }
22011  util.insertSort(grouplist, function (a, b) {
22012    var az = a.zIndex;
22013    var bz = b.zIndex;
22014    if (az === undefined) az = 0;
22015    if (bz === undefined) bz = 0;
22016    return az == bz ? 0 : az < bz ? -1 : 1;
22017  });
22018  var groupIds = new Array(grouplist.length);
22019  for (var i = 0; i < grouplist.length; i++) {
22020    groupIds[i] = grouplist[i].id;
22021  }
22022  return groupIds;
22023};
22024
22025/**
22026 * Update and redraw the graph.
22027 *
22028 * @returns {boolean}
22029 * @private
22030 */
22031LineGraph.prototype._updateGraph = function () {
22032  // reset the svg elements
22033  DOMutil.prepareElements(this.svgElements);
22034  if (this.props.width != 0 && this.itemsData != null) {
22035    var group, i;
22036    var groupRanges = {};
22037    var changeCalled = false;
22038    // this is the range of the SVG canvas
22039    var minDate = this.body.util.toGlobalTime(-this.body.domProps.root.width);
22040    var maxDate = this.body.util.toGlobalTime(2 * this.body.domProps.root.width);
22041
22042    // getting group Ids
22043    var groupIds = this._getSortedGroupIds();
22044    if (groupIds.length > 0) {
22045      var groupsData = {};
22046
22047      // fill groups data, this only loads the data we require based on the timewindow
22048      this._getRelevantData(groupIds, groupsData, minDate, maxDate);
22049
22050      // apply sampling, if disabled, it will pass through this function.
22051      this._applySampling(groupIds, groupsData);
22052
22053      // we transform the X coordinates to detect collisions
22054      for (i = 0; i < groupIds.length; i++) {
22055        this._convertXcoordinates(groupsData[groupIds[i]]);
22056      }
22057
22058      // now all needed data has been collected we start the processing.
22059      this._getYRanges(groupIds, groupsData, groupRanges);
22060
22061      // update the Y axis first, we use this data to draw at the correct Y points
22062      changeCalled = this._updateYAxis(groupIds, groupRanges);
22063
22064      //  at changeCalled, abort this update cycle as the graph needs another update with new Width input from the Redraw container.
22065      //  Cleanup SVG elements on abort.
22066      if (changeCalled == true) {
22067        DOMutil.cleanupElements(this.svgElements);
22068        this.abortedGraphUpdate = true;
22069        return true;
22070      }
22071      this.abortedGraphUpdate = false;
22072
22073      // With the yAxis scaled correctly, use this to get the Y values of the points.
22074      var below = undefined;
22075      for (i = 0; i < groupIds.length; i++) {
22076        group = this.groups[groupIds[i]];
22077        if (this.options.stack === true && this.options.style === 'line') {
22078          if (group.options.excludeFromStacking == undefined || !group.options.excludeFromStacking) {
22079            if (below != undefined) {
22080              this._stack(groupsData[group.id], groupsData[below.id]);
22081              if (group.options.shaded.enabled == true && group.options.shaded.orientation !== "group") {
22082                if (group.options.shaded.orientation == "top" && below.options.shaded.orientation !== "group") {
22083                  below.options.shaded.orientation = "group";
22084                  below.options.shaded.groupId = group.id;
22085                } else {
22086                  group.options.shaded.orientation = "group";
22087                  group.options.shaded.groupId = below.id;
22088                }
22089              }
22090            }
22091            below = group;
22092          }
22093        }
22094        this._convertYcoordinates(groupsData[groupIds[i]], group);
22095      }
22096
22097      //Precalculate paths and draw shading if appropriate. This will make sure the shading is always behind any lines.
22098      var paths = {};
22099      for (i = 0; i < groupIds.length; i++) {
22100        group = this.groups[groupIds[i]];
22101        if (group.options.style === 'line' && group.options.shaded.enabled == true) {
22102          var dataset = groupsData[groupIds[i]];
22103          if (dataset == null || dataset.length == 0) {
22104            continue;
22105          }
22106          if (!paths.hasOwnProperty(groupIds[i])) {
22107            paths[groupIds[i]] = Lines.calcPath(dataset, group);
22108          }
22109          if (group.options.shaded.orientation === "group") {
22110            var subGroupId = group.options.shaded.groupId;
22111            if (groupIds.indexOf(subGroupId) === -1) {
22112              console.log(group.id + ": Unknown shading group target given:" + subGroupId);
22113              continue;
22114            }
22115            if (!paths.hasOwnProperty(subGroupId)) {
22116              paths[subGroupId] = Lines.calcPath(groupsData[subGroupId], this.groups[subGroupId]);
22117            }
22118            Lines.drawShading(paths[groupIds[i]], group, paths[subGroupId], this.framework);
22119          } else {
22120            Lines.drawShading(paths[groupIds[i]], group, undefined, this.framework);
22121          }
22122        }
22123      }
22124
22125      // draw the groups, calculating paths if still necessary.
22126      Bars.draw(groupIds, groupsData, this.framework);
22127      for (i = 0; i < groupIds.length; i++) {
22128        group = this.groups[groupIds[i]];
22129        if (groupsData[groupIds[i]].length > 0) {
22130          switch (group.options.style) {
22131            case "line":
22132              if (!paths.hasOwnProperty(groupIds[i])) {
22133                paths[groupIds[i]] = Lines.calcPath(groupsData[groupIds[i]], group);
22134              }
22135              Lines.draw(paths[groupIds[i]], group, this.framework);
22136            // eslint-disable-line no-fallthrough
22137            case "point":
22138            // eslint-disable-line no-fallthrough
22139            case "points":
22140              if (group.options.style == "point" || group.options.style == "points" || group.options.drawPoints.enabled == true) {
22141                Points.draw(groupsData[groupIds[i]], group, this.framework);
22142              }
22143              break;
22144            case "bar":
22145            // bar needs to be drawn enmasse
22146            // eslint-disable-line no-fallthrough
22147            default:
22148            //do nothing...
22149          }
22150        }
22151      }
22152    }
22153  }
22154
22155  // cleanup unused svg elements
22156  DOMutil.cleanupElements(this.svgElements);
22157  return false;
22158};
22159
22160LineGraph.prototype._stack = function (data, subData) {
22161  var index, dx, dy, subPrevPoint, subNextPoint;
22162  index = 0;
22163  // for each data point we look for a matching on in the set below
22164  for (var j = 0; j < data.length; j++) {
22165    subPrevPoint = undefined;
22166    subNextPoint = undefined;
22167    // we look for time matches or a before-after point
22168    for (var k = index; k < subData.length; k++) {
22169      // if times match exactly
22170      if (subData[k].x === data[j].x) {
22171        subPrevPoint = subData[k];
22172        subNextPoint = subData[k];
22173        index = k;
22174        break;
22175      } else if (subData[k].x > data[j].x) {
22176        // overshoot
22177        subNextPoint = subData[k];
22178        if (k == 0) {
22179          subPrevPoint = subNextPoint;
22180        } else {
22181          subPrevPoint = subData[k - 1];
22182        }
22183        index = k;
22184        break;
22185      }
22186    }
22187    // in case the last data point has been used, we assume it stays like this.
22188    if (subNextPoint === undefined) {
22189      subPrevPoint = subData[subData.length - 1];
22190      subNextPoint = subData[subData.length - 1];
22191    }
22192    // linear interpolation
22193    dx = subNextPoint.x - subPrevPoint.x;
22194    dy = subNextPoint.y - subPrevPoint.y;
22195    if (dx == 0) {
22196      data[j].y = data[j].orginalY + subNextPoint.y;
22197    } else {
22198      data[j].y = data[j].orginalY + dy / dx * (data[j].x - subPrevPoint.x) + subPrevPoint.y; // ax + b where b is data[j].y
22199    }
22200  }
22201};
22202
22203/**
22204 * first select and preprocess the data from the datasets.
22205 * the groups have their preselection of data, we now loop over this data to see
22206 * what data we need to draw. Sorted data is much faster.
22207 * more optimization is possible by doing the sampling before and using the binary search
22208 * to find the end date to determine the increment.
22209 *
22210 * @param {array}  groupIds
22211 * @param {object} groupsData
22212 * @param {date}   minDate
22213 * @param {date}   maxDate
22214 * @private
22215 */
22216LineGraph.prototype._getRelevantData = function (groupIds, groupsData, minDate, maxDate) {
22217  var group, i, j, item;
22218  if (groupIds.length > 0) {
22219    for (i = 0; i < groupIds.length; i++) {
22220      group = this.groups[groupIds[i]];
22221      var itemsData = group.getItems();
22222      // optimization for sorted data
22223      if (group.options.sort == true) {
22224        var dateComparator = function dateComparator(a, b) {
22225          return a.getTime() == b.getTime() ? 0 : a < b ? -1 : 1;
22226        };
22227        var first = Math.max(0, util.binarySearchValue(itemsData, minDate, 'x', 'before', dateComparator));
22228        var last = Math.min(itemsData.length, util.binarySearchValue(itemsData, maxDate, 'x', 'after', dateComparator) + 1);
22229        if (last <= 0) {
22230          last = itemsData.length;
22231        }
22232        var dataContainer = new Array(last - first);
22233        for (j = first; j < last; j++) {
22234          item = group.itemsData[j];
22235          dataContainer[j - first] = item;
22236        }
22237        groupsData[groupIds[i]] = dataContainer;
22238      } else {
22239        // If unsorted data, all data is relevant, just returning entire structure
22240        groupsData[groupIds[i]] = group.itemsData;
22241      }
22242    }
22243  }
22244};
22245
22246/**
22247 *
22248 * @param {Array.<vis.GraphGroup.id>} groupIds
22249 * @param {vis.DataSet} groupsData
22250 * @private
22251 */
22252LineGraph.prototype._applySampling = function (groupIds, groupsData) {
22253  var group;
22254  if (groupIds.length > 0) {
22255    for (var i = 0; i < groupIds.length; i++) {
22256      group = this.groups[groupIds[i]];
22257      if (group.options.sampling == true) {
22258        var dataContainer = groupsData[groupIds[i]];
22259        if (dataContainer.length > 0) {
22260          var increment = 1;
22261          var amountOfPoints = dataContainer.length;
22262
22263          // the global screen is used because changing the width of the yAxis may affect the increment, resulting in an endless loop
22264          // of width changing of the yAxis.
22265          //TODO: This assumes sorted data, but that's not guaranteed!
22266          var xDistance = this.body.util.toGlobalScreen(dataContainer[dataContainer.length - 1].x) - this.body.util.toGlobalScreen(dataContainer[0].x);
22267          var pointsPerPixel = amountOfPoints / xDistance;
22268          increment = Math.min(Math.ceil(0.2 * amountOfPoints), Math.max(1, Math.round(pointsPerPixel)));
22269
22270          var sampledData = new Array(amountOfPoints);
22271          for (var j = 0; j < amountOfPoints; j += increment) {
22272            var idx = Math.round(j / increment);
22273            sampledData[idx] = dataContainer[j];
22274          }
22275          groupsData[groupIds[i]] = sampledData.splice(0, Math.round(amountOfPoints / increment));
22276        }
22277      }
22278    }
22279  }
22280};
22281
22282/**
22283 *
22284 * @param {Array.<vis.GraphGroup.id>} groupIds
22285 * @param {vis.DataSet} groupsData
22286 * @param {object} groupRanges  | this is being filled here
22287 * @private
22288 */
22289LineGraph.prototype._getYRanges = function (groupIds, groupsData, groupRanges) {
22290  var groupData, group, i;
22291  var combinedDataLeft = [];
22292  var combinedDataRight = [];
22293  var options;
22294  if (groupIds.length > 0) {
22295    for (i = 0; i < groupIds.length; i++) {
22296      groupData = groupsData[groupIds[i]];
22297      options = this.groups[groupIds[i]].options;
22298      if (groupData.length > 0) {
22299        group = this.groups[groupIds[i]];
22300        // if bar graphs are stacked, their range need to be handled differently and accumulated over all groups.
22301        if (options.stack === true && options.style === 'bar') {
22302          if (options.yAxisOrientation === 'left') {
22303            combinedDataLeft = combinedDataLeft.concat(groupData);
22304          } else {
22305            combinedDataRight = combinedDataRight.concat(groupData);
22306          }
22307        } else {
22308          groupRanges[groupIds[i]] = group.getYRange(groupData, groupIds[i]);
22309        }
22310      }
22311    }
22312
22313    // if bar graphs are stacked, their range need to be handled differently and accumulated over all groups.
22314    Bars.getStackedYRange(combinedDataLeft, groupRanges, groupIds, '__barStackLeft', 'left');
22315    Bars.getStackedYRange(combinedDataRight, groupRanges, groupIds, '__barStackRight', 'right');
22316  }
22317};
22318
22319/**
22320 * this sets the Y ranges for the Y axis. It also determines which of the axis should be shown or hidden.
22321 * @param {Array.<vis.GraphGroup.id>} groupIds
22322 * @param {Object} groupRanges
22323 * @returns {boolean} resized
22324 * @private
22325 */
22326LineGraph.prototype._updateYAxis = function (groupIds, groupRanges) {
22327  var resized = false;
22328  var yAxisLeftUsed = false;
22329  var yAxisRightUsed = false;
22330  var minLeft = 1e9,
22331      minRight = 1e9,
22332      maxLeft = -1e9,
22333      maxRight = -1e9,
22334      minVal,
22335      maxVal;
22336  // if groups are present
22337  if (groupIds.length > 0) {
22338    // 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.
22339    for (var i = 0; i < groupIds.length; i++) {
22340      var group = this.groups[groupIds[i]];
22341      if (group && group.options.yAxisOrientation != 'right') {
22342        yAxisLeftUsed = true;
22343        minLeft = 1e9;
22344        maxLeft = -1e9;
22345      } else if (group && group.options.yAxisOrientation) {
22346        yAxisRightUsed = true;
22347        minRight = 1e9;
22348        maxRight = -1e9;
22349      }
22350    }
22351
22352    // if there are items:
22353    for (i = 0; i < groupIds.length; i++) {
22354      if (groupRanges.hasOwnProperty(groupIds[i])) {
22355        if (groupRanges[groupIds[i]].ignore !== true) {
22356          minVal = groupRanges[groupIds[i]].min;
22357          maxVal = groupRanges[groupIds[i]].max;
22358
22359          if (groupRanges[groupIds[i]].yAxisOrientation != 'right') {
22360            yAxisLeftUsed = true;
22361            minLeft = minLeft > minVal ? minVal : minLeft;
22362            maxLeft = maxLeft < maxVal ? maxVal : maxLeft;
22363          } else {
22364            yAxisRightUsed = true;
22365            minRight = minRight > minVal ? minVal : minRight;
22366            maxRight = maxRight < maxVal ? maxVal : maxRight;
22367          }
22368        }
22369      }
22370    }
22371
22372    if (yAxisLeftUsed == true) {
22373      this.yAxisLeft.setRange(minLeft, maxLeft);
22374    }
22375    if (yAxisRightUsed == true) {
22376      this.yAxisRight.setRange(minRight, maxRight);
22377    }
22378  }
22379  resized = this._toggleAxisVisiblity(yAxisLeftUsed, this.yAxisLeft) || resized;
22380  resized = this._toggleAxisVisiblity(yAxisRightUsed, this.yAxisRight) || resized;
22381
22382  if (yAxisRightUsed == true && yAxisLeftUsed == true) {
22383    this.yAxisLeft.drawIcons = true;
22384    this.yAxisRight.drawIcons = true;
22385  } else {
22386    this.yAxisLeft.drawIcons = false;
22387    this.yAxisRight.drawIcons = false;
22388  }
22389  this.yAxisRight.master = !yAxisLeftUsed;
22390  this.yAxisRight.masterAxis = this.yAxisLeft;
22391
22392  if (this.yAxisRight.master == false) {
22393    if (yAxisRightUsed == true) {
22394      this.yAxisLeft.lineOffset = this.yAxisRight.width;
22395    } else {
22396      this.yAxisLeft.lineOffset = 0;
22397    }
22398
22399    resized = this.yAxisLeft.redraw() || resized;
22400    resized = this.yAxisRight.redraw() || resized;
22401  } else {
22402    resized = this.yAxisRight.redraw() || resized;
22403  }
22404
22405  // clean the accumulated lists
22406  var tempGroups = ['__barStackLeft', '__barStackRight', '__lineStackLeft', '__lineStackRight'];
22407  for (i = 0; i < tempGroups.length; i++) {
22408    if (groupIds.indexOf(tempGroups[i]) != -1) {
22409      groupIds.splice(groupIds.indexOf(tempGroups[i]), 1);
22410    }
22411  }
22412
22413  return resized;
22414};
22415
22416/**
22417 * This shows or hides the Y axis if needed. If there is a change, the changed event is emitted by the updateYAxis function
22418 *
22419 * @param {boolean} axisUsed
22420 * @param {vis.DataAxis}  axis
22421 * @returns {boolean}
22422 * @private
22423 */
22424LineGraph.prototype._toggleAxisVisiblity = function (axisUsed, axis) {
22425  var changed = false;
22426  if (axisUsed == false) {
22427    if (axis.dom.frame.parentNode && axis.hidden == false) {
22428      axis.hide();
22429      changed = true;
22430    }
22431  } else {
22432    if (!axis.dom.frame.parentNode && axis.hidden == true) {
22433      axis.show();
22434      changed = true;
22435    }
22436  }
22437  return changed;
22438};
22439
22440/**
22441 * This uses the DataAxis object to generate the correct X coordinate on the SVG window. It uses the
22442 * util function toScreen to get the x coordinate from the timestamp. It also pre-filters the data and get the minMax ranges for
22443 * the yAxis.
22444 *
22445 * @param {Array.<Object>} datapoints
22446 * @private
22447 */
22448LineGraph.prototype._convertXcoordinates = function (datapoints) {
22449  var toScreen = this.body.util.toScreen;
22450  for (var i = 0; i < datapoints.length; i++) {
22451    datapoints[i].screen_x = toScreen(datapoints[i].x) + this.props.width;
22452    datapoints[i].screen_y = datapoints[i].y; //starting point for range calculations
22453    if (datapoints[i].end != undefined) {
22454      datapoints[i].screen_end = toScreen(datapoints[i].end) + this.props.width;
22455    } else {
22456      datapoints[i].screen_end = undefined;
22457    }
22458  }
22459};
22460
22461/**
22462 * This uses the DataAxis object to generate the correct X coordinate on the SVG window. It uses the
22463 * util function toScreen to get the x coordinate from the timestamp. It also pre-filters the data and get the minMax ranges for
22464 * the yAxis.
22465 *
22466 * @param {Array.<Object>} datapoints
22467 * @param {vis.GraphGroup} group
22468 * @private
22469 */
22470LineGraph.prototype._convertYcoordinates = function (datapoints, group) {
22471  var axis = this.yAxisLeft;
22472  var svgHeight = Number(this.svg.style.height.replace('px', ''));
22473  if (group.options.yAxisOrientation == 'right') {
22474    axis = this.yAxisRight;
22475  }
22476  for (var i = 0; i < datapoints.length; i++) {
22477    datapoints[i].screen_y = Math.round(axis.convertValue(datapoints[i].y));
22478  }
22479  group.setZeroPosition(Math.min(svgHeight, axis.convertValue(0)));
22480};
22481
22482module.exports = LineGraph;
22483
22484/***/ }),
22485/* 107 */
22486/***/ (function(module, exports, __webpack_require__) {
22487
22488"use strict";
22489
22490
22491var _keys = __webpack_require__(8);
22492
22493var _keys2 = _interopRequireDefault(_keys);
22494
22495function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
22496
22497var util = __webpack_require__(2);
22498var DOMutil = __webpack_require__(14);
22499var Component = __webpack_require__(16);
22500var DataScale = __webpack_require__(108);
22501/**
22502 * A horizontal time axis
22503 * @param {Object} body
22504 * @param {Object} [options]        See DataAxis.setOptions for the available
22505 *                                  options.
22506 * @param {SVGElement} svg
22507 * @param {vis.LineGraph.options} linegraphOptions
22508 * @constructor DataAxis
22509 * @extends Component
22510 */
22511function DataAxis(body, options, svg, linegraphOptions) {
22512  this.id = util.randomUUID();
22513  this.body = body;
22514
22515  this.defaultOptions = {
22516    orientation: 'left', // supported: 'left', 'right'
22517    showMinorLabels: true,
22518    showMajorLabels: true,
22519    icons: false,
22520    majorLinesOffset: 7,
22521    minorLinesOffset: 4,
22522    labelOffsetX: 10,
22523    labelOffsetY: 2,
22524    iconWidth: 20,
22525    width: '40px',
22526    visible: true,
22527    alignZeros: true,
22528    left: {
22529      range: { min: undefined, max: undefined },
22530      format: function format(value) {
22531        return '' + parseFloat(value.toPrecision(3));
22532      },
22533      title: { text: undefined, style: undefined }
22534    },
22535    right: {
22536      range: { min: undefined, max: undefined },
22537      format: function format(value) {
22538        return '' + parseFloat(value.toPrecision(3));
22539      },
22540      title: { text: undefined, style: undefined }
22541    }
22542  };
22543
22544  this.linegraphOptions = linegraphOptions;
22545  this.linegraphSVG = svg;
22546  this.props = {};
22547  this.DOMelements = { // dynamic elements
22548    lines: {},
22549    labels: {},
22550    title: {}
22551  };
22552
22553  this.dom = {};
22554  this.scale = undefined;
22555  this.range = { start: 0, end: 0 };
22556
22557  this.options = util.extend({}, this.defaultOptions);
22558  this.conversionFactor = 1;
22559
22560  this.setOptions(options);
22561  this.width = Number(('' + this.options.width).replace("px", ""));
22562  this.minWidth = this.width;
22563  this.height = this.linegraphSVG.getBoundingClientRect().height;
22564  this.hidden = false;
22565
22566  this.stepPixels = 25;
22567  this.zeroCrossing = -1;
22568  this.amountOfSteps = -1;
22569
22570  this.lineOffset = 0;
22571  this.master = true;
22572  this.masterAxis = null;
22573  this.svgElements = {};
22574  this.iconsRemoved = false;
22575
22576  this.groups = {};
22577  this.amountOfGroups = 0;
22578
22579  // create the HTML DOM
22580  this._create();
22581  this.framework = { svg: this.svg, svgElements: this.svgElements, options: this.options, groups: this.groups };
22582
22583  var me = this;
22584  this.body.emitter.on("verticalDrag", function () {
22585    me.dom.lineContainer.style.top = me.body.domProps.scrollTop + 'px';
22586  });
22587}
22588
22589DataAxis.prototype = new Component();
22590
22591DataAxis.prototype.addGroup = function (label, graphOptions) {
22592  if (!this.groups.hasOwnProperty(label)) {
22593    this.groups[label] = graphOptions;
22594  }
22595  this.amountOfGroups += 1;
22596};
22597
22598DataAxis.prototype.updateGroup = function (label, graphOptions) {
22599  if (!this.groups.hasOwnProperty(label)) {
22600    this.amountOfGroups += 1;
22601  }
22602  this.groups[label] = graphOptions;
22603};
22604
22605DataAxis.prototype.removeGroup = function (label) {
22606  if (this.groups.hasOwnProperty(label)) {
22607    delete this.groups[label];
22608    this.amountOfGroups -= 1;
22609  }
22610};
22611
22612DataAxis.prototype.setOptions = function (options) {
22613  if (options) {
22614    var redraw = false;
22615    if (this.options.orientation != options.orientation && options.orientation !== undefined) {
22616      redraw = true;
22617    }
22618    var fields = ['orientation', 'showMinorLabels', 'showMajorLabels', 'icons', 'majorLinesOffset', 'minorLinesOffset', 'labelOffsetX', 'labelOffsetY', 'iconWidth', 'width', 'visible', 'left', 'right', 'alignZeros'];
22619    util.selectiveDeepExtend(fields, this.options, options);
22620
22621    this.minWidth = Number(('' + this.options.width).replace("px", ""));
22622    if (redraw === true && this.dom.frame) {
22623      this.hide();
22624      this.show();
22625    }
22626  }
22627};
22628
22629/**
22630 * Create the HTML DOM for the DataAxis
22631 */
22632DataAxis.prototype._create = function () {
22633  this.dom.frame = document.createElement('div');
22634  this.dom.frame.style.width = this.options.width;
22635  this.dom.frame.style.height = this.height;
22636
22637  this.dom.lineContainer = document.createElement('div');
22638  this.dom.lineContainer.style.width = '100%';
22639  this.dom.lineContainer.style.height = this.height;
22640  this.dom.lineContainer.style.position = 'relative';
22641
22642  // create svg element for graph drawing.
22643  this.svg = document.createElementNS('http://www.w3.org/2000/svg', "svg");
22644  this.svg.style.position = "absolute";
22645  this.svg.style.top = '0px';
22646  this.svg.style.height = '100%';
22647  this.svg.style.width = '100%';
22648  this.svg.style.display = "block";
22649  this.dom.frame.appendChild(this.svg);
22650};
22651
22652DataAxis.prototype._redrawGroupIcons = function () {
22653  DOMutil.prepareElements(this.svgElements);
22654
22655  var x;
22656  var iconWidth = this.options.iconWidth;
22657  var iconHeight = 15;
22658  var iconOffset = 4;
22659  var y = iconOffset + 0.5 * iconHeight;
22660
22661  if (this.options.orientation === 'left') {
22662    x = iconOffset;
22663  } else {
22664    x = this.width - iconWidth - iconOffset;
22665  }
22666
22667  var groupArray = (0, _keys2['default'])(this.groups);
22668  groupArray.sort(function (a, b) {
22669    return a < b ? -1 : 1;
22670  });
22671
22672  for (var i = 0; i < groupArray.length; i++) {
22673    var groupId = groupArray[i];
22674    if (this.groups[groupId].visible === true && (this.linegraphOptions.visibility[groupId] === undefined || this.linegraphOptions.visibility[groupId] === true)) {
22675      this.groups[groupId].getLegend(iconWidth, iconHeight, this.framework, x, y);
22676      y += iconHeight + iconOffset;
22677    }
22678  }
22679
22680  DOMutil.cleanupElements(this.svgElements);
22681  this.iconsRemoved = false;
22682};
22683
22684DataAxis.prototype._cleanupIcons = function () {
22685  if (this.iconsRemoved === false) {
22686    DOMutil.prepareElements(this.svgElements);
22687    DOMutil.cleanupElements(this.svgElements);
22688    this.iconsRemoved = true;
22689  }
22690};
22691
22692/**
22693 * Create the HTML DOM for the DataAxis
22694 */
22695DataAxis.prototype.show = function () {
22696  this.hidden = false;
22697  if (!this.dom.frame.parentNode) {
22698    if (this.options.orientation === 'left') {
22699      this.body.dom.left.appendChild(this.dom.frame);
22700    } else {
22701      this.body.dom.right.appendChild(this.dom.frame);
22702    }
22703  }
22704
22705  if (!this.dom.lineContainer.parentNode) {
22706    this.body.dom.backgroundHorizontal.appendChild(this.dom.lineContainer);
22707  }
22708};
22709
22710/**
22711 * Create the HTML DOM for the DataAxis
22712 */
22713DataAxis.prototype.hide = function () {
22714  this.hidden = true;
22715  if (this.dom.frame.parentNode) {
22716    this.dom.frame.parentNode.removeChild(this.dom.frame);
22717  }
22718
22719  if (this.dom.lineContainer.parentNode) {
22720    this.dom.lineContainer.parentNode.removeChild(this.dom.lineContainer);
22721  }
22722};
22723
22724/**
22725 * Set a range (start and end)
22726 * @param {number} start
22727 * @param {number} end
22728 */
22729DataAxis.prototype.setRange = function (start, end) {
22730  this.range.start = start;
22731  this.range.end = end;
22732};
22733
22734/**
22735 * Repaint the component
22736 * @return {boolean} Returns true if the component is resized
22737 */
22738DataAxis.prototype.redraw = function () {
22739  var resized = false;
22740  var activeGroups = 0;
22741
22742  // Make sure the line container adheres to the vertical scrolling.
22743  this.dom.lineContainer.style.top = this.body.domProps.scrollTop + 'px';
22744
22745  for (var groupId in this.groups) {
22746    if (this.groups.hasOwnProperty(groupId)) {
22747      if (this.groups[groupId].visible === true && (this.linegraphOptions.visibility[groupId] === undefined || this.linegraphOptions.visibility[groupId] === true)) {
22748        activeGroups++;
22749      }
22750    }
22751  }
22752  if (this.amountOfGroups === 0 || activeGroups === 0) {
22753    this.hide();
22754  } else {
22755    this.show();
22756    this.height = Number(this.linegraphSVG.style.height.replace("px", ""));
22757
22758    // svg offsetheight did not work in firefox and explorer...
22759    this.dom.lineContainer.style.height = this.height + 'px';
22760    this.width = this.options.visible === true ? Number(('' + this.options.width).replace("px", "")) : 0;
22761
22762    var props = this.props;
22763    var frame = this.dom.frame;
22764
22765    // update classname
22766    frame.className = 'vis-data-axis';
22767
22768    // calculate character width and height
22769    this._calculateCharSize();
22770
22771    var orientation = this.options.orientation;
22772    var showMinorLabels = this.options.showMinorLabels;
22773    var showMajorLabels = this.options.showMajorLabels;
22774
22775    // determine the width and height of the elements for the axis
22776    props.minorLabelHeight = showMinorLabels ? props.minorCharHeight : 0;
22777    props.majorLabelHeight = showMajorLabels ? props.majorCharHeight : 0;
22778
22779    props.minorLineWidth = this.body.dom.backgroundHorizontal.offsetWidth - this.lineOffset - this.width + 2 * this.options.minorLinesOffset;
22780    props.minorLineHeight = 1;
22781    props.majorLineWidth = this.body.dom.backgroundHorizontal.offsetWidth - this.lineOffset - this.width + 2 * this.options.majorLinesOffset;
22782    props.majorLineHeight = 1;
22783
22784    //  take frame offline while updating (is almost twice as fast)
22785    if (orientation === 'left') {
22786      frame.style.top = '0';
22787      frame.style.left = '0';
22788      frame.style.bottom = '';
22789      frame.style.width = this.width + 'px';
22790      frame.style.height = this.height + "px";
22791      this.props.width = this.body.domProps.left.width;
22792      this.props.height = this.body.domProps.left.height;
22793    } else {
22794      // right
22795      frame.style.top = '';
22796      frame.style.bottom = '0';
22797      frame.style.left = '0';
22798      frame.style.width = this.width + 'px';
22799      frame.style.height = this.height + "px";
22800      this.props.width = this.body.domProps.right.width;
22801      this.props.height = this.body.domProps.right.height;
22802    }
22803
22804    resized = this._redrawLabels();
22805    resized = this._isResized() || resized;
22806
22807    if (this.options.icons === true) {
22808      this._redrawGroupIcons();
22809    } else {
22810      this._cleanupIcons();
22811    }
22812
22813    this._redrawTitle(orientation);
22814  }
22815  return resized;
22816};
22817
22818/**
22819 * Repaint major and minor text labels and vertical grid lines
22820 *
22821 * @returns {boolean}
22822 * @private
22823 */
22824DataAxis.prototype._redrawLabels = function () {
22825  var _this = this;
22826
22827  var resized = false;
22828  DOMutil.prepareElements(this.DOMelements.lines);
22829  DOMutil.prepareElements(this.DOMelements.labels);
22830  var orientation = this.options['orientation'];
22831  var customRange = this.options[orientation].range != undefined ? this.options[orientation].range : {};
22832
22833  //Override range with manual options:
22834  var autoScaleEnd = true;
22835  if (customRange.max != undefined) {
22836    this.range.end = customRange.max;
22837    autoScaleEnd = false;
22838  }
22839  var autoScaleStart = true;
22840  if (customRange.min != undefined) {
22841    this.range.start = customRange.min;
22842    autoScaleStart = false;
22843  }
22844
22845  this.scale = new DataScale(this.range.start, this.range.end, autoScaleStart, autoScaleEnd, this.dom.frame.offsetHeight, this.props.majorCharHeight, this.options.alignZeros, this.options[orientation].format);
22846
22847  if (this.master === false && this.masterAxis != undefined) {
22848    this.scale.followScale(this.masterAxis.scale);
22849  }
22850
22851  //Is updated in side-effect of _redrawLabel():
22852  this.maxLabelSize = 0;
22853
22854  var lines = this.scale.getLines();
22855  lines.forEach(function (line) {
22856    var y = line.y;
22857    var isMajor = line.major;
22858    if (_this.options['showMinorLabels'] && isMajor === false) {
22859      _this._redrawLabel(y - 2, line.val, orientation, 'vis-y-axis vis-minor', _this.props.minorCharHeight);
22860    }
22861    if (isMajor) {
22862      if (y >= 0) {
22863        _this._redrawLabel(y - 2, line.val, orientation, 'vis-y-axis vis-major', _this.props.majorCharHeight);
22864      }
22865    }
22866    if (_this.master === true) {
22867      if (isMajor) {
22868        _this._redrawLine(y, orientation, 'vis-grid vis-horizontal vis-major', _this.options.majorLinesOffset, _this.props.majorLineWidth);
22869      } else {
22870        _this._redrawLine(y, orientation, 'vis-grid vis-horizontal vis-minor', _this.options.minorLinesOffset, _this.props.minorLineWidth);
22871      }
22872    }
22873  });
22874
22875  // Note that title is rotated, so we're using the height, not width!
22876  var titleWidth = 0;
22877  if (this.options[orientation].title !== undefined && this.options[orientation].title.text !== undefined) {
22878    titleWidth = this.props.titleCharHeight;
22879  }
22880  var offset = this.options.icons === true ? Math.max(this.options.iconWidth, titleWidth) + this.options.labelOffsetX + 15 : titleWidth + this.options.labelOffsetX + 15;
22881
22882  // this will resize the yAxis to accommodate the labels.
22883  if (this.maxLabelSize > this.width - offset && this.options.visible === true) {
22884    this.width = this.maxLabelSize + offset;
22885    this.options.width = this.width + "px";
22886    DOMutil.cleanupElements(this.DOMelements.lines);
22887    DOMutil.cleanupElements(this.DOMelements.labels);
22888    this.redraw();
22889    resized = true;
22890  }
22891  // this will resize the yAxis if it is too big for the labels.
22892  else if (this.maxLabelSize < this.width - offset && this.options.visible === true && this.width > this.minWidth) {
22893      this.width = Math.max(this.minWidth, this.maxLabelSize + offset);
22894      this.options.width = this.width + "px";
22895      DOMutil.cleanupElements(this.DOMelements.lines);
22896      DOMutil.cleanupElements(this.DOMelements.labels);
22897      this.redraw();
22898      resized = true;
22899    } else {
22900      DOMutil.cleanupElements(this.DOMelements.lines);
22901      DOMutil.cleanupElements(this.DOMelements.labels);
22902      resized = false;
22903    }
22904
22905  return resized;
22906};
22907
22908DataAxis.prototype.convertValue = function (value) {
22909  return this.scale.convertValue(value);
22910};
22911
22912DataAxis.prototype.screenToValue = function (x) {
22913  return this.scale.screenToValue(x);
22914};
22915
22916/**
22917 * Create a label for the axis at position x
22918 *
22919 * @param {number} y
22920 * @param {string} text
22921 * @param {'top'|'right'|'bottom'|'left'} orientation
22922 * @param {string} className
22923 * @param {number} characterHeight
22924 * @private
22925 */
22926DataAxis.prototype._redrawLabel = function (y, text, orientation, className, characterHeight) {
22927  // reuse redundant label
22928  var label = DOMutil.getDOMElement('div', this.DOMelements.labels, this.dom.frame); //this.dom.redundant.labels.shift();
22929  label.className = className;
22930  label.innerHTML = text;
22931  if (orientation === 'left') {
22932    label.style.left = '-' + this.options.labelOffsetX + 'px';
22933    label.style.textAlign = "right";
22934  } else {
22935    label.style.right = '-' + this.options.labelOffsetX + 'px';
22936    label.style.textAlign = "left";
22937  }
22938
22939  label.style.top = y - 0.5 * characterHeight + this.options.labelOffsetY + 'px';
22940
22941  text += '';
22942
22943  var largestWidth = Math.max(this.props.majorCharWidth, this.props.minorCharWidth);
22944  if (this.maxLabelSize < text.length * largestWidth) {
22945    this.maxLabelSize = text.length * largestWidth;
22946  }
22947};
22948
22949/**
22950 * Create a minor line for the axis at position y
22951 * @param {number} y
22952 * @param {'top'|'right'|'bottom'|'left'} orientation
22953 * @param {string} className
22954 * @param {number} offset
22955 * @param {number} width
22956 */
22957DataAxis.prototype._redrawLine = function (y, orientation, className, offset, width) {
22958  if (this.master === true) {
22959    var line = DOMutil.getDOMElement('div', this.DOMelements.lines, this.dom.lineContainer); //this.dom.redundant.lines.shift();
22960    line.className = className;
22961    line.innerHTML = '';
22962
22963    if (orientation === 'left') {
22964      line.style.left = this.width - offset + 'px';
22965    } else {
22966      line.style.right = this.width - offset + 'px';
22967    }
22968
22969    line.style.width = width + 'px';
22970    line.style.top = y + 'px';
22971  }
22972};
22973
22974/**
22975 * Create a title for the axis
22976 * @private
22977 * @param {'top'|'right'|'bottom'|'left'} orientation
22978 */
22979DataAxis.prototype._redrawTitle = function (orientation) {
22980  DOMutil.prepareElements(this.DOMelements.title);
22981
22982  // Check if the title is defined for this axes
22983  if (this.options[orientation].title !== undefined && this.options[orientation].title.text !== undefined) {
22984    var title = DOMutil.getDOMElement('div', this.DOMelements.title, this.dom.frame);
22985    title.className = 'vis-y-axis vis-title vis-' + orientation;
22986    title.innerHTML = this.options[orientation].title.text;
22987
22988    // Add style - if provided
22989    if (this.options[orientation].title.style !== undefined) {
22990      util.addCssText(title, this.options[orientation].title.style);
22991    }
22992
22993    if (orientation === 'left') {
22994      title.style.left = this.props.titleCharHeight + 'px';
22995    } else {
22996      title.style.right = this.props.titleCharHeight + 'px';
22997    }
22998
22999    title.style.width = this.height + 'px';
23000  }
23001
23002  // we need to clean up in case we did not use all elements.
23003  DOMutil.cleanupElements(this.DOMelements.title);
23004};
23005
23006/**
23007 * Determine the size of text on the axis (both major and minor axis).
23008 * The size is calculated only once and then cached in this.props.
23009 * @private
23010 */
23011DataAxis.prototype._calculateCharSize = function () {
23012  // determine the char width and height on the minor axis
23013  if (!('minorCharHeight' in this.props)) {
23014    var textMinor = document.createTextNode('0');
23015    var measureCharMinor = document.createElement('div');
23016    measureCharMinor.className = 'vis-y-axis vis-minor vis-measure';
23017    measureCharMinor.appendChild(textMinor);
23018    this.dom.frame.appendChild(measureCharMinor);
23019
23020    this.props.minorCharHeight = measureCharMinor.clientHeight;
23021    this.props.minorCharWidth = measureCharMinor.clientWidth;
23022
23023    this.dom.frame.removeChild(measureCharMinor);
23024  }
23025
23026  if (!('majorCharHeight' in this.props)) {
23027    var textMajor = document.createTextNode('0');
23028    var measureCharMajor = document.createElement('div');
23029    measureCharMajor.className = 'vis-y-axis vis-major vis-measure';
23030    measureCharMajor.appendChild(textMajor);
23031    this.dom.frame.appendChild(measureCharMajor);
23032
23033    this.props.majorCharHeight = measureCharMajor.clientHeight;
23034    this.props.majorCharWidth = measureCharMajor.clientWidth;
23035
23036    this.dom.frame.removeChild(measureCharMajor);
23037  }
23038
23039  if (!('titleCharHeight' in this.props)) {
23040    var textTitle = document.createTextNode('0');
23041    var measureCharTitle = document.createElement('div');
23042    measureCharTitle.className = 'vis-y-axis vis-title vis-measure';
23043    measureCharTitle.appendChild(textTitle);
23044    this.dom.frame.appendChild(measureCharTitle);
23045
23046    this.props.titleCharHeight = measureCharTitle.clientHeight;
23047    this.props.titleCharWidth = measureCharTitle.clientWidth;
23048
23049    this.dom.frame.removeChild(measureCharTitle);
23050  }
23051};
23052
23053module.exports = DataAxis;
23054
23055/***/ }),
23056/* 108 */
23057/***/ (function(module, exports, __webpack_require__) {
23058
23059"use strict";
23060
23061
23062/**
23063 *
23064 * @param {number} start
23065 * @param {number} end
23066 * @param {boolean} autoScaleStart
23067 * @param {boolean} autoScaleEnd
23068 * @param {number} containerHeight
23069 * @param {number} majorCharHeight
23070 * @param {boolean} zeroAlign
23071 * @param {function} formattingFunction
23072 * @constructor DataScale
23073 */
23074function DataScale(start, end, autoScaleStart, autoScaleEnd, containerHeight, majorCharHeight) {
23075  var zeroAlign = arguments.length > 6 && arguments[6] !== undefined ? arguments[6] : false;
23076  var formattingFunction = arguments.length > 7 && arguments[7] !== undefined ? arguments[7] : false;
23077
23078  this.majorSteps = [1, 2, 5, 10];
23079  this.minorSteps = [0.25, 0.5, 1, 2];
23080  this.customLines = null;
23081
23082  this.containerHeight = containerHeight;
23083  this.majorCharHeight = majorCharHeight;
23084  this._start = start;
23085  this._end = end;
23086
23087  this.scale = 1;
23088  this.minorStepIdx = -1;
23089  this.magnitudefactor = 1;
23090  this.determineScale();
23091
23092  this.zeroAlign = zeroAlign;
23093  this.autoScaleStart = autoScaleStart;
23094  this.autoScaleEnd = autoScaleEnd;
23095
23096  this.formattingFunction = formattingFunction;
23097
23098  if (autoScaleStart || autoScaleEnd) {
23099    var me = this;
23100    var roundToMinor = function roundToMinor(value) {
23101      var rounded = value - value % (me.magnitudefactor * me.minorSteps[me.minorStepIdx]);
23102      if (value % (me.magnitudefactor * me.minorSteps[me.minorStepIdx]) > 0.5 * (me.magnitudefactor * me.minorSteps[me.minorStepIdx])) {
23103        return rounded + me.magnitudefactor * me.minorSteps[me.minorStepIdx];
23104      } else {
23105        return rounded;
23106      }
23107    };
23108    if (autoScaleStart) {
23109      this._start -= this.magnitudefactor * 2 * this.minorSteps[this.minorStepIdx];
23110      this._start = roundToMinor(this._start);
23111    }
23112
23113    if (autoScaleEnd) {
23114      this._end += this.magnitudefactor * this.minorSteps[this.minorStepIdx];
23115      this._end = roundToMinor(this._end);
23116    }
23117    this.determineScale();
23118  }
23119}
23120
23121DataScale.prototype.setCharHeight = function (majorCharHeight) {
23122  this.majorCharHeight = majorCharHeight;
23123};
23124
23125DataScale.prototype.setHeight = function (containerHeight) {
23126  this.containerHeight = containerHeight;
23127};
23128
23129DataScale.prototype.determineScale = function () {
23130  var range = this._end - this._start;
23131  this.scale = this.containerHeight / range;
23132  var minimumStepValue = this.majorCharHeight / this.scale;
23133  var orderOfMagnitude = range > 0 ? Math.round(Math.log(range) / Math.LN10) : 0;
23134
23135  this.minorStepIdx = -1;
23136  this.magnitudefactor = Math.pow(10, orderOfMagnitude);
23137
23138  var start = 0;
23139  if (orderOfMagnitude < 0) {
23140    start = orderOfMagnitude;
23141  }
23142
23143  var solutionFound = false;
23144  for (var l = start; Math.abs(l) <= Math.abs(orderOfMagnitude); l++) {
23145    this.magnitudefactor = Math.pow(10, l);
23146    for (var j = 0; j < this.minorSteps.length; j++) {
23147      var stepSize = this.magnitudefactor * this.minorSteps[j];
23148      if (stepSize >= minimumStepValue) {
23149        solutionFound = true;
23150        this.minorStepIdx = j;
23151        break;
23152      }
23153    }
23154    if (solutionFound === true) {
23155      break;
23156    }
23157  }
23158};
23159
23160DataScale.prototype.is_major = function (value) {
23161  return value % (this.magnitudefactor * this.majorSteps[this.minorStepIdx]) === 0;
23162};
23163
23164DataScale.prototype.getStep = function () {
23165  return this.magnitudefactor * this.minorSteps[this.minorStepIdx];
23166};
23167
23168DataScale.prototype.getFirstMajor = function () {
23169  var majorStep = this.magnitudefactor * this.majorSteps[this.minorStepIdx];
23170  return this.convertValue(this._start + (majorStep - this._start % majorStep) % majorStep);
23171};
23172
23173DataScale.prototype.formatValue = function (current) {
23174  var returnValue = current.toPrecision(5);
23175  if (typeof this.formattingFunction === 'function') {
23176    returnValue = this.formattingFunction(current);
23177  }
23178
23179  if (typeof returnValue === 'number') {
23180    return '' + returnValue;
23181  } else if (typeof returnValue === 'string') {
23182    return returnValue;
23183  } else {
23184    return current.toPrecision(5);
23185  }
23186};
23187
23188DataScale.prototype.getLines = function () {
23189  var lines = [];
23190  var step = this.getStep();
23191  var bottomOffset = (step - this._start % step) % step;
23192  for (var i = this._start + bottomOffset; this._end - i > 0.00001; i += step) {
23193    if (i != this._start) {
23194      //Skip the bottom line
23195      lines.push({ major: this.is_major(i), y: this.convertValue(i), val: this.formatValue(i) });
23196    }
23197  }
23198  return lines;
23199};
23200
23201DataScale.prototype.followScale = function (other) {
23202  var oldStepIdx = this.minorStepIdx;
23203  var oldStart = this._start;
23204  var oldEnd = this._end;
23205
23206  var me = this;
23207  var increaseMagnitude = function increaseMagnitude() {
23208    me.magnitudefactor *= 2;
23209  };
23210  var decreaseMagnitude = function decreaseMagnitude() {
23211    me.magnitudefactor /= 2;
23212  };
23213
23214  if (other.minorStepIdx <= 1 && this.minorStepIdx <= 1 || other.minorStepIdx > 1 && this.minorStepIdx > 1) {
23215    //easy, no need to change stepIdx nor multiplication factor
23216  } else if (other.minorStepIdx < this.minorStepIdx) {
23217    //I'm 5, they are 4 per major.
23218    this.minorStepIdx = 1;
23219    if (oldStepIdx == 2) {
23220      increaseMagnitude();
23221    } else {
23222      increaseMagnitude();
23223      increaseMagnitude();
23224    }
23225  } else {
23226    //I'm 4, they are 5 per major
23227    this.minorStepIdx = 2;
23228    if (oldStepIdx == 1) {
23229      decreaseMagnitude();
23230    } else {
23231      decreaseMagnitude();
23232      decreaseMagnitude();
23233    }
23234  }
23235
23236  //Get masters stats:
23237  var otherZero = other.convertValue(0);
vendor: 7,494 bytes, lines 23238-23460
23238  var otherStep = other.getStep() * other.scale;
23239
23240  var done = false;
23241  var count = 0;
23242  //Loop until magnitude is correct for given constrains.
23243  while (!done && count++ < 5) {
23244
23245    //Get my stats:
23246    this.scale = otherStep / (this.minorSteps[this.minorStepIdx] * this.magnitudefactor);
23247    var newRange = this.containerHeight / this.scale;
23248
23249    //For the case the magnitudefactor has changed:
23250    this._start = oldStart;
23251    this._end = this._start + newRange;
23252
23253    var myOriginalZero = this._end * this.scale;
23254    var majorStep = this.magnitudefactor * this.majorSteps[this.minorStepIdx];
23255    var majorOffset = this.getFirstMajor() - other.getFirstMajor();
23256
23257    if (this.zeroAlign) {
23258      var zeroOffset = otherZero - myOriginalZero;
23259      this._end += zeroOffset / this.scale;
23260      this._start = this._end - newRange;
23261    } else {
23262      if (!this.autoScaleStart) {
23263        this._start += majorStep - majorOffset / this.scale;
23264        this._end = this._start + newRange;
23265      } else {
23266        this._start -= majorOffset / this.scale;
23267        this._end = this._start + newRange;
23268      }
23269    }
23270    if (!this.autoScaleEnd && this._end > oldEnd + 0.00001) {
23271      //Need to decrease magnitude to prevent scale overshoot! (end)
23272      decreaseMagnitude();
23273      done = false;
23274      continue;
23275    }
23276    if (!this.autoScaleStart && this._start < oldStart - 0.00001) {
23277      if (this.zeroAlign && oldStart >= 0) {
23278        console.warn("Can't adhere to given 'min' range, due to zeroalign");
23279      } else {
23280        //Need to decrease magnitude to prevent scale overshoot! (start)
23281        decreaseMagnitude();
23282        done = false;
23283        continue;
23284      }
23285    }
23286    if (this.autoScaleStart && this.autoScaleEnd && newRange < oldEnd - oldStart) {
23287      increaseMagnitude();
23288      done = false;
23289      continue;
23290    }
23291    done = true;
23292  }
23293};
23294
23295DataScale.prototype.convertValue = function (value) {
23296  return this.containerHeight - (value - this._start) * this.scale;
23297};
23298
23299DataScale.prototype.screenToValue = function (pixels) {
23300  return (this.containerHeight - pixels) / this.scale + this._start;
23301};
23302
23303module.exports = DataScale;
23304
23305/***/ }),
23306/* 109 */
23307/***/ (function(module, exports, __webpack_require__) {
23308
23309"use strict";
23310
23311
23312var _typeof2 = __webpack_require__(6);
23313
23314var _typeof3 = _interopRequireDefault(_typeof2);
23315
23316function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
23317
23318var util = __webpack_require__(2);
23319var Bars = __webpack_require__(110);
23320var Lines = __webpack_require__(111);
23321var Points = __webpack_require__(72);
23322
23323/**
23324 * /**
23325 * @param {object} group            | the object of the group from the dataset
23326 * @param {string} groupId          | ID of the group
23327 * @param {object} options          | the default options
23328 * @param {array} groupsUsingDefaultStyles  | this array has one entree.
23329 *                                            It is passed as an array so it is passed by reference.
23330 *                                            It enumerates through the default styles
23331 * @constructor GraphGroup
23332 */
23333function GraphGroup(group, groupId, options, groupsUsingDefaultStyles) {
23334  this.id = groupId;
23335  var fields = ['sampling', 'style', 'sort', 'yAxisOrientation', 'barChart', 'drawPoints', 'shaded', 'interpolation', 'zIndex', 'excludeFromStacking', 'excludeFromLegend'];
23336  this.options = util.selectiveBridgeObject(fields, options);
23337  this.usingDefaultStyle = group.className === undefined;
23338  this.groupsUsingDefaultStyles = groupsUsingDefaultStyles;
23339  this.zeroPosition = 0;
23340  this.update(group);
23341  if (this.usingDefaultStyle == true) {
23342    this.groupsUsingDefaultStyles[0] += 1;
23343  }
23344  this.itemsData = [];
23345  this.visible = group.visible === undefined ? true : group.visible;
23346}
23347
23348/**
23349 * this loads a reference to all items in this group into this group.
23350 * @param {array} items
23351 */
23352GraphGroup.prototype.setItems = function (items) {
23353  if (items != null) {
23354    this.itemsData = items;
23355    if (this.options.sort == true) {
23356      util.insertSort(this.itemsData, function (a, b) {
23357        return a.x > b.x ? 1 : -1;
23358      });
23359    }
23360  } else {
23361    this.itemsData = [];
23362  }
23363};
23364
23365GraphGroup.prototype.getItems = function () {
23366  return this.itemsData;
23367};
23368
23369/**
23370 * this is used for barcharts and shading, this way, we only have to calculate it once.
23371 * @param {number} pos
23372 */
23373GraphGroup.prototype.setZeroPosition = function (pos) {
23374  this.zeroPosition = pos;
23375};
23376
23377/**
23378 * set the options of the graph group over the default options.
23379 * @param {Object} options
23380 */
23381GraphGroup.prototype.setOptions = function (options) {
23382  if (options !== undefined) {
23383    var fields = ['sampling', 'style', 'sort', 'yAxisOrientation', 'barChart', 'zIndex', 'excludeFromStacking', 'excludeFromLegend'];
23384    util.selectiveDeepExtend(fields, this.options, options);
23385
23386    // if the group's drawPoints is a function delegate the callback to the onRender property
23387    if (typeof options.drawPoints == 'function') {
23388      options.drawPoints = {
23389        onRender: options.drawPoints
23390      };
23391    }
23392
23393    util.mergeOptions(this.options, options, 'interpolation');
23394    util.mergeOptions(this.options, options, 'drawPoints');
23395    util.mergeOptions(this.options, options, 'shaded');
23396
23397    if (options.interpolation) {
23398      if ((0, _typeof3['default'])(options.interpolation) == 'object') {
23399        if (options.interpolation.parametrization) {
23400          if (options.interpolation.parametrization == 'uniform') {
23401            this.options.interpolation.alpha = 0;
23402          } else if (options.interpolation.parametrization == 'chordal') {
23403            this.options.interpolation.alpha = 1.0;
23404          } else {
23405            this.options.interpolation.parametrization = 'centripetal';
23406            this.options.interpolation.alpha = 0.5;
23407          }
23408        }
23409      }
23410    }
23411  }
23412};
23413
23414/**
23415 * this updates the current group class with the latest group dataset entree, used in _updateGroup in linegraph
23416 * @param {vis.Group} group
23417 */
23418GraphGroup.prototype.update = function (group) {
23419  this.group = group;
23420  this.content = group.content || 'graph';
23421  this.className = group.className || this.className || 'vis-graph-group' + this.groupsUsingDefaultStyles[0] % 10;
23422  this.visible = group.visible === undefined ? true : group.visible;
23423  this.style = group.style;
23424  this.setOptions(group.options);
23425};
23426
23427/**
23428 * return the legend entree for this group.
23429 *
23430 * @param {number} iconWidth
23431 * @param {number} iconHeight
23432 * @param {{svg: (*|Element), svgElements: Object, options: Object, groups: Array.<Object>}} framework
23433 * @param {number} x
23434 * @param {number} y
23435 * @returns {{icon: (*|Element), label: (*|string), orientation: *}}
23436 */
23437GraphGroup.prototype.getLegend = function (iconWidth, iconHeight, framework, x, y) {
23438  if (framework == undefined || framework == null) {
23439    var svg = document.createElementNS('http://www.w3.org/2000/svg', "svg");
23440    framework = { svg: svg, svgElements: {}, options: this.options, groups: [this] };
23441  }
23442  if (x == undefined || x == null) {
23443    x = 0;
23444  }
23445  if (y == undefined || y == null) {
23446    y = 0.5 * iconHeight;
23447  }
23448  switch (this.options.style) {
23449    case "line":
23450      Lines.drawIcon(this, x, y, iconWidth, iconHeight, framework);
23451      break;
23452    case "points": //explicit no break
23453    case "point":
23454      Points.drawIcon(this, x, y, iconWidth, iconHeight, framework);
23455      break;
23456    case "bar":
23457      Bars.drawIcon(this, x, y, iconWidth, iconHeight, framework);
23458      break;
23459  }
23460  return { icon: framework.svg, label: this.content, orientation: this.options.yAxisOr
23460ientation };
23461};
23462
23463GraphGroup.prototype.getYRange = function (groupData) {
23464  var yMin = groupData[0].y;
23465  var yMax = groupData[0].y;
23466  for (var j = 0; j < groupData.length; j++) {
23467    yMin = yMin > groupData[j].y ? groupData[j].y : yMin;
23468    yMax = yMax < groupData[j].y ? groupData[j].y : yMax;
23469  }
23470  return { min: yMin, max: yMax, yAxisOrientation: this.options.yAxisOrientation };
23471};
23472
23473module.exports = GraphGroup;
23474
23475/***/ }),
23476/* 110 */
23477/***/ (function(module, exports, __webpack_require__) {
23478
23479"use strict";
23480
23481
23482var DOMutil = __webpack_require__(14);
23483var Points = __webpack_require__(72);
23484
23485/**
23486 *
23487 * @param {vis.GraphGroup.id} groupId
23488 * @param {Object} options   // TODO: Describe options
23489 * @constructor Bargraph
23490 */
23491function Bargraph(groupId, options) {// eslint-disable-line no-unused-vars
23492}
23493
23494Bargraph.drawIcon = function (group, x, y, iconWidth, iconHeight, framework) {
23495  var fillHeight = iconHeight * 0.5;
23496  var outline = DOMutil.getSVGElement("rect", framework.svgElements, framework.svg);
23497  outline.setAttributeNS(null, "x", x);
23498  outline.setAttributeNS(null, "y", y - fillHeight);
23499  outline.setAttributeNS(null, "width", iconWidth);
23500  outline.setAttributeNS(null, "height", 2 * fillHeight);
23501  outline.setAttributeNS(null, "class", "vis-outline");
23502
23503  var barWidth = Math.round(0.3 * iconWidth);
23504  var originalWidth = group.options.barChart.width;
23505  var scale = originalWidth / barWidth;
23506  var bar1Height = Math.round(0.4 * iconHeight);
23507  var bar2Height = Math.round(0.75 * iconHeight);
23508
23509  var offset = Math.round((iconWidth - 2 * barWidth) / 3);
23510
23511  DOMutil.drawBar(x + 0.5 * barWidth + offset, y + fillHeight - bar1Height - 1, barWidth, bar1Height, group.className + ' vis-bar', framework.svgElements, framework.svg, group.style);
23512  DOMutil.drawBar(x + 1.5 * barWidth + offset + 2, y + fillHeight - bar2Height - 1, barWidth, bar2Height, group.className + ' vis-bar', framework.svgElements, framework.svg, group.style);
23513
23514  if (group.options.drawPoints.enabled == true) {
23515    var groupTemplate = {
23516      style: group.options.drawPoints.style,
23517      styles: group.options.drawPoints.styles,
23518      size: group.options.drawPoints.size / scale,
23519      className: group.className
23520    };
23521    DOMutil.drawPoint(x + 0.5 * barWidth + offset, y + fillHeight - bar1Height - 1, groupTemplate, framework.svgElements, framework.svg);
23522    DOMutil.drawPoint(x + 1.5 * barWidth + offset + 2, y + fillHeight - bar2Height - 1, groupTemplate, framework.svgElements, framework.svg);
23523  }
23524};
23525
23526/**
23527 * draw a bar graph
23528 *
23529 * @param {Array.<vis.GraphGroup.id>} groupIds
23530 * @param {Object} processedGroupData
23531 * @param {{svg: Object, svgElements: Array.<Object>, options: Object, groups: Array.<vis.Group>}} framework
23532 */
23533Bargraph.draw = function (groupIds, processedGroupData, framework) {
23534  var combinedData = [];
23535  var intersections = {};
23536  var coreDistance;
23537  var key, drawData;
23538  var group;
23539  var i, j;
23540  var barPoints = 0;
23541
vendor: 26,712 bytes, lines 23542-24279
23542  // combine all barchart data
23543  for (i = 0; i < groupIds.length; i++) {
23544    group = framework.groups[groupIds[i]];
23545    if (group.options.style === 'bar') {
23546      if (group.visible === true && (framework.options.groups.visibility[groupIds[i]] === undefined || framework.options.groups.visibility[groupIds[i]] === true)) {
23547        for (j = 0; j < processedGroupData[groupIds[i]].length; j++) {
23548          combinedData.push({
23549            screen_x: processedGroupData[groupIds[i]][j].screen_x,
23550            screen_end: processedGroupData[groupIds[i]][j].screen_end,
23551            screen_y: processedGroupData[groupIds[i]][j].screen_y,
23552            x: processedGroupData[groupIds[i]][j].x,
23553            end: processedGroupData[groupIds[i]][j].end,
23554            y: processedGroupData[groupIds[i]][j].y,
23555            groupId: groupIds[i],
23556            label: processedGroupData[groupIds[i]][j].label
23557          });
23558          barPoints += 1;
23559        }
23560      }
23561    }
23562  }
23563
23564  if (barPoints === 0) {
23565    return;
23566  }
23567
23568  // sort by time and by group
23569  combinedData.sort(function (a, b) {
23570    if (a.screen_x === b.screen_x) {
23571      return a.groupId < b.groupId ? -1 : 1;
23572    } else {
23573      return a.screen_x - b.screen_x;
23574    }
23575  });
23576
23577  // get intersections
23578  Bargraph._getDataIntersections(intersections, combinedData);
23579
23580  // plot barchart
23581  for (i = 0; i < combinedData.length; i++) {
23582    group = framework.groups[combinedData[i].groupId];
23583    var minWidth = group.options.barChart.minWidth != undefined ? group.options.barChart.minWidth : 0.1 * group.options.barChart.width;
23584
23585    key = combinedData[i].screen_x;
23586    var heightOffset = 0;
23587    if (intersections[key] === undefined) {
23588      if (i + 1 < combinedData.length) {
23589        coreDistance = Math.abs(combinedData[i + 1].screen_x - key);
23590      }
23591      drawData = Bargraph._getSafeDrawData(coreDistance, group, minWidth);
23592    } else {
23593      var nextKey = i + (intersections[key].amount - intersections[key].resolved);
23594      if (nextKey < combinedData.length) {
23595        coreDistance = Math.abs(combinedData[nextKey].screen_x - key);
23596      }
23597      drawData = Bargraph._getSafeDrawData(coreDistance, group, minWidth);
23598      intersections[key].resolved += 1;
23599
23600      if (group.options.stack === true && group.options.excludeFromStacking !== true) {
23601        if (combinedData[i].screen_y < group.zeroPosition) {
23602          heightOffset = intersections[key].accumulatedNegative;
23603          intersections[key].accumulatedNegative += group.zeroPosition - combinedData[i].screen_y;
23604        } else {
23605          heightOffset = intersections[key].accumulatedPositive;
23606          intersections[key].accumulatedPositive += group.zeroPosition - combinedData[i].screen_y;
23607        }
23608      } else if (group.options.barChart.sideBySide === true) {
23609        drawData.width = drawData.width / intersections[key].amount;
23610        drawData.offset += intersections[key].resolved * drawData.width - 0.5 * drawData.width * (intersections[key].amount + 1);
23611      }
23612    }
23613
23614    var dataWidth = drawData.width;
23615    var start = combinedData[i].screen_x;
23616
23617    // are we drawing explicit boxes? (we supplied an end value)
23618    if (combinedData[i].screen_end != undefined) {
23619      dataWidth = combinedData[i].screen_end - combinedData[i].screen_x;
23620      start += dataWidth * 0.5;
23621    } else {
23622      start += drawData.offset;
23623    }
23624
23625    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);
23626
23627    // draw points
23628    if (group.options.drawPoints.enabled === true) {
23629      var pointData = {
23630        screen_x: combinedData[i].screen_x,
23631        screen_y: combinedData[i].screen_y - heightOffset,
23632        x: combinedData[i].x,
23633        y: combinedData[i].y,
23634        groupId: combinedData[i].groupId,
23635        label: combinedData[i].label
23636      };
23637      Points.draw([pointData], group, framework, drawData.offset);
23638      //DOMutil.drawPoint(combinedData[i].x + drawData.offset, combinedData[i].y, group, framework.svgElements, framework.svg);
23639    }
23640  }
23641};
23642
23643/**
23644 * Fill the intersections object with counters of how many datapoints share the same x coordinates
23645 * @param {Object} intersections
23646 * @param {Array.<Object>} combinedData
23647 * @private
23648 */
23649Bargraph._getDataIntersections = function (intersections, combinedData) {
23650  // get intersections
23651  var coreDistance;
23652  for (var i = 0; i < combinedData.length; i++) {
23653    if (i + 1 < combinedData.length) {
23654      coreDistance = Math.abs(combinedData[i + 1].screen_x - combinedData[i].screen_x);
23655    }
23656    if (i > 0) {
23657      coreDistance = Math.min(coreDistance, Math.abs(combinedData[i - 1].screen_x - combinedData[i].screen_x));
23658    }
23659    if (coreDistance === 0) {
23660      if (intersections[combinedData[i].screen_x] === undefined) {
23661        intersections[combinedData[i].screen_x] = {
23662          amount: 0,
23663          resolved: 0,
23664          accumulatedPositive: 0,
23665          accumulatedNegative: 0
23666        };
23667      }
23668      intersections[combinedData[i].screen_x].amount += 1;
23669    }
23670  }
23671};
23672
23673/**
23674 * Get the width and offset for bargraphs based on the coredistance between datapoints
23675 *
23676 * @param {number} coreDistance
23677 * @param {vis.Group} group
23678 * @param {number} minWidth
23679 * @returns {{width: number, offset: number}}
23680 * @private
23681 */
23682Bargraph._getSafeDrawData = function (coreDistance, group, minWidth) {
23683  var width, offset;
23684  if (coreDistance < group.options.barChart.width && coreDistance > 0) {
23685    width = coreDistance < minWidth ? minWidth : coreDistance;
23686
23687    offset = 0; // recalculate offset with the new width;
23688    if (group.options.barChart.align === 'left') {
23689      offset -= 0.5 * coreDistance;
23690    } else if (group.options.barChart.align === 'right') {
23691      offset += 0.5 * coreDistance;
23692    }
23693  } else {
23694    // default settings
23695    width = group.options.barChart.width;
23696    offset = 0;
23697    if (group.options.barChart.align === 'left') {
23698      offset -= 0.5 * group.options.barChart.width;
23699    } else if (group.options.barChart.align === 'right') {
23700      offset += 0.5 * group.options.barChart.width;
23701    }
23702  }
23703
23704  return { width: width, offset: offset };
23705};
23706
23707Bargraph.getStackedYRange = function (combinedData, groupRanges, groupIds, groupLabel, orientation) {
23708  if (combinedData.length > 0) {
23709    // sort by time and by group
23710    combinedData.sort(function (a, b) {
23711      if (a.screen_x === b.screen_x) {
23712        return a.groupId < b.groupId ? -1 : 1;
23713      } else {
23714        return a.screen_x - b.screen_x;
23715      }
23716    });
23717    var intersections = {};
23718
23719    Bargraph._getDataIntersections(intersections, combinedData);
23720    groupRanges[groupLabel] = Bargraph._getStackedYRange(intersections, combinedData);
23721    groupRanges[groupLabel].yAxisOrientation = orientation;
23722    groupIds.push(groupLabel);
23723  }
23724};
23725
23726Bargraph._getStackedYRange = function (intersections, combinedData) {
23727  var key;
23728  var yMin = combinedData[0].screen_y;
23729  var yMax = combinedData[0].screen_y;
23730  for (var i = 0; i < combinedData.length; i++) {
23731    key = combinedData[i].screen_x;
23732    if (intersections[key] === undefined) {
23733      yMin = yMin > combinedData[i].screen_y ? combinedData[i].screen_y : yMin;
23734      yMax = yMax < combinedData[i].screen_y ? combinedData[i].screen_y : yMax;
23735    } else {
23736      if (combinedData[i].screen_y < 0) {
23737        intersections[key].accumulatedNegative += combinedData[i].screen_y;
23738      } else {
23739        intersections[key].accumulatedPositive += combinedData[i].screen_y;
23740      }
23741    }
23742  }
23743  for (var xpos in intersections) {
23744    if (intersections.hasOwnProperty(xpos)) {
23745      yMin = yMin > intersections[xpos].accumulatedNegative ? intersections[xpos].accumulatedNegative : yMin;
23746      yMin = yMin > intersections[xpos].accumulatedPositive ? intersections[xpos].accumulatedPositive : yMin;
23747      yMax = yMax < intersections[xpos].accumulatedNegative ? intersections[xpos].accumulatedNegative : yMax;
23748      yMax = yMax < intersections[xpos].accumulatedPositive ? intersections[xpos].accumulatedPositive : yMax;
23749    }
23750  }
23751
23752  return { min: yMin, max: yMax };
23753};
23754
23755module.exports = Bargraph;
23756
23757/***/ }),
23758/* 111 */
23759/***/ (function(module, exports, __webpack_require__) {
23760
23761"use strict";
23762
23763
23764var DOMutil = __webpack_require__(14);
23765
23766/**
23767 *
23768 * @param {vis.GraphGroup.id} groupId
23769 * @param {Object} options   // TODO: Describe options
23770 * @constructor Line
23771 */
23772function Line(groupId, options) {// eslint-disable-line no-unused-vars
23773}
23774
23775Line.calcPath = function (dataset, group) {
23776    if (dataset != null) {
23777        if (dataset.length > 0) {
23778            var d = [];
23779
23780            // construct path from dataset
23781            if (group.options.interpolation.enabled == true) {
23782                d = Line._catmullRom(dataset, group);
23783            } else {
23784                d = Line._linear(dataset);
23785            }
23786            return d;
23787        }
23788    }
23789};
23790
23791Line.drawIcon = function (group, x, y, iconWidth, iconHeight, framework) {
23792    var fillHeight = iconHeight * 0.5;
23793    var path, fillPath;
23794
23795    var outline = DOMutil.getSVGElement("rect", framework.svgElements, framework.svg);
23796    outline.setAttributeNS(null, "x", x);
23797    outline.setAttributeNS(null, "y", y - fillHeight);
23798    outline.setAttributeNS(null, "width", iconWidth);
23799    outline.setAttributeNS(null, "height", 2 * fillHeight);
23800    outline.setAttributeNS(null, "class", "vis-outline");
23801
23802    path = DOMutil.getSVGElement("path", framework.svgElements, framework.svg);
23803    path.setAttributeNS(null, "class", group.className);
23804    if (group.style !== undefined) {
23805        path.setAttributeNS(null, "style", group.style);
23806    }
23807
23808    path.setAttributeNS(null, "d", "M" + x + "," + y + " L" + (x + iconWidth) + "," + y + "");
23809    if (group.options.shaded.enabled == true) {
23810        fillPath = DOMutil.getSVGElement("path", framework.svgElements, framework.svg);
23811        if (group.options.shaded.orientation == 'top') {
23812            fillPath.setAttributeNS(null, "d", "M" + x + ", " + (y - fillHeight) + "L" + x + "," + y + " L" + (x + iconWidth) + "," + y + " L" + (x + iconWidth) + "," + (y - fillHeight));
23813        } else {
23814            fillPath.setAttributeNS(null, "d", "M" + x + "," + y + " " + "L" + x + "," + (y + fillHeight) + " " + "L" + (x + iconWidth) + "," + (y + fillHeight) + "L" + (x + iconWidth) + "," + y);
23815        }
23816        fillPath.setAttributeNS(null, "class", group.className + " vis-icon-fill");
23817        if (group.options.shaded.style !== undefined && group.options.shaded.style !== "") {
23818            fillPath.setAttributeNS(null, "style", group.options.shaded.style);
23819        }
23820    }
23821
23822    if (group.options.drawPoints.enabled == true) {
23823        var groupTemplate = {
23824            style: group.options.drawPoints.style,
23825            styles: group.options.drawPoints.styles,
23826            size: group.options.drawPoints.size,
23827            className: group.className
23828        };
23829        DOMutil.drawPoint(x + 0.5 * iconWidth, y, groupTemplate, framework.svgElements, framework.svg);
23830    }
23831};
23832
23833Line.drawShading = function (pathArray, group, subPathArray, framework) {
23834    // append shading to the path
23835    if (group.options.shaded.enabled == true) {
23836        var svgHeight = Number(framework.svg.style.height.replace('px', ''));
23837        var fillPath = DOMutil.getSVGElement('path', framework.svgElements, framework.svg);
23838        var type = "L";
23839        if (group.options.interpolation.enabled == true) {
23840            type = "C";
23841        }
23842        var dFill;
23843        var zero = 0;
23844        if (group.options.shaded.orientation == 'top') {
23845            zero = 0;
23846        } else if (group.options.shaded.orientation == 'bottom') {
23847            zero = svgHeight;
23848        } else {
23849            zero = Math.min(Math.max(0, group.zeroPosition), svgHeight);
23850        }
23851        if (group.options.shaded.orientation == 'group' && subPathArray != null && subPathArray != undefined) {
23852            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";
23853        } else {
23854            dFill = 'M' + pathArray[0][0] + "," + pathArray[0][1] + " " + this.serializePath(pathArray, type, false) + ' V' + zero + ' H' + pathArray[0][0] + " Z";
23855        }
23856
23857        fillPath.setAttributeNS(null, 'class', group.className + ' vis-fill');
23858        if (group.options.shaded.style !== undefined) {
23859            fillPath.setAttributeNS(null, 'style', group.options.shaded.style);
23860        }
23861        fillPath.setAttributeNS(null, 'd', dFill);
23862    }
23863};
23864
23865/**
23866 * draw a line graph
23867 *
23868 * @param {Array.<Object>} pathArray
23869 * @param {vis.Group} group
23870 * @param {{svg: Object, svgElements: Array.<Object>, options: Object, groups: Array.<vis.Group>}} framework
23871 */
23872Line.draw = function (pathArray, group, framework) {
23873    if (pathArray != null && pathArray != undefined) {
23874        var path = DOMutil.getSVGElement('path', framework.svgElements, framework.svg);
23875        path.setAttributeNS(null, "class", group.className);
23876        if (group.style !== undefined) {
23877            path.setAttributeNS(null, "style", group.style);
23878        }
23879
23880        var type = "L";
23881        if (group.options.interpolation.enabled == true) {
23882            type = "C";
23883        }
23884        // copy properties to path for drawing.
23885        path.setAttributeNS(null, 'd', 'M' + pathArray[0][0] + "," + pathArray[0][1] + " " + this.serializePath(pathArray, type, false));
23886    }
23887};
23888
23889Line.serializePath = function (pathArray, type, inverse) {
23890    if (pathArray.length < 2) {
23891        //Too little data to create a path.
23892        return "";
23893    }
23894    var d = type;
23895    var i;
23896    if (inverse) {
23897        for (i = pathArray.length - 2; i > 0; i--) {
23898            d += pathArray[i][0] + "," + pathArray[i][1] + " ";
23899        }
23900    } else {
23901        for (i = 1; i < pathArray.length; i++) {
23902            d += pathArray[i][0] + "," + pathArray[i][1] + " ";
23903        }
23904    }
23905    return d;
23906};
23907
23908/**
23909 * This uses an uniform parametrization of the interpolation algorithm:
23910 * 'On the Parameterization of Catmull-Rom Curves' by Cem Yuksel et al.
23911 * @param {Array.<Object>} data
23912 * @returns {string}
23913 * @private
23914 */
23915Line._catmullRomUniform = function (data) {
23916    // catmull rom
23917    var p0, p1, p2, p3, bp1, bp2;
23918    var d = [];
23919    d.push([Math.round(data[0].screen_x), Math.round(data[0].screen_y)]);
23920    var normalization = 1 / 6;
23921    var length = data.length;
23922    for (var i = 0; i < length - 1; i++) {
23923
23924        p0 = i == 0 ? data[0] : data[i - 1];
23925        p1 = data[i];
23926        p2 = data[i + 1];
23927        p3 = i + 2 < length ? data[i + 2] : p2;
23928
23929        // Catmull-Rom to Cubic Bezier conversion matrix
23930        //    0       1       0       0
23931        //  -1/6      1      1/6      0
23932        //    0      1/6      1     -1/6
23933        //    0       0       1       0
23934
23935        //    bp0 = { x: p1.x,                               y: p1.y };
23936        bp1 = {
23937            screen_x: (-p0.screen_x + 6 * p1.screen_x + p2.screen_x) * normalization,
23938            screen_y: (-p0.screen_y + 6 * p1.screen_y + p2.screen_y) * normalization
23939        };
23940        bp2 = {
23941            screen_x: (p1.screen_x + 6 * p2.screen_x - p3.screen_x) * normalization,
23942            screen_y: (p1.screen_y + 6 * p2.screen_y - p3.screen_y) * normalization
23943        };
23944        //    bp0 = { x: p2.x,                               y: p2.y };
23945
23946        d.push([bp1.screen_x, bp1.screen_y]);
23947        d.push([bp2.screen_x, bp2.screen_y]);
23948        d.push([p2.screen_x, p2.screen_y]);
23949    }
23950
23951    return d;
23952};
23953
23954/**
23955 * This uses either the chordal or centripetal parameterization of the catmull-rom algorithm.
23956 * By default, the centripetal parameterization is used because this gives the nicest results.
23957 * These parameterizations are relatively heavy because the distance between 4 points have to be calculated.
23958 *
23959 * One optimization can be used to reuse distances since this is a sliding window approach.
23960 * @param {Array.<Object>} data
23961 * @param {vis.GraphGroup} group
23962 * @returns {string}
23963 * @private
23964 */
23965Line._catmullRom = function (data, group) {
23966    var alpha = group.options.interpolation.alpha;
23967    if (alpha == 0 || alpha === undefined) {
23968        return this._catmullRomUniform(data);
23969    } else {
23970        var p0, p1, p2, p3, bp1, bp2, d1, d2, d3, A, B, N, M;
23971        var d3powA, d2powA, d3pow2A, d2pow2A, d1pow2A, d1powA;
23972        var d = [];
23973        d.push([Math.round(data[0].screen_x), Math.round(data[0].screen_y)]);
23974        var length = data.length;
23975        for (var i = 0; i < length - 1; i++) {
23976
23977            p0 = i == 0 ? data[0] : data[i - 1];
23978            p1 = data[i];
23979            p2 = data[i + 1];
23980            p3 = i + 2 < length ? data[i + 2] : p2;
23981
23982            d1 = Math.sqrt(Math.pow(p0.screen_x - p1.screen_x, 2) + Math.pow(p0.screen_y - p1.screen_y, 2));
23983            d2 = Math.sqrt(Math.pow(p1.screen_x - p2.screen_x, 2) + Math.pow(p1.screen_y - p2.screen_y, 2));
23984            d3 = Math.sqrt(Math.pow(p2.screen_x - p3.screen_x, 2) + Math.pow(p2.screen_y - p3.screen_y, 2));
23985
23986            // Catmull-Rom to Cubic Bezier conversion matrix
23987
23988            // A = 2d1^2a + 3d1^a * d2^a + d3^2a
23989            // B = 2d3^2a + 3d3^a * d2^a + d2^2a
23990
23991            // [   0             1            0          0          ]
23992            // [   -d2^2a /N     A/N          d1^2a /N   0          ]
23993            // [   0             d3^2a /M     B/M        -d2^2a /M  ]
23994            // [   0             0            1          0          ]
23995
23996            d3powA = Math.pow(d3, alpha);
23997            d3pow2A = Math.pow(d3, 2 * alpha);
23998            d2powA = Math.pow(d2, alpha);
23999            d2pow2A = Math.pow(d2, 2 * alpha);
24000            d1powA = Math.pow(d1, alpha);
24001            d1pow2A = Math.pow(d1, 2 * alpha);
24002
24003            A = 2 * d1pow2A + 3 * d1powA * d2powA + d2pow2A;
24004            B = 2 * d3pow2A + 3 * d3powA * d2powA + d2pow2A;
24005            N = 3 * d1powA * (d1powA + d2powA);
24006            if (N > 0) {
24007                N = 1 / N;
24008            }
24009            M = 3 * d3powA * (d3powA + d2powA);
24010            if (M > 0) {
24011                M = 1 / M;
24012            }
24013
24014            bp1 = {
24015                screen_x: (-d2pow2A * p0.screen_x + A * p1.screen_x + d1pow2A * p2.screen_x) * N,
24016                screen_y: (-d2pow2A * p0.screen_y + A * p1.screen_y + d1pow2A * p2.screen_y) * N
24017            };
24018
24019            bp2 = {
24020                screen_x: (d3pow2A * p1.screen_x + B * p2.screen_x - d2pow2A * p3.screen_x) * M,
24021                screen_y: (d3pow2A * p1.screen_y + B * p2.screen_y - d2pow2A * p3.screen_y) * M
24022            };
24023
24024            if (bp1.screen_x == 0 && bp1.screen_y == 0) {
24025                bp1 = p1;
24026            }
24027            if (bp2.screen_x == 0 && bp2.screen_y == 0) {
24028                bp2 = p2;
24029            }
24030            d.push([bp1.screen_x, bp1.screen_y]);
24031            d.push([bp2.screen_x, bp2.screen_y]);
24032            d.push([p2.screen_x, p2.screen_y]);
24033        }
24034
24035        return d;
24036    }
24037};
24038
24039/**
24040 * this generates the SVG path for a linear drawing between datapoints.
24041 * @param {Array.<Object>} data
24042 * @returns {string}
24043 * @private
24044 */
24045Line._linear = function (data) {
24046    // linear
24047    var d = [];
24048    for (var i = 0; i < data.length; i++) {
24049        d.push([data[i].screen_x, data[i].screen_y]);
24050    }
24051    return d;
24052};
24053
24054module.exports = Line;
24055
24056/***/ }),
24057/* 112 */
24058/***/ (function(module, exports, __webpack_require__) {
24059
24060"use strict";
24061
24062
24063var _keys = __webpack_require__(8);
24064
24065var _keys2 = _interopRequireDefault(_keys);
24066
24067function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
24068
24069var util = __webpack_require__(2);
24070var DOMutil = __webpack_require__(14);
24071var Component = __webpack_require__(16);
24072
24073/**
24074 * Legend for Graph2d
24075 *
24076 * @param {vis.Graph2d.body} body
24077 * @param {vis.Graph2d.options} options
24078 * @param {number} side
24079 * @param {vis.LineGraph.options} linegraphOptions
24080 * @constructor Legend
24081 * @extends Component
24082 */
24083function Legend(body, options, side, linegraphOptions) {
24084  this.body = body;
24085  this.defaultOptions = {
24086    enabled: false,
24087    icons: true,
24088    iconSize: 20,
24089    iconSpacing: 6,
24090    left: {
24091      visible: true,
24092      position: 'top-left' // top/bottom - left,center,right
24093    },
24094    right: {
24095      visible: true,
24096      position: 'top-right' // top/bottom - left,center,right
24097    }
24098  };
24099
24100  this.side = side;
24101  this.options = util.extend({}, this.defaultOptions);
24102  this.linegraphOptions = linegraphOptions;
24103
24104  this.svgElements = {};
24105  this.dom = {};
24106  this.groups = {};
24107  this.amountOfGroups = 0;
24108  this._create();
24109  this.framework = { svg: this.svg, svgElements: this.svgElements, options: this.options, groups: this.groups };
24110
24111  this.setOptions(options);
24112}
24113
24114Legend.prototype = new Component();
24115
24116Legend.prototype.clear = function () {
24117  this.groups = {};
24118  this.amountOfGroups = 0;
24119};
24120
24121Legend.prototype.addGroup = function (label, graphOptions) {
24122
24123  // Include a group only if the group option 'excludeFromLegend: false' is not set.
24124  if (graphOptions.options.excludeFromLegend != true) {
24125    if (!this.groups.hasOwnProperty(label)) {
24126      this.groups[label] = graphOptions;
24127    }
24128    this.amountOfGroups += 1;
24129  }
24130};
24131
24132Legend.prototype.updateGroup = function (label, graphOptions) {
24133  this.groups[label] = graphOptions;
24134};
24135
24136Legend.prototype.removeGroup = function (label) {
24137  if (this.groups.hasOwnProperty(label)) {
24138    delete this.groups[label];
24139    this.amountOfGroups -= 1;
24140  }
24141};
24142
24143Legend.prototype._create = function () {
24144  this.dom.frame = document.createElement('div');
24145  this.dom.frame.className = 'vis-legend';
24146  this.dom.frame.style.position = "absolute";
24147  this.dom.frame.style.top = "10px";
24148  this.dom.frame.style.display = "block";
24149
24150  this.dom.textArea = document.createElement('div');
24151  this.dom.textArea.className = 'vis-legend-text';
24152  this.dom.textArea.style.position = "relative";
24153  this.dom.textArea.style.top = "0px";
24154
24155  this.svg = document.createElementNS('http://www.w3.org/2000/svg', "svg");
24156  this.svg.style.position = 'absolute';
24157  this.svg.style.top = 0 + 'px';
24158  this.svg.style.width = this.options.iconSize + 5 + 'px';
24159  this.svg.style.height = '100%';
24160
24161  this.dom.frame.appendChild(this.svg);
24162  this.dom.frame.appendChild(this.dom.textArea);
24163};
24164
24165/**
24166 * Hide the component from the DOM
24167 */
24168Legend.prototype.hide = function () {
24169  // remove the frame containing the items
24170  if (this.dom.frame.parentNode) {
24171    this.dom.frame.parentNode.removeChild(this.dom.frame);
24172  }
24173};
24174
24175/**
24176 * Show the component in the DOM (when not already visible).
24177 */
24178Legend.prototype.show = function () {
24179  // show frame containing the items
24180  if (!this.dom.frame.parentNode) {
24181    this.body.dom.center.appendChild(this.dom.frame);
24182  }
24183};
24184
24185Legend.prototype.setOptions = function (options) {
24186  var fields = ['enabled', 'orientation', 'icons', 'left', 'right'];
24187  util.selectiveDeepExtend(fields, this.options, options);
24188};
24189
24190Legend.prototype.redraw = function () {
24191  var activeGroups = 0;
24192  var groupArray = (0, _keys2['default'])(this.groups);
24193  groupArray.sort(function (a, b) {
24194    return a < b ? -1 : 1;
24195  });
24196
24197  for (var i = 0; i < groupArray.length; i++) {
24198    var groupId = groupArray[i];
24199    if (this.groups[groupId].visible == true && (this.linegraphOptions.visibility[groupId] === undefined || this.linegraphOptions.visibility[groupId] == true)) {
24200      activeGroups++;
24201    }
24202  }
24203
24204  if (this.options[this.side].visible == false || this.amountOfGroups == 0 || this.options.enabled == false || activeGroups == 0) {
24205    this.hide();
24206  } else {
24207    this.show();
24208    if (this.options[this.side].position == 'top-left' || this.options[this.side].position == 'bottom-left') {
24209      this.dom.frame.style.left = '4px';
24210      this.dom.frame.style.textAlign = "left";
24211      this.dom.textArea.style.textAlign = "left";
24212      this.dom.textArea.style.left = this.options.iconSize + 15 + 'px';
24213      this.dom.textArea.style.right = '';
24214      this.svg.style.left = 0 + 'px';
24215      this.svg.style.right = '';
24216    } else {
24217      this.dom.frame.style.right = '4px';
24218      this.dom.frame.style.textAlign = "right";
24219      this.dom.textArea.style.textAlign = "right";
24220      this.dom.textArea.style.right = this.options.iconSize + 15 + 'px';
24221      this.dom.textArea.style.left = '';
24222      this.svg.style.right = 0 + 'px';
24223      this.svg.style.left = '';
24224    }
24225
24226    if (this.options[this.side].position == 'top-left' || this.options[this.side].position == 'top-right') {
24227      this.dom.frame.style.top = 4 - Number(this.body.dom.center.style.top.replace("px", "")) + 'px';
24228      this.dom.frame.style.bottom = '';
24229    } else {
24230      var scrollableHeight = this.body.domProps.center.height - this.body.domProps.centerContainer.height;
24231      this.dom.frame.style.bottom = 4 + scrollableHeight + Number(this.body.dom.center.style.top.replace("px", "")) + 'px';
24232      this.dom.frame.style.top = '';
24233    }
24234
24235    if (this.options.icons == false) {
24236      this.dom.frame.style.width = this.dom.textArea.offsetWidth + 10 + 'px';
24237      this.dom.textArea.style.right = '';
24238      this.dom.textArea.style.left = '';
24239      this.svg.style.width = '0px';
24240    } else {
24241      this.dom.frame.style.width = this.options.iconSize + 15 + this.dom.textArea.offsetWidth + 10 + 'px';
24242      this.drawLegendIcons();
24243    }
24244
24245    var content = '';
24246    for (i = 0; i < groupArray.length; i++) {
24247      groupId = groupArray[i];
24248      if (this.groups[groupId].visible == true && (this.linegraphOptions.visibility[groupId] === undefined || this.linegraphOptions.visibility[groupId] == true)) {
24249        content += this.groups[groupId].content + '<br />';
24250      }
24251    }
24252    this.dom.textArea.innerHTML = content;
24253    this.dom.textArea.style.lineHeight = 0.75 * this.options.iconSize + this.options.iconSpacing + 'px';
24254  }
24255};
24256
24257Legend.prototype.drawLegendIcons = function () {
24258  if (this.dom.frame.parentNode) {
24259    var groupArray = (0, _keys2['default'])(this.groups);
24260    groupArray.sort(function (a, b) {
24261      return a < b ? -1 : 1;
24262    });
24263
24264    // this resets the elements so the order is maintained
24265    DOMutil.resetElements(this.svgElements);
24266
24267    var padding = window.getComputedStyle(this.dom.frame).paddingTop;
24268    var iconOffset = Number(padding.replace('px', ''));
24269    var x = iconOffset;
24270    var iconWidth = this.options.iconSize;
24271    var iconHeight = 0.75 * this.options.iconSize;
24272    var y = iconOffset + 0.5 * iconHeight + 3;
24273
24274    this.svg.style.width = iconWidth + 5 + iconOffset + 'px';
24275
24276    for (var i = 0; i < groupArray.length; i++) {
24277      var groupId = groupArray[i];
24278      if (this.groups[groupId].visible == true && (this.linegraphOptions.visibility[groupId] === undefined || this.linegraphOptions.visibility[groupId] == true)) {
24279        this.groups[groupId].getLegend(iconWidth, iconHeight, this.framework, x, y);
24280        y += iconHeight + this.options.iconSpacing;
24281      }
24282    }
24283  }
24284};
24285
24286module.exports = Legend;
24287
24288/***/ }),
24289/* 113 */
24290/***/ (function(module, exports, __webpack_require__) {
24291
24292"use strict";
24293
24294
24295Object.defineProperty(exports, "__esModule", {
24296  value: true
24297});
24298/**
24299 * This object contains all possible options. It will check if the types are correct, if required if the option is one
24300 * of the allowed values.
24301 *
24302 * __any__ means that the name of the property does not matter.
24303 * __type__ is a required field for all objects and contains the allowed types of all objects
24304 */
24305var string = 'string';
24306var bool = 'boolean';
24307var number = 'number';
24308var array = 'array';
24309var date = 'date';
24310var object = 'object'; // should only be in a __type__ property
24311var dom = 'dom';
24312var moment = 'moment';
24313var any = 'any';
24314
24315var allOptions = {
24316  configure: {
24317    enabled: { 'boolean': bool },
24318    filter: { 'boolean': bool, 'function': 'function' },
24319    container: { dom: dom },
24320    __type__: { object: object, 'boolean': bool, 'function': 'function' }
24321  },
24322
24323  //globals :
24324  yAxisOrientation: { string: ['left', 'right'] },
24325  defaultGroup: { string: string },
24326  sort: { 'boolean': bool },
24327  sampling: { 'boolean': bool },
24328  stack: { 'boolean': bool },
24329  graphHeight: { string: string, number: number },
24330  shaded: {
24331    enabled: { 'boolean': bool },
24332    orientation: { string: ['bottom', 'top', 'zero', 'group'] }, // top, bottom, zero, group
24333    groupId: { object: object },
24334    __type__: { 'boolean': bool, object: object }
24335  },
24336  style: { string: ['line', 'bar', 'points'] }, // line, bar
24337  barChart: {
24338    width: { number: number },
24339    minWidth: { number: number },
24340    sideBySide: { 'boolean': bool },
24341    align: { string: ['left', 'center', 'right'] },
24342    __type__: { object: object }
24343  },
24344  interpolation: {
24345    enabled: { 'boolean': bool },
24346    parametrization: { string: ['centripetal', 'chordal', 'uniform'] }, // uniform (alpha = 0.0), chordal (alpha = 1.0), centripetal (alpha = 0.5)
24347    alpha: { number: number },
24348    __type__: { object: object, 'boolean': bool }
24349  },
24350  drawPoints: {
24351    enabled: { 'boolean': bool },
24352    onRender: { 'function': 'function' },
24353    size: { number: number },
24354    style: { string: ['square', 'circle'] }, // square, circle
24355    __type__: { object: object, 'boolean': bool, 'function': 'function' }
24356  },
24357  dataAxis: {
24358    showMinorLabels: { 'boolean': bool },
24359    showMajorLabels: { 'boolean': bool },
24360    icons: { 'boolean': bool },
24361    width: { string: string, number: number },
24362    visible: { 'boolean': bool },
24363    alignZeros: { 'boolean': bool },
24364    left: {
24365      range: { min: { number: number, 'undefined': 'undefined' }, max: { number: number, 'undefined': 'undefined' }, __type__: { object: object } },
24366      format: { 'function': 'function' },
24367      title: { text: { string: string, number: number, 'undefined': 'undefined' }, style: { string: string, 'undefined': 'undefined' }, __type__: { object: object } },
24368      __type__: { object: object }
24369    },
24370    right: {
24371      range: { min: { number: number, 'undefined': 'undefined' }, max: { number: number, 'undefined': 'undefined' }, __type__: { object: object } },
24372      format: { 'function': 'function' },
24373      title: { text: { string: string, number: number, 'undefined': 'undefined' }, style: { string: string, 'undefined': 'undefined' }, __type__: { object: object } },
24374      __type__: { object: object }
24375    },
24376    __type__: { object: object }
24377  },
24378  legend: {
24379    enabled: { 'boolean': bool },
24380    icons: { 'boolean': bool },
24381    left: {
24382      visible: { 'boolean': bool },
24383      position: { string: ['top-right', 'bottom-right', 'top-left', 'bottom-left'] },
24384      __type__: { object: object }
24385    },
24386    right: {
24387      visible: { 'boolean': bool },
24388      position: { string: ['top-right', 'bottom-right', 'top-left', 'bottom-left'] },
24389      __type__: { object: object }
24390    },
24391    __type__: { object: object, 'boolean': bool }
24392  },
24393  groups: {
24394    visibility: { any: any },
24395    __type__: { object: object }
24396  },
24397
24398  autoResize: { 'boolean': bool },
24399  throttleRedraw: { number: number }, // TODO: DEPRICATED see https://github.com/almende/vis/issues/2511
24400  clickToUse: { 'boolean': bool },
24401  end: { number: number, date: date, string: string, moment: moment },
24402  format: {
24403    minorLabels: {
24404      millisecond: { string: string, 'undefined': 'undefined' },
24405      second: { string: string, 'undefined': 'undefined' },
24406      minute: { string: string, 'undefined': 'undefined' },
24407      hour: { string: string, 'undefined': 'undefined' },
24408      weekday: { string: string, 'undefined': 'undefined' },
24409      day: { string: string, 'undefined': 'undefined' },
24410      month: { string: string, 'undefined': 'undefined' },
24411      year: { string: string, 'undefined': 'undefined' },
24412      __type__: { object: object }
24413    },
24414    majorLabels: {
24415      millisecond: { string: string, 'undefined': 'undefined' },
24416      second: { string: string, 'undefined': 'undefined' },
24417      minute: { string: string, 'undefined': 'undefined' },
24418      hour: { string: string, 'undefined': 'undefined' },
24419      weekday: { string: string, 'undefined': 'undefined' },
24420      day: { string: string, 'undefined': 'undefined' },
24421      month: { string: string, 'undefined': 'undefined' },
24422      year: { string: string, 'undefined': 'undefined' },
24423      __type__: { object: object }
24424    },
24425    __type__: { object: object }
24426  },
24427  moment: { 'function': 'function' },
24428  height: { string: string, number: number },
24429  hiddenDates: {
24430    start: { date: date, number: number, string: string, moment: moment },
24431    end: { date: date, number: number, string: string, moment: moment },
24432    repeat: { string: string },
24433    __type__: { object: object, array: array }
24434  },
24435  locale: { string: string },
24436  locales: {
24437    __any__: { any: any },
24438    __type__: { object: object }
24439  },
24440  max: { date: date, number: number, string: string, moment: moment },
24441  maxHeight: { number: number, string: string },
24442  maxMinorChars: { number: number },
24443  min: { date: date, number: number, string: string, moment: moment },
24444  minHeight: { number: number, string: string },
24445  moveable: { 'boolean': bool },
24446  multiselect: { 'boolean': bool },
24447  orientation: { string: string },
24448  showCurrentTime: { 'boolean': bool },
24449  showMajorLabels: { 'boolean': bool },
24450  showMinorLabels: { 'boolean': bool },
24451  start: { date: date, number: number, string: string, moment: moment },
24452  timeAxis: {
24453    scale: { string: string, 'undefined': 'undefined' },
24454    step: { number: number, 'undefined': 'undefined' },
24455    __type__: { object: object }
24456  },
24457  width: { string: string, number: number },
24458  zoomable: { 'boolean': bool },
24459  zoomKey: { string: ['ctrlKey', 'altKey', 'metaKey', ''] },
24460  zoomMax: { number: number },
24461  zoomMin: { number: number },
24462  zIndex: { number: number },
24463  __type__: { object: object }
24464};
24465
vendor: 4,605 bytes, lines 24466-24619
24466var configureOptions = {
24467  global: {
24468    //yAxisOrientation: ['left','right'], // TDOO: enable as soon as Grahp2d doesn't crash when changing this on the fly
24469    sort: true,
24470    sampling: true,
24471    stack: false,
24472    shaded: {
24473      enabled: false,
24474      orientation: ['zero', 'top', 'bottom', 'group'] // zero, top, bottom
24475    },
24476    style: ['line', 'bar', 'points'], // line, bar
24477    barChart: {
24478      width: [50, 5, 100, 5],
24479      minWidth: [50, 5, 100, 5],
24480      sideBySide: false,
24481      align: ['left', 'center', 'right'] // left, center, right
24482    },
24483    interpolation: {
24484      enabled: true,
24485      parametrization: ['centripetal', 'chordal', 'uniform'] // uniform (alpha = 0.0), chordal (alpha = 1.0), centripetal (alpha = 0.5)
24486    },
24487    drawPoints: {
24488      enabled: true,
24489      size: [6, 2, 30, 1],
24490      style: ['square', 'circle'] // square, circle
24491    },
24492    dataAxis: {
24493      showMinorLabels: true,
24494      showMajorLabels: true,
24495      icons: false,
24496      width: [40, 0, 200, 1],
24497      visible: true,
24498      alignZeros: true,
24499      left: {
24500        //range: {min:'undefined': 'undefined'ined,max:'undefined': 'undefined'ined},
24501        //format: function (value) {return value;},
24502        title: { text: '', style: '' }
24503      },
24504      right: {
24505        //range: {min:'undefined': 'undefined'ined,max:'undefined': 'undefined'ined},
24506        //format: function (value) {return value;},
24507        title: { text: '', style: '' }
24508      }
24509    },
24510    legend: {
24511      enabled: false,
24512      icons: true,
24513      left: {
24514        visible: true,
24515        position: ['top-right', 'bottom-right', 'top-left', 'bottom-left'] // top/bottom - left,right
24516      },
24517      right: {
24518        visible: true,
24519        position: ['top-right', 'bottom-right', 'top-left', 'bottom-left'] // top/bottom - left,right
24520      }
24521    },
24522
24523    autoResize: true,
24524    clickToUse: false,
24525    end: '',
24526    format: {
24527      minorLabels: {
24528        millisecond: 'SSS',
24529        second: 's',
24530        minute: 'HH:mm',
24531        hour: 'HH:mm',
24532        weekday: 'ddd D',
24533        day: 'D',
24534        month: 'MMM',
24535        year: 'YYYY'
24536      },
24537      majorLabels: {
24538        millisecond: 'HH:mm:ss',
24539        second: 'D MMMM HH:mm',
24540        minute: 'ddd D MMMM',
24541        hour: 'ddd D MMMM',
24542        weekday: 'MMMM YYYY',
24543        day: 'MMMM YYYY',
24544        month: 'YYYY',
24545        year: ''
24546      }
24547    },
24548
24549    height: '',
24550    locale: '',
24551    max: '',
24552    maxHeight: '',
24553    maxMinorChars: [7, 0, 20, 1],
24554    min: '',
24555    minHeight: '',
24556    moveable: true,
24557    orientation: ['both', 'bottom', 'top'],
24558    showCurrentTime: false,
24559    showMajorLabels: true,
24560    showMinorLabels: true,
24561    start: '',
24562    width: '100%',
24563    zoomable: true,
24564    zoomKey: ['ctrlKey', 'altKey', 'metaKey', ''],
24565    zoomMax: [315360000000000, 10, 315360000000000, 1],
24566    zoomMin: [10, 10, 315360000000000, 1],
24567    zIndex: 0
24568  }
24569};
24570
24571exports.allOptions = allOptions;
24572exports.configureOptions = configureOptions;
24573
24574/***/ }),
24575/* 114 */
24576/***/ (function(module, exports, __webpack_require__) {
24577
24578"use strict";
24579
24580
24581var _create = __webpack_require__(29);
24582
24583var _create2 = _interopRequireDefault(_create);
24584
24585function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
24586
24587/**
24588 * Parse a text source containing data in DOT language into a JSON object.
24589 * The object contains two lists: one with nodes and one with edges.
24590 *
24591 * DOT language reference: http://www.graphviz.org/doc/info/lang.html
24592 *
24593 * DOT language attributes: http://graphviz.org/content/attrs
24594 *
24595 * @param {string} data     Text containing a graph in DOT-notation
24596 * @return {Object} graph   An object containing two parameters:
24597 *                          {Object[]} nodes
24598 *                          {Object[]} edges
24599 *
24600 * -------------------------------------------
24601 * TODO
24602 * ====
24603 *
24604 * For label handling, this is an incomplete implementation. From docs (quote #3015):
24605 * 
24606 * > the escape sequences "\n", "\l" and "\r" divide the label into lines, centered, 
24607 * > left-justified, and right-justified, respectively.
24608 *
24609 * Source: http://www.graphviz.org/content/attrs#kescString
24610 *
24611 * > As another aid for readability, dot allows double-quoted strings to span multiple physical
24612 * > lines using the standard C convention of a backslash immediately preceding a newline
24613 * > character
24614 * > In addition, double-quoted strings can be concatenated using a '+' operator.
24615 * > As HTML strings can contain newline characters, which are used solely for formatting,
24616 * > the language does not allow escaped newlines or concatenation operators to be used
24617 * > within them.
24618 *
24619 * - Currently, only '\\n' is handled
24620 * - Note that text explicitly says 'labels'; the dot parser currently handles escape
24621 *   sequences in **all** strings.
24622 */
24623function parseDOT(data) {
24624  dot = data;
24625  return parseGraph();
24626}
24627
24628// mapping of attributes from DOT (the keys) to vis.js (the values)
24629var NODE_ATTR_MAPPING = {
24630  'fontsize': 'font.size',
24631  'fontcolor': 'font.color',
24632  'labelfontcolor': 'font.color',
24633  'fontname': 'font.face',
24634  'color': ['color.border', 'color.background'],
24635  'fillcolor': 'color.background',
24636  'tooltip': 'title',
24637  'labeltooltip': 'title'
24638};
24639var EDGE_ATTR_MAPPING = (0, _create2['default'])(NODE_ATTR_MAPPING);
24640EDGE_ATTR_MAPPING.color = 'color.color';
24641EDGE_ATTR_MAPPING.style = 'dashes';
24642
24643// token types enumeration
24644var TOKENTYPE = {
24645  NULL: 0,
24646  DELIMITER: 1,
24647  IDENTIFIER: 2,
24648  UNKNOWN: 3
24649};
24650
24651// map with all delimiters
24652var DELIMITERS = {
24653  '{': true,
24654  '}': true,
24655  '[': true,
24656  ']': true,
24657  ';': true,
24658  '=': true,
24659  ',': true,
24660
24661  '->': true,
24662  '--': true
24663};
24664
24665var dot = ''; // current dot file
24666var index = 0; // current index in dot file
24667var c = ''; // current token character in expr
24668var token = ''; // current token
24669var tokenType = TOKENTYPE.NULL; // type of the token
24670
24671/**
24672 * Get the first character from the dot file.
24673 * The character is stored into the char c. If the end of the dot file is
24674 * reached, the function puts an empty string in c.
24675 */
24676function first() {
24677  index = 0;
24678  c = dot.charAt(0);
24679}
24680
24681/**
24682 * Get the next character from the dot file.
24683 * The character is stored into the char c. If the end of the dot file is
24684 * reached, the function puts an empty string in c.
24685 */
24686function next() {
24687  index++;
24688  c = dot.charAt(index);
24689}
24690
24691/**
24692 * Preview the next character from the dot file.
24693 * @return {string} cNext
24694 */
24695function nextPreview() {
24696  return dot.charAt(index + 1);
24697}
24698
24699var regexAlphaNumeric = /[a-zA-Z_0-9.:#]/;
24700/**
24701 * Test whether given character is alphabetic or numeric
24702 * @param {string} c
24703 * @return {Boolean} isAlphaNumeric
24704 */
24705function isAlphaNumeric(c) {
24706  return regexAlphaNumeric.test(c);
24707}
24708
24709/**
24710 * Merge all options of object b into object b
24711 * @param {Object} a
24712 * @param {Object} b
24713 * @return {Object} a
24714 */
24715function merge(a, b) {
24716  if (!a) {
24717    a = {};
24718  }
24719
24720  if (b) {
24721    for (var name in b) {
24722      if (b.hasOwnProperty(name)) {
24723        a[name] = b[name];
24724      }
24725    }
24726  }
24727  return a;
24728}
24729
24730/**
24731 * Set a value in an object, where the provided parameter name can be a
24732 * path with nested parameters. For example:
24733 *
24734 *     var obj = {a: 2};
24735 *     setValue(obj, 'b.c', 3);     // obj = {a: 2, b: {c: 3}}
24736 *
24737 * @param {Object} obj
24738 * @param {string} path  A parameter name or dot-separated parameter path,
24739 *                      like "color.highlight.border".
24740 * @param {*} value
24741 */
24742function setValue(obj, path, value) {
24743  var keys = path.split('.');
24744  var o = obj;
24745  while (keys.length) {
24746    var key = keys.shift();
24747    if (keys.length) {
24748      // this isn't the end point
24749      if (!o[key]) {
24750        o[key] = {};
24751      }
24752      o = o[key];
24753    } else {
24754      // this is the end point
24755      o[key] = value;
24756    }
24757  }
24758}
24759
24760/**
24761 * Add a node to a graph object. If there is already a node with
24762 * the same id, their attributes will be merged.
24763 * @param {Object} graph
24764 * @param {Object} node
24765 */
24766function addNode(graph, node) {
24767  var i, len;
24768  var current = null;
24769
24770  // find root graph (in case of subgraph)
24771  var graphs = [graph]; // list with all graphs from current graph to root graph
24772  var root = graph;
24773  while (root.parent) {
24774    graphs.push(root.parent);
24775    root = root.parent;
24776  }
24777
24778  // find existing node (at root level) by its id
24779  if (root.nodes) {
24780    for (i = 0, len = root.nodes.length; i < len; i++) {
24781      if (node.id === root.nodes[i].id) {
24782        current = root.nodes[i];
24783        break;
24784      }
24785    }
24786  }
24787
24788  if (!current) {
24789    // this is a new node
24790    current = {
24791      id: node.id
24792    };
24793    if (graph.node) {
24794      // clone default attributes
24795      current.attr = merge(current.attr, graph.node);
24796    }
24797  }
24798
24799  // add node to this (sub)graph and all its parent graphs
24800  for (i = graphs.length - 1; i >= 0; i--) {
24801    var g = graphs[i];
24802
24803    if (!g.nodes) {
24804      g.nodes = [];
24805    }
24806    if (g.nodes.indexOf(current) === -1) {
24807      g.nodes.push(current);
24808    }
24809  }
24810
24811  // merge attributes
24812  if (node.attr) {
24813    current.attr = merge(current.attr, node.attr);
24814  }
24815}
24816
24817/**
24818 * Add an edge to a graph object
24819 * @param {Object} graph
24820 * @param {Object} edge
24821 */
24822function addEdge(graph, edge) {
24823  if (!graph.edges) {
24824    graph.edges = [];
24825  }
24826  graph.edges.push(edge);
24827  if (graph.edge) {
24828    var attr = merge({}, graph.edge); // clone default attributes
24829    edge.attr = merge(attr, edge.attr); // merge attributes
24830  }
24831}
24832
24833/**
24834 * Create an edge to a graph object
24835 * @param {Object} graph
24836 * @param {string | number | Object} from
24837 * @param {string | number | Object} to
24838 * @param {string} type
24839 * @param {Object | null} attr
24840 * @return {Object} edge
24841 */
24842function createEdge(graph, from, to, type, attr) {
24843  var edge = {
24844    from: from,
24845    to: to,
24846    type: type
24847  };
24848
24849  if (graph.edge) {
24850    edge.attr = merge({}, graph.edge); // clone default attributes
24851  }
24852  edge.attr = merge(edge.attr || {}, attr); // merge attributes
24853
24854  return edge;
24855}
24856
24857/**
24858 * Get next token in the current dot file.
24859 * The token and token type are available as token and tokenType
24860 */
24861function getToken() {
24862  tokenType = TOKENTYPE.NULL;
24863  token = '';
24864
24865  // skip over whitespaces
24866  while (c === ' ' || c === '\t' || c === '\n' || c === '\r') {
24867    // space, tab, enter
24868    next();
24869  }
24870
24871  do {
24872    var isComment = false;
24873
24874    // skip comment
24875    if (c === '#') {
24876      // find the previous non-space character
24877      var i = index - 1;
24878      while (dot.charAt(i) === ' ' || dot.charAt(i) === '\t') {
24879        i--;
24880      }
24881      if (dot.charAt(i) === '\n' || dot.charAt(i) === '') {
24882        // the # is at the start of a line, this is indeed a line comment
24883        while (c != '' && c != '\n') {
24884          next();
24885        }
24886        isComment = true;
24887      }
24888    }
24889    if (c === '/' && nextPreview() === '/') {
24890      // skip line comment
24891      while (c != '' && c != '\n') {
24892        next();
24893      }
24894      isComment = true;
24895    }
24896    if (c === '/' && nextPreview() === '*') {
24897      // skip block comment
24898      while (c != '') {
24899        if (c === '*' && nextPreview() === '/') {
24900          // end of block comment found. skip these last two characters
24901          next();
24902          next();
24903          break;
24904        } else {
24905          next();
24906        }
24907      }
24908      isComment = true;
24909    }
24910
24911    // skip over whitespaces
24912    while (c === ' ' || c === '\t' || c === '\n' || c === '\r') {
24913      // space, tab, enter
24914      next();
24915    }
24916  } while (isComment);
24917
24918  // check for end of dot file
24919  if (c === '') {
24920    // token is still empty
24921    tokenType = TOKENTYPE.DELIMITER;
24922    return;
24923  }
24924
24925  // check for delimiters consisting of 2 characters
24926  var c2 = c + nextPreview();
24927  if (DELIMITERS[c2]) {
24928    tokenType = TOKENTYPE.DELIMITER;
24929    token = c2;
24930    next();
24931    next();
24932    return;
24933  }
24934
24935  // check for delimiters consisting of 1 character
24936  if (DELIMITERS[c]) {
24937    tokenType = TOKENTYPE.DELIMITER;
24938    token = c;
24939    next();
24940    return;
24941  }
24942
24943  // check for an identifier (number or string)
24944  // TODO: more precise parsing of numbers/strings (and the port separator ':')
24945  if (isAlphaNumeric(c) || c === '-') {
24946    token += c;
24947    next();
24948
24949    while (isAlphaNumeric(c)) {
24950      token += c;
24951      next();
24952    }
24953    if (token === 'false') {
24954      token = false; // convert to boolean
24955    } else if (token === 'true') {
24956      token = true; // convert to boolean
24957    } else if (!isNaN(Number(token))) {
24958      token = Number(token); // convert to number
24959    }
24960    tokenType = TOKENTYPE.IDENTIFIER;
24961    return;
24962  }
24963
24964  // check for a string enclosed by double quotes
24965  if (c === '"') {
24966    next();
24967    while (c != '' && (c != '"' || c === '"' && nextPreview() === '"')) {
24968      if (c === '"') {
24969        // skip the escape character
24970        token += c;
24971        next();
24972      } else if (c === '\\' && nextPreview() === 'n') {
24973        // Honor a newline escape sequence
24974        token += '\n';
24975        next();
24976      } else {
24977        token += c;
24978      }
24979      next();
24980    }
24981    if (c != '"') {
24982      throw newSyntaxError('End of string " expected');
24983    }
24984    next();
24985    tokenType = TOKENTYPE.IDENTIFIER;
24986    return;
24987  }
24988
24989  // something unknown is found, wrong characters, a syntax error
24990  tokenType = TOKENTYPE.UNKNOWN;
24991  while (c != '') {
24992    token += c;
24993    next();
24994  }
24995  throw new SyntaxError('Syntax error in part "' + chop(token, 30) + '"');
24996}
24997
24998/**
24999 * Parse a graph.
25000 * @returns {Object} graph
25001 */
25002function parseGraph() {
25003  var graph = {};
25004
25005  first();
25006  getToken();
25007
25008  // optional strict keyword
25009  if (token === 'strict') {
25010    graph.strict = true;
25011    getToken();
25012  }
25013
25014  // graph or digraph keyword
25015  if (token === 'graph' || token === 'digraph') {
25016    graph.type = token;
25017    getToken();
25018  }
25019
25020  // optional graph id
25021  if (tokenType === TOKENTYPE.IDENTIFIER) {
25022    graph.id = token;
25023    getToken();
25024  }
25025
25026  // open angle bracket
25027  if (token != '{') {
25028    throw newSyntaxError('Angle bracket { expected');
25029  }
25030  getToken();
25031
25032  // statements
25033  parseStatements(graph);
25034
25035  // close angle bracket
25036  if (token != '}') {
25037    throw newSyntaxError('Angle bracket } expected');
25038  }
25039  getToken();
25040
25041  // end of file
25042  if (token !== '') {
25043    throw newSyntaxError('End of file expected');
25044  }
25045  getToken();
25046
25047  // remove temporary default options
25048  delete graph.node;
25049  delete graph.edge;
25050  delete graph.graph;
25051
25052  return graph;
25053}
25054
25055/**
25056 * Parse a list with statements.
25057 * @param {Object} graph
25058 */
25059function parseStatements(graph) {
25060  while (token !== '' && token != '}') {
25061    parseStatement(graph);
25062    if (token === ';') {
25063      getToken();
25064    }
25065  }
25066}
25067
25068/**
25069 * Parse a single statement. Can be a an attribute statement, node
25070 * statement, a series of node statements and edge statements, or a
25071 * parameter.
25072 * @param {Object} graph
25073 */
25074function parseStatement(graph) {
25075  // parse subgraph
25076  var subgraph = parseSubgraph(graph);
25077  if (subgraph) {
25078    // edge statements
25079    parseEdge(graph, subgraph);
25080
25081    return;
25082  }
25083
25084  // parse an attribute statement
25085  var attr = parseAttributeStatement(graph);
25086  if (attr) {
25087    return;
25088  }
25089
25090  // parse node
25091  if (tokenType != TOKENTYPE.IDENTIFIER) {
25092    throw newSyntaxError('Identifier expected');
25093  }
25094  var id = token; // id can be a string or a number
25095  getToken();
25096
25097  if (token === '=') {
25098    // id statement
25099    getToken();
25100    if (tokenType != TOKENTYPE.IDENTIFIER) {
25101      throw newSyntaxError('Identifier expected');
25102    }
25103    graph[id] = token;
25104    getToken();
25105    // TODO: implement comma separated list with "a_list: ID=ID [','] [a_list] "
25106  } else {
25107    parseNodeStatement(graph, id);
25108  }
25109}
25110
25111/**
25112 * Parse a subgraph
25113 * @param {Object} graph    parent graph object
25114 * @return {Object | null} subgraph
25115 */
25116function parseSubgraph(graph) {
25117  var subgraph = null;
25118
25119  // optional subgraph keyword
25120  if (token === 'subgraph') {
25121    subgraph = {};
25122    subgraph.type = 'subgraph';
25123    getToken();
25124
25125    // optional graph id
25126    if (tokenType === TOKENTYPE.IDENTIFIER) {
25127      subgraph.id = token;
25128      getToken();
25129    }
25130  }
25131
25132  // open angle bracket
25133  if (token === '{') {
25134    getToken();
25135
25136    if (!subgraph) {
25137      subgraph = {};
25138    }
25139    subgraph.parent = graph;
25140    subgraph.node = graph.node;
25141    subgraph.edge = graph.edge;
25142    subgraph.graph = graph.graph;
25143
25144    // statements
25145    parseStatements(subgraph);
25146
25147    // close angle bracket
25148    if (token != '}') {
25149      throw newSyntaxError('Angle bracket } expected');
25150    }
25151    getToken();
25152
25153    // remove temporary default options
25154    delete subgraph.node;
25155    delete subgraph.edge;
25156    delete subgraph.graph;
25157    delete subgraph.parent;
25158
25159    // register at the parent graph
25160    if (!graph.subgraphs) {
25161      graph.subgraphs = [];
25162    }
25163    graph.subgraphs.push(subgraph);
25164  }
25165
25166  return subgraph;
25167}
25168
25169/**
25170 * parse an attribute statement like "node [shape=circle fontSize=16]".
25171 * Available keywords are 'node', 'edge', 'graph'.
25172 * The previous list with default attributes will be replaced
25173 * @param {Object} graph
25174 * @returns {String | null} keyword Returns the name of the parsed attribute
25175 *                                  (node, edge, graph), or null if nothing
25176 *                                  is parsed.
25177 */
25178function parseAttributeStatement(graph) {
25179  // attribute statements
25180  if (token === 'node') {
25181    getToken();
25182
25183    // node attributes
25184    graph.node = parseAttributeList();
25185    return 'node';
25186  } else if (token === 'edge') {
25187    getToken();
25188
25189    // edge attributes
25190    graph.edge = parseAttributeList();
25191    return 'edge';
25192  } else if (token === 'graph') {
25193    getToken();
25194
25195    // graph attributes
25196    graph.graph = parseAttributeList();
25197    return 'graph';
25198  }
25199
25200  return null;
25201}
25202
25203/**
25204 * parse a node statement
25205 * @param {Object} graph
25206 * @param {string | number} id
25207 */
25208function parseNodeStatement(graph, id) {
25209  // node statement
25210  var node = {
25211    id: id
25212  };
25213  var attr = parseAttributeList();
25214  if (attr) {
25215    node.attr = attr;
25216  }
25217  addNode(graph, node);
25218
25219  // edge statements
25220  parseEdge(graph, id);
25221}
25222
25223/**
25224 * Parse an edge or a series of edges
25225 * @param {Object} graph
25226 * @param {string | number} from        Id of the from node
25227 */
25228function parseEdge(graph, from) {
25229  while (token === '->' || token === '--') {
25230    var to;
25231    var type = token;
25232    getToken();
25233
25234    var subgraph = parseSubgraph(graph);
25235    if (subgraph) {
25236      to = subgraph;
25237    } else {
25238      if (tokenType != TOKENTYPE.IDENTIFIER) {
25239        throw newSyntaxError('Identifier or subgraph expected');
25240      }
25241      to = token;
25242      addNode(graph, {
25243        id: to
25244      });
25245      getToken();
25246    }
25247
25248    // parse edge attributes
25249    var attr = parseAttributeList();
25250
25251    // create edge
25252    var edge = createEdge(graph, from, to, type, attr);
25253    addEdge(graph, edge);
25254
25255    from = to;
25256  }
25257}
25258
25259/**
25260 * Parse a set with attributes,
25261 * for example [label="1.000", shape=solid]
25262 * @return {Object | null} attr
25263 */
25264function parseAttributeList() {
25265  var attr = null;
25266
25267  // edge styles of dot and vis
25268  var edgeStyles = {
25269    'dashed': true,
25270    'solid': false,
25271    'dotted': [1, 5]
25272  };
25273
25274  while (token === '[') {
25275    getToken();
25276    attr = {};
25277    while (token !== '' && token != ']') {
25278      if (tokenType != TOKENTYPE.IDENTIFIER) {
25279        throw newSyntaxError('Attribute name expected');
25280      }
25281      var name = token;
25282
25283      getToken();
25284      if (token != '=') {
25285        throw newSyntaxError('Equal sign = expected');
25286      }
25287      getToken();
25288
25289      if (tokenType != TOKENTYPE.IDENTIFIER) {
25290        throw newSyntaxError('Attribute value expected');
25291      }
25292      var value = token;
25293
25294      // convert from dot style to vis
25295      if (name === 'style') {
25296        value = edgeStyles[value];
25297      }
25298
25299      setValue(attr, name, value); // name can be a path
25300
25301      getToken();
25302      if (token == ',') {
25303        getToken();
25304      }
25305    }
25306
25307    if (token != ']') {
25308      throw newSyntaxError('Bracket ] expected');
25309    }
25310    getToken();
25311  }
25312
25313  return attr;
25314}
25315
25316/**
25317 * Create a syntax error with extra information on current token and index.
25318 * @param {string} message
25319 * @returns {SyntaxError} err
25320 */
25321function newSyntaxError(message) {
25322  return new SyntaxError(message + ', got "' + chop(token, 30) + '" (char ' + index + ')');
25323}
25324
25325/**
25326 * Chop off text after a maximum length
25327 * @param {string} text
25328 * @param {number} maxLength
25329 * @returns {String}
25330 */
25331function chop(text, maxLength) {
25332  return text.length <= maxLength ? text : text.substr(0, 27) + '...';
25333}
25334
25335/**
25336 * Execute a function fn for each pair of elements in two arrays
25337 * @param {Array | *} array1
25338 * @param {Array | *} array2
25339 * @param {function} fn
25340 */
25341function forEach2(array1, array2, fn) {
25342  if (Array.isArray(array1)) {
25343    array1.forEach(function (elem1) {
25344      if (Array.isArray(array2)) {
25345        array2.forEach(function (elem2) {
25346          fn(elem1, elem2);
25347        });
25348      } else {
25349        fn(elem1, array2);
25350      }
25351    });
25352  } else {
25353    if (Array.isArray(array2)) {
25354      array2.forEach(function (elem2) {
25355        fn(array1, elem2);
25356      });
25357    } else {
25358      fn(array1, array2);
25359    }
25360  }
25361}
25362
25363/**
25364 * Set a nested property on an object
25365 * When nested objects are missing, they will be created.
25366 * For example setProp({}, 'font.color', 'red') will return {font: {color: 'red'}}
25367 * @param {Object} object
25368 * @param {string} path   A dot separated string like 'font.color'
25369 * @param {*} value       Value for the property
25370 * @return {Object} Returns the original object, allows for chaining.
25371 */
25372function setProp(object, path, value) {
25373  var names = path.split('.');
25374  var prop = names.pop();
25375
25376  // traverse over the nested objects
25377  var obj = object;
25378  for (var i = 0; i < names.length; i++) {
25379    var name = names[i];
25380    if (!(name in obj)) {
25381      obj[name] = {};
25382    }
25383    obj = obj[name];
25384  }
25385
25386  // set the property value
25387  obj[prop] = value;
25388
25389  return object;
25390}
25391
25392/**
25393 * Convert an object with DOT attributes to their vis.js equivalents.
25394 * @param {Object} attr     Object with DOT attributes
25395 * @param {Object} mapping
25396 * @return {Object}         Returns an object with vis.js attributes
25397 */
25398function convertAttr(attr, mapping) {
25399  var converted = {};
25400
25401  for (var prop in attr) {
25402    if (attr.hasOwnProperty(prop)) {
25403      var visProp = mapping[prop];
25404      if (Array.isArray(visProp)) {
25405        visProp.forEach(function (visPropI) {
25406          setProp(converted, visPropI, attr[prop]);
25407        });
25408      } else if (typeof visProp === 'string') {
25409        setProp(converted, visProp, attr[prop]);
25410      } else {
25411        setProp(converted, prop, attr[prop]);
25412      }
25413    }
25414  }
25415
25416  return converted;
25417}
25418
25419/**
25420 * Convert a string containing a graph in DOT language into a map containing
25421 * with nodes and edges in the format of graph.
25422 * @param {string} data         Text containing a graph in DOT-notation
25423 * @return {Object} graphData
25424 */
25425function DOTToGraph(data) {
25426  // parse the DOT file
25427  var dotData = parseDOT(data);
25428  var graphData = {
25429    nodes: [],
25430    edges: [],
25431    options: {}
25432  };
25433
25434  // copy the nodes
25435  if (dotData.nodes) {
25436    dotData.nodes.forEach(function (dotNode) {
25437      var graphNode = {
25438        id: dotNode.id,
25439        label: String(dotNode.label || dotNode.id)
25440      };
25441      merge(graphNode, convertAttr(dotNode.attr, NODE_ATTR_MAPPING));
25442      if (graphNode.image) {
25443        graphNode.shape = 'image';
25444      }
25445      graphData.nodes.push(graphNode);
25446    });
25447  }
25448
25449  // copy the edges
25450  if (dotData.edges) {
25451    /**
25452     * Convert an edge in DOT format to an edge with VisGraph format
25453     * @param {Object} dotEdge
25454     * @returns {Object} graphEdge
25455     */
25456    var convertEdge = function convertEdge(dotEdge) {
25457      var graphEdge = {
25458        from: dotEdge.from,
25459        to: dotEdge.to
25460      };
25461      merge(graphEdge, convertAttr(dotEdge.attr, EDGE_ATTR_MAPPING));
25462      graphEdge.arrows = dotEdge.type === '->' ? 'to' : undefined;
25463
25464      return graphEdge;
25465    };
25466
25467    dotData.edges.forEach(function (dotEdge) {
25468      var from, to;
25469      if (dotEdge.from instanceof Object) {
25470        from = dotEdge.from.nodes;
25471      } else {
25472        from = {
25473          id: dotEdge.from
25474        };
25475      }
25476
25477      // TODO: support for attributes 'dir' and 'arrowhead' (edge arrows)
25478
25479      if (dotEdge.to instanceof Object) {
25480        to = dotEdge.to.nodes;
25481      } else {
25482        to = {
25483          id: dotEdge.to
25484        };
25485      }
25486
25487      if (dotEdge.from instanceof Object && dotEdge.from.edges) {
25488        dotEdge.from.edges.forEach(function (subEdge) {
25489          var graphEdge = convertEdge(subEdge);
25490          graphData.edges.push(graphEdge);
25491        });
25492      }
25493
25494      forEach2(from, to, function (from, to) {
25495        var subEdge = createEdge(graphData, from.id, to.id, dotEdge.type, dotEdge.attr);
25496        var graphEdge = convertEdge(subEdge);
25497        graphData.edges.push(graphEdge);
25498      });
25499
25500      if (dotEdge.to instanceof Object && dotEdge.to.edges) {
25501        dotEdge.to.edges.forEach(function (subEdge) {
25502          var graphEdge = convertEdge(subEdge);
25503          graphData.edges.push(graphEdge);
25504        });
25505      }
25506    });
25507  }
25508
25509  // copy the options
25510  if (dotData.attr) {
25511    graphData.options = dotData.attr;
25512  }
25513
25514  return graphData;
25515}
25516
25517// exports
25518exports.parseDOT = parseDOT;
25519exports.DOTToGraph = DOTToGraph;
25520
25521/***/ }),
25522/* 115 */
25523/***/ (function(module, exports, __webpack_require__) {
25524
25525"use strict";
25526
25527
25528/**
25529 *
25530 * @param {json} gephiJSON
25531 * @param {obj} optionsObj
25532 * @returns {{nodes: Array, edges: Array}}
25533 */
25534function parseGephi(gephiJSON, optionsObj) {
25535  var edges = [];
25536  var nodes = [];
25537  var options = {
25538    edges: {
25539      inheritColor: false
25540    },
25541    nodes: {
25542      fixed: false,
25543      parseColor: false
25544    }
25545  };
25546
25547  if (optionsObj !== undefined) {
25548    if (optionsObj.fixed !== undefined) {
25549      options.nodes.fixed = optionsObj.fixed;
25550    }
25551    if (optionsObj.parseColor !== undefined) {
25552      options.nodes.parseColor = optionsObj.parseColor;
25553    }
25554    if (optionsObj.inheritColor !== undefined) {
25555      options.edges.inheritColor = optionsObj.inheritColor;
25556    }
25557  }
25558
25559  var gEdges = gephiJSON.edges;
25560  var gNodes = gephiJSON.nodes;
25561  for (var i = 0; i < gEdges.length; i++) {
25562    var edge = {};
25563    var gEdge = gEdges[i];
25564    edge['id'] = gEdge.id;
25565    edge['from'] = gEdge.source;
25566    edge['to'] = gEdge.target;
25567    edge['attributes'] = gEdge.attributes;
25568    edge['label'] = gEdge.label;
25569    edge['title'] = gEdge.attributes !== undefined ? gEdge.attributes.title : undefined;
25570    if (gEdge['type'] === 'Directed') {
25571      edge['arrows'] = 'to';
25572    }
25573    //    edge['value'] = gEdge.attributes !== undefined ? gEdge.attributes.Weight : undefined;
25574    //    edge['width'] = edge['value'] !== undefined ? undefined : edgegEdge.size;
25575    if (gEdge.color && options.inheritColor === false) {
25576      edge['color'] = gEdge.color;
25577    }
25578    edges.push(edge);
25579  }
25580
25581  for (var j = 0; j < gNodes.length; j++) {
25582    var node = {};
25583    var gNode = gNodes[j];
25584    node['id'] = gNode.id;
25585    node['attributes'] = gNode.attributes;
25586    node['x'] = gNode.x;
25587    node['y'] = gNode.y;
25588    node['label'] = gNode.label;
25589    node['title'] = gNode.attributes !== undefined ? gNode.attributes.title : gNode.title;
25590    if (options.nodes.parseColor === true) {
25591      node['color'] = gNode.color;
25592    } else {
25593      node['color'] = gNode.color !== undefined ? { background: gNode.color, border: gNode.color, highlight: { background: gNode.color, border: gNode.color }, hover: { background: gNode.color, border: gNode.color } } : undefined;
25594    }
25595    node['size'] = gNode.size;
25596    node['fixed'] = options.nodes.fixed && gNode.x !== undefined && gNode.y !== undefined;
25597    nodes.push(node);
25598  }
25599
25600  return { nodes: nodes, edges: edges };
25601}
25602
25603exports.parseGephi = parseGephi;
25604
25605/***/ }),
25606/* 116 */
25607/***/ (function(module, exports, __webpack_require__) {
25608
25609"use strict";
25610
25611
25612Object.defineProperty(exports, "__esModule", {
25613    value: true
25614});
25615
25616var _classCallCheck2 = __webpack_require__(0);
25617
25618var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
25619
25620var _createClass2 = __webpack_require__(1);
25621
25622var _createClass3 = _interopRequireDefault(_createClass2);
25623
25624var _CachedImage = __webpack_require__(185);
25625
25626var _CachedImage2 = _interopRequireDefault(_CachedImage);
25627
25628function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
25629
25630/**
25631 * This callback is a callback that accepts an Image.
25632 * @callback ImageCallback
25633 * @param {Image} image
25634 */
25635
25636/**
25637 * This class loads images and keeps them stored.
25638 *
25639 * @param {ImageCallback} callback
25640 */
25641var Images = function () {
25642    /**
25643     * @param {ImageCallback} callback
25644     */
25645    function Images(callback) {
25646        (0, _classCallCheck3["default"])(this, Images);
25647
25648        this.images = {};
25649        this.imageBroken = {};
25650        this.callback = callback;
25651    }
25652
25653    /**
25654     * @param {string} url                      The original Url that failed to load, if the broken image is successfully loaded it will be added to the cache using this Url as the key so that subsequent requests for this Url will return the broken image
25655     * @param {string} brokenUrl                Url the broken image to try and load
25656     * @param {Image} imageToLoadBrokenUrlOn   The image object
25657     */
25658
25659
25660    (0, _createClass3["default"])(Images, [{
25661        key: "_tryloadBrokenUrl",
25662        value: function _tryloadBrokenUrl(url, brokenUrl, imageToLoadBrokenUrlOn) {
25663            //If these parameters aren't specified then exit the function because nothing constructive can be done
25664            if (url === undefined || imageToLoadBrokenUrlOn === undefined) return;
25665            if (brokenUrl === undefined) {
25666                console.warn("No broken url image defined");
25667                return;
25668            }
25669
25670            //Clear the old subscription to the error event and put a new in place that only handle errors in loading the brokenImageUrl
25671            imageToLoadBrokenUrlOn.onerror = function () {
25672                console.error("Could not load brokenImage:", brokenUrl);
25673                // cache item will contain empty image, this should be OK for default
25674            };
25675
25676            //Set the source of the image to the brokenUrl, this is actually what kicks off the loading of the broken image
25677            imageToLoadBrokenUrlOn.image.src = brokenUrl;
25678        }
25679
25680        /**
25681         *
25682         * @param {vis.Image} imageToRedrawWith
25683         * @private
25684         */
25685
25686    }, {
25687        key: "_redrawWithImage",
25688        value: function _redrawWithImage(imageToRedrawWith) {
25689            if (this.callback) {
25690                this.callback(imageToRedrawWith);
25691            }
25692        }
25693
25694        /**
25695         * @param {string} url          Url of the image
25696         * @param {string} brokenUrl    Url of an image to use if the url image is not found
25697         * @return {Image} img          The image object
25698         */
25699
25700    }, {
25701        key: "load",
25702        value: function load(url, brokenUrl) {
25703            var _this = this;
25704
25705            //Try and get the image from the cache, if successful then return the cached image   
25706            var cachedImage = this.images[url];
25707            if (cachedImage) return cachedImage;
25708
25709            //Create a new image
25710            var img = new _CachedImage2["default"]();
25711
25712            // Need to add to cache here, otherwise final return will spawn different copies of the same image,
25713            // Also, there will be multiple loads of the same image.
25714            this.images[url] = img;
25715
25716            //Subscribe to the event that is raised if the image loads successfully 
25717            img.image.onload = function () {
25718                // Properly init the cached item and then request a redraw
25719                _this._fixImageCoordinates(img.image);
25720                img.init();
25721                _this._redrawWithImage(img);
25722            };
25723
25724            //Subscribe to the event that is raised if the image fails to load
25725            img.image.onerror = function () {
25726                console.error("Could not load image:", url);
25727                //Try and load the image specified by the brokenUrl using
25728                _this._tryloadBrokenUrl(url, brokenUrl, img);
25729            };
25730
25731            //Set the source of the image to the url, this is what actually kicks off the loading of the image
25732            img.image.src = url;
25733
25734            //Return the new image
25735            return img;
25736        }
25737
25738        /**
25739         * IE11 fix -- thanks dponch!
25740         *
25741         * Local helper function
25742         * @param {vis.Image} imageToCache
25743         * @private
25744         */
25745
25746    }, {
25747        key: "_fixImageCoordinates",
25748        value: function _fixImageCoordinates(imageToCache) {
25749            if (imageToCache.width === 0) {
25750                document.body.appendChild(imageToCache);
25751                imageToCache.width = imageToCache.offsetWidth;
25752                imageToCache.height = imageToCache.offsetHeight;
25753                document.body.removeChild(imageToCache);
25754            }
25755        }
25756    }]);
25757    return Images;
25758}();
25759
25760exports["default"] = Images;
25761
25762/***/ }),
25763/* 117 */
25764/***/ (function(module, exports, __webpack_require__) {
25765
25766"use strict";
25767
25768
25769Object.defineProperty(exports, "__esModule", {
25770  value: true
25771});
25772
25773var _slicedToArray2 = __webpack_require__(30);
25774
25775var _slicedToArray3 = _interopRequireDefault(_slicedToArray2);
25776
25777var _typeof2 = __webpack_require__(6);
25778
25779var _typeof3 = _interopRequireDefault(_typeof2);
25780
25781var _classCallCheck2 = __webpack_require__(0);
25782
25783var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
25784
25785var _createClass2 = __webpack_require__(1);
25786
25787var _createClass3 = _interopRequireDefault(_createClass2);
25788
25789function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
25790
25791var util = __webpack_require__(2);
25792var ComponentUtil = __webpack_require__(48)['default'];
25793var LabelSplitter = __webpack_require__(191)['default'];
25794
25795/**
25796 * @typedef {'bold'|'ital'|'boldital'|'mono'|'normal'} MultiFontStyle
25797 *
25798 * The allowed specifiers of multi-fonts.
25799 */
25800
25801/**
25802 * @typedef {{color:string, size:number, face:string, mod:string, vadjust:number}} MultiFontOptions
25803 *
25804 * The full set of options of a given multi-font.
25805 */
25806
25807/**
25808 * @typedef {Array.<object>} Pile
25809 *
25810 * Sequence of option objects, the order is significant.
25811 * The sequence is used to determine the value of a given option.
25812 *
25813 * Usage principles:
25814 *
25815 *  - All search is done in the sequence of the pile.
25816 *  - As soon as a value is found, the searching stops.
25817 *  - prototypes are totally ignored. The idea is to add option objects used as prototypes
25818 *    to the pile, in the correct order.
25819 */
25820
25821/**
25822 * List of special styles for multi-fonts
25823 * @private
25824 */
25825var multiFontStyle = ['bold', 'ital', 'boldital', 'mono'];
25826
25827/**
25828 * A Label to be used for Nodes or Edges.
25829 */
25830
25831var Label = function () {
25832
25833  /**
25834   * @param {Object} body
25835   * @param {Object} options
25836   * @param {boolean} [edgelabel=false]
25837   */
25838  function Label(body, options) {
25839    var edgelabel = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
25840    (0, _classCallCheck3['default'])(this, Label);
25841
25842    this.body = body;
25843    this.pointToSelf = false;
25844    this.baseSize = undefined;
25845    this.fontOptions = {}; // instance variable containing the *instance-local* font options
25846    this.setOptions(options);
25847    this.size = { top: 0, left: 0, width: 0, height: 0, yLine: 0 };
25848    this.isEdgeLabel = edgelabel;
25849  }
25850
25851  /**
25852   * @param {Object} options the options of the parent Node-instance
25853   */
25854
25855
25856  (0, _createClass3['default'])(Label, [{
25857    key: 'setOptions',
25858    value: function setOptions(options) {
25859      this.elementOptions = options; // Reference to the options of the parent Node-instance 
25860
25861      this.initFontOptions(options.font);
25862
25863      if (ComponentUtil.isValidLabel(options.label)) {
25864        this.labelDirty = true;
25865      } else {
25866        // Bad label! Change the option value to prevent bad stuff happening
25867        options.label = '';
25868      }
25869
25870      if (options.font !== undefined && options.font !== null) {
25871        // font options can be deleted at various levels
25872        if (typeof options.font === 'string') {
25873          this.baseSize = this.fontOptions.size;
25874        } else if ((0, _typeof3['default'])(options.font) === 'object') {
25875          var size = options.font.size;
25876
25877          if (size !== undefined) {
25878            this.baseSize = size;
25879          }
25880        }
25881      }
25882    }
25883
25884    /**
25885     * Init the font Options structure.
25886     *
25887     * Member fontOptions serves as an accumulator for the current font options.
25888     * As such, it needs to be completely separated from the node options.
25889     *
25890     * @param {Object} newFontOptions the new font options to process
25891     * @private
25892     */
25893
25894  }, {
25895    key: 'initFontOptions',
25896    value: function initFontOptions(newFontOptions) {
25897      var _this = this;
25898
25899      // Prepare the multi-font option objects.
25900      // These will be filled in propagateFonts(), if required
25901      util.forEach(multiFontStyle, function (style) {
25902        _this.fontOptions[style] = {};
25903      });
25904
25905      // Handle shorthand option, if present
25906      if (Label.parseFontString(this.fontOptions, newFontOptions)) {
25907        this.fontOptions.vadjust = 0;
25908        return;
25909      }
25910
25911      // Copy over the non-multifont options, if specified
25912      util.forEach(newFontOptions, function (prop, n) {
25913        if (prop !== undefined && prop !== null && (typeof prop === 'undefined' ? 'undefined' : (0, _typeof3['default'])(prop)) !== 'object') {
25914          _this.fontOptions[n] = prop;
25915        }
25916      });
25917    }
25918
25919    /**
25920     * If in-variable is a string, parse it as a font specifier.
25921     *
25922     * Note that following is not done here and have to be done after the call:
25923     * - No number conversion (size)
25924     * - Not all font options are set (vadjust, mod)
25925     *
25926     * @param {Object} outOptions  out-parameter, object in which to store the parse results (if any)
25927     * @param {Object} inOptions  font options to parse
25928     * @return {boolean} true if font parsed as string, false otherwise
25929     * @static
25930     */
25931
25932  }, {
25933    key: 'constrain',
25934
25935
25936    /**
25937     * Set the width and height constraints based on 'nearest' value
25938     *
25939     * @param {Array} pile array of option objects to consider
25940     * @returns {object} the actual constraint values to use
25941     * @private
25942     */
25943    value: function constrain(pile) {
25944      // NOTE: constrainWidth and  constrainHeight never set!
25945      // NOTE: for edge labels, only 'maxWdt' set
25946      // Node labels can set all the fields
25947      var fontOptions = {
25948        constrainWidth: false,
25949        maxWdt: -1,
25950        minWdt: -1,
25951        constrainHeight: false,
25952        minHgt: -1,
25953        valign: 'middle'
25954      };
25955
25956      var widthConstraint = util.topMost(pile, 'widthConstraint');
25957      if (typeof widthConstraint === 'number') {
25958        fontOptions.maxWdt = Number(widthConstraint);
25959        fontOptions.minWdt = Number(widthConstraint);
25960      } else if ((typeof widthConstraint === 'undefined' ? 'undefined' : (0, _typeof3['default'])(widthConstraint)) === 'object') {
25961        var widthConstraintMaximum = util.topMost(pile, ['widthConstraint', 'maximum']);
25962        if (typeof widthConstraintMaximum === 'number') {
25963          fontOptions.maxWdt = Number(widthConstraintMaximum);
25964        }
25965        var widthConstraintMinimum = util.topMost(pile, ['widthConstraint', 'minimum']);
25966        if (typeof widthConstraintMinimum === 'number') {
25967          fontOptions.minWdt = Number(widthConstraintMinimum);
25968        }
25969      }
25970
25971      var heightConstraint = util.topMost(pile, 'heightConstraint');
25972      if (typeof heightConstraint === 'number') {
25973        fontOptions.minHgt = Number(heightConstraint);
25974      } else if ((typeof heightConstraint === 'undefined' ? 'undefined' : (0, _typeof3['default'])(heightConstraint)) === 'object') {
25975        var heightConstraintMinimum = util.topMost(pile, ['heightConstraint', 'minimum']);
25976        if (typeof heightConstraintMinimum === 'number') {
25977          fontOptions.minHgt = Number(heightConstraintMinimum);
25978        }
25979        var heightConstraintValign = util.topMost(pile, ['heightConstraint', 'valign']);
25980        if (typeof heightConstraintValign === 'string') {
25981          if (heightConstraintValign === 'top' || heightConstraintValign === 'bottom') {
25982            fontOptions.valign = heightConstraintValign;
25983          }
25984        }
25985      }
25986
25987      return fontOptions;
25988    }
25989
25990    /**
25991     * Set options and update internal state
25992     *
25993     * @param {Object} options  options to set
25994     * @param {Array}  pile     array of option objects to consider for option 'chosen'
25995     */
25996
25997  }, {
25998    key: 'update',
25999    value: function update(options, pile) {
26000      this.setOptions(options, true);
26001      this.propagateFonts(pile);
26002      util.deepExtend(this.fontOptions, this.constrain(pile));
26003      this.fontOptions.chooser = ComponentUtil.choosify('label', pile);
26004    }
26005
26006    /**
26007     * When margins are set in an element, adjust sizes is called to remove them
26008     * from the width/height constraints. This must be done prior to label sizing.
26009     *
26010     * @param {{top: number, right: number, bottom: number, left: number}} margins
26011     */
26012
26013  }, {
26014    key: 'adjustSizes',
26015    value: function adjustSizes(margins) {
26016      var widthBias = margins ? margins.right + margins.left : 0;
26017      if (this.fontOptions.constrainWidth) {
26018        this.fontOptions.maxWdt -= widthBias;
26019        this.fontOptions.minWdt -= widthBias;
26020      }
26021      var heightBias = margins ? margins.top + margins.bottom : 0;
26022      if (this.fontOptions.constrainHeight) {
26023        this.fontOptions.minHgt -= heightBias;
26024      }
26025    }
26026
26027    /////////////////////////////////////////////////////////
26028    // Methods for handling options piles
26029    // Eventually, these will be moved to a separate class
26030    /////////////////////////////////////////////////////////
26031
26032    /**
26033     * Add the font members of the passed list of option objects to the pile.
26034     *
26035     * @param {Pile} dstPile  pile of option objects add to 
26036     * @param {Pile} srcPile  pile of option objects to take font options from
26037     * @private
26038     */
26039
26040  }, {
26041    key: 'addFontOptionsToPile',
26042    value: function addFontOptionsToPile(dstPile, srcPile) {
26043      for (var i = 0; i < srcPile.length; ++i) {
26044        this.addFontToPile(dstPile, srcPile[i]);
26045      }
26046    }
26047
26048    /**
26049     * Add given font option object to the list of objects (the 'pile') to consider for determining
26050     * multi-font option values.
26051     *
26052     * @param {Pile} pile  pile of option objects to use
26053     * @param {object} options  instance to add to pile
26054     * @private
26055     */
26056
26057  }, {
26058    key: 'addFontToPile',
26059    value: function addFontToPile(pile, options) {
26060      if (options === undefined) return;
26061      if (options.font === undefined || options.font === null) return;
26062
26063      var item = options.font;
26064      pile.push(item);
26065    }
26066
26067    /**
26068     * Collect all own-property values from the font pile that aren't multi-font option objectss.
26069     *
26070     * @param {Pile} pile  pile of option objects to use
26071     * @returns {object} object with all current own basic font properties
26072     * @private
26073     */
26074
26075  }, {
26076    key: 'getBasicOptions',
26077    value: function getBasicOptions(pile) {
26078      var ret = {};
26079
26080      // Scans the whole pile to get all options present
26081      for (var n = 0; n < pile.length; ++n) {
26082        var fontOptions = pile[n];
26083
26084        // Convert shorthand if necessary
26085        var tmpShorthand = {};
26086        if (Label.parseFontString(tmpShorthand, fontOptions)) {
26087          fontOptions = tmpShorthand;
26088        }
26089
26090        util.forEach(fontOptions, function (opt, name) {
26091          if (opt === undefined) return; // multi-font option need not be present 
26092          if (ret.hasOwnProperty(name)) return; // Keep first value we encounter
26093
26094          if (multiFontStyle.indexOf(name) !== -1) {
26095            // Skip multi-font properties but we do need the structure
26096            ret[name] = {};
26097          } else {
26098            ret[name] = opt;
26099          }
26100        });
26101      }
26102
26103      return ret;
26104    }
26105
26106    /**
26107     * Return the value for given option for the given multi-font.
26108     *
26109     * All available option objects are trawled in the set order to construct the option values.
26110     *
26111     * ---------------------------------------------------------------------
26112     * ## Traversal of pile for multi-fonts
26113     *
26114     * The determination of multi-font option values is a special case, because any values n
26114ot
26115     * present in the multi-font options should by definition be taken from the main font options,
26116     * i.e. from the current 'parent' object of the multi-font option.
26117     *
26118     * ### Search order for multi-fonts
26119     *
26120     * 'bold' used as example:
26121     *
26122     *   - search in option group 'bold' in local properties
26123     *   - search in main font option group in local properties
26124     *
26125     * ---------------------------------------------------------------------
26126     *
26127     * @param {Pile} pile  pile of option objects to use
26128     * @param {MultiFontStyle} multiName sub path for the multi-font
26129     * @param {string} option  the option to search for, for the given multi-font
26130     * @returns {string|number} the value for the given option
26131     * @private
26132     */
26133
26134  }, {
26135    key: 'getFontOption',
26136    value: function getFontOption(pile, multiName, option) {
26137      var multiFont = void 0;
26138
26139      // Search multi font in local properties
26140      for (var n = 0; n < pile.length; ++n) {
26141        var fontOptions = pile[n];
26142
26143        if (fontOptions.hasOwnProperty(multiName)) {
26144          multiFont = fontOptions[multiName];
26145          if (multiFont === undefined || multiFont === null) continue;
26146
26147          // Convert shorthand if necessary
26148          // TODO: inefficient to do this conversion every time; find a better way.
26149          var tmpShorthand = {};
26150          if (Label.parseFontString(tmpShorthand, multiFont)) {
26151            multiFont = tmpShorthand;
26152          }
26153
26154          if (multiFont.hasOwnProperty(option)) {
26155            return multiFont[option];
26156          }
26157        }
26158      }
26159
26160      // Option is not mentioned in the multi font options; take it from the parent font options.
26161      // These have already been converted with getBasicOptions(), so use the converted values.
26162      if (this.fontOptions.hasOwnProperty(option)) {
26163        return this.fontOptions[option];
26164      }
26165
26166      // A value **must** be found; you should never get here.
26167      throw new Error("Did not find value for multi-font for property: '" + option + "'");
26168    }
26169
26170    /**
26171     * Return all options values for the given multi-font.
26172     *
26173     * All available option objects are trawled in the set order to construct the option values.
26174     *
26175     * @param {Pile} pile  pile of option objects to use
26176     * @param {MultiFontStyle} multiName sub path for the mod-font
26177     * @returns {MultiFontOptions}
26178     * @private
26179     */
26180
26181  }, {
26182    key: 'getFontOptions',
26183    value: function getFontOptions(pile, multiName) {
26184      var result = {};
26185      var optionNames = ['color', 'size', 'face', 'mod', 'vadjust']; // List of allowed options per multi-font
26186
26187      for (var i = 0; i < optionNames.length; ++i) {
26188        var mod = optionNames[i];
26189        result[mod] = this.getFontOption(pile, multiName, mod);
26190      }
26191
26192      return result;
26193    }
26194
26195    /////////////////////////////////////////////////////////
26196    // End methods for handling options piles
26197    /////////////////////////////////////////////////////////
26198
26199
26200    /**
26201     * Collapse the font options for the multi-font to single objects, from
26202     * the chain of option objects passed (the 'pile').
26203     *
26204     * @param {Pile} pile  sequence of option objects to consider.
26205     *                     First item in list assumed to be the newly set options.
26206     */
26207
26208  }, {
26209    key: 'propagateFonts',
26210    value: function propagateFonts(pile) {
26211      var _this2 = this;
26212
26213      var fontPile = []; // sequence of font objects to consider, order important
26214
26215      // Note that this.elementOptions is not used here.
26216      this.addFontOptionsToPile(fontPile, pile);
26217      this.fontOptions = this.getBasicOptions(fontPile);
26218
26219      // We set multifont values even if multi === false, for consistency (things break otherwise)
26220
26221      var _loop = function _loop(i) {
26222        var mod = multiFontStyle[i];
26223        var modOptions = _this2.fontOptions[mod];
26224        var tmpMultiFontOptions = _this2.getFontOptions(fontPile, mod);
26225
26226        // Copy over found values
26227        util.forEach(tmpMultiFontOptions, function (option, n) {
26228          modOptions[n] = option;
26229        });
26230
26231        modOptions.size = Number(modOptions.size);
26232        modOptions.vadjust = Number(modOptions.vadjust);
26233      };
26234
26235      for (var i = 0; i < multiFontStyle.length; ++i) {
26236        _loop(i);
26237      }
26238    }
26239
26240    /**
26241     * Main function. This is called from anything that wants to draw a label.
26242     * @param {CanvasRenderingContext2D} ctx
26243     * @param {number} x
26244     * @param {number} y
26245     * @param {boolean} selected
26246     * @param {boolean} hover
26247     * @param {string} [baseline='middle']
26248     */
26249
26250  }, {
26251    key: 'draw',
26252    value: function draw(ctx, x, y, selected, hover) {
26253      var baseline = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : 'middle';
26254
26255      // if no label, return
26256      if (this.elementOptions.label === undefined) return;
26257
26258      // check if we have to render the label
26259      var viewFontSize = this.fontOptions.size * this.body.view.scale;
26260      if (this.elementOptions.label && viewFontSize < this.elementOptions.scaling.label.drawThreshold - 1) return;
26261
26262      // This ensures that there will not be HUGE letters on screen
26263      // by setting an upper limit on the visible text size (regardless of zoomLevel)
26264      if (viewFontSize >= this.elementOptions.scaling.label.maxVisible) {
26265        viewFontSize = Number(this.elementOptions.scaling.label.maxVisible) / this.body.view.scale;
26266      }
26267
26268      // update the size cache if required
26269      this.calculateLabelSize(ctx, selected, hover, x, y, baseline);
26270      this._drawBackground(ctx);
26271      this._drawText(ctx, x, this.size.yLine, baseline, viewFontSize);
26272    }
26273
26274    /**
26275     * Draws the label background
26276     * @param {CanvasRenderingContext2D} ctx
26277     * @private
26278     */
26279
26280  }, {
26281    key: '_drawBackground',
26282    value: function _drawBackground(ctx) {
26283      if (this.fontOptions.background !== undefined && this.fontOptions.background !== "none") {
26284        ctx.fillStyle = this.fontOptions.background;
26285        var size = this.getSize();
26286        ctx.fillRect(size.left, size.top, size.width, size.height);
26287      }
26288    }
26289
26290    /**
26291     *
26292     * @param {CanvasRenderingContext2D} ctx
26293     * @param {number} x
26294     * @param {number} y
26295     * @param {string} [baseline='middle']
26296     * @param {number} viewFontSize 
26297     * @private
26298     */
26299
26300  }, {
26301    key: '_drawText',
26302    value: function _drawText(ctx, x, y) {
26303      var baseline = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 'middle';
26304      var viewFontSize = arguments[4];
26305
26306      var _setAlignment2 = this._setAlignment(ctx, x, y, baseline);
26307
26308      var _setAlignment3 = (0, _slicedToArray3['default'])(_setAlignment2, 2);
26309
26310      x = _setAlignment3[0];
26311      y = _setAlignment3[1];
26312
26313
26314      ctx.textAlign = 'left';
26315      x = x - this.size.width / 2; // Shift label 1/2-distance to the left
26316      if (this.fontOptions.valign && this.size.height > this.size.labelHeight) {
26317        if (this.fontOptions.valign === 'top') {
26318          y -= (this.size.height - this.size.labelHeight) / 2;
26319        }
26320        if (this.fontOptions.valign === 'bottom') {
26321          y += (this.size.height - this.size.labelHeight) / 2;
26322        }
26323      }
26324
26325      // draw the text
26326      for (var i = 0; i < this.lineCount; i++) {
26327        var line = this.lines[i];
26328        if (line && line.blocks) {
26329          var width = 0;
26330          if (this.isEdgeLabel || this.fontOptions.align === 'center') {
26331            width += (this.size.width - line.width) / 2;
26332          } else if (this.fontOptions.align === 'right') {
26333            width += this.size.width - line.width;
26334          }
26335          for (var j = 0; j < line.blocks.length; j++) {
26336            var block = line.blocks[j];
26337            ctx.font = block.font;
26338
26339            var _getColor2 = this._getColor(block.color, viewFontSize, block.strokeColor),
26340                _getColor3 = (0, _slicedToArray3['default'])(_getColor2, 2),
26341                fontColor = _getColor3[0],
26342                strokeColor = _getColor3[1];
26343
26344            if (block.strokeWidth > 0) {
26345              ctx.lineWidth = block.strokeWidth;
26346              ctx.strokeStyle = strokeColor;
26347              ctx.lineJoin = 'round';
26348            }
26349            ctx.fillStyle = fontColor;
26350
26351            if (block.strokeWidth > 0) {
26352              ctx.strokeText(block.text, x + width, y + block.vadjust);
26353            }
26354            ctx.fillText(block.text, x + width, y + block.vadjust);
26355            width += block.width;
26356          }
26357          y += line.height;
26358        }
26359      }
26360    }
26361
26362    /**
26363     *
26364     * @param {CanvasRenderingContext2D} ctx
26365     * @param {number} x
26366     * @param {number} y
26367     * @param {string} baseline
26368     * @returns {Array.<number>}
26369     * @private
26370     */
26371
26372  }, {
26373    key: '_setAlignment',
26374    value: function _setAlignment(ctx, x, y, baseline) {
26375      // check for label alignment (for edges)
26376      // TODO: make alignment for nodes
26377      if (this.isEdgeLabel && this.fontOptions.align !== 'horizontal' && this.pointToSelf === false) {
26378        x = 0;
26379        y = 0;
26380
26381        var lineMargin = 2;
26382        if (this.fontOptions.align === 'top') {
26383          ctx.textBaseline = 'alphabetic';
26384          y -= 2 * lineMargin; // distance from edge, required because we use alphabetic. Alphabetic has less difference between browsers
26385        } else if (this.fontOptions.align === 'bottom') {
26386          ctx.textBaseline = 'hanging';
26387          y += 2 * lineMargin; // distance from edge, required because we use hanging. Hanging has less difference between browsers
26388        } else {
26389          ctx.textBaseline = 'middle';
26390        }
26391      } else {
26392        ctx.textBaseline = baseline;
26393      }
26394      return [x, y];
26395    }
26396
26397    /**
26398     * fade in when relative scale is between threshold and threshold - 1.
26399     * If the relative scale would be smaller than threshold -1 the draw function would have returned before coming here.
26400     *
26401     * @param {string} color  The font color to use
26402     * @param {number} viewFontSize
26403     * @param {string} initialStrokeColor
26404     * @returns {Array.<string>} An array containing the font color and stroke color
26405     * @private
26406     */
26407
26408  }, {
26409    key: '_getColor',
26410    value: function _getColor(color, viewFontSize, initialStrokeColor) {
26411      var fontColor = color || '#000000';
26412      var strokeColor = initialStrokeColor || '#ffffff';
26413      if (viewFontSize <= this.elementOptions.scaling.label.drawThreshold) {
26414        var opacity = Math.max(0, Math.min(1, 1 - (this.elementOptions.scaling.label.drawThreshold - viewFontSize)));
26415        fontColor = util.overrideOpacity(fontColor, opacity);
26416        strokeColor = util.overrideOpacity(strokeColor, opacity);
26417      }
26418      return [fontColor, strokeColor];
26419    }
26420
26421    /**
26422     *
26423     * @param {CanvasRenderingContext2D} ctx
26424     * @param {boolean} selected
26425     * @param {boolean} hover
26426     * @returns {{width: number, height: number}}
26427     */
26428
26429  }, {
26430    key: 'getTextSize',
26431    value: function getTextSize(ctx) {
26432      var selected = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
26433      var hover = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
26434
26435      this._processLabel(ctx, selected, hover);
26436      return {
26437        width: this.size.width,
26438        height: this.size.height,
26439        lineCount: this.lineCount
26440      };
26441    }
26442
26443    /**
26444     * Get the current dimensions of the label
26445     *
26446     * @return {rect}
26447     */
26448
26449  }, {
26450    key: 'getSize',
26451    value: function getSize() {
26452      var lineMargin = 2;
26453      var x = this.size.left; // default values which might be overridden below
26454      var y = this.size.top - 0.5 * lineMargin; // idem
26455
26456      if (this.isEdgeLabel) {
26457        var x2 = -this.size.width * 0.5;
26458
26459        switch (this.fontOptions.align) {
26460          case 'middle':
26461            x = x2;
26462            y = -this.size.height * 0.5;
26463            break;
26464          case 'top':
26465            x = x2;
26466            y = -(this.size.height + lineMargin);
26467            break;
26468          case 'bottom':
26469            x = x2;
26470            y = lineMargin;
26471            break;
26472        }
26473      }
26474
26475      var ret = {
26476        left: x,
26477        top: y,
26478        width: this.size.width,
26479        height: this.size.height
26480      };
26481
26482      return ret;
26483    }
26484
26485    /**
26486     *
26487     * @param {CanvasRenderingContext2D} ctx
26488     * @param {boolean} selected
26489     * @param {boolean} hover
26490     * @param {number} [x=0]
26491     * @param {number} [y=0]
26492     * @param {'middle'|'hanging'} [baseline='middle']
26493     */
26494
26495  }, {
26496    key: 'calculateLabelSize',
26497    value: function calculateLabelSize(ctx, selected, hover) {
26498      var x = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 0;
26499      var y = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : 0;
26500      var baseline = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : 'middle';
26501
26502      this._processLabel(ctx, selected, hover);
26503      this.size.left = x - this.size.width * 0.5;
26504      this.size.top = y - this.size.height * 0.5;
26505      this.size.yLine = y + (1 - this.lineCount) * 0.5 * this.fontOptions.size;
26506      if (baseline === "hanging") {
26507        this.size.top += 0.5 * this.fontOptions.size;
26508        this.size.top += 4; // distance from node, required because we use hanging. Hanging has less difference between browsers
26509        this.size.yLine += 4; // distance from node
26510      }
26511    }
26512
26513    /**
26514     *
26515     * @param {CanvasRenderingContext2D} ctx
26516     * @param {boolean} selected
26517     * @param {boolean} hover
26518     * @param {string} mod
26519     * @returns {{color, size, face, mod, vadjust, strokeWidth: *, strokeColor: (*|string|allOptions.edges.font.strokeColor|{string}|allOptions.nodes.font.strokeColor|Array)}}
26520     */
26521
26522  }, {
26523    key: 'getFormattingValues',
26524    value: function getFormattingValues(ctx, selected, hover, mod) {
26525      var getValue = function getValue(fontOptions, mod, option) {
26526        if (mod === "normal") {
26527          if (option === 'mod') return "";
26528          return fontOptions[option];
26529        }
26530
26531        if (fontOptions[mod][option] !== undefined) {
26532          // Grumbl leaving out test on undefined equals false for "" 
26533          return fontOptions[mod][option];
26534        } else {
26535          // Take from parent font option
26536          return fontOptions[option];
26537        }
26538      };
26539
26540      var values = {
26541        color: getValue(this.fontOptions, mod, 'color'),
26542        size: getValue(this.fontOptions, mod, 'size'),
26543        face: getValue(this.fontOptions, mod, 'face'),
26544        mod: getValue(this.fontOptions, mod, 'mod'),
26545        vadjust: getValue(this.fontOptions, mod, 'vadjust'),
26546        strokeWidth: this.fontOptions.strokeWidth,
26547        strokeColor: this.fontOptions.strokeColor
26548      };
26549      if (selected || hover) {
26550        if (mod === "normal" && this.fontOptions.chooser === true && this.elementOptions.labelHighlightBold) {
26551          values.mod = 'bold';
26552        } else {
26553          if (typeof this.fontOptions.chooser === 'function') {
26554            this.fontOptions.chooser(values, this.elementOptions.id, selected, hover);
26555          }
26556        }
26557      }
26558
26559      var fontString = "";
26560      if (values.mod !== undefined && values.mod !== "") {
26561        // safeguard for undefined - this happened
26562        fontString += values.mod + " ";
26563      }
26564      fontString += values.size + "px " + values.face;
26565
26566      ctx.font = fontString.replace(/"/g, "");
26567      values.font = ctx.font;
26568      values.height = values.size;
26569      return values;
26570    }
26571
26572    /**
26573     *
26574     * @param {boolean} selected
26575     * @param {boolean} hover
26576     * @returns {boolean}
26577     */
26578
26579  }, {
26580    key: 'differentState',
26581    value: function differentState(selected, hover) {
26582      return selected !== this.selectedState || hover !== this.hoverState;
26583    }
26584
26585    /**
26586     * This explodes the passed text into lines and determines the width, height and number of lines.
26587     *
26588     * @param {CanvasRenderingContext2D} ctx
26589     * @param {boolean} selected
26590     * @param {boolean} hover
26591     * @param {string} inText  the text to explode
26592     * @returns {{width, height, lines}|*}
26593     * @private
26594     */
26595
26596  }, {
26597    key: '_processLabelText',
26598    value: function _processLabelText(ctx, selected, hover, inText) {
26599      var splitter = new LabelSplitter(ctx, this, selected, hover);
26600      return splitter.process(inText);
26601    }
26602
26603    /**
26604     * This explodes the label string into lines and sets the width, height and number of lines.
26605     * @param {CanvasRenderingContext2D} ctx
26606     * @param {boolean} selected
26607     * @param {boolean} hover
26608     * @private
26609     */
26610
26611  }, {
26612    key: '_processLabel',
26613    value: function _processLabel(ctx, selected, hover) {
26614
26615      if (this.labelDirty === false && !this.differentState(selected, hover)) return;
26616
26617      var state = this._processLabelText(ctx, selected, hover, this.elementOptions.label);
26618
26619      if (this.fontOptions.minWdt > 0 && state.width < this.fontOptions.minWdt) {
26620        state.width = this.fontOptions.minWdt;
26621      }
26622
26623      this.size.labelHeight = state.height;
26624      if (this.fontOptions.minHgt > 0 && state.height < this.fontOptions.minHgt) {
26625        state.height = this.fontOptions.minHgt;
26626      }
26627
26628      this.lines = state.lines;
26629      this.lineCount = state.lines.length;
26630      this.size.width = state.width;
26631      this.size.height = state.height;
26632      this.selectedState = selected;
26633      this.hoverState = hover;
26634
26635      this.labelDirty = false;
26636    }
26637
26638    /**
26639     * Check if this label is visible
26640     *
26641     * @return {boolean} true if this label will be show, false otherwise
26642     */
26643
26644  }, {
26645    key: 'visible',
26646    value: function visible() {
26647      if (this.size.width === 0 || this.size.height === 0 || this.elementOptions.label === undefined) {
26648        return false; // nothing to display
26649      }
26650
26651      var viewFontSize = this.fontOptions.size * this.body.view.scale;
26652      if (viewFontSize < this.elementOptions.scaling.label.drawThreshold - 1) {
26653        return false; // Too small or too far away to show
26654      }
26655
26656      return true;
26657    }
26658  }], [{
26659    key: 'parseFontString',
26660    value: function parseFontString(outOptions, inOptions) {
26661      if (!inOptions || typeof inOptions !== 'string') return false;
26662
26663      var newOptionsArray = inOptions.split(" ");
26664
26665      outOptions.size = newOptionsArray[0].replace("px", '');
26666      outOptions.face = newOptionsArray[1];
26667      outOptions.color = newOptionsArray[2];
26668
26669      return true;
26670    }
26671  }]);
26672  return Label;
26673}();
26674
26675exports['default'] = Label;
26676
26677/***/ }),
26678/* 118 */
26679/***/ (function(module, exports, __webpack_require__) {
26680
26681"use strict";
26682
26683
26684Object.defineProperty(exports, "__esModule", {
26685  value: true
26686});
26687
26688var _slicedToArray2 = __webpack_require__(30);
26689
26690var _slicedToArray3 = _interopRequireDefault(_slicedToArray2);
26691
26692var _classCallCheck2 = __webpack_require__(0);
26693
26694var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
26695
26696var _createClass2 = __webpack_require__(1);
26697
26698var _createClass3 = _interopRequireDefault(_createClass2);
26699
26700function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
26701
26702var util = __webpack_require__(2);
26703var EndPoints = __webpack_require__(119)["default"];
26704
26705/**
26706 * The Base Class for all edges.
26707 *
26708 */
26709
26710var EdgeBase = function () {
26711  /**
26712   * @param {Object} options
26713   * @param {Object} body
26714   * @param {Label} labelModule
26715   */
26716  function EdgeBase(options, body, labelModule) {
26717    (0, _classCallCheck3["default"])(this, EdgeBase);
26718
26719    this.body = body;
26720    this.labelModule = labelModule;
26721    this.options = {};
26722    this.setOptions(options);
26723    this.colorDirty = true;
26724    this.color = {};
26725    this.selectionWidth = 2;
26726    this.hoverWidth = 1.5;
26727    this.fromPoint = this.from;
26728    this.toPoint = this.to;
26729  }
26730
26731  /**
26732   * Connects a node to itself
26733   */
26734
26735
26736  (0, _createClass3["default"])(EdgeBase, [{
26737    key: "connect",
26738    value: function connect() {
26739      this.from = this.body.nodes[this.options.from];
26740      this.to = this.body.nodes[this.options.to];
26741    }
26742
26743    /**
26744     *
26745     * @returns {boolean} always false
26746     */
26747
26748  }, {
26749    key: "cleanup",
26750    value: function cleanup() {
26751      return false;
26752    }
26753
26754    /**
26755     *
26756     * @param {Object} options
26757     */
26758
26759  }, {
26760    key: "setOptions",
26761    value: function setOptions(options) {
26762      this.options = options;
26763      this.from = this.body.nodes[this.options.from];
26764      this.to = this.body.nodes[this.options.to];
26765      this.id = this.options.id;
26766    }
26767
26768    /**
26769     * Redraw a edge as a line
26770     * Draw this edge in the given canvas
26771     * The 2d context of a HTML canvas can be retrieved by canvas.getContext("2d");
26772     *
26773     * @param {CanvasRenderingContext2D}   ctx
26774     * @param {Array} values
26775     * @param {boolean} selected
26776     * @param {boolean} hover
26777     * @param {Node} viaNode
26778     * @private
26779     */
26780
26781  }, {
26782    key: "drawLine",
26783    value: function drawLine(ctx, values, selected, hover, viaNode) {
26784      // set style
26785      ctx.strokeStyle = this.getColor(ctx, values, selected, hover);
26786      ctx.lineWidth = values.width;
26787
26788      if (values.dashes !== false) {
26789        this._drawDashedLine(ctx, values, viaNode);
26790      } else {
26791        this._drawLine(ctx, values, viaNode);
26792      }
26793    }
26794
26795    /**
26796     *
26797     * @param {CanvasRenderingContext2D}   ctx
26798     * @param {Array} values
26799     * @param {Node} viaNode
26800     * @param {{x: number, y: number}} [fromPoint]
26801     * @param {{x: number, y: number}} [toPoint]
26802     * @private
26803     */
26804
26805  }, {
26806    key: "_drawLine",
26807    value: function _drawLine(ctx, values, viaNode, fromPoint, toPoint) {
26808      if (this.from != this.to) {
26809        // draw line
26810        this._line(ctx, values, viaNode, fromPoint, toPoint);
26811      } else {
26812        var _getCircleData2 = this._getCircleData(ctx),
26813            _getCircleData3 = (0, _slicedToArray3["default"])(_getCircleData2, 3),
26814            x = _getCircleData3[0],
26815            y = _getCircleData3[1],
26816            radius = _getCircleData3[2];
26817
26818        this._circle(ctx, values, x, y, radius);
26819      }
26820    }
26821
26822    /**
26823     *
26824     * @param {CanvasRenderingContext2D} ctx
26825     * @param {Array} values
26826     * @param {Node} viaNode
26827     * @param {{x: number, y: number}} [fromPoint]  TODO: Remove in next major release
26828     * @param {{x: number, y: number}} [toPoint]    TODO: Remove in next major release
26829     * @private
26830     */
26831
26832  }, {
26833    key: "_drawDashedLine",
26834    value: function _drawDashedLine(ctx, values, viaNode, fromPoint, toPoint) {
26835      // eslint-disable-line no-unused-vars
26836      ctx.lineCap = 'round';
26837      var pattern = [5, 5];
26838      if (Array.isArray(values.dashes) === true) {
26839        pattern = values.dashes;
26840      }
26841
26842      // only firefox and chrome support this method, else we use the legacy one.
26843      if (ctx.setLineDash !== undefined) {
26844        ctx.save();
26845
26846        // set dash settings for chrome or firefox
26847        ctx.setLineDash(pattern);
26848        ctx.lineDashOffset = 0;
26849
26850        // draw the line
26851        if (this.from != this.to) {
26852          // draw line
26853          this._line(ctx, values, viaNode);
26854        } else {
26855          var _getCircleData4 = this._getCircleData(ctx),
26856              _getCircleData5 = (0, _slicedToArray3["default"])(_getCircleData4, 3),
26857              x = _getCircleData5[0],
26858              y = _getCircleData5[1],
26859              radius = _getCircleData5[2];
26860
26861          this._circle(ctx, values, x, y, radius);
26862        }
26863
26864        // restore the dash settings.
26865        ctx.setLineDash([0]);
26866        ctx.lineDashOffset = 0;
26867        ctx.restore();
26868      } else {
26869        // unsupporting smooth lines
26870        if (this.from != this.to) {
26871          // draw line
26872          ctx.dashedLine(this.from.x, this.from.y, this.to.x, this.to.y, pattern);
26873        } else {
26874          var _getCircleData6 = this._getCircleData(ctx),
26875              _getCircleData7 = (0, _slicedToArray3["default"])(_getCircleData6, 3),
26876              _x = _getCircleData7[0],
26877              _y = _getCircleData7[1],
26878              _radius = _getCircleData7[2];
26879
26880          this._circle(ctx, values, _x, _y, _radius);
26881        }
26882        // draw shadow if enabled
26883        this.enableShadow(ctx, values);
26884
26885        ctx.stroke();
26886
26887        // disable shadows for other elements.
26888        this.disableShadow(ctx, values);
26889      }
26890    }
26891
26892    /**
26893     *
26894     * @param {Node} nearNode
26895     * @param {CanvasRenderingContext2D} ctx
26896     * @param {Object} options
26897     * @returns {{x: number, y: number}}
26898     */
26899
26900  }, {
26901    key: "findBorderPosition",
26902    value: function findBorderPosition(nearNode, ctx, options) {
26903      if (this.from != this.to) {
26904        return this._findBorderPosition(nearNode, ctx, options);
26905      } else {
26906        return this._findBorderPositionCircle(nearNode, ctx, options);
26907      }
26908    }
26909
26910    /**
26911     *
26912     * @param {CanvasRenderingContext2D} ctx
26913     * @returns {{from: ({x: number, y: number, t: number}|*), to: ({x: number, y: number, t: number}|*)}}
26914     */
26915
26916  }, {
26917    key: "findBorderPositions",
26918    value: function findBorderPositions(ctx) {
26919      var from = {};
26920      var to = {};
26921      if (this.from != this.to) {
26922        from = this._findBorderPosition(this.from, ctx);
26923        to = this._findBorderPosition(this.to, ctx);
26924      } else {
26925        var _getCircleData$slice = this._getCircleData(ctx).slice(0, 2),
26926            _getCircleData$slice2 = (0, _slicedToArray3["default"])(_getCircleData$slice, 2),
26927            x = _getCircleData$slice2[0],
26928            y = _getCircleData$slice2[1];
26929
26930        from = this._findBorderPositionCircle(this.from, ctx, { x: x, y: y, low: 0.25, high: 0.6, direction: -1 });
26931        to = this._findBorderPositionCircle(this.from, ctx, { x: x, y: y, low: 0.6, high: 0.8, direction: 1 });
26932      }
26933      return { from: from, to: to };
26934    }
26935
26936    /**
26937     *
26938     * @param {CanvasRenderingContext2D} ctx
26939     * @returns {Array.<number>} x, y, radius
26940     * @private
26941     */
26942
26943  }, {
26944    key: "_getCircleData",
26945    value: function _getCircleData(ctx) {
26946      var x = void 0,
26947          y = void 0;
26948      var node = this.from;
26949      var radius = this.options.selfReferenceSize;
26950
26951      if (ctx !== undefined) {
26952        if (node.shape.width === undefined) {
26953          node.shape.resize(ctx);
26954        }
26955      }
26956
26957      // get circle coordinates
26958      if (node.shape.width > node.shape.height) {
26959        x = node.x + node.shape.width * 0.5;
26960        y = node.y - radius;
26961      } else {
26962        x = node.x + radius;
26963        y = node.y - node.shape.height * 0.5;
26964      }
26965      return [x, y, radius];
26966    }
26967
26968    /**
26969     * Get a point on a circle
26970     * @param {number} x
26971     * @param {number} y
26972     * @param {number} radius
26973     * @param {number} percentage - Value between 0 (line start) and 1 (line end)
26974     * @return {Object} point
26975     * @private
26976     */
26977
26978  }, {
26979    key: "_pointOnCircle",
26980    value: function _pointOnCircle(x, y, radius, percentage) {
26981      var angle = percentage * 2 * Math.PI;
26982      return {
26983        x: x + radius * Math.cos(angle),
26984        y: y - radius * Math.sin(angle)
26985      };
26986    }
26987
26988    /**
26989     * This function uses binary search to look for the point where the circle crosses the border of the node.
26990     * @param {Node} node
26991     * @param {CanvasRenderingContext2D} ctx
26992     * @param {Object} options
26993     * @returns {*}
26994     * @private
26995     */
26996
26997  }, {
26998    key: "_findBorderPositionCircle",
26999    value: function _findBorderPositionCircle(node, ctx, options) {
27000      var x = options.x;
27001      var y = options.y;
27002      var low = options.low;
27003      var high = options.high;
27004      var direction = options.direction;
27005
27006      var maxIterations = 10;
27007      var iteration = 0;
27008      var radius = this.options.selfReferenceSize;
27009      var pos = void 0,
27010          angle = void 0,
27011          distanceToBorder = void 0,
27012          distanceToPoint = void 0,
27013          difference = void 0;
27014      var threshold = 0.05;
27015      var middle = (low + high) * 0.5;
27016
27017      while (low <= high && iteration < maxIterations) {
27018        middle = (low + high) * 0.5;
27019
27020        pos = this._pointOnCircle(x, y, radius, middle);
27021        angle = Math.atan2(node.y - pos.y, node.x - pos.x);
27022        distanceToBorder = node.distanceToBorder(ctx, angle);
27023        distanceToPoint = Math.sqrt(Math.pow(pos.x - node.x, 2) + Math.pow(pos.y - node.y, 2));
27024        difference = distanceToBorder - distanceToPoint;
27025        if (Math.abs(difference) < threshold) {
27026          break; // found
27027        } else if (difference > 0) {
27028          // distance to nodes is larger than distance to border --> t needs to be bigger if we're looking at the to node.
27029          if (direction > 0) {
27030            low = middle;
27031          } else {
27032            high = middle;
27033          }
27034        } else {
27035          if (direction > 0) {
27036            high = middle;
27037          } else {
27038            low = middle;
27039          }
27040        }
27041        iteration++;
27042      }
27043      pos.t = middle;
27044
27045      return pos;
27046    }
27047
27048    /**
27049     * Get the line width of the edge. Depends on width and whether one of the
27050     * connected nodes is selected.
27051     * @param {boolean} selected
27052     * @param {boolean} hover
27053     * @returns {number} width
27054     * @private
27055     */
27056
27057  }, {
27058    key: "getLineWidth",
27059    value: function getLineWidth(selected, hover) {
27060      if (selected === true) {
27061        return Math.max(this.selectionWidth, 0.3 / this.body.view.scale);
27062      } else {
27063        if (hover === true) {
27064          return Math.max(this.hoverWidth, 0.3 / this.body.view.scale);
27065        } else {
27066          return Math.max(this.options.width, 0.3 / this.body.view.scale);
27067        }
27068      }
27069    }
27070
27071    /**
27072     *
27073     * @param {CanvasRenderingContext2D} ctx
27074     * @param {{toArrow: boolean, toArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), toArrowType: *, middleArrow: boolean, middleArrowScale: (number|allOptions.edges.arrows.middle.scaleFactor|{number}|Array), middleArrowType: (allOptions.edges.arrows.middle.type|{string}|string|*), fromArrow: boolean, fromArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), fromArrowType: *, arrowStrikethrough: (*|boolean|allOptions.edges.arrowStrikethrough|{boolean}), color: undefined, inheritsColor: (string|string|string|allOptions.edges.color.inherit|{string, boolean}|Array|*), opacity: *, hidden: *, length: *, shadow: *, shadowColor: *, shadowSize: *, shadowX: *, shadowY: *, dashes: (*|boolean|Array|allOptions.edges.dashes|{boolean, array}), width: *}} values
27075     * @param {boolean} selected - Unused
27076     * @param {boolean} hover - Unused
27077     * @returns {string}
27078     */
27079
27080  }, {
27081    key: "getColor",
27082    value: function getColor(ctx, values, selected, hover) {
27083      // eslint-disable-line no-unused-vars
27084      if (values.inheritsColor !== false) {
27085        // when this is a loop edge, just use the 'from' method
27086        if (values.inheritsColor === 'both' && this.from.id !== this.to.id) {
27087          var grd = ctx.createLinearGradient(this.from.x, this.from.y, this.to.x, this.to.y);
27088          var fromColor = void 0,
27089              toColor = void 0;
27090          fromColor = this.from.options.color.highlight.border;
27091          toColor = this.to.options.color.highlight.border;
27092
27093          if (this.from.selected === false && this.to.selected === false) {
27094            fromColor = util.overrideOpacity(this.from.options.color.border, values.opacity);
27095            toColor = util.overrideOpacity(this.to.options.color.border, values.opacity);
27096          } else if (this.from.selected === true && this.to.selected === false) {
27097            toColor = this.to.options.color.border;
27098          } else if (this.from.selected === false && this.to.selected === true) {
27099            fromColor = this.from.options.color.border;
27100          }
27101          grd.addColorStop(0, fromColor);
27102          grd.addColorStop(1, toColor);
27103
27104          // -------------------- this returns -------------------- //
27105          return grd;
27106        }
27107
27108        if (values.inheritsColor === "to") {
27109          return util.overrideOpacity(this.to.options.color.border, values.opacity);
27110        } else {
27111          // "from"
27112          return util.overrideOpacity(this.from.options.color.border, values.opacity);
27113        }
27114      } else {
27115        return util.overrideOpacity(values.color, values.opacity);
27116      }
27117    }
27118
27119    /**
27120     * Draw a line from a node to itself, a circle
27121     *
27122     * @param {CanvasRenderingContext2D} ctx
27123     * @param {Array} values
27124     * @param {number} x
27125     * @param {number} y
27126     * @param {number} radius
27127     * @private
27128     */
27129
27130  }, {
27131    key: "_circle",
27132    value: function _circle(ctx, values, x, y, radius) {
27133      // draw shadow if enabled
27134      this.enableShadow(ctx, values);
27135
27136      // draw a circle
27137      ctx.beginPath();
27138      ctx.arc(x, y, radius, 0, 2 * Math.PI, false);
27139      ctx.stroke();
27140
27141      // disable shadows for other elements.
27142      this.disableShadow(ctx, values);
27143    }
27144
27145    /**
27146     * Calculate the distance between a point (x3,y3) and a line segment from (x1,y1) to (x2,y2).
27147     * (x3,y3) is the point.
27148     *
27149     * http://stackoverflow.com/questions/849211/shortest-distancae-between-a-point-and-a-line-segment
27150     *
27151     * @param {number} x1
27152     * @param {number} y1
27153     * @param {number} x2
27154     * @param {number} y2
27155     * @param {number} x3
27156     * @param {number} y3
27157     * @param {Node} via
27158     * @param {Array} values
27159     * @returns {number}
27160     */
27161
27162  }, {
27163    key: "getDistanceToEdge",
27164    value: function getDistanceToEdge(x1, y1, x2, y2, x3, y3, via, values) {
27165      // eslint-disable-line no-unused-vars
27166      var returnValue = 0;
27167      if (this.from != this.to) {
27168        returnValue = this._getDistanceToEdge(x1, y1, x2, y2, x3, y3, via);
27169      } else {
27170        var _getCircleData8 = this._getCircleData(undefined),
27171            _getCircleData9 = (0, _slicedToArray3["default"])(_getCircleData8, 3),
27172            x = _getCircleData9[0],
27173            y = _getCircleData9[1],
27174            radius = _getCircleData9[2];
27175
27176        var dx = x - x3;
27177        var dy = y - y3;
27178        returnValue = Math.abs(Math.sqrt(dx * dx + dy * dy) - radiu
27178s);
27179      }
27180
27181      return returnValue;
27182    }
27183
27184    /**
27185     *
27186     * @param {number} x1
27187     * @param {number} y1
27188     * @param {number} x2
27189     * @param {number} y2
27190     * @param {number} x3
27191     * @param {number} y3
27192     * @returns {number}
27193     * @private
27194     */
27195
27196  }, {
27197    key: "_getDistanceToLine",
27198    value: function _getDistanceToLine(x1, y1, x2, y2, x3, y3) {
27199      var px = x2 - x1;
27200      var py = y2 - y1;
27201      var something = px * px + py * py;
27202      var u = ((x3 - x1) * px + (y3 - y1) * py) / something;
27203
27204      if (u > 1) {
27205        u = 1;
27206      } else if (u < 0) {
27207        u = 0;
27208      }
27209
27210      var x = x1 + u * px;
27211      var y = y1 + u * py;
27212      var dx = x - x3;
27213      var dy = y - y3;
27214
27215      //# Note: If the actual distance does not matter,
27216      //# if you only want to compare what this function
27217      //# returns to other results of this function, you
27218      //# can just return the squared distance instead
27219      //# (i.e. remove the sqrt) to gain a little performance
27220
27221      return Math.sqrt(dx * dx + dy * dy);
27222    }
27223
27224    /**
27225     * @param {CanvasRenderingContext2D} ctx
27226     * @param {string} position
27227     * @param {Node} viaNode
27228     * @param {boolean} selected
27229     * @param {boolean} hover
27230     * @param {Array} values
27231     * @returns {{point: *, core: {x: number, y: number}, angle: *, length: number, type: *}}
27232     */
27233
27234  }, {
27235    key: "getArrowData",
27236    value: function getArrowData(ctx, position, viaNode, selected, hover, values) {
27237      // set lets
27238      var angle = void 0;
27239      var arrowPoint = void 0;
27240      var node1 = void 0;
27241      var node2 = void 0;
27242      var guideOffset = void 0;
27243      var scaleFactor = void 0;
27244      var type = void 0;
27245      var lineWidth = values.width;
27246
27247      if (position === 'from') {
27248        node1 = this.from;
27249        node2 = this.to;
27250        guideOffset = 0.1;
27251        scaleFactor = values.fromArrowScale;
27252        type = values.fromArrowType;
27253      } else if (position === 'to') {
27254        node1 = this.to;
27255        node2 = this.from;
27256        guideOffset = -0.1;
27257        scaleFactor = values.toArrowScale;
27258        type = values.toArrowType;
27259      } else {
27260        node1 = this.to;
27261        node2 = this.from;
27262        scaleFactor = values.middleArrowScale;
27263        type = values.middleArrowType;
27264      }
27265
27266      // if not connected to itself
27267      if (node1 != node2) {
27268        if (position !== 'middle') {
27269          // draw arrow head
27270          if (this.options.smooth.enabled === true) {
27271            arrowPoint = this.findBorderPosition(node1, ctx, { via: viaNode });
27272            var guidePos = this.getPoint(Math.max(0.0, Math.min(1.0, arrowPoint.t + guideOffset)), viaNode);
27273            angle = Math.atan2(arrowPoint.y - guidePos.y, arrowPoint.x - guidePos.x);
27274          } else {
27275            angle = Math.atan2(node1.y - node2.y, node1.x - node2.x);
27276            arrowPoint = this.findBorderPosition(node1, ctx);
27277          }
27278        } else {
27279          angle = Math.atan2(node1.y - node2.y, node1.x - node2.x);
27280          arrowPoint = this.getPoint(0.5, viaNode); // this is 0.6 to account for the size of the arrow.
27281        }
27282      } else {
27283        // draw circle
27284        var _getCircleData10 = this._getCircleData(ctx),
27285            _getCircleData11 = (0, _slicedToArray3["default"])(_getCircleData10, 3),
27286            x = _getCircleData11[0],
27287            y = _getCircleData11[1],
27288            radius = _getCircleData11[2];
27289
27290        if (position === 'from') {
27291          arrowPoint = this.findBorderPosition(this.from, ctx, { x: x, y: y, low: 0.25, high: 0.6, direction: -1 });
27292          angle = arrowPoint.t * -2 * Math.PI + 1.5 * Math.PI + 0.1 * Math.PI;
27293        } else if (position === 'to') {
27294          arrowPoint = this.findBorderPosition(this.from, ctx, { x: x, y: y, low: 0.6, high: 1.0, direction: 1 });
27295          angle = arrowPoint.t * -2 * Math.PI + 1.5 * Math.PI - 1.1 * Math.PI;
27296        } else {
27297          arrowPoint = this._pointOnCircle(x, y, radius, 0.175);
27298          angle = 3.9269908169872414; // === 0.175 * -2 * Math.PI + 1.5 * Math.PI + 0.1 * Math.PI;
27299        }
27300      }
27301
27302      if (position === 'middle' && scaleFactor < 0) lineWidth *= -1; // reversed middle arrow
27303      var length = 15 * scaleFactor + 3 * lineWidth; // 3* lineWidth is the width of the edge.
27304
27305      var xi = arrowPoint.x - length * 0.9 * Math.cos(angle);
27306      var yi = arrowPoint.y - length * 0.9 * Math.sin(angle);
27307      var arrowCore = { x: xi, y: yi };
27308
27309      return { point: arrowPoint, core: arrowCore, angle: angle, length: length, type: type };
27310    }
27311
27312    /**
27313     *
27314     * @param {CanvasRenderingContext2D} ctx
27315     * @param {{toArrow: boolean, toArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), toArrowType: *, middleArrow: boolean, middleArrowScale: (number|allOptions.edges.arrows.middle.scaleFactor|{number}|Array), middleArrowType: (allOptions.edges.arrows.middle.type|{string}|string|*), fromArrow: boolean, fromArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), fromArrowType: *, arrowStrikethrough: (*|boolean|allOptions.edges.arrowStrikethrough|{boolean}), color: undefined, inheritsColor: (string|string|string|allOptions.edges.color.inherit|{string, boolean}|Array|*), opacity: *, hidden: *, length: *, shadow: *, shadowColor: *, shadowSize: *, shadowX: *, shadowY: *, dashes: (*|boolean|Array|allOptions.edges.dashes|{boolean, array}), width: *}} values
27316     * @param {boolean} selected
27317     * @param {boolean} hover
27318     * @param {Object} arrowData
27319     */
27320
27321  }, {
27322    key: "drawArrowHead",
27323    value: function drawArrowHead(ctx, values, selected, hover, arrowData) {
27324      // set style
27325      ctx.strokeStyle = this.getColor(ctx, values, selected, hover);
27326      ctx.fillStyle = ctx.strokeStyle;
27327      ctx.lineWidth = values.width;
27328
27329      EndPoints.draw(ctx, arrowData);
27330
27331      // draw shadow if enabled
27332      this.enableShadow(ctx, values);
27333      ctx.fill();
27334      // disable shadows for other elements.
27335      this.disableShadow(ctx, values);
27336    }
27337
27338    /**
27339     *
27340     * @param {CanvasRenderingContext2D} ctx
27341     * @param {{toArrow: boolean, toArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), toArrowType: *, middleArrow: boolean, middleArrowScale: (number|allOptions.edges.arrows.middle.scaleFactor|{number}|Array), middleArrowType: (allOptions.edges.arrows.middle.type|{string}|string|*), fromArrow: boolean, fromArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), fromArrowType: *, arrowStrikethrough: (*|boolean|allOptions.edges.arrowStrikethrough|{boolean}), color: undefined, inheritsColor: (string|string|string|allOptions.edges.color.inherit|{string, boolean}|Array|*), opacity: *, hidden: *, length: *, shadow: *, shadowColor: *, shadowSize: *, shadowX: *, shadowY: *, dashes: (*|boolean|Array|allOptions.edges.dashes|{boolean, array}), width: *}} values
27342     */
27343
27344  }, {
27345    key: "enableShadow",
27346    value: function enableShadow(ctx, values) {
27347      if (values.shadow === true) {
27348        ctx.shadowColor = values.shadowColor;
27349        ctx.shadowBlur = values.shadowSize;
27350        ctx.shadowOffsetX = values.shadowX;
27351        ctx.shadowOffsetY = values.shadowY;
27352      }
27353    }
27354
27355    /**
27356     *
27357     * @param {CanvasRenderingContext2D} ctx
27358     * @param {{toArrow: boolean, toArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), toArrowType: *, middleArrow: boolean, middleArrowScale: (number|allOptions.edges.arrows.middle.scaleFactor|{number}|Array), middleArrowType: (allOptions.edges.arrows.middle.type|{string}|string|*), fromArrow: boolean, fromArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), fromArrowType: *, arrowStrikethrough: (*|boolean|allOptions.edges.arrowStrikethrough|{boolean}), color: undefined, inheritsColor: (string|string|string|allOptions.edges.color.inherit|{string, boolean}|Array|*), opacity: *, hidden: *, length: *, shadow: *, shadowColor: *, shadowSize: *, shadowX: *, shadowY: *, dashes: (*|boolean|Array|allOptions.edges.dashes|{boolean, array}), width: *}} values
27359     */
27360
27361  }, {
27362    key: "disableShadow",
27363    value: function disableShadow(ctx, values) {
27364      if (values.shadow === true) {
27365        ctx.shadowColor = 'rgba(0,0,0,0)';
27366        ctx.shadowBlur = 0;
27367        ctx.shadowOffsetX = 0;
27368        ctx.shadowOffsetY = 0;
27369      }
27370    }
27371  }]);
27372  return EdgeBase;
27373}();
27374
27375exports["default"] = EdgeBase;
27376
27377/***/ }),
27378/* 119 */
27379/***/ (function(module, exports, __webpack_require__) {
27380
27381"use strict";
27382
27383
27384Object.defineProperty(exports, "__esModule", {
27385  value: true
27386});
27387
27388var _getPrototypeOf = __webpack_require__(3);
27389
27390var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf);
27391
27392var _possibleConstructorReturn2 = __webpack_require__(4);
27393
27394var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2);
27395
27396var _inherits2 = __webpack_require__(5);
27397
27398var _inherits3 = _interopRequireDefault(_inherits2);
27399
27400var _classCallCheck2 = __webpack_require__(0);
27401
27402var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
27403
27404var _createClass2 = __webpack_require__(1);
27405
27406var _createClass3 = _interopRequireDefault(_createClass2);
27407
27408function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
27409
27410/** ============================================================================
27411 * Location of all the endpoint drawing routines.
27412 *
27413 * Every endpoint has its own drawing routine, which contains an endpoint definition.
27414 *
27415 * The endpoint definitions must have the following properies:
27416 *
27417 * - (0,0) is the connection point to the node it attaches to
27418 * - The endpoints are orientated to the positive x-direction
27419 * - The length of the endpoint is at most 1
27420 *
27421 * As long as the endpoint classes remain simple and not too numerous, they will
27422 * be contained within this module.
27423 * All classes here except `EndPoints` should be considered as private to this module.
27424 *
27425 * -----------------------------------------------------------------------------
27426 * ### Further Actions
27427 *
27428 * After adding a new endpoint here, you also need to do the following things:
27429 *
27430 * - Add the new endpoint name to `network/options.js` in array `endPoints`.
27431 * - Add the new endpoint name to the documentation.
27432 *   Scan for 'arrows.to.type` and add it to the description.
27433 * - Add the endpoint to the examples. At the very least, add it to example
27434 *   `edgeStyles/arrowTypes`.
27435 * ============================================================================= */
27436
27437// NOTE: When a typedef is isolated in a separate comment block, an actual description is generated for it,
27438//       using the rest of the commenting in the code block. Usage of typedef in other comments then
27439//       link to there. TIL.
27440//
27441//       Also noteworthy, all typedef's set up in this manner are collected in a single, global page 'global.html'.
27442//       In other words, it doesn't matter *where* the typedef's are defined in the code.
27443//
27444//
27445// TODO: add descriptive commenting to given typedef's
27446
27447/**
27448 * @typedef {{type:string, point:Point, angle:number, length:number}} ArrowData
27449 *
27450 * Object containing instantiation data for a given endpoint.
27451 */
27452
27453/**
27454 * @typedef {{x:number, y:number}} Point
27455 * 
27456 * A point in view-coordinates.
27457 */
27458
27459/**
27460 * Common methods for endpoints
27461 *
27462 * @class
27463 */
27464var EndPoint = function () {
27465  function EndPoint() {
27466    (0, _classCallCheck3['default'])(this, EndPoint);
27467  }
27468
27469  (0, _createClass3['default'])(EndPoint, null, [{
27470    key: 'transform',
27471
27472
27473    /**
27474     * Apply transformation on points for display.
27475     *
27476     * The following is done:
27477     * - rotate by the specified angle
27478     * - multiply the (normalized) coordinates by the passed length
27479     * - offset by the target coordinates
27480     *
27481     * @param {Array<Point>} points
27482     * @param {ArrowData} arrowData
27483     * @static
27484     */
27485    value: function transform(points, arrowData) {
27486      if (!(points instanceof Array)) {
27487        points = [points];
27488      }
27489
27490      var x = arrowData.point.x;
27491      var y = arrowData.point.y;
27492      var angle = arrowData.angle;
27493      var length = arrowData.length;
27494
27495      for (var i = 0; i < points.length; ++i) {
27496        var p = points[i];
27497        var xt = p.x * Math.cos(angle) - p.y * Math.sin(angle);
27498        var yt = p.x * Math.sin(angle) + p.y * Math.cos(angle);
27499
27500        p.x = x + length * xt;
27501        p.y = y + length * yt;
27502      }
27503    }
27504
27505    /**
27506     * Draw a closed path using the given real coordinates.
27507     *
27508     * @param {CanvasRenderingContext2D} ctx
27509     * @param {Array.<Point>} points
27510     * @static
27511     */
27512
27513  }, {
27514    key: 'drawPath',
27515    value: function drawPath(ctx, points) {
27516      ctx.beginPath();
27517      ctx.moveTo(points[0].x, points[0].y);
27518      for (var i = 1; i < points.length; ++i) {
27519        ctx.lineTo(points[i].x, points[i].y);
27520      }
27521      ctx.closePath();
27522    }
27523  }]);
27524  return EndPoint;
27525}();
27526
27527/**
27528 * Drawing methods for the arrow endpoint.
27529 * @extends EndPoint
27530 */
27531
27532
27533var Arrow = function (_EndPoint) {
27534  (0, _inherits3['default'])(Arrow, _EndPoint);
27535
27536  function Arrow() {
27537    (0, _classCallCheck3['default'])(this, Arrow);
27538    return (0, _possibleConstructorReturn3['default'])(this, (Arrow.__proto__ || (0, _getPrototypeOf2['default'])(Arrow)).apply(this, arguments));
27539  }
27540
27541  (0, _createClass3['default'])(Arrow, null, [{
27542    key: 'draw',
27543
27544
27545    /**
27546     * Draw this shape at the end of a line.
27547     *
27548     * @param {CanvasRenderingContext2D} ctx
27549     * @param {ArrowData} arrowData
27550     * @static
27551     */
27552    value: function draw(ctx, arrowData) {
27553      // Normalized points of closed path, in the order that they should be drawn.
27554      // (0, 0) is the attachment point, and the point around which should be rotated
27555      var points = [{ x: 0, y: 0 }, { x: -1, y: 0.3 }, { x: -0.9, y: 0 }, { x: -1, y: -0.3 }];
27556
27557      EndPoint.transform(points, arrowData);
27558      EndPoint.drawPath(ctx, points);
27559    }
27560  }]);
27561  return Arrow;
27562}(EndPoint);
27563
27564/**
27565 * Drawing methods for the circle endpoint.
27566 */
27567
27568
27569var Circle = function () {
27570  function Circle() {
27571    (0, _classCallCheck3['default'])(this, Circle);
27572  }
27573
27574  (0, _createClass3['default'])(Circle, null, [{
27575    key: 'draw',
27576
27577
27578    /**
27579     * Draw this shape at the end of a line.
27580     *
27581     * @param {CanvasRenderingContext2D} ctx
27582     * @param {ArrowData} arrowData
27583     * @static
27584     */
27585    value: function draw(ctx, arrowData) {
27586      var point = { x: -0.4, y: 0 };
27587
27588      EndPoint.transform(point, arrowData);
27589      ctx.circle(point.x, point.y, arrowData.length * 0.4);
27590    }
27591  }]);
27592  return Circle;
27593}();
27594
27595/**
27596 * Drawing methods for the bar endpoint.
27597 */
27598
27599
27600var Bar = function () {
27601  function Bar() {
27602    (0, _classCallCheck3['default'])(this, Bar);
27603  }
27604
27605  (0, _createClass3['default'])(Bar, null, [{
27606    key: 'draw',
27607
27608
27609    /**
27610     * Draw this shape at the end of a line.
27611     *
27612     * @param {CanvasRenderingContext2D} ctx
27613     * @param {ArrowData} arrowData
27614     * @static
27615     */
27616    value: function draw(ctx, arrowData) {
27617      /*
27618          var points = [
27619            {x:0, y:0.5},
27620            {x:0, y:-0.5}
27621          ];
27622      
27623          EndPoint.transform(points, arrowData);
27624          ctx.beginPath();
27625          ctx.moveTo(points[0].x, points[0].y);
27626          ctx.lineTo(points[1].x, points[1].y);
27627          ctx.stroke();
27628      */
27629
27630      var points = [{ x: 0, y: 0.5 }, { x: 0, y: -0.5 }, { x: -0.15, y: -0.5 }, { x: -0.15, y: 0.5 }];
27631
27632      EndPoint.transform(points, arrowData);
27633      EndPoint.drawPath(ctx, points);
27634    }
27635  }]);
27636  return Bar;
27637}();
27638
27639/**
27640 * Drawing methods for the endpoints.
27641 */
27642
27643
27644var EndPoints = function () {
27645  function EndPoints() {
27646    (0, _classCallCheck3['default'])(this, EndPoints);
27647  }
27648
27649  (0, _createClass3['default'])(EndPoints, null, [{
27650    key: 'draw',
27651
27652
27653    /**
27654     * Draw an endpoint
27655     *
27656     * @param {CanvasRenderingContext2D} ctx
27657     * @param {ArrowData} arrowData
27658     * @static
27659     */
27660    value: function draw(ctx, arrowData) {
27661      var type;
27662      if (arrowData.type) {
27663        type = arrowData.type.toLowerCase();
27664      }
27665
27666      switch (type) {
27667        case 'circle':
27668          Circle.draw(ctx, arrowData);
27669          break;
27670        case 'bar':
27671          Bar.draw(ctx, arrowData);
27672          break;
27673        case 'arrow': // fall-through
27674        default:
27675          Arrow.draw(ctx, arrowData);
27676      }
27677    }
27678  }]);
27679  return EndPoints;
27680}();
27681
27682exports['default'] = EndPoints;
27683
27684/***/ }),
27685/* 120 */
27686/***/ (function(module, exports, __webpack_require__) {
27687
27688"use strict";
27689
27690
27691Object.defineProperty(exports, "__esModule", {
27692  value: true
27693});
27694
27695var _classCallCheck2 = __webpack_require__(0);
27696
27697var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
27698
27699var _createClass2 = __webpack_require__(1);
27700
27701var _createClass3 = _interopRequireDefault(_createClass2);
27702
27703function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
27704
27705/**
27706 * Barnes Hut Solver
27707 */
27708var BarnesHutSolver = function () {
27709  /**
27710   * @param {Object} body
27711   * @param {{physicsNodeIndices: Array, physicsEdgeIndices: Array, forces: {}, velocities: {}}} physicsBody
27712   * @param {Object} options
27713   */
27714  function BarnesHutSolver(body, physicsBody, options) {
27715    (0, _classCallCheck3["default"])(this, BarnesHutSolver);
27716
27717    this.body = body;
27718    this.physicsBody = physicsBody;
27719    this.barnesHutTree;
27720    this.setOptions(options);
27721    this.randomSeed = 5;
27722
27723    // debug: show grid
27724    // this.body.emitter.on("afterDrawing", (ctx) => {this._debug(ctx,'#ff0000')})
27725  }
27726
27727  /**
27728   *
27729   * @param {Object} options
27730   */
27731
27732
27733  (0, _createClass3["default"])(BarnesHutSolver, [{
27734    key: "setOptions",
27735    value: function setOptions(options) {
27736      this.options = options;
27737      this.thetaInversed = 1 / this.options.theta;
27738
27739      // if 1 then min distance = 0.5, if 0.5 then min distance = 0.5 + 0.5*node.shape.radiu
27739s
27740      this.overlapAvoidanceFactor = 1 - Math.max(0, Math.min(1, this.options.avoidOverlap));
27741    }
27742
27743    /**
27744     *
27745     * @returns {number} random integer
27746     */
27747
27748  }, {
27749    key: "seededRandom",
27750    value: function seededRandom() {
27751      var x = Math.sin(this.randomSeed++) * 10000;
27752      return x - Math.floor(x);
27753    }
27754
27755    /**
27756     * This function calculates the forces the nodes apply on each other based on a gravitational model.
27757     * The Barnes Hut method is used to speed up this N-body simulation.
27758     *
27759     * @private
27760     */
27761
27762  }, {
27763    key: "solve",
27764    value: function solve() {
27765      if (this.options.gravitationalConstant !== 0 && this.physicsBody.physicsNodeIndices.length > 0) {
27766        var node = void 0;
27767        var nodes = this.body.nodes;
27768        var nodeIndices = this.physicsBody.physicsNodeIndices;
27769        var nodeCount = nodeIndices.length;
27770
27771        // create the tree
27772        var barnesHutTree = this._formBarnesHutTree(nodes, nodeIndices);
27773
27774        // for debugging
27775        this.barnesHutTree = barnesHutTree;
27776
27777        // place the nodes one by one recursively
27778        for (var i = 0; i < nodeCount; i++) {
27779          node = nodes[nodeIndices[i]];
27780          if (node.options.mass > 0) {
27781            // starting with root is irrelevant, it never passes the BarnesHutSolver condition
27782            this._getForceContributions(barnesHutTree.root, node);
27783          }
27784        }
27785      }
27786    }
27787
27788    /**
27789     * @param {Object} parentBranch
27790     * @param {Node} node
27791     * @private
27792     */
27793
27794  }, {
27795    key: "_getForceContributions",
27796    value: function _getForceContributions(parentBranch, node) {
27797      this._getForceContribution(parentBranch.children.NW, node);
27798      this._getForceContribution(parentBranch.children.NE, node);
27799      this._getForceContribution(parentBranch.children.SW, node);
27800      this._getForceContribution(parentBranch.children.SE, node);
27801    }
27802
27803    /**
27804     * This function traverses the barnesHutTree. It checks when it can approximate distant nodes with their center of mass.
27805     * If a region contains a single node, we check if it is not itself, then we apply the force.
27806     *
27807     * @param {Object} parentBranch
27808     * @param {Node} node
27809     * @private
27810     */
27811
27812  }, {
27813    key: "_getForceContribution",
27814    value: function _getForceContribution(parentBranch, node) {
27815      // we get no force contribution from an empty region
27816      if (parentBranch.childrenCount > 0) {
27817        var dx = void 0,
27818            dy = void 0,
27819            distance = void 0;
27820
27821        // get the distance from the center of mass to the node.
27822        dx = parentBranch.centerOfMass.x - node.x;
27823        dy = parentBranch.centerOfMass.y - node.y;
27824        distance = Math.sqrt(dx * dx + dy * dy);
27825
27826        // BarnesHutSolver condition
27827        // original condition : s/d < theta = passed  ===  d/s > 1/theta = passed
27828        // calcSize = 1/s --> d * 1/s > 1/theta = passed
27829        if (distance * parentBranch.calcSize > this.thetaInversed) {
27830          this._calculateForces(distance, dx, dy, node, parentBranch);
27831        } else {
27832          // Did not pass the condition, go into children if available
27833          if (parentBranch.childrenCount === 4) {
27834            this._getForceContributions(parentBranch, node);
27835          } else {
27836            // parentBranch must have only one node, if it was empty we wouldnt be here
27837            if (parentBranch.children.data.id != node.id) {
27838              // if it is not self
27839              this._calculateForces(distance, dx, dy, node, parentBranch);
27840            }
27841          }
27842        }
27843      }
27844    }
27845
27846    /**
27847     * Calculate the forces based on the distance.
27848     *
27849     * @param {number} distance
27850     * @param {number} dx
27851     * @param {number} dy
27852     * @param {Node} node
27853     * @param {Object} parentBranch
27854     * @private
27855     */
27856
27857  }, {
27858    key: "_calculateForces",
27859    value: function _calculateForces(distance, dx, dy, node, parentBranch) {
27860      if (distance === 0) {
27861        distance = 0.1;
27862        dx = distance;
27863      }
27864
27865      if (this.overlapAvoidanceFactor < 1 && node.shape.radius) {
27866        distance = Math.max(0.1 + this.overlapAvoidanceFactor * node.shape.radiu
27866s, distance - node.shape.radius);
27867      }
27868
27869      // the dividing by the distance cubed instead of squared allows us to get the fx and fy components without sines and cosines
27870      // it is shorthand for gravityforce with distance squared and fx = dx/distance * gravityForce
27871      var gravityForce = this.options.gravitationalConstant * parentBranch.mass * node.options.mass / Math.pow(distance, 3);
27872      var fx = dx * gravityForce;
27873      var fy = dy * gravityForce;
27874
27875      this.physicsBody.forces[node.id].x += fx;
27876      this.physicsBody.forces[node.id].y += fy;
27877    }
27878
27879    /**
27880     * This function constructs the barnesHut tree recursively. It creates the root, splits it and starts placing the nodes.
27881     *
27882     * @param {Array.<Node>} nodes
27883     * @param {Array.<number>} nodeIndices
27884     * @returns {{root: {centerOfMass: {x: number, y: number}, mass: number, range: {minX: number, maxX: number, minY: number, maxY: number}, size: number, calcSize: number, children: {data: null}, maxWidth: number, level: number, childrenCount: number}}} BarnesHutTree
27885     * @private
27886     */
27887
27888  }, {
27889    key: "_formBarnesHutTree",
27890    value: function _formBarnesHutTree(nodes, nodeIndices) {
27891      var node = void 0;
27892      var nodeCount = nodeIndices.length;
27893
27894      var minX = nodes[nodeIndices[0]].x;
27895      var minY = nodes[nodeIndices[0]].y;
27896      var maxX = nodes[nodeIndices[0]].x;
27897      var maxY = nodes[nodeIndices[0]].y;
27898
27899      // get the range of the nodes
27900      for (var i = 1; i < nodeCount; i++) {
27901        var _node = nodes[nodeIndices[i]];
27902        var x = _node.x;
27903        var y = _node.y;
27904        if (_node.options.mass > 0) {
27905          if (x < minX) {
27906            minX = x;
27907          }
27908          if (x > maxX) {
27909            maxX = x;
27910          }
27911          if (y < minY) {
27912            minY = y;
27913          }
27914          if (y > maxY) {
27915            maxY = y;
27916          }
27917        }
27918      }
27919      // make the range a square
27920      var sizeDiff = Math.abs(maxX - minX) - Math.abs(maxY - minY); // difference between X and Y
27921      if (sizeDiff > 0) {
27922        minY -= 0.5 * sizeDiff;
27923        maxY += 0.5 * sizeDiff;
27924      } // xSize > ySize
27925      else {
27926          minX += 0.5 * sizeDiff;
27927          maxX -= 0.5 * sizeDiff;
27928        } // xSize < ySize
27929
27930
27931      var minimumTreeSize = 1e-5;
27932      var rootSize = Math.max(minimumTreeSize, Math.abs(maxX - minX));
27933      var halfRootSize = 0.5 * rootSize;
27934      var centerX = 0.5 * (minX + maxX),
27935          centerY = 0.5 * (minY + maxY);
27936
27937      // construct the barnesHutTree
27938      var barnesHutTree = {
27939        root: {
27940          centerOfMass: { x: 0, y: 0 },
27941          mass: 0,
27942          range: {
27943            minX: centerX - halfRootSize, maxX: centerX + halfRootSize,
27944            minY: centerY - halfRootSize, maxY: centerY + halfRootSize
27945          },
27946          size: rootSize,
27947          calcSize: 1 / rootSize,
27948          children: { data: null },
27949          maxWidth: 0,
27950          level: 0,
27951          childrenCount: 4
27952        }
27953      };
27954      this._splitBranch(barnesHutTree.root);
27955
27956      // place the nodes one by one recursively
27957      for (var _i = 0; _i < nodeCount; _i++) {
27958        node = nodes[nodeIndices[_i]];
27959        if (node.options.mass > 0) {
27960          this._placeInTree(barnesHutTree.root, node);
27961        }
27962      }
27963
27964      // make global
27965      return barnesHutTree;
27966    }
27967
27968    /**
27969     * this updates the mass of a branch. this is increased by adding a node.
27970     *
27971     * @param {Object} parentBranch
27972     * @param {Node} node
27973     * @private
27974     */
27975
27976  }, {
27977    key: "_updateBranchMass",
27978    value: function _updateBranchMass(parentBranch, node) {
27979      var centerOfMass = parentBranch.centerOfMass;
27980      var totalMass = parentBranch.mass + node.options.mass;
27981      var totalMassInv = 1 / totalMass;
27982
27983      centerOfMass.x = centerOfMass.x * parentBranch.mass + node.x * node.options.mass;
27984      centerOfMass.x *= totalMassInv;
27985
27986      centerOfMass.y = centerOfMass.y * parentBranch.mass + node.y * node.options.mass;
27987      centerOfMass.y *= totalMassInv;
27988
27989      parentBranch.mass = totalMass;
27990      var biggestSize = Math.max(Math.max(node.height, node.radiu
27990s), node.width);
27991      parentBranch.maxWidth = parentBranch.maxWidth < biggestSize ? biggestSize : parentBranch.maxWidth;
27992    }
27993
27994    /**
27995     * determine in which branch the node will be placed.
27996     *
27997     * @param {Object} parentBranch
27998     * @param {Node} node
27999     * @param {boolean} skipMassUpdate
28000     * @private
28001     */
28002
28003  }, {
28004    key: "_placeInTree",
28005    value: function _placeInTree(parentBranch, node, skipMassUpdate) {
28006      if (skipMassUpdate != true || skipMassUpdate === undefined) {
28007        // update the mass of the branch.
28008        this._updateBranchMass(parentBranch, node);
28009      }
28010
28011      var range = parentBranch.children.NW.range;
28012      var region = void 0;
28013      if (range.maxX > node.x) {
28014        // in NW or SW
28015        if (range.maxY > node.y) {
28016          region = "NW";
28017        } else {
28018          region = "SW";
28019        }
28020      } else {
28021        // in NE or SE
28022        if (range.maxY > node.y) {
28023          region = "NE";
28024        } else {
28025          region = "SE";
28026        }
28027      }
28028
28029      this._placeInRegion(parentBranch, node, region);
28030    }
28031
28032    /**
28033     * actually place the node in a region (or branch)
28034     *
28035     * @param {Object} parentBranch
28036     * @param {Node} node
28037     * @param {'NW'| 'NE' | 'SW' | 'SE'} region
28038     * @private
28039     */
28040
28041  }, {
28042    key: "_placeInRegion",
28043    value: function _placeInRegion(parentBranch, node, region) {
28044      var children = parentBranch.children[region];
28045
28046      switch (children.childrenCount) {
28047        case 0:
28048          // place node here
28049          children.children.data = node;
28050          children.childrenCount = 1;
28051          this._updateBranchMass(children, node);
28052          break;
28053        case 1:
28054          // convert into children
28055          // if there are two nodes exactly overlapping (on init, on opening of cluster etc.)
28056          // we move one node a little bit and we do not put it in the tree.
28057          if (children.children.data.x === node.x && children.children.data.y === node.y) {
28058            node.x += this.seededRandom();
28059            node.y += this.seededRandom();
28060          } else {
28061            this._splitBranch(children);
28062            this._placeInTree(children, node);
28063          }
28064          break;
28065        case 4:
28066          // place in branch
28067          this._placeInTree(children, node);
28068          break;
28069      }
28070    }
28071
28072    /**
28073     * this function splits a branch into 4 sub branches. If the branch contained a node, we place it in the subbranch
28074     * after the split is complete.
28075     *
28076     * @param {Object} parentBranch
28077     * @private
28078     */
28079
28080  }, {
28081    key: "_splitBranch",
28082    value: function _splitBranch(parentBranch) {
28083      // if the branch is shaded with a node, replace the node in the new subset.
28084      var containedNode = null;
28085      if (parentBranch.childrenCount === 1) {
28086        containedNode = parentBranch.children.data;
28087        parentBranch.mass = 0;
28088        parentBranch.centerOfMass.x = 0;
28089        parentBranch.centerOfMass.y = 0;
28090      }
28091      parentBranch.childrenCount = 4;
28092      parentBranch.children.data = null;
28093      this._insertRegion(parentBranch, "NW");
28094      this._insertRegion(parentBranch, "NE");
28095      this._insertRegion(parentBranch, "SW");
28096      this._insertRegion(parentBranch, "SE");
28097
28098      if (containedNode != null) {
28099        this._placeInTree(parentBranch, containedNode);
28100      }
28101    }
28102
28103    /**
28104     * This function subdivides the region into four new segments.
28105     * Specifically, this inserts a single new segment.
28106     * It fills the children section of the parentBranch
28107     *
28108     * @param {Object} parentBranch
28109     * @param {'NW'| 'NE' | 'SW' | 'SE'} region
28110     * @private
28111     */
28112
28113  }, {
28114    key: "_insertRegion",
28115    value: function _insertRegion(parentBranch, region) {
28116      var minX = void 0,
28117          maxX = void 0,
28118          minY = void 0,
28119          maxY = void 0;
28120      var childSize = 0.5 * parentBranch.size;
28121      switch (region) {
28122        case "NW":
28123          minX = parentBranch.range.minX;
28124          maxX = parentBranch.range.minX + childSize;
28125          minY = parentBranch.range.minY;
28126          maxY = parentBranch.range.minY + childSize;
28127          break;
28128        case "NE":
28129          minX = parentBranch.range.minX + childSize;
28130          maxX = parentBranch.range.maxX;
28131          minY = parentBranch.range.minY;
28132          maxY = parentBranch.range.minY + childSize;
28133          break;
28134        case "SW":
28135          minX = parentBranch.range.minX;
28136          maxX = parentBranch.range.minX + childSize;
28137          minY = parentBranch.range.minY + childSize;
28138          maxY = parentBranch.range.maxY;
28139          break;
28140        case "SE":
28141          minX = parentBranch.range.minX + childSize;
28142          maxX = parentBranch.range.maxX;
28143          minY = parentBranch.range.minY + childSize;
28144          maxY = parentBranch.range.maxY;
28145          break;
28146      }
28147
28148      parentBranch.children[region] = {
28149        centerOfMass: { x: 0, y: 0 },
28150        mass: 0,
28151        range: { minX: minX, maxX: maxX, minY: minY, maxY: maxY },
28152        size: 0.5 * parentBranch.size,
28153        calcSize: 2 * parentBranch.calcSize,
28154        children: { data: null },
28155        maxWidth: 0,
28156        level: parentBranch.level + 1,
28157        childrenCount: 0
28158      };
28159    }
28160
28161    //---------------------------  DEBUGGING BELOW  ---------------------------//
28162
28163
28164    /**
28165     * This function is for debugging purposed, it draws the tree.
28166     *
28167     * @param {CanvasRenderingContext2D} ctx
28168     * @param {string} color
28169     * @private
28170     */
28171
28172  }, {
28173    key: "_debug",
28174    value: function _debug(ctx, color) {
28175      if (this.barnesHutTree !== undefined) {
28176
28177        ctx.lineWidth = 1;
28178
28179        this._drawBranch(this.barnesHutTree.root, ctx, color);
28180      }
28181    }
28182
28183    /**
28184     * This function is for debugging purposes. It draws the branches recursively.
28185     *
28186     * @param {Object} branch
28187     * @param {CanvasRenderingContext2D} ctx
28188     * @param {string} color
28189     * @private
28190     */
28191
28192  }, {
28193    key: "_drawBranch",
28194    value: function _drawBranch(branch, ctx, color) {
28195      if (color === undefined) {
28196        color = "#FF0000";
28197      }
28198
28199      if (branch.childrenCount === 4) {
28200        this._drawBranch(branch.children.NW, ctx);
28201        this._drawBranch(branch.children.NE, ctx);
28202        this._drawBranch(branch.children.SE, ctx);
28203        this._drawBranch(branch.children.SW, ctx);
28204      }
28205      ctx.strokeStyle = color;
28206      ctx.beginPath();
28207      ctx.moveTo(branch.range.minX, branch.range.minY);
28208      ctx.lineTo(branch.range.maxX, branch.range.minY);
28209      ctx.stroke();
28210
28211      ctx.beginPath();
28212      ctx.moveTo(branch.range.maxX, branch.range.minY);
28213      ctx.lineTo(branch.range.maxX, branch.range.maxY);
28214      ctx.stroke();
28215
28216      ctx.beginPath();
28217      ctx.moveTo(branch.range.maxX, branch.range.maxY);
28218      ctx.lineTo(branch.range.minX, branch.range.maxY);
28219      ctx.stroke();
28220
28221      ctx.beginPath();
28222      ctx.moveTo(branch.range.minX, branch.range.maxY);
28223      ctx.lineTo(branch.range.minX, branch.range.minY);
28224      ctx.stroke();
28225
28226      /*
28227       if (branch.mass > 0) {
28228       ctx.circle(branch.centerOfMass.x, branch.centerOfMass.y, 3*branch.mass);
28229       ctx.stroke();
28230       }
28231       */
28232    }
28233  }]);
28234  return BarnesHutSolver;
28235}();
28236
28237exports["default"] = BarnesHutSolver;
28238
28239/***/ }),
28240/* 121 */
28241/***/ (function(module, exports, __webpack_require__) {
28242
28243"use strict";
28244
28245
28246Object.defineProperty(exports, "__esModule", {
28247  value: true
28248});
28249
28250var _classCallCheck2 = __webpack_require__(0);
28251
28252var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
28253
28254var _createClass2 = __webpack_require__(1);
28255
28256var _createClass3 = _interopRequireDefault(_createClass2);
28257
28258function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
28259
28260/**
28261 * Central Gravity Solver
28262 */
28263var CentralGravitySolver = function () {
28264  /**
28265   * @param {Object} body
28266   * @param {{physicsNodeIndices: Array, physicsEdgeIndices: Array, forces: {}, velocities: {}}} physicsBody
28267   * @param {Object} options
28268   */
28269  function CentralGravitySolver(body, physicsBody, options) {
28270    (0, _classCallCheck3["default"])(this, CentralGravitySolver);
28271
28272    this.body = body;
28273    this.physicsBody = physicsBody;
28274    this.setOptions(options);
28275  }
28276
28277  /**
28278   *
28279   * @param {Object} options
28280   */
28281
28282
28283  (0, _createClass3["default"])(CentralGravitySolver, [{
28284    key: "setOptions",
28285    value: function setOptions(options) {
28286      this.options = options;
28287    }
28288
28289    /**
28290     * Calculates forces for each node
28291     */
28292
28293  }, {
28294    key: "solve",
28295    value: function solve() {
28296      var dx = void 0,
28297          dy = void 0,
28298          distance = void 0,
28299          node = void 0;
28300      var nodes = this.body.nodes;
28301      var nodeIndices = this.physicsBody.physicsNodeIndices;
28302      var forces = this.physicsBody.forces;
28303
28304      for (var i = 0; i < nodeIndices.length; i++) {
28305        var nodeId = nodeIndices[i];
28306        node = nodes[nodeId];
28307        dx = -node.x;
28308        dy = -node.y;
28309        distance = Math.sqrt(dx * dx + dy * dy);
28310
28311        this._calculateForces(distance, dx, dy, forces, node);
28312      }
28313    }
28314
28315    /**
28316     * Calculate the forces based on the distance.
28317     * @param {number} distance
28318     * @param {number} dx
28319     * @param {number} dy
28320     * @param {Object<Node.id, vis.Node>} forces
28321     * @param {Node} node
28322     * @private
28323     */
28324
28325  }, {
28326    key: "_calculateForces",
28327    value: function _calculateForces(distance, dx, dy, forces, node) {
28328      var gravityForce = distance === 0 ? 0 : this.options.centralGravity / distance;
28329      forces[node.id].x = dx * gravityForce;
28330      forces[node.id].y = dy * gravityForce;
28331    }
28332  }]);
28333  return CentralGravitySolver;
28334}();
28335
28336exports["default"] = CentralGravitySolver;
28337
28338/***/ }),
28339/* 122 */
28340/***/ (function(module, exports, __webpack_require__) {
28341
28342"use strict";
28343
28344
28345Object.defineProperty(exports, "__esModule", {
28346  value: true
28347});
28348/**
28349 * This object contains all possible options. It will check if the types are correct, if required if the option is one
28350 * of the allowed values.
28351 *
28352 * __any__ means that the name of the property does not matter.
28353 * __type__ is a required field for all objects and contains the allowed types of all objects
28354 */
28355var string = 'string';
28356var bool = 'boolean';
28357var number = 'number';
28358var array = 'array';
28359var object = 'object'; // should only be in a __type__ property
28360var dom = 'dom';
28361var any = 'any';
28362
28363// List of endpoints
28364var endPoints = ['arrow', 'circle', 'bar'];
28365
28366var allOptions = {
28367  configure: {
28368    enabled: { boolean: bool },
28369    filter: { boolean: bool, string: string, array: array, 'function': 'function' },
28370    container: { dom: dom },
28371    showButton: { boolean: bool },
28372    __type__: { object: object, boolean: bool, string: string, array: array, 'function': 'function' }
28373  },
28374  edges: {
28375    arrows: {
28376      to: { enabled: { boolean: bool }, scaleFactor: { number: number }, type: { string: endPoints }, __type__: { object: object, boolean: bool } },
28377      middle: { enabled: { boolean: bool }, scaleFactor: { number: number }, type: { string: endPoints }, __type__: { object: object, boolean: bool } },
28378      from: { enabled: { boolean: bool }, scaleFactor: { number: number }, type: { string: endPoints }, __type__: { object: object, boolean: bool } },
28379      __type__: { string: ['from', 'to', 'middle'], object: object }
28380    },
28381    arrowStrikethrough: { boolean: bool },
28382    chosen: {
28383      label: { boolean: bool, 'function': 'function' },
28384      edge: { boolean: bool, 'function': 'function' },
28385      __type__: { object: object, boolean: bool }
28386    },
28387    color: {
28388      color: { string: string },
28389      highlight: { string: string },
28390      hover: { string: string },
28391      inherit: { string: ['from', 'to', 'both'], boolean: bool },
28392      opacity: { number: number },
28393      __type__: { object: object, string: string }
28394    },
28395    dashes: { boolean: bool, array: array },
28396    font: {
28397      color: { string: string },
28398      size: { number: number }, // px
28399      face: { string: string },
28400      background: { string: string },
28401      strokeWidth: { number: number }, // px
28402      strokeColor: { string: string },
28403      align: { string: ['horizontal', 'top', 'middle', 'bottom'] },
28404      vadjust: { number: number },
28405      multi: { boolean: bool, string: string },
28406      bold: {
28407        color: { string: string },
28408        size: { number: number }, // px
28409        face: { string: string },
28410        mod: { string: string },
28411        vadjust: { number: number },
28412        __type__: { object: object, string: string }
28413      },
28414      boldital: {
28415        color: { string: string },
28416        size: { number: number }, // px
28417        face: { string: string },
28418        mod: { string: string },
28419        vadjust: { number: number },
28420        __type__: { object: object, string: string }
28421      },
28422      ital: {
28423        color: { string: string },
28424        size: { number: number }, // px
28425        face: { string: string },
28426        mod: { string: string },
28427        vadjust: { number: number },
28428        __type__: { object: object, string: string }
28429      },
28430      mono: {
28431        color: { string: string },
28432        size: { number: number }, // px
28433        face: { string: string },
28434        mod: { string: string },
28435        vadjust: { number: number },
28436        __type__: { object: object, string: string }
28437      },
28438      __type__: { object: object, string: string }
28439    },
28440    hidden: { boolean: bool },
28441    hoverWidth: { 'function': 'function', number: number },
28442    label: { string: string, 'undefined': 'undefined' },
28443    labelHighlightBold: { boolean: bool },
28444    length: { number: number, 'undefined': 'undefined' },
28445    physics: { boolean: bool },
28446    scaling: {
28447      min: { number: number },
28448      max: { number: number },
28449      label: {
28450        enabled: { boolean: bool },
28451        min: { number: number },
28452        max: { number: number },
28453        maxVisible: { number: number },
28454        drawThreshold: { number: number },
28455        __type__: { object: object, boolean: bool }
28456      },
28457      customScalingFunction: { 'function': 'function' },
28458      __type__: { object: object }
28459    },
28460    selectionWidth: { 'function': 'function', number: number },
28461    selfReferenceSize: { number: number },
28462    shadow: {
28463      enabled: { boolean: bool },
28464      color: { string: string },
28465      size: { number: number },
28466      x: { number: number },
28467      y: { number: number },
28468      __type__: { object: object, boolean: bool }
28469    },
28470    smooth: {
28471      enabled: { boolean: bool },
28472      type: { string: ['dynamic', 'continuous', 'discrete', 'diagonalCross', 'straightCross', 'horizontal', 'vertical', 'curvedCW', 'curvedCCW', 'cubicBezier'] },
28473      roundness: { number: number },
28474      forceDirection: { string: ['horizontal', 'vertical', 'none'], boolean: bool },
28475      __type__: { object: object, boolean: bool }
28476    },
28477    title: { string: string, 'undefined': 'undefined' },
28478    width: { number: number },
28479    widthConstraint: {
28480      maximum: { number: number },
28481      __type__: { object: object, boolean: bool, number: number }
28482    },
28483    value: { number: number, 'undefined': 'undefined' },
28484    __type__: { object: object }
28485  },
28486  groups: {
28487    useDefaultGroups: { boolean: bool },
28488    __any__: 'get from nodes, will be overwritten below',
28489    __type__: { object: object }
28490  },
28491  interaction: {
28492    dragNodes: { boolean: bool },
28493    dragView: { boolean: bool },
28494    hideEdgesOnDrag: { boolean: bool },
28495    hideNodesOnDrag: { boolean: bool },
28496    hover: { boolean: bool },
28497    keyboard: {
28498      enabled: { boolean: bool },
28499      speed: { x: { number: number }, y: { number: number }, zoom: { number: number }, __type__: { object: object } },
28500      bindToWindow: { boolean: bool },
28501      __type__: { object: object, boolean: bool }
28502    },
28503    multiselect: { boolean: bool },
28504    navigationButtons: { boolean: bool },
28505    selectable: { boolean: bool },
28506    selectConnectedEdges: { boolean: bool },
28507    hoverConnectedEdges: { boolean: bool },
28508    tooltipDelay: { number: number },
28509    zoomView: { boolean: bool },
28510    __type__: { object: object }
28511  },
28512  layout: {
28513    randomSeed: { 'undefined': 'undefined', number: number },
28514    improvedLayout: { boolean: bool },
28515    hierarchical: {
28516      enabled: { boolean: bool },
28517      levelSeparation: { number: number },
28518      nodeSpacing: { number: number },
28519      treeSpacing: { number: number },
28520      blockShifting: { boolean: bool },
28521      edgeMinimization: { boolean: bool },
28522      parentCentralization: { boolean: bool },
28523      direction: { string: ['UD', 'DU', 'LR', 'RL'] }, // UD, DU, LR, RL
28524      sortMethod: { string: ['hubsize', 'directed'] }, // hubsize, directed
28525      __type__: { object: object, boolean: bool }
28526    },
28527    __type__: { object: object }
28528  },
28529  manipulation: {
28530    enabled: { boolean: bool },
28531    initiallyActive: { boolean: bool },
28532    addNode: { boolean: bool, 'function': 'function' },
28533    addEdge: { boolean: bool, 'function': 'function' },
28534    editNode: { 'function': 'function' },
28535    editEdge: {
28536      editWithoutDrag: { 'function': 'function' },
28537      __type__: { object: object, boolean: bool, 'function': 'function' }
28538    },
28539    deleteNode: { boolean: bool, 'function': 'function' },
28540    deleteEdge: { boolean: bool, 'function': 'function' },
28541    controlNodeStyle: 'get from nodes, will be overwritten below',
28542    __type__: { object: object, boolean: bool }
28543  },
28544  nodes: {
28545    borderWidth: { number: number },
28546    borderWidthSelected: { number: number, 'undefined': 'undefined' },
28547    brokenImage: { string: string, 'undefined': 'undefined' },
28548    chosen: {
28549      label: { boolean: bool, 'function': 'function' },
28550      node: { boolean: bool, 'function': 'function' },
28551      __type__: { object: object, boolean: bool }
28552    },
28553    color: {
28554      border: { string: string },
28555      background: { string: string },
28556      highlight: {
28557        border: { string: string },
28558        background: { string: string },
28559        __type__: { object: object, string: string }
28560      },
28561      hover: {
28562        border: { string: string },
28563        background: { string: string },
28564        __type__: { object: object, string: string }
28565      },
28566      __type__: { object: object, string: string }
28567    },
28568    fixed: {
28569      x: { boolean: bool },
28570      y: { boolean: bool },
28571      __type__: { object: object, boolean: bool }
28572    },
28573    font: {
28574      align: { string: string },
28575      color: { string: string },
28576      size: { number: number }, // px
28577      face: { string: string },
28578      background: { string: string },
28579      strokeWidth: { number: number }, // px
28580      strokeColor: { string: string },
28581      vadjust: { number: number },
28582      multi: { boolean: bool, string: string },
28583      bold: {
28584        color: { string: string },
28585        size: { number: number }, // px
28586        face: { string: string },
28587        mod: { string: string },
28588        vadjust: { number: number },
28589        __type__: { object: object, string: string }
28590      },
28591      boldital: {
28592        color: { string: string },
28593        size: { number: number }, // px
28594        face: { string: string },
28595        mod: { string: string },
28596        vadjust: { number: number },
28597        __type__: { object: object, string: string }
28598      },
28599      ital: {
28600        color: { string: string },
28601        size: { number: number }, // px
28602        face: { string: string },
28603        mod: { string: string },
28604        vadjust: { number: number },
28605        __type__: { object: object, string: string }
28606      },
28607      mono: {
28608        color: { string: string },
28609        size: { number: number }, // px
28610        face: { string: string },
28611        mod: { string: string },
28612        vadjust: { number: number },
28613        __type__: { object: object, string: string }
28614      },
28615      __type__: { object: object, string: string }
28616    },
28617    group: { string: string, number: number, 'undefined': 'undefined' },
28618    heightConstraint: {
28619      minimum: { number: number },
28620      valign: { string: string },
28621      __type__: { object: object, boolean: bool, number: number }
28622    },
28623    hidden: { boolean: bool },
28624    icon: {
28625      face: { string: string },
28626      code: { string: string }, //'\uf007',
28627      size: { number: number }, //50,
28628      color: { string: string },
28629      __type__: { object: object }
28630    },
28631    id: { string: string, number: number },
28632    image: {
28633      selected: { string: string, 'undefined': 'undefined' }, // --> URL
28634      unselected: { string: string, 'undefined': 'undefined' }, // --> URL
28635      __type__: { object: object, string: string }
28636    },
28637    label: { string: string, 'undefined': 'undefined' },
28638    labelHighlightBold: { boolean: bool },
28639    level: { number: number, 'undefined': 'undefined' },
28640    margin: {
28641      top: { number: number },
28642      right: { number: number },
28643      bottom: { number: number },
28644      left: { number: number },
28645      __type__: { object: object, number: number }
28646    },
28647    mass: { number: number },
28648    physics: { boolean: bool },
28649    scaling: {
28650      min: { number: number },
28651      max: { number: number },
28652      label: {
28653        enabled: { boolean: bool },
28654        min: { number: number },
28655        max: { number: number },
28656        maxVisible: { number: number },
28657        drawThreshold: { number: number },
28658        __type__: { object: object, boolean: bool }
28659      },
28660      customScalingFunction: { 'function': 'function' },
28661      __type__: { object: object }
28662    },
28663    shadow: {
28664      enabled: { boolean: bool },
28665      color: { string: string },
28666      size: { number: number },
28667      x: { number: number },
28668      y: { number: number },
28669      __type__: { object: object, boolean: bool }
28670    },
28671    shape: { string: ['ellipse', 'circle', 'database', 'box', 'text', 'image', 'circularImage', 'diamond', 'dot', 'star', 'triangle', 'triangleDown', 'square', 'icon', 'hexagon'] },
28672    shapeProperties: {
28673      borderDashes: { boolean: bool, array: array },
28674      borderRadius: { number: number },
28675      interpolation: { boolean: bool },
28676      useImageSize: { boolean: bool },
28677      useBorderWithImage: { boolean: bool },
28678      __type__: { object: object }
28679    },
28680    size: { number: number },
28681    title: { string: string, dom: dom, 'undefined': 'undefined' },
28682    value: { number: number, 'undefined': 'undefined' },
28683    widthConstraint: {
28684      minimum: { number: number },
28685      maximum: { number: number },
28686      __type__: { object: object, boolean: bool, number: number }
28687    },
28688    x: { number: number },
28689    y: { number: number },
28690    __type__: { object: object }
28691  },
28692  physics: {
28693    enabled: { boolean: bool },
28694    barnesHut: {
28695      gravitationalConstant: { number: number },
28696      centralGravity: { number: number },
28697      springLength: { number: number },
28698      springConstant: { number: number },
28699      damping: { number: number },
28700      avoidOverlap: { number: number },
28701      __type__: { object: object }
28702    },
28703    forceAtlas2Based: {
28704      gravitationalConstant: { number: number },
28705      centralGravity: { number: number },
28706      springLength: { number: number },
28707      springConstant: { number: number },
28708      damping: { number: number },
28709      avoidOverlap: { number: number },
28710      __type__: { object: object }
28711    },
28712    repulsion: {
28713      centralGravity: { number: number },
28714      springLength: { number: number },
28715      springConstant: { number: number },
28716      nodeDistance: { number: number },
28717      damping: { number: number },
28718      __type__: { object: object }
28719    },
28720    hierarchicalRepulsion: {
28721      centralGravity: { number: number },
28722      springLength: { number: number },
28723      springConstant: { number: number },
28724      nodeDistance: { number: number },
28725      damping: { number: number },
28726      __type__: { object: object }
28727    },
28728    maxVelocity: { number: number },
28729    minVelocity: { number: number }, // px/s
28730    solver: { string: ['barnesHut', 'repulsion', 'hierarchicalRepulsion', 'forceAtlas2Based'] },
28731    stabilization: {
28732      enabled: { boolean: bool },
28733      iterations: { number: number }, // maximum number of iteration to stabilize
28734      updateInterval: { number: number },
28735      onlyDynamicEdges: { boolean: bool },
28736      fit: { boolean: bool },
28737      __type__: { object: object, boolean: bool }
28738    },
28739    timestep: { number: number },
28740    adaptiveTimestep: { boolean: bool },
28741    __type__: { object: object, boolean: bool }
28742  },
28743
28744  //globals :
28745  autoResize: { boolean: bool },
28746  clickToUse: { boolean: bool },
28747  locale: { string: string },
28748  locales: {
28749    __any__: { any: any },
28750    __type__: { object: object }
28751  },
28752  height: { string: string },
28753  width: { string: string },
28754  __type__: { object: object }
28755};
28756
28757allOptions.groups.__any__ = allOptions.nodes;
28758allOptions.manipulation.controlNodeStyle = allOptions.nodes;
28759
28760var configureOptions = {
28761  nodes: {
28762    borderWidth: [1, 0, 10, 1],
28763    borderWidthSelected: [2, 0, 10, 1],
28764    color: {
28765      border: ['color', '#2B7CE9'],
28766      background: ['color', '#97C2FC'],
28767      highlight: {
28768        border: ['color', '#2B7CE9'],
28769        background: ['color', '#D2E5FF']
28770      },
28771      hover: {
28772        border: ['color', '#2B7CE9'],
28773        background: ['color', '#D2E5FF']
28774      }
28775    },
28776    fixed: {
28777      x: false,
28778      y: false
28779    },
28780    font: {
28781      color: ['color', '#343434'],
28782      size: [14, 0, 100, 1], // px
28783      face: ['arial', 'verdana', 'tahoma'],
28784      background: ['color', 'none'],
28785      strokeWidth: [0, 0, 50, 1], // px
28786      strokeColor: ['color', '#ffffff']
28787    },
28788    //group: 'string',
28789    hidden: false,
28790    labelHighlightBold: true,
28791    //icon: {
28792    //  face: 'string',  //'FontAwesome',
28793    //  code: 'string',  //'\uf007',
28794    //  size: [50, 0, 200, 1],  //50,
28795    //  color: ['color','#2B7CE9']   //'#aa00ff'
28796    //},
28797    //image: 'string', // --> URL
28798    physics: true,
28799    scaling: {
28800      min: [10, 0, 200, 1],
28801      max: [30, 0, 200, 1],
28802      label: {
28803        enabled: false,
28804        min: [14, 0, 200, 1],
28805        max: [30, 0, 200, 1],
28806        maxVisible: [30, 0, 200, 1],
28807        drawThreshold: [5, 0, 20, 1]
28808      }
28809    },
28810    shadow: {
28811      enabled: false,
28812      color: 'rgba(0,0,0,0.5)',
28813      size: [10, 0, 20, 1],
28814      x: [5, -30, 30, 1],
28815      y: [5, -30, 30, 1]
28816    },
28817    shape: ['ellipse', 'box', 'circle', 'database', 'diamond', 'dot', 'square', 'star', 'text', 'triangle', 'triangleDown', 'hexagon'],
28818    shapeProperties: {
28819      borderDashes: false,
28820      borderRadius: [6, 0, 20, 1],
28821      interpolation: true,
28822      useImageSize: false
28823    },
28824    size: [25, 0, 200, 1]
28825  },
28826  edges: {
28827    arrows: {
28828      to: { enabled: false, scaleFactor: [1, 0, 3, 0.05], type: 'arrow' },
28829      middle: { enabled: false, scaleFactor: [1, 0, 3, 0.05], type: 'arrow' },
28830      from: { enabled: false, scaleFactor: [1, 0, 3, 0.05], type: 'arrow' }
28831    },
28832    arrowStrikethrough: true,
28833    color: {
28834      color: ['color', '#848484'],
28835      highlight: ['color', '#848484'],
28836      hover: ['color', '#848484'],
28837      inherit: ['from', 'to', 'both', true, false],
28838      opacity: [1, 0, 1, 0.05]
28839    },
28840    dashes: false,
28841    font: {
28842      color: ['color', '#343434'],
28843      size: [14, 0, 100, 1], // px
28844      face: ['arial', 'verdana', 'tahoma'],
28845      background: ['color', 'none'],
28846      strokeWidth: [2, 0, 50, 1], // px
28847      strokeColor: ['color', '#ffffff'],
28848      align: ['horizontal', 'top', 'middle', 'bottom']
28849    },
28850    hidden: false,
28851    hoverWidth: [1.5, 0, 5, 0.1],
28852    labelHighlightBold: true,
28853    physics: true,
28854    scaling: {
28855      min: [1, 0, 100, 1],
28856      max: [15, 0, 100, 1],
28857      label: {
28858        enabled: true,
28859        min: [14, 0, 200, 1],
28860        max: [30, 0, 200, 1],
28861        maxVisible: [30, 0, 200, 1],
28862        drawThreshold: [5, 0, 20, 1]
28863      }
28864    },
28865    selectionWidth: [1.5, 0, 5, 0.1],
28866    selfReferenceSize: [20, 0, 200, 1],
28867    shadow: {
28868      enabled: false,
28869      color: 'rgba(0,0,0,0.5)',
28870      size: [10, 0, 20, 1],
28871      x: [5, -30, 30, 1],
28872      y: [5, -30, 30, 1]
28873    },
28874    smooth: {
28875      enabled: true,
28876      type: ['dynamic', 'continuous', 'discrete', 'diagonalCross', 'straightCross', 'horizontal', 'vertical', 'curvedCW', 'curvedCCW', 'cubicBezier'],
28877      forceDirection: ['horizontal', 'vertical', 'none'],
28878      roundness: [0.5, 0, 1, 0.05]
28879    },
28880    width: [1, 0, 30, 1]
28881  },
28882  layout: {
28883    //randomSeed: [0, 0, 500, 1],
28884    //improvedLayout: true,
28885    hierarchical: {
28886      enabled: false,
28887      levelSeparation: [150, 20, 500, 5],
28888      nodeSpacing: [100, 20, 500, 5],
28889      treeSpacing: [200, 20, 500, 5],
28890      blockShifting: true,
28891      edgeMinimization: true,
28892      parentCentralization: true,
28893      direction: ['UD', 'DU', 'LR', 'RL'], // UD, DU, LR, RL
28894      sortMethod: ['hubsize', 'directed'] // hubsize, directed
28895    }
28896  },
28897  interaction: {
28898    dragNodes: true,
28899    dragView: true,
28900    hideEdgesOnDrag: false,
28901    hideNodesOnDrag: false,
28902    hover: false,
28903    keyboard: {
28904      enabled: false,
28905      speed: { x: [10, 0, 40, 1], y: [10, 0, 40, 1], zoom: [0.02, 0, 0.1, 0.005] },
28906      bindToWindow: true
28907    },
28908    multiselect: false,
28909    navigationButtons: false,
28910    selectable: true,
28911    selectConnectedEdges: true,
28912    hoverConnectedEdges: true,
28913    tooltipDelay: [300, 0, 1000, 25],
28914    zoomView: true
28915  },
28916  manipulation: {
28917    enabled: false,
28918    initiallyActive: false
28919  },
28920  physics: {
28921    enabled: true,
28922    barnesHut: {
28923      //theta: [0.5, 0.1, 1, 0.05],
28924      gravitationalConstant: [-2000, -30000, 0, 50],
28925      centralGravity: [0.3, 0, 10, 0.05],
28926      springLength: [95, 0, 500, 5],
28927      springConstant: [0.04, 0, 1.2, 0.005],
28928      damping: [0.09, 0, 1, 0.01],
28929      avoidOverlap: [0, 0, 1, 0.01]
28930    },
28931    forceAtlas2Based: {
28932      //theta: [0.5, 0.1, 1, 0.05],
28933      gravitationalConstant: [-50, -500, 0, 1],
28934      centralGravity: [0.01, 0, 1, 0.005],
28935      springLength: [95, 0, 500, 5],
28936      springConstant: [0.08, 0, 1.2, 0.005],
28937      damping: [0.4, 0, 1, 0.01],
28938      avoidOverlap: [0, 0, 1, 0.01]
28939    },
28940    repulsion: {
28941      centralGravity: [0.2, 0, 10, 0.05],
28942      springLength: [200, 0, 500, 5],
28943      springConstant: [0.05, 0, 1.2, 0.005],
28944      nodeDistance: [100, 0, 500, 5],
28945      damping: [0.09, 0, 1, 0.01]
28946    },
28947    hierarchicalRepulsion: {
28948      centralGravity: [0.2, 0, 10, 0.05],
28949      springLength: [100, 0, 500, 5],
28950      springConstant: [0.01, 0, 1.2, 0.005],
28951      nodeDistance: [120, 0, 500, 5],
28952      damping: [0.09, 0, 1, 0.01]
28953    },
28954    maxVelocity: [50, 0, 150, 1],
28955    minVelocity: [0.1, 0.01, 0.5, 0.01],
28956    solver: ['barnesHut', 'forceAtlas2Based', 'repulsion', 'hierarchicalRepulsion'],
28957    timestep: [0.5, 0.01, 1, 0.01]
28958    //adaptiveTimestep: true
28959  }
28960};
28961
28962exports.allOptions = allOptions;
28963exports.configureOptions = configureOptions;
28964
28965/***/ }),
28966/* 123 */
28967/***/ (function(module, exports, __webpack_require__) {
28968
28969"use strict";
28970
28971
28972var util = __webpack_require__(2);
28973
28974// Graph3d
28975util.extend(exports, __webpack_require__(159));
28976
28977// Timeline & Graph2d
28978util.extend(exports, __webpack_require__(177));
28979
28980// Network
28981util.extend(exports, __webpack_require__(181));
28982
28983/***/ }),
28984/* 124 */
28985/***/ (function(module, exports, __webpack_require__) {
28986
28987__webpack_require__(49);
28988__webpack_require__(60);
28989module.exports = __webpack_require__(136);
28990
28991
28992/***/ }),
28993/* 125 */
28994/***/ (function(module, exports, __webpack_require__) {
28995
28996"use strict";
28997
28998var addToUnscopables = __webpack_require__(126);
28999var step = __webpack_require__(127);
29000var Iterators = __webpack_require__(31);
29001var toIObject = __webpack_require__(25);
29002
29003// 22.1.3.4 Array.prototype.entries()
29004// 22.1.3.13 Array.prototype.keys()
29005// 22.1.3.29 Array.prototype.values()
29006// 22.1.3.30 Array.prototype[@@iterator]()
29007module.exports = __webpack_require__(79)(Array, 'Array', function (iterated, kind) {
29008  this._t = toIObject(iterated); // target
29009  this._i = 0;                   // next index
29010  this._k = kind;                // kind
29011// 22.1.5.2.1 %ArrayIteratorPrototype%.next()
29012}, function () {
29013  var O = this._t;
29014  var kind = this._k;
29015  var index = this._i++;
29016  if (!O || index >= O.length) {
29017    this._t = undefined;
29018    return step(1);
29019  }
29020  if (kind == 'keys') return step(0, index);
29021  if (kind == 'values') return step(0, O[index]);
29022  return step(0, [index, O[index]]);
29023}, 'values');
29024
29025// argumentsList[@@iterator] is %ArrayProto_values% (9.4.4.6, 9.4.4.7)
29026Iterators.Arguments = Iterators.Array;
29027
29028addToUnscopables('keys');
29029addToUnscopables('values');
29030addToUnscopables('entries');
29031
29032
29033/***/ }),
29034/* 126 */
29035/***/ (function(module, exports) {
29036
29037module.exports = function () { /* empty */ };
29038
29039
29040/***/ }),
29041/* 127 */
29042/***/ (function(module, exports) {
29043
29044module.exports = function (done, value) {
29045  return { value: value, done: !!done };
29046};
29047
29048
29049/***/ }),
29050/* 128 */
29051/***/ (function(module, exports) {
29052
29053module.exports = function (it) {
29054  if (typeof it != 'function') throw TypeError(it + ' is not a function!');
29055  return it;
29056};
29057
29058
29059/***/ }),
29060/* 129 */
29061/***/ (function(module, exports, __webpack_require__) {
29062
29063"use strict";
29064
29065var create = __webpack_require__(54);
29066var descriptor = __webpack_require__(39);
29067var setToStringTag = __webpack_require__(59);
29068var IteratorPrototype = {};
29069
29070// 25.1.2.1.1 %IteratorPrototype%[@@iterator]()
29071__webpack_require__(26)(IteratorPrototype, __webpack_require__(13)('iterator'), function () { return this; });
29072
29073module.exports = function (Constructor, NAME, next) {
29074  Constructor.prototype = create(IteratorPrototype, { next: descriptor(1, next) });
29075  setToStringTag(Constructor, NAME + ' Iterator');
29076};
29077
29078
29079/***/ }),
29080/* 130 */
29081/***/ (function(module, exports, __webpack_require__) {
29082
29083var dP = __webpack_require__(20);
29084var anObject = __webpack_require__(27);
29085var getKeys = __webpack_require__(33);
29086
29087module.exports = __webpack_require__(21) ? Object.defineProperties : function defineProperties(O, Properties) {
29088  anObject(O);
29089  var keys = getKeys(Properties);
29090  var length = keys.length;
29091  var i = 0;
29092  var P;
29093  while (length > i) dP.f(O, P = keys[i++], Properties[P]);
29094  return O;
29095};
29096
29097
29098/***/ }),
29099/* 131 */
29100/***/ (function(module, exports, __webpack_require__) {
29101
29102// false -> Array#indexOf
29103// true  -> Array#includes
29104var toIObject = __webpack_require__(25);
vendor: 12,517 bytes, lines 29105-29496
29105var toLength = __webpack_require__(132);
29106var toAbsoluteIndex = __webpack_require__(133);
29107module.exports = function (IS_INCLUDES) {
29108  return function ($this, el, fromIndex) {
29109    var O = toIObject($this);
29110    var length = toLength(O.length);
29111    var index = toAbsoluteIndex(fromIndex, length);
29112    var value;
29113    // Array#includes uses SameValueZero equality algorithm
29114    // eslint-disable-next-line no-self-compare
29115    if (IS_INCLUDES && el != el) while (length > index) {
29116      value = O[index++];
29117      // eslint-disable-next-line no-self-compare
29118      if (value != value) return true;
29119    // Array#indexOf ignores holes, Array#includes - not
29120    } else for (;length > index; index++) if (IS_INCLUDES || index in O) {
29121      if (O[index] === el) return IS_INCLUDES || index || 0;
29122    } return !IS_INCLUDES && -1;
29123  };
29124};
29125
29126
29127/***/ }),
29128/* 132 */
29129/***/ (function(module, exports, __webpack_require__) {
29130
29131// 7.1.15 ToLength
29132var toInteger = __webpack_require__(55);
29133var min = Math.min;
29134module.exports = function (it) {
29135  return it > 0 ? min(toInteger(it), 0x1fffffffffffff) : 0; // pow(2, 53) - 1 == 9007199254740991
29136};
29137
29138
29139/***/ }),
29140/* 133 */
29141/***/ (function(module, exports, __webpack_require__) {
29142
29143var toInteger = __webpack_require__(55);
29144var max = Math.max;
29145var min = Math.min;
29146module.exports = function (index, length) {
29147  index = toInteger(index);
29148  return index < 0 ? max(index + length, 0) : min(index, length);
29149};
29150
29151
29152/***/ }),
29153/* 134 */
29154/***/ (function(module, exports, __webpack_require__) {
29155
29156var document = __webpack_require__(18).document;
29157module.exports = document && document.documentElement;
29158
29159
29160/***/ }),
29161/* 135 */
29162/***/ (function(module, exports, __webpack_require__) {
29163
29164var toInteger = __webpack_require__(55);
29165var defined = __webpack_require__(51);
29166// true  -> String#at
29167// false -> String#codePointAt
29168module.exports = function (TO_STRING) {
29169  return function (that, pos) {
29170    var s = String(defined(that));
29171    var i = toInteger(pos);
29172    var l = s.length;
29173    var a, b;
29174    if (i < 0 || i >= l) return TO_STRING ? '' : undefined;
29175    a = s.charCodeAt(i);
29176    return a < 0xd800 || a > 0xdbff || i + 1 === l || (b = s.charCodeAt(i + 1)) < 0xdc00 || b > 0xdfff
29177      ? TO_STRING ? s.charAt(i) : a
29178      : TO_STRING ? s.slice(i, i + 2) : (a - 0xd800 << 10) + (b - 0xdc00) + 0x10000;
29179  };
29180};
29181
29182
29183/***/ }),
29184/* 136 */
29185/***/ (function(module, exports, __webpack_require__) {
29186
29187var anObject = __webpack_require__(27);
29188var get = __webpack_require__(137);
29189module.exports = __webpack_require__(7).getIterator = function (it) {
29190  var iterFn = get(it);
29191  if (typeof iterFn != 'function') throw TypeError(it + ' is not iterable!');
29192  return anObject(iterFn.call(it));
29193};
29194
29195
29196/***/ }),
29197/* 137 */
29198/***/ (function(module, exports, __webpack_require__) {
29199
29200var classof = __webpack_require__(86);
29201var ITERATOR = __webpack_require__(13)('iterator');
29202var Iterators = __webpack_require__(31);
29203module.exports = __webpack_require__(7).getIteratorMethod = function (it) {
29204  if (it != undefined) return it[ITERATOR]
29205    || it['@@iterator']
29206    || Iterators[classof(it)];
29207};
29208
29209
29210/***/ }),
29211/* 138 */
29212/***/ (function(module, exports, __webpack_require__) {
29213
29214__webpack_require__(139);
29215var $Object = __webpack_require__(7).Object;
29216module.exports = function create(P, D) {
29217  return $Object.create(P, D);
29218};
29219
29220
29221/***/ }),
29222/* 139 */
29223/***/ (function(module, exports, __webpack_require__) {
29224
29225var $export = __webpack_require__(17);
29226// 19.1.2.2 / 15.2.3.5 Object.create(O [, Properties])
29227$export($export.S, 'Object', { create: __webpack_require__(54) });
29228
29229
29230/***/ }),
29231/* 140 */
29232/***/ (function(module, exports, __webpack_require__) {
29233
29234__webpack_require__(141);
29235module.exports = __webpack_require__(7).Object.keys;
29236
29237
29238/***/ }),
29239/* 141 */
29240/***/ (function(module, exports, __webpack_require__) {
29241
29242// 19.1.2.14 Object.keys(O)
29243var toObject = __webpack_require__(41);
29244var $keys = __webpack_require__(33);
29245
29246__webpack_require__(87)('keys', function () {
29247  return function keys(it) {
29248    return $keys(toObject(it));
29249  };
29250});
29251
29252
29253/***/ }),
29254/* 142 */
29255/***/ (function(module, exports, __webpack_require__) {
29256
29257module.exports = { "default": __webpack_require__(143), __esModule: true };
29258
29259/***/ }),
29260/* 143 */
29261/***/ (function(module, exports, __webpack_require__) {
29262
29263__webpack_require__(60);
29264__webpack_require__(49);
29265module.exports = __webpack_require__(61).f('iterator');
29266
29267
29268/***/ }),
29269/* 144 */
29270/***/ (function(module, exports, __webpack_require__) {
29271
29272module.exports = { "default": __webpack_require__(145), __esModule: true };
29273
29274/***/ }),
29275/* 145 */
29276/***/ (function(module, exports, __webpack_require__) {
29277
29278__webpack_require__(146);
29279__webpack_require__(151);
29280__webpack_require__(152);
29281__webpack_require__(153);
29282module.exports = __webpack_require__(7).Symbol;
29283
29284
29285/***/ }),
29286/* 146 */
29287/***/ (function(module, exports, __webpack_require__) {
29288
29289"use strict";
29290
29291// ECMAScript 6 symbols shim
29292var global = __webpack_require__(18);
29293var has = __webpack_require__(22);
29294var DESCRIPTORS = __webpack_require__(21);
29295var $export = __webpack_require__(17);
29296var redefine = __webpack_require__(83);
29297var META = __webpack_require__(147).KEY;
29298var $fails = __webpack_require__(28);
29299var shared = __webpack_require__(57);
29300var setToStringTag = __webpack_require__(59);
29301var uid = __webpack_require__(40);
29302var wks = __webpack_require__(13);
29303var wksExt = __webpack_require__(61);
29304var wksDefine = __webpack_require__(62);
29305var enumKeys = __webpack_require__(148);
29306var isArray = __webpack_require__(149);
29307var anObject = __webpack_require__(27);
29308var toIObject = __webpack_require__(25);
29309var toPrimitive = __webpack_require__(53);
29310var createDesc = __webpack_require__(39);
29311var _create = __webpack_require__(54);
29312var gOPNExt = __webpack_require__(150);
29313var $GOPD = __webpack_require__(89);
29314var $DP = __webpack_require__(20);
29315var $keys = __webpack_require__(33);
29316var gOPD = $GOPD.f;
29317var dP = $DP.f;
29318var gOPN = gOPNExt.f;
29319var $Symbol = global.Symbol;
29320var $JSON = global.JSON;
29321var _stringify = $JSON && $JSON.stringify;
29322var PROTOTYPE = 'prototype';
29323var HIDDEN = wks('_hidden');
29324var TO_PRIMITIVE = wks('toPrimitive');
29325var isEnum = {}.propertyIsEnumerable;
29326var SymbolRegistry = shared('symbol-registry');
29327var AllSymbols = shared('symbols');
29328var OPSymbols = shared('op-symbols');
29329var ObjectProto = Object[PROTOTYPE];
29330var USE_NATIVE = typeof $Symbol == 'function';
29331var QObject = global.QObject;
29332// Don't use setters in Qt Script, https://github.com/zloirock/core-js/issues/173
29333var setter = !QObject || !QObject[PROTOTYPE] || !QObject[PROTOTYPE].findChild;
29334
29335// fallback for old Android, https://code.google.com/p/v8/issues/detail?id=687
29336var setSymbolDesc = DESCRIPTORS && $fails(function () {
29337  return _create(dP({}, 'a', {
29338    get: function () { return dP(this, 'a', { value: 7 }).a; }
29339  })).a != 7;
29340}) ? function (it, key, D) {
29341  var protoDesc = gOPD(ObjectProto, key);
29342  if (protoDesc) delete ObjectProto[key];
29343  dP(it, key, D);
29344  if (protoDesc && it !== ObjectProto) dP(ObjectProto, key, protoDesc);
29345} : dP;
29346
29347var wrap = function (tag) {
29348  var sym = AllSymbols[tag] = _create($Symbol[PROTOTYPE]);
29349  sym._k = tag;
29350  return sym;
29351};
29352
29353var isSymbol = USE_NATIVE && typeof $Symbol.iterator == 'symbol' ? function (it) {
29354  return typeof it == 'symbol';
29355} : function (it) {
29356  return it instanceof $Symbol;
29357};
29358
29359var $defineProperty = function defineProperty(it, key, D) {
29360  if (it === ObjectProto) $defineProperty(OPSymbols, key, D);
29361  anObject(it);
29362  key = toPrimitive(key, true);
29363  anObject(D);
29364  if (has(AllSymbols, key)) {
29365    if (!D.enumerable) {
29366      if (!has(it, HIDDEN)) dP(it, HIDDEN, createDesc(1, {}));
29367      it[HIDDEN][key] = true;
29368    } else {
29369      if (has(it, HIDDEN) && it[HIDDEN][key]) it[HIDDEN][key] = false;
29370      D = _create(D, { enumerable: createDesc(0, false) });
29371    } return setSymbolDesc(it, key, D);
29372  } return dP(it, key, D);
29373};
29374var $defineProperties = function defineProperties(it, P) {
29375  anObject(it);
29376  var keys = enumKeys(P = toIObject(P));
29377  var i = 0;
29378  var l = keys.length;
29379  var key;
29380  while (l > i) $defineProperty(it, key = keys[i++], P[key]);
29381  return it;
29382};
29383var $create = function create(it, P) {
29384  return P === undefined ? _create(it) : $defineProperties(_create(it), P);
29385};
29386var $propertyIsEnumerable = function propertyIsEnumerable(key) {
29387  var E = isEnum.call(this, key = toPrimitive(key, true));
29388  if (this === ObjectProto && has(AllSymbols, key) && !has(OPSymbols, key)) return false;
29389  return E || !has(this, key) || !has(AllSymbols, key) || has(this, HIDDEN) && this[HIDDEN][key] ? E : true;
29390};
29391var $getOwnPropertyDescriptor = function getOwnPropertyDescriptor(it, key) {
29392  it = toIObject(it);
29393  key = toPrimitive(key, true);
29394  if (it === ObjectProto && has(AllSymbols, key) && !has(OPSymbols, key)) return;
29395  var D = gOPD(it, key);
29396  if (D && has(AllSymbols, key) && !(has(it, HIDDEN) && it[HIDDEN][key])) D.enumerable = true;
29397  return D;
29398};
29399var $getOwnPropertyNames = function getOwnPropertyNames(it) {
29400  var names = gOPN(toIObject(it));
29401  var result = [];
29402  var i = 0;
29403  var key;
29404  while (names.length > i) {
29405    if (!has(AllSymbols, key = names[i++]) && key != HIDDEN && key != META) result.push(key);
29406  } return result;
29407};
29408var $getOwnPropertySymbols = function getOwnPropertySymbols(it) {
29409  var IS_OP = it === ObjectProto;
29410  var names = gOPN(IS_OP ? OPSymbols : toIObject(it));
29411  var result = [];
29412  var i = 0;
29413  var key;
29414  while (names.length > i) {
29415    if (has(AllSymbols, key = names[i++]) && (IS_OP ? has(ObjectProto, key) : true)) result.push(AllSymbols[key]);
29416  } return result;
29417};
29418
29419// 19.4.1.1 Symbol([description])
29420if (!USE_NATIVE) {
29421  $Symbol = function Symbol() {
29422    if (this instanceof $Symbol) throw TypeError('Symbol is not a constructor!');
29423    var tag = uid(arguments.length > 0 ? arguments[0] : undefined);
29424    var $set = function (value) {
29425      if (this === ObjectProto) $set.call(OPSymbols, value);
29426      if (has(this, HIDDEN) && has(this[HIDDEN], tag)) this[HIDDEN][tag] = false;
29427      setSymbolDesc(this, tag, createDesc(1, value));
29428    };
29429    if (DESCRIPTORS && setter) setSymbolDesc(ObjectProto, tag, { configurable: true, set: $set });
29430    return wrap(tag);
29431  };
29432  redefine($Symbol[PROTOTYPE], 'toString', function toString() {
29433    return this._k;
29434  });
29435
29436  $GOPD.f = $getOwnPropertyDescriptor;
29437  $DP.f = $defineProperty;
29438  __webpack_require__(88).f = gOPNExt.f = $getOwnPropertyNames;
29439  __webpack_require__(42).f = $propertyIsEnumerable;
29440  __webpack_require__(63).f = $getOwnPropertySymbols;
29441
29442  if (DESCRIPTORS && !__webpack_require__(52)) {
29443    redefine(ObjectProto, 'propertyIsEnumerable', $propertyIsEnumerable, true);
29444  }
29445
29446  wksExt.f = function (name) {
29447    return wrap(wks(name));
29448  };
29449}
29450
29451$export($export.G + $export.W + $export.F * !USE_NATIVE, { Symbol: $Symbol });
29452
29453for (var es6Symbols = (
29454  // 19.4.2.2, 19.4.2.3, 19.4.2.4, 19.4.2.6, 19.4.2.8, 19.4.2.9, 19.4.2.10, 19.4.2.11, 19.4.2.12, 19.4.2.13, 19.4.2.14
29455  'hasInstance,isConcatSpreadable,iterator,match,replace,search,species,split,toPrimitive,toStringTag,unscopables'
29456).split(','), j = 0; es6Symbols.length > j;)wks(es6Symbols[j++]);
29457
29458for (var wellKnownSymbols = $keys(wks.store), k = 0; wellKnownSymbols.length > k;) wksDefine(wellKnownSymbols[k++]);
29459
29460$export($export.S + $export.F * !USE_NATIVE, 'Symbol', {
29461  // 19.4.2.1 Symbol.for(key)
29462  'for': function (key) {
29463    return has(SymbolRegistry, key += '')
29464      ? SymbolRegistry[key]
29465      : SymbolRegistry[key] = $Symbol(key);
29466  },
29467  // 19.4.2.5 Symbol.keyFor(sym)
29468  keyFor: function keyFor(sym) {
29469    if (!isSymbol(sym)) throw TypeError(sym + ' is not a symbol!');
29470    for (var key in SymbolRegistry) if (SymbolRegistry[key] === sym) return key;
29471  },
29472  useSetter: function () { setter = true; },
29473  useSimple: function () { setter = false; }
29474});
29475
29476$export($export.S + $export.F * !USE_NATIVE, 'Object', {
29477  // 19.1.2.2 Object.create(O [, Properties])
29478  create: $create,
29479  // 19.1.2.4 Object.defineProperty(O, P, Attributes)
29480  defineProperty: $defineProperty,
29481  // 19.1.2.3 Object.defineProperties(O, Properties)
29482  defineProperties: $defineProperties,
29483  // 19.1.2.6 Object.getOwnPropertyDescriptor(O, P)
29484  getOwnPropertyDescriptor: $getOwnPropertyDescriptor,
29485  // 19.1.2.7 Object.getOwnPropertyNames(O)
29486  getOwnPropertyNames: $getOwnPropertyNames,
29487  // 19.1.2.8 Object.getOwnPropertySymbols(O)
29488  getOwnPropertySymbols: $getOwnPropertySymbols
29489});
29490
29491// 24.3.2 JSON.stringify(value [, replacer [, space]])
29492$JSON && $export($export.S + $export.F * (!USE_NATIVE || $fails(function () {
29493  var S = $Symbol();
29494  // MS Edge converts symbol values to JSON as {}
29495  // WebKit converts symbol values to JSON as null
29496  // V8 throws on boxed symbols
vendor: 4,237 bytes, lines 29497-29643
29497  return _stringify([S]) != '[null]' || _stringify({ a: S }) != '{}' || _stringify(Object(S)) != '{}';
29498})), 'JSON', {
29499  stringify: function stringify(it) {
29500    if (it === undefined || isSymbol(it)) return; // IE8 returns string on undefined
29501    var args = [it];
29502    var i = 1;
29503    var replacer, $replacer;
29504    while (arguments.length > i) args.push(arguments[i++]);
29505    replacer = args[1];
29506    if (typeof replacer == 'function') $replacer = replacer;
29507    if ($replacer || !isArray(replacer)) replacer = function (key, value) {
29508      if ($replacer) value = $replacer.call(this, key, value);
29509      if (!isSymbol(value)) return value;
29510    };
29511    args[1] = replacer;
29512    return _stringify.apply($JSON, args);
29513  }
29514});
29515
29516// 19.4.3.4 Symbol.prototype[@@toPrimitive](hint)
29517$Symbol[PROTOTYPE][TO_PRIMITIVE] || __webpack_require__(26)($Symbol[PROTOTYPE], TO_PRIMITIVE, $Symbol[PROTOTYPE].valueOf);
29518// 19.4.3.5 Symbol.prototype[@@toStringTag]
29519setToStringTag($Symbol, 'Symbol');
29520// 20.2.1.9 Math[@@toStringTag]
29521setToStringTag(Math, 'Math', true);
29522// 24.3.3 JSON[@@toStringTag]
29523setToStringTag(global.JSON, 'JSON', true);
29524
29525
29526/***/ }),
29527/* 147 */
29528/***/ (function(module, exports, __webpack_require__) {
29529
29530var META = __webpack_require__(40)('meta');
29531var isObject = __webpack_require__(32);
29532var has = __webpack_require__(22);
29533var setDesc = __webpack_require__(20).f;
29534var id = 0;
29535var isExtensible = Object.isExtensible || function () {
29536  return true;
29537};
29538var FREEZE = !__webpack_require__(28)(function () {
29539  return isExtensible(Object.preventExtensions({}));
29540});
29541var setMeta = function (it) {
29542  setDesc(it, META, { value: {
29543    i: 'O' + ++id, // object ID
29544    w: {}          // weak collections IDs
29545  } });
29546};
29547var fastKey = function (it, create) {
29548  // return primitive with prefix
29549  if (!isObject(it)) return typeof it == 'symbol' ? it : (typeof it == 'string' ? 'S' : 'P') + it;
29550  if (!has(it, META)) {
29551    // can't set metadata to uncaught frozen object
29552    if (!isExtensible(it)) return 'F';
29553    // not necessary to add metadata
29554    if (!create) return 'E';
29555    // add missing metadata
29556    setMeta(it);
29557  // return object ID
29558  } return it[META].i;
29559};
29560var getWeak = function (it, create) {
29561  if (!has(it, META)) {
29562    // can't set metadata to uncaught frozen object
29563    if (!isExtensible(it)) return true;
29564    // not necessary to add metadata
29565    if (!create) return false;
29566    // add missing metadata
29567    setMeta(it);
29568  // return hash weak collections IDs
29569  } return it[META].w;
29570};
29571// add metadata on freeze-family methods calling
29572var onFreeze = function (it) {
29573  if (FREEZE && meta.NEED && isExtensible(it) && !has(it, META)) setMeta(it);
29574  return it;
29575};
29576var meta = module.exports = {
29577  KEY: META,
29578  NEED: false,
29579  fastKey: fastKey,
29580  getWeak: getWeak,
29581  onFreeze: onFreeze
29582};
29583
29584
29585/***/ }),
29586/* 148 */
29587/***/ (function(module, exports, __webpack_require__) {
29588
29589// all enumerable object keys, includes symbols
29590var getKeys = __webpack_require__(33);
29591var gOPS = __webpack_require__(63);
29592var pIE = __webpack_require__(42);
29593module.exports = function (it) {
29594  var result = getKeys(it);
29595  var getSymbols = gOPS.f;
29596  if (getSymbols) {
29597    var symbols = getSymbols(it);
29598    var isEnum = pIE.f;
29599    var i = 0;
29600    var key;
29601    while (symbols.length > i) if (isEnum.call(it, key = symbols[i++])) result.push(key);
29602  } return result;
29603};
29604
29605
29606/***/ }),
29607/* 149 */
29608/***/ (function(module, exports, __webpack_require__) {
29609
29610// 7.2.2 IsArray(argument)
29611var cof = __webpack_require__(50);
29612module.exports = Array.isArray || function isArray(arg) {
29613  return cof(arg) == 'Array';
29614};
29615
29616
29617/***/ }),
29618/* 150 */
29619/***/ (function(module, exports, __webpack_require__) {
29620
29621// fallback for IE11 buggy Object.getOwnPropertyNames with iframe and window
29622var toIObject = __webpack_require__(25);
29623var gOPN = __webpack_require__(88).f;
29624var toString = {}.toString;
29625
29626var windowNames = typeof window == 'object' && window && Object.getOwnPropertyNames
29627  ? Object.getOwnPropertyNames(window) : [];
29628
29629var getWindowNames = function (it) {
29630  try {
29631    return gOPN(it);
29632  } catch (e) {
29633    return windowNames.slice();
29634  }
29635};
29636
29637module.exports.f = function getOwnPropertyNames(it) {
29638  return windowNames && toString.call(it) == '[object Window]' ? getWindowNames(it) : gOPN(toIObject(it));
29639};
29640
29641
29642/***/ }),
29643/* 151 */
vendor: 6,171 bytes, lines 29644-29902
29644/***/ (function(module, exports) {
29645
29646
29647
29648/***/ }),
29649/* 152 */
29650/***/ (function(module, exports, __webpack_require__) {
29651
29652__webpack_require__(62)('asyncIterator');
29653
29654
29655/***/ }),
29656/* 153 */
29657/***/ (function(module, exports, __webpack_require__) {
29658
29659__webpack_require__(62)('observable');
29660
29661
29662/***/ }),
29663/* 154 */
29664/***/ (function(module, exports, __webpack_require__) {
29665
29666/* WEBPACK VAR INJECTION */(function(module) {var require;//! moment.js
29667//! version : 2.19.1
29668//! authors : Tim Wood, Iskren Chernev, Moment.js contributors
29669//! license : MIT
29670//! momentjs.com
29671
29672;(function (global, factory) {
29673     true ? module.exports = factory() :
29674    typeof define === 'function' && define.amd ? define(factory) :
29675    global.moment = factory()
29676}(this, (function () { 'use strict';
29677
29678var hookCallback;
29679
29680function hooks () {
29681    return hookCallback.apply(null, arguments);
29682}
29683
29684// This is done to register the method called with moment()
29685// without creating circular dependencies.
29686function setHookCallback (callback) {
29687    hookCallback = callback;
29688}
29689
29690function isArray(input) {
29691    return input instanceof Array || Object.prototype.toString.call(input) === '[object Array]';
29692}
29693
29694function isObject(input) {
29695    // IE8 will treat undefined and null as object if it wasn't for
29696    // input != null
29697    return input != null && Object.prototype.toString.call(input) === '[object Object]';
29698}
29699
29700function isObjectEmpty(obj) {
29701    if (Object.getOwnPropertyNames) {
29702        return (Object.getOwnPropertyNames(obj).length === 0);
29703    } else {
29704        var k;
29705        for (k in obj) {
29706            if (obj.hasOwnProperty(k)) {
29707                return false;
29708            }
29709        }
29710        return true;
29711    }
29712}
29713
29714function isUndefined(input) {
29715    return input === void 0;
29716}
29717
29718function isNumber(input) {
29719    return typeof input === 'number' || Object.prototype.toString.call(input) === '[object Number]';
29720}
29721
29722function isDate(input) {
29723    return input instanceof Date || Object.prototype.toString.call(input) === '[object Date]';
29724}
29725
29726function map(arr, fn) {
29727    var res = [], i;
29728    for (i = 0; i < arr.length; ++i) {
29729        res.push(fn(arr[i], i));
29730    }
29731    return res;
29732}
29733
29734function hasOwnProp(a, b) {
29735    return Object.prototype.hasOwnProperty.call(a, b);
29736}
29737
29738function extend(a, b) {
29739    for (var i in b) {
29740        if (hasOwnProp(b, i)) {
29741            a[i] = b[i];
29742        }
29743    }
29744
29745    if (hasOwnProp(b, 'toString')) {
29746        a.toString = b.toString;
29747    }
29748
29749    if (hasOwnProp(b, 'valueOf')) {
29750        a.valueOf = b.valueOf;
29751    }
29752
29753    return a;
29754}
29755
29756function createUTC (input, format, locale, strict) {
29757    return createLocalOrUTC(input, format, locale, strict, true).utc();
29758}
29759
29760function defaultParsingFlags() {
29761    // We need to deep clone this object.
29762    return {
29763        empty           : false,
29764        unusedTokens    : [],
29765        unusedInput     : [],
29766        overflow        : -2,
29767        charsLeftOver   : 0,
29768        nullInput       : false,
29769        invalidMonth    : null,
29770        invalidFormat   : false,
29771        userInvalidated : false,
29772        iso             : false,
29773        parsedDateParts : [],
29774        meridiem        : null,
29775        rfc2822         : false,
29776        weekdayMismatch : false
29777    };
29778}
29779
29780function getParsingFlags(m) {
29781    if (m._pf == null) {
29782        m._pf = defaultParsingFlags();
29783    }
29784    return m._pf;
29785}
29786
29787var some;
29788if (Array.prototype.some) {
29789    some = Array.prototype.some;
29790} else {
29791    some = function (fun) {
29792        var t = Object(this);
29793        var len = t.length >>> 0;
29794
29795        for (var i = 0; i < len; i++) {
29796            if (i in t && fun.call(this, t[i], i, t)) {
29797                return true;
29798            }
29799        }
29800
29801        return false;
29802    };
29803}
29804
29805function isValid(m) {
29806    if (m._isValid == null) {
29807        var flags = getParsingFlags(m);
29808        var parsedParts = some.call(flags.parsedDateParts, function (i) {
29809            return i != null;
29810        });
29811        var isNowValid = !isNaN(m._d.getTime()) &&
29812            flags.overflow < 0 &&
29813            !flags.empty &&
29814            !flags.invalidMonth &&
29815            !flags.invalidWeekday &&
29816            !flags.weekdayMismatch &&
29817            !flags.nullInput &&
29818            !flags.invalidFormat &&
29819            !flags.userInvalidated &&
29820            (!flags.meridiem || (flags.meridiem && parsedParts));
29821
29822        if (m._strict) {
29823            isNowValid = isNowValid &&
29824                flags.charsLeftOver === 0 &&
29825                flags.unusedTokens.length === 0 &&
29826                flags.bigHour === undefined;
29827        }
29828
29829        if (Object.isFrozen == null || !Object.isFrozen(m)) {
29830            m._isValid = isNowValid;
29831        }
29832        else {
29833            return isNowValid;
29834        }
29835    }
29836    return m._isValid;
29837}
29838
29839function createInvalid (flags) {
29840    var m = createUTC(NaN);
29841    if (flags != null) {
29842        extend(getParsingFlags(m), flags);
29843    }
29844    else {
29845        getParsingFlags(m).userInvalidated = true;
29846    }
29847
29848    return m;
29849}
29850
29851// Plugins that add properties should also add the key here (null value),
29852// so we can properly clone ourselves.
29853var momentProperties = hooks.momentProperties = [];
29854
29855function copyConfig(to, from) {
29856    var i, prop, val;
29857
29858    if (!isUndefined(from._isAMomentObject)) {
29859        to._isAMomentObject = from._isAMomentObject;
29860    }
29861    if (!isUndefined(from._i)) {
29862        to._i = from._i;
29863    }
29864    if (!isUndefined(from._f)) {
29865        to._f = from._f;
29866    }
29867    if (!isUndefined(from._l)) {
29868        to._l = from._l;
29869    }
29870    if (!isUndefined(from._strict)) {
29871        to._strict = from._strict;
29872    }
29873    if (!isUndefined(from._tzm)) {
29874        to._tzm = from._tzm;
29875    }
29876    if (!isUndefined(from._isUTC)) {
29877        to._isUTC = from._isUTC;
29878    }
29879    if (!isUndefined(from._offset)) {
29880        to._offset = from._offset;
29881    }
29882    if (!isUndefined(from._pf)) {
29883        to._pf = getParsingFlags(from);
29884    }
29885    if (!isUndefined(from._locale)) {
29886        to._locale = from._locale;
29887    }
29888
29889    if (momentProperties.length > 0) {
29890        for (i = 0; i < momentProperties.length; i++) {
29891            prop = momentProperties[i];
29892            val = from[prop];
29893            if (!isUndefined(val)) {
29894                to[prop] = val;
29895            }
29896        }
29897    }
29898
29899    return to;
29900}
29901
29902var updateInProgress = false;
vendor: 60,756 bytes, lines 29903-31940
29903
29904// Moment prototype object
29905function Moment(config) {
29906    copyConfig(this, config);
29907    this._d = new Date(config._d != null ? config._d.getTime() : NaN);
29908    if (!this.isValid()) {
29909        this._d = new Date(NaN);
29910    }
29911    // Prevent infinite loop in case updateOffset creates new moment
29912    // objects.
29913    if (updateInProgress === false) {
29914        updateInProgress = true;
29915        hooks.updateOffset(this);
29916        updateInProgress = false;
29917    }
29918}
29919
29920function isMoment (obj) {
29921    return obj instanceof Moment || (obj != null && obj._isAMomentObject != null);
29922}
29923
29924function absFloor (number) {
29925    if (number < 0) {
29926        // -0 -> 0
29927        return Math.ceil(number) || 0;
29928    } else {
29929        return Math.floor(number);
29930    }
29931}
29932
29933function toInt(argumentForCoercion) {
29934    var coercedNumber = +argumentForCoercion,
29935        value = 0;
29936
29937    if (coercedNumber !== 0 && isFinite(coercedNumber)) {
29938        value = absFloor(coercedNumber);
29939    }
29940
29941    return value;
29942}
29943
29944// compare two arrays, return the number of differences
29945function compareArrays(array1, array2, dontConvert) {
29946    var len = Math.min(array1.length, array2.length),
29947        lengthDiff = Math.abs(array1.length - array2.length),
29948        diffs = 0,
29949        i;
29950    for (i = 0; i < len; i++) {
29951        if ((dontConvert && array1[i] !== array2[i]) ||
29952            (!dontConvert && toInt(array1[i]) !== toInt(array2[i]))) {
29953            diffs++;
29954        }
29955    }
29956    return diffs + lengthDiff;
29957}
29958
29959function warn(msg) {
29960    if (hooks.suppressDeprecationWarnings === false &&
29961            (typeof console !==  'undefined') && console.warn) {
29962        console.warn('Deprecation warning: ' + msg);
29963    }
29964}
29965
29966function deprecate(msg, fn) {
29967    var firstTime = true;
29968
29969    return extend(function () {
29970        if (hooks.deprecationHandler != null) {
29971            hooks.deprecationHandler(null, msg);
29972        }
29973        if (firstTime) {
29974            var args = [];
29975            var arg;
29976            for (var i = 0; i < arguments.length; i++) {
29977                arg = '';
29978                if (typeof arguments[i] === 'object') {
29979                    arg += '\n[' + i + '] ';
29980                    for (var key in arguments[0]) {
29981                        arg += key + ': ' + arguments[0][key] + ', ';
29982                    }
29983                    arg = arg.slice(0, -2); // Remove trailing comma and space
29984                } else {
29985                    arg = arguments[i];
29986                }
29987                args.push(arg);
29988            }
29989            warn(msg + '\nArguments: ' + Array.prototype.slice.call(args).join('') + '\n' + (new Error()).stack);
29990            firstTime = false;
29991        }
29992        return fn.apply(this, arguments);
29993    }, fn);
29994}
29995
29996var deprecations = {};
29997
29998function deprecateSimple(name, msg) {
29999    if (hooks.deprecationHandler != null) {
30000        hooks.deprecationHandler(name, msg);
30001    }
30002    if (!deprecations[name]) {
30003        warn(msg);
30004        deprecations[name] = true;
30005    }
30006}
30007
30008hooks.suppressDeprecationWarnings = false;
30009hooks.deprecationHandler = null;
30010
30011function isFunction(input) {
30012    return input instanceof Function || Object.prototype.toString.call(input) === '[object Function]';
30013}
30014
30015function set (config) {
30016    var prop, i;
30017    for (i in config) {
30018        prop = config[i];
30019        if (isFunction(prop)) {
30020            this[i] = prop;
30021        } else {
30022            this['_' + i] = prop;
30023        }
30024    }
30025    this._config = config;
30026    // Lenient ordinal parsing accepts just a number in addition to
30027    // number + (possibly) stuff coming from _dayOfMonthOrdinalParse.
30028    // TODO: Remove "ordinalParse" fallback in next major release.
30029    this._dayOfMonthOrdinalParseLenient = new RegExp(
30030        (this._dayOfMonthOrdinalParse.source || this._ordinalParse.source) +
30031            '|' + (/\d{1,2}/).source);
30032}
30033
30034function mergeConfigs(parentConfig, childConfig) {
30035    var res = extend({}, parentConfig), prop;
30036    for (prop in childConfig) {
30037        if (hasOwnProp(childConfig, prop)) {
30038            if (isObject(parentConfig[prop]) && isObject(childConfig[prop])) {
30039                res[prop] = {};
30040                extend(res[prop], parentConfig[prop]);
30041                extend(res[prop], childConfig[prop]);
30042            } else if (childConfig[prop] != null) {
30043                res[prop] = childConfig[prop];
30044            } else {
30045                delete res[prop];
30046            }
30047        }
30048    }
30049    for (prop in parentConfig) {
30050        if (hasOwnProp(parentConfig, prop) &&
30051                !hasOwnProp(childConfig, prop) &&
30052                isObject(parentConfig[prop])) {
30053            // make sure changes to properties don't modify parent config
30054            res[prop] = extend({}, res[prop]);
30055        }
30056    }
30057    return res;
30058}
30059
30060function Locale(config) {
30061    if (config != null) {
30062        this.set(config);
30063    }
30064}
30065
30066var keys;
30067
30068if (Object.keys) {
30069    keys = Object.keys;
30070} else {
30071    keys = function (obj) {
30072        var i, res = [];
30073        for (i in obj) {
30074            if (hasOwnProp(obj, i)) {
30075                res.push(i);
30076            }
30077        }
30078        return res;
30079    };
30080}
30081
30082var defaultCalendar = {
30083    sameDay : '[Today at] LT',
30084    nextDay : '[Tomorrow at] LT',
30085    nextWeek : 'dddd [at] LT',
30086    lastDay : '[Yesterday at] LT',
30087    lastWeek : '[Last] dddd [at] LT',
30088    sameElse : 'L'
30089};
30090
30091function calendar (key, mom, now) {
30092    var output = this._calendar[key] || this._calendar['sameElse'];
30093    return isFunction(output) ? output.call(mom, now) : output;
30094}
30095
30096var defaultLongDateFormat = {
30097    LTS  : 'h:mm:ss A',
30098    LT   : 'h:mm A',
30099    L    : 'MM/DD/YYYY',
30100    LL   : 'MMMM D, YYYY',
30101    LLL  : 'MMMM D, YYYY h:mm A',
30102    LLLL : 'dddd, MMMM D, YYYY h:mm A'
30103};
30104
30105function longDateFormat (key) {
30106    var format = this._longDateFormat[key],
30107        formatUpper = this._longDateFormat[key.toUpperCase()];
30108
30109    if (format || !formatUpper) {
30110        return format;
30111    }
30112
30113    this._longDateFormat[key] = formatUpper.replace(/MMMM|MM|DD|dddd/g, function (val) {
30114        return val.slice(1);
30115    });
30116
30117    return this._longDateFormat[key];
30118}
30119
30120var defaultInvalidDate = 'Invalid date';
30121
30122function invalidDate () {
30123    return this._invalidDate;
30124}
30125
30126var defaultOrdinal = '%d';
30127var defaultDayOfMonthOrdinalParse = /\d{1,2}/;
30128
30129function ordinal (number) {
30130    return this._ordinal.replace('%d', number);
30131}
30132
30133var defaultRelativeTime = {
30134    future : 'in %s',
30135    past   : '%s ago',
30136    s  : 'a few seconds',
30137    ss : '%d seconds',
30138    m  : 'a minute',
30139    mm : '%d minutes',
30140    h  : 'an hour',
30141    hh : '%d hours',
30142    d  : 'a day',
30143    dd : '%d days',
30144    M  : 'a month',
30145    MM : '%d months',
30146    y  : 'a year',
30147    yy : '%d years'
30148};
30149
30150function relativeTime (number, withoutSuffix, string, isFuture) {
30151    var output = this._relativeTime[string];
30152    return (isFunction(output)) ?
30153        output(number, withoutSuffix, string, isFuture) :
30154        output.replace(/%d/i, number);
30155}
30156
30157function pastFuture (diff, output) {
30158    var format = this._relativeTime[diff > 0 ? 'future' : 'past'];
30159    return isFunction(format) ? format(output) : format.replace(/%s/i, output);
30160}
30161
30162var aliases = {};
30163
30164function addUnitAlias (unit, shorthand) {
30165    var lowerCase = unit.toLowerCase();
30166    aliases[lowerCase] = aliases[lowerCase + 's'] = aliases[shorthand] = unit;
30167}
30168
30169function normalizeUnits(units) {
30170    return typeof units === 'string' ? aliases[units] || aliases[units.toLowerCase()] : undefined;
30171}
30172
30173function normalizeObjectUnits(inputObject) {
30174    var normalizedInput = {},
30175        normalizedProp,
30176        prop;
30177
30178    for (prop in inputObject) {
30179        if (hasOwnProp(inputObject, prop)) {
30180            normalizedProp = normalizeUnits(prop);
30181            if (normalizedProp) {
30182                normalizedInput[normalizedProp] = inputObject[prop];
30183            }
30184        }
30185    }
30186
30187    return normalizedInput;
30188}
30189
30190var priorities = {};
30191
30192function addUnitPriority(unit, priority) {
30193    priorities[unit] = priority;
30194}
30195
30196function getPrioritizedUnits(unitsObj) {
30197    var units = [];
30198    for (var u in unitsObj) {
30199        units.push({unit: u, priority: priorities[u]});
30200    }
30201    units.sort(function (a, b) {
30202        return a.priority - b.priority;
30203    });
30204    return units;
30205}
30206
30207function zeroFill(number, targetLength, forceSign) {
30208    var absNumber = '' + Math.abs(number),
30209        zerosToFill = targetLength - absNumber.length,
30210        sign = number >= 0;
30211    return (sign ? (forceSign ? '+' : '') : '-') +
30212        Math.pow(10, Math.max(0, zerosToFill)).toString().substr(1) + absNumber;
30213}
30214
30215var 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;
30216
30217var localFormattingTokens = /(\[[^\[]*\])|(\\)?(LTS|LT|LL?L?L?|l{1,4})/g;
30218
30219var formatFunctions = {};
30220
30221var formatTokenFunctions = {};
30222
30223// token:    'M'
30224// padded:   ['MM', 2]
30225// ordinal:  'Mo'
30226// callback: function () { this.month() + 1 }
30227function addFormatToken (token, padded, ordinal, callback) {
30228    var func = callback;
30229    if (typeof callback === 'string') {
30230        func = function () {
30231            return this[callback]();
30232        };
30233    }
30234    if (token) {
30235        formatTokenFunctions[token] = func;
30236    }
30237    if (padded) {
30238        formatTokenFunctions[padded[0]] = function () {
30239            return zeroFill(func.apply(this, arguments), padded[1], padded[2]);
30240        };
30241    }
30242    if (ordinal) {
30243        formatTokenFunctions[ordinal] = function () {
30244            return this.localeData().ordinal(func.apply(this, arguments), token);
30245        };
30246    }
30247}
30248
30249function removeFormattingTokens(input) {
30250    if (input.match(/\[[\s\S]/)) {
30251        return input.replace(/^\[|\]$/g, '');
30252    }
30253    return input.replace(/\\/g, '');
30254}
30255
30256function makeFormatFunction(format) {
30257    var array = format.match(formattingTokens), i, length;
30258
30259    for (i = 0, length = array.length; i < length; i++) {
30260        if (formatTokenFunctions[array[i]]) {
30261            array[i] = formatTokenFunctions[array[i]];
30262        } else {
30263            array[i] = removeFormattingTokens(array[i]);
30264        }
30265    }
30266
30267    return function (mom) {
30268        var output = '', i;
30269        for (i = 0; i < length; i++) {
30270            output += isFunction(array[i]) ? array[i].call(mom, format) : array[i];
30271        }
30272        return output;
30273    };
30274}
30275
30276// format date using native date object
30277function formatMoment(m, format) {
30278    if (!m.isValid()) {
30279        return m.localeData().invalidDate();
30280    }
30281
30282    format = expandFormat(format, m.localeData());
30283    formatFunctions[format] = formatFunctions[format] || makeFormatFunction(format);
30284
30285    return formatFunctions[format](m);
30286}
30287
30288function expandFormat(format, locale) {
30289    var i = 5;
30290
30291    function replaceLongDateFormatTokens(input) {
30292        return locale.longDateFormat(input) || input;
30293    }
30294
30295    localFormattingTokens.lastIndex = 0;
30296    while (i >= 0 && localFormattingTokens.test(format)) {
30297        format = format.replace(localFormattingTokens, replaceLongDateFormatTokens);
30298        localFormattingTokens.lastIndex = 0;
30299        i -= 1;
30300    }
30301
30302    return format;
30303}
30304
30305var match1         = /\d/;            //       0 - 9
30306var match2         = /\d\d/;          //      00 - 99
30307var match3         = /\d{3}/;         //     000 - 999
30308var match4         = /\d{4}/;         //    0000 - 9999
30309var match6         = /[+-]?\d{6}/;    // -999999 - 999999
30310var match1to2      = /\d\d?/;         //       0 - 99
30311var match3to4      = /\d\d\d\d?/;     //     999 - 9999
30312var match5to6      = /\d\d\d\d\d\d?/; //   99999 - 999999
30313var match1to3      = /\d{1,3}/;       //       0 - 999
30314var match1to4      = /\d{1,4}/;       //       0 - 9999
30315var match1to6      = /[+-]?\d{1,6}/;  // -999999 - 999999
30316
30317var matchUnsigned  = /\d+/;           //       0 - inf
30318var matchSigned    = /[+-]?\d+/;      //    -inf - inf
30319
30320var matchOffset    = /Z|[+-]\d\d:?\d\d/gi; // +00:00 -00:00 +0000 -0000 or Z
30321var matchShortOffset = /Z|[+-]\d\d(?::?\d\d)?/gi; // +00 -00 +00:00 -00:00 +0000 -0000 or Z
30322
30323var matchTimestamp = /[+-]?\d+(\.\d{1,3})?/; // 123456789 123456789.123
30324
30325// any word (or two) characters or numbers including two/three word month in arabic.
30326// includes scottish gaelic two word and hyphenated months
30327var matchWord = /[0-9]*['a-z\u00A0-\u05FF\u0700-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+|[\u0600-\u06FF\/]+(\s*?[\u0600-\u06FF]+){1,2}/i;
30328
30329
30330var regexes = {};
30331
30332function addRegexToken (token, regex, strictRegex) {
30333    regexes[token] = isFunction(regex) ? regex : function (isStrict, localeData) {
30334        return (isStrict && strictRegex) ? strictRegex : regex;
30335    };
30336}
30337
30338function getParseRegexForToken (token, config) {
30339    if (!hasOwnProp(regexes, token)) {
30340        return new RegExp(unescapeFormat(token));
30341    }
30342
30343    return regexes[token](config._strict, config._locale);
30344}
30345
30346// Code from http://stackoverflow.com/questions/3561493/is-there-a-regexp-escape-function-in-javascript
30347function unescapeFormat(s) {
30348    return regexEscape(s.replace('\\', '').replace(/\\(\[)|\\(\])|\[([^\]\[]*)\]|\\(.)/g, function (matched, p1, p2, p3, p4) {
30349        return p1 || p2 || p3 || p4;
30350    }));
30351}
30352
30353function regexEscape(s) {
30354    return s.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
30355}
30356
30357var tokens = {};
30358
30359function addParseToken (token, callback) {
30360    var i, func = callback;
30361    if (typeof token === 'string') {
30362        token = [token];
30363    }
30364    if (isNumber(callback)) {
30365        func = function (input, array) {
30366            array[callback] = toInt(input);
30367        };
30368    }
30369    for (i = 0; i < token.length; i++) {
30370        tokens[token[i]] = func;
30371    }
30372}
30373
30374function addWeekParseToken (token, callback) {
30375    addParseToken(token, function (input, array, config, token) {
30376        config._w = config._w || {};
30377        callback(input, config._w, config, token);
30378    });
30379}
30380
30381function addTimeToArrayFromToken(token, input, config) {
30382    if (input != null && hasOwnProp(tokens, token)) {
30383        tokens[token](input, config._a, config, token);
30384    }
30385}
30386
30387var YEAR = 0;
30388var MONTH = 1;
30389var DATE = 2;
30390var HOUR = 3;
30391var MINUTE = 4;
30392var SECOND = 5;
30393var MILLISECOND = 6;
30394var WEEK = 7;
30395var WEEKDAY = 8;
30396
30397// FORMATTING
30398
30399addFormatToken('Y', 0, 0, function () {
30400    var y = this.year();
30401    return y <= 9999 ? '' + y : '+' + y;
30402});
30403
30404addFormatToken(0, ['YY', 2], 0, function () {
30405    return this.year() % 100;
30406});
30407
30408addFormatToken(0, ['YYYY',   4],       0, 'year');
30409addFormatToken(0, ['YYYYY',  5],       0, 'year');
30410addFormatToken(0, ['YYYYYY', 6, true], 0, 'year');
30411
30412// ALIASES
30413
30414addUnitAlias('year', 'y');
30415
30416// PRIORITIES
30417
30418addUnitPriority('year', 1);
30419
30420// PARSING
30421
30422addRegexToken('Y',      matchSigned);
30423addRegexToken('YY',     match1to2, match2);
30424addRegexToken('YYYY',   match1to4, match4);
30425addRegexToken('YYYYY',  match1to6, match6);
30426addRegexToken('YYYYYY', match1to6, match6);
30427
30428addParseToken(['YYYYY', 'YYYYYY'], YEAR);
30429addParseToken('YYYY', function (input, array) {
30430    array[YEAR] = input.length === 2 ? hooks.parseTwoDigitYear(input) : toInt(input);
30431});
30432addParseToken('YY', function (input, array) {
30433    array[YEAR] = hooks.parseTwoDigitYear(input);
30434});
30435addParseToken('Y', function (input, array) {
30436    array[YEAR] = parseInt(input, 10);
30437});
30438
30439// HELPERS
30440
30441function daysInYear(year) {
30442    return isLeapYear(year) ? 366 : 365;
30443}
30444
30445function isLeapYear(year) {
30446    return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0;
30447}
30448
30449// HOOKS
30450
30451hooks.parseTwoDigitYear = function (input) {
30452    return toInt(input) + (toInt(input) > 68 ? 1900 : 2000);
30453};
30454
30455// MOMENTS
30456
30457var getSetYear = makeGetSet('FullYear', true);
30458
30459function getIsLeapYear () {
30460    return isLeapYear(this.year());
30461}
30462
30463function makeGetSet (unit, keepTime) {
30464    return function (value) {
30465        if (value != null) {
30466            set$1(this, unit, value);
30467            hooks.updateOffset(this, keepTime);
30468            return this;
30469        } else {
30470            return get(this, unit);
30471        }
30472    };
30473}
30474
30475function get (mom, unit) {
30476    return mom.isValid() ?
30477        mom._d['get' + (mom._isUTC ? 'UTC' : '') + unit]() : NaN;
30478}
30479
30480function set$1 (mom, unit, value) {
30481    if (mom.isValid() && !isNaN(value)) {
30482        if (unit === 'FullYear' && isLeapYear(mom.year())) {
30483            mom._d['set' + (mom._isUTC ? 'UTC' : '') + unit](value, mom.month(), daysInMonth(value, mom.month()));
30484        }
30485        else {
30486            mom._d['set' + (mom._isUTC ? 'UTC' : '') + unit](value);
30487        }
30488    }
30489}
30490
30491// MOMENTS
30492
30493function stringGet (units) {
30494    units = normalizeUnits(units);
30495    if (isFunction(this[units])) {
30496        return this[units]();
30497    }
30498    return this;
30499}
30500
30501
30502function stringSet (units, value) {
30503    if (typeof units === 'object') {
30504        units = normalizeObjectUnits(units);
30505        var prioritized = getPrioritizedUnits(units);
30506        for (var i = 0; i < prioritized.length; i++) {
30507            this[prioritized[i].unit](units[prioritized[i].unit]);
30508        }
30509    } else {
30510        units = normalizeUnits(units);
30511        if (isFunction(this[units])) {
30512            return this[units](value);
30513        }
30514    }
30515    return this;
30516}
30517
30518function mod(n, x) {
30519    return ((n % x) + x) % x;
30520}
30521
30522var indexOf;
30523
30524if (Array.prototype.indexOf) {
30525    indexOf = Array.prototype.indexOf;
30526} else {
30527    indexOf = function (o) {
30528        // I know
30529        var i;
30530        for (i = 0; i < this.length; ++i) {
30531            if (this[i] === o) {
30532                return i;
30533            }
30534        }
30535        return -1;
30536    };
30537}
30538
30539function daysInMonth(year, month) {
30540    if (isNaN(year) || isNaN(month)) {
30541        return NaN;
30542    }
30543    var modMonth = mod(month, 12);
30544    year += (month - modMonth) / 12;
30545    return modMonth === 1 ? (isLeapYear(year) ? 29 : 28) : (31 - modMonth % 7 % 2);
30546}
30547
30548// FORMATTING
30549
30550addFormatToken('M', ['MM', 2], 'Mo', function () {
30551    return this.month() + 1;
30552});
30553
30554addFormatToken('MMM', 0, 0, function (format) {
30555    return this.localeData().monthsShort(this, format);
30556});
30557
30558addFormatToken('MMMM', 0, 0, function (format) {
30559    return this.localeData().months(this, format);
30560});
30561
30562// ALIASES
30563
30564addUnitAlias('month', 'M');
30565
30566// PRIORITY
30567
30568addUnitPriority('month', 8);
30569
30570// PARSING
30571
30572addRegexToken('M',    match1to2);
30573addRegexToken('MM',   match1to2, match2);
30574addRegexToken('MMM',  function (isStrict, locale) {
30575    return locale.monthsShortRegex(isStrict);
30576});
30577addRegexToken('MMMM', function (isStrict, locale) {
30578    return locale.monthsRegex(isStrict);
30579});
30580
30581addParseToken(['M', 'MM'], function (input, array) {
30582    array[MONTH] = toInt(input) - 1;
30583});
30584
30585addParseToken(['MMM', 'MMMM'], function (input, array, config, token) {
30586    var month = config._locale.monthsParse(input, token, config._strict);
30587    // if we didn't find a month name, mark the date as invalid.
30588    if (month != null) {
30589        array[MONTH] = month;
30590    } else {
30591        getParsingFlags(config).invalidMonth = input;
30592    }
30593});
30594
30595// LOCALES
30596
30597var MONTHS_IN_FORMAT = /D[oD]?(\[[^\[\]]*\]|\s)+MMMM?/;
30598var defaultLocaleMonths = 'January_February_March_April_May_June_July_August_September_October_November_December'.split('_');
30599function localeMonths (m, format) {
30600    if (!m) {
30601        return isArray(this._months) ? this._months :
30602            this._months['standalone'];
30603    }
30604    return isArray(this._months) ? this._months[m.month()] :
30605        this._months[(this._months.isFormat || MONTHS_IN_FORMAT).test(format) ? 'format' : 'standalone'][m.month()];
30606}
30607
30608var defaultLocaleMonthsShort = 'Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec'.split('_');
30609function localeMonthsShort (m, format) {
30610    if (!m) {
30611        return isArray(this._monthsShort) ? this._monthsShort :
30612            this._monthsShort['standalone'];
30613    }
30614    return isArray(this._monthsShort) ? this._monthsShort[m.month()] :
30615        this._monthsShort[MONTHS_IN_FORMAT.test(format) ? 'format' : 'standalone'][m.month()];
30616}
30617
30618function handleStrictParse(monthName, format, strict) {
30619    var i, ii, mom, llc = monthName.toLocaleLowerCase();
30620    if (!this._monthsParse) {
30621        // this is not used
30622        this._monthsParse = [];
30623        this._longMonthsParse = [];
30624        this._shortMonthsParse = [];
30625        for (i = 0; i < 12; ++i) {
30626            mom = createUTC([2000, i]);
30627            this._shortMonthsParse[i] = this.monthsShort(mom, '').toLocaleLowerCase();
30628            this._longMonthsParse[i] = this.months(mom, '').toLocaleLowerCase();
30629        }
30630    }
30631
30632    if (strict) {
30633        if (format === 'MMM') {
30634            ii = indexOf.call(this._shortMonthsParse, llc);
30635            return ii !== -1 ? ii : null;
30636        } else {
30637            ii = indexOf.call(this._longMonthsParse, llc);
30638            return ii !== -1 ? ii : null;
30639        }
30640    } else {
30641        if (format === 'MMM') {
30642            ii = indexOf.call(this._shortMonthsParse, llc);
30643            if (ii !== -1) {
30644                return ii;
30645            }
30646            ii = indexOf.call(this._longMonthsParse, llc);
30647            return ii !== -1 ? ii : null;
30648        } else {
30649            ii = indexOf.call(this._longMonthsParse, llc);
30650            if (ii !== -1) {
30651                return ii;
30652            }
30653            ii = indexOf.call(this._shortMonthsParse, llc);
30654            return ii !== -1 ? ii : null;
30655        }
30656    }
30657}
30658
30659function localeMonthsParse (monthName, format, strict) {
30660    var i, mom, regex;
30661
30662    if (this._monthsParseExact) {
30663        return handleStrictParse.call(this, monthName, format, strict);
30664    }
30665
30666    if (!this._monthsParse) {
30667        this._monthsParse = [];
30668        this._longMonthsParse = [];
30669        this._shortMonthsParse = [];
30670    }
30671
30672    // TODO: add sorting
30673    // Sorting makes sure if one month (or abbr) is a prefix of another
30674    // see sorting in computeMonthsParse
30675    for (i = 0; i < 12; i++) {
30676        // make the regex if we don't have it already
30677        mom = createUTC([2000, i]);
30678        if (strict && !this._longMonthsParse[i]) {
30679            this._longMonthsParse[i] = new RegExp('^' + this.months(mom, '').replace('.', '') + '$', 'i');
30680            this._shortMonthsParse[i] = new RegExp('^' + this.monthsShort(mom, '').replace('.', '') + '$', 'i');
30681        }
30682        if (!strict && !this._monthsParse[i]) {
30683            regex = '^' + this.months(mom, '') + '|^' + this.monthsShort(mom, '');
30684            this._monthsParse[i] = new RegExp(regex.replace('.', ''), 'i');
30685        }
30686        // test the regex
30687        if (strict && format === 'MMMM' && this._longMonthsParse[i].test(monthName)) {
30688            return i;
30689        } else if (strict && format === 'MMM' && this._shortMonthsParse[i].test(monthName)) {
30690            return i;
30691        } else if (!strict && this._monthsParse[i].test(monthName)) {
30692            return i;
30693        }
30694    }
30695}
30696
30697// MOMENTS
30698
30699function setMonth (mom, value) {
30700    var dayOfMonth;
30701
30702    if (!mom.isValid()) {
30703        // No op
30704        return mom;
30705    }
30706
30707    if (typeof value === 'string') {
30708        if (/^\d+$/.test(value)) {
30709            value = toInt(value);
30710        } else {
30711            value = mom.localeData().monthsParse(value);
30712            // TODO: Another silent failure?
30713            if (!isNumber(value)) {
30714                return mom;
30715            }
30716        }
30717    }
30718
30719    dayOfMonth = Math.min(mom.date(), daysInMonth(mom.year(), value));
30720    mom._d['set' + (mom._isUTC ? 'UTC' : '') + 'Month'](value, dayOfMonth);
30721    return mom;
30722}
30723
30724function getSetMonth (value) {
30725    if (value != null) {
30726        setMonth(this, value);
30727        hooks.updateOffset(this, true);
30728        return this;
30729    } else {
30730        return get(this, 'Month');
30731    }
30732}
30733
30734function getDaysInMonth () {
30735    return daysInMonth(this.year(), this.month());
30736}
30737
30738var defaultMonthsShortRegex = matchWord;
30739function monthsShortRegex (isStrict) {
30740    if (this._monthsParseExact) {
30741        if (!hasOwnProp(this, '_monthsRegex')) {
30742            computeMonthsParse.call(this);
30743        }
30744        if (isStrict) {
30745            return this._monthsShortStrictRegex;
30746        } else {
30747            return this._monthsShortRegex;
30748        }
30749    } else {
30750        if (!hasOwnProp(this, '_monthsShortRegex')) {
30751            this._monthsShortRegex = defaultMonthsShortRegex;
30752        }
30753        return this._monthsShortStrictRegex && isStrict ?
30754            this._monthsShortStrictRegex : this._monthsShortRegex;
30755    }
30756}
30757
30758var defaultMonthsRegex = matchWord;
30759function monthsRegex (isStrict) {
30760    if (this._monthsParseExact) {
30761        if (!hasOwnProp(this, '_monthsRegex')) {
30762            computeMonthsParse.call(this);
30763        }
30764        if (isStrict) {
30765            return this._monthsStrictRegex;
30766        } else {
30767            return this._monthsRegex;
30768        }
30769    } else {
30770        if (!hasOwnProp(this, '_monthsRegex')) {
30771            this._monthsRegex = defaultMonthsRegex;
30772        }
30773        return this._monthsStrictRegex && isStrict ?
30774            this._monthsStrictRegex : this._monthsRegex;
30775    }
30776}
30777
30778function computeMonthsParse () {
30779    function cmpLenRev(a, b) {
30780        return b.length - a.length;
30781    }
30782
30783    var shortPieces = [], longPieces = [], mixedPieces = [],
30784        i, mom;
30785    for (i = 0; i < 12; i++) {
30786        // make the regex if we don't have it already
30787        mom = createUTC([2000, i]);
30788        shortPieces.push(this.monthsShort(mom, ''));
30789        longPieces.push(this.months(mom, ''));
30790        mixedPieces.push(this.months(mom, ''));
30791        mixedPieces.push(this.monthsShort(mom, ''));
30792    }
30793    // Sorting makes sure if one month (or abbr) is a prefix of another it
30794    // will match the longer piece.
30795    shortPieces.sort(cmpLenRev);
30796    longPieces.sort(cmpLenRev);
30797    mixedPieces.sort(cmpLenRev);
30798    for (i = 0; i < 12; i++) {
30799        shortPieces[i] = regexEscape(shortPieces[i]);
30800        longPieces[i] = regexEscape(longPieces[i]);
30801    }
30802    for (i = 0; i < 24; i++) {
30803        mixedPieces[i] = regexEscape(mixedPieces[i]);
30804    }
30805
30806    this._monthsRegex = new RegExp('^(' + mixedPieces.join('|') + ')', 'i');
30807    this._monthsShortRegex = this._monthsRegex;
30808    this._monthsStrictRegex = new RegExp('^(' + longPieces.join('|') + ')', 'i');
30809    this._monthsShortStrictRegex = new RegExp('^(' + shortPieces.join('|') + ')', 'i');
30810}
30811
30812function createDate (y, m, d, h, M, s, ms) {
30813    // can't just apply() to create a date:
30814    // https://stackoverflow.com/q/181348
30815    var date = new Date(y, m, d, h, M, s, ms);
30816
30817    // the date constructor remaps years 0-99 to 1900-1999
30818    if (y < 100 && y >= 0 && isFinite(date.getFullYear())) {
30819        date.setFullYear(y);
30820    }
30821    return date;
30822}
30823
30824function createUTCDate (y) {
30825    var date = new Date(Date.UTC.apply(null, arguments));
30826
30827    // the Date.UTC function remaps years 0-99 to 1900-1999
30828    if (y < 100 && y >= 0 && isFinite(date.getUTCFullYear())) {
30829        date.setUTCFullYear(y);
30830    }
30831    return date;
30832}
30833
30834// start-of-first-week - start-of-year
30835function firstWeekOffset(year, dow, doy) {
30836    var // first-week day -- which january is always in the first week (4 for iso, 1 for other)
30837        fwd = 7 + dow - doy,
30838        // first-week day local weekday -- which local weekday is fwd
30839        fwdlw = (7 + createUTCDate(year, 0, fwd).getUTCDay() - dow) % 7;
30840
30841    return -fwdlw + fwd - 1;
30842}
30843
30844// https://en.wikipedia.org/wiki/ISO_week_date#Calculating_a_date_given_the_year.2C_week_number_and_weekday
30845function dayOfYearFromWeeks(year, week, weekday, dow, doy) {
30846    var localWeekday = (7 + weekday - dow) % 7,
30847        weekOffset = firstWeekOffset(year, dow, doy),
30848        dayOfYear = 1 + 7 * (week - 1) + localWeekday + weekOffset,
30849        resYear, resDayOfYear;
30850
30851    if (dayOfYear <= 0) {
30852        resYear = year - 1;
30853        resDayOfYear = daysInYear(resYear) + dayOfYear;
30854    } else if (dayOfYear > daysInYear(year)) {
30855        resYear = year + 1;
30856        resDayOfYear = dayOfYear - daysInYear(year);
30857    } else {
30858        resYear = year;
30859        resDayOfYear = dayOfYear;
30860    }
30861
30862    return {
30863        year: resYear,
30864        dayOfYear: resDayOfYear
30865    };
30866}
30867
30868function weekOfYear(mom, dow, doy) {
30869    var weekOffset = firstWeekOffset(mom.year(), dow, doy),
30870        week = Math.floor((mom.dayOfYear() - weekOffset - 1) / 7) + 1,
30871        resWeek, resYear;
30872
30873    if (week < 1) {
30874        resYear = mom.year() - 1;
30875        resWeek = week + weeksInYear(resYear, dow, doy);
30876    } else if (week > weeksInYear(mom.year(), dow, doy)) {
30877        resWeek = week - weeksInYear(mom.year(), dow, doy);
30878        resYear = mom.year() + 1;
30879    } else {
30880        resYear = mom.year();
30881        resWeek = week;
30882    }
30883
30884    return {
30885        week: resWeek,
30886        year: resYear
30887    };
30888}
30889
30890function weeksInYear(year, dow, doy) {
30891    var weekOffset = firstWeekOffset(year, dow, doy),
30892        weekOffsetNext = firstWeekOffset(year + 1, dow, doy);
30893    return (daysInYear(year) - weekOffset + weekOffsetNext) / 7;
30894}
30895
30896// FORMATTING
30897
30898addFormatToken('w', ['ww', 2], 'wo', 'week');
30899addFormatToken('W', ['WW', 2], 'Wo', 'isoWeek');
30900
30901// ALIASES
30902
30903addUnitAlias('week', 'w');
30904addUnitAlias('isoWeek', 'W');
30905
30906// PRIORITIES
30907
30908addUnitPriority('week', 5);
30909addUnitPriority('isoWeek', 5);
30910
30911// PARSING
30912
30913addRegexToken('w',  match1to2);
30914addRegexToken('ww', match1to2, match2);
30915addRegexToken('W',  match1to2);
30916addRegexToken('WW', match1to2, match2);
30917
30918addWeekParseToken(['w', 'ww', 'W', 'WW'], function (input, week, config, token) {
30919    week[token.substr(0, 1)] = toInt(input);
30920});
30921
30922// HELPERS
30923
30924// LOCALES
30925
30926function localeWeek (mom) {
30927    return weekOfYear(mom, this._week.dow, this._week.doy).week;
30928}
30929
30930var defaultLocaleWeek = {
30931    dow : 0, // Sunday is the first day of the week.
30932    doy : 6  // The week that contains Jan 1st is the first week of the year.
30933};
30934
30935function localeFirstDayOfWeek () {
30936    return this._week.dow;
30937}
30938
30939function localeFirstDayOfYear () {
30940    return this._week.doy;
30941}
30942
30943// MOMENTS
30944
30945function getSetWeek (input) {
30946    var week = this.localeData().week(this);
30947    return input == null ? week : this.add((input - week) * 7, 'd');
30948}
30949
30950function getSetISOWeek (input) {
30951    var week = weekOfYear(this, 1, 4).week;
30952    return input == null ? week : this.add((input - week) * 7, 'd');
30953}
30954
30955// FORMATTING
30956
30957addFormatToken('d', 0, 'do', 'day');
30958
30959addFormatToken('dd', 0, 0, function (format) {
30960    return this.localeData().weekdaysMin(this, format);
30961});
30962
30963addFormatToken('ddd', 0, 0, function (format) {
30964    return this.localeData().weekdaysShort(this, format);
30965});
30966
30967addFormatToken('dddd', 0, 0, function (format) {
30968    return this.localeData().weekdays(this, format);
30969});
30970
30971addFormatToken('e', 0, 0, 'weekday');
30972addFormatToken('E', 0, 0, 'isoWeekday');
30973
30974// ALIASES
30975
30976addUnitAlias('day', 'd');
30977addUnitAlias('weekday', 'e');
30978addUnitAlias('isoWeekday', 'E');
30979
30980// PRIORITY
30981addUnitPriority('day', 11);
30982addUnitPriority('weekday', 11);
30983addUnitPriority('isoWeekday', 11);
30984
30985// PARSING
30986
30987addRegexToken('d',    match1to2);
30988addRegexToken('e',    match1to2);
30989addRegexToken('E',    match1to2);
30990addRegexToken('dd',   function (isStrict, locale) {
30991    return locale.weekdaysMinRegex(isStrict);
30992});
30993addRegexToken('ddd',   function (isStrict, locale) {
30994    return locale.weekdaysShortRegex(isStrict);
30995});
30996addRegexToken('dddd',   function (isStrict, locale) {
30997    return locale.weekdaysRegex(isStrict);
30998});
30999
31000addWeekParseToken(['dd', 'ddd', 'dddd'], function (input, week, config, token) {
31001    var weekday = config._locale.weekdaysParse(input, token, config._strict);
31002    // if we didn't get a weekday name, mark the date as invalid
31003    if (weekday != null) {
31004        week.d = weekday;
31005    } else {
31006        getParsingFlags(config).invalidWeekday = input;
31007    }
31008});
31009
31010addWeekParseToken(['d', 'e', 'E'], function (input, week, config, token) {
31011    week[token] = toInt(input);
31012});
31013
31014// HELPERS
31015
31016function parseWeekday(input, locale) {
31017    if (typeof input !== 'string') {
31018        return input;
31019    }
31020
31021    if (!isNaN(input)) {
31022        return parseInt(input, 10);
31023    }
31024
31025    input = locale.weekdaysParse(input);
31026    if (typeof input === 'number') {
31027        return input;
31028    }
31029
31030    return null;
31031}
31032
31033function parseIsoWeekday(input, locale) {
31034    if (typeof input === 'string') {
31035        return locale.weekdaysParse(input) % 7 || 7;
31036    }
31037    return isNaN(input) ? null : input;
31038}
31039
31040// LOCALES
31041
31042var defaultLocaleWeekdays = 'Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday'.split('_');
31043function localeWeekdays (m, format) {
31044    if (!m) {
31045        return isArray(this._weekdays) ? this._weekdays :
31046            this._weekdays['standalone'];
31047    }
31048    return isArray(this._weekdays) ? this._weekdays[m.day()] :
31049        this._weekdays[this._weekdays.isFormat.test(format) ? 'format' : 'standalone'][m.day()];
31050}
31051
31052var defaultLocaleWeekdaysShort = 'Sun_Mon_Tue_Wed_Thu_Fri_Sat'.split('_');
31053function localeWeekdaysShort (m) {
31054    return (m) ? this._weekdaysShort[m.day()] : this._weekdaysShort;
31055}
31056
31057var defaultLocaleWeekdaysMin = 'Su_Mo_Tu_We_Th_Fr_Sa'.split('_');
31058function localeWeekdaysMin (m) {
31059    return (m) ? this._weekdaysMin[m.day()] : this._weekdaysMin;
31060}
31061
31062function handleStrictParse$1(weekdayName, format, strict) {
31063    var i, ii, mom, llc = weekdayName.toLocaleLowerCase();
31064    if (!this._weekdaysParse) {
31065        this._weekdaysParse = [];
31066        this._shortWeekdaysParse = [];
31067        this._minWeekdaysParse = [];
31068
31069        for (i = 0; i < 7; ++i) {
31070            mom = createUTC([2000, 1]).day(i);
31071            this._minWeekdaysParse[i] = this.weekdaysMin(mom, '').toLocaleLowerCase();
31072            this._shortWeekdaysParse[i] = this.weekdaysShort(mom, '').toLocaleLowerCase();
31073            this._weekdaysParse[i] = this.weekdays(mom, '').toLocaleLowerCase();
31074        }
31075    }
31076
31077    if (strict) {
31078        if (format === 'dddd') {
31079            ii = indexOf.call(this._weekdaysParse, llc);
31080            return ii !== -1 ? ii : null;
31081        } else if (format === 'ddd') {
31082            ii = indexOf.call(this._shortWeekdaysParse, llc);
31083            return ii !== -1 ? ii : null;
31084        } else {
31085            ii = indexOf.call(this._minWeekdaysParse, llc);
31086            return ii !== -1 ? ii : null;
31087        }
31088    } else {
31089        if (format === 'dddd') {
31090            ii = indexOf.call(this._weekdaysParse, llc);
31091            if (ii !== -1) {
31092                return ii;
31093            }
31094            ii = indexOf.call(this._shortWeekdaysParse, llc);
31095            if (ii !== -1) {
31096                return ii;
31097            }
31098            ii = indexOf.call(this._minWeekdaysParse, llc);
31099            return ii !== -1 ? ii : null;
31100        } else if (format === 'ddd') {
31101            ii = indexOf.call(this._shortWeekdaysParse, llc);
31102            if (ii !== -1) {
31103                return ii;
31104            }
31105            ii = indexOf.call(this._weekdaysParse, llc);
31106            if (ii !== -1) {
31107                return ii;
31108            }
31109            ii = indexOf.call(this._minWeekdaysParse, llc);
31110            return ii !== -1 ? ii : null;
31111        } else {
31112            ii = indexOf.call(this._minWeekdaysParse, llc);
31113            if (ii !== -1) {
31114                return ii;
31115            }
31116            ii = indexOf.call(this._weekdaysParse, llc);
31117            if (ii !== -1) {
31118                return ii;
31119            }
31120            ii = indexOf.call(this._shortWeekdaysParse, llc);
31121            return ii !== -1 ? ii : null;
31122        }
31123    }
31124}
31125
31126function localeWeekdaysParse (weekdayName, format, strict) {
31127    var i, mom, regex;
31128
31129    if (this._weekdaysParseExact) {
31130        return handleStrictParse$1.call(this, weekdayName, format, strict);
31131    }
31132
31133    if (!this._weekdaysParse) {
31134        this._weekdaysParse = [];
31135        this._minWeekdaysParse = [];
31136        this._shortWeekdaysParse = [];
31137        this._fullWeekdaysParse = [];
31138    }
31139
31140    for (i = 0; i < 7; i++) {
31141        // make the regex if we don't have it already
31142
31143        mom = createUTC([2000, 1]).day(i);
31144        if (strict && !this._fullWeekdaysParse[i]) {
31145            this._fullWeekdaysParse[i] = new RegExp('^' + this.weekdays(mom, '').replace('.', '\.?') + '$', 'i');
31146            this._shortWeekdaysParse[i] = new RegExp('^' + this.weekdaysShort(mom, '').replace('.', '\.?') + '$', 'i');
31147            this._minWeekdaysParse[i] = new RegExp('^' + this.weekdaysMin(mom, '').replace('.', '\.?') + '$', 'i');
31148        }
31149        if (!this._weekdaysParse[i]) {
31150            regex = '^' + this.weekdays(mom, '') + '|^' + this.weekdaysShort(mom, '') + '|^' + this.weekdaysMin(mom, '');
31151            this._weekdaysParse[i] = new RegExp(regex.replace('.', ''), 'i');
31152        }
31153        // test the regex
31154        if (strict && format === 'dddd' && this._fullWeekdaysParse[i].test(weekdayName)) {
31155            return i;
31156        } else if (strict && format === 'ddd' && this._shortWeekdaysParse[i].test(weekdayName)) {
31157            return i;
31158        } else if (strict && format === 'dd' && this._minWeekdaysParse[i].test(weekdayName)) {
31159            return i;
31160        } else if (!strict && this._weekdaysParse[i].test(weekdayName)) {
31161            return i;
31162        }
31163    }
31164}
31165
31166// MOMENTS
31167
31168function getSetDayOfWeek (input) {
31169    if (!this.isValid()) {
31170        return input != null ? this : NaN;
31171    }
31172    var day = this._isUTC ? this._d.getUTCDay() : this._d.getDay();
31173    if (input != null) {
31174        input = parseWeekday(input, this.localeData());
31175        return this.add(input - day, 'd');
31176    } else {
31177        return day;
31178    }
31179}
31180
31181function getSetLocaleDayOfWeek (input) {
31182    if (!this.isValid()) {
31183        return input != null ? this : NaN;
31184    }
31185    var weekday = (this.day() + 7 - this.localeData()._week.dow) % 7;
31186    return input == null ? weekday : this.add(input - weekday, 'd');
31187}
31188
31189function getSetISODayOfWeek (input) {
31190    if (!this.isValid()) {
31191        return input != null ? this : NaN;
31192    }
31193
31194    // behaves the same as moment#day except
31195    // as a getter, returns 7 instead of 0 (1-7 range instead of 0-6)
31196    // as a setter, sunday should belong to the previous week.
31197
31198    if (input != null) {
31199        var weekday = parseIsoWeekday(input, this.localeData());
31200        return this.day(this.day() % 7 ? weekday : weekday - 7);
31201    } else {
31202        return this.day() || 7;
31203    }
31204}
31205
31206var defaultWeekdaysRegex = matchWord;
31207function weekdaysRegex (isStrict) {
31208    if (this._weekdaysParseExact) {
31209        if (!hasOwnProp(this, '_weekdaysRegex')) {
31210            computeWeekdaysParse.call(this);
31211        }
31212        if (isStrict) {
31213            return this._weekdaysStrictRegex;
31214        } else {
31215            return this._weekdaysRegex;
31216        }
31217    } else {
31218        if (!hasOwnProp(this, '_weekdaysRegex')) {
31219            this._weekdaysRegex = defaultWeekdaysRegex;
31220        }
31221        return this._weekdaysStrictRegex && isStrict ?
31222            this._weekdaysStrictRegex : this._weekdaysRegex;
31223    }
31224}
31225
31226var defaultWeekdaysShortRegex = matchWord;
31227function weekdaysShortRegex (isStrict) {
31228    if (this._weekdaysParseExact) {
31229        if (!hasOwnProp(this, '_weekdaysRegex')) {
31230            computeWeekdaysParse.call(this);
31231        }
31232        if (isStrict) {
31233            return this._weekdaysShortStrictRegex;
31234        } else {
31235            return this._weekdaysShortRegex;
31236        }
31237    } else {
31238        if (!hasOwnProp(this, '_weekdaysShortRegex')) {
31239            this._weekdaysShortRegex = defaultWeekdaysShortRegex;
31240        }
31241        return this._weekdaysShortStrictRegex && isStrict ?
31242            this._weekdaysShortStrictRegex : this._weekdaysShortRegex;
31243    }
31244}
31245
31246var defaultWeekdaysMinRegex = matchWord;
31247function weekdaysMinRegex (isStrict) {
31248    if (this._weekdaysParseExact) {
31249        if (!hasOwnProp(this, '_weekdaysRegex')) {
31250            computeWeekdaysParse.call(this);
31251        }
31252        if (isStrict) {
31253            return this._weekdaysMinStrictRegex;
31254        } else {
31255            return this._weekdaysMinRegex;
31256        }
31257    } else {
31258        if (!hasOwnProp(this, '_weekdaysMinRegex')) {
31259            this._weekdaysMinRegex = defaultWeekdaysMinRegex;
31260        }
31261        return this._weekdaysMinStrictRegex && isStrict ?
31262            this._weekdaysMinStrictRegex : this._weekdaysMinRegex;
31263    }
31264}
31265
31266
31267function computeWeekdaysParse () {
31268    function cmpLenRev(a, b) {
31269        return b.length - a.length;
31270    }
31271
31272    var minPieces = [], shortPieces = [], longPieces = [], mixedPieces = [],
31273        i, mom, minp, shortp, longp;
31274    for (i = 0; i < 7; i++) {
31275        // make the regex if we don't have it already
31276        mom = createUTC([2000, 1]).day(i);
31277        minp = this.weekdaysMin(mom, '');
31278        shortp = this.weekdaysShort(mom, '');
31279        longp = this.weekdays(mom, '');
31280        minPieces.push(minp);
31281        shortPieces.push(shortp);
31282        longPieces.push(longp);
31283        mixedPieces.push(minp);
31284        mixedPieces.push(shortp);
31285        mixedPieces.push(longp);
31286    }
31287    // Sorting makes sure if one weekday (or abbr) is a prefix of another it
31288    // will match the longer piece.
31289    minPieces.sort(cmpLenRev);
31290    shortPieces.sort(cmpLenRev);
31291    longPieces.sort(cmpLenRev);
31292    mixedPieces.sort(cmpLenRev);
31293    for (i = 0; i < 7; i++) {
31294        shortPieces[i] = regexEscape(shortPieces[i]);
31295        longPieces[i] = regexEscape(longPieces[i]);
31296        mixedPieces[i] = regexEscape(mixedPieces[i]);
31297    }
31298
31299    this._weekdaysRegex = new RegExp('^(' + mixedPieces.join('|') + ')', 'i');
31300    this._weekdaysShortRegex = this._weekdaysRegex;
31301    this._weekdaysMinRegex = this._weekdaysRegex;
31302
31303    this._weekdaysStrictRegex = new RegExp('^(' + longPieces.join('|') + ')', 'i');
31304    this._weekdaysShortStrictRegex = new RegExp('^(' + shortPieces.join('|') + ')', 'i');
31305    this._weekdaysMinStrictRegex = new RegExp('^(' + minPieces.join('|') + ')', 'i');
31306}
31307
31308// FORMATTING
31309
31310function hFormat() {
31311    return this.hours() % 12 || 12;
31312}
31313
31314function kFormat() {
31315    return this.hours() || 24;
31316}
31317
31318addFormatToken('H', ['HH', 2], 0, 'hour');
31319addFormatToken('h', ['hh', 2], 0, hFormat);
31320addFormatToken('k', ['kk', 2], 0, kFormat);
31321
31322addFormatToken('hmm', 0, 0, function () {
31323    return '' + hFormat.apply(this) + zeroFill(this.minutes(), 2);
31324});
31325
31326addFormatToken('hmmss', 0, 0, function () {
31327    return '' + hFormat.apply(this) + zeroFill(this.minutes(), 2) +
31328        zeroFill(this.seconds(), 2);
31329});
31330
31331addFormatToken('Hmm', 0, 0, function () {
31332    return '' + this.hours() + zeroFill(this.minutes(), 2);
31333});
31334
31335addFormatToken('Hmmss', 0, 0, function () {
31336    return '' + this.hours() + zeroFill(this.minutes(), 2) +
31337        zeroFill(this.seconds(), 2);
31338});
31339
31340function meridiem (token, lowercase) {
31341    addFormatToken(token, 0, 0, function () {
31342        return this.localeData().meridiem(this.hours(), this.minutes(), lowercase);
31343    });
31344}
31345
31346meridiem('a', true);
31347meridiem('A', false);
31348
31349// ALIASES
31350
31351addUnitAlias('hour', 'h');
31352
31353// PRIORITY
31354addUnitPriority('hour', 13);
31355
31356// PARSING
31357
31358function matchMeridiem (isStrict, locale) {
31359    return locale._meridiemParse;
31360}
31361
31362addRegexToken('a',  matchMeridiem);
31363addRegexToken('A',  matchMeridiem);
31364addRegexToken('H',  match1to2);
31365addRegexToken('h',  match1to2);
31366addRegexToken('k',  match1to2);
31367addRegexToken('HH', match1to2, match2);
31368addRegexToken('hh', match1to2, match2);
31369addRegexToken('kk', match1to2, match2);
31370
31371addRegexToken('hmm', match3to4);
31372addRegexToken('hmmss', match5to6);
31373addRegexToken('Hmm', match3to4);
31374addRegexToken('Hmmss', match5to6);
31375
31376addParseToken(['H', 'HH'], HOUR);
31377addParseToken(['k', 'kk'], function (input, array, config) {
31378    var kInput = toInt(input);
31379    array[HOUR] = kInput === 24 ? 0 : kInput;
31380});
31381addParseToken(['a', 'A'], function (input, array, config) {
31382    config._isPm = config._locale.isPM(input);
31383    config._meridiem = input;
31384});
31385addParseToken(['h', 'hh'], function (input, array, config) {
31386    array[HOUR] = toInt(input);
31387    getParsingFlags(config).bigHour = true;
31388});
31389addParseToken('hmm', function (input, array, config) {
31390    var pos = input.length - 2;
31391    array[HOUR] = toInt(input.substr(0, pos));
31392    array[MINUTE] = toInt(input.substr(pos));
31393    getParsingFlags(config).bigHour = true;
31394});
31395addParseToken('hmmss', function (input, array, config) {
31396    var pos1 = input.length - 4;
31397    var pos2 = input.length - 2;
31398    array[HOUR] = toInt(input.substr(0, pos1));
31399    array[MINUTE] = toInt(input.substr(pos1, 2));
31400    array[SECOND] = toInt(input.substr(pos2));
31401    getParsingFlags(config).bigHour = true;
31402});
31403addParseToken('Hmm', function (input, array, config) {
31404    var pos = input.length - 2;
31405    array[HOUR] = toInt(input.substr(0, pos));
31406    array[MINUTE] = toInt(input.substr(pos));
31407});
31408addParseToken('Hmmss', function (input, array, config) {
31409    var pos1 = input.length - 4;
31410    var pos2 = input.length - 2;
31411    array[HOUR] = toInt(input.substr(0, pos1));
31412    array[MINUTE] = toInt(input.substr(pos1, 2));
31413    array[SECOND] = toInt(input.substr(pos2));
31414});
31415
31416// LOCALES
31417
31418function localeIsPM (input) {
31419    // IE8 Quirks Mode & IE7 Standards Mode do not allow accessing strings like arrays
31420    // Using charAt should be more compatible.
31421    return ((input + '').toLowerCase().charAt(0) === 'p');
31422}
31423
31424var defaultLocaleMeridiemParse = /[ap]\.?m?\.?/i;
31425function localeMeridiem (hours, minutes, isLower) {
31426    if (hours > 11) {
31427        return isLower ? 'pm' : 'PM';
31428    } else {
31429        return isLower ? 'am' : 'AM';
31430    }
31431}
31432
31433
31434// MOMENTS
31435
31436// Setting the hour should keep the time, because the user explicitly
31437// specified which hour he wants. So trying to maintain the same hour (in
31438// a new timezone) makes sense. Adding/subtracting hours does not follow
31439// this rule.
31440var getSetHour = makeGetSet('Hours', true);
31441
31442// months
31443// week
31444// weekdays
31445// meridiem
31446var baseConfig = {
31447    calendar: defaultCalendar,
31448    longDateFormat: defaultLongDateFormat,
31449    invalidDate: defaultInvalidDate,
31450    ordinal: defaultOrdinal,
31451    dayOfMonthOrdinalParse: defaultDayOfMonthOrdinalParse,
31452    relativeTime: defaultRelativeTime,
31453
31454    months: defaultLocaleMonths,
31455    monthsShort: defaultLocaleMonthsShort,
31456
31457    week: defaultLocaleWeek,
31458
31459    weekdays: defaultLocaleWeekdays,
31460    weekdaysMin: defaultLocaleWeekdaysMin,
31461    weekdaysShort: defaultLocaleWeekdaysShort,
31462
31463    meridiemParse: defaultLocaleMeridiemParse
31464};
31465
31466// internal storage for locale config files
31467var locales = {};
31468var localeFamilies = {};
31469var globalLocale;
31470
31471function normalizeLocale(key) {
31472    return key ? key.toLowerCase().replace('_', '-') : key;
31473}
31474
31475// pick the locale from the array
31476// try ['en-au', 'en-gb'] as 'en-au', 'en-gb', 'en', as in move through the list trying each
31477// substring from most specific to least, but move to the next array item if it's a more specific variant than the current root
31478function chooseLocale(names) {
31479    var i = 0, j, next, locale, split;
31480
31481    while (i < names.length) {
31482        split = normalizeLocale(names[i]).split('-');
31483        j = split.length;
31484        next = normalizeLocale(names[i + 1]);
31485        next = next ? next.split('-') : null;
31486        while (j > 0) {
31487            locale = loadLocale(split.slice(0, j).join('-'));
31488            if (locale) {
31489                return locale;
31490            }
31491            if (next && next.length >= j && compareArrays(split, next, true) >= j - 1) {
31492                //the next array item is better than a shallower substring of this one
31493                break;
31494            }
31495            j--;
31496        }
31497        i++;
31498    }
31499    return null;
31500}
31501
31502function loadLocale(name) {
31503    var oldLocale = null;
31504    // TODO: Find a better way to register and load all the locales in Node
31505    if (!locales[name] && (typeof module !== 'undefined') &&
31506            module && module.exports) {
31507        try {
31508            oldLocale = globalLocale._abbr;
31509            var aliasedRequire = require;
31510            !(function webpackMissingModule() { var e = new Error("Cannot find module \"./locale\""); e.code = 'MODULE_NOT_FOUND'; throw e; }());
31511            getSetGlobalLocale(oldLocale);
31512        } catch (e) {}
31513    }
31514    return locales[name];
31515}
31516
31517// This function will load locale and then set the global locale.  If
31518// no arguments are passed in, it will simply return the current global
31519// locale key.
31520function getSetGlobalLocale (key, values) {
31521    var data;
31522    if (key) {
31523        if (isUndefined(values)) {
31524            data = getLocale(key);
31525        }
31526        else {
31527            data = defineLocale(key, values);
31528        }
31529
31530        if (data) {
31531            // moment.duration._locale = moment._locale = data;
31532            globalLocale = data;
31533        }
31534    }
31535
31536    return globalLocale._abbr;
31537}
31538
31539function defineLocale (name, config) {
31540    if (config !== null) {
31541        var parentConfig = baseConfig;
31542        config.abbr = name;
31543        if (locales[name] != null) {
31544            deprecateSimple('defineLocaleOverride',
31545                    'use moment.updateLocale(localeName, config) to change ' +
31546                    'an existing locale. moment.defineLocale(localeName, ' +
31547                    'config) should only be used for creating a new locale ' +
31548                    'See http://momentjs.com/guides/#/warnings/define-locale/ for more info.');
31549            parentConfig = locales[name]._config;
31550        } else if (config.parentLocale != null) {
31551            if (locales[config.parentLocale] != null) {
31552                parentConfig = locales[config.parentLocale]._config;
31553            } else {
31554                if (!localeFamilies[config.parentLocale]) {
31555                    localeFamilies[config.parentLocale] = [];
31556                }
31557                localeFamilies[config.parentLocale].push({
31558                    name: name,
31559                    config: config
31560                });
31561                return null;
31562            }
31563        }
31564        locales[name] = new Locale(mergeConfigs(parentConfig, config));
31565
31566        if (localeFamilies[name]) {
31567            localeFamilies[name].forEach(function (x) {
31568                defineLocale(x.name, x.config);
31569            });
31570        }
31571
31572        // backwards compat for now: also set the locale
31573        // make sure we set the locale AFTER all child locales have been
31574        // created, so we won't end up with the child locale set.
31575        getSetGlobalLocale(name);
31576
31577
31578        return locales[name];
31579    } else {
31580        // useful for testing
31581        delete locales[name];
31582        return null;
31583    }
31584}
31585
31586function updateLocale(name, config) {
31587    if (config != null) {
31588        var locale, parentConfig = baseConfig;
31589        // MERGE
31590        if (locales[name] != null) {
31591            parentConfig = locales[name]._config;
31592        }
31593        config = mergeConfigs(parentConfig, config);
31594        locale = new Locale(config);
31595        locale.parentLocale = locales[name];
31596        locales[name] = locale;
31597
31598        // backwards compat for now: also set the locale
31599        getSetGlobalLocale(name);
31600    } else {
31601        // pass null for config to unupdate, useful for tests
31602        if (locales[name] != null) {
31603            if (locales[name].parentLocale != null) {
31604                locales[name] = locales[name].parentLocale;
31605            } else if (locales[name] != null) {
31606                delete locales[name];
31607            }
31608        }
31609    }
31610    return locales[name];
31611}
31612
31613// returns locale data
31614function getLocale (key) {
31615    var locale;
31616
31617    if (key && key._locale && key._locale._abbr) {
31618        key = key._locale._abbr;
31619    }
31620
31621    if (!key) {
31622        return globalLocale;
31623    }
31624
31625    if (!isArray(key)) {
31626        //short-circuit everything else
31627        locale = loadLocale(key);
31628        if (locale) {
31629            return locale;
31630        }
31631        key = [key];
31632    }
31633
31634    return chooseLocale(key);
31635}
31636
31637function listLocales() {
31638    return keys(locales);
31639}
31640
31641function checkOverflow (m) {
31642    var overflow;
31643    var a = m._a;
31644
31645    if (a && getParsingFlags(m).overflow === -2) {
31646        overflow =
31647            a[MONTH]       < 0 || a[MONTH]       > 11  ? MONTH :
31648            a[DATE]        < 1 || a[DATE]        > daysInMonth(a[YEAR], a[MONTH]) ? DATE :
31649            a[HOUR]        < 0 || a[HOUR]        > 24 || (a[HOUR] === 24 && (a[MINUTE] !== 0 || a[SECOND] !== 0 || a[MILLISECOND] !== 0)) ? HOUR :
31650            a[MINUTE]      < 0 || a[MINUTE]      > 59  ? MINUTE :
31651            a[SECOND]      < 0 || a[SECOND]      > 59  ? SECOND :
31652            a[MILLISECOND] < 0 || a[MILLISECOND] > 999 ? MILLISECOND :
31653            -1;
31654
31655        if (getParsingFlags(m)._overflowDayOfYear && (overflow < YEAR || overflow > DATE)) {
31656            overflow = DATE;
31657        }
31658        if (getParsingFlags(m)._overflowWeeks && overflow === -1) {
31659            overflow = WEEK;
31660        }
31661        if (getParsingFlags(m)._overflowWeekday && overflow === -1) {
31662            overflow = WEEKDAY;
31663        }
31664
31665        getParsingFlags(m).overflow = overflow;
31666    }
31667
31668    return m;
31669}
31670
31671// Pick the first defined of two or three arguments.
31672function defaults(a, b, c) {
31673    if (a != null) {
31674        return a;
31675    }
31676    if (b != null) {
31677        return b;
31678    }
31679    return c;
31680}
31681
31682function currentDateArray(config) {
31683    // hooks is actually the exported moment object
31684    var nowValue = new Date(hooks.now());
31685    if (config._useUTC) {
31686        return [nowValue.getUTCFullYear(), nowValue.getUTCMonth(), nowValue.getUTCDate()];
31687    }
31688    return [nowValue.getFullYear(), nowValue.getMonth(), nowValue.getDate()];
31689}
31690
31691// convert an array to a date.
31692// the array should mirror the parameters below
31693// note: all values past the year are optional and will default to the lowest possible value.
31694// [year, month, day , hour, minute, second, millisecond]
31695function configFromArray (config) {
31696    var i, date, input = [], currentDate, yearToUse;
31697
31698    if (config._d) {
31699        return;
31700    }
31701
31702    currentDate = currentDateArray(config);
31703
31704    //compute day of the year from weeks and weekdays
31705    if (config._w && config._a[DATE] == null && config._a[MONTH] == null) {
31706        dayOfYearFromWeekInfo(config);
31707    }
31708
31709    //if the day of the year is set, figure out what it is
31710    if (config._dayOfYear != null) {
31711        yearToUse = defaults(config._a[YEAR], currentDate[YEAR]);
31712
31713        if (config._dayOfYear > daysInYear(yearToUse) || config._dayOfYear === 0) {
31714            getParsingFlags(config)._overflowDayOfYear = true;
31715        }
31716
31717        date = createUTCDate(yearToUse, 0, config._dayOfYear);
31718        config._a[MONTH] = date.getUTCMonth();
31719        config._a[DATE] = date.getUTCDate();
31720    }
31721
31722    // Default to current date.
31723    // * if no year, month, day of month are given, default to today
31724    // * if day of month is given, default month and year
31725    // * if month is given, default only year
31726    // * if year is given, don't default anything
31727    for (i = 0; i < 3 && config._a[i] == null; ++i) {
31728        config._a[i] = input[i] = currentDate[i];
31729    }
31730
31731    // Zero out whatever was not defaulted, including time
31732    for (; i < 7; i++) {
31733        config._a[i] = input[i] = (config._a[i] == null) ? (i === 2 ? 1 : 0) : config._a[i];
31734    }
31735
31736    // Check for 24:00:00.000
31737    if (config._a[HOUR] === 24 &&
31738            config._a[MINUTE] === 0 &&
31739            config._a[SECOND] === 0 &&
31740            config._a[MILLISECOND] === 0) {
31741        config._nextDay = true;
31742        config._a[HOUR] = 0;
31743    }
31744
31745    config._d = (config._useUTC ? createUTCDate : createDate).apply(null, input);
31746    // Apply timezone offset from input. The actual utcOffset can be changed
31747    // with parseZone.
31748    if (config._tzm != null) {
31749        config._d.setUTCMinutes(config._d.getUTCMinutes() - config._tzm);
31750    }
31751
31752    if (config._nextDay) {
31753        config._a[HOUR] = 24;
31754    }
31755
31756    // check for mismatching day of week
31757    if (config._w && typeof config._w.d !== 'undefined' && config._w.d !== config._d.getDay()) {
31758        getParsingFlags(config).weekdayMismatch = true;
31759    }
31760}
31761
31762function dayOfYearFromWeekInfo(config) {
31763    var w, weekYear, week, weekday, dow, doy, temp, weekdayOverflow;
31764
31765    w = config._w;
31766    if (w.GG != null || w.W != null || w.E != null) {
31767        dow = 1;
31768        doy = 4;
31769
31770        // TODO: We need to take the current isoWeekYear, but that depends on
31771        // how we interpret now (local, utc, fixed offset). So create
31772        // a now version of current config (take local/utc/offset flags, and
31773        // create now).
31774        weekYear = defaults(w.GG, config._a[YEAR], weekOfYear(createLocal(), 1, 4).year);
31775        week = defaults(w.W, 1);
31776        weekday = defaults(w.E, 1);
31777        if (weekday < 1 || weekday > 7) {
31778            weekdayOverflow = true;
31779        }
31780    } else {
31781        dow = config._locale._week.dow;
31782        doy = config._locale._week.doy;
31783
31784        var curWeek = weekOfYear(createLocal(), dow, doy);
31785
31786        weekYear = defaults(w.gg, config._a[YEAR], curWeek.year);
31787
31788        // Default to current week.
31789        week = defaults(w.w, curWeek.week);
31790
31791        if (w.d != null) {
31792            // weekday -- low day numbers are considered next week
31793            weekday = w.d;
31794            if (weekday < 0 || weekday > 6) {
31795                weekdayOverflow = true;
31796            }
31797        } else if (w.e != null) {
31798            // local weekday -- counting starts from begining of week
31799            weekday = w.e + dow;
31800            if (w.e < 0 || w.e > 6) {
31801                weekdayOverflow = true;
31802            }
31803        } else {
31804            // default to begining of week
31805            weekday = dow;
31806        }
31807    }
31808    if (week < 1 || week > weeksInYear(weekYear, dow, doy)) {
31809        getParsingFlags(config)._overflowWeeks = true;
31810    } else if (weekdayOverflow != null) {
31811        getParsingFlags(config)._overflowWeekday = true;
31812    } else {
31813        temp = dayOfYearFromWeeks(weekYear, week, weekday, dow, doy);
31814        config._a[YEAR] = temp.year;
31815        config._dayOfYear = temp.dayOfYear;
31816    }
31817}
31818
31819// iso 8601 regex
31820// 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)
31821var 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)?)?$/;
31822var 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)?)?$/;
31823
31824var tzRegex = /Z|[+-]\d\d(?::?\d\d)?/;
31825
31826var isoDates = [
31827    ['YYYYYY-MM-DD', /[+-]\d{6}-\d\d-\d\d/],
31828    ['YYYY-MM-DD', /\d{4}-\d\d-\d\d/],
31829    ['GGGG-[W]WW-E', /\d{4}-W\d\d-\d/],
31830    ['GGGG-[W]WW', /\d{4}-W\d\d/, false],
31831    ['YYYY-DDD', /\d{4}-\d{3}/],
31832    ['YYYY-MM', /\d{4}-\d\d/, false],
31833    ['YYYYYYMMDD', /[+-]\d{10}/],
31834    ['YYYYMMDD', /\d{8}/],
31835    // YYYYMM is NOT allowed by the standard
31836    ['GGGG[W]WWE', /\d{4}W\d{3}/],
31837    ['GGGG[W]WW', /\d{4}W\d{2}/, false],
31838    ['YYYYDDD', /\d{7}/]
31839];
31840
31841// iso time formats and regexes
31842var isoTimes = [
31843    ['HH:mm:ss.SSSS', /\d\d:\d\d:\d\d\.\d+/],
31844    ['HH:mm:ss,SSSS', /\d\d:\d\d:\d\d,\d+/],
31845    ['HH:mm:ss', /\d\d:\d\d:\d\d/],
31846    ['HH:mm', /\d\d:\d\d/],
31847    ['HHmmss.SSSS', /\d\d\d\d\d\d\.\d+/],
31848    ['HHmmss,SSSS', /\d\d\d\d\d\d,\d+/],
31849    ['HHmmss', /\d\d\d\d\d\d/],
31850    ['HHmm', /\d\d\d\d/],
31851    ['HH', /\d\d/]
31852];
31853
31854var aspNetJsonRegex = /^\/?Date\((\-?\d+)/i;
31855
31856// date from iso format
31857function configFromISO(config) {
31858    var i, l,
31859        string = config._i,
31860        match = extendedIsoRegex.exec(string) || basicIsoRegex.exec(string),
31861        allowTime, dateFormat, timeFormat, tzFormat;
31862
31863    if (match) {
31864        getParsingFlags(config).iso = true;
31865
31866        for (i = 0, l = isoDates.length; i < l; i++) {
31867            if (isoDates[i][1].exec(match[1])) {
31868                dateFormat = isoDates[i][0];
31869                allowTime = isoDates[i][2] !== false;
31870                break;
31871            }
31872        }
31873        if (dateFormat == null) {
31874            config._isValid = false;
31875            return;
31876        }
31877        if (match[3]) {
31878            for (i = 0, l = isoTimes.length; i < l; i++) {
31879                if (isoTimes[i][1].exec(match[3])) {
31880                    // match[2] should be 'T' or space
31881                    timeFormat = (match[2] || ' ') + isoTimes[i][0];
31882                    break;
31883                }
31884            }
31885            if (timeFormat == null) {
31886                config._isValid = false;
31887                return;
31888            }
31889        }
31890        if (!allowTime && timeFormat != null) {
31891            config._isValid = false;
31892            return;
31893        }
31894        if (match[4]) {
31895            if (tzRegex.exec(match[4])) {
31896                tzFormat = 'Z';
31897            } else {
31898                config._isValid = false;
31899                return;
31900            }
31901        }
31902        config._f = dateFormat + (timeFormat || '') + (tzFormat || '');
31903        configFromStringAndFormat(config);
31904    } else {
31905        config._isValid = false;
31906    }
31907}
31908
31909// RFC 2822 regex: For details see https://tools.ietf.org/html/rfc2822#section-3.3
31910var 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}))$/;
31911
31912function extractFromRFC2822Strings(yearStr, monthStr, dayStr, hourStr, minuteStr, secondStr) {
31913    var result = [
31914        untruncateYear(yearStr),
31915        defaultLocaleMonthsShort.indexOf(monthStr),
31916        parseInt(dayStr, 10),
31917        parseInt(hourStr, 10),
31918        parseInt(minuteStr, 10)
31919    ];
31920
31921    if (secondStr) {
31922        result.push(parseInt(secondStr, 10));
31923    }
31924
31925    return result;
31926}
31927
31928function untruncateYear(yearStr) {
31929    var year = parseInt(yearStr, 10);
31930    if (year <= 49) {
31931        return 2000 + year;
31932    } else if (year <= 999) {
31933        return 1900 + year;
31934    }
31935    return year;
31936}
31937
31938function preprocessRFC2822(s) {
31939    // Remove comments and folding whitespace and replace multiple-spaces with a single space
31940    return s.replace(/\([^)]*\)|[\n\t]/g, ' ').replace(/(\s\s+)/g, ' ').trim(
vendor: 6,917 bytes, lines 31940-32169
31940);
31941}
31942
31943function checkWeekday(weekdayStr, parsedInput, config) {
31944    if (weekdayStr) {
31945        // TODO: Replace the vanilla JS Date object with an indepentent day-of-week check.
31946        var weekdayProvided = defaultLocaleWeekdaysShort.indexOf(weekdayStr),
31947            weekdayActual = new Date(parsedInput[0], parsedInput[1], parsedInput[2]).getDay();
31948        if (weekdayProvided !== weekdayActual) {
31949            getParsingFlags(config).weekdayMismatch = true;
31950            config._isValid = false;
31951            return false;
31952        }
31953    }
31954    return true;
31955}
31956
31957var obsOffsets = {
31958    UT: 0,
31959    GMT: 0,
31960    EDT: -4 * 60,
31961    EST: -5 * 60,
31962    CDT: -5 * 60,
31963    CST: -6 * 60,
31964    MDT: -6 * 60,
31965    MST: -7 * 60,
31966    PDT: -7 * 60,
31967    PST: -8 * 60
31968};
31969
31970function calculateOffset(obsOffset, militaryOffset, numOffset) {
31971    if (obsOffset) {
31972        return obsOffsets[obsOffset];
31973    } else if (militaryOffset) {
31974        // the only allowed military tz is Z
31975        return 0;
31976    } else {
31977        var hm = parseInt(numOffset, 10);
31978        var m = hm % 100, h = (hm - m) / 100;
31979        return h * 60 + m;
31980    }
31981}
31982
31983// date and time from ref 2822 format
31984function configFromRFC2822(config) {
31985    var match = rfc2822.exec(preprocessRFC2822(config._i));
31986    if (match) {
31987        var parsedArray = extractFromRFC2822Strings(match[4], match[3], match[2], match[5], match[6], match[7]);
31988        if (!checkWeekday(match[1], parsedArray, config)) {
31989            return;
31990        }
31991
31992        config._a = parsedArray;
31993        config._tzm = calculateOffset(match[8], match[9], match[10]);
31994
31995        config._d = createUTCDate.apply(null, config._a);
31996        config._d.setUTCMinutes(config._d.getUTCMinutes() - config._tzm);
31997
31998        getParsingFlags(config).rfc2822 = true;
31999    } else {
32000        config._isValid = false;
32001    }
32002}
32003
32004// date from iso format or fallback
32005function configFromString(config) {
32006    var matched = aspNetJsonRegex.exec(config._i);
32007
32008    if (matched !== null) {
32009        config._d = new Date(+matched[1]);
32010        return;
32011    }
32012
32013    configFromISO(config);
32014    if (config._isValid === false) {
32015        delete config._isValid;
32016    } else {
32017        return;
32018    }
32019
32020    configFromRFC2822(config);
32021    if (config._isValid === false) {
32022        delete config._isValid;
32023    } else {
32024        return;
32025    }
32026
32027    // Final attempt, use Input Fallback
32028    hooks.createFromInputFallback(config);
32029}
32030
32031hooks.createFromInputFallback = deprecate(
32032    'value provided is not in a recognized RFC2822 or ISO format. moment construction falls back to js Date(), ' +
32033    'which is not reliable across all browsers and versions. Non RFC2822/ISO date formats are ' +
32034    'discouraged and will be removed in an upcoming major release. Please refer to ' +
32035    'http://momentjs.com/guides/#/warnings/js-date/ for more info.',
32036    function (config) {
32037        config._d = new Date(config._i + (config._useUTC ? ' UTC' : ''));
32038    }
32039);
32040
32041// constant that refers to the ISO standard
32042hooks.ISO_8601 = function () {};
32043
32044// constant that refers to the RFC 2822 form
32045hooks.RFC_2822 = function () {};
32046
32047// date from string and format string
32048function configFromStringAndFormat(config) {
32049    // TODO: Move this to another part of the creation flow to prevent circular deps
32050    if (config._f === hooks.ISO_8601) {
32051        configFromISO(config);
32052        return;
32053    }
32054    if (config._f === hooks.RFC_2822) {
32055        configFromRFC2822(config);
32056        return;
32057    }
32058    config._a = [];
32059    getParsingFlags(config).empty = true;
32060
32061    // This array is used to make a Date, either with `new Date` or `Date.UTC`
32062    var string = '' + config._i,
32063        i, parsedInput, tokens, token, skipped,
32064        stringLength = string.length,
32065        totalParsedInputLength = 0;
32066
32067    tokens = expandFormat(config._f, config._locale).match(formattingTokens) || [];
32068
32069    for (i = 0; i < tokens.length; i++) {
32070        token = tokens[i];
32071        parsedInput = (string.match(getParseRegexForToken(token, config)) || [])[0];
32072        // console.log('token', token, 'parsedInput', parsedInput,
32073        //         'regex', getParseRegexForToken(token, config));
32074        if (parsedInput) {
32075            skipped = string.substr(0, string.indexOf(parsedInput));
32076            if (skipped.length > 0) {
32077                getParsingFlags(config).unusedInput.push(skipped);
32078            }
32079            string = string.slice(string.indexOf(parsedInput) + parsedInput.length);
32080            totalParsedInputLength += parsedInput.length;
32081        }
32082        // don't parse if it's not a known token
32083        if (formatTokenFunctions[token]) {
32084            if (parsedInput) {
32085                getParsingFlags(config).empty = false;
32086            }
32087            else {
32088                getParsingFlags(config).unusedTokens.push(token);
32089            }
32090            addTimeToArrayFromToken(token, parsedInput, config);
32091        }
32092        else if (config._strict && !parsedInput) {
32093            getParsingFlags(config).unusedTokens.push(token);
32094        }
32095    }
32096
32097    // add remaining unparsed input length to the string
32098    getParsingFlags(config).charsLeftOver = stringLength - totalParsedInputLength;
32099    if (string.length > 0) {
32100        getParsingFlags(config).unusedInput.push(string);
32101    }
32102
32103    // clear _12h flag if hour is <= 12
32104    if (config._a[HOUR] <= 12 &&
32105        getParsingFlags(config).bigHour === true &&
32106        config._a[HOUR] > 0) {
32107        getParsingFlags(config).bigHour = undefined;
32108    }
32109
32110    getParsingFlags(config).parsedDateParts = config._a.slice(0);
32111    getParsingFlags(config).meridiem = config._meridiem;
32112    // handle meridiem
32113    config._a[HOUR] = meridiemFixWrap(config._locale, config._a[HOUR], config._meridiem);
32114
32115    configFromArray(config);
32116    checkOverflow(config);
32117}
32118
32119
32120function meridiemFixWrap (locale, hour, meridiem) {
32121    var isPm;
32122
32123    if (meridiem == null) {
32124        // nothing to do
32125        return hour;
32126    }
32127    if (locale.meridiemHour != null) {
32128        return locale.meridiemHour(hour, meridiem);
32129    } else if (locale.isPM != null) {
32130        // Fallback
32131        isPm = locale.isPM(meridiem);
32132        if (isPm && hour < 12) {
32133            hour += 12;
32134        }
32135        if (!isPm && hour === 12) {
32136            hour = 0;
32137        }
32138        return hour;
32139    } else {
32140        // this is not supposed to happen
32141        return hour;
32142    }
32143}
32144
32145// date from string and array of format strings
32146function configFromStringAndArray(config) {
32147    var tempConfig,
32148        bestMoment,
32149
32150        scoreToBeat,
32151        i,
32152        currentScore;
32153
32154    if (config._f.length === 0) {
32155        getParsingFlags(config).invalidFormat = true;
32156        config._d = new Date(NaN);
32157        return;
32158    }
32159
32160    for (i = 0; i < config._f.length; i++) {
32161        currentScore = 0;
32162        tempConfig = copyConfig({}, config);
32163        if (config._useUTC != null) {
32164            tempConfig._useUTC = config._useUTC;
32165        }
32166        tempConfig._f = config._f[i];
32167        configFromStringAndFormat(tempConfig);
32168
32169        if (!isValid
vendor: 24,430 bytes, lines 32169-32987
32169(tempConfig)) {
32170            continue;
32171        }
32172
32173        // if there is any input that was not parsed add a penalty for that format
32174        currentScore += getParsingFlags(tempConfig).charsLeftOver;
32175
32176        //or tokens
32177        currentScore += getParsingFlags(tempConfig).unusedTokens.length * 10;
32178
32179        getParsingFlags(tempConfig).score = currentScore;
32180
32181        if (scoreToBeat == null || currentScore < scoreToBeat) {
32182            scoreToBeat = currentScore;
32183            bestMoment = tempConfig;
32184        }
32185    }
32186
32187    extend(config, bestMoment || tempConfig);
32188}
32189
32190function configFromObject(config) {
32191    if (config._d) {
32192        return;
32193    }
32194
32195    var i = normalizeObjectUnits(config._i);
32196    config._a = map([i.year, i.month, i.day || i.date, i.hour, i.minute, i.second, i.millisecond], function (obj) {
32197        return obj && parseInt(obj, 10);
32198    });
32199
32200    configFromArray(config);
32201}
32202
32203function createFromConfig (config) {
32204    var res = new Moment(checkOverflow(prepareConfig(config)));
32205    if (res._nextDay) {
32206        // Adding is smart enough around DST
32207        res.add(1, 'd');
32208        res._nextDay = undefined;
32209    }
32210
32211    return res;
32212}
32213
32214function prepareConfig (config) {
32215    var input = config._i,
32216        format = config._f;
32217
32218    config._locale = config._locale || getLocale(config._l);
32219
32220    if (input === null || (format === undefined && input === '')) {
32221        return createInvalid({nullInput: true});
32222    }
32223
32224    if (typeof input === 'string') {
32225        config._i = input = config._locale.preparse(input);
32226    }
32227
32228    if (isMoment(input)) {
32229        return new Moment(checkOverflow(input));
32230    } else if (isDate(input)) {
32231        config._d = input;
32232    } else if (isArray(format)) {
32233        configFromStringAndArray(config);
32234    } else if (format) {
32235        configFromStringAndFormat(config);
32236    }  else {
32237        configFromInput(config);
32238    }
32239
32240    if (!isValid(config)) {
32241        config._d = null;
32242    }
32243
32244    return config;
32245}
32246
32247function configFromInput(config) {
32248    var input = config._i;
32249    if (isUndefined(input)) {
32250        config._d = new Date(hooks.now());
32251    } else if (isDate(input)) {
32252        config._d = new Date(input.valueOf());
32253    } else if (typeof input === 'string') {
32254        configFromString(config);
32255    } else if (isArray(input)) {
32256        config._a = map(input.slice(0), function (obj) {
32257            return parseInt(obj, 10);
32258        });
32259        configFromArray(config);
32260    } else if (isObject(input)) {
32261        configFromObject(config);
32262    } else if (isNumber(input)) {
32263        // from milliseconds
32264        config._d = new Date(input);
32265    } else {
32266        hooks.createFromInputFallback(config);
32267    }
32268}
32269
32270function createLocalOrUTC (input, format, locale, strict, isUTC) {
32271    var c = {};
32272
32273    if (locale === true || locale === false) {
32274        strict = locale;
32275        locale = undefined;
32276    }
32277
32278    if ((isObject(input) && isObjectEmpty(input)) ||
32279            (isArray(input) && input.length === 0)) {
32280        input = undefined;
32281    }
32282    // object construction must be done this way.
32283    // https://github.com/moment/moment/issues/1423
32284    c._isAMomentObject = true;
32285    c._useUTC = c._isUTC = isUTC;
32286    c._l = locale;
32287    c._i = input;
32288    c._f = format;
32289    c._strict = strict;
32290
32291    return createFromConfig(c);
32292}
32293
32294function createLocal (input, format, locale, strict) {
32295    return createLocalOrUTC(input, format, locale, strict, false);
32296}
32297
32298var prototypeMin = deprecate(
32299    'moment().min is deprecated, use moment.max instead. http://momentjs.com/guides/#/warnings/min-max/',
32300    function () {
32301        var other = createLocal.apply(null, arguments);
32302        if (this.isValid() && other.isValid()) {
32303            return other < this ? this : other;
32304        } else {
32305            return createInvalid();
32306        }
32307    }
32308);
32309
32310var prototypeMax = deprecate(
32311    'moment().max is deprecated, use moment.min instead. http://momentjs.com/guides/#/warnings/min-max/',
32312    function () {
32313        var other = createLocal.apply(null, arguments);
32314        if (this.isValid() && other.isValid()) {
32315            return other > this ? this : other;
32316        } else {
32317            return createInvalid();
32318        }
32319    }
32320);
32321
32322// Pick a moment m from moments so that m[fn](other) is true for all
32323// other. This relies on the function fn to be transitive.
32324//
32325// moments should either be an array of moment objects or an array, whose
32326// first element is an array of moment objects.
32327function pickBy(fn, moments) {
32328    var res, i;
32329    if (moments.length === 1 && isArray(moments[0])) {
32330        moments = moments[0];
32331    }
32332    if (!moments.length) {
32333        return createLocal();
32334    }
32335    res = moments[0];
32336    for (i = 1; i < moments.length; ++i) {
32337        if (!moments[i].isValid() || moments[i][fn](res)) {
32338            res = moments[i];
32339        }
32340    }
32341    return res;
32342}
32343
32344// TODO: Use [].sort instead?
32345function min () {
32346    var args = [].slice.call(arguments, 0);
32347
32348    return pickBy('isBefore', args);
32349}
32350
32351function max () {
32352    var args = [].slice.call(arguments, 0);
32353
32354    return pickBy('isAfter', args);
32355}
32356
32357var now = function () {
32358    return Date.now ? Date.now() : +(new Date());
32359};
32360
32361var ordering = ['year', 'quarter', 'month', 'week', 'day', 'hour', 'minute', 'second', 'millisecond'];
32362
32363function isDurationValid(m) {
32364    for (var key in m) {
32365        if (!(indexOf.call(ordering, key) !== -1 && (m[key] == null || !isNaN(m[key])))) {
32366            return false;
32367        }
32368    }
32369
32370    var unitHasDecimal = false;
32371    for (var i = 0; i < ordering.length; ++i) {
32372        if (m[ordering[i]]) {
32373            if (unitHasDecimal) {
32374                return false; // only allow non-integers for smallest unit
32375            }
32376            if (parseFloat(m[ordering[i]]) !== toInt(m[ordering[i]])) {
32377                unitHasDecimal = true;
32378            }
32379        }
32380    }
32381
32382    return true;
32383}
32384
32385function isValid$1() {
32386    return this._isValid;
32387}
32388
32389function createInvalid$1() {
32390    return createDuration(NaN);
32391}
32392
32393function Duration (duration) {
32394    var normalizedInput = normalizeObjectUnits(duration),
32395        years = normalizedInput.year || 0,
32396        quarters = normalizedInput.quarter || 0,
32397        months = normalizedInput.month || 0,
32398        weeks = normalizedInput.week || 0,
32399        days = normalizedInput.day || 0,
32400        hours = normalizedInput.hour || 0,
32401        minutes = normalizedInput.minute || 0,
32402        seconds = normalizedInput.second || 0,
32403        milliseconds = normalizedInput.millisecond || 0;
32404
32405    this._isValid = isDurationValid(normalizedInput);
32406
32407    // representation for dateAddRemove
32408    this._milliseconds = +milliseconds +
32409        seconds * 1e3 + // 1000
32410        minutes * 6e4 + // 1000 * 60
32411        hours * 1000 * 60 * 60; //using 1000 * 60 * 60 instead of 36e5 to avoid floating point rounding errors https://github.com/moment/moment/issues/2978
32412    // Because of dateAddRemove treats 24 hours as different from a
32413    // day when working around DST, we need to store them separately
32414    this._days = +days +
32415        weeks * 7;
32416    // It is impossible to translate months into days without knowing
32417    // which months you are are talking about, so we have to store
32418    // it separately.
32419    this._months = +months +
32420        quarters * 3 +
32421        years * 12;
32422
32423    this._data = {};
32424
32425    this._locale = getLocale();
32426
32427    this._bubble();
32428}
32429
32430function isDuration (obj) {
32431    return obj instanceof Duration;
32432}
32433
32434function absRound (number) {
32435    if (number < 0) {
32436        return Math.round(-1 * number) * -1;
32437    } else {
32438        return Math.round(number);
32439    }
32440}
32441
32442// FORMATTING
32443
32444function offset (token, separator) {
32445    addFormatToken(token, 0, 0, function () {
32446        var offset = this.utcOffset();
32447        var sign = '+';
32448        if (offset < 0) {
32449            offset = -offset;
32450            sign = '-';
32451        }
32452        return sign + zeroFill(~~(offset / 60), 2) + separator + zeroFill(~~(offset) % 60, 2);
32453    });
32454}
32455
32456offset('Z', ':');
32457offset('ZZ', '');
32458
32459// PARSING
32460
32461addRegexToken('Z',  matchShortOffset);
32462addRegexToken('ZZ', matchShortOffset);
32463addParseToken(['Z', 'ZZ'], function (input, array, config) {
32464    config._useUTC = true;
32465    config._tzm = offsetFromString(matchShortOffset, input);
32466});
32467
32468// HELPERS
32469
32470// timezone chunker
32471// '+10:00' > ['10',  '00']
32472// '-1530'  > ['-15', '30']
32473var chunkOffset = /([\+\-]|\d\d)/gi;
32474
32475function offsetFromString(matcher, string) {
32476    var matches = (string || '').match(matcher);
32477
32478    if (matches === null) {
32479        return null;
32480    }
32481
32482    var chunk   = matches[matches.length - 1] || [];
32483    var parts   = (chunk + '').match(chunkOffset) || ['-', 0, 0];
32484    var minutes = +(parts[1] * 60) + toInt(parts[2]);
32485
32486    return minutes === 0 ?
32487      0 :
32488      parts[0] === '+' ? minutes : -minutes;
32489}
32490
32491// Return a moment from input, that is local/utc/zone equivalent to model.
32492function cloneWithOffset(input, model) {
32493    var res, diff;
32494    if (model._isUTC) {
32495        res = model.clone();
32496        diff = (isMoment(input) || isDate(input) ? input.valueOf() : createLocal(input).valueOf()) - res.valueOf();
32497        // Use low-level api, because this fn is low-level api.
32498        res._d.setTime(res._d.valueOf() + diff);
32499        hooks.updateOffset(res, false);
32500        return res;
32501    } else {
32502        return createLocal(input).local();
32503    }
32504}
32505
32506function getDateOffset (m) {
32507    // On Firefox.24 Date#getTimezoneOffset returns a floating point.
32508    // https://github.com/moment/moment/pull/1871
32509    return -Math.round(m._d.getTimezoneOffset() / 15) * 15;
32510}
32511
32512// HOOKS
32513
32514// This function will be called whenever a moment is mutated.
32515// It is intended to keep the offset in sync with the timezone.
32516hooks.updateOffset = function () {};
32517
32518// MOMENTS
32519
32520// keepLocalTime = true means only change the timezone, without
32521// affecting the local hour. So 5:31:26 +0300 --[utcOffset(2, true)]-->
32522// 5:31:26 +0200 It is possible that 5:31:26 doesn't exist with offset
32523// +0200, so we adjust the time as needed, to be valid.
32524//
32525// Keeping the time actually adds/subtracts (one hour)
32526// from the actual represented time. That is why we call updateOffset
32527// a second time. In case it wants us to change the offset again
32528// _changeInProgress == true case, then we have to adjust, because
32529// there is no such time in the given timezone.
32530function getSetOffset (input, keepLocalTime, keepMinutes) {
32531    var offset = this._offset || 0,
32532        localAdjust;
32533    if (!this.isValid()) {
32534        return input != null ? this : NaN;
32535    }
32536    if (input != null) {
32537        if (typeof input === 'string') {
32538            input = offsetFromString(matchShortOffset, input);
32539            if (input === null) {
32540                return this;
32541            }
32542        } else if (Math.abs(input) < 16 && !keepMinutes) {
32543            input = input * 60;
32544        }
32545        if (!this._isUTC && keepLocalTime) {
32546            localAdjust = getDateOffset(this);
32547        }
32548        this._offset = input;
32549        this._isUTC = true;
32550        if (localAdjust != null) {
32551            this.add(localAdjust, 'm');
32552        }
32553        if (offset !== input) {
32554            if (!keepLocalTime || this._changeInProgress) {
32555                addSubtract(this, createDuration(input - offset, 'm'), 1, false);
32556            } else if (!this._changeInProgress) {
32557                this._changeInProgress = true;
32558                hooks.updateOffset(this, true);
32559                this._changeInProgress = null;
32560            }
32561        }
32562        return this;
32563    } else {
32564        return this._isUTC ? offset : getDateOffset(this);
32565    }
32566}
32567
32568function getSetZone (input, keepLocalTime) {
32569    if (input != null) {
32570        if (typeof input !== 'string') {
32571            input = -input;
32572        }
32573
32574        this.utcOffset(input, keepLocalTime);
32575
32576        return this;
32577    } else {
32578        return -this.utcOffset();
32579    }
32580}
32581
32582function setOffsetToUTC (keepLocalTime) {
32583    return this.utcOffset(0, keepLocalTime);
32584}
32585
32586function setOffsetToLocal (keepLocalTime) {
32587    if (this._isUTC) {
32588        this.utcOffset(0, keepLocalTime);
32589        this._isUTC = false;
32590
32591        if (keepLocalTime) {
32592            this.subtract(getDateOffset(this), 'm');
32593        }
32594    }
32595    return this;
32596}
32597
32598function setOffsetToParsedOffset () {
32599    if (this._tzm != null) {
32600        this.utcOffset(this._tzm, false, true);
32601    } else if (typeof this._i === 'string') {
32602        var tZone = offsetFromString(matchOffset, this._i);
32603        if (tZone != null) {
32604            this.utcOffset(tZone);
32605        }
32606        else {
32607            this.utcOffset(0, true);
32608        }
32609    }
32610    return this;
32611}
32612
32613function hasAlignedHourOffset (input) {
32614    if (!this.isValid()) {
32615        return false;
32616    }
32617    input = input ? createLocal(input).utcOffset() : 0;
32618
32619    return (this.utcOffset() - input) % 60 === 0;
32620}
32621
32622function isDaylightSavingTime () {
32623    return (
32624        this.utcOffset() > this.clone().month(0).utcOffset() ||
32625        this.utcOffset() > this.clone().month(5).utcOffset()
32626    );
32627}
32628
32629function isDaylightSavingTimeShifted () {
32630    if (!isUndefined(this._isDSTShifted)) {
32631        return this._isDSTShifted;
32632    }
32633
32634    var c = {};
32635
32636    copyConfig(c, this);
32637    c = prepareConfig(c);
32638
32639    if (c._a) {
32640        var other = c._isUTC ? createUTC(c._a) : createLocal(c._a);
32641        this._isDSTShifted = this.isValid() &&
32642            compareArrays(c._a, other.toArray()) > 0;
32643    } else {
32644        this._isDSTShifted = false;
32645    }
32646
32647    return this._isDSTShifted;
32648}
32649
32650function isLocal () {
32651    return this.isValid() ? !this._isUTC : false;
32652}
32653
32654function isUtcOffset () {
32655    return this.isValid() ? this._isUTC : false;
32656}
32657
32658function isUtc () {
32659    return this.isValid() ? this._isUTC && this._offset === 0 : false;
32660}
32661
32662// ASP.NET json date format regex
32663var aspNetRegex = /^(\-|\+)?(?:(\d*)[. ])?(\d+)\:(\d+)(?:\:(\d+)(\.\d*)?)?$/;
32664
32665// from http://docs.closure-library.googlecode.com/git/closure_goog_date_date.js.source.html
32666// somewhat more in line with 4.4.3.2 2004 spec, but allows decimal anywhere
32667// and further modified to allow for strings containing both week and day
32668var isoRegex = /^(-|\+)?P(?:([-+]?[0-9,.]*)Y)?(?:([-+]?[0-9,.]*)M)?(?:([-+]?[0-9,.]*)W)?(?:([-+]?[0-9,.]*)D)?(?:T(?:([-+]?[0-9,.]*)H)?(?:([-+]?[0-9,.]*)M)?(?:([-+]?[0-9,.]*)S)?)?$/;
32669
32670function createDuration (input, key) {
32671    var duration = input,
32672        // matching against regexp is expensive, do it on demand
32673        match = null,
32674        sign,
32675        ret,
32676        diffRes;
32677
32678    if (isDuration(input)) {
32679        duration = {
32680            ms : input._milliseconds,
32681            d  : input._days,
32682            M  : input._months
32683        };
32684    } else if (isNumber(input)) {
32685        duration = {};
32686        if (key) {
32687            duration[key] = input;
32688        } else {
32689            duration.milliseconds = input;
32690        }
32691    } else if (!!(match = aspNetRegex.exec(input))) {
32692        sign = (match[1] === '-') ? -1 : 1;
32693        duration = {
32694            y  : 0,
32695            d  : toInt(match[DATE])                         * sign,
32696            h  : toInt(match[HOUR])                         * sign,
32697            m  : toInt(match[MINUTE])                       * sign,
32698            s  : toInt(match[SECOND])                       * sign,
32699            ms : toInt(absRound(match[MILLISECOND] * 1000)) * sign // the millisecond decimal point is included in the match
32700        };
32701    } else if (!!(match = isoRegex.exec(input))) {
32702        sign = (match[1] === '-') ? -1 : (match[1] === '+') ? 1 : 1;
32703        duration = {
32704            y : parseIso(match[2], sign),
32705            M : parseIso(match[3], sign),
32706            w : parseIso(match[4], sign),
32707            d : parseIso(match[5], sign),
32708            h : parseIso(match[6], sign),
32709            m : parseIso(match[7], sign),
32710            s : parseIso(match[8], sign)
32711        };
32712    } else if (duration == null) {// checks for null or undefined
32713        duration = {};
32714    } else if (typeof duration === 'object' && ('from' in duration || 'to' in duration)) {
32715        diffRes = momentsDifference(createLocal(duration.from), createLocal(duration.to));
32716
32717        duration = {};
32718        duration.ms = diffRes.milliseconds;
32719        duration.M = diffRes.months;
32720    }
32721
32722    ret = new Duration(duration);
32723
32724    if (isDuration(input) && hasOwnProp(input, '_locale')) {
32725        ret._locale = input._locale;
32726    }
32727
32728    return ret;
32729}
32730
32731createDuration.fn = Duration.prototype;
32732createDuration.invalid = createInvalid$1;
32733
32734function parseIso (inp, sign) {
32735    // We'd normally use ~~inp for this, but unfortunately it also
32736    // converts floats to ints.
32737    // inp may be undefined, so careful calling replace on it.
32738    var res = inp && parseFloat(inp.replace(',', '.'));
32739    // apply sign while we're at it
32740    return (isNaN(res) ? 0 : res) * sign;
32741}
32742
32743function positiveMomentsDifference(base, other) {
32744    var res = {milliseconds: 0, months: 0};
32745
32746    res.months = other.month() - base.month() +
32747        (other.year() - base.year()) * 12;
32748    if (base.clone().add(res.months, 'M').isAfter(other)) {
32749        --res.months;
32750    }
32751
32752    res.milliseconds = +other - +(base.clone().add(res.months, 'M'));
32753
32754    return res;
32755}
32756
32757function momentsDifference(base, other) {
32758    var res;
32759    if (!(base.isValid() && other.isValid())) {
32760        return {milliseconds: 0, months: 0};
32761    }
32762
32763    other = cloneWithOffset(other, base);
32764    if (base.isBefore(other)) {
32765        res = positiveMomentsDifference(base, other);
32766    } else {
32767        res = positiveMomentsDifference(other, base);
32768        res.milliseconds = -res.milliseconds;
32769        res.months = -res.months;
32770    }
32771
32772    return res;
32773}
32774
32775// TODO: remove 'name' arg after deprecation is removed
32776function createAdder(direction, name) {
32777    return function (val, period) {
32778        var dur, tmp;
32779        //invert the arguments, but complain about it
32780        if (period !== null && !isNaN(+period)) {
32781            deprecateSimple(name, 'moment().' + name  + '(period, number) is deprecated. Please use moment().' + name + '(number, period). ' +
32782            'See http://momentjs.com/guides/#/warnings/add-inverted-param/ for more info.');
32783            tmp = val; val = period; period = tmp;
32784        }
32785
32786        val = typeof val === 'string' ? +val : val;
32787        dur = createDuration(val, period);
32788        addSubtract(this, dur, direction);
32789        return this;
32790    };
32791}
32792
32793function addSubtract (mom, duration, isAdding, updateOffset) {
32794    var milliseconds = duration._milliseconds,
32795        days = absRound(duration._days),
32796        months = absRound(duration._months);
32797
32798    if (!mom.isValid()) {
32799        // No op
32800        return;
32801    }
32802
32803    updateOffset = updateOffset == null ? true : updateOffset;
32804
32805    if (months) {
32806        setMonth(mom, get(mom, 'Month') + months * isAdding);
32807    }
32808    if (days) {
32809        set$1(mom, 'Date', get(mom, 'Date') + days * isAdding);
32810    }
32811    if (milliseconds) {
32812        mom._d.setTime(mom._d.valueOf() + milliseconds * isAdding);
32813    }
32814    if (updateOffset) {
32815        hooks.updateOffset(mom, days || months);
32816    }
32817}
32818
32819var add      = createAdder(1, 'add');
32820var subtract = createAdder(-1, 'subtract');
32821
32822function getCalendarFormat(myMoment, now) {
32823    var diff = myMoment.diff(now, 'days', true);
32824    return diff < -6 ? 'sameElse' :
32825            diff < -1 ? 'lastWeek' :
32826            diff < 0 ? 'lastDay' :
32827            diff < 1 ? 'sameDay' :
32828            diff < 2 ? 'nextDay' :
32829            diff < 7 ? 'nextWeek' : 'sameElse';
32830}
32831
32832function calendar$1 (time, formats) {
32833    // We want to compare the start of today, vs this.
32834    // Getting start-of-today depends on whether we're local/utc/offset or not.
32835    var now = time || createLocal(),
32836        sod = cloneWithOffset(now, this).startOf('day'),
32837        format = hooks.calendarFormat(this, sod) || 'sameElse';
32838
32839    var output = formats && (isFunction(formats[format]) ? formats[format].call(this, now) : formats[format]);
32840
32841    return this.format(output || this.localeData().calendar(format, this, createLocal(now)));
32842}
32843
32844function clone () {
32845    return new Moment(this);
32846}
32847
32848function isAfter (input, units) {
32849    var localInput = isMoment(input) ? input : createLocal(input);
32850    if (!(this.isValid() && localInput.isValid())) {
32851        return false;
32852    }
32853    units = normalizeUnits(!isUndefined(units) ? units : 'millisecond');
32854    if (units === 'millisecond') {
32855        return this.valueOf() > localInput.valueOf();
32856    } else {
32857        return localInput.valueOf() < this.clone().startOf(units).valueOf();
32858    }
32859}
32860
32861function isBefore (input, units) {
32862    var localInput = isMoment(input) ? input : createLocal(input);
32863    if (!(this.isValid() && localInput.isValid())) {
32864        return false;
32865    }
32866    units = normalizeUnits(!isUndefined(units) ? units : 'millisecond');
32867    if (units === 'millisecond') {
32868        return this.valueOf() < localInput.valueOf();
32869    } else {
32870        return this.clone().endOf(units).valueOf() < localInput.valueOf();
32871    }
32872}
32873
32874function isBetween (from, to, units, inclusivity) {
32875    inclusivity = inclusivity || '()';
32876    return (inclusivity[0] === '(' ? this.isAfter(from, units) : !this.isBefore(from, units)) &&
32877        (inclusivity[1] === ')' ? this.isBefore(to, units) : !this.isAfter(to, units));
32878}
32879
32880function isSame (input, units) {
32881    var localInput = isMoment(input) ? input : createLocal(input),
32882        inputMs;
32883    if (!(this.isValid() && localInput.isValid())) {
32884        return false;
32885    }
32886    units = normalizeUnits(units || 'millisecond');
32887    if (units === 'millisecond') {
32888        return this.valueOf() === localInput.valueOf();
32889    } else {
32890        inputMs = localInput.valueOf();
32891        return this.clone().startOf(units).valueOf() <= inputMs && inputMs <= this.clone().endOf(units).valueOf();
32892    }
32893}
32894
32895function isSameOrAfter (input, units) {
32896    return this.isSame(input, units) || this.isAfter(input,units);
32897}
32898
32899function isSameOrBefore (input, units) {
32900    return this.isSame(input, units) || this.isBefore(input,units);
32901}
32902
32903function diff (input, units, asFloat) {
32904    var that,
32905        zoneDelta,
32906        delta, output;
32907
32908    if (!this.isValid()) {
32909        return NaN;
32910    }
32911
32912    that = cloneWithOffset(input, this);
32913
32914    if (!that.isValid()) {
32915        return NaN;
32916    }
32917
32918    zoneDelta = (that.utcOffset() - this.utcOffset()) * 6e4;
32919
32920    units = normalizeUnits(units);
32921
32922    switch (units) {
32923        case 'year': output = monthDiff(this, that) / 12; break;
32924        case 'month': output = monthDiff(this, that); break;
32925        case 'quarter': output = monthDiff(this, that) / 3; break;
32926        case 'second': output = (this - that) / 1e3; break; // 1000
32927        case 'minute': output = (this - that) / 6e4; break; // 1000 * 60
32928        case 'hour': output = (this - that) / 36e5; break; // 1000 * 60 * 60
32929        case 'day': output = (this - that - zoneDelta) / 864e5; break; // 1000 * 60 * 60 * 24, negate dst
32930        case 'week': output = (this - that - zoneDelta) / 6048e5; break; // 1000 * 60 * 60 * 24 * 7, negate dst
32931        default: output = this - that;
32932    }
32933
32934    return asFloat ? output : absFloor(output);
32935}
32936
32937function monthDiff (a, b) {
32938    // difference in months
32939    var wholeMonthDiff = ((b.year() - a.year()) * 12) + (b.month() - a.month()),
32940        // b is in (anchor - 1 month, anchor + 1 month)
32941        anchor = a.clone().add(wholeMonthDiff, 'months'),
32942        anchor2, adjust;
32943
32944    if (b - anchor < 0) {
32945        anchor2 = a.clone().add(wholeMonthDiff - 1, 'months');
32946        // linear across the month
32947        adjust = (b - anchor) / (anchor - anchor2);
32948    } else {
32949        anchor2 = a.clone().add(wholeMonthDiff + 1, 'months');
32950        // linear across the month
32951        adjust = (b - anchor) / (anchor2 - anchor);
32952    }
32953
32954    //check for negative zero, return zero if negative zero
32955    return -(wholeMonthDiff + adjust) || 0;
32956}
32957
32958hooks.defaultFormat = 'YYYY-MM-DDTHH:mm:ssZ';
32959hooks.defaultFormatUtc = 'YYYY-MM-DDTHH:mm:ss[Z]';
32960
32961function toString () {
32962    return this.clone().locale('en').format('ddd MMM DD YYYY HH:mm:ss [GMT]ZZ');
32963}
32964
32965function toISOString() {
32966    if (!this.isValid()) {
32967        return null;
32968    }
32969    var m = this.clone().utc();
32970    if (m.year() < 0 || m.year() > 9999) {
32971        return formatMoment(m, 'YYYYYY-MM-DD[T]HH:mm:ss.SSS[Z]');
32972    }
32973    if (isFunction(Date.prototype.toISOString)) {
32974        // native implementation is ~50x faster, use it when we can
32975        return this.toDate().toISOString();
32976    }
32977    return formatMoment(m, 'YYYY-MM-DD[T]HH:mm:ss.SSS[Z]');
32978}
32979
32980/**
32981 * Return a human readable representation of a moment that can
32982 * also be evaluated to get a new moment which is the same
32983 *
32984 * @link https://nodejs.org/dist/latest/docs/api/util.html#util_custom_inspect_function_on_objects
32985 */
32986function inspect () {
32987    if (!this.isValid()) {
32988        return 'moment.invalid(/* ' + this._i + ' */)';
32989    }
32990    var func = 'moment';
32991    var zone = '';
32992    if (!this.isLocal()) {
32993        func = this.utcOffset() === 0 ? 'moment.utc' : 'moment.parseZone';
32994        zone = 'Z';
32995    }
32996    var prefix = '[' + func + '("]';
32997    var year = (0 <= this.year() && this.year() <= 9999) ? 'YYYY' : 'YYYYYY';
32998    var datetime = '-MM-DD[T]HH:mm:ss.SSS';
32999    var suffix = zone + '[")]';
33000
33001    return this.format(prefix + year + datetime + suffix);
33002}
33003
33004function format (inputString) {
33005    if (!inputString) {
33006        inputString = this.isUtc() ? hooks.defaultFormatUtc : hooks.defaultFormat;
33007    }
33008    var output = formatMoment(this, inputString);
33009    return this.localeData().postformat(output);
33010}
33011
33012function from (time, withoutSuffix) {
33013    if (this.isValid() &&
33014            ((isMoment(time) && time.isValid()) ||
33015             createLocal(time).isValid())) {
33016        return createDuration({to: this, from: time}).locale(this.locale()).humanize(!withoutSuffix);
33017    } else {
33018        return this.localeData().invalidDate();
33019    }
33020}
33021
33022function fromNow (withoutSuffix) {
33023    return this.from(createLocal(), withoutSuffix);
33024}
33025
33026function to (time, withoutSuffix) {
33027    if (this.isValid() &&
33028            ((isMoment(time) && time.isValid()) ||
33029             createLocal(time).isValid())) {
33030        return createDuration({from: this, to: time}).locale(this.locale()).humanize(!withoutSuffix);
33031    } else {
33032        return this.localeData().invalidDate();
33033    }
33034}
33035
33036function toNow (withoutSuffix) {
33037    return this.to(createLocal(), withoutSuffix);
33038}
33039
33040// If passed a locale key, it will set the locale for this
33041// instance.  Otherwise, it will return the locale configuration
33042// variables for this instance.
33043function locale (key) {
33044    var newLocaleData;
33045
33046    if (key === undefined) {
33047        return this._locale._abbr;
33048    } else {
33049        newLocaleData = getLocale(key);
33050        if (newLocaleData != null) {
33051            this._locale = newLocaleData;
33052        }
33053        return this;
33054    }
33055}
33056
33057var lang = deprecate(
33058    'moment().lang() is deprecated. Instead, use moment().localeData() to get the language configuration. Use moment().locale() to change languages.',
33059    function (key) {
33060        if (key === undefined) {
33061            return this.localeData();
33062        } else {
33063            return this.locale(key);
33064        }
33065    }
33066);
33067
33068function localeData () {
33069    return this._locale;
33070}
33071
33072function startOf (units) {
33073    units = normalizeUnits(units);
33074    // the following switch intentionally omits break keywords
33075    // to utilize falling through the cases.
33076    switch (units) {
33077        case 'year':
33078            this.month(0);
33079            /* falls through */
33080        case 'quarter':
33081        case 'month':
33082            this.date(1);
33083            /* falls through */
33084        case 'week':
33085        case 'isoWeek':
33086        case 'day':
33087        case 'date':
33088            this.hours(0);
33089            /* falls through */
33090        case 'hour':
33091            this.minutes(0);
33092            /* falls through */
33093        case 'minute':
33094            this.seconds(0);
33095            /* falls through */
33096        case 'second':
33097            this.milliseconds(0);
33098    }
33099
33100    // weeks are a special case
33101    if (units === 'week') {
33102        this.weekday(0);
33103    }
33104    if (units === 'isoWeek') {
33105        this.isoWeekday(1);
33106    }
33107
33108    // quarters are also special
33109    if (units === 'quarter') {
33110        this.month(Math.floor(this.month() / 3) * 3);
33111    }
33112
33113    return this;
33114}
33115
33116function endOf (units) {
33117    units = normalizeUnits(units);
33118    if (units === undefined || units === 'millisecond') {
33119        return this;
33120    }
33121
33122    // 'date' is an alias for 'day', so it should be considered as such.
33123    if (units === 'date') {
33124        units = 'day';
33125    }
33126
33127    return this.startOf(units).add(1, (units === 'isoWeek' ? 'week' : units)).subtract(1, 'ms');
33128}
33129
33130function valueOf () {
33131    return this._d.valueOf() - ((this._offset || 0) * 60000);
33132}
33133
33134function unix () {
33135    return Math.floor(this.valueOf() / 1000);
33136}
33137
33138function toDate () {
33139    return new Date(this.valueOf());
33140}
33141
33142function toArray () {
33143    var m = this;
33144    return [m.year(), m.month(), m.date(), m.hour(), m.minute(), m.second(), m.millisecond()];
33145}
33146
33147function toObject () {
33148    var m = this;
33149    return {
33150        years: m.year(),
33151        months: m.month(),
33152        date: m.date(),
33153        hours: m.hours(),
33154        minutes: m.minutes(),
33155        seconds: m.seconds(),
33156        milliseconds: m.milliseconds()
33157    };
33158}
33159
33160function toJSON () {
33161    // new Date(NaN).toJSON() === null
33162    return this.isValid() ? this.toISOString() : null;
33163}
33164
33165function isValid$2 () {
33166    return isValid(this);
33167}
33168
33169function parsingFlags () {
33170    return extend({}, getParsingFlags(this));
33171}
33172
vendor: 26,181 bytes, lines 33173-34134
33173function invalidAt () {
33174    return getParsingFlags(this).overflow;
33175}
33176
33177function creationData() {
33178    return {
33179        input: this._i,
33180        format: this._f,
33181        locale: this._locale,
33182        isUTC: this._isUTC,
33183        strict: this._strict
33184    };
33185}
33186
33187// FORMATTING
33188
33189addFormatToken(0, ['gg', 2], 0, function () {
33190    return this.weekYear() % 100;
33191});
33192
33193addFormatToken(0, ['GG', 2], 0, function () {
33194    return this.isoWeekYear() % 100;
33195});
33196
33197function addWeekYearFormatToken (token, getter) {
33198    addFormatToken(0, [token, token.length], 0, getter);
33199}
33200
33201addWeekYearFormatToken('gggg',     'weekYear');
33202addWeekYearFormatToken('ggggg',    'weekYear');
33203addWeekYearFormatToken('GGGG',  'isoWeekYear');
33204addWeekYearFormatToken('GGGGG', 'isoWeekYear');
33205
33206// ALIASES
33207
33208addUnitAlias('weekYear', 'gg');
33209addUnitAlias('isoWeekYear', 'GG');
33210
33211// PRIORITY
33212
33213addUnitPriority('weekYear', 1);
33214addUnitPriority('isoWeekYear', 1);
33215
33216
33217// PARSING
33218
33219addRegexToken('G',      matchSigned);
33220addRegexToken('g',      matchSigned);
33221addRegexToken('GG',     match1to2, match2);
33222addRegexToken('gg',     match1to2, match2);
33223addRegexToken('GGGG',   match1to4, match4);
33224addRegexToken('gggg',   match1to4, match4);
33225addRegexToken('GGGGG',  match1to6, match6);
33226addRegexToken('ggggg',  match1to6, match6);
33227
33228addWeekParseToken(['gggg', 'ggggg', 'GGGG', 'GGGGG'], function (input, week, config, token) {
33229    week[token.substr(0, 2)] = toInt(input);
33230});
33231
33232addWeekParseToken(['gg', 'GG'], function (input, week, config, token) {
33233    week[token] = hooks.parseTwoDigitYear(input);
33234});
33235
33236// MOMENTS
33237
33238function getSetWeekYear (input) {
33239    return getSetWeekYearHelper.call(this,
33240            input,
33241            this.week(),
33242            this.weekday(),
33243            this.localeData()._week.dow,
33244            this.localeData()._week.doy);
33245}
33246
33247function getSetISOWeekYear (input) {
33248    return getSetWeekYearHelper.call(this,
33249            input, this.isoWeek(), this.isoWeekday(), 1, 4);
33250}
33251
33252function getISOWeeksInYear () {
33253    return weeksInYear(this.year(), 1, 4);
33254}
33255
33256function getWeeksInYear () {
33257    var weekInfo = this.localeData()._week;
33258    return weeksInYear(this.year(), weekInfo.dow, weekInfo.doy);
33259}
33260
33261function getSetWeekYearHelper(input, week, weekday, dow, doy) {
33262    var weeksTarget;
33263    if (input == null) {
33264        return weekOfYear(this, dow, doy).year;
33265    } else {
33266        weeksTarget = weeksInYear(input, dow, doy);
33267        if (week > weeksTarget) {
33268            week = weeksTarget;
33269        }
33270        return setWeekAll.call(this, input, week, weekday, dow, doy);
33271    }
33272}
33273
33274function setWeekAll(weekYear, week, weekday, dow, doy) {
33275    var dayOfYearData = dayOfYearFromWeeks(weekYear, week, weekday, dow, doy),
33276        date = createUTCDate(dayOfYearData.year, 0, dayOfYearData.dayOfYear);
33277
33278    this.year(date.getUTCFullYear());
33279    this.month(date.getUTCMonth());
33280    this.date(date.getUTCDate());
33281    return this;
33282}
33283
33284// FORMATTING
33285
33286addFormatToken('Q', 0, 'Qo', 'quarter');
33287
33288// ALIASES
33289
33290addUnitAlias('quarter', 'Q');
33291
33292// PRIORITY
33293
33294addUnitPriority('quarter', 7);
33295
33296// PARSING
33297
33298addRegexToken('Q', match1);
33299addParseToken('Q', function (input, array) {
33300    array[MONTH] = (toInt(input) - 1) * 3;
33301});
33302
33303// MOMENTS
33304
33305function getSetQuarter (input) {
33306    return input == null ? Math.ceil((this.month() + 1) / 3) : this.month((input - 1) * 3 + this.month() % 3);
33307}
33308
33309// FORMATTING
33310
33311addFormatToken('D', ['DD', 2], 'Do', 'date');
33312
33313// ALIASES
33314
33315addUnitAlias('date', 'D');
33316
33317// PRIOROITY
33318addUnitPriority('date', 9);
33319
33320// PARSING
33321
33322addRegexToken('D',  match1to2);
33323addRegexToken('DD', match1to2, match2);
33324addRegexToken('Do', function (isStrict, locale) {
33325    // TODO: Remove "ordinalParse" fallback in next major release.
33326    return isStrict ?
33327      (locale._dayOfMonthOrdinalParse || locale._ordinalParse) :
33328      locale._dayOfMonthOrdinalParseLenient;
33329});
33330
33331addParseToken(['D', 'DD'], DATE);
33332addParseToken('Do', function (input, array) {
33333    array[DATE] = toInt(input.match(match1to2)[0], 10);
33334});
33335
33336// MOMENTS
33337
33338var getSetDayOfMonth = makeGetSet('Date', true);
33339
33340// FORMATTING
33341
33342addFormatToken('DDD', ['DDDD', 3], 'DDDo', 'dayOfYear');
33343
33344// ALIASES
33345
33346addUnitAlias('dayOfYear', 'DDD');
33347
33348// PRIORITY
33349addUnitPriority('dayOfYear', 4);
33350
33351// PARSING
33352
33353addRegexToken('DDD',  match1to3);
33354addRegexToken('DDDD', match3);
33355addParseToken(['DDD', 'DDDD'], function (input, array, config) {
33356    config._dayOfYear = toInt(input);
33357});
33358
33359// HELPERS
33360
33361// MOMENTS
33362
33363function getSetDayOfYear (input) {
33364    var dayOfYear = Math.round((this.clone().startOf('day') - this.clone().startOf('year')) / 864e5) + 1;
33365    return input == null ? dayOfYear : this.add((input - dayOfYear), 'd');
33366}
33367
33368// FORMATTING
33369
33370addFormatToken('m', ['mm', 2], 0, 'minute');
33371
33372// ALIASES
33373
33374addUnitAlias('minute', 'm');
33375
33376// PRIORITY
33377
33378addUnitPriority('minute', 14);
33379
33380// PARSING
33381
33382addRegexToken('m',  match1to2);
33383addRegexToken('mm', match1to2, match2);
33384addParseToken(['m', 'mm'], MINUTE);
33385
33386// MOMENTS
33387
33388var getSetMinute = makeGetSet('Minutes', false);
33389
33390// FORMATTING
33391
33392addFormatToken('s', ['ss', 2], 0, 'second');
33393
33394// ALIASES
33395
33396addUnitAlias('second', 's');
33397
33398// PRIORITY
33399
33400addUnitPriority('second', 15);
33401
33402// PARSING
33403
33404addRegexToken('s',  match1to2);
33405addRegexToken('ss', match1to2, match2);
33406addParseToken(['s', 'ss'], SECOND);
33407
33408// MOMENTS
33409
33410var getSetSecond = makeGetSet('Seconds', false);
33411
33412// FORMATTING
33413
33414addFormatToken('S', 0, 0, function () {
33415    return ~~(this.millisecond() / 100);
33416});
33417
33418addFormatToken(0, ['SS', 2], 0, function () {
33419    return ~~(this.millisecond() / 10);
33420});
33421
33422addFormatToken(0, ['SSS', 3], 0, 'millisecond');
33423addFormatToken(0, ['SSSS', 4], 0, function () {
33424    return this.millisecond() * 10;
33425});
33426addFormatToken(0, ['SSSSS', 5], 0, function () {
33427    return this.millisecond() * 100;
33428});
33429addFormatToken(0, ['SSSSSS', 6], 0, function () {
33430    return this.millisecond() * 1000;
33431});
33432addFormatToken(0, ['SSSSSSS', 7], 0, function () {
33433    return this.millisecond() * 10000;
33434});
33435addFormatToken(0, ['SSSSSSSS', 8], 0, function () {
33436    return this.millisecond() * 100000;
33437});
33438addFormatToken(0, ['SSSSSSSSS', 9], 0, function () {
33439    return this.millisecond() * 1000000;
33440});
33441
33442
33443// ALIASES
33444
33445addUnitAlias('millisecond', 'ms');
33446
33447// PRIORITY
33448
33449addUnitPriority('millisecond', 16);
33450
33451// PARSING
33452
33453addRegexToken('S',    match1to3, match1);
33454addRegexToken('SS',   match1to3, match2);
33455addRegexToken('SSS',  match1to3, match3);
33456
33457var token;
33458for (token = 'SSSS'; token.length <= 9; token += 'S') {
33459    addRegexToken(token, matchUnsigned);
33460}
33461
33462function parseMs(input, array) {
33463    array[MILLISECOND] = toInt(('0.' + input) * 1000);
33464}
33465
33466for (token = 'S'; token.length <= 9; token += 'S') {
33467    addParseToken(token, parseMs);
33468}
33469// MOMENTS
33470
33471var getSetMillisecond = makeGetSet('Milliseconds', false);
33472
33473// FORMATTING
33474
33475addFormatToken('z',  0, 0, 'zoneAbbr');
33476addFormatToken('zz', 0, 0, 'zoneName');
33477
33478// MOMENTS
33479
33480function getZoneAbbr () {
33481    return this._isUTC ? 'UTC' : '';
33482}
33483
33484function getZoneName () {
33485    return this._isUTC ? 'Coordinated Universal Time' : '';
33486}
33487
33488var proto = Moment.prototype;
33489
33490proto.add               = add;
33491proto.calendar          = calendar$1;
33492proto.clone             = clone;
33493proto.diff              = diff;
33494proto.endOf             = endOf;
33495proto.format            = format;
33496proto.from              = from;
33497proto.fromNow           = fromNow;
33498proto.to                = to;
33499proto.toNow             = toNow;
33500proto.get               = stringGet;
33501proto.invalidAt         = invalidAt;
33502proto.isAfter           = isAfter;
33503proto.isBefore          = isBefore;
33504proto.isBetween         = isBetween;
33505proto.isSame            = isSame;
33506proto.isSameOrAfter     = isSameOrAfter;
33507proto.isSameOrBefore    = isSameOrBefore;
33508proto.isValid           = isValid$2;
33509proto.lang              = lang;
33510proto.locale            = locale;
33511proto.localeData        = localeData;
33512proto.max               = prototypeMax;
33513proto.min               = prototypeMin;
33514proto.parsingFlags      = parsingFlags;
33515proto.set               = stringSet;
33516proto.startOf           = startOf;
33517proto.subtract          = subtract;
33518proto.toArray           = toArray;
33519proto.toObject          = toObject;
33520proto.toDate            = toDate;
33521proto.toISOString       = toISOString;
33522proto.inspect           = inspect;
33523proto.toJSON            = toJSON;
33524proto.toString          = toString;
33525proto.unix              = unix;
33526proto.valueOf           = valueOf;
33527proto.creationData      = creationData;
33528
33529// Year
33530proto.year       = getSetYear;
33531proto.isLeapYear = getIsLeapYear;
33532
33533// Week Year
33534proto.weekYear    = getSetWeekYear;
33535proto.isoWeekYear = getSetISOWeekYear;
33536
33537// Quarter
33538proto.quarter = proto.quarters = getSetQuarter;
33539
33540// Month
33541proto.month       = getSetMonth;
33542proto.daysInMonth = getDaysInMonth;
33543
33544// Week
33545proto.week           = proto.weeks        = getSetWeek;
33546proto.isoWeek        = proto.isoWeeks     = getSetISOWeek;
33547proto.weeksInYear    = getWeeksInYear;
33548proto.isoWeeksInYear = getISOWeeksInYear;
33549
33550// Day
33551proto.date       = getSetDayOfMonth;
33552proto.day        = proto.days             = getSetDayOfWeek;
33553proto.weekday    = getSetLocaleDayOfWeek;
33554proto.isoWeekday = getSetISODayOfWeek;
33555proto.dayOfYear  = getSetDayOfYear;
33556
33557// Hour
33558proto.hour = proto.hours = getSetHour;
33559
33560// Minute
33561proto.minute = proto.minutes = getSetMinute;
33562
33563// Second
33564proto.second = proto.seconds = getSetSecond;
33565
33566// Millisecond
33567proto.millisecond = proto.milliseconds = getSetMillisecond;
33568
33569// Offset
33570proto.utcOffset            = getSetOffset;
33571proto.utc                  = setOffsetToUTC;
33572proto.local                = setOffsetToLocal;
33573proto.parseZone            = setOffsetToParsedOffset;
33574proto.hasAlignedHourOffset = hasAlignedHourOffset;
33575proto.isDST                = isDaylightSavingTime;
33576proto.isLocal              = isLocal;
33577proto.isUtcOffset          = isUtcOffset;
33578proto.isUtc                = isUtc;
33579proto.isUTC                = isUtc;
33580
33581// Timezone
33582proto.zoneAbbr = getZoneAbbr;
33583proto.zoneName = getZoneName;
33584
33585// Deprecations
33586proto.dates  = deprecate('dates accessor is deprecated. Use date instead.', getSetDayOfMonth);
33587proto.months = deprecate('months accessor is deprecated. Use month instead', getSetMonth);
33588proto.years  = deprecate('years accessor is deprecated. Use year instead', getSetYear);
33589proto.zone   = deprecate('moment().zone is deprecated, use moment().utcOffset instead. http://momentjs.com/guides/#/warnings/zone/', getSetZone);
33590proto.isDSTShifted = deprecate('isDSTShifted is deprecated. See http://momentjs.com/guides/#/warnings/dst-shifted/ for more information', isDaylightSavingTimeShifted);
33591
33592function createUnix (input) {
33593    return createLocal(input * 1000);
33594}
33595
33596function createInZone () {
33597    return createLocal.apply(null, arguments).parseZone();
33598}
33599
33600function preParsePostFormat (string) {
33601    return string;
33602}
33603
33604var proto$1 = Locale.prototype;
33605
33606proto$1.calendar        = calendar;
33607proto$1.longDateFormat  = longDateFormat;
33608proto$1.invalidDate     = invalidDate;
33609proto$1.ordinal         = ordinal;
33610proto$1.preparse        = preParsePostFormat;
33611proto$1.postformat      = preParsePostFormat;
33612proto$1.relativeTime    = relativeTime;
33613proto$1.pastFuture      = pastFuture;
33614proto$1.set             = set;
33615
33616// Month
33617proto$1.months            =        localeMonths;
33618proto$1.monthsShort       =        localeMonthsShort;
33619proto$1.monthsParse       =        localeMonthsParse;
33620proto$1.monthsRegex       = monthsRegex;
33621proto$1.monthsShortRegex  = monthsShortRegex;
33622
33623// Week
33624proto$1.week = localeWeek;
33625proto$1.firstDayOfYear = localeFirstDayOfYear;
33626proto$1.firstDayOfWeek = localeFirstDayOfWeek;
33627
33628// Day of Week
33629proto$1.weekdays       =        localeWeekdays;
33630proto$1.weekdaysMin    =        localeWeekdaysMin;
33631proto$1.weekdaysShort  =        localeWeekdaysShort;
33632proto$1.weekdaysParse  =        localeWeekdaysParse;
33633
33634proto$1.weekdaysRegex       =        weekdaysRegex;
33635proto$1.weekdaysShortRegex  =        weekdaysShortRegex;
33636proto$1.weekdaysMinRegex    =        weekdaysMinRegex;
33637
33638// Hours
33639proto$1.isPM = localeIsPM;
33640proto$1.meridiem = localeMeridiem;
33641
33642function get$1 (format, index, field, setter) {
33643    var locale = getLocale();
33644    var utc = createUTC().set(setter, index);
33645    return locale[field](utc, format);
33646}
33647
33648function listMonthsImpl (format, index, field) {
33649    if (isNumber(format)) {
33650        index = format;
33651        format = undefined;
33652    }
33653
33654    format = format || '';
33655
33656    if (index != null) {
33657        return get$1(format, index, field, 'month');
33658    }
33659
33660    var i;
33661    var out = [];
33662    for (i = 0; i < 12; i++) {
33663        out[i] = get$1(format, i, field, 'month');
33664    }
33665    return out;
33666}
33667
33668// ()
33669// (5)
33670// (fmt, 5)
33671// (fmt)
33672// (true)
33673// (true, 5)
33674// (true, fmt, 5)
33675// (true, fmt)
33676function listWeekdaysImpl (localeSorted, format, index, field) {
33677    if (typeof localeSorted === 'boolean') {
33678        if (isNumber(format)) {
33679            index = format;
33680            format = undefined;
33681        }
33682
33683        format = format || '';
33684    } else {
33685        format = localeSorted;
33686        index = format;
33687        localeSorted = false;
33688
33689        if (isNumber(format)) {
33690            index = format;
33691            format = undefined;
33692        }
33693
33694        format = format || '';
33695    }
33696
33697    var locale = getLocale(),
33698        shift = localeSorted ? locale._week.dow : 0;
33699
33700    if (index != null) {
33701        return get$1(format, (index + shift) % 7, field, 'day');
33702    }
33703
33704    var i;
33705    var out = [];
33706    for (i = 0; i < 7; i++) {
33707        out[i] = get$1(format, (i + shift) % 7, field, 'day');
33708    }
33709    return out;
33710}
33711
33712function listMonths (format, index) {
33713    return listMonthsImpl(format, index, 'months');
33714}
33715
33716function listMonthsShort (format, index) {
33717    return listMonthsImpl(format, index, 'monthsShort');
33718}
33719
33720function listWeekdays (localeSorted, format, index) {
33721    return listWeekdaysImpl(localeSorted, format, index, 'weekdays');
33722}
33723
33724function listWeekdaysShort (localeSorted, format, index) {
33725    return listWeekdaysImpl(localeSorted, format, index, 'weekdaysShort');
33726}
33727
33728function listWeekdaysMin (localeSorted, format, index) {
33729    return listWeekdaysImpl(localeSorted, format, index, 'weekdaysMin');
33730}
33731
33732getSetGlobalLocale('en', {
33733    dayOfMonthOrdinalParse: /\d{1,2}(th|st|nd|rd)/,
33734    ordinal : function (number) {
33735        var b = number % 10,
33736            output = (toInt(number % 100 / 10) === 1) ? 'th' :
33737            (b === 1) ? 'st' :
33738            (b === 2) ? 'nd' :
33739            (b === 3) ? 'rd' : 'th';
33740        return number + output;
33741    }
33742});
33743
33744// Side effect imports
33745hooks.lang = deprecate('moment.lang is deprecated. Use moment.locale instead.', getSetGlobalLocale);
33746hooks.langData = deprecate('moment.langData is deprecated. Use moment.localeData instead.', getLocale);
33747
33748var mathAbs = Math.abs;
33749
33750function abs () {
33751    var data           = this._data;
33752
33753    this._milliseconds = mathAbs(this._milliseconds);
33754    this._days         = mathAbs(this._days);
33755    this._months       = mathAbs(this._months);
33756
33757    data.milliseconds  = mathAbs(data.milliseconds);
33758    data.seconds       = mathAbs(data.seconds);
33759    data.minutes       = mathAbs(data.minutes);
33760    data.hours         = mathAbs(data.hours);
33761    data.months        = mathAbs(data.months);
33762    data.years         = mathAbs(data.years);
33763
33764    return this;
33765}
33766
33767function addSubtract$1 (duration, input, value, direction) {
33768    var other = createDuration(input, value);
33769
33770    duration._milliseconds += direction * other._milliseconds;
33771    duration._days         += direction * other._days;
33772    duration._months       += direction * other._months;
33773
33774    return duration._bubble();
33775}
33776
33777// supports only 2.0-style add(1, 's') or add(duration)
33778function add$1 (input, value) {
33779    return addSubtract$1(this, input, value, 1);
33780}
33781
33782// supports only 2.0-style subtract(1, 's') or subtract(duration)
33783function subtract$1 (input, value) {
33784    return addSubtract$1(this, input, value, -1);
33785}
33786
33787function absCeil (number) {
33788    if (number < 0) {
33789        return Math.floor(number);
33790    } else {
33791        return Math.ceil(number);
33792    }
33793}
33794
33795function bubble () {
33796    var milliseconds = this._milliseconds;
33797    var days         = this._days;
33798    var months       = this._months;
33799    var data         = this._data;
33800    var seconds, minutes, hours, years, monthsFromDays;
33801
33802    // if we have a mix of positive and negative values, bubble down first
33803    // check: https://github.com/moment/moment/issues/2166
33804    if (!((milliseconds >= 0 && days >= 0 && months >= 0) ||
33805            (milliseconds <= 0 && days <= 0 && months <= 0))) {
33806        milliseconds += absCeil(monthsToDays(months) + days) * 864e5;
33807        days = 0;
33808        months = 0;
33809    }
33810
33811    // The following code bubbles up values, see the tests for
33812    // examples of what that means.
33813    data.milliseconds = milliseconds % 1000;
33814
33815    seconds           = absFloor(milliseconds / 1000);
33816    data.seconds      = seconds % 60;
33817
33818    minutes           = absFloor(seconds / 60);
33819    data.minutes      = minutes % 60;
33820
33821    hours             = absFloor(minutes / 60);
33822    data.hours        = hours % 24;
33823
33824    days += absFloor(hours / 24);
33825
33826    // convert days to months
33827    monthsFromDays = absFloor(daysToMonths(days));
33828    months += monthsFromDays;
33829    days -= absCeil(monthsToDays(monthsFromDays));
33830
33831    // 12 months -> 1 year
33832    years = absFloor(months / 12);
33833    months %= 12;
33834
33835    data.days   = days;
33836    data.months = months;
33837    data.years  = years;
33838
33839    return this;
33840}
33841
33842function daysToMonths (days) {
33843    // 400 years have 146097 days (taking into account leap year rules)
33844    // 400 years have 12 months === 4800
33845    return days * 4800 / 146097;
33846}
33847
33848function monthsToDays (months) {
33849    // the reverse of daysToMonths
33850    return months * 146097 / 4800;
33851}
33852
33853function as (units) {
33854    if (!this.isValid()) {
33855        return NaN;
33856    }
33857    var days;
33858    var months;
33859    var milliseconds = this._milliseconds;
33860
33861    units = normalizeUnits(units);
33862
33863    if (units === 'month' || units === 'year') {
33864        days   = this._days   + milliseconds / 864e5;
33865        months = this._months + daysToMonths(days);
33866        return units === 'month' ? months : months / 12;
33867    } else {
33868        // handle milliseconds separately because of floating point math errors (issue #1867)
33869        days = this._days + Math.round(monthsToDays(this._months));
33870        switch (units) {
33871            case 'week'   : return days / 7     + milliseconds / 6048e5;
33872            case 'day'    : return days         + milliseconds / 864e5;
33873            case 'hour'   : return days * 24    + milliseconds / 36e5;
33874            case 'minute' : return days * 1440  + milliseconds / 6e4;
33875            case 'second' : return days * 86400 + milliseconds / 1000;
33876            // Math.floor prevents floating point math errors here
33877            case 'millisecond': return Math.floor(days * 864e5) + milliseconds;
33878            default: throw new Error('Unknown unit ' + units);
33879        }
33880    }
33881}
33882
33883// TODO: Use this.as('ms')?
33884function valueOf$1 () {
33885    if (!this.isValid()) {
33886        return NaN;
33887    }
33888    return (
33889        this._milliseconds +
33890        this._days * 864e5 +
33891        (this._months % 12) * 2592e6 +
33892        toInt(this._months / 12) * 31536e6
33893    );
33894}
33895
33896function makeAs (alias) {
33897    return function () {
33898        return this.as(alias);
33899    };
33900}
33901
33902var asMilliseconds = makeAs('ms');
33903var asSeconds      = makeAs('s');
33904var asMinutes      = makeAs('m');
33905var asHours        = makeAs('h');
33906var asDays         = makeAs('d');
33907var asWeeks        = makeAs('w');
33908var asMonths       = makeAs('M');
33909var asYears        = makeAs('y');
33910
33911function clone$1 () {
33912    return createDuration(this);
33913}
33914
33915function get$2 (units) {
33916    units = normalizeUnits(units);
33917    return this.isValid() ? this[units + 's']() : NaN;
33918}
33919
33920function makeGetter(name) {
33921    return function () {
33922        return this.isValid() ? this._data[name] : NaN;
33923    };
33924}
33925
33926var milliseconds = makeGetter('milliseconds');
33927var seconds      = makeGetter('seconds');
33928var minutes      = makeGetter('minutes');
33929var hours        = makeGetter('hours');
33930var days         = makeGetter('days');
33931var months       = makeGetter('months');
33932var years        = makeGetter('years');
33933
33934function weeks () {
33935    return absFloor(this.days() / 7);
33936}
33937
33938var round = Math.round;
33939var thresholds = {
33940    ss: 44,         // a few seconds to seconds
33941    s : 45,         // seconds to minute
33942    m : 45,         // minutes to hour
33943    h : 22,         // hours to day
33944    d : 26,         // days to month
33945    M : 11          // months to year
33946};
33947
33948// helper function for moment.fn.from, moment.fn.fromNow, and moment.duration.fn.humanize
33949function substituteTimeAgo(string, number, withoutSuffix, isFuture, locale) {
33950    return locale.relativeTime(number || 1, !!withoutSuffix, string, isFuture);
33951}
33952
33953function relativeTime$1 (posNegDuration, withoutSuffix, locale) {
33954    var duration = createDuration(posNegDuration).abs();
33955    var seconds  = round(duration.as('s'));
33956    var minutes  = round(duration.as('m'));
33957    var hours    = round(duration.as('h'));
33958    var days     = round(duration.as('d'));
33959    var months   = round(duration.as('M'));
33960    var years    = round(duration.as('y'));
33961
33962    var a = seconds <= thresholds.ss && ['s', seconds]  ||
33963            seconds < thresholds.s   && ['ss', seconds] ||
33964            minutes <= 1             && ['m']           ||
33965            minutes < thresholds.m   && ['mm', minutes] ||
33966            hours   <= 1             && ['h']           ||
33967            hours   < thresholds.h   && ['hh', hours]   ||
33968            days    <= 1             && ['d']           ||
33969            days    < thresholds.d   && ['dd', days]    ||
33970            months  <= 1             && ['M']           ||
33971            months  < thresholds.M   && ['MM', months]  ||
33972            years   <= 1             && ['y']           || ['yy', years];
33973
33974    a[2] = withoutSuffix;
33975    a[3] = +posNegDuration > 0;
33976    a[4] = locale;
33977    return substituteTimeAgo.apply(null, a);
33978}
33979
33980// This function allows you to set the rounding function for relative time strings
33981function getSetRelativeTimeRounding (roundingFunction) {
33982    if (roundingFunction === undefined) {
33983        return round;
33984    }
33985    if (typeof(roundingFunction) === 'function') {
33986        round = roundingFunction;
33987        return true;
33988    }
33989    return false;
33990}
33991
33992// This function allows you to set a threshold for relative time strings
33993function getSetRelativeTimeThreshold (threshold, limit) {
33994    if (thresholds[threshold] === undefined) {
33995        return false;
33996    }
33997    if (limit === undefined) {
33998        return thresholds[threshold];
33999    }
34000    thresholds[threshold] = limit;
34001    if (threshold === 's') {
34002        thresholds.ss = limit - 1;
34003    }
34004    return true;
34005}
34006
34007function humanize (withSuffix) {
34008    if (!this.isValid()) {
34009        return this.localeData().invalidDate();
34010    }
34011
34012    var locale = this.localeData();
34013    var output = relativeTime$1(this, !withSuffix, locale);
34014
34015    if (withSuffix) {
34016        output = locale.pastFuture(+this, output);
34017    }
34018
34019    return locale.postformat(output);
34020}
34021
34022var abs$1 = Math.abs;
34023
34024function sign(x) {
34025    return ((x > 0) - (x < 0)) || +x;
34026}
34027
34028function toISOString$1() {
34029    // for ISO strings we do not use the normal bubbling rules:
34030    //  * milliseconds bubble up until they become hours
34031    //  * days do not bubble at all
34032    //  * months bubble up until they become years
34033    // This is because there is no context-free conversion between hours and days
34034    // (think of clock changes)
34035    // and also not between days and months (28-31 days per month)
34036    if (!this.isValid()) {
34037        return this.localeData().invalidDate();
34038    }
34039
34040    var seconds = abs$1(this._milliseconds) / 1000;
34041    var days         = abs$1(this._days);
34042    var months       = abs$1(this._months);
34043    var minutes, hours, years;
34044
34045    // 3600 seconds -> 60 minutes -> 1 hour
34046    minutes           = absFloor(seconds / 60);
34047    hours             = absFloor(minutes / 60);
34048    seconds %= 60;
34049    minutes %= 60;
34050
34051    // 12 months -> 1 year
34052    years  = absFloor(months / 12);
34053    months %= 12;
34054
34055
34056    // inspired by https://github.com/dordille/moment-isoduration/blob/master/moment.isoduration.js
34057    var Y = years;
34058    var M = months;
34059    var D = days;
34060    var h = hours;
34061    var m = minutes;
34062    var s = seconds ? seconds.toFixed(3).replace(/\.?0+$/, '') : '';
34063    var total = this.asSeconds();
34064
34065    if (!total) {
34066        // this is the same as C#'s (Noda) and python (isodate)...
34067        // but not other JS (goog.date)
34068        return 'P0D';
34069    }
34070
34071    var totalSign = total < 0 ? '-' : '';
34072    var ymSign = sign(this._months) !== sign(total) ? '-' : '';
34073    var daysSign = sign(this._days) !== sign(total) ? '-' : '';
34074    var hmsSign = sign(this._milliseconds) !== sign(total) ? '-' : '';
34075
34076    return totalSign + 'P' +
34077        (Y ? ymSign + Y + 'Y' : '') +
34078        (M ? ymSign + M + 'M' : '') +
34079        (D ? daysSign + D + 'D' : '') +
34080        ((h || m || s) ? 'T' : '') +
34081        (h ? hmsSign + h + 'H' : '') +
34082        (m ? hmsSign + m + 'M' : '') +
34083        (s ? hmsSign + s + 'S' : '');
34084}
34085
34086var proto$2 = Duration.prototype;
34087
34088proto$2.isValid        = isValid$1;
34089proto$2.abs            = abs;
34090proto$2.add            = add$1;
34091proto$2.subtract       = subtract$1;
34092proto$2.as             = as;
34093proto$2.asMilliseconds = asMilliseconds;
34094proto$2.asSeconds      = asSeconds;
34095proto$2.asMinutes      = asMinutes;
34096proto$2.asHours        = asHours;
34097proto$2.asDays         = asDays;
34098proto$2.asWeeks        = asWeeks;
34099proto$2.asMonths       = asMonths;
34100proto$2.asYears        = asYears;
34101proto$2.valueOf        = valueOf$1;
34102proto$2._bubble        = bubble;
34103proto$2.clone          = clone$1;
34104proto$2.get            = get$2;
34105proto$2.milliseconds   = milliseconds;
34106proto$2.seconds        = seconds;
34107proto$2.minutes        = minutes;
34108proto$2.hours          = hours;
34109proto$2.days           = days;
34110proto$2.weeks          = weeks;
34111proto$2.months         = months;
34112proto$2.years          = years;
34113proto$2.humanize       = humanize;
34114proto$2.toISOString    = toISOString$1;
34115proto$2.toString       = toISOString$1;
34116proto$2.toJSON         = toISOString$1;
34117proto$2.locale         = locale;
34118proto$2.localeData     = localeData;
34119
34120// Deprecations
34121proto$2.toIsoString = deprecate('toIsoString() is deprecated. Please use toISOString() instead (notice the capitals)', toISOString$1);
34122proto$2.lang = lang;
34123
34124// Side effect imports
34125
34126// FORMATTING
34127
34128addFormatToken('X', 0, 0, 'unix');
34129addFormatToken('x', 0, 0, 'valueOf');
34130
34131// PARSING
34132
34133addRegexToken('x', matchSigned);
34134addRegexToken('X', matchTimestamp);
34135addParseToken('X', function (input, array, config) {
34136    config._d = new Date(parseFloat(input, 10) * 1000);
34137});
34138addParseToken('x', function (input, array, config) {
34139    config._d = new Date(toInt(input));
34140});
34141
34142// Side effect imports
34143
34144
34145hooks.version = '2.19.1';
34146
34147setHookCallback(createLocal);
34148
34149hooks.fn                    = proto;
34150hooks.min                   = min;
34151hooks.max                   = max;
34152hooks.now                   = now;
34153hooks.utc                   = createUTC;
34154hooks.unix                  = createUnix;
34155hooks.months                = listMonths;
34156hooks.isDate                = isDate;
34157hooks.locale                = getSetGlobalLocale;
34158hooks.invalid               = createInvalid;
34159hooks.duration              = createDuration;
34160hooks.isMoment              = isMoment;
34161hooks.weekdays              = listWeekdays;
34162hooks.parseZone             = createInZone;
34163hooks.localeData            = getLocale;
34164hooks.isDuration            = isDuration;
34165hooks.monthsShort           = listMonthsShort;
34166hooks.weekdaysMin           = listWeekdaysMin;
34167hooks.defineLocale          = defineLocale;
34168hooks.updateLocale          = updateLocale;
34169hooks.locales               = listLocales;
34170hooks.weekdaysShort         = listWeekdaysShort;
34171hooks.normalizeUnits        = normalizeUnits;
34172hooks.relativeTimeRounding  = getSetRelativeTimeRounding;
34173hooks.relativeTimeThreshold = getSetRelativeTimeThreshold;
34174hooks.calendarFormat        = getCalendarFormat;
34175hooks.prototype             = proto;
34176
34177return hooks;
34178
34179})));
34180
34181/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(155)(module)))
34182
34183/***/ }),
34184/* 155 */
34185/***/ (function(module, exports) {
34186
34187module.exports = function(module) {
34188	if(!module.webpackPolyfill) {
34189		module.deprecate = function() {};
34190		module.paths = [];
34191		// module.parent = undefined by default
34192		if(!module.children) module.children = [];
34193		Object.defineProperty(module, "loaded", {
34194			enumerable: true,
34195			get: function() {
34196				return module.l;
34197			}
34198		});
34199		Object.defineProperty(module, "id", {
34200			enumerable: true,
34201			get: function() {
34202				return module.i;
34203			}
34204		});
34205		module.webpackPolyfill = 1;
34206	}
34207	return module;
34208};
34209
34210
34211/***/ }),
34212/* 156 */
34213/***/ (function(module, exports) {
34214
34215function webpackEmptyContext(req) {
34216	throw new Error("Cannot find module '" + req + "'.");
34217}
34218webpackEmptyContext.keys = function() { return []; };
34219webpackEmptyContext.resolve = webpackEmptyContext;
34220module.exports = webpackEmptyContext;
34221webpackEmptyContext.id = 156;
34222
34223/***/ }),
34224/* 157 */
34225/***/ (function(module, exports, __webpack_require__) {
34226
34227"use strict";
34228/* WEBPACK VAR INJECTION */(function(global) {
34229
34230/* eslint-disable require-jsdoc */
34231
34232var _rng;
34233
34234var globalVar = typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : null;
34235
34236if (globalVar && globalVar.crypto && crypto.getRandomValues) {
34237  // WHATWG crypto-based RNG - http://wiki.whatwg.org/wiki/Crypto
34238  // Moderately fast, high quality
34239  var _rnds8 = new Uint8Array(16);
34240  _rng = function whatwgRNG() {
34241    crypto.getRandomValues(_rnds8);
34242    return _rnds8;
34243  };
34244}
34245
34246if (!_rng) {
34247  // Math.random()-based (RNG)
34248  //
34249  // If all else fails, use Math.random().  It's fast, but is of unspecified
34250  // quality.
34251  var _rnds = new Array(16);
34252  _rng = function _rng() {
34253    for (var i = 0, r; i < 16; i++) {
34254      if ((i & 0x03) === 0) r = Math.random() * 0x100000000;
34255      _rnds[i] = r >>> ((i & 0x03) << 3) & 0xff;
34256    }
34257
34258    return _rnds;
34259  };
34260}
34261
34262//     uuid.js
34263//
34264//     Copyright (c) 2010-2012 Robert Kieffer
34265//     MIT License - http://opensource.org/licenses/mit-license.php
34266
34267// Unique ID creation requires a high quality random # generator.  We feature
34268// detect to determine the best RNG source, normalizing to a function that
34269// returns 128-bits of randomness, since that's what's usually required
34270
34271//var _rng = require('./rng');
34272
34273// Maps for number <-> hex string conversion
34274var _byteToHex = [];
34275var _hexToByte = {};
34276for (var i = 0; i < 256; i++) {
34277  _byteToHex[i] = (i + 0x100).toString(16).substr(1);
34278  _hexToByte[_byteToHex[i]] = i;
34279}
34280
34281// **`parse()` - Parse a UUID into it's component bytes**
34282function parse(s, buf, offset) {
34283  var i = buf && offset || 0,
34284      ii = 0;
34285
34286  buf = buf || [];
34287  s.toLowerCase().replace(/[0-9a-f]{2}/g, function (oct) {
34288    if (ii < 16) {
34289      // Don't overflow!
34290      buf[i + ii++] = _hexToByte[oct];
34291    }
34292  });
34293
34294  // Zero out remaining bytes if string was short
34295  while (ii < 16) {
34296    buf[i + ii++] = 0;
34297  }
34298
34299  return buf;
34300}
34301
34302// **`unparse()` - Convert UUID byte array (ala parse()) into a string**
34303function unparse(buf, offset) {
34304  var i = offset || 0,
34305      bth = _byteToHex;
34306  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++]];
34307}
34308
34309// **`v1()` - Generate time-based UUID**
34310//
34311// Inspired by https://github.com/LiosK/UUID.js
34312// and http://docs.python.org/library/uuid.html
34313
34314// random #'s we need to init node and clockseq
34315var _seedBytes = _rng();
34316
34317// Per 4.5, create and 48-bit node id, (47 random bits + multicast bit = 1)
34318var _nodeId = [_seedBytes[0] | 0x01, _seedBytes[1], _seedBytes[2], _seedBytes[3], _seedBytes[4], _seedBytes[5]];
34319
34320// Per 4.2.2, randomize (14 bit) clockseq
34321var _clockseq = (_seedBytes[6] << 8 | _seedBytes[7]) & 0x3fff;
34322
34323// Previous uuid creation time
34324var _lastMSecs = 0,
34325    _lastNSecs = 0;
34326
34327// See https://github.com/broofa/node-uuid for API details
34328function v1(options, buf, offset) {
34329  var i = buf && offset || 0;
34330  var b = buf || [];
34331
34332  options = options || {};
34333
34334  var clockseq = options.clockseq !== undefined ? options.clockseq : _clockseq;
34335
34336  // UUID timestamps are 100 nano-second units since the Gregorian epoch,
34337  // (1582-10-15 00:00).  JSNumbers aren't precise enough for this, so
34338  // time is handled internally as 'msecs' (integer milliseconds) and 'nsecs'
34339  // (100-nanoseconds offset from msecs) since unix epoch, 1970-01-01 00:00.
34340  var msecs = options.msecs !== undefined ? options.msecs : new Date().getTime();
34341
34342  // Per 4.2.1.2, use count of uuid's generated during the current clock
34343  // cycle to simulate higher resolution clock
34344  var nsecs = options.nsecs !== undefined ? options.nsecs : _lastNSecs + 1;
34345
34346  // Time since last uuid creation (in msecs)
34347  var dt = msecs - _lastMSecs + (nsecs - _lastNSecs) / 10000;
34348
34349  // Per 4.2.1.2, Bump clockseq on clock regression
34350  if (dt < 0 && options.clockseq === undefined) {
34351    clockseq = clockseq + 1 & 0x3fff;
34352  }
34353
34354  // Reset nsecs if clock regresses (new clockseq) or we've moved onto a new
34355  // time interval
34356  if ((dt < 0 || msecs > _lastMSecs) && options.nsecs === undefined) {
34357    nsecs = 0;
34358  }
34359
34360  // Per 4.2.1.2 Throw error if too many uuids are requested
34361  if (nsecs >= 10000) {
34362    throw new Error('uuid.v1(): Can\'t create more than 10M uuids/sec');
34363  }
34364
34365  _lastMSecs = msecs;
34366  _lastNSecs = nsecs;
34367  _clockseq = clockseq;
34368
34369  // Per 4.1.4 - Convert from unix epoch to Gregorian epoch
34370  msecs += 12219292800000;
34371
34372  // `time_low`
34373  var tl = ((msecs & 0xfffffff) * 10000 + nsecs) % 0x100000000;
34374  b[i++] = tl >>> 24 & 0xff;
34375  b[i++] = tl >>> 16 & 0xff;
34376  b[i++] = tl >>> 8 & 0xff;
34377  b[i++] = tl & 0xff;
34378
34379  // `time_mid`
34380  var tmh = msecs / 0x100000000 * 10000 & 0xfffffff;
34381  b[i++] = tmh >>> 8 & 0xff;
34382  b[i++] = tmh & 0xff;
34383
34384  // `time_high_and_version`
34385  b[i++] = tmh >>> 24 & 0xf | 0x10; // include version
34386  b[i++] = tmh >>> 16 & 0xff;
34387
34388  // `clock_seq_hi_and_reserved` (Per 4.2.2 - include variant)
34389  b[i++] = clockseq >>> 8 | 0x80;
34390
34391  // `clock_seq_low`
34392  b[i++] = clockseq & 0xff;
34393
34394  // `node`
34395  var node = options.node || _nodeId;
34396  for (var n = 0; n < 6; n++) {
34397    b[i + n] = node[n];
34398  }
34399
34400  return buf ? buf : unparse(b);
34401}
34402
34403// **`v4()` - Generate random UUID**
34404
34405// See https://github.com/broofa/node-uuid for API details
34406function v4(options, buf, offset) {
34407  // Deprecated - 'format' argument, as supported in v1.2
34408  var i = buf && offset || 0;
34409
34410  if (typeof options == 'string') {
34411    buf = options == 'binary' ? new Array(16) : null;
34412    options = null;
34413  }
34414  options = options || {};
34415
34416  var rnds = options.random || (options.rng || _rng)();
34417
34418  // Per 4.4, set bits for version and `clock_seq_hi_and_reserved`
34419  rnds[6] = rnds[6] & 0x0f | 0x40;
34420  rnds[8] = rnds[8] & 0x3f | 0x80;
34421
34422  // Copy bytes to buffer, if provided
34423  if (buf) {
34424    for (var ii = 0; ii < 16; ii++) {
34425      buf[i + ii] = rnds[ii];
34426    }
34427  }
34428
34429  return buf || unparse(rnds);
34430}
34431
34432// Export public API
34433var uuid = v4;
34434uuid.v1 = v1;
34435uuid.v4 = v4;
34436uuid.parse = parse;
34437uuid.unparse = unparse;
34438
34439module.exports = uuid;
34440/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(158)))
34441
34442/***/ }),
34443/* 158 */
34444/***/ (function(module, exports) {
34445
34446var g;
34447
34448// This works in non-strict mode
34449g = (function() {
34450	return this;
34451})();
34452
34453try {
34454	// This works if eval is allowed (see CSP)
34455	g = g || Function("return this")() || (1,eval)("this");
34456} catch(e) {
34457	// This works if the window reference is available
34458	if(typeof window === "object")
34459		g = window;
34460}
34461
34462// g can still be undefined, but nothing to do about it...
34463// We return undefined, instead of nothing here, so it's
34464// easier to handle this case. if(!global) { ...}
34465
34466module.exports = g;
34467
34468
34469/***/ }),
34470/* 159 */
34471/***/ (function(module, exports, __webpack_require__) {
34472
34473"use strict";
34474
34475
34476// utils
34477exports.util = __webpack_require__(2);
34478exports.DOMutil = __webpack_require__(14);
34479
34480// data
34481exports.DataSet = __webpack_require__(11);
34482exports.DataView = __webpack_require__(12);
34483exports.Queue = __webpack_require__(43);
34484
34485// Graph3d
34486exports.Graph3d = __webpack_require__(161);
34487exports.graph3d = {
34488  Camera: __webpack_require__(95),
34489  Filter: __webpack_require__(96),
34490  Point2d: __webpack_require__(91),
34491  Point3d: __webpack_require__(34),
34492  Slider: __webpack_require__(92),
34493  StepNumber: __webpack_require__(93)
34494};
34495
34496// bundled external libraries
34497exports.moment = __webpack_require__(9);
34498exports.Hammer = __webpack_require__(10);
34499exports.keycharm = __webpack_require__(35);
34500
34501/***/ }),
34502/* 160 */
34503/***/ (function(module, exports, __webpack_require__) {
34504
34505var core = __webpack_require__(7);
34506var $JSON = core.JSON || (core.JSON = { stringify: JSON.stringify });
34507module.exports = function stringify(it) { // eslint-disable-line no-unused-vars
34508  return $JSON.stringify.apply($JSON, arguments);
34509};
34510
34511
34512/***/ }),
34513/* 161 */
34514/***/ (function(module, exports, __webpack_require__) {
34515
34516"use strict";
34517
34518
34519var _assign = __webpack_require__(90);
34520
34521var _assign2 = _interopRequireDefault(_assign);
34522
34523function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
34524
34525var Emitter = __webpack_require__(44);
34526var util = __webpack_require__(2);
34527var Point3d = __webpack_require__(34);
34528var Point2d = __webpack_require__(91);
34529var Slider = __webpack_require__(92);
34530var StepNumber = __webpack_require__(93);
34531var Settings = __webpack_require__(94);
34532var Validator = __webpack_require__(15)['default'];
34533
34534var _require = __webpack_require__(15),
34535    printStyle = _require.printStyle;
34536
34537var _require2 = __webpack_require__(172),
34538    allOptions = _require2.allOptions;
34539
34540var DataGroup = __webpack_require__(173);
34541
34542/// enumerate the available styles
34543Graph3d.STYLE = Settings.STYLE;
34544
34545/**
34546 * Following label is used in the settings to describe values which should be
34547 * determined by the code while running, from the current data and graph style.
34548 *
34549 * Using 'undefined' directly achieves the same thing, but this is more
34550 * descriptive by describing the intent.
34551 */
34552var autoByDefault = undefined;
34553
34554/**
34555 * Default values for option settings.
34556 *
34557 * These are the values used when a Graph3d instance is initialized without
34558 * custom settings.
34559 *
34560 * If a field is not in this list, a default value of 'autoByDefault' is assumed,
34561 * which is just an alias for 'undefined'.
34562 */
34563Graph3d.DEFAULTS = {
34564  width: '400px',
34565  height: '400px',
34566  filterLabel: 'time',
34567  legendLabel: 'value',
34568  xLabel: 'x',
34569  yLabel: 'y',
34570  zLabel: 'z',
34571  xValueLabel: function xValueLabel(v) {
34572    return v;
34573  },
34574  yValueLabel: function yValueLabel(v) {
34575    return v;
34576  },
34577  zValueLabel: function zValueLabel(v) {
34578    return v;
34579  },
34580  showXAxis: true,
34581  showYAxis: true,
34582  showZAxis: true,
34583  showGrid: true,
34584  showPerspective: true,
34585  showShadow: false,
34586  keepAspectRatio: true,
34587  verticalRatio: 0.5, // 0.1 to 1.0, where 1.0 results in a 'cube'
34588
34589  dotSizeRatio: 0.02, // size of the dots as a fraction of the graph width
34590  dotSizeMinFraction: 0.5, // size of min-value dot as a fraction of dotSizeRatio	
34591  dotSizeMaxFraction: 2.5, // size of max-value dot as a fraction of dotSizeRatio	
34592
34593  showAnimationControls: autoByDefault,
34594  animationInterval: 1000, // milliseconds
34595  animationPreload: false,
34596  animationAutoStart: autoByDefault,
34597
34598  axisColor: '#4D4D4D',
34599  gridColor: '#D3D3D3',
34600  xCenter: '55%',
34601  yCenter: '50%',
34602
34603  style: Graph3d.STYLE.DOT,
34604  tooltip: false,
34605
34606  tooltipStyle: {
34607    content: {
34608      padding: '10px',
34609      border: '1px solid #4d4d4d',
34610      color: '#1a1a1a',
34611      background: 'rgba(255,255,255,0.7)',
34612      borderRadius: '2px',
34613      boxShadow: '5px 5px 10px rgba(128,128,128,0.5)'
34614    },
34615    line: {
34616      height: '40px',
34617      width: '0',
34618      borderLeft: '1px solid #4d4d4d'
34619    },
34620    dot: {
34621      height: '0',
34622      width: '0',
34623      border: '5px solid #4d4d4d',
34624      borderRadius: '5px'
34625    }
34626  },
34627
34628  dataColor: {
34629    fill: '#7DC1FF',
34630    stroke: '#3267D2',
34631    strokeWidth: 1 // px
34632  },
34633
34634  cameraPosition: {
34635    horizontal: 1.0,
34636    vertical: 0.5,
34637    distance: 1.7
34638  },
34639
34640  /*
34641    The following fields are 'auto by default', see above.
34642   */
34643  showLegend: autoByDefault, // determined by graph style
34644  backgroundColor: autoByDefault,
34645
34646  xBarWidth: autoByDefault,
34647  yBarWidth: autoByDefault,
34648  valueMin: autoByDefault,
34649  valueMax: autoByDefault,
34650  xMin: autoByDefault,
34651  xMax: autoByDefault,
34652  xStep: autoByDefault,
34653  yMin: autoByDefault,
34654  yMax: autoByDefault,
34655  yStep: autoByDefault,
34656  zMin: autoByDefault,
34657  zMax: autoByDefault,
34658  zStep: autoByDefault
34659};
34660
34661// -----------------------------------------------------------------------------
34662// Class Graph3d
34663// -----------------------------------------------------------------------------
34664
34665
34666/**
34667 * Graph3d displays data in 3d.
34668 *
34669 * Graph3d is developed in javascript as a Google Visualization Chart.
34670 *
34671 * @constructor Graph3d
34672 * @param {Element} container   The DOM element in which the Graph3d will
34673 *                              be created. Normally a div element.
34674 * @param {DataSet | DataView | Array} [data]
34675 * @param {Object} [options]
34676 */
34677function Graph3d(container, data, options) {
34678  if (!(this instanceof Graph3d)) {
34679    throw new SyntaxError('Constructor must be called with the new operator');
34680  }
34681
34682  // create variables and set default values
34683  this.containerElement = container;
34684
34685  this.dataGroup = new DataGroup();
34686  this.dataPoints = null; // The table with point objects
34687
34688  // create a frame and canvas
34689  this.create();
34690
34691  Settings.setDefaults(Graph3d.DEFAULTS, this);
34692
34693  // the column indexes
34694  this.colX = undefined;
34695  this.colY = undefined;
34696  this.colZ = undefined;
34697  this.colValue = undefined;
34698
34699  // TODO: customize axis range
34700
34701  // apply options (also when undefined)
34702  this.setOptions(options);
34703
34704  // apply data
34705  this.setData(data);
34706}
34707
34708// Extend Graph3d with an Emitter mixin
34709Emitter(Graph3d.prototype);
34710
34711/**
34712 * Calculate the scaling values, dependent on the range in x, y, and z direction
34713 */
34714Graph3d.prototype._setScale = function () {
34715  this.scale = new Point3d(1 / this.xRange.range(), 1 / this.yRange.range(), 1 / this.zRange.range());
34716
34717  // keep aspect ration between x and y scale if desired
34718  if (this.keepAspectRatio) {
34719    if (this.scale.x < this.scale.y) {
34720      //noinspection JSSuspiciousNameCombination
34721      this.scale.y = this.scale.x;
34722    } else {
34723      //noinspection JSSuspiciousNameCombination
34724      this.scale.x = this.scale.y;
34725    }
34726  }
34727
34728  // scale the vertical axis
34729  this.scale.z *= this.verticalRatio;
34730  // TODO: can this be automated? verticalRatio?
34731
34732  // determine scale for (optional) value
34733  if (this.valueRange !== undefined) {
34734    this.scale.value = 1 / this.valueRange.range();
34735  }
34736
34737  // position the camera arm
34738  var xCenter = this.xRange.center() * this.scale.x;
34739  var yCenter = this.yRange.center() * this.scale.y;
34740  var zCenter = this.zRange.center() * this.scale.z;
34741  this.camera.setArmLocation(xCenter, yCenter, zCenter);
34742};
34743
34744/**
34745 * Convert a 3D location to a 2D location on screen
34746 * Source: ttp://en.wikipedia.org/wiki/3D_projection
34747 *
34748 * @param   {Point3d} point3d  A 3D point with parameters x, y, z
34749 * @returns {Point2d} point2d  A 2D point with parameters x, y
34750 */
34751Graph3d.prototype._convert3Dto2D = function (point3d) {
34752  var translation = this._convertPointToTranslation(point3d);
34753  return this._convertTranslationToScreen(translation);
34754};
34755
34756/**
34757 * Convert a 3D location its translation seen from the camera
34758 * Source: http://en.wikipedia.org/wiki/3D_projection
34759 *
34760 * @param   {Point3d} point3d     A 3D point with parameters x, y, z
34761 * @returns {Point3d} translation A 3D point with parameters x, y, z This is
34762 *                                the translation of the point, seen from the
34763 *                                camera.
34764 */
34765Graph3d.prototype._convertPointToTranslation = function (point3d) {
34766  var cameraLocation = this.camera.getCameraLocation(),
34767      cameraRotation = this.camera.getCameraRotation(),
34768      ax = point3d.x * this.scale.x,
34769      ay = point3d.y * this.scale.y,
34770      az = point3d.z * this.scale.z,
34771      cx = cameraLocation.x,
34772      cy = cameraLocation.y,
34773      cz = cameraLocation.z,
34774
34775
34776  // calculate angles
34777  sinTx = Math.sin(cameraRotation.x),
34778      cosTx = Math.cos(cameraRotation.x),
34779      sinTy = Math.sin(cameraRotation.y),
34780      cosTy = Math.cos(cameraRotation.y),
34781      sinTz = Math.sin(cameraRotation.z),
34782      cosTz = Math.cos(cameraRotation.z),
34783
34784
34785  // calculate translation
34786  dx = cosTy * (sinTz * (ay - cy) + cosTz * (ax - cx)) - sinTy * (az - cz),
34787      dy = sinTx * (cosTy * (az - cz) + sinTy * (sinTz * (ay - cy) + cosTz * (ax - cx))) + cosTx * (cosTz * (ay - cy) - sinTz * (ax - cx)),
34788      dz = cosTx * (cosTy * (az - cz) + sinTy * (sinTz * (ay - cy) + cosTz * (ax - cx))) - sinTx * (cosTz * (ay - cy) - sinTz * (ax - cx));
34789
34790  return new Point3d(dx, dy, dz);
34791};
34792
34793/**
34794 * Convert a translation point to a point on the screen
34795 *
34796 * @param   {Point3d} translation A 3D point with parameters x, y, z This is
34797 *                                the translation of the point, seen from the
34798 *                                camera.
34799 * @returns {Point2d} point2d     A 2D point with parameters x, y
34800 */
34801Graph3d.prototype._convertTranslationToScreen = function (translation) {
34802  var ex = this.eye.x,
34803      ey = this.eye.y,
34804      ez = this.eye.z,
34805      dx = translation.x,
34806      dy = translation.y,
34807      dz = translation.z;
34808
34809  // calculate position on screen from translation
34810  var bx;
34811  var by;
34812  if (this.showPerspective) {
34813    bx = (dx - ex) * (ez / dz);
34814    by = (dy - ey) * (ez / dz);
34815  } else {
34816    bx = dx * -(ez / this.camera.getArmLength());
34817    by = dy * -(ez / this.camera.getArmLength());
34818  }
34819
34820  // shift and scale the point to the center of the screen
34821  // use the width of the graph to scale both horizontally and vertically.
34822  return new Point2d(this.currentXCenter + bx * this.frame.canvas.clientWidth, this.currentYCenter - by * this.frame.canvas.clientWidth);
34823};
34824
34825/**
34826 * Calculate the translations and screen positions of all points
34827 *
34828 * @param {Array.<Point3d>} points
34829 * @private
34830 */
34831Graph3d.prototype._calcTranslations = function (points) {
34832  for (var i = 0; i < points.length; i++) {
34833    var point = points[i];
34834    point.trans = this._convertPointToTranslation(point.point);
34835    point.screen = this._convertTranslationToScreen(point.trans);
34836
34837    // calculate the translation of the point at the bottom (needed for sorting)
34838    var transBottom = this._convertPointToTranslation(point.bottom);
34839    point.dist = this.showPerspective ? transBottom.length() : -transBottom.z;
34840  }
34841
34842  // sort the points on depth of their (x,y) position (not on z)
34843  var sortDepth = function sortDepth(a, b) {
34844    return b.dist - a.dist;
34845  };
34846  points.sort(sortDepth);
34847};
34848
34849/**
34850 * Transfer min/max values to the Graph3d instance.
34851 */
34852Graph3d.prototype._initializeRanges = function () {
34853  // TODO: later on, all min/maxes of all datagroups will be combined here
34854  var dg = this.dataGroup;
34855  this.xRange = dg.xRange;
34856  this.yRange = dg.yRange;
34857  this.zRange = dg.zRange;
34858  this.valueRange = dg.valueRange;
34859
34860  // Values currently needed but which need to be sorted out for
34861  // the multiple graph case.
34862  this.xStep = dg.xStep;
34863  this.yStep = dg.yStep;
34864  this.zStep = dg.zStep;
34865  this.xBarWidth = dg.xBarWidth;
34866  this.yBarWidth = dg.yBarWidth;
34867  this.colX = dg.colX;
34868  this.colY = dg.colY;
34869  this.colZ = dg.colZ;
34870  this.colValue = dg.colValue;
34871
34872  // set the scale dependent on the ranges.
34873  this._setScale();
34874};
34875
34876/**
34877 * Return all data values as a list of Point3d objects
34878 *
34879 * @param {vis.DataSet} data
34880 * @returns {Array.<Object>}
34881 */
34882Graph3d.prototype.getDataPoints = function (data) {
34883  var dataPoints = [];
34884
34885  for (var i = 0; i < data.length; i++) {
34886    var point = new Point3d();
34887    point.x = data[i][this.colX] || 0;
34888    point.y = data[i][this.colY] || 0;
34889    point.z = data[i][this.colZ] || 0;
34890    point.data = data[i];
34891
34892    if (this.colValue !== undefined) {
34893      point.value = data[i][this.colValue] || 0;
34894    }
34895
34896    var obj = {};
34897    obj.point = point;
34898    obj.bottom = new Point3d(point.x, point.y, this.zRange.min);
34899    obj.trans = undefined;
34900    obj.screen = undefined;
34901
34902    dataPoints.push(obj);
34903  }
34904
34905  return dataPoints;
34906};
34907
34908/**
34909 * Filter the data based on the current filter
34910 *
34911 * @param   {Array} data
34912 * @returns {Array} dataPoints Array with point objects which can be drawn on
34913 *                             screen
34914 */
34915Graph3d.prototype._getDataPoints = function (data) {
34916  // TODO: store the created matrix dataPoints in the filters instead of
34917  //       reloading each time.
34918  var x, y, i, obj;
34919
34920  var dataPoints = [];
34921
34922  if (this.style === Graph3d.STYLE.GRID || this.style === Graph3d.STYLE.SURFACE) {
34923    // copy all values from the data table to a matrix
34924    // the provided values are supposed to form a grid of (x,y) positions
34925
34926    // create two lists with all present x and y values
34927    var dataX = this.dataGroup.getDistinctValues(this.colX, data);
34928    var dataY = this.dataGroup.getDistinctValues(this.colY, data);
34929
34930    dataPoints = this.getDataPoints(data);
34931
34932    // create a grid, a 2d matrix, with all values.
34933    var dataMatrix = []; // temporary data matrix
34934    for (i = 0; i < dataPoints.length; i++) {
34935      obj = dataPoints[i];
34936
34937      // TODO: implement Array().indexOf() for Internet Explorer
34938      var xIndex = dataX.indexOf(obj.point.x);
34939      var yIndex = dataY.indexOf(obj.point.y);
34940
34941      if (dataMatrix[xIndex] === undefined) {
34942        dataMatrix[xIndex] = [];
34943      }
34944
34945      dataMatrix[xIndex][yIndex] = obj;
34946    }
34947
34948    // fill in the pointers to the neighbors.
34949    for (x = 0; x < dataMatrix.length; x++) {
34950      for (y = 0; y < dataMatrix[x].length; y++) {
34951        if (dataMatrix[x][y]) {
34952          dataMatrix[x][y].pointRight = x < dataMatrix.length - 1 ? dataMatrix[x + 1][y] : undefined;
34953          dataMatrix[x][y].pointTop = y < dataMatrix[x].length - 1 ? dataMatrix[x][y + 1] : undefined;
34954          dataMatrix[x][y].pointCross = x < dataMatrix.length - 1 && y < dataMatrix[x].length - 1 ? dataMatrix[x + 1][y + 1] : undefined;
34955        }
34956      }
34957    }
34958  } else {
34959    // 'dot', 'dot-line', etc.
34960    this._checkValueField(data);
34961    dataPoints = this.getDataPoints(data);
34962
34963    if (this.style === Graph3d.STYLE.LINE) {
34964      // Add next member points for line drawing
34965      for (i = 0; i < dataPoints.length; i++) {
34966        if (i > 0) {
34967          dataPoints[i - 1].pointNext = dataPoints[i];
34968        }
34969      }
34970    }
34971  }
34972
34973  return dataPoints;
34974};
34975
34976/**
34977 * Create the main frame for the Graph3d.
34978 *
34979 * This function is executed once when a Graph3d object is created. The frame
34980 * contains a canvas, and this canvas contains all objects like the axis and
34981 * nodes.
34982 */
34983Graph3d.prototype.create = function () {
34984  // remove all elements from the container element.
34985  while (this.containerElement.hasChildNodes()) {
34986    this.containerElement.removeChild(this.containerElement.firstChild);
34987  }
34988
34989  this.frame = document.createElement('div');
34990  this.frame.style.position = 'relative';
34991  this.frame.style.overflow = 'hidden';
34992
34993  // create the graph canvas (HTML canvas element)
34994  this.frame.canvas = document.createElement('canvas');
34995  this.frame.canvas.style.position = 'relative';
34996  this.frame.appendChild(this.frame.canvas);
34997  //if (!this.frame.canvas.getContext) {
34998  {
34999    var noCanvas = document.createElement('DIV');
35000    noCanvas.style.color = 'red';
35001    noCanvas.style.fontWeight = 'bold';
35002    noCanvas.style.padding = '10px';
35003    noCanvas.innerHTML = 'Error: your browser does not support HTML canvas';
35004    this.frame.canvas.appendChild(noCanvas);
35005  }
35006
35007  this.frame.filter = document.createElement('div');
35008  this.frame.filter.style.position = 'absolute';
35009  this.frame.filter.style.bottom = '0px';
35010  this.frame.filter.style.left = '0px';
35011  this.frame.filter.style.width = '100%';
35012  this.frame.appendChild(this.frame.filter);
35013
35014  // add event listeners to handle moving and zooming the contents
35015  var me = this;
35016  var onmousedown = function onmousedown(event) {
35017    me._onMouseDown(event);
35018  };
35019  var ontouchstart = function ontouchstart(event) {
35020    me._onTouchStart(event);
35021  };
35022  var onmousewheel = function onmousewheel(event) {
35023    me._onWheel(event);
35024  };
35025  var ontooltip = function ontooltip(event) {
35026    me._onTooltip(event);
35027  };
35028  var onclick = function onclick(event) {
35029    me._onClick(event);
35030  };
35031  // TODO: these events are never cleaned up... can give a 'memory leakage'
35032
35033  util.addEventListener(this.frame.canvas, 'mousedown', onmousedown);
35034  util.addEventListener(this.frame.canvas, 'touchstart', ontouchstart);
35035  util.addEventListener(this.frame.canvas, 'mousewheel', onmousewheel);
35036  util.addEventListener(this.frame.canvas, 'mousemove', ontooltip);
35037  util.addEventListener(this.frame.canvas, 'click', onclick);
35038
35039  // add the new graph to the container element
35040  this.containerElement.appendChild(this.frame);
35041};
35042
35043/**
35044 * Set a new size for the graph
35045 *
35046 * @param {number} width
35047 * @param {number} height
35048 * @private
35049 */
35050Graph3d.prototype._setSize = function (width, height) {
35051  this.frame.style.width = width;
35052  this.frame.style.height = height;
35053
35054  this._resizeCanvas();
35055};
35056
35057/**
35058 * Resize the canvas to the current size of the frame
35059 */
35060Graph3d.prototype._resizeCanvas = function () {
35061  this.frame.canvas.style.width = '100%';
35062  this.frame.canvas.style.height = '100%';
35063
35064  this.frame.canvas.width = this.frame.canvas.clientWidth;
35065  this.frame.canvas.height = this.frame.canvas.clientHeight;
35066
35067  // adjust with for margin
35068  this.frame.filter.style.width = this.frame.canvas.clientWidth - 2 * 10 + 'px';
35069};
35070
35071/**
35072 * Start playing the animation, if requested and filter present. Only applicable
35073 * when animation data is available.
35074 */
35075Graph3d.prototype.animationStart = function () {
35076  // start animation when option is true
35077  if (!this.animationAutoStart || !this.dataGroup.dataFilter) return;
35078
35079  if (!this.frame.filter || !this.frame.filter.slider) throw new Error('No animation available');
35080
35081  this.frame.filter.slider.play();
35082};
35083
35084/**
35085 * Stop animation
35086 */
35087Graph3d.prototype.animationStop = function () {
35088  if (!this.frame.filter || !this.frame.filter.slider) return;
35089
35090  this.frame.filter.slider.stop();
35091};
35092
35093/**
35094 * Resize the center position based on the current values in this.xCenter
35095 * and this.yCenter (which are strings with a percentage or a value
35096 * in pixels). The center positions are the variables this.currentXCenter
35097 * and this.currentYCenter
35098 */
35099Graph3d.prototype._resizeCenter = function () {
35100  // calculate the horizontal center position
35101  if (this.xCenter.charAt(this.xCenter.length - 1) === '%') {
35102    this.currentXCenter = parseFloat(this.xCenter) / 100 * this.frame.canvas.clientWidth;
35103  } else {
35104    this.currentXCenter = parseFloat(this.xCenter); // supposed to be in px
35105  }
35106
35107  // calculate the vertical center position
35108  if (this.yCenter.charAt(this.yCenter.length - 1) === '%') {
35109    this.currentYCenter = parseFloat(this.yCenter) / 100 * (this.frame.canvas.clientHeight - this.frame.filter.clientHeight);
35110  } else {
35111    this.currentYCenter = parseFloat(this.yCenter); // supposed to be in px
35112  }
35113};
35114
35115/**
35116 * Retrieve the current camera rotation
35117 *
35118 * @returns {object} An object with parameters horizontal, vertical, and
35119 *                   distance
35120 */
35121Graph3d.prototype.getCameraPosition = function () {
35122  var pos = this.camera.getArmRotation();
35123  pos.distance = this.camera.getArmLength();
35124  return pos;
35125};
35126
35127/**
35128 * Load data into the 3D Graph
35129 *
35130 * @param {vis.DataSet} data
35131 * @private
35132 */
35133Graph3d.prototype._readData = function (data) {
35134  // read the data
35135  this.dataPoints = this.dataGroup.initializeData(this, data, this.style);
35136
35137  this._initializeRanges();
35138  this._redrawFilter();
35139};
35140
35141/**
35142 * Replace the dataset of the Graph3d
35143 *
35144 * @param {Array | DataSet | DataView} data
35145 */
35146Graph3d.prototype.setData = function (data) {
35147  if (data === undefined || data === null) return;
35148
35149  this._readData(data);
35150  this.redraw();
35151  this.animationStart();
35152};
35153
35154/**
35155 * Update the options. Options will be merged with current options
35156 *
35157 * @param {Object} options
35158 */
35159Graph3d.prototype.setOptions = function (options) {
35160  if (options === undefined) return;
35161
35162  var errorFound = Validator.validate(options, allOptions);
35163  if (errorFound === true) {
35164    console.log('%cErrors have been found in the supplied options object.', printStyle);
35165  }
35166
35167  this.animationStop();
35168
35169  Settings.setOptions(options, this);
35170  this.setPointDrawingMethod();
35171  this._setSize(this.width, this.height);
35172
35173  this.setData(this.dataGroup.getDataTable());
35174  this.animationStart();
35175};
35176
35177/**
35178 * Determine which point drawing method to use for the current graph style.
35179 */
35180Graph3d.prototype.setPointDrawingMethod = function () {
35181  var method = undefined;
35182
35183  switch (this.style) {
35184    case Graph3d.STYLE.BAR:
35185      method = Graph3d.prototype._redrawBarGraphPoint;
35186      break;
35187    case Graph3d.STYLE.BARCOLOR:
35188      method = Graph3d.prototype._redrawBarColorGraphPoint;
35189      break;
35190    case Graph3d.STYLE.BARSIZE:
35191      method = Graph3d.prototype._redrawBarSizeGraphPoint;
35192      break;
35193    case Graph3d.STYLE.DOT:
35194      method = Graph3d.prototype._redrawDotGraphPoint;
35195      break;
35196    case Graph3d.STYLE.DOTLINE:
35197      method = Graph3d.prototype._redrawDotLineGraphPoint;
35198      break;
35199    case Graph3d.STYLE.DOTCOLOR:
35200      method = Graph3d.prototype._redrawDotColorGraphPoint;
35201      break;
35202    case Graph3d.STYLE.DOTSIZE:
35203      method = Graph3d.prototype._redrawDotSizeGraphPoint;
35204      break;
35205    case Graph3d.STYLE.SURFACE:
35206      method = Graph3d.prototype._redrawSurfaceGraphPoint;
35207      break;
35208    case Graph3d.STYLE.GRID:
35209      method = Graph3d.prototype._redrawGridGraphPoint;
35210      break;
35211    case Graph3d.STYLE.LINE:
35212      method = Graph3d.prototype._redrawLineGraphPoint;
35213      break;
35214    default:
35215      throw new Error('Can not determine point drawing method ' + 'for graph style \'' + this.style + '\'');
35216  }
35217
35218  this._pointDrawingMethod = method;
35219};
35220
35221/**
35222 * Redraw the Graph.
35223 */
35224Graph3d.prototype.redraw = function () {
35225  if (this.dataPoints === undefined) {
35226    throw new Error('Graph data not initialized');
35227  }
35228
35229  this._resizeCanvas();
35230  this._resizeCenter();
35231  this._redrawSlider();
35232  this._redrawClear();
35233  this._redrawAxis();
35234
35235  this._redrawDataGraph();
35236
35237  this._redrawInfo();
35238  this._redrawLegend();
35239};
35240
35241/**
35242 * Get drawing context without exposing canvas
35243 *
35244 * @returns {CanvasRenderingContext2D}
35245 * @private
35246 */
35247Graph3d.prototype._getContext = function () {
35248  var canvas = this.frame.canvas;
35249  var ctx = canvas.getContext('2d');
35250
35251  ctx.lineJoin = 'round';
35252  ctx.lineCap = 'round';
35253
35254  return ctx;
35255};
35256
35257/**
35258 * Clear the canvas before redrawing
35259 */
35260Graph3d.prototype._redrawClear = function () {
35261  var canvas = this.frame.canvas;
35262  var ctx = canvas.getContext('2d');
35263
35264  ctx.clearRect(0, 0, canvas.width, canvas.height);
35265};
35266
35267Graph3d.prototype._dotSize = function () {
35268  return this.frame.clientWidth * this.dotSizeRatio;
35269};
35270
35271/**
35272 * Get legend width
35273 *
35274 * @returns {*}
35275 * @private
35276 */
35277Graph3d.prototype._getLegendWidth = function () {
35278  var width;
35279
35280  if (this.style === Graph3d.STYLE.DOTSIZE) {
35281    var dotSize = this._dotSize();
35282    //width =  dotSize / 2 + dotSize * 2;
35283    width = dotSize * this.dotSizeMaxFraction;
35284  } else if (this.style === Graph3d.STYLE.BARSIZE) {
35285    width = this.xBarWidth;
35286  } else {
35287    width = 20;
35288  }
35289  return width;
35290};
35291
35292/**
35293 * Redraw the legend based on size, dot color, or surface height
35294 */
35295Graph3d.prototype._redrawLegend = function () {
35296
35297  //Return without drawing anything, if no legend is specified
35298  if (this.showLegend !== true) {
35299    return;
35300  }
35301
35302  // Do not draw legend when graph style does not support
35303  if (this.style === Graph3d.STYLE.LINE || this.style === Graph3d.STYLE.BARSIZE //TODO add legend support for BARSIZE
35304  ) {
35305      return;
35306    }
35307
35308  // Legend types - size and color. Determine if size legend.
35309  var isSizeLegend = this.style === Graph3d.STYLE.BARSIZE || this.style === Graph3d.STYLE.DOTSIZE;
35310
35311  // Legend is either tracking z values or style values. This flag if false means use z values.
35312  var isValueLegend = this.style === Graph3d.STYLE.DOTSIZE || this.style === Graph3d.STYLE.DOTCOLOR || this.style === Graph3d.STYLE.BARCOLOR;
35313
35314  var height = Math.max(this.frame.clientHeight * 0.25, 100);
35315  var top = this.margin;
35316  var width = this._getLegendWidth(); // px - overwritten by size legend
35317  var right = this.frame.clientWidth - this.margin;
35318  var left = right - width;
35319  var bottom = top + height;
35320
35321  var ctx = this._getContext();
35322  ctx.lineWidth = 1;
35323  ctx.font = '14px arial'; // TODO: put in options
35324
35325  if (isSizeLegend === false) {
35326    // draw the color bar
35327    var ymin = 0;
35328    var ymax = height; // Todo: make height customizable
35329    var y;
35330
35331    for (y = ymin; y < ymax; y++) {
35332      var f = (y - ymin) / (ymax - ymin);
35333      var hue = f * 240;
35334      var color = this._hsv2rgb(hue, 1, 1);
35335
35336      ctx.strokeStyle = color;
35337      ctx.beginPath();
35338      ctx.moveTo(left, top + y);
35339      ctx.lineTo(right, top + y);
35340      ctx.stroke();
35341    }
35342    ctx.strokeStyle = this.axisColor;
35343    ctx.strokeRect(left, top, width, height);
35344  } else {
35345
35346    // draw the size legend box
35347    var widthMin;
35348    if (this.style === Graph3d.STYLE.DOTSIZE) {
35349      // Get the proportion to max and min right
35350      widthMin = width * (this.dotSizeMinFraction / this.dotSizeMaxFraction);
35351    } else if (this.style === Graph3d.STYLE.BARSIZE) {
35352      //widthMin = this.xBarWidth * 0.2 this is wrong - barwidth measures in terms of xvalues
35353    }
35354    ctx.strokeStyle = this.axisColor;
35355    ctx.fillStyle = this.dataColor.fill;
35356    ctx.beginPath();
35357    ctx.moveTo(left, top);
35358    ctx.lineTo(right, top);
35359    ctx.lineTo(left + widthMin, bottom);
35360    ctx.lineTo(left, bottom);
35361    ctx.closePath();
35362    ctx.fill();
35363    ctx.stroke();
35364  }
35365
35366  // print value text along the legend edge
35367  var gridLineLen = 5; // px
35368
35369  var legendMin = isValueLegend ? this.valueRange.min : this.zRange.min;
35370  var legendMax = isValueLegend ? this.valueRange.max : this.zRange.max;
35371  var step = new StepNumber(legendMin, legendMax, (legendMax - legendMin) / 5, true);
35372  step.start(true);
35373
35374  var from;
35375  var to;
35376  while (!step.end()) {
35377    y = bottom - (step.getCurrent() - legendMin) / (legendMax - legendMin) * height;
35378    from = new Point2d(left - gridLineLen, y);
35379    to = new Point2d(left, y);
35380    this._line(ctx, from, to);
35381
35382    ctx.textAlign = 'right';
35383    ctx.textBaseline = 'middle';
35384    ctx.fillStyle = this.axisColor;
35385    ctx.fillText(step.getCurrent(), left - 2 * gridLineLen, y);
35386
35387    step.next();
35388  }
35389
35390  ctx.textAlign = 'right';
35391  ctx.textBaseline = 'top';
35392  var label = this.legendLabel;
35393  ctx.fillText(label, right, bottom + this.margin);
35394};
35395
35396/**
35397 * Redraw the filter
35398 */
35399Graph3d.prototype._redrawFilter = function () {
35400  var dataFilter = this.dataGroup.dataFilter;
35401  var filter = this.frame.filter;
35402  filter.innerHTML = '';
35403
35404  if (!dataFilter) {
35405    filter.slider = undefined;
35406    return;
35407  }
35408
35409  var options = {
35410    'visible': this.showAnimationControls
35411  };
35412  var slider = new Slider(filter, options);
35413  filter.slider = slider;
35414
35415  // TODO: css here is not nice here...
35416  filter.style.padding = '10px';
35417  //this.frame.filter.style.backgroundColor = '#EFEFEF';
35418
35419  slider.setValues(dataFilter.values);
35420  slider.setPlayInterval(this.animationInterval);
35421
35422  // create an event handler
35423  var me = this;
35424  var onchange = function onchange() {
35425    var dataFilter = me.dataGroup.dataFilter;
35426    var index = slider.getIndex();
35427
35428    dataFilter.selectValue(index);
35429    me.dataPoints = dataFilter._getDataPoints();
35430
35431    me.redraw();
35432  };
35433
35434  slider.setOnChangeCallback(onchange);
35435};
35436
35437/**
35438 * Redraw the slider
35439 */
35440Graph3d.prototype._redrawSlider = function () {
35441  if (this.frame.filter.slider !== undefined) {
35442    this.frame.filter.slider.redraw();
35443  }
35444};
35445
35446/**
35447 * Redraw common information
35448 */
35449Graph3d.prototype._redrawInfo = function () {
35450  var info = this.dataGroup.getInfo();
35451  if (info === undefined) return;
35452
35453  var ctx = this._getContext();
35454
35455  ctx.font = '14px arial'; // TODO: put in options
35456  ctx.lineStyle = 'gray';
35457  ctx.fillStyle = 'gray';
35458  ctx.textAlign = 'left';
35459  ctx.textBaseline = 'top';
35460
35461  var x = this.margin;
35462  var y = this.margin;
35463  ctx.fillText(info, x, y);
35464};
35465
35466/**
35467 * Draw a line between 2d points 'from' and 'to'.
35468 *
35469 * If stroke style specified, set that as well.
35470 *
35471 * @param {CanvasRenderingContext2D} ctx
35472 * @param {vis.Point2d} from
35473 * @param {vis.Point2d} to
35474 * @param {string} [strokeStyle]
35475 * @private
35476 */
35477Graph3d.prototype._line = function (ctx, from, to, strokeStyle) {
35478  if (strokeStyle !== undefined) {
35479    ctx.strokeStyle = strokeStyle;
35480  }
35481
35482  ctx.beginPath();
35483  ctx.moveTo(from.x, from.y);
35484  ctx.lineTo(to.x, to.y);
35485  ctx.stroke();
35486};
35487
35488/**
35489 *
35490 * @param {CanvasRenderingContext2D} ctx
35491 * @param {vis.Point3d} point3d
35492 * @param {string} text
35493 * @param {number} armAngle
35494 * @param {number} [yMargin=0]
35495 */
35496Graph3d.prototype.drawAxisLabelX = function (ctx, point3d, text, armAngle, yMargin) {
35497  if (yMargin === undefined) {
35498    yMargin = 0;
35499  }
35500
35501  var point2d = this._convert3Dto2D(point3d);
35502
35503  if (Math.cos(armAngle * 2) > 0) {
35504    ctx.textAlign = 'center';
35505    ctx.textBaseline = 'top';
35506    point2d.y += yMargin;
35507  } else if (Math.sin(armAngle * 2) < 0) {
35508    ctx.textAlign = 'right';
35509    ctx.textBaseline = 'middle';
35510  } else {
35511    ctx.textAlign = 'left';
35512    ctx.textBaseline = 'middle';
35513  }
35514
35515  ctx.fillStyle = this.axisColor;
35516  ctx.fillText(text, point2d.x, point2d.y);
35517};
35518
35519/**
35520 *
35521 * @param {CanvasRenderingContext2D} ctx
35522 * @param {vis.Point3d} point3d
35523 * @param {string} text
35524 * @param {number} armAngle
35525 * @param {number} [yMargin=0]
35526 */
35527Graph3d.prototype.drawAxisLabelY = function (ctx, point3d, text, armAngle, yMargin) {
35528  if (yMargin === undefined) {
35529    yMargin = 0;
35530  }
35531
35532  var point2d = this._convert3Dto2D(point3d);
35533
35534  if (Math.cos(armAngle * 2) < 0) {
35535    ctx.textAlign = 'center';
35536    ctx.textBaseline = 'top';
35537    point2d.y += yMargin;
35538  } else if (Math.sin(armAngle * 2) > 0) {
35539    ctx.textAlign = 'right';
35540    ctx.textBaseline = 'middle';
35541  } else {
35542    ctx.textAlign = 'left';
35543    ctx.textBaseline = 'middle';
35544  }
35545
35546  ctx.fillStyle = this.axisColor;
35547  ctx.fillText(text, point2d.x, point2d.y);
35548};
35549
35550/**
35551 *
35552 * @param {CanvasRenderingContext2D} ctx
35553 * @param {vis.Point3d} point3d
35554 * @param {string} text
35555 * @param {number} [offset=0]
35556 */
35557Graph3d.prototype.drawAxisLabelZ = function (ctx, point3d, text, offset) {
35558  if (offset === undefined) {
35559    offset = 0;
35560  }
35561
35562  var point2d = this._convert3Dto2D(point3d);
35563  ctx.textAlign = 'right';
35564  ctx.textBaseline = 'middle';
35565  ctx.fillStyle = this.axisColor;
35566  ctx.fillText(text, point2d.x - offset, point2d.y);
35567};
35568
35569/**
35570
35571
35572/**
35573 * Draw a line between 2d points 'from' and 'to'.
35574 *
35575 * If stroke style specified, set that as well.
35576 *
35577 * @param {CanvasRenderingContext2D} ctx
35578 * @param {vis.Point2d} from
35579 * @param {vis.Point2d} to
35580 * @param {string} [strokeStyle]
35581 * @private
35582 */
35583Graph3d.prototype._line3d = function (ctx, from, to, strokeStyle) {
35584  var from2d = this._convert3Dto2D(from);
35585  var to2d = this._convert3Dto2D(to);
35586
35587  this._line(ctx, from2d, to2d, strokeStyle);
35588};
35589
35590/**
35591 * Redraw the axis
35592 */
35593Graph3d.prototype._redrawAxis = function () {
35594  var ctx = this._getContext(),
35595      from,
35596      to,
35597      step,
35598      prettyStep,
35599      text,
35600      xText,
35601      yText,
35602      zText,
35603      offset,
35604      xOffset,
35605      yOffset;
35606
35607  // TODO: get the actual rendered style of the containerElement
35608  //ctx.font = this.containerElement.style.font;
35609  ctx.font = 24 / this.camera.getArmLength() + 'px arial';
35610
35611  // calculate the length for the short grid lines
35612  var gridLenX = 0.025 / this.scale.x;
35613  var gridLenY = 0.025 / this.scale.y;
35614  var textMargin = 5 / this.camera.getArmLength(); // px
35615  var armAngle = this.camera.getArmRotation().horizontal;
35616  var armVector = new Point2d(Math.cos(armAngle), Math.sin(armAngle));
35617
35618  var xRange = this.xRange;
35619  var yRange = this.yRange;
35620  var zRange = this.zRange;
35621  var point3d;
35622
35623  // draw x-grid lines
35624  ctx.lineWidth = 1;
35625  prettyStep = this.defaultXStep === undefined;
35626  step = new StepNumber(xRange.min, xRange.max, this.xStep, prettyStep);
35627  step.start(true);
35628
35629  while (!step.end()) {
35630    var x = step.getCurrent();
35631
35632    if (this.showGrid) {
35633      from = new Point3d(x, yRange.min, zRange.min);
35634      to = new Point3d(x, yRange.max, zRange.min);
35635      this._line3d(ctx, from, to, this.gridColor);
35636    } else if (this.showXAxis) {
35637      from = new Point3d(x, yRange.min, zRange.min);
35638      to = new Point3d(x, yRange.min + gridLenX, zRange.min);
35639      this._line3d(ctx, from, to, this.axisColor);
35640
35641      from = new Point3d(x, yRange.max, zRange.min);
35642      to = new Point3d(x, yRange.max - gridLenX, zRange.min);
35643      this._line3d(ctx, from, to, this.axisColor);
35644    }
35645
35646    if (this.showXAxis) {
35647      yText = armVector.x > 0 ? yRange.min : yRange.max;
35648      point3d = new Point3d(x, yText, zRange.min);
35649      var msg = '  ' + this.xValueLabel(x) + '  ';
35650      this.drawAxisLabelX(ctx, point3d, msg, armAngle, textMargin);
35651    }
35652
35653    step.next();
35654  }
35655
35656  // draw y-grid lines
35657  ctx.lineWidth = 1;
35658  prettyStep = this.defaultYStep === undefined;
35659  step = new StepNumber(yRange.min, yRange.max, this.yStep, prettyStep);
35660  step.start(true);
35661
35662  while (!step.end()) {
35663    var y = step.getCurrent();
35664
35665    if (this.showGrid) {
35666      from = new Point3d(xRange.min, y, zRange.min);
35667      to = new Point3d(xRange.max, y, zRange.min);
35668      this._line3d(ctx, from, to, this.gridColor);
35669    } else if (this.showYAxis) {
35670      from = new Point3d(xRange.min, y, zRange.min);
35671      to = new Point3d(xRange.min + gridLenY, y, zRange.min);
35672      this._line3d(ctx, from, to, this.axisColor);
35673
35674      from = new Point3d(xRange.max, y, zRange.min);
35675      to = new Point3d(xRange.max - gridLenY, y, zRange.min);
35676      this._line3d(ctx, from, to, this.axisColor);
35677    }
35678
35679    if (this.showYAxis) {
35680      xText = armVector.y > 0 ? xRange.min : xRange.max;
35681      point3d = new Point3d(xText, y, zRange.min);
35682      var _msg = '  ' + this.yValueLabel(y) + '  ';
35683      this.drawAxisLabelY(ctx, point3d, _msg, armAngle, textMargin);
35684    }
35685
35686    step.next();
35687  }
35688
35689  // draw z-grid lines and axis
35690  if (this.showZAxis) {
35691    ctx.lineWidth = 1;
35692    prettyStep = this.defaultZStep === undefined;
35693    step = new StepNumber(zRange.min, zRange.max, this.zStep, prettyStep);
35694    step.start(true);
35695
35696    xText = armVector.x > 0 ? xRange.min : xRange.max;
35697    yText = armVector.y < 0 ? yRange.min : yRange.max;
35698
35699    while (!step.end()) {
35700      var z = step.getCurrent();
35701
35702      // TODO: make z-grid lines really 3d?
35703      var from3d = new Point3d(xText, yText, z);
35704      var from2d = this._convert3Dto2D(from3d);
35705      to = new Point2d(from2d.x - textMargin, from2d.y);
35706      this._line(ctx, from2d, to, this.axisColor);
35707
35708      var _msg2 = this.zValueLabel(z) + ' ';
35709      this.drawAxisLabelZ(ctx, from3d, _msg2, 5);
35710
35711      step.next();
35712    }
35713
35714    ctx.lineWidth = 1;
35715    from = new Point3d(xText, yText, zRange.min);
35716    to = new Point3d(xText, yText, zRange.max);
35717    this._line3d(ctx, from, to, this.axisColor);
35718  }
35719
35720  // draw x-axis
35721  if (this.showXAxis) {
35722    var xMin2d;
35723    var xMax2d;
35724    ctx.lineWidth = 1;
35725
35726    // line at yMin
35727    xMin2d = new Point3d(xRange.min, yRange.min, zRange.min);
35728    xMax2d = new Point3d(xRange.max, yRange.min, zRange.min);
35729    this._line3d(ctx, xMin2d, xMax2d, this.axisColor);
35730    // line at ymax
35731    xMin2d = new Point3d(xRange.min, yRange.max, zRange.min);
35732    xMax2d = new Point3d(xRange.max, yRange.max, zRange.min);
35733    this._line3d(ctx, xMin2d, xMax2d, this.axisColor);
35734  }
35735
35736  // draw y-axis
35737  if (this.showYAxis) {
35738    ctx.lineWidth = 1;
35739    // line at xMin
35740    from = new Point3d(xRange.min, yRange.min, zRange.min);
35741    to = new Point3d(xRange.min, yRange.max, zRange.min);
35742    this._line3d(ctx, from, to, this.axisColor);
35743    // line at xMax
35744    from = new Point3d(xRange.max, yRange.min, zRange.min);
35745    to = new Point3d(xRange.max, yRange.max, zRange.min);
35746    this._line3d(ctx, from, to, this.axisColor);
35747  }
35748
35749  // draw x-label
35750  var xLabel = this.xLabel;
35751  if (xLabel.length > 0 && this.showXAxis) {
35752    yOffset = 0.1 / this.scale.y;
35753    xText = (xRange.max + 3 * xRange.min) / 4;
35754    yText = armVector.x > 0 ? yRange.min - yOffset : yRange.max + yOffset;
35755    text = new Point3d(xText, yText, zRange.min);
35756    this.drawAxisLabelX(ctx, text, xLabel, armAngle);
35757  }
35758
35759  // draw y-label
35760  var yLabel = this.yLabel;
35761  if (yLabel.length > 0 && this.showYAxis) {
35762    xOffset = 0.1 / this.scale.x;
35763    xText = armVector.y > 0 ? xRange.min - xOffset : xRange.max + xOffset;
35764    yText = (yRange.max + 3 * yRange.min) / 4;
35765    text = new Point3d(xText, yText, zRange.min);
35766
35767    this.drawAxisLabelY(ctx, text, yLabel, armAngle);
35768  }
35769
35770  // draw z-label
35771  var zLabel = this.zLabel;
35772  if (zLabel.length > 0 && this.showZAxis) {
35773    offset = 30; // pixels.  // TODO: relate to the max width of the values on the z axis?
35774    xText = armVector.x > 0 ? xRange.min : xRange.max;
35775    yText = armVector.y < 0 ? yRange.min : yRange.max;
35776    zText = (zRange.max + 3 * zRange.min) / 4;
35777    text = new Point3d(xText, yText, zText);
35778
35779    this.drawAxisLabelZ(ctx, text, zLabel, offset);
35780  }
35781};
35782
35783/**
35784 * Calculate the color based on the given value.
35785 * @param {number} H   Hue, a value be between 0 and 360
35786 * @param {number} S   Saturation, a value between 0 and 1
35787 * @param {number} V   Value, a value between 0 and 1
35788 * @returns {string}
35789 * @private
35790 */
35791Graph3d.prototype._hsv2rgb = function (H, S, V) {
35792  var R, G, B, C, Hi, X;
35793
35794  C = V * S;
35795  Hi = Math.floor(H / 60); // hi = 0,1,2,3,4,5
35796  X = C * (1 - Math.abs(H / 60 % 2 - 1));
35797
35798  switch (Hi) {
35799    case 0:
35800      R = C;G = X;B = 0;break;
35801    case 1:
35802      R = X;G = C;B = 0;break;
35803    case 2:
35804      R = 0;G = C;B = X;break;
35805    case 3:
35806      R = 0;G = X;B = C;break;
35807    case 4:
35808      R = X;G = 0;B = C;break;
35809    case 5:
35810      R = C;G = 0;B = X;break;
35811
35812    default:
35813      R = 0;G = 0;B = 0;break;
35814  }
35815
35816  return 'RGB(' + parseInt(R * 255) + ',' + parseInt(G * 255) + ',' + parseInt(B * 255) + ')';
35817};
35818
35819/**
35820 *
35821 * @param {vis.Point3d} point
35822 * @returns {*}
35823 * @private
35824 */
35825Graph3d.prototype._getStrokeWidth = function (point) {
35826  if (point !== undefined) {
35827    if (this.showPerspective) {
35828      return 1 / -point.trans.z * this.dataColor.strokeWidth;
35829    } else {
35830      return -(this.eye.z / this.camera.getArmLength()) * this.dataColor.strokeWidth;
35831    }
35832  }
35833
35834  return this.dataColor.strokeWidth;
35835};
35836
35837// -----------------------------------------------------------------------------
35838// Drawing primitives for the graphs
35839// -----------------------------------------------------------------------------
35840
35841
35842/**
35843 * Draw a bar element in the view with the given properties.
35844 *
35845 * @param {CanvasRenderingContext2D} ctx
35846 * @param {Object} point
35847 * @param {number} xWidth
35848 * @param {number} yWidth
35849 * @param {string} color
35850 * @param {string} borderColor
35851 * @private
35852 */
35853Graph3d.prototype._redrawBar = function (ctx, point, xWidth, yWidth, color, borderColor) {
35854  var surface;
35855
35856  // calculate all corner points
35857  var me = this;
35858  var point3d = point.point;
35859  var zMin = this.zRange.min;
35860  var top = [{ point: new Point3d(point3d.x - xWidth, point3d.y - yWidth, point3d.z) }, { point: new Point3d(point3d.x + xWidth, point3d.y - yWidth, point3d.z) }, { point: new Point3d(point3d.x + xWidth, point3d.y + yWidth, point3d.z) }, { point: new Point3d(point3d.x - xWidth, point3d.y + yWidth, point3d.z) }];
35861  var bottom = [{ point: new Point3d(point3d.x - xWidth, point3d.y - yWidth, zMin) }, { point: new Point3d(point3d.x + xWidth, point3d.y - yWidth, zMin) }, { point: new Point3d(point3d.x + xWidth, point3d.y + yWidth, zMin) }, { point: new Point3d(point3d.x - xWidth, point3d.y + yWidth, zMin) }];
35862
35863  // calculate screen location of the points
35864  top.forEach(function (obj) {
35865    obj.screen = me._convert3Dto2D(obj.point);
35866  });
35867  bottom.forEach(function (obj) {
35868    obj.screen = me._convert3Dto2D(obj.point);
35869  });
35870
35871  // create five sides, calculate both corner points and center points
35872  var surfaces = [{ corners: top, center: Point3d.avg(bottom[0].point, bottom[2].point) }, { corners: [top[0], top[1], bottom[1], bottom[0]], center: Point3d.avg(bottom[1].point, bottom[0].point) }, { corners: [top[1], top[2], bottom[2], bottom[1]], center: Point3d.avg(bottom[2].point, bottom[1].point) }, { corners: [top[2], top[3], bottom[3], bottom[2]], center: Point3d.avg(bottom[3].point, bottom[2].point) }, { corners: [top[3], top[0], bottom[0], bottom[3]], center: Point3d.avg(bottom[0].point, bottom[3].point) }];
35873  point.surfaces = surfaces;
35874
35875  // calculate the distance of each of the surface centers to the camera
35876  for (var j = 0; j < surfaces.length; j++) {
35877    surface = surfaces[j];
35878    var transCenter = this._convertPointToTranslation(surface.center);
35879    surface.dist = this.showPerspective ? transCenter.length() : -transCenter.z;
35880    // TODO: this dept calculation doesn't work 100% of the cases due to perspective,
35881    //     but the current solution is fast/simple and works in 99.9% of all cases
35882    //     the issue is visible in example 14, with graph.setCameraPosition({horizontal: 2.97, vertical: 0.5, distance: 0.9})
35883  }
35884
35885  // order the surfaces by their (translated) depth
35886  surfaces.sort(function (a, b) {
35887    var diff = b.dist - a.dist;
35888    if (diff) return diff;
35889
35890    // if equal depth, sort the top surface last
35891    if (a.corners === top) return 1;
35892    if (b.corners === top) return -1;
35893
35894    // both are equal
35895    return 0;
35896  });
35897
35898  // draw the ordered surfaces
35899  ctx.lineWidth = this._getStrokeWidth(point);
35900  ctx.strokeStyle = borderColor;
35901  ctx.fillStyle = color;
35902  // NOTE: we start at j=2 instead of j=0 as we don't need to draw the two surfaces at the backside
35903  for (var _j = 2; _j < surfaces.length; _j++) {
35904    surface = surfaces[_j];
35905    this._polygon(ctx, surface.corners);
35906  }
35907};
35908
35909/**
35910 * Draw a polygon using the passed points and fill it with the passed style and stroke.
35911 *
35912 * @param {CanvasRenderingContext2D} ctx
35913 * @param {Array.<vis.Point3d>} points      an array of points.
35914 * @param {string} [fillStyle] the fill style to set
35915 * @param {string} [strokeStyle] the stroke style to set
35916 */
35917Graph3d.prototype._polygon = function (ctx, points, fillStyle, strokeStyle) {
35918  if (points.length < 2) {
35919    return;
35920  }
35921
35922  if (fillStyle !== undefined) {
35923    ctx.fillStyle = fillStyle;
35924  }
35925  if (strokeStyle !== undefined) {
35926    ctx.strokeStyle = strokeStyle;
35927  }
35928  ctx.beginPath();
35929  ctx.moveTo(points[0].screen.x, points[0].screen.y);
35930
35931  for (var i = 1; i < points.length; ++i) {
35932    var point = points[i];
35933    ctx.lineTo(point.screen.x, point.screen.y);
35934  }
35935
35936  ctx.closePath();
35937  ctx.fill();
35938  ctx.stroke(); // TODO: only draw stroke when strokeWidth > 0
35939};
35940
35941/**
35942 * @param {CanvasRenderingContext2D} ctx
35943 * @param {object} point
35944 * @param {string} color
35945 * @param {string} borderColor
35946 * @param {number} [size=this._dotSize()]
35947 * @private
35948 */
35949Graph3d.prototype._drawCircle = function (ctx, point, color, borderColor, size) {
35950  var radius = this._calcRadius(point, size);
35951
35952  ctx.lineWidth = this._getStrokeWidth(point);
35953  ctx.strokeStyle = borderColor;
35954  ctx.fillStyle = color;
35955  ctx.beginPath();
35956  ctx.arc(point.screen.x, point.screen.y, radius, 0, Math.PI * 2, true);
35957  ctx.fill();
35958  ctx.stroke();
35959};
35960
35961/**
35962 * Determine the colors for the 'regular' graph styles.
35963 *
35964 * @param {object} point
35965 * @returns {{fill, border}}
35966 * @private
35967 */
35968Graph3d.prototype._getColorsRegular = function (point) {
35969  // calculate Hue from the current value. At zMin the hue is 240, at zMax the hue is 0
35970  var hue = (1 - (point.point.z - this.zRange.min) * this.scale.z / this.verticalRatio) * 240;
35971  var color = this._hsv2rgb(hue, 1, 1);
35972  var borderColor = this._hsv2rgb(hue, 1, 0.8);
35973
35974  return {
35975    fill: color,
35976    border: borderColor
35977  };
35978};
35979
35980/**
35981 * Get the colors for the 'color' graph styles.
35982 * These styles are currently: 'bar-color' and 'dot-color'
35983 * Color may be set as a string representation of HTML color, like #ff00ff,
35984 * or calculated from a number, for example, distance from this point
35985 * The first option is useful when we have some pre-given legend, to which we have to adjust ourselves
35986 * The second option is useful when we are interested in automatically setting the color, from some value,
35987 * using some color scale
35988 * @param {object} point
35989 * @returns {{fill: *, border: *}}
35990 * @private
35991 */
35992Graph3d.prototype._getColorsColor = function (point) {
35993  // calculate the color based on the value
35994  var color, borderColor;
35995
35996  if (typeof point.point.value === "string") {
35997    color = point.point.value;
35998    borderColor = point.point.value;
35999  } else {
36000    var hue = (1 - (point.point.value - this.valueRange.min) * this.scale.value) * 240;
36001    color = this._hsv2rgb(hue, 1, 1);
36002    borderColor = this._hsv2rgb(hue, 1, 0.8);
36003  }
36004  return {
36005    fill: color,
36006    border: borderColor
36007  };
36008};
36009
36010/**
36011 * Get the colors for the 'size' graph styles.
36012 * These styles are currently: 'bar-size' and 'dot-size'
36013 *
36014 * @returns {{fill: *, border: (string|colorOptions.stroke|{string, undefined}|string|colorOptions.stroke|{string}|*)}}
36015 * @private
36016 */
36017Graph3d.prototype._getColorsSize = function () {
36018  return {
36019    fill: this.dataColor.fill,
36020    border: this.dataColor.stroke
36021  };
36022};
36023
36024/**
36025 * Determine the size of a point on-screen, as determined by the
36026 * distance to the camera.
36027 *
36028 * @param {Object} point
36029 * @param {number} [size=this._dotSize()] the size that needs to be translated to screen coordinates.
36030 *             optional; if not passed, use the default point size.
36031 * @returns {number}
36032 * @private
36033 */
36034Graph3d.prototype._calcRadius = function (point, size) {
36035  if (size === undefined) {
36036    size = this._dotSize();
36037  }
36038
36039  var radius;
36040  if (this.showPerspective) {
36041    radius = size / -point.trans.z;
36042  } else {
36043    radius = size * -(this.eye.z / this.camera.getArmLength());
36044  }
36045  if (radius < 0) {
36046    radius = 0;
36047  }
36048
36049  return radius;
36050};
36051
36052// -----------------------------------------------------------------------------
36053// Methods for drawing points per graph style.
36054// -----------------------------------------------------------------------------
36055
36056
36057/**
36058 * Draw single datapoint for graph style 'bar'.
36059 *
36060 * @param {CanvasRenderingContext2D} ctx
36061 * @param {Object} point
36062 * @private
36063 */
36064Graph3d.prototype._redrawBarGraphPoint = function (ctx, point) {
36065  var xWidth = this.xBarWidth / 2;
36066  var yWidth = this.yBarWidth / 2;
36067  var colors = this._getColorsRegular(point);
36068
36069  this._redrawBar(ctx, point, xWidth, yWidth, colors.fill, colors.border);
36070};
36071
36072/**
36073 * Draw single datapoint for graph style 'bar-color'.
36074 *
36075 * @param {CanvasRenderingContext2D} ctx
36076 * @param {Object} point
36077 * @private
36078 */
36079Graph3d.prototype._redrawBarColorGraphPoint = function (ctx, point) {
36080  var xWidth = this.xBarWidth / 2;
36081  var yWidth = this.yBarWidth / 2;
36082  var colors = this._getColorsColor(point);
36083
36084  this._redrawBar(ctx, point, xWidth, yWidth, colors.fill, colors.border);
36085};
36086
36087/**
36088 * Draw single datapoint for graph style 'bar-size'.
36089 *
36090 * @param {CanvasRenderingContext2D} ctx
36091 * @param {Object} point
36092 * @private
36093 */
36094Graph3d.prototype._redrawBarSizeGraphPoint = function (ctx, point) {
36095  // calculate size for the bar
36096  var fraction = (point.point.value - this.valueRange.min) / this.valueRange.range();
36097  var xWidth = this.xBarWidth / 2 * (fraction * 0.8 + 0.2);
36098  var yWidth = this.yBarWidth / 2 * (fraction * 0.8 + 0.2);
36099
36100  var colors = this._getColorsSize();
36101
36102  this._redrawBar(ctx, point, xWidth, yWidth, colors.fill, colors.border);
36103};
36104
36105/**
36106 * Draw single datapoint for graph style 'dot'.
36107 *
36108 * @param {CanvasRenderingContext2D} ctx
36109 * @param {Object} point
36110 * @private
36111 */
36112Graph3d.prototype._redrawDotGraphPoint = function (ctx, point) {
36113  var colors = this._getColorsRegular(point);
36114
36115  this._drawCircle(ctx, point, colors.fill, colors.border);
36116};
36117
36118/**
36119 * Draw single datapoint for graph style 'dot-line'.
36120 *
36121 * @param {CanvasRenderingContext2D} ctx
36122 * @param {Object} point
36123 * @private
36124 */
36125Graph3d.prototype._redrawDotLineGraphPoint = function (ctx, point) {
36126  // draw a vertical line from the XY-plane to the graph value
36127  var from = this._convert3Dto2D(point.bottom);
36128  ctx.lineWidth = 1;
36129  this._line(ctx, from, point.screen, this.gridColor);
36130
36131  this._redrawDotGraphPoint(ctx, point);
36132};
36133
36134/**
36135 * Draw single datapoint for graph style 'dot-color'.
36136 *
36137 * @param {CanvasRenderingContext2D} ctx
36138 * @param {Object} point
36139 * @private
36140 */
36141Graph3d.prototype._redrawDotColorGraphPoint = function (ctx, point) {
36142  var colors = this._getColorsColor(point);
36143
36144  this._drawCircle(ctx, point, colors.fill, colors.border);
36145};
36146
36147/**
36148 * Draw single datapoint for graph style 'dot-size'.
36149 *
36150 * @param {CanvasRenderingContext2D} ctx
36151 * @param {Object} point
36152 * @private
36153 */
36154Graph3d.prototype._redrawDotSizeGraphPoint = function (ctx, point) {
36155  var dotSize = this._dotSize();
36156  var fraction = (point.point.value - this.valueRange.min) / this.valueRange.range();
36157
36158  var sizeMin = dotSize * this.dotSizeMinFraction;
36159  var sizeRange = dotSize * this.dotSizeMaxFraction - sizeMin;
36160  var size = sizeMin + sizeRange * fraction;
36161
36162  var colors = this._getColorsSize();
36163
36164  this._drawCircle(ctx, point, colors.fill, colors.border, size);
36165};
36166
36167/**
36168 * Draw single datapoint for graph style 'surface'.
36169 *
36170 * @param {CanvasRenderingContext2D} ctx
36171 * @param {Object} point
36172 * @private
36173 */
36174Graph3d.prototype._redrawSurfaceGraphPoint = function (ctx, point) {
36175  var right = point.pointRight;
36176  var top = point.pointTop;
36177  var cross = point.pointCross;
36178
36179  if (point === undefined || right === undefined || top === undefined || cross === undefined) {
36180    return;
36181  }
36182
36183  var topSideVisible = true;
36184  var fillStyle;
36185  var strokeStyle;
36186
36187  if (this.showGrayBottom || this.showShadow) {
36188    // calculate the cross product of the two vectors from center
36189    // to left and right, in order to know whether we are looking at the
36190    // bottom or at the top side. We can also use the cross product
36191    // for calculating light intensity
36192    var aDiff = Point3d.subtract(cross.trans, point.trans);
36193    var bDiff = Point3d.subtract(top.trans, right.trans);
36194    var crossproduct = Point3d.crossProduct(aDiff, bDiff);
36195    var len = crossproduct.length();
36196    // FIXME: there is a bug with determining the surface side (shadow or colored)
36197
36198    topSideVisible = crossproduct.z > 0;
36199  }
36200
36201  if (topSideVisible) {
36202
36203    // calculate Hue from the current value. At zMin the hue is 240, at zMax the hue is 0
36204    var zAvg = (point.point.z + right.point.z + top.point.z + cross.point.z) / 4;
36205    var h = (1 - (zAvg - this.zRange.min) * this.scale.z / this.verticalRatio) * 240;
36206    var s = 1; // saturation
36207    var v;
36208
36209    if (this.showShadow) {
36210      v = Math.min(1 + crossproduct.x / len / 2, 1); // value. TODO: scale
36211      fillStyle = this._hsv2rgb(h, s, v);
36212      strokeStyle = fillStyle;
36213    } else {
36214      v = 1;
36215      fillStyle = this._hsv2rgb(h, s, v);
36216      strokeStyle = this.axisColor; // TODO: should be customizable
36217    }
36218  } else {
36219    fillStyle = 'gray';
36220    strokeStyle = this.axisColor;
36221  }
36222
36223  ctx.lineWidth = this._getStrokeWidth(point);
36224  // TODO: only draw stroke when strokeWidth > 0
36225
36226  var points = [point, right, cross, top];
36227  this._polygon(ctx, points, fillStyle, strokeStyle);
36228};
36229
36230/**
36231 * Helper method for _redrawGridGraphPoint()
36232 *
36233 * @param {CanvasRenderingContext2D} ctx
36234 * @param {Object} from
36235 * @param {Object} to
36236 * @private
36237 */
36238Graph3d.prototype._drawGridLine = function (ctx, from, to) {
36239  if (from === undefined || to === undefined) {
36240    return;
36241  }
36242
36243  // calculate Hue from the current value. At zMin the hue is 240, at zMax the hue is 0
36244  var zAvg = (from.point.z + to.point.z) / 2;
36245  var h = (1 - (zAvg - this.zRange.min) * this.scale.z / this.verticalRatio) * 240;
36246
36247  ctx.lineWidth = this._getStrokeWidth(from) * 2;
36248  ctx.strokeStyle = this._hsv2rgb(h, 1, 1);
36249  this._line(ctx, from.screen, to.screen);
36250};
36251
36252/**
36253 * Draw single datapoint for graph style 'Grid'.
36254 *
36255 * @param {CanvasRenderingContext2D} ctx
36256 * @param {Object} point
36257 * @private
36258 */
36259Graph3d.prototype._redrawGridGraphPoint = function (ctx, point) {
36260  this._drawGridLine(ctx, point, point.pointRight);
36261  this._drawGridLine(ctx, point, point.pointTop);
36262};
36263
36264/**
36265 * Draw single datapoint for graph style 'line'.
36266 *
36267 * @param {CanvasRenderingContext2D} ctx
36268 * @param {Object} point
36269 * @private
36270 */
36271Graph3d.prototype._redrawLineGraphPoint = function (ctx, point) {
36272  if (point.pointNext === undefined) {
36273    return;
36274  }
36275
36276  ctx.lineWidth = this._getStrokeWidth(point);
36277  ctx.strokeStyle = this.dataColor.stroke;
36278
36279  this._line(ctx, point.screen, point.pointNext.screen);
36280};
36281
36282/**
36283 * Draw all datapoints for currently selected graph style.
36284 *
36285 */
36286Graph3d.prototype._redrawDataGraph = function () {
36287  var ctx = this._getContext();
36288  var i;
36289
36290  if (this.dataPoints === undefined || this.dataPoints.length <= 0) return; // TODO: throw exception?
36291
36292  this._calcTranslations(this.dataPoints);
36293
36294  for (i = 0; i < this.dataPoints.length; i++) {
36295    var point = this.dataPoints[i];
36296
36297    // Using call() ensures that the correct context is used
36298    this._pointDrawingMethod.call(this, ctx, point);
36299  }
36300};
36301
36302// -----------------------------------------------------------------------------
36303// End methods for drawing points per graph style.
36304// -----------------------------------------------------------------------------
36305
36306/**
36307 * Store startX, startY and startOffset for mouse operations
36308 *
36309 * @param {Event}     event     The event that occurred
36310 */
36311Graph3d.prototype._storeMousePosition = function (event) {
36312  // get mouse position (different code for IE and all other browsers)
36313  this.startMouseX = getMouseX(event);
36314  this.startMouseY = getMouseY(event);
36315
36316  this._startCameraOffset = this.camera.getOffset();
36317};
36318
36319/**
36320 * Start a moving operation inside the provided parent element
36321 * @param {Event}     event     The event that occurred (required for
36322 *                  retrieving the  mouse position)
36323 */
36324Graph3d.prototype._onMouseDown = function (event) {
36325  event = event || window.event;
36326
36327  // check if mouse is still down (may be up when focus is lost for example
36328  // in an iframe)
36329  if (this.leftButtonDown) {
36330    this._onMouseUp(event);
36331  }
36332
36333  // only react on left mouse button down
36334  this.leftButtonDown = event.which ? event.which === 1 : event.button === 1;
36335  if (!this.leftButtonDown && !this.touchDown) return;
36336
36337  this._storeMousePosition(event);
36338
36339  this.startStart = new Date(this.start);
36340  this.startEnd = new Date(this.end);
36341  this.startArmRotation = this.camera.getArmRotation();
36342
36343  this.frame.style.cursor = 'move';
36344
36345  // add event listeners to handle moving the contents
36346  // we store the function onmousemove and onmouseup in the graph, so we can
36347  // remove the eventlisteners lateron in the function mouseUp()
36348  var me = this;
36349  this.onmousemove = function (event) {
36350    me._onMouseMove(event);
36351  };
36352  this.onmouseup = function (event) {
36353    me._onMouseUp(event);
36354  };
36355  util.addEventListener(document, 'mousemove', me.onmousemove);
36356  util.addEventListener(document, 'mouseup', me.onmouseup);
36357  util.preventDefault(event);
36358};
36359
36360/**
36361 * Perform moving operating.
36362 * This function activated from within the funcion Graph.mouseDown().
36363 * @param {Event}   event  Well, eehh, the event
36364 */
36365Graph3d.prototype._onMouseMove = function (event) {
36366  this.moving = true;
36367  event = event || window.event;
36368
36369  // calculate change in mouse position
36370  var diffX = parseFloat(getMouseX(event)) - this.startMouseX;
36371  var diffY = parseFloat(getMouseY(event)) - this.startMouseY;
36372
36373  // move with ctrl or rotate by other
36374  if (event && event.ctrlKey === true) {
36375    // calculate change in mouse position
36376    var scaleX = this.frame.clientWidth * 0.5;
36377    var scaleY = this.frame.clientHeight * 0.5;
36378
36379    var offXNew = (this._startCameraOffset.x || 0) - diffX / scaleX * this.camera.armLength * 0.8;
36380    var offYNew = (this._startCameraOffset.y || 0) + diffY / scaleY * this.camera.armLength * 0.8;
36381
36382    this.camera.setOffset(offXNew, offYNew);
36383    this._storeMousePosition(event);
36384  } else {
36385    var horizontalNew = this.startArmRotation.horizontal + diffX / 200;
36386    var verticalNew = this.startArmRotation.vertical + diffY / 200;
36387
36388    var snapAngle = 4; // degrees
36389    var snapValue = Math.sin(snapAngle / 360 * 2 * Math.PI);
36390
36391    // snap horizontally to nice angles at 0pi, 0.5pi, 1pi, 1.5pi, etc...
36392    // the -0.001 is to take care that the vertical axis is always drawn at the left front corner
36393    if (Math.abs(Math.sin(horizontalNew)) < snapValue) {
36394      horizontalNew = Math.round(horizontalNew / Math.PI) * Math.PI - 0.001;
36395    }
36396    if (Math.abs(Math.cos(horizontalNew)) < snapValue) {
36397      horizontalNew = (Math.round(horizontalNew / Math.PI - 0.5) + 0.5) * Math.PI - 0.001;
36398    }
36399
36400    // snap vertically to nice angles
36401    if (Math.abs(Math.sin(verticalNew)) < snapValue) {
36402      verticalNew = Math.round(verticalNew / Math.PI) * Math.PI;
36403    }
36404    if (Math.abs(Math.cos(verticalNew)) < snapValue) {
36405      verticalNew = (Math.round(verticalNew / Math.PI - 0.5) + 0.5) * Math.PI;
36406    }
36407    this.camera.setArmRotation(horizontalNew, verticalNew);
36408  }
36409
36410  this.redraw();
36411
36412  // fire a cameraPositionChange event
36413  var parameters = this.getCameraPosition();
36414  this.emit('cameraPositionChange', parameters);
36415
36416  util.preventDefault(event);
36417};
36418
36419/**
36420 * Stop moving operating.
36421 * This function activated from within the funcion Graph.mouseDown().
36422 * @param {Event}  event   The event
36423 */
36424Graph3d.prototype._onMouseUp = function (event) {
36425  this.frame.style.cursor = 'auto';
36426  this.leftButtonDown = false;
36427
36428  // remove event listeners here
36429  util.removeEventListener(document, 'mousemove', this.onmousemove);
36430  util.removeEventListener(document, 'mouseup', this.onmouseup);
36431  util.preventDefault(event);
36432};
36433
36434/**
36435 * @param {Event}  event   The event
36436 */
36437Graph3d.prototype._onClick = function (event) {
36438  if (!this.onclick_callback) return;
36439  if (!this.moving) {
36440    var boundingRect = this.frame.getBoundingClientRect();
36441    var mouseX = getMouseX(event) - boundingRect.left;
36442    var mouseY = getMouseY(event) - boundingRect.top;
36443    var dataPoint = this._dataPointFromXY(mouseX, mouseY);
36444    if (dataPoint) this.onclick_callback(dataPoint.point.data);
36445  } else {
36446    // disable onclick callback, if it came immediately after rotate/pan
36447    this.moving = false;
36448  }
36449  util.preventDefault(event);
36450};
36451
36452/**
36453 * After having moved the mouse, a tooltip should pop up when the mouse is resting on a data point
36454 * @param {Event}  event   A mouse move event
36455 */
36456Graph3d.prototype._onTooltip = function (event) {
36457  var delay = 300; // ms
36458  var boundingRect = this.frame.getBoundingClientRect();
36459  var mouseX = getMouseX(event) - boundingRect.left;
36460  var mouseY = getMouseY(event) - boundingRect.top;
36461
36462  if (!this.showTooltip) {
36463    return;
36464  }
36465
36466  if (this.tooltipTimeout) {
36467    clearTimeout(this.tooltipTimeout);
36468  }
36469
36470  // (delayed) display of a tooltip only if no mouse button is down
36471  if (this.leftButtonDown) {
36472    this._hideTooltip();
36473    return;
36474  }
36475
36476  if (this.tooltip && this.tooltip.dataPoint) {
36477    // tooltip is currently visible
36478    var dataPoint = this._dataPointFromXY(mouseX, mouseY);
36479    if (dataPoint !== this.tooltip.dataPoint) {
36480      // datapoint changed
36481      if (dataPoint) {
36482        this._showTooltip(dataPoint);
36483      } else {
36484        this._hideTooltip();
36485      }
36486    }
36487  } else {
36488    // tooltip is currently not visible
36489    var me = this;
36490    this.tooltipTimeout = setTimeout(function () {
36491      me.tooltipTimeout = null;
36492
36493      // show a tooltip if we have a data point
36494      var dataPoint = me._dataPointFromXY(mouseX, mouseY);
36495      if (dataPoint) {
36496        me._showTooltip(dataPoint);
36497      }
36498    }, delay);
36499  }
36500};
36501
36502/**
36503 * Event handler for touchstart event on mobile devices
36504 * @param {Event}  event   The event
36505 */
36506Graph3d.prototype._onTouchStart = function (event) {
36507  this.touchDown = true;
36508
36509  var me = this;
36510  this.ontouchmove = function (event) {
36511    me._onTouchMove(event);
36512  };
36513  this.ontouchend = function (event) {
36514    me._onTouchEnd(event);
36515  };
36516  util.addEventListener(document, 'touchmove', me.ontouchmove);
36517  util.addEventListener(document, 'touchend', me.ontouchend);
36518
36519  this._onMouseDown(event);
36520};
36521
36522/**
36523 * Event handler for touchmove event on mobile devices
36524 * @param {Event}  event   The event
36525 */
36526Graph3d.prototype._onTouchMove = function (event) {
36527  this._onMouseMove(event);
36528};
36529
36530/**
36531 * Event handler for touchend event on mobile devices
36532 * @param {Event}  event   The event
36533 */
36534Graph3d.prototype._onTouchEnd = function (event) {
36535  this.touchDown = false;
36536
36537  util.removeEventListener(document, 'touchmove', this.ontouchmove);
36538  util.removeEventListener(document, 'touchend', this.ontouchend);
36539
36540  this._onMouseUp(event);
36541};
36542
36543/**
36544 * Event handler for mouse wheel event, used to zoom the graph
36545 * Code from http://adomas.org/javascript-mouse-wheel/
36546 * @param {Event}  event   The event
36547 */
36548Graph3d.prototype._onWheel = function (event) {
36549  if (!event) /* For IE. */
36550    event = window.event;
36551
36552  // retrieve delta
36553  var delta = 0;
36554  if (event.wheelDelta) {
36555    /* IE/Opera. */
36556    delta = event.wheelDelta / 120;
36557  } else if (event.detail) {
36558    /* Mozilla case. */
36559    // In Mozilla, sign of delta is different than in IE.
36560    // Also, delta is multiple of 3.
36561    delta = -event.detail / 3;
36562  }
36563
36564  // If delta is nonzero, handle it.
36565  // Basically, delta is now positive if wheel was scrolled up,
36566  // and negative, if wheel was scrolled down.
36567  if (delta) {
36568    var oldLength = this.camera.getArmLength();
36569    var newLength = oldLength * (1 - delta / 10);
36570
36571    this.camera.setArmLength(newLength);
36572    this.redraw();
36573
36574    this._hideTooltip();
36575  }
36576
36577  // fire a cameraPositionChange event
36578  var parameters = this.getCameraPosition();
36579  this.emit('cameraPositionChange', parameters);
36580
36581  // Prevent default actions caused by mouse wheel.
36582  // That might be ugly, but we handle scrolls somehow
36583  // anyway, so don't bother here..
36584  util.preventDefault(event);
36585};
36586
36587/**
36588 * Test whether a point lies inside given 2D triangle
36589 *
36590 * @param   {vis.Point2d}   point
36591 * @param   {vis.Point2d[]} triangle
36592 * @returns {boolean}   true if given point lies inside or on the edge of the
36593 *                      triangle, false otherwise
36594 * @private
36595 */
36596Graph3d.prototype._insideTriangle = function (point, triangle) {
36597  var a = triangle[0],
36598      b = triangle[1],
36599      c = triangle[2];
36600
36601  /**
36602   *
36603   * @param {number} x
36604   * @returns {number}
36605   */
36606  function sign(x) {
36607    return x > 0 ? 1 : x < 0 ? -1 : 0;
36608  }
36609
36610  var as = sign((b.x - a.x) * (point.y - a.y) - (b.y - a.y) * (point.x - a.x));
36611  var bs = sign((c.x - b.x) * (point.y - b.y) - (c.y - b.y) * (point.x - b.x));
36612  var cs = sign((a.x - c.x) * (point.y - c.y) - (a.y - c.y) * (point.x - c.x));
36613
36614  // each of the three signs must be either equal to each other or zero
36615  return (as == 0 || bs == 0 || as == bs) && (bs == 0 || cs == 0 || bs == cs) && (as == 0 || cs == 0 || as == cs);
36616};
36617
36618/**
36619 * Find a data point close to given screen position (x, y)
36620 *
36621 * @param   {number} x
36622 * @param   {number} y
36623 * @returns {Object | null} The closest data point or null if not close to any
36624 *                          data point
36625 * @private
36626 */
36627Graph3d.prototype._dataPointFromXY = function (x, y) {
36628  var i,
36629      distMax = 100,
36630      // px
36631  dataPoint = null,
36632      closestDataPoint = null,
36633      closestDist = null,
36634      center = new Point2d(x, y);
36635
36636  if (this.style === Graph3d.STYLE.BAR || this.style === Graph3d.STYLE.BARCOLOR || this.style === Graph3d.STYLE.BARSIZE) {
36637    // the data points are ordered from far away to closest
36638    for (i = this.dataPoints.length - 1; i >= 0; i--) {
36639      dataPoint = this.dataPoints[i];
36640      var surfaces = dataPoint.surfaces;
36641      if (surfaces) {
36642        for (var s = surfaces.length - 1; s >= 0; s--) {
36643          // split each surface in two triangles, and see if the center point is inside one of these
36644          var surface = surfaces[s];
36645          var corners = surface.corners;
36646          var triangle1 = [corners[0].screen, corners[1].screen, corners[2].screen];
36647          var triangle2 = [corners[2].screen, corners[3].screen, corners[0].screen];
36648          if (this._insideTriangle(center, triangle1) || this._insideTriangle(center, triangle2)) {
36649            // return immediately at the first hit
36650            return dataPoint;
36651          }
36652        }
36653      }
36654    }
36655  } else {
36656    // find the closest data point, using distance to the center of the point on 2d screen
36657    for (i = 0; i < this.dataPoints.length; i++) {
36658      dataPoint = this.dataPoints[i];
36659      var point = dataPoint.screen;
36660      if (point) {
36661        var distX = Math.abs(x - point.x);
36662        var distY = Math.abs(y - point.y);
36663        var dist = Math.sqrt(distX * distX + distY * distY);
36664
36665        if ((closestDist === null || dist < closestDist) && dist < distMax) {
36666          closestDist = dist;
36667          closestDataPoint = dataPoint;
36668        }
36669      }
36670    }
36671  }
36672
36673  return closestDataPoint;
36674};
36675
36676/**
36677 * Determine if the given style has bars
36678 *
36679 * @param   {number} style the style to check
36680 * @returns {boolean} true if bar style, false otherwise
36681 */
36682Graph3d.prototype.hasBars = function (style) {
36683  return style == Graph3d.STYLE.BAR || style == Graph3d.STYLE.BARCOLOR || style == Graph3d.STYLE.BARSIZE;
36684};
36685
36686/**
36687 * Display a tooltip for given data point
36688 * @param {Object} dataPoint
36689 * @private
36690 */
36691Graph3d.prototype._showTooltip = function (dataPoint) {
36692  var content, line, dot;
36693
36694  if (!this.tooltip) {
36695    content = document.createElement('div');
36696    (0, _assign2['default'])(content.style, {}, this.tooltipStyle.content);
36697    content.style.position = 'absolute';
36698
36699    line = document.createElement('div');
36700    (0, _assign2['default'])(line.style, {}, this.tooltipStyle.line);
36701    line.style.position = 'absolute';
36702
36703    dot = document.createElement('div');
36704    (0, _assign2['default'])(dot.style, {}, this.tooltipStyle.dot);
36705    dot.style.position = 'absolute';
36706
36707    this.tooltip = {
36708      dataPoint: null,
36709      dom: {
36710        content: content,
36711        line: line,
36712        dot: dot
36713      }
36714    };
36715  } else {
36716    content = this.tooltip.dom.content;
36717    line = this.tooltip.dom.line;
36718    dot = this.tooltip.dom.dot;
36719  }
36720
36721  this._hideTooltip();
36722
36723  this.tooltip.dataPoint = dataPoint;
36724  if (typeof this.showTooltip === 'function') {
36725    content.innerHTML = this.showTooltip(dataPoint.point);
36726  } else {
36727    content.innerHTML = '<table>' + '<tr><td>' + this.xLabel + ':</td><td>' + dataPoint.point.x + '</td></tr>' + '<tr><td>' + this.yLabel + ':</td><td>' + dataPoint.point.y + '</td></tr>' + '<tr><td>' + this.zLabel + ':</td><td>' + dataPoint.point.z + '</td></tr>' + '</table>';
36728  }
36729
36730  content.style.left = '0';
36731  content.style.top = '0';
36732  this.frame.appendChild(content);
36733  this.frame.appendChild(line);
36734  this.frame.appendChild(dot);
36735
36736  // calculate sizes
36737  var contentWidth = content.offsetWidth;
36738  var contentHeight = content.offsetHeight;
36739  var lineHeight = line.offsetHeight;
36740  var dotWidth = dot.offsetWidth;
36741  var dotHeight = dot.offsetHeight;
36742
36743  var left = dataPoint.screen.x - contentWidth / 2;
36744  left = Math.min(Math.max(left, 10), this.frame.clientWidth - 10 - contentWidth);
36745
36746  line.style.left = dataPoint.screen.x + 'px';
36747  line.style.top = dataPoint.screen.y - lineHeight + 'px';
36748  content.style.left = left + 'px';
36749  content.style.top = dataPoint.screen.y - lineHeight - contentHeight + 'px';
36750  dot.style.left = dataPoint.screen.x - dotWidth / 2 + 'px';
36751  dot.style.top = dataPoint.screen.y - dotHeight / 2 + 'px';
36752};
36753
36754/**
36755 * Hide the tooltip when displayed
36756 * @private
36757 */
36758Graph3d.prototype._hideTooltip = function () {
36759  if (this.tooltip) {
36760    this.tooltip.dataPoint = null;
36761
36762    for (var prop in this.tooltip.dom) {
36763      if (this.tooltip.dom.hasOwnProperty(prop)) {
36764        var elem = this.tooltip.dom[prop];
36765        if (elem && elem.parentNode) {
36766          elem.parentNode.removeChild(elem);
36767        }
36768      }
36769    }
36770  }
36771};
36772
36773/**--------------------------------------------------------------------------**/
36774
36775/**
36776 * Get the horizontal mouse position from a mouse event
36777 *
36778 * @param   {Event}  event
36779 * @returns {number} mouse x
36780 */
36781function getMouseX(event) {
36782  if ('clientX' in event) return event.clientX;
36783  return event.targetTouches[0] && event.targetTouches[0].clientX || 0;
36784}
36785
36786/**
36787 * Get the vertical mouse position from a mouse event
36788 *
36789 * @param   {Event}  event
36790 * @returns {number} mouse y
36791 */
36792function getMouseY(event) {
36793  if ('clientY' in event) return event.clientY;
36794  return event.targetTouches[0] && event.targetTouches[0].clientY || 0;
36795}
36796
36797// -----------------------------------------------------------------------------
36798//  Public methods for specific settings
36799// -----------------------------------------------------------------------------
36800
36801/**
36802 * Set the rotation and distance of the camera
36803 *
36804 * @param {Object}  pos            An object with the camera position
36805 * @param {number} [pos.horizontal] The horizontal rotation, between 0 and 2*PI.
36806 *                                 Optional, can be left undefined.
36807 * @param {number} [pos.vertical]  The vertical rotation, between 0 and 0.5*PI.
36808 *                                 if vertical=0.5*PI, the graph is shown from
36809 *                                 the top. Optional, can be left undefined.
36810 * @param {number} [pos.distance]  The (normalized) distance of the camera to the
36811 *                                 center of the graph, a value between 0.71 and
36812 *                                 5.0. Optional, can be left undefined.
36813 */
36814Graph3d.prototype.setCameraPosition = function (pos) {
36815  Settings.setCameraPosition(pos, this);
36816  this.redraw();
36817};
36818
36819/**
36820 * Set a new size for the graph
36821 *
36822 * @param {string} width  Width in pixels or percentage (for example '800px'
36823 *                        or '50%')
36824 * @param {string} height Height in pixels or percentage  (for example '400px'
vendor: 3,074 bytes, lines 36825-36943
36825 *                        or '30%')
36826 */
36827Graph3d.prototype.setSize = function (width, height) {
36828  this._setSize(width, height);
36829  this.redraw();
36830};
36831
36832// -----------------------------------------------------------------------------
36833//  End public methods for specific settings
36834// -----------------------------------------------------------------------------
36835
36836
36837module.exports = Graph3d;
36838
36839/***/ }),
36840/* 162 */
36841/***/ (function(module, exports, __webpack_require__) {
36842
36843__webpack_require__(163);
36844module.exports = __webpack_require__(7).Object.assign;
36845
36846
36847/***/ }),
36848/* 163 */
36849/***/ (function(module, exports, __webpack_require__) {
36850
36851// 19.1.3.1 Object.assign(target, source)
36852var $export = __webpack_require__(17);
36853
36854$export($export.S + $export.F, 'Object', { assign: __webpack_require__(164) });
36855
36856
36857/***/ }),
36858/* 164 */
36859/***/ (function(module, exports, __webpack_require__) {
36860
36861"use strict";
36862
36863// 19.1.2.1 Object.assign(target, source, ...)
36864var getKeys = __webpack_require__(33);
36865var gOPS = __webpack_require__(63);
36866var pIE = __webpack_require__(42);
36867var toObject = __webpack_require__(41);
36868var IObject = __webpack_require__(78);
36869var $assign = Object.assign;
36870
36871// should work with symbols and should have deterministic property order (V8 bug)
36872module.exports = !$assign || __webpack_require__(28)(function () {
36873  var A = {};
36874  var B = {};
36875  // eslint-disable-next-line no-undef
36876  var S = Symbol();
36877  var K = 'abcdefghijklmnopqrst';
36878  A[S] = 7;
36879  K.split('').forEach(function (k) { B[k] = k; });
36880  return $assign({}, A)[S] != 7 || Object.keys($assign({}, B)).join('') != K;
36881}) ? function assign(target, source) { // eslint-disable-line no-unused-vars
36882  var T = toObject(target);
36883  var aLen = arguments.length;
36884  var index = 1;
36885  var getSymbols = gOPS.f;
36886  var isEnum = pIE.f;
36887  while (aLen > index) {
36888    var S = IObject(arguments[index++]);
36889    var keys = getSymbols ? getKeys(S).concat(getSymbols(S)) : getKeys(S);
36890    var length = keys.length;
36891    var j = 0;
36892    var key;
36893    while (length > j) if (isEnum.call(S, key = keys[j++])) T[key] = S[key];
36894  } return T;
36895} : $assign;
36896
36897
36898/***/ }),
36899/* 165 */
36900/***/ (function(module, exports, __webpack_require__) {
36901
36902module.exports = { "default": __webpack_require__(166), __esModule: true };
36903
36904/***/ }),
36905/* 166 */
36906/***/ (function(module, exports, __webpack_require__) {
36907
36908__webpack_require__(167);
36909module.exports = __webpack_require__(7).Math.sign;
36910
36911
36912/***/ }),
36913/* 167 */
36914/***/ (function(module, exports, __webpack_require__) {
36915
36916// 20.2.2.28 Math.sign(x)
36917var $export = __webpack_require__(17);
36918
36919$export($export.S, 'Math', { sign: __webpack_require__(168) });
36920
36921
36922/***/ }),
36923/* 168 */
36924/***/ (function(module, exports) {
36925
36926// 20.2.2.28 Math.sign(x)
36927module.exports = Math.sign || function sign(x) {
36928  // eslint-disable-next-line no-self-compare
36929  return (x = +x) == 0 || x != x ? x : x < 0 ? -1 : 1;
36930};
36931
36932
36933/***/ }),
36934/* 169 */
36935/***/ (function(module, exports, __webpack_require__) {
36936
36937module.exports = { "default": __webpack_require__(170), __esModule: true };
36938
36939/***/ }),
36940/* 170 */
36941/***/ (function(module, exports, __webpack_require__) {
36942
36943__webpack_require__(171);
36944var $Object = __webpack_require__(7).Object;
36945module.exports = function defineProperty(it, key, desc) {
36946  return $Object.defineProperty(it, key, desc);
36947};
36948
36949
36950/***/ }),
36951/* 171 */
36952/***/ (function(module, exports, __webpack_require__) {
36953
36954var $export = __webpack_require__(17);
36955// 19.1.2.4 / 15.2.3.6 Object.defineProperty(O, P, Attributes)
36956$export($export.S + $export.F * !__webpack_require__(21), 'Object', { defineProperty: __webpack_require__(20).f });
36957
36958
36959/***/ }),
36960/* 172 */
36961/***/ (function(module, exports, __webpack_require__) {
36962
36963"use strict";
36964
36965
36966Object.defineProperty(exports, "__esModule", {
36967  value: true
36968});
36969/**
36970 * This object contains all possible options. It will check if the types are correct, if required if the option is one
36971 * of the allowed values.
36972 *
36973 * __any__ means that the name of the property does not matter.
36974 * __type__ is a required field for all objects and contains the allowed types of all objects
36975 */
36976var string = 'string';
36977var bool = 'boolean';
36978var number = 'number';
36979var object = 'object'; // should only be in a __type__ property
36980// Following not used here, but useful for reference
36981//let array    = 'array';
36982//let dom      = 'dom';
36983//let any      = 'any';
36984
36985
36986var colorOptions = {
36987  fill: { string: string },
36988  stroke: { string: string },
36989  strokeWidth: { number: number },
36990  __type__: { string: string, object: object, 'undefined': 'undefined' }
36991};
36992
36993/**
36994 * Order attempted to be alphabetical.
36995 *   - x/y/z-prefixes ignored in sorting
36996 *   - __type__ always at end
36997 *   - globals at end
36998 */
36999var allOptions = {
37000  animationAutoStart: { boolean: bool, 'undefined': 'undefined' },
37001  animationInterval: { number: number },
37002  animationPreload: { boolean: bool },
37003  axisColor: { string: string },
37004  backgroundColor: colorOptions,
37005  xBarWidth: { number: number, 'undefined': 'undefined' },
37006  yBarWidth: { number: number, 'undefined': 'undefined' },
37007  cameraPosition: {
37008    distance: { number: number },
37009    horizontal: { number: number },
37010    vertical: { number: number },
37011    __type__: { object: object }
37012  },
37013  xCenter: { string: string },
37014  yCenter: { string: string },
37015  dataColor: colorOptions,
37016  dotSizeMinFraction: { number: number },
37017  dotSizeMaxFraction: { number: number },
37018  dotSizeRatio: { number: number },
37019  filterLabel: { string: string },
37020  gridColor: { string: string },
37021  onclick: { 'function': 'function' },
37022  keepAspectRatio: { boolean: bool },
37023  xLabel: { string: string },
37024  yLabel: { string: string },
37025  zLabel: { string: string },
37026  legendLabel: { string: string },
37027  xMin: { number: number, 'undefined': 'undefined' },
37028  yMin: { number: number, 'undefined': 'undefined' },
37029  zMin: { number: number, 'undefined': 'undefined' },
37030  xMax: { number: number, 'undefined': 'undefined' },
37031  yMax: { number: number, 'undefined': 'undefined' },
37032  zMax: { number: number, 'undefined': 'undefined' },
37033  showAnimationControls: { boolean: bool, 'undefined': 'undefined' },
37034  showGrid: { boolean: bool },
37035  showLegend: { boolean: bool, 'undefined': 'undefined' },
37036  showPerspective: { boolean: bool },
37037  showShadow: { boolean: bool },
37038  showXAxis: { boolean: bool },
37039  showYAxis: { boolean: bool },
37040  showZAxis: { boolean: bool },
37041  xStep: { number: number, 'undefined': 'undefined' },
37042  yStep: { number: number, 'undefined': 'undefined' },
37043  zStep: { number: number, 'undefined': 'undefined' },
37044  style: {
37045    number: number, // TODO: either Graph3d.DEFAULT has string, or number allowed in documentation
37046    string: ['bar', 'bar-color', 'bar-size', 'dot', 'dot-line', 'dot-color', 'dot-size', 'line', 'grid', 'surface']
37047  },
37048  tooltip: { boolean: bool, 'function': 'function' },
37049  tooltipStyle: {
37050    content: {
37051      color: { string: string },
37052      background: { string: string },
37053      border: { string: string },
37054      borderRadius: { string: string },
37055      boxShadow: { string: string },
37056      padding: { string: string },
37057      __type__: { object: object }
37058    },
37059    line: {
37060      borderLeft: { string: string },
37061      height: { string: string },
37062      width: { string: string },
37063      __type__: { object: object }
37064    },
37065    dot: {
37066      border: { string: string },
37067      borderRadius: { string: string },
37068      height: { string: string },
37069      width: { string: string },
37070      __type__: { object: object }
37071    },
37072    __type__: { object: object }
37073  },
37074  xValueLabel: { 'function': 'function' },
37075  yValueLabel: { 'function': 'function' },
37076  zValueLabel: { 'function': 'function' },
37077  valueMax: { number: number, 'undefined': 'undefined' },
37078  valueMin: { number: number, 'undefined': 'undefined' },
37079  verticalRatio: { number: number },
37080
37081  //globals :
37082  height: { string: string },
37083  width: { string: string },
37084  __type__: { object: object }
37085};
37086
37087exports.allOptions = allOptions;
37088
37089/***/ }),
37090/* 173 */
37091/***/ (function(module, exports, __webpack_require__) {
37092
37093"use strict";
37094
37095
37096var DataSet = __webpack_require__(11);
37097var DataView = __webpack_require__(12);
37098var Range = __webpack_require__(174);
37099var Filter = __webpack_require__(96);
37100var Settings = __webpack_require__(94);
37101var Point3d = __webpack_require__(34);
37102
37103/**
37104 * Creates a container for all data of one specific 3D-graph.
37105 *
37106 * On construction, the container is totally empty; the data
37107 * needs to be initialized with method initializeData().
37108 * Failure to do so will result in the following exception begin thrown
37109 * on instantiation of Graph3D:
37110 *
37111 *     Error: Array, DataSet, or DataView expected
37112 *
37113 * @constructor DataGroup
37114 */
37115function DataGroup() {
37116  this.dataTable = null; // The original data table
37117}
37118
37119/**
37120 * Initializes the instance from the passed data.
37121 *
37122 * Calculates minimum and maximum values and column index values.
37123 *
37124 * The graph3d instance is used internally to access the settings for
37125 * the given instance.
37126 * TODO: Pass settings only instead.
37127 *
37128 * @param {vis.Graph3d}  graph3d Reference to the calling Graph3D instance.
37129 * @param {Array | DataSet | DataView} rawData The data containing the items for
37130 *                                             the Graph.
37131 * @param {number}   style   Style Number
37132 * @returns {Array.<Object>}
37133 */
37134DataGroup.prototype.initializeData = function (graph3d, rawData, style) {
37135  if (rawData === undefined) return;
37136
37137  if (Array.isArray(rawData)) {
37138    rawData = new DataSet(rawData);
37139  }
37140
37141  var data;
37142  if (rawData instanceof DataSet || rawData instanceof DataView) {
37143    data = rawData.get();
37144  } else {
37145    throw new Error('Array, DataSet, or DataView expected');
37146  }
37147
37148  if (data.length == 0) return;
37149
37150  this.style = style;
37151
37152  // unsubscribe from the dataTable
37153  if (this.dataSet) {
37154    this.dataSet.off('*', this._onChange);
37155  }
37156
37157  this.dataSet = rawData;
37158  this.dataTable = data;
37159
37160  // subscribe to changes in the dataset
37161  var me = this;
37162  this._onChange = function () {
37163    graph3d.setData(me.dataSet);
37164  };
37165  this.dataSet.on('*', this._onChange);
37166
37167  // determine the location of x,y,z,value,filter columns
37168  this.colX = 'x';
37169  this.colY = 'y';
37170  this.colZ = 'z';
37171
37172  var withBars = graph3d.hasBars(style);
37173
37174  // determine barWidth from data
37175  if (withBars) {
37176    if (graph3d.defaultXBarWidth !== undefined) {
37177      this.xBarWidth = graph3d.defaultXBarWidth;
37178    } else {
37179      this.xBarWidth = this.getSmallestDifference(data, this.colX) || 1;
37180    }
37181
37182    if (graph3d.defaultYBarWidth !== undefined) {
37183      this.yBarWidth = graph3d.defaultYBarWidth;
37184    } else {
37185      this.yBarWidth = this.getSmallestDifference(data, this.colY) || 1;
37186    }
37187  }
37188
37189  // calculate minima and maxima
37190  this._initializeRange(data, this.colX, graph3d, withBars);
37191  this._initializeRange(data, this.colY, graph3d, withBars);
37192  this._initializeRange(data, this.colZ, graph3d, false);
37193
37194  if (data[0].hasOwnProperty('style')) {
37195    this.colValue = 'style';
37196    var valueRange = this.getColumnRange(data, this.colValue);
37197    this._setRangeDefaults(valueRange, graph3d.defaultValueMin, graph3d.defaultValueMax);
37198    this.valueRange = valueRange;
37199  }
37200
37201  // Initialize data filter if a filter column is provided
37202  var table = this.getDataTable();
37203  if (table[0].hasOwnProperty('filter')) {
37204    if (this.dataFilter === undefined) {
37205      this.dataFilter = new Filter(this, 'filter', graph3d);
37206      this.dataFilter.setOnLoadCallback(function () {
37207        graph3d.redraw();
37208      });
37209    }
37210  }
37211
37212  var dataPoints;
37213  if (this.dataFilter) {
37214    // apply filtering
37215    dataPoints = this.dataFilter._getDataPoints();
37216  } else {
37217    // no filtering. load all data
37218    dataPoints = this._getDataPoints(this.getDataTable());
37219  }
37220  return dataPoints;
37221};
37222
37223/**
37224 * Collect the range settings for the given data column.
37225 *
37226 * This internal method is intended to make the range 
37227 * initalization more generic.
37228 *
37229 * TODO: if/when combined settings per axis defined, get rid of this.
37230 *
37231 * @private
37232 *
37233 * @param {'x'|'y'|'z'} column  The data column to process
37234 * @param {vis.Graph3d} graph3d Reference to the calling Graph3D instance;
37235 *                              required for access to settings
37236 * @returns {Object}
37237 */
37238DataGroup.prototype._collectRangeSettings = function (column, graph3d) {
37239  var index = ['x', 'y', 'z'].indexOf(column);
37240
37241  if (index == -1) {
37242    throw new Error('Column \'' + column + '\' invalid');
37243  }
37244
37245  var upper = column.toUpperCase();
37246
37247  return {
37248    barWidth: this[column + 'BarWidth'],
37249    min: graph3d['default' + upper + 'Min'],
37250    max: graph3d['default' + upper + 'Max'],
37251    step: graph3d['default' + upper + 'Step'],
37252    range_label: column + 'Range', // Name of instance field to write to
37253    step_label: column + 'Step' // Name of instance field to write to
37254  };
37255};
37256
37257/**
37258 * Initializes the settings per given column.
37259 *
37260 * TODO: if/when combined settings per axis defined, rewrite this.
37261 *
37262 * @private
37263 *
37264 * @param {DataSet | DataView} data     The data containing the items for the Graph
37265 * @param {'x'|'y'|'z'}        column   The data column to process
37266 * @param {vis.Graph3d}        graph3d  Reference to the calling Graph3D instance;
37267 *                                      required for access to settings
37268 * @param {boolean}            withBars True if initializing for bar graph
37269 */
37270DataGroup.prototype._initializeRange = function (data, column, graph3d, withBars) {
37271  var NUMSTEPS = 5;
37272  var settings = this._collectRangeSettings(column, graph3d);
37273
37274  var range = this.getColumnRange(data, column);
37275  if (withBars && column != 'z') {
37276    // Safeguard for 'z'; it doesn't have a bar width
37277    range.expand(settings.barWidth / 2);
37278  }
37279
37280  this._setRangeDefaults(range, settings.min, settings.max);
37281  this[settings.range_label] = range;
37282  this[settings.step_label] = settings.step !== undefined ? settings.step : range.range() / NUMSTEPS;
37283};
37284
37285/**
37286 * Creates a list with all the different values in the data for the given column.
37287 *
37288 * If no data passed, use the internal data of this instance.
37289 *
37290 * @param {'x'|'y'|'z'}                column The data column to process
37291 * @param {DataSet|DataView|undefined} data   The data containing the items for the Graph
37292 *
37293 * @returns {Array} All distinct values in the given column data, sorted ascending.
37294 */
37295DataGroup.prototype.getDistinctValues = function (column, data) {
37296  if (data === undefined) {
37297    data = this.dataTable;
37298  }
37299
37300  var values = [];
37301
37302  for (var i = 0; i < data.length; i++) {
37303    var value = data[i][column] || 0;
37304    if (values.indexOf(value) === -1) {
37305      values.push(value);
37306    }
37307  }
37308
37309  return values.sort(function (a, b) {
37310    return a - b;
37311  });
37312};
37313
37314/**
37315 * Determine the smallest difference between the values for given
37316 * column in the passed data set.
37317 *
37318 * @param {DataSet|DataView|undefined} data   The data containing the items for the Graph
37319 * @param {'x'|'y'|'z'}                column The data column to process
37320 *
37321 * @returns {number|null} Smallest difference value or
37322 *                        null, if it can't be determined.
37323 */
37324DataGroup.prototype.getSmallestDifference = function (data, column) {
37325  var values = this.getDistinctValues(data, column);
37326
37327  // Get all the distinct diffs
37328  // Array values is assumed to be sorted here
37329  var smallest_diff = null;
37330
37331  for (var i = 1; i < values.length; i++) {
37332    var diff = values[i] - values[i - 1];
37333
37334    if (smallest_diff == null || smallest_diff > diff) {
37335      smallest_diff = diff;
37336    }
37337  }
37338
37339  return smallest_diff;
37340};
37341
37342/**
37343 * Get the absolute min/max values for the passed data column.
37344 *
37345 * @param {DataSet|DataView|undefined} data   The data containing the items for the Graph
37346 * @param {'x'|'y'|'z'}                column The data column to process
37347 *
37348 * @returns {Range} A Range instance with min/max members properly set.
37349 */
37350DataGroup.prototype.getColumnRange = function (data, column) {
37351  var range = new Range();
37352
37353  // Adjust the range so that it covers all values in the passed data elements.
37354  for (var i = 0; i < data.length; i++) {
37355    var item = data[i][column];
37356    range.adjust(item);
37357  }
37358
37359  return range;
37360};
37361
37362/**
37363 * Determines the number of rows in the current data.
37364 *
37365 * @returns {number}
37366 */
37367DataGroup.prototype.getNumberOfRows = function () {
37368  return this.dataTable.length;
37369};
37370
37371/**
37372 * Set default values for range
37373 *
37374 * The default values override the range values, if defined.
37375 *
37376 * Because it's possible that only defaultMin or defaultMax is set, it's better
37377 * to pass in a range already set with the min/max set from the data. Otherwise,
37378 * it's quite hard to process the min/max properly.
37379 *
37380 * @param {vis.Range} range
37381 * @param {number} [defaultMin=range.min]
37382 * @param {number} [defaultMax=range.max]
37383 * @private
37384 */
37385DataGroup.prototype._setRangeDefaults = function (range, defaultMin, defaultMax) {
37386  if (defaultMin !== undefined) {
37387    range.min = defaultMin;
37388  }
37389
37390  if (defaultMax !== undefined) {
37391    range.max = defaultMax;
37392  }
37393
37394  // This is the original way that the default min/max values were adjusted.
37395  // TODO: Perhaps it's better if an error is thrown if the values do not agree.
37396  //       But this will change the behaviour.
37397  if (range.max <= range.min) range.max = range.min + 1;
37398};
37399
37400DataGroup.prototype.getDataTable = function () {
37401  return this.dataTable;
37402};
37403
37404DataGroup.prototype.getDataSet = function () {
37405  return this.dataSet;
37406};
37407
37408/**
37409 * Return all data values as a list of Point3d objects
37410 * @param {Array.<Object>} data
37411 * @returns {Array.<Object>}
37412 */
37413DataGroup.prototype.getDataPoints = function (data) {
37414  var dataPoints = [];
37415
37416  for (var i = 0; i < data.length; i++) {
37417    var point = new Point3d();
37418    point.x = data[i][this.colX] || 0;
37419    point.y = data[i][this.colY] || 0;
37420    point.z = data[i][this.colZ] || 0;
37421    point.data = data[i];
37422
37423    if (this.colValue !== undefined) {
37424      point.value = data[i][this.colValue] || 0;
37425    }
37426
37427    var obj = {};
37428    obj.point = point;
37429    obj.bottom = new Point3d(point.x, point.y, this.zRange.min);
37430    obj.trans = undefined;
37431    obj.screen = undefined;
37432
37433    dataPoints.push(obj);
37434  }
37435
37436  return dataPoints;
37437};
37438
37439/**
37440 * Copy all values from the data table to a matrix.
37441 *
37442 * The provided values are supposed to form a grid of (x,y) positions.
37443 * @param {Array.<Object>} data
37444 * @returns {Array.<Object>}
37445 * @private
37446 */
37447DataGroup.prototype.initDataAsMatrix = function (data) {
37448  // TODO: store the created matrix dataPoints in the filters instead of
37449  //       reloading each time.
37450  var x, y, i, obj;
37451
37452  // create two lists with all present x and y values
37453  var dataX = this.getDistinctValues(this.colX, data);
37454  var dataY = this.getDistinctValues(this.colY, data);
37455
37456  var dataPoints = this.getDataPoints(data);
37457
37458  // create a grid, a 2d matrix, with all values.
37459  var dataMatrix = []; // temporary data matrix
37460  for (i = 0; i < dataPoints.length; i++) {
37461    obj = dataPoints[i];
37462
37463    // TODO: implement Array().indexOf() for Internet Explorer
37464    var xIndex = dataX.indexOf(obj.point.x);
37465    var yIndex = dataY.indexOf(obj.point.y);
37466
37467    if (dataMatrix[xIndex] === undefined) {
37468      dataMatrix[xIndex] = [];
37469    }
37470
37471    dataMatrix[xIndex][yIndex] = obj;
37472  }
37473
37474  // fill in the pointers to the neighbors.
37475  for (x = 0; x < dataMatrix.length; x++) {
37476    for (y = 0; y < dataMatrix[x].length; y++) {
37477      if (dataMatrix[x][y]) {
37478        dataMatrix[x][y].pointRight = x < dataMatrix.length - 1 ? dataMatrix[x + 1][y] : undefined;
37479        dataMatrix[x][y].pointTop = y < dataMatrix[x].length - 1 ? dataMatrix[x][y + 1] : undefined;
37480        dataMatrix[x][y].pointCross = x < dataMatrix.length - 1 && y < dataMatrix[x].length - 1 ? dataMatrix[x + 1][y + 1] : undefined;
37481      }
37482    }
37483  }
37484
37485  return dataPoints;
37486};
37487
37488/**
37489 * Return common information, if present
37490 *
37491 * @returns {string}
37492 */
37493DataGroup.prototype.getInfo = function () {
37494  var dataFilter = this.dataFilter;
37495  if (!dataFilter) return undefined;
37496
37497  return dataFilter.getLabel() + ': ' + dataFilter.getSelectedValue();
37498};
37499
37500/**
37501 * Reload the data
37502 */
37503DataGroup.prototype.reload = function () {
37504  if (this.dataTable) {
37505    this.setData(this.dataTable);
37506  }
37507};
37508
37509/**
37510 * Filter the data based on the current filter
37511 *
37512 * @param   {Array} data
37513 * @returns {Array} dataPoints Array with point objects which can be drawn on
37514 *                             screen
37515 */
37516DataGroup.prototype._getDataPoints = function (data) {
37517  var dataPoints = [];
37518
37519  if (this.style === Settings.STYLE.GRID || this.style === Settings.STYLE.SURFACE) {
37520    dataPoints = this.initDataAsMatrix(data);
37521  } else {
37522    // 'dot', 'dot-line', etc.
37523    this._checkValueField(data);
37524    dataPoints = this.getDataPoints(data);
37525
37526    if (this.style === Settings.STYLE.LINE) {
37527      // Add next member points for line drawing
37528      for (var i = 0; i < dataPoints.length; i++) {
37529        if (i > 0) {
37530          dataPoints[i - 1].pointNext = dataPoints[i];
37531        }
37532      }
37533    }
37534  }
37535
37536  return dataPoints;
37537};
37538
37539/**
37540 * Check if the state is consistent for the use of the value field.
37541 *
37542 * Throws if a problem is detected.
37543 *
37544 * @param {Array.<Object>} data
37545 * @private
37546 */
37547DataGroup.prototype._checkValueField = function (data) {
37548
37549  var hasValueField = this.style === Settings.STYLE.BARCOLOR || this.style === Settings.STYLE.BARSIZE || this.style === Settings.STYLE.DOTCOLOR || this.style === Settings.STYLE.DOTSIZE;
37550
37551  if (!hasValueField) {
37552    return; // No need to check further
37553  }
37554
37555  // Following field must be present for the current graph style
37556  if (this.colValue === undefined) {
37557    throw new Error('Expected data to have ' + ' field \'style\' ' + ' for graph style \'' + this.style + '\'');
37558  }
37559
37560  // The data must also contain this field.
37561  // Note that only first data element is checked.
37562  if (data[0][this.colValue] === undefined) {
37563    throw new Error('Expected data to have ' + ' field \'' + this.colValue + '\' ' + ' for graph style \'' + this.style + '\'');
37564  }
37565};
37566
37567module.exports = DataGroup;
37568
37569/***/ }),
37570/* 174 */
37571/***/ (function(module, exports, __webpack_require__) {
37572
37573"use strict";
37574
37575
37576/**
37577 * @prototype Range
37578 *
37579 * Helper class to make working with related min and max values easier.
37580 *
37581 * The range is inclusive; a given value is considered part of the range if:
37582 *
37583 *    this.min <= value <= this.max
37584 */
37585function Range() {
37586  this.min = undefined;
37587  this.max = undefined;
37588}
37589
37590/**
37591 * Adjust the range so that the passed value fits in it.
37592 *
37593 * If the value is outside of the current extremes, adjust
37594 * the min or max so that the value is within the range.
37595 *
37596 * @param {number} value Numeric value to fit in range
37597 */
37598Range.prototype.adjust = function (value) {
37599  if (value === undefined) return;
37600
37601  if (this.min === undefined || this.min > value) {
37602    this.min = value;
37603  }
37604
37605  if (this.max === undefined || this.max < value) {
37606    this.max = value;
37607  }
37608};
37609
37610/**
37611 * Adjust the current range so that the passed range fits in it.
37612 *
37613 * @param {Range} range Range instance to fit in current instance
37614 */
37615Range.prototype.combine = function (range) {
37616  this.add(range.min);
37617  this.add(range.max);
37618};
37619
37620/**
37621 * Expand the range by the given value
37622 *
37623 * min will be lowered by given value;
37624 * max will be raised by given value
37625 *
37626 * Shrinking by passing a negative value is allowed.
37627 *
37628 * @param {number} val Amount by which to expand or shrink current range with
37629 */
37630Range.prototype.expand = function (val) {
37631  if (val === undefined) {
37632    return;
37633  }
37634
37635  var newMin = this.min - val;
37636  var newMax = this.max + val;
37637
37638  // Note that following allows newMin === newMax.
37639  // This should be OK, since method expand() allows this also.
37640  if (newMin > newMax) {
37641    throw new Error('Passed expansion value makes range invalid');
37642  }
37643
37644  this.min = newMin;
37645  this.max = newMax;
37646};
37647
37648/**
37649 * Determine the full range width of current instance.
37650 *
37651 * @returns {num} The calculated width of this range
37652 */
37653Range.prototype.range = function () {
37654  return this.max - this.min;
37655};
37656
37657/**
37658 * Determine the central point of current instance.
37659 *
37660 * @returns {number} the value in the middle of min and max
37661 */
37662Range.prototype.center = function () {
37663  return (this.min + this.max) / 2;
37664};
37665
37666module.exports = Range;
37667
37668/***/ }),
37669/* 175 */
37670/***/ (function(module, exports, __webpack_require__) {
37671
37672"use strict";
37673var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;
37674
37675(function (factory) {
37676  if (true) {
37677    // AMD. Register as an anonymous module.
37678    !(__WEBPACK_AMD_DEFINE_ARRAY__ = [], __WEBPACK_AMD_DEFINE_FACTORY__ = (factory),
37679				__WEBPACK_AMD_DEFINE_RESULT__ = (typeof __WEBPACK_AMD_DEFINE_FACTORY__ === 'function' ?
37680				(__WEBPACK_AMD_DEFINE_FACTORY__.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__)) : __WEBPACK_AMD_DEFINE_FACTORY__),
37681				__WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));
37682  } else if (typeof exports === 'object') {
37683    // Node. Does not work with strict CommonJS, but
37684    // only CommonJS-like environments that support module.exports,
37685    // like Node.
37686    module.exports = factory();
37687  } else {
37688    // Browser globals (root is window)
37689    window.propagating = factory();
37690  }
37691}(function () {
37692  var _firstTarget = null; // singleton, will contain the target element where the touch event started
37693
37694  /**
37695   * Extend an Hammer.js instance with event propagation.
37696   *
37697   * Features:
37698   * - Events emitted by hammer will propagate in order from child to parent
37699   *   elements.
37700   * - Events are extended with a function `event.stopPropagation()` to stop
37701   *   propagation to parent elements.
37702   * - An option `preventDefault` to stop all default browser behavior.
37703   *
37704   * Usage:
37705   *   var hammer = propagatingHammer(new Hammer(element));
37706   *   var hammer = propagatingHammer(new Hammer(element), {preventDefault: true});
37707   *
37708   * @param {Hammer.Manager} hammer   An hammer instance.
37709   * @param {Object} [options]        Available options:
37710   *                                  - `preventDefault: true | false | 'mouse' | 'touch' | 'pen'`.
37711   *                                    Enforce preventing the default browser behavior.
37712   *                                    Cannot be set to `false`.
37713   * @return {Hammer.Manager} Returns the same hammer instance with extended
37714   *                          functionality
37715   */
37716  return function propagating(hammer, options) {
37717    var _options = options || {
37718      preventDefault: false
37719    };
37720
37721    if (hammer.Manager) {
37722      // This looks like the Hammer constructor.
37723      // Overload the constructors with our own.
37724      var Hammer = hammer;
37725
37726      var PropagatingHammer = function(element, options) {
37727        var o = Object.create(_options);
37728        if (options) Hammer.assign(o, options);
37729        return propagating(new Hammer(element, o), o);
37730      };
37731      Hammer.assign(PropagatingHammer, Hammer);
37732
37733      PropagatingHammer.Manager = function (element, options) {
37734        var o = Object.create(_options);
37735        if (options) Hammer.assign(o, options);
37736        return propagating(new Hammer.Manager(element, o), o);
37737      };
37738
37739      return PropagatingHammer;
37740    }
37741
37742    // create a wrapper object which will override the functions
37743    // `on`, `off`, `destroy`, and `emit` of the hammer instance
37744    var wrapper = Object.create(hammer);
37745
37746    // attach to DOM element
37747    var element = hammer.element;
37748
37749    if(!element.hammer) element.hammer = [];
37750    element.hammer.push(wrapper);
37751
37752    // register an event to catch the start of a gesture and store the
37753    // target in a singleton
37754    hammer.on('hammer.input', function (event) {
37755      if (_options.preventDefault === true || (_options.preventDefault === event.pointerType)) {
37756        event.preventDefault();
37757      }
37758      if (event.isFirst) {
37759        _firstTarget = event.target;
37760      }
37761    });
37762
37763    /** @type {Object.<String, Array.<function>>} */
37764    wrapper._handlers = {};
37765
37766    /**
37767     * Register a handler for one or multiple events
37768     * @param {String} events    A space separated string with events
37769     * @param {function}
vendor: 4,256 bytes, lines 37769-37913
37769 handler A callback function, called as handler(event)
37770     * @returns {Hammer.Manager} Returns the hammer instance
37771     */
37772    wrapper.on = function (events, handler) {
37773      // register the handler
37774      split(events).forEach(function (event) {
37775        var _handlers = wrapper._handlers[event];
37776        if (!_handlers) {
37777          wrapper._handlers[event] = _handlers = [];
37778
37779          // register the static, propagated handler
37780          hammer.on(event, propagatedHandler);
37781        }
37782        _handlers.push(handler);
37783      });
37784
37785      return wrapper;
37786    };
37787
37788    /**
37789     * Unregister a handler for one or multiple events
37790     * @param {String} events      A space separated string with events
37791     * @param {function} [handler] Optional. The registered handler. If not
37792     *                             provided, all handlers for given events
37793     *                             are removed.
37794     * @returns {Hammer.Manager}   Returns the hammer instance
37795     */
37796    wrapper.off = function (events, handler) {
37797      // unregister the handler
37798      split(events).forEach(function (event) {
37799        var _handlers = wrapper._handlers[event];
37800        if (_handlers) {
37801          _handlers = handler ? _handlers.filter(function (h) {
37802            return h !== handler;
37803          }) : [];
37804
37805          if (_handlers.length > 0) {
37806            wrapper._handlers[event] = _handlers;
37807          }
37808          else {
37809            // remove static, propagated handler
37810            hammer.off(event, propagatedHandler);
37811            delete wrapper._handlers[event];
37812          }
37813        }
37814      });
37815
37816      return wrapper;
37817    };
37818
37819    /**
37820     * Emit to the event listeners
37821     * @param {string} eventType
37822     * @param {Event} event
37823     */
37824    wrapper.emit = function(eventType, event) {
37825      _firstTarget = event.target;
37826      hammer.emit(eventType, event);
37827    };
37828
37829    wrapper.destroy = function () {
37830      // Detach from DOM element
37831      var hammers = hammer.element.hammer;
37832      var idx = hammers.indexOf(wrapper);
37833      if(idx !== -1) hammers.splice(idx,1);
37834      if(!hammers.length) delete hammer.element.hammer;
37835
37836      // clear all handlers
37837      wrapper._handlers = {};
37838
37839      // call original hammer destroy
37840      hammer.destroy();
37841    };
37842
37843    // split a string with space separated words
37844    function split(events) {
37845      return events.match(/[^ ]+/g);
37846    }
37847
37848    /**
37849     * A static event handler, applying event propagation.
37850     * @param {Object} event
37851     */
37852    function propagatedHandler(event) {
37853      // let only a single hammer instance handle this event
37854      if (event.type !== 'hammer.input') {
37855        // it is possible that the same srcEvent is used with multiple hammer events,
37856        // we keep track on which events are handled in an object _handled
37857        if (!event.srcEvent._handled) {
37858          event.srcEvent._handled = {};
37859        }
37860
37861        if (event.srcEvent._handled[event.type]) {
37862          return;
37863        }
37864        else {
37865          event.srcEvent._handled[event.type] = true;
37866        }
37867      }
37868
37869      // attach a stopPropagation function to the event
37870      var stopped = false;
37871      event.stopPropagation = function () {
37872        stopped = true;
37873      };
37874
37875      //wrap the srcEvent's stopPropagation to also stop hammer propagation:
37876      var srcStop = event.srcEvent.stopPropagation.bind(event.srcEvent);
37877      if(typeof srcStop == "function") {
37878        event.srcEvent.stopPropagation = function(){
37879          srcStop();
37880          event.stopPropagation();
37881        }
37882      }
37883
37884      // attach firstTarget property to the event
37885      event.firstTarget = _firstTarget;
37886
37887      // propagate over all elements (until stopped)
37888      var elem = _firstTarget;
37889      while (elem && !stopped) {
37890        var elemHammer = elem.hammer;
37891        if(elemHammer){
37892          var _handlers;
37893          for(var k = 0; k < elemHammer.length; k++){
37894            _handlers = elemHammer[k]._handlers[event.type];
37895            if(_handlers) for (var i = 0; i < _handlers.length && !stopped; i++) {
37896              _handlers[i](event);
37897            }
37898          }
37899        }
37900        elem = elem.parentNode;
37901      }
37902    }
37903
37904    return wrapper;
37905  };
37906}));
37907
37908
37909/***/ }),
37910/* 176 */
37911/***/ (function(module, exports, __webpack_require__) {
37912
37913var __WEBPACK_AMD_DEFINE_RESULT__;
vendor: 73,525 bytes, lines 37913-40545
37913/*! Hammer.JS - v2.0.7 - 2016-04-22
37914 * http://hammerjs.github.io/
37915 *
37916 * Copyright (c) 2016 Jorik Tangelder;
37917 * Licensed under the MIT license */
37918(function(window, document, exportName, undefined) {
37919  'use strict';
37920
37921var VENDOR_PREFIXES = ['', 'webkit', 'Moz', 'MS', 'ms', 'o'];
37922var TEST_ELEMENT = document.createElement('div');
37923
37924var TYPE_FUNCTION = 'function';
37925
37926var round = Math.round;
37927var abs = Math.abs;
37928var now = Date.now;
37929
37930/**
37931 * set a timeout with a given scope
37932 * @param {Function} fn
37933 * @param {Number} timeout
37934 * @param {Object} context
37935 * @returns {number}
37936 */
37937function setTimeoutContext(fn, timeout, context) {
37938    return setTimeout(bindFn(fn, context), timeout);
37939}
37940
37941/**
37942 * if the argument is an array, we want to execute the fn on each entry
37943 * if it aint an array we don't want to do a thing.
37944 * this is used by all the methods that accept a single and array argument.
37945 * @param {*|Array} arg
37946 * @param {String} fn
37947 * @param {Object} [context]
37948 * @returns {Boolean}
37949 */
37950function invokeArrayArg(arg, fn, context) {
37951    if (Array.isArray(arg)) {
37952        each(arg, context[fn], context);
37953        return true;
37954    }
37955    return false;
37956}
37957
37958/**
37959 * walk objects and arrays
37960 * @param {Object} obj
37961 * @param {Function} iterator
37962 * @param {Object} context
37963 */
37964function each(obj, iterator, context) {
37965    var i;
37966
37967    if (!obj) {
37968        return;
37969    }
37970
37971    if (obj.forEach) {
37972        obj.forEach(iterator, context);
37973    } else if (obj.length !== undefined) {
37974        i = 0;
37975        while (i < obj.length) {
37976            iterator.call(context, obj[i], i, obj);
37977            i++;
37978        }
37979    } else {
37980        for (i in obj) {
37981            obj.hasOwnProperty(i) && iterator.call(context, obj[i], i, obj);
37982        }
37983    }
37984}
37985
37986/**
37987 * wrap a method with a deprecation warning and stack trace
37988 * @param {Function} method
37989 * @param {String} name
37990 * @param {String} message
37991 * @returns {Function} A new function wrapping the supplied method.
37992 */
37993function deprecate(method, name, message) {
37994    var deprecationMessage = 'DEPRECATED METHOD: ' + name + '\n' + message + ' AT \n';
37995    return function() {
37996        var e = new Error('get-stack-trace');
37997        var stack = e && e.stack ? e.stack.replace(/^[^\(]+?[\n$]/gm, '')
37998            .replace(/^\s+at\s+/gm, '')
37999            .replace(/^Object.<anonymous>\s*\(/gm, '{anonymous}()@') : 'Unknown Stack Trace';
38000
38001        var log = window.console && (window.console.warn || window.console.log);
38002        if (log) {
38003            log.call(window.console, deprecationMessage, stack);
38004        }
38005        return method.apply(this, arguments);
38006    };
38007}
38008
38009/**
38010 * extend object.
38011 * means that properties in dest will be overwritten by the ones in src.
38012 * @param {Object} target
38013 * @param {...Object} objects_to_assign
38014 * @returns {Object} target
38015 */
38016var assign;
38017if (typeof Object.assign !== 'function') {
38018    assign = function assign(target) {
38019        if (target === undefined || target === null) {
38020            throw new TypeError('Cannot convert undefined or null to object');
38021        }
38022
38023        var output = Object(target);
38024        for (var index = 1; index < arguments.length; index++) {
38025            var source = arguments[index];
38026            if (source !== undefined && source !== null) {
38027                for (var nextKey in source) {
38028                    if (source.hasOwnProperty(nextKey)) {
38029                        output[nextKey] = source[nextKey];
38030                    }
38031                }
38032            }
38033        }
38034        return output;
38035    };
38036} else {
38037    assign = Object.assign;
38038}
38039
38040/**
38041 * extend object.
38042 * means that properties in dest will be overwritten by the ones in src.
38043 * @param {Object} dest
38044 * @param {Object} src
38045 * @param {Boolean} [merge=false]
38046 * @returns {Object} dest
38047 */
38048var extend = deprecate(function extend(dest, src, merge) {
38049    var keys = Object.keys(src);
38050    var i = 0;
38051    while (i < keys.length) {
38052        if (!merge || (merge && dest[keys[i]] === undefined)) {
38053            dest[keys[i]] = src[keys[i]];
38054        }
38055        i++;
38056    }
38057    return dest;
38058}, 'extend', 'Use `assign`.');
38059
38060/**
38061 * merge the values from src in the dest.
38062 * means that properties that exist in dest will not be overwritten by src
38063 * @param {Object} dest
38064 * @param {Object} src
38065 * @returns {Object} dest
38066 */
38067var merge = deprecate(function merge(dest, src) {
38068    return extend(dest, src, true);
38069}, 'merge', 'Use `assign`.');
38070
38071/**
38072 * simple class inheritance
38073 * @param {Function} child
38074 * @param {Function} base
38075 * @param {Object} [properties]
38076 */
38077function inherit(child, base, properties) {
38078    var baseP = base.prototype,
38079        childP;
38080
38081    childP = child.prototype = Object.create(baseP);
38082    childP.constructor = child;
38083    childP._super = baseP;
38084
38085    if (properties) {
38086        assign(childP, properties);
38087    }
38088}
38089
38090/**
38091 * simple function bind
38092 * @param {Function} fn
38093 * @param {Object} context
38094 * @returns {Function}
38095 */
38096function bindFn(fn, context) {
38097    return function boundFn() {
38098        return fn.apply(context, arguments);
38099    };
38100}
38101
38102/**
38103 * let a boolean value also be a function that must return a boolean
38104 * this first item in args will be used as the context
38105 * @param {Boolean|Function} val
38106 * @param {Array} [args]
38107 * @returns {Boolean}
38108 */
38109function boolOrFn(val, args) {
38110    if (typeof val == TYPE_FUNCTION) {
38111        return val.apply(args ? args[0] || undefined : undefined, args);
38112    }
38113    return val;
38114}
38115
38116/**
38117 * use the val2 when val1 is undefined
38118 * @param {*} val1
38119 * @param {*} val2
38120 * @returns {*}
38121 */
38122function ifUndefined(val1, val2) {
38123    return (val1 === undefined) ? val2 : val1;
38124}
38125
38126/**
38127 * addEventListener with multiple events at once
38128 * @param {EventTarget} target
38129 * @param {String} types
38130 * @param {Function} handler
38131 */
38132function addEventListeners(target, types, handler) {
38133    each(splitStr(types), function(type) {
38134        target.addEventListener(type, handler, false);
38135    });
38136}
38137
38138/**
38139 * removeEventListener with multiple events at once
38140 * @param {EventTarget} target
38141 * @param {String} types
38142 * @param {Function} handler
38143 */
38144function removeEventListeners(target, types, handler) {
38145    each(splitStr(types), function(type) {
38146        target.removeEventListener(type, handler, false);
38147    });
38148}
38149
38150/**
38151 * find if a node is in the given parent
38152 * @method hasParent
38153 * @param {HTMLElement} node
38154 * @param {HTMLElement} parent
38155 * @return {Boolean} found
38156 */
38157function hasParent(node, parent) {
38158    while (node) {
38159        if (node == parent) {
38160            return true;
38161        }
38162        node = node.parentNode;
38163    }
38164    return false;
38165}
38166
38167/**
38168 * small indexOf wrapper
38169 * @param {String} str
38170 * @param {String} find
38171 * @returns {Boolean} found
38172 */
38173function inStr(str, find) {
38174    return str.indexOf(find) > -1;
38175}
38176
38177/**
38178 * split string on whitespace
38179 * @param {String} str
38180 * @returns {Array} words
38181 */
38182function splitStr(str) {
38183    return str.trim().split(/\s+/g);
38184}
38185
38186/**
38187 * find if a array contains the object using indexOf or a simple polyFill
38188 * @param {Array} src
38189 * @param {String} find
38190 * @param {String} [findByKey]
38191 * @return {Boolean|Number} false when not found, or the index
38192 */
38193function inArray(src, find, findByKey) {
38194    if (src.indexOf && !findByKey) {
38195        return src.indexOf(find);
38196    } else {
38197        var i = 0;
38198        while (i < src.length) {
38199            if ((findByKey && src[i][findByKey] == find) || (!findByKey && src[i] === find)) {
38200                return i;
38201            }
38202            i++;
38203        }
38204        return -1;
38205    }
38206}
38207
38208/**
38209 * convert array-like objects to real arrays
38210 * @param {Object} obj
38211 * @returns {Array}
38212 */
38213function toArray(obj) {
38214    return Array.prototype.slice.call(obj, 0);
38215}
38216
38217/**
38218 * unique array with objects based on a key (like 'id') or just by the array's value
38219 * @param {Array} src [{id:1},{id:2},{id:1}]
38220 * @param {String} [key]
38221 * @param {Boolean} [sort=False]
38222 * @returns {Array} [{id:1},{id:2}]
38223 */
38224function uniqueArray(src, key, sort) {
38225    var results = [];
38226    var values = [];
38227    var i = 0;
38228
38229    while (i < src.length) {
38230        var val = key ? src[i][key] : src[i];
38231        if (inArray(values, val) < 0) {
38232            results.push(src[i]);
38233        }
38234        values[i] = val;
38235        i++;
38236    }
38237
38238    if (sort) {
38239        if (!key) {
38240            results = results.sort();
38241        } else {
38242            results = results.sort(function sortUniqueArray(a, b) {
38243                return a[key] > b[key];
38244            });
38245        }
38246    }
38247
38248    return results;
38249}
38250
38251/**
38252 * get the prefixed property
38253 * @param {Object} obj
38254 * @param {String} property
38255 * @returns {String|Undefined} prefixed
38256 */
38257function prefixed(obj, property) {
38258    var prefix, prop;
38259    var camelProp = property[0].toUpperCase() + property.slice(1);
38260
38261    var i = 0;
38262    while (i < VENDOR_PREFIXES.length) {
38263        prefix = VENDOR_PREFIXES[i];
38264        prop = (prefix) ? prefix + camelProp : property;
38265
38266        if (prop in obj) {
38267            return prop;
38268        }
38269        i++;
38270    }
38271    return undefined;
38272}
38273
38274/**
38275 * get a unique id
38276 * @returns {number} uniqueId
38277 */
38278var _uniqueId = 1;
38279function uniqueId() {
38280    return _uniqueId++;
38281}
38282
38283/**
38284 * get the window object of an element
38285 * @param {HTMLElement} element
38286 * @returns {DocumentView|Window}
38287 */
38288function getWindowForElement(element) {
38289    var doc = element.ownerDocument || element;
38290    return (doc.defaultView || doc.parentWindow || window);
38291}
38292
38293var MOBILE_REGEX = /mobile|tablet|ip(ad|hone|od)|android/i;
38294
38295var SUPPORT_TOUCH = ('ontouchstart' in window);
38296var SUPPORT_POINTER_EVENTS = prefixed(window, 'PointerEvent') !== undefined;
38297var SUPPORT_ONLY_TOUCH = SUPPORT_TOUCH && MOBILE_REGEX.test(navigator.userAgent);
38298
38299var INPUT_TYPE_TOUCH = 'touch';
38300var INPUT_TYPE_PEN = 'pen';
38301var INPUT_TYPE_MOUSE = 'mouse';
38302var INPUT_TYPE_KINECT = 'kinect';
38303
38304var COMPUTE_INTERVAL = 25;
38305
38306var INPUT_START = 1;
38307var INPUT_MOVE = 2;
38308var INPUT_END = 4;
38309var INPUT_CANCEL = 8;
38310
38311var DIRECTION_NONE = 1;
38312var DIRECTION_LEFT = 2;
38313var DIRECTION_RIGHT = 4;
38314var DIRECTION_UP = 8;
38315var DIRECTION_DOWN = 16;
38316
38317var DIRECTION_HORIZONTAL = DIRECTION_LEFT | DIRECTION_RIGHT;
38318var DIRECTION_VERTICAL = DIRECTION_UP | DIRECTION_DOWN;
38319var DIRECTION_ALL = DIRECTION_HORIZONTAL | DIRECTION_VERTICAL;
38320
38321var PROPS_XY = ['x', 'y'];
38322var PROPS_CLIENT_XY = ['clientX', 'clientY'];
38323
38324/**
38325 * create new input type manager
38326 * @param {Manager} manager
38327 * @param {Function} callback
38328 * @returns {Input}
38329 * @constructor
38330 */
38331function Input(manager, callback) {
38332    var self = this;
38333    this.manager = manager;
38334    this.callback = callback;
38335    this.element = manager.element;
38336    this.target = manager.options.inputTarget;
38337
38338    // smaller wrapper around the handler, for the scope and the enabled state of the manager,
38339    // so when disabled the input events are completely bypassed.
38340    this.domHandler = function(ev) {
38341        if (boolOrFn(manager.options.enable, [manager])) {
38342            self.handler(ev);
38343        }
38344    };
38345
38346    this.init();
38347
38348}
38349
38350Input.prototype = {
38351    /**
38352     * should handle the inputEvent data and trigger the callback
38353     * @virtual
38354     */
38355    handler: function() { },
38356
38357    /**
38358     * bind the events
38359     */
38360    init: function() {
38361        this.evEl && addEventListeners(this.element, this.evEl, this.domHandler);
38362        this.evTarget && addEventListeners(this.target, this.evTarget, this.domHandler);
38363        this.evWin && addEventListeners(getWindowForElement(this.element), this.evWin, this.domHandler);
38364    },
38365
38366    /**
38367     * unbind the events
38368     */
38369    destroy: function() {
38370        this.evEl && removeEventListeners(this.element, this.evEl, this.domHandler);
38371        this.evTarget && removeEventListeners(this.target, this.evTarget, this.domHandler);
38372        this.evWin && removeEventListeners(getWindowForElement(this.element), this.evWin, this.domHandler);
38373    }
38374};
38375
38376/**
38377 * create new input type manager
38378 * called by the Manager constructor
38379 * @param {Hammer} manager
38380 * @returns {Input}
38381 */
38382function createInputInstance(manager) {
38383    var Type;
38384    var inputClass = manager.options.inputClass;
38385
38386    if (inputClass) {
38387        Type = inputClass;
38388    } else if (SUPPORT_POINTER_EVENTS) {
38389        Type = PointerEventInput;
38390    } else if (SUPPORT_ONLY_TOUCH) {
38391        Type = TouchInput;
38392    } else if (!SUPPORT_TOUCH) {
38393        Type = MouseInput;
38394    } else {
38395        Type = TouchMouseInput;
38396    }
38397    return new (Type)(manager, inputHandler);
38398}
38399
38400/**
38401 * handle input events
38402 * @param {Manager} manager
38403 * @param {String} eventType
38404 * @param {Object} input
38405 */
38406function inputHandler(manager, eventType, input) {
38407    var pointersLen = input.pointers.length;
38408    var changedPointersLen = input.changedPointers.length;
38409    var isFirst = (eventType & INPUT_START && (pointersLen - changedPointersLen === 0));
38410    var isFinal = (eventType & (INPUT_END | INPUT_CANCEL) && (pointersLen - changedPointersLen === 0));
38411
38412    input.isFirst = !!isFirst;
38413    input.isFinal = !!isFinal;
38414
38415    if (isFirst) {
38416        manager.session = {};
38417    }
38418
38419    // source event is the normalized value of the domEvents
38420    // like 'touchstart, mouseup, pointerdown'
38421    input.eventType = eventType;
38422
38423    // compute scale, rotation etc
38424    computeInputData(manager, input);
38425
38426    // emit secret event
38427    manager.emit('hammer.input', input);
38428
38429    manager.recognize(input);
38430    manager.session.prevInput = input;
38431}
38432
38433/**
38434 * extend the data with some usable properties like scale, rotate, velocity etc
38435 * @param {Object} manager
38436 * @param {Object} input
38437 */
38438function computeInputData(manager, input) {
38439    var session = manager.session;
38440    var pointers = input.pointers;
38441    var pointersLength = pointers.length;
38442
38443    // store the first input to calculate the distance and direction
38444    if (!session.firstInput) {
38445        session.firstInput = simpleCloneInputData(input);
38446    }
38447
38448    // to compute scale and rotation we need to store the multiple touches
38449    if (pointersLength > 1 && !session.firstMultiple) {
38450        session.firstMultiple = simpleCloneInputData(input);
38451    } else if (pointersLength === 1) {
38452        session.firstMultiple = false;
38453    }
38454
38455    var firstInput = session.firstInput;
38456    var firstMultiple = session.firstMultiple;
38457    var offsetCenter = firstMultiple ? firstMultiple.center : firstInput.center;
38458
38459    var center = input.center = getCenter(pointers);
38460    input.timeStamp = now();
38461    input.deltaTime = input.timeStamp - firstInput.timeStamp;
38462
38463    input.angle = getAngle(offsetCenter, center);
38464    input.distance = getDistance(offsetCenter, center);
38465
38466    computeDeltaXY(session, input);
38467    input.offsetDirection = getDirection(input.deltaX, input.deltaY);
38468
38469    var overallVelocity = getVelocity(input.deltaTime, input.deltaX, input.deltaY);
38470    input.overallVelocityX = overallVelocity.x;
38471    input.overallVelocityY = overallVelocity.y;
38472    input.overallVelocity = (abs(overallVelocity.x) > abs(overallVelocity.y)) ? overallVelocity.x : overallVelocity.y;
38473
38474    input.scale = firstMultiple ? getScale(firstMultiple.pointers, pointers) : 1;
38475    input.rotation = firstMultiple ? getRotation(firstMultiple.pointers, pointers) : 0;
38476
38477    input.maxPointers = !session.prevInput ? input.pointers.length : ((input.pointers.length >
38478        session.prevInput.maxPointers) ? input.pointers.length : session.prevInput.maxPointers);
38479
38480    computeIntervalInputData(session, input);
38481
38482    // find the correct target
38483    var target = manager.element;
38484    if (hasParent(input.srcEvent.target, target)) {
38485        target = input.srcEvent.target;
38486    }
38487    input.target = target;
38488}
38489
38490function computeDeltaXY(session, input) {
38491    var center = input.center;
38492    var offset = session.offsetDelta || {};
38493    var prevDelta = session.prevDelta || {};
38494    var prevInput = session.prevInput || {};
38495
38496    if (input.eventType === INPUT_START || prevInput.eventType === INPUT_END) {
38497        prevDelta = session.prevDelta = {
38498            x: prevInput.deltaX || 0,
38499            y: prevInput.deltaY || 0
38500        };
38501
38502        offset = session.offsetDelta = {
38503            x: center.x,
38504            y: center.y
38505        };
38506    }
38507
38508    input.deltaX = prevDelta.x + (center.x - offset.x);
38509    input.deltaY = prevDelta.y + (center.y - offset.y);
38510}
38511
38512/**
38513 * velocity is calculated every x ms
38514 * @param {Object} session
38515 * @param {Object} input
38516 */
38517function computeIntervalInputData(session, input) {
38518    var last = session.lastInterval || input,
38519        deltaTime = input.timeStamp - last.timeStamp,
38520        velocity, velocityX, velocityY, direction;
38521
38522    if (input.eventType != INPUT_CANCEL && (deltaTime > COMPUTE_INTERVAL || last.velocity === undefined)) {
38523        var deltaX = input.deltaX - last.deltaX;
38524        var deltaY = input.deltaY - last.deltaY;
38525
38526        var v = getVelocity(deltaTime, deltaX, deltaY);
38527        velocityX = v.x;
38528        velocityY = v.y;
38529        velocity = (abs(v.x) > abs(v.y)) ? v.x : v.y;
38530        direction = getDirection(deltaX, deltaY);
38531
38532        session.lastInterval = input;
38533    } else {
38534        // use latest velocity info if it doesn't overtake a minimum period
38535        velocity = last.velocity;
38536        velocityX = last.velocityX;
38537        velocityY = last.velocityY;
38538        direction = last.direction;
38539    }
38540
38541    input.velocity = velocity;
38542    input.velocityX = velocityX;
38543    input.velocityY = velocityY;
38544    input.direction = direction;
38545}
38546
38547/**
38548 * create a simple clone from the input used for storage of firstInput and firstMultiple
38549 * @param {Object} input
38550 * @returns {Object} clonedInputData
38551 */
38552function simpleCloneInputData(input) {
38553    // make a simple copy of the pointers because we will get a reference if we don't
38554    // we only need clientXY for the calculations
38555    var pointers = [];
38556    var i = 0;
38557    while (i < input.pointers.length) {
38558        pointers[i] = {
38559            clientX: round(input.pointers[i].clientX),
38560            clientY: round(input.pointers[i].clientY)
38561        };
38562        i++;
38563    }
38564
38565    return {
38566        timeStamp: now(),
38567        pointers: pointers,
38568        center: getCenter(pointers),
38569        deltaX: input.deltaX,
38570        deltaY: input.deltaY
38571    };
38572}
38573
38574/**
38575 * get the center of all the pointers
38576 * @param {Array} pointers
38577 * @return {Object} center contains `x` and `y` properties
38578 */
38579function getCenter(pointers) {
38580    var pointersLength = pointers.length;
38581
38582    // no need to loop when only one touch
38583    if (pointersLength === 1) {
38584        return {
38585            x: round(pointers[0].clientX),
38586            y: round(pointers[0].clientY)
38587        };
38588    }
38589
38590    var x = 0, y = 0, i = 0;
38591    while (i < pointersLength) {
38592        x += pointers[i].clientX;
38593        y += pointers[i].clientY;
38594        i++;
38595    }
38596
38597    return {
38598        x: round(x / pointersLength),
38599        y: round(y / pointersLength)
38600    };
38601}
38602
38603/**
38604 * calculate the velocity between two points. unit is in px per ms.
38605 * @param {Number} deltaTime
38606 * @param {Number} x
38607 * @param {Number} y
38608 * @return {Object} velocity `x` and `y`
38609 */
38610function getVelocity(deltaTime, x, y) {
38611    return {
38612        x: x / deltaTime || 0,
38613        y: y / deltaTime || 0
38614    };
38615}
38616
38617/**
38618 * get the direction between two points
38619 * @param {Number} x
38620 * @param {Number} y
38621 * @return {Number} direction
38622 */
38623function getDirection(x, y) {
38624    if (x === y) {
38625        return DIRECTION_NONE;
38626    }
38627
38628    if (abs(x) >= abs(y)) {
38629        return x < 0 ? DIRECTION_LEFT : DIRECTION_RIGHT;
38630    }
38631    return y < 0 ? DIRECTION_UP : DIRECTION_DOWN;
38632}
38633
38634/**
38635 * calculate the absolute distance between two points
38636 * @param {Object} p1 {x, y}
38637 * @param {Object} p2 {x, y}
38638 * @param {Array} [props] containing x and y keys
38639 * @return {Number} distance
38640 */
38641function getDistance(p1, p2, props) {
38642    if (!props) {
38643        props = PROPS_XY;
38644    }
38645    var x = p2[props[0]] - p1[props[0]],
38646        y = p2[props[1]] - p1[props[1]];
38647
38648    return Math.sqrt((x * x) + (y * y));
38649}
38650
38651/**
38652 * calculate the angle between two coordinates
38653 * @param {Object} p1
38654 * @param {Object} p2
38655 * @param {Array} [props] containing x and y keys
38656 * @return {Number} angle
38657 */
38658function getAngle(p1, p2, props) {
38659    if (!props) {
38660        props = PROPS_XY;
38661    }
38662    var x = p2[props[0]] - p1[props[0]],
38663        y = p2[props[1]] - p1[props[1]];
38664    return Math.atan2(y, x) * 180 / Math.PI;
38665}
38666
38667/**
38668 * calculate the rotation degrees between two pointersets
38669 * @param {Array} start array of pointers
38670 * @param {Array} end array of pointers
38671 * @return {Number} rotation
38672 */
38673function getRotation(start, end) {
38674    return getAngle(end[1], end[0], PROPS_CLIENT_XY) + getAngle(start[1], start[0], PROPS_CLIENT_XY);
38675}
38676
38677/**
38678 * calculate the scale factor between two pointersets
38679 * no scale is 1, and goes down to 0 when pinched together, and bigger when pinched out
38680 * @param {Array} start array of pointers
38681 * @param {Array} end array of pointers
38682 * @return {Number} scale
38683 */
38684function getScale(start, end) {
38685    return getDistance(end[0], end[1], PROPS_CLIENT_XY) / getDistance(start[0], start[1], PROPS_CLIENT_XY);
38686}
38687
38688var MOUSE_INPUT_MAP = {
38689    mousedown: INPUT_START,
38690    mousemove: INPUT_MOVE,
38691    mouseup: INPUT_END
38692};
38693
38694var MOUSE_ELEMENT_EVENTS = 'mousedown';
38695var MOUSE_WINDOW_EVENTS = 'mousemove mouseup';
38696
38697/**
38698 * Mouse events input
38699 * @constructor
38700 * @extends Input
38701 */
38702function MouseInput() {
38703    this.evEl = MOUSE_ELEMENT_EVENTS;
38704    this.evWin = MOUSE_WINDOW_EVENTS;
38705
38706    this.pressed = false; // mousedown state
38707
38708    Input.apply(this, arguments);
38709}
38710
38711inherit(MouseInput, Input, {
38712    /**
38713     * handle mouse events
38714     * @param {Object} ev
38715     */
38716    handler: function MEhandler(ev) {
38717        var eventType = MOUSE_INPUT_MAP[ev.type];
38718
38719        // on start we want to have the left mouse button down
38720        if (eventType & INPUT_START && ev.button === 0) {
38721            this.pressed = true;
38722        }
38723
38724        if (eventType & INPUT_MOVE && ev.which !== 1) {
38725            eventType = INPUT_END;
38726        }
38727
38728        // mouse must be down
38729        if (!this.pressed) {
38730            return;
38731        }
38732
38733        if (eventType & INPUT_END) {
38734            this.pressed = false;
38735        }
38736
38737        this.callback(this.manager, eventType, {
38738            pointers: [ev],
38739            changedPointers: [ev],
38740            pointerType: INPUT_TYPE_MOUSE,
38741            srcEvent: ev
38742        });
38743    }
38744});
38745
38746var POINTER_INPUT_MAP = {
38747    pointerdown: INPUT_START,
38748    pointermove: INPUT_MOVE,
38749    pointerup: INPUT_END,
38750    pointercancel: INPUT_CANCEL,
38751    pointerout: INPUT_CANCEL
38752};
38753
38754// in IE10 the pointer types is defined as an enum
38755var IE10_POINTER_TYPE_ENUM = {
38756    2: INPUT_TYPE_TOUCH,
38757    3: INPUT_TYPE_PEN,
38758    4: INPUT_TYPE_MOUSE,
38759    5: INPUT_TYPE_KINECT // see https://twitter.com/jacobrossi/status/480596438489890816
38760};
38761
38762var POINTER_ELEMENT_EVENTS = 'pointerdown';
38763var POINTER_WINDOW_EVENTS = 'pointermove pointerup pointercancel';
38764
38765// IE10 has prefixed support, and case-sensitive
38766if (window.MSPointerEvent && !window.PointerEvent) {
38767    POINTER_ELEMENT_EVENTS = 'MSPointerDown';
38768    POINTER_WINDOW_EVENTS = 'MSPointerMove MSPointerUp MSPointerCancel';
38769}
38770
38771/**
38772 * Pointer events input
38773 * @constructor
38774 * @extends Input
38775 */
38776function PointerEventInput() {
38777    this.evEl = POINTER_ELEMENT_EVENTS;
38778    this.evWin = POINTER_WINDOW_EVENTS;
38779
38780    Input.apply(this, arguments);
38781
38782    this.store = (this.manager.session.pointerEvents = []);
38783}
38784
38785inherit(PointerEventInput, Input, {
38786    /**
38787     * handle mouse events
38788     * @param {Object} ev
38789     */
38790    handler: function PEhandler(ev) {
38791        var store = this.store;
38792        var removePointer = false;
38793
38794        var eventTypeNormalized = ev.type.toLowerCase().replace('ms', '');
38795        var eventType = POINTER_INPUT_MAP[eventTypeNormalized];
38796        var pointerType = IE10_POINTER_TYPE_ENUM[ev.pointerType] || ev.pointerType;
38797
38798        var isTouch = (pointerType == INPUT_TYPE_TOUCH);
38799
38800        // get index of the event in the store
38801        var storeIndex = inArray(store, ev.pointerId, 'pointerId');
38802
38803        // start and mouse must be down
38804        if (eventType & INPUT_START && (ev.button === 0 || isTouch)) {
38805            if (storeIndex < 0) {
38806                store.push(ev);
38807                storeIndex = store.length - 1;
38808            }
38809        } else if (eventType & (INPUT_END | INPUT_CANCEL)) {
38810            removePointer = true;
38811        }
38812
38813        // it not found, so the pointer hasn't been down (so it's probably a hover)
38814        if (storeIndex < 0) {
38815            return;
38816        }
38817
38818        // update the event in the store
38819        store[storeIndex] = ev;
38820
38821        this.callback(this.manager, eventType, {
38822            pointers: store,
38823            changedPointers: [ev],
38824            pointerType: pointerType,
38825            srcEvent: ev
38826        });
38827
38828        if (removePointer) {
38829            // remove from the store
38830            store.splice(storeIndex, 1);
38831        }
38832    }
38833});
38834
38835var SINGLE_TOUCH_INPUT_MAP = {
38836    touchstart: INPUT_START,
38837    touchmove: INPUT_MOVE,
38838    touchend: INPUT_END,
38839    touchcancel: INPUT_CANCEL
38840};
38841
38842var SINGLE_TOUCH_TARGET_EVENTS = 'touchstart';
38843var SINGLE_TOUCH_WINDOW_EVENTS = 'touchstart touchmove touchend touchcancel';
38844
38845/**
38846 * Touch events input
38847 * @constructor
38848 * @extends Input
38849 */
38850function SingleTouchInput() {
38851    this.evTarget = SINGLE_TOUCH_TARGET_EVENTS;
38852    this.evWin = SINGLE_TOUCH_WINDOW_EVENTS;
38853    this.started = false;
38854
38855    Input.apply(this, arguments);
38856}
38857
38858inherit(SingleTouchInput, Input, {
38859    handler: function TEhandler(ev) {
38860        var type = SINGLE_TOUCH_INPUT_MAP[ev.type];
38861
38862        // should we handle the touch events?
38863        if (type === INPUT_START) {
38864            this.started = true;
38865        }
38866
38867        if (!this.started) {
38868            return;
38869        }
38870
38871        var touches = normalizeSingleTouches.call(this, ev, type);
38872
38873        // when done, reset the started state
38874        if (type & (INPUT_END | INPUT_CANCEL) && touches[0].length - touches[1].length === 0) {
38875            this.started = false;
38876        }
38877
38878        this.callback(this.manager, type, {
38879            pointers: touches[0],
38880            changedPointers: touches[1],
38881            pointerType: INPUT_TYPE_TOUCH,
38882            srcEvent: ev
38883        });
38884    }
38885});
38886
38887/**
38888 * @this {TouchInput}
38889 * @param {Object} ev
38890 * @param {Number} type flag
38891 * @returns {undefined|Array} [all, changed]
38892 */
38893function normalizeSingleTouches(ev, type) {
38894    var all = toArray(ev.touches);
38895    var changed = toArray(ev.changedTouches);
38896
38897    if (type & (INPUT_END | INPUT_CANCEL)) {
38898        all = uniqueArray(all.concat(changed), 'identifier', true);
38899    }
38900
38901    return [all, changed];
38902}
38903
38904var TOUCH_INPUT_MAP = {
38905    touchstart: INPUT_START,
38906    touchmove: INPUT_MOVE,
38907    touchend: INPUT_END,
38908    touchcancel: INPUT_CANCEL
38909};
38910
38911var TOUCH_TARGET_EVENTS = 'touchstart touchmove touchend touchcancel';
38912
38913/**
38914 * Multi-user touch events input
38915 * @constructor
38916 * @extends Input
38917 */
38918function TouchInput() {
38919    this.evTarget = TOUCH_TARGET_EVENTS;
38920    this.targetIds = {};
38921
38922    Input.apply(this, arguments);
38923}
38924
38925inherit(TouchInput, Input, {
38926    handler: function MTEhandler(ev) {
38927        var type = TOUCH_INPUT_MAP[ev.type];
38928        var touches = getTouches.call(this, ev, type);
38929        if (!touches) {
38930            return;
38931        }
38932
38933        this.callback(this.manager, type, {
38934            pointers: touches[0],
38935            changedPointers: touches[1],
38936            pointerType: INPUT_TYPE_TOUCH,
38937            srcEvent: ev
38938        });
38939    }
38940});
38941
38942/**
38943 * @this {TouchInput}
38944 * @param {Object} ev
38945 * @param {Number} type flag
38946 * @returns {undefined|Array} [all, changed]
38947 */
38948function getTouches(ev, type) {
38949    var allTouches = toArray(ev.touches);
38950    var targetIds = this.targetIds;
38951
38952    // when there is only one touch, the process can be simplified
38953    if (type & (INPUT_START | INPUT_MOVE) && allTouches.length === 1) {
38954        targetIds[allTouches[0].identifier] = true;
38955        return [allTouches, allTouches];
38956    }
38957
38958    var i,
38959        targetTouches,
38960        changedTouches = toArray(ev.changedTouches),
38961        changedTargetTouches = [],
38962        target = this.target;
38963
38964    // get target touches from touches
38965    targetTouches = allTouches.filter(function(touch) {
38966        return hasParent(touch.target, target);
38967    });
38968
38969    // collect touches
38970    if (type === INPUT_START) {
38971        i = 0;
38972        while (i < targetTouches.length) {
38973            targetIds[targetTouches[i].identifier] = true;
38974            i++;
38975        }
38976    }
38977
38978    // filter changed touches to only contain touches that exist in the collected target ids
38979    i = 0;
38980    while (i < changedTouches.length) {
38981        if (targetIds[changedTouches[i].identifier]) {
38982            changedTargetTouches.push(changedTouches[i]);
38983        }
38984
38985        // cleanup removed touches
38986        if (type & (INPUT_END | INPUT_CANCEL)) {
38987            delete targetIds[changedTouches[i].identifier];
38988        }
38989        i++;
38990    }
38991
38992    if (!changedTargetTouches.length) {
38993        return;
38994    }
38995
38996    return [
38997        // merge targetTouches with changedTargetTouches so it contains ALL touches, including 'end' and 'cancel'
38998        uniqueArray(targetTouches.concat(changedTargetTouches), 'identifier', true),
38999        changedTargetTouches
39000    ];
39001}
39002
39003/**
39004 * Combined touch and mouse input
39005 *
39006 * Touch has a higher priority then mouse, and while touching no mouse events are allowed.
39007 * This because touch devices also emit mouse events while doing a touch.
39008 *
39009 * @constructor
39010 * @extends Input
39011 */
39012
39013var DEDUP_TIMEOUT = 2500;
39014var DEDUP_DISTANCE = 25;
39015
39016function TouchMouseInput() {
39017    Input.apply(this, arguments);
39018
39019    var handler = bindFn(this.handler, this);
39020    this.touch = new TouchInput(this.manager, handler);
39021    this.mouse = new MouseInput(this.manager, handler);
39022
39023    this.primaryTouch = null;
39024    this.lastTouches = [];
39025}
39026
39027inherit(TouchMouseInput, Input, {
39028    /**
39029     * handle mouse and touch events
39030     * @param {Hammer} manager
39031     * @param {String} inputEvent
39032     * @param {Object} inputData
39033     */
39034    handler: function TMEhandler(manager, inputEvent, inputData) {
39035        var isTouch = (inputData.pointerType == INPUT_TYPE_TOUCH),
39036            isMouse = (inputData.pointerType == INPUT_TYPE_MOUSE);
39037
39038        if (isMouse && inputData.sourceCapabilities && inputData.sourceCapabilities.firesTouchEvents) {
39039            return;
39040        }
39041
39042        // when we're in a touch event, record touches to  de-dupe synthetic mouse event
39043        if (isTouch) {
39044            recordTouches.call(this, inputEvent, inputData);
39045        } else if (isMouse && isSyntheticEvent.call(this, inputData)) {
39046            return;
39047        }
39048
39049        this.callback(manager, inputEvent, inputData);
39050    },
39051
39052    /**
39053     * remove the event listeners
39054     */
39055    destroy: function destroy() {
39056        this.touch.destroy();
39057        this.mouse.destroy();
39058    }
39059});
39060
39061function recordTouches(eventType, eventData) {
39062    if (eventType & INPUT_START) {
39063        this.primaryTouch = eventData.changedPointers[0].identifier;
39064        setLastTouch.call(this, eventData);
39065    } else if (eventType & (INPUT_END | INPUT_CANCEL)) {
39066        setLastTouch.call(this, eventData);
39067    }
39068}
39069
39070function setLastTouch(eventData) {
39071    var touch = eventData.changedPointers[0];
39072
39073    if (touch.identifier === this.primaryTouch) {
39074        var lastTouch = {x: touch.clientX, y: touch.clientY};
39075        this.lastTouches.push(lastTouch);
39076        var lts = this.lastTouches;
39077        var removeLastTouch = function() {
39078            var i = lts.indexOf(lastTouch);
39079            if (i > -1) {
39080                lts.splice(i, 1);
39081            }
39082        };
39083        setTimeout(removeLastTouch, DEDUP_TIMEOUT);
39084    }
39085}
39086
39087function isSyntheticEvent(eventData) {
39088    var x = eventData.srcEvent.clientX, y = eventData.srcEvent.clientY;
39089    for (var i = 0; i < this.lastTouches.length; i++) {
39090        var t = this.lastTouches[i];
39091        var dx = Math.abs(x - t.x), dy = Math.abs(y - t.y);
39092        if (dx <= DEDUP_DISTANCE && dy <= DEDUP_DISTANCE) {
39093            return true;
39094        }
39095    }
39096    return false;
39097}
39098
39099var PREFIXED_TOUCH_ACTION = prefixed(TEST_ELEMENT.style, 'touchAction');
39100var NATIVE_TOUCH_ACTION = PREFIXED_TOUCH_ACTION !== undefined;
39101
39102// magical touchAction value
39103var TOUCH_ACTION_COMPUTE = 'compute';
39104var TOUCH_ACTION_AUTO = 'auto';
39105var TOUCH_ACTION_MANIPULATION = 'manipulation'; // not implemented
39106var TOUCH_ACTION_NONE = 'none';
39107var TOUCH_ACTION_PAN_X = 'pan-x';
39108var TOUCH_ACTION_PAN_Y = 'pan-y';
39109var TOUCH_ACTION_MAP = getTouchActionProps();
39110
39111/**
39112 * Touch Action
39113 * sets the touchAction property or uses the js alternative
39114 * @param {Manager} manager
39115 * @param {String} value
39116 * @constructor
39117 */
39118function TouchAction(manager, value) {
39119    this.manager = manager;
39120    this.set(value);
39121}
39122
39123TouchAction.prototype = {
39124    /**
39125     * set the touchAction value on the element or enable the polyfill
39126     * @param {String} value
39127     */
39128    set: function(value) {
39129        // find out the touch-action by the event handlers
39130        if (value == TOUCH_ACTION_COMPUTE) {
39131            value = this.compute();
39132        }
39133
39134        if (NATIVE_TOUCH_ACTION && this.manager.element.style && TOUCH_ACTION_MAP[value]) {
39135            this.manager.element.style[PREFIXED_TOUCH_ACTION] = value;
39136        }
39137        this.actions = value.toLowerCase().trim();
39138    },
39139
39140    /**
39141     * just re-set the touchAction value
39142     */
39143    update: function() {
39144        this.set(this.manager.options.touchAction);
39145    },
39146
39147    /**
39148     * compute the value for the touchAction property based on the recognizer's settings
39149     * @returns {String} value
39150     */
39151    compute: function() {
39152        var actions = [];
39153        each(this.manager.recognizers, function(recognizer) {
39154            if (boolOrFn(recognizer.options.enable, [recognizer])) {
39155                actions = actions.concat(recognizer.getTouchAction());
39156            }
39157        });
39158        return cleanTouchActions(actions.join(' '));
39159    },
39160
39161    /**
39162     * this method is called on each input cycle and provides the preventing of the browser behavior
39163     * @param {Object} input
39164     */
39165    preventDefaults: function(input) {
39166        var srcEvent = input.srcEvent;
39167        var direction = input.offsetDirection;
39168
39169        // if the touch action did prevented once this session
39170        if (this.manager.session.prevented) {
39171            srcEvent.preventDefault();
39172            return;
39173        }
39174
39175        var actions = this.actions;
39176        var hasNone = inStr(actions, TOUCH_ACTION_NONE) && !TOUCH_ACTION_MAP[TOUCH_ACTION_NONE];
39177        var hasPanY = inStr(actions, TOUCH_ACTION_PAN_Y) && !TOUCH_ACTION_MAP[TOUCH_ACTION_PAN_Y];
39178        var hasPanX = inStr(actions, TOUCH_ACTION_PAN_X) && !TOUCH_ACTION_MAP[TOUCH_ACTION_PAN_X];
39179
39180        if (hasNone) {
39181            //do not prevent defaults if this is a tap gesture
39182
39183            var isTapPointer = input.pointers.length === 1;
39184            var isTapMovement = input.distance < 2;
39185            var isTapTouchTime = input.deltaTime < 250;
39186
39187            if (isTapPointer && isTapMovement && isTapTouchTime) {
39188                return;
39189            }
39190        }
39191
39192        if (hasPanX && hasPanY) {
39193            // `pan-x pan-y` means browser handles all scrolling/panning, do not prevent
39194            return;
39195        }
39196
39197        if (hasNone ||
39198            (hasPanY && direction & DIRECTION_HORIZONTAL) ||
39199            (hasPanX && direction & DIRECTION_VERTICAL)) {
39200            return this.preventSrc(srcEvent);
39201        }
39202    },
39203
39204    /**
39205     * call preventDefault to prevent the browser's default behavior (scrolling in most cases)
39206     * @param {Object} srcEvent
39207     */
39208    preventSrc: function(srcEvent) {
39209        this.manager.session.prevented = true;
39210        srcEvent.preventDefault();
39211    }
39212};
39213
39214/**
39215 * when the touchActions are collected they are not a valid value, so we need to clean things up. *
39216 * @param {String} actions
39217 * @returns {*}
39218 */
39219function cleanTouchActions(actions) {
39220    // none
39221    if (inStr(actions, TOUCH_ACTION_NONE)) {
39222        return TOUCH_ACTION_NONE;
39223    }
39224
39225    var hasPanX = inStr(actions, TOUCH_ACTION_PAN_X);
39226    var hasPanY = inStr(actions, TOUCH_ACTION_PAN_Y);
39227
39228    // if both pan-x and pan-y are set (different recognizers
39229    // for different directions, e.g. horizontal pan but vertical swipe?)
39230    // we need none (as otherwise with pan-x pan-y combined none of these
39231    // recognizers will work, since the browser would handle all panning
39232    if (hasPanX && hasPanY) {
39233        return TOUCH_ACTION_NONE;
39234    }
39235
39236    // pan-x OR pan-y
39237    if (hasPanX || hasPanY) {
39238        return hasPanX ? TOUCH_ACTION_PAN_X : TOUCH_ACTION_PAN_Y;
39239    }
39240
39241    // manipulation
39242    if (inStr(actions, TOUCH_ACTION_MANIPULATION)) {
39243        return TOUCH_ACTION_MANIPULATION;
39244    }
39245
39246    return TOUCH_ACTION_AUTO;
39247}
39248
39249function getTouchActionProps() {
39250    if (!NATIVE_TOUCH_ACTION) {
39251        return false;
39252    }
39253    var touchMap = {};
39254    var cssSupports = window.CSS && window.CSS.supports;
39255    ['auto', 'manipulation', 'pan-y', 'pan-x', 'pan-x pan-y', 'none'].forEach(function(val) {
39256
39257        // If css.supports is not supported but there is native touch-action assume it supports
39258        // all values. This is the case for IE 10 and 11.
39259        touchMap[val] = cssSupports ? window.CSS.supports('touch-action', val) : true;
39260    });
39261    return touchMap;
39262}
39263
39264/**
39265 * Recognizer flow explained; *
39266 * All recognizers have the initial state of POSSIBLE when a input session starts.
39267 * The definition of a input session is from the first input until the last input, with all it's movement in it. *
39268 * Example session for mouse-input: mousedown -> mousemove -> mouseup
39269 *
39270 * On each recognizing cycle (see Manager.recognize) the .recognize() method is executed
39271 * which determines with state it should be.
39272 *
39273 * If the recognizer has the state FAILED, CANCELLED or RECOGNIZED (equals ENDED), it is reset to
39274 * POSSIBLE to give it another change on the next cycle.
39275 *
39276 *               Possible
39277 *                  |
39278 *            +-----+---------------+
39279 *            |                     |
39280 *      +-----+-----+               |
39281 *      |           |               |
39282 *   Failed      Cancelled          |
39283 *                          +-------+------+
39284 *                          |              |
39285 *                      Recognized       Began
39286 *                                         |
39287 *                                      Changed
39288 *                                         |
39289 *                                  Ended/Recognized
39290 */
39291var STATE_POSSIBLE = 1;
39292var STATE_BEGAN = 2;
39293var STATE_CHANGED = 4;
39294var STATE_ENDED = 8;
39295var STATE_RECOGNIZED = STATE_ENDED;
39296var STATE_CANCELLED = 16;
39297var STATE_FAILED = 32;
39298
39299/**
39300 * Recognizer
39301 * Every recognizer needs to extend from this class.
39302 * @constructor
39303 * @param {Object} options
39304 */
39305function Recognizer(options) {
39306    this.options = assign({}, this.defaults, options || {});
39307
39308    this.id = uniqueId();
39309
39310    this.manager = null;
39311
39312    // default is enable true
39313    this.options.enable = ifUndefined(this.options.enable, true);
39314
39315    this.state = STATE_POSSIBLE;
39316
39317    this.simultaneous = {};
39318    this.requireFail = [];
39319}
39320
39321Recognizer.prototype = {
39322    /**
39323     * @virtual
39324     * @type {Object}
39325     */
39326    defaults: {},
39327
39328    /**
39329     * set options
39330     * @param {Object} options
39331     * @return {Recognizer}
39332     */
39333    set: function(options) {
39334        assign(this.options, options);
39335
39336        // also update the touchAction, in case something changed about the directions/enabled state
39337        this.manager && this.manager.touchAction.update();
39338        return this;
39339    },
39340
39341    /**
39342     * recognize simultaneous with an other recognizer.
39343     * @param {Recognizer} otherRecognizer
39344     * @returns {Recognizer} this
39345     */
39346    recognizeWith: function(otherRecognizer) {
39347        if (invokeArrayArg(otherRecognizer, 'recognizeWith', this)) {
39348            return this;
39349        }
39350
39351        var simultaneous = this.simultaneous;
39352        otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this);
39353        if (!simultaneous[otherRecognizer.id]) {
39354            simultaneous[otherRecognizer.id] = otherRecognizer;
39355            otherRecognizer.recognizeWith(this);
39356        }
39357        return this;
39358    },
39359
39360    /**
39361     * drop the simultaneous link. it doesnt remove the link on the other recognizer.
39362     * @param {Recognizer} otherRecognizer
39363     * @returns {Recognizer} this
39364     */
39365    dropRecognizeWith: function(otherRecognizer) {
39366        if (invokeArrayArg(otherRecognizer, 'dropRecognizeWith', this)) {
39367            return this;
39368        }
39369
39370        otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this);
39371        delete this.simultaneous[otherRecognizer.id];
39372        return this;
39373    },
39374
39375    /**
39376     * recognizer can only run when an other is failing
39377     * @param {Recognizer} otherRecognizer
39378     * @returns {Recognizer} this
39379     */
39380    requireFailure: function(otherRecognizer) {
39381        if (invokeArrayArg(otherRecognizer, 'requireFailure', this)) {
39382            return this;
39383        }
39384
39385        var requireFail = this.requireFail;
39386        otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this);
39387        if (inArray(requireFail, otherRecognizer) === -1) {
39388            requireFail.push(otherRecognizer);
39389            otherRecognizer.requireFailure(this);
39390        }
39391        return this;
39392    },
39393
39394    /**
39395     * drop the requireFailure link. it does not remove the link on the other recognizer.
39396     * @param {Recognizer} otherRecognizer
39397     * @returns {Recognizer} this
39398     */
39399    dropRequireFailure: function(otherRecognizer) {
39400        if (invokeArrayArg(otherRecognizer, 'dropRequireFailure', this)) {
39401            return this;
39402        }
39403
39404        otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this);
39405        var index = inArray(this.requireFail, otherRecognizer);
39406        if (index > -1) {
39407            this.requireFail.splice(index, 1);
39408        }
39409        return this;
39410    },
39411
39412    /**
39413     * has require failures boolean
39414     * @returns {boolean}
39415     */
39416    hasRequireFailures: function() {
39417        return this.requireFail.length > 0;
39418    },
39419
39420    /**
39421     * if the recognizer can recognize simultaneous with an other recognizer
39422     * @param {Recognizer} otherRecognizer
39423     * @returns {Boolean}
39424     */
39425    canRecognizeWith: function(otherRecognizer) {
39426        return !!this.simultaneous[otherRecognizer.id];
39427    },
39428
39429    /**
39430     * You should use `tryEmit` instead of `emit` directly to check
39431     * that all the needed recognizers has failed before emitting.
39432     * @param {Object} input
39433     */
39434    emit: function(input) {
39435        var self = this;
39436        var state = this.state;
39437
39438        function emit(event) {
39439            self.manager.emit(event, input);
39440        }
39441
39442        // 'panstart' and 'panmove'
39443        if (state < STATE_ENDED) {
39444            emit(self.options.event + stateStr(state));
39445        }
39446
39447        emit(self.options.event); // simple 'eventName' events
39448
39449        if (input.additionalEvent) { // additional event(panleft, panright, pinchin, pinchout...)
39450            emit(input.additionalEvent);
39451        }
39452
39453        // panend and pancancel
39454        if (state >= STATE_ENDED) {
39455            emit(self.options.event + stateStr(state));
39456        }
39457    },
39458
39459    /**
39460     * Check that all the require failure recognizers has failed,
39461     * if true, it emits a gesture event,
39462     * otherwise, setup the state to FAILED.
39463     * @param {Object} input
39464     */
39465    tryEmit: function(input) {
39466        if (this.canEmit()) {
39467            return this.emit(input);
39468        }
39469        // it's failing anyway
39470        this.state = STATE_FAILED;
39471    },
39472
39473    /**
39474     * can we emit?
39475     * @returns {boolean}
39476     */
39477    canEmit: function() {
39478        var i = 0;
39479        while (i < this.requireFail.length) {
39480            if (!(this.requireFail[i].state & (STATE_FAILED | STATE_POSSIBLE))) {
39481                return false;
39482            }
39483            i++;
39484        }
39485        return true;
39486    },
39487
39488    /**
39489     * update the recognizer
39490     * @param {Object} inputData
39491     */
39492    recognize: function(inputData) {
39493        // make a new copy of the inputData
39494        // so we can change the inputData without messing up the other recognizers
39495        var inputDataClone = assign({}, inputData);
39496
39497        // is is enabled and allow recognizing?
39498        if (!boolOrFn(this.options.enable, [this, inputDataClone])) {
39499            this.reset();
39500            this.state = STATE_FAILED;
39501            return;
39502        }
39503
39504        // reset when we've reached the end
39505        if (this.state & (STATE_RECOGNIZED | STATE_CANCELLED | STATE_FAILED)) {
39506            this.state = STATE_POSSIBLE;
39507        }
39508
39509        this.state = this.process(inputDataClone);
39510
39511        // the recognizer has recognized a gesture
39512        // so trigger an event
39513        if (this.state & (STATE_BEGAN | STATE_CHANGED | STATE_ENDED | STATE_CANCELLED)) {
39514            this.tryEmit(inputDataClone);
39515        }
39516    },
39517
39518    /**
39519     * return the state of the recognizer
39520     * the actual recognizing happens in this method
39521     * @virtual
39522     * @param {Object} inputData
39523     * @returns {Const} STATE
39524     */
39525    process: function(inputData) { }, // jshint ignore:line
39526
39527    /**
39528     * return the preferred touch-action
39529     * @virtual
39530     * @returns {Array}
39531     */
39532    getTouchAction: function() { },
39533
39534    /**
39535     * called when the gesture isn't allowed to recognize
39536     * like when another is being recognized or it is disabled
39537     * @virtual
39538     */
39539    reset: function() { }
39540};
39541
39542/**
39543 * get a usable string, used as event postfix
39544 * @param {Const} state
39545 * @returns {String} state
39546 */
39547function stateStr(state) {
39548    if (state & STATE_CANCELLED) {
39549        return 'cancel';
39550    } else if (state & STATE_ENDED) {
39551        return 'end';
39552    } else if (state & STATE_CHANGED) {
39553        return 'move';
39554    } else if (state & STATE_BEGAN) {
39555        return 'start';
39556    }
39557    return '';
39558}
39559
39560/**
39561 * direction cons to string
39562 * @param {Const} direction
39563 * @returns {String}
39564 */
39565function directionStr(direction) {
39566    if (direction == DIRECTION_DOWN) {
39567        return 'down';
39568    } else if (direction == DIRECTION_UP) {
39569        return 'up';
39570    } else if (direction == DIRECTION_LEFT) {
39571        return 'left';
39572    } else if (direction == DIRECTION_RIGHT) {
39573        return 'right';
39574    }
39575    return '';
39576}
39577
39578/**
39579 * get a recognizer by name if it is bound to a manager
39580 * @param {Recognizer|String} otherRecognizer
39581 * @param {Recognizer} recognizer
39582 * @returns {Recognizer}
39583 */
39584function getRecognizerByNameIfManager(otherRecognizer, recognizer) {
39585    var manager = recognizer.manager;
39586    if (manager) {
39587        return manager.get(otherRecognizer);
39588    }
39589    return otherRecognizer;
39590}
39591
39592/**
39593 * This recognizer is just used as a base for the simple attribute recognizers.
39594 * @constructor
39595 * @extends Recognizer
39596 */
39597function AttrRecognizer() {
39598    Recognizer.apply(this, arguments);
39599}
39600
39601inherit(AttrRecognizer, Recognizer, {
39602    /**
39603     * @namespace
39604     * @memberof AttrRecognizer
39605     */
39606    defaults: {
39607        /**
39608         * @type {Number}
39609         * @default 1
39610         */
39611        pointers: 1
39612    },
39613
39614    /**
39615     * Used to check if it the recognizer receives valid input, like input.distance > 10.
39616     * @memberof AttrRecognizer
39617     * @param {Object} input
39618     * @returns {Boolean} recognized
39619     */
39620    attrTest: function(input) {
39621        var optionPointers = this.options.pointers;
39622        return optionPointers === 0 || input.pointers.length === optionPointers;
39623    },
39624
39625    /**
39626     * Process the input and return the state for the recognizer
39627     * @memberof AttrRecognizer
39628     * @param {Object} input
39629     * @returns {*} State
39630     */
39631    process: function(input) {
39632        var state = this.state;
39633        var eventType = input.eventType;
39634
39635        var isRecognized = state & (STATE_BEGAN | STATE_CHANGED);
39636        var isValid = this.attrTest(input);
39637
39638        // on cancel input and we've recognized before, return STATE_CANCELLED
39639        if (isRecognized && (eventType & INPUT_CANCEL || !isValid)) {
39640            return state | STATE_CANCELLED;
39641        } else if (isRecognized || isValid) {
39642            if (eventType & INPUT_END) {
39643                return state | STATE_ENDED;
39644            } else if (!(state & STATE_BEGAN)) {
39645                return STATE_BEGAN;
39646            }
39647            return state | STATE_CHANGED;
39648        }
39649        return STATE_FAILED;
39650    }
39651});
39652
39653/**
39654 * Pan
39655 * Recognized when the pointer is down and moved in the allowed direction.
39656 * @constructor
39657 * @extends AttrRecognizer
39658 */
39659function PanRecognizer() {
39660    AttrRecognizer.apply(this, arguments);
39661
39662    this.pX = null;
39663    this.pY = null;
39664}
39665
39666inherit(PanRecognizer, AttrRecognizer, {
39667    /**
39668     * @namespace
39669     * @memberof PanRecognizer
39670     */
39671    defaults: {
39672        event: 'pan',
39673        threshold: 10,
39674        pointers: 1,
39675        direction: DIRECTION_ALL
39676    },
39677
39678    getTouchAction: function() {
39679        var direction = this.options.direction;
39680        var actions = [];
39681        if (direction & DIRECTION_HORIZONTAL) {
39682            actions.push(TOUCH_ACTION_PAN_Y);
39683        }
39684        if (direction & DIRECTION_VERTICAL) {
39685            actions.push(TOUCH_ACTION_PAN_X);
39686        }
39687        return actions;
39688    },
39689
39690    directionTest: function(input) {
39691        var options = this.options;
39692        var hasMoved = true;
39693        var distance = input.distance;
39694        var direction = input.direction;
39695        var x = input.deltaX;
39696        var y = input.deltaY;
39697
39698        // lock to axis?
39699        if (!(direction & options.direction)) {
39700            if (options.direction & DIRECTION_HORIZONTAL) {
39701                direction = (x === 0) ? DIRECTION_NONE : (x < 0) ? DIRECTION_LEFT : DIRECTION_RIGHT;
39702                hasMoved = x != this.pX;
39703                distance = Math.abs(input.deltaX);
39704            } else {
39705                direction = (y === 0) ? DIRECTION_NONE : (y < 0) ? DIRECTION_UP : DIRECTION_DOWN;
39706                hasMoved = y != this.pY;
39707                distance = Math.abs(input.deltaY);
39708            }
39709        }
39710        input.direction = direction;
39711        return hasMoved && distance > options.threshold && direction & options.direction;
39712    },
39713
39714    attrTest: function(input) {
39715        return AttrRecognizer.prototype.attrTest.call(this, input) &&
39716            (this.state & STATE_BEGAN || (!(this.state & STATE_BEGAN) && this.directionTest(input)));
39717    },
39718
39719    emit: function(input) {
39720
39721        this.pX = input.deltaX;
39722        this.pY = input.deltaY;
39723
39724        var direction = directionStr(input.direction);
39725
39726        if (direction) {
39727            input.additionalEvent = this.options.event + direction;
39728        }
39729        this._super.emit.call(this, input);
39730    }
39731});
39732
39733/**
39734 * Pinch
39735 * Recognized when two or more pointers are moving toward (zoom-in) or away from each other (zoom-out).
39736 * @constructor
39737 * @extends AttrRecognizer
39738 */
39739function PinchRecognizer() {
39740    AttrRecognizer.apply(this, arguments);
39741}
39742
39743inherit(PinchRecognizer, AttrRecognizer, {
39744    /**
39745     * @namespace
39746     * @memberof PinchRecognizer
39747     */
39748    defaults: {
39749        event: 'pinch',
39750        threshold: 0,
39751        pointers: 2
39752    },
39753
39754    getTouchAction: function() {
39755        return [TOUCH_ACTION_NONE];
39756    },
39757
39758    attrTest: function(input) {
39759        return this._super.attrTest.call(this, input) &&
39760            (Math.abs(input.scale - 1) > this.options.threshold || this.state & STATE_BEGAN);
39761    },
39762
39763    emit: function(input) {
39764        if (input.scale !== 1) {
39765            var inOut = input.scale < 1 ? 'in' : 'out';
39766            input.additionalEvent = this.options.event + inOut;
39767        }
39768        this._super.emit.call(this, input);
39769    }
39770});
39771
39772/**
39773 * Press
39774 * Recognized when the pointer is down for x ms without any movement.
39775 * @constructor
39776 * @extends Recognizer
39777 */
39778function PressRecognizer() {
39779    Recognizer.apply(this, arguments);
39780
39781    this._timer = null;
39782    this._input = null;
39783}
39784
39785inherit(PressRecognizer, Recognizer, {
39786    /**
39787     * @namespace
39788     * @memberof PressRecognizer
39789     */
39790    defaults: {
39791        event: 'press',
39792        pointers: 1,
39793        time: 251, // minimal time of the pointer to be pressed
39794        threshold: 9 // a minimal movement is ok, but keep it low
39795    },
39796
39797    getTouchAction: function() {
39798        return [TOUCH_ACTION_AUTO];
39799    },
39800
39801    process: function(input) {
39802        var options = this.options;
39803        var validPointers = input.pointers.length === options.pointers;
39804        var validMovement = input.distance < options.threshold;
39805        var validTime = input.deltaTime > options.time;
39806
39807        this._input = input;
39808
39809        // we only allow little movement
39810        // and we've reached an end event, so a tap is possible
39811        if (!validMovement || !validPointers || (input.eventType & (INPUT_END | INPUT_CANCEL) && !validTime)) {
39812            this.reset();
39813        } else if (input.eventType & INPUT_START) {
39814            this.reset();
39815            this._timer = setTimeoutContext(function() {
39816                this.state = STATE_RECOGNIZED;
39817                this.tryEmit();
39818            }, options.time, this);
39819        } else if (input.eventType & INPUT_END) {
39820            return STATE_RECOGNIZED;
39821        }
39822        return STATE_FAILED;
39823    },
39824
39825    reset: function() {
39826        clearTimeout(this._timer);
39827    },
39828
39829    emit: function(input) {
39830        if (this.state !== STATE_RECOGNIZED) {
39831            return;
39832        }
39833
39834        if (input && (input.eventType & INPUT_END)) {
39835            this.manager.emit(this.options.event + 'up', input);
39836        } else {
39837            this._input.timeStamp = now();
39838            this.manager.emit(this.options.event, this._input);
39839        }
39840    }
39841});
39842
39843/**
39844 * Rotate
39845 * Recognized when two or more pointer are moving in a circular motion.
39846 * @constructor
39847 * @extends AttrRecognizer
39848 */
39849function RotateRecognizer() {
39850    AttrRecognizer.apply(this, arguments);
39851}
39852
39853inherit(RotateRecognizer, AttrRecognizer, {
39854    /**
39855     * @namespace
39856     * @memberof RotateRecognizer
39857     */
39858    defaults: {
39859        event: 'rotate',
39860        threshold: 0,
39861        pointers: 2
39862    },
39863
39864    getTouchAction: function() {
39865        return [TOUCH_ACTION_NONE];
39866    },
39867
39868    attrTest: function(input) {
39869        return this._super.attrTest.call(this, input) &&
39870            (Math.abs(input.rotation) > this.options.threshold || this.state & STATE_BEGAN);
39871    }
39872});
39873
39874/**
39875 * Swipe
39876 * Recognized when the pointer is moving fast (velocity), with enough distance in the allowed direction.
39877 * @constructor
39878 * @extends AttrRecognizer
39879 */
39880function SwipeRecognizer() {
39881    AttrRecognizer.apply(this, arguments);
39882}
39883
39884inherit(SwipeRecognizer, AttrRecognizer, {
39885    /**
39886     * @namespace
39887     * @memberof SwipeRecognizer
39888     */
39889    defaults: {
39890        event: 'swipe',
39891        threshold: 10,
39892        velocity: 0.3,
39893        direction: DIRECTION_HORIZONTAL | DIRECTION_VERTICAL,
39894        pointers: 1
39895    },
39896
39897    getTouchAction: function() {
39898        return PanRecognizer.prototype.getTouchAction.call(this);
39899    },
39900
39901    attrTest: function(input) {
39902        var direction = this.options.direction;
39903        var velocity;
39904
39905        if (direction & (DIRECTION_HORIZONTAL | DIRECTION_VERTICAL)) {
39906            velocity = input.overallVelocity;
39907        } else if (direction & DIRECTION_HORIZONTAL) {
39908            velocity = input.overallVelocityX;
39909        } else if (direction & DIRECTION_VERTICAL) {
39910            velocity = input.overallVelocityY;
39911        }
39912
39913        return this._super.attrTest.call(this, input) &&
39914            direction & input.offsetDirection &&
39915            input.distance > this.options.threshold &&
39916            input.maxPointers == this.options.pointers &&
39917            abs(velocity) > this.options.velocity && input.eventType & INPUT_END;
39918    },
39919
39920    emit: function(input) {
39921        var direction = directionStr(input.offsetDirection);
39922        if (direction) {
39923            this.manager.emit(this.options.event + direction, input);
39924        }
39925
39926        this.manager.emit(this.options.event, input);
39927    }
39928});
39929
39930/**
39931 * A tap is ecognized when the pointer is doing a small tap/click. Multiple taps are recognized if they occur
39932 * between the given interval and position. The delay option can be used to recognize multi-taps without firing
39933 * a single tap.
39934 *
39935 * The eventData from the emitted event contains the property `tapCount`, which contains the amount of
39936 * multi-taps being recognized.
39937 * @constructor
39938 * @extends Recognizer
39939 */
39940function TapRecognizer() {
39941    Recognizer.apply(this, arguments);
39942
39943    // previous time and center,
39944    // used for tap counting
39945    this.pTime = false;
39946    this.pCenter = false;
39947
39948    this._timer = null;
39949    this._input = null;
39950    this.count = 0;
39951}
39952
39953inherit(TapRecognizer, Recognizer, {
39954    /**
39955     * @namespace
39956     * @memberof PinchRecognizer
39957     */
39958    defaults: {
39959        event: 'tap',
39960        pointers: 1,
39961        taps: 1,
39962        interval: 300, // max time between the multi-tap taps
39963        time: 250, // max time of the pointer to be down (like finger on the screen)
39964        threshold: 9, // a minimal movement is ok, but keep it low
39965        posThreshold: 10 // a multi-tap can be a bit off the initial position
39966    },
39967
39968    getTouchAction: function() {
39969        return [TOUCH_ACTION_MANIPULATION];
39970    },
39971
39972    process: function(input) {
39973        var options = this.options;
39974
39975        var validPointers = input.pointers.length === options.pointers;
39976        var validMovement = input.distance < options.threshold;
39977        var validTouchTime = input.deltaTime < options.time;
39978
39979        this.reset();
39980
39981        if ((input.eventType & INPUT_START) && (this.count === 0)) {
39982            return this.failTimeout();
39983        }
39984
39985        // we only allow little movement
39986        // and we've reached an end event, so a tap is possible
39987        if (validMovement && validTouchTime && validPointers) {
39988            if (input.eventType != INPUT_END) {
39989                return this.failTimeout();
39990            }
39991
39992            var validInterval = this.pTime ? (input.timeStamp - this.pTime < options.interval) : true;
39993            var validMultiTap = !this.pCenter || getDistance(this.pCenter, input.center) < options.posThreshold;
39994
39995            this.pTime = input.timeStamp;
39996            this.pCenter = input.center;
39997
39998            if (!validMultiTap || !validInterval) {
39999                this.count = 1;
40000            } else {
40001                this.count += 1;
40002            }
40003
40004            this._input = input;
40005
40006            // if tap count matches we have recognized it,
40007            // else it has began recognizing...
40008            var tapCount = this.count % options.taps;
40009            if (tapCount === 0) {
40010                // no failing requirements, immediately trigger the tap event
40011                // or wait as long as the multitap interval to trigger
40012                if (!this.hasRequireFailures()) {
40013                    return STATE_RECOGNIZED;
40014                } else {
40015                    this._timer = setTimeoutContext(function() {
40016                        this.state = STATE_RECOGNIZED;
40017                        this.tryEmit();
40018                    }, options.interval, this);
40019                    return STATE_BEGAN;
40020                }
40021            }
40022        }
40023        return STATE_FAILED;
40024    },
40025
40026    failTimeout: function() {
40027        this._timer = setTimeoutContext(function() {
40028            this.state = STATE_FAILED;
40029        }, this.options.interval, this);
40030        return STATE_FAILED;
40031    },
40032
40033    reset: function() {
40034        clearTimeout(this._timer);
40035    },
40036
40037    emit: function() {
40038        if (this.state == STATE_RECOGNIZED) {
40039            this._input.tapCount = this.count;
40040            this.manager.emit(this.options.event, this._input);
40041        }
40042    }
40043});
40044
40045/**
40046 * Simple way to create a manager with a default set of recognizers.
40047 * @param {HTMLElement} element
40048 * @param {Object} [options]
40049 * @constructor
40050 */
40051function Hammer(element, options) {
40052    options = options || {};
40053    options.recognizers = ifUndefined(options.recognizers, Hammer.defaults.preset);
40054    return new Manager(element, options);
40055}
40056
40057/**
40058 * @const {string}
40059 */
40060Hammer.VERSION = '2.0.7';
40061
40062/**
40063 * default settings
40064 * @namespace
40065 */
40066Hammer.defaults = {
40067    /**
40068     * set if DOM events are being triggered.
40069     * But this is slower and unused by simple implementations, so disabled by default.
40070     * @type {Boolean}
40071     * @default false
40072     */
40073    domEvents: false,
40074
40075    /**
40076     * The value for the touchAction property/fallback.
40077     * When set to `compute` it will magically set the correct value based on the added recognizers.
40078     * @type {String}
40079     * @default compute
40080     */
40081    touchAction: TOUCH_ACTION_COMPUTE,
40082
40083    /**
40084     * @type {Boolean}
40085     * @default true
40086     */
40087    enable: true,
40088
40089    /**
40090     * EXPERIMENTAL FEATURE -- can be removed/changed
40091     * Change the parent input target element.
40092     * If Null, then it is being set the to main element.
40093     * @type {Null|EventTarget}
40094     * @default null
40095     */
40096    inputTarget: null,
40097
40098    /**
40099     * force an input class
40100     * @type {Null|Function}
40101     * @default null
40102     */
40103    inputClass: null,
40104
40105    /**
40106     * Default recognizer setup when calling `Hammer()`
40107     * When creating a new Manager these will be skipped.
40108     * @type {Array}
40109     */
40110    preset: [
40111        // RecognizerClass, options, [recognizeWith, ...], [requireFailure, ...]
40112        [RotateRecognizer, {enable: false}],
40113        [PinchRecognizer, {enable: false}, ['rotate']],
40114        [SwipeRecognizer, {direction: DIRECTION_HORIZONTAL}],
40115        [PanRecognizer, {direction: DIRECTION_HORIZONTAL}, ['swipe']],
40116        [TapRecognizer],
40117        [TapRecognizer, {event: 'doubletap', taps: 2}, ['tap']],
40118        [PressRecognizer]
40119    ],
40120
40121    /**
40122     * Some CSS properties can be used to improve the working of Hammer.
40123     * Add them to this method and they will be set when creating a new Manager.
40124     * @namespace
40125     */
40126    cssProps: {
40127        /**
40128         * Disables text selection to improve the dragging gesture. Mainly for desktop browsers.
40129         * @type {String}
40130         * @default 'none'
40131         */
40132        userSelect: 'none',
40133
40134        /**
40135         * Disable the Windows Phone grippers when pressing an element.
40136         * @type {String}
40137         * @default 'none'
40138         */
40139        touchSelect: 'none',
40140
40141        /**
40142         * Disables the default callout shown when you touch and hold a touch target.
40143         * On iOS, when you touch and hold a touch target such as a link, Safari displays
40144         * a callout containing information about the link. This property allows you to disable that callout.
40145         * @type {String}
40146         * @default 'none'
40147         */
40148        touchCallout: 'none',
40149
40150        /**
40151         * Specifies whether zooming is enabled. Used by IE10>
40152         * @type {String}
40153         * @default 'none'
40154         */
40155        contentZooming: 'none',
40156
40157        /**
40158         * Specifies that an entire element should be draggable instead of its contents. Mainly for desktop browsers.
40159         * @type {String}
40160         * @default 'none'
40161         */
40162        userDrag: 'none',
40163
40164        /**
40165         * Overrides the highlight color shown when the user taps a link or a JavaScript
40166         * clickable element in iOS. This property obeys the alpha value, if specified.
40167         * @type {String}
40168         * @default 'rgba(0,0,0,0)'
40169         */
40170        tapHighlightColor: 'rgba(0,0,0,0)'
40171    }
40172};
40173
40174var STOP = 1;
40175var FORCED_STOP = 2;
40176
40177/**
40178 * Manager
40179 * @param {HTMLElement} element
40180 * @param {Object} [options]
40181 * @constructor
40182 */
40183function Manager(element, options) {
40184    this.options = assign({}, Hammer.defaults, options || {});
40185
40186    this.options.inputTarget = this.options.inputTarget || element;
40187
40188    this.handlers = {};
40189    this.session = {};
40190    this.recognizers = [];
40191    this.oldCssProps = {};
40192
40193    this.element = element;
40194    this.input = createInputInstance(this);
40195    this.touchAction = new TouchAction(this, this.options.touchAction);
40196
40197    toggleCssProps(this, true);
40198
40199    each(this.options.recognizers, function(item) {
40200        var recognizer = this.add(new (item[0])(item[1]));
40201        item[2] && recognizer.recognizeWith(item[2]);
40202        item[3] && recognizer.requireFailure(item[3]);
40203    }, this);
40204}
40205
40206Manager.prototype = {
40207    /**
40208     * set options
40209     * @param {Object} options
40210     * @returns {Manager}
40211     */
40212    set: function(options) {
40213        assign(this.options, options);
40214
40215        // Options that need a little more setup
40216        if (options.touchAction) {
40217            this.touchAction.update();
40218        }
40219        if (options.inputTarget) {
40220            // Clean up existing event listeners and reinitialize
40221            this.input.destroy();
40222            this.input.target = options.inputTarget;
40223            this.input.init();
40224        }
40225        return this;
40226    },
40227
40228    /**
40229     * stop recognizing for this session.
40230     * This session will be discarded, when a new [input]start event is fired.
40231     * When forced, the recognizer cycle is stopped immediately.
40232     * @param {Boolean} [force]
40233     */
40234    stop: function(force) {
40235        this.session.stopped = force ? FORCED_STOP : STOP;
40236    },
40237
40238    /**
40239     * run the recognizers!
40240     * called by the inputHandler function on every movement of the pointers (touches)
40241     * it walks through all the recognizers and tries to detect the gesture that is being made
40242     * @param {Object} inputData
40243     */
40244    recognize: function(inputData) {
40245        var session = this.session;
40246        if (session.stopped) {
40247            return;
40248        }
40249
40250        // run the touch-action polyfill
40251        this.touchAction.preventDefaults(inputData);
40252
40253        var recognizer;
40254        var recognizers = this.recognizers;
40255
40256        // this holds the recognizer that is being recognized.
40257        // so the recognizer's state needs to be BEGAN, CHANGED, ENDED or RECOGNIZED
40258        // if no recognizer is detecting a thing, it is set to `null`
40259        var curRecognizer = session.curRecognizer;
40260
40261        // reset when the last recognizer is recognized
40262        // or when we're in a new session
40263        if (!curRecognizer || (curRecognizer && curRecognizer.state & STATE_RECOGNIZED)) {
40264            curRecognizer = session.curRecognizer = null;
40265        }
40266
40267        var i = 0;
40268        while (i < recognizers.length) {
40269            recognizer = recognizers[i];
40270
40271            // find out if we are allowed try to recognize the input for this one.
40272            // 1.   allow if the session is NOT forced stopped (see the .stop() method)
40273            // 2.   allow if we still haven't recognized a gesture in this session, or the this recognizer is the one
40274            //      that is being recognized.
40275            // 3.   allow if the recognizer is allowed to run simultaneous with the current recognized recognizer.
40276            //      this can be setup with the `recognizeWith()` method on the recognizer.
40277            if (session.stopped !== FORCED_STOP && ( // 1
40278                    !curRecognizer || recognizer == curRecognizer || // 2
40279                    recognizer.canRecognizeWith(curRecognizer))) { // 3
40280                recognizer.recognize(inputData);
40281            } else {
40282                recognizer.reset();
40283            }
40284
40285            // if the recognizer has been recognizing the input as a valid gesture, we want to store this one as the
40286            // current active recognizer. but only if we don't already have an active recognizer
40287            if (!curRecognizer && recognizer.state & (STATE_BEGAN | STATE_CHANGED | STATE_ENDED)) {
40288                curRecognizer = session.curRecognizer = recognizer;
40289            }
40290            i++;
40291        }
40292    },
40293
40294    /**
40295     * get a recognizer by its event name.
40296     * @param {Recognizer|String} recognizer
40297     * @returns {Recognizer|Null}
40298     */
40299    get: function(recognizer) {
40300        if (recognizer instanceof Recognizer) {
40301            return recognizer;
40302        }
40303
40304        var recognizers = this.recognizers;
40305        for (var i = 0; i < recognizers.length; i++) {
40306            if (recognizers[i].options.event == recognizer) {
40307                return recognizers[i];
40308            }
40309        }
40310        return null;
40311    },
40312
40313    /**
40314     * add a recognizer to the manager
40315     * existing recognizers with the same event name will be removed
40316     * @param {Recognizer} recognizer
40317     * @returns {Recognizer|Manager}
40318     */
40319    add: function(recognizer) {
40320        if (invokeArrayArg(recognizer, 'add', this)) {
40321            return this;
40322        }
40323
40324        // remove existing
40325        var existing = this.get(recognizer.options.event);
40326        if (existing) {
40327            this.remove(existing);
40328        }
40329
40330        this.recognizers.push(recognizer);
40331        recognizer.manager = this;
40332
40333        this.touchAction.update();
40334        return recognizer;
40335    },
40336
40337    /**
40338     * remove a recognizer by name or instance
40339     * @param {Recognizer|String} recognizer
40340     * @returns {Manager}
40341     */
40342    remove: function(recognizer) {
40343        if (invokeArrayArg(recognizer, 'remove', this)) {
40344            return this;
40345        }
40346
40347        recognizer = this.get(recognizer);
40348
40349        // let's make sure this recognizer exists
40350        if (recognizer) {
40351            var recognizers = this.recognizers;
40352            var index = inArray(recognizers, recognizer);
40353
40354            if (index !== -1) {
40355                recognizers.splice(index, 1);
40356                this.touchAction.update();
40357            }
40358        }
40359
40360        return this;
40361    },
40362
40363    /**
40364     * bind event
40365     * @param {String} events
40366     * @param {Function} handler
40367     * @returns {EventEmitter} this
40368     */
40369    on: function(events, handler) {
40370        if (events === undefined) {
40371            return;
40372        }
40373        if (handler === undefined) {
40374            return;
40375        }
40376
40377        var handlers = this.handlers;
40378        each(splitStr(events), function(event) {
40379            handlers[event] = handlers[event] || [];
40380            handlers[event].push(handler);
40381        });
40382        return this;
40383    },
40384
40385    /**
40386     * unbind event, leave emit blank to remove all handlers
40387     * @param {String} events
40388     * @param {Function} [handler]
40389     * @returns {EventEmitter} this
40390     */
40391    off: function(events, handler) {
40392        if (events === undefined) {
40393            return;
40394        }
40395
40396        var handlers = this.handlers;
40397        each(splitStr(events), function(event) {
40398            if (!handler) {
40399                delete handlers[event];
40400            } else {
40401                handlers[event] && handlers[event].splice(inArray(handlers[event], handler), 1);
40402            }
40403        });
40404        return this;
40405    },
40406
40407    /**
40408     * emit event to the listeners
40409     * @param {String} event
40410     * @param {Object} data
40411     */
40412    emit: function(event, data) {
40413        // we also want to trigger dom events
40414        if (this.options.domEvents) {
40415            triggerDomEvent(event, data);
40416        }
40417
40418        // no handlers, so skip it all
40419        var handlers = this.handlers[event] && this.handlers[event].slice();
40420        if (!handlers || !handlers.length) {
40421            return;
40422        }
40423
40424        data.type = event;
40425        data.preventDefault = function() {
40426            data.srcEvent.preventDefault();
40427        };
40428
40429        var i = 0;
40430        while (i < handlers.length) {
40431            handlers[i](data);
40432            i++;
40433        }
40434    },
40435
40436    /**
40437     * destroy the manager and unbinds all events
40438     * it doesn't unbind dom events, that is the user own responsibility
40439     */
40440    destroy: function() {
40441        this.element && toggleCssProps(this, false);
40442
40443        this.handlers = {};
40444        this.session = {};
40445        this.input.destroy();
40446        this.element = null;
40447    }
40448};
40449
40450/**
40451 * add/remove the css properties as defined in manager.options.cssProps
40452 * @param {Manager} manager
40453 * @param {Boolean} add
40454 */
40455function toggleCssProps(manager, add) {
40456    var element = manager.element;
40457    if (!element.style) {
40458        return;
40459    }
40460    var prop;
40461    each(manager.options.cssProps, function(value, name) {
40462        prop = prefixed(element.style, name);
40463        if (add) {
40464            manager.oldCssProps[prop] = element.style[prop];
40465            element.style[prop] = value;
40466        } else {
40467            element.style[prop] = manager.oldCssProps[prop] || '';
40468        }
40469    });
40470    if (!add) {
40471        manager.oldCssProps = {};
40472    }
40473}
40474
40475/**
40476 * trigger dom event
40477 * @param {String} event
40478 * @param {Object} data
40479 */
40480function triggerDomEvent(event, data) {
40481    var gestureEvent = document.createEvent('Event');
40482    gestureEvent.initEvent(event, true, true);
40483    gestureEvent.gesture = data;
40484    data.target.dispatchEvent(gestureEvent);
40485}
40486
40487assign(Hammer, {
40488    INPUT_START: INPUT_START,
40489    INPUT_MOVE: INPUT_MOVE,
40490    INPUT_END: INPUT_END,
40491    INPUT_CANCEL: INPUT_CANCEL,
40492
40493    STATE_POSSIBLE: STATE_POSSIBLE,
40494    STATE_BEGAN: STATE_BEGAN,
40495    STATE_CHANGED: STATE_CHANGED,
40496    STATE_ENDED: STATE_ENDED,
40497    STATE_RECOGNIZED: STATE_RECOGNIZED,
40498    STATE_CANCELLED: STATE_CANCELLED,
40499    STATE_FAILED: STATE_FAILED,
40500
40501    DIRECTION_NONE: DIRECTION_NONE,
40502    DIRECTION_LEFT: DIRECTION_LEFT,
40503    DIRECTION_RIGHT: DIRECTION_RIGHT,
40504    DIRECTION_UP: DIRECTION_UP,
40505    DIRECTION_DOWN: DIRECTION_DOWN,
40506    DIRECTION_HORIZONTAL: DIRECTION_HORIZONTAL,
40507    DIRECTION_VERTICAL: DIRECTION_VERTICAL,
40508    DIRECTION_ALL: DIRECTION_ALL,
40509
40510    Manager: Manager,
40511    Input: Input,
40512    TouchAction: TouchAction,
40513
40514    TouchInput: TouchInput,
40515    MouseInput: MouseInput,
40516    PointerEventInput: PointerEventInput,
40517    TouchMouseInput: TouchMouseInput,
40518    SingleTouchInput: SingleTouchInput,
40519
40520    Recognizer: Recognizer,
40521    AttrRecognizer: AttrRecognizer,
40522    Tap: TapRecognizer,
40523    Pan: PanRecognizer,
40524    Swipe: SwipeRecognizer,
40525    Pinch: PinchRecognizer,
40526    Rotate: RotateRecognizer,
40527    Press: PressRecognizer,
40528
40529    on: addEventListeners,
40530    off: removeEventListeners,
40531    each: each,
40532    merge: merge,
40533    extend: extend,
40534    assign: assign,
40535    inherit: inherit,
40536    bindFn: bindFn,
40537    prefixed: prefixed
40538});
40539
40540// this prevents errors when Hammer is loaded in the presence of an AMD
40541//  style loader but by script tag, not by the loader.
40542var freeGlobal = (typeof window !== 'undefined' ? window : (typeof self !== 'undefined' ? self : {})); // jshint ignore:line
40543freeGlobal.Hammer = Hammer;
40544
40545if (t
40545rue) {
40546    !(__WEBPACK_AMD_DEFINE_RESULT__ = function() {
40547        return Hammer;
40548    }.call(exports, __webpack_require__, exports, module),
40549				__WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));
40550} else if (typeof module != 'undefined' && module.exports) {
40551    module.exports = Hammer;
40552} else {
40553    window[exportName] = Hammer;
40554}
40555
40556})(window, document, 'Hammer');
40557
40558
40559/***/ }),
40560/* 177 */
40561/***/ (function(module, exports, __webpack_require__) {
40562
40563"use strict";
40564
40565
40566// utils
40567exports.util = __webpack_require__(2);
40568exports.DOMutil = __webpack_require__(14);
40569
40570// data
40571exports.DataSet = __webpack_require__(11);
40572exports.DataView = __webpack_require__(12);
40573exports.Queue = __webpack_require__(43);
40574
40575// Timeline
40576exports.Timeline = __webpack_require__(178);
40577exports.Graph2d = __webpack_require__(180);
40578exports.timeline = {
40579  Core: __webpack_require__(65),
40580  DateUtil: __webpack_require__(36),
40581  Range: __webpack_require__(64),
40582  stack: __webpack_require__(100),
40583  TimeStep: __webpack_require__(66),
40584
40585  components: {
40586    items: {
40587      Item: __webpack_require__(38),
40588      BackgroundItem: __webpack_require__(103),
40589      BoxItem: __webpack_require__(101),
40590      PointItem: __webpack_require__(102),
40591      RangeItem: __webpack_require__(70)
40592    },
40593
40594    BackgroundGroup: __webpack_require__(69),
40595    Component: __webpack_require__(16),
40596    CurrentTime: __webpack_require__(67),
40597    CustomTime: __webpack_require__(46),
40598    DataAxis: __webpack_require__(107),
40599    DataScale: __webpack_require__(108),
40600    GraphGroup: __webpack_require__(109),
40601    Group: __webpack_require__(68),
40602    ItemSet: __webpack_require__(99),
40603    Legend: __webpack_require__(112),
40604    LineGraph: __webpack_require__(106),
40605    TimeAxis: __webpack_require__(45)
40606  }
40607};
40608
40609// bundled external libraries
40610exports.moment = __webpack_require__(9);
40611exports.Hammer = __webpack_require__(10);
40612exports.keycharm = __webpack_require__(35);
40613
40614/***/ }),
40615/* 178 */
40616/***/ (function(module, exports, __webpack_require__) {
40617
40618"use strict";
40619
40620
40621var moment = __webpack_require__(9);
40622var util = __webpack_require__(2);
40623var DataSet = __webpack_require__(11);
40624var DataView = __webpack_require__(12);
40625var Range = __webpack_require__(64);
40626var Core = __webpack_require__(65);
40627var TimeAxis = __webpack_require__(45);
40628var CurrentTime = __webpack_require__(67);
40629var CustomTime = __webpack_require__(46);
40630var ItemSet = __webpack_require__(99);
40631
40632var printStyle = __webpack_require__(15).printStyle;
40633var allOptions = __webpack_require__(105).allOptions;
40634var configureOptions = __webpack_require__(105).configureOptions;
40635
40636var Configurator = __webpack_require__(71)['default'];
40637var Validator = __webpack_require__(15)['default'];
40638
40639/**
40640 * Create a timeline visualization
40641 * @param {HTMLElement} container
40642 * @param {vis.DataSet | vis.DataView | Array} [items]
40643 * @param {vis.DataSet | vis.DataView | Array} [groups]
40644 * @param {Object} [options]  See Timeline.setOptions for the available options.
40645 * @constructor Timeline
40646 * @extends Core
40647 */
40648function Timeline(container, items, groups, options) {
40649
40650  if (!(this instanceof Timeline)) {
40651    throw new SyntaxError('Constructor must be called with the new operator');
40652  }
40653
40654  // if the third element is options, the forth is groups (optionally);
40655  if (!(Array.isArray(groups) || groups instanceof DataSet || groups instanceof DataView) && groups instanceof Object) {
40656    var forthArgument = options;
40657    options = groups;
40658    groups = forthArgument;
40659  }
40660
40661  // TODO: REMOVE THIS in the next MAJOR release
40662  // see https://github.com/almende/vis/issues/2511
40663  if (options && options.throttleRedraw) {
40664    console.warn("Timeline option \"throttleRedraw\" is DEPRICATED and no longer supported. It will be removed in the next MAJOR release.");
40665  }
40666
40667  var me = this;
40668  this.defaultOptions = {
40669    start: null,
40670    end: null,
40671    autoResize: true,
40672    orientation: {
40673      axis: 'bottom', // axis orientation: 'bottom', 'top', or 'both'
40674      item: 'bottom' // not relevant
40675    },
40676    moment: moment,
40677    width: null,
40678    height: null,
40679    maxHeight: null,
40680    minHeight: null
40681  };
40682  this.options = util.deepExtend({}, this.defaultOptions);
40683
40684  // Create the DOM, props, and emitter
40685  this._create(container);
40686  if (!options || options && typeof options.rtl == "undefined") {
40687    this.dom.root.style.visibility = 'hidden';
40688    var directionFromDom,
40689        domNode = this.dom.root;
40690    while (!directionFromDom && domNode) {
40691      directionFromDom = window.getComputedStyle(domNode, null).direction;
40692      domNode = domNode.parentElement;
40693    }
40694    this.options.rtl = directionFromDom && directionFromDom.toLowerCase() == "rtl";
40695  } else {
40696    this.options.rtl = options.rtl;
40697  }
40698
40699  this.options.rollingMode = options && options.rollingMode;
40700  this.options.onInitialDrawComplete = options && options.onInitialDrawComplete;
40701
40702  // all components listed here will be repainted automatically
40703  this.components = [];
40704
40705  this.body = {
40706    dom: this.dom,
40707    domProps: this.props,
40708    emitter: {
40709      on: this.on.bind(this),
40710      off: this.off.bind(this),
40711      emit: this.emit.bind(this)
40712    },
40713    hiddenDates: [],
40714    util: {
40715      getScale: function getScale() {
40716        return me.timeAxis.step.scale;
40717      },
40718      getStep: function getStep() {
40719        return me.timeAxis.step.step;
40720      },
40721
40722      toScreen: me._toScreen.bind(me),
40723      toGlobalScreen: me._toGlobalScreen.bind(me), // this refers to the root.width
40724      toTime: me._toTime.bind(me),
40725      toGlobalTime: me._toGlobalTime.bind(me)
40726    }
40727  };
40728
40729  // range
40730  this.range = new Range(this.body, this.options);
40731  this.components.push(this.range);
40732  this.body.range = this.range;
40733
40734  // time axis
40735  this.timeAxis = new TimeAxis(this.body, this.options);
40736  this.timeAxis2 = null; // used in case of orientation option 'both'
40737  this.components.push(this.timeAxis);
40738
40739  // current time bar
40740  this.currentTime = new CurrentTime(this.body, this.options);
40741  this.components.push(this.currentTime);
40742
40743  // item set
40744  this.itemSet = new ItemSet(this.body, this.options);
40745  this.components.push(this.itemSet);
40746
40747  this.itemsData = null; // DataSet
40748  this.groupsData = null; // DataSet
40749
40750  this.dom.root.onclick = function (event) {
40751    me.emit('click', me.getEventProperties(event));
40752  };
40753  this.dom.root.ondblclick = function (event) {
40754    me.emit('doubleClick', me.getEventProperties(event));
40755  };
40756  this.dom.root.oncontextmenu = function (event) {
40757    me.emit('contextmenu', me.getEventProperties(event));
40758  };
40759  this.dom.root.onmouseover = function (event) {
40760    me.emit('mouseOver', me.getEventProperties(event));
40761  };
40762  if (window.PointerEvent) {
40763    this.dom.root.onpointerdown = function (event) {
40764      me.emit('mouseDown', me.getEventProperties(event));
40765    };
40766    this.dom.root.onpointermove = function (event) {
40767      me.emit('mouseMove', me.getEventProperties(event));
40768    };
40769    this.dom.root.onpointerup = function (event) {
40770      me.emit('mouseUp', me.getEventProperties(event));
40771    };
40772  } else {
40773    this.dom.root.onmousemove = function (event) {
40774      me.emit('mouseMove', me.getEventProperties(event));
40775    };
40776    this.dom.root.onmousedown = function (event) {
40777      me.emit('mouseDown', me.getEventProperties(event));
40778    };
40779    this.dom.root.onmouseup = function (event) {
40780      me.emit('mouseUp', me.getEventProperties(event));
40781    };
40782  }
40783
40784  //Single time autoscale/fit
40785  this.initialFitDone = false;
40786  this.on('changed', function () {
40787    if (this.itemsData == null || this.options.rollingMode) return;
40788    if (!me.initialFitDone) {
40789      me.initialFitDone = true;
40790      if (me.options.start != undefined || me.options.end != undefined) {
40791        if (me.options.start == undefined || me.options.end == undefined) {
40792          var range = me.getItemRange();
40793        }
40794
40795        var start = me.options.start != undefined ? me.options.start : range.min;
40796        var end = me.options.end != undefined ? me.options.end : range.max;
40797        me.setWindow(start, end, { animation: false });
40798      } else {
40799        me.fit({ animation: false });
40800      }
40801    }
40802
40803    if (!me.initialDrawDone && me.initialRangeChangeDone) {
40804      me.initialDrawDone = true;
40805      me.dom.root.style.visibility = 'visible';
40806      if (me.options.onInitialDrawComplete) {
40807        setTimeout(function () {
40808          return me.options.onInitialDrawComplete();
40809        }, 0);
40810      }
40811    }
40812  });
40813
40814  // apply options
40815  if (options) {
40816    this.setOptions(options);
40817  }
40818
40819  // IMPORTANT: THIS HAPPENS BEFORE SET ITEMS!
40820  if (groups) {
40821    this.setGroups(groups);
40822  }
40823
40824  // create itemset
40825  if (items) {
40826    this.setItems(items);
40827  }
40828
40829  // draw for the first time
40830  this._redraw();
40831}
40832
40833// Extend the functionality from Core
40834Timeline.prototype = new Core();
40835
40836/**
40837 * Load a configurator
40838 * @return {Object}
40839 * @private
40840 */
40841Timeline.prototype._createConfigurator = function () {
40842  return new Configurator(this, this.dom.container, configureOptions);
40843};
40844
40845/**
40846 * Force a redraw. The size of all items will be recalculated.
40847 * Can be useful to manually redraw when option autoResize=false and the window
40848 * has been resized, or when the items CSS has been changed.
40849 *
40850 * Note: this function will be overridden on construction with a trottled version
40851 */
40852Timeline.prototype.redraw = function () {
40853  this.itemSet && this.itemSet.markDirty({ refreshItems: true });
40854  this._redraw();
40855};
40856
40857Timeline.prototype.setOptions = function (options) {
40858  // validate options
40859  var errorFound = Validator.validate(options, allOptions);
40860
40861  if (errorFound === true) {
40862    console.log('%cErrors have been found in the supplied options object.', printStyle);
40863  }
40864  Core.prototype.setOptions.call(this, options);
40865
40866  if ('type' in options) {
40867    if (options.type !== this.options.type) {
40868      this.options.type = options.type;
40869
40870      // force recreation of all items
40871      var itemsData = this.itemsData;
40872      if (itemsData) {
40873        var selection = this.getSelection();
40874        this.setItems(null); // remove all
40875        this.setItems(itemsData); // add all
40876        this.setSelection(selection); // restore selection
40877      }
40878    }
40879  }
40880};
40881
40882/**
40883 * Set items
40884 * @param {vis.DataSet | Array | null} items
40885 */
40886Timeline.prototype.setItems = function (items) {
40887  // convert to type DataSet when needed
40888  var newDataSet;
40889  if (!items) {
40890    newDataSet = null;
40891  } else if (items instanceof DataSet || items instanceof DataView) {
40892    newDataSet = items;
40893  } else {
40894    // turn an array into a dataset
40895    newDataSet = new DataSet(items, {
40896      type: {
40897        start: 'Date',
40898        end: 'Date'
40899      }
40900    });
40901  }
40902
40903  // set items
40904  this.itemsData = newDataSet;
40905  this.itemSet && this.itemSet.setItems(newDataSet);
40906};
40907
40908/**
40909 * Set groups
40910 * @param {vis.DataSet | Array} groups
40911 */
40912Timeline.prototype.setGroups = function (groups) {
40913  // convert to type DataSet when needed
40914  var newDataSet;
40915  if (!groups) {
40916    newDataSet = null;
40917  } else {
40918    var filter = function filter(group) {
40919      return group.visible !== false;
40920    };
40921    if (groups instanceof DataSet || groups instanceof DataView) {
40922      newDataSet = new DataView(groups, { filter: filter });
40923    } else {
40924      // turn an array into a dataset
40925      newDataSet = new DataSet(groups.filter(filter));
40926    }
40927  }
40928
40929  this.groupsData = newDataSet;
40930  this.itemSet.setGroups(newDataSet);
40931};
40932
40933/**
40934 * Set both items and groups in one go
40935 * @param {{items: (Array | vis.DataSet), groups: (Array | vis.DataSet)}} data
40936 */
40937Timeline.prototype.setData = function (data) {
40938  if (data && data.groups) {
40939    this.setGroups(data.groups);
40940  }
40941
40942  if (data && data.items) {
40943    this.setItems(data.items);
40944  }
40945};
40946
40947/**
40948 * Set selected items by their id. Replaces the current selection
40949 * Unknown id's are silently ignored.
40950 * @param {string[] | string} [ids]  An array with zero or more id's of the items to be
40951 *                                selected. If ids is an empty array, all items will be
40952 *                                unselected.
40953 * @param {Object} [options]      Available options:
40954 *                                `focus: boolean`
40955 *                                    If true, focus will be set to the selected item(s)
40956 *                                `animation: boolean | {duration: number, easingFunction: string}`
40957 *                                    If true (default), the range is animated
40958 *                                    smoothly to the new window. An object can be
40959 *                                    provided to specify duration and easing function.
40960 *                                    Default duration is 500 ms, and default easing
40961 *                                    function is 'easeInOutQuad'.
40962 *                                    Only applicable when option focus is true.
40963 */
40964Timeline.prototype.setSelection = function (ids, options) {
40965  this.itemSet && this.itemSet.setSelection(ids);
40966
40967  if (options && options.focus) {
40968    this.focus(ids, options);
40969  }
40970};
40971
40972/**
40973 * Get the selected items by their id
40974 * @return {Array} ids  The ids of the selected items
40975 */
40976Timeline.prototype.getSelection = function () {
40977  return this.itemSet && this.itemSet.getSelection() || [];
40978};
40979
40980/**
40981 * Adjust the visible window such that the selected item (or multiple items)
40982 * are centered on screen.
40983 * @param {string | String[]} id     An item id or array with item ids
40984 * @param {Object} [options]      Available options:
40985 *                                `animation: boolean | {duration: number, easingFunction: string}`
40986 *                                    If true (default), the range is animated
40987 *                                    smoothly to the new window. An object can be
40988 *                                    provided to specify duration and easing function.
40989 *                                    Default duration is 500 ms, and default easing
40990 *                                    function is 'easeInOutQuad'.
40991 */
40992Timeline.prototype.focus = function (id, options) {
40993  if (!this.itemsData || id == undefined) return;
40994
40995  var ids = Array.isArray(id) ? id : [id];
40996
40997  // get the specified item(s)
40998  var itemsData = this.itemsData.getDataSet().get(ids, {
40999    type: {
41000      start: 'Date',
41001      end: 'Date'
41002    }
41003  });
41004
41005  // calculate minimum start and maximum end of specified items
41006  var start = null;
41007  var end = null;
41008  itemsData.forEach(function (itemData) {
41009    var s = itemData.start.valueOf();
41010    var e = 'end' in itemData ? itemData.end.valueOf() : itemData.start.valueOf();
41011
41012    if (start === null || s < start) {
41013      start = s;
41014    }
41015
41016    if (end === null || e > end) {
41017      end = e;
41018    }
41019  });
41020
41021  if (start !== null && end !== null) {
41022    var me = this;
41023    // Use the first item for the vertical focus
41024    var item = this.itemSet.items[ids[0]];
41025    var startPos = this._getScrollTop() * -1;
41026    var initialVerticalScroll = null;
41027
41028    // Setup a handler for each frame of the vertical scroll
41029    var verticalAnimationFrame = function verticalAnimationFrame(ease, willDraw, done) {
41030      var verticalScroll = getItemVerticalScroll(me, item);
41031
41032      if (!initialVerticalScroll) {
41033        initialVerticalScroll = verticalScroll;
41034      }
41035
41036      if (initialVerticalScroll.itemTop == verticalScroll.itemTop && !initialVerticalScroll.shouldScroll) {
41037        return; // We don't need to scroll, so do nothing
41038      } else if (initialVerticalScroll.itemTop != verticalScroll.itemTop && verticalScroll.shouldScroll) {
41039        // The redraw shifted elements, so reset the animation to correct
41040        initialVerticalScroll = verticalScroll;
41041        startPos = me._getScrollTop() * -1;
41042      }
41043
41044      var from = startPos;
41045      var to = initialVerticalScroll.scrollOffset;
41046      var scrollTop = done ? to : from + (to - from) * ease;
41047
41048      me._setScrollTop(-scrollTop);
41049
41050      if (!willDraw) {
41051        me._redraw();
41052      }
41053    };
41054
41055    // Enforces the final vertical scroll position
41056    var setFinalVerticalPosition = function setFinalVerticalPosition() {
41057      var finalVerticalScroll = getItemVerticalScroll(me, item);
41058
41059      if (finalVerticalScroll.shouldScroll && finalVerticalScroll.itemTop != initialVerticalScroll.itemTop) {
41060        me._setScrollTop(-finalVerticalScroll.scrollOffset);
41061        me._redraw();
41062      }
41063    };
41064
41065    // Perform one last check at the end to make sure the final vertical
41066    // position is correct
41067    var finalVerticalCallback = function finalVerticalCallback() {
41068      // Double check we ended at the proper scroll position
41069      setFinalVerticalPosition();
41070
41071      // Let the redraw settle and finalize the position.      
41072      setTimeout(setFinalVerticalPosition, 100);
41073    };
41074
41075    // calculate the new middle and interval for the window
41076    var middle = (start + end) / 2;
41077    var interval = Math.max(this.range.end - this.range.start, (end - start) * 1.1);
41078
41079    var animation = options && options.animation !== undefined ? options.animation : true;
41080
41081    if (!animation) {
41082      // We aren't animating so set a default so that the final callback forces the vertical location
41083      initialVerticalScroll = { shouldScroll: false, scrollOffset: -1, itemTop: -1 };
41084    }
41085
41086    this.range.setRange(middle - interval / 2, middle + interval / 2, { animation: animation }, finalVerticalCallback, verticalAnimationFrame);
41087  }
41088};
41089
41090/**
41091 * Set Timeline window such that it fits all items
41092 * @param {Object} [options]  Available options:
41093 *                                `animation: boolean | {duration: number, easingFunction: string}`
41094 *                                    If true (default), the range is animated
41095 *                                    smoothly to the new window. An object can be
41096 *                                    provided to specify duration and easing function.
41097 *                                    Default duration is 500 ms, and default easing
41098 *                                    function is 'easeInOutQuad'.
41099 * @param {function} [callback]
41100 */
41101Timeline.prototype.fit = function (options, callback) {
41102  var animation = options && options.animation !== undefined ? options.animation : true;
41103  var range;
41104
41105  var dataset = this.itemsData && this.itemsData.getDataSet();
41106  if (dataset.length === 1 && dataset.get()[0].end === undefined) {
41107    // a single item -> don't fit, just show a range around the item from -4 to +3 days
41108    range = this.getDataRange();
41109    this.moveTo(range.min.valueOf(), { animation: animation }, callback);
41110  } else {
41111    // exactly fit the items (plus a small margin)
41112    range = this.getItemRange();
41113    this.range.setRange(range.min, range.max, { animation: animation }, callback);
41114  }
41115};
41116
41117/**
41118 *
41119 * @param {vis.Item} item
41120 * @returns {number}
41121 */
41122function getStart(item) {
41123  return util.convert(item.data.start, 'Date').valueOf();
41124}
41125
41126/**
41127 *
41128 * @param {vis.Item} item
41129 * @returns {number}
41130 */
41131function getEnd(item) {
41132  var end = item.data.end != undefined ? item.data.end : item.data.start;
41133  return util.convert(end, 'Date').valueOf();
41134}
41135
41136/**
41137 * @param {vis.Timeline} timeline
41138 * @param {vis.Item} item
41139 * @return {{shouldScroll: bool, scrollOffset: number, itemTop: number}}
41140 */
41141function getItemVerticalScroll(timeline, item) {
41142  var leftHeight = timeline.props.leftContainer.height;
41143  var contentHeight = timeline.props.left.height;
41144
41145  var group = item.parent;
41146  var offset = group.top;
41147  var shouldScroll = true;
41148  var orientation = timeline.timeAxis.options.orientation.axis;
41149
41150  var itemTop = function itemTop() {
41151    if (orientation == "bottom") {
41152      return group.height - item.top - item.height;
41153    } else {
41154      return item.top;
41155    }
41156  };
41157
41158  var currentScrollHeight = timeline._getScrollTop() * -1;
41159  var targetOffset = offset + itemTop();
41160  var height = item.height;
41161
41162  if (targetOffset < currentScrollHeight) {
41163    if (offset + leftHeight <= offset + itemTop() + height) {
41164      offset += itemTop() - timeline.itemSet.options.margin.item.vertical;
41165    }
41166  } else if (targetOffset + height > currentScrollHeight + leftHeight) {
41167    offset += itemTop() + height - leftHeight + timeline.itemSet.options.margin.item.vertical;
41168  } else {
41169    shouldScroll = false;
41170  }
41171
41172  offset = Math.min(offset, contentHeight - leftHeight);
41173
41174  return { shouldScroll: shouldScroll, scrollOffset: offset, itemTop: targetOffset };
41175}
41176
41177/**
41178 * Determine the range of the items, taking into account their actual width
41179 * and a margin of 10 pixels on both sides.
41180 *
41181 * @returns {{min: Date, max: Date}}
41182 */
41183Timeline.prototype.getItemRange = function () {
41184  // get a rough approximation for the range based on the items start and end dates
41185  var range = this.getDataRange();
41186  var min = range.min !== null ? range.min.valueOf() : null;
41187  var max = range.max !== null ? range.max.valueOf() : null;
41188  var minItem = null;
41189  var maxItem = null;
41190
41191  if (min != null && max != null) {
41192    var interval = max - min; // ms
41193    if (interval <= 0) {
41194      interval = 10;
41195    }
41196    var factor = interval / this.props.center.width;
41197
41198    var redrawQueue = {};
41199    var redrawQueueLength = 0;
41200
41201    // collect redraw functions
41202    util.forEach(this.itemSet.items, function (item, key) {
41203      if (item.groupShowing) {
41204        var returnQueue = true;
41205        redrawQueue[key] = item.redraw(returnQueue);
41206        redrawQueueLength = redrawQueue[key].length;
41207      }
41208    });
41209
41210    var needRedraw = redrawQueueLength > 0;
41211    if (needRedraw) {
41212      // redraw all regular items
41213      for (var i = 0; i < redrawQueueLength; i++) {
41214        util.forEach(redrawQueue, function (fns) {
41215          fns[i]();
41216        });
41217      }
41218    }
41219
41220    // calculate the date of the left side and right side of the items given
41221    util.forEach(this.itemSet.items, function (item) {
41222      var start = getStart(item);
41223      var end = getEnd(item);
41224      var startSide;
41225      var endSide;
41226
41227      if (this.options.rtl) {
41228        startSide = start - (item.getWidthRight() + 10) * factor;
41229        endSide = end + (item.getWidthLeft() + 10) * factor;
41230      } else {
41231        startSide = start - (item.getWidthLeft() + 10) * factor;
41232        endSide = end + (item.getWidthRight() + 10) * factor;
41233      }
41234
41235      if (startSide < min) {
41236        min = startSide;
41237        minItem = item;
41238      }
41239      if (endSide > max) {
41240        max = endSide;
41241        maxItem = item;
41242      }
41243    }.bind(this));
41244
41245    if (minItem && maxItem) {
41246      var lhs = minItem.getWidthLeft() + 10;
41247      var rhs = maxItem.getWidthRight() + 10;
41248      var delta = this.props.center.width - lhs - rhs; // px
41249
41250      if (delta > 0) {
41251        if (this.options.rtl) {
41252          min = getStart(minItem) - rhs * interval / delta; // ms
41253          max = getEnd(maxItem) + lhs * interval / delta; // ms
41254        } else {
41255          min = getStart(minItem) - lhs * interval / delta; // ms
41256          max = getEnd(maxItem) + rhs * interval / delta; // ms
41257        }
41258      }
41259    }
41260  }
41261
41262  return {
41263    min: min != null ? new Date(min) : null,
41264    max: max != null ? new Date(max) : null
41265  };
41266};
41267
41268/**
41269 * Calculate the data range of the items start and end dates
41270 * @returns {{min: Date, max: Date}}
41271 */
41272Timeline.prototype.getDataRange = function () {
41273  var min = null;
41274  var max = null;
41275
41276  var dataset = this.itemsData && this.itemsData.getDataSet();
41277  if (dataset) {
41278    dataset.forEach(function (item) {
41279      var start = util.convert(item.start, 'Date').valueOf();
41280      var end = util.convert(item.end != undefined ? item.end : item.start, 'Date').valueOf();
41281      if (min === null || start < min) {
41282        min = start;
41283      }
41284      if (max === null || end > max) {
41285        max = end;
41286      }
41287    });
41288  }
41289
41290  return {
41291    min: min != null ? new Date(min) : null,
41292    max: max != null ? new Date(max) : null
41293  };
41294};
41295
41296/**
41297 * Generate Timeline related information from an event
41298 * @param {Event} event
41299 * @return {Object} An object with related information, like on which area
41300 *                  The event happened, whether clicked on an item, etc.
41301 */
41302Timeline.prototype.getEventProperties = function (event) {
41303  var clientX = event.center ? event.center.x : event.clientX;
41304  var clientY = event.center ? event.center.y : event.clientY;
41305  var x;
41306  if (this.options.rtl) {
41307    x = util.getAbsoluteRight(this.dom.centerContainer) - clientX;
41308  } else {
41309    x = clientX - util.getAbsoluteLeft(this.dom.centerContainer);
41310  }
41311  var y = clientY - util.getAbsoluteTop(this.dom.centerContainer);
41312
41313  var item = this.itemSet.itemFromTarget(event);
41314  var group = this.itemSet.groupFromTarget(event);
41315  var customTime = CustomTime.customTimeFromTarget(event);
41316
41317  var snap = this.itemSet.options.snap || null;
41318  var scale = this.body.util.getScale();
41319  var step = this.body.util.getStep();
41320  var time = this._toTime(x);
41321  var snappedTime = snap ? snap(time, scale, step) : time;
41322
41323  var element = util.getTarget(event);
41324  var what = null;
41325  if (item != null) {
41326    what = 'item';
41327  } else if (customTime != null) {
41328    what = 'custom-time';
41329  } else if (util.hasParent(element, this.timeAxis.dom.foreground)) {
41330    what = 'axis';
41331  } else if (this.timeAxis2 && util.hasParent(element, this.timeAxis2.dom.foreground)) {
41332    what = 'axis';
41333  } else if (util.hasParent(element, this.itemSet.dom.labelSet)) {
41334    what = 'group-label';
41335  } else if (util.hasParent(element, this.currentTime.bar)) {
41336    what = 'current-time';
41337  } else if (util.hasParent(element, this.dom.center)) {
41338    what = 'background';
41339  }
41340
41341  return {
41342    event: event,
41343    item: item ? item.id : null,
41344    group: group ? group.groupId : null,
41345    what: what,
41346    pageX: event.srcEvent ? event.srcEvent.pageX : event.pageX,
41347    pageY: event.srcEvent ? event.srcEvent.pageY : event.pageY,
41348    x: x,
41349    y: y,
41350    time: time,
41351    snappedTime: snappedTime
41352  };
41353};
41354
41355/**
41356 * Toggle Timeline rolling mode
41357 */
41358
41359Timeline.prototype.toggleRollingMode = function () {
41360  if (this.range.rolling) {
41361    this.range.stopRolling();
41362  } else {
41363    if (this.options.rollingMode == undefined) {
41364      this.setOptions(this.options);
41365    }
41366    this.range.startRolling();
41367  }
41368};
41369
41370module.exports = Timeline;
41371
41372/***/ }),
41373/* 179 */
41374/***/ (function(module, exports, __webpack_require__) {
41375
41376"use strict";
41377
41378
41379Object.defineProperty(exports, "__esModule", {
41380  value: true
41381});
41382
41383var _stringify = __webpack_require__(19);
41384
41385var _stringify2 = _interopRequireDefault(_stringify);
41386
41387var _classCallCheck2 = __webpack_require__(0);
41388
41389var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
41390
41391var _createClass2 = __webpack_require__(1);
41392
41393var _createClass3 = _interopRequireDefault(_createClass2);
41394
41395function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
41396
41397var Hammer = __webpack_require__(10);
41398var hammerUtil = __webpack_require__(37);
41399var util = __webpack_require__(2);
41400
41401/**
41402 * @param {number} [pixelRatio=1]
41403 */
41404
41405var ColorPicker = function () {
41406  /**
41407   * @param {number} [pixelRatio=1]
41408   */
41409  function ColorPicker() {
41410    var pixelRatio = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 1;
41411    (0, _classCallCheck3['default'])(this, ColorPicker);
41412
41413    this.pixelRatio = pixelRatio;
41414    this.generated = false;
41415    this.centerCoordinates = { x: 289 / 2, y: 289 / 2 };
41416    this.r = 289 * 0.49;
41417    this.color = { r: 255, g: 255, b: 255, a: 1.0 };
41418    this.hueCircle = undefined;
41419    this.initialColor = { r: 255, g: 255, b: 255, a: 1.0 };
41420    this.previousColor = undefined;
41421    this.applied = false;
41422
41423    // bound by
41424    this.updateCallback = function () {};
41425    this.closeCallback = function () {};
41426
41427    // create all DOM elements
41428    this._create();
41429  }
41430
41431  /**
41432   * this inserts the colorPicker into a div from the DOM
41433   * @param {Element} container
41434   */
41435
41436
41437  (0, _createClass3['default'])(ColorPicker, [{
41438    key: 'insertTo',
41439    value: function insertTo(container) {
41440      if (this.hammer !== undefined) {
41441        this.hammer.destroy();
41442        this.hammer = undefined;
41443      }
41444      this.container = container;
41445      this.container.appendChild(this.frame);
41446      this._bindHammer();
41447
41448      this._setSize();
41449    }
41450
41451    /**
41452     * the callback is executed on apply and save. Bind it to the application
41453     * @param {function} callback
41454     */
41455
41456  }, {
41457    key: 'setUpdateCallback',
41458    value: function setUpdateCallback(callback) {
41459      if (typeof callback === 'function') {
41460        this.updateCallback = callback;
41461      } else {
41462        throw new Error("Function attempted to set as colorPicker update callback is not a function.");
41463      }
41464    }
41465
41466    /**
41467     * the callback is executed on apply and save. Bind it to the application
41468     * @param {function} callback
41469     */
41470
41471  }, {
41472    key: 'setCloseCallback',
41473    value: function setCloseCallback(callback) {
41474      if (typeof callback === 'function') {
41475        this.closeCallback = callback;
41476      } else {
41477        throw new Error("Function attempted to set as colorPicker closing callback is not a function.");
41478      }
41479    }
41480
41481    /**
41482     *
41483     * @param {string} color
41484     * @returns {String}
41485     * @private
41486     */
41487
41488  }, {
41489    key: '_isColorString',
41490    value: function _isColorString(color) {
41491      var htmlColors = { black: '#000000', navy: '#000080', darkblue: '#00008B', mediumblue: '#0000CD', blue: '#0000FF', darkgreen: '#006400', green: '#008000', teal: '#008080', darkcyan: '#008B8B', deepskyblue: '#00BFFF', darkturquoise: '#00CED1', mediumspringgreen: '#00FA9A', lime: '#00FF00', springgreen: '#00FF7F', aqua: '#00FFFF', cyan: '#00FFFF', midnightblue: '#191970', dodgerblue: '#1E90FF', lightseagreen: '#20B2AA', forestgreen: '#228B22', seagreen: '#2E8B57', darkslategray: '#2F4F4F', limegreen: '#32CD32', mediumseagreen: '#3CB371', turquoise: '#40E0D0', royalblue: '#4169E1', steelblue: '#4682B4', darkslateblue: '#483D8B', mediumturquoise: '#48D1CC', indigo: '#4B0082', darkolivegreen: '#556B2F', cadetblue: '#5F9EA0', cornflowerblue: '#6495ED', mediumaquamarine: '#66CDAA', dimgray: '#696969', slateblue: '#6A5ACD', olivedrab: '#6B8E23', slategray: '#708090', lightslategray: '#778899', mediumslateblue: '#7B68EE', lawngreen: '#7CFC00', chartreuse: '#7FFF00', aquamarine: '#7FFFD4', maroon: '#800000', purple: '#800080', olive: '#808000', gray: '#808080', skyblue: '#87CEEB', lightskyblue: '#87CEFA', blueviolet: '#8A2BE2', darkred: '#8B0000', darkmagenta: '#8B008B', saddlebrown: '#8B4513', darkseagreen: '#8FBC8F', lightgreen: '#90EE90', mediumpurple: '#9370D8', darkviolet: '#9400D3', palegreen: '#98FB98', darkorchid: '#9932CC', yellowgreen: '#9ACD32', sienna: '#A0522D', brown: '#A52A2A', darkgray: '#A9A9A9', lightblue: '#ADD8E6', greenyellow: '#ADFF2F', paleturquoise: '#AFEEEE', lightsteelblue: '#B0C4DE', powderblue: '#B0E0E6', firebrick: '#B22222', darkgoldenrod: '#B8860B', mediumorchid: '#BA55D3', rosybrown: '#BC8F8F', darkkhaki: '#BDB76B', silver: '#C0C0C0', mediumvioletred: '#C71585', indianred: '#CD5C5C', peru: '#CD853F', chocolate: '#D2691E', tan: '#D2B48C', lightgrey: '#D3D3D3', palevioletred: '#D87093', thistle: '#D8BFD8', orchid: '#DA70D6', goldenrod: '#DAA520', crimson: '#DC143C', gainsboro: '#DCDCDC', plum: '#DDA0DD', burlywood: '#DEB887', lightcyan: '#E0FFFF', lavender: '#E6E6FA', darksalmon: '#E9967A', violet: '#EE82EE', palegoldenrod: '#EEE8AA', lightcoral: '#F08080', khaki: '#F0E68C', aliceblue: '#F0F8FF', honeydew: '#F0FFF0', azure: '#F0FFFF', sandybrown: '#F4A460', wheat: '#F5DEB3', beige: '#F5F5DC', whitesmoke: '#F5F5F5', mintcream: '#F5FFFA', ghostwhite: '#F8F8FF', salmon: '#FA8072', antiquewhite: '#FAEBD7', linen: '#FAF0E6', lightgoldenrodyellow: '#FAFAD2', oldlace: '#FDF5E6', red: '#FF0000', fuchsia: '#FF00FF', magenta: '#FF00FF', deeppink: '#FF1493', orangered: '#FF4500', tomato: '#FF6347', hotpink: '#FF69B4', coral: '#FF7F50', darkorange: '#FF8C00', lightsalmon: '#FFA07A', orange: '#FFA500', lightpink: '#FFB6C1', pink: '#FFC0CB', gold: '#FFD700', peachpuff: '#FFDAB9', navajowhite: '#FFDEAD', moccasin: '#FFE4B5', bisque: '#FFE4C4', mistyrose: '#FFE4E1', blanchedalmond: '#FFEBCD', papayawhip: '#FFEFD5', lavenderblush: '#FFF0F5', seashell: '#FFF5EE', cornsilk: '#FFF8DC', lemonchiffon: '#FFFACD', floralwhite: '#FFFAF0', snow: '#FFFAFA', yellow: '#FFFF00', lightyellow: '#FFFFE0', ivory: '#FFFFF0', white: '#FFFFFF' };
41492      if (typeof color === 'string') {
41493        return htmlColors[color];
41494      }
41495    }
41496
41497    /**
41498     * Set the color of the colorPicker
41499     * Supported formats:
41500     * 'red'                   --> HTML color string
41501     * '#ffffff'               --> hex string
41502     * 'rbg(255,255,255)'      --> rgb string
41503     * 'rgba(255,255,255,1.0)' --> rgba string
41504     * {r:255,g:255,b:255}     --> rgb object
41505     * {r:255,g:255,b:255,a:1.0} --> rgba object
41506     * @param {string|Object} color
41507     * @param {boolean} [setInitial=true]
41508     */
41509
41510  }, {
41511    key: 'setColor',
41512    value: function setColor(color) {
41513      var setInitial = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
41514
41515      if (color === 'none') {
41516        return;
41517      }
41518
41519      var rgba = void 0;
41520
41521      // if a html color shorthand is used, convert to hex
41522      var htmlColor = this._isColorString(color);
41523      if (htmlColor !== undefined) {
41524        color = htmlColor;
41525      }
41526
41527      // check format
41528      if (util.isString(color) === true) {
41529        if (util.isValidRGB(color) === true) {
41530          var rgbaArray = color.substr(4).substr(0, color.length - 5).split(',');
41531          rgba = { r: rgbaArray[0], g: rgbaArray[1], b: rgbaArray[2], a: 1.0 };
41532        } else if (util.isValidRGBA(color) === true) {
41533          var _rgbaArray = color.substr(5).substr(0, color.length - 6).split(',');
41534          rgba = { r: _rgbaArray[0], g: _rgbaArray[1], b: _rgbaArray[2], a: _rgbaArray[3] };
41535        } else if (util.isValidHex(color) === true) {
41536          var rgbObj = util.hexToRGB(color);
41537          rgba = { r: rgbObj.r, g: rgbObj.g, b: rgbObj.b, a: 1.0 };
41538        }
41539      } else {
41540        if (color instanceof Object) {
41541          if (color.r !== undefined && color.g !== undefined && color.b !== undefined) {
41542            var alpha = color.a !== undefined ? color.a : '1.0';
41543            rgba = { r: color.r, g: color.g, b: color.b, a: alpha };
41544          }
41545        }
41546      }
41547
41548      // set color
41549      if (rgba === undefined) {
41550        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: " + (0, _stringify2['default'])(color));
41551      } else {
41552        this._setColor(rgba, setInitial);
41553      }
41554    }
41555
41556    /**
41557     * this shows the color picker.
41558     * The hue circle is constructed once and stored.
41559     */
41560
41561  }, {
41562    key: 'show',
41563    value: function show() {
41564      if (this.closeCallback !== undefined) {
41565        this.closeCallback();
41566        this.closeCallback = undefined;
41567      }
41568
41569      this.applied = false;
41570      this.frame.style.display = 'block';
41571      this._generateHueCircle();
41572    }
41573
41574    // ------------------------------------------ PRIVATE ----------------------------- //
41575
41576    /**
41577     * Hide the picker. Is called by the cancel button.
41578     * Optional boolean to store the previous color for easy access later on.
41579     * @param {boolean} [storePrevious=true]
41580     * @private
41581     */
41582
41583  }, {
41584    key: '_hide',
41585    value: function _hide() {
41586      var _this = this;
41587
41588      var storePrevious = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
41589
41590      // store the previous color for next time;
41591      if (storePrevious === true) {
41592        this.previousColor = util.extend({}, this.color);
41593      }
41594
41595      if (this.applied === true) {
41596        this.updateCallback(this.initialColor);
41597      }
41598
41599      this.frame.style.display = 'none';
41600
41601      // call the closing callback, restoring the onclick method.
41602      // this is in a setTimeout because it will trigger the show again before the click is done.
41603      setTimeout(function () {
41604        if (_this.closeCallback !== undefined) {
41605          _this.closeCallback();
41606          _this.closeCallback = undefined;
41607        }
41608      }, 0);
41609    }
41610
41611    /**
41612     * bound to the save button. Saves and hides.
41613     * @private
41614     */
41615
41616  }, {
41617    key: '_save',
41618    value: function _save() {
41619      this.updateCallback(this.color);
41620      this.applied = false;
41621      this._hide();
41622    }
41623
41624    /**
41625     * Bound to apply button. Saves but does not close. Is undone by the cancel button.
41626     * @private
41627     */
41628
41629  }, {
41630    key: '_apply',
41631    value: function _apply() {
41632      this.applied = true;
41633      this.updateCallback(this.color);
41634      this._updatePicker(this.color);
41635    }
41636
41637    /**
41638     * load the color from the previous session.
41639     * @private
41640     */
41641
41642  }, {
41643    key: '_loadLast',
41644    value: function _loadLast() {
41645      if (this.previousColor !== undefined) {
41646        this.setColor(this.previousColor, false);
41647      } else {
41648        alert("There is no last color to load...");
41649      }
41650    }
41651
41652    /**
41653     * set the color, place the picker
41654     * @param {Object} rgba
41655     * @param {boolean} [setInitial=true]
41656     * @private
41657     */
41658
41659  }, {
41660    key: '_setColor',
41661    value: function _setColor(rgba) {
41662      var setInitial = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
41663
41664      // store the initial color
41665      if (setInitial === true) {
41666        this.initialColor = util.extend({}, rgba);
41667      }
41668
41669      this.color = rgba;
41670      var hsv = util.RGBToHSV(rgba.r, rgba.g, rgba.b);
41671
41672      var angleConvert = 2 * Math.PI;
41673      var radius = this.r * hsv.s;
41674      var x = this.centerCoordinates.x + radius * Math.sin(angleConvert * hsv.h);
41675      var y = this.centerCoordinates.y + radius * Math.cos(angleConvert * hsv.h);
41676
41677      this.colorPickerSelector.style.left = x - 0.5 * this.colorPickerSelector.clientWidth + 'px';
41678      this.colorPickerSelector.style.top = y - 0.5 * this.colorPickerSelector.clientHeight + 'px';
41679
41680      this._updatePicker(rgba);
41681    }
41682
41683    /**
41684     * bound to opacity control
41685     * @param {number} value
41686     * @private
41687     */
41688
41689  }, {
41690    key: '_setOpacity',
41691    value: function _setOpacity(value) {
41692      this.color.a = value / 100;
41693      this._updatePicker(this.color);
41694    }
41695
41696    /**
41697     * bound to brightness control
41698     * @param {number} value
41699     * @private
41700     */
41701
41702  }, {
41703    key: '_setBrightness',
41704    value: function _setBrightness(value) {
41705      var hsv = util.RGBToHSV(this.color.r, this.color.g, this.color.b);
41706      hsv.v = value / 100;
41707      var rgba = util.HSVToRGB(hsv.h, hsv.s, hsv.v);
41708      rgba['a'] = this.color.a;
41709      this.color = rgba;
41710      this._updatePicker();
41711    }
41712
41713    /**
41714     * update the color picker. A black circle overlays the hue circle to mimic the brightness decreasing.
41715     * @param {Object} rgba
41716     * @private
41717     */
41718
41719  }, {
41720    key: '_updatePicker',
41721    value: function _updatePicker() {
41722      var rgba = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : this.color;
41723
41724      var hsv = util.RGBToHSV(rgba.r, rgba.g, rgba.b);
41725      var ctx = this.colorPickerCanvas.getContext('2d');
41726      if (this.pixelRation === undefined) {
41727        this.pixelRatio = (window.devicePixelRatio || 1) / (ctx.webkitBackingStorePixelRatio || ctx.mozBackingStorePixelRatio || ctx.msBackingStorePixelRatio || ctx.oBackingStorePixelRatio || ctx.backingStorePixelRatio || 1);
41728      }
41729      ctx.setTransform(this.pixelRatio, 0, 0, this.pixelRatio, 0, 0);
41730
41731      // clear the canvas
41732      var w = this.colorPickerCanvas.clientWidth;
41733      var h = this.colorPickerCanvas.clientHeight;
41734      ctx.clearRect(0, 0, w, h);
41735
41736      ctx.putImageData(this.hueCircle, 0, 0);
41737      ctx.fillStyle = 'rgba(0,0,0,' + (1 - hsv.v) + ')';
41738      ctx.circle(this.centerCoordinates.x, this.centerCoordinates.y, this.r);
41739      ctx.fill();
41740
41741      this.brightnessRange.value = 100 * hsv.v;
41742      this.opacityRange.value = 100 * rgba.a;
41743
41744      this.initialColorDiv.style.backgroundColor = 'rgba(' + this.initialColor.r + ',' + this.initialColor.g + ',' + this.initialColor.b + ',' + this.initialColor.a + ')';
41745      this.newColorDiv.style.backgroundColor = 'rgba(' + this.color.r + ',' + this.color.g + ',' + this.color.b + ',' + this.color.a + ')';
41746    }
41747
41748    /**
41749     * used by create to set the size of the canvas.
41750     * @private
41751     */
41752
41753  }, {
41754    key: '_setSize',
41755    value: function _setSize() {
41756      this.colorPickerCanvas.style.width = '100%';
41757      this.colorPickerCanvas.style.height = '100%';
41758
41759      this.colorPickerCanvas.width = 289 * this.pixelRatio;
41760      this.colorPickerCanvas.height = 289 * this.pixelRatio;
41761    }
41762
41763    /**
41764     * create all dom elements
41765     * TODO: cleanup, lots of similar dom elements
41766     * @private
41767     */
41768
41769  }, {
41770    key: '_create',
41771    value: function _create() {
41772      this.frame = document.createElement('div');
41773      this.frame.className = 'vis-color-picker';
41774
41775      this.colorPickerDiv = document.createElement('div');
41776      this.colorPickerSelector = document.createElement('div');
41777      this.colorPickerSelector.className = 'vis-selector';
41778      this.colorPickerDiv.appendChild(this.colorPickerSelector);
41779
41780      this.colorPickerCanvas = document.createElement('canvas');
41781      this.colorPickerDiv.appendChild(this.colorPickerCanvas);
41782
41783      if (!this.colorPickerCanvas.getContext) {
41784        var noCanvas = document.createElement('DIV');
41785        noCanvas.style.color = 'red';
41786        noCanvas.style.fontWeight = 'bold';
41787        noCanvas.style.padding = '10px';
41788        noCanvas.innerHTML = 'Error: your browser does not support HTML canvas';
41789        this.colorPickerCanvas.appendChild(noCanvas);
41790      } else {
41791        var ctx = this.colorPickerCanvas.getContext("2d");
41792        this.pixelRatio = (window.devicePixelRatio || 1) / (ctx.webkitBackingStorePixelRatio || ctx.mozBackingStorePixelRatio || ctx.msBackingStorePixelRatio || ctx.oBackingStorePixelRatio || ctx.backingStorePixelRatio || 1);
41793
41794        this.colorPickerCanvas.getContext("2d").setTransform(this.pixelRatio, 0, 0, this.pixelRatio, 0, 0);
41795      }
41796
41797      this.colorPickerDiv.className = 'vis-color';
41798
41799      this.opacityDiv = document.createElement('div');
41800      this.opacityDiv.className = 'vis-opacity';
41801
41802      this.brightnessDiv = document.createElement('div');
41803      this.brightnessDiv.className = 'vis-brightness';
41804
41805      this.arrowDiv = document.createElement('div');
41806      this.arrowDiv.className = 'vis-arrow';
41807
41808      this.opacityRange = document.createElement('input');
41809      try {
41810        this.opacityRange.type = 'range'; // Not supported on IE9
41811        this.opacityRange.min = '0';
41812        this.opacityRange.max = '100';
41813      }
41814      // TODO: Add some error handling and remove this lint exception
41815      catch (err) {} // eslint-disable-line no-empty
41816      this.opacityRange.value = '100';
41817      this.opacityRange.className = 'vis-range';
41818
41819      this.brightnessRange = document.createElement('input');
41820      try {
41821        this.brightnessRange.type = 'range'; // Not supported on IE9
41822        this.brightnessRange.min = '0';
41823        this.brightnessRange.max = '100';
41824      }
41825      // TODO: Add some error handling and remove this lint exception
41826      catch (err) {} // eslint-disable-line no-empty
41827      this.brightnessRange.value = '100';
41828      this.brightnessRange.className = 'vis-range';
41829
41830      this.opacityDiv.appendChild(this.opacityRange);
41831      this.brightnessDiv.appendChild(this.brightnessRange);
41832
41833      var me = this;
41834      this.opacityRange.onchange = function () {
41835        me._setOpacity(this.value);
41836      };
41837      this.opacityRange.oninput = function () {
41838        me._setOpacity(this.value);
41839      };
41840      this.brightnessRange.onchange = function () {
41841        me._setBrightness(this.value);
41842      };
41843      this.brightnessRange.oninput = function () {
41844        me._setBrightness(this.value);
41845      };
41846
41847      this.brightnessLabel = document.createElement("div");
41848      this.brightnessLabel.className = "vis-label vis-brightness";
41849      this.brightnessLabel.innerHTML = 'brightness:';
41850
41851      this.opacityLabel = document.createElement("div");
41852      this.opacityLabel.className = "vis-label vis-opacity";
41853      this.opacityLabel.innerHTML = 'opacity:';
41854
41855      this.newColorDiv = document.createElement("div");
41856      this.newColorDiv.className = "vis-new-color";
41857      this.newColorDiv.innerHTML = 'new';
41858
41859      this.initialColorDiv = document.createElement("div");
41860      this.initialColorDiv.className = "vis-initial-color";
41861      this.initialColorDiv.innerHTML = 'initial';
41862
41863      this.cancelButton = document.createElement("div");
41864      this.cancelButton.className = "vis-button vis-cancel";
41865      this.cancelButton.innerHTML = 'cancel';
41866      this.cancelButton.onclick = this._hide.bind(this, false);
41867
41868      this.applyButton = document.createElement("div");
41869      this.applyButton.className = "vis-button vis-apply";
41870      this.applyButton.innerHTML = 'apply';
41871      this.applyButton.onclick = this._apply.bind(this);
41872
41873      this.saveButton = document.createElement("div");
41874      this.saveButton.className = "vis-button vis-save";
41875      this.saveButton.innerHTML = 'save';
41876      this.saveButton.onclick = this._save.bind(this);
41877
41878      this.loadButton = document.createElement("div");
41879      this.loadButton.className = "vis-button vis-load";
41880      this.loadButton.innerHTML = 'load last';
41881      this.loadButton.onclick = this._loadLast.bind(this);
41882
41883      this.frame.appendChild(this.colorPickerDiv);
41884      this.frame.appendChild(this.arrowDiv);
41885      this.frame.appendChild(this.brightnessLabel);
41886      this.frame.appendChild(this.brightnessDiv);
41887      this.frame.appendChild(this.opacityLabel);
41888      this.frame.appendChild(this.opacityDiv);
41889      this.frame.appendChild(this.newColorDiv);
41890      this.frame.appendChild(this.initialColorDiv);
41891
41892      this.frame.appendChild(this.cancelButton);
41893      this.frame.appendChild(this.applyButton);
41894      this.frame.appendChild(this.saveButton);
41895      this.frame.appendChild(this.loadButton);
41896    }
41897
41898    /**
41899     * bind hammer to the color picker
41900     * @private
41901     */
41902
41903  }, {
41904    key: '_bindHammer',
41905    value: function _bindHammer() {
41906      var _this2 = this;
41907
41908      this.drag = {};
41909      this.pinch = {};
41910      this.hammer = new Hammer(this.colorPickerCanvas);
41911      this.hammer.get('pinch').set({ enable: true });
41912
41913      hammerUtil.onTouch(this.hammer, function (event) {
41914        _this2._moveSelector(event);
41915      });
41916      this.hammer.on('tap', function (event) {
41917        _this2._moveSelector(event);
41918      });
41919      this.hammer.on('panstart', function (event) {
41920        _this2._moveSelector(event);
41921      });
41922      this.hammer.on('panmove', function (event) {
41923        _this2._moveSelector(event);
41924      });
41925      this.hammer.on('panend', function (event) {
41926        _this2._moveSelector(event);
41927      });
41928    }
41929
41930    /**
41931     * generate the hue circle. This is relatively heavy (200ms) and is done only once on the first time it is shown.
41932     * @private
41933     */
41934
41935  }, {
41936    key: '_generateHueCircle',
41937    value: function _generateHueCircle() {
41938      if (this.generated === false) {
41939        var ctx = this.colorPickerCanvas.getContext('2d');
41940        if (this.pixelRation === undefined) {
41941          this.pixelRatio = (window.devicePixelRatio || 1) / (ctx.webkitBackingStorePixelRatio || ctx.mozBackingStorePixelRatio || ctx.msBackingStorePixelRatio || ctx.oBackingStorePixelRatio || ctx.backingStorePixelRatio || 1);
41942        }
41943        ctx.setTransform(this.pixelRatio, 0, 0, this.pixelRatio, 0, 0);
41944
41945        // clear the canvas
41946        var w = this.colorPickerCanvas.clientWidth;
41947        var h = this.colorPickerCanvas.clientHeight;
41948        ctx.clearRect(0, 0, w, h);
41949
41950        // draw hue circle
41951        var x = void 0,
41952            y = void 0,
41953            hue = void 0,
41954            sat = void 0;
41955        this.centerCoordinates = { x: w * 0.5, y: h * 0.5 };
41956        this.r = 0.49 * w;
41957        var angleConvert = 2 * Math.PI / 360;
41958        var hfac = 1 / 360;
41959        var sfac = 1 / this.r;
41960        var rgb = void 0;
41961        for (hue = 0; hue < 360; hue++) {
41962          for (sat = 0; sat < this.r; sat++) {
41963            x = this.centerCoordinates.x + sat * Math.sin(angleConvert * hue);
41964            y = this.centerCoordinates.y + sat * Math.cos(angleConvert * hue);
41965            rgb = util.HSVToRGB(hue * hfac, sat * sfac, 1);
41966            ctx.fillStyle = 'rgb(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ')';
41967            ctx.fillRect(x - 0.5, y - 0.5, 2, 2);
41968          }
41969        }
41970        ctx.strokeStyle = 'rgba(0,0,0,1)';
41971        ctx.circle(this.centerCoordinates.x, this.centerCoordinates.y, this.r);
41972        ctx.stroke();
41973
41974        this.hueCircle = ctx.getImageData(0, 0, w, h);
41975      }
41976      this.generated = true;
41977    }
41978
41979    /**
41980     * move the selector. This is called by hammer functions.
41981     *
41982     * @param {Event}  event   The event
41983     * @private
41984     */
41985
41986  }, {
41987    key: '_moveSelector',
41988    value: function _moveSelector(event) {
41989      var rect = this.colorPickerDiv.getBoundingClientRect();
41990      var left = event.center.x - rect.left;
41991      var top = event.center.y - rect.top;
41992
41993      var centerY = 0.5 * this.colorPickerDiv.clientHeight;
41994      var centerX = 0.5 * this.colorPickerDiv.clientWidth;
41995
41996      var x = left - centerX;
41997      var y = top - centerY;
41998
41999      var angle = Math.atan2(x, y);
42000      var radius = 0.98 * Math.min(Math.sqrt(x * x + y * y), centerX);
42001
42002      var newTop = Math.cos(angle) * radius + centerY;
42003      var newLeft = Math.sin(angle) * radius + centerX;
42004
42005      this.colorPickerSelector.style.top = newTop - 0.5 * this.colorPickerSelector.clientHeight + 'px';
42006      this.colorPickerSelector.style.left = newLeft - 0.5 * this.colorPickerSelector.clientWidth + 'px';
42007
42008      // set color
42009      var h = angle / (2 * Math.PI);
42010      h = h < 0 ? h + 1 : h;
42011      var s = radius / this.r;
42012      var hsv = util.RGBToHSV(this.color.r, this.color.g, this.color.b);
42013      hsv.h = h;
42014      hsv.s = s;
42015      var rgba = util.HSVToRGB(hsv.h, hsv.s, hsv.v);
42016      rgba['a'] = this.color.a;
42017      this.color = rgba;
42018
42019      // update previews
42020      this.initialColorDiv.style.backgroundColor = 'rgba(' + this.initialColor.r + ',' + this.initialColor.g + ',' + this.initialColor.b + ',' + this.initialColor.a + ')';
42021      this.newColorDiv.style.backgroundColor = 'rgba(' + this.color.r + ',' + this.color.g + ',' + this.color.b + ',' + this.color.a + ')';
42022    }
42023  }]);
42024  return ColorPicker;
42025}();
42026
42027exports['default'] = ColorPicker;
42028
42029/***/ }),
42030/* 180 */
42031/***/ (function(module, exports, __webpack_require__) {
42032
42033"use strict";
42034
42035
42036var moment = __webpack_require__(9);
42037var util = __webpack_require__(2);
42038var DataSet = __webpack_require__(11);
42039var DataView = __webpack_require__(12);
42040var Range = __webpack_require__(64);
42041var Core = __webpack_require__(65);
42042var TimeAxis = __webpack_require__(45);
42043var CurrentTime = __webpack_require__(67);
42044var CustomTime = __webpack_require__(46);
42045var LineGraph = __webpack_require__(106);
42046
42047var printStyle = __webpack_require__(15).printStyle;
42048var allOptions = __webpack_require__(113).allOptions;
42049var configureOptions = __webpack_require__(113).configureOptions;
42050
42051var Configurator = __webpack_require__(71)['default'];
42052var Validator = __webpack_require__(15)['default'];
42053
42054/**
42055 * Create a timeline visualization
42056 * @param {HTMLElement} container
42057 * @param {vis.DataSet | Array} [items]
42058 * @param {vis.DataSet | Array | vis.DataView | Object} [groups]
42059 * @param {Object} [options]  See Graph2d.setOptions for the available options.
42060 * @constructor Graph2d
42061 * @extends Core
42062 */
42063function Graph2d(container, items, groups, options) {
42064  // if the third element is options, the forth is groups (optionally);
42065  if (!(Array.isArray(groups) || groups instanceof DataSet || groups instanceof DataView) && groups instanceof Object) {
42066    var forthArgument = options;
42067    options = groups;
42068    groups = forthArgument;
42069  }
42070
42071  // TODO: REMOVE THIS in the next MAJOR release
42072  // see https://github.com/almende/vis/issues/2511
42073  if (options && options.throttleRedraw) {
42074    console.warn("Graph2d option \"throttleRedraw\" is DEPRICATED and no longer supported. It will be removed in the next MAJOR release.");
42075  }
42076
42077  var me = this;
42078  this.defaultOptions = {
42079    start: null,
42080    end: null,
42081
42082    autoResize: true,
42083
42084    orientation: {
42085      axis: 'bottom', // axis orientation: 'bottom', 'top', or 'both'
42086      item: 'bottom' // not relevant for Graph2d
42087    },
42088
42089    moment: moment,
42090
42091    width: null,
42092    height: null,
42093    maxHeight: null,
42094    minHeight: null
42095  };
42096  this.options = util.deepExtend({}, this.defaultOptions);
42097
42098  // Create the DOM, props, and emitter
42099  this._create(container);
42100
42101  // all components listed here will be repainted automatically
42102  this.components = [];
42103
42104  this.body = {
42105    dom: this.dom,
42106    domProps: this.props,
42107    emitter: {
42108      on: this.on.bind(this),
42109      off: this.off.bind(this),
42110      emit: this.emit.bind(this)
42111    },
42112    hiddenDates: [],
42113    util: {
42114      toScreen: me._toScreen.bind(me),
42115      toGlobalScreen: me._toGlobalScreen.bind(me), // this refers to the root.width
42116      toTime: me._toTime.bind(me),
42117      toGlobalTime: me._toGlobalTime.bind(me)
42118    }
42119  };
42120
42121  // range
42122  this.range = new Range(this.body);
42123  this.components.push(this.range);
42124  this.body.range = this.range;
42125
42126  // time axis
42127  this.timeAxis = new TimeAxis(this.body);
42128  this.components.push(this.timeAxis);
42129  //this.body.util.snap = this.timeAxis.snap.bind(this.timeAxis);
42130
42131  // current time bar
42132  this.currentTime = new CurrentTime(this.body);
42133  this.components.push(this.currentTime);
42134
42135  // item set
42136  this.linegraph = new LineGraph(this.body);
42137
42138  this.components.push(this.linegraph);
42139
42140  this.itemsData = null; // DataSet
42141  this.groupsData = null; // DataSet
42142
42143
42144  this.on('tap', function (event) {
42145    me.emit('click', me.getEventProperties(event));
42146  });
42147  this.on('doubletap', function (event) {
42148    me.emit('doubleClick', me.getEventProperties(event));
42149  });
42150  this.dom.root.oncontextmenu = function (event) {
42151    me.emit('contextmenu', me.getEventProperties(event));
42152  };
42153
42154  // apply options
42155  if (options) {
42156    this.setOptions(options);
42157  }
42158
42159  // IMPORTANT: THIS HAPPENS BEFORE SET ITEMS!
42160  if (groups) {
42161    this.setGroups(groups);
42162  }
42163
42164  // create itemset
42165  if (items) {
42166    this.setItems(items);
42167  }
42168
42169  // draw for the first time
42170  this._redraw();
42171}
42172
42173// Extend the functionality from Core
42174Graph2d.prototype = new Core();
42175
42176Graph2d.prototype.setOptions = function (options) {
42177  // validate options
42178  var errorFound = Validator.validate(options, allOptions);
42179  if (errorFound === true) {
42180    console.log('%cErrors have been found in the supplied options object.', printStyle);
42181  }
42182
42183  Core.prototype.setOptions.call(this, options);
42184};
42185
42186/**
42187 * Set items
42188 * @param {vis.DataSet | Array | null} items
42189 */
42190Graph2d.prototype.setItems = function (items) {
42191  var initialLoad = this.itemsData == null;
42192
42193  // convert to type DataSet when needed
42194  var newDataSet;
42195  if (!items) {
42196    newDataSet = null;
42197  } else if (items instanceof DataSet || items instanceof DataView) {
42198    newDataSet = items;
42199  } else {
42200    // turn an array into a dataset
42201    newDataSet = new DataSet(items, {
42202      type: {
42203        start: 'Date',
42204        end: 'Date'
42205      }
42206    });
42207  }
42208
42209  // set items
42210  this.itemsData = newDataSet;
42211  this.linegraph && this.linegraph.setItems(newDataSet);
42212
42213  if (initialLoad) {
42214    if (this.options.start != undefined || this.options.end != undefined) {
42215      var start = this.options.start != undefined ? this.options.start : null;
42216      var end = this.options.end != undefined ? this.options.end : null;
42217      this.setWindow(start, end, { animation: false });
42218    } else {
42219      this.fit({ animation: false });
42220    }
42221  }
42222};
42223
42224/**
42225 * Set groups
42226 * @param {vis.DataSet | Array} groups
42227 */
42228Graph2d.prototype.setGroups = function (groups) {
42229  // convert to type DataSet when needed
42230  var newDataSet;
42231  if (!groups) {
42232    newDataSet = null;
42233  } else if (groups instanceof DataSet || groups instanceof DataView) {
42234    newDataSet = groups;
42235  } else {
42236    // turn an array into a dataset
42237    newDataSet = new DataSet(groups);
42238  }
42239
42240  this.groupsData = newDataSet;
42241  this.linegraph.setGroups(newDataSet);
42242};
42243
42244/**
vendor: 4,944 bytes, lines 42245-42395
42245 * 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).
42246 * @param {vis.GraphGroup.id} groupId
42247 * @param {number} width
42248 * @param {number} height
42249 * @returns {{icon: SVGElement, label: string, orientation: string}|string}
42250 */
42251Graph2d.prototype.getLegend = function (groupId, width, height) {
42252  if (width === undefined) {
42253    width = 15;
42254  }
42255  if (height === undefined) {
42256    height = 15;
42257  }
42258  if (this.linegraph.groups[groupId] !== undefined) {
42259    return this.linegraph.groups[groupId].getLegend(width, height);
42260  } else {
42261    return "cannot find group:'" + groupId + "'";
42262  }
42263};
42264
42265/**
42266 * This checks if the visible option of the supplied group (by ID) is true or false.
42267 * @param {vis.GraphGroup.id} groupId
42268 * @returns {boolean}
42269 */
42270Graph2d.prototype.isGroupVisible = function (groupId) {
42271  if (this.linegraph.groups[groupId] !== undefined) {
42272    return this.linegraph.groups[groupId].visible && (this.linegraph.options.groups.visibility[groupId] === undefined || this.linegraph.options.groups.visibility[groupId] == true);
42273  } else {
42274    return false;
42275  }
42276};
42277
42278/**
42279 * Get the data range of the item set.
42280 * @returns {{min: Date, max: Date}} range  A range with a start and end Date.
42281 *                                          When no minimum is found, min==null
42282 *                                          When no maximum is found, max==null
42283 */
42284Graph2d.prototype.getDataRange = function () {
42285  var min = null;
42286  var max = null;
42287
42288  // calculate min from start filed
42289  for (var groupId in this.linegraph.groups) {
42290    if (this.linegraph.groups.hasOwnProperty(groupId)) {
42291      if (this.linegraph.groups[groupId].visible == true) {
42292        for (var i = 0; i < this.linegraph.groups[groupId].itemsData.length; i++) {
42293          var item = this.linegraph.groups[groupId].itemsData[i];
42294          var value = util.convert(item.x, 'Date').valueOf();
42295          min = min == null ? value : min > value ? value : min;
42296          max = max == null ? value : max < value ? value : max;
42297        }
42298      }
42299    }
42300  }
42301
42302  return {
42303    min: min != null ? new Date(min) : null,
42304    max: max != null ? new Date(max) : null
42305  };
42306};
42307
42308/**
42309 * Generate Timeline related information from an event
42310 * @param {Event} event
42311 * @return {Object} An object with related information, like on which area
42312 *                  The event happened, whether clicked on an item, etc.
42313 */
42314Graph2d.prototype.getEventProperties = function (event) {
42315  var clientX = event.center ? event.center.x : event.clientX;
42316  var clientY = event.center ? event.center.y : event.clientY;
42317  var x = clientX - util.getAbsoluteLeft(this.dom.centerContainer);
42318  var y = clientY - util.getAbsoluteTop(this.dom.centerContainer);
42319  var time = this._toTime(x);
42320
42321  var customTime = CustomTime.customTimeFromTarget(event);
42322
42323  var element = util.getTarget(event);
42324  var what = null;
42325  if (util.hasParent(element, this.timeAxis.dom.foreground)) {
42326    what = 'axis';
42327  } else if (this.timeAxis2 && util.hasParent(element, this.timeAxis2.dom.foreground)) {
42328    what = 'axis';
42329  } else if (util.hasParent(element, this.linegraph.yAxisLeft.dom.frame)) {
42330    what = 'data-axis';
42331  } else if (util.hasParent(element, this.linegraph.yAxisRight.dom.frame)) {
42332    what = 'data-axis';
42333  } else if (util.hasParent(element, this.linegraph.legendLeft.dom.frame)) {
42334    what = 'legend';
42335  } else if (util.hasParent(element, this.linegraph.legendRight.dom.frame)) {
42336    what = 'legend';
42337  } else if (customTime != null) {
42338    what = 'custom-time';
42339  } else if (util.hasParent(element, this.currentTime.bar)) {
42340    what = 'current-time';
42341  } else if (util.hasParent(element, this.dom.center)) {
42342    what = 'background';
42343  }
42344
42345  var value = [];
42346  var yAxisLeft = this.linegraph.yAxisLeft;
42347  var yAxisRight = this.linegraph.yAxisRight;
42348  if (!yAxisLeft.hidden && this.itemsData.length > 0) {
42349    value.push(yAxisLeft.screenToValue(y));
42350  }
42351  if (!yAxisRight.hidden && this.itemsData.length > 0) {
42352    value.push(yAxisRight.screenToValue(y));
42353  }
42354
42355  return {
42356    event: event,
42357    what: what,
42358    pageX: event.srcEvent ? event.srcEvent.pageX : event.pageX,
42359    pageY: event.srcEvent ? event.srcEvent.pageY : event.pageY,
42360    x: x,
42361    y: y,
42362    time: time,
42363    value: value
42364  };
42365};
42366
42367/**
42368 * Load a configurator
42369 * @return {Object}
42370 * @private
42371 */
42372Graph2d.prototype._createConfigurator = function () {
42373  return new Configurator(this, this.dom.container, configureOptions);
42374};
42375
42376module.exports = Graph2d;
42377
42378/***/ }),
42379/* 181 */
42380/***/ (function(module, exports, __webpack_require__) {
42381
42382"use strict";
42383
42384
42385// utils
42386exports.util = __webpack_require__(2);
42387exports.DOMutil = __webpack_require__(14);
42388
42389// data
42390exports.DataSet = __webpack_require__(11);
42391exports.DataView = __webpack_require__(12);
42392exports.Queue = __webpack_require__(43);
42393
42394// Network
42395exports.Network = __webpack_require__(182);
42396exports.network = {
42397  Images: __webpack_require__(116),
42398  dotparser: __webpack_require__(114),
42399  gephiParser: __webpack_require__(115),
42400  allOptions: __webpack_require__(122)
42401};
42402exports.network.convertDot = function (input) {
42403  return exports.network.dotparser.DOTToGraph(input);
42404};
42405exports.network.convertGephi = function (input, options) {
42406  return exports.network.gephiParser.parseGephi(input, options);
42407};
42408
42409// bundled external libraries
42410exports.moment = __webpack_require__(9);
42411exports.Hammer = __webpack_require__(10);
42412exports.keycharm = __webpack_require__(35);
42413
42414/***/ }),
42415/* 182 */
42416/***/ (function(module, exports, __webpack_require__) {
42417
42418"use strict";
42419
42420
42421// Load custom shapes into CanvasRenderingContext2D
42422__webpack_require__(183);
42423
42424var Emitter = __webpack_require__(44);
42425var util = __webpack_require__(2);
42426var dotparser = __webpack_require__(114);
42427var gephiParser = __webpack_require__(115);
42428var Activator = __webpack_require__(97);
42429var locales = __webpack_require__(184);
42430
42431var Images = __webpack_require__(116)['default'];
42432var Groups = __webpack_require__(186)['default'];
42433var NodesHandler = __webpack_require__(187)['default'];
42434var EdgesHandler = __webpack_require__(214)['default'];
42435var PhysicsEngine = __webpack_require__(220)['default'];
42436var ClusterEngine = __webpack_require__(227)['default'];
42437var CanvasRenderer = __webpack_require__(229)['default'];
42438var Canvas = __webpack_require__(230)['default'];
42439var View = __webpack_require__(231)['default'];
42440var InteractionHandler = __webpack_require__(232)['default'];
42441var SelectionHandler = __webpack_require__(234)['default'];
42442var LayoutEngine = __webpack_require__(235)['default'];
42443var ManipulationSystem = __webpack_require__(237)['default'];
42444var Configurator = __webpack_require__(71)['default'];
42445var Validator = __webpack_require__(15)['default'];
42446
42447var _require = __webpack_require__(15),
42448    printStyle = _require.printStyle;
42449
42450var _require2 = __webpack_require__(122),
42451    allOptions = _require2.allOptions,
42452    configureOptions = _require2.configureOptions;
42453
42454var KamadaKawai = __webpack_require__(238)['default'];
42455
42456/**
42457 * Create a network visualization, displaying nodes and edges.
42458 *
42459 * @param {Element} container   The DOM element in which the Network will
42460 *                                  be created. Normally a div element.
42461 * @param {Object} data         An object containing parameters
42462 *                              {Array} nodes
42463 *                              {Array} edges
42464 * @param {Object} options      Options
42465 * @constructor Network
42466 */
42467function Network(container, data, options) {
42468  var _this = this;
42469
42470  if (!(this instanceof Network)) {
42471    throw new SyntaxError('Constructor must be called with the new operator');
42472  }
42473
42474  // set constant values
42475  this.options = {};
42476  this.defaultOptions = {
42477    locale: 'en',
42478    locales: locales,
42479    clickToUse: false
42480  };
42481  util.extend(this.options, this.defaultOptions);
42482
42483  /**
42484   * Containers for nodes and edges.
42485   *
42486   * 'edges' and 'nodes' contain the full definitions of all the network elements.
42487   * 'nodeIndices' and 'edgeIndices' contain the id's of the active elements.
42488   *
42489   * The distinction is important, because a defined node need not be active, i.e.
42490   * visible on the canvas. This happens in particular when clusters are defined, in
42491   * that case there will be nodes and edges not displayed.
42492   * The bottom line is that all code with actions related to visibility, *must* use
42493   * 'nodeIndices' and 'edgeIndices', not 'nodes' and 'edges' directly.
42494   */
42495  this.body = {
42496    container: container,
42497
42498    // See comment above for following fields
42499    nodes: {},
42500    nodeIndices: [],
42501    edges: {},
42502    edgeIndices: [],
42503
42504    emitter: {
42505      on: this.on.bind(this),
42506      off: this.off.bind(this),
42507      emit: this.emit.bind(this),
42508      once: this.once.bind(this)
42509    },
42510    eventListeners: {
42511      onTap: function onTap() {},
42512      onTouch: function onTouch() {},
42513      onDoubleTap: function onDoubleTap() {},
42514      onHold: function onHold() {},
42515      onDragStart: function onDragStart() {},
42516      onDrag: function onDrag() {},
42517      onDragEnd: function onDragEnd() {},
42518      onMouseWheel: function onMouseWheel() {},
42519      onPinch: function onPinch() {},
42520      onMouseMove: function onMouseMove() {},
42521      onRelease: function onRelease() {},
42522      onContext: function onContext() {}
42523    },
42524    data: {
42525      nodes: null, // A DataSet or DataView
42526      edges: null // A DataSet or DataView
42527    },
42528    functions: {
42529      createNode: function createNode() {},
42530      createEdge: function createEdge() {},
42531      getPointer: function getPointer() {}
42532    },
42533    modules: {},
42534    view: {
42535      scale: 1,
42536      translation: { x: 0, y: 0 }
42537    }
42538  };
42539
42540  // bind the event listeners
42541  this.bindEventListeners();
42542
42543  // setting up all modules
42544  this.images = new Images(function () {
42545    return _this.body.emitter.emit("_requestRedraw");
42546  }); // object with images
42547  this.groups = new Groups(); // object with groups
42548  this.canvas = new Canvas(this.body); // DOM handler
42549  this.selectionHandler = new SelectionHandler(this.body, this.canvas); // Selection handler
42550  this.interactionHandler = new InteractionHandler(this.body, this.canvas, this.selectionHandler); // Interaction handler handles all the hammer bindings (that are bound by canvas), key
42551  this.view = new View(this.body, this.canvas); // camera handler, does animations and zooms
42552  this.renderer = new CanvasRenderer(this.body, this.canvas); // renderer, starts renderloop, has events that modules can hook into
42553  this.physics = new PhysicsEngine(this.body); // physics engine, does all the simulations
42554  this.layoutEngine = new LayoutEngine(this.body); // layout engine for inital layout and hierarchical layout
42555  this.clustering = new ClusterEngine(this.body); // clustering api
42556  this.manipulation = new ManipulationSystem(this.body, this.canvas, this.selectionHandler); // data manipulation system
42557
42558  this.nodesHandler = new NodesHandler(this.body, this.images, this.groups, this.layoutEngine); // Handle adding, deleting and updating of nodes as well as global options
42559  this.edgesHandler = new EdgesHandler(this.body, this.images, this.groups); // Handle adding, deleting and updating of edges as well as global options
42560
42561  this.body.modules["kamadaKawai"] = new KamadaKawai(this.body, 150, 0.05); // Layouting algorithm.
42562  this.body.modules["clustering"] = this.clustering;
42563
42564  // create the DOM elements
42565  this.canvas._create();
42566
42567  // apply options
42568  this.setOptions(options);
42569
42570  // load data (the disable start variable will be the same as the enabled clustering)
42571  this.setData(data);
42572}
42573
42574// Extend Network with an Emitter mixin
42575Emitter(Network.prototype);
42576
42577/**
42578 * Set options
42579 * @param {Object} options
42580 */
42581Network.prototype.setOptions = function (options) {
42582  var _this2 = this;
42583
42584  if (options !== undefined) {
42585    var errorFound = Validator.validate(options, allOptions);
42586    if (errorFound === true) {
42587      console.log('%cErrors have been found in the supplied options object.', printStyle);
42588    }
42589
42590    // copy the global fields over
42591    var fields = ['locale', 'locales', 'clickToUse'];
42592    util.selectiveDeepExtend(fields, this.options, options);
42593
42594    // the hierarchical system can adapt the edges and the physics to it's own options because not all combinations work with the hierarichical system.
42595    options = this.layoutEngine.setOptions(options.layout, options);
42596
42597    this.canvas.setOptions(options); // options for canvas are in globals
42598
42599    // pass the options to the modules
42600    this.groups.setOptions(options.groups);
42601    this.nodesHandler.setOptions(options.nodes);
42602    this.edgesHandler.setOptions(options.edges);
42603    this.physics.setOptions(options.physics);
42604    this.manipulation.setOptions(options.manipulation, options, this.options); // manipulation uses the locales in the globals
42605
42606    this.interactionHandler.setOptions(options.interaction);
42607    this.renderer.setOptions(options.interaction); // options for rendering are in interaction
42608    this.selectionHandler.setOptions(options.interaction); // options for selection are in interaction
42609
42610    // reload the settings of the nodes to apply changes in groups that are not referenced by pointer.
42611    if (options.groups !== undefined) {
42612      this.body.emitter.emit("refreshNodes");
42613    }
42614    // these two do not have options at the moment, here for completeness
42615    //this.view.setOptions(options.view);
42616    //this.clustering.setOptions(options.clustering);
42617
42618    if ('configure' in options) {
42619      if (!this.configurator) {
42620        this.configurator = new Configurator(this, this.body.container, configureOptions, this.canvas.pixelRatio);
42621      }
42622
42623      this.configurator.setOptions(options.configure);
42624    }
42625
42626    // if the configuration system is enabled, copy all options and put them into the config system
42627    if (this.configurator && this.configurator.options.enabled === true) {
42628      var networkOptions = { nodes: {}, edges: {}, layout: {}, interaction: {}, manipulation: {}, physics: {}, global: {} };
42629      util.deepExtend(networkOptions.nodes, this.nodesHandler.options);
42630      util.deepExtend(networkOptions.edges, this.edgesHandler.options);
42631      util.deepExtend(networkOptions.layout, this.layoutEngine.options);
42632      // load the selectionHandler and render default options in to the interaction group
42633      util.deepExtend(networkOptions.interaction, this.selectionHandler.options);
42634      util.deepExtend(networkOptions.interaction, this.renderer.options);
42635
42636      util.deepExtend(networkOptions.interaction, this.interactionHandler.options);
42637      util.deepExtend(networkOptions.manipulation, this.manipulation.options);
42638      util.deepExtend(networkOptions.physics, this.physics.options);
42639
42640      // load globals into the global object
42641      util.deepExtend(networkOptions.global, this.canvas.options);
42642      util.deepExtend(networkOptions.global, this.options);
42643
42644      this.configurator.setModuleOptions(networkOptions);
42645    }
42646
42647    // handle network global options
42648    if (options.clickToUse !== undefined) {
42649      if (options.clickToUse === true) {
42650        if (this.activator === undefined) {
42651          this.activator = new Activator(this.canvas.frame);
42652          this.activator.on('change', function () {
42653            _this2.body.emitter.emit("activate");
42654          });
42655        }
42656      } else {
42657        if (this.activator !== undefined) {
42658          this.activator.destroy();
42659          delete this.activator;
42660        }
42661        this.body.emitter.emit("activate");
42662      }
42663    } else {
42664      this.body.emitter.emit("activate");
42665    }
42666
42667    this.canvas.setSize();
42668    // start the physics simulation. Can be safely called multiple times.
42669    this.body.emitter.emit("startSimulation");
42670  }
42671};
42672
42673/**
42674 * Update the visible nodes and edges list with the most recent node state.
42675 *
42676 * Visible nodes are stored in this.body.nodeIndices.
42677 * Visible edges are stored in this.body.edgeIndices.
42678 * A node or edges is visible if it is not hidden or clustered.
42679 *
42680 * @private
42681 */
42682Network.prototype._updateVisibleIndices = function () {
42683  var nodes = this.body.nodes;
42684  var edges = this.body.edges;
42685  this.body.nodeIndices = [];
42686  this.body.edgeIndices = [];
42687
42688  for (var nodeId in nodes) {
42689    if (nodes.hasOwnProperty(nodeId)) {
42690      if (!this.clustering._isClusteredNode(nodeId) && nodes[nodeId].options.hidden === false) {
42691        this.body.nodeIndices.push(nodes[nodeId].id);
42692      }
42693    }
42694  }
42695
42696  for (var edgeId in edges) {
42697    if (edges.hasOwnProperty(edgeId)) {
42698      var edge = edges[edgeId];
42699
42700      // It can happen that this is executed *after* a node edge has been removed,
42701      // but *before* the edge itself has been removed. Taking this into account.
42702      var fromNode = nodes[edge.fromId];
42703      var toNode = nodes[edge.toId];
42704      var edgeNodesPresent = fromNode !== undefined && toNode !== undefined;
42705
42706      var isVisible = !this.clustering._isClusteredEdge(edgeId) && edge.options.hidden === false && edgeNodesPresent && fromNode.options.hidden === false // Also hidden if any of its connecting nodes are hidden
42707      && toNode.options.hidden === false; // idem
42708
42709      if (isVisible) {
42710        this.body.edgeIndices.push(edge.id);
42711      }
42712    }
42713  }
42714};
42715
42716/**
42717 * Bind all events
42718 */
42719Network.prototype.bindEventListeners = function () {
42720  var _this3 = this;
42721
42722  // This event will trigger a rebuilding of the cache everything.
42723  // Used when nodes or edges have been added or removed.
42724  this.body.emitter.on("_dataChanged", function () {
42725    _this3.edgesHandler._updateState();
42726    _this3.body.emitter.emit("_dataUpdated");
42727  });
42728
42729  // this is called when options of EXISTING nodes or edges have changed.
42730  this.body.emitter.on("_dataUpdated", function () {
42731    // Order important in following block
42732    _this3.clustering._updateState();
42733    _this3._updateVisibleIndices();
42734
42735    _this3._updateValueRange(_this3.body.nodes);
42736    _this3._updateValueRange(_this3.body.edges);
42737    // start simulation (can be called safely, even if already running)
42738    _this3.body.emitter.emit("startSimulation");
42739    _this3.body.emitter.emit("_requestRedraw");
42740  });
42741};
42742
42743/**
42744 * Set nodes and edges, and optionally options as well.
42745 *
42746 * @param {Object} data              Object containing parameters:
42747 *                                   {Array | DataSet | DataView} [nodes] Array with nodes
42748 *                                   {Array | DataSet | DataView} [edges] Array with edges
42749 *                                   {String} [dot] String containing data in DOT format
42750 *                                   {String} [gephi] String containing data in gephi JSON format
42751 *                                   {Options} [options] Object with options
42752 */
42753Network.prototype.setData = function (data) {
42754  // reset the physics engine.
42755  this.body.emitter.emit("resetPhysics");
42756  this.body.emitter.emit("_resetData");
42757
42758  // unselect all to ensure no selections from old data are carried over.
42759  this.selectionHandler.unselectAll();
42760
42761  if (data && data.dot && (data.nodes || data.edges)) {
42762    throw new SyntaxError('Data must contain either parameter "dot" or ' + ' parameter pair "nodes" and "edges", but not both.');
42763  }
42764
42765  // set options
42766  this.setOptions(data && data.options);
42767  // set all data
42768  if (data && data.dot) {
42769    console.log('The dot property has been deprecated. Please use the static convertDot method to convert DOT into vis.network format and use the normal data format with nodes and edges. This converter is used like this: var data = vis.network.convertDot(dotString);');
42770    // parse DOT file
42771    var dotData = dotparser.DOTToGraph(data.dot);
42772    this.setData(dotData);
42773    return;
42774  } else if (data && data.gephi) {
42775    // parse DOT file
42776    console.log('The gephi property has been deprecated. Please use the static convertGephi method to convert gephi into vis.network format and use the normal data format with nodes and edges. This converter is used like this: var data = vis.network.convertGephi(gephiJson);');
42777    var gephiData = gephiParser.parseGephi(data.gephi);
42778    this.setData(gephiData);
42779    return;
42780  } else {
42781    this.nodesHandler.setData(data && data.nodes, true);
42782    this.edgesHandler.setData(data && data.edges, true);
42783  }
42784
42785  // emit change in data
42786  this.body.emitter.emit("_dataChanged");
42787
42788  // emit data loaded
42789  this.body.emitter.emit("_dataLoaded");
42790
42791  // find a stable position or start animating to a stable position
42792  this.body.emitter.emit("initPhysics");
42793};
42794
42795/**
42796 * Cleans up all bindings of the network, removing it fully from the memory IF the variable is set to null after calling this function.
42797 * var network = new vis.Network(..);
42798 * network.destroy();
42799 * network = null;
42800 */
42801Network.prototype.destroy = function () {
42802  this.body.emitter.emit("destroy");
42803  // clear events
42804  this.body.emitter.off();
42805  this.off();
42806
42807  // delete modules
42808  delete this.groups;
42809  delete this.canvas;
42810  delete this.selectionHandler;
42811  delete this.interactionHandler;
42812  delete this.view;
42813  delete this.renderer;
42814  delete this.physics;
42815  delete this.layoutEngine;
42816  delete this.clustering;
42817  delete this.manipulation;
42818  delete this.nodesHandler;
42819  delete this.edgesHandler;
42820  delete this.configurator;
42821  delete this.images;
42822
42823  for (var nodeId in this.body.nodes) {
42824    if (!this.body.nodes.hasOwnProperty(nodeId)) continue;
42825    delete this.body.nodes[nodeId];
42826  }
42827
42828  for (var edgeId in this.body.edges) {
42829    if (!this.body.edges.hasOwnProperty(edgeId)) continue;
42830    delete this.body.edges[edgeId];
42831  }
42832
42833  // remove the container and everything inside it recursively
42834  util.recursiveDOMDelete(this.body.container);
42835};
42836
42837/**
42838 * Update the values of all object in the given array according to the current
42839 * value range of the objects in the array.
42840 * @param {Object} obj    An object containing a set of Edges or Nodes
42841 *                        The objects must have a method getValue() and
42842 *                        setValueRange(min, max).
42843 * @private
42844 */
42845Network.prototype._updateValueRange = function (obj) {
42846  var id;
42847
42848  // determine the range of the objects
42849  var valueMin = undefined;
42850  var valueMax = undefined;
42851  var valueTotal = 0;
42852  for (id in obj) {
42853    if (obj.hasOwnProperty(id)) {
42854      var value = obj[id].getValue();
42855      if (value !== undefined) {
42856        valueMin = valueMin === undefined ? value : Math.min(value, valueMin);
42857        valueMax = valueMax === undefined ? value : Math.max(value, valueMax);
42858        valueTotal += value;
42859      }
42860    }
42861  }
42862
42863  // adjust the range of all objects
42864  if (valueMin !== undefined && valueMax !== undefined) {
42865    for (id in obj) {
42866      if (obj.hasOwnProperty(id)) {
42867        obj[id].setValueRange(valueMin, valueMax, valueTotal);
42868      }
42869    }
42870  }
42871};
42872
42873/**
42874 * Returns true when the Network is active.
42875 * @returns {boolean}
42876 */
42877Network.prototype.isActive = function () {
42878  return !this.activator || this.activator.active;
42879};
42880
42881Network.prototype.setSize = function () {
42882  return this.canvas.setSize.apply(this.canvas, arguments);
42883};
42884Network.prototype.canvasToDOM = function () {
42885  return this.canvas.canvasToDOM.apply(this.canvas, arguments);
42886};
42887Network.prototype.DOMtoCanvas = function () {
42888  return this.canvas.DOMtoCanvas.apply(this.canvas, arguments);
42889};
42890Network.prototype.findNode = function () {
42891  return this.clustering.findNode.apply(this.clustering, arguments);
42892};
42893Network.prototype.isCluster = function () {
42894  return this.clustering.isCluster.apply(this.clustering, arguments);
42895};
42896Network.prototype.openCluster = function () {
42897  return this.clustering.openCluster.apply(this.clustering, arguments);
42898};
42899Network.prototype.cluster = function () {
42900  return this.clustering.cluster.apply(this.clustering, arguments);
42901};
42902Network.prototype.getNodesInCluster = function () {
42903  return this.clustering.getNodesInCluster.apply(this.clustering, arguments);
42904};
42905Network.prototype.clusterByConnection = function () {
42906  return this.clustering.clusterByConnection.apply(this.clustering, arguments);
42907};
42908Network.prototype.clusterByHubsize = function () {
42909  return this.clustering.clusterByHubsize.apply(this.clustering, arguments);
42910};
42911Network.prototype.clusterOutliers = function () {
42912  return this.clustering.clusterOutliers.apply(this.clustering, arguments);
42913};
42914Network.prototype.getSeed = function () {
42915  return this.layoutEngine.getSeed.apply(this.layoutEngine, arguments);
42916};
42917Network.prototype.enableEditMode = function () {
42918  return this.manipulation.enableEditMode.apply(this.manipulation, arguments);
42919};
42920Network.prototype.disableEditMode = function () {
42921  return this.manipulation.disableEditMode.apply(this.manipulation, arguments);
42922};
42923Network.prototype.addNodeMode = function () {
42924  return this.manipulation.addNodeMode.apply(this.manipulation, arguments);
42925};
42926Network.prototype.editNode = function () {
42927  return this.manipulation.editNode.apply(this.manipulation, arguments);
42928};
42929Network.prototype.editNodeMode = function () {
42930  console.log("Deprecated: Please use editNode instead of editNodeMode.");return this.manipulation.editNode.apply(this.manipulation, arguments);
42931};
42932Network.prototype.addEdgeMode = function () {
42933  return this.manipulation.addEdgeMode.apply(this.manipulation, arguments);
42934};
42935Network.prototype.editEdgeMode = function () {
42936  return this.manipulation.editEdgeMode.apply(this.manipulation, arguments);
42937};
42938Network.prototype.deleteSelected = function () {
42939  return this.manipulation.deleteSelected.apply(this.manipulation, arguments);
42940};
42941Network.prototype.getPositions = function () {
42942  return this.nodesHandler.getPositions.apply(this.nodesHandler, arguments);
42943};
42944Network.prototype.storePositions = function () {
42945  return this.nodesHandler.storePositions.apply(this.nodesHandler, arguments);
42946};
42947Network.prototype.moveNode = function () {
42948  return this.nodesHandler.moveNode.apply(this.nodesHandler, arguments);
42949};
42950Network.prototype.getBoundingBox = function () {
42951  return this.nodesHandler.getBoundingBox.apply(this.nodesHandler, arguments);
42952};
42953Network.prototype.getConnectedNodes = function (objectId) {
42954  if (this.body.nodes[objectId] !== undefined) {
42955    return this.nodesHandler.getConnectedNodes.apply(this.nodesHandler, arguments);
42956  } else {
42957    return this.edgesHandler.getConnectedNodes.apply(this.edgesHandler, arguments);
42958  }
42959};
42960Network.prototype.getConnectedEdges = function () {
42961  return this.nodesHandler.getConnectedEdges.apply(this.nodesHandler, arguments);
42962};
42963Network.prototype.startSimulation = function () {
42964  return this.physics.startSimulation.apply(this.physics, arguments);
42965};
42966Network.prototype.stopSimulation = function () {
42967  return this.physics.stopSimulation.apply(this.physics, arguments);
42968};
42969Network.prototype.stabilize = function () {
42970  return this.physics.stabilize.apply(this.physics, arguments);
42971};
42972Network.prototype.getSelection = function () {
42973  return this.selectionHandler.getSelection.apply(this.selectionHandler, arguments);
42974};
42975Network.prototype.setSelection = function () {
42976  return this.selectionHandler.setSelection.apply(this.selectionHandler, arguments);
42977};
42978Network.prototype.getSelectedNodes = function () {
42979  return this.selectionHandler.getSelectedNodes.apply(this.selectionHandler, arguments);
42980};
42981Network.prototype.getSelectedEdges = function () {
42982  return this.selectionHandler.getSelectedEdges.apply(this.selectionHandler, arguments);
42983};
42984Network.prototype.getNodeAt = function () {
42985  var node = this.selectionHandler.getNodeAt.apply(this.selectionHandler, arguments);
42986  if (node !== undefined && node.id !== undefined) {
42987    return node.id;
42988  }
42989  return node;
42990};
42991Network.prototype.getEdgeAt = function () {
42992  var edge = this.selectionHandler.getEdgeAt.apply(this.selectionHandler, arguments);
42993  if (edge !== undefined && edge.id !== undefined) {
42994    return edge.id;
42995  }
42996  return edge;
42997};
42998Network.prototype.selectNodes = function () {
42999  return this.selectionHandler.selectNodes.apply(this.selectionHandler, arguments);
43000};
43001Network.prototype.selectEdges = function () {
43002  return this.selectionHandler.selectEdges.apply(this.selectionHandler, arguments);
43003};
43004Network.prototype.unselectAll = function () {
43005  this.selectionHandler.unselectAll.apply(this.selectionHandler, arguments);
43006  this.redraw();
43007};
43008Network.prototype.redraw = function () {
43009  return this.renderer.redraw.apply(this.renderer, arguments);
43010};
43011Network.prototype.getScale = function () {
43012  return this.view.getScale.apply(this.view, arguments);
43013};
43014Network.prototype.getViewPosition = function () {
43015  return this.view.getViewPosition.apply(this.view, arguments);
43016};
43017Network.prototype.fit = function () {
43018  return this.view.fit.apply(this.view, arguments);
43019};
43020Network.prototype.moveTo = function () {
43021  return this.view.moveTo.apply(this.view, arguments);
43022};
43023Network.prototype.focus = function () {
43024  return this.view.focus.apply(this.view, arguments);
43025};
43026Network.prototype.releaseNode = function () {
43027  return this.view.releaseNode.apply(this.view, arguments);
43028};
43029Network.prototype.getOptionsFromConfigurator = function () {
43030  var options = {};
43031  if (this.configurator) {
43032    options = this.configurator.getOptions.apply(this.configurator);
43033  }
43034  return options;
43035};
43036
43037module.exports = Network;
43038
43039/***/ }),
43040/* 183 */
43041/***/ (function(module, exports, __webpack_require__) {
43042
43043"use strict";
43044
43045
43046/**
43047 * Canvas shapes used by Network
43048 */
43049if (typeof CanvasRenderingContext2D !== 'undefined') {
43050
43051  /**
43052   * Draw a circle shape
43053   *
43054   * @param {number} x
43055   * @param {number} y
43056   * @param {number} r
43057   */
43058  CanvasRenderingContext2D.prototype.circle = function (x, y, r) {
43059    this.beginPath();
43060    this.arc(x, y, r, 0, 2 * Math.PI, false);
43061    this.closePath();
43062  };
43063
43064  /**
43065   * Draw a square shape
43066   * @param {number} x horizontal center
43067   * @param {number} y vertical center
43068   * @param {number} r   size, width and height of the square
43069   */
43070  CanvasRenderingContext2D.prototype.square = function (x, y, r) {
43071    this.beginPath();
43072    this.rect(x - r, y - r, r * 2, r * 2);
43073    this.closePath();
43074  };
43075
43076  /**
43077   * Draw a triangle shape
43078   * @param {number} x horizontal center
43079   * @param {number} y vertical center
43080   * @param {number} r   radius, half the length of the sides of the triangle
43081   */
43082  CanvasRenderingContext2D.prototype.triangle = function (x, y, r) {
43083    // http://en.wikipedia.org/wiki/Equilateral_triangle
43084    this.beginPath();
43085
43086    // the change in radius and the offset is here to center the shape
43087    r *= 1.15;
43088    y += 0.275 * r;
43089
43090    var s = r * 2;
43091    var s2 = s / 2;
43092    var ir = Math.sqrt(3) / 6 * s; // radius of inner circle
43093    var h = Math.sqrt(s * s - s2 * s2); // height
43094
43095
43096    this.moveTo(x, y - (h - ir));
43097    this.lineTo(x + s2, y + ir);
43098    this.lineTo(x - s2, y + ir);
43099    this.lineTo(x, y - (h - ir));
43100    this.closePath();
43101  };
43102
43103  /**
43104   * Draw a triangle shape in downward orientation
43105   * @param {number} x horizontal center
43106   * @param {number} y vertical center
43107   * @param {number} r radius
43108   */
43109  CanvasRenderingContext2D.prototype.triangleDown = function (x, y, r) {
43110    // http://en.wikipedia.org/wiki/Equilateral_triangle
43111    this.beginPath();
43112
43113    // the change in radius and the offset is here to center the shape
43114    r *= 1.15;
43115    y -= 0.275 * r;
43116
43117    var s = r * 2;
43118    var s2 = s / 2;
43119    var ir = Math.sqrt(3) / 6 * s; // radius of inner circle
43120    var h = Math.sqrt(s * s - s2 * s2); // height
43121
43122    this.moveTo(x, y + (h - ir));
43123    this.lineTo(x + s2, y - ir);
43124    this.lineTo(x - s2, y - ir);
43125    this.lineTo(x, y + (h - ir));
43126    this.closePath();
43127  };
43128
43129  /**
43130   * Draw a star shape, a star with 5 points
43131   * @param {number} x horizontal center
43132   * @param {number} y vertical center
43133   * @param {number} r   radius, half the length of the sides of the triangle
43134   */
43135  CanvasRenderingContext2D.prototype.star = function (x, y, r) {
43136    // http://www.html5canvastutorials.com/labs/html5-canvas-star-spinner/
43137    this.beginPath();
43138
43139    // the change in radius and the offset is here to center the shape
43140    r *= 0.82;
43141    y += 0.1 * r;
43142
43143    for (var n = 0; n < 10; n++) {
43144      var radius = n % 2 === 0 ? r * 1.3 : r * 0.5;
43145      this.lineTo(x + radius * Math.sin(n * 2 * Math.PI / 10), y - radius * Math.cos(n * 2 * Math.PI / 10));
43146    }
43147
43148    this.closePath();
43149  };
43150
43151  /**
43152   * Draw a Diamond shape
43153   * @param {number} x horizontal center
43154   * @param {number} y vertical center
43155   * @param {number} r   radius, half the length of the sides of the triangle
43156   */
43157  CanvasRenderingContext2D.prototype.diamond = function (x, y, r) {
43158    // http://www.html5canvastutorials.com/labs/html5-canvas-star-spinner/
43159    this.beginPath();
43160
43161    this.lineTo(x, y + r);
43162    this.lineTo(x + r, y);
43163    this.lineTo(x, y - r);
43164    this.lineTo(x - r, y);
43165
43166    this.closePath();
43167  };
43168
43169  /**
43170   * http://stackoverflow.com/questions/1255512/how-to-draw-a-rounded-rectangle-on-html-canvas
43171   *
43172   * @param {number} x
43173   * @param {number} y
43174   * @param {number} w
43175   * @param {number} h
43176   * @param {number} r
43177   */
43178  CanvasRenderingContext2D.prototype.roundRect = function (x, y, w, h, r) {
43179    var r2d = Math.PI / 180;
43180    if (w - 2 * r < 0) {
43181      r = w / 2;
43182    } //ensure that the radius isn't too large for x
43183    if (h - 2 * r < 0) {
43184      r = h / 2;
43185    } //ensure that the radius isn't too large for y
43186    this.beginPath();
43187    this.moveTo(x + r, y);
43188    this.lineTo(x + w - r, y);
43189    this.arc(x + w - r, y + r, r, r2d * 270, r2d * 360, false);
43190    this.lineTo(x + w, y + h - r);
43191    this.arc(x + w - r, y + h - r, r, 0, r2d * 90, false);
43192    this.lineTo(x + r, y + h);
43193    this.arc(x + r, y + h - r, r, r2d * 90, r2d * 180, false);
43194    this.lineTo(x, y + r);
43195    this.arc(x + r, y + r, r, r2d * 180, r2d * 270, false);
43196    this.closePath();
43197  };
43198
43199  /**
43200   * http://stackoverflow.com/questions/2172798/how-to-draw-an-oval-in-html5-canvas
43201   *
43202   * Postfix '_vis' added to discern it from standard method ellipse().
43203   *
43204   * @param {number} x
43205   * @param {number} y
43206   * @param {number} w
43207   * @param {number} h
43208   */
43209  CanvasRenderingContext2D.prototype.ellipse_vis = function (x, y, w, h) {
43210    var kappa = .5522848,
43211        ox = w / 2 * kappa,
43212        // control point offset horizontal
43213    oy = h / 2 * kappa,
43214        // control point offset vertical
43215    xe = x + w,
43216        // x-end
43217    ye = y + h,
43218        // y-end
43219    xm = x + w / 2,
43220        // x-middle
43221    ym = y + h / 2; // y-middle
43222
43223    this.beginPath();
43224    this.moveTo(x, ym);
43225    this.bezierCurveTo(x, ym - oy, xm - ox, y, xm, y);
43226    this.bezierCurveTo(xm + ox, y, xe, ym - oy, xe, ym);
43227    this.bezierCurveTo(xe, ym + oy, xm + ox, ye, xm, ye);
43228    this.bezierCurveTo(xm - ox, ye, x, ym + oy, x, ym);
43229    this.closePath();
43230  };
43231
43232  /**
43233   * http://stackoverflow.com/questions/2172798/how-to-draw-an-oval-in-html5-canvas
43234   *
43235   * @param {number} x
43236   * @param {number} y
43237   * @param {number} w
43238   * @param {number} h
43239   */
43240  CanvasRenderingContext2D.prototype.database = function (x, y, w, h) {
43241    var f = 1 / 3;
43242    var wEllipse = w;
43243    var hEllipse = h * f;
43244
43245    var kappa = .5522848,
43246        ox = wEllipse / 2 * kappa,
43247        // control point offset horizontal
43248    oy = hEllipse / 2 * kappa,
43249        // control point offset vertical
43250    xe = x + wEllipse,
43251        // x-end
43252    ye = y + hEllipse,
43253        // y-end
43254    xm = x + wEllipse / 2,
43255        // x-middle
43256    ym = y + hEllipse / 2,
43257        // y-middle
43258    ymb = y + (h - hEllipse / 2),
43259        // y-midlle, bottom ellipse
43260    yeb = y + h; // y-end, bottom ellipse
43261
43262    this.beginPath();
43263    this.moveTo(xe, ym);
43264
43265    this.bezierCurveTo(xe, ym + oy, xm + ox, ye, xm, ye);
43266    this.bezierCurveTo(xm - ox, ye, x, ym + oy, x, ym);
43267
43268    this.bezierCurveTo(x, ym - oy, xm - ox, y, xm, y);
43269    this.bezierCurveTo(xm + ox, y, xe, ym - oy, xe, ym);
43270
43271    this.lineTo(xe, ymb);
43272
43273    this.bezierCurveTo(xe, ymb + oy, xm + ox, yeb, xm, yeb);
43274    this.bezierCurveTo(xm - ox, yeb, x, ymb + oy, x, ymb);
43275
43276    this.lineTo(x, ym);
43277  };
43278
43279  /**
43280   * Sets up the dashedLine functionality for drawing
43281   * Original code came from http://stackoverflow.com/questions/4576724/dotted-stroke-in-canvas
43282   * @author David Jordan
43283   * @date 2012-08-08
43284   *
43285   * @param {number} x
43286   * @param {number} y
43287   * @param {number} x2
43288   * @param {number} y2
43289   * @param {string} pattern
43290   */
43291  CanvasRenderingContext2D.prototype.dashedLine = function (x, y, x2, y2, pattern) {
43292    this.beginPath();
43293    this.moveTo(x, y);
43294
43295    var patternLength = pattern.length;
43296    var dx = x2 - x;
43297    var dy = y2 - y;
43298    var slope = dy / dx;
43299    var distRemaining = Math.sqrt(dx * dx + dy * dy);
43300    var patternIndex = 0;
43301    var draw = true;
43302    var xStep = 0;
43303    var dashLength = pattern[0];
43304
43305    while (distRemaining >= 0.1) {
43306      dashLength = pattern[patternIndex++ % patternLength];
43307      if (dashLength > distRemaining) {
43308        dashLength = distRemaining;
43309      }
43310
43311      xStep = Math.sqrt(dashLength * dashLength / (1 + slope * slope));
43312      xStep = dx < 0 ? -xStep : xStep;
43313      x += xStep;
43314      y += slope * xStep;
43315
43316      if (draw === true) {
43317        this.lineTo(x, y);
43318      } else {
43319        this.moveTo(x, y);
43320      }
43321
43322      distRemaining -= dashLength;
43323      draw = !draw;
43324    }
43325  };
43326
43327  /**
43328   * Draw a Hexagon shape with 6 sides
43329   * @param {Number} x horizontal center
43330   * @param {Number} y vertical center
43331   * @param {Number} r   radius
43332   */
43333  CanvasRenderingContext2D.prototype.hexagon = function (x, y, r) {
43334    this.beginPath();
43335    var sides = 6;
43336    var a = Math.PI * 2 / sides;
43337    this.moveTo(x + r, y);
43338    for (var i = 1; i < sides; i++) {
43339      this.lineTo(x + r * Math.cos(a * i), y + r * Math.sin(a * i));
43340    }
43341    this.closePath();
43342  };
43343}
43344
43345/***/ }),
43346/* 184 */
43347/***/ (function(module, exports, __webpack_require__) {
43348
43349"use strict";
43350
43351
43352// English
43353exports['en'] = {
43354  edit: 'Edit',
43355  del: 'Delete selected',
43356  back: 'Back',
43357  addNode: 'Add Node',
43358  addEdge: 'Add Edge',
43359  editNode: 'Edit Node',
43360  editEdge: 'Edit Edge',
43361  addDescription: 'Click in an empty space to place a new node.',
43362  edgeDescription: 'Click on a node and drag the edge to another node to connect them.',
43363  editEdgeDescription: 'Click on the control points and drag them to a node to connect to it.',
43364  createEdgeError: 'Cannot link edges to a cluster.',
43365  deleteClusterError: 'Clusters cannot be deleted.',
43366  editClusterError: 'Clusters cannot be edited.'
43367};
43368exports['en_EN'] = exports['en'];
43369exports['en_US'] = exports['en'];
43370
43371// German
43372exports['de'] = {
43373  edit: 'Editieren',
43374  del: 'L\xF6sche Auswahl',
43375  back: 'Zur\xFCck',
43376  addNode: 'Knoten hinzuf\xFCgen',
43377  addEdge: 'Kante hinzuf\xFCgen',
43378  editNode: 'Knoten editieren',
43379  editEdge: 'Kante editieren',
43380  addDescription: 'Klicke auf eine freie Stelle, um einen neuen Knoten zu plazieren.',
43381  edgeDescription: 'Klicke auf einen Knoten und ziehe die Kante zu einem anderen Knoten, um diese zu verbinden.',
43382  editEdgeDescription: 'Klicke auf die Verbindungspunkte und ziehe diese auf einen Knoten, um sie zu verbinden.',
43383  createEdgeError: 'Es ist nicht m\xF6glich, Kanten mit Clustern zu verbinden.',
43384  deleteClusterError: 'Cluster k\xF6nnen nicht gel\xF6scht werden.',
43385  editClusterError: 'Cluster k\xF6nnen nicht editiert werden.'
43386};
43387exports['de_DE'] = exports['de'];
43388
43389// Spanish
43390exports['es'] = {
43391  edit: 'Editar',
43392  del: 'Eliminar selecci\xF3n',
43393  back: '\xC1tras',
43394  addNode: 'A\xF1adir nodo',
43395  addEdge: 'A\xF1adir arista',
43396  editNode: 'Editar nodo',
43397  editEdge: 'Editar arista',
43398  addDescription: 'Haga clic en un lugar vac\xEDo para colocar un nuevo nodo.',
43399  edgeDescription: 'Haga clic en un nodo y arrastre la arista hacia otro nodo para conectarlos.',
43400  editEdgeDescription: 'Haga clic en un punto de control y arrastrelo a un nodo para conectarlo.',
43401  createEdgeError: 'No se puede conectar una arista a un grupo.',
43402  deleteClusterError: 'No es posible eliminar grupos.',
43403  editClusterError: 'No es posible editar grupos.'
43404};
43405exports['es_ES'] = exports['es'];
43406
43407//Italiano
43408exports['it'] = {
43409  edit: 'Modifica',
43410  del: 'Cancella la selezione',
43411  back: 'Indietro',
43412  addNode: 'Aggiungi un nodo',
43413  addEdge: 'Aggiungi un vertice',
43414  editNode: 'Modifica il nodo',
43415  editEdge: 'Modifica il vertice',
43416  addDescription: 'Clicca per aggiungere un nuovo nodo',
43417  edgeDescription: 'Clicca su un nodo e trascinalo ad un altro nodo per connetterli.',
43418  editEdgeDescription: 'Clicca sui Punti di controllo e trascinali ad un nodo per connetterli.',
43419  createEdgeError: 'Non si possono collegare vertici ad un cluster',
43420  deleteClusterError: 'I cluster non possono essere cancellati',
43421  editClusterError: 'I clusters non possono essere modificati.'
43422};
43423exports['it_IT'] = exports['it'];
43424
43425// Dutch
43426exports['nl'] = {
43427  edit: 'Wijzigen',
43428  del: 'Selectie verwijderen',
43429  back: 'Terug',
43430  addNode: 'Node toevoegen',
43431  addEdge: 'Link toevoegen',
43432  editNode: 'Node wijzigen',
43433  editEdge: 'Link wijzigen',
43434  addDescription: 'Klik op een leeg gebied om een nieuwe node te maken.',
43435  edgeDescription: 'Klik op een node en sleep de link naar een andere node om ze te verbinden.',
43436  editEdgeDescription: 'Klik op de verbindingspunten en sleep ze naar een node om daarmee te verbinden.',
43437  createEdgeError: 'Kan geen link maken naar een cluster.',
43438  deleteClusterError: 'Clusters kunnen niet worden verwijderd.',
43439  editClusterError: 'Clusters kunnen niet worden aangepast.'
43440};
43441exports['nl_NL'] = exports['nl'];
43442exports['nl_BE'] = exports['nl'];
43443
43444// Portuguese Brazil
43445exports['pt-br'] = {
43446  edit: 'Editar',
43447  del: 'Remover selecionado',
43448  back: 'Voltar',
43449  addNode: 'Adicionar nó',
43450  addEdge: 'Adicionar aresta',
43451  editNode: 'Editar nó',
43452  editEdge: 'Editar aresta',
43453  addDescription: 'Clique em um espaço em branco para adicionar um novo nó',
43454  edgeDescription: 'Clique em um nó e arraste a aresta até outro nó para conectá-los',
43455  editEdgeDescription: 'Clique nos pontos de controle e os arraste para um nó para conectá-los',
43456  createEdgeError: 'Não foi possível linkar arestas a um cluster.',
43457  deleteClusterError: 'Clusters não puderam ser removidos.',
43458  editClusterError: 'Clusters não puderam ser editados.'
43459};
43460exports['pt-BR'] = exports['pt-br'];
43461exports['pt_BR'] = exports['pt-br'];
43462exports['pt_br'] = exports['pt-br'];
43463
43464// Russian
43465exports['ru'] = {
43466  edit: 'Редактировать',
43467  del: 'Удалить выбранное',
43468  back: 'Назад',
43469  addNode: 'Добавить узел',
43470  addEdge: 'Добавить ребро',
43471  editNode: 'Редактировать узел',
43472  editEdge: 'Редактировать ребро',
43473  addDescription: 'Кликните в свободное место, чтобы добавить новый узел.',
43474  edgeDescription: 'Кликните на узел и протяните ребро к другому узлу, чтобы соединить их.',
43475  editEdgeDescription: 'Кликните на контрольные точки и перетащите их в узел, чтобы подключиться к нему.',
43476  createEdgeError: 'Невозможно соединить ребра в кластер.',
43477  deleteClusterError: 'Кластеры не могут быть удалены',
43478  editClusterError: 'Кластеры недоступны для редактирования.'
43479};
43480exports['ru_RU'] = exports['ru'];
43481
43482// Chinese
43483exports['cn'] = {
43484  edit: '编辑',
43485  del: '删除选定',
43486  back: '返回',
43487  addNode: '添加节点',
43488  addEdge: '添加连接线',
43489  editNode: '编辑节点',
43490  editEdge: '编辑连接线',
43491  addDescription: '单击空白处放置新节点。',
43492  edgeDescription: '单击某个节点并将该连接线拖动到另一个节点以连接它们。',
43493  editEdgeDescription: '单击控制节点并将它们拖到节点上连接。',
43494  createEdgeError: '无法将连接线连接到群集。',
43495  deleteClusterError: '无法删除群集。',
43496  editClusterError: '无法编辑群集。'
43497};
43498exports['zh_CN'] = exports['cn'];
43499
43500/***/ }),
43501/* 185 */
43502/***/ (function(module, exports, __webpack_require__) {
43503
43504"use strict";
43505
43506
43507Object.defineProperty(exports, "__esModule", {
43508  value: true
43509});
43510
43511var _classCallCheck2 = __webpack_require__(0);
43512
43513var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
43514
43515var _createClass2 = __webpack_require__(1);
43516
43517var _createClass3 = _interopRequireDefault(_createClass2);
43518
43519function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
43520
43521/**
43522 * Associates a canvas to a given image, containing a number of renderings
43523 * of the image at various sizes.
43524 *
43525 * This technique is known as 'mipmapping'.
43526 *
43527 * NOTE: Images can also be of type 'data:svg+xml`. This code also works
43528 *       for svg, but the mipmapping may not be necessary.
43529 *
43530 * @param {Image} image
43531 */
43532var CachedImage = function () {
43533  /**
43534   * @ignore
43535   */
43536  function CachedImage() {
43537    (0, _classCallCheck3['default'])(this, CachedImage);
43538    // eslint-disable-line no-unused-vars
43539    this.NUM_ITERATIONS = 4; // Number of items in the coordinates array
43540
43541    this.image = new Image();
43542    this.canvas = document.createElement('canvas');
43543  }
43544
43545  /**
43546   * Called when the image has been successfully loaded.
43547   */
43548
43549
43550  (0, _createClass3['default'])(CachedImage, [{
43551    key: 'init',
43552    value: function init() {
43553      if (this.initialized()) return;
43554
43555      this.src = this.image.src; // For same interface with Image
43556      var w = this.image.width;
43557      var h = this.image.height;
43558
43559      // Ease external access
43560      this.width = w;
43561      this.height = h;
43562
43563      var h2 = Math.floor(h / 2);
43564      var h4 = Math.floor(h / 4);
43565      var h8 = Math.floor(h / 8);
43566      var h16 = Math.floor(h / 16);
43567
43568      var w2 = Math.floor(w / 2);
43569      var w4 = Math.floor(w / 4);
43570      var w8 = Math.floor(w / 8);
43571      var w16 = Math.floor(w / 16);
43572
43573      // Make canvas as small as possible
43574      this.canvas.width = 3 * w4;
43575      this.canvas.height = h2;
43576
43577      // Coordinates and sizes of images contained in the canvas
43578      // Values per row:  [top x, left y, width, height]
43579
43580      this.coordinates = [[0, 0, w2, h2], [w2, 0, w4, h4], [w2, h4, w8, h8], [5 * w8, h4, w16, h16]];
43581
43582      this._fillMipMap();
43583    }
43584
43585    /**
43586     * @return {Boolean} true if init() has been called, false otherwise.
43587     */
43588
43589  }, {
43590    key: 'initialized',
43591    value: function initialized() {
43592      return this.coordinates !== undefined;
43593    }
43594
43595    /**
43596     * Redraw main image in various sizes to the context.
43597     *
43598     * The rationale behind this is to reduce artefacts due to interpolation
43599     * at differing zoom levels.
43600     *
43601     * Source: http://stackoverflow.com/q/18761404/1223531
43602     *
43603     * This methods takes the resizing out of the drawing loop, in order to
43604     * reduce performance overhead.
43605     *
43606     * TODO: The code assumes that a 2D context can always be gotten. This is
43607     *       not necessarily true! OTOH, if not true then usage of this class
43608     *       is senseless.
43609     *
43610     * @private
43611     */
43612
43613  }, {
43614    key: '_fillMipMap',
43615    value: function _fillMipMap() {
43616      var ctx = this.canvas.getContext('2d');
43617
43618      // First zoom-level comes from the image
43619      var to = this.coordinates[0];
43620      ctx.drawImage(this.image, to[0], to[1], to[2], to[3]);
43621
43622      // The rest are copy actions internal to the canvas/context
43623      for (var iterations = 1; iterations < this.NUM_ITERATIONS; iterations++) {
43624        var from = this.coordinates[iterations - 1];
43625        var _to = this.coordinates[iterations];
43626
43627        ctx.drawImage(this.canvas, from[0], from[1], from[2], from[3], _to[0], _to[1], _to[2], _to[3]);
43628      }
43629    }
43630
43631    /**
43632     * Draw the image, using the mipmap if necessary.
43633     *
43634     * MipMap is only used if param factor > 2; otherwise, original bitmap
43635     * is resized. This is also used to skip mipmap usage, e.g. by setting factor = 1
43636     *
43637     * Credits to 'Alex de Mulder' for original implementation.
43638     *
43639     * @param {CanvasRenderingContext2D} ctx  context on which to draw zoomed image
43640     * @param {Float} factor scale factor at which to draw
43641     * @param {number} left
43642     * @param {number} top
43643     * @param {number} width
43644     * @param {number} height
43645     */
43646
43647  }, {
43648    key: 'drawImageAtPosition',
43649    value: function drawImageAtPosition(ctx, factor, left, top, width, height) {
43650
43651      if (!this.initialized()) return; //can't draw image yet not intialized
43652
43653      if (factor > 2) {
43654        // Determine which zoomed image to use
43655        factor *= 0.5;
43656        var iterations = 0;
43657        while (factor > 2 && iterations < this.NUM_ITERATIONS) {
43658          factor *= 0.5;
43659          iterations += 1;
43660        }
43661
43662        if (iterations >= this.NUM_ITERATIONS) {
43663          iterations = this.NUM_ITERATIONS - 1;
43664        }
43665        //console.log("iterations: " + iterations);
43666
43667        var from = this.coordinates[iterations];
43668        ctx.drawImage(this.canvas, from[0], from[1], from[2], from[3], left, top, width, height);
43669      } else {
43670        // Draw image directly
43671        ctx.drawImage(this.image, left, top, width, height);
43672      }
43673    }
43674  }]);
43675  return CachedImage;
43676}();
43677
43678exports['default'] = CachedImage;
43679
43680/***/ }),
43681/* 186 */
43682/***/ (function(module, exports, __webpack_require__) {
43683
43684"use strict";
43685
43686
43687Object.defineProperty(exports, "__esModule", {
43688  value: true
43689});
43690
43691var _classCallCheck2 = __webpack_require__(0);
43692
43693var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
43694
43695var _createClass2 = __webpack_require__(1);
43696
43697var _createClass3 = _interopRequireDefault(_createClass2);
43698
43699function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
43700
43701var util = __webpack_require__(2);
43702
43703/**
43704 * This class can store groups and options specific for groups.
43705 */
43706
43707var Groups = function () {
43708  /**
43709   * @ignore
43710   */
43711  function Groups() {
43712    (0, _classCallCheck3["default"])(this, Groups);
43713
43714    this.clear();
43715    this.defaultIndex = 0;
43716    this.groupsArray = [];
43717    this.groupIndex = 0;
43718
43719    this.defaultGroups = [{ border: "#2B7CE9", background: "#97C2FC", highlight: { border: "#2B7CE9", background: "#D2E5FF" }, hover: { border: "#2B7CE9", background: "#D2E5FF" } }, // 0: blue
43720    { border: "#FFA500", background: "#FFFF00", highlight: { border: "#FFA500", background: "#FFFFA3" }, hover: { border: "#FFA500", background: "#FFFFA3" } }, // 1: yellow
43721    { border: "#FA0A10", background: "#FB7E81", highlight: { border: "#FA0A10", background: "#FFAFB1" }, hover: { border: "#FA0A10", background: "#FFAFB1" } }, // 2: red
43722    { border: "#41A906", background: "#7BE141", highlight: { border: "#41A906", background: "#A1EC76" }, hover: { border: "#41A906", background: "#A1EC76" } }, // 3: green
43723    { border: "#E129F0", background: "#EB7DF4", highlight: { border: "#E129F0", background: "#F0B3F5" }, hover: { border: "#E129F0", background: "#F0B3F5" } }, // 4: magenta
43724    { border: "#7C29F0", background: "#AD85E4", highlight: { border: "#7C29F0", background: "#D3BDF0" }, hover: { border: "#7C29F0", background: "#D3BDF0" } }, // 5: purple
43725    { border: "#C37F00", background: "#FFA807", highlight: { border: "#C37F00", background: "#FFCA66" }, hover: { border: "#C37F00", background: "#FFCA66" } }, // 6: orange
43726    { border: "#4220FB", background: "#6E6EFD", highlight: { border: "#4220FB", background: "#9B9BFD" }, hover: { border: "#4220FB", background: "#9B9BFD" } }, // 7: darkblue
43727    { border: "#FD5A77", background: "#FFC0CB", highlight: { border: "#FD5A77", background: "#FFD1D9" }, hover: { border: "#FD5A77", background: "#FFD1D9" } }, // 8: pink
43728    { border: "#4AD63A", background: "#C2FABC", highlight: { border: "#4AD63A", background: "#E6FFE3" }, hover: { border: "#4AD63A", background: "#E6FFE3" } }, // 9: mint
43729
43730    { border: "#990000", background: "#EE0000", highlight: { border: "#BB0000", background: "#FF3333" }, hover: { border: "#BB0000", background: "#FF3333" } }, // 10:bright red
43731
43732    { border: "#FF6000", background: "#FF6000", highlight: { border: "#FF6000", background: "#FF6000" }, hover: { border: "#FF6000", background: "#FF6000" } }, // 12: real orange
43733    { border: "#97C2FC", background: "#2B7CE9", highlight: { border: "#D2E5FF", background: "#2B7CE9" }, hover: { border: "#D2E5FF", background: "#2B7CE9" } }, // 13: blue
43734    { border: "#399605", background: "#255C03", highlight: { border: "#399605", background: "#255C03" }, hover: { border: "#399605", background: "#255C03" } }, // 14: green
43735    { border: "#B70054", background: "#FF007E", highlight: { border: "#B70054", background: "#FF007E" }, hover: { border: "#B70054", background: "#FF007E" } }, // 15: magenta
43736    { border: "#AD85E4", background: "#7C29F0", highlight: { border: "#D3BDF0", background: "#7C29F0" }, hover: { border: "#D3BDF0", background: "#7C29F0" } }, // 16: purple
43737    { border: "#4557FA", background: "#000EA1", highlight: { border: "#6E6EFD", background: "#000EA1" }, hover: { border: "#6E6EFD", background: "#000EA1" } }, // 17: darkblue
43738    { border: "#FFC0CB", background: "#FD5A77", highlight: { border: "#FFD1D9", background: "#FD5A77" }, hover: { border: "#FFD1D9", background: "#FD5A77" } }, // 18: pink
43739    { border: "#C2FABC", background: "#74D66A", highlight: { border: "#E6FFE3", background: "#74D66A" }, hover: { border: "#E6FFE3", background: "#74D66A" } }, // 19: mint
43740
43741    { border: "#EE0000", background: "#990000", highlight: { border: "#FF3333", background: "#BB0000" }, hover: { border: "#FF3333", background: "#BB0000" } // 20:bright red
43742    }];
43743
43744    this.options = {};
43745    this.defaultOptions = {
43746      useDefaultGroups: true
43747    };
43748    util.extend(this.options, this.defaultOptions);
43749  }
43750
43751  /**
43752   *
43753   * @param {Object} options
43754   */
43755
43756
43757  (0, _createClass3["default"])(Groups, [{
43758    key: "setOptions",
43759    value: function setOptions(options) {
43760      var optionFields = ['useDefaultGroups'];
43761
43762      if (options !== undefined) {
43763        for (var groupName in options) {
43764          if (options.hasOwnProperty(groupName)) {
43765            if (optionFields.indexOf(groupName) === -1) {
43766              var group = options[groupName];
43767              this.add(groupName, group);
43768            }
43769          }
43770        }
43771      }
43772    }
43773
43774    /**
43775     * Clear all groups
43776     */
43777
43778  }, {
43779    key: "clear",
43780    value: function clear() {
43781      this.groups = {};
43782      this.groupsArray = [];
43783    }
43784
43785    /**
43786     * Get group options of a groupname.
43787     * If groupname is not found, a new group may be created.
43788     *
43789     * @param {*}       groupname     Can be a number, string, Date, etc.
43790     * @param {boolean} [shouldCreate=true] If true, create a new group
43791     * @return {Object} The found or created group
43792     */
43793
43794  }, {
43795    key: "get",
43796    value: function get(groupname) {
43797      var shouldCreate = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
43798
43799      var group = this.groups[groupname];
43800
43801      if (group === undefined && shouldCreate) {
43802        if (this.options.useDefaultGroups === false && this.groupsArray.length > 0) {
43803          // create new group
43804          var index = this.groupIndex % this.groupsArray.length;
43805          this.groupIndex++;
43806          group = {};
43807          group.color = this.groups[this.groupsArray[index]];
43808          this.groups[groupname] = group;
43809        } else {
43810          // create new group
43811          var _index = this.defaultIndex % this.defaultGroups.length;
43812          this.defaultIndex++;
43813          group = {};
43814          group.color = this.defaultGroups[_index];
43815          this.groups[groupname] = group;
43816        }
43817      }
43818
43819      return group;
43820    }
43821
43822    /**
43823     * Add a custom group style
43824     * @param {string} groupName
43825     * @param {Object}
43825 style       An object containing borderColor,
43826     *                             backgroundColor, etc.
43827     * @return {Object} group      The created group object
43828     */
43829
43830  }, {
43831    key: "add",
43832    value: function add(groupName, style) {
43833      this.groups[groupName] = style;
43834      this.groupsArray.push(groupName);
43835      return style;
43836    }
43837  }]);
43838  return Groups;
43839}();
43840
43841exports["default"] = Groups;
43842
43843/***/ }),
43844/* 187 */
43845/***/ (function(module, exports, __webpack_require__) {
43846
43847"use strict";
43848
43849
43850Object.defineProperty(exports, "__esModule", {
43851  value: true
43852});
43853
43854var _classCallCheck2 = __webpack_require__(0);
43855
43856var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
43857
43858var _createClass2 = __webpack_require__(1);
43859
43860var _createClass3 = _interopRequireDefault(_createClass2);
43861
43862function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
43863
43864var util = __webpack_require__(2);
43865var DataSet = __webpack_require__(11);
43866var DataView = __webpack_require__(12);
43867var Node = __webpack_require__(47)['default'];
43868
43869/**
43870 * Handler for Nodes
43871 */
43872
43873var NodesHandler = function () {
43874  /**
43875   * @param {Object} body
43876   * @param {Images} images
43877   * @param {Array.<Group>} groups
43878   * @param {LayoutEngine} layoutEngine
43879   */
43880  function NodesHandler(body, images, groups, layoutEngine) {
43881    var _this = this;
43882
43883    (0, _classCallCheck3['default'])(this, NodesHandler);
43884
43885    this.body = body;
43886    this.images = images;
43887    this.groups = groups;
43888    this.layoutEngine = layoutEngine;
43889
43890    // create the node API in the body container
43891    this.body.functions.createNode = this.create.bind(this);
43892
43893    this.nodesListeners = {
43894      add: function add(event, params) {
43895        _this.add(params.items);
43896      },
43897      update: function update(event, params) {
43898        _this.update(params.items, params.data, params.oldData);
43899      },
43900      remove: function remove(event, params) {
43901        _this.remove(params.items);
43902      }
43903    };
43904
43905    this.defaultOptions = {
43906      borderWidth: 1,
43907      borderWidthSelected: 2,
43908      brokenImage: undefined,
43909      color: {
43910        border: '#2B7CE9',
43911        background: '#97C2FC',
43912        highlight: {
43913          border: '#2B7CE9',
43914          background: '#D2E5FF'
43915        },
43916        hover: {
43917          border: '#2B7CE9',
43918          background: '#D2E5FF'
43919        }
43920      },
43921      fixed: {
43922        x: false,
43923        y: false
43924      },
43925      font: {
43926        color: '#343434',
43927        size: 14, // px
43928        face: 'arial',
43929        background: 'none',
43930        strokeWidth: 0, // px
43931        strokeColor: '#ffffff',
43932        align: 'center',
43933        vadjust: 0,
43934        multi: false,
43935        bold: {
43936          mod: 'bold'
43937        },
43938        boldital: {
43939          mod: 'bold italic'
43940        },
43941        ital: {
43942          mod: 'italic'
43943        },
43944        mono: {
43945          mod: '',
43946          size: 15, // px
43947          face: 'monospace',
43948          vadjust: 2
43949        }
43950      },
43951      group: undefined,
43952      hidden: false,
43953      icon: {
43954        face: 'FontAwesome', //'FontAwesome',
43955        code: undefined, //'\uf007',
43956        size: 50, //50,
43957        color: '#2B7CE9' //'#aa00ff'
43958      },
43959      image: undefined, // --> URL
43960      label: undefined,
43961      labelHighlightBold: true,
43962      level: undefined,
43963      margin: {
43964        top: 5,
43965        right: 5,
43966        bottom: 5,
43967        left: 5
43968      },
43969      mass: 1,
43970      physics: true,
43971      scaling: {
43972        min: 10,
43973        max: 30,
43974        label: {
43975          enabled: false,
43976          min: 14,
43977          max: 30,
43978          maxVisible: 30,
43979          drawThreshold: 5
43980        },
43981        customScalingFunction: function customScalingFunction(min, max, total, value) {
43982          if (max === min) {
43983            return 0.5;
43984          } else {
43985            var scale = 1 / (max - min);
43986            return Math.max(0, (value - min) * scale);
43987          }
43988        }
43989      },
43990      shadow: {
43991        enabled: false,
43992        color: 'rgba(0,0,0,0.5)',
43993        size: 10,
43994        x: 5,
43995        y: 5
43996      },
43997      shape: 'ellipse',
43998      shapeProperties: {
43999        borderDashes: false, // only for borders
44000        borderRadius: 6, // only for box shape
44001        interpolation: true, // only for image and circularImage shapes
44002        useImageSize: false, // only for image and circularImage shapes
44003        useBorderWithImage: false // only for image shape
44004      },
44005      size: 25,
44006      title: undefined,
44007      value: undefined,
44008      x: undefined,
44009      y: undefined
44010    };
44011
44012    // Protect from idiocy
44013    if (this.defaultOptions.mass <= 0) {
44014      throw 'Internal error: mass in defaultOptions of NodesHandler may not be zero or negative';
44015    }
44016
44017    this.options = util.bridgeObject(this.defaultOptions);
44018
44019    this.bindEventListeners();
44020  }
44021
44022  /**
44023   * Binds event listeners
44024   */
44025
44026
44027  (0, _createClass3['default'])(NodesHandler, [{
44028    key: 'bindEventListeners',
44029    value: function bindEventListeners() {
44030      var _this2 = this;
44031
44032      // refresh the nodes. Used when reverting from hierarchical layout
44033      this.body.emitter.on('refreshNodes', this.refresh.bind(this));
44034      this.body.emitter.on('refresh', this.refresh.bind(this));
44035      this.body.emitter.on('destroy', function () {
44036        util.forEach(_this2.nodesListeners, function (callback, event) {
44037          if (_this2.body.data.nodes) _this2.body.data.nodes.off(event, callback);
44038        });
44039        delete _this2.body.functions.createNode;
44040        delete _this2.nodesListeners.add;
44041        delete _this2.nodesListeners.update;
44042        delete _this2.nodesListeners.remove;
44043        delete _this2.nodesListeners;
44044      });
44045    }
44046
44047    /**
44048     *
44049     * @param {Object} options
44050     */
44051
44052  }, {
44053    key: 'setOptions',
44054    value: function setOptions(options) {
44055      if (options !== undefined) {
44056        Node.parseOptions(this.options, options);
44057
44058        // update the shape in all nodes
44059        if (options.shape !== undefined) {
44060          for (var nodeId in this.body.nodes) {
44061            if (this.body.nodes.hasOwnProperty(nodeId)) {
44062              this.body.nodes[nodeId].updateShape();
44063            }
44064          }
44065        }
44066
44067        // update the font in all nodes
44068        if (options.font !== undefined) {
44069          for (var _nodeId in this.body.nodes) {
44070            if (this.body.nodes.hasOwnProperty(_nodeId)) {
44071              this.body.nodes[_nodeId].updateLabelModule();
44072              this.body.nodes[_nodeId].needsRefresh();
44073            }
44074          }
44075        }
44076
44077        // update the shape size in all nodes
44078        if (options.size !== undefined) {
44079          for (var _nodeId2 in this.body.nodes) {
44080            if (this.body.nodes.hasOwnProperty(_nodeId2)) {
44081              this.body.nodes[_nodeId2].needsRefresh();
44082            }
44083          }
44084        }
44085
44086        // update the state of the variables if needed
44087        if (options.hidden !== undefined || options.physics !== undefined) {
44088          this.body.emitter.emit('_dataChanged');
44089        }
44090      }
44091    }
44092
44093    /**
44094     * Set a data set with nodes for the network
44095     * @param {Array | DataSet | DataView} nodes         The data containing the nodes.
44096     * @param {boolean} [doNotEmit=false]
44097     * @private
44098     */
44099
44100  }, {
44101    key: 'setData',
44102    value: function setData(nodes) {
44103      var doNotEmit = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
44104
44105      var oldNodesData = this.body.data.nodes;
44106
44107      if (nodes instanceof DataSet || nodes instanceof DataView) {
44108        this.body.data.nodes = nodes;
44109      } else if (Array.isArray(nodes)) {
44110        this.body.data.nodes = new DataSet();
44111        this.body.data.nodes.add(nodes);
44112      } else if (!nodes) {
44113        this.body.data.nodes = new DataSet();
44114      } else {
44115        throw new TypeError('Array or DataSet expected');
44116      }
44117
44118      if (oldNodesData) {
44119        // unsubscribe from old dataset
44120        util.forEach(this.nodesListeners, function (callback, event) {
44121          oldNodesData.off(event, callback);
44122        });
44123      }
44124
44125      // remove drawn nodes
44126      this.body.nodes = {};
44127
44128      if (this.body.data.nodes) {
44129        // subscribe to new dataset
44130        var me = this;
44131        util.forEach(this.nodesListeners, function (callback, event) {
44132          me.body.data.nodes.on(event, callback);
44133        });
44134
44135        // draw all new nodes
44136        var ids = this.body.data.nodes.getIds();
44137        this.add(ids, true);
44138      }
44139
44140      if (doNotEmit === false) {
44141        this.body.emitter.emit("_dataChanged");
44142      }
44143    }
44144
44145    /**
44146     * Add nodes
44147     * @param {number[] | string[]} ids
44148     * @param {boolean} [doNotEmit=false]
44149     * @private
44150     */
44151
44152  }, {
44153    key: 'add',
44154    value: function add(ids) {
44155      var doNotEmit = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
44156
44157      var id = void 0;
44158      var newNodes = [];
44159      for (var i = 0; i < ids.length; i++) {
44160        id = ids[i];
44161        var properties = this.body.data.nodes.get(id);
44162        var node = this.create(properties);
44163        newNodes.push(node);
44164        this.body.nodes[id] = node; // note: this may replace an existing node
44165      }
44166
44167      this.layoutEngine.positionInitially(newNodes);
44168
44169      if (doNotEmit === false) {
44170        this.body.emitter.emit("_dataChanged");
44171      }
44172    }
44173
44174    /**
44175     * Update existing nodes, or create them when not yet existing
44176     * @param {number[] | string[]} ids id's of changed nodes
44177     * @param {Array} changedData array with changed data
44178     * @param {Array|undefined} oldData optional; array with previous data
44179     * @private
44180     */
44181
44182  }, {
44183    key: 'update',
44184    value: function update(ids, changedData, oldData) {
44185      var nodes = this.body.nodes;
44186      var dataChanged = false;
44187      for (var i = 0; i < ids.length; i++) {
44188        var id = ids[i];
44189        var node = nodes[id];
44190        var data = changedData[i];
44191        if (node !== undefined) {
44192          // update node
44193          if (node.setOptions(data)) {
44194            dataChanged = true;
44195          }
44196        } else {
44197          dataChanged = true;
44198          // create node
44199          node = this.create(data);
44200          nodes[id] = node;
44201        }
44202      }
44203
44204      if (!dataChanged && oldData !== undefined) {
44205        // Check for any changes which should trigger a layout recalculation
44206        // For now, this is just 'level' for hierarchical layout
44207        // Assumption: old and new data arranged in same order; at time of writing, this holds.
44208        dataChanged = changedData.some(function (newValue, index) {
44209          var oldValue = oldData[index];
44210          return oldValue && oldValue.level !== newValue.level;
44211        });
44212      }
44213
44214      if (dataChanged === true) {
44215        this.body.emitter.emit("_dataChanged");
44216      } else {
44217        this.body.emitter.emit("_dataUpdated");
44218      }
44219    }
44220
44221    /**
44222     * Remove existing nodes. If nodes do not exist, the method will just ignore it.
44223     * @param {number[] | string[]} ids
44224     * @private
44225     */
44226
44227  }, {
44228    key: 'remove',
44229    value: function remove(ids) {
44230      var nodes = this.body.nodes;
44231
44232      for (var i = 0; i < ids.length; i++) {
44233        var id = ids[i];
44234        delete nodes[id];
44235      }
44236
44237      this.body.emitter.emit("_dataChanged");
44238    }
44239
44240    /**
44241     * create a node
44242     * @param {Object} properties
44243     * @param {class} [constructorClass=Node.default]
44244     * @returns {*}
44245     */
44246
44247  }, {
44248    key: 'create',
44249    value: function create(properties) {
44250      var constructorClass = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : Node;
44251
44252      return new constructorClass(properties, this.body, this.images, this.groups, this.options, this.defaultOptions);
44253    }
44254
44255    /**
44256     *
44257     * @param {boolean} [clearPositions=false]
44258     */
44259
44260  }, {
44261    key: 'refresh',
44262    value: function refresh() {
44263      var _this3 = this;
44264
44265      var clearPositions = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
44266
44267      util.forEach(this.body.nodes, function (node, nodeId) {
44268        var data = _this3.body.data.nodes.get(nodeId);
44269        if (data !== undefined) {
44270          if (clearPositions === true) {
44271            node.setOptions({ x: null, y: null });
44272          }
44273          node.setOptions({ fixed: false });
44274          node.setOptions(data);
44275        }
44276      });
44277    }
44278
44279    /**
44280     * Returns the positions of the nodes.
44281     * @param {Array.<Node.id>|String} [ids]  --> optional, can be array of nodeIds, can be string
44282     * @returns {{}}
44283     */
44284
44285  }, {
44286    key: 'getPositions',
44287    value: function getPositions(ids) {
44288      var dataArray = {};
44289      if (ids !== undefined) {
44290        if (Array.isArray(ids) === true) {
44291          for (var i = 0; i < ids.length; i++) {
44292            if (this.body.nodes[ids[i]] !== undefined) {
44293              var node = this.body.nodes[ids[i]];
44294              dataArray[ids[i]] = { x: Math.round(node.x), y: Math.round(node.y) };
44295            }
44296          }
44297        } else {
44298          if (this.body.nodes[ids] !== undefined) {
44299            var _node = this.body.nodes[ids];
44300            dataArray[ids] = { x: Math.round(_node.x), y: Math.round(_node.y) };
44301          }
44302        }
44303      } else {
44304        for (var _i = 0; _i < this.body.nodeIndices.length; _i++) {
44305          var _node2 = this.body.nodes[this.body.nodeIndices[_i]];
44306          dataArray[this.body.nodeIndices[_i]] = { x: Math.round(_node2.x), y: Math.round(_node2.y) };
44307        }
44308      }
44309      return dataArray;
44310    }
44311
44312    /**
44313     * Load the XY positions of the nodes into the dataset.
44314     */
44315
44316  }, {
44317    key: 'storePositions',
44318    value: function storePositions() {
44319      // todo: add support for clusters and hierarchical.
44320      var dataArray = [];
44321      var dataset = this.body.data.nodes.getDataSet();
44322
44323      for (var nodeId in dataset._data) {
44324        if (dataset._data.hasOwnProperty(nodeId)) {
44325          var node = this.body.nodes[nodeId];
44326          if (dataset._data[nodeId].x != Math.round(node.x) || dataset._data[nodeId].y != Math.round(node.y)) {
44327            dataArray.push({ id: node.id, x: Math.round(node.x), y: Math.round(node.y) });
44328          }
44329        }
44330      }
44331      dataset.update(dataArray);
44332    }
44333
44334    /**
44335     * get the bounding box of a node.
44336     * @param {Node.id} nodeId
44337     * @returns {j|*}
44338     */
44339
44340  }, {
44341    key: 'getBoundingBox',
44342    value: function getBoundingBox(nodeId) {
44343      if (this.body.nodes[nodeId] !== undefined) {
44344        return this.body.nodes[nodeId].shape.boundingBox;
44345      }
44346    }
44347
44348    /**
44349     * Get the Ids of nodes connected to this node.
44350     * @param {Node.id} nodeId
44351     * @param {'to'|'from'|undefined} direction values 'from' and 'to' select respectively parent and child nodes only.
44352     *                                          Any other value returns both parent and child nodes.
44353     * @returns {Array}
44354     */
44355
44356  }, {
44357    key: 'getConnectedNodes',
44358    value: function getConnectedNodes(nodeId, direction) {
44359      var nodeList = [];
44360      if (this.body.nodes[nodeId] !== undefined) {
44361        var node = this.body.nodes[nodeId];
44362        var nodeObj = {}; // used to quickly check if node already exists
44363        for (var i = 0; i < node.edges.length; i++) {
44364          var edge = node.edges[i];
44365          if (direction !== 'to' && edge.toId == node.id) {
44366            // these are double equals since ids can be numeric or string
44367            if (nodeObj[edge.fromId] === undefined) {
44368              nodeList.push(edge.fromId);
44369              nodeObj[edge.fromId] = true;
44370            }
44371          } else if (direction !== 'from' && edge.fromId == node.id) {
44372            // these are double equals since ids can be numeric or string
44373            if (nodeObj[edge.toId] === undefined) {
44374              nodeList.push(edge.toId);
44375              nodeObj[edge.toId] = true;
44376            }
44377          }
44378        }
44379      }
44380      return nodeList;
44381    }
44382
44383    /**
44384     * Get the ids of the edges connected to this node.
44385     * @param {Node.id} nodeId
44386     * @returns {*}
44387     */
44388
44389  }, {
44390    key: 'getConnectedEdges',
44391    value: function getConnectedEdges(nodeId) {
44392      var edgeList = [];
44393      if (this.body.nodes[nodeId] !== undefined) {
44394        var node = this.body.nodes[nodeId];
44395        for (var i = 0; i < node.edges.length; i++) {
44396          edgeList.push(node.edges[i].id);
44397        }
44398      } else {
44399        console.log("NodeId provided for getConnectedEdges does not exist. Provided: ", nodeId);
44400      }
44401      return edgeList;
44402    }
44403
44404    /**
44405     * Move a node.
44406     *
44407     * @param {Node.id} nodeId
44408     * @param {number} x
44409     * @param {number} y
44410     */
44411
44412  }, {
44413    key: 'moveNode',
44414    value: function moveNode(nodeId, x, y) {
44415      var _this4 = this;
44416
44417      if (this.body.nodes[nodeId] !== undefined) {
44418        this.body.nodes[nodeId].x = Number(x);
44419        this.body.nodes[nodeId].y = Number(y);
44420        setTimeout(function () {
44421          _this4.body.emitter.emit("startSimulation");
44422        }, 0);
44423      } else {
44424        console.log("Node id supplied to moveNode does not exist. Provided: ", nodeId);
44425      }
44426    }
44427  }]);
44428  return NodesHandler;
44429}();
44430
44431exports['default'] = NodesHandler;
44432
44433/***/ }),
44434/* 188 */
44435/***/ (function(module, exports, __webpack_require__) {
44436
44437module.exports = { "default": __webpack_require__(189), __esModule: true };
44438
44439/***/ }),
44440/* 189 */
44441/***/ (function(module, exports, __webpack_require__) {
44442
44443__webpack_require__(49);
44444__webpack_require__(60);
44445module.exports = __webpack_require__(190);
44446
44447
44448/***/ }),
44449/* 190 */
44450/***/ (function(module, exports, __webpack_require__) {
44451
44452var classof = __webpack_require__(86);
44453var ITERATOR = __webpack_require__(13)('iterator');
44454var Iterators = __webpack_require__(31);
44455module.exports = __webpack_require__(7).isIterable = function (it) {
44456  var O = Object(it);
44457  return O[ITERATOR] !== undefined
44458    || '@@iterator' in O
44459    // eslint-disable-next-line no-prototype-builtins
44460    || Iterators.hasOwnProperty(classof(O));
44461};
44462
44463
44464/***/ }),
44465/* 191 */
44466/***/ (function(module, exports, __webpack_require__) {
44467
44468"use strict";
44469
44470
44471Object.defineProperty(exports, "__esModule", {
44472  value: true
44473});
44474
44475var _classCallCheck2 = __webpack_require__(0);
44476
44477var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
44478
44479var _createClass2 = __webpack_require__(1);
44480
44481var _createClass3 = _interopRequireDefault(_createClass2);
44482
44483function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
44484
44485var LabelAccumulator = __webpack_require__(192)['default'];
44486var ComponentUtil = __webpack_require__(48)['default'];
44487
44488/**
44489 * Helper class for Label which explodes the label text into lines and blocks within lines
44490 *
44491 * @private
44492 */
44493
44494var LabelSplitter = function () {
44495
44496  /**
44497   * @param {CanvasRenderingContext2D} ctx Canvas rendering context
44498   * @param {Label} parent reference to the Label instance using current instance
44499   * @param {boolean} selected 
44500   * @param {boolean} hover
44501   */
44502  function LabelSplitter(ctx, parent, selected, hover) {
44503    var _this = this;
44504
44505    (0, _classCallCheck3['default'])(this, LabelSplitter);
44506
44507    this.ctx = ctx;
44508    this.parent = parent;
44509
44510    /**
44511     * Callback to determine text width; passed to LabelAccumulator instance
44512     *
44513     * @param  {String} text string to determine width of
44514     * @param  {String} mod  font type to use for this text
44515     * @return {Object} { width, values} width in pixels and font attributes
44516     */
44517    var textWidth = function textWidth(text, mod) {
44518      if (text === undefined) return 0;
44519
44520      // TODO: This can be done more efficiently with caching
44521      var values = _this.parent.getFormattingValues(ctx, selected, hover, mod);
44522
44523      var width = 0;
44524      if (text !== '') {
44525        // NOTE: The following may actually be *incorrect* for the mod fonts!
44526        //       This returns the size with a regular font, bold etc. may
44527        //       have different sizes.
44528        var measure = _this.ctx.measureText(text);
44529        width = measure.width;
44530      }
44531
44532      return { width: width, values: values };
44533    };
44534
44535    this.lines = new LabelAccumulator(textWidth);
44536  }
44537
44538  /**
44539   * Split passed text of a label into lines and blocks.
44540   *
44541   * # NOTE
44542   *
44543   * The handling of spacing is option dependent:
44544   *
44545   * - if `font.multi : false`, all spaces are retained
44546   * - if `font.multi : true`, every sequence of spaces is compressed to a single space
44547   *
44548   * This might not be the best way to do it, but this is as it has been working till now.
44549   * In order not to break existing functionality, for the time being this behaviour will
44550   * be retained in any code changes. 
44551   *
44552   * @param {string} text  text to split
44553   * @returns {Array<line>}
44554   */
44555
44556
44557  (0, _createClass3['default'])(LabelSplitter, [{
44558    key: 'process',
44559    value: function process(text) {
44560      if (!ComponentUtil.isValidLabel(text)) {
44561        return this.lines.finalize();
44562      }
44563
44564      var font = this.parent.fontOptions;
44565
44566      // Normalize the end-of-line's to a single representation - order important
44567      text = text.replace(/\r\n/g, '\n'); // Dos EOL's
44568      text = text.replace(/\r/g, '\n'); // Mac EOL's
44569
44570      // Note that at this point, there can be no \r's in the text.
44571      // This is used later on splitStringIntoLines() to split multifont texts.
44572
44573      var nlLines = String(text).split('\n');
44574      var lineCount = nlLines.length;
44575
44576      if (font.multi) {
44577        // Multi-font case: styling tags active
44578        for (var i = 0; i < lineCount; i++) {
44579          var blocks = this.splitBlocks(nlLines[i], font.multi);
44580          // Post: Sequences of tabs and spaces are reduced to single space
44581
44582          if (blocks === undefined) continue;
44583
44584          if (blocks.length === 0) {
44585            this.lines.newLine("");
44586            continue;
44587          }
44588
44589          if (font.maxWdt > 0) {
44590            // widthConstraint.maximum defined
44591            //console.log('Running widthConstraint multi, max: ' + this.fontOptions.maxWdt);
44592            for (var j = 0; j < blocks.length; j++) {
44593              var mod = blocks[j].mod;
44594              var _text = blocks[j].text;
44595              this.splitStringIntoLines(_text, mod, true);
44596            }
44597          } else {
44598            // widthConstraint.maximum NOT defined
44599            for (var _j = 0; _j < blocks.length; _j++) {
44600              var _mod = blocks[_j].mod;
44601              var _text2 = blocks[_j].text;
44602              this.lines.append(_text2, _mod);
44603            }
44604          }
44605
44606          this.lines.newLine();
44607        }
44608      } else {
44609        // Single-font case
44610        if (font.maxWdt > 0) {
44611          // widthConstraint.maximum defined
44612          // console.log('Running widthConstraint normal, max: ' + this.fontOptions.maxWdt);
44613          for (var _i = 0; _i < lineCount; _i++) {
44614            this.splitStringIntoLines(nlLines[_i]);
44615          }
44616        } else {
44617          // widthConstraint.maximum NOT defined
44618          for (var _i2 = 0; _i2 < lineCount; _i2++) {
44619            this.lines.newLine(nlLines[_i2]);
44620          }
44621        }
44622      }
44623
44624      return this.lines.finalize();
44625    }
44626
44627    /**
44628     * normalize the markup system
44629     *
44630     * @param {boolean|'md'|'markdown'|'html'} markupSystem
44631     * @returns {string}
44632     */
44633
44634  }, {
44635    key: 'decodeMarkupSystem',
44636    value: function decodeMarkupSystem(markupSystem) {
44637      var system = 'none';
44638      if (markupSystem === 'markdown' || markupSystem === 'md') {
44639        system = 'markdown';
44640      } else if (markupSystem === true || markupSystem === 'html') {
44641        system = 'html';
44642      }
44643      return system;
44644    }
44645
44646    /**
44647     *
44648     * @param {string} text
44649     * @returns {Array}
44650     */
44651
44652  }, {
44653    key: 'splitHtmlBlocks',
44654    value: function splitHtmlBlocks(text) {
44655      var blocks = [];
44656
44657      // TODO: consolidate following + methods/closures with splitMarkdownBlocks()
44658      // NOTE: sequences of tabs and spaces are reduced to single space; scan usage of `this.spacing` within method
44659      var s = {
44660        bold: false,
44661        ital: false,
44662        mono: false,
44663        spacing: false,
44664        position: 0,
44665        buffer: "",
44666        modStack: []
44667      };
44668
44669      s.mod = function () {
44670        return this.modStack.length === 0 ? 'normal' : this.modStack[0];
44671      };
44672
44673      s.modName = function () {
44674        if (this.modStack.length === 0) return 'normal';else if (this.modStack[0] === 'mono') return 'mono';else {
44675          if (s.bold && s.ital) {
44676            return 'boldital';
44677          } else if (s.bold) {
44678            return 'bold';
44679          } else if (s.ital) {
44680            return 'ital';
44681          }
44682        }
44683      };
44684
44685      s.emitBlock = function () {
44686        var override = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
44687        // eslint-disable-line no-unused-vars
44688        if (this.spacing) {
44689          this.add(" ");
44690          this.spacing = false;
44691        }
44692        if (this.buffer.length > 0) {
44693          blocks.push({ text: this.buffer, mod: this.modName() });
44694          this.buffer = "";
44695        }
44696      };
44697
44698      s.add = function (text) {
44699        if (text === " ") {
44700          s.spacing = true;
44701        }
44702        if (s.spacing) {
44703          this.buffer += " ";
44704          this.spacing = false;
44705        }
44706        if (text != " ") {
44707          this.buffer += text;
44708        }
44709      };
44710
44711      while (s.position < text.length) {
44712        var ch = text.charAt(s.position);
44713        if (/[ \t]/.test(ch)) {
44714          if (!s.mono) {
44715            s.spacing = true;
44716          } else {
44717            s.add(ch);
44718          }
44719        } else if (/</.test(ch)) {
44720          if (!s.mono && !s.bold && /<b>/.test(text.substr(s.position, 3))) {
44721            s.emitBlock();
44722            s.bold = true;
44723            s.modStack.unshift("bold");
44724            s.position += 2;
44725          } else if (!s.mono && !s.ital && /<i>/.test(text.substr(s.position, 3))) {
44726            s.emitBlock();
44727            s.ital = true;
44728            s.modStack.unshift("ital");
44729            s.position += 2;
44730          } else if (!s.mono && /<code>/.test(text.substr(s.position, 6))) {
44731            s.emitBlock();
44732            s.mono = true;
44733            s.modStack.unshift("mono");
44734            s.position += 5;
44735          } else if (!s.mono && s.mod() === 'bold' && /<\/b>/.test(text.substr(s.position, 4))) {
44736            s.emitBlock();
44737            s.bold = false;
44738            s.modStack.shift();
44739            s.position += 3;
44740          } else if (!s.mono && s.mod() === 'ital' && /<\/i>/.test(text.substr(s.position, 4))) {
44741            s.emitBlock();
44742            s.ital = false;
44743            s.modStack.shift();
44744            s.position += 3;
44745          } else if (s.mod() === 'mono' && /<\/code>/.test(text.substr(s.position, 7))) {
44746            s.emitBlock();
44747            s.mono = false;
44748            s.modStack.shift();
44749            s.position += 6;
44750          } else {
44751            s.add(ch);
44752          }
44753        } else if (/&/.test(ch)) {
44754          if (/&lt;/.test(text.substr(s.position, 4))) {
44755            s.add("<");
44756            s.position += 3;
44757          } else if (/&amp;/.test(text.substr(s.position, 5))) {
44758            s.add("&");
44759            s.position += 4;
44760          } else {
44761            s.add("&");
44762          }
44763        } else {
44764          s.add(ch);
44765        }
44766        s.position++;
44767      }
44768      s.emitBlock();
44769      return blocks;
44770    }
44771
44772    /**
44773     *
44774     * @param {string} text
44775     * @returns {Array}
44776     */
44777
44778  }, {
44779    key: 'splitMarkdownBlocks',
44780    value: function splitMarkdownBlocks(text) {
44781      var blocks = [];
44782
44783      // TODO: consolidate following + methods/closures with splitHtmlBlocks()
44784      // NOTE: sequences of tabs and spaces are reduced to single space; scan usage of `this.spacing` within method
44785      var s = {
44786        bold: false,
44787        ital: false,
44788        mono: false,
44789        beginable: true,
44790        spacing: false,
44791        position: 0,
44792        buffer: "",
44793        modStack: []
44794      };
44795
44796      s.mod = function () {
44797        return this.modStack.length === 0 ? 'normal' : this.modStack[0];
44798      };
44799
44800      s.modName = function () {
44801        if (this.modStack.length === 0) return 'normal';else if (this.modStack[0] === 'mono') return 'mono';else {
44802          if (s.bold && s.ital) {
44803            return 'boldital';
44804          } else if (s.bold) {
44805            return 'bold';
44806          } else if (s.ital) {
44807            return 'ital';
44808          }
44809        }
44810      };
44811
44812      s.emitBlock = function () {
44813        var override = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
44814        // eslint-disable-line no-unused-vars
44815        if (this.spacing) {
44816          this.add(" ");
44817          this.spacing = false;
44818        }
44819        if (this.buffer.length > 0) {
44820          blocks.push({ text: this.buffer, mod: this.modName() });
44821          this.buffer = "";
44822        }
44823      };
44824
44825      s.add = function (text) {
44826        if (text === " ") {
44827          s.spacing = true;
44828        }
44829        if (s.spacing) {
44830          this.buffer += " ";
44831          this.spacing = false;
44832        }
44833        if (text != " ") {
44834          this.buffer += text;
44835        }
44836      };
44837
44838      while (s.position < text.length) {
44839        var ch = text.charAt(s.position);
44840        if (/[ \t]/.test(ch)) {
44841          if (!s.mono) {
44842            s.spacing = true;
44843          } else {
44844            s.add(ch);
44845          }
44846          s.beginable = true;
44847        } else if (/\\/.test(ch)) {
44848          if (s.position < text.length + 1) {
44849            s.position++;
44850            ch = text.charAt(s.position);
44851            if (/ \t/.test(ch)) {
44852              s.spacing = true;
44853            } else {
44854              s.add(ch);
44855              s.beginable = false;
44856            }
44857          }
44858        } else if (!s.mono && !s.bold && (s.beginable || s.spacing) && /\*/.test(ch)) {
44859          s.emitBlock();
44860          s.bold = true;
44861          s.modStack.unshift("bold");
44862        } else if (!s.mono && !s.ital && (s.beginable || s.spacing) && /\_/.test(ch)) {
44863          s.emitBlock();
44864          s.ital = true;
44865          s.modStack.unshift("ital");
44866        } else if (!s.mono && (s.beginable || s.spacing) && /`/.test(ch)) {
44867          s.emitBlock();
44868          s.mono = true;
44869          s.modStack.unshift("mono");
44870        } else if (!s.mono && s.mod() === "bold" && /\*/.test(ch)) {
44871          if (s.position === text.length - 1 || /[.,_` \t\n]/.test(text.charAt(s.position + 1))) {
44872            s.emitBlock();
44873            s.bold = false;
44874            s.modStack.shift();
44875          } else {
44876            s.add(ch);
44877          }
44878        } else if (!s.mono && s.mod() === "ital" && /\_/.test(ch)) {
44879          if (s.position === text.length - 1 || /[.,*` \t\n]/.test(text.charAt(s.position + 1))) {
44880            s.emitBlock();
44881            s.ital = false;
44882            s.modStack.shift();
44883          } else {
44884            s.add(ch);
44885          }
44886        } else if (s.mono && s.mod() === "mono" && /`/.test(ch)) {
44887          if (s.position === text.length - 1 || /[.,*_ \t\n]/.test(text.charAt(s.position + 1))) {
44888            s.emitBlock();
44889            s.mono = false;
44890            s.modStack.shift();
44891          } else {
44892            s.add(ch);
44893          }
44894        } else {
44895          s.add(ch);
44896          s.beginable = false;
44897        }
44898        s.position++;
44899      }
44900      s.emitBlock();
44901      return blocks;
44902    }
44903
44904    /**
44905     * Explodes a piece of text into single-font blocks using a given markup
44906     *
44907     * @param {string} text
44908     * @param {boolean|'md'|'markdown'|'html'} markupSystem
44909     * @returns {Array.<{text: string, mod: string}>}
44910     * @private
44911     */
44912
44913  }, {
44914    key: 'splitBlocks',
44915    value: function splitBlocks(text, markupSystem) {
44916      var system = this.decodeMarkupSystem(markupSystem);
44917      if (system === 'none') {
44918        return [{
44919          text: text,
44920          mod: 'normal'
44921        }];
44922      } else if (system === 'markdown') {
44923        return this.splitMarkdownBlocks(text);
44924      } else if (system === 'html') {
44925        return this.splitHtmlBlocks(text);
44926      }
44927    }
44928
44929    /**
44930     * @param {string} text
44931     * @returns {boolean} true if text length over the current max with
44932     * @private
44933     */
44934
44935  }, {
44936    key: 'overMaxWidth',
44937    value: function overMaxWidth(text) {
44938      var width = this.ctx.measureText(text).width;
44939      return this.lines.curWidth() + width > this.parent.fontOptions.maxWdt;
44940    }
44941
44942    /**
44943     * Determine the longest part of the sentence which still fits in the 
44944     * current max width.
44945     * 
44946     * @param {Array}
44946 words  Array of strings signifying a text lines
44947     * @return {number}      index of first item in string making string go over max
44948     * @private
44949     */
44950
44951  }, {
44952    key: 'getLongestFit',
44953    value: function getLongestFit(words) {
44954      var text = '';
44955      var w = 0;
44956
44957      while (w < words.length) {
44958        var pre = text === '' ? '' : ' ';
44959        var newText = text + pre + words[w];
44960
44961        if (this.overMaxWidth(newText)) break;
44962        text = newText;
44963        w++;
44964      }
44965
44966      return w;
44967    }
44968
44969    /**
44970     * Determine the longest part of the string which still fits in the
44971     * current max width.
44972     * 
44973     * @param {Array} words Array of strings signifying a text lines
44974     * @return {number} index of first item in string making string go over max
44975     */
44976
44977  }, {
44978    key: 'getLongestFitWord',
44979    value: function getLongestFitWord(words) {
44980      var w = 0;
44981
44982      while (w < words.length) {
44983        if (this.overMaxWidth(words.slice(0, w))) break;
44984        w++;
44985      }
44986
44987      return w;
44988    }
44989
44990    /**
44991     * Split the passed text into lines, according to width constraint (if any).
44992     * 
44993     * The method assumes that the input string is a single line, i.e. without lines break.
44994     *
44995     * This method retains spaces, if still present (case `font.multi: false`).
44996     * A space which falls on an internal line break, will be replaced by a newline.
44997     * There is no special handling of tabs; these go along with the flow.
44998     * 
44999     * @param {string} str
45000     * @param {string} [mod='normal']
45001     * @param {boolean} [appendLast=false]
45002     * @private
45003     */
45004
45005  }, {
45006    key: 'splitStringIntoLines',
45007    value: function splitStringIntoLines(str) {
45008      var mod = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'normal';
45009      var appendLast = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
45010
45011      // Still-present spaces are relevant, retain them
45012      str = str.replace(/^( +)/g, '$1\r');
45013      str = str.replace(/([^\r][^ ]*)( +)/g, '$1\r$2\r');
45014      var words = str.split('\r');
45015
45016      while (words.length > 0) {
45017        var w = this.getLongestFit(words);
45018
45019        if (w === 0) {
45020          // Special case: the first word is already larger than the max width.
45021          var word = words[0];
45022
45023          // Break the word to the largest part that fits the line
45024          var x = this.getLongestFitWord(word);
45025          this.lines.newLine(word.slice(0, x), mod);
45026
45027          // Adjust the word, so that the rest will be done next iteration
45028          words[0] = word.slice(x);
45029        } else {
45030          // skip any space that is replaced by a newline
45031          var newW = w;
45032          if (words[w - 1] === ' ') {
45033            w--;
45034          } else if (words[newW] === ' ') {
45035            newW++;
45036          }
45037
45038          var text = words.slice(0, w).join("");
45039
45040          if (w == words.length && appendLast) {
45041            this.lines.append(text, mod);
45042          } else {
45043            this.lines.newLine(text, mod);
45044          }
45045
45046          // Adjust the word, so that the rest will be done next iteration
45047          words = words.slice(newW);
45048        }
45049      }
45050    }
45051  }]);
45052  return LabelSplitter;
45053}();
45054
45055exports['default'] = LabelSplitter;
45056
45057/***/ }),
45058/* 192 */
45059/***/ (function(module, exports, __webpack_require__) {
45060
45061"use strict";
45062
45063
45064Object.defineProperty(exports, "__esModule", {
45065  value: true
45066});
45067
45068var _assign = __webpack_require__(90);
45069
45070var _assign2 = _interopRequireDefault(_assign);
45071
45072var _classCallCheck2 = __webpack_require__(0);
45073
45074var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
45075
45076var _createClass2 = __webpack_require__(1);
45077
45078var _createClass3 = _interopRequireDefault(_createClass2);
45079
45080function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
45081
45082/**
45083 * Callback to determine text dimensions, using the parent label settings.
45084 * @callback MeasureText
45085 * @param {text} text
45086 * @param {text} mod
45087 * @return {Object} { width, values} width in pixels and font attributes
45088 */
45089
45090/**
45091 * Helper class for Label which collects results of splitting labels into lines and blocks.
45092 *
45093 * @private
45094 */
45095var LabelAccumulator = function () {
45096
45097  /**
45098   * @param {MeasureText} measureText
45099   */
45100  function LabelAccumulator(measureText) {
45101    (0, _classCallCheck3["default"])(this, LabelAccumulator);
45102
45103    this.measureText = measureText;
45104    this.current = 0;
45105    this.width = 0;
45106    this.height = 0;
45107    this.lines = [];
45108  }
45109
45110  /**
45111   * Append given text to the given line.
45112   *
45113   * @param {number}  l    index of line to add to
45114   * @param {string}
45114  text string to append to line
45115   * @param {'bold'|'ital'|'boldital'|'mono'|'normal'} [mod='normal']
45116   * @private
45117   */
45118
45119
45120  (0, _createClass3["default"])(LabelAccumulator, [{
45121    key: "_add",
45122    value: function _add(l, text) {
45123      var mod = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 'normal';
45124
45125
45126      if (this.lines[l] === undefined) {
45127        this.lines[l] = {
45128          width: 0,
45129          height: 0,
45130          blocks: []
45131        };
45132      }
45133
45134      // We still need to set a block for undefined and empty texts, hence return at this point
45135      // This is necessary because we don't know at this point if we're at the
45136      // start of an empty line or not.
45137      // To compensate, empty blocks are removed in `finalize()`.
45138      //
45139      // Empty strings should still have a height
45140      var tmpText = text;
45141      if (text === undefined || text === "") tmpText = " ";
45142
45143      // Determine width and get the font properties
45144      var result = this.measureText(tmpText, mod);
45145      var block = (0, _assign2["default"])({}, result.values);
45146      block.text = text;
45147      block.width = result.width;
45148      block.mod = mod;
45149
45150      if (text === undefined || text === "") {
45151        block.width = 0;
45152      }
45153
45154      this.lines[l].blocks.push(block);
45155
45156      // Update the line width. We need this for determining if a string goes over max width
45157      this.lines[l].width += block.width;
45158    }
45159
45160    /**
45161     * Returns the width in pixels of the current line.
45162     *
45163     * @returns {number}
45164     */
45165
45166  }, {
45167    key: "curWidth",
45168    value: function curWidth() {
45169      var line = this.lines[this.current];
45170      if (line === undefined) return 0;
45171
45172      return line.width;
45173    }
45174
45175    /**
45176     * Add text in block to current line
45177     *
45178     * @param {string} text
45179     * @param {'bold'|'ital'|'boldital'|'mono'|'normal'} [mod='normal']
45180     */
45181
45182  }, {
45183    key: "append",
45184    value: function append(text) {
45185      var mod = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'normal';
45186
45187      this._add(this.current, text, mod);
45188    }
45189
45190    /**
45191     * Add text in block to current line and start a new line
45192     *
45193     * @param {string} text
45194     * @param {'bold'|'ital'|'boldital'|'mono'|'normal'} [mod='normal']
45195     */
45196
45197  }, {
45198    key: "newLine",
45199    value: function newLine(text) {
45200      var mod = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'normal';
45201
45202      this._add(this.current, text, mod);
45203      this.current++;
45204    }
45205
45206    /**
45207     * Determine and set the heights of all the lines currently contained in this instance
45208     *
45209     * Note that width has already been set.
45210     * 
45211     * @private
45212     */
45213
45214  }, {
45215    key: "determineLineHeights",
45216    value: function determineLineHeights() {
45217      for (var k = 0; k < this.lines.length; k++) {
45218        var line = this.lines[k];
45219
45220        // Looking for max height of blocks in line
45221        var height = 0;
45222
45223        if (line.blocks !== undefined) {
45224          // Can happen if text contains e.g. '\n '
45225          for (var l = 0; l < line.blocks.length; l++) {
45226            var block = line.blocks[l];
45227
45228            if (height < block.height) {
45229              height = block.height;
45230            }
45231          }
45232        }
45233
45234        line.height = height;
45235      }
45236    }
45237
45238    /**
45239     * Determine the full size of the label text, as determined by current lines and blocks
45240     * 
45241     * @private
45242     */
45243
45244  }, {
45245    key: "determineLabelSize",
45246    value: function determineLabelSize() {
45247      var width = 0;
45248      var height = 0;
45249      for (var k = 0; k < this.lines.length; k++) {
45250        var line = this.lines[k];
45251
45252        if (line.width > width) {
45253          width = line.width;
45254        }
45255        height += line.height;
45256      }
45257
45258      this.width = width;
45259      this.height = height;
45260    }
45261
45262    /**
45263     * Remove all empty blocks and empty lines we don't need
45264     * 
45265     * This must be done after the width/height determination,
45266     * so that these are set properly for processing here.
45267     *
45268     * @returns {Array<Line>} Lines with empty blocks (and some empty lines) removed
45269     * @private
45270     */
45271
45272  }, {
45273    key: "removeEmptyBlocks",
45274    value: function removeEmptyBlocks() {
45275      var tmpLines = [];
45276      for (var k = 0; k < this.lines.length; k++) {
45277        var line = this.lines[k];
45278
45279        // Note: an empty line in between text has width zero but is still relevant to layout.
45280        // So we can't use width for testing empty line here
45281        if (line.blocks.length === 0) continue;
45282
45283        // Discard final empty line always
45284        if (k === this.lines.length - 1) {
45285          if (line.width === 0) continue;
45286        }
45287
45288        var tmpLine = {};
45289        (0, _assign2["default"])(tmpLine, line);
45290        tmpLine.blocks = [];
45291
45292        var firstEmptyBlock = void 0;
45293        var tmpBlocks = [];
45294        for (var l = 0; l < line.blocks.length; l++) {
45295          var block = line.blocks[l];
45296          if (block.width !== 0) {
45297            tmpBlocks.push(block);
45298          } else {
45299            if (firstEmptyBlock === undefined) {
45300              firstEmptyBlock = block;
45301            }
45302          }
45303        }
45304
45305        // Ensure that there is *some* text present
45306        if (tmpBlocks.length === 0 && firstEmptyBlock !== undefined) {
45307          tmpBlocks.push(firstEmptyBlock);
45308        }
45309
45310        tmpLine.blocks = tmpBlocks;
45311
45312        tmpLines.push(tmpLine);
45313      }
45314
45315      return tmpLines;
45316    }
45317
45318    /**
45319     * Set the sizes for all lines and the whole thing.
45320     *
45321     * @returns {{width: (number|*), height: (number|*), lines: Array}}
45322     */
45323
45324  }, {
45325    key: "finalize",
45326    value: function finalize() {
45327      //console.log(JSON.stringify(this.lines, null, 2));
45328
45329      this.determineLineHeights();
45330      this.determineLabelSize();
45331      var tmpLines = this.removeEmptyBlocks();
45332
45333      // Return a simple hash object for further processing.
45334      return {
45335        width: this.width,
45336        height: this.height,
45337        lines: tmpLines
45338      };
45339    }
45340  }]);
45341  return LabelAccumulator;
45342}();
45343
45344exports["default"] = LabelAccumulator;
45345
45346/***/ }),
45347/* 193 */
45348/***/ (function(module, exports, __webpack_require__) {
45349
45350"use strict";
45351
45352
45353Object.defineProperty(exports, "__esModule", {
45354  value: true
45355});
45356
45357var _getPrototypeOf = __webpack_require__(3);
45358
45359var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf);
45360
45361var _classCallCheck2 = __webpack_require__(0);
45362
45363var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
45364
45365var _createClass2 = __webpack_require__(1);
45366
45367var _createClass3 = _interopRequireDefault(_createClass2);
45368
45369var _possibleConstructorReturn2 = __webpack_require__(4);
45370
45371var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2);
45372
45373var _inherits2 = __webpack_require__(5);
45374
45375var _inherits3 = _interopRequireDefault(_inherits2);
45376
45377var _NodeBase2 = __webpack_require__(23);
45378
45379var _NodeBase3 = _interopRequireDefault(_NodeBase2);
45380
45381function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
45382
45383/**
45384 * A Box Node/Cluster shape.
45385 *
45386 * @extends NodeBase
45387 */
45388var Box = function (_NodeBase) {
45389  (0, _inherits3['default'])(Box, _NodeBase);
45390
45391  /**
45392   * @param {Object} options
45393   * @param {Object} body
45394   * @param {Label} labelModule
45395   */
45396  function Box(options, body, labelModule) {
45397    (0, _classCallCheck3['default'])(this, Box);
45398
45399    var _this = (0, _possibleConstructorReturn3['default'])(this, (Box.__proto__ || (0, _getPrototypeOf2['default'])(Box)).call(this, options, body, labelModule));
45400
45401    _this._setMargins(labelModule);
45402    return _this;
45403  }
45404
45405  /**
45406   *
45407   * @param {CanvasRenderingContext2D} ctx
45408   * @param {boolean} [selected]
45409   * @param {boolean} [hover]
45410   */
45411
45412
45413  (0, _createClass3['default'])(Box, [{
45414    key: 'resize',
45415    value: function resize(ctx) {
45416      var selected = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : this.selected;
45417      var hover = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : this.hover;
45418
45419      if (this.needsRefresh(selected, hover)) {
45420        var dimensions = this.getDimensionsFromLabel(ctx, selected, hover);
45421
45422        this.width = dimensions.width + this.margin.right + this.margin.left;
45423        this.height = dimensions.height + this.margin.top + this.margin.bottom;
45424        this.radius = this.width / 2;
45425      }
45426    }
45427
45428    /**
45429     *
45430     * @param {CanvasRenderingContext2D} ctx
45431     * @param {number} x width
45432     * @param {number} y height
45433     * @param {boolean} selected
45434     * @param {boolean} hover
45435     * @param {{toArrow: boolean, toArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), toArrowType: *, middleArrow: boolean, middleArrowScale: (number|allOptions.edges.arrows.middle.scaleFactor|{number}|Array), middleArrowType: (allOptions.edges.arrows.middle.type|{string}|string|*), fromArrow: boolean, fromArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), fromArrowType: *, arrowStrikethrough: (*|boolean|allOptions.edges.arrowStrikethrough|{boolean}), color: undefined, inheritsColor: (string|string|string|allOptions.edges.color.inherit|{string, boolean}|Array|*), opacity: *, hidden: *, length: *, shadow: *, shadowColor: *, shadowSize: *, shadowX: *, shadowY: *, dashes: (*|boolean|Array|allOptions.edges.dashes|{boolean, array}), width: *}} values
45436     */
45437
45438  }, {
45439    key: 'draw',
45440    value: function draw(ctx, x, y, selected, hover, values) {
45441      this.resize(ctx, selected, hover);
45442      this.left = x - this.width / 2;
45443      this.top = y - this.height / 2;
45444
45445      this.initContextForDraw(ctx, values);
45446      ctx.roundRect(this.left, this.top, this.width, this.height, values.borderRadius);
45447      this.performFill(ctx, values);
45448
45449      this.updateBoundingBox(x, y, ctx, selected, hover);
45450      this.labelModule.draw(ctx, this.left + this.textSize.width / 2 + this.margin.left, this.top + this.textSize.height / 2 + this.margin.top, selected, hover);
45451    }
45452
45453    /**
45454     *
45455     * @param {number} x width
45456     * @param {number} y height
45457     * @param {CanvasRenderingContext2D} ctx
45458     * @param {boolean} selected
45459     * @param {boolean} hover
45460     */
45461
45462  }, {
45463    key: 'updateBoundingBox',
45464    value: function updateBoundingBox(x, y, ctx, selected, hover) {
45465      this._updateBoundingBox(x, y, ctx, selected, hover);
45466
45467      var borderRadius = this.options.shapeProperties.borderRadius; // only effective for box
45468      this._addBoundingBoxMargin(borderRadius);
45469    }
45470
45471    /**
45472     *
45473     * @param {CanvasRenderingContext2D} ctx
45474     * @param {number} angle
45475     * @returns {number}
45476     */
45477
45478  }, {
45479    key: 'distanceToBorder',
45480    value: function distanceToBorder(ctx, angle) {
45481      this.resize(ctx);
45482      var borderWidth = this.options.borderWidth;
45483
45484      return Math.min(Math.abs(this.width / 2 / Math.cos(angle)), Math.abs(this.height / 2 / Math.sin(angle))) + borderWidth;
45485    }
45486  }]);
45487  return Box;
45488}(_NodeBase3['default']);
45489
45490exports['default'] = Box;
45491
45492/***/ }),
45493/* 194 */
45494/***/ (function(module, exports, __webpack_require__) {
45495
45496__webpack_require__(195);
45497module.exports = __webpack_require__(7).Object.getPrototypeOf;
45498
45499
45500/***/ }),
45501/* 195 */
45502/***/ (function(module, exports, __webpack_require__) {
45503
45504// 19.1.2.9 Object.getPrototypeOf(O)
45505var toObject = __webpack_require__(41);
45506var $getPrototypeOf = __webpack_require__(85);
45507
45508__webpack_require__(87)('getPrototypeOf', function () {
45509  return function getPrototypeOf(it) {
45510    return $getPrototypeOf(toObject(it));
45511  };
45512});
45513
45514
45515/***/ }),
45516/* 196 */
45517/***/ (function(module, exports, __webpack_require__) {
45518
45519module.exports = { "default": __webpack_require__(197), __esModule: true };
45520
45521/***/ }),
45522/* 197 */
45523/***/ (function(module, exports, __webpack_require__) {
45524
45525__webpack_require__(198);
45526module.exports = __webpack_require__(7).Object.setPrototypeOf;
45527
45528
45529/***/ }),
45530/* 198 */
45531/***/ (function(module, exports, __webpack_require__) {
45532
45533// 19.1.3.19 Object.setPrototypeOf(O, proto)
45534var $export = __webpack_require__(17);
45535$export($export.S, 'Object', { setPrototypeOf: __webpack_require__(199).set });
45536
45537
45538/***/ }),
45539/* 199 */
45540/***/ (function(module, exports, __webpack_require__) {
45541
45542// Works with __proto__ only. Old v8 can't work with null proto objects.
45543/* eslint-disable no-proto */
45544var isObject = __webpack_require__(32);
45545var anObject = __webpack_require__(27);
45546var check = function (O, proto) {
45547  anObject(O);
45548  if (!isObject(proto) && proto !== null) throw TypeError(proto + ": can't set as prototype!");
45549};
45550module.exports = {
45551  set: Object.setPrototypeOf || ('__proto__' in {} ? // eslint-disable-line
45552    function (test, buggy, set) {
45553      try {
45554        set = __webpack_require__(80)(Function.call, __webpack_require__(89).f(Object.prototype, '__proto__').set, 2);
45555        set(test, []);
45556        buggy = !(test instanceof Array);
45557      } catch (e) { buggy = true; }
45558      return function setPrototypeOf(O, proto) {
45559        check(O, proto);
45560        if (buggy) O.__proto__ = proto;
45561        else set(O, proto);
45562        return O;
45563      };
45564    }({}, false) : undefined),
45565  check: check
45566};
45567
45568
45569/***/ }),
45570/* 200 */
45571/***/ (function(module, exports, __webpack_require__) {
45572
45573"use strict";
45574
45575
45576Object.defineProperty(exports, "__esModule", {
45577  value: true
45578});
45579
45580var _getPrototypeOf = __webpack_require__(3);
45581
45582var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf);
45583
45584var _classCallCheck2 = __webpack_require__(0);
45585
45586var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
45587
45588var _createClass2 = __webpack_require__(1);
45589
45590var _createClass3 = _interopRequireDefault(_createClass2);
45591
45592var _possibleConstructorReturn2 = __webpack_require__(4);
45593
45594var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2);
45595
45596var _inherits2 = __webpack_require__(5);
45597
45598var _inherits3 = _interopRequireDefault(_inherits2);
45599
45600var _CircleImageBase2 = __webpack_require__(73);
45601
45602var _CircleImageBase3 = _interopRequireDefault(_CircleImageBase2);
45603
45604function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
45605
45606/**
45607 * A Circle Node/Cluster shape.
45608 *
45609 * @extends CircleImageBase
45610 */
45611var Circle = function (_CircleImageBase) {
45612  (0, _inherits3['default'])(Circle, _CircleImageBase);
45613
45614  /**
45615   * @param {Object} options
45616   * @param {Object} body
45617   * @param {Label} labelModule
45618   */
45619  function Circle(options, body, labelModule) {
45620    (0, _classCallCheck3['default'])(this, Circle);
45621
45622    var _this = (0, _possibleConstructorReturn3['default'])(this, (Circle.__proto__ || (0, _getPrototypeOf2['default'])(Circle)).call(this, options, body, labelModule));
45623
45624    _this._setMargins(labelModule);
45625    return _this;
45626  }
45627
45628  /**
45629   *
45630   * @param {CanvasRenderingContext2D} ctx
45631   * @param {boolean} [selected]
45632   * @param {boolean} [hover]
45633   */
45634
45635
45636  (0, _createClass3['default'])(Circle, [{
45637    key: 'resize',
45638    value: function resize(ctx) {
45639      var selected = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : this.selected;
45640      var hover = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : this.hover;
45641
45642      if (this.needsRefresh(selected, hover)) {
45643        var dimensions = this.getDimensionsFromLabel(ctx, selected, hover);
45644
45645        var diameter = Math.max(dimensions.width + this.margin.right + this.margin.left, dimensions.height + this.margin.top + this.margin.bottom);
45646
45647        this.options.size = diameter / 2; // NOTE: this size field only set here, not in Ellipse, Database, Box
45648        this.width = diameter;
45649        this.height = diameter;
45650        this.radius = this.width / 2;
45651      }
45652    }
45653
45654    /**
45655     *
45656     * @param {CanvasRenderingContext2D} ctx
45657     * @param {number} x width
45658     * @param {number} y height
45659     * @param {boolean} selected
45660     * @param {boolean} hover
45661     * @param {{toArrow: boolean, toArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), toArrowType: *, middleArrow: boolean, middleArrowScale: (number|allOptions.edges.arrows.middle.scaleFactor|{number}|Array), middleArrowType: (allOptions.edges.arrows.middle.type|{string}|string|*), fromArrow: boolean, fromArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), fromArrowType: *, arrowStrikethrough: (*|boolean|allOptions.edges.arrowStrikethrough|{boolean}), color: undefined, inheritsColor: (string|string|string|allOptions.edges.color.inherit|{string, boolean}|Array|*), opacity: *, hidden: *, length: *, shadow: *, shadowColor: *, shadowSize: *, shadowX: *, shadowY: *, dashes: (*|boolean|Array|allOptions.edges.dashes|{boolean, array}), width: *}} values
45662     */
45663
45664  }, {
45665    key: 'draw',
45666    value: function draw(ctx, x, y, selected, hover, values) {
45667      this.resize(ctx, selected, hover);
45668      this.left = x - this.width / 2;
45669      this.top = y - this.height / 2;
45670
45671      this._drawRawCircle(ctx, x, y, values);
45672
45673      this.updateBoundingBox(x, y);
45674      this.labelModule.draw(ctx, this.left + this.textSize.width / 2 + this.margin.left, y, selected, hover);
45675    }
45676
45677    /**
45678     *
45679     * @param {number} x width
45680     * @param {number} y height
45681     */
45682
45683  }, {
45684    key: 'updateBoundingBox',
45685    value: function updateBoundingBox(x, y) {
45686      this.boundingBox.top = y - this.options.size;
45687      this.boundingBox.left = x - this.options.size;
45688      this.boundingBox.right = x + this.options.size;
45689      this.boundingBox.bottom = y + this.options.size;
45690    }
45691
45692    /**
45693     *
45694     * @param {CanvasRenderingContext2D} ctx
45695     * @param {number} angle - Unused
45696     * @returns {number}
45697     */
45698
45699  }, {
45700    key: 'distanceToBorder',
45701    value: function distanceToBorder(ctx, angle) {
45702      // eslint-disable-line no-unused-vars
45703      this.resize(ctx);
45704      return this.width * 0.5;
45705    }
45706  }]);
45707  return Circle;
45708}
45708(_CircleImageBase3['default']);
45709
45710exports['default'] = Circle;
45711
45712/***/ }),
45713/* 201 */
45714/***/ (function(module, exports, __webpack_require__) {
45715
45716"use strict";
45717
45718
45719Object.defineProperty(exports, "__esModule", {
45720  value: true
45721});
45722
45723var _getPrototypeOf = __webpack_require__(3);
45724
45725var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf);
45726
45727var _classCallCheck2 = __webpack_require__(0);
45728
45729var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
45730
45731var _createClass2 = __webpack_require__(1);
45732
45733var _createClass3 = _interopRequireDefault(_createClass2);
45734
45735var _possibleConstructorReturn2 = __webpack_require__(4);
45736
45737var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2);
45738
45739var _inherits2 = __webpack_require__(5);
45740
45741var _inherits3 = _interopRequireDefault(_inherits2);
45742
45743var _CircleImageBase2 = __webpack_require__(73);
45744
45745var _CircleImageBase3 = _interopRequireDefault(_CircleImageBase2);
45746
45747function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
45748
45749/**
45750 * A CircularImage Node/Cluster shape.
45751 *
45752 * @extends CircleImageBase
45753 */
45754var CircularImage = function (_CircleImageBase) {
45755  (0, _inherits3['default'])(CircularImage, _CircleImageBase);
45756
45757  /**
45758   * @param {Object} options
45759   * @param {Object} body
45760   * @param {Label} labelModule
45761   * @param {Image} imageObj
45762   * @param {Image} imageObjAlt
45763   */
45764  function CircularImage(options, body, labelModule, imageObj, imageObjAlt) {
45765    (0, _classCallCheck3['default'])(this, CircularImage);
45766
45767    var _this = (0, _possibleConstructorReturn3['default'])(this, (CircularImage.__proto__ || (0, _getPrototypeOf2['default'])(CircularImage)).call(this, options, body, labelModule));
45768
45769    _this.setImages(imageObj, imageObjAlt);
45770    return _this;
45771  }
45772
45773  /**
45774   *
45775   * @param {CanvasRenderingContext2D} ctx
45776   * @param {boolean} [selected]
45777   * @param {boolean} [hover]
45778   */
45779
45780
45781  (0, _createClass3['default'])(CircularImage, [{
45782    key: 'resize',
45783    value: function resize(ctx) {
45784      var selected = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : this.selected;
45785      var hover = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : this.hover;
45786
45787      var imageAbsent = this.imageObj.src === undefined || this.imageObj.width === undefined || this.imageObj.height === undefined;
45788
45789      if (imageAbsent) {
45790        var diameter = this.options.size * 2;
45791        this.width = diameter;
45792        this.height = diameter;
45793        this.radius = 0.5 * this.width;
45794        return;
45795      }
45796
45797      // At this point, an image is present, i.e. this.imageObj is valid.
45798      if (this.needsRefresh(selected, hover)) {
45799        this._resizeImage();
45800      }
45801    }
45802
45803    /**
45804     *
45805     * @param {CanvasRenderingContext2D} ctx
45806     * @param {number} x width
45807     * @param {number} y height
45808     * @param {boolean} selected
45809     * @param {boolean} hover
45810     * @param {{toArrow: boolean, toArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), toArrowType: *, middleArrow: boolean, middleArrowScale: (number|allOptions.edges.arrows.middle.scaleFactor|{number}|Array), middleArrowType: (allOptions.edges.arrows.middle.type|{string}|string|*), fromArrow: boolean, fromArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), fromArrowType: *, arrowStrikethrough: (*|boolean|allOptions.edges.arrowStrikethrough|{boolean}), color: undefined, inheritsColor: (string|string|string|allOptions.edges.color.inherit|{string, boolean}|Array|*), opacity: *, hidden: *, length: *, shadow: *, shadowColor: *, shadowSize: *, shadowX: *, shadowY: *, dashes: (*|boolean|Array|allOptions.edges.dashes|{boolean, array}), width: *}} values
45811     */
45812
45813  }, {
45814    key: 'draw',
45815    value: function draw(ctx, x, y, selected, hover, values) {
45816      this.switchImages(selected);
45817      this.resize();
45818      this.left = x - this.width / 2;
45819      this.top = y - this.height / 2;
45820
45821      // draw the background circle. IMPORTANT: the stroke in this method is used by the clip method below.
45822      this._drawRawCircle(ctx, x, y, values);
45823
45824      // now we draw in the circle, we save so we can revert the clip operation after drawing.
45825      ctx.save();
45826      // clip is used to use the stroke in drawRawCircle as an area that we can draw in.
45827      ctx.clip();
45828      // draw the image
45829      this._drawImageAtPosition(ctx, values);
45830      // restore so we can again draw on the full canvas
45831      ctx.restore();
45832
45833      this._drawImageLabel(ctx, x, y, selected, hover);
45834
45835      this.updateBoundingBox(x, y);
45836    }
45837
45838    // TODO: compare with Circle.updateBoundingBox(), consolidate? More stuff is happening here
45839    /**
45840     *
45841     * @param {number} x width
45842     * @param {number} y height
45843     */
45844
45845  }, {
45846    key: 'updateBoundingBox',
45847    value: function updateBoundingBox(x, y) {
45848      this.boundingBox.top = y - this.options.size;
45849      this.boundingBox.left = x - this.options.size;
45850      this.boundingBox.right = x + this.options.size;
45851      this.boundingBox.bottom = y + this.options.size;
45852
45853      // TODO: compare with Image.updateBoundingBox(), consolidate?
45854      this.boundingBox.left = Math.min(this.boundingBox.left, this.labelModule.size.left);
45855      this.boundingBox.right = Math.max(this.boundingBox.right, this.labelModule.size.left + this.labelModule.size.width);
45856      this.boundingBox.bottom = Math.max(this.boundingBox.bottom, this.boundingBox.bottom + this.labelOffset);
45857    }
45858
45859    /**
45860     *
45861     * @param {CanvasRenderingContext2D} ctx
45862     * @param {number} angle - Unused
45863     * @returns {number}
45864     */
45865
45866  }, {
45867    key: 'distanceToBorder',
45868    value: function distanceToBorder(ctx, angle) {
45869      // eslint-disable-line no-unused-vars
45870      this.resize(ctx);
45871      return this.width * 0.5;
45872    }
45873  }]);
45874  return CircularImage;
45875}
45875(_CircleImageBase3['default']);
45876
45877exports['default'] = CircularImage;
45878
45879/***/ }),
45880/* 202 */
45881/***/ (function(module, exports, __webpack_require__) {
45882
45883"use strict";
45884
45885
45886Object.defineProperty(exports, "__esModule", {
45887  value: true
45888});
45889
45890var _getPrototypeOf = __webpack_require__(3);
45891
45892var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf);
45893
45894var _classCallCheck2 = __webpack_require__(0);
45895
45896var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
45897
45898var _createClass2 = __webpack_require__(1);
45899
45900var _createClass3 = _interopRequireDefault(_createClass2);
45901
45902var _possibleConstructorReturn2 = __webpack_require__(4);
45903
45904var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2);
45905
45906var _inherits2 = __webpack_require__(5);
45907
45908var _inherits3 = _interopRequireDefault(_inherits2);
45909
45910var _NodeBase2 = __webpack_require__(23);
45911
45912var _NodeBase3 = _interopRequireDefault(_NodeBase2);
45913
45914function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
45915
45916/**
45917 * A Database Node/Cluster shape.
45918 *
45919 * @extends NodeBase
45920 */
45921var Database = function (_NodeBase) {
45922  (0, _inherits3['default'])(Database, _NodeBase);
45923
45924  /**
45925   * @param {Object} options
45926   * @param {Object} body
45927   * @param {Label} labelModule
45928   */
45929  function Database(options, body, labelModule) {
45930    (0, _classCallCheck3['default'])(this, Database);
45931
45932    var _this = (0, _possibleConstructorReturn3['default'])(this, (Database.__proto__ || (0, _getPrototypeOf2['default'])(Database)).call(this, options, body, labelModule));
45933
45934    _this._setMargins(labelModule);
45935    return _this;
45936  }
45937
45938  /**
45939   *
45940   * @param {CanvasRenderingContext2D} ctx
45941   * @param {boolean} selected
45942   * @param {boolean} hover
45943   */
45944
45945
45946  (0, _createClass3['default'])(Database, [{
45947    key: 'resize',
45948    value: function resize(ctx, selected, hover) {
45949      if (this.needsRefresh(selected, hover)) {
45950        var dimensions = this.getDimensionsFromLabel(ctx, selected, hover);
45951        var size = dimensions.width + this.margin.right + this.margin.left;
45952
45953        this.width = size;
45954        this.height = size;
45955        this.radius = this.width / 2;
45956      }
45957    }
45958
45959    /**
45960     *
45961     * @param {CanvasRenderingContext2D} ctx
45962     * @param {number} x width
45963     * @param {number} y height
45964     * @param {boolean} selected
45965     * @param {boolean} hover
45966     * @param {{toArrow: boolean, toArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), toArrowType: *, middleArrow: boolean, middleArrowScale: (number|allOptions.edges.arrows.middle.scaleFactor|{number}|Array), middleArrowType: (allOptions.edges.arrows.middle.type|{string}|string|*), fromArrow: boolean, fromArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), fromArrowType: *, arrowStrikethrough: (*|boolean|allOptions.edges.arrowStrikethrough|{boolean}), color: undefined, inheritsColor: (string|string|string|allOptions.edges.color.inherit|{string, boolean}|Array|*), opacity: *, hidden: *, length: *, shadow: *, shadowColor: *, shadowSize: *, shadowX: *, shadowY: *, dashes: (*|boolean|Array|allOptions.edges.dashes|{boolean, array}), width: *}} values
45967     */
45968
45969  }, {
45970    key: 'draw',
45971    value: function draw(ctx, x, y, selected, hover, values) {
45972      this.resize(ctx, selected, hover);
45973      this.left = x - this.width / 2;
45974      this.top = y - this.height / 2;
45975
45976      this.initContextForDraw(ctx, values);
45977      ctx.database(x - this.width / 2, y - this.height / 2, this.width, this.height);
45978      this.performFill(ctx, values);
45979
45980      this.updateBoundingBox(x, y, ctx, selected, hover);
45981      this.labelModule.draw(ctx, this.left + this.textSize.width / 2 + this.margin.left, this.top + this.textSize.height / 2 + this.margin.top, selected, hover);
45982    }
45983    /**
45984     *
45985     * @param {CanvasRenderingContext2D} ctx
45986     * @param {number} angle
45987     * @returns {number}
45988     */
45989
45990  }, {
45991    key: 'distanceToBorder',
45992    value: function distanceToBorder(ctx, angle) {
45993      return this._distanceToBorder(ctx, angle);
45994    }
45995  }]);
45996  return Database;
45997}(_NodeBase3['default']);
45998
45999exports['default'] = Database;
46000
46001/***/ }),
46002/* 203 */
46003/***/ (function(module, exports, __webpack_require__) {
46004
46005"use strict";
46006
46007
46008Object.defineProperty(exports, "__esModule", {
46009  value: true
46010});
46011
46012var _getPrototypeOf = __webpack_require__(3);
46013
46014var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf);
46015
46016var _classCallCheck2 = __webpack_require__(0);
46017
46018var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
46019
46020var _createClass2 = __webpack_require__(1);
46021
46022var _createClass3 = _interopRequireDefault(_createClass2);
46023
46024var _possibleConstructorReturn2 = __webpack_require__(4);
46025
46026var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2);
46027
46028var _inherits2 = __webpack_require__(5);
46029
46030var _inherits3 = _interopRequireDefault(_inherits2);
46031
46032var _ShapeBase2 = __webpack_require__(24);
46033
46034var _ShapeBase3 = _interopRequireDefault(_ShapeBase2);
46035
46036function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
46037
46038/**
46039 * A Diamond Node/Cluster shape.
46040 *
46041 * @extends ShapeBase
46042 */
46043var Diamond = function (_ShapeBase) {
46044  (0, _inherits3['default'])(Diamond, _ShapeBase);
46045
46046  /**
46047   * @param {Object} options
46048   * @param {Object} body
46049   * @param {Label} labelModule
46050   */
46051  function Diamond(options, body, labelModule) {
46052    (0, _classCallCheck3['default'])(this, Diamond);
46053    return (0, _possibleConstructorReturn3['default'])(this, (Diamond.__proto__ || (0, _getPrototypeOf2['default'])(Diamond)).call(this, options, body, labelModule));
46054  }
46055
46056  /**
46057   *
46058   * @param {CanvasRenderingContext2D} ctx
46059   * @param {number} x width
46060   * @param {number} y height
46061   * @param {boolean} selected
46062   * @param {boolean} hover
46063   * @param {{toArrow: boolean, toArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), toArrowType: *, middleArrow: boolean, middleArrowScale: (number|allOptions.edges.arrows.middle.scaleFactor|{number}|Array), middleArrowType: (allOptions.edges.arrows.middle.type|{string}|string|*), fromArrow: boolean, fromArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), fromArrowType: *, arrowStrikethrough: (*|boolean|allOptions.edges.arrowStrikethrough|{boolean}), color: undefined, inheritsColor: (string|string|string|allOptions.edges.color.inherit|{string, boolean}|Array|*), opacity: *, hidden: *, length: *, shadow: *, shadowColor: *, shadowSize: *, shadowX: *, shadowY: *, dashes: (*|boolean|Array|allOptions.edges.dashes|{boolean, array}), width: *}} values
46064   */
46065
46066
46067  (0, _createClass3['default'])(Diamond, [{
46068    key: 'draw',
46069    value: function draw(ctx, x, y, selected, hover, values) {
46070      this._drawShape(ctx, 'diamond', 4, x, y, selected, hover, values);
46071    }
46072
46073    /**
46074     *
46075     * @param {CanvasRenderingContext2D} ctx
46076     * @param {number} angle
46077     * @returns {number}
46078     */
46079
46080  }, {
46081    key: 'distanceToBorder',
46082    value: function distanceToBorder(ctx, angle) {
46083      return this._distanceToBorder(ctx, angle);
46084    }
46085  }]);
46086  return Diamond;
46087}(_ShapeBase3['default']);
46088
46089exports['default'] = Diamond;
46090
46091/***/ }),
46092/* 204 */
46093/***/ (function(module, exports, __webpack_require__) {
46094
46095"use strict";
46096
46097
46098Object.defineProperty(exports, "__esModule", {
46099  value: true
46100});
46101
46102var _getPrototypeOf = __webpack_require__(3);
46103
46104var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf);
46105
46106var _classCallCheck2 = __webpack_require__(0);
46107
46108var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
46109
46110var _createClass2 = __webpack_require__(1);
46111
46112var _createClass3 = _interopRequireDefault(_createClass2);
46113
46114var _possibleConstructorReturn2 = __webpack_require__(4);
46115
46116var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2);
46117
46118var _inherits2 = __webpack_require__(5);
46119
46120var _inherits3 = _interopRequireDefault(_inherits2);
46121
46122var _ShapeBase2 = __webpack_require__(24);
46123
46124var _ShapeBase3 = _interopRequireDefault(_ShapeBase2);
46125
46126function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
46127
46128/**
46129 * A Dot Node/Cluster shape.
46130 *
46131 * @extends ShapeBase
46132 */
46133var Dot = function (_ShapeBase) {
46134  (0, _inherits3['default'])(Dot, _ShapeBase);
46135
46136  /**
46137   * @param {Object} options
46138   * @param {Object} body
46139   * @param {Label} labelModule
46140   */
46141  function Dot(options, body, labelModule) {
46142    (0, _classCallCheck3['default'])(this, Dot);
46143    return (0, _possibleConstructorReturn3['default'])(this, (Dot.__proto__ || (0, _getPrototypeOf2['default'])(Dot)).call(this, options, body, labelModule));
46144  }
46145
46146  /**
46147   *
46148   * @param {CanvasRenderingContext2D} ctx
46149   * @param {number} x width
46150   * @param {number} y height
46151   * @param {boolean} selected
46152   * @param {boolean} hover
46153   * @param {{toArrow: boolean, toArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), toArrowType: *, middleArrow: boolean, middleArrowScale: (number|allOptions.edges.arrows.middle.scaleFactor|{number}|Array), middleArrowType: (allOptions.edges.arrows.middle.type|{string}|string|*), fromArrow: boolean, fromArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), fromArrowType: *, arrowStrikethrough: (*|boolean|allOptions.edges.arrowStrikethrough|{boolean}), color: undefined, inheritsColor: (string|string|string|allOptions.edges.color.inherit|{string, boolean}|Array|*), opacity: *, hidden: *, length: *, shadow: *, shadowColor: *, shadowSize: *, shadowX: *, shadowY: *, dashes: (*|boolean|Array|allOptions.edges.dashes|{boolean, array}), width: *}} values
46154   */
46155
46156
46157  (0, _createClass3['default'])(Dot, [{
46158    key: 'draw',
46159    value: function draw(ctx, x, y, selected, hover, values) {
46160      this._drawShape(ctx, 'circle', 2, x, y, selected, hover, values);
46161    }
46162
46163    /**
46164     *
46165     * @param {CanvasRenderingContext2D} ctx
46166     * @param {number} angle
46167     * @returns {number}
46168     */
46169
46170  }, {
46171    key: 'distanceToBorder',
46172    value: function distanceToBorder(ctx, angle) {
46173      // eslint-disable-line no-unused-vars
46174      this.resize(ctx);
46175      return this.options.size;
46176    }
46177  }]);
46178  return Dot;
46179}(_ShapeBase3['default']);
46180
46181exports['default'] = Dot;
46182
46183/***/ }),
46184/* 205 */
46185/***/ (function(module, exports, __webpack_require__) {
46186
46187"use strict";
46188
46189
46190Object.defineProperty(exports, "__esModule", {
46191  value: true
46192});
46193
46194var _getPrototypeOf = __webpack_require__(3);
46195
46196var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf);
46197
46198var _classCallCheck2 = __webpack_require__(0);
46199
46200var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
46201
46202var _createClass2 = __webpack_require__(1);
46203
46204var _createClass3 = _interopRequireDefault(_createClass2);
46205
46206var _possibleConstructorReturn2 = __webpack_require__(4);
46207
46208var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2);
46209
46210var _inherits2 = __webpack_require__(5);
46211
46212var _inherits3 = _interopRequireDefault(_inherits2);
46213
46214var _NodeBase2 = __webpack_require__(23);
46215
46216var _NodeBase3 = _interopRequireDefault(_NodeBase2);
46217
46218function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
46219
46220/**
46221 * Am Ellipse Node/Cluster shape.
46222 *
46223 * @extends NodeBase
46224 */
46225var Ellipse = function (_NodeBase) {
46226  (0, _inherits3['default'])(Ellipse, _NodeBase);
46227
46228  /**
46229   * @param {Object} options
46230   * @param {Object} body
46231   * @param {Label} labelModule
46232   */
46233  function Ellipse(options, body, labelModule) {
46234    (0, _classCallCheck3['default'])(this, Ellipse);
46235    return (0, _possibleConstructorReturn3['default'])(this, (Ellipse.__proto__ || (0, _getPrototypeOf2['default'])(Ellipse)).call(this, options, body, labelModule));
46236  }
46237
46238  /**
46239   *
46240   * @param {CanvasRenderingContext2D} ctx
46241   * @param {boolean} [selected]
46242   * @param {boolean} [hover]
46243   */
46244
46245
46246  (0, _createClass3['default'])(Ellipse, [{
46247    key: 'resize',
46248    value: function resize(ctx) {
46249      var selected = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : this.selected;
46250      var hover = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : this.hover;
46251
46252      if (this.needsRefresh(selected, hover)) {
46253        var dimensions = this.getDimensionsFromLabel(ctx, selected, hover);
46254
46255        this.height = dimensions.height * 2;
46256        this.width = dimensions.width + dimensions.height;
46257        this.radius = 0.5 * this.width;
46258      }
46259    }
46260
46261    /**
46262     *
46263     * @param {CanvasRenderingContext2D} ctx
46264     * @param {number} x width
46265     * @param {number} y height
46266     * @param {boolean} selected
46267     * @param {boolean} hover
46268     * @param {{toArrow: boolean, toArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), toArrowType: *, middleArrow: boolean, middleArrowScale: (number|allOptions.edges.arrows.middle.scaleFactor|{number}|Array), middleArrowType: (allOptions.edges.arrows.middle.type|{string}|string|*), fromArrow: boolean, fromArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), fromArrowType: *, arrowStrikethrough: (*|boolean|allOptions.edges.arrowStrikethrough|{boolean}), color: undefined, inheritsColor: (string|string|string|allOptions.edges.color.inherit|{string, boolean}|Array|*), opacity: *, hidden: *, length: *, shadow: *, shadowColor: *, shadowSize: *, shadowX: *, shadowY: *, dashes: (*|boolean|Array|allOptions.edges.dashes|{boolean, array}), width: *}} values
46269     */
46270
46271  }, {
46272    key: 'draw',
46273    value: function draw(ctx, x, y, selected, hover, values) {
46274      this.resize(ctx, selected, hover);
46275      this.left = x - this.width * 0.5;
46276      this.top = y - this.height * 0.5;
46277
46278      this.initContextForDraw(ctx, values);
46279      ctx.ellipse_vis(this.left, this.top, this.width, this.height);
46280      this.performFill(ctx, values);
46281
46282      this.updateBoundingBox(x, y, ctx, selected, hover);
46283      this.labelModule.draw(ctx, x, y, selected, hover);
46284    }
46285
46286    /**
46287     *
46288     * @param {CanvasRenderingContext2D} ctx
46289     * @param {number} angle
46290     * @returns {number}
46291     */
46292
46293  }, {
46294    key: 'distanceToBorder',
46295    value: function distanceToBorder(ctx, angle) {
46296      this.resize(ctx);
46297      var a = this.width * 0.5;
46298      var b = this.height * 0.5;
46299      var w = Math.sin(angle) * a;
46300      var h = Math.cos(angle) * b;
46301      return a * b / Math.sqrt(w * w + h * h);
46302    }
46303  }]);
46304  return Ellipse;
46305}(_NodeBase3['default']);
46306
46307exports['default'] = Ellipse;
46308
46309/***/ }),
46310/* 206 */
46311/***/ (function(module, exports, __webpack_require__) {
46312
46313"use strict";
46314
46315
46316Object.defineProperty(exports, "__esModule", {
46317  value: true
46318});
46319
46320var _getPrototypeOf = __webpack_require__(3);
46321
46322var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf);
46323
46324var _classCallCheck2 = __webpack_require__(0);
46325
46326var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
46327
46328var _createClass2 = __webpack_require__(1);
46329
46330var _createClass3 = _interopRequireDefault(_createClass2);
46331
46332var _possibleConstructorReturn2 = __webpack_require__(4);
46333
46334var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2);
46335
46336var _inherits2 = __webpack_require__(5);
46337
46338var _inherits3 = _interopRequireDefault(_inherits2);
46339
46340var _NodeBase2 = __webpack_require__(23);
46341
46342var _NodeBase3 = _interopRequireDefault(_NodeBase2);
46343
46344function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
46345
46346/**
46347 * An icon replacement for the default Node shape.
46348 *
46349 * @extends NodeBase
46350 */
46351var Icon = function (_NodeBase) {
46352  (0, _inherits3['default'])(Icon, _NodeBase);
46353
46354  /**
46355   * @param {Object} options
46356   * @param {Object} body
46357   * @param {Label} labelModule
46358   */
46359  function Icon(options, body, labelModule) {
46360    (0, _classCallCheck3['default'])(this, Icon);
46361
46362    var _this = (0, _possibleConstructorReturn3['default'])(this, (Icon.__proto__ || (0, _getPrototypeOf2['default'])(Icon)).call(this, options, body, labelModule));
46363
46364    _this._setMargins(labelModule);
46365    return _this;
46366  }
46367
46368  /**
46369   *
46370   * @param {CanvasRenderingContext2D} ctx - Unused.
46371   * @param {boolean} [selected]
46372   * @param {boolean} [hover]
46373   */
46374
46375
46376  (0, _createClass3['default'])(Icon, [{
46377    key: 'resize',
46378    value: function resize(ctx, selected, hover) {
46379      if (this.needsRefresh(selected, hover)) {
46380        this.iconSize = {
46381          width: Number(this.options.icon.size),
46382          height: Number(this.options.icon.size)
46383        };
46384        this.width = this.iconSize.width + this.margin.right + this.margin.left;
46385        this.height = this.iconSize.height + this.margin.top + this.margin.bottom;
46386        this.radius = 0.5 * this.width;
46387      }
46388    }
46389
46390    /**
46391     *
46392     * @param {CanvasRenderingContext2D} ctx
46393     * @param {number} x width
46394     * @param {number} y height
46395     * @param {boolean} selected
46396     * @param {boolean} hover
46397     * @param {{toArrow: boolean, toArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), toArrowType: *, middleArrow: boolean, middleArrowScale: (number|allOptions.edges.arrows.middle.scaleFactor|{number}|Array), middleArrowType: (allOptions.edges.arrows.middle.type|{string}|string|*), fromArrow: boolean, fromArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), fromArrowType: *, arrowStrikethrough: (*|boolean|allOptions.edges.arrowStrikethrough|{boolean}), color: undefined, inheritsColor: (string|string|string|allOptions.edges.color.inherit|{string, boolean}|Array|*), opacity: *, hidden: *, length: *, shadow: *, shadowColor: *, shadowSize: *, shadowX: *, shadowY: *, dashes: (*|boolean|Array|allOptions.edges.dashes|{boolean, array}), width: *}} values
46398     */
46399
46400  }, {
46401    key: 'draw',
46402    value: function draw(ctx, x, y, selected, hover, values) {
46403      this.resize(ctx, selected, hover);
46404      this.options.icon.size = this.options.icon.size || 50;
46405
46406      this.left = x - this.width / 2;
46407      this.top = y - this.height / 2;
46408      this._icon(ctx, x, y, selected, hover, values);
46409
46410      if (this.options.label !== undefined) {
46411        var iconTextSpacing = 5;
46412        this.labelModule.draw(ctx, this.left + this.iconSize.width / 2 + this.margin.left, y + this.height / 2 + iconTextSpacing, selected);
46413      }
46414
46415      this.updateBoundingBox(x, y);
46416    }
46417
46418    /**
46419     *
46420     * @param {number} x
46421     * @param {number} y
46422     */
46423
46424  }, {
46425    key: 'updateBoundingBox',
46426    value: function updateBoundingBox(x, y) {
46427      this.boundingBox.top = y - this.options.icon.size * 0.5;
46428      this.boundingBox.left = x - this.options.icon.size * 0.5;
46429      this.boundingBox.right = x + this.options.icon.size * 0.5;
46430      this.boundingBox.bottom = y + this.options.icon.size * 0.5;
46431
46432      if (this.options.label !== undefined && this.labelModule.size.width > 0) {
46433        var iconTextSpacing = 5;
46434        this.boundingBox.left = Math.min(this.boundingBox.left, this.labelModule.size.left);
46435        this.boundingBox.right = Math.max(this.boundingBox.right, this.labelModule.size.left + this.labelModule.size.width);
46436        this.boundingBox.bottom = Math.max(this.boundingBox.bottom, this.boundingBox.bottom + this.labelModule.size.height + iconTextSpacing);
46437      }
46438    }
46439
46440    /**
46441     *
46442     * @param {CanvasRenderingContext2D} ctx
46443     * @param {number} x width
46444     * @param {number} y height
46445     * @param {boolean} selected
46446     * @param {boolean} hover - Unused
46447     * @param {{toArrow: boolean, toArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), toArrowType: *, middleArrow: boolean, middleArrowScale: (number|allOptions.edges.arrows.middle.scaleFactor|{number}|Array), middleArrowType: (allOptions.edges.arrows.middle.type|{string}|string|*), fromArrow: boolean, fromArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), fromArrowType: *, arrowStrikethrough: (*|boolean|allOptions.edges.arrowStrikethrough|{boolean}), color: undefined, inheritsColor: (string|string|string|allOptions.edges.color.inherit|{string, boolean}|Array|*), opacity: *, hidden: *, length: *, shadow: *, shadowColor: *, shadowSize: *, shadowX: *, shadowY: *, dashes: (*|boolean|Array|allOptions.edges.dashes|{boolean, array}), width: *}} values
46448     */
46449
46450  }, {
46451    key: '_icon',
46452    value: function _icon(ctx, x, y, selected, hover, values) {
46453      var iconSize = Number(this.options.icon.size);
46454
46455      if (this.options.icon.code !== undefined) {
46456        ctx.font = (selected ? "bold " : "") + iconSize + "px " + this.options.icon.face;
46457
46458        // draw icon
46459        ctx.fillStyle = this.options.icon.color || "black";
46460        ctx.textAlign = "center";
46461        ctx.textBaseline = "middle";
46462
46463        // draw shadow if enabled
46464        this.enableShadow(ctx, values);
46465        ctx.fillText(this.options.icon.code, x, y);
46466
46467        // disable shadows for other elements.
46468        this.disableShadow(ctx, values);
46469      } else {
46470        console.error('When using the icon shape, you need to define the code in the icon options object. This can be done per node or globally.');
46471      }
46472    }
46473
46474    /**
46475     *
46476     * @param {CanvasRenderingContext2D} ctx
46477     * @param {number} angle
46478     * @returns {number}
46479     */
46480
46481  }, {
46482    key: 'distanceToBorder',
46483    value: function distanceToBorder(ctx, angle) {
46484      return this._distanceToBorder(ctx, angle);
46485    }
46486  }]);
46487  return Icon;
46488}(_NodeBase3['default']);
46489
46490exports['default'] = Icon;
46491
46492/***/ }),
46493/* 207 */
46494/***/ (function(module, exports, __webpack_require__) {
46495
46496"use strict";
46497
46498
46499Object.defineProperty(exports, "__esModule", {
46500  value: true
46501});
46502
46503var _getPrototypeOf = __webpack_require__(3);
46504
46505var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf);
46506
46507var _classCallCheck2 = __webpack_require__(0);
46508
46509var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
46510
46511var _createClass2 = __webpack_require__(1);
46512
46513var _createClass3 = _interopRequireDefault(_createClass2);
46514
46515var _possibleConstructorReturn2 = __webpack_require__(4);
46516
46517var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2);
46518
46519var _inherits2 = __webpack_require__(5);
46520
46521var _inherits3 = _interopRequireDefault(_inherits2);
46522
46523var _CircleImageBase2 = __webpack_require__(73);
46524
46525var _CircleImageBase3 = _interopRequireDefault(_CircleImageBase2);
46526
46527function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
46528
46529/**
46530 * An image-based replacement for the default Node shape.
46531 *
46532 * @extends CircleImageBase
46533 */
46534var Image = function (_CircleImageBase) {
46535  (0, _inherits3['default'])(Image, _CircleImageBase);
46536
46537  /**
46538   * @param {Object} options
46539   * @param {Object} body
46540   * @param {Label} labelModule
46541   * @param {Image} imageObj
46542   * @param {Image} imageObjAlt
46543   */
46544  function Image(options, body, labelModule, imageObj, imageObjAlt) {
46545    (0, _classCallCheck3['default'])(this, Image);
46546
46547    var _this = (0, _possibleConstructorReturn3['default'])(this, (Image.__proto__ || (0, _getPrototypeOf2['default'])(Image)).call(this, options, body, labelModule));
46548
46549    _this.setImages(imageObj, imageObjAlt);
46550    return _this;
46551  }
46552
46553  /**
46554   *
46555   * @param {CanvasRenderingContext2D} ctx - Unused.
46556   * @param {boolean} [selected]
46557   * @param {boolean} [hover]
46558   */
46559
46560
46561  (0, _createClass3['default'])(Image, [{
46562    key: 'resize',
46563    value: function resize(ctx) {
46564      var selected = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : this.selected;
46565      var hover = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : this.hover;
46566
46567      var imageAbsent = this.imageObj.src === undefined || this.imageObj.width === undefined || this.imageObj.height === undefined;
46568
46569      if (imageAbsent) {
46570        var side = this.options.size * 2;
46571        this.width = side;
46572        this.height = side;
46573        return;
46574      }
46575
46576      if (this.needsRefresh(selected, hover)) {
46577        this._resizeImage();
46578      }
46579    }
46580
46581    /**
46582     *
46583     * @param {CanvasRenderingContext2D} ctx
46584     * @param {number} x width
46585     * @param {number} y height
46586     * @param {boolean} selected
46587     * @param {boolean} hover
46588     * @param {{toArrow: boolean, toArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), toArrowType: *, middleArrow: boolean, middleArrowScale: (number|allOptions.edges.arrows.middle.scaleFactor|{number}|Array), middleArrowType: (allOptions.edges.arrows.middle.type|{string}|string|*), fromArrow: boolean, fromArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), fromArrowType: *, arrowStrikethrough: (*|boolean|allOptions.edges.arrowStrikethrough|{boolean}), color: undefined, inheritsColor: (string|string|string|allOptions.edges.color.inherit|{string, boolean}|Array|*), opacity: *, hidden: *, length: *, shadow: *, shadowColor: *, shadowSize: *, shadowX: *, shadowY: *, dashes: (*|boolean|Array|allOptions.edges.dashes|{boolean, array}), width: *}} values
46589     */
46590
46591  }, {
46592    key: 'draw',
46593    value: function draw(ctx, x, y, selected, hover, values) {
46594      this.switchImages(selected);
46595      this.resize();
46596      this.left = x - this.width / 2;
46597      this.top = y - this.height / 2;
46598
46599      if (this.options.shapeProperties.useBorderWithImage === true) {
46600        var neutralborderWidth = this.options.borderWidth;
46601        var selectionLineWidth = this.options.borderWidthSelected || 2 * this.options.borderWidth;
46602        var borderWidth = (selected ? selectionLineWidth : neutralborderWidth) / this.body.view.scale;
46603        ctx.lineWidth = Math.min(this.width, borderWidth);
46604
46605        ctx.beginPath();
46606
46607        // setup the line properties.
46608        ctx.strokeStyle = selected ? this.options.color.highlight.border : hover ? this.option
46608s.color.hover.border : this.options.color.border;
46609
46610        // set a fillstyle
46611        ctx.fillStyle = selected ? this.options.color.highlight.background : hover ? this.options.color.hover.background : this.options.color.background;
46612
46613        // draw a rectangle to form the border around. This rectangle is filled so the opacity of a picture (in future vis releases?) can be used to tint the image
46614        ctx.rect(this.left - 0.5 * ctx.lineWidth, this.top - 0.5 * ctx.lineWidth, this.width + ctx.lineWidth, this.height + ctx.lineWidth);
46615        ctx.fill();
46616
46617        this.performStroke(ctx, values);
46618
46619        ctx.closePath();
46620      }
46621
46622      this._drawImageAtPosition(ctx, values);
46623
46624      this._drawImageLabel(ctx, x, y, selected, hover);
46625
46626      this.updateBoundingBox(x, y);
46627    }
46628
46629    /**
46630     *
46631     * @param {number} x
46632     * @param {number} y
46633     */
46634
46635  }, {
46636    key: 'updateBoundingBox',
46637    value: function updateBoundingBox(x, y) {
46638      this.resize();
46639      this._updateBoundingBox(x, y);
46640
46641      if (this.options.label !== undefined && this.labelModule.size.width > 0) {
46642        this.boundingBox.left = Math.min(this.boundingBox.left, this.labelModule.size.left);
46643        this.boundingBox.right = Math.max(this.boundingBox.right, this.labelModule.size.left + this.labelModule.size.width);
46644        this.boundingBox.bottom = Math.max(this.boundingBox.bottom, this.boundingBox.bottom + this.labelOffset);
46645      }
46646    }
46647
46648    /**
46649     *
46650     * @param {CanvasRenderingContext2D} ctx
46651     * @param {number} angle
46652     * @returns {number}
46653     */
46654
46655  }, {
46656    key: 'distanceToBorder',
46657    value: function distanceToBorder(ctx, angle) {
46658      return this._distanceToBorder(ctx, angle);
46659    }
46660  }]);
46661  return Image;
46662}
46662(_CircleImageBase3['default']);
46663
46664exports['default'] = Image;
46665
46666/***/ }),
46667/* 208 */
46668/***/ (function(module, exports, __webpack_require__) {
46669
46670"use strict";
46671
46672
46673Object.defineProperty(exports, "__esModule", {
46674  value: true
46675});
46676
46677var _getPrototypeOf = __webpack_require__(3);
46678
46679var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf);
46680
46681var _classCallCheck2 = __webpack_require__(0);
46682
46683var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
46684
46685var _createClass2 = __webpack_require__(1);
46686
46687var _createClass3 = _interopRequireDefault(_createClass2);
46688
46689var _possibleConstructorReturn2 = __webpack_require__(4);
46690
46691var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2);
46692
46693var _inherits2 = __webpack_require__(5);
46694
46695var _inherits3 = _interopRequireDefault(_inherits2);
46696
46697var _ShapeBase2 = __webpack_require__(24);
46698
46699var _ShapeBase3 = _interopRequireDefault(_ShapeBase2);
46700
46701function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
46702
46703/**
46704 * A Square Node/Cluster shape.
46705 *
46706 * @extends ShapeBase
46707 */
46708var Square = function (_ShapeBase) {
46709  (0, _inherits3['default'])(Square, _ShapeBase);
46710
46711  /**
46712   * @param {Object} options
46713   * @param {Object} body
46714   * @param {Label} labelModule
46715   */
46716  function Square(options, body, labelModule) {
46717    (0, _classCallCheck3['default'])(this, Square);
46718    return (0, _possibleConstructorReturn3['default'])(this, (Square.__proto__ || (0, _getPrototypeOf2['default'])(Square)).call(this, options, body, labelModule));
46719  }
46720
46721  /**
46722   *
46723   * @param {CanvasRenderingContext2D} ctx
46724   * @param {number} x width
46725   * @param {number} y height
46726   * @param {boolean} selected
46727   * @param {boolean} hover
46728   * @param {{toArrow: boolean, toArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), toArrowType: *, middleArrow: boolean, middleArrowScale: (number|allOptions.edges.arrows.middle.scaleFactor|{number}|Array), middleArrowType: (allOptions.edges.arrows.middle.type|{string}|string|*), fromArrow: boolean, fromArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), fromArrowType: *, arrowStrikethrough: (*|boolean|allOptions.edges.arrowStrikethrough|{boolean}), color: undefined, inheritsColor: (string|string|string|allOptions.edges.color.inherit|{string, boolean}|Array|*), opacity: *, hidden: *, length: *, shadow: *, shadowColor: *, shadowSize: *, shadowX: *, shadowY: *, dashes: (*|boolean|Array|allOptions.edges.dashes|{boolean, array}), width: *}} values
46729   */
46730
46731
46732  (0, _createClass3['default'])(Square, [{
46733    key: 'draw',
46734    value: function draw(ctx, x, y, selected, hover, values) {
46735      this._drawShape(ctx, 'square', 2, x, y, selected, hover, values);
46736    }
46737
46738    /**
46739     *
46740     * @param {CanvasRenderingContext2D} ctx
46741     * @param {number} angle
46742     * @returns {number}
46743     */
46744
46745  }, {
46746    key: 'distanceToBorder',
46747    value: function distanceToBorder(ctx, angle) {
46748      return this._distanceToBorder(ctx, angle);
46749    }
46750  }]);
46751  return Square;
46752}(_ShapeBase3['default']);
46753
46754exports['default'] = Square;
46755
46756/***/ }),
46757/* 209 */
46758/***/ (function(module, exports, __webpack_require__) {
46759
46760"use strict";
46761
46762
46763Object.defineProperty(exports, "__esModule", {
46764  value: true
46765});
46766
46767var _getPrototypeOf = __webpack_require__(3);
46768
46769var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf);
46770
46771var _classCallCheck2 = __webpack_require__(0);
46772
46773var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
46774
46775var _createClass2 = __webpack_require__(1);
46776
46777var _createClass3 = _interopRequireDefault(_createClass2);
46778
46779var _possibleConstructorReturn2 = __webpack_require__(4);
46780
46781var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2);
46782
46783var _inherits2 = __webpack_require__(5);
46784
46785var _inherits3 = _interopRequireDefault(_inherits2);
46786
46787var _ShapeBase2 = __webpack_require__(24);
46788
46789var _ShapeBase3 = _interopRequireDefault(_ShapeBase2);
46790
46791function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
46792
46793/**
46794 * A Hexagon Node/Cluster shape.
46795 *
46796 * @extends ShapeBase
46797 */
46798var Hexagon = function (_ShapeBase) {
46799  (0, _inherits3['default'])(Hexagon, _ShapeBase);
46800
46801  /**
46802   * @param {Object} options
46803   * @param {Object} body
46804   * @param {Label} labelModule
46805   */
46806  function Hexagon(options, body, labelModule) {
46807    (0, _classCallCheck3['default'])(this, Hexagon);
46808    return (0, _possibleConstructorReturn3['default'])(this, (Hexagon.__proto__ || (0, _getPrototypeOf2['default'])(Hexagon)).call(this, options, body, labelModule));
46809  }
46810
46811  /**
46812   *
46813   * @param {CanvasRenderingContext2D} ctx
46814   * @param {number} x width
46815   * @param {number} y height
46816   * @param {boolean} selected
46817   * @param {boolean} hover
46818   * @param {{toArrow: boolean, toArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), toArrowType: *, middleArrow: boolean, middleArrowScale: (number|allOptions.edges.arrows.middle.scaleFactor|{number}|Array), middleArrowType: (allOptions.edges.arrows.middle.type|{string}|string|*), fromArrow: boolean, fromArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), fromArrowType: *, arrowStrikethrough: (*|boolean|allOptions.edges.arrowStrikethrough|{boolean}), color: undefined, inheritsColor: (string|string|string|allOptions.edges.color.inherit|{string, boolean}|Array|*), opacity: *, hidden: *, length: *, shadow: *, shadowColor: *, shadowSize: *, shadowX: *, shadowY: *, dashes: (*|boolean|Array|allOptions.edges.dashes|{boolean, array}), width: *}} values
46819   */
46820
46821
46822  (0, _createClass3['default'])(Hexagon, [{
46823    key: 'draw',
46824    value: function draw(ctx, x, y, selected, hover, values) {
46825      this._drawShape(ctx, 'hexagon', 4, x, y, selected, hover, values);
46826    }
46827
46828    /**
46829     *
46830     * @param {CanvasRenderingContext2D} ctx
46831     * @param {number} angle
46832     * @returns {number}
46833     */
46834
46835  }, {
46836    key: 'distanceToBorder',
46837    value: function distanceToBorder(ctx, angle) {
46838      return this._distanceToBorder(ctx, angle);
46839    }
46840  }]);
46841  return Hexagon;
46842}(_ShapeBase3['default']);
46843
46844exports['default'] = Hexagon;
46845
46846/***/ }),
46847/* 210 */
46848/***/ (function(module, exports, __webpack_require__) {
46849
46850"use strict";
46851
46852
46853Object.defineProperty(exports, "__esModule", {
46854  value: true
46855});
46856
46857var _getPrototypeOf = __webpack_require__(3);
46858
46859var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf);
46860
46861var _classCallCheck2 = __webpack_require__(0);
46862
46863var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
46864
46865var _createClass2 = __webpack_require__(1);
46866
46867var _createClass3 = _interopRequireDefault(_createClass2);
46868
46869var _possibleConstructorReturn2 = __webpack_require__(4);
46870
46871var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2);
46872
46873var _inherits2 = __webpack_require__(5);
46874
46875var _inherits3 = _interopRequireDefault(_inherits2);
46876
46877var _ShapeBase2 = __webpack_require__(24);
46878
46879var _ShapeBase3 = _interopRequireDefault(_ShapeBase2);
46880
46881function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
46882
46883/**
46884 * A Star Node/Cluster shape.
46885 *
46886 * @extends ShapeBase
46887 */
46888var Star = function (_ShapeBase) {
46889  (0, _inherits3['default'])(Star, _ShapeBase);
46890
46891  /**
46892   * @param {Object} options
46893   * @param {Object} body
46894   * @param {Label} labelModule
46895   */
46896  function Star(options, body, labelModule) {
46897    (0, _classCallCheck3['default'])(this, Star);
46898    return (0, _possibleConstructorReturn3['default'])(this, (Star.__proto__ || (0, _getPrototypeOf2['default'])(Star)).call(this, options, body, labelModule));
46899  }
46900
46901  /**
46902   *
46903   * @param {CanvasRenderingContext2D} ctx
46904   * @param {number} x width
46905   * @param {number} y height
46906   * @param {boolean} selected
46907   * @param {boolean} hover
46908   * @param {{toArrow: boolean, toArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), toArrowType: *, middleArrow: boolean, middleArrowScale: (number|allOptions.edges.arrows.middle.scaleFactor|{number}|Array), middleArrowType: (allOptions.edges.arrows.middle.type|{string}|string|*), fromArrow: boolean, fromArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), fromArrowType: *, arrowStrikethrough: (*|boolean|allOptions.edges.arrowStrikethrough|{boolean}), color: undefined, inheritsColor: (string|string|string|allOptions.edges.color.inherit|{string, boolean}|Array|*), opacity: *, hidden: *, length: *, shadow: *, shadowColor: *, shadowSize: *, shadowX: *, shadowY: *, dashes: (*|boolean|Array|allOptions.edges.dashes|{boolean, array}), width: *}} values
46909   */
46910
46911
46912  (0, _createClass3['default'])(Star, [{
46913    key: 'draw',
46914    value: function draw(ctx, x, y, selected, hover, values) {
46915      this._drawShape(ctx, 'star', 4, x, y, selected, hover, values);
46916    }
46917
46918    /**
46919     *
46920     * @param {CanvasRenderingContext2D} ctx
46921     * @param {number} angle
46922     * @returns {number}
46923     */
46924
46925  }, {
46926    key: 'distanceToBorder',
46927    value: function distanceToBorder(ctx, angle) {
46928      return this._distanceToBorder(ctx, angle);
46929    }
46930  }]);
46931  return Star;
46932}(_ShapeBase3['default']);
46933
46934exports['default'] = Star;
46935
46936/***/ }),
46937/* 211 */
46938/***/ (function(module, exports, __webpack_require__) {
46939
46940"use strict";
46941
46942
46943Object.defineProperty(exports, "__esModule", {
46944  value: true
46945});
46946
46947var _getPrototypeOf = __webpack_require__(3);
46948
46949var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf);
46950
46951var _classCallCheck2 = __webpack_require__(0);
46952
46953var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
46954
46955var _createClass2 = __webpack_require__(1);
46956
46957var _createClass3 = _interopRequireDefault(_createClass2);
46958
46959var _possibleConstructorReturn2 = __webpack_require__(4);
46960
46961var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2);
46962
46963var _inherits2 = __webpack_require__(5);
46964
46965var _inherits3 = _interopRequireDefault(_inherits2);
46966
46967var _NodeBase2 = __webpack_require__(23);
46968
46969var _NodeBase3 = _interopRequireDefault(_NodeBase2);
46970
46971function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
46972
46973/**
46974 * A text-based replacement for the default Node shape.
46975 *
46976 * @extends NodeBase
46977 */
46978var Text = function (_NodeBase) {
46979  (0, _inherits3['default'])(Text, _NodeBase);
46980
46981  /**
46982   * @param {Object} options
46983   * @param {Object} body
46984   * @param {Label} labelModule
46985   */
46986  function Text(options, body, labelModule) {
46987    (0, _classCallCheck3['default'])(this, Text);
46988
46989    var _this = (0, _possibleConstructorReturn3['default'])(this, (Text.__proto__ || (0, _getPrototypeOf2['default'])(Text)).call(this, options, body, labelModule));
46990
46991    _this._setMargins(labelModule);
46992    return _this;
46993  }
46994
46995  /**
46996   *
46997   * @param {CanvasRenderingContext2D} ctx
46998   * @param {boolean} selected
46999   * @param {boolean} hover
47000   */
47001
47002
47003  (0, _createClass3['default'])(Text, [{
47004    key: 'resize',
47005    value: function resize(ctx, selected, hover) {
47006      if (this.needsRefresh(selected, hover)) {
47007        this.textSize = this.labelModule.getTextSize(ctx, selected, hover);
47008        this.width = this.textSize.width + this.margin.right + this.margin.left;
47009        this.height = this.textSize.height + this.margin.top + this.margin.bottom;
47010        this.radius = 0.5 * this.width;
47011      }
47012    }
47013
47014    /**
47015     *
47016     * @param {CanvasRenderingContext2D} ctx
47017     * @param {number} x width
47018     * @param {number} y height
47019     * @param {boolean} selected
47020     * @param {boolean} hover
47021     * @param {{toArrow: boolean, toArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), toArrowType: *, middleArrow: boolean, middleArrowScale: (number|allOptions.edges.arrows.middle.scaleFactor|{number}|Array), middleArrowType: (allOptions.edges.arrows.middle.type|{string}|string|*), fromArrow: boolean, fromArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), fromArrowType: *, arrowStrikethrough: (*|boolean|allOptions.edges.arrowStrikethrough|{boolean}), color: undefined, inheritsColor: (string|string|string|allOptions.edges.color.inherit|{string, boolean}|Array|*), opacity: *, hidden: *, length: *, shadow: *, shadowColor: *, shadowSize: *, shadowX: *, shadowY: *, dashes: (*|boolean|Array|allOptions.edges.dashes|{boolean, array}), width: *}} values
47022     */
47023
47024  }, {
47025    key: 'draw',
47026    value: function draw(ctx, x, y, selected, hover, values) {
47027      this.resize(ctx, selected, hover);
47028      this.left = x - this.width / 2;
47029      this.top = y - this.height / 2;
47030
47031      // draw shadow if enabled
47032      this.enableShadow(ctx, values);
47033      this.labelModule.draw(ctx, this.left + this.textSize.width / 2 + this.margin.left, this.top + this.textSize.height / 2 + this.margin.top, selected, hover);
47034
47035      // disable shadows for other elements.
47036      this.disableShadow(ctx, values);
47037
47038      this.updateBoundingBox(x, y, ctx, selected, hover);
47039    }
47040
47041    /**
47042     *
47043     * @param {CanvasRenderingContext2D} ctx
47044     * @param {number} angle
47045     * @returns {number}
47046     */
47047
47048  }, {
47049    key: 'distanceToBorder',
47050    value: function distanceToBorder(ctx, angle) {
47051      return this._distanceToBorder(ctx, angle);
47052    }
47053  }]);
47054  return Text;
47055}(_NodeBase3['default']);
47056
47057exports['default'] = Text;
47058
47059/***/ }),
47060/* 212 */
47061/***/ (function(module, exports, __webpack_require__) {
47062
47063"use strict";
47064
47065
47066Object.defineProperty(exports, "__esModule", {
47067  value: true
47068});
47069
47070var _getPrototypeOf = __webpack_require__(3);
47071
47072var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf);
47073
47074var _classCallCheck2 = __webpack_require__(0);
47075
47076var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
47077
47078var _createClass2 = __webpack_require__(1);
47079
47080var _createClass3 = _interopRequireDefault(_createClass2);
47081
47082var _possibleConstructorReturn2 = __webpack_require__(4);
47083
47084var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2);
47085
47086var _inherits2 = __webpack_require__(5);
47087
47088var _inherits3 = _interopRequireDefault(_inherits2);
47089
47090var _ShapeBase2 = __webpack_require__(24);
47091
47092var _ShapeBase3 = _interopRequireDefault(_ShapeBase2);
47093
47094function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
47095
47096/**
47097 * A Triangle Node/Cluster shape.
47098 *
47099 * @extends ShapeBase
47100 */
47101var Triangle = function (_ShapeBase) {
47102  (0, _inherits3['default'])(Triangle, _ShapeBase);
47103
47104  /**
47105   * @param {Object} options
47106   * @param {Object} body
47107   * @param {Label} labelModule
47108   */
47109  function Triangle(options, body, labelModule) {
47110    (0, _classCallCheck3['default'])(this, Triangle);
47111    return (0, _possibleConstructorReturn3['default'])(this, (Triangle.__proto__ || (0, _getPrototypeOf2['default'])(Triangle)).call(this, options, body, labelModule));
47112  }
47113
47114  /**
47115   *
47116   * @param {CanvasRenderingContext2D} ctx
47117   * @param {number} x
47118   * @param {number} y
47119   * @param {boolean} selected
47120   * @param {boolean} hover
47121   * @param {{toArrow: boolean, toArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), toArrowType: *, middleArrow: boolean, middleArrowScale: (number|allOptions.edges.arrows.middle.scaleFactor|{number}|Array), middleArrowType: (allOptions.edges.arrows.middle.type|{string}|string|*), fromArrow: boolean, fromArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), fromArrowType: *, arrowStrikethrough: (*|boolean|allOptions.edges.arrowStrikethrough|{boolean}), color: undefined, inheritsColor: (string|string|string|allOptions.edges.color.inherit|{string, boolean}|Array|*), opacity: *, hidden: *, length: *, shadow: *, shadowColor: *, shadowSize: *, shadowX: *, shadowY: *, dashes: (*|boolean|Array|allOptions.edges.dashes|{boolean, array}), width: *}} values
47122   */
47123
47124
47125  (0, _createClass3['default'])(Triangle, [{
47126    key: 'draw',
47127    value: function draw(ctx, x, y, selected, hover, values) {
47128      this._drawShape(ctx, 'triangle', 3, x, y, selected, hover, values);
47129    }
47130
47131    /**
47132     *
47133     * @param {CanvasRenderingContext2D} ctx
47134     * @param {number} angle
47135     * @returns {number}
47136     */
47137
47138  }, {
47139    key: 'distanceToBorder',
47140    value: function distanceToBorder(ctx, angle) {
47141      return this._distanceToBorder(ctx, angle);
47142    }
47143  }]);
47144  return Triangle;
47145}(_ShapeBase3['default']);
47146
47147exports['default'] = Triangle;
47148
47149/***/ }),
47150/* 213 */
47151/***/ (function(module, exports, __webpack_require__) {
47152
47153"use strict";
47154
47155
47156Object.defineProperty(exports, "__esModule", {
47157  value: true
47158});
47159
47160var _getPrototypeOf = __webpack_require__(3);
47161
47162var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf);
47163
47164var _classCallCheck2 = __webpack_require__(0);
47165
47166var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
47167
47168var _createClass2 = __webpack_require__(1);
47169
47170var _createClass3 = _interopRequireDefault(_createClass2);
47171
47172var _possibleConstructorReturn2 = __webpack_require__(4);
47173
47174var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2);
47175
47176var _inherits2 = __webpack_require__(5);
47177
47178var _inherits3 = _interopRequireDefault(_inherits2);
47179
47180var _ShapeBase2 = __webpack_require__(24);
47181
47182var _ShapeBase3 = _interopRequireDefault(_ShapeBase2);
47183
47184function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
47185
47186/**
47187 * A downward facing Triangle Node/Cluster shape.
47188 *
47189 * @extends ShapeBase
47190 */
47191var TriangleDown = function (_ShapeBase) {
47192  (0, _inherits3['default'])(TriangleDown, _ShapeBase);
47193
47194  /**
47195   * @param {Object} options
47196   * @param {Object} body
47197   * @param {Label} labelModule
47198   */
47199  function TriangleDown(options, body, labelModule) {
47200    (0, _classCallCheck3['default'])(this, TriangleDown);
47201    return (0, _possibleConstructorReturn3['default'])(this, (TriangleDown.__proto__ || (0, _getPrototypeOf2['default'])(TriangleDown)).call(this, options, body, labelModule));
47202  }
47203
47204  /**
47205   *
47206   * @param {CanvasRenderingContext2D} ctx
47207   * @param {number} x
47208   * @param {number} y
47209   * @param {boolean} selected
47210   * @param {boolean} hover
47211   * @param {{toArrow: boolean, toArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), toArrowType: *, middleArrow: boolean, middleArrowScale: (number|allOptions.edges.arrows.middle.scaleFactor|{number}|Array), middleArrowType: (allOptions.edges.arrows.middle.type|{string}|string|*), fromArrow: boolean, fromArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), fromArrowType: *, arrowStrikethrough: (*|boolean|allOptions.edges.arrowStrikethrough|{boolean}), color: undefined, inheritsColor: (string|string|string|allOptions.edges.color.inherit|{string, boolean}|Array|*), opacity: *, hidden: *, length: *, shadow: *, shadowColor: *, shadowSize: *, shadowX: *, shadowY: *, dashes: (*|boolean|Array|allOptions.edges.dashes|{boolean, array}), width: *}} values
47212   */
47213
47214
47215  (0, _createClass3['default'])(TriangleDown, [{
47216    key: 'draw',
47217    value: function draw(ctx, x, y, selected, hover, values) {
47218      this._drawShape(ctx, 'triangleDown', 3, x, y, selected, hover, values);
47219    }
47220
47221    /**
47222     *
47223     * @param {CanvasRenderingContext2D} ctx
47224     * @param {number} angle
47225     * @returns {number}
47226     */
47227
47228  }, {
47229    key: 'distanceToBorder',
47230    value: function distanceToBorder(ctx, angle) {
47231      return this._distanceToBorder(ctx, angle);
47232    }
47233  }]);
47234  return TriangleDown;
47235}(_ShapeBase3['default']);
47236
47237exports['default'] = TriangleDown;
47238
47239/***/ }),
47240/* 214 */
47241/***/ (function(module, exports, __webpack_require__) {
47242
47243"use strict";
47244
47245
47246Object.defineProperty(exports, "__esModule", {
47247  value: true
47248});
47249
47250var _classCallCheck2 = __webpack_require__(0);
47251
47252var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
47253
47254var _createClass2 = __webpack_require__(1);
47255
47256var _createClass3 = _interopRequireDefault(_createClass2);
47257
47258function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
47259
47260var util = __webpack_require__(2);
47261var DataSet = __webpack_require__(11);
47262var DataView = __webpack_require__(12);
47263var Edge = __webpack_require__(74)['default'];
47264
47265/**
47266 * Handler for Edges
47267 */
47268
47269var EdgesHandler = function () {
47270  /**
47271   * @param {Object} body
47272   * @param {Array.<Image>} images
47273   * @param {Array.<Group>} groups
47274   */
47275  function EdgesHandler(body, images, groups) {
47276    var _this = this;
47277
47278    (0, _classCallCheck3['default'])(this, EdgesHandler);
47279
47280    this.body = body;
47281    this.images = images;
47282    this.groups = groups;
47283
47284    // create the edge API in the body container
47285    this.body.functions.createEdge = this.create.bind(this);
47286
47287    this.edgesListeners = {
47288      add: function add(event, params) {
47289        _this.add(params.items);
47290      },
47291      update: function update(event, params) {
47292        _this.update(params.items);
47293      },
47294      remove: function remove(event, params) {
47295        _this.remove(params.items);
47296      }
47297    };
47298
47299    this.options = {};
47300    this.defaultOptions = {
47301      arrows: {
47302        to: { enabled: false, scaleFactor: 1, type: 'arrow' }, // boolean / {arrowScaleFactor:1} / {enabled: false, arrowScaleFactor:1}
47303        middle: { enabled: false, scaleFactor: 1, type: 'arrow' },
47304        from: { enabled: false, scaleFactor: 1, type: 'arrow' }
47305      },
47306      arrowStrikethrough: true,
47307      color: {
47308        color: '#848484',
47309        highlight: '#848484',
47310        hover: '#848484',
47311        inherit: 'from',
47312        opacity: 1.0
47313      },
47314      dashes: false,
47315      font: {
47316        color: '#343434',
47317        size: 14, // px
47318        face: 'arial',
47319        background: 'none',
47320        strokeWidth: 2, // px
47321        strokeColor: '#ffffff',
47322        align: 'horizontal',
47323        multi: false,
47324        vadjust: 0,
47325        bold: {
47326          mod: 'bold'
47327        },
47328        boldital: {
47329          mod: 'bold italic'
47330        },
47331        ital: {
47332          mod: 'italic'
47333        },
47334        mono: {
47335          mod: '',
47336          size: 15, // px
47337          face: 'courier new',
47338          vadjust: 2
47339        }
47340      },
47341      hidden: false,
47342      hoverWidth: 1.5,
47343      label: undefined,
47344      labelHighlightBold: true,
47345      length: undefined,
47346      physics: true,
47347      scaling: {
47348        min: 1,
47349        max: 15,
47350        label: {
47351          enabled: true,
47352          min: 14,
47353          max: 30,
47354          maxVisible: 30,
47355          drawThreshold: 5
47356        },
47357        customScalingFunction: function customScalingFunction(min, max, total, value) {
47358          if (max === min) {
47359            return 0.5;
47360          } else {
47361            var scale = 1 / (max - min);
47362            return Math.max(0, (value - min) * scale);
47363          }
47364        }
47365      },
47366      selectionWidth: 1.5,
47367      selfReferenceSize: 20,
47368      shadow: {
47369        enabled: false,
47370        color: 'rgba(0,0,0,0.5)',
47371        size: 10,
47372        x: 5,
47373        y: 5
47374      },
47375      smooth: {
47376        enabled: true,
47377        type: "dynamic",
47378        forceDirection: 'none',
47379        roundness: 0.5
47380      },
47381      title: undefined,
47382      width: 1,
47383      value: undefined
47384    };
47385
47386    util.deepExtend(this.options, this.defaultOptions);
47387
47388    this.bindEventListeners();
47389  }
47390
47391  /**
47392   * Binds event listeners
47393   */
47394
47395
47396  (0, _createClass3['default'])(EdgesHandler, [{
47397    key: 'bindEventListeners',
47398    value: function bindEventListeners() {
47399      var _this2 = this;
47400
47401      // this allows external modules to force all dynamic curves to turn static.
47402      this.body.emitter.on("_forceDisableDynamicCurves", function (type) {
47403        var emit = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
47404
47405        if (type === 'dynamic') {
47406          type = 'continuous';
47407        }
47408        var dataChanged = false;
47409        for (var edgeId in _this2.body.edges) {
47410          if (_this2.body.edges.hasOwnProperty(edgeId)) {
47411            var edge = _this2.body.edges[edgeId];
47412            var edgeData = _this2.body.data.edges._data[edgeId];
47413
47414            // only forcibly remove the smooth curve if the data has been set of the edge has the smooth curves defined.
47415            // this is because a change in the global would not affect these curves.
47416            if (edgeData !== undefined) {
47417              var smoothOptions = edgeData.smooth;
47418              if (smoothOptions !== undefined) {
47419                if (smoothOptions.enabled === true && smoothOptions.type === 'dynamic') {
47420                  if (type === undefined) {
47421                    edge.setOptions({ smooth: false });
47422                  } else {
47423                    edge.setOptions({ smooth: { type: type } });
47424                  }
47425                  dataChanged = true;
47426                }
47427              }
47428            }
47429          }
47430        }
47431        if (emit === true && dataChanged === true) {
47432          _this2.body.emitter.emit("_dataChanged");
47433        }
47434      });
47435
47436      // this is called when options of EXISTING nodes or edges have changed.
47437      //
47438      // NOTE: Not true, called when options have NOT changed, for both existing as well as new nodes.
47439      //       See update() for logic.
47440      // TODO: Verify and examine the consequences of this. It might still trigger when
47441      //       non-option fields have changed, but then reconnecting edges is still useless.
47442      //       Alternatively, it might also be called when edges are removed.
47443      //
47444      this.body.emitter.on("_dataUpdated", function () {
47445        _this2.reconnectEdges();
47446      });
47447
47448      // refresh the edges. Used when reverting from hierarchical layout
47449      this.body.emitter.on("refreshEdges", this.refresh.bind(this));
47450      this.body.emitter.on("refresh", this.refresh.bind(this));
47451      this.body.emitter.on("destroy", function () {
47452        util.forEach(_this2.edgesListeners, function (callback, event) {
47453          if (_this2.body.data.edges) _this2.body.data.edges.off(event, callback);
47454        });
47455        delete _this2.body.functions.createEdge;
47456        delete _this2.edgesListeners.add;
47457        delete _this2.edgesListeners.update;
47458        delete _this2.edgesListeners.remove;
47459        delete _this2.edgesListeners;
47460      });
47461    }
47462
47463    /**
47464     *
47465     * @param {Object} options
47466     */
47467
47468  }, {
47469    key: 'setOptions',
47470    value: function setOptions(options) {
47471      if (options !== undefined) {
47472        // use the parser from the Edge class to fill in all shorthand notations
47473        Edge.parseOptions(this.options, options, true, this.defaultOptions, true);
47474
47475        // update smooth settings in all edges
47476        var dataChanged = false;
47477        if (options.smooth !== undefined) {
47478          for (var edgeId in this.body.edges) {
47479            if (this.body.edges.hasOwnProperty(edgeId)) {
47480              dataChanged = this.body.edges[edgeId].updateEdgeType() || dataChanged;
47481            }
47482          }
47483        }
47484
47485        // update fonts in all edges
47486        if (options.font !== undefined) {
47487          for (var _edgeId in this.body.edges) {
47488            if (this.body.edges.hasOwnProperty(_edgeId)) {
47489              this.body.edges[_edgeId].updateLabelModule();
47490            }
47491          }
47492        }
47493
47494        // update the state of the variables if needed
47495        if (options.hidden !== undefined || options.physics !== undefined || dataChanged === true) {
47496          this.body.emitter.emit('_dataChanged');
47497        }
47498      }
47499    }
47500
47501    /**
47502     * Load edges by reading the data table
47503     * @param {Array | DataSet | DataView} edges    The data containing the edges.
47504     * @param {boolean} [doNotEmit=false]
47505     * @private
47506     */
47507
47508  }, {
47509    key: 'setData',
47510    value: function setData(edges) {
47511      var _this3 = this;
47512
47513      var doNotEmit = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
47514
47515      var oldEdgesData = this.body.data.edges;
47516
47517      if (edges instanceof DataSet || edges instanceof DataView) {
47518        this.body.data.edges = edges;
47519      } else if (Array.isArray(edges)) {
47520        this.body.data.edges = new DataSet();
47521        this.body.data.edges.add(edges);
47522      } else if (!edges) {
47523        this.body.data.edges = new DataSet();
47524      } else {
47525        throw new TypeError('Array or DataSet expected');
47526      }
47527
47528      // TODO: is this null or undefined or false?
47529      if (oldEdgesData) {
47530        // unsubscribe from old dataset
47531        util.forEach(this.edgesListeners, function (callback, event) {
47532          oldEdgesData.off(event, callback);
47533        });
47534      }
47535
47536      // remove drawn edges
47537      this.body.edges = {};
47538
47539      // TODO: is this null or undefined or false?
47540      if (this.body.data.edges) {
47541        // subscribe to new dataset
47542        util.forEach(this.edgesListeners, function (callback, event) {
47543          _this3.body.data.edges.on(event, callback);
47544        });
47545
47546        // draw all new nodes
47547        var ids = this.body.data.edges.getIds();
47548        this.add(ids, true);
47549      }
47550
47551      this.body.emitter.emit('_adjustEdgesForHierarchicalLayout');
47552      if (doNotEmit === false) {
47553        this.body.emitter.emit("_dataChanged");
47554      }
47555    }
47556
47557    /**
47558     * Add edges
47559     * @param {number[] | string[]} ids
47560     * @param {boolean} [doNotEmit=false]
47561     * @private
47562     */
47563
47564  }, {
47565    key: 'add',
47566    value: function add(ids) {
47567      var doNotEmit = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
47568
47569      var edges = this.body.edges;
47570      var edgesData = this.body.data.edges;
47571
47572      for (var i = 0; i < ids.length; i++) {
47573        var id = ids[i];
47574
47575        var oldEdge = edges[id];
47576        if (oldEdge) {
47577          oldEdge.disconnect();
47578        }
47579
47580        var data = edgesData.get(id, { "showInternalIds": true });
47581        edges[id] = this.create(data);
47582      }
47583
47584      this.body.emitter.emit('_adjustEdgesForHierarchicalLayout');
47585
47586      if (doNotEmit === false) {
47587        this.body.emitter.emit("_dataChanged");
47588      }
47589    }
47590
47591    /**
47592     * Update existing edges, or create them when not yet existing
47593     * @param {number[] | string[]} ids
47594     * @private
47595     */
47596
47597  }, {
47598    key: 'update',
47599    value: function update(ids) {
47600      var edges = this.body.edges;
47601      var edgesData = this.body.data.edges;
47602      var dataChanged = false;
47603      for (var i = 0; i < ids.length; i++) {
47604        var id = ids[i];
47605        var data = edgesData.get(id);
47606        var edge = edges[id];
47607        if (edge !== undefined) {
47608          // update edge
47609          edge.disconnect();
47610          dataChanged = edge.setOptions(data) || dataChanged; // if a support node is added, data can be changed.
47611          edge.connect();
47612        } else {
47613          // create edge
47614          this.body.edges[id] = this.create(data);
47615          dataChanged = true;
47616        }
47617      }
47618
47619      if (dataChanged === true) {
47620        this.body.emitter.emit('_adjustEdgesForHierarchicalLayout');
47621        this.body.emitter.emit("_dataChanged");
47622      } else {
47623        this.body.emitter.emit("_dataUpdated");
47624      }
47625    }
47626
47627    /**
47628     * Remove existing edges. Non existing ids will be ignored
47629     * @param {number[] | string[]} ids
47630     * @param {boolean} [emit=true]
47631     * @private
47632     */
47633
47634  }, {
47635    key: 'remove',
47636    value: function remove(ids) {
47637      var emit = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
47638
47639      if (ids.length === 0) return; // early out
47640
47641      var edges = this.body.edges;
47642      util.forEach(ids, function (id) {
47643        var edge = edges[id];
47644        if (edge !== undefined) {
47645          edge.remove();
47646        }
47647      });
47648
47649      if (emit) {
47650        this.body.emitter.emit("_dataChanged");
47651      }
47652    }
47653
47654    /**
47655     * Refreshes Edge Handler
47656     */
47657
47658  }, {
47659    key: 'refresh',
47660    value: function refresh() {
47661      var _this4 = this;
47662
47663      util.forEach(this.body.edges, function (edge, edgeId) {
47664        var data = _this4.body.data.edges._data[edgeId];
47665        if (data !== undefined) {
47666          edge.setOptions(data);
47667        }
47668      });
47669    }
47670
47671    /**
47672     *
47673     * @param {Object} properties
47674     * @returns {Edge}
47675     */
47676
47677  }, {
47678    key: 'create',
47679    value: function create(properties) {
47680      return new Edge(properties, this.body, this.options, this.defaultOptions);
47681    }
47682
47683    /**
47684     * Reconnect all edges
47685     * @private
47686     */
47687
47688  }, {
47689    key: 'reconnectEdges',
47690    value: function reconnectEdges() {
47691      var id;
47692      var nodes = this.body.nodes;
47693      var edges = this.body.edges;
47694
47695      for (id in nodes) {
47696        if (nodes.hasOwnProperty(id)) {
47697          nodes[id].edges = [];
47698        }
47699      }
47700
47701      for (id in edges) {
47702        if (edges.hasOwnProperty(id)) {
47703          var edge = edges[id];
47704          edge.from = null;
47705          edge.to = null;
47706          edge.connect();
47707        }
47708      }
47709    }
47710
47711    /**
47712     *
47713     * @param {Edge.id} edgeId
47714     * @returns {Array}
47715     */
47716
47717  }, {
47718    key: 'getConnectedNodes',
47719    value: function getConnectedNodes(edgeId) {
47720      var nodeList = [];
47721      if (this.body.edges[edgeId] !== undefined) {
47722        var edge = this.body.edges[edgeId];
47723        if (edge.fromId !== undefined) {
47724          nodeList.push(edge.fromId);
47725        }
47726        if (edge.toId !== undefined) {
47727          nodeList.push(edge.toId);
47728        }
47729      }
47730      return nodeList;
47731    }
47732
47733    /**
47734     * There is no direct relation between the nodes and the edges DataSet,
47735     * so the right place to do call this is in the handler for event `_dataUpdated`.
47736     */
47737
47738  }, {
47739    key: '_updateState',
47740    value: function _updateState() {
47741      this._addMissingEdges();
47742      this._removeInvalidEdges();
47743    }
47744
47745    /**
47746     * Scan for missing nodes and remove corresponding edges, if any.
47747     * @private
47748     */
47749
47750  }, {
47751    key: '_removeInvalidEdges',
47752    value: function _removeInvalidEdges() {
47753      var _this5 = this;
47754
47755      var edgesToDelete = [];
47756
47757      util.forEach(this.body.edges, function (edge, id) {
47758        var toNode = _this5.body.nodes[edge.toId];
47759        var fromNode = _this5.body.nodes[edge.fromId];
47760
47761        // Skip clustering edges here, let the Clustering module handle those
47762        if (toNode !== undefined && toNode.isCluster === true || fromNode !== undefined && fromNode.isCluster === true) {
47763          return;
47764        }
47765
47766        if (toNode === undefined || fromNode === undefined) {
47767          edgesToDelete.push(id);
47768        }
47769      });
47770
47771      this.remove(edgesToDelete, false);
47772    }
47773
47774    /**
47775     * add all edges from dataset that are not in the cached state
47776     * @private
47777     */
47778
47779  }, {
47780    key: '_addMissingEdges',
47781    value: function _addMissingEdges() {
47782      var edges = this.body.edges;
47783      var edgesData = this.body.data.edges;
47784      var addIds = [];
47785
47786      edgesData.forEach(function (edgeData, edgeId) {
47787        var edge = edges[edgeId];
47788        if (edge === undefined) {
47789          addIds.push(edgeId);
47790        }
47791      });
47792
47793      this.add(addIds, true);
47794    }
47795  }]);
47796  return EdgesHandler;
47797}();
47798
47799exports['default'] = EdgesHandler;
47800
47801/***/ }),
47802/* 215 */
47803/***/ (function(module, exports, __webpack_require__) {
47804
47805"use strict";
47806
47807
47808Object.defineProperty(exports, "__esModule", {
47809  value: true
47810});
47811
47812var _slicedToArray2 = __webpack_require__(30);
47813
47814var _slicedToArray3 = _interopRequireDefault(_slicedToArray2);
47815
47816var _getPrototypeOf = __webpack_require__(3);
47817
47818var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf);
47819
47820var _classCallCheck2 = __webpack_require__(0);
47821
47822var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
47823
47824var _createClass2 = __webpack_require__(1);
47825
47826var _createClass3 = _interopRequireDefault(_createClass2);
47827
47828var _possibleConstructorReturn2 = __webpack_require__(4);
47829
47830var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2);
47831
47832var _inherits2 = __webpack_require__(5);
47833
47834var _inherits3 = _interopRequireDefault(_inherits2);
47835
47836var _CubicBezierEdgeBase2 = __webpack_require__(216);
47837
47838var _CubicBezierEdgeBase3 = _interopRequireDefault(_CubicBezierEdgeBase2);
47839
47840function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
47841
47842/**
47843 * A Cubic Bezier Edge. Bezier curves are used to model smooth gradual
47844 * curves in paths between nodes.
47845 *
47846 * @extends CubicBezierEdgeBase
47847 */
47848var CubicBezierEdge = function (_CubicBezierEdgeBase) {
47849  (0, _inherits3['default'])(CubicBezierEdge, _CubicBezierEdgeBase);
47850
47851  /**
47852   * @param {Object} options
47853   * @param {Object} body
47854   * @param {Label} labelModule
47855   */
47856  function CubicBezierEdge(options, body, labelModule) {
47857    (0, _classCallCheck3['default'])(this, CubicBezierEdge);
47858    return (0, _possibleConstructorReturn3['default'])(this, (CubicBezierEdge.__proto__ || (0, _getPrototypeOf2['default'])(CubicBezierEdge)).call(this, options, body, labelModule));
47859  }
47860
47861  /**
47862   * Draw a line between two nodes
47863   * @param {CanvasRenderingContext2D} ctx
47864   * @param {{toArrow: boolean, toArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), toArrowType: *, middleArrow: boolean, middleArrowScale: (number|allOptions.edges.arrows.middle.scaleFactor|{number}|Array), middleArrowType: (allOptions.edges.arrows.middle.type|{string}|string|*), fromArrow: boolean, fromArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), fromArrowType: *, arrowStrikethrough: (*|boolean|allOptions.edges.arrowStrikethrough|{boolean}), color: undefined, inheritsColor: (string|string|string|allOptions.edges.color.inherit|{string, boolean}|Array|*), opacity: *, hidden: *, length: *, shadow: *, shadowColor: *, shadowSize: *, shadowX: *, shadowY: *, dashes: (*|boolean|Array|allOptions.edges.dashes|{boolean, array}), width: *}} values
47865   * @param {Array.<Node>} viaNodes
47866   * @private
47867   */
47868
47869
47870  (0, _createClass3['default'])(CubicBezierEdge, [{
47871    key: '_line',
47872    value: function _line(ctx, values, viaNodes) {
47873      // get the coordinates of the support points.
47874      var via1 = viaNodes[0];
47875      var via2 = viaNodes[1];
47876      this._bezierCurve(ctx, values, via1, via2);
47877    }
47878
47879    /**
47880     *
47881     * @returns {Array.<{x: number, y: number}>}
47882     * @private
47883     */
47884
47885  }, {
47886    key: '_getViaCoordinates',
47887    value: function _getViaCoordinates() {
47888      var dx = this.from.x - this.to.x;
47889      var dy = this.from.y - this.to.y;
47890
47891      var x1 = void 0,
47892          y1 = void 0,
47893          x2 = void 0,
47894          y2 = void 0;
47895      var roundness = this.options.smooth.roundness;
47896
47897      // horizontal if x > y or if direction is forced or if direction is horizontal
47898      if ((Math.abs(dx) > Math.abs(dy) || this.options.smooth.forceDirection === true || this.options.smooth.forceDirection === 'horizontal') && this.options.smooth.forceDirection !== 'vertical') {
47899        y1 = this.from.y;
47900        y2 = this.to.y;
47901        x1 = this.from.x - roundness * dx;
47902        x2 = this.to.x + roundness * dx;
47903      } else {
47904        y1 = this.from.y - roundness * dy;
47905        y2 = this.to.y + roundness * dy;
47906        x1 = this.from.x;
47907        x2 = this.to.x;
47908      }
47909
47910      return [{ x: x1, y: y1 }, { x: x2, y: y2 }];
47911    }
47912
47913    /**
47914     *
47915     * @returns {Array.<{x: number, y: number}>}
47916     */
47917
47918  }, {
47919    key: 'getViaNode',
47920    value: function getViaNode() {
47921      return this._getViaCoordinates();
47922    }
47923
47924    /**
47925     *
47926     * @param {Node} nearNode
47927     * @param {CanvasRenderingContext2D} ctx
47928     * @returns {{x: number, y: number, t: number}}
47929     * @private
47930     */
47931
47932  }, {
47933    key: '_findBorderPosition',
47934    value: function _findBorderPosition(nearNode, ctx) {
47935      return this._findBorderPositionBezier(nearNode, ctx);
47936    }
47937
47938    /**
47939     *
47940     * @param {number} x1
47941     * @param {number} y1
47942     * @param {number} x2
47943     * @param {number} y2
47944     * @param {number} x3
47945     * @param {number} y3
47946     * @param {Node} via1
47947     * @param {Node} via2
47948     * @returns {number}
47949     * @private
47950     */
47951
47952  }, {
47953    key: '_getDistanceToEdge',
47954    value: function _getDistanceToEdge(x1, y1, x2, y2, x3, y3) {
47955      var _ref = arguments.length > 6 && arguments[6] !== undefined ? arguments[6] : this._getViaCoordinates(),
47956          _ref2 = (0, _slicedToArray3['default'])(_ref, 2),
47957          via1 = _ref2[0],
47958          via2 = _ref2[1];
47959
47960      // x3,y3 is the point
47961      return this._getDistanceToBezierEdge(x1, y1, x2, y2, x3, y3, via1, via2);
47962    }
47963
47964    /**
47965     * Combined function of pointOnLine and pointOnBezier. This gives the coordinates of a point on the line at a certain percentage of the way
47966     * @param {number} percentage
47967     * @param {{x: number, y: number}} [via1=this._getViaCoordinates()[0]]
47968     * @param {{x: number, y: number}} [via2=this._getViaCoordinates()[1]]
47969     * @returns {{x: number, y: number}}
47970     * @private
47971     */
47972
47973  }, {
47974    key: 'getPoint',
47975    value: function getPoint(percentage) {
47976      var _ref3 = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : this._getViaCoordinates(),
47977          _ref4 = (0, _slicedToArray3['default'])(_ref3, 2),
47978          via1 = _ref4[0],
47979          via2 = _ref4[1];
47980
47981      var t = percentage;
47982      var vec = [];
47983      vec[0] = Math.pow(1 - t, 3);
47984      vec[1] = 3 * t * Math.pow(1 - t, 2);
47985      vec[2] = 3 * Math.pow(t, 2) * (1 - t);
47986      vec[3] = Math.pow(t, 3);
47987      var x = vec[0] * this.fromPoint.x + vec[1] * via1.x + vec[2] * via2.x + vec[3] * this.toPoint.x;
47988      var y = vec[0] * this.fromPoint.y + vec[1] * via1.y + vec[2] * via2.y + vec[3] * this.toPoint.y;
47989
47990      return { x: x, y: y };
47991    }
47992  }]);
47993  return CubicBezierEdge;
47994}(_CubicBezierEdgeBase3['default']);
47995
47996exports['default'] = CubicBezierEdge;
47997
47998/***/ }),
47999/* 216 */
48000/***/ (function(module, exports, __webpack_require__) {
48001
48002"use strict";
48003
48004
48005Object.defineProperty(exports, "__esModule", {
48006  value: true
48007});
48008
48009var _getPrototypeOf = __webpack_require__(3);
48010
48011var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf);
48012
48013var _classCallCheck2 = __webpack_require__(0);
48014
48015var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
48016
48017var _createClass2 = __webpack_require__(1);
48018
48019var _createClass3 = _interopRequireDefault(_createClass2);
48020
48021var _possibleConstructorReturn2 = __webpack_require__(4);
48022
48023var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2);
48024
48025var _inherits2 = __webpack_require__(5);
48026
48027var _inherits3 = _interopRequireDefault(_inherits2);
48028
48029var _BezierEdgeBase2 = __webpack_require__(75);
48030
48031var _BezierEdgeBase3 = _interopRequireDefault(_BezierEdgeBase2);
48032
48033function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
48034
48035/**
48036 * A Base Class for all Cubic Bezier Edges. Bezier curves are used to model
48037 * smooth gradual curves in paths between nodes.
48038 *
48039 * @extends BezierEdgeBase
48040 */
48041var CubicBezierEdgeBase = function (_BezierEdgeBase) {
48042  (0, _inherits3['default'])(CubicBezierEdgeBase, _BezierEdgeBase);
48043
48044  /**
48045   * @param {Object} options
48046   * @param {Object} body
48047   * @param {Label} labelModule
48048   */
48049  function CubicBezierEdgeBase(options, body, labelModule) {
48050    (0, _classCallCheck3['default'])(this, CubicBezierEdgeBase);
48051    return (0, _possibleConstructorReturn3['default'])(this, (CubicBezierEdgeBase.__proto__ || (0, _getPrototypeOf2['default'])(CubicBezierEdgeBase)).call(this, options, body, labelModule));
48052  }
48053
48054  /**
48055   * Calculate the distance between a point (x3,y3) and a line segment from
48056   * (x1,y1) to (x2,y2).
48057   * http://stackoverflow.com/questions/849211/shortest-distancae-between-a-point-and-a-line-segment
48058   * https://en.wikipedia.org/wiki/B%C3%A9zier_curve
48059   * @param {number} x1 from x
48060   * @param {number} y1 from y
48061   * @param {number} x2 to x
48062   * @param {number} y2 to y
48063   * @param {number} x3 point to check x
48064   * @param {number} y3 point to check y
48065   * @param {Node} via1
48066   * @param {Node} via2
48067   * @returns {number}
48068   * @private
48069   */
48070
48071
48072  (0, _createClass3['default'])(CubicBezierEdgeBase, [{
48073    key: '_getDistanceToBezierEdge',
48074    value: function _getDistanceToBezierEdge(x1, y1, x2, y2, x3, y3, via1, via2) {
48075      // x3,y3 is the point
48076      var minDistance = 1e9;
48077      var distance = void 0;
48078      var i = void 0,
48079          t = void 0,
48080          x = void 0,
48081          y = void 0;
48082      var lastX = x1;
48083      var lastY = y1;
48084      var vec = [0, 0, 0, 0];
48085      for (i = 1; i < 10; i++) {
48086        t = 0.1 * i;
48087        vec[0] = Math.pow(1 - t, 3);
48088        vec[1] = 3 * t * Math.pow(1 - t, 2);
48089        vec[2] = 3 * Math.pow(t, 2) * (1 - t);
48090        vec[3] = Math.pow(t, 3);
48091        x = vec[0] * x1 + vec[1] * via1.x + vec[2] * via2.x + vec[3] * x2;
48092        y = vec[0] * y1 + vec[1] * via1.y + vec[2] * via2.y + vec[3] * y2;
48093        if (i > 0) {
48094          distance = this._getDistanceToLine(lastX, lastY, x, y, x3, y3);
48095          minDistance = distance < minDistance ? distance : minDistance;
48096        }
48097        lastX = x;
48098        lastY = y;
48099      }
48100
48101      return minDistance;
48102    }
48103  }]);
48104  return CubicBezierEdgeBase;
48105}(_BezierEdgeBase3['default']);
48106
48107exports['default'] = CubicBezierEdgeBase;
48108
48109/***/ }),
48110/* 217 */
48111/***/ (function(module, exports, __webpack_require__) {
48112
48113"use strict";
48114
48115
48116Object.defineProperty(exports, "__esModule", {
48117  value: true
48118});
48119
48120var _slicedToArray2 = __webpack_require__(30);
48121
48122var _slicedToArray3 = _interopRequireDefault(_slicedToArray2);
48123
48124var _getPrototypeOf = __webpack_require__(3);
48125
48126var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf);
48127
48128var _classCallCheck2 = __webpack_require__(0);
48129
48130var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
48131
48132var _createClass2 = __webpack_require__(1);
48133
48134var _createClass3 = _interopRequireDefault(_createClass2);
48135
48136var _possibleConstructorReturn2 = __webpack_require__(4);
48137
48138var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2);
48139
48140var _inherits2 = __webpack_require__(5);
48141
48142var _inherits3 = _interopRequi
48142reDefault(_inherits2);
48143
48144var _BezierEdgeBase2 = __webpack_require__(75);
48145
48146var _BezierEdgeBase3 = _interopRequireDefault(_BezierEdgeBase2);
48147
48148function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
48149
48150/**
48151 * A Dynamic Bezier Edge. Bezier curves are used to model smooth gradual
48152 * curves in paths between nodes. The Dynamic piece refers to how the curve
48153 * reacts to physics changes.
48154 *
48155 * @extends BezierEdgeBase
48156 */
48157var BezierEdgeDynamic = function (_BezierEdgeBase) {
48158  (0, _inherits3["default"])(BezierEdgeDynamic, _BezierEdgeBase);
48159
48160  /**
48161   * @param {Object} options
48162   * @param {Object} body
48163   * @param {Label} labelModule
48164   */
48165  function BezierEdgeDynamic(options, body, labelModule) {
48166    (0, _classCallCheck3["default"])(this, BezierEdgeDynamic);
48167
48168    // --> this calls the setOptions below
48169    var _this = (0, _possibleConstructorReturn3["default"])(this, (BezierEdgeDynamic.__proto__ || (0, _getPrototypeOf2["default"])(BezierEdgeDynamic)).call(this, options, body, labelModule));
48170    //this.via = undefined; // Here for completeness but not allowed to defined before super() is invoked.
48171
48172
48173    _this._boundFunction = function () {
48174      _this.positionBezierNode();
48175    };
48176    _this.body.emitter.on("_repositionBezierNodes", _this._boundFunction);
48177    return _this;
48178  }
48179
48180  /**
48181   *
48182   * @param {Object} options
48183   */
48184
48185
48186  (0, _createClass3["default"])(BezierEdgeDynamic, [{
48187    key: "setOptions",
48188    value: function setOptions(options) {
48189      // check if the physics has changed.
48190      var physicsChange = false;
48191      if (this.options.physics !== options.physics) {
48192        physicsChange = true;
48193      }
48194
48195      // set the options and the to and from nodes
48196      this.options = options;
48197      this.id = this.options.id;
48198      this.from = this.body.nodes[this.options.from];
48199      this.to = this.body.nodes[this.options.to];
48200
48201      // setup the support node and connect
48202      this.setupSupportNode();
48203      this.connect();
48204
48205      // when we change the physics state of the edge, we reposition the support node.
48206      if (physicsChange === true) {
48207        this.via.setOptions({ physics: this.options.physics });
48208        this.positionBezierNode();
48209      }
48210    }
48211
48212    /**
48213     * Connects an edge to node(s)
48214     */
48215
48216  }, {
48217    key: "connect",
48218    value: function connect() {
48219      this.from = this.body.nodes[this.options.from];
48220      this.to = this.body.nodes[this.options.to];
48221      if (this.from === undefined || this.to === undefined || this.options.physics === false) {
48222        this.via.setOptions({ physics: false });
48223      } else {
48224        // fix weird behaviour where a self referencing node has physics enabled
48225        if (this.from.id === this.to.id) {
48226          this.via.setOptions({ physics: false });
48227        } else {
48228          this.via.setOptions({ physics: true });
48229        }
48230      }
48231    }
48232
48233    /**
48234     * remove the support nodes
48235     * @returns {boolean}
48236     */
48237
48238  }, {
48239    key: "cleanup",
48240    value: function cleanup() {
48241      this.body.emitter.off("_repositionBezierNodes", this._boundFunction);
48242      if (this.via !== undefined) {
48243        delete this.body.nodes[this.via.id];
48244        this.via = undefined;
48245        return true;
48246      }
48247      return false;
48248    }
48249
48250    /**
48251     * Bezier curves require an anchor point to calculate the smooth flow. These points are nodes. These nodes are invisible but
48252     * are used for the force calculation.
48253     *
48254     * The changed data is not called, if needed, it is returned by the main edge constructor.
48255     * @private
48256     */
48257
48258  }, {
48259    key: "setupSupportNode",
48260    value: function setupSupportNode() {
48261      if (this.via === undefined) {
48262        var nodeId = "edgeId:" + this.id;
48263        var node = this.body.functions.createNode({
48264          id: nodeId,
48265          shape: 'circle',
48266          physics: true,
48267          hidden: true
48268        });
48269        this.body.nodes[nodeId] = node;
48270        this.via = node;
48271        this.via.parentEdgeId = this.id;
48272        this.positionBezierNode();
48273      }
48274    }
48275
48276    /**
48277     * Positions bezier node
48278     */
48279
48280  }, {
48281    key: "positionBezierNode",
48282    value: function positionBezierNode() {
48283      if (this.via !== undefined && this.from !== undefined && this.to !== undefined) {
48284        this.via.x = 0.5 * (this.from.x + this.to.x);
48285        this.via.y = 0.5 * (this.from.y + this.to.y);
48286      } else if (this.via !== undefined) {
48287        this.via.x = 0;
48288        this.via.y = 0;
48289      }
48290    }
48291
48292    /**
48293     * Draw a line between two nodes
48294     * @param {CanvasRenderingContext2D} ctx
48295     * @param {{toArrow: boolean, toArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), toArrowType: *, middleArrow: boolean, middleArrowScale: (number|allOptions.edges.arrows.middle.scaleFactor|{number}|Array), middleArrowType: (allOptions.edges.arrows.middle.type|{string}|string|*), fromArrow: boolean, fromArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), fromArrowType: *, arrowStrikethrough: (*|boolean|allOptions.edges.arrowStrikethrough|{boolean}), color: undefined, inheritsColor: (string|string|string|allOptions.edges.color.inherit|{string, boolean}|Array|*), opacity: *, hidden: *, length: *, shadow: *, shadowColor: *, shadowSize: *, shadowX: *, shadowY: *, dashes: (*|boolean|Array|allOptions.edges.dashes|{boolean, array}), width: *}} values
48296     * @param {Node} viaNode
48297     * @private
48298     */
48299
48300  }, {
48301    key: "_line",
48302    value: function _line(ctx, values, viaNode) {
48303      this._bezierCurve(ctx, values, viaNode);
48304    }
48305
48306    /**
48307     *
48308     * @returns {Node|undefined|*|{index, line, column}}
48309     */
48310
48311  }, {
48312    key: "getViaNode",
48313    value: function getViaNode() {
48314      return this.via;
48315    }
48316
48317    /**
48318     * Combined function of pointOnLine and pointOnBezier. This gives the coordinates of a point on the line at a certain percentage of the way
48319     *
48320     * @param {number} percentage
48321     * @param {Node} viaNode
48322     * @returns {{x: number, y: number}}
48323     * @private
48324     */
48325
48326  }, {
48327    key: "getPoint",
48328    value: function getPoint(percentage) {
48329      var viaNode = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : this.via;
48330
48331      var t = percentage;
48332      var x = void 0,
48333          y = void 0;
48334      if (this.from === this.to) {
48335        var _getCircleData = this._getCircleData(this.from),
48336            _getCircleData2 = (0, _slicedToArray3["default"])(_getCircleData, 3),
48337            cx = _getCircleData2[0],
48338            cy = _getCircleData2[1],
48339            cr = _getCircleData2[2];
48340
48341        var a = 2 * Math.PI * (1 - t);
48342        x = cx + cr * Math.sin(a);
48343        y = cy + cr - cr * (1 - Math.cos(a));
48344      } else {
48345        x = Math.pow(1 - t, 2) * this.fromPoint.x + 2 * t * (1 - t) * viaNode.x + Math.pow(t, 2) * this.toPoint.x;
48346        y = Math.pow(1 - t, 2) * this.fromPoint.y + 2 * t * (1 - t) * viaNode.y + Math.pow(t, 2) * this.toPoint.y;
48347      }
48348
48349      return { x: x, y: y };
48350    }
48351
48352    /**
48353     *
48354     * @param {Node} nearNode
48355     * @param {CanvasRenderingContext2D} ctx
48356     * @returns {*}
48357     * @private
48358     */
48359
48360  }, {
48361    key: "_findBorderPosition",
48362    value: function _findBorderPosition(nearNode, ctx) {
48363      return this._findBorderPositionBezier(nearNode, ctx, this.via);
48364    }
48365
48366    /**
48367     *
48368     * @param {number} x1
48369     * @param {number} y1
48370     * @param {number} x2
48371     * @param {number} y2
48372     * @param {number} x3
48373     * @param {number} y3
48374     * @returns {number}
48375     * @private
48376     */
48377
48378  }, {
48379    key: "_getDistanceToEdge",
48380    value: function _getDistanceToEdge(x1, y1, x2, y2, x3, y3) {
48381      // x3,y3 is the point
48382      return this._getDistanceToBezierEdge(x1, y1, x2, y2, x3, y3, this.via);
48383    }
48384  }]);
48385  return BezierEdgeDynamic;
48386}(_BezierEdgeBase3["default"]);
48387
48388exports["default"] = BezierEdgeDynamic;
48389
48390/***/ }),
48391/* 218 */
48392/***/ (function(module, exports, __webpack_require__) {
48393
48394"use strict";
48395
48396
48397Object.defineProperty(exports, "__esModule", {
48398  value: true
48399});
48400
48401var _getPrototypeOf = __webpack_require__(3);
48402
48403var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf);
48404
48405var _classCallCheck2 = __webpack_require__(0);
48406
48407var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
48408
48409var _createClass2 = __webpack_require__(1);
48410
48411var _createClass3 = _interopRequireDefault(_createClass2);
48412
48413var _possibleConstructorReturn2 = __webpack_require__(4);
48414
48415var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2);
48416
48417var _inherits2 = __webpack_require__(5);
48418
48419var _inherits3 = _interopRequireDefault(_inherits2);
48420
48421var _BezierEdgeBase2 = __webpack_require__(75);
48422
48423var _BezierEdgeBase3 = _interopRequireDefault(_BezierEdgeBase2);
48424
48425function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
48426
48427/**
48428 * A Static Bezier Edge. Bezier curves are used to model smooth gradual
48429 * curves in paths between nodes.
48430 *
48431 * @extends BezierEdgeBase
48432 */
48433var BezierEdgeStatic = function (_BezierEdgeBase) {
48434  (0, _inherits3['default'])(BezierEdgeStatic, _BezierEdgeBase);
48435
48436  /**
48437   * @param {Object} options
48438   * @param {Object} body
48439   * @param {Label} labelModule
48440   */
48441  function BezierEdgeStatic(options, body, labelModule) {
48442    (0, _classCallCheck3['default'])(this, BezierEdgeStatic);
48443    return (0, _possibleConstructorReturn3['default'])(this, (BezierEdgeStatic.__proto__ || (0, _getPrototypeOf2['default'])(BezierEdgeStatic)).call(this, options, body, labelModule));
48444  }
48445
48446  /**
48447   * Draw a line between two nodes
48448   * @param {CanvasRenderingContext2D} ctx
48449   * @param {{toArrow: boolean, toArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), toArrowType: *, middleArrow: boolean, middleArrowScale: (number|allOptions.edges.arrows.middle.scaleFactor|{number}|Array), middleArrowType: (allOptions.edges.arrows.middle.type|{string}|string|*), fromArrow: boolean, fromArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), fromArrowType: *, arrowStrikethrough: (*|boolean|allOptions.edges.arrowStrikethrough|{boolean}), color: undefined, inheritsColor: (string|string|string|allOptions.edges.color.inherit|{string, boolean}|Array|*), opacity: *, hidden: *, length: *, shadow: *, shadowColor: *, shadowSize: *, shadowX: *, shadowY: *, dashes: (*|boolean|Array|allOptions.edges.dashes|{boolean, array}), width: *}} values
48450   * @param {Node} viaNode
48451   * @private
48452   */
48453
48454
48455  (0, _createClass3['default'])(BezierEdgeStatic, [{
48456    key: '_line',
48457    value: function _line(ctx, values, viaNode) {
48458      this._bezierCurve(ctx, values, viaNode);
48459    }
48460
48461    /**
48462     *
48463     * @returns {Array.<{x: number, y: number}>}
48464     */
48465
48466  }, {
48467    key: 'getViaNode',
48468    value: function getViaNode() {
48469      return this._getViaCoordinates();
48470    }
48471
48472    /**
48473     * We do not use the to and fromPoints here to make the via nodes the same as edges without arrows.
48474     * @returns {{x: undefined, y: undefined}}
48475     * @private
48476     */
48477
48478  }, {
48479    key: '_getViaCoordinates',
48480    value: function _getViaCoordinates() {
48481      // Assumption: x/y coordinates in from/to always defined
48482      var xVia = undefined;
48483      var yVia = undefined;
48484      var factor = this.options.smooth.roundness;
48485      var type = this.options.smooth.type;
48486      var dx = Math.abs(this.from.x - this.to.x);
48487      var dy = Math.abs(this.from.y - this.to.y);
48488      if (type === 'discrete' || type === 'diagonalCross') {
48489        var stepX = void 0;
48490        var stepY = void 0;
48491
48492        if (dx <= dy) {
48493          stepX = stepY = factor * dy;
48494        } else {
48495          stepX = stepY = factor * dx;
48496        }
48497
48498        if (this.from.x > this.to.x) stepX = -stepX;
48499        if (this.from.y >= this.to.y) stepY = -stepY;
48500
48501        xVia = this.from.x + stepX;
48502        yVia = this.from.y + stepY;
48503
48504        if (type === "discrete") {
48505          if (dx <= dy) {
48506            xVia = dx < factor * dy ? this.from.x : xVia;
48507          } else {
48508            yVia = dy < factor * dx ? this.from.y : yVia;
48509          }
48510        }
48511      } else if (type === "straightCross") {
48512        var _stepX = (1 - factor) * dx;
48513        var _stepY = (1 - factor) * dy;
48514
48515        if (dx <= dy) {
48516          // up - down
48517          _stepX = 0;
48518          if (this.from.y < this.to.y) _stepY = -_stepY;
48519        } else {
48520          // left - right
48521          if (this.from.x < this.to.x) _stepX = -_stepX;
48522          _stepY = 0;
48523        }
48524        xVia = this.to.x + _stepX;
48525        yVia = this.to.y + _stepY;
48526      } else if (type === 'horizontal') {
48527        var _stepX2 = (1 - factor) * dx;
48528        if (this.from.x < this.to.x) _stepX2 = -_stepX2;
48529        xVia = this.to.x + _stepX2;
48530        yVia = this.from.y;
48531      } else if (type === 'vertical') {
48532        var _stepY2 = (1 - factor) * dy;
48533        if (this.from.y < this.to.y) _stepY2 = -_stepY2;
48534        xVia = this.from.x;
48535        yVia = this.to.y + _stepY2;
48536      } else if (type === 'curvedCW') {
48537        dx = this.to.x - this.from.x;
48538        dy = this.from.y - this.to.y;
48539        var radius = Math.sqrt(dx * dx + dy * dy);
48540        var pi = Math.PI;
48541
48542        var originalAngle = Math.atan2(dy, dx);
48543        var myAngle = (originalAngle + (factor * 0.5 + 0.5) * pi) % (2 * pi);
48544
48545        xVia = this.from.x + (factor * 0.5 + 0.5) * radius * Math.sin(myAngle);
48546        yVia = this.from.y + (factor * 0.5 + 0.5) * radius * Math.cos(myAngle);
48547      } else if (type === 'curvedCCW') {
48548        dx = this.to.x - this.from.x;
48549        dy = this.from.y - this.to.y;
48550        var _radius = Math.sqrt(dx * dx + dy * dy);
48551        var _pi = Math.PI;
48552
48553        var _originalAngle = Math.atan2(dy, dx);
48554        var _myAngle = (_originalAngle + (-factor * 0.5 + 0.5) * _pi) % (2 * _pi);
48555
48556        xVia = this.from.x + (factor * 0.5 + 0.5) * _radius * Math.sin(_myAngle);
48557        yVia = this.from.y + (factor * 0.5 + 0.5) * _radius * Math.cos(_myAngle);
48558      } else {
48559        // continuous
48560        var _stepX3 = void 0;
48561        var _stepY3 = void 0;
48562
48563        if (dx <= dy) {
48564          _stepX3 = _stepY3 = factor * dy;
48565        } else {
48566          _stepX3 = _stepY3 = factor * dx;
48567        }
48568
48569        if (this.from.x > this.to.x) _stepX3 = -_stepX3;
48570        if (this.from.y >= this.to.y) _stepY3 = -_stepY3;
48571
48572        xVia = this.from.x + _stepX3;
48573        yVia = this.from.y + _stepY3;
48574
48575        if (dx <= dy) {
48576          if (this.from.x <= this.to.x) {
48577            xVia = this.to.x < xVia ? this.to.x : xVia;
48578          } else {
48579            xVia = this.to.x > xVia ? this.to.x : xVia;
48580          }
48581        } else {
48582          if (this.from.y >= this.to.y) {
48583            yVia = this.to.y > yVia ? this.to.y : yVia;
48584          } else {
48585            yVia = this.to.y < yVia ? this.to.y : yVia;
48586          }
48587        }
48588      }
48589      return { x: xVia, y: yVia };
48590    }
48591
48592    /**
48593     *
48594     * @param {Node} nearNode
48595     * @param {CanvasRenderingContext2D} ctx
48596     * @param {Object} options
48597     * @returns {*}
48598     * @private
48599     */
48600
48601  }, {
48602    key: '_findBorderPosition',
48603    value: function _findBorderPosition(nearNode, ctx) {
48604      var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
48605
48606      return this._findBorderPositionBezier(nearNode, ctx, options.via);
48607    }
48608
48609    /**
48610     *
48611     * @param {number} x1
48612     * @param {number} y1
48613     * @param {number} x2
48614     * @param {number} y2
48615     * @param {number} x3
48616     * @param {number} y3
48617     * @param {Node} viaNode
48618     * @returns {number}
48619     * @private
48620     */
48621
48622  }, {
48623    key: '_getDistanceToEdge',
48624    value: function _getDistanceToEdge(x1, y1, x2, y2, x3, y3) {
48625      var viaNode = arguments.length > 6 && arguments[6] !== undefined ? arguments[6] : this._getViaCoordinates();
48626      // x3,y3 is the point
48627      return this._getDistanceToBezierEdge(x1, y1, x2, y2, x3, y3, viaNode);
48628    }
48629
48630    /**
48631     * Combined function of pointOnLine and pointOnBezier. This gives the coordinates of a point on the line at a certain percentage of the way
48632     * @param {number} percentage
48633     * @param {Node} viaNode
48634     * @returns {{x: number, y: number}}
48635     * @private
48636     */
48637
48638  }, {
48639    key: 'getPoint',
48640    value: function getPoint(percentage) {
48641      var viaNode = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : this._getViaCoordinates();
48642
48643      var t = percentage;
48644      var x = Math.pow(1 - t, 2) * this.fromPoint.x + 2 * t * (1 - t) * viaNode.x + Math.pow(t, 2) * this.toPoint.x;
48645      var y = Math.pow(1 - t, 2) * this.fromPoint.y + 2 * t * (1 - t) * viaNode.y + Math.pow(t, 2) * this.toPoint.y;
48646
48647      return { x: x, y: y };
48648    }
48649  }]);
48650  return BezierEdgeStatic;
48651}(_BezierEdgeBase3['default']);
48652
48653exports['default'] = BezierEdgeStatic;
48654
48655/***/ }),
48656/* 219 */
48657/***/ (function(module, exports, __webpack_require__) {
48658
48659"use strict";
48660
48661
48662Object.defineProperty(exports, "__esModule", {
48663  value: true
48664});
48665
48666var _getPrototypeOf = __webpack_require__(3);
48667
48668var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf);
48669
48670var _classCallCheck2 = __webpack_require__(0);
48671
48672var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
48673
48674var _createClass2 = __webpack_require__(1);
48675
48676var _createClass3 = _interopRequireDefault(_createClass2);
48677
48678var _possibleConstructorReturn2 = __webpack_require__(4);
48679
48680var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2);
48681
48682var _inherits2 = __webpack_require__(5);
48683
48684var _inherits3 = _interopRequireDefault(_inherits2);
48685
48686var _EdgeBase2 = __webpack_require__(118);
48687
48688var _EdgeBase3 = _interopRequireDefault(_EdgeBase2);
48689
48690function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
48691
48692/**
48693 * A Straight Edge.
48694 *
48695 * @extends EdgeBase
48696 */
48697var StraightEdge = function (_EdgeBase) {
48698  (0, _inherits3['default'])(StraightEdge, _EdgeBase);
48699
48700  /**
48701   * @param {Object} options
48702   * @param {Object} body
48703   * @param {Label} labelModule
48704   */
48705  function StraightEdge(options, body, labelModule) {
48706    (0, _classCallCheck3['default'])(this, StraightEdge);
48707    return (0, _possibleConstructorReturn3['default'])(this, (StraightEdge.__proto__ || (0, _getPrototypeOf2['default'])(StraightEdge)).call(this, options, body, labelModule));
48708  }
48709
48710  /**
48711   * Draw a line between two nodes
48712   * @param {CanvasRenderingContext2D} ctx
48713   * @param {{toArrow: boolean, toArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), toArrowType: *, middleArrow: boolean, middleArrowScale: (number|allOptions.edges.arrows.middle.scaleFactor|{number}|Array), middleArrowType: (allOptions.edges.arrows.middle.type|{string}|string|*), fromArrow: boolean, fromArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), fromArrowType: *, arrowStrikethrough: (*|boolean|allOptions.edges.arrowStrikethrough|{boolean}), color: undefined, inheritsColor: (string|string|string|allOptions.edges.color.inherit|{string, boolean}|Array|*), opacity: *, hidden: *, length: *, shadow: *, shadowColor: *, shadowSize: *, shadowX: *, shadowY: *, dashes: (*|boolean|Array|allOptions.edges.dashes|{boolean, array}), width: *}} values
48714   * @private
48715   */
48716
48717
48718  (0, _createClass3['default'])(StraightEdge, [{
48719    key: '_line',
48720    value: function _line(ctx, values) {
48721      // draw a straight line
48722      ctx.beginPath();
48723      ctx.moveTo(this.fromPoint.x, this.fromPoint.y);
48724      ctx.lineTo(this.toPoint.x, this.toPoint.y);
48725      // draw shadow if enabled
48726      this.enableShadow(ctx, values);
48727      ctx.stroke();
48728      this.disableShadow(ctx, values);
48729    }
48730
48731    /**
48732     *
48733     * @returns {undefined}
48734     */
48735
48736  }, {
48737    key: 'getViaNode',
48738    value: function getViaNode() {
48739      return undefined;
48740    }
48741
48742    /**
48743     * Combined function of pointOnLine and pointOnBezier. This gives the coordinates of a point on the line at a certain percentage of the way
48744     *
48745     * @param {number} percentage
48746     * @returns {{x: number, y: number}}
48747     * @private
48748     */
48749
48750  }, {
48751    key: 'getPoint',
48752    value: function getPoint(percentage) {
48753      return {
48754        x: (1 - percentage) * this.fromPoint.x + percentage * this.toPoint.x,
48755        y: (1 - percentage) * this.fromPoint.y + percentage * this.toPoint.y
48756      };
48757    }
48758
48759    /**
48760     *
48761     * @param {Node} nearNode
48762     * @param {CanvasRenderingContext2D} ctx
48763     * @returns {{x: number, y: number}}
48764     * @private
48765     */
48766
48767  }, {
48768    key: '_findBorderPosition',
48769    value: function _findBorderPosition(nearNode, ctx) {
48770      var node1 = this.to;
48771      var node2 = this.from;
48772      if (nearNode.id === this.from.id) {
48773        node1 = this.from;
48774        node2 = this.to;
48775      }
48776
48777      var angle = Math.atan2(node1.y - node2.y, node1.x - node2.x);
48778      var dx = node1.x - node2.x;
48779      var dy = node1.y - node2.y;
48780      var edgeSegmentLength = Math.sqrt(dx * dx + dy * dy);
48781      var toBorderDist = nearNode.distanceToBorder(ctx, angle);
48782      var toBorderPoint = (edgeSegmentLength - toBorderDist) / edgeSegmentLength;
48783
48784      var borderPos = {};
48785      borderPos.x = (1 - toBorderPoint) * node2.x + toBorderPoint * node1.x;
48786      borderPos.y = (1 - toBorderPoint) * node2.y + toBorderPoint * node1.y;
48787
48788      return borderPos;
48789    }
48790
48791    /**
48792     *
48793     * @param {number} x1
48794     * @param {number} y1
48795     * @param {number} x2
48796     * @param {number} y2
48797     * @param {number} x3
48798     * @param {number} y3
48799     * @returns {number}
48800     * @private
48801     */
48802
48803  }, {
48804    key: '_getDistanceToEdge',
48805    value: function _getDistanceToEdge(x1, y1, x2, y2, x3, y3) {
48806      // x3,y3 is the point
48807      return this._getDistanceToLine(x1, y1, x2, y2, x3, y3);
48808    }
48809  }]);
48810  return StraightEdge;
48811}(_EdgeBase3['default']);
48812
48813exports['default'] = StraightEdge;
48814
48815/***/ }),
48816/* 220 */
48817/***/ (function(module, exports, __webpack_require__) {
48818
48819"use strict";
48820
48821
48822Object.defineProperty(exports, "__esModule", {
48823  value: true
48824});
48825
48826var _keys = __webpack_require__(8);
48827
48828var _keys2 = _interopRequireDefault(_keys);
48829
48830var _classCallCheck2 = __webpack_require__(0);
48831
48832var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
48833
48834var _createClass2 = __webpack_require__(1);
48835
48836var _createClass3 = _interopRequireDefault(_createClass2);
48837
48838function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
48839
48840var BarnesHutSolver = __webpack_require__(120)['default'];
48841var Repulsion = __webpack_require__(221)['default'];
48842var HierarchicalRepulsion = __webpack_require__(222)['default'];
48843var SpringSolver = __webpack_require__(223)['default'];
48844var HierarchicalSpringSolver = __webpack_require__(224)['default'];
48845var CentralGravitySolver = __webpack_require__(121)['default'];
48846var ForceAtlas2BasedRepulsionSolver = __webpack_require__(225)['default'];
48847var ForceAtlas2BasedCentralGravitySolver = __webpack_require__(226)['default'];
48848var util = __webpack_require__(2);
48849var EndPoints = __webpack_require__(119)['default']; // for debugging with _drawForces()
48850
48851
48852/**
48853 * The physics engine
48854 */
48855
48856var PhysicsEngine = function () {
48857  /**
48858   * @param {Object} body
48859   */
48860  function PhysicsEngine(body) {
48861    (0, _classCallCheck3['default'])(this, PhysicsEngine);
48862
48863    this.body = body;
48864    this.physicsBody = { physicsNodeIndices: [], physicsEdgeIndices: [], forces: {}, velocities: {} };
48865
48866    this.physicsEnabled = true;
48867    this.simulationInterval = 1000 / 60;
48868    this.requiresTimeout = true;
48869    this.previousStates = {};
48870    this.referenceState = {};
48871    this.freezeCache = {};
48872    this.renderTimer = undefined;
48873
48874    // parameters for the adaptive timestep
48875    this.adaptiveTimestep = false;
48876    this.adaptiveTimestepEnabled = false;
48877    this.adaptiveCounter = 0;
48878    this.adaptiveInterval = 3;
48879
48880    this.stabilized = false;
48881    this.startedStabilization = false;
48882    this.stabilizationIterations = 0;
48883    this.ready = false; // will be set to true if the stabilize
48884
48885    // default options
48886    this.options = {};
48887    this.defaultOptions = {
48888      enabled: true,
48889      barnesHut: {
48890        theta: 0.5,
48891        gravitationalConstant: -2000,
48892        centralGravity: 0.3,
48893        springLength: 95,
48894        springConstant: 0.04,
48895        damping: 0.09,
48896        avoidOverlap: 0
48897      },
48898      forceAtlas2Based: {
48899        theta: 0.5,
48900        gravitationalConstant: -50,
48901        centralGravity: 0.01,
48902        springConstant: 0.08,
48903        springLength: 100,
48904        damping: 0.4,
48905        avoidOverlap: 0
48906      },
48907      repulsion: {
48908        centralGravity: 0.2,
48909        springLength: 200,
48910        springConstant: 0.05,
48911        nodeDistance: 100,
48912        damping: 0.09,
48913        avoidOverlap: 0
48914      },
48915      hierarchicalRepulsion: {
48916        centralGravity: 0.0,
48917        springLength: 100,
48918        springConstant: 0.01,
48919        nodeDistance: 120,
48920        damping: 0.09
48921      },
48922      maxVelocity: 50,
48923      minVelocity: 0.75, // px/s
48924      solver: 'barnesHut',
48925      stabilization: {
48926        enabled: true,
48927        iterations: 1000, // maximum number of iteration to stabilize
48928        updateInterval: 50,
48929        onlyDynamicEdges: false,
48930        fit: true
48931      },
48932      timestep: 0.5,
48933      adaptiveTimestep: true
48934    };
48935    util.extend(this.options, this.defaultOptions);
48936    this.timestep = 0.5;
48937    this.layoutFailed = false;
48938
48939    this.bindEventListeners();
48940  }
48941
48942  /**
48943   * Binds event listeners
48944   */
48945
48946
48947  (0, _createClass3['default'])(PhysicsEngine, [{
48948    key: 'bindEventListeners',
48949    value: function bindEventListeners() {
48950      var _this = this;
48951
48952      this.body.emitter.on('initPhysics', function () {
48953        _this.initPhysics();
48954      });
48955      this.body.emitter.on('_layoutFailed', function () {
48956        _this.layoutFailed = true;
48957      });
48958      this.body.emitter.on('resetPhysics', function () {
48959        _this.stopSimulation();_this.ready = false;
48960      });
48961      this.body.emitter.on('disablePhysics', function () {
48962        _this.physicsEnabled = false;_this.stopSimulation();
48963      });
48964      this.body.emitter.on('restorePhysics', function () {
48965        _this.setOptions(_this.options);
48966        if (_this.ready === true) {
48967          _this.startSimulation();
48968        }
48969      });
48970      this.body.emitter.on('startSimulation', function () {
48971        if (_this.ready === true) {
48972          _this.startSimulation();
48973        }
48974      });
48975      this.body.emitter.on('stopSimulation', function () {
48976        _this.stopSimulation();
48977      });
48978      this.body.emitter.on('destroy', function () {
48979        _this.stopSimulation(false);
48980        _this.body.emitter.off();
48981      });
48982      this.body.emitter.on("_dataChanged", function () {
48983        // Nodes and/or edges have been added or removed, update shortcut lists.
48984        _this.updatePhysicsData();
48985      });
48986
48987      // debug: show forces
48988      // this.body.emitter.on("afterDrawing", (ctx) => {this._drawForces(ctx);});
48989    }
48990
48991    /**
48992     * set the physics options
48993     * @param {Object} options
48994     */
48995
48996  }, {
48997    key: 'setOptions',
48998    value: function setOptions(options) {
48999      if (options !== undefined) {
49000        if (options === false) {
49001          this.options.enabled = false;
49002          this.physicsEnabled = false;
49003          this.stopSimulation();
49004        } else if (options === true) {
49005          this.options.enabled = true;
49006          this.physicsEnabled = true;
49007          this.startSimulation();
49008        } else {
49009          this.physicsEnabled = true;
49010          util.selectiveNotDeepExtend(['stabilization'], this.options, options);
49011          util.mergeOptions(this.options, options, 'stabilization');
49012
49013          if (options.enabled === undefined) {
49014            this.options.enabled = true;
49015          }
49016
49017          if (this.options.enabled === false) {
49018            this.physicsEnabled = false;
49019            this.stopSimulation();
49020          }
49021
49022          // set the timestep
49023          this.timestep = this.options.timestep;
49024        }
49025      }
49026      this.init();
49027    }
49028
49029    /**
49030     * configure the engine.
49031     */
49032
49033  }, {
49034    key: 'init',
49035    value: function init() {
49036      var options;
49037      if (this.options.solver === 'forceAtlas2Based') {
49038        options = this.options.forceAtlas2Based;
49039        this.nodesSolver = new ForceAtlas2BasedRepulsionSolver(this.body, this.physicsBody, options);
49040        this.edgesSolver = new SpringSolver(this.body, this.physicsBody, options);
49041        this.gravitySolver = new ForceAtlas2BasedCentralGravitySolver(this.body, this.physicsBody, options);
49042      } else if (this.options.solver === 'repulsion') {
49043        options = this.options.repulsion;
49044        this.nodesSolver = new Repulsion(this.body, this.physicsBody, options);
49045        this.edgesSolver = new SpringSolver(this.body, this.physicsBody, options);
49046        this.gravitySolver = new CentralGravitySolver(this.body, this.physicsBody, options);
49047      } else if (this.options.solver === 'hierarchicalRepulsion') {
49048        options = this.options.hierarchicalRepulsion;
49049        this.nodesSolver = new HierarchicalRepulsion(this.body, this.physicsBody, options);
49050        this.edgesSolver = new HierarchicalSpringSolver(this.body, this.physicsBody, options);
49051        this.gravitySolver = new CentralGravitySolver(this.body, this.physicsBody, options);
49052      } else {
49053        // barnesHut
49054        options = this.options.barnesHut;
49055        this.nodesSolver = new BarnesHutSolver(this.body, this.physicsBody, options);
49056        this.edgesSolver = new SpringSolver(this.body, this.physicsBody, options);
49057        this.gravitySolver = new CentralGravitySolver(this.body, this.physicsBody, options);
49058      }
49059
49060      this.modelOptions = options;
49061    }
49062
49063    /**
49064     * initialize the engine
49065     */
49066
49067  }, {
49068    key: 'initPhysics',
49069    value: function initPhysics() {
49070      if (this.physicsEnabled === true && this.options.enabled === true) {
49071        if (this.options.stabilization.enabled === true) {
49072          this.stabilize();
49073        } else {
49074          this.stabilized = false;
49075          this.ready = true;
49076          this.body.emitter.emit('fit', {}, this.layoutFailed); // if the layout failed, we use the approximation for the zoom
49077          this.startSimulation();
49078        }
49079      } else {
49080        this.ready = true;
49081        this.body.emitter.emit('fit');
49082      }
49083    }
49084
49085    /**
49086     * Start the simulation
49087     */
49088
49089  }, {
49090    key: 'startSimulation',
49091    value: function startSimulation() {
49092      if (this.physicsEnabled === true && this.options.enabled === true) {
49093        this.stabilized = false;
49094
49095        // when visible, adaptivity is disabled.
49096        this.adaptiveTimestep = false;
49097
49098        // this sets the width of all nodes initially which could be required for the avoidOverlap
49099        this.body.emitter.emit("_resizeNodes");
49100        if (this.viewFunction === undefined) {
49101          this.viewFunction = this.simulationStep.bind(this);
49102          this.body.emitter.on('initRedraw', this.viewFunction);
49103          this.body.emitter.emit('_startRendering');
49104        }
49105      } else {
49106        this.body.emitter.emit('_redraw');
49107      }
49108    }
49109
49110    /**
49111     * Stop the simulation, force stabilization.
49112     * @param {boolean} [emit=true]
49113     */
49114
49115  }, {
49116    key: 'stopSimulation',
49117    value: function stopSimulation() {
49118      var emit = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
49119
49120      this.stabilized = true;
49121      if (emit === true) {
49122        this._emitStabilized();
49123      }
49124      if (this.viewFunction !== undefined) {
49125        this.body.emitter.off('initRedraw', this.viewFunction);
49126        this.viewFunction = undefined;
49127        if (emit === true) {
49128          this.body.emitter.emit('_stopRendering');
49129        }
49130      }
49131    }
49132
49133    /**
49134     * The viewFunction inserts this step into each render loop. It calls the physics tick and handles the cleanup at stabilized.
49135     *
49136     */
49137
49138  }, {
49139    key: 'simulationStep',
49140    value: function simulationStep() {
49141      // check if the physics have settled
49142      var startTime = Date.now();
49143      this.physicsTick();
49144      var physicsTime = Date.now() - startTime;
49145
49146      // run double speed if it is a little graph
49147      if ((physicsTime < 0.4 * this.simulationInterval || this.runDoubleSpeed === true) && this.stabilized === false) {
49148        this.physicsTick();
49149
49150        // this makes sure there is no jitter. The decision is taken once to run it at double speed.
49151        this.runDoubleSpeed = true;
49152      }
49153
49154      if (this.stabilized === true) {
49155        this.stopSimulation();
49156      }
49157    }
49158
49159    /**
49160     * trigger the stabilized event.
49161     *
49162     * @param {number} [amountOfIterations=this.stabilizationIterations]
49163     * @private
49164     */
49165
49166  }, {
49167    key: '_emitStabilized',
49168    value: function _emitStabilized() {
49169      var _this2 = this;
49170
49171      var amountOfIterations = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : this.stabilizationIterations;
49172
49173      if (this.stabilizationIterations > 1 || this.startedStabilization === true) {
49174        setTimeout(function () {
49175          _this2.body.emitter.emit('stabilized', { iterations: amountOfIterations });
49176          _this2.startedStabilization = false;
49177          _this2.stabilizationIterations = 0;
49178        }, 0);
49179      }
49180    }
49181
49182    /**
49183     * Calculate the forces for one physics iteration and move the nodes.
49184     * @private
49185     */
49186
49187  }, {
49188    key: 'physicsStep',
49189    value: function physicsStep() {
49190      this.gravitySolver.solve();
49191      this.nodesSolver.solve();
49192      this.edgesSolver.solve();
49193      this.moveNodes();
49194    }
49195
49196    /**
49197     * Make dynamic adjustments to the timestep, based on current state.
49198     *
49199     * Helper function for physicsTick().
49200     * @private
49201     */
49202
49203  }, {
49204    key: 'adjustTimeStep',
49205    value: function adjustTimeStep() {
49206      var factor = 1.2; // Factor for increasing the timestep on success.
49207
49208      // we compare the two steps. if it is acceptable we double the step.
49209      if (this._evaluateStepQuality() === true) {
49210        this.timestep = factor * this.timestep;
49211      } else {
49212        // if not, we decrease the step to a minimum of the options timestep.
49213        // if the decreased timestep is smaller than the options step, we do not reset the counter
49214        // we assume that the options timestep is stable enough.
49215        if (this.timestep / factor < this.options.timestep) {
49216          this.timestep = this.options.timestep;
49217        } else {
49218          // if the timestep was larger than 2 times the option one we check the adaptivity again to ensure
49219          // that large instabilities do not form.
49220          this.adaptiveCounter = -1; // check again next iteration
49221          this.timestep = Math.max(this.options.timestep, this.timestep / factor);
49222        }
49223      }
49224    }
49225
49226    /**
49227     * A single simulation step (or 'tick') in the physics simulation
49228     *
49229     * @private
49230     */
49231
49232  }, {
49233    key: 'physicsTick',
49234    value: function physicsTick() {
49235      this._startStabilizing(); // this ensures that there is no start event when the network is already stable.
49236      if (this.stabilized === true) return;
49237
49238      // adaptivity means the timestep adapts to the situation, only applicable for stabilization
49239      if (this.adaptiveTimestep === true && this.adaptiveTimestepEnabled === true) {
49240        // timestep remains stable for "interval" iterations.
49241        var doAdaptive = this.adaptiveCounter % this.adaptiveInterval === 0;
49242
49243        if (doAdaptive) {
49244          // first the big step and revert.
49245          this.timestep = 2 * this.timestep;
49246          this.physicsStep();
49247          this.revert(); // saves the reference state
49248
49249          // now the normal step. Since this is the last step, it is the more stable one and we will take this.
49250          this.timestep = 0.5 * this.timestep;
49251
49252          // since it's half the step, we do it twice.
49253          this.physicsStep();
49254          this.physicsStep();
49255
49256          this.adjustTimeStep();
49257        } else {
49258          this.physicsStep(); // normal step, keeping timestep constant
49259        }
49260
49261        this.adaptiveCounter += 1;
49262      } else {
49263        // case for the static timestep, we reset it to the one in options and take a normal step.
49264        this.timestep = this.options.timestep;
49265        this.physicsStep();
49266      }
49267
49268      if (this.stabilized === true) this.revert();
49269      this.stabilizationIterations++;
49270    }
49271
49272    /**
49273     * Nodes and edges can have the physics toggles on or off. A collection of indices is created here so we can skip the check all the time.
49274     *
49275     * @private
49276     */
49277
49278  }, {
49279    key: 'updatePhysicsData',
49280    value: function updatePhysicsData() {
49281      this.physicsBody.forces = {};
49282      this.physicsBody.physicsNodeIndices = [];
49283      this.physicsBody.physicsEdgeIndices = [];
49284      var nodes = this.body.nodes;
49285      var edges = this.body.edges;
49286
49287      // get node indices for physics
49288      for (var nodeId in nodes) {
49289        if (nodes.hasOwnProperty(nodeId)) {
49290          if (nodes[nodeId].options.physics === true) {
49291            this.physicsBody.physicsNodeIndices.push(nodes[nodeId].id);
49292          }
49293        }
49294      }
49295
49296      // get edge indices for physics
49297      for (var edgeId in edges) {
49298        if (edges.hasOwnProperty(edgeId)) {
49299          if (edges[edgeId].options.physics === true) {
49300            this.physicsBody.physicsEdgeIndices.push(edges[edgeId].id);
49301          }
49302        }
49303      }
49304
49305      // get the velocity and the forces vector
49306      for (var i = 0; i < this.physicsBody.physicsNodeIndices.length; i++) {
49307        var _nodeId = this.physicsBody.physicsNodeIndices[i];
49308        this.physicsBody.forces[_nodeId] = { x: 0, y: 0 };
49309
49310        // forces can be reset because they are recalculated. Velocities have to persist.
49311        if (this.physicsBody.velocities[_nodeId] === undefined) {
49312          this.physicsBody.velocities[_nodeId] = { x: 0, y: 0 };
49313        }
49314      }
49315
49316      // clean deleted nodes from the velocity vector
49317      for (var _nodeId2 in this.physicsBody.velocities) {
49318        if (nodes[_nodeId2] === undefined) {
49319          delete this.physicsBody.velocities[_nodeId2];
49320        }
49321      }
49322    }
49323
49324    /**
49325     * Revert the simulation one step. This is done so after stabilization, every new start of the simulation will also say stabilized.
49326     */
49327
49328  }, {
49329    key: 'revert',
49330    value: function revert() {
49331      var nodeIds = (0, _keys2['default'])(this.previousStates);
49332      var nodes = this.body.nodes;
49333      var velocities = this.physicsBody.velocities;
49334      this.referenceState = {};
49335
49336      for (var i = 0; i < nodeIds.length; i++) {
49337        var nodeId = nodeIds[i];
49338        if (nodes[nodeId] !== undefined) {
49339          if (nodes[nodeId].options.physics === true) {
49340            this.referenceState[nodeId] = {
49341              positions: { x: nodes[nodeId].x, y: nodes[nodeId].y }
49342            };
49343            velocities[nodeId].x = this.previousStates[nodeId].vx;
49344            velocities[nodeId].y = this.previousStates[nodeId].vy;
49345            nodes[nodeId].x = this.previousStates[nodeId].x;
49346            nodes[nodeId].y = this.previousStates[nodeId].y;
49347          }
49348        } else {
49349          delete this.previousStates[nodeId];
49350        }
49351      }
49352    }
49353
49354    /**
49355     * This compares the reference state to the current state
49356     *
49357     * @returns {boolean}
49358     * @private
49359     */
49360
49361  }, {
49362    key: '_evaluateStepQuality',
49363    value: function _evaluateStepQuality() {
49364      var dx = void 0,
49365          dy = void 0,
49366          dpos = void 0;
49367      var nodes = this.body.nodes;
49368      var reference = this.referenceState;
49369      var posThreshold = 0.3;
49370
49371      for (var nodeId in this.referenceState) {
49372        if (this.referenceState.hasOwnProperty(nodeId) && nodes[nodeId] !== undefined) {
49373          dx = nodes[nodeId].x - reference[nodeId].positions.x;
49374          dy = nodes[nodeId].y - reference[nodeId].positions.y;
49375
49376          dpos = Math.sqrt(Math.pow(dx, 2) + Math.pow(dy, 2));
49377
49378          if (dpos > posThreshold) {
49379            return false;
49380          }
49381        }
49382      }
49383      return true;
49384    }
49385
49386    /**
49387     * move the nodes one timestep and check if they are stabilized
49388     */
49389
49390  }, {
49391    key: 'moveNodes',
49392    value: function moveNodes() {
49393      var nodeIndices = this.physicsBody.physicsNodeIndices;
49394      var maxNodeVelocity = 0;
49395      var averageNodeVelocity = 0;
49396
49397      // the velocity threshold (energy in the system) for the adaptivity toggle
49398      var velocityAdaptiveThreshold = 5;
49399
49400      for (var i = 0; i < nodeIndices.length; i++) {
49401        var nodeId = nodeIndices[i];
49402        var nodeVelocity = this._performStep(nodeId);
49403        // stabilized is true if stabilized is true and velocity is smaller than vmin --> all nodes must be stabilized
49404        maxNodeVelocity = Math.max(maxNodeVelocity, nodeVelocity);
49405        averageNodeVelocity += nodeVelocity;
49406      }
49407
49408      // evaluating the stabilized and adaptiveTimestepEnabled conditions
49409      this.adaptiveTimestepEnabled = averageNodeVelocity / nodeIndices.length < velocityAdaptiveThreshold;
49410      this.stabilized = maxNodeVelocity < this.options.minVelocity;
49411    }
49412
49413    /**
49414     * Calculate new velocity for a coordinate direction
49415     *
49416     * @param {number} v  velocity for current coordinate
49417     * @param {number} f  regular force for current coordinate
49418     * @param {number} m  mass of current node
49419     * @returns {number} new velocity for current coordinate
49420     * @private
49421     */
49422
49423  }, {
49424    key: 'calculateComponentVelocity',
49425    value: function calculateComponentVelocity(v, f, m) {
49426      var df = this.modelOptions.damping * v; // damping force
49427      var a = (f - df) / m; // acceleration
49428
49429      v += a * this.timestep;
49430
49431      // Put a limit on the velocities if it is really high
49432      var maxV = this.options.maxVelocity || 1e9;
49433      if (Math.abs(v) > maxV) {
49434        v = v > 0 ? maxV : -maxV;
49435      }
49436
49437      return v;
49438    }
49439
49440    /**
49441     * Perform the actual step
49442     *
49443     * @param {Node.id} nodeId
49444     * @returns {number} the new velocity of given node
49445     * @private
49446     */
49447
49448  }, {
49449    key: '_performStep',
49450    value: function _performStep(nodeId) {
49451      var node = this.body.nodes[nodeId];
49452      var force = this.physicsBody.forces[nodeId];
49453      var velocity = this.physicsBody.velocities[nodeId];
49454
49455      // store the state so we can revert
49456      this.previousStates[nodeId] = { x: node.x, y: node.y, vx: velocity.x, vy: velocity.y };
49457
49458      if (node.options.fixed.x === false) {
49459        velocity.x = this.calculateComponentVelocity(velocity.x, force.x, node.options.mass);
49460        node.x += velocity.x * this.timestep;
49461      } else {
49462        force.x = 0;
49463        velocity.x = 0;
49464      }
49465
49466      if (node.options.fixed.y === false) {
49467        velocity.y = this.calculateComponentVelocity(velocity.y, force.y, node.options.mass);
49468        node.y += velocity.y * this.timestep;
49469      } else {
49470        force.y = 0;
49471        velocity.y = 0;
49472      }
49473
49474      var totalVelocity = Math.sqrt(Math.pow(velocity.x, 2) + Math.pow(velocity.y, 2));
49475      return totalVelocity;
49476    }
49477
49478    /**
49479     * When initializing and stabilizing, we can freeze nodes with a predefined position.
49480     * This greatly speeds up stabilization because only the supportnodes for the smoothCurves have to settle.
49481     *
49482     * @private
49483     */
49484
49485  }, {
49486    key: '_freezeNodes',
49487    value: function _freezeNodes() {
49488      var nodes = this.body.nodes;
49489      for (var id in nodes) {
49490        if (nodes.hasOwnProperty(id)) {
49491          if (nodes[id].x && nodes[id].y) {
49492            var fixed = nodes[id].options.fixed;
49493            this.freezeCache[id] = { x: fixed.x, y: fixed.y };
49494            fixed.x = true;
49495            fixed.y = true;
49496          }
49497        }
49498      }
49499    }
49500
49501    /**
49502     * Unfreezes the nodes that have been frozen by _freezeDefinedNodes.
49503     *
49504     * @private
49505     */
49506
49507  }, {
49508    key: '_restoreFrozenNodes',
49509    value: function _restoreFrozenNodes() {
49510      var nodes = this.body.nodes;
49511      for (var id in nodes) {
49512        if (nodes.hasOwnProperty(id)) {
49513          if (this.freezeCache[id] !== undefined) {
49514            nodes[id].options.fixed.x = this.freezeCache[id].x;
49515            nodes[id].options.fixed.y = this.freezeCache[id].y;
49516          }
49517        }
49518      }
49519      this.freezeCache = {};
49520    }
49521
49522    /**
49523     * Find a stable position for all nodes
49524     *
49525     * @param {number} [iterations=this.options.stabilization.iterations]
49526     */
49527
49528  }, {
49529    key: 'stabilize',
49530    value: function stabilize() {
49531      var _this3 = this;
49532
49533      var iterations = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : this.options.stabilization.iterations;
49534
49535      if (typeof iterations !== 'number') {
49536        iterations = this.options.stabilization.iterations;
49537        console.log('The stabilize method needs a numeric amount of iterations. Switching to default: ', iterations);
49538      }
49539
49540      if (this.physicsBody.physicsNodeIndices.length === 0) {
49541        this.ready = true;
49542        return;
49543      }
49544
49545      // enable adaptive timesteps
49546      this.adaptiveTimestep = true && this.options.adaptiveTimestep;
49547
49548      // this sets the width of all nodes initially which could be required for the avoidOverlap
49549      this.body.emitter.emit("_resizeNodes");
49550
49551      this.stopSimulation(); // stop the render loop
49552      this.stabilized = false;
49553
49554      // block redraw requests
49555      this.body.emitter.emit('_blockRedraw');
49556      this.targetIterations = iterations;
49557
49558      // start the stabilization
49559      if (this.options.stabilization.onlyDynamicEdges === true) {
49560        this._freezeNodes();
49561      }
49562      this.stabilizationIterations = 0;
49563
49564      setTimeout(function () {
49565        return _this3._stabilizationBatch();
49566      }, 0);
49567    }
49568
49569    /**
49570     * If not already stabilizing, start it and emit a start event.
49571     *
49572     * @returns {boolean} true if stabilization started with this call
49573     * @private
49574     */
49575
49576  }, {
49577    key: '_startStabilizing',
49578    value: function _startStabilizing() {
49579      if (this.startedStabilization === true) return false;
49580
49581      this.body.emitter.emit('startStabilizing');
49582      this.startedStabilization = true;
49583      return true;
49584    }
49585
49586    /**
49587     * One batch of stabilization
49588     * @private
49589     */
49590
49591  }, {
49592    key: '_stabilizationBatch',
49593    value: function _stabilizationBatch() {
49594      var _this4 = this;
49595
49596      var running = function running() {
49597        return _this4.stabilized === false && _this4.stabilizationIterations < _this4.targetIterations;
49598      };
49599
49600      var sendProgress = function sendProgress() {
49601        _this4.body.emitter.emit('stabilizationProgress', {
49602          iterations: _this4.stabilizationIterations,
49603          total: _this4.targetIterations
49604        });
49605      };
49606
49607      if (this._startStabilizing()) {
49608        sendProgress(); // Ensure that there is at least one start event.
49609      }
49610
49611      var count = 0;
49612      while (running() && count < this.options.stabilization.updateInterval) {
49613        this.physicsTick();
49614        count++;
49615      }
49616
49617      sendProgress();
49618
49619      if (running()) {
49620        setTimeout(this._stabilizationBatch.bind(this), 0);
49621      } else {
49622        this._finalizeStabilization();
49623      }
49624    }
49625
49626    /**
49627     * Wrap up the stabilization, fit and emit the events.
49628     * @private
49629     */
49630
49631  }, {
49632    key: '_finalizeStabilization',
49633    value: function _finalizeStabilization() {
49634      this.body.emitter.emit('_allowRedraw');
49635      if (this.options.stabilization.fit === true) {
49636        this.body.emitter.emit('fit');
49637      }
49638
49639      if (this.options.stabilization.onlyDynamicEdges === true) {
49640        this._restoreFrozenNodes();
49641      }
49642
49643      this.body.emitter.emit('stabilizationIterationsDone');
49644      this.body.emitter.emit('_requestRedraw');
49645
49646      if (this.stabilized === true) {
49647        this._emitStabilized();
49648      } else {
49649        this.startSimulation();
49650      }
49651
49652      this.ready = true;
49653    }
49654
49655    //---------------------------  DEBUGGING BELOW  ---------------------------//
49656
49657
49658    /**
49659     * Debug function that display arrows for the forces currently active in the network.
49660     *
49661     * Use this when debugging only.
49662     *
49663     * @param {CanvasRenderingContext2D} ctx
49664     * @private
49665     */
49666
49667  }, {
49668    key: '_drawForces',
49669    value: function _drawForces(ctx) {
49670      for (var i = 0; i < this.physicsBody.physicsNodeIndices.length; i++) {
49671        var index = this.physicsBody.physicsNodeIndices[i];
49672        var node = this.body.nodes[index];
49673        var force = this.physicsBody.forces[index];
49674        var factor = 20;
49675        var colorFactor = 0.03;
49676        var forceSize = Math.sqrt(Math.pow(force.x, 2) + Math.pow(force.x, 2));
49677
49678        var size = Math.min(Math.max(5, forceSize), 15);
49679        var arrowSize = 3 * size;
49680
49681        var color = util.HSVToHex((180 - Math.min(1, Math.max(0, colorFactor * forceSize)) * 180) / 360, 1, 1);
49682
49683        var point = {
49684          x: node.x + factor * force.x,
49685          y: node.y + factor * force.y
49686        };
49687
49688        ctx.lineWidth = size;
49689        ctx.strokeStyle = color;
49690        ctx.beginPath();
49691        ctx.moveTo(node.x, node.y);
49692        ctx.lineTo(point.x, point.y);
49693        ctx.stroke();
49694
49695        var angle = Math.atan2(force.y, force.x);
49696        ctx.fillStyle = color;
49697        EndPoints.draw(ctx, { type: 'arrow', point: point, angle: angle, length: arrowSize });
49698        ctx.fill();
49699      }
49700    }
49701  }]);
49702  return PhysicsEngine;
49703}();
49704
49705exports['default'] = PhysicsEngine;
49706
49707/***/ }),
49708/* 221 */
49709/***/ (function(module, exports, __webpack_require__) {
49710
49711"use strict";
49712
49713
49714Object.defineProperty(exports, "__esModule", {
49715  value: true
49716});
49717
49718var _classCallCheck2 = __webpack_require__(0);
49719
49720var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
49721
49722var _createClass2 = __webpack_require__(1);
49723
49724var _createClass3 = _interopRequireDefault(_createClass2);
49725
49726function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
49727
49728/**
49729 * Repulsion Solver
49730 */
49731var RepulsionSolver = function () {
49732  /**
49733   * @param {Object} body
49734   * @param {{physicsNodeIndices: Array, physicsEdgeIndices: Array, forces: {}, velocities: {}}} physicsBody
49735   * @param {Object} options
49736   */
49737  function RepulsionSolver(body, physicsBody, options) {
49738    (0, _classCallCheck3["default"])(this, RepulsionSolver);
49739
49740    this.body = body;
49741    this.physicsBody = physicsBody;
49742    this.setOptions(options);
49743  }
49744
49745  /**
49746   *
49747   * @param {Object} options
49748   */
49749
49750
49751  (0, _createClass3["default"])(RepulsionSolver, [{
49752    key: "setOptions",
49753    value: function setOptions(options) {
49754      this.options = options;
49755    }
49756
49757    /**
49758     * Calculate the forces the nodes apply on each other based on a repulsion field.
49759     * This field is linearly approximated.
49760     *
49761     * @private
49762     */
49763
49764  }, {
49765    key: "solve",
49766    value: function solve() {
49767      var dx, dy, distance, fx, fy, repulsingForce, node1, node2;
49768
49769      var nodes = this.body.nodes;
49770      var nodeIndices = this.physicsBody.physicsNodeIndices;
49771      var forces = this.physicsBody.forces;
49772
49773      // repulsing forces between nodes
49774      var nodeDistance = this.options.nodeDistance;
49775
49776      // approximation constants
49777      var a = -2 / 3 / nodeDistance;
49778      var b = 4 / 3;
49779
49780      // we loop from i over all but the last entree in the array
49781      // j loops from i+1 to the last. This way we do not double count any of the indices, nor i === j
49782      for (var i = 0; i < nodeIndices.length - 1; i++) {
49783        node1 = nodes[nodeIndices[i]];
49784        for (var j = i + 1; j < nodeIndices.length; j++) {
49785          node2 = nodes[nodeIndices[j]];
49786
49787          dx = node2.x - node1.x;
49788          dy = node2.y - node1.y;
49789          distance = Math.sqrt(dx * dx + dy * dy);
49790
49791          // same condition as BarnesHutSolver, making sure nodes are never 100% overlapping.
49792          if (distance === 0) {
49793            distance = 0.1 * Math.random();
49794            dx = distance;
49795          }
49796
49797          if (distance < 2 * nodeDistance) {
49798            if (distance < 0.5 * nodeDistance) {
49799              repulsingForce = 1.0;
49800            } else {
49801              repulsingForce = a * distance + b; // linear approx of  1 / (1 + Math.exp((distance / nodeDistance - 1) * steepne
49801ss))
49802            }
49803            repulsingForce = repulsingForce / distance;
49804
49805            fx = dx * repulsingForce;
49806            fy = dy * repulsingForce;
49807
49808            forces[node1.id].x -= fx;
49809            forces[node1.id].y -= fy;
49810            forces[node2.id].x += fx;
49811            forces[node2.id].y += fy;
49812          }
49813        }
49814      }
49815    }
49816  }]);
49817  return RepulsionSolver;
49818}();
49819
49820exports["default"] = RepulsionSolver;
49821
49822/***/ }),
49823/* 222 */
49824/***/ (function(module, exports, __webpack_require__) {
49825
49826"use strict";
49827
49828
49829Object.defineProperty(exports, "__esModule", {
49830  value: true
49831});
49832
49833var _classCallCheck2 = __webpack_require__(0);
49834
49835var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
49836
49837var _createClass2 = __webpack_require__(1);
49838
49839var _createClass3 = _interopRequireDefault(_createClass2);
49840
49841function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
49842
49843/**
49844 * Hierarchical Repulsion Solver
49845 */
49846var HierarchicalRepulsionSolver = function () {
49847  /**
49848   * @param {Object} body
49849   * @param {{physicsNodeIndices: Array, physicsEdgeIndices: Array, forces: {}, velocities: {}}} physicsBody
49850   * @param {Object} options
49851   */
49852  function HierarchicalRepulsionSolver(body, physicsBody, options) {
49853    (0, _classCallCheck3["default"])(this, HierarchicalRepulsionSolver);
49854
49855    this.body = body;
49856    this.physicsBody = physicsBody;
49857    this.setOptions(options);
49858  }
49859
49860  /**
49861   *
49862   * @param {Object} options
49863   */
49864
49865
49866  (0, _createClass3["default"])(HierarchicalRepulsionSolver, [{
49867    key: "setOptions",
49868    value: function setOptions(options) {
49869      this.options = options;
49870    }
49871
49872    /**
49873     * Calculate the forces the nodes apply on each other based on a repulsion field.
49874     * This field is linearly approximated.
49875     *
49876     * @private
49877     */
49878
49879  }, {
49880    key: "solve",
49881    value: function solve() {
49882      var dx, dy, distance, fx, fy, repulsingForce, node1, node2, i, j;
49883
49884      var nodes = this.body.nodes;
49885      var nodeIndices = this.physicsBody.physicsNodeIndices;
49886      var forces = this.physicsBody.forces;
49887
49888      // repulsing forces between nodes
49889      var nodeDistance = this.options.nodeDistance;
49890
49891      // we loop from i over all but the last entree in the array
49892      // j loops from i+1 to the last. This way we do not double count any of the indices, nor i === j
49893      for (i = 0; i < nodeIndices.length - 1; i++) {
49894        node1 = nodes[nodeIndices[i]];
49895        for (j = i + 1; j < nodeIndices.length; j++) {
49896          node2 = nodes[nodeIndices[j]];
49897
49898          // nodes only affect nodes on their level
49899          if (node1.level === node2.level) {
49900            dx = node2.x - node1.x;
49901            dy = node2.y - node1.y;
49902            distance = Math.sqrt(dx * dx + dy * dy);
49903
49904            var steepness = 0.05;
49905            if (distance < nodeDistance) {
49906              repulsingForce = -Math.pow(steepness * distance, 2) + Math.pow(steepness * nodeDistance, 2);
49907            } else {
49908              repulsingForce = 0;
49909            }
49910            // normalize force with
49911            if (distance === 0) {
49912              distance = 0.01;
49913            } else {
49914              repulsingForce = repulsingForce / distance;
49915            }
49916            fx = dx * repulsingForce;
49917            fy = dy * repulsingForce;
49918
49919            forces[node1.id].x -= fx;
49920            forces[node1.id].y -= fy;
49921            forces[node2.id].x += fx;
49922            forces[node2.id].y += fy;
49923          }
49924        }
49925      }
49926    }
49927  }]);
49928  return HierarchicalRepulsionSolver;
49929}();
49930
49931exports["default"] = HierarchicalRepulsionSolver;
49932
49933/***/ }),
49934/* 223 */
49935/***/ (function(module, exports, __webpack_require__) {
49936
49937"use strict";
49938
49939
49940Object.defineProperty(exports, "__esModule", {
49941  value: true
49942});
49943
49944var _classCallCheck2 = __webpack_require__(0);
49945
49946var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
49947
49948var _createClass2 = __webpack_require__(1);
49949
49950var _createClass3 = _interopRequireDefault(_createClass2);
49951
49952function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
49953
49954/**
49955 * Spring Solver
49956 */
49957var SpringSolver = function () {
49958  /**
49959   * @param {Object} body
49960   * @param {{physicsNodeIndices: Array, physicsEdgeIndices: Array, forces: {}, velocities: {}}} physicsBody
49961   * @param {Object} options
49962   */
49963  function SpringSolver(body, physicsBody, options) {
49964    (0, _classCallCheck3["default"])(this, SpringSolver);
49965
49966    this.body = body;
49967    this.physicsBody = physicsBody;
49968    this.setOptions(options);
49969  }
49970
49971  /**
49972   *
49973   * @param {Object} options
49974   */
49975
49976
49977  (0, _createClass3["default"])(SpringSolver, [{
49978    key: "setOptions",
49979    value: function setOptions(options) {
49980      this.options = options;
49981    }
49982
49983    /**
49984     * This function calculates the springforces on the nodes, accounting for the support nodes.
49985     *
49986     * @private
49987     */
49988
49989  }, {
49990    key: "solve",
49991    value: function solve() {
49992      var edgeLength = void 0,
49993          edge = void 0;
49994      var edgeIndices = this.physicsBody.physicsEdgeIndices;
49995      var edges = this.body.edges;
49996      var node1 = void 0,
49997          node2 = void 0,
49998          node3 = void 0;
49999
50000      // forces caused by the edges, modelled as springs
50001      for (var i = 0; i < edgeIndices.length; i++) {
50002        edge = edges[edgeIndices[i]];
50003        if (edge.connected === true && edge.toId !== edge.fromId) {
50004          // only calculate forces if nodes are in the same sector
50005          if (this.body.nodes[edge.toId] !== undefined && this.body.nodes[edge.fromId] !== undefined) {
50006            if (edge.edgeType.via !== undefined) {
50007              edgeLength = edge.options.length === undefined ? this.options.springLength : edge.options.length;
50008              node1 = edge.to;
50009              node2 = edge.edgeType.via;
50010              node3 = edge.from;
50011
50012              this._calculateSpringForce(node1, node2, 0.5 * edgeLength);
50013              this._calculateSpringForce(node2, node3, 0.5 * edgeLength);
50014            } else {
50015              // the * 1.5 is here so the edge looks as large as a smooth edge. It does not initially because the smooth edges use
50016              // the support nodes which exert a repulsive force on the to and from nodes, making the edge appear larger.
50017              edgeLength = edge.options.length === undefined ? this.options.springLength * 1.5 : edge.options.length;
50018              this._calculateSpringForce(edge.from, edge.to, edgeLength);
50019            }
50020          }
50021        }
50022      }
50023    }
50024
50025    /**
50026     * This is the code actually performing the calculation for the function above.
50027     *
50028     * @param {Node} node1
50029     * @param {Node} node2
50030     * @param {number} edgeLength
50031     * @private
50032     */
50033
50034  }, {
50035    key: "_calculateSpringForce",
50036    value: function _calculateSpringForce(node1, node2, edgeLength) {
50037      var dx = node1.x - node2.x;
50038      var dy = node1.y - node2.y;
50039      var distance = Math.max(Math.sqrt(dx * dx + dy * dy), 0.01);
50040
50041      // the 1/distance is so the fx and fy can be calculated without sine or cosine.
50042      var springForce = this.options.springConstant * (edgeLength - distance) / distance;
50043
50044      var fx = dx * springForce;
50045      var fy = dy * springForce;
50046
50047      // handle the case where one node is not part of the physcis
50048      if (this.physicsBody.forces[node1.id] !== undefined) {
50049        this.physicsBody.forces[node1.id].x += fx;
50050        this.physicsBody.forces[node1.id].y += fy;
50051      }
50052
50053      if (this.physicsBody.forces[node2.id] !== undefined) {
50054        this.physicsBody.forces[node2.id].x -= fx;
50055        this.physicsBody.forces[node2.id].y -= fy;
50056      }
50057    }
50058  }]);
50059  return SpringSolver;
50060}();
50061
50062exports["default"] = SpringSolver;
50063
50064/***/ }),
50065/* 224 */
50066/***/ (function(module, exports, __webpack_require__) {
50067
50068"use strict";
50069
50070
50071Object.defineProperty(exports, "__esModule", {
50072  value: true
50073});
50074
50075var _classCallCheck2 = __webpack_require__(0);
50076
50077var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
50078
50079var _createClass2 = __webpack_require__(1);
50080
50081var _createClass3 = _interopRequireDefault(_createClass2);
50082
50083function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
50084
50085/**
50086 * Hierarchical Spring Solver
50087 */
50088var HierarchicalSpringSolver = function () {
50089  /**
50090   * @param {Object} body
50091   * @param {{physicsNodeIndices: Array, physicsEdgeIndices: Array, forces: {}, velocities: {}}} physicsBody
50092   * @param {Object} options
50093   */
50094  function HierarchicalSpringSolver(body, physicsBody, options) {
50095    (0, _classCallCheck3["default"])(this, HierarchicalSpringSolver);
50096
50097    this.body = body;
50098    this.physicsBody = physicsBody;
50099    this.setOptions(options);
50100  }
50101
50102  /**
50103   *
50104   * @param {Object} options
50105   */
50106
50107
50108  (0, _createClass3["default"])(HierarchicalSpringSolver, [{
50109    key: "setOptions",
50110    value: function setOptions(options) {
50111      this.options = options;
50112    }
50113
50114    /**
50115     * This function calculates the springforces on the nodes, accounting for the support nodes.
50116     *
50117     * @private
50118     */
50119
50120  }, {
50121    key: "solve",
50122    value: function solve() {
50123      var edgeLength, edge;
50124      var dx, dy, fx, fy, springForce, distance;
50125      var edges = this.body.edges;
50126      var factor = 0.5;
50127
50128      var edgeIndices = this.physicsBody.physicsEdgeIndices;
50129      var nodeIndices = this.physicsBody.physicsNodeIndices;
50130      var forces = this.physicsBody.forces;
50131
50132      // initialize the spring force counters
50133      for (var i = 0; i < nodeIndices.length; i++) {
50134        var nodeId = nodeIndices[i];
50135        forces[nodeId].springFx = 0;
50136        forces[nodeId].springFy = 0;
50137      }
50138
50139      // forces caused by the edges, modelled as springs
50140      for (var _i = 0; _i < edgeIndices.length; _i++) {
50141        edge = edges[edgeIndices[_i]];
50142        if (edge.connected === true) {
50143          edgeLength = edge.options.length === undefined ? this.options.springLength : edge.options.length;
50144
50145          dx = edge.from.x - edge.to.x;
50146          dy = edge.from.y - edge.to.y;
50147          distance = Math.sqrt(dx * dx + dy * dy);
50148          distance = distance === 0 ? 0.01 : distance;
50149
50150          // the 1/distance is so the fx and fy can be calculated without sine or cosine.
50151          springForce = this.options.springConstant * (edgeLength - distance) / distance;
50152
50153          fx = dx * springForce;
50154          fy = dy * springForce;
50155
50156          if (edge.to.level != edge.from.level) {
50157            if (forces[edge.toId] !== undefined) {
50158              forces[edge.toId].springFx -= fx;
50159              forces[edge.toId].springFy -= fy;
50160            }
50161            if (forces[edge.fromId] !== undefined) {
50162              forces[edge.fromId].springFx += fx;
50163              forces[edge.fromId].springFy += fy;
50164            }
50165          } else {
50166            if (forces[edge.toId] !== undefined) {
50167              forces[edge.toId].x -= factor * fx;
50168              forces[edge.toId].y -= factor * fy;
50169            }
50170            if (forces[edge.fromId] !== undefined) {
50171              forces[edge.fromId].x += factor * fx;
50172              forces[edge.fromId].y += factor * fy;
50173            }
50174          }
50175        }
50176      }
50177
50178      // normalize spring forces
50179      springForce = 1;
50180      var springFx, springFy;
50181      for (var _i2 = 0; _i2 < nodeIndices.length; _i2++) {
50182        var _nodeId = nodeIndices[_i2];
50183        springFx = Math.min(springForce, Math.max(-springForce, forces[_nodeId].springFx));
50184        springFy = Math.min(springForce, Math.max(-springForce, forces[_nodeId].springFy));
50185
50186        forces[_nodeId].x += springFx;
50187        forces[_nodeId].y += springFy;
50188      }
50189
50190      // retain energy balance
50191      var totalFx = 0;
50192      var totalFy = 0;
50193      for (var _i3 = 0; _i3 < nodeIndices.length; _i3++) {
50194        var _nodeId2 = nodeIndices[_i3];
50195        totalFx += forces[_nodeId2].x;
50196        totalFy += forces[_nodeId2].y;
50197      }
50198      var correctionFx = totalFx / nodeIndices.length;
50199      var correctionFy = totalFy / nodeIndices.length;
50200
50201      for (var _i4 = 0; _i4 < nodeIndices.length; _i4++) {
50202        var _nodeId3 = nodeIndices[_i4];
50203        forces[_nodeId3].x -= correctionFx;
50204        forces[_nodeId3].y -= correctionFy;
50205      }
50206    }
50207  }]);
50208  return HierarchicalSpringSolver;
50209}();
50210
50211exports["default"] = HierarchicalSpringSolver;
50212
50213/***/ }),
50214/* 225 */
50215/***/ (function(module, exports, __webpack_require__) {
50216
50217"use strict";
50218
50219
50220Object.defineProperty(exports, "__esModule", {
50221  value: true
50222});
50223
50224var _getPrototypeOf = __webpack_require__(3);
50225
50226var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf);
50227
50228var _classCallCheck2 = __webpack_require__(0);
50229
50230var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
50231
50232var _createClass2 = __webpack_require__(1);
50233
50234var _createClass3 = _interopRequireDefault(_createClass2);
50235
50236var _possibleConstructorReturn2 = __webpack_require__(4);
50237
50238var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2);
50239
50240var _inherits2 = __webpack_require__(5);
50241
50242var _inherits3 = _interopRequireDefault(_inherits2);
50243
50244var _BarnesHutSolver2 = __webpack_require__(120);
50245
50246var _BarnesHutSolver3 = _interopRequireDefault(_BarnesHutSolver2);
50247
50248function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
50249
50250/**
50251 * @extends BarnesHutSolver
50252 */
50253var ForceAtlas2BasedRepulsionSolver = function (_BarnesHutSolver) {
50254  (0, _inherits3["default"])(ForceAtlas2BasedRepulsionSolver, _BarnesHutSolver);
50255
50256  /**
50257   * @param {Object} body
50258   * @param {{physicsNodeIndices: Array, physicsEdgeIndices: Array, forces: {}, velocities: {}}} physicsBody
50259   * @param {Object} options
50260   */
50261  function ForceAtlas2BasedRepulsionSolver(body, physicsBody, options) {
50262    (0, _classCallCheck3["default"])(this, ForceAtlas2BasedRepulsionSolver);
50263    return (0, _possibleConstructorReturn3["default"])(this, (ForceAtlas2BasedRepulsionSolver.__proto__ || (0, _getPrototypeOf2["default"])(ForceAtlas2BasedRepulsionSolver)).call(this, body, physicsBody, options));
50264  }
50265
50266  /**
50267   * Calculate the forces based on the distance.
50268   *
50269   * @param {number} distance
50270   * @param {number} dx
50271   * @param {number} dy
50272   * @param {Node} node
50273   * @param {Object} parentBranch
50274   * @private
50275   */
50276
50277
50278  (0, _createClass3["default"])(ForceAtlas2BasedRepulsionSolver, [{
50279    key: "_calculateForces",
50280    value: function _calculateForces(distance, dx, dy, node, parentBranch) {
50281      if (distance === 0) {
50282        distance = 0.1 * Math.random();
50283        dx = distance;
50284      }
50285
50286      if (this.overlapAvoidanceFactor < 1 && node.shape.radius) {
50287        distance = Math.max(0.1 + this.overlapAvoidanceFactor * node.shape.radius, distance - node.shape.radius);
50288      }
50289
50290      var degree = node.edges.length + 1;
50291      // the dividing by the distance cubed instead of squared allows us to get the fx and fy components without sines and cosines
50292      // it is shorthand for gravityforce with distance squared and fx = dx/distance * gravityForce
50293      var gravityForce = this.options.gravitationalConstant * parentBranch.mass * node.options.mass * degree / Math.pow(distance, 2);
50294      var fx = dx * gravityForce;
50295      var fy = dy * gravityForce;
50296
50297      this.physicsBody.forces[node.id].x += fx;
50298      this.physicsBody.forces[node.id].y += fy;
50299    }
50300  }]);
50301  return ForceAtlas2BasedRepulsionSolver;
50302}(_BarnesHutSolver3["default"]);
50303
50304exports["default"] = ForceAtlas2BasedRepulsionSolver;
50305
50306/***/ }),
50307/* 226 */
50308/***/ (function(module, exports, __webpack_require__) {
50309
50310"use strict";
50311
50312
50313Object.defineProperty(exports, "__esModule", {
50314  value: true
50315});
50316
50317var _getPrototypeOf = __webpack_require__(3);
50318
50319var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf);
50320
50321var _classCallCheck2 = __webpack_require__(0);
50322
50323var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
50324
50325var _createClass2 = __webpack_require__(1);
50326
50327var _createClass3 = _interopRequireDefault(_createClass2);
50328
50329var _possibleConstructorReturn2 = __webpack_require__(4);
50330
50331var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2);
50332
50333var _inherits2 = __webpack_require__(5);
50334
50335var _inherits3 = _interopRequireDefault(_inherits2);
50336
50337var _CentralGravitySolver2 = __webpack_require__(121);
50338
50339var _CentralGravitySolver3 = _interopRequireDefault(_CentralGravitySolver2);
50340
50341function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
50342
50343/**
50344 * @extends CentralGravitySolver
50345 */
50346var ForceAtlas2BasedCentralGravitySolver = function (_CentralGravitySolver) {
50347  (0, _inherits3["default"])(ForceAtlas2BasedCentralGravitySolver, _CentralGravitySolver);
50348
50349  /**
50350   * @param {Object} body
50351   * @param {{physicsNodeIndices: Array, physicsEdgeIndices: Array, forces: {}, velocities: {}}} physicsBody
50352   * @param {Object} options
50353   */
50354  function ForceAtlas2BasedCentralGravitySolver(body, physicsBody, options) {
50355    (0, _classCallCheck3["default"])(this, ForceAtlas2BasedCentralGravitySolver);
50356    return (0, _possibleConstructorReturn3["default"])(this, (ForceAtlas2BasedCentralGravitySolver.__proto__ || (0, _getPrototypeOf2["default"])(ForceAtlas2BasedCentralGravitySolver)).call(this, body, physicsBody, options));
50357  }
50358
50359  /**
50360   * Calculate the forces based on the distance.
50361   *
50362   * @param {number} distance
50363   * @param {number} dx
50364   * @param {number} dy
50365   * @param {Object<Node.id, Node>} forces
50366   * @param {Node} node
50367   * @private
50368   */
50369
50370
50371  (0, _createClass3["default"])(ForceAtlas2BasedCentralGravitySolver, [{
50372    key: "_calculateForces",
50373    value: function _calculateForces(distance, dx, dy, forces, node) {
50374      if (distance > 0) {
50375        var degree = node.edges.length + 1;
50376        var gravityForce = this.options.centralGravity * degree * node.options.mass;
50377        forces[node.id].x = dx * gravityForce;
50378        forces[node.id].y = dy * gravityForce;
50379      }
50380    }
50381  }]);
50382  return ForceAtlas2BasedCentralGravitySolver;
50383}(_CentralGravitySolver3["default"]);
50384
50385exports["default"] = ForceAtlas2BasedCentralGravitySolver;
50386
50387/***/ }),
50388/* 227 */
50389/***/ (function(module, exports, __webpack_require__) {
50390
50391"use strict";
50392
50393
50394Object.defineProperty(exports, "__esModule", {
50395  value: true
50396});
50397
50398var _keys = __webpack_require__(8);
50399
50400var _keys2 = _interopRequireDefault(_keys);
50401
50402var _typeof2 = __webpack_require__(6);
50403
50404var _typeof3 = _interopRequireDefault(_typeof2);
50405
50406var _classCallCheck2 = __webpack_require__(0);
50407
50408var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
50409
50410var _createClass2 = __webpack_require__(1);
50411
50412var _createClass3 = _interopRequireDefault(_createClass2);
50413
50414function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
50415
50416/* ===========================================================================
50417
50418# TODO
50419
50420- `edgeReplacedById` not cleaned up yet on cluster edge removal
50421- allowSingleNodeCluster could be a global option as well; currently needs to always
50422  be passed to clustering methods
50423
50424----------------------------------------------
50425
50426# State Model for Clustering
50427
50428The total state for clustering is non-trivial. It is useful to have a model
50429available as to how it works. The following documents the relevant state items.
50430
50431
50432## Network State
50433
50434The following `network`-members are relevant to clustering:
50435
50436- `body.nodes`       - all nodes actively participating in the network
50437- `body.edges`       - same for edges
50438- `body.nodeIndices` - id's of nodes that are visible at a given moment
50439- `body.edgeIndices` - same for edges
50440
50441This includes:
50442
50443- helper nodes for dragging in `manipulation`
50444- helper nodes for edge type `dynamic`
50445- cluster nodes and edges
50446- there may be more than this.
50447
50448A node/edge may be missing in the `Indices` member if:
50449
50450- it is a helper node
50451- the node or edge state has option `hidden` set
50452- It is not visible due to clustering
50453
50454
50455## Clustering State
50456
50457For the hashes, the id's of the nodes/edges are used as key.
50458
50459Member `network.clustering` contains the following items:
50460
50461- `clusteredNodes` - hash with values: { clusterId: <id of cluster>, node: <node instance>}
50462- `clusteredEdges` - hash with values: restore information for given edge
50463
50464
50465Due to nesting of clusters, these members can contain cluster nodes and edges as well.
50466
50467The important thing to note here, is that the clustered nodes and edges also
50468appear in the members of the cluster nodes. For data update, it is therefore 
50469important to scan these lists as well as the cluster nodes.
50470
50471
50472### Cluster Node
50473
50474A cluster node has the following extra fields:
50475
50476- `isCluster : true` - indication that this is a cluster node
50477- `containedNodes`   - hash of nodes contained in this cluster
50478- `containedEdges`   - same for edges
50479- `edges`            - array of cluster edges for this node 
50480
50481
50482**NOTE:**
50483
50484- `containedEdges` can also contain edges which are not clustered; e.g. an edge
50485   connecting two nodes in the same cluster.
50486
50487
50488### Cluster Edge
50489
50490These are the items in the `edges` member of a clustered node. They have the
50491following relevant members:
50492
50493- 'clusteringEdgeReplacingIds` - array of id's of edges replaced by this edge
50494
50495Note that it's possible to nest clusters, so that `clusteringEdgeReplacingIds`
50496can contain edge id's of other clusters.
50497
50498
50499### Clustered Edge
50500
50501This is any edge contained by a cluster edge. It gets the following additional
50502member:
50503
50504- `edgeReplacedById` - id of the cluster edge in which current edge is clustered
50505
50506
50507   =========================================================================== */
50508var util = __webpack_require__(2);
50509var NetworkUtil = __webpack_require__(76)['default'];
50510var Cluster = __webpack_require__(228)['default'];
50511var Edge = __webpack_require__(74)['default']; // Only needed for check on type!
50512var Node = __webpack_require__(47)['default']; // Only needed for check on type!
50513
50514/**
50515 * The clustering engine
50516 */
50517
50518var ClusterEngine = function () {
50519  /**
50520   * @param {Object} body
50521   */
50522  function ClusterEngine(body) {
50523    var _this = this;
50524
50525    (0, _classCallCheck3['default'])(this, ClusterEngine);
50526
50527    this.body = body;
50528    this.clusteredNodes = {}; // key: node id, value: { clusterId: <id of cluster>, node: <node instance>}
50529    this.clusteredEdges = {}; // key: edge id, value: restore information for given edge
50530
50531    this.options = {};
50532    this.defaultOptions = {};
50533    util.extend(this.options, this.defaultOptions);
50534
50535    this.body.emitter.on('_resetData', function () {
50536      _this.clusteredNodes = {};_this.clusteredEdges = {};
50537    });
50538  }
50539
50540  /**
50541  *
50542  * @param {number} hubsize
50543  * @param {Object} options
50544  */
50545
50546
50547  (0, _createClass3['default'])(ClusterEngine, [{
50548    key: 'clusterByHubsize',
50549    value: function clusterByHubsize(hubsize, options) {
50550      if (hubsize === undefined) {
50551        hubsize = this._getHubSize();
50552      } else if ((typeof hubsize === 'undefined' ? 'undefined' : (0, _typeof3['default'])(hubsize)) === "object") {
50553        options = this._checkOptions(hubsize);
50554        hubsize = this._getHubSize();
50555      }
50556
50557      var nodesToCluster = [];
50558      for (var i = 0; i < this.body.nodeIndices.length; i++) {
50559        var node = this.body.nodes[this.body.nodeIndices[i]];
50560        if (node.edges.length >= hubsize) {
50561          nodesToCluster.push(node.id);
50562        }
50563      }
50564
50565      for (var _i = 0; _i < nodesToCluster.length; _i++) {
50566        this.clusterByConnection(nodesToCluster[_i], options, true);
50567      }
50568
50569      this.body.emitter.emit('_dataChanged');
50570    }
50571
50572    /**
50573     * loop over all nodes, check if they adhere to the condition and cluster if needed.
50574     * @param {Object} options
50575     * @param {boolean} [refreshData=true]
50576     */
50577
50578  }, {
50579    key: 'cluster',
50580    value: function cluster() {
50581      var _this2 = this;
50582
50583      var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
50584      var refreshData = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
50585
50586      if (options.joinCondition === undefined) {
50587        throw new Error("Cannot call clusterByNodeData without a joinCondition function in the options.");
50588      }
50589
50590      // check if the options object is fine, append if needed
50591      options = this._checkOptions(options);
50592
50593      var childNodesObj = {};
50594      var childEdgesObj = {};
50595
50596      // collect the nodes that will be in the cluster
50597      util.forEach(this.body.nodes, function (node, nodeId) {
50598        var clonedOptions = NetworkUtil.cloneOptions(node);
50599        if (options.joinCondition(clonedOptions) === true) {
50600          childNodesObj[nodeId] = node;
50601
50602          // collect the edges that will be in the cluster
50603          util.forEach(node.edges, function (edge) {
50604            if (_this2.clusteredEdges[edge.id] === undefined) {
50605              childEdgesObj[edge.id] = edge;
50606            }
50607          });
50608        }
50609      });
50610
50611      this._cluster(childNodesObj, childEdgesObj, options, refreshData);
50612    }
50613
50614    /**
50615     * Cluster all nodes in the network that have only X edges
50616     * @param {number} edgeCount
50617     * @param {Object} options
50618     * @param {boolean} [refreshData=true]
50619     */
50620
50621  }, {
50622    key: 'clusterByEdgeCount',
50623    value: function clusterByEdgeCount(edgeCount, options) {
50624      var _this3 = this;
50625
50626      var refreshData = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
50627
50628      options = this._checkOptions(options);
50629      var clusters = [];
50630      var usedNodes = {};
50631      var edge = void 0,
50632          edges = void 0,
50633          relevantEdgeCount = void 0;
50634      // collect the nodes that will be in the cluster
50635
50636      var _loop = function _loop(i) {
50637        var childNodesObj = {};
50638        var childEdgesObj = {};
50639        var nodeId = _this3.body.nodeIndices[i];
50640        var node = _this3.body.nodes[nodeId];
50641
50642        // if this node is already used in another cluster this session, we do not have to re-evaluate it.
50643        if (usedNodes[nodeId] === undefined) {
50644          relevantEdgeCount = 0;
50645          edges = [];
50646          for (var j = 0; j < node.edges.length; j++) {
50647            edge = node.edges[j];
50648            if (_this3.clusteredEdges[edge.id] === undefined) {
50649              if (edge.toId !== edge.fromId) {
50650                relevantEdgeCount++;
50651              }
50652              edges.push(edge);
50653            }
50654          }
50655
50656          // this node qualifies, we collect its neighbours to start the clustering process.
50657          if (relevantEdgeCount === edgeCount) {
50658            checkJoinCondition = function checkJoinCondition(node) {
50659              if (options.joinCondition === undefined || options.joinCondition === null) {
50660                return true;
50661              }
50662
50663              var clonedOptions = NetworkUtil.cloneOptions(node);
50664              return options.joinCondition(clonedOptions);
50665            };
50666
50667            var gatheringSuccessful = true;
50668            for (var _j = 0; _j < edges.length; _j++) {
50669              edge = edges[_j];
50670              var childNodeId = _this3._getConnectedId(edge, nodeId);
50671              // add the nodes to the list by the join condition.
50672              if (checkJoinCondition(node)) {
50673                childEdgesObj[edge.id] = edge;
50674                childNodesObj[nodeId] = node;
50675                childNodesObj[childNodeId] = _this3.body.nodes[childNodeId];
50676                usedNodes[nodeId] = true;
50677              } else {
50678                // this node does not qualify after all.
50679                gatheringSuccessful = false;
50680                break;
50681              }
50682            }
50683
50684            // add to the cluster queue
50685            if ((0, _keys2['default'])(childNodesObj).length > 0 && (0, _keys2['default'])(childEdgesObj).length > 0 && gatheringSuccessful === true) {
50686              /**
50687               * Search for cluster data that contains any of the node id's
50688               * @returns {Boolean} true if no joinCondition, otherwise return value of joinCondition
50689               */
50690              findClusterData = function findClusterData() {
50691                for (var n = 0; n < clusters.length; ++n) {
50692                  // Search for a cluster containing any of the node id's
50693                  for (var m in childNodesObj) {
50694                    if (clusters[n].nodes[m] !== undefined) {
50695                      return clusters[n];
50696                    }
50697                  }
50698                }
50699
50700                return undefined;
50701              };
50702
50703              // If any of the found nodes is part of a cluster found in this method,
50704              // add the current values to that cluster
50705
50706
50707              foundCluster = findClusterData();
50708
50709              if (foundCluster !== undefined) {
50710                // Add nodes to found cluster if not present
50711                for (var m in childNodesObj) {
50712                  if (foundCluster.nodes[m] === undefined) {
50713                    foundCluster.nodes[m] = childNodesObj[m];
50714                  }
50715                }
50716
50717                // Add edges to found cluster, if not present
50718                for (var _m in childEdgesObj) {
50719                  if (foundCluster.edges[_m] === undefined) {
50720                    foundCluster.edges[_m] = childEdgesObj[_m];
50721                  }
50722                }
50723              } else {
50724                // Create a new cluster group
50725                clusters.push({ nodes: childNodesObj, edges: childEdgesObj });
50726              }
50727            }
50728          }
50729        }
50730      };
50731
50732      for (var i = 0; i < this.body.nodeIndices.length; i++) {
50733        var checkJoinCondition;
50734        var findClusterData;
50735        var foundCluster;
50736
50737        _loop(i);
50738      }
50739
50740      for (var i = 0; i < clusters.length; i++) {
50741        this._cluster(clusters[i].nodes, clusters[i].edges, options, false);
50742      }
50743
50744      if (refreshData === true) {
50745        this.body.emitter.emit('_dataChanged');
50746      }
50747    }
50748
50749    /**
50750     * Cluster all nodes in the network that have only 1 edge
50751     * @param {Object} options
50752     * @param {boolean} [refreshData=true]
50753     */
50754
50755  }, {
50756    key: 'clusterOutliers',
50757    value: function clusterOutliers(options) {
50758      var refreshData = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
50759
50760      this.clusterByEdgeCount(1, options, refreshData);
50761    }
50762
50763    /**
50764     * Cluster all nodes in the network that have only 2 edge
50765     * @param {Object} options
50766     * @param {boolean} [refreshData=true]
50767     */
50768
50769  }, {
50770    key: 'clusterBridges',
50771    value: function clusterBridges(options) {
50772      var refreshData = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
50773
50774      this.clusterByEdgeCount(2, options, refreshData);
50775    }
50776
50777    /**
50778    * suck all connected nodes of a node into the node.
50779    * @param {Node.id} nodeId
50780    * @param {Object} options
50781    * @param {boolean} [refreshData=true]
50782    */
50783
50784  }, {
50785    key: 'clusterByConnection',
50786    value: function clusterByConnection(nodeId, options) {
50787      var refreshData = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
50788
50789      // kill conditions
50790      if (nodeId === undefined) {
50791        throw new Error("No nodeId supplied to clusterByConnection!");
50792      }
50793      if (this.body.nodes[nodeId] === undefined) {
50794        throw new Error("The nodeId given to clusterByConnection does not exist!");
50795      }
50796
50797      var node = this.body.nodes[nodeId];
50798      options = this._checkOptions(options, node);
50799      if (options.clusterNodeProperties.x === undefined) {
50800        options.clusterNodeProperties.x = node.x;
50801      }
50802      if (options.clusterNodeProperties.y === undefined) {
50803        options.clusterNodeProperties.y = node.y;
50804      }
50805      if (options.clusterNodeProperties.fixed === undefined) {
50806        options.clusterNodeProperties.fixed = {};
50807        options.clusterNodeProperties.fixed.x = node.options.fixed.x;
50808        options.clusterNodeProperties.fixed.y = node.options.fixed.y;
50809      }
50810
50811      var childNodesObj = {};
50812      var childEdgesObj = {};
50813      var parentNodeId = node.id;
50814      var parentClonedOptions = NetworkUtil.cloneOptions(node);
50815      childNodesObj[parentNodeId] = node;
50816
50817      // collect the nodes that will be in the cluster
50818      for (var i = 0; i < node.edges.length; i++) {
50819        var edge = node.edges[i];
50820        if (this.clusteredEdges[edge.id] === undefined) {
50821          var childNodeId = this._getConnectedId(edge, parentNodeId);
50822
50823          // if the child node is not in a cluster
50824          if (this.clusteredNodes[childNodeId] === undefined) {
50825            if (childNodeId !== parentNodeId) {
50826              if (options.joinCondition === undefined) {
50827                childEdgesObj[edge.id] = edge;
50828                childNodesObj[childNodeId] = this.body.nodes[childNodeId];
50829              } else {
50830                // clone the options and insert some additional parameters that could be interesting.
50831                var childClonedOptions = NetworkUtil.cloneOptions(this.body.nodes[childNodeId]);
50832                if (options.joinCondition(parentClonedOptions, childClonedOptions) === true) {
50833                  childEdgesObj[edge.id] = edge;
50834                  childNodesObj[childNodeId] = this.body.nodes[childNodeId];
50835                }
50836              }
50837            } else {
50838              // swallow the edge if it is self-referencing.
50839              childEdgesObj[edge.id] = edge;
50840            }
50841          }
50842        }
50843      }
50844      var childNodeIDs = (0, _keys2['default'])(childNodesObj).map(function (childNode) {
50845        return childNodesObj[childNode].id;
50846      });
50847
50848      for (childNode in childNodesObj) {
50849        if (!childNodesObj.hasOwnProperty(childNode)) continue;
50850
50851        var childNode = childNodesObj[childNode];
50852        for (var y = 0; y < childNode.edges.length; y++) {
50853          var childEdge = childNode.edges[y];
50854          if (childNodeIDs.indexOf(this._getConnectedId(childEdge, childNode.id)) > -1) {
50855            childEdgesObj[childEdge.id] = childEdge;
50856          }
50857        }
50858      }
50859      this._cluster(childNodesObj, childEdgesObj, options, refreshData);
50860    }
50861
50862    /**
50863    * This function creates the edges that will be attached to the cluster
50864    * It looks for edges that are connected to the nodes from the "outside' of the cluster.
50865    *
50866    * @param {{Node.id: vis.Node}} childNodesObj
50867    * @param {{vis.Edge.id: vis.Edge}} childEdgesObj
50868    * @param {Object} clusterNodeProperties
50869    * @param {Object} clusterEdgeProperties
50870    * @private
50871    */
50872
50873  }, {
50874    key: '_createClusterEdges',
50875    value: function _createClusterEdges(childNodesObj, childEdgesObj, clusterNodeProperties, clusterEdgeProperties) {
50876      var edge = void 0,
50877          childNodeId = void 0,
50878          childNode = void 0,
50879          toId = void 0,
50880          fromId = void 0,
50881          otherNodeId = void 0;
50882
50883      // loop over all child nodes and their edges to find edges going out of the cluster
50884      // these edges will be replaced by clusterEdges.
50885      var childKeys = (0, _keys2['default'])(childNodesObj);
50886      var createEdges = [];
50887      for (var i = 0; i < childKeys.length; i++) {
50888        childNodeId = childKeys[i];
50889        childNode = childNodesObj[childNodeId];
50890
50891        // construct new edges from the cluster to others
50892        for (var j = 0; j < childNode.edges.length; j++) {
50893          edge = childNode.edges[j];
50894          // we only handle edges that are visible to the system, not the disabled ones from the clustering process.
50895          if (this.clusteredEdges[edge.id] === undefined) {
50896            // self-referencing edges will be added to the "hidden" list
50897            if (edge.toId == edge.fromId) {
50898              childEdgesObj[edge.id] = edge;
50899            } else {
50900              // set up the from and to.
50901              if (edge.toId == childNodeId) {
50902                // this is a double equals because ints and strings can be interchanged here.
50903                toId = clusterNodeProperties.id;
50904                fromId = edge.fromId;
50905                otherNodeId = fromId;
50906              } else {
50907                toId = edge.toId;
50908                fromId = clusterNodeProperties.id;
50909                otherNodeId = toId;
50910              }
50911            }
50912
50913            // Only edges from the cluster outwards are being replaced.
50914            if (childNodesObj[otherNodeId] === undefined) {
50915              createEdges.push({ edge: edge, fromId: fromId, toId: toId });
50916            }
50917          }
50918        }
50919      }
50920
50921      //
50922      // Here we actually create the replacement edges.
50923      //
50924      // We could not do this in the loop above as the creation process
50925      // would add an edge to the edges array we are iterating over.
50926      //
50927      // NOTE: a clustered edge can have multiple base edges!
50928      //
50929      var newEdges = [];
50930
50931      /**
50932       * Find a cluster edge which matches the given created edge.
50933       * @param {vis.Edge} createdEdge
50934       * @returns {vis.Edge}
50935       */
50936      var getNewEdge = function getNewEdge(createdEdge) {
50937        for (var _j2 = 0; _j2 < newEdges.length; _j2++) {
50938          var newEdge = newEdges[_j2];
50939
50940          // We replace both to and from edges with a single cluster edge
50941          var matchToDirection = createdEdge.fromId === newEdge.fromId && createdEdge.toId === newEdge.toId;
50942          var matchFromDirection = createdEdge.fromId === newEdge.toId && createdEdge.toId === newEdge.fromId;
50943
50944          if (matchToDirection || matchFromDirection) {
50945            return newEdge;
50946          }
50947        }
50948
50949        return null;
50950      };
50951
50952      for (var _j3 = 0; _j3 < createEdges.length; _j3++) {
50953        var createdEdge = createEdges[_j3];
50954        var _edge = createdEdge.edge;
50955        var newEdge = getNewEdge(createdEdge);
50956
50957        if (newEdge === null) {
50958          // Create a clustered edge for this connection
50959          newEdge = this._createClusteredEdge(createdEdge.fromId, createdEdge.toId, _edge, clusterEdgeProperties);
50960
50961          newEdges.push(newEdge);
50962        } else {
50963          newEdge.clusteringEdgeReplacingIds.push(_edge.id);
50964        }
50965
50966        // also reference the new edge in the old edge
50967        this.body.edges[_edge.id].edgeReplacedById = newEdge.id;
50968
50969        // hide the replaced edge
50970        this._backupEdgeOptions(_edge);
50971        _edge.setOptions({ physics: false });
50972      }
50973    }
50974
50975    /**
50976    * This function checks the options that can be supplied to the different cluster functions
50977    * for certain fields and inserts defaults if needed
50978    * @param {Object} options
50979    * @returns {*}
50980    * @private
50981    */
50982
50983  }, {
50984    key: '_checkOptions',
50985    value: function _checkOptions() {
50986      var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
50987
50988      if (options.clusterEdgeProperties === undefined) {
50989        options.clusterEdgeProperties = {};
50990      }
50991      if (options.clusterNodeProperties === undefined) {
50992        options.clusterNodeProperties = {};
50993      }
50994
50995      return options;
50996    }
50997
50998    /**
50999    *
51000    * @param {Object}    childNodesObj         | object with node objects, id as keys, same as childNodes except it also contains a source node
51001    * @param {Object}    childEdgesObj         | object with edge objects, id as keys
51002    * @param {Array}     options               | object with {clusterNodeProperties, clusterEdgeProperties, processProperties}
51003    * @param {boolean}   refreshData | when true, do not wrap up
51004    * @private
51005    */
51006
51007  }, {
51008    key: '_cluster',
51009    value: function _cluster(childNodesObj, childEdgesObj, options) {
51010      var refreshData = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true;
51011
51012      // Remove nodes which are already clustered
51013      var tmpNodesToRemove = [];
51014      for (var _nodeId in childNodesObj) {
51015        if (childNodesObj.hasOwnProperty(_nodeId)) {
51016          if (this.clusteredNodes[_nodeId] !== undefined) {
51017            tmpNodesToRemove.push(_nodeId);
51018          }
51019        }
51020      }
51021
51022      for (var n = 0; n < tmpNodesToRemove.length; ++n) {
51023        delete childNodesObj[tmpNodesToRemove[n]];
51024      }
51025
51026      // kill condition: no nodes don't bother
51027      if ((0, _keys2['default'])(childNodesObj).length == 0) {
51028        return;
51029      }
51030
51031      // allow clusters of 1 if options allow
51032      if ((0, _keys2['default'])(childNodesObj).length == 1 && options.clusterNodeProperties.allowSingleNodeCluster != true) {
51033        return;
51034      }
51035
51036      var clusterNodeProperties = util.deepExtend({}, options.clusterNodeProperties);
51037
51038      // construct the clusterNodeProperties
51039      if (options.processProperties !== undefined) {
51040        // get the childNode options
51041        var childNodesOptions = [];
51042        for (var _nodeId2 in childNodesObj) {
51043          if (childNodesObj.hasOwnProperty(_nodeId2)) {
51044            var clonedOptions = NetworkUtil.cloneOptions(childNodesObj[_nodeId2]);
51045            childNodesOptions.push(clonedOptions);
51046          }
51047        }
51048
51049        // get cluster properties based on childNodes
51050        var childEdgesOptions = [];
51051        for (var edgeId in childEdgesObj) {
51052          if (childEdgesObj.hasOwnProperty(edgeId)) {
51053            // these cluster edges will be removed on creation of the cluster.
51054            if (edgeId.substr(0, 12) !== "clusterEdge:") {
51055              var _clonedOptions = NetworkUtil.cloneOptions(childEdgesObj[edgeId], 'edge');
51056              childEdgesOptions.push(_clonedOptions);
51057            }
51058          }
51059        }
51060
51061        clusterNodeProperties = options.processProperties(clusterNodeProperties, childNodesOptions, childEdgesOptions);
51062        if (!clusterNodeProperties) {
51063          throw new Error("The processProperties function does not return properties!");
51064        }
51065      }
51066
51067      // check if we have an unique id;
51068      if (clusterNodeProperties.id === undefined) {
51069        clusterNodeProperties.id = 'cluster:' + util.randomUUID();
51070      }
51071      var clusterId = clusterNodeProperties.id;
51072
51073      if (clusterNodeProperties.label === undefined) {
51074        clusterNodeProperties.label = 'cluster';
51075      }
51076
51077      // give the clusterNode a position if it does not have one.
51078      var pos = undefined;
51079      if (clusterNodeProperties.x === undefined) {
51080        pos = this._getClusterPosition(childNodesObj);
51081        clusterNodeProperties.x = pos.x;
51082      }
51083      if (clusterNodeProperties.y === undefined) {
51084        if (pos === undefined) {
51085          pos = this._getClusterPosition(childNodesObj);
51086        }
51087        clusterNodeProperties.y = pos.y;
51088      }
51089
51090      // force the ID to remain the same
51091      clusterNodeProperties.id = clusterId;
51092
51093      // create the cluster Node
51094      // Note that allowSingleNodeCluster, if present, is stored in the options as well
51095      var clusterNode = this.body.functions.createNode(clusterNodeProperties, Cluster);
51096      clusterNode.containedNodes = childNodesObj;
51097      clusterNode.containedEdges = childEdgesObj;
51098      // cache a copy from the cluster edge properties if we have to reconnect others later on
51099      clusterNode.clusterEdgeProperties = options.clusterEdgeProperties;
51100
51101      // finally put the cluster node into global
51102      this.body.nodes[clusterNodeProperties.id] = clusterNode;
51103
51104      this._clusterEdges(childNodesObj, childEdgesObj, clusterNodeProperties, options.clusterEdgeProperties);
51105
51106      // set ID to undefined so no duplicates arise
51107      clusterNodeProperties.id = undefined;
51108
51109      // wrap up
51110      if (refreshData === true) {
51111        this.body.emitter.emit('_dataChanged');
51112      }
51113    }
51114
51115    /**
51116     *
51117     * @param {Edge} edge
51118     * @private
51119     */
51120
51121  }, {
51122    key: '_backupEdgeOptions',
51123    value: function _backupEdgeOptions(edge) {
51124      if (this.clusteredEdges[edge.id] === undefined) {
51125        this.clusteredEdges[edge.id] = { physics: edge.options.physics };
51126      }
51127    }
51128
51129    /**
51130     *
51131     * @param {Edge} edge
51132     * @private
51133     */
51134
51135  }, {
51136    key: '_restoreEdge',
51137    value: function _restoreEdge(edge) {
51138      var originalOptions = this.clusteredEdges[edge.id];
51139      if (originalOptions !== undefined) {
51140        edge.setOptions({ physics: originalOptions.physics });
51141        delete this.clusteredEdges[edge.id];
51142      }
51143    }
51144
51145    /**
51146    * Check if a node is a cluster.
51147    * @param {Node.id} nodeId
51148    * @returns {*}
51149    */
51150
51151  }, {
51152    key: 'isCluster',
51153    value: function isCluster(nodeId) {
51154      if (this.body.nodes[nodeId] !== undefined) {
51155        return this.body.nodes[nodeId].isCluster === true;
51156      } else {
51157        console.log("Node does not exist.");
51158        return false;
51159      }
51160    }
51161
51162    /**
51163    * get the position of the cluster node based on what's inside
51164    * @param {object} childNodesObj    | object with node objects, id as keys
51165    * @returns {{x: number, y: number}}
51166    * @private
51167    */
51168
51169  }, {
51170    key: '_getClusterPosition',
51171    value: function _getClusterPosition(childNodesObj) {
51172      var childKeys = (0, _keys2['default'])(childNodesObj);
51173      var minX = childNodesObj[childKeys[0]].x;
51174      var maxX = childNodesObj[childKeys[0]].x;
51175      var minY = childNodesObj[childKeys[0]].y;
51176      var maxY = childNodesObj[childKeys[0]].y;
51177      var node = void 0;
51178      for (var i = 1; i < childKeys.length; i++) {
51179        node = childNodesObj[childKeys[i]];
51180        minX = node.x < minX ? node.x : minX;
51181        maxX = node.x >
51181 maxX ? node.x : maxX;
51182        minY = node.y < minY ? node.y : minY;
51183        maxY = node.y > maxY ? node.y : maxY;
51184      }
51185
51186      return { x: 0.5 * (minX + maxX), y: 0.5 * (minY + maxY) };
51187    }
51188
51189    /**
51190     * Open a cluster by calling this function.
51191     * @param {vis.Edge.id}  clusterNodeId | the ID of the cluster node
51192     * @param {Object} options
51193     * @param {boolean} refreshData | wrap up afterwards if not true
51194     */
51195
51196  }, {
51197    key: 'openCluster',
51198    value: function openCluster(clusterNodeId, options) {
51199      var refreshData = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
51200
51201      // kill conditions
51202      if (clusterNodeId === undefined) {
51203        throw new Error("No clusterNodeId supplied to openCluster.");
51204      }
51205
51206      var clusterNode = this.body.nodes[clusterNodeId];
51207
51208      if (clusterNode === undefined) {
51209        throw new Error("The clusterNodeId supplied to openCluster does not exist.");
51210      }
51211      if (clusterNode.isCluster !== true || clusterNode.containedNodes === undefined || clusterNode.containedEdges === undefined) {
51212        throw new Error("The node:" + clusterNodeId + " is not a valid cluster.");
51213      }
51214
51215      // Check if current cluster is clustered itself
51216      var stack = this.findNode(clusterNodeId);
51217      var parentIndex = stack.indexOf(clusterNodeId) - 1;
51218      if (parentIndex >= 0) {
51219        // Current cluster is clustered; transfer contained nodes and edges to parent
51220        var parentClusterNodeId = stack[parentIndex];
51221        var parentClusterNode = this.body.nodes[parentClusterNodeId];
51222
51223        // clustering.clusteredNodes and clustering.clusteredEdges remain unchanged
51224        parentClusterNode._openChildCluster(clusterNodeId);
51225
51226        // All components of child cluster node have been transferred. It can die now.
51227        delete this.body.nodes[clusterNodeId];
51228        if (refreshData === true) {
51229          this.body.emitter.emit('_dataChanged');
51230        }
51231
51232        return;
51233      }
51234
51235      // main body 
51236      var containedNodes = clusterNode.containedNodes;
51237      var containedEdges = clusterNode.containedEdges;
51238
51239      // allow the user to position the nodes after release.
51240      if (options !== undefined && options.releaseFunction !== undefined && typeof options.releaseFunction === 'function') {
51241        var positions = {};
51242        var clusterPosition = { x: clusterNode.x, y: clusterNode.y };
51243        for (var _nodeId3 in containedNodes) {
51244          if (containedNodes.hasOwnProperty(_nodeId3)) {
51245            var containedNode = this.body.nodes[_nodeId3];
51246            positions[_nodeId3] = { x: containedNode.x, y: containedNode.y };
51247          }
51248        }
51249        var newPositions = options.releaseFunction(clusterPosition, positions);
51250
51251        for (var _nodeId4 in containedNodes) {
51252          if (containedNodes.hasOwnProperty(_nodeId4)) {
51253            var _containedNode = this.body.nodes[_nodeId4];
51254            if (newPositions[_nodeId4] !== undefined) {
51255              _containedNode.x = newPositions[_nodeId4].x === undefined ? clusterNode.x : newPositions[_nodeId4].x;
51256              _containedNode.y = newPositions[_nodeId4].y === undefined ? clusterNode.y : newPositions[_nodeId4].y;
51257            }
51258          }
51259        }
51260      } else {
51261        // copy the position from the cluster
51262        util.forEach(containedNodes, function (containedNode) {
51263          // inherit position
51264          if (containedNode.options.fixed.x === false) {
51265            containedNode.x = clusterNode.x;
51266          }
51267          if (containedNode.options.fixed.y === false) {
51268            containedNode.y = clusterNode.y;
51269          }
51270        });
51271      }
51272
51273      // release nodes
51274      for (var _nodeId5 in containedNodes) {
51275        if (containedNodes.hasOwnProperty(_nodeId5)) {
51276          var _containedNode2 = this.body.nodes[_nodeId5];
51277
51278          // inherit speed
51279          _containedNode2.vx = clusterNode.vx;
51280          _containedNode2.vy = clusterNode.vy;
51281
51282          _containedNode2.setOptions({ physics: true });
51283
51284          delete this.clusteredNodes[_nodeId5];
51285        }
51286      }
51287
51288      // copy the clusterNode edges because we cannot iterate over an object that we add or remove from.
51289      var edgesToBeDeleted = [];
51290      for (var i = 0; i < clusterNode.edges.length; i++) {
51291        edgesToBeDeleted.push(clusterNode.edges[i]);
51292      }
51293
51294      // actually handling the deleting.
51295      for (var _i2 = 0; _i2 < edgesToBeDeleted.length; _i2++) {
51296        var edge = edgesToBeDeleted[_i2];
51297        var otherNodeId = this._getConnectedId(edge, clusterNodeId);
51298        var otherNode = this.clusteredNodes[otherNodeId];
51299
51300        for (var j = 0; j < edge.clusteringEdgeReplacingIds.length; j++) {
51301          var transferId = edge.clusteringEdgeReplacingIds[j];
51302          var transferEdge = this.body.edges[transferId];
51303          if (transferEdge === undefined) continue;
51304
51305          // if the other node is in another cluster, we transfer ownership of this edge to the other cluster
51306          if (otherNode !== undefined) {
51307            // transfer ownership:
51308            var otherCluster = this.body.nodes[otherNode.clusterId];
51309            otherCluster.containedEdges[transferEdge.id] = transferEdge;
51310
51311            // delete local reference
51312            delete containedEdges[transferEdge.id];
51313
51314            // get to and from
51315            var fromId = transferEdge.fromId;
51316            var toId = transferEdge.toId;
51317            if (transferEdge.toId == otherNodeId) {
51318              toId = otherNode.clusterId;
51319            } else {
51320              fromId = otherNode.clusterId;
51321            }
51322
51323            // create new cluster edge from the otherCluster
51324            this._createClusteredEdge(fromId, toId, transferEdge, otherCluster.clusterEdgeProperties, { hidden: false, physics: true });
51325          } else {
51326            this._restoreEdge(transferEdge);
51327          }
51328        }
51329
51330        edge.remove();
51331      }
51332
51333      // handle the releasing of the edges
51334      for (var edgeId in containedEdges) {
51335        if (containedEdges.hasOwnProperty(edgeId)) {
51336          this._restoreEdge(containedEdges[edgeId]);
51337        }
51338      }
51339
51340      // remove clusterNode
51341      delete this.body.nodes[clusterNodeId];
51342
51343      if (refreshData === true) {
51344        this.body.emitter.emit('_dataChanged');
51345      }
51346    }
51347
51348    /**
51349     *
51350     * @param {Cluster.id} clusterId
51351     * @returns {Array.<Node.id>}
51352     */
51353
51354  }, {
51355    key: 'getNodesInCluster',
51356    value: function getNodesInCluster(clusterId) {
51357      var nodesArray = [];
51358      if (this.isCluster(clusterId) === true) {
51359        var containedNodes = this.body.nodes[clusterId].containedNodes;
51360        for (var _nodeId6 in containedNodes) {
51361          if (containedNodes.hasOwnProperty(_nodeId6)) {
51362            nodesArray.push(this.body.nodes[_nodeId6].id);
51363          }
51364        }
51365      }
51366
51367      return nodesArray;
51368    }
51369
51370    /**
51371    * Get the stack clusterId's that a certain node resides in. cluster A -> cluster B -> cluster C -> node
51372    *
51373    * If a node can't be found in the chain, return an empty array.
51374    *
51375    * @param {string|number} nodeId
51376    * @returns {Array}
51377    */
51378
51379  }, {
51380    key: 'findNode',
51381    value: function findNode(nodeId) {
51382      var stack = [];
51383      var max = 100;
51384      var counter = 0;
51385      var node = void 0;
51386
51387      while (this.clusteredNodes[nodeId] !== undefined && counter < max) {
51388        node = this.body.nodes[nodeId];
51389        if (node === undefined) return [];
51390        stack.push(node.id);
51391
51392        nodeId = this.clusteredNodes[nodeId].clusterId;
51393        counter++;
51394      }
51395
51396      node = this.body.nodes[nodeId];
51397      if (node === undefined) return [];
51398      stack.push(node.id);
51399
51400      stack.reverse();
51401      return stack;
51402    }
51403
51404    /**
51405    * Using a clustered nodeId, update with the new options
51406    * @param {vis.Edge.id} clusteredNodeId
51407    * @param {object} newOptions
51408    */
51409
51410  }, {
51411    key: 'updateClusteredNode',
51412    value: function updateClusteredNode(clusteredNodeId, newOptions) {
51413      if (clusteredNodeId === undefined) {
51414        throw new Error("No clusteredNodeId supplied to updateClusteredNode.");
51415      }
51416      if (newOptions === undefined) {
51417        throw new Error("No newOptions supplied to updateClusteredNode.");
51418      }
51419      if (this.body.nodes[clusteredNodeId] === undefined) {
51420        throw new Error("The clusteredNodeId supplied to updateClusteredNode does not exist.");
51421      }
51422
51423      this.body.nodes[clusteredNodeId].setOptions(newOptions);
51424      this.body.emitter.emit('_dataChanged');
51425    }
51426
51427    /**
51428    * Using a base edgeId, update all related clustered edges with the new options
51429    * @param {vis.Edge.id} startEdgeId
51430    * @param {object} newOptions
51431    */
51432
51433  }, {
51434    key: 'updateEdge',
51435    value: function updateEdge(startEdgeId, newOptions) {
51436      if (startEdgeId === undefined) {
51437        throw new Error("No startEdgeId supplied to updateEdge.");
51438      }
51439      if (newOptions === undefined) {
51440        throw new Error("No newOptions supplied to updateEdge.");
51441      }
51442      if (this.body.edges[startEdgeId] === undefined) {
51443        throw new Error("The startEdgeId supplied to updateEdge does not exist.");
51444      }
51445
51446      var allEdgeIds = this.getClusteredEdges(startEdgeId);
51447      for (var i = 0; i < allEdgeIds.length; i++) {
51448        var edge = this.body.edges[allEdgeIds[i]];
51449        edge.setOptions(newOptions);
51450      }
51451      this.body.emitter.emit('_dataChanged');
51452    }
51453
51454    /**
51455    * Get a stack of clusterEdgeId's (+base edgeid) that a base edge is the same as. cluster edge C -> cluster edge B -> cluster edge A -> base edge(edgeId)
51456    * @param {vis.Edge.id} edgeId
51457    * @returns {Array.<vis.Edge.id>}
51458    */
51459
51460  }, {
51461    key: 'getClusteredEdges',
51462    value: function getClusteredEdges(edgeId) {
51463      var stack = [];
51464      var max = 100;
51465      var counter = 0;
51466
51467      while (edgeId !== undefined && this.body.edges[edgeId] !== undefined && counter < max) {
51468        stack.push(this.body.edges[edgeId].id);
51469        edgeId = this.body.edges[edgeId].edgeReplacedById;
51470        counter++;
51471      }
51472      stack.reverse();
51473      return stack;
51474    }
51475
51476    /**
51477    * Get the base edge id of clusterEdgeId. cluster edge (clusteredEdgeId) -> cluster edge B -> cluster edge C -> base edge
51478    * @param {vis.Edge.id} clusteredEdgeId
51479    * @returns {vis.Edge.id} baseEdgeId
51480    *
51481    * TODO: deprecate in 5.0.0. Method getBaseEdges() is the correct one to use.
51482    */
51483
51484  }, {
51485    key: 'getBaseEdge',
51486    value: function getBaseEdge(clusteredEdgeId) {
51487      // Just kludge this by returning the first base edge id found
51488      return this.getBaseEdges(clusteredEdgeId)[0];
51489    }
51490
51491    /**
51492     * Get all regular edges for this clustered edge id.
51493     *
51494     * @param {vis.Edge.id} clusteredEdgeId
51495     * @returns {Array.<vis.Edge.id>} all baseEdgeId's under this clustered edge
51496     */
51497
51498  }, {
51499    key: 'getBaseEdges',
51500    value: function getBaseEdges(clusteredEdgeId) {
51501      var IdsToHandle = [clusteredEdgeId];
51502      var doneIds = [];
51503      var foundIds = [];
51504      var max = 100;
51505      var counter = 0;
51506
51507      while (IdsToHandle.length > 0 && counter < max) {
51508        var nextId = IdsToHandle.pop();
51509        if (nextId === undefined) continue; // Paranoia here and onwards
51510        var nextEdge = this.body.edges[nextId];
51511        if (nextEdge === undefined) continue;
51512        counter++;
51513
51514        var replacingIds = nextEdge.clusteringEdgeReplacingIds;
51515        if (replacingIds === undefined) {
51516          // nextId is a base id
51517          foundIds.push(nextId);
51518        } else {
51519          // Another cluster edge, unravel this one as well
51520          for (var i = 0; i < replacingIds.length; ++i) {
51521            var replacingId = replacingIds[i];
51522
51523            // Don't add if already handled
51524            // TODO: never triggers; find a test-case which does
51525            if (IdsToHandle.indexOf(replacingIds) !== -1 || doneIds.indexOf(replacingIds) !== -1) {
51526              continue;
51527            }
51528
51529            IdsToHandle.push(replacingId);
51530          }
51531        }
51532
51533        doneIds.push(nextId);
51534      }
51535
51536      return foundIds;
51537    }
51538
51539    /**
51540    * Get the Id the node is connected to
51541    * @param {vis.Edge} edge
51542    * @param {Node.id} nodeId
51543    * @returns {*}
51544    * @private
51545    */
51546
51547  }, {
51548    key: '_getConnectedId',
51549    value: function _getConnectedId(edge, nodeId) {
51550      if (edge.toId != nodeId) {
51551        return edge.toId;
51552      } else if (edge.fromId != nodeId) {
51553        return edge.fromId;
51554      } else {
51555        return edge.fromId;
51556      }
51557    }
51558
51559    /**
51560    * We determine how many connections denote an important hub.
51561    * We take the mean + 2*std as the important hub size. (Assuming a normal distribution of data, ~2.2%)
51562    *
51563    * @returns {number}
51564    * @private
51565    */
51566
51567  }, {
51568    key: '_getHubSize',
51569    value: function _getHubSize() {
51570      var average = 0;
51571      var averageSquared = 0;
51572      var hubCounter = 0;
51573      var largestHub = 0;
51574
51575      for (var i = 0; i < this.body.nodeIndices.length; i++) {
51576        var _node = this.body.nodes[this.body.nodeIndices[i]];
51577        if (_node.edges.length > largestHub) {
51578          largestHub = _node.edges.length;
51579        }
51580        average += _node.edges.length;
51581        averageSquared += Math.pow(_node.edges.length, 2);
51582        hubCounter += 1;
51583      }
51584      average = average / hubCounter;
51585      averageSquared = averageSquared / hubCounter;
51586
51587      var variance = averageSquared - Math.pow(average, 2);
51588      var standardDeviation = Math.sqrt(variance);
51589
51590      var hubThreshold = Math.floor(average + 2 * standardDeviation);
51591
51592      // always have at least one to cluster
51593      if (hubThreshold > largestHub) {
51594        hubThreshold = largestHub;
51595      }
51596
51597      return hubThreshold;
51598    }
51599
51600    /**
51601     * Create an edge for the cluster representation.
51602     *
51603     * @param {Node.id} fromId
51604     * @param {Node.id} toId
51605     * @param {vis.Edge} baseEdge
51606     * @param {Object} clusterEdgeProperties
51607     * @param {Object} extraOptions
51608     * @returns {Edge} newly created clustered edge
51609     * @private
51610     */
51611
51612  }, {
51613    key: '_createClusteredEdge',
51614    value: function _createClusteredEdge(fromId, toId, baseEdge, clusterEdgeProperties, extraOptions) {
51615      // copy the options of the edge we will replace
51616      var clonedOptions = NetworkUtil.cloneOptions(baseEdge, 'edge');
51617      // make sure the properties of clusterEdges are superimposed on it
51618      util.deepExtend(clonedOptions, clusterEdgeProperties);
51619
51620      // set up the edge
51621      clonedOptions.from = fromId;
51622      clonedOptions.to = toId;
51623      clonedOptions.id = 'clusterEdge:' + util.randomUUID();
51624
51625      // apply the edge specific options to it if specified
51626      if (extraOptions !== undefined) {
51627        util.deepExtend(clonedOptions, extraOptions);
51628      }
51629
51630      var newEdge = this.body.functions.createEdge(clonedOptions);
51631      newEdge.clusteringEdgeReplacingIds = [baseEdge.id];
51632      newEdge.connect();
51633
51634      // Register the new edge
51635      this.body.edges[newEdge.id] = newEdge;
51636
51637      return newEdge;
51638    }
51639
51640    /**
51641     * Add the passed child nodes and edges to the given cluster node.
51642     *
51643     * @param {Object|Node} childNodes  hash of nodes or single node to add in cluster
51644     * @param {Object|Edge} childEdges  hash of edges or single edge to take into account when clustering
51645     * @param {Node} clusterNode  cluster node to add nodes and edges to
51646     * @param {Object} [clusterEdgeProperties]
51647     * @private
51648     */
51649
51650  }, {
51651    key: '_clusterEdges',
51652    value: function _clusterEdges(childNodes, childEdges, clusterNode, clusterEdgeProperties) {
51653      if (childEdges instanceof Edge) {
51654        var edge = childEdges;
51655        var obj = {};
51656        obj[edge.id] = edge;
51657        childEdges = obj;
51658      }
51659
51660      if (childNodes instanceof Node) {
51661        var _node2 = childNodes;
51662        var _obj = {};
51663        _obj[_node2.id] = _node2;
51664        childNodes = _obj;
51665      }
51666
51667      if (clusterNode === undefined || clusterNode === null) {
51668        throw new Error("_clusterEdges: parameter clusterNode required");
51669      }
51670
51671      if (clusterEdgeProperties === undefined) {
51672        // Take the required properties from the cluster node
51673        clusterEdgeProperties = clusterNode.clusterEdgeProperties;
51674      }
51675
51676      // create the new edges that will connect to the cluster.
51677      // All self-referencing edges will be added to childEdges here.
51678      this._createClusterEdges(childNodes, childEdges, clusterNode, clusterEdgeProperties);
51679
51680      // disable the childEdges
51681      for (var edgeId in childEdges) {
51682        if (childEdges.hasOwnProperty(edgeId)) {
51683          if (this.body.edges[edgeId] !== undefined) {
51684            var _edge2 = this.body.edges[edgeId];
51685            // cache the options before changing
51686            this._backupEdgeOptions(_edge2);
51687            // disable physics and hide the edge
51688            _edge2.setOptions({ physics: false });
51689          }
51690        }
51691      }
51692
51693      // disable the childNodes
51694      for (var _nodeId7 in childNodes) {
51695        if (childNodes.hasOwnProperty(_nodeId7)) {
51696          this.clusteredNodes[_nodeId7] = { clusterId: clusterNode.id, node: this.body.nodes[_nodeId7] };
51697          this.body.nodes[_nodeId7].setOptions({ physics: false });
51698        }
51699      }
51700    }
51701
51702    /**
51703     * Determine in which cluster given nodeId resides.
51704     *
51705     * If not in cluster, return undefined.
51706     *
51707     * NOTE: If you know a cleaner way to do this, please enlighten me (wimrijnders).
51708     *
51709     * @param {Node.id} nodeId
51710     * @returns {Node|undefined} Node instance for cluster, if present
51711     * @private
51712     */
51713
51714  }, {
51715    key: '_getClusterNodeForNode',
51716    value: function _getClusterNodeForNode(nodeId) {
51717      if (nodeId === undefined) return undefined;
51718      var clusteredNode = this.clusteredNodes[nodeId];
51719
51720      // NOTE: If no cluster info found, it should actually be an error
51721      if (clusteredNode === undefined) return undefined;
51722      var clusterId = clusteredNode.clusterId;
51723      if (clusterId === undefined) return undefined;
51724
51725      return this.body.nodes[clusterId];
51726    }
51727
51728    /**
51729     * Internal helper function for conditionally removing items in array
51730     *
51731     * Done like this because Array.filter() is not fully supported by all IE's.
51732     *
51733     * @param {Array} arr
51734     * @param {function} callback
51735     * @returns {Array}
51736     * @private
51737     */
51738
51739  }, {
51740    key: '_filter',
51741    value: function _filter(arr, callback) {
51742      var ret = [];
51743
51744      util.forEach(arr, function (item) {
51745        if (callback(item)) {
51746          ret.push(item);
51747        }
51748      });
51749
51750      return ret;
51751    }
51752
51753    /**
51754     * Scan all edges for changes in clustering and adjust this if necessary.
51755     *
51756     * Call this (internally) after there has been a change in node or edge data.
51757     *
51758     * Pre: States of this.body.nodes and this.body.edges consistent
51759     * Pre: this.clusteredNodes and this.clusteredEdge consistent with containedNodes and containedEdges
51760     *      of cluster nodes.
51761     */
51762
51763  }, {
51764    key: '_updateState',
51765    value: function _updateState() {
51766      var _this4 = this;
51767
51768      var nodeId = void 0;
51769      var deletedNodeIds = [];
51770      var deletedEdgeIds = [];
51771
51772      /**
51773       * Utility function to iterate over clustering nodes only
51774       *
51775       * @param {Function} callback  function to call for each cluster node
51776       */
51777      var eachClusterNode = function eachClusterNode(callback) {
51778        util.forEach(_this4.body.nodes, function (node) {
51779          if (node.isCluster === true) {
51780            callback(node);
51781          }
51782        });
51783      };
51784
51785      //
51786      // Remove deleted regular nodes from clustering
51787      //
51788
51789      // Determine the deleted nodes
51790      for (nodeId in this.clusteredNodes) {
51791        if (!this.clusteredNodes.hasOwnProperty(nodeId)) continue;
51792        var _node3 = this.body.nodes[nodeId];
51793
51794        if (_node3 === undefined) {
51795          deletedNodeIds.push(nodeId);
51796        }
51797      }
51798
51799      // Remove nodes from cluster nodes
51800      eachClusterNode(function (clusterNode) {
51801        for (var n = 0; n < deletedNodeIds.length; n++) {
51802          delete clusterNode.containedNodes[deletedNodeIds[n]];
51803        }
51804      });
51805
51806      // Remove nodes from cluster list
51807      for (var n = 0; n < deletedNodeIds.length; n++) {
51808        delete this.clusteredNodes[deletedNodeIds[n]];
51809      }
51810
51811      //
51812      // Remove deleted edges from clustering
51813      //
51814
51815      // Add the deleted clustered edges to the list
51816      util.forEach(this.clusteredEdges, function (edgeId) {
51817        var edge = _this4.body.edges[edgeId];
51818        if (edge === undefined || !edge.endPointsValid()) {
51819          deletedEdgeIds.push(edgeId);
51820        }
51821      });
51822
51823      // Cluster nodes can also contain edges which are not clustered, 
51824      // i.e. nodes 1-2 within cluster with an edge in between.
51825      // So the cluster nodes also need to be scanned for invalid edges
51826      eachClusterNode(function (clusterNode) {
51827        util.forEach(clusterNode.containedEdges, function (edge, edgeId) {
51828          if (!edge.endPointsValid() && deletedEdgeIds.indexOf(edgeId) === -1) {
51829            deletedEdgeIds.push(edgeId);
51830          }
51831        });
51832      });
51833
51834      // Also scan for cluster edges which need to be removed in the active list.
51835      // Regular edges have been removed beforehand, so this only picks up the cluster edges.
51836      util.forEach(this.body.edges, function (edge, edgeId) {
51837        // Explicitly scan the contained edges for validity
51838        var isValid = true;
51839        var replacedIds = edge.clusteringEdgeReplacingIds;
51840        if (replacedIds !== undefined) {
51841          var numValid = 0;
51842
51843          util.forEach(replacedIds, function (containedEdgeId) {
51844            var containedEdge = _this4.body.edges[containedEdgeId];
51845
51846            if (containedEdge !== undefined && containedEdge.endPointsValid()) {
51847              numValid += 1;
51848            }
51849          });
51850
51851          isValid = numValid > 0;
51852        }
51853
51854        if (!edge.endPointsValid() || !isValid) {
51855          deletedEdgeIds.push(edgeId);
51856        }
51857      });
51858
51859      // Remove edges from cluster nodes
51860      eachClusterNode(function (clusterNode) {
51861        util.forEach(deletedEdgeIds, function (deletedEdgeId) {
51862          delete clusterNode.containedEdges[deletedEdgeId];
51863
51864          util.forEach(clusterNode.edges, function (edge, m) {
51865            if (edge.id === deletedEdgeId) {
51866              clusterNode.edges[m] = null; // Don't want to directly delete here, because in the loop
51867              return;
51868            }
51869
51870            edge.clusteringEdgeReplacingIds = _this4._filter(edge.clusteringEdgeReplacingIds, function (id) {
51871              return deletedEdgeIds.indexOf(id) === -1;
51872            });
51873          });
51874
51875          // Clean up the nulls
51876          clusterNode.edges = _this4._filter(clusterNode.edges, function (item) {
51877            return item !== null;
51878          });
51879        });
51880      });
51881
51882      // Remove from cluster list
51883      util.forEach(deletedEdgeIds, function (edgeId) {
51884        delete _this4.clusteredEdges[edgeId];
51885      });
51886
51887      // Remove cluster edges from active list (this.body.edges).
51888      // deletedEdgeIds still contains id of regular edges, but these should all
51889      // be gone when you reach here.
51890      util.forEach(deletedEdgeIds, function (edgeId) {
51891        delete _this4.body.edges[edgeId];
51892      });
51893
51894      //
51895      // Check changed cluster state of edges
51896      //
51897
51898      // Iterating over keys here, because edges may be removed in the loop
51899      var ids = (0, _keys2['default'])(this.body.edges);
51900      util.forEach(ids, function (edgeId) {
51901        var edge = _this4.body.edges[edgeId];
51902
51903        var shouldBeClustered = _this4._isClusteredNode(edge.fromId) || _this4._isClusteredNode(edge.toId);
51904        if (shouldBeClustered === _this4._isClusteredEdge(edge.id)) {
51905          return;
51905 // all is well
51906        }
51907
51908        if (shouldBeClustered) {
51909          // add edge to clustering
51910          var clusterFrom = _this4._getClusterNodeForNode(edge.fromId);
51911          if (clusterFrom !== undefined) {
51912            _this4._clusterEdges(_this4.body.nodes[edge.fromId], edge, clusterFrom);
51913          }
51914
51915          var clusterTo = _this4._getClusterNodeForNode(edge.toId);
51916          if (clusterTo !== undefined) {
51917            _this4._clusterEdges(_this4.body.nodes[edge.toId], edge, clusterTo);
51918          }
51919
51920          // TODO: check that it works for both edges clustered
51921          //       (This might be paranoia)
51922        } else {
51923          // This should not be happening, the state should
51924          // be properly updated at this point.
51925          // 
51926          // If it *is* reached during normal operation, then we have to implement
51927          // undo clustering for this edge here.
51928          throw new Error('remove edge from clustering not implemented!');
51929        }
51930      });
51931
51932      // Clusters may be nested to any level. Keep on opening until nothing to open
51933      var changed = false;
51934      var continueLoop = true;
51935
51936      var _loop2 = function _loop2() {
51937        var clustersToOpen = [];
51938
51939        // Determine the id's of clusters that need opening
51940        eachClusterNode(function (clusterNode) {
51941          var numNodes = (0, _keys2['default'])(clusterNode.containedNodes).length;
51942          var allowSingle = clusterNode.options.allowSingleNodeCluster === true;
51943          if (allowSingle && numNodes < 1 || !allowSingle && numNodes < 2) {
51944            clustersToOpen.push(clusterNode.id);
51945          }
51946        });
51947
51948        // Open them
51949        for (var _n = 0; _n < clustersToOpen.length; ++_n) {
51950          _this4.openCluster(clustersToOpen[_n], {}, false /* Don't refresh, we're in an refresh/update already */);
51951        }
51952
51953        continueLoop = clustersToOpen.length > 0;
51954        changed = changed || continueLoop;
51955      };
51956
51957      while (continueLoop) {
51958        _loop2();
51959      }
51960
51961      if (changed) {
51962        this._updateState(); // Redo this method (recursion possible! should be safe)
51963      }
51964    }
51965
51966    /**
51967     * Determine if node with given id is part of a cluster.
51968     *
51969     * @param {Node.id} nodeId
51970     * @return {boolean} true if part of a cluster.
51971     */
51972
51973  }, {
51974    key: '_isClusteredNode',
51975    value: function _isClusteredNode(nodeId) {
51976      return this.clusteredNodes[nodeId] !== undefined;
51977    }
51978
51979    /**
51980     * Determine if edge with given id is not visible due to clustering.
51981     *
51982     * An edge is considered clustered if:
51983     * - it is directly replaced by a clustering edge
51984     * - any of its connecting nodes is in a cluster
51985     *
51986     * @param {vis.Edge.id} edgeId
51987     * @return {boolean} true if part of a cluster.
51988     */
51989
51990  }, {
51991    key: '_isClusteredEdge',
51992    value: function _isClusteredEdge(edgeId) {
51993      return this.clusteredEdges[edgeId] !== undefined;
51994    }
51995  }]);
51996  return ClusterEngine;
51997}();
51998
51999exports['default'] = ClusterEngine;
52000
52001/***/ }),
52002/* 228 */
52003/***/ (function(module, exports, __webpack_require__) {
52004
52005"use strict";
52006
52007
52008Object.defineProperty(exports, "__esModule", {
52009  value: true
52010});
52011
52012var _getPrototypeOf = __webpack_require__(3);
52013
52014var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf);
52015
52016var _classCallCheck2 = __webpack_require__(0);
52017
52018var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
52019
52020var _createClass2 = __webpack_require__(1);
52021
52022var _createClass3 = _interopRequireDefault(_createClass2);
52023
52024var _possibleConstructorReturn2 = __webpack_require__(4);
52025
52026var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2);
52027
52028var _inherits2 = __webpack_require__(5);
52029
52030var _inherits3 = _interopRequireDefault(_inherits2);
52031
52032function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
52033
52034var util = __webpack_require__(2);
52035var Node = __webpack_require__(47)["default"];
52036
52037/**
52038 * A Cluster is a special Node that allows a group of Nodes positioned closely together
52039 * to be represented by a single Cluster Node.
52040 *
52041 * @extends Node
52042 */
52043
52044var Cluster = function (_Node) {
52045  (0, _inherits3["default"])(Cluster, _Node);
52046
52047  /**
52048   * @param {Object} options
52049   * @param {Object} body
52050   * @param {Array.<HTMLImageElement>}imagelist
52051   * @param {Array} grouplist
52052   * @param {Object} globalOptions
52053   * @param {Object} defaultOptions     Global default options for nodes
52054   */
52055  function Cluster(options, body, imagelist, grouplist, globalOptions, defaultOptions) {
52056    (0, _classCallCheck3["default"])(this, Cluster);
52057
52058    var _this = (0, _possibleConstructorReturn3["default"])(this, (Cluster.__proto__ || (0, _getPrototypeOf2["default"])(Cluster)).call(this, options, body, imagelist, grouplist, globalOptions, defaultOptions));
52059
52060    _this.isCluster = true;
52061    _this.containedNodes = {};
52062    _this.containedEdges = {};
52063    return _this;
52064  }
52065
52066  /**
52067   * Transfer child cluster data to current and disconnect the child cluster.
52068   *
52069   * Please consult the header comment in 'Clustering.js' for the fields set here.
52070   *
52071   * @param {string|number} childClusterId  id of child cluster to open
52072   */
52073
52074
52075  (0, _createClass3["default"])(Cluster, [{
52076    key: "_openChildCluster",
52077    value: function _openChildCluster(childClusterId) {
52078      var _this2 = this;
52079
52080      var childCluster = this.body.nodes[childClusterId];
52081      if (this.containedNodes[childClusterId] === undefined) {
52082        throw new Error('node with id: ' + childClusterId + ' not in current cluster');
52083      }
52084      if (!childCluster.isCluster) {
52085        throw new Error('node with id: ' + childClusterId + ' is not a cluster');
52086      }
52087
52088      // Disconnect child cluster from current cluster
52089      delete this.containedNodes[childClusterId];
52090      util.forEach(childCluster.edges, function (edge) {
52091        delete _this2.containedEdges[edge.id];
52092      });
52093
52094      // Transfer nodes and edges
52095      util.forEach(childCluster.containedNodes, function (node, nodeId) {
52096        _this2.containedNodes[nodeId] = node;
52097      });
52098      childCluster.containedNodes = {};
52099
52100      util.forEach(childCluster.containedEdges, function (edge, edgeId) {
52101        _this2.containedEdges[edgeId] = edge;
52102      });
52103      childCluster.containedEdges = {};
52104
52105      // Transfer edges within cluster edges which are clustered
52106      util.forEach(childCluster.edges, function (clusterEdge) {
52107        util.forEach(_this2.edges, function (parentClusterEdge) {
52108          // Assumption: a clustered edge can only be present in a single clustering edge
52109          // Not tested here
52110          var index = parentClusterEdge.clusteringEdgeReplacingIds.indexOf(clusterEdge.id);
52111          if (index === -1) return;
52112
52113          util.forEach(clusterEdge.clusteringEdgeReplacingIds, function (srcId) {
52114            parentClusterEdge.clusteringEdgeReplacingIds.push(srcId);
52115
52116            // Maintain correct bookkeeping for transferred edge
52117            _this2.body.edges[srcId].edgeReplacedById = parentClusterEdge.id;
52118          });
52119
52120          // Remove cluster edge from parent cluster edge
52121          parentClusterEdge.clusteringEdgeReplacingIds.splice(index, 1);
52122        });
52123      });
52124      childCluster.edges = [];
52125    }
52126  }]);
52127  return Cluster;
52128}(Node);
52129
52130exports["default"] = Cluster;
52131
52132/***/ }),
52133/* 229 */
52134/***/ (function(module, exports, __webpack_require__) {
52135
52136"use strict";
52137
52138
52139Object.defineProperty(exports, "__esModule", {
52140  value: true
52141});
52142
52143var _classCallCheck2 = __webpack_require__(0);
52144
52145var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
52146
52147var _createClass2 = __webpack_require__(1);
52148
52149var _createClass3 = _interopRequireDefault(_createClass2);
52150
52151function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
52152
52153/**
52154 * Initializes window.requestAnimationFrame() to a usable form.
52155 *
52156 * Specifically, set up this method for the case of running on node.js with jsdom enabled.
52157 *
52158 * NOTES:
52159 *
52160 * * On node.js, when calling this directly outside of this class, `window` is not defined.
52161 *   This happens even if jsdom is used.
52162 * * For node.js + jsdom, `window` is available at the moment the constructor is called.
52163 *   For this reason, the called is placed within the constructor.
52164 * * Even then, `window.requestAnimationFrame()` is not defined, so it still needs to be added.
52165 * * During unit testing, it happens that the window object is reset during execution, causing
52166 *   a runtime error due to missing `requestAnimationFrame()`. This needs to be compensated for,
52167 *   see `_requestNextFrame()`.
52168 * * Since this is a global object, it may affect other modules besides `Network`. With normal
52169 *   usage, this does not cause any problems. During unit testing, errors may occur. These have
52170 *   been compensated for, see comment block in _requestNextFrame().
52171 *
52172 * @private
52173 */
52174function _initRequestAnimationFrame() {
52175  var func;
52176
52177  if (window !== undefined) {
52178    func = window.requestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame || window.msRequestAnimationFrame;
52179  }
52180
52181  if (func === undefined) {
52182    // window or method not present, setting mock requestAnimationFrame
52183    window.requestAnimationFrame = function (callback) {
52184      //console.log("Called mock requestAnimationFrame");
52185      callback();
52186    };
52187  } else {
52188    window.requestAnimationFrame = func;
52189  }
52190}
52191
52192var util = __webpack_require__(2);
52193
52194/**
52195 * The canvas renderer
52196 */
52197
52198var CanvasRenderer = function () {
52199  /**
52200   * @param {Object} body
52201   * @param {Canvas} canvas
52202   */
52203  function CanvasRenderer(body, canvas) {
52204    (0, _classCallCheck3["default"])(this, CanvasRenderer);
52205
52206    _initRequestAnimationFrame();
52207    this.body = body;
52208    this.canvas = canvas;
52209
52210    this.redrawRequested = false;
52211    this.renderTimer = undefined;
52212    this.requiresTimeout = true;
52213    this.renderingActive = false;
52214    this.renderRequests = 0;
52215    this.allowRedraw = true;
52216
52217    this.dragging = false;
52218    this.options = {};
52219    this.defaultOptions = {
52220      hideEdgesOnDrag: false,
52221      hideNodesOnDrag: false
52222    };
52223    util.extend(this.options, this.defaultOptions);
52224
52225    this._determineBrowserMethod();
52226    this.bindEventListeners();
52227  }
52228
52229  /**
52230   * Binds event listeners
52231   */
52232
52233
52234  (0, _createClass3["default"])(CanvasRenderer, [{
52235    key: "bindEventListeners",
52236    value: function bindEventListeners() {
52237      var _this = this;
52238
52239      this.body.emitter.on("dragStart", function () {
52240        _this.dragging = true;
52241      });
52242      this.body.emitter.on("dragEnd", function () {
52243        _this.dragging = false;
52244      });
52245      this.body.emitter.on("_resizeNodes", function () {
52246        _this._resizeNodes();
52247      });
52248      this.body.emitter.on("_redraw", function () {
52249        if (_this.renderingActive === false) {
52250          _this._redraw();
52251        }
52252      });
52253      this.body.emitter.on("_blockRedraw", function () {
52254        _this.allowRedraw = false;
52255      });
52256      this.body.emitter.on("_allowRedraw", function () {
52257        _this.allowRedraw = true;_this.redrawRequested = false;
52258      });
52259      this.body.emitter.on("_requestRedraw", this._requestRedraw.bind(this));
52260      this.body.emitter.on("_startRendering", function () {
52261        _this.renderRequests += 1;
52262        _this.renderingActive = true;
52263        _this._startRendering();
52264      });
52265      this.body.emitter.on("_stopRendering", function () {
52266        _this.renderRequests -= 1;
52267        _this.renderingActive = _this.renderRequests > 0;
52268        _this.renderTimer = undefined;
52269      });
52270      this.body.emitter.on('destroy', function () {
52271        _this.renderRequests = 0;
52272        _this.allowRedraw = false;
52273        _this.renderingActive = false;
52274        if (_this.requiresTimeout === true) {
52275          clearTimeout(_this.renderTimer);
52276        } else {
52277          window.cancelAnimationFrame(_this.renderTimer);
52278        }
52279        _this.body.emitter.off();
52280      });
52281    }
52282
52283    /**
52284     *
52285     * @param {Object} options
52286     */
52287
52288  }, {
52289    key: "setOptions",
52290    value: function setOptions(options) {
52291      if (options !== undefined) {
52292        var fields = ['hideEdgesOnDrag', 'hideNodesOnDrag'];
52293        util.selectiveDeepExtend(fields, this.options, options);
52294      }
52295    }
52296
52297    /**
52298     * Prepare the drawing of the next frame.
52299     *
52300     * Calls the callback when the next frame can or will be drawn.
52301     *
52302     * @param {function} callback
52303     * @param {number} delay - timeout case only, wait this number of milliseconds
52304     * @returns {function|undefined}
52305     * @private
52306     */
52307
52308  }, {
52309    key: "_requestNextFrame",
52310    value: function _requestNextFrame(callback, delay) {
52311      // During unit testing, it happens that the mock window object is reset while
52312      // the next frame is still pending. Then, either 'window' is not present, or
52313      // 'requestAnimationFrame()' is not present because it is not defined on the
52314      // mock window object.
52315      //
52316      // As a consequence, unrelated unit tests may appear to fail, even if the problem
52317      // described happens in the current unit test.
52318      //
52319      // This is not something that will happen in normal operation, but we still need
52320      // to take it into account.
52321      //
52322      if (typeof window === 'undefined') return; // Doing `if (window === undefined)` does not work here!
52323
52324      var timer = void 0;
52325
52326      var myWindow = window; // Grab a reference to reduce the possibility that 'window' is reset
52327      // while running this method.
52328
52329      if (this.requiresTimeout === true) {
52330        // wait given number of milliseconds and perform the animation step function
52331        timer = myWindow.setTimeout(callback, delay);
52332      } else {
52333        if (myWindow.requestAnimationFrame) {
52334          timer = myWindow.requestAnimationFrame(callback);
52335        }
52336      }
52337
52338      return timer;
52339    }
52340
52341    /**
52342     *
52343     * @private
52344     */
52345
52346  }, {
52347    key: "_startRendering",
52348    value: function _startRendering() {
52349      if (this.renderingActive === true) {
52350        if (this.renderTimer === undefined) {
52351          this.renderTimer = this._requestNextFrame(this._renderStep.bind(this), this.simulationInterval);
52352        }
52353      }
52354    }
52355
52356    /**
52357     *
52358     * @private
52359     */
52360
52361  }, {
52362    key: "_renderStep",
52363    value: function _renderStep() {
52364      if (this.renderingActive === true) {
52365        // reset the renderTimer so a new scheduled animation step can be set
52366        this.renderTimer = undefined;
52367
52368        if (this.requiresTimeout === true) {
52369          // this schedules a new simulation step
52370          this._startRendering();
52371        }
52372
52373        this._redraw();
52374
52375        if (this.requiresTimeout === false) {
52376          // this schedules a new simulation step
52377          this._startRendering();
52378        }
52379      }
52380    }
52381
52382    /**
52383     * Redraw the network with the current data
52384     * chart will be resized too.
52385     */
52386
52387  }, {
52388    key: "redraw",
52389    value: function redraw() {
52390      this.body.emitter.emit('setSize');
52391      this._redraw();
52392    }
52393
52394    /**
52395     * Redraw the network with the current data
52396     * @private
52397     */
52398
52399  }, {
52400    key: "_requestRedraw",
52401    value: function _requestRedraw() {
52402      var _this2 = this;
52403
52404      if (this.redrawRequested !== true && this.renderingActive === false && this.allowRedraw === true) {
52405        this.redrawRequested = true;
52406        this._requestNextFrame(function () {
52407          _this2._redraw(false);
52408        }, 0);
52409      }
52410    }
52411
52412    /**
52413     * Redraw the network with the current data
52414     * @param {boolean} [hidden=false] | Used to get the first estimate of the node sizes.
52415     *                                   Only the nodes are drawn after which they are quickly drawn over.
52416     * @private
52417     */
52418
52419  }, {
52420    key: "_redraw",
52421    value: function _redraw() {
52422      var hidden = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
52423
52424      if (this.allowRedraw === true) {
52425        this.body.emitter.emit("initRedraw");
52426
52427        this.redrawRequested = false;
52428
52429        // when the container div was hidden, this fixes it back up!
52430        if (this.canvas.frame.canvas.width === 0 || this.canvas.frame.canvas.height === 0) {
52431          this.canvas.setSize();
52432        }
52433
52434        this.canvas.setTransform();
52435
52436        var ctx = this.canvas.getContext();
52437
52438        // clear the canvas
52439        var w = this.canvas.frame.canvas.clientWidth;
52440        var h = this.canvas.frame.canvas.clientHeight;
52441        ctx.clearRect(0, 0, w, h);
52442
52443        // if the div is hidden, we stop the redraw here for performance.
52444        if (this.canvas.frame.clientWidth === 0) {
52445          return;
52446        }
52447
52448        // set scaling and translation
52449        ctx.save();
52450        ctx.translate(this.body.view.translation.x, this.body.view.translation.y);
52451        ctx.scale(this.body.view.scale, this.body.view.scale);
52452
52453        ctx.beginPath();
52454        this.body.emitter.emit("beforeDrawing", ctx);
52455        ctx.closePath();
52456
52457        if (hidden === false) {
52458          if (this.dragging === false || this.dragging === true && this.options.hideEdgesOnDrag === false) {
52459            this._drawEdges(ctx);
52460          }
52461        }
52462
52463        if (this.dragging === false || this.dragging === true && this.options.hideNodesOnDrag === false) {
52464          this._drawNodes(ctx, hidden);
52465        }
52466
52467        ctx.beginPath();
52468        this.body.emitter.emit("afterDrawing", ctx);
52469        ctx.closePath();
52470
52471        // restore original scaling and translation
52472        ctx.restore();
52473        if (hidden === true) {
52474          ctx.clearRect(0, 0, w, h);
52475        }
52476      }
52477    }
52478
52479    /**
52480     * Redraw all nodes
52481     *
52482     * @param {CanvasRenderingContext2D}   ctx
52483     * @param {boolean} [alwaysShow]
52484     * @private
52485     */
52486
52487  }, {
52488    key: "_resizeNodes",
52489    value: function _resizeNodes() {
52490      this.canvas.setTransform();
52491      var ctx = this.canvas.getContext();
52492      ctx.save();
52493      ctx.translate(this.body.view.translation.x, this.body.view.translation.y);
52494      ctx.scale(this.body.view.scale, this.body.view.scale);
52495
52496      var nodes = this.body.nodes;
52497      var node = void 0;
52498
52499      // resize all nodes
52500      for (var nodeId in nodes) {
52501        if (nodes.hasOwnProperty(nodeId)) {
52502          node = nodes[nodeId];
52503          node.resize(ctx);
52504          node.updateBoundingBox(ctx, node.selected);
52505        }
52506      }
52507
52508      // restore original scaling and translation
52509      ctx.restore();
52510    }
52511
52512    /**
52513     * Redraw all nodes
52514     *
52515     * @param {CanvasRenderingContext2D} ctx  2D context of a HTML canvas
52516     * @param {boolean} [alwaysShow]
52517     * @private
52518     */
52519
52520  }, {
52521    key: "_drawNodes",
52522    value: function _drawNodes(ctx) {
52523      var alwaysShow = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
52524
52525      var nodes = this.body.nodes;
52526      var nodeIndices = this.body.nodeIndices;
52527      var node = void 0;
52528      var selected = [];
52529      var margin = 20;
52530      var topLeft = this.canvas.DOMtoCanvas({ x: -margin, y: -margin });
52531      var bottomRight = this.canvas.DOMtoCanvas({
52532        x: this.canvas.frame.canvas.clientWidth + margin,
52533        y: this.canvas.frame.canvas.clientHeight + margin
52534      });
52535      var viewableArea = { top: topLeft.y, left: topLeft.x, bottom: bottomRight.y, right: bottomRight.x };
52536
52537      // draw unselected nodes;
52538      for (var i = 0; i < nodeIndices.length; i++) {
52539        node = nodes[nodeIndices[i]];
52540        // set selected nodes aside
52541        if (node.isSelected()) {
52542          selected.push(nodeIndices[i]);
52543        } else {
52544          if (alwaysShow === true) {
52545            node.draw(ctx);
52546          } else if (node.isBoundingBoxOverlappingWith(viewableArea) === true) {
52547            node.draw(ctx);
52548          } else {
52549            node.updateBoundingBox(ctx, node.selected);
52550          }
52551        }
52552      }
52553
52554      // draw the selected nodes on top
52555      for (var _i = 0; _i < selected.length; _i++) {
52556        node = nodes[selected[_i]];
52557        node.draw(ctx);
52558      }
52559    }
52560
52561    /**
52562     * Redraw all edges
52563     * @param {CanvasRenderingContext2D} ctx  2D context of a HTML canvas
52564     * @private
52565     */
52566
52567  }, {
52568    key: "_drawEdges",
52569    value: function _drawEdges(ctx) {
52570      var edges = this.body.edges;
52571      var edgeIndices = this.body.edgeIndices;
52572      var edge = void 0;
52573
52574      for (var i = 0; i < edgeIndices.length; i++) {
52575        edge = edges[edgeIndices[i]];
52576        if (edge.connected === true) {
52577          edge.draw(ctx);
52578        }
52579      }
52580    }
52581
52582    /**
52583     * Determine if the browser requires a setTimeout or a requestAnimationFrame. This was required because
52584     * some implementations (safari and IE9) did not support requestAnimationFrame
52585     * @private
52586     */
52587
52588  }, {
52589    key: "_determineBrowserMethod",
52590    value: function _determineBrowserMethod() {
52591      if (typeof window !== 'undefined') {
52592        var browserType = navigator.userAgent.toLowerCase();
52593        this.requiresTimeout = false;
52594        if (browserType.indexOf('msie 9.0') != -1) {
52595          // IE 9
52596          this.requiresTimeout = true;
52597        } else if (browserType.indexOf('safari') != -1) {
52598          // safari
52599          if (browserType.indexOf('chrome') <= -1) {
52600            this.requiresTimeout = true;
52601          }
52602        }
52603      } else {
52604        this.requiresTimeout = true;
52605      }
52606    }
52607  }]);
52608  return CanvasRenderer;
52609}();
52610
52611exports["default"] = CanvasRenderer;
52612
52613/***/ }),
52614/* 230 */
52615/***/ (function(module, exports, __webpack_require__) {
52616
52617"use strict";
52618
52619
52620Object.defineProperty(exports, "__esModule", {
52621  value: true
52622});
52623
52624var _classCallCheck2 = __webpack_require__(0);
52625
52626var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
52627
52628var _createClass2 = __webpack_require__(1);
52629
52630var _createClass3 = _interopRequireDefault(_createClass2);
52631
52632function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
52633
52634var Hammer = __webpack_require__(10);
52635var hammerUtil = __webpack_require__(37);
52636
52637var util = __webpack_require__(2);
52638
52639/**
52640 * Create the main frame for the Network.
52641 * This function is executed once when a Network object is created. The frame
52642 * contains a canvas, and this canvas contains all objects like the axis and
52643 * nodes.
52644 */
52645
52646var Canvas = function () {
52647  /**
52648   * @param {Object} body
52649   */
52650  function Canvas(body) {
52651    (0, _classCallCheck3['default'])(this, Canvas);
52652
52653    this.body = body;
52654    this.pixelRatio = 1;
52655    this.resizeTimer = undefined;
52656    this.resizeFunction = this._onResize.bind(this);
52657    this.cameraState = {};
52658    this.initialized = false;
52659    this.canvasViewCenter = {};
52660
52661    this.options = {};
52662    this.defaultOptions = {
52663      autoResize: true,
52664      height: '100%',
52665      width: '100%'
52666    };
52667    util.extend(this.options, this.defaultOptions);
52668
52669    this.bindEventListeners();
52670  }
52671
52672  /**
52673   * Binds event listeners
52674   */
52675
52676
52677  (0, _createClass3['default'])(Canvas, [{
52678    key: 'bindEventListeners',
52679    value: function bindEventListeners() {
52680      var _this = this;
52681
52682      // bind the events
52683      this.body.emitter.once("resize", function (obj) {
52684        if (obj.width !== 0) {
52685          _this.body.view.translation.x = obj.width * 0.5;
52686        }
52687        if (obj.height !== 0) {
52688          _this.body.view.translation.y = obj.height * 0.5;
52689        }
52690      });
52691      this.body.emitter.on("setSize", this.setSize.bind(this));
52692      this.body.emitter.on("destroy", function () {
52693        _this.hammerFrame.destroy();
52694        _this.hammer.destroy();
52695        _this._cleanUp();
52696      });
52697    }
52698
52699    /**
52700     * @param {Object} options
52701     */
52702
52703  }, {
52704    key: 'setOptions',
52705    value: function setOptions(options) {
52706      var _this2 = this;
52707
52708      if (options !== undefined) {
52709        var fields = ['width', 'height', 'autoResize'];
52710        util.selectiveDeepExtend(fields, this.options, options);
52711      }
52712
52713      if (this.options.autoResize === true) {
52714        // automatically adapt to a changing size of the browser.
52715        this._cleanUp();
52716        this.resizeTimer = setInterval(function () {
52717          var changed = _this2.setSize();
52718          if (changed === true) {
52719            _this2.body.emitter.emit("_requestRedraw");
52720          }
52721        }, 1000);
52722        this.resizeFunction = this._onResize.bind(this);
52723        util.addEventListener(window, 'resize', this.resizeFunction);
52724      }
52725    }
52726
52727    /**
52728     * @private
52729     */
52730
52731  }, {
52732    key: '_cleanUp',
52733    value: function _cleanUp() {
52734      // automatically adapt to a changing size of the browser.
52735      if (this.resizeTimer !== undefined) {
52736        clearInterval(this.resizeTimer);
52737      }
52738      util.removeEventListener(window, 'resize', this.resizeFunction);
52739      this.resizeFunction = undefined;
52740    }
52741
52742    /**
52743     * @private
52744     */
52745
52746  }, {
52747    key: '_onResize',
52748    value: function _onResize() {
52749      this.setSize();
52750      this.body.emitter.emit("_redraw");
52751    }
52752
52753    /**
52754     * Get and store the cameraState
52755     *
52756     * @param {number} [pixelRatio=this.pixelRatio]
52757     * @private
52758     */
52759
52760  }, {
52761    key: '_getCameraState',
52762    value: function _getCameraState() {
52763      var pixelRatio = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : this.pixelRatio;
52764
52765      if (this.initialized === true) {
52766        this.cameraState.previousWidth = this.frame.canvas.width / pixelRatio;
52767        this.cameraState.previousHeight = this.frame.canvas.height / pixelRatio;
52768        this.cameraState.scale = this.body.view.scale;
52769        this.cameraState.position = this.DOMtoCanvas({
52770          x: 0.5 * this.frame.canvas.width / pixelRatio,
52771          y: 0.5 * this.frame.canvas.height / pixelRatio
52772        });
52773      }
52774    }
52775
52776    /**
52777     * Set the cameraState
52778     * @private
52779     */
52780
52781  }, {
52782    key: '_setCameraState',
52783    value: function _setCameraState() {
52784      if (this.cameraState.scale !== undefined && this.frame.canvas.clientWidth !== 0 && this.frame.canvas.clientHeight !== 0 && this.pixelRatio !== 0 && this.cameraState.previousWidth > 0) {
52785
52786        var widthRatio = this.frame.canvas.width / this.pixelRatio / this.cameraState.previousWidth;
52787        var heightRatio = this.frame.canvas.height / this.pixelRatio / this.cameraState.previousHeight;
52788        var newScale = this.cameraState.scale;
52789
52790        if (widthRatio != 1 && heightRatio != 1) {
52791          newScale = this.cameraState.scale * 0.5 * (widthRatio + heightRatio);
52792        } else if (widthRatio != 1) {
52793          newScale = this.cameraState.scale * widthRatio;
52794        } else if (heightRatio != 1) {
52795          newScale = this.cameraState.scale * heightRatio;
52796        }
52797
52798        this.body.view.scale = newScale;
52799        // this comes from the view module.
52800        var currentViewCenter = this.DOMtoCanvas({
52801          x: 0.5 * this.frame.canvas.clientWidth,
52802          y: 0.5 * this.frame.canvas.clientHeight
52803        });
52804
52805        var distanceFromCenter = { // offset from view, distance view has to change by these x and y to center the node
52806          x: currentViewCenter.x - this.cameraState.position.x,
52807          y: currentViewCenter.y - this.cameraState.position.y
52808        };
52809        this.body.view.translation.x += distanceFromCenter.x * this.body.view.scale;
52810        this.body.view.translation.y += distanceFromCenter.y * this.body.view.scale;
52811      }
52812    }
52813
52814    /**
52815     *
52816     * @param {number|string} value
52817     * @returns {string}
52818     * @private
52819     */
52820
52821  }, {
52822    key: '_prepareValue',
52823    value: function _prepareValue(value) {
52824      if (typeof value === 'number') {
52825        return value + 'px';
52826      } else if (typeof value === 'string') {
52827        if (value.indexOf('%') !== -1 || value.indexOf('px') !== -1) {
52828          return value;
52829        } else if (value.indexOf('%') === -1) {
52830          return value + 'px';
52831        }
52832      }
52833      throw new Error('Could not use the value supplied for width or height:' + value);
52834    }
52835
52836    /**
52837     * Create the HTML
52838     */
52839
52840  }, {
52841    key: '_create',
52842    value: function _create() {
52843      // remove all elements from the container element.
52844      while (this.body.container.hasChildNodes()) {
52845        this.body.container.removeChild(this.body.container.firstChild);
52846      }
52847
52848      this.frame = document.createElement('div');
52849      this.frame.className = 'vis-network';
52850      this.frame.style.position = 'relative';
52851      this.frame.style.overflow = 'hidden';
52852      this.frame.tabIndex = 900; // tab index is required for keycharm to bind keystrokes to the div instead of the window
52853
52854      //////////////////////////////////////////////////////////////////
52855
52856      this.frame.canvas = document.createElement("canvas");
52857      this.frame.canvas.style.position = 'relative';
52858      this.frame.appendChild(this.frame.canvas);
52859
52860      if (!this.frame.canvas.getContext) {
52861        var noCanvas = document.createElement('DIV');
52862        noCanvas.style.color = 'red';
52863        noCanvas.style.fontWeight = 'bold';
52864        noCanvas.style.padding = '10px';
52865        noCanvas.innerHTML = 'Error: your browser does not support HTML canvas';
52866        this.frame.canvas.appendChild(noCanvas);
52867      } else {
52868        this._setPixelRatio();
52869        this.setTransform();
52870      }
52871
52872      // add the frame to the container element
52873      this.body.container.appendChild(this.frame);
52874
52875      this.body.view.scale = 1;
52876      this.body.view.translation = { x: 0.5 * this.frame.canvas.clientWidth, y: 0.5 * this.frame.canvas.clientHeight };
52877
52878      this._bindHammer();
52879    }
52880
52881    /**
52882     * This function binds hammer, it can be repeated over and over due to the uniqueness check.
52883     * @private
52884     */
52885
52886  }, {
52887    key: '_bindHammer',
52888    value: function _bindHammer() {
52889      var _this3 = this;
52890
52891      if (this.hammer !== undefined) {
52892        this.hammer.destroy();
52893      }
52894      this.drag = {};
52895      this.pinch = {};
52896
52897      // init hammer
52898      this.hammer = new Hammer(this.frame.canvas);
52899      this.hammer.get('pinch').set({ enable: true });
52900      // enable to get better response, todo: test on mobile.
52901      this.hammer.get('pan').set({ threshold: 5, direction: Hammer.DIRECTION_ALL });
52902
52903      hammerUtil.onTouch(this.hammer, function (event) {
52904        _this3.body.eventListeners.onTouch(event);
52905      });
52906      this.hammer.on('tap', function (event) {
52907        _this3.body.eventListeners.onTap(event);
52908      });
52909      this.hammer.on('doubletap', function (event) {
52910        _this3.body.eventListeners.onDoubleTap(event);
52911      });
52912      this.hammer.on('press', function (event) {
52913        _this3.body.eventListeners.onHold(event);
52914      });
52915      this.hammer.on('panstart', function (event) {
52916        _this3.body.eventListeners.onDragStart(event);
52917      });
52918      this.hammer.on('panmove', function (event) {
52919        _this3.body.eventListeners.onDrag(event);
52920      });
52921      this.hammer.on('panend', function (event) {
52922        _this3.body.eventListeners.onDragEnd(event);
52923      });
52924      this.hammer.on('pinch', function (event) {
52925        _this3.body.eventListeners.onPinch(event);
52926      });
52927
52928      // TODO: neatly cleanup these handlers when re-creating the Canvas, IF these are done with hammer, event.stopPropagation will not work?
52929      this.frame.canvas.addEventListener('mousewheel', function (event) {
52930        _this3.body.eventListeners.onMouseWheel(event);
52931      });
52932      this.frame.canvas.addEventListener('DOMMouseScroll', function (event) {
52933        _this3.body.eventListeners.onMouseWheel(event);
52934      });
52935
52936      this.frame.canvas.addEventListener('mousemove', function (event) {
52937        _this3.body.eventListeners.onMouseMove(event);
52938      });
52939      this.frame.canvas.addEventListener('contextmenu', function (event) {
52940        _this3.body.eventListeners.onContext(event);
52941      });
52942
52943      this.hammerFrame = new Hammer(this.frame);
52944      hammerUtil.onRelease(this.hammerFrame, function (event) {
52945        _this3.body.eventListeners.onRelease(event);
52946      });
52947    }
52948
52949    /**
52950     * Set a new size for the network
52951     * @param {string} width   Width in pixels or percentage (for example '800px'
52952     *                         or '50%')
52953     * @param {string} height  Height in pixels or percentage  (for example '400px'
52954     *                         or '30%')
52955     * @returns {boolean}
52956     */
52957
52958  }, {
52959    key: 'setSize',
52960    value: function setSize() {
52961      var width = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : this.options.width;
52962      var height = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : this.options.height;
52963
52964      width = this._prepareValue(width);
52965      height = this._prepareValue(height);
52966
52967      var emitEvent = false;
52968      var oldWidth = this.frame.canvas.width;
52969      var oldHeight = this.frame.canvas.height;
52970
52971      // update the pixel ratio
52972      //
52973      // NOTE: Comment in following is rather inconsistent; this is the ONLY place in the code
52974      //       where it is assumed that the pixel ratio could change at runtime.
52975      //       The only way I can think of this happening is a rotating screen or tablet; but then
52976      //       there should be a mechanism for reloading the data (TODO: check if this is present).
52977      //
52978      //       If the assumption is true (i.e. pixel ratio can change at runtime), then *all* usage
52979      //       of pixel ratio must be overhauled for this.
52980      //
52981      //       For the time being, I will humor the assumption here, and in the rest of the code assume it is
52982      //       constant.
52983      var previousRatio = this.pixelRatio; // we cache this because the camera state storage needs the old value
52984      this._setPixelRatio();
52985
52986      if (width != this.options.width || height != this.options.height || this.frame.style.width != width || this.frame.style.height != height) {
52987        this._getCameraState(previousRatio);
52988
52989        this.frame.style.width = width;
52990        this.frame.style.height = height;
52991
52992        this.frame.canvas.style.width = '100%';
52993        this.frame.canvas.style.height = '100%';
52994
52995        this.frame.canvas.width = Math.round(this.frame.canvas.clientWidth * this.pixelRatio);
52996        this.frame.canvas.height = Math.round(this.frame.canvas.clientHeight * this.pixelRatio);
52997
52998        this.options.width = width;
52999        this.options.height = height;
53000
53001        this.canvasViewCenter = {
53002          x: 0.5 * this.frame.clientWidth,
53003          y: 0.5 * this.frame.clientHeight
53004        };
53005
53006        emitEvent = true;
53007      } else {
53008        // this would adapt the width of the canvas to the width from 100% if and only if
53009        // there is a change.
53010
53011        var newWidth = Math.round(this.frame.canvas.clientWidth * this.pixelRatio);
53012        var newHeight = Math.round(this.frame.canvas.clientHeight * this.pixelRatio);
53013
53014        // store the camera if there is a change in size.
53015        if (this.frame.canvas.width !== newWidth || this.frame.canvas.height !== newHeight) {
53016          this._getCameraState(previousRatio);
53017        }
53018
53019        if (this.frame.canvas.width !== newWidth) {
53020          this.frame.canvas.width = newWidth;
53021          emitEvent = true;
53022        }
53023        if (this.frame.canvas.height !== newHeight) {
53024          this.frame.canvas.height = newHeight;
53025          emitEvent = true;
53026        }
53027      }
53028
53029      if (emitEvent === true) {
53030        this.body.emitter.emit('resize', {
53031          width: Math.round(this.frame.canvas.width / this.pixelRatio),
53032          height: Math.round(this.frame.canvas.height / this.pixelRatio),
53033          oldWidth: Math.round(oldWidth / this.pixelRatio),
53034          oldHeight: Math.round(oldHeight / this.pixelRatio)
53035        });
53036
53037        // restore the camera on change.
53038        this._setCameraState();
53039      }
53040
53041      // set initialized so the get and set camera will work from now on.
53042      this.initialized = true;
53043      return emitEvent;
53044    }
53045
53046    /**
53047     *
53048     * @returns {CanvasRenderingContext2D}
53049     */
53050
53051  }, {
53052    key: 'getContext',
53053    value: function getContext() {
53054      return this.frame.canvas.getContext("2d");
53055    }
53056
53057    /**
53058     * Determine the pixel ratio for various browsers.
53059     *
53060     * @returns {number}
53061     * @private
53062     */
53063
53064  }, {
53065    key: '_determinePixelRatio',
53066    value: function _determinePixelRatio() {
53067      var ctx = this.getContext();
53068      if (ctx === undefined) {
53069        throw new Error("Could not get canvax context");
53070      }
53071
53072      var numerator = 1;
53073      if (typeof window !== 'undefined') {
53074        // (window !== undefined) doesn't work here!
53075        // Protection during unit tests, where 'window' can be missing
53076        numerator = window.devicePixelRatio || 1;
53077      }
53078
53079      var denominator = ctx.webkitBackingStorePixelRatio || ctx.mozBackingStorePixelRatio || ctx.msBackingStorePixelRatio || ctx.oBackingStorePixelRatio || ctx.backingStorePixelRatio || 1;
53080
53081      return numerator / denominator;
53082    }
53083
53084    /**
53085     * Lazy determination of pixel ratio.
53086     *
53087     * @private
53088     */
53089
53090  }, {
53091    key: '_setPixelRatio',
53092    value: function _setPixelRatio() {
53093      this.pixelRatio = this._determinePixelRatio();
53094    }
53095
53096    /**
53097     * Set the transform in the contained context, based on its pixelRatio
53098     */
53099
53100  }, {
53101    key: 'setTransform',
53102    value: function setTransform() {
53103      var ctx = this.getContext();
53104      if (ctx === undefined) {
53105        throw new Error("Could not get canvax context");
53106      }
53107
53108      ctx.setTransform(this.pixelRatio, 0, 0, this.pixelRatio, 0, 0);
53109    }
53110
53111    /**
53112     * Convert the X coordinate in DOM-space (coordinate point in browser relative to the container div) to
53113     * the X coordinate in canvas-space (the simulation sandbox, which the camera looks upon)
53114     * @param {number} x
53115     * @returns {number}
53116     * @private
53117     */
53118
53119  }, {
53120    key: '_XconvertDOMtoCanvas',
53121    value: function _XconvertDOMtoCanvas(x) {
53122      return (x - this.body.view.translation.x) / this.body.view.scale;
53123    }
53124
53125    /**
53126     * Convert the X coordinate in canvas-space (the simulation sandbox, which the camera looks upon) to
53127     * the X coordinate in DOM-space (coordinate point in browser relative to the container div)
53128     * @param {number} x
53129     * @returns {number}
53130     * @private
53131     */
53132
53133  }, {
53134    key: '_XconvertCanvasToDOM',
53135    value: function _XconvertCanvasToDOM(x) {
53136      return x * this.body.view.scale + this.body.view.translation.x;
53137    }
53138
53139    /**
53140     * Convert the Y coordinate in DOM-space (coordinate point in browser relative to the container div) to
53141     * the Y coordinate in canvas-space (the simulation sandbox, which the camera looks upon)
53142     * @param {number} y
53143     * @returns {number}
53144     * @private
53145     */
53146
53147  }, {
53148    key: '_YconvertDOMtoCanvas',
53149    value: function _YconvertDOMtoCanvas(y) {
53150      return (y - this.body.view.translation.y) / this.body.view.scale;
53151    }
53152
53153    /**
53154     * Convert the Y coordinate in canvas-space (the simulation sandbox, which the camera looks upon) to
53155     * the Y coordinate in DOM-space (coordinate point in browser relative to the container div)
53156     * @param {number} y
53157     * @returns {number}
53158     * @private
53159     */
53160
53161  }, {
53162    key: '_YconvertCanvasToDOM',
53163    value: function _YconvertCanvasToDOM(y) {
53164      return y * this.body.view.scale + this.body.view.translation.y;
53165    }
53166
53167    /**
53168     * @param {point} pos
53169     * @returns {point}
53170     */
53171
53172  }, {
53173    key: 'canvasToDOM',
53174    value: function canvasToDOM(pos) {
53175      return { x: this._XconvertCanvasToDOM(pos.x), y: this._YconvertCanvasToDOM(pos.y) };
53176    }
53177
53178    /**
53179     *
53180     * @param {point} pos
53181     * @returns {point}
53182     */
53183
53184  }, {
53185    key: 'DOMtoCanvas',
53186    value: function DOMtoCanvas(pos) {
53187      return { x: this._XconvertDOMtoCanvas(pos.x), y: this._YconvertDOMtoCanvas(pos.y) };
53188    }
53189  }]);
53190  return Canvas;
53191}();
53192
53193exports['default'] = Canvas;
53194
53195/***/ }),
53196/* 231 */
53197/***/ (function(module, exports, __webpack_require__) {
53198
53199"use strict";
53200
53201
53202Object.defineProperty(exports, "__esModule", {
53203  value: true
53204});
53205
53206var _classCallCheck2 = __webpack_require__(0);
53207
53208var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
53209
53210var _createClass2 = __webpack_require__(1);
53211
53212var _createClass3 = _interopRequireDefault(_createClass2);
53213
53214function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
53215
53216var util = __webpack_require__(2);
53217
53218var NetworkUtil = __webpack_require__(76)['default'];
53219
53220/**
53221 * The view
53222 */
53223
53224var View = function () {
53225  /**
53226   * @param {Object} body
53227   * @param {Canvas} canvas
53228   */
53229  function View(body, canvas) {
53230    var _this = this;
53231
53232    (0, _classCallCheck3['default'])(this, View);
53233
53234    this.body = body;
53235    this.canvas = canvas;
53236
53237    this.animationSpeed = 1 / this.renderRefreshRate;
53238    this.animationEasingFunction = "easeInOutQuint";
53239    this.easingTime = 0;
53240    this.sourceScale = 0;
53241    this.targetScale = 0;
53242    this.sourceTranslation = 0;
53243    this.targetTranslation = 0;
53244    this.lockedOnNodeId = undefined;
53245    this.lockedOnNodeOffset = undefined;
53246    this.touchTime = 0;
53247
53248    this.viewFunction = undefined;
53249
53250    this.body.emitter.on("fit", this.fit.bind(this));
53251    this.body.emitter.on("animationFinished", function () {
53252      _this.body.emitter.emit("_stopRendering");
53253    });
53254    this.body.emitter.on("unlockNode", this.releaseNode.bind(this));
53255  }
53256
53257  /**
53258   *
53259   * @param {Object} [options={}]
53260   */
53261
53262
53263  (0, _createClass3['default'])(View, [{
53264    key: 'setOptions',
53265    value: function setOptions() {
53266      var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
53267
53268      this.options = options;
53269    }
53270
53271    /**
53272     * This function zooms out to fit all data on screen based on amount of nodes
53273     * @param {Object} [options={{nodes=Array}}]
53274     * @param {boolean} [initialZoom=false]  | zoom based on fitted formula or range, true = fitted, default = false;
53275     */
53276
53277  }, {
53278    key: 'fit',
53279    value: function fit() {
53280      var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : { nodes: [] };
53281      var initialZoom = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
53282
53283      var range = void 0;
53284      var zoomLevel = void 0;
53285      if (options.nodes === undefined || options.nodes.length === 0) {
53286        options.nodes = this.body.nodeIndices;
53287      }
53288
53289      if (initialZoom === true) {
53290        // check if more than half of the nodes have a predefined position. If so, we use the range, not the approximation.
53291        var positionDefined = 0;
53292        for (var nodeId in this.body.nodes) {
53293          if (this.body.nodes.hasOwnProperty(nodeId)) {
53294            var node = this.body.nodes[nodeId];
53295            if (node.predefinedPosition === true) {
53296              positionDefined += 1;
53297            }
53298          }
53299        }
53300        if (positionDefined > 0.5 * this.body.nodeIndices.length) {
53301          this.fit(options, false);
53302          return;
53303        }
53304
53305        range = NetworkUtil.getRange(this.body.nodes, options.nodes);
53306
53307        var numberOfNodes = this.body.nodeIndices.length;
53308        zoomLevel = 12.662 / (numberOfNodes + 7.4147) + 0.0964822; // this is obtained from fitting a dataset from 5 points with scale levels that looked good.
53309
53310        // correct for larger canvasses.
53311        var factor = Math.min(this.canvas.frame.canvas.clientWidth / 600, this.canvas.frame.canvas.clientHeight / 600);
53312        zoomLevel *= factor;
53313      } else {
53314        this.body.emitter.emit("_resizeNodes");
53315        range = NetworkUtil.getRange(this.body.nodes, options.nodes);
53316
53317        var xDistance = Math.abs(range.maxX - range.minX) * 1.1;
53318        var yDistance = Math.abs(range.maxY - range.minY) * 1.1;
53319
53320        var xZoomLevel = this.canvas.frame.canvas.clientWidth / xDistance;
53321        var yZoomLevel = this.canvas.frame.canvas.clientHeight / yDistance;
53322
53323        zoomLevel = xZoomLevel <= yZoomLevel ? xZoomLevel : yZoomLevel;
53324      }
53325
53326      if (zoomLevel > 1.0) {
53327        zoomLevel = 1.0;
53328      } else if (zoomLevel === 0) {
53329        zoomLevel = 1.0;
53330      }
53331
53332      var center = NetworkUtil.findCenter(range);
53333      var animationOptions = { position: center, scale: zoomLevel, animation: options.animation };
53334      this.moveTo(animationOptions);
53335    }
53336
53337    // animation
53338
53339    /**
53340     * Center a node in view.
53341     *
53342     * @param {number} nodeId
53343     * @param {number} [options]
53344     */
53345
53346  }, {
53347    key: 'focus',
53348    value: function focus(nodeId) {
53349      var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
53350
53351      if (this.body.nodes[nodeId] !== undefined) {
53352        var nodePosition = { x: this.body.nodes[nodeId].x, y: this.body.nodes[nodeId].y };
53353        options.position = nodePosition;
53354        options.lockedOnNode = nodeId;
53355
53356        this.moveTo(options);
53357      } else {
53358        console.log("Node: " + nodeId + " cannot be found.");
53359      }
53360    }
53361
53362    /**
53363     *
53364     * @param {Object} options  |  options.offset   = {x:number, y:number}   // offset from the center in DOM pixels
53365     *                          |  options.scale    = number                 // scale to move to
53366     *                          |  options.position = {x:number, y:number}   // position to move to
53367     *                          |  options.animation = {duration:number, easingFunction:String} || Boolean   // position to move to
53368     */
53369
53370  }, {
53371    key: 'moveTo',
53372    value: function moveTo(options) {
53373      if (options === undefined) {
53374        options = {};
53375        return;
53376      }
53377      if (options.offset === undefined) {
53378        options.offset = { x: 0, y: 0 };
53379      }
53380      if (options.offset.x === undefined) {
53381        options.offset.x = 0;
53382      }
53383      if (options.offset.y === undefined) {
53384        options.offset.y = 0;
53385      }
53386      if (options.scale === undefined) {
53387        options.scale = this.body.view.scale;
53388      }
53389      if (options.position === undefined) {
53390        options.position = this.getViewPosition();
53391      }
53392      if (options.animation === undefined) {
53393        options.animation = { duration: 0 };
53394      }
53395      if (options.animation === false) {
53396        options.animation = { duration: 0 };
53397      }
53398      if (options.animation === true) {
53399        options.animation = {};
53400      }
53401      if (options.animation.duration === undefined) {
53402        options.animation.duration = 1000;
53403      } // default duration
53404      if (options.animation.easingFunction === undefined) {
53405        options.animation.easingFunction = "easeInOutQuad";
53406      } // default easing function
53407
53408      this.animateView(options);
53409    }
53410
53411    /**
53412     *
53413     * @param {Object} options  |  options.offset   = {x:number, y:number}   // offset from the center in DOM pixels
53414     *                          |  options.time     = number                 // animation time in milliseconds
53415     *                          |  options.scale    = number                 // scale to animate to
53416     *                          |  options.position = {x:number, y:number}   // position to animate to
53417     *                          |  options.easingFunction = String           // linear, easeInQuad, easeOutQuad, easeInOutQuad,
53418     *                                                                       // easeInCubic, easeOutCubic, easeInOutCubic,
53419     *                                                                       // easeInQuart, easeOutQuart, easeInOutQuart,
53420     *                                                                       // easeInQuint, easeOutQuint, easeInOutQuint
53421     */
53422
53423  }, {
53424    key: 'animateView',
53425    value: function animateView(options) {
53426      if (options === undefined) {
53427        return;
53428      }
53429      this.animationEasingFunction = options.animation.easingFunction;
53430      // release if something focussed on the node
53431      this.releaseNode();
53432      if (options.locked === true) {
53433        this.lockedOnNodeId = options.lockedOnNode;
53434        this.lockedOnNodeOffset = options.offset;
53435      }
53436
53437      // forcefully complete the old animation if it was still running
53438      if (this.easingTime != 0) {
53439        this._transitionRedraw(true); // by setting easingtime to 1, we finish the animation.
53440      }
53441
53442      this.sourceScale = this.body.view.scale;
53443      this.sourceTranslation = this.body.view.translation;
53444      this.targetScale = options.scale;
53445
53446      // set the scale so the viewCenter is based on the correct zoom level. This is overridden in the transitionRedraw
53447      // but at least then we'll have the target transition
53448      this.body.view.scale = this.targetScale;
53449      var viewCenter = this.canvas.DOMtoCanvas({ x: 0.5 * this.canvas.frame.canvas.clientWidth, y: 0.5 * this.canvas.frame.canvas.clientHeight });
53450
53451      var distanceFromCenter = { // offset from view, distance view has to change by these x and y to center the node
53452        x: viewCenter.x - options.position.x,
53453        y: viewCenter.y - options.position.y
53454      };
53455      this.targetTranslation = {
53456        x: this.sourceTranslation.x + distanceFromCenter.x * this.targetScale + options.offset.x,
53457        y: this.sourceTranslation.y + distanceFromCenter.y * this.targetScale + options.offset.y
53458      };
53459
53460      // if the time is set to 0, don't do an animation
53461      if (options.animation.duration === 0) {
53462        if (this.lockedOnNodeId != undefined) {
53463          this.viewFunction = this._lockedRedraw.bind(this);
53464          this.body.emitter.on("initRedraw", this.viewFunction);
53465        } else {
53466          this.body.view.scale = this.targetScale;
53467          this.body.view.translation = this.targetTranslation;
53468          this.body.emitter.emit("_requestRedraw");
53469        }
53470      } else {
53471        this.animationSpeed = 1 / (60 * options.animation.duration * 0.001) || 1 / 60; // 60 for 60 seconds, 0.001 for milli's
53472        this.animationEasingFunction = options.animation.easingFunction;
53473
53474        this.viewFunction = this._transitionRedraw.bind(this);
53475        this.body.emitter.on("initRedraw", this.viewFunction);
53476        this.body.emitter.emit("_startRendering");
53477      }
53478    }
53479
53480    /**
53481     * used to animate smoothly by hijacking the redraw function.
53482     * @private
53483     */
53484
53485  }, {
53486    key: '_lockedRedraw',
53487    value: function _lockedRedraw() {
53488      var nodePosition = { x: this.body.nodes[this.lockedOnNodeId].x, y: this.body.nodes[this.lockedOnNodeId].y };
53489      var viewCenter = this.canvas.DOMtoCanvas({ x: 0.5 * this.canvas.frame.canvas.clientWidth, y: 0.5 * this.canvas.frame.canvas.clientHeight });
53490      var distanceFromCenter = { // offset from view, distance view has to change by these x and y to center the node
53491        x: viewCenter.x - nodePosition.x,
53492        y: viewCenter.y - nodePosition.y
53493      };
53494      var sourceTranslation = this.body.view.translation;
53495      var targetTranslation = {
53496        x: sourceTranslation.x + distanceFromCenter.x * this.body.view.scale + this.lockedOnNodeOffset.x,
53497        y: sourceTranslation.y + distanceFromCenter.y * this.body.view.scale + this.lockedOnNodeOffset.y
53498      };
53499
53500      this.body.view.translation = targetTranslation;
53501    }
53502
53503    /**
53504     * Resets state of a locked on Node
53505     */
53506
53507  }, {
53508    key: 'releaseNode',
53509    value: function releaseNode() {
53510      if (this.lockedOnNodeId !== undefined && this.viewFunction !== undefined) {
53511        this.body.emitter.off("initRedraw", this.viewFunction);
53512        this.lockedOnNodeId = undefined;
53513        this.lockedOnNodeOffset = undefined;
53514      }
53515    }
53516
53517    /**
53518     * @param {boolean} [finished=false]
53519     * @private
53520     */
53521
53522  }, {
53523    key: '_transitionRedraw',
53524    value: function _transitionRedraw() {
53525      var finished = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
53526
53527      this.easingTime += this.animationSpeed;
53528      this.easingTime = finished === true ? 1.0 : this.easingTime;
53529
53530      var progress = util.easingFunctions[this.animationEasingFunction](this.easingTime);
53531
53532      this.body.view.scale = this.sourceScale + (this.targetScale - this.sourceScale) * progress;
53533      this.body.view.translation = {
53534        x: this.sourceTranslation.x + (this.targetTranslation.x - this.sourceTranslation.x) * progress,
53535        y: this.sourceTranslation.y + (this.targetTranslation.y - this.sourceTranslation.y) * progress
53536      };
53537
53538      // cleanup
53539      if (this.easingTime >= 1.0) {
53540        this.body.emitter.off("initRedraw", this.viewFunction);
53541        this.easingTime = 0;
53542        if (this.lockedOnNodeId != undefined) {
53543          this.viewFunction = this._lockedRedraw.bind(this);
53544          this.body.emitter.on("initRedraw", this.viewFunction);
53545        }
53546        this.body.emitter.emit("animationFinished");
53547      }
53548    }
53549
53550    /**
53551     *
53552     * @returns {number}
53553     */
53554
53555  }, {
53556    key: 'getScale',
53557    value: function getScale() {
53558      return this.body.view.scale;
53559    }
53560
53561    /**
53562     *
53563     * @returns {{x: number, y: number}}
53564     */
53565
53566  }, {
53567    key: 'getViewPosition',
53568    value: function getViewPosition() {
53569      return this.canvas.DOMtoCanvas({ x: 0.5 * this.canvas.frame.canvas.clientWidth, y: 0.5 * this.canvas.frame.canvas.clientHeight });
53570    }
53571  }]);
53572  return View;
53573}();
53574
53575exports['default'] = View;
53576
53577/***/ }),
53578/* 232 */
53579/***/ (function(module, exports, __webpack_require__) {
53580
53581"use strict";
53582
53583
53584Object.defineProperty(exports, "__esModule", {
53585  value: true
53586});
53587
53588var _classCallCheck2 = __webpack_require__(0);
53589
53590var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
53591
53592var _createClass2 = __webpack_require__(1);
53593
53594var _createClass3 = _interopRequireDefault(_createClass2);
53595
53596function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
53597
53598var util = __webpack_require__(2);
53599var NavigationHandler = __webpack_require__(233)['default'];
53600var Popup = __webpack_require__(104)['default'];
53601
53602/**
53603 * Handler for interactions
53604 */
53605
53606var InteractionHandler = function () {
53607  /**
53608   * @param {Object} body
53609   * @param {Canvas} canvas
53610   * @param {SelectionHandler} selectionHandler
53611   */
53612  function InteractionHandler(body, canvas, selectionHandler) {
53613    (0, _classCallCheck3['default'])(this, InteractionHandler);
53614
53615    this.body = body;
53616    this.canvas = canvas;
53617    this.selectionHandler = selectionHandler;
53618    this.navigationHandler = new NavigationHandler(body, canvas);
53619
53620    // bind the events from hammer to functions in this object
53621    this.body.eventListeners.onTap = this.onTap.bind(this);
53622    this.body.eventListeners.onTouch = this.onTouch.bind(this);
53623    this.body.eventListeners.onDoubleTap = this.onDoubleTap.bind(this);
53624    this.body.eventListeners.onHold = this.onHold.bind(this);
53625    this.body.eventListeners.onDragStart = this.onDragStart.bind(this);
53626    this.body.eventListeners.onDrag = this.onDrag.bind(this);
53627    this.body.eventListeners.onDragEnd = this.onDragEnd.bind(this);
53628    this.body.eventListeners.onMouseWheel = this.onMouseWheel.bind(this);
53629    this.body.eventListeners.onPinch = this.onPinch.bind(this);
53630    this.body.eventListeners.onMouseMove = this.onMouseMove.bind(this);
53631    this.body.eventListeners.onRelease = this.onRelease.bind(this);
53632    this.body.eventListeners.onContext = this.onContext.bind(this);
53633
53634    this.touchTime = 0;
53635    this.drag = {};
53636    this.pinch = {};
53637    this.popup = undefined;
53638    this.popupObj = undefined;
53639    this.popupTimer = undefined;
53640
53641    this.body.functions.getPointer = this.getPointer.bind(this);
53642
53643    this.options = {};
53644    this.defaultOptions = {
53645      dragNodes: true,
53646      dragView: true,
53647      hover: false,
53648      keyboard: {
53649        enabled: false,
53650        speed: { x: 10, y: 10, zoom: 0.02 },
53651        bindToWindow: true
53652      },
53653      navigationButtons: false,
53654      tooltipDelay: 300,
53655      zoomView: true
53656    };
53657    util.extend(this.options, this.defaultOptions);
53658
53659    this.bindEventListeners();
53660  }
53661
53662  /**
53663   * Binds event listeners
53664   */
53665
53666
53667  (0, _createClass3['default'])(InteractionHandler, [{
53668    key: 'bindEventListeners',
53669    value: function bindEventListeners() {
53670      var _this = this;
53671
53672      this.body.emitter.on('destroy', function () {
53673        clearTimeout(_this.popupTimer);
53674        delete _this.body.functions.getPointer;
53675      });
53676    }
53677
53678    /**
53679     *
53680     * @param {Object} options
53681     */
53682
53683  }, {
53684    key: 'setOptions',
53685    value: function setOptions(options) {
53686      if (options !== undefined) {
53687        // extend all but the values in fields
53688        var fields = ['hideEdgesOnDrag', 'hideNodesOnDrag', 'keyboard', 'multiselect', 'selectable', 'selectConnectedEdges'];
53689        util.selectiveNotDeepExtend(fields, this.options, options);
53690
53691        // merge the keyboard options in.
53692        util.mergeOptions(this.options, options, 'keyboard');
53693
53694        if (options.tooltip) {
53695          util.extend(this.options.tooltip, options.tooltip);
53696          if (options.tooltip.color) {
53697            this.options.tooltip.color = util.parseColor(options.tooltip.color);
53698          }
53699        }
53700      }
53701
53702      this.navigationHandler.setOptions(this.options);
53703    }
53704
53705    /**
53706     * Get the pointer location from a touch location
53707     * @param {{x: number, y: number}} touch
53708     * @return {{x: number, y: number}} pointer
53709     * @private
53710     */
53711
53712  }, {
53713    key: 'getPointer',
53714    value: function getPointer(touch) {
53715      return {
53716        x: touch.x - util.getAbsoluteLeft(this.canvas.frame.canvas),
53717        y: touch.y - util.getAbsoluteTop(this.canvas.frame.canvas)
53718      };
53719    }
53720
53721    /**
53722     * On start of a touch gesture, store the pointer
53723     * @param {Event}  event   The event
53724     * @private
53725     */
53726
53727  }, {
53728    key: 'onTouch',
53729    value: function onTouch(event) {
53730      if (new Date().valueOf() - this.touchTime > 50) {
53731        this.drag.pointer = this.getPointer(event.center);
53732        this.drag.pinched = false;
53733        this.pinch.scale = this.body.view.scale;
53734        // to avoid double fireing of this event because we have two hammer instances. (on canvas and on frame)
53735        this.touchTime = new Date().valueOf();
53736      }
53737    }
53738
53739    /**
53740     * handle tap/click event: select/unselect a node
53741     * @param {Event} event
53742     * @private
53743     */
53744
53745  }, {
53746    key: 'onTap',
53747    value: function onTap(event) {
53748      var pointer = this.getPointer(event.center);
53749      var multiselect = this.selectionHandler.options.multiselect && (event.changedPointers[0].ctrlKey || event.changedPointers[0].metaKey);
53750
53751      this.checkSelectionChanges(pointer, event, multiselect);
53752      this.selectionHandler._generateClickEvent('click', event, pointer);
53753    }
53754
53755    /**
53756     * handle doubletap event
53757     * @param {Event} event
53758     * @private
53759     */
53760
53761  }, {
53762    key: 'onDoubleTap',
53763    value: function onDoubleTap(event) {
53764      var pointer = this.getPointer(event.center);
53765      this.selectionHandler._generateClickEvent('doubleClick', event, pointer);
53766    }
53767
53768    /**
53769     * handle long tap event: multi select nodes
53770     * @param {Event} event
53771     * @private
53772     */
53773
53774  }, {
53775    key: 'onHold',
53776    value: function onHold(event) {
53777      var pointer = this.getPointer(event.center);
53778      var multiselect = this.selectionHandler.options.multiselect;
53779
53780      this.checkSelectionChanges(pointer, event, multiselect);
53781
53782      this.selectionHandler._generateClickEvent('click', event, pointer);
53783      this.selectionHandler._generateClickEvent('hold', event, pointer);
53784    }
53785
53786    /**
53787     * handle the release of the screen
53788     *
53789     * @param {Event} event
53790     * @private
53791     */
53792
53793  }, {
53794    key: 'onRelease',
53795    value: function onRelease(event) {
53796      if (new Date().valueOf() - this.touchTime > 10) {
53797        var pointer = this.getPointer(event.center);
53798        this.selectionHandler._generateClickEvent('release', event, pointer);
53799        // to avoid double fireing of this event because we have two hammer instances. (on canvas and on frame)
53800        this.touchTime = new Date().valueOf();
53801      }
53802    }
53803
53804    /**
53805     *
53806     * @param {Event} event
53807     */
53808
53809  }, {
53810    key: 'onContext',
53811    value: function onContext(event) {
53812      var pointer = this.getPointer({ x: event.clientX, y: event.clientY });
53813      this.selectionHandler._generateClickEvent('oncontext', event, pointer);
53814    }
53815
53816    /**
53817     * Select and deselect nodes depending current selection change.
53818     *
53819     * For changing nodes, select/deselect events are fired.
53820     *
53821     * NOTE: For a given edge, if one connecting node is deselected and with the same
53822     *       click the other node is selected, no events for the edge will fire.
53823     *       It was selected and it will remain selected.
53824     *
53825     * TODO: This is all SelectionHandler calls; the method should be moved to there.
53826     *
53827     * @param {{x: number, y: number}} pointer
53828     * @param {Event} event
53829     * @param {boolean} [add=false]
53830     */
53831
53832  }, {
53833    key: 'checkSelectionChanges',
53834    value: function checkSelectionChanges(pointer, event) {
53835      var add = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
53836
53837      var previousSelection = this.selectionHandler.getSelection();
53838      var selected = false;
53839      if (add === true) {
53840        selected = this.selectionHandler.selectAdditionalOnPoint(pointer);
53841      } else {
53842        selected = this.selectionHandler.selectOnPoint(pointer);
53843      }
53844      var currentSelection = this.selectionHandler.getSelection();
53845
53846      // See NOTE in method comment for the reason to do it like this
53847      var deselectedItems = this._determineDifference(previousSelection, currentSelection);
53848      var selectedItems = this._determineDifference(currentSelection, previousSelection);
53849
53850      if (deselectedItems.edges.length > 0) {
53851        this.selectionHandler._generateClickEvent('deselectEdge', event, pointer, previousSelection);
53852        selected = true;
53853      }
53854
53855      if (deselectedItems.nodes.length > 0) {
53856        this.selectionHandler._generateClickEvent('deselectNode', event, pointer, previousSelection);
53857        selected = true;
53858      }
53859
53860      if (selectedItems.nodes.length > 0) {
53861        this.selectionHandler._generateClickEvent('selectNode', event, pointer);
53862        selected = true;
53863      }
53864
53865      if (selectedItems.edges.length > 0) {
53866        this.selectionHandler._generateClickEvent('selectEdge', event, pointer);
53867        selected = true;
53868      }
53869
53870      // fire the select event if anything has been selected or deselected
53871      if (selected === true) {
53872        // select or unselect
53873        this.selectionHandler._generateClickEvent('select', event, pointer);
53874      }
53875    }
53876
53877    /**
53878     * Remove all node and edge id's from the first set that are present in the second one.
53879     *
53880     * @param {{nodes: Array.<Node>, edges: Array.<vis.Edge>}} firstSet
53881     * @param {{nodes: Array.<Node>, edges: Array.<vis.Edge>}} secondSet
53882     * @returns {{nodes: Array.<Node>, edges: Array.<vis.Edge>}}
53883     * @private
53884     */
53885
53886  }, {
53887    key: '_determineDifference',
53888    value: function _determineDifference(firstSet, secondSet) {
53889      var arrayDiff = function arrayDiff(firstArr, secondArr) {
53890        var result = [];
53891
53892        for (var i = 0; i < firstArr.length; i++) {
53893          var value = firstArr[i];
53894          if (secondArr.indexOf(value) === -1) {
53895            result.push(value);
53896          }
53897        }
53898
53899        return result;
53900      };
53901
53902      return {
53903        nodes: arrayDiff(firstSet.nodes, secondSet.nodes),
53904        edges: arrayDiff(firstSet.edges, secondSet.edges)
53905      };
53906    }
53907
53908    /**
53909     * This function is called by onDragStart.
53910     * It is separated out because we can then overload it for the datamanipulation system.
53911     *
53912     * @param {Event} event
53913     * @private
53914     */
53915
53916  }, {
53917    key: 'onDragStart',
53918    value: function onDragStart(event) {
53919      //in case the touch event was triggered on an external div, do the initial touch now.
53920      if (this.drag.pointer === undefined) {
53921        this.onTouch(event);
53922      }
53923
53924      // note: drag.pointer is set in onTouch to get the initial touch location
53925      var node = this.selectionHandler.getNodeAt(this.drag.pointer);
53926
53927      this.drag.dragging = true;
53928      this.drag.selection = [];
53929      this.drag.translation = util.extend({}, this.body.view.translation); // copy the object
53930      this.drag.nodeId = undefined;
53931
53932      if (node !== undefined && this.options.dragNodes === true) {
53933        this.drag.nodeId = node.id;
53934        // select the clicked node if not yet selected
53935        if (node.isSelected() === false) {
53936          this.selectionHandler.unselectAll();
53937          this.selectionHandler.selectObject(node);
53938        }
53939
53940        // after select to contain the node
53941        this.selectionHandler._generateClickEvent('dragStart', event, this.drag.pointer);
53942
53943        var selection = this.selectionHandler.selectionObj.nodes;
53944        // create an array with the selected nodes and their original location and status
53945        for (var nodeId in selection) {
53946          if (selection.hasOwnProperty(nodeId)) {
53947            var object = selection[nodeId];
53948            var s = {
53949              id: object.id,
53950              node: object,
53951
53952              // store original x, y, xFixed and yFixed, make the node temporarily Fixed
53953              x: object.x,
53954              y: object.y,
53955              xFixed: object.options.fixed.x,
53956              yFixed: object.options.fixed.y
53957            };
53958
53959            object.options.fixed.x = true;
53960            object.options.fixed.y = true;
53961
53962            this.drag.selection.push(s);
53963          }
53964        }
53965      } else {
53966        // fallback if no node is selected and thus the view is dragged.
53967        this.selectionHandler._generateClickEvent('dragStart', event, this.drag.pointer, undefined, true);
53968      }
53969    }
53970
53971    /**
53972     * handle drag event
53973     * @param {Event} event
53974     * @private
53975     */
53976
53977  }, {
53978    key: 'onDrag',
53979    value: function onDrag(event) {
53980      var _this2 = this;
53981
53982      if (this.drag.pinched === true) {
53983        return;
53984      }
53985
53986      // remove the focus on node if it is focussed on by the focusOnNode
53987      this.body.emitter.emit('unlockNode');
53988
53989      var pointer = this.getPointer(event.center);
53990
53991      var selection = this.drag.selection;
53992      if (selection && selection.length && this.options.dragNodes === true) {
53993        this.selectionHandler._generateClickEvent('dragging', event, pointer);
53994
53995        // calculate delta's and new location
53996        var deltaX = pointer.x - this.drag.pointer.x;
53997        var deltaY = pointer.y - this.drag.pointer.y;
53998
53999        // update position of all selected nodes
54000        selection.forEach(function (selection) {
54001          var node = selection.node;
54002          // only move the node if it was not fixed initially
54003          if (selection.xFixed === false) {
54004            node.x = _this2.canvas._XconvertDOMtoCanvas(_this2.canvas._XconvertCanvasToDOM(selection.x) + deltaX);
54005          }
54006          // only move the node if it was not fixed initially
54007          if (selection.yFixed === false) {
54008            node.y = _this2.canvas._YconvertDOMtoCanvas(_this2.canvas._YconvertCanvasToDOM(selection.y) + deltaY);
54009          }
54010        });
54011
54012        // start the simulation of the physics
54013        this.body.emitter.emit('startSimulation');
54014      } else {
54015        // move the network
54016        if (this.options.dragView === true) {
54017          this.selectionHandler._generateClickEvent('dragging', event, pointer, undefined, true);
54018
54019          // if the drag was not started properly because the click started outside the network div, start it now.
54020          if (this.drag.pointer === undefined) {
54021            this.onDragStart(event);
54022            return;
54023          }
54024          var diffX = pointer.x - this.drag.pointer.x;
54025          var diffY = pointer.y - this.drag.pointer.y;
54026
54027          this.body.view.translation = { x: this.drag.translation.x + diffX, y: this.drag.translation.y + diffY };
54028          this.body.emitter.emit('_requestRedraw');
54029        }
54030      }
54031    }
54032
54033    /**
54034     * handle drag start event
54035     * @param {Event} event
54036     * @private
54037     */
54038
54039  }, {
54040    key: 'onDragEnd',
54041    value: function onDragEnd(event) {
54042      this.drag.dragging = false;
54043      var selection = this.drag.selection;
54044      if (selection && selection.length) {
54045        selection.forEach(function (s) {
54046          // restore original xFixed and yFixed
54047          s.node.options.fixed.x = s.xFixed;
54048          s.node.options.fixed.y = s.yFixed;
54049        });
54050        this.selectionHandler._generateClickEvent('dragEnd', event, this.getPointer(event.center));
54051        this.body.emitter.emit('startSimulation');
54052      } else {
54053        this.selectionHandler._generateClickEvent('dragEnd', event, this.getPointer(event.center), undefined, true);
54054        this.body.emitter.emit('_requestRedraw');
54055      }
54056    }
54057
54058    /**
54059     * Handle pinch event
54060     * @param {Event}  event   The event
54061     * @private
54062     */
54063
54064  }, {
54065    key: 'onPinch',
54066    value: function onPinch(event) {
54067      var pointer = this.getPointer(event.center);
54068
54069      this.drag.pinched = true;
54070      if (this.pinch['scale'] === undefined) {
54071        this.pinch.scale = 1;
54072      }
54073
54074      // TODO: enabled moving while pinching?
54075      var scale = this.pinch.scale * event.scale;
54076      this.zoom(scale, pointer);
54077    }
54078
54079    /**
54080     * Zoom the network in or out
54081     * @param {number} scale a number around 1, and between 0.01 and 10
54082     * @param {{x: number, y: number}} pointer    Position on screen
54083     * @private
54084     */
54085
54086  }, {
54087    key: 'zoom',
54088    value: function zoom(scale, pointer) {
54089      if (this.options.zoomView === true) {
54090        var scaleOld = this.body.view.scale;
54091        if (scale < 0.00001) {
54092          scale = 0.00001;
54093        }
54094        if (scale > 10) {
54095          scale = 10;
54096        }
54097
54098        var preScaleDragPointer = undefined;
54099        if (this.drag !== undefined) {
54100          if (this.drag.dragging === true) {
54101            preScaleDragPointer = this.canvas.DOMtoCanvas(this.drag.pointer);
54102          }
54103        }
54104        // + this.canvas.frame.canvas.clientHeight / 2
54105        var translation = this.body.view.translation;
54106
54107        var scaleFrac = scale / scaleOld;
54108        var tx = (1 - scaleFrac) * pointer.x + translation.x * scaleFrac;
54109        var ty = (1 - scaleFrac) * pointer.y + translation.y * scaleFrac;
54110
54111        this.body.view.scale = scale;
54112        this.body.view.translation = { x: tx, y: ty };
54113
54114        if (preScaleDragPointer != undefined) {
54115          var postScaleDragPointer = this.canvas.canvasToDOM(preScaleDragPointer);
54116          this.drag.pointer.x = postScaleDragPointer.x;
54117          this.drag.pointer.y = postScaleDragPointer.y;
54118        }
54119
54120        this.body.emitter.emit('_requestRedraw');
54121
54122        if (scaleOld < scale) {
54123          this.body.emitter.emit('zoom', { direction: '+', scale: this.body.view.scale, pointer: pointer });
54124        } else {
54125          this.body.emitter.emit('zoom', { direction: '-', scale: this.body.view.scale, pointer: pointer });
54126        }
54127      }
54128    }
54129
54130    /**
54131     * Event handler for mouse wheel event, used to zoom the timeline
54132     * See http://adomas.org/javascript-mouse-wheel/
54133     *     https://github.com/EightMedia/hammer.js/issues/256
54134     * @param {MouseEvent}  event
54135     * @private
54136     */
54137
54138  }, {
54139    key: 'onMouseWheel',
54140    value: function onMouseWheel(event) {
54141      if (this.options.zoomView === true) {
54142        // retrieve delta
54143        var delta = 0;
54144        if (event.wheelDelta) {
54145          /* IE/Opera. */
54146          delta = event.wheelDelta / 120;
54147        } else if (event.detail) {
54148          /* Mozilla case. */
54149          // In Mozilla, sign of delta is different than in IE.
54150          // Also, delta is multiple of 3.
54151          delta = -event.detail / 3;
54152        }
54153
54154        // If delta is nonzero, handle it.
54155        // Basically, delta is now positive if wheel was scrolled up,
54156        // and negative, if wheel was scrolled down.
54157        if (delta !== 0) {
54158
54159          // calculate the new scale
54160          var scale = this.body.view.scale;
54161          var zoom = delta / 10;
54162          if (delta < 0) {
54163            zoom = zoom / (1 - zoom);
54164          }
54165          scale *= 1 + zoom;
54166
54167          // calculate the pointer location
54168          var pointer = this.getPointer({ x: event.clientX, y: event.clientY });
54169
54170          // apply the new scale
54171          this.zoom(scale, pointer);
54172        }
54173
54174        // Prevent default actions caused by mouse wheel.
54175        event.preventDefault();
54176      }
54177    }
54178
54179    /**
54180     * Mouse move handler for checking whether the title moves over a node with a title.
54181     * @param  {Event} event
54182     * @private
54183     */
54184
54185  }, {
54186    key: 'onMouseMove',
54187    value: function onMouseMove(event) {
54188      var _this3 = this;
54189
54190      var pointer = this.getPointer({ x: event.clientX, y: event.clientY });
54191      var popupVisible = false;
54192
54193      // check if the previously selected node is still selected
54194      if (this.popup !== undefined) {
54195        if (this.popup.hidden === false) {
54196          this._checkHidePopup(pointer);
54197        }
54198
54199        // if the popup was not hidden above
54200        if (this.popup.hidden === false) {
54201          popupVisible = true;
54202          this.popup.setPosition(pointer.x + 3, pointer.y - 5);
54203          this.popup.show();
54204        }
54205      }
54206
54207      // if we bind the keyboard to the div, we have to highlight it to use it. This highlights it on mouse over.
54208      if (this.options.keyboard.bindToWindow === false && this.options.keyboard.enabled === true) {
54209        this.canvas.frame.focus();
54210      }
54211
54212      // start a timeout that will check if the mouse is positioned above an element
54213      if (popupVisible === false) {
54214        if (this.popupTimer !== undefined) {
54215          clearInterval(this.popupTimer); // stop any running calculationTimer
54216          this.popupTimer = undefined;
54217        }
54218        if (!this.drag.dragging) {
54219          this.popupTimer = setTimeout(function () {
54220            return _this3._checkShowPopup(pointer);
54221          }, this.options.tooltipDelay);
54222        }
54223      }
54224
54225      // adding hover highlights
54226      if (this.options.hover === true) {
54227        this.selectionHandler.hoverObject(event, pointer);
54228      }
54229    }
54230
54231    /**
54232     * Check if there is an element on the given position in the network
54233     * (a node or edge). If so, and if this element has a title,
54234     * show a popup window with its title.
54235     *
54236     * @param {{x:number, y:number}} pointer
54237     * @private
54238     */
54239
54240  }, {
54241    key: '_checkShowPopup',
54242    value: function _checkShowPopup(pointer) {
54243      var x = this.canvas._XconvertDOMtoCanvas(pointer.x);
54244      var y = this.canvas._YconvertDOMtoCanvas(pointer.y);
54245      var pointerObj = {
54246        left: x,
54247        top: y,
54248        right: x,
54249        bottom: y
54250      };
54251
54252      var previousPopupObjId = this.popupObj === undefined ? undefined : this.popupObj.id;
54253      var nodeUnderCursor = false;
54254      var popupType = 'node';
54255
54256      // check if a node is under the cursor.
54257      if (this.popupObj === undefined) {
54258        // search the nodes for overlap, select the top one in case of multiple nodes
54259        var nodeIndices = this.body.nodeIndices;
54260        var nodes = this.body.nodes;
54261        var node = void 0;
54262        var overlappingNodes = [];
54263        for (var i = 0; i < nodeIndices.length; i++) {
54264          node = nodes[nodeIndices[i]];
54265          if (node.isOverlappingWith(pointerObj) === true) {
54266            if (node.getTitle() !== undefined) {
54267              overlappingNodes.push(nodeIndices[i]);
54268            }
54269          }
54270        }
54271
54272        if (overlappingNodes.length > 0) {
54273          // if there are overlapping nodes, select the last one, this is the one which is drawn on top of the others
54274          this.popupObj = nodes[overlappingNodes[overlappingNodes.length - 1]];
54275          // if you hover over a node, the title of the edge is not supposed to be shown.
54276          nodeUnderCursor = true;
54277        }
54278      }
54279
54280      if (this.popupObj === undefined && nodeUnderCursor === false) {
54281        // search the edges for overlap
54282        var edgeIndices = this.body.edgeIndices;
54283        var edges = this.body.edges;
54284        var edge = void 0;
54285        var overlappingEdges = [];
54286        for (var _i = 0; _i < edgeIndices.length; _i++) {
54287          edge = edges[edgeIndices[_i]];
54288          if (edge.isOverlappingWith(pointerObj) === true) {
54289            if (edge.connected === true && edge.getTitle() !== undefined) {
54290              overlappingEdges.push(edgeIndices[_i]);
54291            }
54292          }
54293        }
54294
54295        if (overlappingEdges.length > 0) {
54296          this.popupObj = edges[overlappingEdges[overlappingEdges.length - 1]];
54297          popupType = 'edge';
54298        }
54299      }
54300
54301      if (this.popupObj !== undefined) {
54302        // show popup message window
54303        if (this.popupObj.id !== previousPopupObjId) {
54304          if (this.popup === undefined) {
54305            this.popup = new Popup(this.canvas.frame);
54306          }
54307
54308          this.popup.popupTargetType = popupType;
54309          this.popup.popupTargetId = this.popupObj.id;
54310
54311          // adjust a small offset such that the mouse cursor is located in the
54312          // bottom left location of the popup, and you can easily move over the
54313          // popup area
54314          this.popup.setPosition(pointer.x + 3, pointer.y - 5);
54315          this.popup.setText(this.popupObj.getTitle());
54316          this.popup.show();
54317          this.body.emitter.emit('showPopup', this.popupObj.id);
54318        }
54319      } else {
54320        if (this.popup !== undefined) {
54321          this.popup.hide();
54322          this.body.emitter.emit('hidePopup');
54323        }
54324      }
54325    }
54326
54327    /**
54328     * Check if the popup must be hidden, which is the case when the mouse is no
54329     * longer hovering on the object
54330     * @param {{x:number, y:number}} pointer
54331     * @private
54332     */
54333
54334  }, {
54335    key: '_checkHidePopup',
54336    value: function _checkHidePopup(pointer) {
54337      var pointerObj = this.selectionHandler._pointerToPositionObject(pointer);
54338
54339      var stillOnObj = false;
54340      if (this.popup.popupTargetType === 'node') {
54341        if (this.body.nodes[this.popup.popupTargetId] !== undefined) {
54342          stillOnObj = this.body.nodes[this.popup.popupTargetId].isOverlappingWith(pointerObj);
54343
54344          // if the mouse is still one the node, we have to check if it is not also on one that is drawn on top of it.
54345          // we initially only check stillOnObj because this is much faster.
54346          if (stillOnObj === true) {
54347            var overNode = this.selectionHandler.getNodeAt(pointer);
54348            stillOnObj = overNode === undefined ? false : overNode.id === this.popup.popupTargetId;
54349          }
54350        }
54351      } else {
54352        if (this.selectionHandler.getNodeAt(pointer) === undefined) {
54353          if (this.body.edges[this.popup.popupTargetId] !== undefined) {
54354            stillOnObj = this.body.edges[this.popup.popupTargetId].isOverlappingWith(pointerObj);
54355          }
54356        }
54357      }
54358
54359      if (stillOnObj === false) {
54360        this.popupObj = undefined;
54361        this.popup.hide();
54362        this.body.emitter.emit('hidePopup');
54363      }
54364    }
54365  }]);
54366  return InteractionHandler;
54367}();
54368
54369exports['default'] = InteractionHandler;
54370
54371/***/ }),
54372/* 233 */
54373/***/ (function(module, exports, __webpack_require__) {
54374
54375"use strict";
54376
54377
54378Object.defineProperty(exports, "__esModule", {
54379  value: true
54380});
54381
54382var _classCallCheck2 = __webpack_require__(0);
54383
54384var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
54385
54386var _createClass2 = __webpack_require__(1);
54387
54388var _createClass3 = _interopRequireDefault(_createClass2);
54389
54390function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
54391
54392var Hammer = __webpack_require__(10);
54393var hammerUtil = __webpack_require__(37);
54394var keycharm = __webpack_require__(35);
54395
54396/**
54397 * Navigation Handler
54398 */
54399
54400var NavigationHandler = function () {
54401  /**
54402   * @param {Object} body
54403   * @param {Canvas} canvas
54404   */
54405  function NavigationHandler(body, canvas) {
54406    var _this = this;
54407
54408    (0, _classCallCheck3['default'])(this, NavigationHandler);
54409
54410    this.body = body;
54411    this.canvas = canvas;
54412
54413    this.iconsCreated = false;
54414    this.navigationHammers = [];
54415    this.boundFunctions = {};
54416    this.touchTime = 0;
54417    this.activated = false;
54418
54419    this.body.emitter.on("activate", function () {
54420      _this.activated = true;_this.configureKeyboardBindings();
54421    });
54422    this.body.emitter.on("deactivate", function () {
54423      _this.activated = false;_this.configureKeyboardBindings();
54424    });
54425    this.body.emitter.on("destroy", function () {
54426      if (_this.keycharm !== undefined) {
54427        _this.keycharm.destroy();
54428      }
54429    });
54430
54431    this.options = {};
54432  }
54433
54434  /**
54435   *
54436   * @param {Object} options
54437   */
54438
54439
54440  (0, _createClass3['default'])(NavigationHandler, [{
54441    key: 'setOptions',
54442    value: function setOptions(options) {
54443      if (options !== undefined) {
54444        this.options = options;
54445        this.create();
54446      }
54447    }
54448
54449    /**
54450     * Creates or refreshes navigation and sets key bindings
54451     */
54452
54453  }, {
54454    key: 'create',
54455    value: function create() {
54456      if (this.options.navigationButtons === true) {
54457        if (this.iconsCreated === false) {
54458          this.loadNavigationElements();
54459        }
54460      } else if (this.iconsCreated === true) {
54461        this.cleanNavigation();
54462      }
54463
54464      this.configureKeyboardBindings();
54465    }
54466
54467    /**
54468     * Cleans up previous navigation items
54469     */
54470
54471  }, {
54472    key: 'cleanNavigation',
54473    value: function cleanNavigation() {
54474      // clean hammer bindings
54475      if (this.navigationHammers.length != 0) {
54476        for (var i = 0; i < this.navigationHammers.length; i++) {
54477          this.navigationHammers[i].destroy();
54478        }
54479        this.navigationHammers = [];
54480      }
54481
54482      // clean up previous navigation items
54483      if (this.navigationDOM && this.navigationDOM['wrapper'] && this.navigationDOM['wrapper'].parentNode) {
54484        this.navigationDOM['wrapper'].parentNode.removeChild(this.navigationDOM['wrapper']);
54485      }
54486
54487      this.iconsCreated = false;
54488    }
54489
54490    /**
54491     * Creation of the navigation controls nodes. They are drawn over the rest of the nodes and are not affected by scale and translation
54492     * they have a triggerFunction which is called on click. If the position of the navigation controls is dependent
54493     * on this.frame.canvas.clientWidth or this.frame.canvas.clientHeight, we flag horizontalAlignLeft and verticalAlignTop false.
54494     * This means that the location will be corrected by the _relocateNavigation function on a size change of the canvas.
54495     *
54496     * @private
54497     */
54498
54499  }, {
54500    key: 'loadNavigationElements',
54501    value: function loadNavigationElements() {
54502      var _this2 = this;
54503
54504      this.cleanNavigation();
54505
54506      this.navigationDOM = {};
54507      var navigationDivs = ['up', 'down', 'left', 'right', 'zoomIn', 'zoomOut', 'zoomExtends'];
54508      var navigationDivActions = ['_moveUp', '_moveDown', '_moveLeft', '_moveRight', '_zoomIn', '_zoomOut', '_fit'];
54509
54510      this.navigationDOM['wrapper'] = document.createElement('div');
54511      this.navigationDOM['wrapper'].className = 'vis-navigation';
54512      this.canvas.frame.appendChild(this.navigationDOM['wrapper']);
54513
54514      for (var i = 0; i < navigationDivs.length; i++) {
54515        this.navigationDOM[navigationDivs[i]] = document.createElement('div');
54516        this.navigationDOM[navigationDivs[i]].className = 'vis-button vis-' + navigationDivs[i];
54517        this.navigationDOM['wrapper'].appendChild(this.navigationDOM[navigationDivs[i]]);
54518
54519        var hammer = new Hammer(this.navigationDOM[navigationDivs[i]]);
54520        if (navigationDivActions[i] === "_fit") {
54521          hammerUtil.onTouch(hammer, this._fit.bind(this));
54522        } else {
54523          hammerUtil.onTouch(hammer, this.bindToRedraw.bind(this, navigationDivActions[i]));
54524        }
54525
54526        this.navigationHammers.push(hammer);
54527      }
54528
54529      // use a hammer for the release so we do not require the one used in the rest of the network
54530      // the one the rest uses can be overloaded by the manipulation system.
54531      var hammerFrame = new Hammer(this.canvas.frame);
54532      hammerUtil.onRelease(hammerFrame, function () {
54533        _this2._stopMovement();
54534      });
54535      this.navigationHammers.push(hammerFrame);
54536
54537      this.iconsCreated = true;
54538    }
54539
54540    /**
54541     *
54542     * @param {string} action
54543     */
54544
54545  }, {
54546    key: 'bindToRedraw',
54547    value: function bindToRedraw(action) {
54548      if (this.boundFunctions[action] === undefined) {
54549        this.boundFunctions[action] = this[action].bind(this);
54550        this.body.emitter.on("initRedraw", this.boundFunctions[action]);
54551        this.body.emitter.emit("_startRendering");
54552      }
54553    }
54554
54555    /**
54556     *
54557     * @param {string} action
54558     */
54559
54560  }, {
54561    key: 'unbindFromRedraw',
54562    value: function unbindFromRedraw(action) {
54563      if (this.boundFunctions[action] !== undefined) {
54564        this.body.emitter.off("initRedraw", this.boundFunctions[action]);
54565        this.body.emitter.emit("_stopRendering");
54566        delete this.boundFunctions[action];
54567      }
54568    }
54569
54570    /**
54571     * this stops all movement induced by the navigation buttons
54572     *
54573     * @private
54574     */
54575
54576  }, {
54577    key: '_fit',
54578    value: function _fit() {
54579      if (new Date().valueOf() - this.touchTime > 700) {
54580        // TODO: fix ugly hack to avoid hammer's double fireing of event (because we use release?)
54581        this.body.emitter.emit("fit", { duration: 700 });
54582        this.touchTime = new Date().valueOf();
54583      }
54584    }
54585
54586    /**
54587     * this stops all movement induced by the navigation buttons
54588     *
54589     * @private
54590     */
54591
54592  }, {
54593    key: '_stopMovement',
54594    value: function _stopMovement() {
54595      for (var boundAction in this.boundFunctions) {
54596        if (this.boundFunctions.hasOwnProperty(boundAction)) {
54597          this.body.emitter.off("initRedraw", this.boundFunctions[boundAction]);
54598          this.body.emitter.emit("_stopRendering");
54599        }
54600      }
54601      this.boundFunctions = {};
54602    }
54603    /**
54604     *
54605     * @private
54606     */
54607
54608  }, {
54609    key: '_moveUp',
54610    value: function _moveUp() {
54611      this.body.view.translation.y += this.options.keyboard.speed.y;
54612    }
54613    /**
54614     *
54615     * @private
54616     */
54617
54618  }, {
54619    key: '_moveDown',
54620    value: function _moveDown() {
54621      this.body.view.translation.y -= this.options.keyboard.speed.y;
54622    }
54623    /**
54624     *
54625     * @private
54626     */
54627
54628  }, {
54629    key: '_moveLeft',
54630    value: function _moveLeft() {
54631      this.body.view.translation.x += this.options.keyboard.speed.x;
54632    }
54633    /**
54634     *
54635     * @private
54636     */
54637
54638  }, {
54639    key: '_moveRight',
54640    value: function _moveRight() {
54641      this.body.view.translation.x -= this.options.keyboard.speed.x;
54642    }
54643    /**
54644     *
54645     * @private
54646     */
54647
54648  }, {
54649    key: '_zoomIn',
54650    value: function _zoomIn() {
54651      var scaleOld = this.body.view.scale;
54652      var scale = this.body.view.scale * (1 + this.options.keyboard.speed.zoom);
54653      var translation = this.body.view.translation;
54654      var scaleFrac = scale / scaleOld;
54655      var tx = (1 - scaleFrac) * this.canvas.canvasViewCenter.x + translation.x * scaleFrac;
54656      var ty = (1 - scaleFrac) * this.canvas.canvasViewCenter.y + translation.y * scaleFrac;
54657
54658      this.body.view.scale = scale;
54659      this.body.view.translation = { x: tx, y: ty };
54660      this.body.emitter.emit('zoom', { direction: '+', scale: this.body.view.scale, pointer: null });
54661    }
54662
54663    /**
54664     *
54665     * @private
54666     */
54667
54668  }, {
54669    key: '_zoomOut',
54670    value: function _zoomOut() {
54671      var scaleOld = this.body.view.scale;
54672      var scale = this.body.view.scale / (1 + this.options.keyboard.speed.zoom);
54673      var translation = this.body.view.translation;
54674      var scaleFrac = scale / scaleOld;
54675      var tx = (1 - scaleFrac) * this.canvas.canvasViewCenter.x + translation.x * scaleFrac;
54676      var ty = (1 - scaleFrac) * this.canvas.canvasViewCenter.y + translation.y * scaleFrac;
54677
54678      this.body.view.scale = scale;
54679      this.body.view.translation = { x: tx, y: ty };
54680      this.body.emitter.emit('zoom', { direction: '-', scale: this.body.view.scale, pointer: null });
54681    }
54682
54683    /**
54684     * bind all keys using keycharm.
54685     */
54686
54687  }, {
54688    key: 'configureKeyboardBindings',
54689    value: function configureKeyboardBindings() {
54690      var _this3 = this;
54691
54692      if (this.keycharm !== undefined) {
54693        this.keycharm.destroy();
54694      }
54695
54696      if (this.options.keyboard.enabled === true) {
54697        if (this.options.keyboard.bindToWindow === true) {
54698          this.keycharm = keycharm({ container: window, preventDefault: true });
54699        } else {
54700          this.keycharm = keycharm({ container: this.canvas.frame, preventDefault: true });
54701        }
54702
54703        this.keycharm.reset();
54704
54705        if (this.activated === true) {
54706          this.keycharm.bind("up", function () {
54707            _this3.bindToRedraw("_moveUp");
54708          }, "keydown");
54709          this.keycharm.bind("down", function () {
54710            _this3.bindToRedraw("_moveDown");
54711          }, "keydown");
54712          this.keycharm.bind("left", function () {
54713            _this3.bindToRedraw("_moveLeft");
54714          }, "keydown");
54715          this.keycharm.bind("right", function () {
54716            _this3.bindToRedraw("_moveRight");
54717          }, "keydown");
54718          this.keycharm.bind("=", function () {
54719            _this3.bindToRedraw("_zoomIn");
54720          }, "keydown");
54721          this.keycharm.bind("num+", function () {
54722            _this3.bindToRedraw("_zoomIn");
54723          }, "keydown");
54724          this.keycharm.bind("num-", function () {
54725            _this3.bindToRedraw("_zoomOut");
54726          }, "keydown");
54727          this.keycharm.bind("-", function () {
54728            _this3.bindToRedraw("_zoomOut");
54729          }, "keydown");
54730          this.keycharm.bind("[", function () {
54731            _this3.bindToRedraw("_zoomOut");
54732          }, "keydown");
54733          this.keycharm.bind("]", function () {
54734            _this3.bindToRedraw("_zoomIn");
54735          }, "keydown");
54736          this.keycharm.bind("pageup", function () {
54737            _this3.bindToRedraw("_zoomIn");
54738          }, "keydown");
54739          this.keycharm.bind("pagedown", function () {
54740            _this3.bindToRedraw("_zoomOut");
54741          }, "keydown");
54742
54743          this.keycharm.bind("up", function () {
54744            _this3.unbindFromRedraw("_moveUp");
54745          }, "keyup");
54746          this.keycharm.bind("down", function () {
54747            _this3.unbindFromRedraw("_moveDown");
54748          }, "keyup");
54749          this.keycharm.bind("left", function () {
54750            _this3.unbindFromRedraw("_moveLeft");
54751          }, "keyup");
54752          this.keycharm.bind("right", function () {
54753            _this3.unbindFromRedraw("_moveRight");
54754          }, "keyup");
54755          this.keycharm.bind("=", function () {
54756            _this3.unbindFromRedraw("_zoomIn");
54757          }, "keyup");
54758          this.keycharm.bind("num+", function () {
54759            _this3.unbindFromRedraw("_zoomIn");
54760          }, "keyup");
54761          this.keycharm.bind("num-", function () {
54762            _this3.unbindFromRedraw("_zoomOut");
54763          }, "keyup");
54764          this.keycharm.bind("-", function () {
54765            _this3.unbindFromRedraw("_zoomOut");
54766          }, "keyup");
54767          this.keycharm.bind("[", function () {
54768            _this3.unbindFromRedraw("_zoomOut");
54769          }, "keyup");
54770          this.keycharm.bind("]", function () {
54771            _this3.unbindFromRedraw("_zoomIn");
54772          }, "keyup");
54773          this.keycharm.bind("pageup", function () {
54774            _this3.unbindFromRedraw("_zoomIn");
54775          }, "keyup");
54776          this.keycharm.bind("pagedown", function () {
54777            _this3.unbindFromRedraw("_zoomOut");
54778          }, "keyup");
54779        }
54780      }
54781    }
54782  }]);
54783  return NavigationHandler;
54784}();
54785
54786exports['default'] = NavigationHandler;
54787
54788/***/ }),
54789/* 234 */
54790/***/ (function(module, exports, __webpack_require__) {
54791
54792"use strict";
54793
54794
54795Object.defineProperty(exports, "__esModule", {
54796  value: true
54797});
54798
54799var _classCallCheck2 = __webpack_require__(0);
54800
54801var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
54802
54803var _createClass2 = __webpack_require__(1);
54804
54805var _createClass3 = _interopRequireDefault(_createClass2);
54806
54807function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
54808
54809var Node = __webpack_require__(47)['default'];
54810var Edge = __webpack_require__(74)['default'];
54811
54812var util = __webpack_require__(2);
54813
54814/**
54815 * The handler for selections
54816 */
54817
54818var SelectionHandler = function () {
54819  /**
54820   * @param {Object} body
54821   * @param {Canvas} canvas
54822   */
54823  function SelectionHandler(body, canvas) {
54824    var _this = this;
54825
54826    (0, _classCallCheck3['default'])(this, SelectionHandler);
54827
54828    this.body = body;
54829    this.canvas = canvas;
54830    this.selectionObj = { nodes: [], edges: [] };
54831    this.hoverObj = { nodes: {}, edges: {} };
54832
54833    this.options = {};
54834    this.defaultOptions = {
54835      multiselect: false,
54836      selectable: true,
54837      selectConnectedEdges: true,
54838      hoverConnectedEdges: true
54839    };
54840    util.extend(this.options, this.defaultOptions);
54841
54842    this.body.emitter.on("_dataChanged", function () {
54843      _this.updateSelection();
54844    });
54845  }
54846
54847  /**
54848   *
54849   * @param {Object} [options]
54850   */
54851
54852
54853  (0, _createClass3['default'])(SelectionHandler, [{
54854    key: 'setOptions',
54855    value: function setOptions(options) {
54856      if (options !== undefined) {
54857        var fields = ['multiselect', 'hoverConnectedEdges', 'selectable', 'selectConnectedEdges'];
54858        util.selectiveDeepExtend(fields, this.options, options);
54859      }
54860    }
54861
54862    /**
54863     * handles the selection part of the tap;
54864     *
54865     * @param {{x: number, y: number}} pointer
54866     * @returns {boolean}
54867     */
54868
54869  }, {
54870    key: 'selectOnPoint',
54871    value: function selectOnPoint(pointer) {
54872      var selected = false;
54873      if (this.options.selectable === true) {
54874        var obj = this.getNodeAt(pointer) || this.getEdgeAt(pointer);
54875
54876        // unselect after getting the objects in order to restore width and height.
54877        this.unselectAll();
54878
54879        if (obj !== undefined) {
54880          selected = this.selectObject(obj);
54881        }
54882        this.body.emitter.emit("_requestRedraw");
54883      }
54884      return selected;
54885    }
54886
54887    /**
54888     *
54889     * @param {{x: number, y: number}} pointer
54890     * @returns {boolean}
54891     */
54892
54893  }, {
54894    key: 'selectAdditionalOnPoint',
54895    value: function selectAdditionalOnPoint(pointer) {
54896      var selectionChanged = false;
54897      if (this.options.selectable === true) {
54898        var obj = this.getNodeAt(pointer) || this.getEdgeAt(pointer);
54899
54900        if (obj !== undefined) {
54901          selectionChanged = true;
54902          if (obj.isSelected() === true) {
54903            this.deselectObject(obj);
54904          } else {
54905            this.selectObject(obj);
54906          }
54907
54908          this.body.emitter.emit("_requestRedraw");
54909        }
54910      }
54911      return selectionChanged;
54912    }
54913
54914    /**
54915     * Create an object containing the standard fields for an event.
54916     *
54917     * @param {Event} event
54918     * @param {{x: number, y: number}} pointer Object with the x and y screen coordinates of the mouse
54919     * @returns {{}}
54920     * @private
54921     */
54922
54923  }, {
54924    key: '_initBaseEvent',
54925    value: function _initBaseEvent(event, pointer) {
54926      var properties = {};
54927
54928      properties['pointer'] = {
54929        DOM: { x: pointer.x, y: pointer.y },
54930        canvas: this.canvas.DOMtoCanvas(pointer)
54931      };
54932      properties['event'] = event;
54933
54934      return properties;
54935    }
54936
54937    /**
54938     * Generate an event which the user can catch.
54939     *
54940     * This adds some extra data to the event with respect to cursor position and
54941     * selected nodes and edges.
54942     *
54943     * @param {string} eventType                          Name of event to send
54944     * @param {Event}  event
54945     * @param {{x: number, y: number}} pointer            Object with the x and y screen coordinates of the mouse
54946     * @param {Object|undefined} oldSelection             If present, selection state before event occured
54947     * @param {boolean|undefined} [emptySelection=false]  Indicate if selection data should be passed
54948     */
54949
54950  }, {
54951    key: '_generateClickEvent',
54952    value: function _generateClickEvent(eventType, event, pointer, oldSelection) {
54953      var emptySelection = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : false;
54954
54955      var properties = this._initBaseEvent(event, pointer);
54956
54957      if (emptySelection === true) {
54958        properties.nodes = [];
54959        properties.edges = [];
54960      } else {
54961        var tmp = this.getSelection();
54962        properties.nodes = tmp.nodes;
54963        properties.edges = tmp.edges;
54964      }
54965
54966      if (oldSelection !== undefined) {
54967        properties['previousSelection'] = oldSelection;
54968      }
54969
54970      if (eventType == 'click') {
54971        // For the time being, restrict this functionality to
54972        // just the click event.
54973        properties.items = this.getClickedItems(pointer);
54974      }
54975
54976      this.body.emitter.emit(eventType, properties);
54977    }
54978
54979    /**
54980     *
54981     * @param {Object} obj
54982     * @param {boolean} [highlightEdges=this.options.selectConnectedEdges]
54983     * @returns {boolean}
54984     */
54985
54986  }, {
54987    key: 'selectObject',
54988    value: function selectObject(obj) {
54989      var highlightEdges = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : this.options.selectConnectedEdges;
54990
54991      if (obj !== undefined) {
54992        if (obj instanceof Node) {
54993          if (highlightEdges === true) {
54994            this._selectConnectedEdges(obj);
54995          }
54996        }
54997        obj.select();
54998        this._addToSelection(obj);
54999        return true;
55000      }
55001      return false;
55002    }
55003
55004    /**
55005     *
55006     * @param {Object} obj
55007     */
55008
55009  }, {
55010    key: 'deselectObject',
55011    value: function deselectObject(obj) {
55012      if (obj.isSelected() === true) {
55013        obj.selected = false;
55014        this._removeFromSelection(obj);
55015      }
55016    }
55017
55018    /**
55019     * retrieve all nodes overlapping with given object
55020     * @param {Object} object  An object with parameters left, top, right, bottom
55021     * @return {number[]}   An array with id's of the overlapping nodes
55022     * @private
55023     */
55024
55025  }, {
55026    key: '_getAllNodesOverlappingWith',
55027    value: function _getAllNodesOverlappingWith(object) {
55028      var overlappingNodes = [];
55029      var nodes = this.body.nodes;
55030      for (var i = 0; i < this.body.nodeIndices.length; i++) {
55031        var nodeId = this.body.nodeIndices[i];
55032        if (nodes[nodeId].isOverlappingWith(object)) {
55033          overlappingNodes.push(nodeId);
55034        }
55035      }
55036      return overlappingNodes;
55037    }
55038
55039    /**
55040     * Return a position object in canvasspace from a single point in screenspace
55041     *
55042     * @param {{x: number, y: number}} pointer
55043     * @returns {{left: number, top: number, right: number, bottom: number}}
55044     * @private
55045     */
55046
55047  }, {
55048    key: '_pointerToPositionObject',
55049    value: function _pointerToPositionObject(pointer) {
55050      var canvasPos = this.canvas.DOMtoCanvas(pointer);
55051      return {
55052        left: canvasPos.x - 1,
55053        top: canvasPos.y + 1,
55054        right: canvasPos.x + 1,
55055        bottom: canvasPos.y - 1
55056      };
55057    }
55058
55059    /**
55060     * Get the top node at the passed point (like a click)
55061     *
55062     * @param {{x: number, y: number}} pointer
55063     * @param {boolean} [returnNode=true]
55064     * @return {Node | undefined} node
55065     */
55066
55067  }, {
55068    key: 'getNodeAt',
55069    value: function getNodeAt(pointer) {
55070      var returnNode = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
55071
55072      // we first check if this is an navigation controls element
55073      var positionObject = this._pointerToPositionObject(pointer);
55074      var overlappingNodes = this._getAllNodesOverlappingWith(positionObject);
55075      // if there are overlapping nodes, select the last one, this is the
55076      // one which is drawn on top of the others
55077      if (overlappingNodes.length > 0) {
55078        if (returnNode === true) {
55079          return this.body.nodes[overlappingNodes[overlappingNodes.length - 1]];
55080        } else {
55081          return overlappingNodes[overlappingNodes.length - 1];
55082        }
55083      } else {
55084        return undefined;
55085      }
55086    }
55087
55088    /**
55089     * retrieve all edges overlapping with given object, selector is around center
55090     * @param {Object} object  An object with parameters left, top, right, bottom
55091     * @param {number[]} overlappingEdges An array with id's of the overlapping nodes
55092     * @private
55093     */
55094
55095  }, {
55096    key: '_getEdgesOverlappingWith',
55097    value: function _getEdgesOverlappingWith(object, overlappingEdges) {
55098      var edges = this.body.edges;
55099      for (var i = 0; i < this.body.edgeIndices.length; i++) {
55100        var edgeId = this.body.edgeIndices[i];
55101        if (edges[edgeId].isOverlappingWith(object)) {
55102          overlappingEdges.push(edgeId);
55103        }
55104      }
55105    }
55106
55107    /**
55108     * retrieve all nodes overlapping with given object
55109     * @param {Object} object  An object with parameters left, top, right, bottom
55110     * @return {number[]}   An array with id's of the overlapping nodes
55111     * @private
55112     */
55113
55114  }, {
55115    key: '_getAllEdgesOverlappingWith',
55116    value: function _getAllEdgesOverlappingWith(object) {
55117      var overlappingEdges = [];
55118      this._getEdgesOverlappingWith(object, overlappingEdges);
55119      return overlappingEdges;
55120    }
55121
55122    /**
55123     * Get the edges nearest to the passed point (like a click)
55124     *
55125     * @param {{x: number, y: number}} pointer
55126     * @param {boolean} [returnEdge=true]
55127     * @return {Edge | undefined} node
55128     */
55129
55130  }, {
55131    key: 'getEdgeAt',
55132    value: function getEdgeAt(pointer) {
55133      var returnEdge = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
55134
55135      // Iterate over edges, pick closest within 10
55136      var canvasPos = this.canvas.DOMtoCanvas(pointer);
55137      var mindist = 10;
55138      var overlappingEdge = null;
55139      var edges = this.body.edges;
55140      for (var i = 0; i < this.body.edgeIndices.length; i++) {
55141        var edgeId = this.body.edgeIndices[i];
55142        var edge = edges[edgeId];
55143        if (edge.connected) {
55144          var xFrom = edge.from.x;
55145          var yFrom = edge.from.y;
55146          var xTo = edge.to.x;
55147          var yTo = edge.to.y;
55148          var dist = edge.edgeType.getDistanceToEdge(xFrom, yFrom, xTo, yTo, canvasPos.x, canvasPos.y);
55149          if (dist < mindist) {
55150            overlappingEdge = edgeId;
55151            mindist = dist;
55152          }
55153        }
55154      }
55155      if (overlappingEdge !== null) {
55156        if (returnEdge === true) {
55157          return this.body.edges[overlappingEdge];
55158        } else {
55159          return overlappingEdge;
55160        }
55161      } else {
55162        return undefined;
55163      }
55164    }
55165
55166    /**
55167     * Add object to the selection array.
55168     *
55169     * @param {Object} obj
55170     * @private
55171     */
55172
55173  }, {
55174    key: '_addToSelection',
55175    value: function _addToSelection(obj) {
55176      if (obj instanceof Node) {
55177        this.selectionObj.nodes[obj.id] = obj;
55178      } else {
55179        this.selectionObj.edges[obj.id] = obj;
55180      }
55181    }
55182
55183    /**
55184     * Add object to the selection array.
55185     *
55186     * @param {Object} obj
55187     * @private
55188     */
55189
55190  }, {
55191    key: '_addToHover',
55192    value: function _addToHover(obj) {
55193      if (obj instanceof Node) {
55194        this.hoverObj.nodes[obj.id] = obj;
55195      } else {
55196        this.hoverObj.edges[obj.id] = obj;
55197      }
55198    }
55199
55200    /**
55201     * Remove a single option from selection.
55202     *
55203     * @param {Object} obj
55204     * @private
55205     */
55206
55207  }, {
55208    key: '_removeFromSelection',
55209    value: function _removeFromSelection(obj) {
55210      if (obj instanceof Node) {
55211        delete this.selectionObj.nodes[obj.id];
55212        this._unselectConnectedEdges(obj);
55213      } else {
55214        delete this.selectionObj.edges[obj.id];
55215      }
55216    }
55217
55218    /**
55219     * Unselect all. The selectionObj is useful for this.
55220     */
55221
55222  }, {
55223    key: 'unselectAll',
55224    value: function unselectAll() {
55225      for (var nodeId in this.selectionObj.nodes) {
55226        if (this.selectionObj.nodes.hasOwnProperty(nodeId)) {
55227          this.selectionObj.nodes[nodeId].unselect();
55228        }
55229      }
55230      for (var edgeId in this.selectionObj.edges) {
55231        if (this.selectionObj.edges.hasOwnProperty(edgeId)) {
55232          this.selectionObj.edges[edgeId].unselect();
55233        }
55234      }
55235
55236      this.selectionObj = { nodes: {}, edges: {} };
55237    }
55238
55239    /**
55240     * return the number of selected nodes
55241     *
55242     * @returns {number}
55243     * @private
55244     */
55245
55246  }, {
55247    key: '_getSelectedNodeCount',
55248    value: function _getSelectedNodeCount() {
55249      var count = 0;
55250      for (var nodeId in this.selectionObj.nodes) {
55251        if (this.selectionObj.nodes.hasOwnProperty(nodeId)) {
55252          count += 1;
55253        }
55254      }
55255      return count;
55256    }
55257
55258    /**
55259     * return the selected node
55260     *
55261     * @returns {number}
55262     * @private
55263     */
55264
55265  }, {
55266    key: '_getSelectedNode',
55267    value: function _getSelectedNode() {
55268      for (var nodeId in this.selectionObj.nodes) {
55269        if (this.selectionObj.nodes.hasOwnProperty(nodeId)) {
55270          return this.selectionObj.nodes[nodeId];
55271        }
55272      }
55273      return undefined;
55274    }
55275
55276    /**
55277     * return the selected edge
55278     *
55279     * @returns {number}
55280     * @private
55281     */
55282
55283  }, {
55284    key: '_getSelectedEdge',
55285    value: function _getSelectedEdge() {
55286      for (var edgeId in this.selectionObj.edges) {
55287        if (this.selectionObj.edges.hasOwnProperty(edgeId)) {
55288          return this.selectionObj.edges[edgeId];
55289        }
55290      }
55291      return undefined;
55292    }
55293
55294    /**
55295     * return the number of selected edges
55296     *
55297     * @returns {number}
55298     * @private
55299     */
55300
55301  }, {
55302    key: '_getSelectedEdgeCount',
55303    value: function _getSelectedEdgeCount() {
55304      var count = 0;
55305      for (var edgeId in this.selectionObj.edges) {
55306        if (this.selectionObj.edges.hasOwnProperty(edgeId)) {
55307          count += 1;
55308        }
55309      }
55310      return count;
55311    }
55312
55313    /**
55314     * return the number of selected objects.
55315     *
55316     * @returns {number}
55317     * @private
55318     */
55319
55320  }, {
55321    key: '_getSelectedObjectCount',
55322    value: function _getSelectedObjectCount() {
55323      var count = 0;
55324      for (var nodeId in this.selectionObj.nodes) {
55325        if (this.selectionObj.nodes.hasOwnProperty(nodeId)) {
55326          count += 1;
55327        }
55328      }
55329      for (var edgeId in this.selectionObj.edges) {
55330        if (this.selectionObj.edges.hasOwnProperty(edgeId)) {
55331          count += 1;
55332        }
55333      }
55334      return count;
55335    }
55336
55337    /**
55338     * Check if anything is selected
55339     *
55340     * @returns {boolean}
55341     * @private
55342     */
55343
55344  }, {
55345    key: '_selectionIsEmpty',
55346    value: function _selectionIsEmpty() {
55347      for (var nodeId in this.selectionObj.nodes) {
55348        if (this.selectionObj.nodes.hasOwnProperty(nodeId)) {
55349          return false;
55350        }
55351      }
55352      for (var edgeId in this.selectionObj.edges) {
55353        if (this.selectionObj.edges.hasOwnProperty(edgeId)) {
55354          return false;
55355        }
55356      }
55357      return true;
55358    }
55359
55360    /**
55361     * check if one of the selected nodes is a cluster.
55362     *
55363     * @returns {boolean}
55364     * @private
55365     */
55366
55367  }, {
55368    key: '_clusterInSelection',
55369    value: function _clusterInSelection() {
55370      for (var nodeId in this.selectionObj.nodes) {
55371        if (this.selectionObj.nodes.hasOwnProperty(nodeId)) {
55372          if (this.selectionObj.nodes[nodeId].clusterSize > 1) {
55373            return true;
55374          }
55375        }
55376      }
55377      return false;
55378    }
55379
55380    /**
55381     * select the edges connected to the node that is being selected
55382     *
55383     * @param {Node} node
55384     * @private
55385     */
55386
55387  }, {
55388    key: '_selectConnectedEdges',
55389    value: function _selectConnectedEdges(node) {
55390      for (var i = 0; i < node.edges.length; i++) {
55391        var edge = node.edges[i];
55392        edge.select();
55393        this._addToSelection(edge);
55394      }
55395    }
55396
55397    /**
55398     * select the edges connected to the node that is being selected
55399     *
55400     * @param {Node} node
55401     * @private
55402     */
55403
55404  }, {
55405    key: '_hoverConnectedEdges',
55406    value: function _hoverConnectedEdges(node) {
55407      for (var i = 0; i < node.edges.length; i++) {
55408        var edge = node.edges[i];
55409        edge.hover = true;
55410        this._addToHover(edge);
55411      }
55412    }
55413
55414    /**
55415     * unselect the edges connected to the node that is being selected
55416     *
55417     * @param {Node} node
55418     * @private
55419     */
55420
55421  }, {
55422    key: '_unselectConnectedEdges',
55423    value: function _unselectConnectedEdges(node) {
55424      for (var i = 0; i < node.edges.length; i++) {
55425        var edge = node.edges[i];
55426        edge.unselect();
55427        this._removeFromSelection(edge);
55428      }
55429    }
55430
55431    /**
55432     * Remove the highlight from a node or edge, in response to mouse movement
55433     *
55434     * @param {Event}  event
55435     * @param {{x: number, y: number}} pointer object with the x and y screen coordinates of the mouse
55436     * @param {Node|vis.Edge} object
55437     * @private
55438     */
55439
55440  }, {
55441    key: 'emitBlurEvent',
55442    value: function emitBlurEvent(event, pointer, object) {
55443      var properties = this._initBaseEvent(event, pointer);
55444
55445      if (object.hover === true) {
55446        object.hover = false;
55447        if (object instanceof Node) {
55448          properties.node = object.id;
55449          this.body.emitter.emit("blurNode", properties);
55450        } else {
55451          properties.edge = object.id;
55452          this.body.emitter.emit("blurEdge", properties);
55453        }
55454      }
55455    }
55456
55457    /**
55458     * Create the highlight for a node or edge, in response to mouse movement
55459     *
55460     * @param {Event}  event
55461     * @param {{x: number, y: number}} pointer object with the x and y screen coordinates of the mouse
55462     * @param {Node|vis.Edge} object
55463     * @returns {boolean} hoverChanged
55464     * @private
55465     */
55466
55467  }, {
55468    key: 'emitHoverEvent',
55469    value: function emitHoverEvent(event, pointer, object) {
55470      var properties = this._initBaseEvent(event, pointer);
55471      var hoverChanged = false;
55472
55473      if (object.hover === false) {
55474        object.hover = true;
55475        this._addToHover(object);
55476        hoverChanged = true;
55477        if (object instanceof Node) {
55478          properties.node = object.id;
55479          this.body.emitter.emit("hoverNode", properties);
55480        } else {
55481          properties.edge = object.id;
55482          this.body.emitter.emit("hoverEdge", properties);
55483        }
55484      }
55485
55486      return hoverChanged;
55487    }
55488
55489    /**
55490     * Perform actions in response to a mouse movement.
55491     *
55492     * @param {Event}  event
55493     * @param {{x: number, y: number}} pointer | object with the x and y screen coordinates of the mouse
55494     */
55495
55496  }, {
55497    key: 'hoverObject',
55498    value: function hoverObject(event, pointer) {
55499      var object = this.getNodeAt(pointer);
55500      if (object === undefined) {
55501        object = this.getEdgeAt(pointer);
55502      }
55503
55504      var hoverChanged = false;
55505      // remove all node hover highlights
55506      for (var nodeId in this.hoverObj.nodes) {
55507        if (this.hoverObj.nodes.hasOwnProperty(nodeId)) {
55508          if (object === undefined || object instanceof Node && object.id != nodeId || object instanceof Edge) {
55509            this.emitBlurEvent(event, pointer, this.hoverObj.nodes[nodeId]);
55510            delete this.hoverObj.nodes[nodeId];
55511            hoverChanged = true;
55512          }
55513        }
55514      }
55515
55516      // removing all edge hover highlights
55517      for (var edgeId in this.hoverObj.edges) {
55518        if (this.hoverObj.edges.hasOwnProperty(edgeId)) {
55519          // if the hover has been changed here it means that the node has been hovered over or off
55520          // we then do not use the emitBlurEvent method here.
55521          if (hoverChanged === true) {
55522            this.hoverObj.edges[edgeId].hover = false;
55523            delete this.hoverObj.edges[edgeId];
55524          }
55525          // if the blur remains the same and the object is undefined (mouse off) or another
55526          // edge has been hovered, or another node has been hovered we blur the edge.
55527          else if (object === undefined || object instanceof Edge && object.id != edgeId || object instanceof Node && !object.hover) {
55528              this.emitBlurEvent(event, pointer, this.hoverObj.edges[edgeId]);
55529              delete this.hoverObj.edges[edgeId];
55530              hoverChanged = true;
55531            }
55532        }
55533      }
55534
55535      if (object !== undefined) {
55536        hoverChanged = hoverChanged || this.emitHoverEvent(event, pointer, object);
55537        if (object instanceof Node && this.options.hoverConnectedEdges === true) {
55538          this._hoverConnectedEdges(object);
55539        }
55540      }
55541
55542      if (hoverChanged === true) {
55543        this.body.emitter.emit('_requestRedraw');
55544      }
55545    }
55546
55547    /**
55548     *
55549     * retrieve the currently selected objects
55550     * @return {{nodes: Array.<string>, edges: Array.<string>}} selection
55551     */
55552
55553  }, {
55554    key: 'getSelection',
55555    value: function getSelection() {
55556      var nodeIds = this.getSelectedNodes();
55557      var edgeIds = this.getSelectedEdges();
55558      return { nodes: nodeIds, edges: edgeIds };
55559    }
55560
55561    /**
55562     *
55563     * retrieve the currently selected nodes
55564     * @return {string[]} selection    An array with the ids of the
55565     *                                            selected nodes.
55566     */
55567
55568  }, {
55569    key: 'getSelectedNodes',
55570    value: function getSelectedNodes() {
55571      var idArray = [];
55572      if (this.options.selectable === true) {
55573        for (var nodeId in this.selectionObj.nodes) {
55574          if (this.selectionObj.nodes.hasOwnProperty(nodeId)) {
55575            idArray.push(this.selectionObj.nodes[nodeId].id);
55576          }
55577        }
55578      }
55579      return idArray;
55580    }
55581
55582    /**
55583     *
55584     * retrieve the currently selected edges
55585     * @return {Array} selection    An array with the ids of the
55586     *                                            selected nodes.
55587     */
55588
55589  }, {
55590    key: 'getSelectedEdges',
55591    value: function getSelectedEdges() {
55592      var idArray = [];
55593      if (this.options.selectable === true) {
55594        for (var edgeId in this.selectionObj.edges) {
55595          if (this.selectionObj.edges.hasOwnProperty(edgeId)) {
55596            idArray.push(this.selectionObj.edges[edgeId].id);
55597          }
55598        }
55599      }
55600      return idArray;
55601    }
55602
55603    /**
55604     * Updates the current selection
55605     * @param {{nodes: Array.<string>, edges: Array.<string>}} selection
55606     * @param {Object} options                                 Options
55607     */
55608
55609  }, {
55610    key: 'setSelection',
55611    value: function setSelection(selection) {
55612      var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
55613
55614      var i = void 0,
55615          id = void 0;
55616
55617      if (!selection || !selection.nodes && !selection.edges) throw 'Selection must be an object with nodes and/or edges properties';
55618      // first unselect any selected node, if option is true or undefined
55619      if (options.unselectAll || options.unselectAll === undefined) {
55620        this.unselectAll();
55621      }
55622      if (selection.nodes) {
55623        for (i = 0; i < selection.nodes.length; i++) {
55624          id = selection.nodes[i];
55625
55626          var node = this.body.nodes[id];
55627          if (!node) {
55628            throw new RangeError('Node with id "' + id + '" not found');
55629          }
55630          // don't select edges with it
55631          this.selectObject(node, options.highlightEdges);
55632        }
55633      }
55634
55635      if (selection.edges) {
55636        for (i = 0; i < selection.edges.length; i++) {
55637          id = selection.edges[i];
55638
55639          var edge = this.body.edges[id];
55640          if (!edge) {
55641            throw new RangeError('Edge with id "' + id + '" not found');
55642          }
55643          this.selectObject(edge);
55644        }
55645      }
55646      this.body.emitter.emit('_requestRedraw');
55647    }
55648
55649    /**
55650     * select zero or more nodes with the option to highlight edges
55651     * @param {number[] | string[]} selection     An array with the ids of the
55652     *                                            selected nodes.
55653     * @param {boolean} [highlightEdges]
55654     */
55655
55656  }, {
55657    key: 'selectNodes',
55658    value: function selectNodes(selection) {
55659      var highlightEdges = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
55660
55661      if (!selection || selection.length === undefined) throw 'Selection must be an array with ids';
55662
55663      this.setSelection({ nodes: selection }, { highlightEdges: highlightEdges });
55664    }
55665
55666    /**
55667     * select zero or more edges
55668     * @param {number[] | string[]} selection     An array with the ids of the
55669     *                                            selected nodes.
55670     */
55671
55672  }, {
55673    key: 'selectEdges',
55674    value: function selectEdges(selection) {
55675      if (!selection || selection.length === undefined) throw 'Selection must be an array with ids';
55676
55677      this.setSelection({ edges: selection });
55678    }
55679
55680    /**
55681     * Validate the selection: remove ids of nodes which no longer exist
55682     * @private
55683     */
55684
55685  }, {
55686    key: 'updateSelection',
55687    value: function updateSelection() {
55688      for (var nodeId in this.selectionObj.nodes) {
55689        if (this.selectionObj.nodes.hasOwnProperty(nodeId)) {
55690          if (!this.body.nodes.hasOwnProperty(nodeId)) {
55691            delete this.selectionObj.nodes[nodeId];
55692          }
55693        }
55694      }
55695      for (var edgeId in this.selectionObj.edges) {
55696        if (this.selectionObj.edges.hasOwnProperty(edgeId)) {
55697          if (!this.body.edges.hasOwnProperty(edgeId)) {
55698            delete this.selectionObj.edges[edgeId];
55699          }
55700        }
55701      }
55702    }
55703
55704    /**
55705     * Determine all the visual elements clicked which are on the given point.
55706     *
55707     * All elements are returned; this includes nodes, edges and their labels.
55708     * The order returned is from highest to lowest, i.e. element 0 of the return
55709     * value is the topmost item clicked on.
55710     *
55711     * The return value consists of an array of the following possible elements:
55712     *
55713     * - `{nodeId:number}`             - node with given id clicked on
55714     * - `{nodeId:number, labelId:0}`  - label of node with given id clicked on
55715     * - `{edgeId:number}`             - edge with given id clicked on
55716     * - `{edge:number, labelId:0}`    - label of edge with given id clicked on
55717     *
55718     * ## NOTES
55719     *
55720     * - Currently, there is only one label associated with a node or an edge,
55721     *   but this is expected to change somewhere in the future.
55722     * - Since there is no z-indexing yet, it is not really possible to set the nodes and
55723     *   edges in the correct order. For the time being, nodes come first.
55724     *
55725     * @param {point} pointer  mouse position in screen coordinates
55726     * @returns {Array.<nodeClickItem|nodeLabelClickItem|edgeClickItem|edgeLabelClickItem>}
55727     * @private
55728     */
55729
55730  }, {
55731    key: 'getClickedItems',
55732    value: function getClickedItems(pointer) {
55733      var point = this.canvas.DOMtoCanvas(pointer);
55734      var items = [];
55735
55736      // Note reverse order; we want the topmost clicked items to be first in the array
55737      // Also note that selected nodes are disregarded here; these normally display on top
55738      var nodeIndices = this.body.nodeIndices;
55739      var nodes = this.body.nodes;
55740      for (var i = nodeIndices.length - 1; i >= 0; i--) {
55741        var node = nodes[nodeIndices[i]];
55742        var ret = node.getItemsOnPoint(point);
55743        items.push.apply(items, ret); // Append the return value to the running list.
55744      }
55745
55746      var edgeIndices = this.body.edgeIndices;
55747      var edges = this.body.edges;
55748      for (var _i = edgeIndices.length - 1; _i >= 0; _i--) {
55749        var edge = edges[edgeIndices[_i]];
55750        var _ret = edge.getItemsOnPoint(point);
55751        items.push.apply(items, _ret); // Append the return value to the running list.
55752      }
55753
55754      return items;
55755    }
55756  }]);
55757  return SelectionHandler;
55758}();
55759
55760exports['default'] = SelectionHandler;
55761
55762/***/ }),
55763/* 235 */
55764/***/ (function(module, exports, __webpack_require__) {
55765
55766"use strict";
55767
55768/**
55769 * There's a mix-up with terms in the code. Following are the formal definitions:
55770 *
55771 *   tree   - a strict hierarchical network, i.e. every node has at most one parent
55772 *   forest - a collection of trees. These distinct trees are thus not connected.
55773 *
55774 * So:
55775 * - in a network that is not a tree, there exist nodes with multiple parents.
55776 * - a network consisting of unconnected sub-networks, of which at least one
55777 *   is not a tree, is not a forest.
55778 *
55779 * In the code, the definitions are:
55780 *
55781 *   tree   - any disconnected sub-network, strict hierarchical or not.
55782 *   forest - a bunch of these sub-networks
55783 *
55784 * The difference between tree and not-tree is important in the code, notably within
55785 * to the block-shifting algorithm. The algorithm assumes formal trees and fails
55786 * for not-trees, often in a spectacular manner (search for 'exploding network' in the issues).
55787 *
55788 * In order to distinguish the definitions in the following code, the adjective 'formal' is
55789 * used. If 'formal' is absent, you must assume the non-formal definition.
55790 *
55791 * ----------------------------------------------------------------------------------
55792 * NOTES
55793 * =====
55794 *
55795 * A hierarchical layout is a different thing from a hierarchical network.
55796 * The layout is a way to arrange the nodes in the view; this can be done
55797 * on non-hierarchical networks as well. The converse is also possible.
55798 */
55799
55800Object.defineProperty(exports, "__esModule", {
55801  value: true
55802});
55803
55804var _slicedToArray2 = __webpack_require__(30);
55805
55806var _slicedToArray3 = _interopRequireDefault(_slicedToArray2);
55807
55808var _typeof2 = __webpack_require__(6);
55809
55810var _typeof3 = _interopRequireDefault(_typeof2);
55811
55812var _keys = __webpack_require__(8);
55813
55814var _keys2 = _interopRequireDefault(_keys);
55815
55816var _classCallCheck2 = __webpack_require__(0);
55817
55818var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
55819
55820var _createClass2 = __webpack_require__(1);
55821
55822var _createClass3 = _interopRequireDefault(_createClass2);
55823
55824function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
55825
55826var util = __webpack_require__(2);
55827var NetworkUtil = __webpack_require__(76)['default'];
55828
55829var _require = __webpack_require__(236),
55830    HorizontalStrategy = _require.HorizontalStrategy,
55831    VerticalStrategy = _require.VerticalStrategy;
55832
55833/**
55834 * Container for derived data on current network, relating to hierarchy.
55835 *
55836 * @private
55837 */
55838
55839
55840var HierarchicalStatus = function () {
55841  /**
55842   * @ignore
55843   */
55844  function HierarchicalStatus() {
55845    (0, _classCallCheck3['default'])(this, HierarchicalStatus);
55846
55847    this.childrenReference = {}; // child id's per node id
55848    this.parentReference = {}; // parent id's per node id
55849    this.trees = {}; // tree id per node id; i.e. to which tree does given node id belong
55850
55851    this.distributionOrdering = {}; // The nodes per level, in the display order
55852    this.levels = {}; // hierarchy level per node id
55853    this.distributionIndex = {}; // The position of the node in the level sorting order, per node id.
55854
55855    this.isTree = false; // True if current network is a formal tree 
55856    this.treeIndex = -1; // Highest tree id in current network.
55857  }
55858
55859  /**
55860   * Add the relation between given nodes to the current state.
55861   *
55862   * @param {Node.id} parentNodeId
55863   * @param {Node.id} childNodeId
55864   */
55865
55866
55867  (0, _createClass3['default'])(HierarchicalStatus, [{
55868    key: 'addRelation',
55869    value: function addRelation(parentNodeId, childNodeId) {
55870      if (this.childrenReference[parentNodeId] === undefined) {
55871        this.childrenReference[parentNodeId] = [];
55872      }
55873      this.childrenReference[parentNodeId].push(childNodeId);
55874
55875      if (this.parentReference[childNodeId] === undefined) {
55876        this.parentReference[childNodeId] = [];
55877      }
55878      this.parentReference[childNodeId].push(parentNodeId);
55879    }
55880
55881    /**
55882     * Check if the current state is for a formal tree or formal forest.
55883     *
55884     * This is the case if every node has at most one parent.
55885     *
55886     * Pre: parentReference init'ed properly for current network
55887     */
55888
55889  }, {
55890    key: 'checkIfTree',
55891    value: function checkIfTree() {
55892      for (var i in this.parentReference) {
55893        if (this.parentReference[i].length > 1) {
55894          this.isTree = false;
55895          return;
55896        }
55897      }
55898
55899      this.isTree = true;
55900    }
55901
55902    /**
55903     * Return the number of separate trees in the current network.
55904     * @returns {number}
55905     */
55906
55907  }, {
55908    key: 'numTrees',
55909    value: function numTrees() {
55910      return this.treeIndex + 1; // This assumes the indexes are assigned consecitively
55911    }
55912
55913    /**
55914     * Assign a tree id to a node
55915     * @param {Node} node
55916     * @param {string|number} treeId
55917     */
55918
55919  }, {
55920    key: 'setTreeIndex',
55921    value: function setTreeIndex(node, treeId) {
55922      if (treeId === undefined) return; // Don't bother
55923
55924      if (this.trees[node.id] === undefined) {
55925        this.trees[node.id] = treeId;
55926        this.treeIndex = Math.max(treeId, this.treeIndex);
55927      }
55928    }
55929
55930    /**
55931     * Ensure level for given id is defined.
55932     *
55933     * Sets level to zero for given node id if not already present
55934     *
55935     * @param {Node.id} nodeId
55936     */
55937
55938  }, {
55939    key: 'ensureLevel',
55940    value: function ensureLevel(nodeId) {
55941      if (this.levels[nodeId] === undefined) {
55942        this.levels[nodeId] = 0;
55943      }
55944    }
55945
55946    /**
55947     * get the maximum level of a branch.
55948     *
55949     * TODO: Never entered; find a test case to test this!
55950     * @param {Node.id} nodeId
55951     * @returns {number}
55952     */
55953
55954  }, {
55955    key: 'getMaxLevel',
55956    value: function getMaxLevel(nodeId) {
55957      var _this = this;
55958
55959      var accumulator = {};
55960
55961      var _getMaxLevel = function _getMaxLevel(nodeId) {
55962        if (accumulator[nodeId] !== undefined) {
55963          return accumulator[nodeId];
55964        }
55965        var level = _this.levels[nodeId];
55966        if (_this.childrenReference[nodeId]) {
55967          var children = _this.childrenReference[nodeId];
55968          if (children.length > 0) {
55969            for (var i = 0; i < children.length; i++) {
55970              level = Math.max(level, _getMaxLevel(children[i]));
55971            }
55972          }
55973        }
55974        accumulator[nodeId] = level;
55975        return level;
55976      };
55977
55978      return _getMaxLevel(nodeId);
55979    }
55980
55981    /**
55982     *
55983     * @param {Node} nodeA
55984     * @param {Node} nodeB
55985     */
55986
55987  }, {
55988    key: 'levelDownstream',
55989    value: function levelDownstream(nodeA, nodeB) {
55990      if (this.levels[nodeB.id] === undefined) {
55991        // set initial level
55992        if (this.levels[nodeA.id] === undefined) {
55993          this.levels[nodeA.id] = 0;
55994        }
55995        // set level
55996        this.levels[nodeB.id] = this.levels[nodeA.id] + 1;
55997      }
55998    }
55999
56000    /**
56001     * Small util method to set the minimum levels of the nodes to zero.
56002     *
56003     * @param {Array.<Node>} nodes
56004     */
56005
56006  }, {
56007    key: 'setMinLevelToZero',
56008    value: function setMinLevelToZero(nodes) {
56009      var minLevel = 1e9;
56010      // get the minimum level
56011      for (var nodeId in nodes) {
56012        if (nodes.hasOwnProperty(nodeId)) {
56013          if (this.levels[nodeId] !== undefined) {
56014            minLevel = Math.min(this.levels[nodeId], minLevel);
56015          }
56016        }
56017      }
56018
56019      // subtract the minimum from the set so we have a range starting from 0
56020      for (var _nodeId in nodes) {
56021        if (nodes.hasOwnProperty(_nodeId)) {
56022          if (this.levels[_nodeId] !== undefined) {
56023            this.levels[_nodeId] -= minLevel;
56024          }
56025        }
56026      }
56027    }
56028
56029    /**
56030     * Get the min and max xy-coordinates of a given tree
56031     *
56032     * @param {Array.<Node>} nodes
56033     * @param {number} index
56034     * @returns {{min_x: number, max_x: number, min_y: number, max_y: number}}
56035     */
56036
56037  }, {
56038    key: 'getTreeSize',
56039    value: function getTreeSize(nodes, index) {
56040      var min_x = 1e9;
56041      var max_x = -1e9;
56042      var min_y = 1e9;
56043      var max_y = -1e9;
56044
56045      for (var nodeId in this.trees) {
56046        if (this.trees.hasOwnProperty(nodeId)) {
56047          if (this.trees[nodeId] === index) {
56048            var node = nodes[nodeId];
56049            min_x = Math.min(node.x, min_x);
56050            max_x = Math.max(node.x, max_x);
56051            min_y = Math.min(node.y, min_y);
56052            max_y = Math.max(node.y, max_y);
56053          }
56054        }
56055      }
56056
56057      return {
56058        min_x: min_x,
56059        max_x: max_x,
56060        min_y: min_y,
56061        max_y: max_y
56062      };
56063    }
56064
56065    /**
56066     * Check if two nodes have the same parent(s)
56067     *
56068     * @param {Node} node1
56069     * @param {Node} node2
56070     * @return {boolean} true if the two nodes have a same ancestor node, false otherwise
56071     */
56072
56073  }, {
56074    key: 'hasSameParent',
56075    value: function hasSameParent(node1, node2) {
56076      var parents1 = this.parentReference[node1.id];
56077      var parents2 = this.parentReference[node2.id];
56078      if (parents1 === undefined || parents2 === undefined) {
56079        return false;
56080      }
56081
56082      for (var i = 0; i < parents1.length; i++) {
56083        for (var j = 0; j < parents2.length; j++) {
56084          if (parents1[i] == parents2[j]) {
56085            return true;
56086          }
56087        }
56088      }
56089      return false;
56090    }
56091
56092    /**
56093     * Check if two nodes are in the same tree.
56094     *
56095     * @param {Node} node1
56096     * @param {Node} node2
56097     * @return {Boolean} true if this is so, false otherwise
56098     */
56099
56100  }, {
56101    key: 'inSameSubNetwork',
56102    value: function inSameSubNetwork(node1, node2) {
56103      return this.trees[node1.id] === this.trees[node2.id];
56104    }
56105
56106    /**
56107     * Get a list of the distinct levels in the current network
56108     *
56109     * @returns {Array}
56110     */
56111
56112  }, {
56113    key: 'getLevels',
56114    value: function getLevels() {
56115      return (0, _keys2['default'])(this.distributionOrdering);
56116    }
56117
56118    /**
56119     * Add a node to the ordering per level
56120     *
56121     * @param {Node} node
56122     * @param {number} level
56123     */
56124
56125  }, {
56126    key: 'addToOrdering',
56127    value: function addToOrdering(node, level) {
56128      if (this.distributionOrdering[level] === undefined) {
56129        this.distributionOrdering[level] = [];
56130      }
56131
56132      var isPresent = false;
56133      var curLevel = this.distributionOrdering[level];
56134      for (var n in curLevel) {
56135        //if (curLevel[n].id === node.id) {
56136        if (curLevel[n] === node) {
56137          isPresent = true;
56138          break;
56139        }
56140      }
56141
56142      if (!isPresent) {
56143        this.distributionOrdering[level].push(node);
56144        this.distributionIndex[node.id] = this.distributionOrdering[level].length - 1;
56145      }
56146    }
56147  }]);
56148  return HierarchicalStatus;
56149}();
56150
56151/**
56152 * The Layout Engine
56153 */
56154
56155
56156var LayoutEngine = function () {
56157  /**
56158   * @param {Object} body
56159   */
56160  function LayoutEngine(body) {
56161    (0, _classCallCheck3['default'])(this, LayoutEngine);
56162
56163    this.body = body;
56164
56165    this.initialRandomSeed = Math.round(Math.random() * 1000000);
56166    this.randomSeed = this.initialRandomSeed;
56167    this.setPhysics = false;
56168    this.options = {};
56169    this.optionsBackup = { physics: {} };
56170
56171    this.defaultOptions = {
56172      randomSeed: undefined,
56173      improvedLayout: true,
56174      hierarchical: {
56175        enabled: false,
56176        levelSeparation: 150,
56177        nodeSpacing: 100,
56178        treeSpacing: 200,
56179        blockShifting: true,
56180        edgeMinimization: true,
56181        parentCentralization: true,
56182        direction: 'UD', // UD, DU, LR, RL
56183        sortMethod: 'hubsize' // hubsize, directed
56184      }
56185    };
56186    util.extend(this.options, this.defaultOptions);
56187    this.bindEventListeners();
56188  }
56189
56190  /**
56191   * Binds event listeners
56192   */
56193
56194
56195  (0, _createClass3['default'])(LayoutEngine, [{
56196    key: 'bindEventListeners',
56197    value: function bindEventListeners() {
56198      var _this2 = this;
56199
56200      this.body.emitter.on('_dataChanged', function () {
56201        _this2.setupHierarchicalLayout();
56202      });
56203      this.body.emitter.on('_dataLoaded', function () {
56204        _this2.layoutNetwork();
56205      });
56206      this.body.emitter.on('_resetHierarchicalLayout', function () {
56207        _this2.setupHierarchicalLayout();
56208      });
56209      this.body.emitter.on('_adjustEdgesForHierarchicalLayout', function () {
56210        if (_this2.options.hierarchical.enabled !== true) {
56211          return;
56212        }
56213        // get the type of static smooth curve in case it is required
56214        var type = _this2.direction.curveType();
56215
56216        // force all edges into static smooth curves.
56217        _this2.body.emitter.emit('_forceDisableDynamicCurves', type, false);
56218      });
56219    }
56220
56221    /**
56222     *
56223     * @param {Object} options
56224     * @param {Object} allOptions
56225     * @returns {Object}
56226     */
56227
56228  }, {
56229    key: 'setOptions',
56230    value: function setOptions(options, allOptions) {
56231      if (options !== undefined) {
56232        var hierarchical = this.options.hierarchical;
56233        var prevHierarchicalState = hierarchical.enabled;
56234        util.selectiveDeepExtend(["randomSeed", "improvedLayout"], this.options, options);
56235        util.mergeOptions(this.options, options, 'hierarchical');
56236        if (options.randomSeed !== undefined) {
56237          this.initialRandomSeed = options.randomSeed;
56238        }
56239
56240        if (hierarchical.enabled === true) {
56241          if (prevHierarchicalState === true) {
56242            // refresh the overridden options for nodes and edges.
56243            this.body.emitter.emit('refresh', true);
56244          }
56245
56246          // make sure the level separation is the right way up
56247          if (hierarchical.direction === 'RL' || hierarchical.direction === 'DU') {
56248            if (hierarchical.levelSeparation > 0) {
56249              hierarchical.levelSeparation *= -1;
56250            }
56251          } else {
56252            if (hierarchical.levelSeparation < 0) {
56253              hierarchical.levelSeparation *= -1;
56254            }
56255          }
56256
56257          this.setDirectionStrategy();
56258
56259          this.body.emitter.emit('_resetHierarchicalLayout');
56260          // because the hierarchical system needs it's own physics and smooth curve settings,
56261          // we adapt the other options if needed.
56262          return this.adaptAllOptionsForHierarchicalLayout(allOptions);
56263        } else {
56264          if (prevHierarchicalState === true) {
56265            // refresh the overridden options for nodes and edges.
56266            this.body.emitter.emit('refresh');
56267            return util.deepExtend(allOptions, this.optionsBackup);
56268          }
56269        }
56270      }
56271      return allOptions;
56272    }
56273
56274    /**
56275     *
56276     * @param {Object} allOptions
56277     * @returns {Object}
56278     */
56279
56280  }, {
56281    key: 'adaptAllOptionsForHierarchicalLayout',
56282    value: function adaptAllOptionsForHierarchicalLayout(allOptions) {
56283      if (this.options.hierarchical.enabled === true) {
56284        var backupPhysics = this.optionsBackup.physics;
56285
56286        // set the physics
56287        if (allOptions.physics === undefined || allOptions.physics === true) {
56288          allOptions.physics = {
56289            enabled: backupPhysics.enabled === undefined ? true : backupPhysics.enabled,
56290            solver: 'hierarchicalRepulsion'
56291          };
56292          backupPhysics.enabled = backupPhysics.enabled === undefined ? true : backupPhysics.enabled;
56293          backupPhysics.solver = backupPhysics.solver || 'barnesHut';
56294        } else if ((0, _typeof3['default'])(allOptions.physics) === 'object') {
56295          backupPhysics.enabled = allOptions.physics.enabled === undefined ? true : allOptions.physics.enabled;
56296          backupPhysics.solver = allOptions.physics.solver || 'barnesHut';
56297          allOptions.physics.solver = 'hierarchicalRepulsion';
56298        } else if (allOptions.physics !== false) {
56299          backupPhysics.solver = 'barnesHut';
56300          allOptions.physics = { solver: 'hierarchicalRepulsion' };
56301        }
56302
56303        // get the type of static smooth curve in case it is required
56304        var type = this.direction.curveType();
56305
56306        // disable smooth curves if nothing is defined. If smooth curves have been turned on,
56307        // turn them into static smooth curves.
56308        if (allOptions.edges === undefined) {
56309          this.optionsBackup.edges = { smooth: { enabled: true, type: 'dynamic' } };
56310          allOptions.edges = { smooth: false };
56311        } else if (allOptions.edges.smooth === undefined) {
56312          this.optionsBackup.edges = { smooth: { enabled: true, type: 'dynamic' } };
56313          allOptions.edges.smooth = false;
56314        } else {
56315          if (typeof allOptions.edges.smooth === 'boolean') {
56316            this.optionsBackup.edges = { smooth: allOptions.edges.smooth };
56317            allOptions.edges.smooth = { enabled: allOptions.edges.smooth, type: type };
56318          } else {
56319            var smooth = allOptions.edges.smooth;
56320
56321            // allow custom types except for dynamic
56322            if (smooth.type !== undefined && smooth.type !== 'dynamic') {
56323              type = smooth.type;
56324            }
56325
56326            // TODO: this is options merging; see if the standard routines can be used here.
56327            this.optionsBackup.edges = {
56328              smooth: smooth.enabled === undefined ? true : smooth.enabled,
56329              type: smooth.type === undefined ? 'dynamic' : smooth.type,
56330              roundness: smooth.roundness === undefined ? 0.5 : smooth.roundness,
56331              forceDirection: smooth.forceDirection === undefined ? false : smooth.forceDirection
56332            };
56333
56334            // NOTE: Copying an object to self; this is basically setting defaults for undefined variables
56335            allOptions.edges.smooth = {
56336              enabled: smooth.enabled === undefined ? true : smooth.enabled,
56337              type: type,
56338              roundness: smooth.roundness === undefined ? 0.5 : smooth.roundness,
56339              forceDirection: smooth.forceDirection === undefined ? false : smooth.forceDirection
56340            };
56341          }
56342        }
56343
56344        // Force all edges into static smooth curves.
56345        // Only applies to edges that do not use the global options for smooth.
56346        this.body.emitter.emit('_forceDisableDynamicCurves', type);
56347      }
56348
56349      return allOptions;
56350    }
56351
56352    /**
56353     *
56354     * @returns {number}
56355     */
56356
56357  }, {
56358    key: 'seededRandom',
56359    value: function seededRandom() {
56360      var x = Math.sin(this.randomSeed++) * 10000;
56361      return x - Math.floor(x);
56362    }
56363
56364    /**
56365     *
56366     * @param {Array.<Node>} nodesArray
56367     */
56368
56369  }, {
56370    key: 'positionInitially',
56371    value: function positionInitially(nodesArray) {
56372      if (this.options.hierarchical.enabled !== true) {
56373        this.randomSeed = this.initialRandomSeed;
56374        var radius = nodesArray.length + 50;
56375        for (var i = 0; i < nodesArray.length; i++) {
56376          var node = nodesArray[i];
56377          var angle = 2 * Math.PI * this.seededRandom();
56378          if (node.x === undefined) {
56379            node.x = radius * Math.cos(angle);
56380          }
56381          if (node.y === undefined) {
56382            node.y = radius * Math.sin(angle);
56383          }
56384        }
56385      }
56386    }
56387
56388    /**
56389     * Use Kamada Kawai to position nodes. This is quite a heavy algorithm so if there are a lot of nodes we
56390     * cluster them first to reduce the amount.
56391     */
56392
56393  }, {
56394    key: 'layoutNetwork',
56395    value: function layoutNetwork() {
56396      if (this.options.hierarchical.enabled !== true && this.options.improvedLayout === true) {
56397        var indices = this.body.nodeIndices;
56398
56399        // first check if we should Kamada Kawai to layout. The threshold is if less than half of the visible
56400        // nodes have predefined positions we use this.
56401        var positionDefined = 0;
56402        for (var i = 0; i < indices.length; i++) {
56403          var node = this.body.nodes[indices[i]];
56404          if (node.predefinedPosition === true) {
56405            positionDefined += 1;
56406          }
56407        }
56408
56409        // if less than half of the nodes have a predefined position we continue
56410        if (positionDefined < 0.5 * indices.length) {
56411          var MAX_LEVELS = 10;
56412          var level = 0;
56413          var clusterThreshold = 150; // TODO add this to options
56414
56415          //
56416          // Define the options for the hidden cluster nodes
56417          // These options don't propagate outside the clustering phase.
56418          //
56419          // Some options are explicitly disabled, because they may be set in group or default node options.
56420          // The clusters are never displayed, so most explicit settings here serve as performance optimizations.
56421          //
56422          // The explicit setting of 'shape' is to avoid `shape: 'image'`; images are not passed to the hidden
56423          // cluster nodes, leading to an exception on creation.
56424          //
56425          // All settings here are performance related, except when noted otherwise.
56426          //
56427          var clusterOptions = {
56428            clusterNodeProperties: {
56429              shape: 'ellipse', // Bugfix: avoid type 'image', no images supplied
56430              label: '', // avoid label handling
56431              group: '', // avoid group handling
56432              font: { multi: false } // avoid font propagation
56433            },
56434            clusterEdgeProperties: {
56435              label: '', // avoid label handling
56436              font: { multi: false }, // avoid font propagation
56437              smooth: {
56438                enabled: false // avoid drawing penalty for complex edges
56439              }
56440            }
56441          };
56442
56443          // if there are a lot of nodes, we cluster before we run the algorithm.
56444          // NOTE: this part fails to find clusters for large scale-free networks, which should
56445          //       be easily clusterable.
56446          // TODO: examine why this is so
56447          if (indices.length > clusterThreshold) {
56448            var startLength = indices.length;
56449            while (indices.length > clusterThreshold && level <= MAX_LEVELS) {
56450              //console.time("clustering")
56451              level += 1;
56452              var before = indices.length;
56453              // if there are many nodes we do a hubsize cluster
56454              if (level % 3 === 0) {
56455                this.body.modules.clustering.clusterBridges(clusterOptions);
56456              } else {
56457                this.body.modules.clustering.clusterOutliers(clusterOptions);
56458              }
56459              var after = indices.length;
56460              if (before == after && level % 3 !== 0) {
56461                this._declusterAll();
56462                this.body.emitter.emit("_layoutFailed");
56463                console.info("This network could not be positioned by this version of the improved layout algorithm." + " Please disable improvedLayout for better performance.");
56464                return;
56465              }
56466              //console.timeEnd("clustering")
56467              //console.log(before,level,after);
56468            }
56469            // increase the size of the edges
56470            this.body.modules.kamadaKawai.setOptions({ springLength: Math.max(150, 2 * startLength) });
56471          }
56472          if (level > MAX_LEVELS) {
56473            console.info("The clustering didn't succeed within the amount of interations allowed," + " progressing with partial result.");
56474          }
56475
56476          // position the system for these nodes and edges
56477          this.body.modules.kamadaKawai.solve(indices, this.body.edgeIndices, true);
56478
56479          // shift to center point
56480          this._shiftToCenter();
56481
56482          // perturb the nodes a little bit to force the physics to kick in
56483          var offset = 70;
56484          for (var _i = 0; _i < indices.length; _i++) {
56485            // Only perturb the nodes that aren't fixed
56486            var _node = this.body.nodes[indices[_i]];
56487            if (_node.predefinedPosition === false) {
56488              _node.x += (0.5 - this.seededRandom()) * offset;
56489              _node.y += (0.5 - this.seededRandom()) * offset;
56490            }
56491          }
56492
56493          // uncluster all clusters
56494          this._declusterAll();
56495
56496          // reposition all bezier nodes.
56497          this.body.emitter.emit("_repositionBezierNodes");
56498        }
56499      }
56500    }
56501
56502    /**
56503     * Move all the nodes towards to the center so gravitational pull wil not move the nodes away from view
56504     * @private
56505     */
56506
56507  }, {
56508    key: '_shiftToCenter',
56509    value: function _shiftToCenter() {
56510      var range = NetworkUtil.getRangeCore(this.body.nodes, this.body.nodeIndices);
56511      var center = NetworkUtil.findCenter(range);
56512      for (var i = 0; i < this.body.nodeIndices.length; i++) {
56513        var node = this.body.nodes[this.body.nodeIndices[i]];
56514        node.x -= center.x;
56515        node.y -= center.y;
56516      }
56517    }
56518
56519    /**
56520     * Expands all clusters
56521     * @private
56522     */
56523
56524  }, {
56525    key: '_declusterAll',
56526    value: function _declusterAll() {
56527      var clustersPresent = true;
56528      while (clustersPresent === true) {
56529        clustersPresent = false;
56530        for (var i = 0; i < this.body.nodeIndices.length; i++) {
56531          if (this.body.nodes[this.body.nodeIndices[i]].isCluster === true) {
56532            clustersPresent = true;
56533            this.body.modules.clustering.openCluster(this.body.nodeIndices[i], {}, false);
56534          }
56535        }
56536        if (clustersPresent === true) {
56537          this.body.emitter.emit('_dataChanged');
56538        }
56539      }
56540    }
56541
56542    /**
56543     *
56544     * @returns {number|*}
56545     */
56546
56547  }, {
56548    key: 'getSeed',
56549    value: function getSeed() {
56550      return this.initialRandomSeed;
56551    }
56552
56553    /**
56554     * This is the main function to layout the nodes in a hierarchical way.
56555     * It checks if the node details are supplied correctly
56556     *
56557     * @private
56558     */
56559
56560  }, {
56561    key: 'setupHierarchicalLayout',
56562    value: function setupHierarchicalLayout() {
56563      if (this.options.hierarchical.enabled === true && this.body.nodeIndices.length > 0) {
56564        // get the size of the largest hubs and check if the user has defined a level for a node.
56565        var node = void 0,
56566            nodeId = void 0;
56567        var definedLevel = false;
56568        var undefinedLevel = false;
56569        this.lastNodeOnLevel = {};
56570        this.hierarchical = new HierarchicalStatus();
56571
56572        for (nodeId in this.body.nodes) {
56573          if (this.body.nodes.hasOwnProperty(nodeId)) {
56574            node = this.body.nodes[nodeId];
56575            if (node.options.level !== undefined) {
56576              definedLevel = true;
56577              this.hierarchical.levels[nodeId] = node.options.level;
56578            } else {
56579              undefinedLevel = true;
56580            }
56581          }
56582        }
56583
56584        // if the user defined some levels but not all, alert and run without hierarchical layout
56585        if (undefinedLevel === true && definedLevel === true) {
56586          throw new Error('To use the hierarchical layout, nodes require either no predefined levels' + ' or levels have to be defined for all nodes.');
56587        } else {
56588          // define levels if undefined by the users. Based on hubsize.
56589          if (undefinedLevel === true) {
56590            var sortMethod = this.options.hierarchical.sortMethod;
56591            if (sortMethod === 'hubsize') {
56592              this._determineLevelsByHubsize();
56593            } else if (sortMethod === 'directed') {
56594              this._determineLevelsDirected();
56595            } else if (sortMethod === 'custom') {
56596              this._determineLevelsCustomCallback();
56597            }
56598          }
56599
56600          // fallback for cases where there are nodes but no edges
56601          for (var _nodeId2 in this.body.nodes) {
56602            if (this.body.nodes.hasOwnProperty(_nodeId2)) {
56603              this.hierarchical.ensureLevel(_nodeId2);
56604            }
56605          }
56606          // check the distribution of the nodes per level.
56607          var distribution = this._getDistribution();
56608
56609          // get the parent children relations.
56610          this._generateMap();
56611
56612          // place the nodes on the canvas.
56613          this._placeNodesByHierarchy(distribution);
56614
56615          // condense the whitespace.
56616          this._condenseHierarchy();
56617
56618          // shift to center so gravity does not have to do much
56619          this._shiftToCenter();
56620        }
56621      }
56622    }
56623
56624    /**
56625     * @private
56626     */
56627
56628  }, {
56629    key: '_condenseHierarchy',
56630    value: function _condenseHierarchy() {
56631      var _this3 = this;
56632
56633      // Global var in this scope to define when the movement has stopped.
56634      var stillShifting = false;
56635      var branches = {};
56636      // first we have some methods to help shifting trees around.
56637      // the main method to shift the trees
56638      var shiftTrees = function shiftTrees() {
56639        var treeSizes = getTreeSizes();
56640        var shiftBy = 0;
56641        for (var i = 0; i < treeSizes.length - 1; i++) {
56642          var diff = treeSizes[i].max - treeSizes[i + 1].min;
56643          shiftBy += diff + _this3.options.hierarchical.treeSpacing;
56644          shiftTree(i + 1, shiftBy);
56645        }
56646      };
56647
56648      // shift a single tree by an offset
56649      var shiftTree = function shiftTree(index, offset) {
56650        var trees = _this3.hierarchical.trees;
56651
56652        for (var nodeId in trees) {
56653          if (trees.hasOwnProperty(nodeId)) {
56654            if (trees[nodeId] === index) {
56655              _this3.direction.shift(nodeId, offset);
56656            }
56657          }
56658        }
56659      };
56660
56661      // get the width of all trees
56662      var getTreeSizes = function getTreeSizes() {
56663        var treeWidths = [];
56664        for (var i = 0; i < _this3.hierarchical.numTrees(); i++) {
56665          treeWidths.push(_this3.direction.getTreeSize(i));
56666        }
56667        return treeWidths;
56668      };
56669
56670      // get a map of all nodes in this branch
56671      var getBranchNodes = function getBranchNodes(source, map) {
56672        if (map[source.id]) {
56673          return;
56674        }
56675        map[source.id] = true;
56676        if (_this3.hierarchical.childrenReference[source.id]) {
56677          var children = _this3.hierarchical.childrenReference[source.id];
56678          if (children.length > 0) {
56679            for (var i = 0; i < children.length; i++) {
56680              getBranchNodes(_this3.body.nodes[children[i]], map);
56681            }
56682          }
56683        }
56684      };
56685
56686      // get a min max width as well as the maximum movement space it has on either sides
56687      // we use min max terminology because width and height can interchange depending on the direction of the layout
56688      var getBranchBoundary = function getBranchBoundary(branchMap) {
56689        var maxLevel = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 1e9;
56690
56691        var minSpace = 1e9;
56692        var maxSpace = 1e9;
56693        var min = 1e9;
56694        var max = -1e9;
56695        for (var branchNode in branchMap) {
56696          if (branchMap.hasOwnProperty(branchNode)) {
56697            var node = _this3.body.nodes[branchNode];
56698            var level = _this3.hierarchical.levels[node.id];
56699            var position = _this3.direction.getPosition(node);
56700
56701            // get the space around the node.
56702
56703            var _getSpaceAroundNode2 = _this3._getSpaceAroundNode(node, branchMap),
56704                _getSpaceAroundNode3 = (0, _slicedToArray3['default'])(_getSpaceAroundNode2, 2),
56705                minSpaceNode = _getSpaceAroundNode3[0],
56706                maxSpaceNode = _getSpaceAroundNode3[1];
56707
56708            minSpace = Math.min(minSpaceNode, minSpace);
56709            maxSpace = Math.min(maxSpaceNode, maxSpace);
56710
56711            // the width is only relevant for the levels two nodes have in common. This is why we filter on this.
56712            if (level <= maxLevel) {
56713              min = Math.min(position, min);
56714              max = Math.max(position, max);
56715            }
56716          }
56717        }
56718
56719        return [min, max, minSpace, maxSpace];
56720      };
56721
56722      // check what the maximum level is these nodes have in common.
56723      var getCollisionLevel = function getCollisionLevel(node1, node2) {
56724        var maxLevel1 = _this3.hierarchical.getMaxLevel(node1.id);
56725        var maxLevel2 = _this3.hierarchical.getMaxLevel(node2.id);
56726        return Math.min(maxLevel1, maxLevel2);
56727      };
56728
56729      /**
56730       * Condense elements. These can be nodes or branches depending on the callback.
56731       *
56732       * @param {function} callback
56733       * @param {Array.<number>} levels
56734       * @param {*} centerParents
56735       */
56736      var shiftElementsCloser = function shiftElementsCloser(callback, levels, centerParents) {
56737        var hier = _this3.hierarchical;
56738
56739        for (var i = 0; i < levels.length; i++) {
56740          var level = levels[i];
56741          var levelNodes = hier.distributionOrdering[level];
56742          if (levelNodes.length > 1) {
56743            for (var j = 0; j < levelNodes.length - 1; j++) {
56744              var node1 = levelNodes[j];
56745              var node2 = levelNodes[j + 1];
56746
56747              // NOTE: logic maintained as it was; if nodes have same ancestor,
56748              //       then of course they are in the same sub-network.
56749              if (hier.hasSameParent(node1, node2) && hier.inSameSubNetwork(node1, node2)) {
56750                callback(node1, node2, centerParents);
56751              }
56752            }
56753          }
56754        }
56755      };
56756
56757      // callback for shifting branches
56758      var branchShiftCallback = function branchShiftCallback(node1, node2) {
56759        var centerParent = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
56760
56761        //window.CALLBACKS.push(() => {
56762        var pos1 = _this3.direction.getPosition(node1);
56763        var pos2 = _this3.direction.getPosition(node2);
56764        var diffAbs = Math.abs(pos2 - pos1);
56765        var nodeSpacing = _this3.options.hierarchical.nodeSpacing;
56766        //console.log("NOW CHECKING:", node1.id, node2.id, diffAbs);
56767        if (diffAbs > nodeSpacing) {
56768          var branchNodes1 = {};
56769          var branchNodes2 = {};
56770
56771          getBranchNodes(node1, branchNodes1);
56772          getBranchNodes(node2, branchNodes2);
56773
56774          // check the largest distance between the branches
56775          var maxLevel = getCollisionLevel(node1, node2);
56776          var branchNodeBoundary1 = getBranchBoundary(branchNodes1, maxLevel);
56777          var branchNodeBoundary2 = getBranchBoundary(branchNodes2, maxLevel);
56778          var max1 = branchNodeBoundary1[1];
56779          var min2 = branchNodeBoundary2[0];
56780          var minSpace2 = branchNodeBoundary2[2];
56781
56782          //console.log(node1.id, getBranchBoundary(branchNodes1, maxLevel), node2.id,
56783          //            getBranchBoundary(branchNodes2, maxLevel), maxLevel);
56784          var diffBranch = Math.abs(max1 - min2);
56785          if (diffBranch > nodeSpacing) {
56786            var offset = max1 - min2 + nodeSpacing;
56787            if (offset < -minSpace2 + nodeSpacing) {
56788              offset = -minSpace2 + nodeSpacing;
56789              //console.log("RESETTING OFFSET", max1 - min2 + this.options.hierarchical.nodeSpacing, -minSpace2, offset);
56790            }
56791            if (offset < 0) {
56792              //console.log("SHIFTING", node2.id, offset);
56793              _this3._shiftBlock(node2.id, offset);
56794              stillShifting = true;
56795
56796              if (centerParent === true) _this3._centerParent(node2);
56797            }
56798          }
56799        }
56800        //this.body.emitter.emit("_redraw");})
56801      };
56802
56803      var minimizeEdgeLength = function minimizeEdgeLength(iterations, node) {
56804        //window.CALLBACKS.push(() => {
56805        //  console.log("ts",node.id);
56806        var nodeId = node.id;
56807        var allEdges = node.edges;
56808        var nodeLevel = _this3.hierarchical.levels[node.id];
56809
56810        // gather constants
56811        var C2 = _this3.options.hierarchical.levelSeparation * _this3.options.hierarchical.levelSeparation;
56812        var referenceNodes = {};
56813        var aboveEdges = [];
56814        for (var i = 0; i < allEdges.length; i++) {
56815          var edge = allEdges[i];
56816          if (edge.toId != edge.fromId) {
56817            var otherNode = edge.toId == nodeId ? edge.from : edge.to;
56818            referenceNodes[allEdges[i].id] = otherNode;
56819            if (_this3.hierarchical.levels[otherNode.id] < nodeLevel) {
56820              aboveEdges.push(edge);
56821            }
56822          }
56823        }
56824
56825        // differentiated sum of lengths based on only moving one node over one axis
56826        var getFx = function getFx(point, edges) {
56827          var sum = 0;
56828          for (var _i2 = 0; _i2 < edges.length; _i2++) {
56829            if (referenceNodes[edges[_i2].id] !== undefined) {
56830              var a = _this3.direction.getPosition(referenceNodes[edges[_i2].id]) - point;
56831              sum += a / Math.sqrt(a * a + C2);
56832            }
56833          }
56834          return sum;
56835        };
56836
56837        // doubly differentiated sum of lengths based on only moving one node over one axis
56838        var getDFx = function getDFx(point, edges) {
56839          var sum = 0;
56840          for (var _i3 = 0; _i3 < edges.length; _i3++) {
56841            if (referenceNodes[edges[_i3].id] !== undefined) {
56842              var a = _this3.direction.getPosition(referenceNodes[edges[_i3].id]) - point;
56843              sum -= C2 * Math.pow(a * a + C2, -1.5);
56844            }
56845          }
56846          return sum;
56847        };
56848
56849        var getGuess = function getGuess(iterations, edges) {
56850          var guess = _this3.direction.getPosition(node);
56851          // Newton's method for optimization
56852          var guessMap = {};
56853          for (var _i4 = 0; _i4 < iterations; _i4++) {
56854            var fx = getFx(guess, edges);
56855            var dfx = getDFx(guess, edges);
56856
56857            // we limit the movement to avoid instability.
56858            var limit = 40;
56859            var ratio = Math.max(-limit, Math.min(limit, Math.round(fx / dfx)));
56860            guess = guess - ratio;
56861            // reduce duplicates
56862            if (guessMap[guess] !== undefined) {
56863              break;
56864            }
56865            guessMap[guess] = _i4;
56866          }
56867          return guess;
56868        };
56869
56870        var moveBranch = function moveBranch(guess) {
56871          // position node if there is space
56872          var nodePosition = _this3.direction.getPosition(node);
56873
56874          // check movable area of the branch
56875          if (branches[node.id] === undefined) {
56876            var branchNodes = {};
56877            getBranchNodes(node, branchNodes);
56878            branches[node.id] = branchNodes;
56879          }
56880          var branchBoundary = getBranchBoundary(branches[node.id]);
56881          var minSpaceBranch = branchBoundary[2];
56882          var maxSpaceBranch = branchBoundary[3];
56883
56884          var diff = guess - nodePosition;
56885
56886          // check if we are allowed to move the node:
56887          var branchOffset = 0;
56888          if (diff > 0) {
56889            branchOffset = Math.min(diff, maxSpaceBranch - _this3.options.hierarchical.nodeSpacing);
56890          } else if (diff < 0) {
56891            branchOffset = -Math.min(-diff, minSpaceBranch - _this3.options.hierarchical.nodeSpacing);
56892          }
56893
56894          if (branchOffset != 0) {
56895            //console.log("moving branch:",branchOffset, maxSpaceBranch, minS
56895paceBranch)
56896            _this3._shiftBlock(node.id, branchOffset);
56897            //this.body.emitter.emit("_redraw");
56898            stillShifting = true;
56899          }
56900        };
56901
56902        var moveNode = function moveNode(guess) {
56903          var nodePosition = _this3.direction.getPosition(node);
56904
56905          // position node if there is space
56906
56907          var _getSpaceAroundNode4 = _this3._getSpaceAroundNode(node),
56908              _getSpaceAroundNode5 = (0, _slicedToArray3['default'])(_getSpaceAroundNode4, 2),
56909              minSpace = _getSpaceAroundNode5[0],
56910              maxSpace = _getSpaceAroundNode5[1];
56911
56912          var diff = guess - nodePosition;
56913          // check if we are allowed to move the node:
56914          var newPosition = nodePosition;
56915          if (diff > 0) {
56916            newPosition = Math.min(nodePosition + (maxSpace - _this3.options.hierarchical.nodeSpacing), guess);
56917          } else if (diff < 0) {
56918            newPosition = Math.max(nodePosition - (minSpace - _this3.options.hierarchical.nodeSpacing), guess);
56919          }
56920
56921          if (newPosition !== nodePosition) {
56922            //console.log("moving Node:",diff, minSpace, maxSpace);
56923            _this3.direction.setPosition(node, newPosition);
56924            //this.body.emitter.emit("_redraw");
56925            stillShifting = true;
56926          }
56927        };
56928
56929        var guess = getGuess(iterations, aboveEdges);
56930        moveBranch(guess);
56931        guess = getGuess(iterations, allEdges);
56932        moveNode(guess);
56933        //})
56934      };
56935
56936      // method to remove whitespace between branches. Because we do bottom up, we can center the parents.
56937      var minimizeEdgeLengthBottomUp = function minimizeEdgeLengthBottomUp(iterations) {
56938        var levels = _this3.hierarchical.getLevels();
56939        levels = levels.reverse();
56940        for (var i = 0; i < iterations; i++) {
56941          stillShifting = false;
56942          for (var j = 0; j < levels.length; j++) {
56943            var level = levels[j];
56944            var levelNodes = _this3.hierarchical.distributionOrdering[level];
56945            for (var k = 0; k < levelNodes.length; k++) {
56946              minimizeEdgeLength(1000, levelNodes[k]);
56947            }
56948          }
56949          if (stillShifting !== true) {
56950            //console.log("FINISHED minimizeEdgeLengthBottomUp IN " + i);
56951            break;
56952          }
56953        }
56954      };
56955
56956      // method to remove whitespace between branches. Because we do bottom up, we can center the parents.
56957      var shiftBranchesCloserBottomUp = function shiftBranchesCloserBottomUp(iterations) {
56958        var levels = _this3.hierarchical.getLevels();
56959        levels = levels.reverse();
56960        for (var i = 0; i < iterations; i++) {
56961          stillShifting = false;
56962          shiftElementsCloser(branchShiftCallback, levels, true);
56963          if (stillShifting !== true) {
56964            //console.log("FINISHED shiftBranchesCloserBottomUp IN " + (i+1));
56965            break;
56966          }
56967        }
56968      };
56969
56970      // center all parents
56971      var centerAllParents = function centerAllParents() {
56972        for (var nodeId in _this3.body.nodes) {
56973          if (_this3.body.nodes.hasOwnProperty(nodeId)) _this3._centerParent(_this3.body.nodes[nodeId]);
56974        }
56975      };
56976
56977      // center all parents
56978      var centerAllParentsBottomUp = function centerAllParentsBottomUp() {
56979        var levels = _this3.hierarchical.getLevels();
56980        levels = levels.reverse();
56981        for (var i = 0; i < levels.length; i++) {
56982          var level = levels[i];
56983          var levelNodes = _this3.hierarchical.distributionOrdering[level];
56984          for (var j = 0; j < levelNodes.length; j++) {
56985            _this3._centerParent(levelNodes[j]);
56986          }
56987        }
56988      };
56989
56990      // the actual work is done here.
56991      if (this.options.hierarchical.blockShifting === true) {
56992        shiftBranchesCloserBottomUp(5);
56993        centerAllParents();
56994      }
56995
56996      // minimize edge length
56997      if (this.options.hierarchical.edgeMinimization === true) {
56998        minimizeEdgeLengthBottomUp(20);
56999      }
57000
57001      if (this.options.hierarchical.parentCentralization === true) {
57002        centerAllParentsBottomUp();
57003      }
57004
57005      shiftTrees();
57006    }
57007
57008    /**
57009     * This gives the space around the node. IF a map is supplied, it will only check against nodes NOT in the map.
57010     * This is used to only get the distances to nodes outside of a branch.
57011     * @param {Node} node
57012     * @param {{Node.id: vis.Node}} map
57013     * @returns {number[]}
57014     * @private
57015     */
57016
57017  }, {
57018    key: '_getSpaceAroundNode',
57019    value: function _getSpaceAroundNode(node, map) {
57020      var useMap = true;
57021      if (map === undefined) {
57022        useMap = false;
57023      }
57024      var level = this.hierarchical.levels[node.id];
57025      if (level !== undefined) {
57026        var index = this.hierarchical.distributionIndex[node.id];
57027        var position = this.direction.getPosition(node);
57028        var ordering = this.hierarchical.distributionOrdering[level];
57029        var minSpace = 1e9;
57030        var maxSpace = 1e9;
57031        if (index !== 0) {
57032          var prevNode = ordering[index - 1];
57033          if (useMap === true && map[prevNode.id] === undefined || useMap === false) {
57034            var prevPos = this.direction.getPosition(prevNode);
57035            minSpace = position - prevPos;
57036          }
57037        }
57038
57039        if (index != ordering.length - 1) {
57040          var nextNode = ordering[index + 1];
57041          if (useMap === true && map[nextNode.id] === undefined || useMap === false) {
57042            var nextPos = this.direction.getPosition(nextNode);
57043            maxSpace = Math.min(maxSpace, nextPos - position);
57044          }
57045        }
57046
57047        return [minSpace, maxSpace];
57048      } else {
57049        return [0, 0];
57050      }
57051    }
57052
57053    /**
57054     * We use this method to center a parent node and check if it does not cross other nodes when it does.
57055     * @param {Node} node
57056     * @private
57057     */
57058
57059  }, {
57060    key: '_centerParent',
57061    value: function _centerParent(node) {
57062      if (this.hierarchical.parentReference[node.id]) {
57063        var parents = this.hierarchical.parentReference[node.id];
57064        for (var i = 0; i < parents.length; i++) {
57065          var parentId = parents[i];
57066          var parentNode = this.body.nodes[parentId];
57067          var children = this.hierarchical.childrenReference[parentId];
57068
57069          if (children !== undefined) {
57070            // get the range of the children
57071            var newPosition = this._getCenterPosition(children);
57072
57073            var position = this.direction.getPosition(parentNode);
57074
57075            var _getSpaceAroundNode6 = this._getSpaceAroundNode(parentNode),
57076                _getSpaceAroundNode7 = (0, _slicedToArray3['default'])(_getSpaceAroundNode6, 2),
57077                minSpace = _getSpaceAroundNode7[0],
57078                maxSpace = _getSpaceAroundNode7[1];
57079
57080            var diff = position - newPosition;
57081            if (diff < 0 && Math.abs(diff) < maxSpace - this.options.hierarchical.nodeSpacing || diff > 0 && Math.abs(diff) < minSpace - this.options.hierarchical.nodeSpacing) {
57082              this.direction.setPosition(parentNode, newPosition);
57083            }
57084          }
57085        }
57086      }
57087    }
57088
57089    /**
57090     * This function places the nodes on the canvas based on the hierarchial distribution.
57091     *
57092     * @param {Object} distribution | obtained by the function this._getDistribution()
57093     * @private
57094     */
57095
57096  }, {
57097    key: '_placeNodesByHierarchy',
57098    value: function _placeNodesByHierarchy(distribution) {
57099      this.positionedNodes = {};
57100      // start placing all the level 0 nodes first. Then recursively position their branches.
57101      for (var level in distribution) {
57102        if (distribution.hasOwnProperty(level)) {
57103          // sort nodes in level by position:
57104          var nodeArray = (0, _keys2['default'])(distribution[level]);
57105          nodeArray = this._indexArrayToNodes(nodeArray);
57106          this.direction.sort(nodeArray);
57107          var handledNodeCount = 0;
57108
57109          for (var i = 0; i < nodeArray.length; i++) {
57110            var node = nodeArray[i];
57111            if (this.positionedNodes[node.id] === undefined) {
57112              var spacing = this.options.hierarchical.nodeSpacing;
57113              var pos = spacing * handledNodeCount;
57114              // We get the X or Y values we need and store them in pos and previousPos.
57115              // The get and set make sure we get X or Y
57116              if (handledNodeCount > 0) {
57117                pos = this.direction.getPosition(nodeArray[i - 1]) + spacing;
57118              }
57119              this.direction.setPosition(node, pos, level);
57120              this._validatePositionAndContinue(node, level, pos);
57121
57122              handledNodeCount++;
57123            }
57124          }
57125        }
57126      }
57127    }
57128
57129    /**
57130     * This is a recursively called function to enumerate the branches from the largest hubs and place the nodes
57131     * on a X position that ensures there will be no overlap.
57132     *
57133     * @param {Node.id} parentId
57134     * @param {number} parentLevel
57135     * @private
57136     */
57137
57138  }, {
57139    key: '_placeBranchNodes',
57140    value: function _placeBranchNodes(parentId, parentLevel) {
57141      var childRef = this.hierarchical.childrenReference[parentId];
57142
57143      // if this is not a parent, cancel the placing. This can happen with multiple parents to one child.
57144      if (childRef === undefined) {
57145        return;
57146      }
57147
57148      // get a list of childNodes
57149      var childNodes = [];
57150      for (var i = 0; i < childRef.length; i++) {
57151        childNodes.push(this.body.nodes[childRef[i]]);
57152      }
57153
57154      // use the positions to order the nodes.
57155      this.direction.sort(childNodes);
57156
57157      // position the childNodes
57158      for (var _i5 = 0; _i5 < childNodes.length; _i5++) {
57159        var childNode = childNodes[_i5];
57160        var childNodeLevel = this.hierarchical.levels[childNode.id];
57161        // check if the child node is below the parent node and if it has already been positioned.
57162        if (childNodeLevel > parentLevel && this.positionedNodes[childNode.id] === undefined) {
57163          // get the amount of space required for this node. If parent the width is based on the amount of children.
57164          var spacing = this.options.hierarchical.nodeSpacing;
57165          var pos = void 0;
57166
57167          // we get the X or Y values we need and store them in pos and previousPos.
57168          // The get and set make sure we get X or Y
57169          if (_i5 === 0) {
57170            pos = this.direction.getPosition(this.body.nodes[parentId]);
57171          } else {
57172            pos = this.direction.getPosition(childNodes[_i5 - 1]) + spacing;
57173          }
57174          this.direction.setPosition(childNode, pos, childNodeLevel);
57175          this._validatePositionAndContinue(childNode, childNodeLevel, pos);
57176        } else {
57177          return;
57178        }
57179      }
57180
57181      // center the parent nodes.
57182      var center = this._getCenterPosition(childNodes);
57183      this.direction.setPosition(this.body.nodes[parentId], center, parentLevel);
57184    }
57185
57186    /**
57187     * This method checks for overlap and if required shifts the branch. It also keeps records of positioned nodes.
57188     * Finally it will call _placeBranchNodes to place the branch nodes.
57189     * @param {Node} node
57190     * @param {number} level
57191     * @param {number} pos
57192     * @private
57193     */
57194
57195  }, {
57196    key: '_validatePositionAndContinue',
57197    value: function _validatePositionAndContinue(node, level, pos) {
57198      // This method only works for formal trees and formal forests
57199      // Early exit if this is not the case
57200      if (!this.hierarchical.isTree) return;
57201
57202      // if overlap has been detected, we shift the branch
57203      if (this.lastNodeOnLevel[level] !== undefined) {
57204        var previousPos = this.direction.getPosition(this.body.nodes[this.lastNodeOnLevel[level]]);
57205        if (pos - previousPos < this.options.hierarchical.nodeSpacing) {
57206          var diff = previousPos + this.options.hierarchical.nodeSpacing - pos;
57207          var sharedParent = this._findCommonParent(this.lastNodeOnLevel[level], node.id);
57208          this._shiftBlock(sharedParent.withChild, diff);
57209        }
57210      }
57211
57212      this.lastNodeOnLevel[level] = node.id; // store change in position.
57213      this.positionedNodes[node.id] = true;
57214      this._placeBranchNodes(node.id, level);
57215    }
57216
57217    /**
57218     * Receives an array with node indices and returns an array with the actual node references.
57219     * Used for sorting based on node properties.
57220     * @param {Array.<Node.id>} idArray
57221     * @returns {Array.<Node>}
57222     */
57223
57224  }, {
57225    key: '_indexArrayToNodes',
57226    value: function _indexArrayToNodes(idArray) {
57227      var array = [];
57228      for (var i = 0; i < idArray.length; i++) {
57229        array.push(this.body.nodes[idArray[i]]);
57230      }
57231      return array;
57232    }
57233
57234    /**
57235     * This function get the distribution of levels based on hubsize
57236     *
57237     * @returns {Object}
57238     * @private
57239     */
57240
57241  }, {
57242    key: '_getDistribution',
57243    value: function _getDistribution() {
57244      var distribution = {};
57245      var nodeId = void 0,
57246          node = void 0;
57247
57248      // we fix Y because the hierarchy is vertical,
57249      // we fix X so we do not give a node an x position for a second time.
57250      // the fix of X is removed after the x value has been set.
57251      for (nodeId in this.body.nodes) {
57252        if (this.body.nodes.hasOwnProperty(nodeId)) {
57253          node = this.body.nodes[nodeId];
57254          var level = this.hierarchical.levels[nodeId] === undefined ? 0 : this.hierarchical.levels[nodeId];
57255          this.direction.fix(node, level);
57256          if (distribution[level] === undefined) {
57257            distribution[level] = {};
57258          }
57259          distribution[level][nodeId] = node;
57260        }
57261      }
57262      return distribution;
57263    }
57264
57265    /**
57266     * Return the active (i.e. visible) edges for this node
57267     *
57268     * @param {Node} node
57269     * @returns {Array.<vis.Edge>} Array of edge instances
57270     * @private
57271     */
57272
57273  }, {
57274    key: '_getActiveEdges',
57275    value: function _getActiveEdges(node) {
57276      var _this4 = this;
57277
57278      var result = [];
57279
57280      util.forEach(node.edges, function (edge) {
57281        if (_this4.body.edgeIndices.indexOf(edge.id) !== -1) {
57282          result.push(edge);
57283        }
57284      });
57285
57286      return result;
57287    }
57288
57289    /**
57290     * Get the hubsizes for all active nodes.
57291     *
57292     * @returns {number}
57293     * @private
57294     */
57295
57296  }, {
57297    key: '_getHubSizes',
57298    value: function _getHubSizes() {
57299      var _this5 = this;
57300
57301      var hubSizes = {};
57302      var nodeIds = this.body.nodeIndices;
57303
57304      util.forEach(nodeIds, function (nodeId) {
57305        var node = _this5.body.nodes[nodeId];
57306        var hubSize = _this5._getActiveEdges(node).length;
57307        hubSizes[hubSize] = true;
57308      });
57309
57310      // Make an array of the size sorted descending
57311      var result = [];
57312      util.forEach(hubSizes, function (size) {
57313        result.push(Number(size));
57314      });
57315
57316      result.sort(function (a, b) {
57317        return b - a;
57318      });
57319
57320      return result;
57321    }
57322
57323    /**
57324     * this function allocates nodes in levels based on the recursive branching from the largest hubs.
57325     *
57326     * @private
57327     */
57328
57329  }, {
57330    key: '_determineLevelsByHubsize',
57331    value: function _determineLevelsByHubsize() {
57332      var _this6 = this;
57333
57334      var levelDownstream = function levelDownstream(nodeA, nodeB) {
57335        _this6.hierarchical.levelDownstream(nodeA, nodeB);
57336      };
57337
57338      var hubSizes = this._getHubSizes();
57339
57340      var _loop = function _loop(i) {
57341        var hubSize = hubSizes[i];
57342        if (hubSize === 0) return 'break';
57343
57344        util.forEach(_this6.body.nodeIndices, function (nodeId) {
57345          var node = _this6.body.nodes[nodeId];
57346
57347          if (hubSize === _this6._getActiveEdges(node).length) {
57348            _this6._crawlNetwork(levelDownstream, nodeId);
57349          }
57350        });
57351      };
57352
57353      for (var i = 0; i < hubSizes.length; ++i) {
57354        var _ret = _loop(i);
57355
57356        if (_ret === 'break') break;
57357      }
57358    }
57359
57360    /**
57361     * TODO: release feature
57362     * TODO: Determine if this feature is needed at all
57363     *
57364     * @private
57365     */
57366
57367  }, {
57368    key: '_determineLevelsCustomCallback',
57369    value: function _determineLevelsCustomCallback() {
57370      var _this7 = this;
57371
57372      var minLevel = 100000;
57373
57374      // TODO: this should come from options.
57375      var customCallback = function customCallback(nodeA, nodeB, edge) {// eslint-disable-line no-unused-vars
57376
57377      };
57378
57379      // TODO: perhaps move to HierarchicalStatus.
57380      //       But I currently don't see the point, this method is not used.
57381      var levelByDirection = function levelByDirection(nodeA, nodeB, edge) {
57382        var levelA = _this7.hierarchical.levels[nodeA.id];
57383        // set initial level
57384        if (levelA === undefined) {
57385          levelA = _this7.hierarchical.levels[nodeA.id] = minLevel;
57386        }
57387
57388        var diff = customCallback(NetworkUtil.cloneOptions(nodeA, 'node'), NetworkUtil.cloneOptions(nodeB, 'node'), NetworkUtil.cloneOptions(edge, 'edge'));
57389
57390        _this7.hierarchical.levels[nodeB.id] = levelA + diff;
57391      };
57392
57393      this._crawlNetwork(levelByDirection);
57394      this.hierarchical.setMinLevelToZero(this.body.nodes);
57395    }
57396
57397    /**
57398     * Allocate nodes in levels based on the direction of the edges.
57399     *
57400     * @private
57401     */
57402
57403  }, {
57404    key: '_determineLevelsDirected',
57405    value: function _determineLevelsDirected() {
57406      var _this8 = this;
57407
57408      var minLevel = 10000;
57409
57410      /**
57411       * Check if there is an edge going the opposite direction for given edge
57412       *
57413       * @param {Edge} edge  edge to check
57414       * @returns {boolean} true if there's another edge going into the opposite direction
57415       */
57416      var isBidirectional = function isBidirectional(edge) {
57417        util.forEach(_this8.body.edges, function (otherEdge) {
57418          if (otherEdge.toId === edge.fromId && otherEdge.fromId === edge.toId) {
57419            return true;
57420          }
57421        });
57422
57423        return false;
57424      };
57425
57426      var levelByDirection = function levelByDirection(nodeA, nodeB, edge) {
57427        var levelA = _this8.hierarchical.levels[nodeA.id];
57428        var levelB = _this8.hierarchical.levels[nodeB.id];
57429
57430        if (isBidirectional(edge) && levelA !== undefined && levelB !== undefined) {
57431          // Don't redo the level determination if already done in this case.
57432          return;
57433        }
57434
57435        // set initial level
57436        if (levelA === undefined) {
57437          levelA = _this8.hierarchical.levels[nodeA.id] = minLevel;
57438        }
57439        if (edge.toId == nodeB.id) {
57440          _this8.hierarchical.levels[nodeB.id] = levelA + 1;
57441        } else {
57442          _this8.hierarchical.levels[nodeB.id] = levelA - 1;
57443        }
57444      };
57445
57446      this._crawlNetwork(levelByDirection);
57447      this.hierarchical.setMinLevelToZero(this.body.nodes);
57448    }
57449
57450    /**
57451     * Update the bookkeeping of parent and child.
57452     * @private
57453     */
57454
57455  }, {
57456    key: '_generateMap',
57457    value: function _generateMap() {
57458      var _this9 = this;
57459
57460      var fillInRelations = function fillInRelations(parentNode, childNode) {
57461        if (_this9.hierarchical.levels[childNode.id] > _this9.hierarchical.levels[parentNode.id]) {
57462          _this9.hierarchical.addRelation(parentNode.id, childNode.id);
57463        }
57464      };
57465
57466      this._crawlNetwork(fillInRelations);
57467      this.hierarchical.checkIfTree();
57468    }
57469
57470    /**
57471     * Crawl over the entire network and use a callback on each node couple that is connected to each other.
57472     * @param {function} [callback=function(){}]          | will receive nodeA, nodeB and the connecting edge. A and B are distinct.
57473     * @param {Node.id} startingNodeId
57474     * @private
57475     */
57476
57477  }, {
57478    key: '_crawlNetwork',
57479    value: function _crawlNetwork() {
57480      var _this10 = this;
57481
57482      var callback = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : function () {};
57483      var startingNodeId = arguments[1];
57484
57485      var progress = {};
57486
57487      var crawler = function crawler(node, tree) {
57488        if (progress[node.id] === undefined) {
57489          _this10.hierarchical.setTreeIndex(node, tree);
57490
57491          progress[node.id] = true;
57492          var childNode = void 0;
57493          var edges = _this10._getActiveEdges(node);
57494          for (var i = 0; i < edges.length; i++) {
57495            var edge = edges[i];
57496            if (edge.connected === true) {
57497              if (edge.toId == node.id) {
57498                // Not '===' because id's can be string and numeric
57499                childNode = edge.from;
57500              } else {
57501                childNode = edge.to;
57502              }
57503
57504              if (node.id != childNode.id) {
57505                // Not '!==' because id's can be string and numeric
57506                callback(node, childNode, edge);
57507                crawler(childNode, tree);
57508              }
57509            }
57510          }
57511        }
57512      };
57513
57514      if (startingNodeId === undefined) {
57515        // Crawl over all nodes
57516        var treeIndex = 0; // Serves to pass a unique id for the current distinct tree
57517
57518        for (var i = 0; i < this.body.nodeIndices.length; i++) {
57519          var nodeId = this.body.nodeIndices[i];
57520
57521          if (progress[nodeId] === undefined) {
57522            var node = this.body.nodes[nodeId];
57523            crawler(node, treeIndex);
57524            treeIndex += 1;
57525          }
57526        }
57527      } else {
57528        // Crawl from the given starting node
57529        var _node2 = this.body.nodes[startingNodeId];
57530        if (_node2 === undefined) {
57531          console.error("Node not found:", startingNodeId);
57532          return;
57533        }
57534        crawler(_node2);
57535      }
57536    }
57537
57538    /**
57539     * Shift a branch a certain distance
57540     * @param {Node.id} parentId
57541     * @param {number} diff
57542     * @private
57543     */
57544
57545  }, {
57546    key: '_shiftBlock',
57547    value: function _shiftBlock(parentId, diff) {
57548      var _this11 = this;
57549
57550      var progress = {};
57551      var shifter = function shifter(parentId) {
57552        if (progress[parentId]) {
57553          return;
57554        }
57555        progress[parentId] = true;
57556        _this11.direction.shift(parentId, diff);
57557
57558        var childRef = _this11.hierarchical.childrenReference[parentId];
57559        if (childRef !== undefined) {
57560          for (var i = 0; i < childRef.length; i++) {
57561            shifter(childRef[i]);
57562          }
57563        }
57564      };
57565      shifter(parentId);
57566    }
57567
57568    /**
57569     * Find a common parent between branches.
57570     * @param {Node.id} childA
57571     * @param {Node.id} childB
57572     * @returns {{foundParent, withChild}}
57573     * @private
57574     */
57575
57576  }, {
57577    key: '_findCommonParent',
57578    value: function _findCommonParent(childA, childB) {
57579      var _this12 = this;
57580
57581      var parents = {};
57582      var iterateParents = function iterateParents(parents, child) {
57583        var parentRef = _this12.hierarchical.parentReference[child];
57584        if (parentRef !== undefined) {
57585          for (var i = 0; i < parentRef.length; i++) {
57586            var parent = parentRef[i];
57587            parents[parent] = true;
57588            iterateParents(parents, parent);
57589          }
57590        }
57591      };
57592      var findParent = function findParent(parents, child) {
57593        var parentRef = _this12.hierarchical.parentReference[child];
57594        if (parentRef !== undefined) {
57595          for (var i = 0; i < parentRef.length; i++) {
57596            var parent = parentRef[i];
57597            if (parents[parent] !== undefined) {
57598              return { foundParent: parent, withChild: child };
57599            }
57600            var branch = findParent(parents, parent);
57601            if (branch.foundParent !== null) {
57602              return branch;
57603            }
57604          }
57605        }
57606        return { foundParent: null, withChild: child };
57607      };
57608
57609      iterateParents(parents, childA);
57610      return findParent(parents, childB);
57611    }
57612
57613    /**
57614     * Set the strategy pattern for handling the coordinates given the current direction.
57615     *
57616     * The individual instances contain all the operations and data specific to a layout direction.
57617     *
57618     * @param {Node} node
57619     * @param {{x: number, y: number}} position
57620     * @param {number} level
57621     * @param {boolean} [doNotUpdate=false]
57622     * @private
57623     */
57624
57625  }, {
57626    key: 'setDirectionStrategy',
57627    value: function setDirectionStrategy() {
57628      var isVertical = this.options.hierarchical.direction === 'UD' || this.options.hierarchical.direction === 'DU';
57629
57630      if (isVertical) {
57631        this.direction = new VerticalStrategy(this);
57632      } else {
57633        this.direction = new HorizontalStrategy(this);
57634      }
57635    }
57636
57637    /**
57638     * Determine the center position of a branch from the passed list of child nodes
57639     *
57640     * This takes into account the positions of all the child nodes.
57641     * @param {Array.<Node|vis.Node.id>} childNodes  Array of either child nodes or node id's
57642     * @return {number}
57643     * @private
57644     */
57645
57646  }, {
57647    key: '_getCenterPosition',
57648    value: function _getCenterPosition(childNodes) {
57649      var minPos = 1e9;
57650      var maxPos = -1e9;
57651
57652      for (var i = 0; i < childNodes.length; i++) {
57653        var childNode = void 0;
57654        if (childNodes[i].id !== undefined) {
57655          childNode = childNodes[i];
57656        } else {
57657          var childNodeId = childNodes[i];
57658          childNode = this.body.nodes[childNodeId];
57659        }
57660
57661        var position = this.direction.getPosition(childNode);
57662        minPos = Math.min(minPos, position);
57663        maxPos = Math.max(maxPos, position);
57664      }
57665
57666      return 0.5 * (minPos + maxPos);
57667    }
57668  }]);
57669  return LayoutEngine;
57670}();
57671
57672exports['default'] = LayoutEngine;
57673
57674/***/ }),
57675/* 236 */
57676/***/ (function(module, exports, __webpack_require__) {
57677
57678"use strict";
57679
57680
57681Object.defineProperty(exports, "__esModule", {
57682  value: true
57683});
57684exports.VerticalStrategy = exports.HorizontalStrategy = undefined;
57685
57686var _getPrototypeOf = __webpack_require__(3);
57687
57688var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf);
57689
57690var _possibleConstructorReturn2 = __webpack_require__(4);
57691
57692var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2);
57693
57694var _inherits2 = __webpack_require__(5);
57695
57696var _inherits3 = _interopRequireDefault(_inherits2);
57697
57698var _classCallCheck2 = __webpack_require__(0);
57699
57700var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
57701
57702var _createClass2 = __webpack_require__(1);
57703
57704var _createClass3 = _interopRequireDefault(_createClass2);
57705
57706function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
57707
57708/**
57709 * Helper classes for LayoutEngine.
57710 *
57711 * Strategy pattern for usage of direction methods for hierarchical layouts.
57712 */
57713
57714/**
57715 * Interface definition for direction strategy classes.
57716 *
57717 * This class describes the interface for the Strategy
57718 * pattern classes used to differentiate horizontal and vertical
57719 * direction of hierarchical results.
57720 *
57721 * For a given direction, one coordinate will be 'fixed', meaning that it is
57722 * determined by level.
57723 * The other coordinate is 'unfixed', meaning that the nodes on a given level
57724 * can still move along that coordinate. So:
57725 *
57726 * - `vertical` layout: `x` unfixed, `y` fixed per level
57727 * - `horizontal` layout: `x` fixed per level, `y` unfixed
57728 *
57729 * The local methods are stubs and should be regarded as abstract.
57730 * Derived classes **must** implement all the methods themselves.
57731 *
57732 * @private
57733 */
57734var DirectionInterface = function () {
57735  function DirectionInterface() {
57736    (0, _classCallCheck3['default'])(this, DirectionInterface);
57737  }
57738
57739  (0, _createClass3['default'])(DirectionInterface, [{
57740    key: 'abstract',
57741
57742    /** @ignore **/
57743    value: function abstract() {
57744      throw new Error("Can't instantiate abstract class!");
57745    }
57746
57747    /**
57748     * This is a dummy call which is used to suppress the jsdoc errors of type:
57749     *
57750     *   "'param' is assigned a value but never used"
57751     *
57752     * @ignore
57753     **/
57754
57755  }, {
57756    key: 'fake_use',
57757    value: function fake_use() {}
57758    // Do nothing special
57759
57760
57761    /**
57762     * Type to use to translate dynamic curves to, in the case of hierarchical layout.
57763     * Dynamic curves do not work for these.
57764     *
57765     * The value should be perpendicular to the actual direction of the layout.
57766     *
57767     * @return {string} Direction, either 'vertical' or 'horizontal'
57768     */
57769
57770  }, {
57771    key: 'curveType',
57772    value: function curveType() {
57773      return this.abstract();
57774    }
57775
57776    /**
57777     * Return the value of the coordinate that is not fixed for this direction.
57778     *
57779     * @param {Node} node The node to read
57780     * @return {number} Value of the unfixed coordinate
57781     */
57782
57783  }, {
57784    key: 'getPosition',
57785    value: function getPosition(node) {
57786      this.fake_use(node);return this.abstract();
57787    }
57788
57789    /**
57790     * Set the value of the coordinate that is not fixed for this direction.
57791     *
57792     * @param {Node} node The node to adjust
57793     * @param {number} position
57794     * @param {number} [level] if specified, the hierarchy level that this node should be fixed to
57795     */
57796
57797  }, {
57798    key: 'setPosition',
57799    value: function setPosition(node, position) {
57800      var level = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : undefined;
57801      this.fake_use(node, position, level);this.abstract();
57802    }
57803
57804    /**
57805     * Get the width of a tree.
57806     *
57807     * A `tree` here is a subset of nodes within the network which are not connected to other nodes,
57808     * only among themselves. In essence, it is a sub-network.
57809     *
57810     * @param {number} index The index number of a tree
57811     * @return {number} the width of a tree in the view coordinates
57812     */
57813
57814  }, {
57815    key: 'getTreeSize',
57816    value: function getTreeSize(index) {
57817      this.fake_use(index);return this.abstract();
57818    }
57819
57820    /**
57821     * Sort array of nodes on the unfixed coordinates.
57822     *
57823     * @param {Array.<Node>} nodeArray array of nodes to sort
57824     */
57825
57826  }, {
57827    key: 'sort',
57828    value: function sort(nodeArray) {
57829      this.fake_use(nodeArray);this.abstract();
57830    }
57831
57832    /**
57833     * Assign the fixed coordinate of the node to the given level
57834     *
57835     * @param {Node} node The node to adjust
57836     * @param {number} level The level to fix to
57837     */
57838
57839  }, {
57840    key: 'fix',
57841    value: function fix(node, level) {
57842      this.fake_use(node, level);this.abstract();
57843    }
57844
57845    /**
57846     * Add an offset to the unfixed coordinate of the given node. 
57847     *
57848     * @param {NodeId} nodeId Id of the node to adjust
57849     * @param {number} diff Offset to add to the unfixed coordinate
57850     */
57851
57852  }, {
57853    key: 'shift',
57854    value: function shift(nodeId, diff) {
57855      this.fake_use(nodeId, diff);this.abstract();
57856    }
57857  }]);
57858  return DirectionInterface;
57859}();
57860
57861/**
57862 * Vertical Strategy
57863 *
57864 * Coordinate `y` is fixed on levels, coordinate `x` is unfixed.
57865 *
57866 * @extends DirectionInterface
57867 * @private
57868 */
57869
57870
57871var VerticalStrategy = function (_DirectionInterface) {
57872  (0, _inherits3['default'])(VerticalStrategy, _DirectionInterface);
57873
57874  /**
57875   * Constructor
57876   *
57877   * @param {Object} layout reference to the parent LayoutEngine instance.
57878   */
57879  function VerticalStrategy(layout) {
57880    (0, _classCallCheck3['default'])(this, VerticalStrategy);
57881
57882    var _this = (0, _possibleConstructorReturn3['default'])(this, (VerticalStrategy.__proto__ || (0, _getPrototypeOf2['default'])(VerticalStrategy)).call(this));
57883
57884    _this.layout = layout;
57885    return _this;
57886  }
57887
57888  /** @inheritdoc */
57889
57890
57891  (0, _createClass3['default'])(VerticalStrategy, [{
57892    key: 'curveType',
57893    value: function curveType() {
57894      return 'horizontal';
57895    }
57896
57897    /** @inheritdoc */
57898
57899  }, {
57900    key: 'getPosition',
57901    value: function getPosition(node) {
57902      return node.x;
57903    }
57904
57905    /** @inheritdoc */
57906
57907  }, {
57908    key: 'setPosition',
57909    value: function setPosition(node, position) {
57910      var level = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : undefined;
57911
57912      if (level !== undefined) {
57913        this.layout.hierarchical.addToOrdering(node, level);
57914      }
57915      node.x = position;
57916    }
57917
57918    /** @inheritdoc */
57919
57920  }, {
57921    key: 'getTreeSize',
57922    value: function getTreeSize(index) {
57923      var res = this.layout.hierarchical.getTreeSize(this.layout.body.nodes, index);
57924      return { min: res.min_x, max: res.max_x };
57925    }
57926
57927    /** @inheritdoc */
57928
57929  }, {
57930    key: 'sort',
57931    value: function sort(nodeArray) {
57932      nodeArray.sort(function (a, b) {
57933        // Test on 'undefined' takes care of divergent behaviour in chrome
57934        if (a.x === undefined || b.x === undefined) return 0; // THIS HAPPENS
57935        return a.x - b.x;
57936      });
57937    }
57938
57939    /** @inheritdoc */
57940
57941  }, {
57942    key: 'fix',
57943    value: function fix(node, level) {
57944      node.y = this.layout.options.hierarchical.levelSeparation * level;
57945      node.options.fixed.y = true;
57946    }
57947
57948    /** @inheritdoc */
57949
57950  }, {
57951    key: 'shift',
57952    value: function shift(nodeId, diff) {
57953      this.layout.body.nodes[nodeId].x += diff;
57954    }
57955  }]);
57956  return VerticalStrategy;
57957}(DirectionInterface);
57958
57959/**
57960 * Horizontal Strategy
57961 *
57962 * Coordinate `x` is fixed on levels, coordinate `y` is unfixed.
57963 *
57964 * @extends DirectionInterface
57965 * @private
57966 */
57967
57968
57969var HorizontalStrategy = function (_DirectionInterface2) {
57970  (0, _inherits3['default'])(HorizontalStrategy, _DirectionInterface2);
57971
57972  /**
57973   * Constructor
57974   *
57975   * @param {Object} layout reference to the parent LayoutEngine instance.
57976   */
57977  function HorizontalStrategy(layout) {
57978    (0, _classCallCheck3['default'])(this, HorizontalStrategy);
57979
57980    var _this2 = (0, _possibleConstructorReturn3['default'])(this, (HorizontalStrategy.__proto__ || (0, _getPrototypeOf2['default'])(HorizontalStrategy)).c
57980all(this));
57981
57982    _this2.layout = layout;
57983    return _this2;
57984  }
57985
57986  /** @inheritdoc */
57987
57988
57989  (0, _createClass3['default'])(HorizontalStrategy, [{
57990    key: 'curveType',
57991    value: function curveType() {
57992      return 'vertical';
57993    }
57994
57995    /** @inheritdoc */
57996
57997  }, {
57998    key: 'getPosition',
57999    value: function getPosition(node) {
58000      return node.y;
58001    }
58002
58003    /** @inheritdoc */
58004
58005  }, {
58006    key: 'setPosition',
58007    value: function setPosition(node, position) {
58008      var level = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : undefined;
58009
58010      if (level !== undefined) {
58011        this.layout.hierarchical.addToOrdering(node, level);
58012      }
58013      node.y = position;
58014    }
58015
58016    /** @inheritdoc */
58017
58018  }, {
58019    key: 'getTreeSize',
58020    value: function getTreeSize(index) {
58021      var res = this.layout.hierarchical.getTreeSize(this.layout.body.nodes, index);
58022      return { min: res.min_y, max: res.max_y };
58023    }
58024
58025    /** @inheritdoc */
58026
58027  }, {
58028    key: 'sort',
58029    value: function sort(nodeArray) {
58030      nodeArray.sort(function (a, b) {
58031        // Test on 'undefined' takes care of divergent behaviour in chrome
58032        if (a.y === undefined || b.y === undefined) return 0; // THIS HAPPENS
58033        return a.y - b.y;
58034      });
58035    }
58036
58037    /** @inheritdoc */
58038
58039  }, {
58040    key: 'fix',
58041    value: function fix(node, level) {
58042      node.x = this.layout.options.hierarchical.levelSeparation * level;
58043      node.options.fixed.x = true;
58044    }
58045
58046    /** @inheritdoc */
58047
58048  }, {
58049    key: 'shift',
58050    value: function shift(nodeId, diff) {
58051      this.layout.body.nodes[nodeId].y += diff;
58052    }
58053  }]);
58054  return HorizontalStrategy;
58055}(DirectionInterface);
58056
58057exports.HorizontalStrategy = HorizontalStrategy;
58058exports.VerticalStrategy = VerticalStrategy;
58059
58060/***/ }),
58061/* 237 */
58062/***/ (function(module, exports, __webpack_require__) {
58063
58064"use strict";
58065
58066
58067Object.defineProperty(exports, "__esModule", {
58068  value: true
58069});
58070
58071var _keys = __webpack_require__(8);
58072
58073var _keys2 = _interopRequireDefault(_keys);
58074
58075var _stringify = __webpack_require__(19);
58076
58077var _stringify2 = _interopRequireDefault(_stringify);
58078
58079var _typeof2 = __webpack_require__(6);
58080
58081var _typeof3 = _interopRequireDefault(_typeof2);
58082
58083var _classCallCheck2 = __webpack_require__(0);
58084
58085var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
58086
58087var _createClass2 = __webpack_require__(1);
58088
58089var _createClass3 = _interopRequireDefault(_createClass2);
58090
58091function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
58092
58093var util = __webpack_require__(2);
58094var Hammer = __webpack_require__(10);
58095var hammerUtil = __webpack_require__(37);
58096
58097/**
58098 * Clears the toolbar div element of children
58099 *
58100 * @private
58101 */
58102
58103var ManipulationSystem = function () {
58104  /**
58105   * @param {Object} body
58106   * @param {Canvas} canvas
58107   * @param {SelectionHandler} selectionHandler
58108   */
58109  function ManipulationSystem(body, canvas, selectionHandler) {
58110    var _this = this;
58111
58112    (0, _classCallCheck3['default'])(this, ManipulationSystem);
58113
58114    this.body = body;
58115    this.canvas = canvas;
58116    this.selectionHandler = selectionHandler;
58117
58118    this.editMode = false;
58119    this.manipulationDiv = undefined;
58120    this.editModeDiv = undefined;
58121    this.closeDiv = undefined;
58122
58123    this.manipulationHammers = [];
58124    this.temporaryUIFunctions = {};
58125    this.temporaryEventFunctions = [];
58126
58127    this.touchTime = 0;
58128    this.temporaryIds = { nodes: [], edges: [] };
58129    this.guiEnabled = false;
58130    this.inMode = false;
58131    this.selectedControlNode = undefined;
58132
58133    this.options = {};
58134    this.defaultOptions = {
58135      enabled: false,
58136      initiallyActive: false,
58137      addNode: true,
58138      addEdge: true,
58139      editNode: undefined,
58140      editEdge: true,
58141      deleteNode: true,
58142      deleteEdge: true,
58143      controlNodeStyle: {
58144        shape: 'dot',
58145        size: 6,
58146        color: { background: '#ff0000', border: '#3c3c3c', highlight: { background: '#07f968', border: '#3c3c3c' } },
58147        borderWidth: 2,
58148        borderWidthSelected: 2
58149      }
58150    };
58151    util.extend(this.options, this.defaultOptions);
58152
58153    this.body.emitter.on('destroy', function () {
58154      _this._clean();
58155    });
58156    this.body.emitter.on('_dataChanged', this._restore.bind(this));
58157    this.body.emitter.on('_resetData', this._restore.bind(this));
58158  }
58159
58160  /**
58161   * If something changes in the data during editing, switch back to the initial datamanipulation state and close all edit modes.
58162   * @private
58163   */
58164
58165
58166  (0, _createClass3['default'])(ManipulationSystem, [{
58167    key: '_restore',
58168    value: function _restore() {
58169      if (this.inMode !== false) {
58170        if (this.options.initiallyActive === true) {
58171          this.enableEditMode();
58172        } else {
58173          this.disableEditMode();
58174        }
58175      }
58176    }
58177
58178    /**
58179     * Set the Options
58180     *
58181     * @param {Object} options
58182     * @param {Object} allOptions
58183     * @param {Object} globalOptions
58184     */
58185
58186  }, {
58187    key: 'setOptions',
58188    value: function setOptions(options, allOptions, globalOptions) {
58189      if (allOptions !== undefined) {
58190        if (allOptions.locale !== undefined) {
58191          this.options.locale = allOptions.locale;
58192        } else {
58193          this.options.locale = globalOptions.locale;
58194        }
58195        if (allOptions.locales !== undefined) {
58196          this.options.locales = allOptions.locales;
58197        } else {
58198          this.options.locales = globalOptions.locales;
58199        }
58200      }
58201
58202      if (options !== undefined) {
58203        if (typeof options === 'boolean') {
58204          this.options.enabled = options;
58205        } else {
58206          this.options.enabled = true;
58207          util.deepExtend(this.options, options);
58208        }
58209        if (this.options.initiallyActive === true) {
58210          this.editMode = true;
58211        }
58212        this._setup();
58213      }
58214    }
58215
58216    /**
58217     * Enable or disable edit-mode. Draws the DOM required and cleans up after itself.
58218     *
58219     * @private
58220     */
58221
58222  }, {
58223    key: 'toggleEditMode',
58224    value: function toggleEditMode() {
58225      if (this.editMode === true) {
58226        this.disableEditMode();
58227      } else {
58228        this.enableEditMode();
58229      }
58230    }
58231
58232    /**
58233     * Enables Edit Mode
58234     */
58235
58236  }, {
58237    key: 'enableEditMode',
58238    value: function enableEditMode() {
58239      this.editMode = true;
58240
58241      this._clean();
58242      if (this.guiEnabled === true) {
58243        this.manipulationDiv.style.display = 'block';
58244        this.closeDiv.style.display = 'block';
58245        this.editModeDiv.style.display = 'none';
58246        this.showManipulatorToolbar();
58247      }
58248    }
58249
58250    /**
58251     * Disables Edit Mode
58252     */
58253
58254  }, {
58255    key: 'disableEditMode',
58256    value: function disableEditMode() {
58257      this.editMode = false;
58258
58259      this._clean();
58260      if (this.guiEnabled === true) {
58261        this.manipulationDiv.style.display = 'none';
58262        this.closeDiv.style.display = 'none';
58263        this.editModeDiv.style.display = 'block';
58264        this._createEditButton();
58265      }
58266    }
58267
58268    /**
58269     * Creates the main toolbar. Removes functions bound to the select event. Binds all the buttons of the toolbar.
58270     *
58271     * @private
58272     */
58273
58274  }, {
58275    key: 'showManipulatorToolbar',
58276    value: function showManipulatorToolbar() {
58277      // restore the state of any bound functions or events, remove control nodes, restore physics
58278      this._clean();
58279
58280      // reset global variables
58281      this.manipulationDOM = {};
58282
58283      // if the gui is enabled, draw all elements.
58284      if (this.guiEnabled === true) {
58285        // a _restore will hide these menus
58286        this.editMode = true;
58287        this.manipulationDiv.style.display = 'block';
58288        this.closeDiv.style.display = 'block';
58289
58290        var selectedNodeCount = this.selectionHandler._getSelectedNodeCount();
58291        var selectedEdgeCount = this.selectionHandler._getSelectedEdgeCount();
58292        var selectedTotalCount = selectedNodeCount + selectedEdgeCount;
58293        var locale = this.options.locales[this.options.locale];
58294        var needSeperator = false;
58295
58296        if (this.options.addNode !== false) {
58297          this._createAddNodeButton(locale);
58298          needSeperator = true;
58299        }
58300        if (this.options.addEdge !== false) {
58301          if (needSeperator === true) {
58302            this._createSeperator(1);
58303          } else {
58304            needSeperator = true;
58305          }
58306          this._createAddEdgeButton(locale);
58307        }
58308
58309        if (selectedNodeCount === 1 && typeof this.options.editNode === 'function') {
58310          if (needSeperator === true) {
58311            this._createSeperator(2);
58312          } else {
58313            needSeperator = true;
58314          }
58315          this._createEditNodeButton(locale);
58316        } else if (selectedEdgeCount === 1 && selectedNodeCount === 0 && this.options.editEdge !== false) {
58317          if (needSeperator === true) {
58318            this._createSeperator(3);
58319          } else {
58320            needSeperator = true;
58321          }
58322          this._createEditEdgeButton(locale);
58323        }
58324
58325        // remove buttons
58326        if (selectedTotalCount !== 0) {
58327          if (selectedNodeCount > 0 && this.options.deleteNode !== false) {
58328            if (needSeperator === true) {
58329              this._createSeperator(4);
58330            }
58331            this._createDeleteButton(locale);
58332          } else if (selectedNodeCount === 0 && this.options.deleteEdge !== false) {
58333            if (needSeperator === true) {
58334              this._createSeperator(4);
58335            }
58336            this._createDeleteButton(locale);
58337          }
58338        }
58339
58340        // bind the close button
58341        this._bindHammerToDiv(this.closeDiv, this.toggleEditMode.bind(this));
58342
58343        // refresh this bar based on what has been selected
58344        this._temporaryBindEvent('select', this.showManipulatorToolbar.bind(this));
58345      }
58346
58347      // redraw to show any possible changes
58348      this.body.emitter.emit('_redraw');
58349    }
58350
58351    /**
58352     * Create the toolbar for adding Nodes
58353     */
58354
58355  }, {
58356    key: 'addNodeMode',
58357    value: function addNodeMode() {
58358      // when using the gui, enable edit mode if it wasnt already.
58359      if (this.editMode !== true) {
58360        this.enableEditMode();
58361      }
58362
58363      // restore the state of any bound functions or events, remove control nodes, restore physics
58364      this._clean();
58365
58366      this.inMode = 'addNode';
58367      if (this.guiEnabled === true) {
58368        var locale = this.options.locales[this.options.locale];
58369        this.manipulationDOM = {};
58370        this._createBackButton(locale);
58371        this._createSeperator();
58372        this._createDescription(locale['addDescription'] || this.options.locales['en']['addDescription']);
58373
58374        // bind the close button
58375        this._bindHammerToDiv(this.closeDiv, this.toggleEditMode.bind(this));
58376      }
58377
58378      this._temporaryBindEvent('click', this._performAddNode.bind(this));
58379    }
58380
58381    /**
58382     * call the bound function to handle the editing of the node. The node has to be selected.
58383     */
58384
58385  }, {
58386    key: 'editNode',
58387    value: function editNode() {
58388      var _this2 = this;
58389
58390      // when using the gui, enable edit mode if it wasnt already.
58391      if (this.editMode !== true) {
58392        this.enableEditMode();
58393      }
58394
58395      // restore the state of any bound functions or events, remove control nodes, restore physics
58396      this._clean();
58397      var node = this.selectionHandler._getSelectedNode();
58398      if (node !== undefined) {
58399        this.inMode = 'editNode';
58400        if (typeof this.options.editNode === 'function') {
58401          if (node.isCluster !== true) {
58402            var data = util.deepExtend({}, node.options, false);
58403            data.x = node.x;
58404            data.y = node.y;
58405
58406            if (this.options.editNode.length === 2) {
58407              this.options.editNode(data, function (finalizedData) {
58408                if (finalizedData !== null && finalizedData !== undefined && _this2.inMode === 'editNode') {
58409                  // if for whatever reason the mode has changes (due to dataset change) disregard the callback) {
58410                  _this2.body.data.nodes.getDataSet().update(finalizedData);
58411                }
58412                _this2.showManipulatorToolbar();
58413              });
58414            } else {
58415              throw new Error('The function for edit does not support two arguments (data, callback)');
58416            }
58417          } else {
58418            alert(this.options.locales[this.options.locale]['editClusterError'] || this.options.locales['en']['editClusterError']);
58419          }
58420        } else {
58421          throw new Error('No function has been configured to handle the editing of nodes.');
58422        }
58423      } else {
58424        this.showManipulatorToolbar();
58425      }
58426    }
58427
58428    /**
58429     * create the toolbar to connect nodes
58430     */
58431
58432  }, {
58433    key: 'addEdgeMode',
58434    value: function addEdgeMode() {
58435      // when using the gui, enable edit mode if it wasnt already.
58436      if (this.editMode !== true) {
58437        this.enableEditMode();
58438      }
58439
58440      // restore the state of any bound functions or events, remove control nodes, restore physics
58441      this._clean();
58442
58443      this.inMode = 'addEdge';
58444      if (this.guiEnabled === true) {
58445        var locale = this.options.locales[this.options.locale];
58446        this.manipulationDOM = {};
58447        this._createBackButton(locale);
58448        this._createSeperator();
58449        this._createDescription(locale['edgeDescription'] || this.options.locales['en']['edgeDescription']);
58450
58451        // bind the close button
58452        this._bindHammerToDiv(this.closeDiv, this.toggleEditMode.bind(this));
58453      }
58454
58455      // temporarily overload functions
58456      this._temporaryBindUI('onTouch', this._handleConnect.bind(this));
58457      this._temporaryBindUI('onDragEnd', this._finishConnect.bind(this));
58458      this._temporaryBindUI('onDrag', this._dragControlNode.bind(this));
58459      this._temporaryBindUI('onRelease', this._finishConnect.bind(this));
58460      this._temporaryBindUI('onDragStart', this._dragStartEdge.bind(this));
58461      this._temporaryBindUI('onHold', function () {});
58462    }
58463
58464    /**
58465     * create the toolbar to edit edges
58466     */
58467
58468  }, {
58469    key: 'editEdgeMode',
58470    value: function editEdgeMode() {
58471      // when using the gui, enable edit mode if it wasn't already.
58472      if (this.editMode !== true) {
58473        this.enableEditMode();
58474      }
58475
58476      // restore the state of any bound functions or events, remove control nodes, restore physics
58477      this._clean();
58478
58479      this.inMode = 'editEdge';
58480      if ((0, _typeof3['default'])(this.options.editEdge) === 'object' && typeof this.options.editEdge.editWithoutDrag === "function") {
58481        this.edgeBeingEditedId = this.selectionHandler.getSelectedEdges()[0];
58482        if (this.edgeBeingEditedId !== undefined) {
58483          var edge = this.body.edges[this.edgeBeingEditedId];
58484          this._performEditEdge(edge.from, edge.to);
58485          return;
58486        }
58487      }
58488      if (this.guiEnabled === true) {
58489        var locale = this.options.locales[this.options.locale];
58490        this.manipulationDOM = {};
58491        this._createBackButton(locale);
58492        this._createSeperator();
58493        this._createDescription(locale['editEdgeDescription'] || this.options.locales['en']['editEdgeDescription']);
58494
58495        // bind the close button
58496        this._bindHammerToDiv(this.closeDiv, this.toggleEditMode.bind(this));
58497      }
58498
58499      this.edgeBeingEditedId = this.selectionHandler.getSelectedEdges()[0];
58500      if (this.edgeBeingEditedId !== undefined) {
58501        var _edge = this.body.edges[this.edgeBeingEditedId];
58502
58503        // create control nodes
58504        var controlNodeFrom = this._getNewTargetNode(_edge.from.x, _edge.from.y);
58505        var controlNodeTo = this._getNewTargetNode(_edge.to.x, _edge.to.y);
58506
58507        this.temporaryIds.nodes.push(controlNodeFrom.id);
58508        this.temporaryIds.nodes.push(controlNodeTo.id);
58509
58510        this.body.nodes[controlNodeFrom.id] = controlNodeFrom;
58511        this.body.nodeIndices.push(controlNodeFrom.id);
58512        this.body.nodes[controlNodeTo.id] = controlNodeTo;
58513        this.body.nodeIndices.push(controlNodeTo.id);
58514
58515        // temporarily overload UI functions, cleaned up automatically because of _temporaryBindUI
58516        this._temporaryBindUI('onTouch', this._controlNodeTouch.bind(this)); // used to get the position
58517        this._temporaryBindUI('onTap', function () {}); // disabled
58518        this._temporaryBindUI('onHold', function () {}); // disabled
58519        this._temporaryBindUI('onDragStart', this._controlNodeDragStart.bind(this)); // used to select control node
58520        this._temporaryBindUI('onDrag', this._controlNodeDrag.bind(this)); // used to drag control node
58521        this._temporaryBindUI('onDragEnd', this._controlNodeDragEnd.bind(this)); // used to connect or revert control nodes
58522        this._temporaryBindUI('onMouseMove', function () {}); // disabled
58523
58524        // create function to position control nodes correctly on movement
58525        // automatically cleaned up because we use the temporary bind
58526        this._temporaryBindEvent('beforeDrawing', function (ctx) {
58527          var positions = _edge.edgeType.findBorderPositions(ctx);
58528          if (controlNodeFrom.selected === false) {
58529            controlNodeFrom.x = positions.from.x;
58530            controlNodeFrom.y = positions.from.y;
58531          }
58532          if (controlNodeTo.selected === false) {
58533            controlNodeTo.x = positions.to.x;
58534            controlNodeTo.y = positions.to.y;
58535          }
58536        });
58537
58538        this.body.emitter.emit('_redraw');
58539      } else {
58540        this.showManipulatorToolbar();
58541      }
58542    }
58543
58544    /**
58545     * delete everything in the selection
58546     */
58547
58548  }, {
58549    key: 'deleteSelected',
58550    value: function deleteSelected() {
58551      var _this3 = this;
58552
58553      // when using the gui, enable edit mode if it wasnt already.
58554      if (this.editMode !== true) {
58555        this.enableEditMode();
58556      }
58557
58558      // restore the state of any bound functions or events, remove control nodes, restore physics
58559      this._clean();
58560
58561      this.inMode = 'delete';
58562      var selectedNodes = this.selectionHandler.getSelectedNodes();
58563      var selectedEdges = this.selectionHandler.getSelectedEdges();
58564      var deleteFunction = undefined;
58565      if (selectedNodes.length > 0) {
58566        for (var i = 0; i < selectedNodes.length; i++) {
58567          if (this.body.nodes[selectedNodes[i]].isCluster === true) {
58568            alert(this.options.locales[this.options.locale]['deleteClusterError'] || this.options.locales['en']['deleteClusterError']);
58569            return;
58570          }
58571        }
58572
58573        if (typeof this.options.deleteNode === 'function') {
58574          deleteFunction = this.options.deleteNode;
58575        }
58576      } else if (selectedEdges.length > 0) {
58577        if (typeof this.options.deleteEdge === 'function') {
58578          deleteFunction = this.options.deleteEdge;
58579        }
58580      }
58581
58582      if (typeof deleteFunction === 'function') {
58583        var data = { nodes: selectedNodes, edges: selectedEdges };
58584        if (deleteFunction.length === 2) {
58585          deleteFunction(data, function (finalizedData) {
58586            if (finalizedData !== null && finalizedData !== undefined && _this3.inMode === 'delete') {
58587              // if for whatever reason the mode has changes (due to dataset change) disregard the callback) {
58588              _this3.body.data.edges.getDataSet().remove(finalizedData.edges);
58589              _this3.body.data.nodes.getDataSet().remove(finalizedData.nodes);
58590              _this3.body.emitter.emit('startSimulation');
58591              _this3.showManipulatorToolbar();
58592            } else {
58593              _this3.body.emitter.emit('startSimulation');
58594              _this3.showManipulatorToolbar();
58595            }
58596          });
58597        } else {
58598          throw new Error('The function for delete does not support two arguments (data, callback)');
58599        }
58600      } else {
58601        this.body.data.edges.getDataSet().remove(selectedEdges);
58602        this.body.data.nodes.getDataSet().remove(selectedNodes);
58603        this.body.emitter.emit('startSimulation');
58604        this.showManipulatorToolbar();
58605      }
58606    }
58607
58608    //********************************************** PRIVATE ***************************************//
58609
58610    /**
58611     * draw or remove the DOM
58612     * @private
58613     */
58614
58615  }, {
58616    key: '_setup',
58617    value: function _setup() {
58618      if (this.options.enabled === true) {
58619        // Enable the GUI
58620        this.guiEnabled = true;
58621
58622        this._createWrappers();
58623        if (this.editMode === false) {
58624          this._createEditButton();
58625        } else {
58626          this.showManipulatorToolbar();
58627        }
58628      } else {
58629        this._removeManipulationDOM();
58630
58631        // disable the gui
58632        this.guiEnabled = false;
58633      }
58634    }
58635
58636    /**
58637     * create the div overlays that contain the DOM
58638     * @private
58639     */
58640
58641  }, {
58642    key: '_createWrappers',
58643    value: function _createWrappers() {
58644      // load the manipulator HTML elements. All styling done in css.
58645      if (this.manipulationDiv === undefined) {
58646        this.manipulationDiv = document.createElement('div');
58647        this.manipulationDiv.className = 'vis-manipulation';
58648        if (this.editMode === true) {
58649          this.manipulationDiv.style.display = 'block';
58650        } else {
58651          this.manipulationDiv.style.display = 'none';
58652        }
58653        this.canvas.frame.appendChild(this.manipulationDiv);
58654      }
58655
58656      // container for the edit button.
58657      if (this.editModeDiv === undefined) {
58658        this.editModeDiv = document.createElement('div');
58659        this.editModeDiv.className = 'vis-edit-mode';
58660        if (this.editMode === true) {
58661          this.editModeDiv.style.display = 'none';
58662        } else {
58663          this.editModeDiv.style.display = 'block';
58664        }
58665        this.canvas.frame.appendChild(this.editModeDiv);
58666      }
58667
58668      // container for the close div button
58669      if (this.closeDiv === undefined) {
58670        this.closeDiv = document.createElement('div');
58671        this.closeDiv.className = 'vis-close';
58672        this.closeDiv.style.display = this.manipulationDiv.style.display;
58673        this.canvas.frame.appendChild(this.closeDiv);
58674      }
58675    }
58676
58677    /**
58678     * generate a new target node. Used for creating new edges and editing edges
58679     *
58680     * @param {number} x
58681     * @param {number} y
58682     * @returns {Node}
58683     * @private
58684     */
58685
58686  }, {
58687    key: '_getNewTargetNode',
58688    value: function _getNewTargetNode(x, y) {
58689      var controlNodeStyle = util.deepExtend({}, this.options.controlNodeStyle);
58690
58691      controlNodeStyle.id = 'targetNode' + util.randomUUID();
58692      controlNodeStyle.hidden = false;
58693      controlNodeStyle.physics = false;
58694      controlNodeStyle.x = x;
58695      controlNodeStyle.y = y;
58696
58697      // we have to define the bounding box in order for the nodes to be drawn immediately
58698      var node = this.body.functions.createNode(controlNodeStyle);
58699      node.shape.boundingBox = { left: x, right: x, top: y, bottom: y };
58700
58701      return node;
58702    }
58703
58704    /**
58705     * Create the edit button
58706     */
58707
58708  }, {
58709    key: '_createEditButton',
58710    value: function _createEditButton() {
58711      // restore everything to it's original state (if applicable)
58712      this._clean();
58713
58714      // reset the manipulationDOM
58715      this.manipulationDOM = {};
58716
58717      // empty the editModeDiv
58718      util.recursiveDOMDelete(this.editModeDiv);
58719
58720      // create the contents for the editMode button
58721      var locale = this.options.locales[this.options.locale];
58722      var button = this._createButton('editMode', 'vis-button vis-edit vis-edit-mode', locale['edit'] || this.options.locales['en']['edit']);
58723      this.editModeDiv.appendChild(button);
58724
58725      // bind a hammer listener to the button, calling the function toggleEditMode.
58726      this._bindHammerToDiv(button, this.toggleEditMode.bind(this));
58727    }
58728
58729    /**
58730     * this function cleans up after everything this module does. Temporary elements, functions and events are removed, physics restored, hammers removed.
58731     * @private
58732     */
58733
58734  }, {
58735    key: '_clean',
58736    value: function _clean() {
58737      // not in mode
58738      this.inMode = false;
58739
58740      // _clean the divs
58741      if (this.guiEnabled === true) {
58742        util.recursiveDOMDelete(this.editModeDiv);
58743        util.recursiveDOMDelete(this.manipulationDiv);
58744
58745        // removes all the bindings and overloads
58746        this._cleanManipulatorHammers();
58747      }
58748
58749      // remove temporary nodes and edges
58750      this._cleanupTemporaryNodesAndEdges();
58751
58752      // restore overloaded UI functions
58753      this._unbindTemporaryUIs();
58754
58755      // remove the temporaryEventFunctions
58756      this._unbindTemporaryEvents();
58757
58758      // restore the physics if required
58759      this.body.emitter.emit('restorePhysics');
58760    }
58761
58762    /**
58763     * Each dom element has it's own hammer. They are stored in this.manipulationHammer
58763s. This cleans them up.
58764     * @private
58765     */
58766
58767  }, {
58768    key: '_cleanManipulatorHammers',
58769    value: function _cleanManipulatorHammers() {
58770      // _clean hammer bindings
58771      if (this.manipulationHammers.length != 0) {
58772        for (var i = 0; i < this.manipulationHammers.length; i++) {
58773          this.manipulationHammers[i].destroy();
58774        }
58775        this.manipulationHammers = [];
58776      }
58777    }
58778
58779    /**
58780     * Remove all DOM elements created by this module.
58781     * @private
58782     */
58783
58784  }, {
58785    key: '_removeManipulationDOM',
58786    value: function _removeManipulationDOM() {
58787      // removes all the bindings and overloads
58788      this._clean();
58789
58790      // empty the manipulation divs
58791      util.recursiveDOMDelete(this.manipulationDiv);
58792      util.recursiveDOMDelete(this.editModeDiv);
58793      util.recursiveDOMDelete(this.closeDiv);
58794
58795      // remove the manipulation divs
58796      if (this.manipulationDiv) {
58797        this.canvas.frame.removeChild(this.manipulationDiv);
58798      }
58799      if (this.editModeDiv) {
58800        this.canvas.frame.removeChild(this.editModeDiv);
58801      }
58802      if (this.closeDiv) {
58803        this.canvas.frame.removeChild(this.closeDiv);
58804      }
58805
58806      // set the references to undefined
58807      this.manipulationDiv = undefined;
58808      this.editModeDiv = undefined;
58809      this.closeDiv = undefined;
58810    }
58811
58812    /**
58813     * create a seperator line. the index is to differentiate in the manipulation dom
58814     * @param {number} [index=1]
58815     * @private
58816     */
58817
58818  }, {
58819    key: '_createSeperator',
58820    value: function _createSeperator() {
58821      var index = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 1;
58822
58823      this.manipulationDOM['seperatorLineDiv' + index] = document.createElement('div');
58824      this.manipulationDOM['seperatorLineDiv' + index].className = 'vis-separator-line';
58825      this.manipulationDiv.appendChild(this.manipulationDOM['seperatorLineDiv' + index]);
58826    }
58827
58828    // ----------------------    DOM functions for buttons    --------------------------//
58829
58830    /**
58831     *
58832     * @param {Locale} locale
58833     * @private
58834     */
58835
58836  }, {
58837    key: '_createAddNodeButton',
58838    value: function _createAddNodeButton(locale) {
58839      var button = this._createButton('addNode', 'vis-button vis-add', locale['addNode'] || this.options.locales['en']['addNode']);
58840      this.manipulationDiv.appendChild(button);
58841      this._bindHammerToDiv(button, this.addNodeMode.bind(this));
58842    }
58843
58844    /**
58845     *
58846     * @param {Locale} locale
58847     * @private
58848     */
58849
58850  }, {
58851    key: '_createAddEdgeButton',
58852    value: function _createAddEdgeButton(locale) {
58853      var button = this._createButton('addEdge', 'vis-button vis-connect', locale['addEdge'] || this.options.locales['en']['addEdge']);
58854      this.manipulationDiv.appendChild(button);
58855      this._bindHammerToDiv(button, this.addEdgeMode.bind(this));
58856    }
58857
58858    /**
58859     *
58860     * @param {Locale} locale
58861     * @private
58862     */
58863
58864  }, {
58865    key: '_createEditNodeButton',
58866    value: function _createEditNodeButton(locale) {
58867      var button = this._createButton('editNode', 'vis-button vis-edit', locale['editNode'] || this.options.locales['en']['editNode']);
58868      this.manipulationDiv.appendChild(button);
58869      this._bindHammerToDiv(button, this.editNode.bind(this));
58870    }
58871
58872    /**
58873     *
58874     * @param {Locale} locale
58875     * @private
58876     */
58877
58878  }, {
58879    key: '_createEditEdgeButton',
58880    value: function _createEditEdgeButton(locale) {
58881      var button = this._createButton('editEdge', 'vis-button vis-edit', locale['editEdge'] || this.options.locales['en']['editEdge']);
58882      this.manipulationDiv.appendChild(button);
58883      this._bindHammerToDiv(button, this.editEdgeMode.bind(this));
58884    }
58885
58886    /**
58887     *
58888     * @param {Locale} locale
58889     * @private
58890     */
58891
58892  }, {
58893    key: '_createDeleteButton',
58894    value: function _createDeleteButton(locale) {
58895      var deleteBtnClass;
58896      if (this.options.rtl) {
58897        deleteBtnClass = 'vis-button vis-delete-rtl';
58898      } else {
58899        deleteBtnClass = 'vis-button vis-delete';
58900      }
58901      var button = this._createButton('delete', deleteBtnClass, locale['del'] || this.options.locales['en']['del']);
58902      this.manipulationDiv.appendChild(button);
58903      this._bindHammerToDiv(button, this.deleteSelected.bind(this));
58904    }
58905
58906    /**
58907     *
58908     * @param {Locale} locale
58909     * @private
58910     */
58911
58912  }, {
58913    key: '_createBackButton',
58914    value: function _createBackButton(locale) {
58915      var button = this._createButton('back', 'vis-button vis-back', locale['back'] || this.options.locales['en']['back']);
58916      this.manipulationDiv.appendChild(button);
58917      this._bindHammerToDiv(button, this.showManipulatorToolbar.bind(this));
58918    }
58919
58920    /**
58921     *
58922     * @param {number|string} id
58923     * @param {string} className
58924     * @param {label} label
58925     * @param {string} labelClassName
58926     * @returns {HTMLElement}
58927     * @private
58928     */
58929
58930  }, {
58931    key: '_createButton',
58932    value: function _createButton(id, className, label) {
58933      var labelClassName = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 'vis-label';
58934
58935
58936      this.manipulationDOM[id + 'Div'] = document.createElement('div');
58937      this.manipulationDOM[id + 'Div'].className = className;
58938      this.manipulationDOM[id + 'Label'] = document.createElement('div');
58939      this.manipulationDOM[id + 'Label'].className = labelClassName;
58940      this.manipulationDOM[id + 'Label'].innerHTML = label;
58941      this.manipulationDOM[id + 'Div'].appendChild(this.manipulationDOM[id + 'Label']);
58942      return this.manipulationDOM[id + 'Div'];
58943    }
58944
58945    /**
58946     *
58947     * @param {Label} label
58948     * @private
58949     */
58950
58951  }, {
58952    key: '_createDescription',
58953    value: function _createDescription(label) {
58954      this.manipulationDiv.appendChild(this._createButton('description', 'vis-button vis-none', label));
58955    }
58956
58957    // -------------------------- End of DOM functions for buttons ------------------------------//
58958
58959    /**
58960     * this binds an event until cleanup by the clean functions.
58961     * @param {Event}  event   The event
58962     * @param {function} newFunction
58963     * @private
58964     */
58965
58966  }, {
58967    key: '_temporaryBindEvent',
58968    value: function _temporaryBindEvent(event, newFunction) {
58969      this.temporaryEventFunctions.push({ event: event, boundFunction: newFunction });
58970      this.body.emitter.on(event, newFunction);
58971    }
58972
58973    /**
58974     * this overrides an UI function until cleanup by the clean function
58975     * @param {string} UIfunctionName
58976     * @param {function} newFunction
58977     * @private
58978     */
58979
58980  }, {
58981    key: '_temporaryBindUI',
58982    value: function _temporaryBindUI(UIfunctionName, newFunction) {
58983      if (this.body.eventListeners[UIfunctionName] !== undefined) {
58984        this.temporaryUIFunctions[UIfunctionName] = this.body.eventListeners[UIfunctionName];
58985        this.body.eventListeners[UIfunctionName] = newFunction;
58986      } else {
58987        throw new Error('This UI function does not exist. Typo? You tried: ' + UIfunctionName + ' possible are: ' + (0, _stringify2['default'])((0, _keys2['default'])(this.body.eventListeners)));
58988      }
58989    }
58990
58991    /**
58992     * Restore the overridden UI functions to their original state.
58993     *
58994     * @private
58995     */
58996
58997  }, {
58998    key: '_unbindTemporaryUIs',
58999    value: function _unbindTemporaryUIs() {
59000      for (var functionName in this.temporaryUIFunctions) {
59001        if (this.temporaryUIFunctions.hasOwnProperty(functionName)) {
59002          this.body.eventListeners[functionName] = this.temporaryUIFunctions[functionName];
59003          delete this.temporaryUIFunctions[functionName];
59004        }
59005      }
59006      this.temporaryUIFunctions = {};
59007    }
59008
59009    /**
59010     * Unbind the events created by _temporaryBindEvent
59011     * @private
59012     */
59013
59014  }, {
59015    key: '_unbindTemporaryEvents',
59016    value: function _unbindTemporaryEvents() {
59017      for (var i = 0; i < this.temporaryEventFunctions.length; i++) {
59018        var eventName = this.temporaryEventFunctions[i].event;
59019        var boundFunction = this.temporaryEventFunctions[i].boundFunction;
59020        this.body.emitter.off(eventName, boundFunction);
59021      }
59022      this.temporaryEventFunctions = [];
59023    }
59024
59025    /**
59026     * Bind an hammer instance to a DOM element.
59027     *
59028     * @param {Element} domElement
59029     * @param {function} boundFunction
59030     */
59031
59032  }, {
59033    key: '_bindHammerToDiv',
59034    value: function _bindHammerToDiv(domElement, boundFunction) {
59035      var hammer = new Hammer(domElement, {});
59036      hammerUtil.onTouch(hammer, boundFunction);
59037      this.manipulationHammers.push(hammer);
59038    }
59039
59040    /**
59041     * Neatly clean up temporary edges and nodes
59042     * @private
59043     */
59044
59045  }, {
59046    key: '_cleanupTemporaryNodesAndEdges',
59047    value: function _cleanupTemporaryNodesAndEdges() {
59048      // _clean temporary edges
59049      for (var i = 0; i < this.temporaryIds.edges.length; i++) {
59050        this.body.edges[this.temporaryIds.edges[i]].disconnect();
59051        delete this.body.edges[this.temporaryIds.edges[i]];
59052        var indexTempEdge = this.body.edgeIndices.indexOf(this.temporaryIds.edges[i]);
59053        if (indexTempEdge !== -1) {
59054          this.body.edgeIndices.splice(indexTempEdge, 1);
59055        }
59056      }
59057
59058      // _clean temporary nodes
59059      for (var _i = 0; _i < this.temporaryIds.nodes.length; _i++) {
59060        delete this.body.nodes[this.temporaryIds.nodes[_i]];
59061        var indexTempNode = this.body.nodeIndices.indexOf(this.temporaryIds.nodes[_i]);
59062        if (indexTempNode !== -1) {
59063          this.body.nodeIndices.splice(indexTempNode, 1);
59064        }
59065      }
59066
59067      this.temporaryIds = { nodes: [], edges: [] };
59068    }
59069
59070    // ------------------------------------------ EDIT EDGE FUNCTIONS -----------------------------------------//
59071
59072    /**
59073     * the touch is used to get the position of the initial click
59074     * @param {Event}  event   The event
59075     * @private
59076     */
59077
59078  }, {
59079    key: '_controlNodeTouch',
59080    value: function _controlNodeTouch(event) {
59081      this.selectionHandler.unselectAll();
59082      this.lastTouch = this.body.functions.getPointer(event.center);
59083      this.lastTouch.translation = util.extend({}, this.body.view.translation); // copy the object
59084    }
59085
59086    /**
59087     * the drag start is used to mark one of the control nodes as selected.
59088     * @param {Event}  event   The event
59089     * @private
59090     */
59091
59092  }, {
59093    key: '_controlNodeDragStart',
59094    value: function _controlNodeDragStart(event) {
59095      // eslint-disable-line no-unused-vars
59096      var pointer = this.lastTouch;
59097      var pointerObj = this.selectionHandler._pointerToPositionObject(pointer);
59098      var from = this.body.nodes[this.temporaryIds.nodes[0]];
59099      var to = this.body.nodes[this.temporaryIds.nodes[1]];
59100      var edge = this.body.edges[this.edgeBeingEditedId];
59101      this.selectedControlNode = undefined;
59102
59103      var fromSelect = from.isOverlappingWith(pointerObj);
59104      var toSelect = to.isOverlappingWith(pointerObj);
59105
59106      if (fromSelect === true) {
59107        this.selectedControlNode = from;
59108        edge.edgeType.from = from;
59109      } else if (toSelect === true) {
59110        this.selectedControlNode = to;
59111        edge.edgeType.to = to;
59112      }
59113
59114      // we use the selection to find the node that is being dragged. We explicitly select it here.
59115      if (this.selectedControlNode !== undefined) {
59116        this.selectionHandler.selectObject(this.selectedControlNode);
59117      }
59118
59119      this.body.emitter.emit('_redraw');
59120    }
59121
59122    /**
59123     * dragging the control nodes or the canvas
59124     * @param {Event}  event   The event
59125     * @private
59126     */
59127
59128  }, {
59129    key: '_controlNodeDrag',
59130    value: function _controlNodeDrag(event) {
59131      this.body.emitter.emit('disablePhysics');
59132      var pointer = this.body.functions.getPointer(event.center);
59133      var pos = this.canvas.DOMtoCanvas(pointer);
59134      if (this.selectedControlNode !== undefined) {
59135        this.selectedControlNode.x = pos.x;
59136        this.selectedControlNode.y = pos.y;
59137      } else {
59138        // if the drag was not started properly because the click started outside the network div, start it now.
59139        var diffX = pointer.x - this.lastTouch.x;
59140        var diffY = pointer.y - this.lastTouch.y;
59141        this.body.view.translation = { x: this.lastTouch.translation.x + diffX, y: this.lastTouch.translation.y + diffY };
59142      }
59143      this.body.emitter.emit('_redraw');
59144    }
59145
59146    /**
59147     * connecting or restoring the control nodes.
59148     * @param {Event}  event   The event
59149     * @private
59150     */
59151
59152  }, {
59153    key: '_controlNodeDragEnd',
59154    value: function _controlNodeDragEnd(event) {
59155      var pointer = this.body.functions.getPointer(event.center);
59156      var pointerObj = this.selectionHandler._pointerToPositionObject(pointer);
59157      var edge = this.body.edges[this.edgeBeingEditedId];
59158      // if the node that was dragged is not a control node, return
59159      if (this.selectedControlNode === undefined) {
59160        return;
59161      }
59162
59163      // we use the selection to find the node that is being dragged. We explicitly DEselect the control node here.
59164      this.selectionHandler.unselectAll();
59165      var overlappingNodeIds = this.selectionHandler._getAllNodesOverlappingWith(pointerObj);
59166      var node = undefined;
59167      for (var i = overlappingNodeIds.length - 1; i >= 0; i--) {
59168        if (overlappingNodeIds[i] !== this.selectedControlNode.id) {
59169          node = this.body.nodes[overlappingNodeIds[i]];
59170          break;
59171        }
59172      }
59173      // perform the connection
59174      if (node !== undefined && this.selectedControlNode !== undefined) {
59175        if (node.isCluster === true) {
59176          alert(this.options.locales[this.options.locale]['createEdgeError'] || this.options.locales['en']['createEdgeError']);
59177        } else {
59178          var from = this.body.nodes[this.temporaryIds.nodes[0]];
59179          if (this.selectedControlNode.id === from.id) {
59180            this._performEditEdge(node.id, edge.to.id);
59181          } else {
59182            this._performEditEdge(edge.from.id, node.id);
59183          }
59184        }
59185      } else {
59186        edge.updateEdgeType();
59187        this.body.emitter.emit('restorePhysics');
59188      }
59189
59190      this.body.emitter.emit('_redraw');
59191    }
59192
59193    // ------------------------------------ END OF EDIT EDGE FUNCTIONS -----------------------------------------//
59194
59195
59196    // ------------------------------------------- ADD EDGE FUNCTIONS -----------------------------------------//
59197    /**
59198     * the function bound to the selection event. It checks if you want to connect a cluster and changes the description
59199     * to walk the user through the process.
59200     *
59201     * @param {Event} event
59202     * @private
59203     */
59204
59205  }, {
59206    key: '_handleConnect',
59207    value: function _handleConnect(event) {
59208      // check to avoid double fireing of this function.
59209      if (new Date().valueOf() - this.touchTime > 100) {
59210        this.lastTouch = this.body.functions.getPointer(event.center);
59211        this.lastTouch.translation = util.extend({}, this.body.view.translation); // copy the object
59212
59213        var pointer = this.lastTouch;
59214        var node = this.selectionHandler.getNodeAt(pointer);
59215
59216        if (node !== undefined) {
59217          if (node.isCluster === true) {
59218            alert(this.options.locales[this.options.locale]['createEdgeError'] || this.options.locales['en']['createEdgeError']);
59219          } else {
59220            // create a node the temporary line can look at
59221            var targetNode = this._getNewTargetNode(node.x, node.y);
59222            this.body.nodes[targetNode.id] = targetNode;
59223            this.body.nodeIndices.push(targetNode.id);
59224
59225            // create a temporary edge
59226            var connectionEdge = this.body.functions.createEdge({
59227              id: 'connectionEdge' + util.randomUUID(),
59228              from: node.id,
59229              to: targetNode.id,
59230              physics: false,
59231              smooth: {
59232                enabled: true,
59233                type: 'continuous',
59234                roundness: 0.5
59235              }
59236            });
59237            this.body.edges[connectionEdge.id] = connectionEdge;
59238            this.body.edgeIndices.push(connectionEdge.id);
59239
59240            this.temporaryIds.nodes.push(targetNode.id);
59241            this.temporaryIds.edges.push(connectionEdge.id);
59242          }
59243        }
59244        this.touchTime = new Date().valueOf();
59245      }
59246    }
59247
59248    /**
59249     *
59250     * @param {Event} event
59251     * @private
59252     */
59253
59254  }, {
59255    key: '_dragControlNode',
59256    value: function _dragControlNode(event) {
59257      var pointer = this.body.functions.getPointer(event.center);
59258      if (this.temporaryIds.nodes[0] !== undefined) {
59259        var targetNode = this.body.nodes[this.temporaryIds.nodes[0]]; // there is only one temp node in the add edge mode.
59260        targetNode.x = this.canvas._XconvertDOMtoCanvas(pointer.x);
59261        targetNode.y = this.canvas._YconvertDOMtoCanvas(pointer.y);
59262        this.body.emitter.emit('_redraw');
59263      } else {
59264        var diffX = pointer.x - this.lastTouch.x;
59265        var diffY = pointer.y - this.lastTouch.y;
59266        this.body.view.translation = { x: this.lastTouch.translation.x + diffX, y: this.lastTouch.translation.y + diffY };
59267      }
59268    }
59269
59270    /**
59271     * Connect the new edge to the target if one exists, otherwise remove temp line
59272     * @param {Event}  event   The event
59273     * @private
59274     */
59275
59276  }, {
59277    key: '_finishConnect',
59278    value: function _finishConnect(event) {
59279      var pointer = this.body.functions.getPointer(event.center);
59280      var pointerObj = this.selectionHandler._pointerToPositionObject(pointer);
59281
59282      // remember the edge id
59283      var connectFromId = undefined;
59284      if (this.temporaryIds.edges[0] !== undefined) {
59285        connectFromId = this.body.edges[this.temporaryIds.edges[0]].fromId;
59286      }
59287
59288      // get the overlapping node but NOT the temporary node;
59289      var overlappingNodeIds = this.selectionHandler._getAllNodesOverlappingWith(pointerObj);
59290      var node = undefined;
59291      for (var i = overlappingNodeIds.length - 1; i >= 0; i--) {
59292        // if the node id is NOT a temporary node, accept the node.
59293        if (this.temporaryIds.nodes.indexOf(overlappingNodeIds[i]) === -1) {
59294          node = this.body.nodes[overlappingNodeIds[i]];
59295          break;
59296        }
59297      }
59298
59299      // clean temporary nodes and edges.
59300      this._cleanupTemporaryNodesAndEdges();
59301
59302      // perform the connection
59303      if (node !== undefined) {
59304        if (node.isCluster === true) {
59305          alert(this.options.locales[this.options.locale]['createEdgeError'] || this.options.locales['en']['createEdgeError']);
59306        } else {
59307          if (this.body.nodes[connectFromId] !== undefined && this.body.nodes[node.id] !== undefined) {
59308            this._performAddEdge(connectFromId, node.id);
59309          }
59310        }
59311      }
59312
59313      // No need to do _generateclickevent('dragEnd') here, the regular dragEnd event fires.
59314      this.body.emitter.emit('_redraw');
59315    }
59316
59317    /**
59318     *
59319     * @param {Event} event
59320     * @private
59321     */
59322
59323  }, {
59324    key: '_dragStartEdge',
59325    value: function _dragStartEdge(event) {
59326      var pointer = this.lastTouch;
59327      this.selectionHandler._generateClickEvent('dragStart', event, pointer, undefined, true);
59328    }
59329
59330    // --------------------------------------- END OF ADD EDGE FUNCTIONS -------------------------------------//
59331
59332
59333    // ------------------------------ Performing all the actual data manipulation ------------------------//
59334
59335    /**
59336     * Adds a node on the specified location
59337     *
59338     * @param {Object} clickData
59339     * @private
59340     */
59341
59342  }, {
59343    key: '_performAddNode',
59344    value: function _performAddNode(clickData) {
59345      var _this4 = this;
59346
59347      var defaultData = {
59348        id: util.randomUUID(),
59349        x: clickData.pointer.canvas.x,
59350        y: clickData.pointer.canvas.y,
59351        label: 'new'
59352      };
59353
59354      if (typeof this.options.addNode === 'function') {
59355        if (this.options.addNode.length === 2) {
59356          this.options.addNode(defaultData, function (finalizedData) {
59357            if (finalizedData !== null && finalizedData !== undefined && _this4.inMode === 'addNode') {
59358              // if for whatever reason the mode has changes (due to dataset change) disregard the callback
59359              _this4.body.data.nodes.getDataSet().add(finalizedData);
59360              _this4.showManipulatorToolbar();
59361            }
59362          });
59363        } else {
59364          this.showManipulatorToolbar();
59365          throw new Error('The function for add does not support two arguments (data,callback)');
59366        }
59367      } else {
59368        this.body.data.nodes.getDataSet().add(defaultData);
59369        this.showManipulatorToolbar();
59370      }
59371    }
59372
59373    /**
59374     * connect two nodes with a new edge.
59375     *
59376     * @param {Node.id} sourceNodeId
59377     * @param {Node.id} targetNodeId
59378     * @private
59379     */
59380
59381  }, {
59382    key: '_performAddEdge',
59383    value: function _performAddEdge(sourceNodeId, targetNodeId) {
59384      var _this5 = this;
59385
59386      var defaultData = { from: sourceNodeId, to: targetNodeId };
59387      if (typeof this.options.addEdge === 'function') {
59388        if (this.options.addEdge.length === 2) {
59389          this.options.addEdge(defaultData, function (finalizedData) {
59390            if (finalizedData !== null && finalizedData !== undefined && _this5.inMode === 'addEdge') {
59391              // if for whatever reason the mode has changes (due to dataset change) disregard the callback
59392              _this5.body.data.edges.getDataSet().add(finalizedData);
59393              _this5.selectionHandler.unselectAll();
59394              _this5.showManipulatorToolbar();
59395            }
59396          });
59397        } else {
59398          throw new Error('The function for connect does not support two arguments (data,callback)');
59399        }
59400      } else {
59401        this.body.data.edges.getDataSet().add(defaultData);
59402        this.selectionHandler.unselectAll();
59403        this.showManipulatorToolbar();
59404      }
59405    }
59406
59407    /**
59408     * connect two nodes with a new edge.
59409     *
59410     * @param {Node.id} sourceNodeId
59411     * @param {Node.id} targetNodeId
59412     * @private
59413     */
59414
59415  }, {
59416    key: '_performEditEdge',
59417    value: function _performEditEdge(sourceNodeId, targetNodeId) {
59418      var _this6 = this;
59419
59420      var defaultData = { id: this.edgeBeingEditedId, from: sourceNodeId, to: targetNodeId, label: this.body.data.edges._data[this.edgeBeingEditedId].label };
59421      var eeFunct = this.options.editEdge;
59422      if ((typeof eeFunct === 'undefined' ? 'undefined' : (0, _typeof3['default'])(eeFunct)) === 'object') {
59423        eeFunct = eeFunct.editWithoutDrag;
59424      }
59425      if (typeof eeFunct === 'function') {
59426        if (eeFunct.length === 2) {
59427          eeFunct(defaultData, function (finalizedData) {
59428            if (finalizedData === null || finalizedData === undefined || _this6.inMode !== 'editEdge') {
59429              // if for whatever reason the mode has changes (due to dataset change) disregard the callback) {
59430              _this6.body.edges[defaultData.id].updateEdgeType();
59431              _this6.body.emitter.emit('_redraw');
59432              _this6.showManipulatorToolbar();
59433            } else {
59434              _this6.body.data.edges.getDataSet().update(finalizedData);
59435              _this6.selectionHandler.unselectAll();
59436              _this6.showManipulatorToolbar();
59437            }
59438          });
59439        } else {
59440          throw new Error('The function for edit does not support two arguments (data, callback)');
59441        }
59442      } else {
59443        this.body.data.edges.getDataSet().update(defaultData);
59444        this.selectionHandler.unselectAll();
59445        this.showManipulatorToolbar();
59446      }
59447    }
59448  }]);
59449  return ManipulationSystem;
59450}();
59451
59452exports['default'] = ManipulationSystem;
59453
59454/***/ }),
59455/* 238 */
59456/***/ (function(module, exports, __webpack_require__) {
59457
59458"use strict";
59459
59460
59461Object.defineProperty(exports, "__esModule", {
59462  value: true
59463});
59464
59465var _slicedToArray2 = __webpack_require__(30);
59466
59467var _slicedToArray3 = _interopRequireDefault(_slicedToArray2);
59468
59469var _classCallCheck2 = __webpack_require__(0);
59470
59471var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
59472
59473var _createClass2 = __webpack_require__(1);
59474
59475var _createClass3 = _interopRequireDefault(_createClass2);
59476
59477var _FloydWarshall = __webpack_require__(239);
59478
59479var _FloydWarshall2 = _interopRequireDefault(_FloydWarshall);
59480
59481function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
59482
59483/**
59484 * KamadaKawai positions the nodes initially based on
59485 *
59486 * "AN ALGORITHM FOR DRAWING GENERAL UNDIRECTED GRAPHS"
59487 * -- Tomihisa KAMADA and Satoru KAWAI in 1989
59488 *
59489 * Possible optimizations in the distance calculation can be implemented.
59490 */
59491var KamadaKawai = function () {
59492  /**
59493   * @param {Object} body
59494   * @param {number} edgeLength
59495   * @param {number} edgeStrength
59496   */
59497  function KamadaKawai(body, edgeLength, edgeStrength) {
59498    (0, _classCallCheck3["default"])(this, KamadaKawai);
59499
59500    this.body = body;
59501    this.springLength = edgeLength;
59502    this.springConstant = edgeStrength;
59503    this.distanceSolver = new _FloydWarshall2["default"]();
59504  }
59505
59506  /**
59507   * Not sure if needed but can be used to update the spring length and spring constant
59508   * @param {Object} options
59509   */
59510
59511
59512  (0, _createClass3["default"])(KamadaKawai, [{
59513    key: "setOptions",
59514    value: function setOptions(options) {
59515      if (options) {
59516        if (options.springLength) {
59517          this.springLength = options.springLength;
59518        }
59519        if (options.springConstant) {
59520          this.springConstant = options.springConstant;
59521        }
59522      }
59523    }
59524
59525    /**
59526     * Position the system
59527     * @param {Array.<Node>} nodesArray
59528     * @param {Array.<vis.Edge>} edgesArray
59529     * @param {boolean}
59529 [ignoreClusters=false]
59530     */
59531
59532  }, {
59533    key: "solve",
59534    value: function solve(nodesArray, edgesArray) {
59535      var ignoreClusters = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
59536
59537      // get distance matrix
59538      var D_matrix = this.distanceSolver.getDistances(this.body, nodesArray, edgesArray); // distance matrix
59539
59540      // get the L Matrix
59541      this._createL_matrix(D_matrix);
59542
59543      // get the K Matrix
59544      this._createK_matrix(D_matrix);
59545
59546      // initial E Matrix
59547      this._createE_matrix();
59548
59549      // calculate positions
59550      var threshold = 0.01;
59551      var innerThreshold = 1;
59552      var iterations = 0;
59553      var maxIterations = Math.max(1000, Math.min(10 * this.body.nodeIndices.length, 6000));
59554      var maxInnerIterations = 5;
59555
59556      var maxEnergy = 1e9;
59557      var highE_nodeId = 0,
59558          dE_dx = 0,
59559          dE_dy = 0,
59560          delta_m = 0,
59561          subIterations = 0;
59562
59563      while (maxEnergy > threshold && iterations < maxIterations) {
59564        iterations += 1;
59565
59566        var _getHighestEnergyNode2 = this._getHighestEnergyNode(ignoreClusters);
59567
59568        var _getHighestEnergyNode3 = (0, _slicedToArray3["default"])(_getHighestEnergyNode2, 4);
59569
59570        highE_nodeId = _getHighestEnergyNode3[0];
59571        maxEnergy = _getHighestEnergyNode3[1];
59572        dE_dx = _getHighestEnergyNode3[2];
59573        dE_dy = _getHighestEnergyNode3[3];
59574
59575        delta_m = maxEnergy;
59576        subIterations = 0;
59577        while (delta_m > innerThreshold && subIterations < maxInnerIterations) {
59578          subIterations += 1;
59579          this._moveNode(highE_nodeId, dE_dx, dE_dy);
59580
59581          var _getEnergy2 = this._getEnergy(highE_nodeId);
59582
59583          var _getEnergy3 = (0, _slicedToArray3["default"])(_getEnergy2, 3);
59584
59585          delta_m = _getEnergy3[0];
59586          dE_dx = _getEnergy3[1];
59587          dE_dy = _getEnergy3[2];
59588        }
59589      }
59590    }
59591
59592    /**
59593     * get the node with the highest energy
59594     * @param {boolean} ignoreClusters
59595     * @returns {number[]}
59596     * @private
59597     */
59598
59599  }, {
59600    key: "_getHighestEnergyNode",
59601    value: function _getHighestEnergyNode(ignoreClusters) {
59602      var nodesArray = this.body.nodeIndices;
59603      var nodes = this.body.nodes;
59604      var maxEnergy = 0;
59605      var maxEnergyNodeId = nodesArray[0];
59606      var dE_dx_max = 0,
59607          dE_dy_max = 0;
59608
59609      for (var nodeIdx = 0; nodeIdx < nodesArray.length; nodeIdx++) {
59610        var m = nodesArray[nodeIdx];
59611        // by not evaluating nodes with predefined positions we should only move nodes that have no positions.
59612        if (nodes[m].predefinedPosition === false || nodes[m].isCluster === true && ignoreClusters === true || nodes[m].options.fixed.x === true || nodes[m].options.fixed.y === true) {
59613          var _getEnergy4 = this._getEnergy(m),
59614              _getEnergy5 = (0, _slicedToArray3["default"])(_getEnergy4, 3),
59615              delta_m = _getEnergy5[0],
59616              dE_dx = _getEnergy5[1],
59617              dE_dy = _getEnergy5[2];
59618
59619          if (maxEnergy < delta_m) {
59620            maxEnergy = delta_m;
59621            maxEnergyNodeId = m;
59622            dE_dx_max = dE_dx;
59623            dE_dy_max = dE_dy;
59624          }
59625        }
59626      }
59627
59628      return [maxEnergyNodeId, maxEnergy, dE_dx_max, dE_dy_max];
59629    }
59630
59631    /**
59632     * calculate the energy of a single node
59633     * @param {Node.id} m
59634     * @returns {number[]}
59635     * @private
59636     */
59637
59638  }, {
59639    key: "_getEnergy",
59640    value: function _getEnergy(m) {
59641      var _E_sums$m = (0, _slicedToArray3["default"])(this.E_sums[m], 2),
59642          dE_dx = _E_sums$m[0],
59643          dE_dy = _E_sums$m[1];
59644
59645      var delta_m = Math.sqrt(Math.pow(dE_dx, 2) + Math.pow(dE_dy, 2));
59646      return [delta_m, dE_dx, dE_dy];
59647    }
59648
59649    /**
59650     * move the node based on it's energy
59651     * the dx and dy are calculated from the linear system proposed by Kamada and Kawai
59652     * @param {number} m
59653     * @param {number} dE_dx
59654     * @param {number} dE_dy
59655     * @private
59656     */
59657
59658  }, {
59659    key: "_moveNode",
59660    value: function _moveNode(m, dE_dx, dE_dy) {
59661      var nodesArray = this.body.nodeIndices;
59662      var nodes = this.body.nodes;
59663      var d2E_dx2 = 0;
59664      var d2E_dxdy = 0;
59665      var d2E_dy2 = 0;
59666
59667      var x_m = nodes[m].x;
59668      var y_m = nodes[m].y;
59669      var km = this.K_matrix[m];
59670      var lm = this.L_matrix[m];
59671
59672      for (var iIdx = 0; iIdx < nodesArray.length; iIdx++) {
59673        var i = nodesArray[iIdx];
59674        if (i !== m) {
59675          var x_i = nodes[i].x;
59676          var y_i = nodes[i].y;
59677          var kmat = km[i];
59678          var lmat = lm[i];
59679          var denominator = 1.0 / Math.pow(Math.pow(x_m - x_i, 2) + Math.pow(y_m - y_i, 2), 1.5);
59680          d2E_dx2 += kmat * (1 - lmat * Math.pow(y_m - y_i, 2) * denominator);
59681          d2E_dxdy += kmat * (lmat * (x_m - x_i) * (y_m - y_i) * denominator);
59682          d2E_dy2 += kmat * (1 - lmat * Math.pow(x_m - x_i, 2) * denominator);
59683        }
59684      }
59685      // make the variable names easier to make the solving of the linear system easier to read
59686      var A = d2E_dx2,
59687          B = d2E_dxdy,
59688          C = dE_dx,
59689          D = d2E_dy2,
59690          E = dE_dy;
59691
59692      // solve the linear system for dx and dy
59693      var dy = (C / A + E / B) / (B / A - D / B);
59694      var dx = -(B * dy + C) / A;
59695
59696      // move the node
59697      nodes[m].x += dx;
59698      nodes[m].y += dy;
59699
59700      // Recalculate E_matrix (should be incremental)
59701      this._updateE_matrix(m);
59702    }
59703
59704    /**
59705     * Create the L matrix: edge length times shortest path
59706     * @param {Object} D_matrix
59707     * @private
59708     */
59709
59710  }, {
59711    key: "_createL_matrix",
59712    value: function _createL_matrix(D_matrix) {
59713      var nodesArray = this.body.nodeIndices;
59714      var edgeLength = this.springLength;
59715
59716      this.L_matrix = [];
59717      for (var i = 0; i < nodesArray.length; i++) {
59718        this.L_matrix[nodesArray[i]] = {};
59719        for (var j = 0; j < nodesArray.length; j++) {
59720          this.L_matrix[nodesArray[i]][nodesArray[j]] = edgeLength * D_matrix[nodesArray[i]][nodesArray[j]];
59721        }
59722      }
59723    }
59724
59725    /**
59726     * Create the K matrix: spring constants times shortest path
59727     * @param {Object} D_matrix
59728     * @private
59729     */
59730
59731  }, {
59732    key: "_createK_matrix",
59733    value: function _createK_matrix(D_matrix) {
59734      var nodesArray = this.body.nodeIndices;
59735      var edgeStrength = this.springConstant;
59736
59737      this.K_matrix = [];
59738      for (var i = 0; i < nodesArray.length; i++) {
59739        this.K_matrix[nodesArray[i]] = {};
59740        for (var j = 0; j < nodesArray.length; j++) {
59741          this.K_matrix[nodesArray[i]][nodesArray[j]] = edgeStrength * Math.pow(D_matrix[nodesArray[i]][nodesArray[j]], -2);
59742        }
59743      }
59744    }
59745
59746    /**
59747     *  Create matrix with all energies between nodes
59748     *  @private
59749     */
59750
59751  }, {
59752    key: "_createE_matrix",
59753    value: function _createE_matrix() {
59754      var nodesArray = this.body.nodeIndices;
59755      var nodes = this.body.nodes;
59756      this.E_matrix = {};
59757      this.E_sums = {};
59758      for (var mIdx = 0; mIdx < nodesArray.length; mIdx++) {
59759        this.E_matrix[nodesArray[mIdx]] = [];
59760      }
59761      for (var _mIdx = 0; _mIdx < nodesArray.length; _mIdx++) {
59762        var m = nodesArray[_mIdx];
59763        var x_m = nodes[m].x;
59764        var y_m = nodes[m].y;
59765        var dE_dx = 0;
59766        var dE_dy = 0;
59767        for (var iIdx = _mIdx; iIdx < nodesArray.length; iIdx++) {
59768          var i = nodesArray[iIdx];
59769          if (i !== m) {
59770            var x_i = nodes[i].x;
59771            var y_i = nodes[i].y;
59772            var denominator = 1.0 / Math.sqrt(Math.pow(x_m - x_i, 2) + Math.pow(y_m - y_i, 2));
59773            this.E_matrix[m][iIdx] = [this.K_matrix[m][i] * (x_m - x_i - this.L_matrix[m][i] * (x_m - x_i) * denominator), this.K_matrix[m][i] * (y_m - y_i - this.L_matrix[m][i] * (y_m - y_i) * denominator)];
59774            this.E_matrix[i][_mIdx] = this.E_matrix[m][iIdx];
59775            dE_dx += this.E_matrix[m][iIdx][0];
59776            dE_dy += this.E_matrix[m][iIdx][1];
59777          }
59778        }
59779        //Store sum
59780        this.E_sums[m] = [dE_dx, dE_dy];
59781      }
59782    }
59783
59784    /**
59785     * Update method, just doing single column (rows are auto-updated) (update all sums)
59786     *
59787     * @param {number} m
59788     * @private
59789     */
59790
59791  }, {
59792    key: "_updateE_matrix",
59793    value: function _updateE_matrix(m) {
59794      var nodesArray = this.body.nodeIndices;
59795      var nodes = this.body.nodes;
59796      var colm = this.E_matrix[m];
59797      var kcolm = this.K_matrix[m];
59798      var lcolm = this.L_matrix[m];
59799      var x_m = nodes[m].x;
59800      var y_m = nodes[m].y;
59801      var dE_dx = 0;
59802      var dE_dy = 0;
59803      for (var iIdx = 0; iIdx < nodesArray.length; iIdx++) {
59804        var i = nodesArray[iIdx];
59805        if (i !== m) {
59806          //Keep old energy value for sum modification below
59807          var cell = colm[iIdx];
59808          var oldDx = cell[0];
59809          var oldDy = cell[1];
59810
59811          //Calc new energy:
59812          var x_i = nodes[i].x;
59813          var y_i = nodes[i].y;
59814          var denominator = 1.0 / Math.sqrt(Math.pow(x_m - x_i, 2) + Math.pow(y_m - y_i, 2));
59815          var dx = kcolm[i] * (x_m - x_i - lcolm[i] * (x_m - x_i) * denominator);
59816          var dy = kcolm[i] * (y_m - y_i - lcolm[i] * (y_m - y_i) * denominator);
59817          colm[iIdx] = [dx, dy];
59818          dE_dx += dx;
59819          dE_dy += dy;
59820
59821          //add new energy to sum of each column
59822          var sum = this.E_sums[i];
59823          sum[0] += dx - oldDx;
59824          sum[1] += dy - oldDy;
59825        }
59826      }
59827      //Store sum at -1 index
59828      this.E_sums[m] = [dE_dx, dE_dy];
59829    }
59830  }]);
59831  return KamadaKawai;
59832}(); // distance finding algorithm
59833
59834
59835exports["default"] = KamadaKawai;
59836
59837/***/ }),
59838/* 239 */
59839/***/ (function(module, exports, __webpack_require__) {
59840
59841"use strict";
59842
59843
59844Object.defineProperty(exports, "__esModule", {
59845  value: true
59846});
59847
59848var _classCallCheck2 = __webpack_require__(0);
59849
59850var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
59851
59852var _createClass2 = __webpack_require__(1);
59853
59854var _createClass3 = _interopRequireDefault(_createClass2);
59855
59856function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
59857
59858/**
59859 *  The Floyd–Warshall algorithm is an algorithm for finding shortest paths in
59860 *  a weighted graph with positive or negative edge weights (but with no negative
59861 *  cycles). - https://en.wikipedia.org/wiki/Floyd–Warshall_algorithm
59862 */
59863var FloydWarshall = function () {
59864  /**
59865   * @ignore
59866   */
59867  function FloydWarshall() {
59868    (0, _classCallCheck3["default"])(this, FloydWarshall);
59869  }
59870
59871  /**
59872   *
59873   * @param {Object} body
59874   * @param {Array.<Node>} nodesArray
59875   * @param {Array.<Edge>} edgesArray
59876   * @returns {{}}
59877   */
59878
59879
59880  (0, _createClass3["default"])(FloydWarshall, [{
59881    key: "getDistances",
59882    value: function getDistances(body, nodesArray, edgesArray) {
59883      var D_matrix = {};
59884      var edges = body.edges;
59885
59886      // prepare matrix with large numbers
59887      for (var i = 0; i < nodesArray.length; i++) {
59888        var node = nodesArray[i];
59889        var cell = {};
59890        D_matrix[node] = cell;
59891        for (var j = 0; j < nodesArray.length; j++) {
59892          cell[nodesArray[j]] = i == j ? 0 : 1e9;
59893        }
59894      }
59895
59896      // put the weights for the edges in. This assumes unidirectionality.
59897      for (var _i = 0; _i < edgesArray.length; _i++) {
59898        var edge = edges[edgesArray[_i]];
59899        // edge has to be connected if it counts to the distances. If it is connected to inner clusters it will crash so we also check if it is in the D_matrix
59900        if (edge.connected === true && D_matrix[edge.fromId] !== undefined && D_matrix[edge.toId] !== undefined) {
59901          D_matrix[edge.fromId][edge.toId] = 1;
59902          D_matrix[edge.toId][edge.fromId] = 1;
59903        }
59904      }
59905
59906      var nodeCount = nodesArray.length;
59907
59908      // Adapted FloydWarshall based on unidirectionality to greatly reduce complexity.
59909      for (var k = 0; k < nodeCount; k++) {
59910        var knode = nodesArray[k];
59911        var kcolm = D_matrix[knode];
59912        for (var _i2 = 0; _i2 < nodeCount - 1; _i2++) {
59913          var inode = nodesArray[_i2];
59914          var icolm = D_matrix[inode];
59915          for (var _j = _i2 + 1; _j < nodeCount; _j++) {
59916            var jnode = nodesArray[_j];
59917            var jcolm = D_matrix[jnode];
59918
59919            var val = Math.min(icolm[jnode], icolm[knode] + kcolm[jnode]);
59920            icolm[jnode] = val;
59921            jcolm[inode] = val;
59922          }
59923        }
59924      }
59925
59926      return D_matrix;
59927    }
59928  }]);
59929  return FloydWarshall;
59930}();
59931
59932exports["default"] = FloydWarshall;
59933
59934/***/ })
59935/******/ ]);
59936});

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.