PageSourceSearch

https://www.plateforme1418.com/vendor/bower/vis/dist/vis.js

js plateforme1418.com collected 2026-09-25 05:20:50 UTC 1,491,319 bytes, 43,387 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.9.0
8 * @date    2015-10-01
9 *
10 * @license
11 * Copyright (C) 2011-2015 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/******/ 			exports: {},
52/******/ 			id: moduleId,
53/******/ 			loaded: false
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.loaded = 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/******/ 	// __webpack_public_path__
74/******/ 	__webpack_require__.p = "";
75
76/******/ 	// Load entry module and return exports
77/******/ 	return __webpack_require__(0);
78/******/ })
79/************************************************************************/
80/******/ ([
81/* 0 */
82/***/ function(module, exports, __webpack_require__) {
83
84  // utils
85  'use strict';
86
87  exports.util = __webpack_require__(1);
88  exports.DOMutil = __webpack_require__(7);
89
90  // data
91  exports.DataSet = __webpack_require__(8);
92  exports.DataView = __webpack_require__(10);
93  exports.Queue = __webpack_require__(9);
94
95  // Graph3d
96  exports.Graph3d = __webpack_require__(11);
97  exports.graph3d = {
98    Camera: __webpack_require__(15),
99    Filter: __webpack_require__(16),
100    Point2d: __webpack_require__(14),
101    Point3d: __webpack_require__(13),
102    Slider: __webpack_require__(17),
103    StepNumber: __webpack_require__(18)
104  };
105
106  // Timeline
107  exports.Timeline = __webpack_require__(19);
108  exports.Graph2d = __webpack_require__(49);
109  exports.timeline = {
110    DateUtil: __webpack_require__(27),
111    DataStep: __webpack_require__(52),
112    Range: __webpack_require__(24),
113    stack: __webpack_require__(32),
114    TimeStep: __webpack_require__(30),
115
116    components: {
117      items: {
118        Item: __webpack_require__(34),
119        BackgroundItem: __webpack_require__(38),
120        BoxItem: __webpack_require__(36),
121        PointItem: __webpack_require__(37),
122        RangeItem: __webpack_require__(33)
123      },
124
125      Component: __webpack_require__(26),
126      CurrentTime: __webpack_require__(44),
127      CustomTime: __webpack_require__(42),
128      DataAxis: __webpack_require__(51),
129      GraphGroup: __webpack_require__(53),
130      Group: __webpack_require__(31),
131      BackgroundGroup: __webpack_require__(35),
132      ItemSet: __webpack_require__(29),
133      Legend: __webpack_require__(57),
134      LineGraph: __webpack_require__(50),
135      TimeAxis: __webpack_require__(39)
136    }
137  };
138
139  // Network
140  exports.Network = __webpack_require__(59);
141  exports.network = {
142    Images: __webpack_require__(117),
143    dotparser: __webpack_require__(115),
144    gephiParser: __webpack_require__(116),
145    allOptions: __webpack_require__(111)
146  };
147  exports.network.convertDot = function (input) {
148    return exports.network.dotparser.DOTToGraph(input);
149  };
150  exports.network.convertGephi = function (input, options) {
151    return exports.network.gephiParser.parseGephi(input, options);
152  };
153
154  // bundled external libraries
155  exports.moment = __webpack_require__(2);
156  exports.Hammer = __webpack_require__(20);
157  exports.keycharm = __webpack_require__(41);
158
159/***/ },
160/* 1 */
161/***/ function(module, exports, __webpack_require__) {
162
163  // utility functions
164
165  // first check if moment.js is already loaded in the browser window, if so,
166  // use this instance. Else, load via commonjs.
167
168  'use strict';
169
170  var moment = __webpack_require__(2);
171  var uuid = __webpack_require__(6);
172
173  /**
174   * Test whether given object is a number
175   * @param {*} object
176   * @return {Boolean} isNumber
177   */
178  exports.isNumber = function (object) {
179    return object instanceof Number || typeof object == 'number';
180  };
181
182  /**
183   * Remove everything in the DOM object
184   * @param DOMobject
185   */
186  exports.recursiveDOMDelete = function (DOMobject) {
187    if (DOMobject) {
188      while (DOMobject.hasChildNodes() === true) {
189        exports.recursiveDOMDelete(DOMobject.firstChild);
190        DOMobject.removeChild(DOMobject.firstChild);
191      }
192    }
193  };
194
195  /**
196   * 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.
197   *
198   * @param min
199   * @param max
200   * @param total
201   * @param value
202   * @returns {number}
203   */
204  exports.giveRange = function (min, max, total, value) {
205    if (max == min) {
206      return 0.5;
207    } else {
208      var scale = 1 / (max - min);
209      return Math.max(0, (value - min) * scale);
210    }
211  };
212
213  /**
214   * Test whether given object is a string
215   * @param {*} object
216   * @return {Boolean} isString
217   */
218  exports.isString = function (object) {
219    return object instanceof String || typeof object == 'string';
220  };
221
222  /**
223   * Test whether given object is a Date, or a String containing a Date
224   * @param {Date | String} object
225   * @return {Boolean} isDate
226   */
227  exports.isDate = function (object) {
228    if (object instanceof Date) {
229      return true;
230    } else if (exports.isString(object)) {
231      // test whether this string contains a date
232      var match = ASPDateRegex.exec(object);
233      if (match) {
234        return true;
235      } else if (!isNaN(Date.parse(object))) {
236        return true;
237      }
238    }
239
240    return false;
241  };
242
243  /**
244   * Create a semi UUID
245   * source: http://stackoverflow.com/a/105074/1262753
246   * @return {String} uuid
247   */
248  exports.randomUUID = function () {
249    return uuid.v4();
250  };
251
252  /**
253   * assign all keys of an object that are not nested objects to a certain value (used for color objects).
254   * @param obj
255   * @param value
256   */
257  exports.assignAllKeys = function (obj, value) {
258    for (var prop in obj) {
259      if (obj.hasOwnProperty(prop)) {
260        if (typeof obj[prop] !== 'object') {
261          obj[prop] = value;
262        }
263      }
264    }
265  };
266
267  /**
268   * Fill an object with a possibly partially defined other object. Only copies values if the a object has an object requiring values.
269   * That means an object is not created on a property if only the b object has it.
270   * @param obj
271   * @param value
272   */
273  exports.fillIfDefined = function (a, b) {
274    var allowDeletion = arguments.length <= 2 || arguments[2] === undefined ? false : arguments[2];
275
276    for (var prop in a) {
277      if (b[prop] !== undefined) {
278        if (typeof b[prop] !== 'object') {
279          if ((b[prop] === undefined || b[prop] === null) && a[prop] !== undefined && allowDeletion === true) {
280            delete a[prop];
281          } else {
282            a[prop] = b[prop];
283          }
284        } else {
285          if (typeof a[prop] === 'object') {
286            exports.fillIfDefined(a[prop], b[prop], allowDeletion);
287          }
288        }
289      }
290    }
291  };
292
293  /**
294   * Extend object a with the properties of object b or a series of objects
295   * Only properties with defined values are copied
296   * @param {Object} a
297   * @param {... Object} b
298   * @return {Object} a
299   */
300  exports.protoExtend = function (a, b) {
301    for (var i = 1; i < arguments.length; i++) {
302      var other = arguments[i];
303      for (var prop in other) {
304        a[prop] = other[prop];
305      }
306    }
307    return a;
308  };
309
310  /**
311   * Extend object a with the properties of object b or a series of objects
312   * Only properties with defined values are copied
313   * @param {Object} a
314   * @param {... Object} b
315   * @return {Object} a
316   */
317  exports.extend = function (a, b) {
318    for (var i = 1; i < arguments.length; i++) {
319      var other = arguments[i];
320      for (var prop in other) {
321        if (other.hasOwnProperty(prop)) {
322          a[prop] = other[prop];
323        }
324      }
325    }
326    return a;
327  };
328
329  /**
330   * Extend object a with selected properties of object b or a series of objects
331   * Only properties with defined values are copied
332   * @param {Array.<String>} props
333   * @param {Object} a
334   * @param {Object} b
335   * @return {Object} a
336   */
337  exports.selectiveExtend = function (props, a, b) {
338    if (!Array.isArray(props)) {
339      throw new Error('Array with property names expected as first argument');
340    }
341
342    for (var i = 2; i < arguments.length; i++) {
343      var other = arguments[i];
344
345      for (var p = 0; p < props.length; p++) {
346        var prop = props[p];
347        if (other.hasOwnProperty(prop)) {
348          a[prop] = other[prop];
349        }
350      }
351    }
352    return a;
353  };
354
355  /**
356   * Extend object a with selected properties of object b or a series of objects
357   * Only properties with defined values are copied
358   * @param {Array.<String>} props
359   * @param {Object} a
360   * @param {Object} b
361   * @return {Object} a
362   */
363  exports.selectiveDeepExtend = function (props, a, b) {
364    var allowDeletion = arguments.length <= 3 || arguments[3] === undefined ? false : arguments[3];
365
366    // TODO: add support for Arrays to deepExtend
367    if (Array.isArray(b)) {
368      throw new TypeError('Arrays are not supported by deepExtend');
369    }
370    for (var i = 2; i < arguments.length; i++) {
371      var other = arguments[i];
372      for (var p = 0; p < props.length; p++) {
373        var prop = props[p];
374        if (other.hasOwnProperty(prop)) {
375          if (b[prop] && b[prop].constructor === Object) {
376            if (a[prop] === undefined) {
377              a[prop] = {};
378            }
379            if (a[prop].constructor === Object) {
380              exports.deepExtend(a[prop], b[prop], false, allowDeletion);
381            } else {
382              if (b[prop] === null && a[prop] !== undefined && allowDeletion === true) {
383                delete a[prop];
384              } else {
385                a[prop] = b[prop];
386              }
387            }
388          } else if (Array.isArray(b[prop])) {
389            throw new TypeError('Arrays are not supported by deepExtend');
390          } else {
391            if (b[prop] === null && a[prop] !== undefined && allowDeletion === true) {
392              delete a[prop];
393            } else {
394              a[prop] = b[prop];
395            }
396          }
397        }
398      }
399    }
400    return a;
401  };
402
403  /**
404   * Extend object a with selected properties of object b or a series of objects
405   * Only properties with defined values are copied
406   * @param {Array.<String>} props
407   * @param {Object} a
408   * @param {Object} b
409   * @return {Object} a
410   */
411  exports.selectiveNotDeepExtend = function (props, a, b) {
412    var allowDeletion = arguments.length <= 3 || arguments[3] === undefined ? false : arguments[3];
413
414    // TODO: add support for Arrays to deepExtend
415    if (Array.isArray(b)) {
416      throw new TypeError('Arrays are not supported by deepExtend');
417    }
418    for (var prop in b) {
419      if (b.hasOwnProperty(prop)) {
420        if (props.indexOf(prop) == -1) {
421          if (b[prop] && b[prop].constructor === Object) {
422            if (a[prop] === undefined) {
423              a[prop] = {};
424            }
425            if (a[prop].constructor === Object) {
426              exports.deepExtend(a[prop], b[prop]);
427            } else {
428              if (b[prop] === null && a[prop] !== undefined && allowDeletion === true) {
429                delete a[prop];
430              } else {
431                a[prop] = b[prop];
432              }
433            }
434          } else if (Array.isArray(b[prop])) {
435            a[prop] = [];
436            for (var i = 0; i < b[prop].length; i++) {
437              a[prop].push(b[prop][i]);
438            }
439          } else {
440            if (b[prop] === null && a[prop] !== undefined && allowDeletion === true) {
441              delete a[prop];
442            } else {
443              a[prop] = b[prop];
444            }
445          }
446        }
447      }
448    }
449    return a;
450  };
451
452  /**
453   * Deep extend an object a with the properties of object b
454   * @param {Object} a
455   * @param {Object} b
456   * @param [Boolean] protoExtend --> optional parameter. If true, the prototype values will also be extended.
457   *                                  (ie. the options objects that inherit from others will also get the inherited options)
458   * @param [Boolean] global      --> optional parameter. If true, the values of fields that are null will not deleted
459   * @returns {Object}
460   */
461  exports.deepExtend = function (a, b, protoExtend, allowDeletion) {
462    for (var prop in b) {
463      if (b.hasOwnProperty(prop) || protoExtend === true) {
464        if (b[prop] && b[prop].constructor === Object) {
465          if (a[prop] === undefined) {
466            a[prop] = {};
467          }
468          if (a[prop].constructor === Object) {
469            exports.deepExtend(a[prop], b[prop], protoExtend);
470          } else {
471            if (b[prop] === null && a[prop] !== undefined && allowDeletion === true) {
472              delete a[prop];
473            } else {
474              a[prop] = b[prop];
475            }
476          }
477        } else if (Array.isArray(b[prop])) {
478          a[prop] = [];
479          for (var i = 0; i < b[prop].length; i++) {
480            a[prop].push(b[prop][i]);
481          }
482        } else {
483          if (b[prop] === null && a[prop] !== undefined && allowDeletion === true) {
484            delete a[prop];
485          } else {
486            a[prop] = b[prop];
487          }
488        }
489      }
490    }
491    return a;
492  };
493
494  /**
495   * Test whether all elements in two arrays are equal.
496   * @param {Array} a
497   * @param {Array} b
498   * @return {boolean} Returns true if both arrays have the same length and same
499   *                   elements.
500   */
501  exports.equalArray = function (a, b) {
502    if (a.length != b.length) return false;
503
504    for (var i = 0, len = a.length; i < len; i++) {
505      if (a[i] != b[i]) return false;
506    }
507
508    return true;
509  };
510
511  /**
512   * Convert an object to another type
513   * @param {Boolean | Number | String | Date | Moment | Null | undefined} object
514   * @param {String | undefined} type   Name of the type. Available types:
515   *                                    'Boolean', 'Number', 'String',
516   *                                    'Date', 'Moment', ISODate', 'ASPDate'.
517   * @return {*} object
518   * @throws Error
519   */
520  exports.convert = function (object, type) {
521    var match;
522
523    if (object === undefined) {
524      return undefined;
525    }
526    if (object === null) {
527      return null;
528    }
529
530    if (!type) {
531      return object;
532    }
533    if (!(typeof type === 'string') && !(type instanceof String)) {
534      throw new Error('Type must be a string');
535    }
536
537    //noinspection FallthroughInSwitchStatementJS
538    switch (type) {
539      case 'boolean':
540      case 'Boolean':
541        return Boolean(object);
542
543      case 'number':
544      case 'Number':
545        return Number(object.valueOf());
546
547      case 'string':
548      case 'String':
549        return String(object);
550
551      case 'Date':
552        if (exports.isNumber(object)) {
553          return new Date(object);
554        }
555        if (object instanceof Date) {
556          return new Date(object.valueOf());
557        } else if (moment.isMoment(object)) {
558          return new Date(object.valueOf());
559        }
560        if (exports.isString(object)) {
561          match = ASPDateRegex.exec(object);
562          if (match) {
563            // object is an ASP date
564            return new Date(Number(match[1])); // parse number
565          } else {
566            return moment(object).toDate(); // parse string
567          }
568        } else {
569          throw new Error('Cannot convert object of type ' + exports.getType(object) + ' to type Date');
570        }
571
572      case 'Moment':
573        if (exports.isNumber(object)) {
574          return moment(object);
575        }
576        if (object instanceof Date) {
577          return moment(object.valueOf());
578        } else if (moment.isMoment(object)) {
579          return moment(object);
580        }
581        if (exports.isString(object)) {
582          match = ASPDateRegex.exec(object);
583          if (match) {
584            // object is an ASP date
585            return moment(Number(match[1])); // parse number
586          } else {
587            return moment(object); // parse string
588          }
589        } else {
590          throw new Error('Cannot convert object of type ' + exports.getType(object) + ' to type Date');
591        }
592
593      case 'ISODate':
594        if (exports.isNumber(object)) {
595          return new Date(object);
596        } else if (object instanceof Date) {
597          return object.toISOString();
598        } else if (moment.isMoment(object)) {
599          return object.toDate().toISOString();
600        } else if (exports.isString(object)) {
601          match = ASPDateRegex.exec(object);
602          if (match) {
603            // object is an ASP date
604            return new Date(Number(match[1])).toISOString(); // parse number
605          } else {
606            return new Date(object).toISOString(); // parse string
607          }
608        } else {
609          throw new Error('Cannot convert object of type ' + exports.getType(object) + ' to type ISODate');
610        }
611
612      case 'ASPDate':
613        if (exports.isNumber(object)) {
614          return '/Date(' + object + ')/';
615        } else if (object instanceof Date) {
616          return '/Date(' + object.valueOf() + ')/';
617        } else if (exports.isString(object)) {
618          match = ASPDateRegex.exec(object);
619          var value;
620          if (match) {
621            // object is an ASP date
622            value = new Date(Number(match[1])).valueOf(); // parse number
623          } else {
624            value = new Date(object).valueOf(); // parse string
625          }
626          return '/Date(' + value + ')/';
627        } else {
628          throw new Error('Cannot convert object of type ' + exports.getType(object) + ' to type ASPDate');
629        }
630
631      default:
632        throw new Error('Unknown type "' + type + '"');
633    }
634  };
635
636  // parse ASP.Net Date pattern,
637  // for example '/Date(1198908717056)/' or '/Date(1198908717056-0700)/'
638  // code from http://momentjs.com/
639  var ASPDateRegex = /^\/?Date\((\-?\d+)/i;
640
641  /**
642   * Get the type of an object, for example exports.getType([]) returns 'Array'
643   * @param {*} object
644   * @return {String} type
645   */
646  exports.getType = function (object) {
647    var type = typeof object;
648
649    if (type == 'object') {
650      if (object === null) {
651        return 'null';
652      }
653      if (object instanceof Boolean) {
654        return 'Boolean';
655      }
656      if (object instanceof Number) {
657        return 'Number';
658      }
659      if (object instanceof String) {
660        return 'String';
661      }
662      if (Array.isArray(object)) {
663        return 'Array';
664      }
665      if (object instanceof Date) {
666        return 'Date';
667      }
668      return 'Object';
669    } else if (type == 'number') {
670      return 'Number';
671    } else if (type == 'boolean') {
672      return 'Boolean';
673    } else if (type == 'string') {
674      return 'String';
675    } else if (type === undefined) {
676      return 'undefined';
677    }
678
679    return type;
680  };
681
682  /**
683   * Used to extend an array and copy it. This is used to propagate paths recursively.
684   *
685   * @param arr
686   * @param newValue
687   * @returns {Array}
688   */
689  exports.copyAndExtendArray = function (arr, newValue) {
690    var newArr = [];
691    for (var i = 0; i < arr.length; i++) {
692      newArr.push(arr[i]);
693    }
694    newArr.push(newValue);
695    return newArr;
696  };
697
698  /**
699   * Used to extend an array and copy it. This is used to propagate paths recursively.
700   *
701   * @param arr
702   * @param newValue
703   * @returns {Array}
704   */
705  exports.copyArray = function (arr) {
706    var newArr = [];
707    for (var i = 0; i < arr.length; i++) {
708      newArr.push(arr[i]);
709    }
710    return newArr;
711  };
712
713  /**
714   * Retrieve the absolute left value of a DOM element
715   * @param {Element} elem        A dom element, for example a div
716   * @return {number} left        The absolute left position of this element
717   *                              in the browser page.
718   */
719  exports.getAbsoluteLeft = function (elem) {
720    return elem.getBoundingClientRect().left;
721  };
722
723  /**
724   * Retrieve the absolute top value of a DOM element
725   * @param {Element} elem        A dom element, for example a div
726   * @return {number} top        The absolute top position of this element
727   *                              in the browser page.
728   */
729  exports.getAbsoluteTop = function (elem) {
730    return elem.getBoundingClientRect().top;
731  };
732
733  /**
734   * add a className to the given elements style
735   * @param {Element} elem
736   * @param {String} className
737   */
738  exports.addClassName = function (elem, className) {
739    var classes = elem.className.split(' ');
740    if (classes.indexOf(className) == -1) {
741      classes.push(className); // add the class to the array
742      elem.className = classes.join(' ');
743    }
744  };
745
746  /**
747   * add a className to the given elements style
748   * @param {Element} elem
749   * @param {String} className
750   */
751  exports.removeClassName = function (elem, className) {
752    var classes = elem.className.split(' ');
753    var index = classes.indexOf(className);
754    if (index != -1) {
755      classes.splice(index, 1); // remove the class from the array
756      elem.className = classes.join(' ');
757    }
758  };
759
760  /**
761   * For each method for both arrays and objects.
762   * In case of an array, the built-in Array.forEach() is applied.
763   * In case of an Object, the method loops over all properties of the object.
764   * @param {Object | Array} object   An Object or Array
765   * @param {function} callback       Callback method, called for each item in
766   *                                  the object or array with three parameters:
767   *                                  callback(value, index, object)
768   */
769  exports.forEach = function (object, callback) {
770    var i, len;
771    if (Array.isArray(object)) {
772      // array
773      for (i = 0, len = object.length; i < len; i++) {
774        callback(object[i], i, object);
775      }
776    } else {
777      // object
778      for (i in object) {
779        if (object.hasOwnProperty(i)) {
780          callback(object[i], i, object);
781        }
782      }
783    }
784  };
785
786  /**
787   * Convert an object into an array: all objects properties are put into the
788   * array. The resulting array is unordered.
789   * @param {Object} object
790   * @param {Array} array
791   */
792  exports.toArray = function (object) {
793    var array = [];
794
795    for (var prop in object) {
796      if (object.hasOwnProperty(prop)) array.push(object[prop]);
797    }
798
799    return array;
800  };
801
802  /**
803   * Update a property in an object
804   * @param {Object} object
805   * @param {String} key
806   * @param {*} value
807   * @return {Boolean} changed
808   */
809  exports.updateProperty = function (object, key, value) {
810    if (object[key] !== value) {
811      object[key] = value;
812      return true;
813    } else {
814      return false;
815    }
816  };
817
818  /**
819   * Throttle the given function to be only executed once every `wait` milliseconds
820   * @param {function} fn
821   * @param {number} wait    Time in milliseconds
822   * @returns {function} Returns the throttled function
823   */
824  exports.throttle = function (fn, wait) {
825    var timeout = null;
826    var needExecution = false;
827
828    return function throttled() {
829      if (!timeout) {
830        needExecution = false;
831        fn();
832
833        timeout = setTimeout(function () {
834          timeout = null;
835          if (needExecution) {
836            throttled();
837          }
838        }, wait);
839      } else {
840        needExecution = true;
841      }
842    };
843  };
844
845  /**
846   * Add and event listener. Works for all browsers
847   * @param {Element}     element    An html element
848   * @param {string}      action     The action, for example "click",
849   *                                 without the prefix "on"
850   * @param {function}    listener   The callback function to be executed
851   * @param {boolean}     [useCapture]
852   */
853  exports.addEventListener = function (element, action, listener, useCapture) {
854    if (element.addEventListener) {
855      if (useCapture === undefined) useCapture = false;
856
857      if (action === 'mousewheel' && navigator.userAgent.indexOf('Firefox') >= 0) {
858        action = 'DOMMouseScroll'; // For Firefox
859      }
860
861      element.addEventListener(action, listener, useCapture);
862    } else {
863      element.attachEvent('on' + action, listener); // IE browsers
864    }
865  };
866
867  /**
868   * Remove an event listener from an element
869   * @param {Element}     element         An html dom element
870   * @param {string}      action          The name of the event, for example "mousedown"
871   * @param {function}    listener        The listener function
872   * @param {boolean}     [useCapture]
873   */
874  exports.removeEventListener = function (element, action, listener, useCapture) {
875    if (element.removeEventListener) {
876      // non-IE browsers
877      if (useCapture === undefined) useCapture = false;
878
879      if (action === 'mousewheel' && navigator.userAgent.indexOf('Firefox') >= 0) {
880        action = 'DOMMouseScroll'; // For Firefox
881      }
882
883      element.removeEventListener(action, listener, useCapture);
884    } else {
885      // IE browsers
886      element.detachEvent('on' + action, listener);
887    }
888  };
889
890  /**
891   * Cancels the event if it is cancelable, without stopping further propagation of the event.
892   */
893  exports.preventDefault = function (event) {
894    if (!event) event = window.event;
895
896    if (event.preventDefault) {
897      event.preventDefault(); // non-IE browsers
898    } else {
899      event.returnValue = false; // IE browsers
900    }
901  };
902
903  /**
904   * Get HTML element which is the target of the event
905   * @param {Event} event
906   * @return {Element} target element
907   */
908  exports.getTarget = function (event) {
909    // code from http://www.quirksmode.org/js/events_properties.html
910    if (!event) {
911      event = window.event;
912    }
913
914    var target;
915
916    if (event.target) {
917      target = event.target;
918    } else if (event.srcElement) {
919      target = event.srcElement;
920    }
921
922    if (target.nodeType != undefined && target.nodeType == 3) {
923      // defeat Safari bug
924      target = target.parentNode;
925    }
926
927    return target;
928  };
929
930  /**
931   * Check if given element contains given parent somewhere in the DOM tree
932   * @param {Element} element
933   * @param {Element} parent
934   */
935  exports.hasParent = function (element, parent) {
936    var e = element;
937
938    while (e) {
939      if (e === parent) {
940        return true;
941      }
942      e = e.parentNode;
943    }
944
945    return false;
946  };
947
948  exports.option = {};
949
950  /**
951   * Convert a value into a boolean
952   * @param {Boolean | function | undefined} value
953   * @param {Boolean} [defaultValue]
954   * @returns {Boolean} bool
955   */
956  exports.option.asBoolean = function (value, defaultValue) {
957    if (typeof value == 'function') {
958      value = value();
959    }
960
961    if (value != null) {
962      return value != false;
963    }
964
965    return defaultValue || null;
966  };
967
968  /**
969   * Convert a value into a number
970   * @param {Boolean | function | undefined} value
971   * @param {Number} [defaultValue]
972   * @returns {Number} number
973   */
974  exports.option.asNumber = function (value, defaultValue) {
975    if (typeof value == 'function') {
976      value = value();
977    }
978
979    if (value != null) {
980      return Number(value) || defaultValue || null;
981    }
982
983    return defaultValue || null;
984  };
985
986  /**
987   * Convert a value into a string
988   * @param {String | function | undefined} value
989   * @param {String} [defaultValue]
990   * @returns {String} str
991   */
992  exports.option.asString = function (value, defaultValue) {
993    if (typeof value == 'function') {
994      value = value();
995    }
996
997    if (value != null) {
998      return String(value);
999    }
1000
1001    return defaultValue || null;
1002  };
1003
1004  /**
1005   * Convert a size or location into a string with pixels or a percentage
1006   * @param {String | Number | function | undefined} value
1007   * @param {String} [defaultValue]
1008   * @returns {String} size
1009   */
1010  exports.option.asSize = function (value, defaultValue) {
1011    if (typeof value == 'function') {
1012      value = value();
1013    }
1014
1015    if (exports.isString(value)) {
1016      return value;
1017    } else if (exports.isNumber(value)) {
1018      return value + 'px';
1019    } else {
1020      return defaultValue || null;
1021    }
1022  };
1023
1024  /**
1025   * Convert a value into a DOM element
1026   * @param {HTMLElement | function | undefined} value
1027   * @param {HTMLElement} [defaultValue]
1028   * @returns {HTMLElement | null} dom
1029   */
1030  exports.option.asElement = function (value, defaultValue) {
1031    if (typeof value == 'function') {
1032      value = value();
1033    }
1034
1035    return value || defaultValue || null;
1036  };
1037
1038  /**
1039   * http://stackoverflow.com/questions/5623838/rgb-to-hex-and-hex-to-rgb
1040   *
1041   * @param {String} hex
1042   * @returns {{r: *, g: *, b: *}} | 255 range
1043   */
1044  exports.hexToRGB = function (hex) {
1045    // Expand shorthand form (e.g. "03F") to full form (e.g. "0033FF")
1046    var shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i;
1047    hex = hex.replace(shorthandRegex, function (m, r, g, b) {
1048      return r + r + g + g + b + b;
1049    });
1050    var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
1051    return result ? {
1052      r: parseInt(result[1], 16),
1053      g: parseInt(result[2], 16),
1054      b: parseInt(result[3], 16)
1055    } : null;
1056  };
1057
1058  /**
1059   * This function takes color in hex format or rgb() or rgba() format and overrides the opacity. Returns rgba() string.
1060   * @param color
1061   * @param opacity
1062   * @returns {*}
1063   */
1064  exports.overrideOpacity = function (color, opacity) {
1065    if (color.indexOf('rgba') != -1) {
1066      return color;
1067    } else if (color.indexOf('rgb') != -1) {
1068      var rgb = color.substr(color.indexOf('(') + 1).replace(')', '').split(',');
1069      return 'rgba(' + rgb[0] + ',' + rgb[1] + ',' + rgb[2] + ',' + opacity + ')';
1070    } else {
1071      var rgb = exports.hexToRGB(color);
1072      if (rgb == null) {
1073        return color;
1074      } else {
1075        return 'rgba(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ',' + opacity + ')';
1076      }
1077    }
1078  };
1079
1080  /**
1081   *
1082   * @param red     0 -- 255
1083   * @param green   0 -- 255
1084   * @param blue    0 -- 255
1085   * @returns {string}
1086   * @constructor
1087   */
1088  exports.RGBToHex = function (red, green, blue) {
1089    return '#' + ((1 << 24) + (red << 16) + (green << 8) + blue).toString(16).slice(1);
1090  };
1091
1092  /**
1093   * Parse a color property into an object with border, background, and
1094   * highlight colors
1095   * @param {Object | String} color
1096   * @return {Object} colorObject
1097   */
1098  exports.parseColor = function (color) {
1099    var c;
1100    if (exports.isString(color) === true) {
1101      if (exports.isValidRGB(color) === true) {
1102        var rgb = color.substr(4).substr(0, color.length - 5).split(',').map(function (value) {
1103          return parseInt(value);
1104        });
1105        color = exports.RGBToHex(rgb[0], rgb[1], rgb[2]);
1106      }
1107      if (exports.isValidHex(color) === true) {
1108        var hsv = exports.hexToHSV(color);
1109        var lighterColorHSV = { h: hsv.h, s: hsv.s * 0.8, v: Math.min(1, hsv.v * 1.02) };
1110        var darkerColorHSV = { h: hsv.h, s: Math.min(1, hsv.s * 1.25), v: hsv.v * 0.8 };
1111        var darkerColorHex = exports.HSVToHex(darkerColorHSV.h, darkerColorHSV.s, darkerColorHSV.v);
1112        var lighterColorHex = exports.HSVToHex(lighterColorHSV.h, lighterColorHSV.s, lighterColorHSV.v);
1113        c = {
1114          background: color,
1115          border: darkerColorHex,
1116          highlight: {
1117            background: lighterColorHex,
1118            border: darkerColorHex
1119          },
1120          hover: {
1121            background: lighterColorHex,
1122            border: darkerColorHex
1123          }
1124        };
1125      } else {
1126        c = {
1127          background: color,
1128          border: color,
1129          highlight: {
1130            background: color,
1131            border: color
1132          },
1133          hover: {
1134            background: color,
1135            border: color
1136          }
1137        };
1138      }
1139    } else {
1140      c = {};
1141      c.background = color.background || undefined;
1142      c.border = color.border || undefined;
1143
1144      if (exports.isString(color.highlight)) {
1145        c.highlight = {
1146          border: color.highlight,
1147          background: color.highlight
1148        };
1149      } else {
1150        c.highlight = {};
1151        c.highlight.background = color.highlight && color.highlight.background || undefined;
1152        c.highlight.border = color.highlight && color.highlight.border || undefined;
1153      }
1154
1155      if (exports.isString(color.hover)) {
1156        c.hover = {
1157          border: color.hover,
1158          background: color.hover
1159        };
1160      } else {
1161        c.hover = {};
1162        c.hover.background = color.hover && color.hover.background || undefined;
1163        c.hover.border = color.hover && color.hover.border || undefined;
1164      }
1165    }
1166
1167    return c;
1168  };
1169
1170  /**
1171   * http://www.javascripter.net/faq/rgb2hsv.htm
1172   *
1173   * @param red
1174   * @param green
1175   * @param blue
1176   * @returns {*}
1177   * @constructor
1178   */
1179  exports.RGBToHSV = function (red, green, blue) {
1180    red = red / 255;
1180green = green / 255;blue = blue / 255;
1181    var minRGB = Math.min(red, Math.min(green, blue));
1182    var maxRGB = Math.max(red, Math.max(green, blue));
1183
1184    // Black-gray-white
1185    if (minRGB == maxRGB) {
1186      return { h: 0, s: 0, v: minRGB };
1187    }
1188
1189    // Colors other than black-gray-white:
1190    var d = red == minRGB ? green - blue : blue == minRGB ? red - green : blue - red;
1191    var h = red == minRGB ? 3 : blue == minRGB ? 1 : 5;
1192    var hue = 60 * (h - d / (maxRGB - minRGB)) / 360;
1193    var saturation = (maxRGB - minRGB) / maxRGB;
1194    var value = maxRGB;
1195    return { h: hue, s: saturation, v: value };
1196  };
1197
1198  var cssUtil = {
1199    // split a string with css styles into an object with key/values
1200    split: function split(cssText) {
1201      var styles = {};
1202
1203      cssText.split(';').forEach(function (style) {
1204        if (style.trim() != '') {
1205          var parts = style.split(':');
1206          var key = parts[0].trim();
1207          var value = parts[1].trim();
1208          styles[key] = value;
1209        }
1210      });
1211
1212      return styles;
1213    },
1214
1215    // build a css text string from an object with key/values
1216    join: function join(styles) {
1217      return Object.keys(styles).map(function (key) {
1218        return key + ': ' + styles[key];
1219      }).join('; ');
1220    }
1221  };
1222
1223  /**
1224   * Append a string with css styles to an element
1225   * @param {Element} element
1226   * @param {String} cssText
1227   */
1228  exports.addCssText = function (element, cssText) {
1229    var currentStyles = cssUtil.split(element.style.cssText);
1230    var newStyles = cssUtil.split(cssText);
1231    var styles = exports.extend(currentStyles, newStyles);
1232
1233    element.style.cssText = cssUtil.join(styles);
1234  };
1235
1236  /**
1237   * Remove a string with css styles from an element
1238   * @param {Element} element
1239   * @param {String} cssText
1240   */
1241  exports.removeCssText = function (element, cssText) {
1242    var styles = cssUtil.split(element.style.cssText);
1243    var removeStyles = cssUtil.split(cssText);
1244
1245    for (var key in removeStyles) {
1246      if (removeStyles.hasOwnProperty(key)) {
1247        delete styles[key];
1248      }
1249    }
1250
1251    element.style.cssText = cssUtil.join(styles);
1252  };
1253
1254  /**
1255   * https://gist.github.com/mjijackson/5311256
1256   * @param h
1257   * @param s
1258   * @param v
1259   * @returns {{r: number, g: number, b: number}}
1260   * @constructor
1261   */
1262  exports.HSVToRGB = function (h, s, v) {
1263    var r, g, b;
1264
1265    var i = Math.floor(h * 6);
1266    var f = h * 6 - i;
1267    var p = v * (1 - s);
1268    var q = v * (1 - f * s);
1269    var t = v * (1 - (1 - f) * s);
1270
1271    switch (i % 6) {
1272      case 0:
1273        r = v, g = t, b = p;break;
1274      case 1:
1275        r = q, g = v, b = p;break;
1276      case 2:
1277        r = p, g = v, b = t;break;
1278      case 3:
1279        r = p, g = q, b = v;break;
1280      case 4:
1281        r = t, g = p, b = v;break;
1282      case 5:
1283        r = v, g = p, b = q;break;
1284    }
1285
1286    return { r: Math.floor(r * 255), g: Math.floor(g * 255), b: Math.floor(b * 255) };
1287  };
1288
1289  exports.HSVToHex = function (h, s, v) {
1290    var rgb = exports.HSVToRGB(h, s, v);
1291    return exports.RGBToHex(rgb.r, rgb.g, rgb.b);
1292  };
1293
1294  exports.hexToHSV = function (hex) {
1295    var rgb = exports.hexToRGB(hex);
1296    return exports.RGBToHSV(rgb.r, rgb.g, rgb.b);
1297  };
1298
1299  exports.isValidHex = function (hex) {
1300    var isOk = /(^#[0-9A-F]{6}$)|(^#[0-9A-F]{3}$)/i.test(hex);
1301    return isOk;
1302  };
1303
1304  exports.isValidRGB = function (rgb) {
1305    rgb = rgb.replace(' ', '');
1306    var isOk = /rgb\((\d{1,3}),(\d{1,3}),(\d{1,3})\)/i.test(rgb);
1307    return isOk;
1308  };
1309  exports.isValidRGBA = function (rgba) {
1310    rgba = rgba.replace(' ', '');
1311    var isOk = /rgba\((\d{1,3}),(\d{1,3}),(\d{1,3}),(.{1,3})\)/i.test(rgba);
1312    return isOk;
1313  };
1314
1315  /**
1316   * This recursively redirects the prototype of JSON objects to the referenceObject
1317   * This is used for default options.
1318   *
1319   * @param referenceObject
1320   * @returns {*}
1321   */
1322  exports.selectiveBridgeObject = function (fields, referenceObject) {
1323    if (typeof referenceObject == 'object') {
1324      var objectTo = Object.create(referenceObject);
1325      for (var i = 0; i < fields.length; i++) {
1326        if (referenceObject.hasOwnProperty(fields[i])) {
1327          if (typeof referenceObject[fields[i]] == 'object') {
1328            objectTo[fields[i]] = exports.bridgeObject(referenceObject[fields[i]]);
1329          }
1330        }
1331      }
1332      return objectTo;
1333    } else {
1334      return null;
1335    }
1336  };
1337
1338  /**
1339   * This recursively redirects the prototype of JSON objects to the referenceObject
1340   * This is used for default options.
1341   *
1342   * @param referenceObject
1343   * @returns {*}
1344   */
1345  exports.bridgeObject = function (referenceObject) {
1346    if (typeof referenceObject == 'object') {
1347      var objectTo = Object.create(referenceObject);
1348      for (var i in referenceObject) {
1349        if (referenceObject.hasOwnProperty(i)) {
1350          if (typeof referenceObject[i] == 'object') {
1351            objectTo[i] = exports.bridgeObject(referenceObject[i]);
1352          }
1353        }
1354      }
1355      return objectTo;
1356    } else {
1357      return null;
1358    }
1359  };
1360
1361  /**
1362   * this is used to set the options of subobjects in the options object. A requirement of these subobjects
1363   * is that they have an 'enabled' element which is optional for the user but mandatory for the program.
1364   *
1365   * @param [object] mergeTarget | this is either this.options or the options used for the groups.
1366   * @param [object] options     | options
1367   * @param [String] option      | this is the option key in the options argument
1368   * @private
1369   */
1370  exports.mergeOptions = function (mergeTarget, options, option) {
1371    var allowDeletion = arguments.length <= 3 || arguments[3] === undefined ? false : arguments[3];
1372    var globalOptions = arguments.length <= 4 || arguments[4] === undefined ? {} : arguments[4];
1373
1374    if (options[option] === null) {
1375      mergeTarget[option] = Object.create(globalOptions[option]);
1376    } else {
1377      if (options[option] !== undefined) {
1378        if (typeof options[option] === 'boolean') {
1379          mergeTarget[option].enabled = options[option];
1380        } else {
1381          if (options[option].enabled === undefined) {
1382            mergeTarget[option].enabled = true;
1383          }
1384          for (var prop in options[option]) {
1385            if (options[option].hasOwnProperty(prop)) {
1386              mergeTarget[option][prop] = options[option][prop];
1387            }
1388          }
1389        }
1390      }
1391    }
1392  };
1393
1394  /**
1395   * This function does a binary search for a visible item in a sorted list. If we find a visible item, the code that uses
1396   * this function will then iterate in both directions over this sorted list to find all visible items.
1397   *
1398   * @param {Item[]} orderedItems       | Items ordered by start
1399   * @param {function} searchFunction   | -1 is lower, 0 is found, 1 is higher
1400   * @param {String} field
1401   * @param {String} field2
1402   * @returns {number}
1403   * @private
1404   */
1405  exports.binarySearchCustom = function (orderedItems, searchFunction, field, field2) {
1406    var maxIterations = 10000;
1407    var iteration = 0;
1408    var low = 0;
1409    var high = orderedItems.length - 1;
1410
1411    while (low <= high && iteration < maxIterations) {
1412      var middle = Math.floor((low + high) / 2);
1413
1414      var item = orderedItems[middle];
1415      var value = field2 === undefined ? item[field] : item[field][field2];
1416
1417      var searchResult = searchFunction(value);
1418      if (searchResult == 0) {
1419        // jihaa, found a visible item!
1420        return middle;
1421      } else if (searchResult == -1) {
1422        // it is too small --> increase low
1423        low = middle + 1;
1424      } else {
1425        // it is too big --> decrease high
1426        high = middle - 1;
1427      }
1428
1429      iteration++;
1430    }
1431
1432    return -1;
1433  };
1434
1435  /**
1436   * This function does a binary search for a specific value in a sorted array. If it does not exist but is in between of
1437   * two values, we return either the one before or the one after, depending on user input
1438   * If it is found, we return the index, else -1.
1439   *
1440   * @param {Array} orderedItems
1441   * @param {{start: number, end: number}} target
1442   * @param {String} field
1443   * @param {String} sidePreference   'before' or 'after'
1444   * @returns {number}
1445   * @private
1446   */
1447  exports.binarySearchValue = function (orderedItems, target, field, sidePreference) {
1448    var maxIterations = 10000;
1449    var iteration = 0;
1450    var low = 0;
1451    var high = orderedItems.length - 1;
1452    var prevValue, value, nextValue, middle;
1453
1454    while (low <= high && iteration < maxIterations) {
1455      // get a new guess
1456      middle = Math.floor(0.5 * (high + low));
1457      prevValue = orderedItems[Math.max(0, middle - 1)][field];
1458      value = orderedItems[middle][field];
1459      nextValue = orderedItems[Math.min(orderedItems.length - 1, middle + 1)][field];
1460
1461      if (value == target) {
1462        // we found the target
1463        return middle;
1464      } else if (prevValue < target && value > target) {
1465        // target is in between of the previous and the current
1466        return sidePreference == 'before' ? Math.max(0, middle - 1) : middle;
1467      } else if (value < target && nextValue > target) {
1468        // target is in between of the current and the next
1469        return sidePreference == 'before' ? middle : Math.min(orderedItems.length - 1, middle + 1);
1470      } else {
1471        // didnt find the target, we need to change our boundaries.
1472        if (value < target) {
1473          // it is too small --> increase low
1474          low = middle + 1;
1475        } else {
1476          // it is too big --> decrease high
1477          high = middle - 1;
1478        }
1479      }
1480      iteration++;
1481    }
1482
1483    // didnt find anything. Return -1.
1484    return -1;
1485  };
1486
1487  /*
1488   * Easing Functions - inspired from http://gizma.com/easing/
1489   * only considering the t value for the range [0, 1] => [0, 1]
1490   * https://gist.github.com/gre/1650294
1491   */
1492  exports.easingFunctions = {
1493    // no easing, no acceleration
1494    linear: function linear(t) {
1495      return t;
1496    },
1497    // accelerating from zero velocity
1498    easeInQuad: function easeInQuad(t) {
1499      return t * t;
1500    },
1501    // decelerating to zero velocity
1502    easeOutQuad: function easeOutQuad(t) {
1503      return t * (2 - t);
1504    },
1505    // acceleration until halfway, then deceleration
1506    easeInOutQuad: function easeInOutQuad(t) {
1507      return t < .5 ? 2 * t * t : -1 + (4 - 2 * t) * t;
1508    },
1509    // accelerating from zero velocity
1510    easeInCubic: function easeInCubic(t) {
1511      return t * t * t;
1512    },
1513    // decelerating to zero velocity
1514    easeOutCubic: function easeOutCubic(t) {
1515      return --t * t * t + 1;
1516    },
1517    // acceleration until halfway, then deceleration
1518    easeInOutCubic: function easeInOutCubic(t) {
1519      return t < .5 ? 4 * t * t * t : (t - 1) * (2 * t - 2) * (2 * t - 2) + 1;
1520    },
1521    // accelerating from zero velocity
1522    easeInQuart: function easeInQuart(t) {
1523      return t * t * t * t;
1524    },
1525    // decelerating to zero velocity
1526    easeOutQuart: function easeOutQuart(t) {
1527      return 1 - --t * t * t * t;
1528    },
1529    // acceleration until halfway, then deceleration
1530    easeInOutQuart: function easeInOutQuart(t) {
1531      return t < .5 ? 8 * t * t * t * t : 1 - 8 * --t * t * t * t;
1532    },
1533    // accelerating from zero velocity
1534    easeInQuint: function easeInQuint(t) {
1535      return t * t * t * t * t;
1536    },
1537    // decelerating to zero velocity
1538    easeOutQuint: function easeOutQuint(t) {
1539      return 1 + --t * t * t * t * t;
1540    },
1541    // acceleration until halfway, then deceleration
1542    easeInOutQuint: function easeInOutQuint(t) {
1543      return t < .5 ? 16 * t * t * t * t * t : 1 + 16 * --t * t * t * t * t;
1544    }
1545  };
1546
1547/***/ },
1548/* 2 */
1549/***/ function(module, exports, __webpack_require__) {
1550
1551  // first check if moment.js is already loaded in the browser window, if so,
1552  // use this instance. Else, load via commonjs.
1553  'use strict';
1554
1555  module.exports = typeof window !== 'undefined' && window['moment'] || __webpack_require__(3);
1556
1557/***/ },
1558/* 3 */
1559/***/ function(module, exports, __webpack_require__) {
1560
1561  /* WEBPACK VAR INJECTION */(function(module) {//! moment.js
1562  //! version : 2.10.6
1563  //! authors : Tim Wood, Iskren Chernev, Moment.js contributors
1564  //! license : MIT
1565  //! momentjs.com
1566
1567  (function (global, factory) {
1568       true ? module.exports = factory() :
1569      typeof define === 'function' && define.amd ? define(factory) :
1570      global.moment = factory()
1571  }(this, function () { 'use strict';
1572
1573      var hookCallback;
1574
1575      function utils_hooks__hooks () {
1576          return hookCallback.apply(null, arguments);
1577      }
1578
1579      // This is done to register the method called with moment()
1580      // without creating circular dependencies.
1581      function setHookCallback (callback) {
1582          hookCallback = callback;
1583      }
1584
1585      function isArray(input) {
1586          return Object.prototype.toString.call(input) === '[object Array]';
1587      }
1588
1589      function isDate(input) {
1590          return input instanceof Date || Object.prototype.toString.call(input) === '[object Date]';
1591      }
1592
1593      function map(arr, fn) {
1594          var res = [], i;
1595          for (i = 0; i < arr.length; ++i) {
1596              res.push(fn(arr[i], i));
1597          }
1598          return res;
1599      }
1600
1601      function hasOwnProp(a, b) {
1602          return Object.prototype.hasOwnProperty.call(a, b);
1603      }
1604
1605      function extend(a, b) {
1606          for (var i in b) {
1607              if (hasOwnProp(b, i)) {
1608                  a[i] = b[i];
1609              }
1610          }
1611
1612          if (hasOwnProp(b, 'toString')) {
1613              a.toString = b.toString;
1614          }
1615
1616          if (hasOwnProp(b, 'valueOf')) {
1617              a.valueOf = b.valueOf;
1618          }
1619
1620          return a;
1621      }
1622
1623      function create_utc__createUTC (input, format, locale, strict) {
1624          return createLocalOrUTC(input, format, locale, strict, true).utc();
1625      }
1626
1627      function defaultParsingFlags() {
1628          // We need to deep clone this object.
1629          return {
1630              empty           : false,
1631              unusedTokens    : [],
1632              unusedInput     : [],
1633              overflow        : -2,
1634              charsLeftOver   : 0,
1635              nullInput       : false,
1636              invalidMonth    : null,
1637              invalidFormat   : false,
1638              userInvalidated : false,
1639              iso             : false
1640          };
1641      }
1642
1643      function getParsingFlags(m) {
1644          if (m._pf == null) {
1645              m._pf = defaultParsingFlags();
1646          }
1647          return m._pf;
1648      }
1649
1650      function valid__isValid(m) {
1651          if (m._isValid == null) {
1652              var flags = getParsingFlags(m);
1653              m._isValid = !isNaN(m._d.getTime()) &&
1654                  flags.overflow < 0 &&
1655                  !flags.empty &&
1656                  !flags.invalidMonth &&
1657                  !flags.invalidWeekday &&
1658                  !flags.nullInput &&
1659                  !flags.invalidFormat &&
1660                  !flags.userInvalidated;
1661
1662              if (m._strict) {
1663                  m._isValid = m._isValid &&
1664                      flags.charsLeftOver === 0 &&
1665                      flags.unusedTokens.length === 0 &&
1666                      flags.bigHour === undefined;
1667              }
1668          }
1669          return m._isValid;
1670      }
1671
1672      function valid__createInvalid (flags) {
1673          var m = create_utc__createUTC(NaN);
1674          if (flags != null) {
1675              extend(getParsingFlags(m), flags);
1676          }
1677          else {
1678              getParsingFlags(m).userInvalidated = true;
1679          }
1680
1681          return m;
1682      }
1683
1684      var momentProperties = utils_hooks__hooks.momentProperties = [];
1685
1686      function copyConfig(to, from) {
1687          var i, prop, val;
1688
1689          if (typeof from._isAMomentObject !== 'undefined') {
1690              to._isAMomentObject = from._isAMomentObject;
1691          }
1692          if (typeof from._i !== 'undefined') {
1693              to._i = from._i;
1694          }
1695          if (typeof from._f !== 'undefined') {
1696              to._f = from._f;
1697          }
1698          if (typeof from._l !== 'undefined') {
1699              to._l = from._l;
1700          }
1701          if (typeof from._strict !== 'undefined') {
1702              to._strict = from._strict;
1703          }
1704          if (typeof from._tzm !== 'undefined') {
1705              to._tzm = from._tzm;
1706          }
1707          if (typeof from._isUTC !== 'undefined') {
1708              to._isUTC = from._isUTC;
1709          }
1710          if (typeof from._offset !== 'undefined') {
1711              to._offset = from._offset;
1712          }
1713          if (typeof from._pf !== 'undefined') {
1714              to._pf = getParsingFlags(from);
1715          }
1716          if (typeof from._locale !== 'undefined') {
1717              to._locale = from._locale;
1718          }
1719
1720          if (momentProperties.length > 0) {
1721              for (i in momentProperties) {
1722                  prop = momentProperties[i];
1723                  val = from[prop];
1724                  if (typeof val !== 'undefined') {
1725                      to[prop] = val;
1726                  }
1727              }
1728          }
1729
1730          return to;
1731      }
1732
1733      var updateInProgress = false;
1734
1735      // Moment prototype object
1736      function Moment(config) {
1737          copyConfig(this, config);
1738          this._d = new Date(config._d != null ? config._d.getTime() : NaN);
1739          // Prevent infinite loop in case updateOffset creates new moment
1740          // objects.
1741          if (updateInProgress === false) {
1742              updateInProgress = true;
1743              utils_hooks__hooks.updateOffset(this);
1744              updateInProgress = false;
1745          }
1746      }
1747
1748      function isMoment (obj) {
1749          return obj instanceof Moment || (obj != null && obj._isAMomentObject != null);
1750      }
1751
1752      function absFloor (number) {
1753          if (number < 0) {
1754              return Math.ceil(number);
1755          } else {
1756              return Math.floor(number);
1757          }
1758      }
1759
1760      function toInt(argumentForCoercion) {
1761          var coercedNumber = +argumentForCoercion,
1762              value = 0;
1763
1764          if (coercedNumber !== 0 && isFinite(coercedNumber)) {
1765              value = absFloor(coercedNumber);
1766          }
1767
1768          return value;
1769      }
1770
1771      function compareArrays(array1, array2, dontConvert) {
1772          var len = Math.min(array1.length, array2.length),
1773              lengthDiff = Math.abs(array1.length - array2.length),
1774              diffs = 0,
1775              i;
1776          for (i = 0; i < len; i++) {
1777              if ((dontConvert && array1[i] !== array2[i]) ||
1778                  (!dontConvert && toInt(array1[i]) !== toInt(array2[i]))) {
1779                  diffs++;
1780              }
1781          }
1782          return diffs + lengthDiff;
1783      }
1784
1785      function Locale() {
1786      }
1787
1788      var locales = {};
1789      var globalLocale;
1790
1791      function normalizeLocale(key) {
1792          return key ? key.toLowerCase().replace('_', '-') : key;
1793      }
1794
1795      // pick the locale from the array
1796      // try ['en-au', 'en-gb'] as 'en-au', 'en-gb', 'en', as in move through the list tr
1796ying each
1797      // substring from most specific to least, but move to the next array item if it's a more specific variant than the current root
1798      function chooseLocale(names) {
1799          var i = 0, j, next, locale, split;
1800
1801          while (i < names.length) {
1802              split = normalizeLocale(names[i]).split('-');
1803              j = split.length;
1804              next = normalizeLocale(names[i + 1]);
1805              next = next ? next.split('-') : null;
1806              while (j > 0) {
1807                  locale = loadLocale(split.slice(0, j).join('-'));
1808                  if (locale) {
1809                      return locale;
1810                  }
1811                  if (next && next.length >= j && compareArrays(split, next, true) >= j - 1) {
1812                      //the next array item is better than a shallower substring of this one
1813                      break;
1814                  }
1815                  j--;
1816              }
1817              i++;
1818          }
1819          return null;
1820      }
1821
1822      function loadLocale(name) {
1823          var oldLocale = null;
1824          // TODO: Find a better way to register and load all the locales in Node
1825          if (!locales[name] && typeof module !== 'undefined' &&
1826                  module && module.exports) {
1827              try {
1828                  oldLocale = globalLocale._abbr;
1829                  !(function webpackMissingModule() { var e = new Error("Cannot find module \"./locale\""); e.code = 'MODULE_NOT_FOUND'; throw e; }());
1830                  // because defineLocale currently also sets the global locale, we
1831                  // want to undo that for lazy loaded locales
1832                  locale_locales__getSetGlobalLocale(oldLocale);
1833              } catch (e) { }
1834          }
1835          return locales[name];
1836      }
1837
1838      // This function will load locale and then set the global locale.  If
1839      // no arguments are passed in, it will simply return the current global
1840      // locale key.
1841      function locale_locales__getSetGlobalLocale (key, values) {
1842          var data;
1843          if (key) {
1844              if (typeof values === 'undefined') {
1845                  data = locale_locales__getLocale(key);
1846              }
1847              else {
1848                  data = defineLocale(key, values);
1849              }
1850
1851              if (data) {
1852                  // moment.duration._locale = moment._locale = data;
1853                  globalLocale = data;
1854              }
1855          }
1856
1857          return globalLocale._abbr;
1858      }
1859
1860      function defineLocale (name, values) {
1861          if (values !== null) {
1862              values.abbr = name;
1863              locales[name] = locales[name] || new Locale();
1864              locales[name].set(values);
1865
1866              // backwards compat for now: also set the locale
1867              locale_locales__getSetGlobalLocale(name);
1868
1869              return locales[name];
1870          } else {
1871              // useful for testing
1872              delete locales[name];
1873              return null;
1874          }
1875      }
1876
1877      // returns locale data
1878      function locale_locales__getLocale (key) {
1879          var locale;
1880
1881          if (key && key._locale && key._locale._abbr) {
1882              key = key._locale._abbr;
1883          }
1884
1885          if (!key) {
1886              return globalLocale;
1887          }
1888
1889          if (!isArray(key)) {
1890              //short-circuit everything else
1891              locale = loadLocale(key);
1892              if (locale) {
1893                  return locale;
1894              }
1895              key = [key];
1896          }
1897
1898          return chooseLocale(key);
1899      }
1900
1901      var aliases = {};
1902
1903      function addUnitAlias (unit, shorthand) {
1904          var lowerCase = unit.toLowerCase();
1905          aliases[lowerCase] = aliases[lowerCase + 's'] = aliases[shorthand] = unit;
1906      }
1907
1908      function normalizeUnits(units) {
1909          return typeof units === 'string' ? aliases[units] || aliases[units.toLowerCase()] : undefined;
1910      }
1911
1912      function normalizeObjectUnits(inputObject) {
1913          var normalizedInput = {},
1914              normalizedProp,
1915              prop;
1916
1917          for (prop in inputObject) {
1918              if (hasOwnProp(inputObject, prop)) {
1919                  normalizedProp = normalizeUnits(prop);
1920                  if (normalizedProp) {
1921                      normalizedInput[normalizedProp] = inputObject[prop];
1922                  }
1923              }
1924          }
1925
1926          return normalizedInput;
1927      }
1928
1929      function makeGetSet (unit, keepTime) {
1930          return function (value) {
1931              if (value != null) {
1932                  get_set__set(this, unit, value);
1933                  utils_hooks__hooks.updateOffset(this, keepTime);
1934                  return this;
1935              } else {
1936                  return get_set__get(this, unit);
1937              }
1938          };
1939      }
1940
1941      function get_set__get (mom, unit) {
1942          return mom._d['get' + (mom._isUTC ? 'UTC' : '') + unit]();
1943      }
1944
1945      function get_set__set (mom, unit, value) {
1946          return mom._d['set' + (mom._isUTC ? 'UTC' : '') + unit](value);
1947      }
1948
1949      // MOMENTS
1950
1951      function getSet (units, value) {
1952          var unit;
1953          if (typeof units === 'object') {
1954              for (unit in units) {
1955                  this.set(unit, units[unit]);
1956              }
1957          } else {
1958              units = normalizeUnits(units);
1959              if (typeof this[units] === 'function') {
1960                  return this[units](value);
1961              }
1962          }
1963          return this;
1964      }
1965
1966      function zeroFill(number, targetLength, forceSign) {
1967          var absNumber = '' + Math.abs(number),
1968              zerosToFill = targetLength - absNumber.length,
1969              sign = number >= 0;
1970          return (sign ? (forceSign ? '+' : '') : '-') +
1971              Math.pow(10, Math.max(0, zerosToFill)).toString().substr(1) + absNumber;
1972      }
1973
1974      var formattingTokens = /(\[[^\[]*\])|(\\)?(Mo|MM?M?M?|Do|DDDo|DD?D?D?|ddd?d?|do?|w[o|w]?|W[o|W]?|Q|YYYYYY|YYYYY|YYYY|YY|gg(ggg?)?|GG(GGG?)?|e|E|a|A|hh?|HH?|mm?|ss?|S{1,9}
vendor: 11,529 bytes, lines 1974-2307
1974|x|X|zz?|ZZ?|.)/g;
1975
1976      var localFormattingTokens = /(\[[^\[]*\])|(\\)?(LTS|LT|LL?L?L?|l{1,4})/g;
1977
1978      var formatFunctions = {};
1979
1980      var formatTokenFunctions = {};
1981
1982      // token:    'M'
1983      // padded:   ['MM', 2]
1984      // ordinal:  'Mo'
1985      // callback: function () { this.month() + 1 }
1986      function addFormatToken (token, padded, ordinal, callback) {
1987          var func = callback;
1988          if (typeof callback === 'string') {
1989              func = function () {
1990                  return this[callback]();
1991              };
1992          }
1993          if (token) {
1994              formatTokenFunctions[token] = func;
1995          }
1996          if (padded) {
1997              formatTokenFunctions[padded[0]] = function () {
1998                  return zeroFill(func.apply(this, arguments), padded[1], padded[2]);
1999              };
2000          }
2001          if (ordinal) {
2002              formatTokenFunctions[ordinal] = function () {
2003                  return this.localeData().ordinal(func.apply(this, arguments), token);
2004              };
2005          }
2006      }
2007
2008      function removeFormattingTokens(input) {
2009          if (input.match(/\[[\s\S]/)) {
2010              return input.replace(/^\[|\]$/g, '');
2011          }
2012          return input.replace(/\\/g, '');
2013      }
2014
2015      function makeFormatFunction(format) {
2016          var array = format.match(formattingTokens), i, length;
2017
2018          for (i = 0, length = array.length; i < length; i++) {
2019              if (formatTokenFunctions[array[i]]) {
2020                  array[i] = formatTokenFunctions[array[i]];
2021              } else {
2022                  array[i] = removeFormattingTokens(array[i]);
2023              }
2024          }
2025
2026          return function (mom) {
2027              var output = '';
2028              for (i = 0; i < length; i++) {
2029                  output += array[i] instanceof Function ? array[i].call(mom, format) : array[i];
2030              }
2031              return output;
2032          };
2033      }
2034
2035      // format date using native date object
2036      function formatMoment(m, format) {
2037          if (!m.isValid()) {
2038              return m.localeData().invalidDate();
2039          }
2040
2041          format = expandFormat(format, m.localeData());
2042          formatFunctions[format] = formatFunctions[format] || makeFormatFunction(format);
2043
2044          return formatFunctions[format](m);
2045      }
2046
2047      function expandFormat(format, locale) {
2048          var i = 5;
2049
2050          function replaceLongDateFormatTokens(input) {
2051              return locale.longDateFormat(input) || input;
2052          }
2053
2054          localFormattingTokens.lastIndex = 0;
2055          while (i >= 0 && localFormattingTokens.test(format)) {
2056              format = format.replace(localFormattingTokens, replaceLongDateFormatTokens);
2057              localFormattingTokens.lastIndex = 0;
2058              i -= 1;
2059          }
2060
2061          return format;
2062      }
2063
2064      var match1         = /\d/;            //       0 - 9
2065      var match2         = /\d\d/;          //      00 - 99
2066      var match3         = /\d{3}/;         //     000 - 999
2067      var match4         = /\d{4}/;         //    0000 - 9999
2068      var match6         = /[+-]?\d{6}/;    // -999999 - 999999
2069      var match1to2      = /\d\d?/;         //       0 - 99
2070      var match1to3      = /\d{1,3}/;       //       0 - 999
2071      var match1to4      = /\d{1,4}/;       //       0 - 9999
2072      var match1to6      = /[+-]?\d{1,6}/;  // -999999 - 999999
2073
2074      var matchUnsigned  = /\d+/;           //       0 - inf
2075      var matchSigned    = /[+-]?\d+/;      //    -inf - inf
2076
2077      var matchOffset    = /Z|[+-]\d\d:?\d\d/gi; // +00:00 -00:00 +0000 -0000 or Z
2078
2079      var matchTimestamp = /[+-]?\d+(\.\d{1,3})?/; // 123456789 123456789.123
2080
2081      // any word (or two) characters or numbers including two/three word month in arabic.
2082      var matchWord = /[0-9]*['a-z\u00A0-\u05FF\u0700-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+|[\u0600-\u06FF\/]+(\s*?[\u0600-\u06FF]+){1,2}/i;
2083
2084      var regexes = {};
2085
2086      function isFunction (sth) {
2087          // https://github.com/moment/moment/issues/2325
2088          return typeof sth === 'function' &&
2089              Object.prototype.toString.call(sth) === '[object Function]';
2090      }
2091
2092
2093      function addRegexToken (token, regex, strictRegex) {
2094          regexes[token] = isFunction(regex) ? regex : function (isStrict) {
2095              return (isStrict && strictRegex) ? strictRegex : regex;
2096          };
2097      }
2098
2099      function getParseRegexForToken (token, config) {
2100          if (!hasOwnProp(regexes, token)) {
2101              return new RegExp(unescapeFormat(token));
2102          }
2103
2104          return regexes[token](config._strict, config._locale);
2105      }
2106
2107      // Code from http://stackoverflow.com/questions/3561493/is-there-a-regexp-escape-function-in-javascript
2108      function unescapeFormat(s) {
2109          return s.replace('\\', '').replace(/\\(\[)|\\(\])|\[([^\]\[]*)\]|\\(.)/g, function (matched, p1, p2, p3, p4) {
2110              return p1 || p2 || p3 || p4;
2111          }).replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
2112      }
2113
2114      var tokens = {};
2115
2116      function addParseToken (token, callback) {
2117          var i, func = callback;
2118          if (typeof token === 'string') {
2119              token = [token];
2120          }
2121          if (typeof callback === 'number') {
2122              func = function (input, array) {
2123                  array[callback] = toInt(input);
2124              };
2125          }
2126          for (i = 0; i < token.length; i++) {
2127              tokens[token[i]] = func;
2128          }
2129      }
2130
2131      function addWeekParseToken (token, callback) {
2132          addParseToken(token, function (input, array, config, token) {
2133              config._w = config._w || {};
2134              callback(input, config._w, config, token);
2135          });
2136      }
2137
2138      function addTimeToArrayFromToken(token, input, config) {
2139          if (input != null && hasOwnProp(tokens, token)) {
2140              tokens[token](input, config._a, config, token);
2141          }
2142      }
2143
2144      var YEAR = 0;
2145      var MONTH = 1;
2146      var DATE = 2;
2147      var HOUR = 3;
2148      var MINUTE = 4;
2149      var SECOND = 5;
2150      var MILLISECOND = 6;
2151
2152      function daysInMonth(year, month) {
2153          return new Date(Date.UTC(year, month + 1, 0)).getUTCDate();
2154      }
2155
2156      // FORMATTING
2157
2158      addFormatToken('M', ['MM', 2], 'Mo', function () {
2159          return this.month() + 1;
2160      });
2161
2162      addFormatToken('MMM', 0, 0, function (format) {
2163          return this.localeData().monthsShort(this, format);
2164      });
2165
2166      addFormatToken('MMMM', 0, 0, function (format) {
2167          return this.localeData().months(this, format);
2168      });
2169
2170      // ALIASES
2171
2172      addUnitAlias('month', 'M');
2173
2174      // PARSING
2175
2176      addRegexToken('M',    match1to2);
2177      addRegexToken('MM',   match1to2, match2);
2178      addRegexToken('MMM',  matchWord);
2179      addRegexToken('MMMM', matchWord);
2180
2181      addParseToken(['M', 'MM'], function (input, array) {
2182          array[MONTH] = toInt(input) - 1;
2183      });
2184
2185      addParseToken(['MMM', 'MMMM'], function (input, array, config, token) {
2186          var month = config._locale.monthsParse(input, token, config._strict);
2187          // if we didn't find a month name, mark the date as invalid.
2188          if (month != null) {
2189              array[MONTH] = month;
2190          } else {
2191              getParsingFlags(config).invalidMonth = input;
2192          }
2193      });
2194
2195      // LOCALES
2196
2197      var defaultLocaleMonths = 'January_February_March_April_May_June_July_August_September_October_November_December'.split('_');
2198      function localeMonths (m) {
2199          return this._months[m.month()];
2200      }
2201
2202      var defaultLocaleMonthsShort = 'Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec'.split('_');
2203      function localeMonthsShort (m) {
2204          return this._monthsShort[m.month()];
2205      }
2206
2207      function localeMonthsParse (monthName, format, strict) {
2208          var i, mom, regex;
2209
2210          if (!this._monthsParse) {
2211              this._monthsParse = [];
2212              this._longMonthsParse = [];
2213              this._shortMonthsParse = [];
2214          }
2215
2216          for (i = 0; i < 12; i++) {
2217              // make the regex if we don't have it already
2218              mom = create_utc__createUTC([2000, i]);
2219              if (strict && !this._longMonthsParse[i]) {
2220                  this._longMonthsParse[i] = new RegExp('^' + this.months(mom, '').replace('.', '') + '$', 'i');
2221                  this._shortMonthsParse[i] = new RegExp('^' + this.monthsShort(mom, '').replace('.', '') + '$', 'i');
2222              }
2223              if (!strict && !this._monthsParse[i]) {
2224                  regex = '^' + this.months(mom, '') + '|^' + this.monthsShort(mom, '');
2225                  this._monthsParse[i] = new RegExp(regex.replace('.', ''), 'i');
2226              }
2227              // test the regex
2228              if (strict && format === 'MMMM' && this._longMonthsParse[i].test(monthName)) {
2229                  return i;
2230              } else if (strict && format === 'MMM' && this._shortMonthsParse[i].test(monthName)) {
2231                  return i;
2232              } else if (!strict && this._monthsParse[i].test(monthName)) {
2233                  return i;
2234              }
2235          }
2236      }
2237
2238      // MOMENTS
2239
2240      function setMonth (mom, value) {
2241          var dayOfMonth;
2242
2243          // TODO: Move this out of here!
2244          if (typeof value === 'string') {
2245              value = mom.localeData().monthsParse(value);
2246              // TODO: Another silent failure?
2247              if (typeof value !== 'number') {
2248                  return mom;
2249              }
2250          }
2251
2252          dayOfMonth = Math.min(mom.date(), daysInMonth(mom.year(), value));
2253          mom._d['set' + (mom._isUTC ? 'UTC' : '') + 'Month'](value, dayOfMonth);
2254          return mom;
2255      }
2256
2257      function getSetMonth (value) {
2258          if (value != null) {
2259              setMonth(this, value);
2260              utils_hooks__hooks.updateOffset(this, true);
2261              return this;
2262          } else {
2263              return get_set__get(this, 'Month');
2264          }
2265      }
2266
2267      function getDaysInMonth () {
2268          return daysInMonth(this.year(), this.month());
2269      }
2270
2271      function checkOverflow (m) {
2272          var overflow;
2273          var a = m._a;
2274
2275          if (a && getParsingFlags(m).overflow === -2) {
2276              overflow =
2277                  a[MONTH]       < 0 || a[MONTH]       > 11  ? MONTH :
2278                  a[DATE]        < 1 || a[DATE]        > daysInMonth(a[YEAR], a[MONTH]) ? DATE :
2279                  a[HOUR]        < 0 || a[HOUR]        > 24 || (a[HOUR] === 24 && (a[MINUTE] !== 0 || a[SECOND] !== 0 || a[MILLISECOND] !== 0)) ? HOUR :
2280                  a[MINUTE]      < 0 || a[MINUTE]      > 59  ? MINUTE :
2281                  a[SECOND]      < 0 || a[SECOND]      > 59  ? SECOND :
2282                  a[MILLISECOND] < 0 || a[MILLISECOND] > 999 ? MILLISECOND :
2283                  -1;
2284
2285              if (getParsingFlags(m)._overflowDayOfYear && (overflow < YEAR || overflow > DATE)) {
2286                  overflow = DATE;
2287              }
2288
2289              getParsingFlags(m).overflow = overflow;
2290          }
2291
2292          return m;
2293      }
2294
2295      function warn(msg) {
2296          if (utils_hooks__hooks.suppressDeprecationWarnings === false && typeof console !== 'undefined' && console.warn) {
2297              console.warn('Deprecation warning: ' + msg);
2298          }
2299      }
2300
2301      function deprecate(msg, fn) {
2302          var firstTime = true;
2303
2304          return extend(function () {
2305              if (firstTime) {
2306                  warn(msg + '\n' + (new Error()).stack);
2307                  firstTime = false;
2308              }
2309              return fn.apply(this, arguments);
2310          }, fn);
2311      }
2312
2313      var deprecations = {};
2314
2315      function deprecateSimple(name, msg) {
2316          if (!deprecations[name]) {
2317              warn(msg);
2318              deprecations[name] = true;
2319          }
2320      }
2321
2322      utils_hooks__hooks.suppressDeprecationWarnings = false;
2323
2324      var from_string__isoRegex = /^\s*(?:[+-]\d{6}|\d{4})-(?:(\d\d-\d\d)|(W\d\d$)|(W\d\d-\d)|(\d\d\d))((T| )(\d\d(:\d\d(:\d\d(\.\d+)?)?)?)?([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?$/;
2325
2326      var isoDates = [
2327          ['YYYYYY-MM-DD', /[+-]\d{6}-\d{2}-\d{2}/],
2328          ['YYYY-MM-DD', /\d{4}-\d{2}-\d{2}/],
2329          ['GGGG-[W]WW-E', /\d{4}-W\d{2}-\d/],
2330          ['GGGG-[W]WW', /\d{4}-W\d{2}/],
2331          ['YYYY-DDD', /\d{4}-\d{3}/]
2332      ];
2333
2334      // iso time formats and regexes
2335      var isoTimes = [
2336          ['HH:mm:ss.SSSS', /(T| )\d\d:\d\d:\d\d\.\d+/],
2337          ['HH:mm:ss', /(T| )\d\d:\d\d:\d\d/],
2338          ['HH:mm', /(T| )\d\d:\d\d/],
2339          ['HH', /(T| )\d\d/]
2340      ];
2341
2342      var aspNetJsonRegex = /^\/?Date\((\-?\d+)/i;
2343
2344      // date from iso format
2345      function configFromISO(config) {
2346          var i, l,
2347              string = config._i,
2348              match = from_string__isoRegex.exec(string);
2349
2350          if (match) {
2351              getParsingFlags(config).iso = true;
2352              for (i = 0, l = isoDates.length; i < l; i++) {
2353                  if (isoDates[i][1].exec(string)) {
2354                      config._f = isoDates[i][0];
2355                      break;
2356                  }
2357              }
2358              for (i = 0, l = isoTimes.length; i < l; i++) {
2359                  if (isoTimes[i][1].exec(string)) {
2360                      // match[6] should be 'T' or space
2361                      config._f += (match[6] || ' ') + isoTimes[i][0];
2362                      break;
2363                  }
2364              }
2365              if (string.match(matchOffset)) {
2366                  config._f += 'Z';
2367              }
2368              configFromStringAndFormat(config);
2369          } else {
2370              config._isValid = false;
2371          }
2372      }
2373
2374      // date from iso format or fallback
2375      function configFromString(config) {
2376          var matched = aspNetJsonRegex.exec(config._i);
2377
2378          if (matched !== null) {
2379              config._d = new Date(+matched[1]);
2380              return;
2381          }
2382
2383          configFromISO(config);
2384          if (config._isValid === false) {
2385              delete config._isValid;
2386              utils_hooks__hooks.createFromInputFallback(config);
2387          }
2388      }
2389
2390      utils_hooks__hooks.createFromInputFallback = deprecate(
2391          'moment construction falls back to js Date. This is ' +
2392          'discouraged and will be removed in upcoming major ' +
2393          'release. Please refer to ' +
2394          'https://github.com/moment/moment/issues/1407 for more info.',
2395          function (config) {
2396              config._d = new Date(config._i + (config._useUTC ? ' UTC' : ''));
2397          }
2398      );
2399
2400      function createDate (y, m, d, h, M, s, ms) {
2401          //can't just apply() to create a date:
2402          //http://stackoverflow.com/questions/181348/instantiating-a-javascript-object-by-calling-prototype-constructor-apply
2403          var date = new Date(y, m, d, h, M, s, ms);
2404
2405          //the date constructor doesn't accept years < 1970
2406          if (y < 1970) {
2407              date.setFullYear(y);
2408          }
2409          return date;
2410      }
2411
2412      function createUTCDate (y) {
2413          var date = new Date(Date.UTC.apply(null, arguments));
2414          if (y < 1970) {
2415              date.setUTCFullYear(y);
2416          }
2417          return date;
2418      }
2419
2420      addFormatToken(0, ['YY', 2], 0, function () {
2421          return this.year() % 100;
2422      });
2423
2424      addFormatToken(0, ['YYYY',   4],       0, 'year');
2425      addFormatToken(0, ['YYYYY',  5],       0, 'year');
2426      addFormatToken(0, ['YYYYYY', 6, true], 0, 'year');
2427
2428      // ALIASES
2429
2430      addUnitAlias('year', 'y');
2431
2432      // PARSING
2433
2434      addRegexToken('Y',      matchSigned);
2435      addRegexToken('YY',     match1to2, match2);
2436      addRegexToken('YYYY',   match1to4, match4);
2437      addRegexToken('YYYYY',  match1to6, match6);
2438      addRegexToken('YYYYYY', match1to6, match6);
2439
2440      addParseToken(['YYYYY', 'YYYYYY'], YEAR);
2441      addParseToken('YYYY', function (input, array) {
2442          array[YEAR] = input.length === 2 ? utils_hooks__hooks.parseTwoDigitYear(input) : toInt(input);
2443      });
2444      addParseToken('YY', function (input, array) {
2445          array[YEAR] = utils_hooks__hooks.parseTwoDigitYear(input);
2446      });
2447
2448      // HELPERS
2449
2450      function daysInYear(year) {
2451          return isLeapYear(year) ? 366 : 365;
2452      }
2453
2454      function isLeapYear(year) {
2455          return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0;
2456      }
2457
2458      // HOOKS
2459
2460      utils_hooks__hooks.parseTwoDigitYear = function (input) {
2461          return toInt(input) + (toInt(input) > 68 ? 1900 : 2000);
2462      };
2463
2464      // MOMENTS
2465
2466      var getSetYear = makeGetSet('FullYear', false);
2467
2468      function getIsLeapYear () {
2469          return isLeapYear(this.year());
2470      }
2471
2472      addFormatToken('w', ['ww', 2], 'wo', 'week');
2473      addFormatToken('W', ['WW', 2], 'Wo', 'isoWeek');
2474
2475      // ALIASES
2476
2477      addUnitAlias('week', 'w');
2478      addUnitAlias('isoWeek', 'W');
2479
2480      // PARSING
2481
2482      addRegexToken('w',  match1to2);
2483      addRegexToken('ww', match1to2, match2);
2484      addRegexToken('W',  match1to2);
2485      addRegexToken('WW', match1to2, match2);
2486
2487      addWeekParseToken(['w', 'ww', 'W', 'WW'], function (input, week, config, token) {
2488          week[token.substr(0, 1)] = toInt(input);
2489      });
2490
2491      // HELPERS
2492
2493      // firstDayOfWeek       0 = sun, 6 = sat
2494      //                      the day of the week that starts the week
2495      //                      (usually sunday or monday)
2496      // firstDayOfWeekOfYear 0 = sun, 6 = sat
2497      //                      the first week is the week that contains the first
2498      //                      of this day of the week
2499      //                      (eg. ISO weeks use thursday (4))
2500      function weekOfYear(mom, firstDayOfWeek, firstDayOfWeekOfYear) {
2501          var end = firstDayOfWeekOfYear - firstDayOfWeek,
2502              daysToDayOfWeek = firstDayOfWeekOfYear - mom.day(),
2503              adjustedMoment;
2504
2505
2506          if (daysToDayOfWeek > end) {
2507              daysToDayOfWeek -= 7;
2508          }
2509
2510          if (daysToDayOfWeek < end - 7) {
2511              daysToDayOfWeek += 7;
2512          }
2513
2514          adjustedMoment = local__createLocal(mom).add(daysToDayOfWeek, 'd');
2515          return {
2516              week: Math.ceil(adjustedMoment.dayOfYear() / 7),
2517              year: adjustedMoment.year()
2518          };
2519      }
2520
2521      // LOCALES
2522
2523      function localeWeek (mom) {
2524          return weekOfYear(mom, this._week.dow, this._week.doy).week;
2525      }
2526
2527      var defaultLocaleWeek = {
2528          dow : 0, // Sunday is the first day of the week.
2529          doy : 6  // The week that contains Jan 1st is the first week of the year.
2530      };
2531
2532      function localeFirstDayOfWeek () {
2533          return this._week.dow;
2534      }
2535
2536      function localeFirstDayOfYear () {
2537          return this._week.doy;
2538      }
2539
2540      // MOMENTS
2541
2542      function getSetWeek (input) {
2543          var week = this.localeData().week(this);
2544          return input == null ? week : this.add((input - week) * 7, 'd');
2545      }
2546
2547      function getSetISOWeek (input) {
2548          var week = weekOfYear(this, 1, 4).week;
2549          return input == null ? week : this.add((input - week) * 7, 'd');
2550      }
2551
2552      addFormatToken('DDD', ['DDDD', 3], 'DDDo', 'dayOfYear');
2553
2554      // ALIASES
2555
2556      addUnitAlias('dayOfYear', 'DDD');
2557
2558      // PARSING
2559
2560      addRegexToken('DDD',  match1to3);
2561      addRegexToken('DDDD', match3);
2562      addParseToken(['DDD', 'DDDD'], function (input, array, config) {
2563          config._dayOfYear = toInt(input);
2564      });
2565
2566      // HELPERS
2567
2568      //http://en.wikipedia.org/wiki/ISO_week_date#Calculating_a_date_given_the_year.2C_week_number_and_weekday
2569      function dayOfYearFromWeeks(year, week, weekday, firstDayOfWeekOfYear, firstDayOfWeek) {
2570          var week1Jan = 6 + firstDayOfWeek - firstDayOfWeekOfYear, janX = createUTCDate(year, 0, 1 + week1Jan), d = janX.getUTCDay(), dayOfYear;
2571          if (d < firstDayOfWeek) {
2572              d += 7;
2573          }
2574
2575          weekday = weekday != null ? 1 * weekday : firstDayOfWeek;
2576
2577          dayOfYear = 1 + week1Jan + 7 * (week - 1) - d + weekday;
2578
2579          return {
2580              year: dayOfYear > 0 ? year : year - 1,
2581              dayOfYear: dayOfYear > 0 ?  dayOfYear : daysInYear(year - 1) + dayOfYear
2582          };
2583      }
2584
2585      // MOMENTS
2586
2587      function getSetDayOfYear (input) {
2588          var dayOfYear = Math.round((this.clone().startOf('day') - this.clone().startOf('year')) / 864e5) + 1;
2589          return input == null ? dayOfYear : this.add((input - dayOfYear), 'd');
2590      }
2591
2592      // Pick the first defined of two or three arguments.
2593      function defaults(a, b, c) {
2594          if (a != null) {
2595              return a;
2596          }
2597          if (b != null) {
2598              return b;
2599          }
2600          return c;
2601      }
2602
2603      function currentDateArray(config) {
2604          var now = new Date();
2605          if (config._useUTC) {
2606              return [now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate()];
2607          }
2608          return [now.getFullYear(), now.getMonth(), now.getDate()];
2609      }
2610
2611      // convert an array to a date.
2612      // the array should mirror the parameters below
2613      // note: all values past the year are optional and will default to the lowest possible value.
2614      // [year, month, day , hour, minute, second, millisecond]
2615      function configFromArray (config) {
2616          var i, date, input = [], currentDate, yearToUse;
2617
2618          if (config._d) {
2619              return;
2620          }
2621
2622          currentDate = currentDateArray(config);
2623
2624          //compute day of the year from weeks and weekdays
2625          if (config._w && config._a[DATE] == null && config._a[MONTH] == null) {
2626              dayOfYearFromWeekInfo(config);
2627          }
2628
2629          //if the day of the year is set, figure out what it is
2630          if (config._dayOfYear) {
2631              yearToUse = defaults(config._a[YEAR], currentDate[YEAR]);
2632
2633              if (config._dayOfYear > daysInYear(yearToUse)) {
2634                  getParsingFlags(config)._overflowDayOfYear = true;
2635              }
2636
2637              date = createUTCDate(yearToUse, 0, config._dayOfYear);
2638              config._a[MONTH] = date.getUTCMonth();
2639              config._a[DATE] = date.getUTCDate();
2640          }
2641
2642          // Default to current date.
2643          // * if no year, month, day of month are given, default to today
2644          // * if day of month is given, default month and year
2645          // * if month is given, default only year
2646          // * if year is given, don't default anything
2647          for (i = 0; i < 3 && config._a[i] == null; ++i) {
vendor: 16,336 bytes, lines 2648-3116
2648              config._a[i] = input[i] = currentDate[i];
2649          }
2650
2651          // Zero out whatever was not defaulted, including time
2652          for (; i < 7; i++) {
2653              config._a[i] = input[i] = (config._a[i] == null) ? (i === 2 ? 1 : 0) : config._a[i];
2654          }
2655
2656          // Check for 24:00:00.000
2657          if (config._a[HOUR] === 24 &&
2658                  config._a[MINUTE] === 0 &&
2659                  config._a[SECOND] === 0 &&
2660                  config._a[MILLISECOND] === 0) {
2661              config._nextDay = true;
2662              config._a[HOUR] = 0;
2663          }
2664
2665          config._d = (config._useUTC ? createUTCDate : createDate).apply(null, input);
2666          // Apply timezone offset from input. The actual utcOffset can be changed
2667          // with parseZone.
2668          if (config._tzm != null) {
2669              config._d.setUTCMinutes(config._d.getUTCMinutes() - config._tzm);
2670          }
2671
2672          if (config._nextDay) {
2673              config._a[HOUR] = 24;
2674          }
2675      }
2676
2677      function dayOfYearFromWeekInfo(config) {
2678          var w, weekYear, week, weekday, dow, doy, temp;
2679
2680          w = config._w;
2681          if (w.GG != null || w.W != null || w.E != null) {
2682              dow = 1;
2683              doy = 4;
2684
2685              // TODO: We need to take the current isoWeekYear, but that depends on
2686              // how we interpret now (local, utc, fixed offset). So create
2687              // a now version of current config (take local/utc/offset flags, and
2688              // create now).
2689              weekYear = defaults(w.GG, config._a[YEAR], weekOfYear(local__createLocal(), 1, 4).year);
2690              week = defaults(w.W, 1);
2691              weekday = defaults(w.E, 1);
2692          } else {
2693              dow = config._locale._week.dow;
2694              doy = config._locale._week.doy;
2695
2696              weekYear = defaults(w.gg, config._a[YEAR], weekOfYear(local__createLocal(), dow, doy).year);
2697              week = defaults(w.w, 1);
2698
2699              if (w.d != null) {
2700                  // weekday -- low day numbers are considered next week
2701                  weekday = w.d;
2702                  if (weekday < dow) {
2703                      ++week;
2704                  }
2705              } else if (w.e != null) {
2706                  // local weekday -- counting starts from begining of week
2707                  weekday = w.e + dow;
2708              } else {
2709                  // default to begining of week
2710                  weekday = dow;
2711              }
2712          }
2713          temp = dayOfYearFromWeeks(weekYear, week, weekday, doy, dow);
2714
2715          config._a[YEAR] = temp.year;
2716          config._dayOfYear = temp.dayOfYear;
2717      }
2718
2719      utils_hooks__hooks.ISO_8601 = function () {};
2720
2721      // date from string and format string
2722      function configFromStringAndFormat(config) {
2723          // TODO: Move this to another part of the creation flow to prevent circular deps
2724          if (config._f === utils_hooks__hooks.ISO_8601) {
2725              configFromISO(config);
2726              return;
2727          }
2728
2729          config._a = [];
2730          getParsingFlags(config).empty = true;
2731
2732          // This array is used to make a Date, either with `new Date` or `Date.UTC`
2733          var string = '' + config._i,
2734              i, parsedInput, tokens, token, skipped,
2735              stringLength = string.length,
2736              totalParsedInputLength = 0;
2737
2738          tokens = expandFormat(config._f, config._locale).match(formattingTokens) || [];
2739
2740          for (i = 0; i < tokens.length; i++) {
2741              token = tokens[i];
2742              parsedInput = (string.match(getParseRegexForToken(token, config)) || [])[0];
2743              if (parsedInput) {
2744                  skipped = string.substr(0, string.indexOf(parsedInput));
2745                  if (skipped.length > 0) {
2746                      getParsingFlags(config).unusedInput.push(skipped);
2747                  }
2748                  string = string.slice(string.indexOf(parsedInput) + parsedInput.length);
2749                  totalParsedInputLength += parsedInput.length;
2750              }
2751              // don't parse if it's not a known token
2752              if (formatTokenFunctions[token]) {
2753                  if (parsedInput) {
2754                      getParsingFlags(config).empty = false;
2755                  }
2756                  else {
2757                      getParsingFlags(config).unusedTokens.push(token);
2758                  }
2759                  addTimeToArrayFromToken(token, parsedInput, config);
2760              }
2761              else if (config._strict && !parsedInput) {
2762                  getParsingFlags(config).unusedTokens.push(token);
2763              }
2764          }
2765
2766          // add remaining unparsed input length to the string
2767          getParsingFlags(config).charsLeftOver = stringLength - totalParsedInputLength;
2768          if (string.length > 0) {
2769              getParsingFlags(config).unusedInput.push(string);
2770          }
2771
2772          // clear _12h flag if hour is <= 12
2773          if (getParsingFlags(config).bigHour === true &&
2774                  config._a[HOUR] <= 12 &&
2775                  config._a[HOUR] > 0) {
2776              getParsingFlags(config).bigHour = undefined;
2777          }
2778          // handle meridiem
2779          config._a[HOUR] = meridiemFixWrap(config._locale, config._a[HOUR], config._meridiem);
2780
2781          configFromArray(config);
2782          checkOverflow(config);
2783      }
2784
2785
2786      function meridiemFixWrap (locale, hour, meridiem) {
2787          var isPm;
2788
2789          if (meridiem == null) {
2790              // nothing to do
2791              return hour;
2792          }
2793          if (locale.meridiemHour != null) {
2794              return locale.meridiemHour(hour, meridiem);
2795          } else if (locale.isPM != null) {
2796              // Fallback
2797              isPm = locale.isPM(meridiem);
2798              if (isPm && hour < 12) {
2799                  hour += 12;
2800              }
2801              if (!isPm && hour === 12) {
2802                  hour = 0;
2803              }
2804              return hour;
2805          } else {
2806              // this is not supposed to happen
2807              return hour;
2808          }
2809      }
2810
2811      function configFromStringAndArray(config) {
2812          var tempConfig,
2813              bestMoment,
2814
2815              scoreToBeat,
2816              i,
2817              currentScore;
2818
2819          if (config._f.length === 0) {
2820              getParsingFlags(config).invalidFormat = true;
2821              config._d = new Date(NaN);
2822              return;
2823          }
2824
2825          for (i = 0; i < config._f.length; i++) {
2826              currentScore = 0;
2827              tempConfig = copyConfig({}, config);
2828              if (config._useUTC != null) {
2829                  tempConfig._useUTC = config._useUTC;
2830              }
2831              tempConfig._f = config._f[i];
2832              configFromStringAndFormat(tempConfig);
2833
2834              if (!valid__isValid(tempConfig)) {
2835                  continue;
2836              }
2837
2838              // if there is any input that was not parsed add a penalty for that format
2839              currentScore += getParsingFlags(tempConfig).charsLeftOver;
2840
2841              //or tokens
2842              currentScore += getParsingFlags(tempConfig).unusedTokens.length * 10;
2843
2844              getParsingFlags(tempConfig).score = currentScore;
2845
2846              if (scoreToBeat == null || currentScore < scoreToBeat) {
2847                  scoreToBeat = currentScore;
2848                  bestMoment = tempConfig;
2849              }
2850          }
2851
2852          extend(config, bestMoment || tempConfig);
2853      }
2854
2855      function configFromObject(config) {
2856          if (config._d) {
2857              return;
2858          }
2859
2860          var i = normalizeObjectUnits(config._i);
2861          config._a = [i.year, i.month, i.day || i.date, i.hour, i.minute, i.second, i.millisecond];
2862
2863          configFromArray(config);
2864      }
2865
2866      function createFromConfig (config) {
2867          var res = new Moment(checkOverflow(prepareConfig(config)));
2868          if (res._nextDay) {
2869              // Adding is smart enough around DST
2870              res.add(1, 'd');
2871              res._nextDay = undefined;
2872          }
2873
2874          return res;
2875      }
2876
2877      function prepareConfig (config) {
2878          var input = config._i,
2879              format = config._f;
2880
2881          config._locale = config._locale || locale_locales__getLocale(config._l);
2882
2883          if (input === null || (format === undefined && input === '')) {
2884              return valid__createInvalid({nullInput: true});
2885          }
2886
2887          if (typeof input === 'string') {
2888              config._i = input = config._locale.preparse(input);
2889          }
2890
2891          if (isMoment(input)) {
2892              return new Moment(checkOverflow(input));
2893          } else if (isArray(format)) {
2894              configFromStringAndArray(config);
2895          } else if (format) {
2896              configFromStringAndFormat(config);
2897          } else if (isDate(input)) {
2898              config._d = input;
2899          } else {
2900              configFromInput(config);
2901          }
2902
2903          return config;
2904      }
2905
2906      function configFromInput(config) {
2907          var input = config._i;
2908          if (input === undefined) {
2909              config._d = new Date();
2910          } else if (isDate(input)) {
2911              config._d = new Date(+input);
2912          } else if (typeof input === 'string') {
2913              configFromString(config);
2914          } else if (isArray(input)) {
2915              config._a = map(input.slice(0), function (obj) {
2916                  return parseInt(obj, 10);
2917              });
2918              configFromArray(config);
2919          } else if (typeof(input) === 'object') {
2920              configFromObject(config);
2921          } else if (typeof(input) === 'number') {
2922              // from milliseconds
2923              config._d = new Date(input);
2924          } else {
2925              utils_hooks__hooks.createFromInputFallback(config);
2926          }
2927      }
2928
2929      function createLocalOrUTC (input, format, locale, strict, isUTC) {
2930          var c = {};
2931
2932          if (typeof(locale) === 'boolean') {
2933              strict = locale;
2934              locale = undefined;
2935          }
2936          // object construction must be done this way.
2937          // https://github.com/moment/moment/issues/1423
2938          c._isAMomentObject = true;
2939          c._useUTC = c._isUTC = isUTC;
2940          c._l = locale;
2941          c._i = input;
2942          c._f = format;
2943          c._strict = strict;
2944
2945          return createFromConfig(c);
2946      }
2947
2948      function local__createLocal (input, format, locale, strict) {
2949          return createLocalOrUTC(input, format, locale, strict, false);
2950      }
2951
2952      var prototypeMin = deprecate(
2953           'moment().min is deprecated, use moment.min instead. https://github.com/moment/moment/issues/1548',
2954           function () {
2955               var other = local__createLocal.apply(null, arguments);
2956               return other < this ? this : other;
2957           }
2958       );
2959
2960      var prototypeMax = deprecate(
2961          'moment().max is deprecated, use moment.max instead. https://github.com/moment/moment/issues/1548',
2962          function () {
2963              var other = local__createLocal.apply(null, arguments);
2964              return other > this ? this : other;
2965          }
2966      );
2967
2968      // Pick a moment m from moments so that m[fn](other) is true for all
2969      // other. This relies on the function fn to be transitive.
2970      //
2971      // moments should either be an array of moment objects or an array, whose
2972      // first element is an array of moment objects.
2973      function pickBy(fn, moments) {
2974          var res, i;
2975          if (moments.length === 1 && isArray(moments[0])) {
2976              moments = moments[0];
2977          }
2978          if (!moments.length) {
2979              return local__createLocal();
2980          }
2981          res = moments[0];
2982          for (i = 1; i < moments.length; ++i) {
2983              if (!moments[i].isValid() || moments[i][fn](res)) {
2984                  res = moments[i];
2985              }
2986          }
2987          return res;
2988      }
2989
2990      // TODO: Use [].sort instead?
2991      function min () {
2992          var args = [].slice.call(arguments, 0);
2993
2994          return pickBy('isBefore', args);
2995      }
2996
2997      function max () {
2998          var args = [].slice.call(arguments, 0);
2999
3000          return pickBy('isAfter', args);
3001      }
3002
3003      function Duration (duration) {
3004          var normalizedInput = normalizeObjectUnits(duration),
3005              years = normalizedInput.year || 0,
3006              quarters = normalizedInput.quarter || 0,
3007              months = normalizedInput.month || 0,
3008              weeks = normalizedInput.week || 0,
3009              days = normalizedInput.day || 0,
3010              hours = normalizedInput.hour || 0,
3011              minutes = normalizedInput.minute || 0,
3012              seconds = normalizedInput.second || 0,
3013              milliseconds = normalizedInput.millisecond || 0;
3014
3015          // representation for dateAddRemove
3016          this._milliseconds = +milliseconds +
3017              seconds * 1e3 + // 1000
3018              minutes * 6e4 + // 1000 * 60
3019              hours * 36e5; // 1000 * 60 * 60
3020          // Because of dateAddRemove treats 24 hours as different from a
3021          // day when working around DST, we need to store them separately
3022          this._days = +days +
3023              weeks * 7;
3024          // It is impossible translate months into days without knowing
3025          // which months you are are talking about, so we have to store
3026          // it separately.
3027          this._months = +months +
3028              quarters * 3 +
3029              years * 12;
3030
3031          this._data = {};
3032
3033          this._locale = locale_locales__getLocale();
3034
3035          this._bubble();
3036      }
3037
3038      function isDuration (obj) {
3039          return obj instanceof Duration;
3040      }
3041
3042      function offset (token, separator) {
3043          addFormatToken(token, 0, 0, function () {
3044              var offset = this.utcOffset();
3045              var sign = '+';
3046              if (offset < 0) {
3047                  offset = -offset;
3048                  sign = '-';
3049              }
3050              return sign + zeroFill(~~(offset / 60), 2) + separator + zeroFill(~~(offset) % 60, 2);
3051          });
3052      }
3053
3054      offset('Z', ':');
3055      offset('ZZ', '');
3056
3057      // PARSING
3058
3059      addRegexToken('Z',  matchOffset);
3060      addRegexToken('ZZ', matchOffset);
3061      addParseToken(['Z', 'ZZ'], function (input, array, config) {
3062          config._useUTC = true;
3063          config._tzm = offsetFromString(input);
3064      });
3065
3066      // HELPERS
3067
3068      // timezone chunker
3069      // '+10:00' > ['10',  '00']
3070      // '-1530'  > ['-15', '30']
3071      var chunkOffset = /([\+\-]|\d\d)/gi;
3072
3073      function offsetFromString(string) {
3074          var matches = ((string || '').match(matchOffset) || []);
3075          var chunk   = matches[matches.length - 1] || [];
3076          var parts   = (chunk + '').match(chunkOffset) || ['-', 0, 0];
3077          var minutes = +(parts[1] * 60) + toInt(parts[2]);
3078
3079          return parts[0] === '+' ? minutes : -minutes;
3080      }
3081
3082      // Return a moment from input, that is local/utc/zone equivalent to model.
3083      function cloneWithOffset(input, model) {
3084          var res, diff;
3085          if (model._isUTC) {
3086              res = model.clone();
3087              diff = (isMoment(input) || isDate(input) ? +input : +local__createLocal(input)) - (+res);
3088              // Use low-level api, because this fn is low-level api.
3089              res._d.setTime(+res._d + diff);
3090              utils_hooks__hooks.updateOffset(res, false);
3091              return res;
3092          } else {
3093              return local__createLocal(input).local();
3094          }
3095      }
3096
3097      function getDateOffset (m) {
3098          // On Firefox.24 Date#getTimezoneOffset returns a floating point.
3099          // https://github.com/moment/moment/pull/1871
3100          return -Math.round(m._d.getTimezoneOffset() / 15) * 15;
3101      }
3102
3103      // HOOKS
3104
3105      // This function will be called whenever a moment is mutated.
3106      // It is intended to keep the offset in sync with the timezone.
3107      utils_hooks__hooks.updateOffset = function () {};
3108
3109      // MOMENTS
3110
3111      // keepLocalTime = true means only change the timezone, without
3112      // affecting the local hour. So 5:31:26 +0300 --[utcOffset(2, true)]-->
3113      // 5:31:26 +0200 It is possible that 5:31:26 doesn't exist with offset
3114      // +0200, so we adjust the time as needed, to be valid.
3115      //
3116      // Keeping the time actually adds/subtracts (one hour)
3117      // from the actual represented time. That is why we call updateOffset
3118      // a second time. In case it wants us to change the offset again
3119      // _changeInProgress == true case, then we have to adjust, because
3120      // there is no such time in the given timezone.
3121      function getSetOffset (input, keepLocalTime) {
3122          var offset = this._offset || 0,
3123              localAdjust;
3124          if (input != null) {
3125              if (typeof input === 'string') {
3126                  input = offsetFromString(input);
3127              }
3128              if (Math.abs(input) < 16) {
3129                  input = input * 60;
3130              }
3131              if (!this._isUTC && keepLocalTime) {
3132                  localAdjust = getDateOffset(this);
3133              }
3134              this._offset = input;
3135              this._isUTC = true;
3136              if (localAdjust != null) {
3137                  this.add(localAdjust, 'm');
3138              }
3139              if (offset !== input) {
3140                  if (!keepLocalTime || this._changeInProgress) {
3141                      add_subtract__addSubtract(this, create__createDuration(input - offset, 'm'), 1, false);
3142                  } else if (!this._changeInProgress) {
3143                      this._changeInProgress = true;
3144                      utils_hooks__hooks.updateOffset(this, true);
3145                      this._changeInProgress = null;
3146                  }
3147              }
3148              return this;
3149          } else {
3150              return this._isUTC ? offset : getDateOffset(this);
3151          }
3152      }
3153
3154      function getSetZone (input, keepLocalTime) {
3155          if (input != null) {
3156              if (typeof input !== 'string') {
3157                  input = -input;
3158              }
3159
3160              this.utcOffset(input, keepLocalTime);
3161
3162              return this;
3163          } else {
3164              return -this.utcOffset();
3165          }
3166      }
3167
3168      function setOffsetToUTC (keepLocalTime) {
3169          return this.utcOffset(0, keepLocalTime);
3170      }
3171
3172      function setOffsetToLocal (keepLocalTime) {
3173          if (this._isUTC) {
3174              this.utcOffset(0, keepLocalTime);
3175              this._isUTC = false;
3176
3177              if (keepLocalTime) {
3178                  this.subtract(getDateOffset(this), 'm');
3179              }
3180          }
3181          return this;
3182      }
3183
3184      function setOffsetToParsedOffset () {
3185          if (this._tzm) {
3186              this.utcOffset(this._tzm);
3187          } else if (typeof this._i === 'string') {
3188              this.utcOffset(offsetFromString(this._i));
3189          }
3190          return this;
3191      }
3192
3193      function hasAlignedHourOffset (input) {
3194          input = input ? local__createLocal(input).utcOffset() : 0;
3195
3196          return (this.utcOffset() - input) % 60 === 0;
3197      }
3198
3199      function isDaylightSavingTime () {
3200          return (
3201              this.utcOffset() > this.clone().month(0).utcOffset() ||
3202              this.utcOffset() > this.clone().month(5).utcOffset()
3203          );
3204      }
3205
3206      function isDaylightSavingTimeShifted () {
3207          if (typeof this._isDSTShifted !== 'undefined') {
3208              return this._isDSTShifted;
3209          }
3210
3211          var c = {};
3212
3213          copyConfig(c, this);
3214          c = prepareConfig(c);
3215
3216          if (c._a) {
3217              var other = c._isUTC ? create_utc__createUTC(c._a) : local__createLocal(c._a);
3218              this._isDSTShifted = this.isValid() &&
3219                  compareArrays(c._a, other.toArray()) > 0;
3220          } else {
3221              this._isDSTShifted = false;
3222          }
3223
3224          return this._isDSTShifted;
3225      }
3226
3227      function isLocal () {
3228          return !this._isUTC;
3229      }
3230
3231      function isUtcOffset () {
3232          return this._isUTC;
3233      }
3234
3235      function isUtc () {
3236          return this._isUTC && this._offset === 0;
3237      }
3238
3239      var aspNetRegex = /(\-)?(?:(\d*)\.)?(\d+)\:(\d+)(?:\:(\d+)\.?(\d{3})?)?/;
3240
3241      // from http://docs.closure-library.googlecode.com/git/closure_goog_date_date.js.source.html
3242      // somewhat more in line with 4.4.3.2 2004 spec, but allows decimal anywhere
3243      var create__isoRegex = /^(-)?P(?:(?:([0-9,.]*)Y)?(?:([0-9,.]*)M)?(?:([0-9,.]*)D)?(?:T(?:([0-9,.]*)H)?(?:([0-9,.]*)M)?(?:([0-9,.]*)S)?)?|([0-9,.]*)W)$/;
3244
3245      function create__createDuration (input, key) {
3246          var duration = input,
3247              // matching against regexp is expensive, do it on demand
3248              match = null,
3249              sign,
3250              ret,
3251              diffRes;
3252
3253          if (isDuration(input)) {
3254              duration = {
3255                  ms : input._milliseconds,
3256                  d  : input._days,
3257                  M  : input._months
3258              };
3259          } else if (typeof input === 'number') {
3260              duration = {};
3261              if (key) {
3262                  duration[key] = input;
3263              } else {
3264                  duration.milliseconds = input;
3265              }
3266          } else if (!!(match = aspNetRegex.exec(input))) {
3267              sign = (match[1] === '-') ? -1 : 1;
3268              duration = {
3269                  y  : 0,
3270                  d  : toInt(match[DATE])        * sign,
3271                  h  : toInt(match[HOUR])        * sign,
3272                  m  : toInt(match[MINUTE])      * sign,
3273                  s  : toInt(match[SECOND])      * sign,
3274                  ms : toInt(match[MILLISECOND]) * sign
3275              };
3276          } else if (!!(match = create__isoRegex.exec(input))) {
3277              sign = (match[1] === '-') ? -1 : 1;
3278              duration = {
3279                  y : parseIso(match[2], sign),
3280                  M : parseIso(match[3], sign),
3281                  d : parseIso(match[4], sign),
3282                  h : parseIso(match[5], sign),
3283                  m : parseIso(match[6], sign),
3284                  s : parseIso(match[7], sign),
3285                  w : parseIso(match[8], sign)
3286              };
3287          } else if (duration == null) {// checks for null or undefined
3288              duration = {};
3289          } else if (typeof duration === 'object' && ('from' in duration || 'to' in duration)) {
3290              diffRes = momentsDifference(local__createLocal(duration.from), local__createLocal(duration.to));
3291
3292              duration = {};
3293              duration.ms = diffRes.milliseconds;
3294              duration.M = diffRes.months;
3295          }
3296
3297          ret = new Duration(duration);
3298
3299          if (isDuration(input) && hasOwnProp(input, '_locale')) {
3300              ret._locale = input._locale;
3301          }
3302
3303          return ret;
3304      }
3305
3306      create__createDuration.fn = Duration.prototype;
3307
3308      function parseIso (inp, sign) {
3309          // We'd normally use ~~inp for this, but unfortunately it also
3310          // converts floats to ints.
3311          // inp may be undefined, so careful calling replace on it.
3312          var res = inp && parseFloat(inp.replace(',', '.'));
3313          // apply sign while we're at it
3314          return (isNaN(res) ? 0 : res) * sign;
3315      }
3316
3317      function positiveMomentsDifference(base, other) {
3318          var res = {milliseconds: 0, months: 0};
3319
3320          res.months = other.month() - base.month() +
3321              (other.year() - base.year()) * 12;
3322          if (base.clone().add(res.months, 'M').isAfter(other)) {
3323              --res.months;
3324          }
3325
3326          res.milliseconds = +other - +(base.clone().add(res.months, 'M'));
3327
3328          return res;
3329      }
3330
3331      function momentsDifference(base, other) {
3332          var res;
3333          other = cloneWithOffset(other, base);
3334          if (base.isBefore(other)) {
3335              res = positiveMomentsDifference(base, other);
3336          } else {
3337              res = positiveMomentsDifference(other, base);
3338              res.milliseconds = -res.milliseconds;
3339              res.months = -res.months;
3340          }
3341
3342          return res;
3343      }
3344
3345      function createAdder(direction, name) {
3346          return function (val, period) {
3347              var dur, tmp;
3348              //invert the arguments, but complain about it
3349              if (period !== null && !isNaN(+period)) {
3350                  deprecateSimple(name, 'moment().' + name  + '(period, number) is deprecated. Please use moment().' + name + '(number, period).');
3351                  tmp = val; val = period; period = tmp;
3352              }
3353
3354              val = typeof val === 'string' ? +val : val;
3355              dur = create__createDuration(val, period);
3356              add_subtract__addSubtract(this, dur, direction);
3357              return this;
3358          };
3359      }
3360
3361      function add_subtract__addSubtract (mom, duration, isAdding, updateOffset) {
3362          var milliseconds = duration._milliseconds,
3363              days = duration._days,
3364              months = duration._months;
3365          updateOffset = updateOffset == null ? true : updateOffset;
3366
3367          if (milliseconds) {
3368              mom._d.setTime(+mom._d + milliseconds * isAdding);
3369          }
3370          if (days) {
3371              get_set__set(mom, 'Date', get_set__get(mom, 'Date') + days * isAdding);
3372          }
3373          if (months) {
3374              setMonth(mom, get_set__get(mom, 'Month') + months * isAdding);
3375          }
3376          if (updateOffset) {
3377              utils_hooks__hooks.updateOffset(mom, days || months);
3378          }
3379      }
3380
3381      var add_subtract__add      = createAdder(1, 'add');
3382      var add_subtract__subtract = createAdder(-1, 'subtract');
3383
3384      function moment_calendar__calendar (time, formats) {
3385          // We want to compare the start of today, vs this.
3386          // Getting start-of-today depends on whether we're local/utc/offset or not.
3387          var now = time || local__createLocal(),
3388              sod = cloneWithOffset(now, this).startOf('day'),
3389              diff = this.diff(sod, 'days', true),
3390              format = diff < -6 ? 'sameElse' :
3391                  diff < -1 ? 'lastWeek' :
3392                  diff < 0 ? 'lastDay' :
3393                  diff < 1 ? 'sameDay' :
3394                  diff < 2 ? 'nextDay' :
3395                  diff < 7 ? 'nextWeek' : 'sameElse';
3396          return this.format(formats && formats[format] || this.localeData().calendar(format, this, local__createLocal(now)));
3397      }
3398
3399      function clone () {
3400          return new Moment(this);
3401      }
3402
3403      function isAfter (input, units) {
3404          var inputMs;
3405          units = normalizeUnits(typeof units !== 'undefined' ? units : 'millisecond');
3406          if (units === 'millisecond') {
3407              input = isMoment(input) ? input : local__createLocal(input);
3408              return +this > +input;
3409          } else {
3410              inputMs = isMoment(input) ? +input : +local__createLocal(input);
3411              return inputMs < +this.clone().startOf(units);
3412          }
3413      }
3414
3415      function isBefore (input, units) {
3416          var inputMs;
3417          units = normalizeUnits(typeof units !== 'undefined' ? units : 'millisecond');
3418          if (units === 'millisecond') {
3419              input = isMoment(input) ? input : local__createLocal(input);
3420              return +this < +input;
3421          } else {
3422              inputMs = isMoment(input) ? +input : +local__createLocal(input);
3423              return +this.clone().endOf(units) < inputMs;
3424          }
3425      }
3426
3427      function isBetween (from, to, units) {
3428          return this.isAfter(from, units) && this.isBefore(to, units);
3429      }
3430
3431      function isSame (input, units) {
3432          var inputMs;
3433          units = normalizeUnits(units || 'millisecond');
3434          if (units === 'millisecond') {
3435              input = isMoment(input) ? input : local__createLocal(input);
3436              return +this === +input;
3437          } else {
3438              inputMs = +local__createLocal(input);
3439              return +(this.clone().startOf(units)) <= inputMs && inputMs <= +(this.clone().endOf(units));
3440          }
3441      }
3442
3443      function diff (input, units, asFloat) {
3444          var that = cloneWithOffset(input, this),
3445              zoneDelta = (that.utcOffset() - this.utcOffset()) * 6e4,
3446              delta, output;
3447
3448          units = normalizeUnits(units);
3449
3450          if (units === 'year' || units === 'month' || units === 'quarter') {
3451              output = monthDiff(this, that);
3452              if (units === 'quarter') {
3453                  output = output / 3;
3454              } else if (units === 'year') {
3455                  output = output / 12;
3456              }
3457          } else {
3458              delta = this - that;
3459              output = units === 'second' ? delta / 1e3 : // 1000
3460                  units === 'minute' ? delta / 6e4 : // 1000 * 60
3461                  units === 'hour' ? delta / 36e5 : // 1000 * 60 * 60
3462                  units === 'day' ? (delta - zoneDelta) / 864e5 : // 1000 * 60 * 60 * 24, negate dst
3463                  units === 'week' ? (delta - zoneDelta) / 6048e5 : // 1000 * 60 * 60 * 24 * 7, negate dst
3464                  delta;
3465          }
3466          return asFloat ? output : absFloor(output);
3467      }
3468
3469      function monthDiff (a, b) {
3470          // difference in months
3471          var wholeMonthDiff = ((b.year() - a.year()) * 12) + (b.month() - a.month()),
3472              // b is in (anchor - 1 month, anchor + 1 month)
3473              anchor = a.clone().add(wholeMonthDiff, 'months'),
3474              anchor2, adjust;
3475
3476          if (b - anchor < 0) {
3477              anchor2 = a.clone().add(wholeMonthDiff - 1, 'months');
3478              // linear across the month
3479              adjust = (b - anchor) / (anchor - anchor2);
3480          } else {
3481              anchor2 = a.clone().add(wholeMonthDiff + 1, 'months');
3482              // linear across the month
3483              adjust = (b - anchor) / (anchor2 - anchor);
3484          }
3485
3486          return -(wholeMonthDiff + adjust);
3487      }
3488
3489      utils_hooks__hooks.defaultFormat = 'YYYY-MM-DDTHH:mm:ssZ';
3490
3491      function toString () {
3492          return this.clone().locale('en').format('ddd MMM DD YYYY HH:mm:ss [GMT]ZZ');
3493      }
3494
3495      function moment_format__toISOString () {
3496          var m = this.clone().utc();
3497          if (0 < m.year() && m.year() <= 9999) {
3498              if ('function' === typeof Date.prototype.toISOString) {
3499                  // native implementation is ~50x faster, use it when we can
3500                  return this.toDate().toISOString();
3501              } else {
3502                  return formatMoment(m, 'YYYY-MM-DD[T]HH:mm:ss.SSS[Z]');
3503              }
3504          } else {
3505              return formatMoment(m, 'YYYYYY-MM-DD[T]HH:mm:ss.SSS[Z]');
3506          }
3507      }
3508
3509      function format (inputString) {
3510          var output = formatMoment(this, inputString || utils_hooks__hooks.defaultFormat);
3511          return this.localeData().postformat(output);
3512      }
3513
3514      function from (time, withoutSuffix) {
3515          if (!this.isValid()) {
3516              return this.localeData().invalidDate();
3517          }
3518          return create__createDuration({to: this, from: time}).locale(this.locale()).humanize(!withoutSuffix);
3519      }
3520
3521      function fromNow (withoutSuffix) {
3522          return this.from(local__createLocal(), withoutSuffix);
3523      }
3524
3525      function to (time, withoutSuffix) {
3526          if (!this.isValid()) {
3527              return this.localeData().invalidDate();
3528          }
3529          return create__createDuration({from: this, to: time}).locale(this.locale()).humanize(!withoutSuffix);
3530      }
3531
3532      function toNow (withoutSuffix) {
3533          return this.to(local__createLocal(), withoutSuffix);
3534      }
3535
3536      function locale (key) {
3537          var newLocaleData;
3538
3539          if (key === undefined) {
3540              return this._locale._abbr;
3541          } else {
3542              newLocaleData = locale_locales__getLocale(key);
3543              if (newLocaleData != null) {
3544                  this._locale = newLocaleData;
3545              }
3546              return this;
3547          }
3548      }
3549
3550      var lang = deprecate(
3551          'moment().lang() is deprecated. Instead, use moment().localeData() to get the language configuration. Use moment().locale() to change languages.',
3552          function (key) {
3553              if (key === undefined) {
3554                  return this.localeData();
3555              } else {
3556                  return this.locale(key);
3557              }
3558          }
3559      );
3560
3561      function localeData () {
3562          return this._locale;
3563      }
3564
3565      function startOf (units) {
3566          units = normalizeUnits(units);
3567          // the following switch intentionally omits break keywords
3568          // to utilize falling through the cases.
3569          switch (units) {
3570          case 'year':
3571              this.month(0);
3572              /* falls through */
3573          case 'quarter':
3574          case 'month':
3575              this.date(1);
3576              /* falls through */
3577          case 'week':
3578          case 'isoWeek':
3579          case 'day':
3580              this.hours(0);
3581              /* falls through */
3582          case 'hour':
3583              this.minutes(0);
3584              /* falls through */
3585          case 'minute':
3586              this.seconds(0);
3587              /* falls through */
3588          case 'second':
3589              this.milliseconds(0);
3590          }
3591
3592          // weeks are a special case
3593          if (units === 'week') {
3594              this.weekday(0);
3595          }
3596          if (units === 'isoWeek') {
3597              this.isoWeekday(1);
3598          }
3599
3600          // quarters are also special
3601          if (units === 'quarter') {
3602              this.month(Math.floor(this.month() / 3) * 3);
3603          }
3604
3605          return this;
3606      }
3607
3608      function endOf (units) {
3609          units = normalizeUnits(units);
3610          if (units === undefined || units === 'millisecond') {
3611              return this;
3612          }
3613          return this.startOf(units).add(1, (units === 'isoWeek' ? 'week' : units)).subtract(1, 'ms');
3614      }
3615
3616      function to_type__valueOf () {
3617          return +this._d - ((this._offset || 0) * 60000);
3618      }
3619
3620      function unix () {
3621          return Math.floor(+this / 1000);
3622      }
3623
3624      function toDate () {
3625          return this._offset ? new Date(+this) : this._d;
3626      }
3627
3628      function toArray () {
3629          var m = this;
3630          return [m.year(), m.month(), m.date(), m.hour(), m.minute(), m.second(), m.millisecond()];
3631      }
3632
3633      function toObject () {
3634          var m = this;
3635          return {
3636              years: m.year(),
3637              months: m.month(),
3638              date: m.date(),
3639              hours: m.hours(),
3640              minutes: m.minutes(),
3641              seconds: m.seconds(),
3642              milliseconds: m.milliseconds()
3643          };
3644      }
3645
3646      function moment_valid__isValid () {
3647          return valid__isValid(this);
3648      }
3649
3650      function parsingFlags () {
3651          return extend({}, getParsingFlags(this));
3652      }
3653
vendor: 4,245 bytes, lines 3654-3799
3654      function invalidAt () {
3655          return getParsingFlags(this).overflow;
3656      }
3657
3658      addFormatToken(0, ['gg', 2], 0, function () {
3659          return this.weekYear() % 100;
3660      });
3661
3662      addFormatToken(0, ['GG', 2], 0, function () {
3663          return this.isoWeekYear() % 100;
3664      });
3665
3666      function addWeekYearFormatToken (token, getter) {
3667          addFormatToken(0, [token, token.length], 0, getter);
3668      }
3669
3670      addWeekYearFormatToken('gggg',     'weekYear');
3671      addWeekYearFormatToken('ggggg',    'weekYear');
3672      addWeekYearFormatToken('GGGG',  'isoWeekYear');
3673      addWeekYearFormatToken('GGGGG', 'isoWeekYear');
3674
3675      // ALIASES
3676
3677      addUnitAlias('weekYear', 'gg');
3678      addUnitAlias('isoWeekYear', 'GG');
3679
3680      // PARSING
3681
3682      addRegexToken('G',      matchSigned);
3683      addRegexToken('g',      matchSigned);
3684      addRegexToken('GG',     match1to2, match2);
3685      addRegexToken('gg',     match1to2, match2);
3686      addRegexToken('GGGG',   match1to4, match4);
3687      addRegexToken('gggg',   match1to4, match4);
3688      addRegexToken('GGGGG',  match1to6, match6);
3689      addRegexToken('ggggg',  match1to6, match6);
3690
3691      addWeekParseToken(['gggg', 'ggggg', 'GGGG', 'GGGGG'], function (input, week, config, token) {
3692          week[token.substr(0, 2)] = toInt(input);
3693      });
3694
3695      addWeekParseToken(['gg', 'GG'], function (input, week, config, token) {
3696          week[token] = utils_hooks__hooks.parseTwoDigitYear(input);
3697      });
3698
3699      // HELPERS
3700
3701      function weeksInYear(year, dow, doy) {
3702          return weekOfYear(local__createLocal([year, 11, 31 + dow - doy]), dow, doy).week;
3703      }
3704
3705      // MOMENTS
3706
3707      function getSetWeekYear (input) {
3708          var year = weekOfYear(this, this.localeData()._week.dow, this.localeData()._week.doy).year;
3709          return input == null ? year : this.add((input - year), 'y');
3710      }
3711
3712      function getSetISOWeekYear (input) {
3713          var year = weekOfYear(this, 1, 4).year;
3714          return input == null ? year : this.add((input - year), 'y');
3715      }
3716
3717      function getISOWeeksInYear () {
3718          return weeksInYear(this.year(), 1, 4);
3719      }
3720
3721      function getWeeksInYear () {
3722          var weekInfo = this.localeData()._week;
3723          return weeksInYear(this.year(), weekInfo.dow, weekInfo.doy);
3724      }
3725
3726      addFormatToken('Q', 0, 0, 'quarter');
3727
3728      // ALIASES
3729
3730      addUnitAlias('quarter', 'Q');
3731
3732      // PARSING
3733
3734      addRegexToken('Q', match1);
3735      addParseToken('Q', function (input, array) {
3736          array[MONTH] = (toInt(input) - 1) * 3;
3737      });
3738
3739      // MOMENTS
3740
3741      function getSetQuarter (input) {
3742          return input == null ? Math.ceil((this.month() + 1) / 3) : this.month((input - 1) * 3 + this.month() % 3);
3743      }
3744
3745      addFormatToken('D', ['DD', 2], 'Do', 'date');
3746
3747      // ALIASES
3748
3749      addUnitAlias('date', 'D');
3750
3751      // PARSING
3752
3753      addRegexToken('D',  match1to2);
3754      addRegexToken('DD', match1to2, match2);
3755      addRegexToken('Do', function (isStrict, locale) {
3756          return isStrict ? locale._ordinalParse : locale._ordinalParseLenient;
3757      });
3758
3759      addParseToken(['D', 'DD'], DATE);
3760      addParseToken('Do', function (input, array) {
3761          array[DATE] = toInt(input.match(match1to2)[0], 10);
3762      });
3763
3764      // MOMENTS
3765
3766      var getSetDayOfMonth = makeGetSet('Date', true);
3767
3768      addFormatToken('d', 0, 'do', 'day');
3769
3770      addFormatToken('dd', 0, 0, function (format) {
3771          return this.localeData().weekdaysMin(this, format);
3772      });
3773
3774      addFormatToken('ddd', 0, 0, function (format) {
3775          return this.localeData().weekdaysShort(this, format);
3776      });
3777
3778      addFormatToken('dddd', 0, 0, function (format) {
3779          return this.localeData().weekdays(this, format);
3780      });
3781
3782      addFormatToken('e', 0, 0, 'weekday');
3783      addFormatToken('E', 0, 0, 'isoWeekday');
3784
3785      // ALIASES
3786
3787      addUnitAlias('day', 'd');
3788      addUnitAlias('weekday', 'e');
3789      addUnitAlias('isoWeekday', 'E');
3790
3791      // PARSING
3792
3793      addRegexToken('d',    match1to2);
3794      addRegexToken('e',    match1to2);
3795      addRegexToken('E',    match1to2);
3796      addRegexToken('dd',   matchWord);
3797      addRegexToken('ddd',  matchWord);
3798      addRegexToken('dddd', matchWord);
3799
3800      addWeekParseToken(['dd', 'ddd', 'dddd'], function (input, week, config) {
3801          var weekday = config._locale.weekdaysParse(input);
3802          // if we didn't get a weekday name, mark the date as invalid
3803          if (weekday != null) {
3804              week.d = weekday;
3805          } else {
3806              getParsingFlags(config).invalidWeekday = input;
3807          }
3808      });
3809
3810      addWeekParseToken(['d', 'e', 'E'], function (input, week, config, token) {
3811          week[token] = toInt(input);
3812      });
3813
3814      // HELPERS
3815
3816      function parseWeekday(input, locale) {
3817          if (typeof input !== 'string') {
3818              return input;
3819          }
3820
3821          if (!isNaN(input)) {
3822              return parseInt(input, 10);
3823          }
3824
3825          input = locale.weekdaysParse(input);
3826          if (typeof input === 'number') {
3827              return input;
3828          }
3829
3830          return null;
3831      }
3832
3833      // LOCALES
3834
3835      var defaultLocaleWeekdays = 'Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday'.split('_');
3836      function localeWeekdays (m) {
3837          return this._weekdays[m.day()];
3838      }
3839
3840      var defaultLocaleWeekdaysShort = 'Sun_Mon_Tue_Wed_Thu_Fri_Sat'.split('_');
3841      function localeWeekdaysShort (m) {
3842          return this._weekdaysShort[m.day()];
3843      }
3844
3845      var defaultLocaleWeekdaysMin = 'Su_Mo_Tu_We_Th_Fr_Sa'.split('_');
3846      function localeWeekdaysMin (m) {
3847          return this._weekdaysMin[m.day()];
3848      }
3849
3850      function localeWeekdaysParse (weekdayName) {
3851          var i, mom, regex;
3852
3853          this._weekdaysParse = this._weekdaysParse || [];
3854
3855          for (i = 0; i < 7; i++) {
3856              // make the regex if we don't have it already
3857              if (!this._weekdaysParse[i]) {
3858                  mom = local__createLocal([2000, 1]).day(i);
3859                  regex = '^' + this.weekdays(mom, '') + '|^' + this.weekdaysShort(mom, '') + '|^' + this.weekdaysMin(mom, '');
3860                  this._weekdaysParse[i] = new RegExp(regex.replace('.', ''), 'i');
3861              }
3862              // test the regex
3863              if (this._weekdaysParse[i].test(weekdayName)) {
3864                  return i;
3865              }
3866          }
3867      }
3868
3869      // MOMENTS
3870
3871      function getSetDayOfWeek (input) {
3872          var day = this._isUTC ? this._d.getUTCDay() : this._d.getDay();
3873          if (input != null) {
3874              input = parseWeekday(input, this.localeData());
3875              return this.add(input - day, 'd');
3876          } else {
3877              return day;
3878          }
3879      }
3880
3881      function getSetLocaleDayOfWeek (input) {
3882          var weekday = (this.day() + 7 - this.localeData()._week.dow) % 7;
3883          return input == null ? weekday : this.add(input - weekday, 'd');
3884      }
3885
3886      function getSetISODayOfWeek (input) {
3887          // behaves the same as moment#day except
3888          // as a getter, returns 7 instead of 0 (1-7 range instead of 0-6)
3889          // as a setter, sunday should belong to the previous week.
3890          return input == null ? this.day() || 7 : this.day(this.day() % 7 ? input : input - 7);
3891      }
3892
3893      addFormatToken('H', ['HH', 2], 0, 'hour');
3894      addFormatToken('h', ['hh', 2], 0, function () {
3895          return this.hours() % 12 || 12;
3896      });
3897
3898      function meridiem (token, lowercase) {
3899          addFormatToken(token, 0, 0, function () {
3900              return this.localeData().meridiem(this.hours(), this.minutes(), lowercase);
3901          });
3902      }
3903
3904      meridiem('a', true);
3905      meridiem('A', false);
3906
3907      // ALIASES
3908
3909      addUnitAlias('hour', 'h');
3910
3911      // PARSING
3912
3913      function matchMeridiem (isStrict, locale) {
3914          return locale._meridiemParse;
3915      }
3916
3917      addRegexToken('a',  matchMeridiem);
3918      addRegexToken('A',  matchMeridiem);
3919      addRegexToken('H',  match1to2);
3920      addRegexToken('h',  match1to2);
3921      addRegexToken('HH', match1to2, match2);
3922      addRegexToken('hh', match1to2, match2);
3923
3924      addParseToken(['H', 'HH'], HOUR);
3925      addParseToken(['a', 'A'], function (input, array, config) {
3926          config._isPm = config._locale.isPM(input);
3927          config._meridiem = input;
3928      });
3929      addParseToken(['h', 'hh'], function (input, array, config) {
3930          array[HOUR] = toInt(input);
3931          getParsingFlags(config).bigHour = true;
3932      });
3933
3934      // LOCALES
3935
3936      function localeIsPM (input) {
3937          // IE8 Quirks Mode & IE7 Standards Mode do not allow accessing str
vendor: 9,770 bytes, lines 3937-4209
3937ings like arrays
3938          // Using charAt should be more compatible.
3939          return ((input + '').toLowerCase().charAt(0) === 'p');
3940      }
3941
3942      var defaultLocaleMeridiemParse = /[ap]\.?m?\.?/i;
3943      function localeMeridiem (hours, minutes, isLower) {
3944          if (hours > 11) {
3945              return isLower ? 'pm' : 'PM';
3946          } else {
3947              return isLower ? 'am' : 'AM';
3948          }
3949      }
3950
3951
3952      // MOMENTS
3953
3954      // Setting the hour should keep the time, because the user explicitly
3955      // specified which hour he wants. So trying to maintain the same hour (in
3956      // a new timezone) makes sense. Adding/subtracting hours does not follow
3957      // this rule.
3958      var getSetHour = makeGetSet('Hours', true);
3959
3960      addFormatToken('m', ['mm', 2], 0, 'minute');
3961
3962      // ALIASES
3963
3964      addUnitAlias('minute', 'm');
3965
3966      // PARSING
3967
3968      addRegexToken('m',  match1to2);
3969      addRegexToken('mm', match1to2, match2);
3970      addParseToken(['m', 'mm'], MINUTE);
3971
3972      // MOMENTS
3973
3974      var getSetMinute = makeGetSet('Minutes', false);
3975
3976      addFormatToken('s', ['ss', 2], 0, 'second');
3977
3978      // ALIASES
3979
3980      addUnitAlias('second', 's');
3981
3982      // PARSING
3983
3984      addRegexToken('s',  match1to2);
3985      addRegexToken('ss', match1to2, match2);
3986      addParseToken(['s', 'ss'], SECOND);
3987
3988      // MOMENTS
3989
3990      var getSetSecond = makeGetSet('Seconds', false);
3991
3992      addFormatToken('S', 0, 0, function () {
3993          return ~~(this.millisecond() / 100);
3994      });
3995
3996      addFormatToken(0, ['SS', 2], 0, function () {
3997          return ~~(this.millisecond() / 10);
3998      });
3999
4000      addFormatToken(0, ['SSS', 3], 0, 'millisecond');
4001      addFormatToken(0, ['SSSS', 4], 0, function () {
4002          return this.millisecond() * 10;
4003      });
4004      addFormatToken(0, ['SSSSS', 5], 0, function () {
4005          return this.millisecond() * 100;
4006      });
4007      addFormatToken(0, ['SSSSSS', 6], 0, function () {
4008          return this.millisecond() * 1000;
4009      });
4010      addFormatToken(0, ['SSSSSSS', 7], 0, function () {
4011          return this.millisecond() * 10000;
4012      });
4013      addFormatToken(0, ['SSSSSSSS', 8], 0, function () {
4014          return this.millisecond() * 100000;
4015      });
4016      addFormatToken(0, ['SSSSSSSSS', 9], 0, function () {
4017          return this.millisecond() * 1000000;
4018      });
4019
4020
4021      // ALIASES
4022
4023      addUnitAlias('millisecond', 'ms');
4024
4025      // PARSING
4026
4027      addRegexToken('S',    match1to3, match1);
4028      addRegexToken('SS',   match1to3, match2);
4029      addRegexToken('SSS',  match1to3, match3);
4030
4031      var token;
4032      for (token = 'SSSS'; token.length <= 9; token += 'S') {
4033          addRegexToken(token, matchUnsigned);
4034      }
4035
4036      function parseMs(input, array) {
4037          array[MILLISECOND] = toInt(('0.' + input) * 1000);
4038      }
4039
4040      for (token = 'S'; token.length <= 9; token += 'S') {
4041          addParseToken(token, parseMs);
4042      }
4043      // MOMENTS
4044
4045      var getSetMillisecond = makeGetSet('Milliseconds', false);
4046
4047      addFormatToken('z',  0, 0, 'zoneAbbr');
4048      addFormatToken('zz', 0, 0, 'zoneName');
4049
4050      // MOMENTS
4051
4052      function getZoneAbbr () {
4053          return this._isUTC ? 'UTC' : '';
4054      }
4055
4056      function getZoneName () {
4057          return this._isUTC ? 'Coordinated Universal Time' : '';
4058      }
4059
4060      var momentPrototype__proto = Moment.prototype;
4061
4062      momentPrototype__proto.add          = add_subtract__add;
4063      momentPrototype__proto.calendar     = moment_calendar__calendar;
4064      momentPrototype__proto.clone        = clone;
4065      momentPrototype__proto.diff         = diff;
4066      momentPrototype__proto.endOf        = endOf;
4067      momentPrototype__proto.format       = format;
4068      momentPrototype__proto.from         = from;
4069      momentPrototype__proto.fromNow      = fromNow;
4070      momentPrototype__proto.to           = to;
4071      momentPrototype__proto.toNow        = toNow;
4072      momentPrototype__proto.get          = getSet;
4073      momentPrototype__proto.invalidAt    = invalidAt;
4074      momentPrototype__proto.isAfter      = isAfter;
4075      momentPrototype__proto.isBefore     = isBefore;
4076      momentPrototype__proto.isBetween    = isBetween;
4077      momentPrototype__proto.isSame       = isSame;
4078      momentPrototype__proto.isValid      = moment_valid__isValid;
4079      momentPrototype__proto.lang         = lang;
4080      momentPrototype__proto.locale       = locale;
4081      momentPrototype__proto.localeData   = localeData;
4082      momentPrototype__proto.max          = prototypeMax;
4083      momentPrototype__proto.min          = prototypeMin;
4084      momentPrototype__proto.parsingFlags = parsingFlags;
4085      momentPrototype__proto.set          = getSet;
4086      momentPrototype__proto.startOf      = startOf;
4087      momentPrototype__proto.subtract     = add_subtract__subtract;
4088      momentPrototype__proto.toArray      = toArray;
4089      momentPrototype__proto.toObject     = toObject;
4090      momentPrototype__proto.toDate       = toDate;
4091      momentPrototype__proto.toISOString  = moment_format__toISOString;
4092      momentPrototype__proto.toJSON       = moment_format__toISOString;
4093      momentPrototype__proto.toString     = toString;
4094      momentPrototype__proto.unix         = unix;
4095      momentPrototype__proto.valueOf      = to_type__valueOf;
4096
4097      // Year
4098      momentPrototype__proto.year       = getSetYear;
4099      momentPrototype__proto.isLeapYear = getIsLeapYear;
4100
4101      // Week Year
4102      momentPrototype__proto.weekYear    = getSetWeekYear;
4103      momentPrototype__proto.isoWeekYear = getSetISOWeekYear;
4104
4105      // Quarter
4106      momentPrototype__proto.quarter = momentPrototype__proto.quarters = getSetQuarter;
4107
4108      // Month
4109      momentPrototype__proto.month       = getSetMonth;
4110      momentPrototype__proto.daysInMonth = getDaysInMonth;
4111
4112      // Week
4113      momentPrototype__proto.week           = momentPrototype__proto.weeks        = getSetWeek;
4114      momentPrototype__proto.isoWeek        = momentPrototype__proto.isoWeeks     = getSetISOWeek;
4115      momentPrototype__proto.weeksInYear    = getWeeksInYear;
4116      momentPrototype__proto.isoWeeksInYear = getISOWeeksInYear;
4117
4118      // Day
4119      momentPrototype__proto.date       = getSetDayOfMonth;
4120      momentPrototype__proto.day        = momentPrototype__proto.days             = getSetDayOfWeek;
4121      momentPrototype__proto.weekday    = getSetLocaleDayOfWeek;
4122      momentPrototype__proto.isoWeekday = getSetISODayOfWeek;
4123      momentPrototype__proto.dayOfYear  = getSetDayOfYear;
4124
4125      // Hour
4126      momentPrototype__proto.hour = momentPrototype__proto.hours = getSetHour;
4127
4128      // Minute
4129      momentPrototype__proto.minute = momentPrototype__proto.minutes = getSetMinute;
4130
4131      // Second
4132      momentPrototype__proto.second = momentPrototype__proto.seconds = getSetSecond;
4133
4134      // Millisecond
4135      momentPrototype__proto.millisecond = momentPrototype__proto.milliseconds = getSetMillisecond;
4136
4137      // Offset
4138      momentPrototype__proto.utcOffset            = getSetOffset;
4139      momentPrototype__proto.utc                  = setOffsetToUTC;
4140      momentPrototype__proto.local                = setOffsetToLocal;
4141      momentPrototype__proto.parseZone            = setOffsetToParsedOffset;
4142      momentPrototype__proto.hasAlignedHourOffset = hasAlignedHourOffset;
4143      momentPrototype__proto.isDST                = isDaylightSavingTime;
4144      momentPrototype__proto.isDSTShifted         = isDaylightSavingTimeShifted;
4145      momentPrototype__proto.isLocal              = isLocal;
4146      momentPrototype__proto.isUtcOffset          = isUtcOffset;
4147      momentPrototype__proto.isUtc                = isUtc;
4148      momentPrototype__proto.isUTC                = isUtc;
4149
4150      // Timezone
4151      momentPrototype__proto.zoneAbbr = getZoneAbbr;
4152      momentPrototype__proto.zoneName = getZoneName;
4153
4154      // Deprecations
4155      momentPrototype__proto.dates  = deprecate('dates accessor is deprecated. Use date instead.', getSetDayOfMonth);
4156      momentPrototype__proto.months = deprecate('months accessor is deprecated. Use month instead', getSetMonth);
4157      momentPrototype__proto.years  = deprecate('years accessor is deprecated. Use year instead', getSetYear);
4158      momentPrototype__proto.zone   = deprecate('moment().zone is deprecated, use moment().utcOffset instead. https://github.com/moment/moment/issues/1779', getSetZone);
4159
4160      var momentPrototype = momentPrototype__proto;
4161
4162      function moment__createUnix (input) {
4163          return local__createLocal(input * 1000);
4164      }
4165
4166      function moment__createInZone () {
4167          return local__createLocal.apply(null, arguments).parseZone();
4168      }
4169
4170      var defaultCalendar = {
4171          sameDay : '[Today at] LT',
4172          nextDay : '[Tomorrow at] LT',
4173          nextWeek : 'dddd [at] LT',
4174          lastDay : '[Yesterday at] LT',
4175          lastWeek : '[Last] dddd [at] LT',
4176          sameElse : 'L'
4177      };
4178
4179      function locale_calendar__calendar (key, mom, now) {
4180          var output = this._calendar[key];
4181          return typeof output === 'function' ? output.call(mom, now) : output;
4182      }
4183
4184      var defaultLongDateFormat = {
4185          LTS  : 'h:mm:ss A',
4186          LT   : 'h:mm A',
4187          L    : 'MM/DD/YYYY',
4188          LL   : 'MMMM D, YYYY',
4189          LLL  : 'MMMM D, YYYY h:mm A',
4190          LLLL : 'dddd, MMMM D, YYYY h:mm A'
4191      };
4192
4193      function longDateFormat (key) {
4194          var format = this._longDateFormat[key],
4195              formatUpper = this._longDateFormat[key.toUpperCase()];
4196
4197          if (format || !formatUpper) {
4198              return format;
4199          }
4200
4201          this._longDateFormat[key] = formatUpper.replace(/MMMM|MM|DD|dddd/g, function (val) {
4202              return val.slice(1);
4203          });
4204
4205          return this._longDateFormat[key];
4206      }
4207
4208      var defaultInvalidDate = 'Invalid date';
4209
4210      function invalidDate () {
4211          return this._invalidDate;
4212      }
4213
4214      var defaultOrdinal = '%d';
4215      var defaultOrdinalParse = /\d{1,2}/;
4216
4217      function ordinal (number) {
4218          return this._ordinal.replace('%d', number);
4219      }
4220
4221      function preParsePostFormat (string) {
4222          return string;
4223      }
4224
4225      var defaultRelativeTime = {
4226          future : 'in %s',
4227          past   : '%s ago',
4228          s  : 'a few seconds',
4229          m  : 'a minute',
4230          mm : '%d minutes',
4231          h  : 'an hour',
4232          hh : '%d hours',
4233          d  : 'a day',
4234          dd : '%d days',
4235          M  : 'a month',
4236          MM : '%d months',
4237          y  : 'a year',
4238          yy : '%d years'
4239      };
4240
4241      function relative__relativeTime (number, withoutSuffix, string, isFuture) {
4242          var output = this._relativeTime[string];
4243          return (typeof output === 'function') ?
4244              output(number, withoutSuffix, string, isFuture) :
4245              output.replace(/%d/i, number);
4246      }
4247
4248      function pastFuture (diff, output) {
4249          var format = this._relativeTime[diff > 0 ? 'future' : 'past'];
4250          return typeof format === 'function' ? format(output) : format.replace(/%s/i, output);
4251      }
4252
4253      function locale_set__set (config) {
4254          var prop, i;
4255          for (i in config) {
4256              prop = config[i];
4257              if (typeof prop === 'function') {
4258                  this[i] = prop;
4259              } else {
4260                  this['_' + i] = prop;
4261              }
4262          }
4263          // Lenient ordinal parsing accepts just a number in addition to
4264          // number + (possibly) stuff coming from _ordinalParseLenient.
4265          this._ordinalParseLenient = new RegExp(this._ordinalParse.source + '|' + (/\d{1,2}/).source);
4266      }
4267
4268      var prototype__proto = Locale.prototype;
4269
4270      prototype__proto._calendar       = defaultCalendar;
4271      prototype__proto.calendar        = locale_calendar__calendar;
4272      prototype__proto._longDateFormat = defaultLongDateFormat;
4273      prototype__proto.longDateFormat  = longDateFormat;
4274      prototype__proto._invalidDate    = defaultInvalidDate;
4275      prototype__proto.invalidDate     = invalidDate;
4276      prototype__proto._ordinal        = defaultOrdinal;
4277      prototype__proto.ordinal         = ordinal;
4278      prototype__proto._ordinalParse   = defaultOrdinalParse;
4279      prototype__proto.preparse        = preParsePostFormat;
4280      prototype__proto.postformat      = preParsePostFormat;
4281      prototype__proto._relativeTime   = defaultRelativeTime;
4282      prototype__proto.relativeTime    = relative__relativeTime;
4283      prototype__proto.pastFuture      = pastFuture;
4284      prototype__proto.set             = locale_set__set;
4285
4286      // Month
4287      prototype__proto.months       =        localeMonths;
4288      prototype__proto._months      = defaultLocaleMonths;
4289      prototype__proto.monthsShort  =        localeMonthsShort;
4290      prototype__proto._monthsShort = defaultLocaleMonthsShort;
4291      prototype__proto.monthsParse  =        localeMonthsParse;
4292
4293      // Week
4294      prototype__proto.week = localeWeek;
4295      prototype__proto._week = defaultLocaleWeek;
4296      prototype__proto.firstDayOfYear = localeFirstDayOfYear;
4297      prototype__proto.firstDayOfWeek = localeFirstDayOfWeek;
4298
4299      // Day of Week
4300      prototype__proto.weekdays       =        localeWeekdays;
4301      prototype__proto._weekdays      = defaultLocaleWeekdays;
4302      prototype__proto.weekdaysMin    =        localeWeekdaysMin;
4303      prototype__proto._weekdaysMin   = defaultLocaleWeekdaysMin;
4304      prototype__proto.weekdaysShort  =        localeWeekdaysShort;
4305      prototype__proto._weekdaysShort = defaultLocaleWeekdaysShort;
4306      prototype__proto.weekdaysParse  =        localeWeekdaysParse;
4307
4308      // Hours
4309      prototype__proto.isPM = localeIsPM;
4310      prototype__proto._meridiemParse = defaultLocaleMeridiemParse;
4311      prototype__proto.meridiem = localeMeridiem;
4312
4313      function lists__get (format, index, field, setter) {
4314          var locale = locale_locales__getLocale();
4315          var utc = create_utc__createUTC().set(setter, index);
vendor: 16,852 bytes, lines 4316-4759
4316          return locale[field](utc, format);
4317      }
4318
4319      function list (format, index, field, count, setter) {
4320          if (typeof format === 'number') {
4321              index = format;
4322              format = undefined;
4323          }
4324
4325          format = format || '';
4326
4327          if (index != null) {
4328              return lists__get(format, index, field, setter);
4329          }
4330
4331          var i;
4332          var out = [];
4333          for (i = 0; i < count; i++) {
4334              out[i] = lists__get(format, i, field, setter);
4335          }
4336          return out;
4337      }
4338
4339      function lists__listMonths (format, index) {
4340          return list(format, index, 'months', 12, 'month');
4341      }
4342
4343      function lists__listMonthsShort (format, index) {
4344          return list(format, index, 'monthsShort', 12, 'month');
4345      }
4346
4347      function lists__listWeekdays (format, index) {
4348          return list(format, index, 'weekdays', 7, 'day');
4349      }
4350
4351      function lists__listWeekdaysShort (format, index) {
4352          return list(format, index, 'weekdaysShort', 7, 'day');
4353      }
4354
4355      function lists__listWeekdaysMin (format, index) {
4356          return list(format, index, 'weekdaysMin', 7, 'day');
4357      }
4358
4359      locale_locales__getSetGlobalLocale('en', {
4360          ordinalParse: /\d{1,2}(th|st|nd|rd)/,
4361          ordinal : function (number) {
4362              var b = number % 10,
4363                  output = (toInt(number % 100 / 10) === 1) ? 'th' :
4364                  (b === 1) ? 'st' :
4365                  (b === 2) ? 'nd' :
4366                  (b === 3) ? 'rd' : 'th';
4367              return number + output;
4368          }
4369      });
4370
4371      // Side effect imports
4372      utils_hooks__hooks.lang = deprecate('moment.lang is deprecated. Use moment.locale instead.', locale_locales__getSetGlobalLocale);
4373      utils_hooks__hooks.langData = deprecate('moment.langData is deprecated. Use moment.localeData instead.', locale_locales__getLocale);
4374
4375      var mathAbs = Math.abs;
4376
4377      function duration_abs__abs () {
4378          var data           = this._data;
4379
4380          this._milliseconds = mathAbs(this._milliseconds);
4381          this._days         = mathAbs(this._days);
4382          this._months       = mathAbs(this._months);
4383
4384          data.milliseconds  = mathAbs(data.milliseconds);
4385          data.seconds       = mathAbs(data.seconds);
4386          data.minutes       = mathAbs(data.minutes);
4387          data.hours         = mathAbs(data.hours);
4388          data.months        = mathAbs(data.months);
4389          data.years         = mathAbs(data.years);
4390
4391          return this;
4392      }
4393
4394      function duration_add_subtract__addSubtract (duration, input, value, direction) {
4395          var other = create__createDuration(input, value);
4396
4397          duration._milliseconds += direction * other._milliseconds;
4398          duration._days         += direction * other._days;
4399          duration._months       += direction * other._months;
4400
4401          return duration._bubble();
4402      }
4403
4404      // supports only 2.0-style add(1, 's') or add(duration)
4405      function duration_add_subtract__add (input, value) {
4406          return duration_add_subtract__addSubtract(this, input, value, 1);
4407      }
4408
4409      // supports only 2.0-style subtract(1, 's') or subtract(duration)
4410      function duration_add_subtract__subtract (input, value) {
4411          return duration_add_subtract__addSubtract(this, input, value, -1);
4412      }
4413
4414      function absCeil (number) {
4415          if (number < 0) {
4416              return Math.floor(number);
4417          } else {
4418              return Math.ceil(number);
4419          }
4420      }
4421
4422      function bubble () {
4423          var milliseconds = this._milliseconds;
4424          var days         = this._days;
4425          var months       = this._months;
4426          var data         = this._data;
4427          var seconds, minutes, hours, years, monthsFromDays;
4428
4429          // if we have a mix of positive and negative values, bubble down first
4430          // check: https://github.com/moment/moment/issues/2166
4431          if (!((milliseconds >= 0 && days >= 0 && months >= 0) ||
4432                  (milliseconds <= 0 && days <= 0 && months <= 0))) {
4433              milliseconds += absCeil(monthsToDays(months) + days) * 864e5;
4434              days = 0;
4435              months = 0;
4436          }
4437
4438          // The following code bubbles up values, see the tests for
4439          // examples of what that means.
4440          data.milliseconds = milliseconds % 1000;
4441
4442          seconds           = absFloor(milliseconds / 1000);
4443          data.seconds      = seconds % 60;
4444
4445          minutes           = absFloor(seconds / 60);
4446          data.minutes      = minutes % 60;
4447
4448          hours             = absFloor(minutes / 60);
4449          data.hours        = hours % 24;
4450
4451          days += absFloor(hours / 24);
4452
4453          // convert days to months
4454          monthsFromDays = absFloor(daysToMonths(days));
4455          months += monthsFromDays;
4456          days -= absCeil(monthsToDays(monthsFromDays));
4457
4458          // 12 months -> 1 year
4459          years = absFloor(months / 12);
4460          months %= 12;
4461
4462          data.days   = days;
4463          data.months = months;
4464          data.years  = years;
4465
4466          return this;
4467      }
4468
4469      function daysToMonths (days) {
4470          // 400 years have 146097 days (taking into account leap year rules)
4471          // 400 years have 12 months === 4800
4472          return days * 4800 / 146097;
4473      }
4474
4475      function monthsToDays (months) {
4476          // the reverse of daysToMonths
4477          return months * 146097 / 4800;
4478      }
4479
4480      function as (units) {
4481          var days;
4482          var months;
4483          var milliseconds = this._milliseconds;
4484
4485          units = normalizeUnits(units);
4486
4487          if (units === 'month' || units === 'year') {
4488              days   = this._days   + milliseconds / 864e5;
4489              months = this._months + daysToMonths(days);
4490              return units === 'month' ? months : months / 12;
4491          } else {
4492              // handle milliseconds separately because of floating point math errors (issue #1867)
4493              days = this._days + Math.round(monthsToDays(this._months));
4494              switch (units) {
4495                  case 'week'   : return days / 7     + milliseconds / 6048e5;
4496                  case 'day'    : return days         + milliseconds / 864e5;
4497                  case 'hour'   : return days * 24    + milliseconds / 36e5;
4498                  case 'minute' : return days * 1440  + milliseconds / 6e4;
4499                  case 'second' : return days * 86400 + milliseconds / 1000;
4500                  // Math.floor prevents floating point math errors here
4501                  case 'millisecond': return Math.floor(days * 864e5) + milliseconds;
4502                  default: throw new Error('Unknown unit ' + units);
4503              }
4504          }
4505      }
4506
4507      // TODO: Use this.as('ms')?
4508      function duration_as__valueOf () {
4509          return (
4510              this._milliseconds +
4511              this._days * 864e5 +
4512              (this._months % 12) * 2592e6 +
4513              toInt(this._months / 12) * 31536e6
4514          );
4515      }
4516
4517      function makeAs (alias) {
4518          return function () {
4519              return this.as(alias);
4520          };
4521      }
4522
4523      var asMilliseconds = makeAs('ms');
4524      var asSeconds      = makeAs('s');
4525      var asMinutes      = makeAs('m');
4526      var asHours        = makeAs('h');
4527      var asDays         = makeAs('d');
4528      var asWeeks        = makeAs('w');
4529      var asMonths       = makeAs('M');
4530      var asYears        = makeAs('y');
4531
4532      function duration_get__get (units) {
4533          units = normalizeUnits(units);
4534          return this[units + 's']();
4535      }
4536
4537      function makeGetter(name) {
4538          return function () {
4539              return this._data[name];
4540          };
4541      }
4542
4543      var milliseconds = makeGetter('milliseconds');
4544      var seconds      = makeGetter('seconds');
4545      var minutes      = makeGetter('minutes');
4546      var hours        = makeGetter('hours');
4547      var days         = makeGetter('days');
4548      var months       = makeGetter('months');
4549      var years        = makeGetter('years');
4550
4551      function weeks () {
4552          return absFloor(this.days() / 7);
4553      }
4554
4555      var round = Math.round;
4556      var thresholds = {
4557          s: 45,  // seconds to minute
4558          m: 45,  // minutes to hour
4559          h: 22,  // hours to day
4560          d: 26,  // days to month
4561          M: 11   // months to year
4562      };
4563
4564      // helper function for moment.fn.from, moment.fn.fromNow, and moment.duration.fn.humanize
4565      function substituteTimeAgo(string, number, withoutSuffix, isFuture, locale) {
4566          return locale.relativeTime(number || 1, !!withoutSuffix, string, isFuture);
4567      }
4568
4569      function duration_humanize__relativeTime (posNegDuration, withoutSuffix, locale) {
4570          var duration = create__createDuration(posNegDuration).abs();
4571          var seconds  = round(duration.as('s'));
4572          var minutes  = round(duration.as('m'));
4573          var hours    = round(duration.as('h'));
4574          var days     = round(duration.as('d'));
4575          var months   = round(duration.as('M'));
4576          var years    = round(duration.as('y'));
4577
4578          var a = seconds < thresholds.s && ['s', seconds]  ||
4579                  minutes === 1          && ['m']           ||
4580                  minutes < thresholds.m && ['mm', minutes] ||
4581                  hours   === 1          && ['h']           ||
4582                  hours   < thresholds.h && ['hh', hours]   ||
4583                  days    === 1          && ['d']           ||
4584                  days    < thresholds.d && ['dd', days]    ||
4585                  months  === 1          && ['M']           ||
4586                  months  < thresholds.M && ['MM', months]  ||
4587                  years   === 1          && ['y']           || ['yy', years];
4588
4589          a[2] = withoutSuffix;
4590          a[3] = +posNegDuration > 0;
4591          a[4] = locale;
4592          return substituteTimeAgo.apply(null, a);
4593      }
4594
4595      // This function allows you to set a threshold for relative time strings
4596      function duration_humanize__getSetRelativeTimeThreshold (threshold, limit) {
4597          if (thresholds[threshold] === undefined) {
4598              return false;
4599          }
4600          if (limit === undefined) {
4601              return thresholds[threshold];
4602          }
4603          thresholds[threshold] = limit;
4604          return true;
4605      }
4606
4607      function humanize (withSuffix) {
4608          var locale = this.localeData();
4609          var output = duration_humanize__relativeTime(this, !withSuffix, locale);
4610
4611          if (withSuffix) {
4612              output = locale.pastFuture(+this, output);
4613          }
4614
4615          return locale.postformat(output);
4616      }
4617
4618      var iso_string__abs = Math.abs;
4619
4620      function iso_string__toISOString() {
4621          // for ISO strings we do not use the normal bubbling rules:
4622          //  * milliseconds bubble up until they become hours
4623          //  * days do not bubble at all
4624          //  * months bubble up until they become years
4625          // This is because there is no context-free conversion between hours and days
4626          // (think of clock changes)
4627          // and also not between days and months (28-31 days per month)
4628          var seconds = iso_string__abs(this._milliseconds) / 1000;
4629          var days         = iso_string__abs(this._days);
4630          var months       = iso_string__abs(this._months);
4631          var minutes, hours, years;
4632
4633          // 3600 seconds -> 60 minutes -> 1 hour
4634          minutes           = absFloor(seconds / 60);
4635          hours             = absFloor(minutes / 60);
4636          seconds %= 60;
4637          minutes %= 60;
4638
4639          // 12 months -> 1 year
4640          years  = absFloor(months / 12);
4641          months %= 12;
4642
4643
4644          // inspired by https://github.com/dordille/moment-isoduration/blob/master/moment.isoduration.js
4645          var Y = years;
4646          var M = months;
4647          var D = days;
4648          var h = hours;
4649          var m = minutes;
4650          var s = seconds;
4651          var total = this.asSeconds();
4652
4653          if (!total) {
4654              // this is the same as C#'s (Noda) and python (isodate)...
4655              // but not other JS (goog.date)
4656              return 'P0D';
4657          }
4658
4659          return (total < 0 ? '-' : '') +
4660              'P' +
4661              (Y ? Y + 'Y' : '') +
4662              (M ? M + 'M' : '') +
4663              (D ? D + 'D' : '') +
4664              ((h || m || s) ? 'T' : '') +
4665              (h ? h + 'H' : '') +
4666              (m ? m + 'M' : '') +
4667              (s ? s + 'S' : '');
4668      }
4669
4670      var duration_prototype__proto = Duration.prototype;
4671
4672      duration_prototype__proto.abs            = duration_abs__abs;
4673      duration_prototype__proto.add            = duration_add_subtract__add;
4674      duration_prototype__proto.subtract       = duration_add_subtract__subtract;
4675      duration_prototype__proto.as             = as;
4676      duration_prototype__proto.asMilliseconds = asMilliseconds;
4677      duration_prototype__proto.asSeconds      = asSeconds;
4678      duration_prototype__proto.asMinutes      = asMinutes;
4679      duration_prototype__proto.asHours        = asHours;
4680      duration_prototype__proto.asDays         = asDays;
4681      duration_prototype__proto.asWeeks        = asWeeks;
4682      duration_prototype__proto.asMonths       = asMonths;
4683      duration_prototype__proto.asYears        = asYears;
4684      duration_prototype__proto.valueOf        = duration_as__valueOf;
4685      duration_prototype__proto._bubble        = bubble;
4686      duration_prototype__proto.get            = duration_get__get;
4687      duration_prototype__proto.milliseconds   = milliseconds;
4688      duration_prototype__proto.seconds        = seconds;
4689      duration_prototype__proto.minutes        = minutes;
4690      duration_prototype__proto.hours          = hours;
4691      duration_prototype__proto.days           = days;
4692      duration_prototype__proto.weeks          = weeks;
4693      duration_prototype__proto.months         = months;
4694      duration_prototype__proto.years          = years;
4695      duration_prototype__proto.humanize       = humanize;
4696      duration_prototype__proto.toISOString    = iso_string__toISOString;
4697      duration_prototype__proto.toString       = iso_string__toISOString;
4698      duration_prototype__proto.toJSON         = iso_string__toISOString;
4699      duration_prototype__proto.locale         = locale;
4700      duration_prototype__proto.localeData     = localeData;
4701
4702      // Deprecations
4703      duration_prototype__proto.toIsoString = deprecate('toIsoString() is deprecated. Please use toISOString() instead (notice the capitals)', iso_string__toISOString);
4704      duration_prototype__proto.lang = lang;
4705
4706      // Side effect imports
4707
4708      addFormatToken('X', 0, 0, 'unix');
4709      addFormatToken('x', 0, 0, 'valueOf');
4710
4711      // PARSING
4712
4713      addRegexToken('x', matchSigned);
4714      addRegexToken('X', matchTimestamp);
4715      addParseToken('X', function (input, array, config) {
4716          config._d = new Date(parseFloat(input, 10) * 1000);
4717      });
4718      addParseToken('x', function (input, array, config) {
4719          config._d = new Date(toInt(input));
4720      });
4721
4722      // Side effect imports
4723
4724
4725      utils_hooks__hooks.version = '2.10.6';
4726
4727      setHookCallback(local__createLocal);
4728
4729      utils_hooks__hooks.fn                    = momentPrototype;
4730      utils_hooks__hooks.min                   = min;
4731      utils_hooks__hooks.max                   = max;
4732      utils_hooks__hooks.utc                   = create_utc__createUTC;
4733      utils_hooks__hooks.unix                  = moment__createUnix;
4734      utils_hooks__hooks.months                = lists__listMonths;
4735      utils_hooks__hooks.isDate                = isDate;
4736      utils_hooks__hooks.locale                = locale_locales__getSetGlobalLocale;
4737      utils_hooks__hooks.invalid               = valid__createInvalid;
4738      utils_hooks__hooks.duration              = create__createDuration;
4739      utils_hooks__hooks.isMoment              = isMoment;
4740      utils_hooks__hooks.weekdays              = lists__listWeekdays;
4741      utils_hooks__hooks.parseZone             = moment__createInZone;
4742      utils_hooks__hooks.localeData            = locale_locales__getLocale;
4743      utils_hooks__hooks.isDuration            = isDuration;
4744      utils_hooks__hooks.monthsShort           = lists__listMonthsShort;
4745      utils_hooks__hooks.weekdaysMin           = lists__listWeekdaysMin;
4746      utils_hooks__hooks.defineLocale          = defineLocale;
4747      utils_hooks__hooks.weekdaysShort         = lists__listWeekdaysShort;
4748      utils_hooks__hooks.normalizeUnits        = normalizeUnits;
4749      utils_hooks__hooks.relativeTimeThreshold = duration_humanize__getSetRelativeTimeThreshold;
4750
4751      var _moment = utils_hooks__hooks;
4752
4753      return _moment;
4754
4755  }));
4756  /* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(4)(module)))
4757
4758/***/ },
4759/* 4 */
4760/***/ function(module, exports) {
4761
4762  module.exports = function(module) {
4763  	if(!module.webpackPolyfill) {
4764  		module.deprecate = function() {};
4765  		module.paths = [];
4766  		// module.parent = undefined by default
4767  		module.children = [];
4768  		module.webpackPolyfill = 1;
4769  	}
4770  	return module;
4771  }
4772
4773
4774/***/ },
4775/* 5 */
4776/***/ function(module, exports) {
4777
4778  function webpackContext(req) {
4779  	throw new Error("Cannot find module '" + req + "'.");
4780  }
4781  webpackContext.keys = function() { return []; };
4782  webpackContext.resolve = webpackContext;
4783  module.exports = webpackContext;
4784  webpackContext.id = 5;
4785
4786
4787/***/ },
4788/* 6 */
4789/***/ function(module, exports) {
4790
4791  /* WEBPACK VAR INJECTION */(function(global) {'use strict';
4792
4793  var _rng;
4794
4795  var globalVar = typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : null;
4796
4797  if (globalVar && globalVar.crypto && crypto.getRandomValues) {
4798    // WHATWG crypto-based RNG - http://wiki.whatwg.org/wiki/Crypto
4799    // Moderately fast, high quality
4800    var _rnds8 = new Uint8Array(16);
4801    _rng = function whatwgRNG() {
4802      crypto.getRandomValues(_rnds8);
4803      return _rnds8;
4804    };
4805  }
4806
4807  if (!_rng) {
4808    // Math.random()-based (RNG)
4809    //
4810    // If all else fails, use Math.random().  It's fast, but is of unspecified
4811    // quality.
4812    var _rnds = new Array(16);
4813    _rng = function () {
4814      for (var i = 0, r; i < 16; i++) {
4815        if ((i & 0x03) === 0) r = Math.random() * 0x100000000;
4816        _rnds[i] = r >>> ((i & 0x03) << 3) & 0xff;
vendor: 4,189 bytes, lines 4817-4943
4817      }
4818
4819      return _rnds;
4820    };
4821  }
4822
4823  //     uuid.js
4824  //
4825  //     Copyright (c) 2010-2012 Robert Kieffer
4826  //     MIT License - http://opensource.org/licenses/mit-license.php
4827
4828  // Unique ID creation requires a high quality random # generator.  We feature
4829  // detect to determine the best RNG source, normalizing to a function that
4830  // returns 128-bits of randomness, since that's what's usually required
4831
4832  //var _rng = require('./rng');
4833
4834  // Maps for number <-> hex string conversion
4835  var _byteToHex = [];
4836  var _hexToByte = {};
4837  for (var i = 0; i < 256; i++) {
4838    _byteToHex[i] = (i + 0x100).toString(16).substr(1);
4839    _hexToByte[_byteToHex[i]] = i;
4840  }
4841
4842  // **`parse()` - Parse a UUID into it's component bytes**
4843  function parse(s, buf, offset) {
4844    var i = buf && offset || 0,
4845        ii = 0;
4846
4847    buf = buf || [];
4848    s.toLowerCase().replace(/[0-9a-f]{2}/g, function (oct) {
4849      if (ii < 16) {
4850        // Don't overflow!
4851        buf[i + ii++] = _hexToByte[oct];
4852      }
4853    });
4854
4855    // Zero out remaining bytes if string was short
4856    while (ii < 16) {
4857      buf[i + ii++] = 0;
4858    }
4859
4860    return buf;
4861  }
4862
4863  // **`unparse()` - Convert UUID byte array (ala parse()) into a string**
4864  function unparse(buf, offset) {
4865    var i = offset || 0,
4866        bth = _byteToHex;
4867    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++]];
4868  }
4869
4870  // **`v1()` - Generate time-based UUID**
4871  //
4872  // Inspired by https://github.com/LiosK/UUID.js
4873  // and http://docs.python.org/library/uuid.html
4874
4875  // random #'s we need to init node and clockseq
4876  var _seedBytes = _rng();
4877
4878  // Per 4.5, create and 48-bit node id, (47 random bits + multicast bit = 1)
4879  var _nodeId = [_seedBytes[0] | 0x01, _seedBytes[1], _seedBytes[2], _seedBytes[3], _seedBytes[4], _seedBytes[5]];
4880
4881  // Per 4.2.2, randomize (14 bit) clockseq
4882  var _clockseq = (_seedBytes[6] << 8 | _seedBytes[7]) & 0x3fff;
4883
4884  // Previous uuid creation time
4885  var _lastMSecs = 0,
4886      _lastNSecs = 0;
4887
4888  // See https://github.com/broofa/node-uuid for API details
4889  function v1(options, buf, offset) {
4890    var i = buf && offset || 0;
4891    var b = buf || [];
4892
4893    options = options || {};
4894
4895    var clockseq = options.clockseq !== undefined ? options.clockseq : _clockseq;
4896
4897    // UUID timestamps are 100 nano-second units since the Gregorian epoch,
4898    // (1582-10-15 00:00).  JSNumbers aren't precise enough for this, so
4899    // time is handled internally as 'msecs' (integer milliseconds) and 'nsecs'
4900    // (100-nanoseconds offset from msecs) since unix epoch, 1970-01-01 00:00.
4901    var msecs = options.msecs !== undefined ? options.msecs : new Date().getTime();
4902
4903    // Per 4.2.1.2, use count of uuid's generated during the current clock
4904    // cycle to simulate higher resolution clock
4905    var nsecs = options.nsecs !== undefined ? options.nsecs : _lastNSecs + 1;
4906
4907    // Time since last uuid creation (in msecs)
4908    var dt = msecs - _lastMSecs + (nsecs - _lastNSecs) / 10000;
4909
4910    // Per 4.2.1.2, Bump clockseq on clock regression
4911    if (dt < 0 && options.clockseq === undefined) {
4912      clockseq = clockseq + 1 & 0x3fff;
4913    }
4914
4915    // Reset nsecs if clock regresses (new clockseq) or we've moved onto a new
4916    // time interval
4917    if ((dt < 0 || msecs > _lastMSecs) && options.nsecs === undefined) {
4918      nsecs = 0;
4919    }
4920
4921    // Per 4.2.1.2 Throw error if too many uuids are requested
4922    if (nsecs >= 10000) {
4923      throw new Error('uuid.v1(): Can\'t create more than 10M uuids/sec');
4924    }
4925
4926    _lastMSecs = msecs;
4927    _lastNSecs = nsecs;
4928    _clockseq = clockseq;
4929
4930    // Per 4.1.4 - Convert from unix epoch to Gregorian epoch
4931    msecs += 12219292800000;
4932
4933    // `time_low`
4934    var tl = ((msecs & 0xfffffff) * 10000 + nsecs) % 0x100000000;
4935    b[i++] = tl >>> 24 & 0xff;
4936    b[i++] = tl >>> 16 & 0xff;
4937    b[i++] = tl >>> 8 & 0xff;
4938    b[i++] = tl & 0xff;
4939
4940    // `time_mid`
4941    var tmh = msecs / 0x100000000 * 10000 & 0xfffffff;
4942    b[i++] = tmh >>> 8 & 0xff;
4943    b[i++] = tmh & 0xff;
4944
4945    // `time_high_and_version`
4946    b[i++] = tmh >>> 24 & 0xf | 0x10; // include version
4947    b[i++] = tmh >>> 16 & 0xff;
4948
4949    // `clock_seq_hi_and_reserved` (Per 4.2.2 - include variant)
4950    b[i++] = clockseq >>> 8 | 0x80;
4951
4952    // `clock_seq_low`
4953    b[i++] = clockseq & 0xff;
4954
4955    // `node`
4956    var node = options.node || _nodeId;
4957    for (var n = 0; n < 6; n++) {
4958      b[i + n] = node[n];
4959    }
4960
4961    return buf ? buf : unparse(b);
4962  }
4963
4964  // **`v4()` - Generate random UUID**
4965
4966  // See https://github.com/broofa/node-uuid for API details
4967  function v4(options, buf, offset) {
4968    // Deprecated - 'format' argument, as supported in v1.2
4969    var i = buf && offset || 0;
4970
4971    if (typeof options == 'string') {
4972      buf = options == 'binary' ? new Array(16) : null;
4973      options = null;
4974    }
4975    options = options || {};
4976
4977    var rnds = options.random || (options.rng || _rng)();
4978
4979    // Per 4.4, set bits for version and `clock_seq_hi_and_reserved`
4980    rnds[6] = rnds[6] & 0x0f | 0x40;
4981    rnds[8] = rnds[8] & 0x3f | 0x80;
4982
4983    // Copy bytes to buffer, if provided
4984    if (buf) {
4985      for (var ii = 0; ii < 16; ii++) {
4986        buf[i + ii] = rnds[ii];
4987      }
4988    }
4989
4990    return buf || unparse(rnds);
4991  }
4992
4993  // Export public API
4994  var uuid = v4;
4995  uuid.v1 = v1;
4996  uuid.v4 = v4;
4997  uuid.parse = parse;
4998  uuid.unparse = unparse;
4999
5000  module.exports = uuid;
5001  /* WEBPACK VAR INJECTION */}.call(exports, (function() { return this; }())))
5002
5003/***/ },
5004/* 7 */
5005/***/ function(module, exports) {
5006
5007  // DOM utility methods
5008
5009  /**
5010   * this prepares the JSON container for allocating SVG elements
5011   * @param JSONcontainer
5012   * @private
5013   */
5014  'use strict';
5015
5016  exports.prepareElements = function (JSONcontainer) {
5017    // cleanup the redundant svgElements;
5018    for (var elementType in JSONcontainer) {
5019      if (JSONcontainer.hasOwnProperty(elementType)) {
5020        JSONcontainer[elementType].redundant = JSONcontainer[elementType].used;
5021        JSONcontainer[elementType].used = [];
5022      }
5023    }
5024  };
5025
5026  /**
5027   * this cleans up all the unused SVG elements. By asking for the parentNode, we only need to supply the JSON container from
5028   * which to remove the redundant elements.
5029   *
5030   * @param JSONcontainer
5031   * @private
5032   */
5033  exports.cleanupElements = function (JSONcontainer) {
5034    // cleanup the redundant svgElements;
5035    for (var elementType in JSONcontainer) {
5036      if (JSONcontainer.hasOwnProperty(elementType)) {
5037        if (JSONcontainer[elementType].redundant) {
5038          for (var i = 0; i < JSONcontainer[elementType].redundant.length; i++) {
5039            JSONcontainer[elementType].redundant[i].parentNode.removeChild(JSONcontainer[elementType].redundant[i]);
5040          }
5041          JSONcontainer[elementType].redundant = [];
5042        }
5043      }
5044    }
5045  };
5046
5047  /**
5048   * Allocate or generate an SVG element if needed. Store a reference to it in the JSON container and draw it in the svgContainer
5049   * the JSON container and the SVG container have to be supplied so other svg containers (like the legend) can use this.
5050   *
5051   * @param elementType
5052   * @param JSONcontainer
5053   * @param svgContainer
5054   * @returns {*}
5055   * @private
5056   */
5057  exports.getSVGElement = function (elementType, JSONcontainer, svgContainer) {
5058    var element;
5059    // allocate SVG element, if it doesnt yet exist, create one.
5060    if (JSONcontainer.hasOwnProperty(elementType)) {
5061      // this element has been created before
5062      // check if there is an redundant element
5063      if (JSONcontainer[elementType].redundant.length > 0) {
5064        element = JSONcontainer[elementType].redundant[0];
5065        JSONcontainer[elementType].redundant.shift();
5066      } else {
5067        // create a new element and add it to the SVG
5068        element = document.createElementNS('http://www.w3.org/2000/svg', elementType);
5069        svgContainer.appendChild(element);
5070      }
5071    } else {
5072      // create a new element and add it to the SVG, also create a new object in the svgElements to keep track of it.
5073      element = document.createElementNS('http://www.w3.org/2000/svg', elementType);
5074      JSONcontainer[elementType] = { used: [], redundant: [] };
5075      svgContainer.appendChild(element);
5076    }
5077    JSONcontainer[elementType].used.push(element);
5078    return element;
5079  };
5080
5081  /**
5082   * Allocate or generate an SVG element if needed. Store a reference to it in the JSON container and draw it in the svgContainer
5083   * the JSON container and the SVG container have to be supplied so other svg containers (like the legend) can use this.
5084   *
5085   * @param elementType
5086   * @param JSONcontainer
5087   * @param DOMContainer
5088   * @returns {*}
5089   * @private
5090   */
5091  exports.getDOMElement = function (elementType, JSONcontainer, DOMContainer, insertBefore) {
5092    var element;
5093    // allocate DOM element, if it doesnt yet exist, create one.
5094    if (JSONcontainer.hasOwnProperty(elementType)) {
5095      // this element has been created before
5096      // check if there is an redundant element
5097      if (JSONcontainer[elementType].redundant.length > 0) {
5098        element = JSONcontainer[elementType].redundant[0];
5099        JSONcontainer[elementType].redundant.shift();
5100      } else {
5101        // create a new element and add it to the SVG
5102        element = document.createElement(elementType);
5103        if (insertBefore !== undefined) {
5104          DOMContainer.insertBefore(element, insertBefore);
5105        } else {
5106          DOMContainer.appendChild(element);
5107        }
5108      }
5109    } else {
5110      // create a new element and add it to the SVG, also create a new object in the svgElements to keep track of it.
5111      element = document.createElement(elementType);
5112      JSONcontainer[elementType] = { used: [], redundant: [] };
5113      if (insertBefore !== undefined) {
5114        DOMContainer.insertBefore(element, insertBefore);
5115      } else {
5116        DOMContainer.appendChild(element);
5117      }
5118    }
5119    JSONcontainer[elementType].used.push(element);
5120    return element;
5121  };
5122
5123  /**
5124   * Draw a point object. This is a separate function because it can also be called by the legend.
5125   * The reason the JSONcontainer and the target SVG svgContainer have to be supplied is so the legend can use these functions
5126   * as well.
5127   *
5128   * @param x
5129   * @param y
5130   * @param groupTemplate: A template containing the necessary information to draw the datapoint e.g., {style: 'circle', size: 5, className: 'className' }
5131   * @param JSONcontainer
5132   * @param svgContainer
5133   * @param labelObj
5134   * @returns {*}
5135   */
5136  exports.drawPoint = function (x, y, groupTemplate, JSONcontainer, svgContainer, labelObj) {
5137    var point;
5138    if (groupTemplate.style == 'circle') {
5139      point = exports.getSVGElement('circle', JSONcontainer, svgContainer);
5140      point.setAttributeNS(null, 'cx', x);
5141      point.setAttributeNS(null, 'cy', y);
5142      point.setAttributeNS(null, 'r', 0.5 * groupTemplate.size);
5143    } else {
5144      point = exports.getSVGElement('rect', JSONcontainer, svgContainer);
5145      point.setAttributeNS(null, 'x', x - 0.5 * groupTemplate.size);
5146      point.setAttributeNS(null, 'y', y - 0.5 * groupTemplate.size);
5147      point.setAttributeNS(null, 'width', groupTemplate.size);
5148      point.setAttributeNS(null, 'height', groupTemplate.size);
5149    }
5150
5151    if (groupTemplate.styles !== undefined) {
5152      point.setAttributeNS(null, 'style', groupTemplate.styles);
5153    }
5154    point.setAttributeNS(null, 'class', groupTemplate.className + ' vis-point');
5155    //handle label
5156
5157    if (labelObj) {
5158      var label = exports.getSVGElement('text', JSONcontainer, svgContainer);
5159      if (labelObj.xOffset) {
5160        x = x + labelObj.xOffset;
5161      }
5162
5163      if (labelObj.yOffset) {
5164        y = y + labelObj.yOffset;
5165      }
5166      if (labelObj.content) {
5167        label.textContent = labelObj.content;
5168      }
5169
5170      if (labelObj.className) {
5171        label.setAttributeNS(null, 'class', labelObj.className + ' vis-label');
5172      }
5173      label.setAttributeNS(null, 'x', x);
5174      label.setAttributeNS(null, 'y', y);
5175    }
5176
5177    return point;
5178  };
5179
5180  /**
5181   * draw a bar SVG element centered on the X coordinate
5182   *
5183   * @param x
5184   * @param y
5185   * @param className
5186   */
5187  exports.drawBar = function (x, y, width, height, className, JSONcontainer, svgContainer, style) {
5188    if (height != 0) {
5189      if (height < 0) {
5190        height *= -1;
5191        y -= height;
5192      }
5193      var rect = exports.getSVGElement('rect', JSONcontainer, svgContainer);
5194      rect.setAttributeNS(null, 'x', x - 0.5 * width);
5195      rect.setAttributeNS(null, 'y', y);
5196      rect.setAttributeNS(null, 'width', width);
5197      rect.setAttributeNS(null, 'height', height);
5198      rect.setAttributeNS(null, 'class', className);
5199      if (style) {
5200        rect.setAttributeNS(null, 'style', style);
5201      }
5202    }
5203  };
5204
5205/***/ },
5206/* 8 */
5207/***/ function(module, exports, __webpack_require__) {
5208
5209  'use strict';
5210
5211  var util = __webpack_require__(1);
5212  var Queue = __webpack_require__(9);
5213
5214  /**
5215   * DataSet
5216   *
5217   * Usage:
5218   *     var dataSet = new DataSet({
5219   *         fieldId: '_id',
5220   *         type: {
5221   *             // ...
5222   *         }
5223   *     });
5224   *
5225   *     dataSet.add(item);
5226   *     dataSet.add(data);
5227   *     dataSet.update(item);
5228   *     dataSet.update(data);
5229   *     dataSet.remove(id);
5230   *     dataSet.remove(ids);
5231   *     var data = dataSet.get();
5232   *     var data = dataSet.get(id);
5233   *     var data = dataSet.get(ids);
5234   *     var data = dataSet.get(ids, options, data);
5235   *     dataSet.clear();
5236   *
5237   * A data set can:
5238   * - add/remove/update data
5239   * - gives triggers upon changes in the data
5240   * - can  import/export data in various data formats
5241   *
5242   * @param {Array} [data]    Optional array with initial data
5243   * @param {Object} [options]   Available options:
5244   *                             {String} fieldId Field name of the id in the
5245   *                                              items, 'id' by default.
5246   *                             {Object.<String, String} type
5247   *                                              A map with field names as key,
5248   *                                              and the field type as value.
5249   *                             {Object} queue   Queue changes to the DataSet,
5250   *                                              flush them all at once.
5251   *                                              Queue options:
5252   *                                              - {number}
5252 delay  Delay in ms, null by default
5253   *                                              - {number} max    Maximum number of entries in the queue, Infinity by default
5254   * @constructor DataSet
5255   */
5256  // TODO: add a DataSet constructor DataSet(data, options)
5257  function DataSet(data, options) {
5258    // correctly read optional arguments
5259    if (data && !Array.isArray(data)) {
5260      options = data;
5261      data = null;
5262    }
5263
5264    this._options = options || {};
5265    this._data = {}; // map with data indexed by id
5266    this.length = 0; // number of items in the DataSet
5267    this._fieldId = this._options.fieldId || 'id'; // name of the field containing id
5268    this._type = {}; // internal field types (NOTE: this can differ from this._options.type)
5269
5270    // all variants of a Date are internally stored as Date, so we can convert
5271    // from everything to everything (also from ISODate to Number for example)
5272    if (this._options.type) {
5273      for (var field in this._options.type) {
5274        if (this._options.type.hasOwnProperty(field)) {
5275          var value = this._options.type[field];
5276          if (value == 'Date' || value == 'ISODate' || value == 'ASPDate') {
5277            this._type[field] = 'Date';
5278          } else {
5279            this._type[field] = value;
5280          }
5281        }
5282      }
5283    }
5284
5285    // TODO: deprecated since version 1.1.1 (or 2.0.0?)
5286    if (this._options.convert) {
5287      throw new Error('Option "convert" is deprecated. Use "type" instead.');
5288    }
5289
5290    this._subscribers = {}; // event subscribers
5291
5292    // add initial data when provided
5293    if (data) {
5294      this.add(data);
5295    }
5296
5297    this.setOptions(options);
5298  }
5299
5300  /**
5301   * @param {Object} [options]   Available options:
5302   *                             {Object} queue   Queue changes to the DataSet,
5303   *                                              flush them all at once.
5304   *                                              Queue options:
5305   *                                              - {number} delay  Delay in ms, null by default
5306   *                                              - {number} max    Maximum number of entries in the queue, Infinity by default
5307   * @param options
5308   */
5309  DataSet.prototype.setOptions = function (options) {
5310    if (options && options.queue !== undefined) {
5311      if (options.queue === false) {
5312        // delete queue if loaded
5313        if (this._queue) {
5314          this._queue.destroy();
5315          delete this._queue;
5316        }
5317      } else {
5318        // create queue and update its options
5319        if (!this._queue) {
5320          this._queue = Queue.extend(this, {
5321            replace: ['add', 'update', 'remove']
5322          });
5323        }
5324
5325        if (typeof options.queue === 'object') {
5326          this._queue.setOptions(options.queue);
5327        }
5328      }
5329    }
5330  };
5331
5332  /**
5333   * Subscribe to an event, add an event listener
5334   * @param {String} event        Event name. Available events: 'put', 'update',
5335   *                              'remove'
5336   * @param {function} callback   Callback method. Called with three parameters:
5337   *                                  {String} event
5338   *                                  {Object | null} params
5339   *                                  {String | Number} senderId
5340   */
5341  DataSet.prototype.on = function (event, callback) {
5342    var subscribers = this._subscribers[event];
5343    if (!subscribers) {
5344      subscribers = [];
5345      this._subscribers[event] = subscribers;
5346    }
5347
5348    subscribers.push({
5349      callback: callback
5350    });
5351  };
5352
5353  // TODO: remove this deprecated function some day (replaced with `on` since version 0.5, deprecated since v4.0)
5354  DataSet.prototype.subscribe = function () {
5355    throw new Error('DataSet.subscribe is deprecated. Use DataSet.on instead.');
5356  };
5357
5358  /**
5359   * Unsubscribe from an event, remove an event listener
5360   * @param {String} event
5361   * @param {function} callback
5362   */
5363  DataSet.prototype.off = function (event, callback) {
5364    var subscribers = this._subscribers[event];
5365    if (subscribers) {
5366      this._subscribers[event] = subscribers.filter(function (listener) {
5367        return listener.callback != callback;
5368      });
5369    }
5370  };
5371
5372  // TODO: remove this deprecated function some day (replaced with `on` since version 0.5, deprecated since v4.0)
5373  DataSet.prototype.unsubscribe = function () {
5374    throw new Error('DataSet.unsubscribe is deprecated. Use DataSet.off instead.');
5375  };
5376
5377  /**
5378   * Trigger an event
5379   * @param {String} event
5380   * @param {Object | null} params
5381   * @param {String} [senderId]       Optional id of the sender.
5382   * @private
5383   */
5384  DataSet.prototype._trigger = function (event, params, senderId) {
5385    if (event == '*') {
5386      throw new Error('Cannot trigger event *');
5387    }
5388
5389    var subscribers = [];
5390    if (event in this._subscribers) {
5391      subscribers = subscribers.concat(this._subscribers[event]);
5392    }
5393    if ('*' in this._subscribers) {
5394      subscribers = subscribers.concat(this._subscribers['*']);
5395    }
5396
5397    for (var i = 0; i < subscribers.length; i++) {
5398      var subscriber = subscribers[i];
5399      if (subscriber.callback) {
5400        subscriber.callback(event, params, senderId || null);
5401      }
5402    }
5403  };
5404
5405  /**
5406   * Add data.
5407   * Adding an item will fail when there already is an item with the same id.
5408   * @param {Object | Array} data
5409   * @param {String} [senderId] Optional sender id
5410   * @return {Array} addedIds      Array with the ids of the added items
5411   */
5412  DataSet.prototype.add = function (data, senderId) {
5413    var addedIds = [],
5414        id,
5415        me = this;
5416
5417    if (Array.isArray(data)) {
5418      // Array
5419      for (var i = 0, len = data.length; i < len; i++) {
5420        id = me._addItem(data[i]);
5421        addedIds.push(id);
5422      }
5423    } else if (data instanceof Object) {
5424      // Single item
5425      id = me._addItem(data);
5426      addedIds.push(id);
5427    } else {
5428      throw new Error('Unknown dataType');
5429    }
5430
5431    if (addedIds.length) {
5432      this._trigger('add', { items: addedIds }, senderId);
5433    }
5434
5435    return addedIds;
5436  };
5437
5438  /**
5439   * Update existing items. When an item does not exist, it will be created
5440   * @param {Object | Array} data
5441   * @param {String} [senderId] Optional sender id
5442   * @return {Array} updatedIds     The ids of the added or updated items
5443   */
5444  DataSet.prototype.update = function (data, senderId) {
5445    var addedIds = [];
5446    var updatedIds = [];
5447    var updatedData = [];
5448    var me = this;
5449    var fieldId = me._fieldId;
5450
5451    var addOrUpdate = function addOrUpdate(item) {
5452      var id = item[fieldId];
5453      if (me._data[id]) {
5454        // update item
5455        id = me._updateItem(item);
5456        updatedIds.push(id);
5457        updatedData.push(item);
5458      } else {
5459        // add new item
5460        id = me._addItem(item);
5461        addedIds.push(id);
5462      }
5463    };
5464
5465    if (Array.isArray(data)) {
5466      // Array
5467      for (var i = 0, len = data.length; i < len; i++) {
5468        addOrUpdate(data[i]);
5469      }
5470    } else if (data instanceof Object) {
5471      // Single item
5472      addOrUpdate(data);
5473    } else {
5474      throw new Error('Unknown dataType');
5475    }
5476
5477    if (addedIds.length) {
5478      this._trigger('add', { items: addedIds }, senderId);
5479    }
5480    if (updatedIds.length) {
5481      this._trigger('update', { items: updatedIds, data: updatedData }, senderId);
5482    }
5483
5484    return addedIds.concat(updatedIds);
5485  };
5486
5487  /**
5488   * Get a data item or multiple items.
5489   *
5490   * Usage:
5491   *
5492   *     get()
5493   *     get(options: Object)
5494   *
5495   *     get(id: Number | String)
5496   *     get(id: Number | String, options: Object)
5497   *
5498   *     get(ids: Number[] | String[])
5499   *     get(ids: Number[] | String[], options: Object)
5500   *
5501   * Where:
5502   *
5503   * {Number | String} id         The id of an item
5504   * {Number[] | String{}} ids    An array with ids of items
5505   * {Object} options             An Object with options. Available options:
5506   * {String} [returnType]        Type of data to be returned.
5507   *                              Can be 'Array' (default) or 'Object'.
5508   * {Object.<String, String>} [type]
5509   * {String[]} [fields]          field names to be returned
5510   * {function} [filter]          filter items
5511   * {String | function} [order]  Order the items by a field name or custom sort function.
5512   * @throws Error
5513   */
5514  DataSet.prototype.get = function (args) {
5515    var me = this;
5516
5517    // parse the arguments
5518    var id, ids, options;
5519    var firstType = util.getType(arguments[0]);
5520    if (firstType == 'String' || firstType == 'Number') {
5521      // get(id [, options])
5522      id = arguments[0];
5523      options = arguments[1];
5524    } else if (firstType == 'Array') {
5525      // get(ids [, options])
5526      ids = arguments[0];
5527      options = arguments[1];
5528    } else {
5529      // get([, options])
5530      options = arguments[0];
5531    }
5532
5533    // determine the return type
5534    var returnType;
5535    if (options && options.returnType) {
5536      var allowedValues = ['Array', 'Object'];
5537      returnType = allowedValues.indexOf(options.returnType) == -1 ? 'Array' : options.returnType;
5538    } else {
5539      returnType = 'Array';
5540    }
5541
5542    // build options
5543    var type = options && options.type || this._options.type;
5544    var filter = options && options.filter;
5545    var items = [],
5546        item,
5547        itemId,
5548        i,
5549        len;
5550
5551    // convert items
5552    if (id != undefined) {
5553      // return a single item
5554      item = me._getItem(id, type);
5555      if (filter && !filter(item)) {
5556        item = null;
5557      }
5558    } else if (ids != undefined) {
5559      // return a subset of items
5560      for (i = 0, len = ids.length; i < len; i++) {
5561        item = me._getItem(ids[i], type);
5562        if (!filter || filter(item)) {
5563          items.push(item);
5564        }
5565      }
5566    } else {
5567      // return all items
5568      for (itemId in this._data) {
5569        if (this._data.hasOwnProperty(itemId)) {
5570          item = me._getItem(itemId, type);
5571          if (!filter || filter(item)) {
5572            items.push(item);
5573          }
5574        }
5575      }
5576    }
5577
5578    // order the results
5579    if (options && options.order && id == undefined) {
5580      this._sort(items, options.order);
5581    }
5582
5583    // filter fields of the items
5584    if (options && options.fields) {
5585      var fields = options.fields;
5586      if (id != undefined) {
5587        item = this._filterFields(item, fields);
5588      } else {
5589        for (i = 0, len = items.length; i < len; i++) {
5590          items[i] = this._filterFields(items[i], fields);
5591        }
5592      }
5593    }
5594
5595    // return the results
5596    if (returnType == 'Object') {
5597      var result = {};
5598      for (i = 0; i < items.length; i++) {
5599        result[items[i].id] = items[i];
5600      }
5601      return result;
5602    } else {
5603      if (id != undefined) {
5604        // a single item
5605        return item;
5606      } else {
5607        // just return our array
5608        return items;
5609      }
5610    }
5611  };
5612
5613  /**
5614   * Get ids of all items or from a filtered set of items.
5615   * @param {Object} [options]    An Object with options. Available options:
5616   *                              {function} [filter] filter items
5617   *                              {String | function} [order] Order the items by
5618   *                                  a field name or custom sort function.
5619   * @return {Array} ids
5620   */
5621  DataSet.prototype.getIds = function (options) {
5622    var data = this._data,
5623        filter = options && options.filter,
5624        order = options && options.order,
5625        type = options && options.type || this._options.type,
5626        i,
5627        len,
5628        id,
5629        item,
5630        items,
5631        ids = [];
5632
5633    if (filter) {
5634      // get filtered items
5635      if (order) {
5636        // create ordered list
5637        items = [];
5638        for (id in data) {
5639          if (data.hasOwnProperty(id)) {
5640            item = this._getItem(id, type);
5641            if (filter(item)) {
5642              items.push(item);
5643            }
5644          }
5645        }
5646
5647        this._sort(items, order);
5648
5649        for (i = 0, len = items.length; i < len; i++) {
5650          ids[i] = items[i][this._fieldId];
5651        }
5652      } else {
5653        // create unordered list
5654        for (id in data) {
5655          if (data.hasOwnProperty(id)) {
5656            item = this._getItem(id, type);
5657            if (filter(item)) {
5658              ids.push(item[this._fieldId]);
5659            }
5660          }
5661        }
5662      }
5663    } else {
5664      // get all items
5665      if (order) {
5666        // create an ordered list
5667        items = [];
5668        for (id in data) {
5669          if (data.hasOwnProperty(id)) {
5670            items.push(data[id]);
5671          }
5672        }
5673
5674        this._sort(items, order);
5675
5676        for (i = 0, len = items.length; i < len; i++) {
5677          ids[i] = items[i][this._fieldId];
5678        }
5679      } else {
5680        // create unordered list
5681        for (id in data) {
5682          if (data.hasOwnProperty(id)) {
5683            item = data[id];
5684            ids.push(item[this._fieldId]);
5685          }
5686        }
5687      }
5688    }
5689
5690    return ids;
5691  };
5692
5693  /**
5694   * Returns the DataSet itself. Is overwritten for example by the DataView,
5695   * which returns the DataSet it is connected to instead.
5696   */
5697  DataSet.prototype.getDataSet = function () {
5698    return this;
5699  };
5700
5701  /**
5702   * Execute a callback function for every item in the dataset.
5703   * @param {function} callback
5704   * @param {Object} [options]    Available options:
5705   *                              {Object.<String, String>} [type]
5706   *                              {String[]} [fields] filter fields
5707   *                              {function} [filter] filter items
5708   *                              {String | function} [order] Order the items by
5709   *                                  a field name or custom sort function.
5710   */
5711  DataSet.prototype.forEach = function (callback, options) {
5712    var filter = options && options.filter,
5713        type = options && options.type || this._options.type,
5714        data = this._data,
5715        item,
5716        id;
5717
5718    if (options && options.order) {
5719      // execute forEach on ordered list
5720      var items = this.get(options);
5721
5722      for (var i = 0, len = items.length; i < len; i++) {
5723        item = items[i];
5724        id = item[this._fieldId];
5725        callback(item, id);
5726      }
5727    } else {
5728      // unordered
5729      for (id in data) {
5730        if (data.hasOwnProperty(id)) {
5731          item = this._getItem(id, type);
5732          if (!filter || filter(item)) {
5733            callback(item, id);
5734          }
5735        }
5736      }
5737    }
5738  };
5739
5740  /**
5741   * Map every item in the dataset.
5742   * @param {function} callback
5743   * @param {Object} [options]    Available options:
5744   *                              {Object.<String, String>} [type]
5745   *                              {String[]} [fields] filter fields
5746   *                              {function} [filter] filter items
5747   *                              {String | function} [order] Order the items by
5748   *                                  a field name or custom sort function.
5749   * @return {Object[]} mappedItems
5750   */
5751  DataSet.prototype.map = function (callback, options) {
5752    var filter = options && options.filter,
5753        type = options && options.type || this._options.type,
5754        mappedItems = [],
5755        data = this._data,
5756        item;
5757
5758    // convert and filter items
5759    for (var id in data) {
5760      if (data.hasOwnProperty(id)) {
5761        item = this._getItem(id, type);
5762        if (!filter || filter(item)) {
5763          mappedItems.push(callback(item, id));
5764        }
5765      }
5766    }
5767
5768    // order items
5769    if (options && options.order) {
5770      this._sort(mappedItems, options.order);
5771    }
5772
5773    return mappedItems;
5774  };
5775
5776  /**
5777   * Filter the fields of an item
5778   * @param {Object | null} item
5779   * @param {String[]} fields     Field names
5780   * @return {Object | null} filteredItem or null if no item is provided
5781   * @private
5782   */
5783  DataSet.prototype._filterFields = function (item, fields) {
5784    if (!item) {
5785      // item is null
5786      return item;
5787    }
5788
5789    var filteredItem = {};
5790
5791    if (Array.isArray(fields)) {
5792      for (var field in item) {
5793        if (item.hasOwnProperty(field) && fields.indexOf(field) != -1) {
5794          filteredItem[field] = item[field];
5795        }
5796      }
5797    } else {
5798      for (var field in item) {
5799        if (item.hasOwnProperty(field) && fields.hasOwnProperty(field)) {
5800          filteredItem[fields[field]] = item[field];
5801        }
5802      }
5803    }
5804
5805    return filteredItem;
5806  };
5807
5808  /**
5809   * Sort the provided array with items
5810   * @param {Object[]} items
5811   * @param {String | function} order      A field name or custom sort function.
5812   * @private
5813   */
5814  DataSet.prototype._sort = function (items, order) {
5815    if (util.isString(order)) {
5816      // order by provided field name
5817      var name = order; // field name
5818      items.sort(function (a, b) {
5819        var av = a[name];
5820        var bv = b[name];
5821        return av > bv ? 1 : av < bv ? -1 : 0;
5822      });
5823    } else if (typeof order === 'function') {
5824      // order by sort function
5825      items.sort(order);
5826    }
5827    // TODO: extend order by an Object {field:String, direction:String}
5828    //       where direction can be 'asc' or 'desc'
5829    else {
5830      throw new TypeError('Order must be a function or a string');
5831    }
5832  };
5833
5834  /**
5835   * Remove an object by pointer or by id
5836   * @param {String | Number | Object | Array} id Object or id, or an array with
5837   *                                              objects or ids to be removed
5838   * @param {String} [senderId] Optional sender id
5839   * @return {Array} removedIds
5840   */
5841  DataSet.prototype.remove = function (id, senderId) {
5842    var removedIds = [],
5843        i,
5844        len,
5845        removedId;
5846
5847    if (Array.isArray(id)) {
5848      for (i = 0, len = id.length; i < len; i++) {
5849        removedId = this._remove(id[i]);
5850        if (removedId != null) {
5851          removedIds.push(removedId);
5852        }
5853      }
5854    } else {
5855      removedId = this._remove(id);
5856      if (removedId != null) {
5857        removedIds.push(removedId);
5858      }
5859    }
5860
5861    if (removedIds.length) {
5862      this._trigger('remove', { items: removedIds }, senderId);
5863    }
5864
5865    return removedIds;
5866  };
5867
5868  /**
5869   * Remove an item by its id
5870   * @param {Number | String | Object} id   id or item
5871   * @returns {Number | String | null} id
5872   * @private
5873   */
5874  DataSet.prototype._remove = function (id) {
5875    if (util.isNumber(id) || util.isString(id)) {
5876      if (this._data[id]) {
5877        delete this._data[id];
5878        this.length--;
5879        return id;
5880      }
5881    } else if (id instanceof Object) {
5882      var itemId = id[this._fieldId];
5883      if (itemId && this._data[itemId]) {
5884        delete this._data[itemId];
5885        this.length--;
5886        return itemId;
5887      }
5888    }
5889    return null;
5890  };
5891
5892  /**
5893   * Clear the data
5894   * @param {String} [senderId] Optional sender id
5895   * @return {Array} removedIds    The ids of all removed items
5896   */
5897  DataSet.prototype.clear = function (senderId) {
5898    var ids = Object.keys(this._data);
5899
5900    this._data = {};
5901    this.length = 0;
5902
5903    this._trigger('remove', { items: ids }, senderId);
5904
5905    return ids;
5906  };
5907
5908  /**
5909   * Find the item with maximum value of a specified field
5910   * @param {String} field
5911   * @return {Object | null} item  Item containing max value, or null if no items
5912   */
5913  DataSet.prototype.max = function (field) {
5914    var data = this._data,
5915        max = null,
5916        maxField = null;
5917
5918    for (var id in data) {
5919      if (data.hasOwnProperty(id)) {
5920        var item = data[id];
5921        var itemField = item[field];
5922        if (itemField != null && (!max || itemField > maxField)) {
5923          max = item;
5924          maxField = itemField;
5925        }
5926      }
5927    }
5928
5929    return max;
5930  };
5931
5932  /**
5933   * Find the item with minimum value of a specified field
5934   * @param {String} field
5935   * @return {Object | null} item  Item containing max value, or null if no items
5936   */
5937  DataSet.prototype.min = function (field) {
5938    var data = this._data,
5939        min = null,
5940        minField = null;
5941
5942    for (var id in data) {
5943      if (data.hasOwnProperty(id)) {
5944        var item = data[id];
5945        var itemField = item[field];
5946        if (itemField != null && (!min || itemField < minField)) {
5947          min = item;
5948          minField = itemField;
5949        }
5950      }
5951    }
5952
5953    return min;
5954  };
5955
5956  /**
5957   * Find all distinct values of a specified field
5958   * @param {String} field
5959   * @return {Array} values  Array containing all distinct values. If data items
5960   *                         do not contain the specified field are ignored.
5961   *                         The returned array is unordered.
5962   */
5963  DataSet.prototype.distinct = function (field) {
5964    var data = this._data;
5965    var values = [];
5966    var fieldType = this._options.type && this._options.type[field] || null;
5967    var count = 0;
5968    var i;
5969
5970    for (var prop in data) {
5971      if (data.hasOwnProperty(prop)) {
5972        var item = data[prop];
5973        var value = item[field];
5974        var exists = false;
5975        for (i = 0; i < count; i++) {
5976          if (values[i] == value) {
5977            exists = true;
5978            break;
5979          }
5980        }
5981        if (!exists && value !== undefined) {
5982          values[count] = value;
5983          count++;
5984        }
5985      }
5986    }
5987
5988    if (fieldType) {
5989      for (i = 0; i < values.length; i++) {
5990        values[i] = util.convert(values[i], fieldType);
5991      }
5992    }
5993
5994    return values;
5995  };
5996
5997  /**
5998   * Add a single item. Will fail when an item with the same id already exists.
5999   * @param {Object} item
6000   * @return {String} id
6001   * @private
6002   */
6003  DataSet.prototype._addItem = function (item) {
6004    var id = item[this._fieldId];
6005
6006    if (id != undefined) {
6007      // check whether this id is already taken
6008      if (this._data[id]) {
6009        // item already exists
6010        throw new Error('Cannot add item: item with id ' + id + ' already exists');
6011      }
6012    } else {
6013      // generate an id
6014      id = util.randomUUID();
6015      item[this._fieldId] = id;
6016    }
6017
6018    var d = {};
6019    for (var field in item) {
6020      if (item.hasOwnProperty(field)) {
6021        var fieldType = this._type[field]; // type may be undefined
6022        d[field] = util.convert(item[field], fieldType);
6023      }
6024    }
6025    this._data[id] = d;
6026    this.length++;
6027
6028    return id;
6029  };
6030
6031  /**
6032   * Get an item. Fields can be converted to a specific type
6033   * @param {String} id
6034   * @param {Object.<String, String>} [types]  field types to convert
6035   * @return {Object | null} item
6036   * @private
6037   */
6038  DataSet.prototype._getItem = function (id, types) {
6039    var field, value;
6040
6041    // get the item from the dataset
6042    var raw = this._data[id];
6043    if (!raw) {
6044      return null;
6045    }
6046
6047    // convert the items field types
6048    var converted = {};
6049    if (types) {
6050      for (field in raw) {
6051        if (raw.hasOwnProperty(field)) {
6052          value = raw[field];
6053          converted[field] = util.convert(value, types[field]);
6054        }
6055      }
6056    } else {
6057      // no field types specified, no converting needed
6058      for (field in raw) {
6059        if (raw.hasOwnProperty(field)) {
6060          value = raw[field];
6061          converted[field] = value;
6062        }
6063      }
6064    }
6065    return converted;
6066  };
6067
6068  /**
6069   * Update a single item: merge with existing item.
6070   * Will fail when the item has no id, or when there does not exist an item
6071   * with the same id.
6072   * @param {Object} item
6073   * @return {String} id
6074   * @private
6075   */
6076  DataSet.prototype._updateItem = function (item) {
6077    var id = item[this._fieldId];
6078    if (id == undefined) {
6079      throw new Error('Cannot update item: item has no id (item: ' + JSON.stringify(item) + ')');
6080    }
6081    var d = this._data[id];
6082    if (!d) {
6083      // item doesn't exist
6084      throw new Error('Cannot update item: no item with id ' + id + ' found');
6085    }
6086
6087    // merge with current item
6088    for (var field in item) {
6089      if (item.hasOwnProperty(field)) {
6090        var fieldType = this._type[field]; // type may be undefined
6091        d[field] = util.convert(item[field], fieldType);
6092      }
6093    }
6094
6095    return id;
6096  };
6097
6098  module.exports = DataSet;
6099
6100/***/ },
6101/* 9 */
6102/***/ function(module, exports) {
6103
6104  /**
6105   * A queue
6106   * @param {Object} options
6107   *            Available options:
6108   *            - delay: number    When provided, the queue will be flushed
6109   *                               automatically after an inactivity of this delay
6110   *                               in milliseconds.
6111   *                               Default value is null.
6112   *            - max: number      When the queue exceeds the given maximum number
6113   *                               of entries, the queue is flushed automatically.
6114   *                               Default value of max is Infinity.
6115   * @constructor
6116   */
6117  'use strict';
6118
6119  function Queue(options) {
6120    // options
6121    this.delay = null;
6122    this.max = Infinity;
6123
6124    // properties
6125    this._queue = [];
6126    this._timeout = null;
6127    this._extended = null;
6128
6129    this.setOptions(options);
6130  }
6131
6132  /**
6133   * Update the configuration of the queue
6134   * @param {Object} options
6135   *            Available options:
6136   *            - delay: number    When provided, the queue will be flushed
6137   *                               automatically after an inactivity of this delay
6138   *                               in milliseconds.
6139   *                               Default value is null.
6140   *            - max: number      When the queue exceeds the given maximum number
6141   *                               of entries, the queue is flushed automatically.
6142   *                               Default value of max is Infinity.
6143   * @param options
6144   */
6145  Queue.prototype.setOptions = function (options) {
6146    if (options && typeof options.delay !== 'undefined') {
6147      this.delay = options.delay;
6148    }
6149    if (options && typeof options.max !== 'undefined') {
6150      this.max = options.max;
6151    }
6152
6153    this._flushIfNeeded();
6154  };
6155
6156  /**
6157   * Extend an object with queuing functionality.
6158   * The object will be extended with a function flush, and the methods provided
6159   * in options.replace will be replaced with queued ones.
6160   * @param {Object} object
6161   * @param {Object} options
6162   *            Available options:
6163   *            - replace: Array.<string>
6164   *                               A list with method names of the methods
6165   *                               on the object to be replaced with queued ones.
6166   *            - delay: number    When provided, the queue will be flushed
6167   *                               automatically after an inactivity of this delay
6168   *                               in milliseconds.
6169   *                               Default value is null.
6170   *            - max: number      When the queue exceeds the given maximum number
6171   *                               of entries, the queue is flushed automatically.
6172   *                               Default value of max is Infinity.
6173   * @return {Queue} Returns the created queue
6174   */
6175  Queue.extend = function (object, options) {
6176    var queue = new Queue(options);
6177
6178    if (object.flush !== undefined) {
6179      throw new Error('Target object already has a property flush');
6180    }
6181    object.flush = function () {
6182      queue.flush();
6183    };
6184
6185    var methods = [{
6186      name: 'flush',
6187      original: undefined
6188    }];
6189
6190    if (options && options.replace) {
6191      for (var i = 0; i < options.replace.length; i++) {
6192        var name = options.replace[i];
6193        methods.push({
6194          name: name,
6195          original: object[name]
6196        });
6197        queue.replace(object, name);
6198      }
6199    }
6200
6201    queue._extended = {
6202      object: object,
6203      methods: methods
6204    };
6205
6206    return queue;
6207  };
6208
6209  /**
6210   * Destroy the queue. The queue will first flush all queued actions, and in
6211   * case it has extended an object, will restore the original object.
6212   */
6213  Queue.prototype.destroy = function () {
6214    this.flush();
6215
6216    if (this._extended) {
6217      var object = this._extended.object;
6218      var methods = this._extended.methods;
6219      for (var i = 0; i < methods.length; i++) {
6220        var method = methods[i];
6221        if (method.original) {
6222          object[method.name] = method.original;
6223        } else {
6224          delete object[method.name];
6225        }
6226      }
6227      this._extended = null;
6228    }
6229  };
6230
6231  /**
6232   * Replace a method on an object with a queued version
6233   * @param {Object} object   Object having the method
6234   * @param {string} method   The method name
6235   */
6236  Queue.prototype.replace = function (object, method) {
6237    var me = this;
6238    var original = object[method];
6239    if (!original) {
6240      throw new Error('Method ' + method + ' undefined');
6241    }
6242
6243    object[method] = function () {
6244      // create an Array with the arguments
6245      var args = [];
6246      for (var i = 0; i < arguments.length; i++) {
6247        args[i] = arguments[i];
6248      }
6249
6250      // add this call to the queue
6251      me.queue({
6252        args: args,
6253        fn: original,
6254        context: this
6255      });
6256    };
6257  };
6258
6259  /**
6260   * Queue a call
6261   * @param {function | {fn: function, args: Array} | {fn: function, args: Array, context: Object}} entry
6262   */
6263  Queue.prototype.queue = function (entry) {
6264    if (typeof entry === 'function') {
6265      this._queue.push({ fn: entry });
6266    } else {
6267      this._queue.push(entry);
6268    }
6269
6270    this._flushIfNeeded();
6271  };
6272
6273  /**
6274   * Check whether the queue needs to be flushed
6275   * @private
6276   */
6277  Queue.prototype._flushIfNeeded = function () {
6278    // flush when the maximum is exceeded.
6279    if (this._queue.length > this.max) {
6280      this.flush();
6281    }
6282
6283    // flush after a period of inactivity when a delay is configured
6284    clearTimeout(this._timeout);
6285    if (this.queue.length > 0 && typeof this.delay === 'number') {
6286      var me = this;
6287      this._timeout = setTimeout(function () {
6288        me.flush();
6289      }, this.delay);
6290    }
6291  };
6292
6293  /**
6294   * Flush all queued calls
6295   */
6296  Queue.prototype.flush = function () {
6297    while (this._queue.length > 0) {
6298      var entry = this._queue.shift();
6299      entry.fn.apply(entry.context || entry.fn, entry.args || []);
6300    }
6301  };
6302
6303  module.exports = Queue;
6304
6305/***/ },
6306/* 10 */
6307/***/ function(module, exports, __webpack_require__) {
6308
6309  'use strict';
6310
6311  var util = __webpack_require__(1);
6312  var DataSet = __webpack_require__(8);
6313
6314  /**
6315   * DataView
6316   *
6317   * a dataview offers a filtered view on a dataset or an other dataview.
6318   *
6319   * @param {DataSet | DataView} data
6320   * @param {Object} [options]   Available options: see method get
6321   *
6322   * @constructor DataView
6323   */
6324  function DataView(data, options) {
6325    this._data = null;
6326    this._ids = {}; // ids of the items currently in memory (just contains a boolean true)
6327    this.length = 0; // number of items in the DataView
6328    this._options = options || {};
6329    this._fieldId = 'id'; // name of the field containing id
6330    this._subscribers = {}; // event subscribers
6331
6332    var me = this;
6333    this.listener = function () {
6334      me._onEvent.apply(me, arguments);
6335    };
6336
6337    this.setData(data);
6338  }
6339
6340  // TODO: implement a function .config() to dynamically update things like configured filter
6341  // and trigger changes accordingly
6342
6343  /**
6344   * Set a data source for the view
6345   * @param {DataSet | DataView} data
6346   */
6347  DataView.prototype.setData = function (data) {
6348    var ids, i, len;
6349
6350    if (this._data) {
6351      // unsubscribe from current dataset
6352      if (this._data.off) {
6353        this._data.off('*', this.listener);
6354      }
6355
6356      // trigger a remove of all items in memory
6357      ids = [];
6358      for (var id in this._ids) {
6359        if (this._ids.hasOwnProperty(id)) {
6360          ids.push(id);
6361        }
6362      }
6363      this._ids = {};
6364      this.length = 0;
6365      this._trigger('remove', { items: ids });
6366    }
6367
6368    this._data = data;
6369
6370    if (this._data) {
6371      // update fieldId
6372      this._fieldId = this._options.fieldId || this._data && this._data.options && this._data.options.fieldId || 'id';
6373
6374      // trigger an add of all added items
6375      ids = this._data.getIds({ filter: this._options && this._options.filter });
6376      for (i = 0, len = ids.length; i < len; i++) {
6377        id = ids[i];
6378        this._ids[id] = true;
6379      }
6380      this.length = ids.length;
6381      this._trigger('add', { items: ids });
6382
6383      // subscribe to new dataset
6384      if (this._data.on) {
6385        this._data.on('*', this.listener);
6386      }
6387    }
6388  };
6389
6390  /**
6391   * Refresh the DataView. Useful when the DataView has a filter function
6392   * containing a variable parameter.
6393   */
6394  DataView.prototype.refresh = function () {
6395    var id;
6396    var ids = this._data.getIds({ filter: this._options && this._options.filter });
6397    var newIds = {};
6398    var added = [];
6399    var removed = [];
6400
6401    // check for additions
6402    for (var i = 0; i < ids.length; i++) {
6403      id = ids[i];
6404      newIds[id] = true;
6405      if (!this._ids[id]) {
6406        added.push(id);
6407        this._ids[id] = true;
6408        this.length++;
6409      }
6410    }
6411
6412    // check for removals
6413    for (id in this._ids) {
6414      if (this._ids.hasOwnProperty(id)) {
6415        if (!newIds[id]) {
6416          removed.push(id);
6417          delete this._ids[id];
6418          this.length--;
6419        }
6420      }
6421    }
6422
6423    // trigger events
6424    if (added.length) {
6425      this._trigger('add', { items: added });
6426    }
6427    if (removed.length) {
6428      this._trigger('remove', { items: removed });
6429    }
6430  };
6431
6432  /**
6433   * Get data from the data view
6434   *
6435   * Usage:
6436   *
6437   *     get()
6438   *     get(options: Object)
6439   *     get(options: Object, data: Array | DataTable)
6440   *
6441   *     get(id: Number)
6442   *     get(id: Number, options: Object)
6443   *     get(id: Number, options: Object, data: Array | DataTable)
6444   *
6445   *     get(ids: Number[])
6446   *     get(ids: Number[], options: Object)
6447   *     get(ids: Number[], options: Object, data: Array | DataTable)
6448   *
6449   * Where:
6450   *
6451   * {Number | String} id         The id of an item
6452   * {Number[] | String{}} ids    An array with ids of items
6453   * {Object} options             An Object with options. Available options:
6454   *                              {String} [type] Type of data to be returned. Can
6455   *                                              be 'DataTable' or 'Array' (default)
6456   *                              {Object.<String, String>} [convert]
6457   *                              {String[]} [fields] field names to be returned
6458   *                              {function} [filter] filter items
6459   *                              {String | function} [order] Order the items by
6460   *                                  a field name or custom sort function.
6461   * {Array | DataTable} [data]   If provided, items will be appended to this
6462   *                              array or table. Required in case of Google
6463   *                              DataTable.
6464   * @param args
6465   */
6466  DataView.prototype.get = function (args) {
6467    var me = this;
6468
6469    // parse the arguments
6470    var ids, options, data;
6471    var firstType = util.getType(arguments[0]);
6472    if (firstType == 'String' || firstType == 'Number' || firstType == 'Array') {
6473      // get(id(s) [, options] [, data])
6474      ids = arguments[0]; // can be a single id or an array with ids
6475      options = arguments[1];
6476      data = arguments[2];
6477    } else {
6478      // get([, options] [, data])
6479      options = arguments[0];
6480      data = arguments[1];
6481    }
6482
6483    // extend the options with the default options and provided options
6484    var viewOptions = util.extend({}, this._options, options);
6485
6486    // create a combined filter method when needed
6487    if (this._options.filter && options && options.filter) {
6488      viewOptions.filter = function (item) {
6489        return me._options.filter(item) && options.filter(item);
6490      };
6491    }
6492
6493    // build up the call to the linked data set
6494    var getArguments = [];
6495    if (ids != undefined) {
6496      getArguments.push(ids);
6497    }
6498    getArguments.push(viewOptions);
6499    getArguments.push(data);
6500
6501    return this._data && this._data.get.apply(this._data, getArguments);
6502  };
6503
6504  /**
6505   * Get ids of all items or from a filtered set of items.
6506   * @param {Object} [options]    An Object with options. Available options:
6507   *                              {function} [filter] filter items
6508   *                              {String | function} [order] Order the items by
6509   *                                  a field name or custom sort function.
6510   * @return {Array} ids
6511   */
6512  DataView.prototype.getIds = function (options) {
6513    var ids;
6514
6515    if (this._data) {
6516      var defaultFilter = this._options.filter;
6517      var filter;
6518
6519      if (options && options.filter) {
6520        if (defaultFilter) {
6521          filter = function (item) {
6522            return defaultFilter(item) && options.filter(item);
6523          };
6524        } else {
6525          filter = options.filter;
6526        }
6527      } else {
6528        filter = defaultFilter;
6529      }
6530
6531      ids = this._data.getIds({
6532        filter: filter,
6533        order: options && options.order
6534      });
6535    } else {
6536      ids = [];
6537    }
6538
6539    return ids;
6540  };
6541
6542  /**
6543   * Get the DataSet to which this DataView is connected. In case there is a chain
6544   * of multiple DataViews, the root DataSet of this chain is returned.
6545   * @return {DataSet} dataSet
6546   */
6547  DataView.prototype.getDataSet = function () {
6548    var dataSet = this;
6549    while (dataSet instanceof DataView) {
6550      dataSet = dataSet._data;
6551    }
6552    return dataSet || null;
6553  };
6554
6555  /**
6556   * Event listener. Will propagate all events from the connected data set to
6557   * the subscribers of the DataView, but will filter the items and only trigger
6558   * when there are changes in the filtered data set.
6559   * @param {String} event
6560   * @param {Object | null} params
6561   * @param {String} senderId
6562   * @private
6563   */
6564  DataView.prototype._onEvent = function (event, params, senderId) {
6565    var i, len, id, item;
6566    var ids = params && params.items;
6567    var data = this._data;
6568    var updatedData = [];
6569    var added = [];
6570    var updated = [];
6571    var removed = [];
6572
6573    if (ids && data) {
6574      switch (event) {
6575        case 'add':
6576          // filter the ids of the added items
6577          for (i = 0, len = ids.length; i < len; i++) {
6578            id = ids[i];
6579            item = this.get(id);
6580            if (item) {
6581              this._ids[id] = true;
6582              added.push(id);
6583            }
6584          }
6585
6586          break;
6587
6588        case 'update':
6589          // determine the event from the views viewpoint: an updated
6590          // item can be added, updated, or removed from this view.
6591          for (i = 0, len = ids.length; i < len; i++) {
6592            id = ids[i];
6593            item = this.get(id);
6594
6595            if (item) {
6596              if (this._ids[id]) {
6597                updated.push(id);
6598                updatedData.push(params.data[i]);
6599              } else {
6600                this._ids[id] = true;
6601                added.push(id);
6602              }
6603            } else {
6604              if (this._ids[id]) {
6605                delete this._ids[id];
6606                removed.push(id);
6607              } else {}
6608            }
6609          }
6610
6611          break;
6612
6613        case 'remove':
6614          // filter the ids of the removed items
6615          for (i = 0, len = ids.length; i < len; i++) {
6616            id = ids[i];
6617            if (this._ids[id]) {
6618              delete this._ids[id];
6619              removed.push(id);
6620            }
6621          }
6622
6623          break;
6624      }
6625
6626      this.length += added.length - removed.length;
6627
6628      if (added.length) {
6629        this._trigger('add', { items: added }, senderId);
6630      }
6631      if (updated.length) {
6632        this._trigger('update', { items: updated, data: updatedData }, senderId);
6633      }
6634      if (removed.length) {
6635        this._trigger('remove', { items: removed }, senderId);
6636      }
6637    }
6638  };
6639
6640  // copy subscription functionality from DataSet
6641  DataView.prototype.on = DataSet.prototype.on;
6642  DataView.prototype.off = DataSet.prototype.off;
6643  DataView.prototype._trigger = DataSet.prototype._trigger;
6644
6645  // TODO: make these functions deprecated (replaced with `on` and `off` since version 0.5)
6646  DataView.prototype.subscribe = DataView.prototype.on;
6647  DataView.prototype.unsubscribe = DataView.prototype.off;
6648
6649  module.exports = DataView;
6650
6651  // nothing interesting for me :-(
6652
6653/***/ },
6654/* 11 */
6655/***/ function(module, exports, __webpack_require__) {
6656
6657  'use strict';
6658
6659  var Emitter = __webpack_require__(12);
6660  var DataSet = __webpack_require__(8);
6661  var DataView = __webpack_require__(10);
6662  var util = __webpack_require__(1);
6663  var Point3d = __webpack_require__(13);
6664  var Point2d = __webpack_require__(14);
6665  var Camera = __webpack_require__(15);
6666  var Filter = __webpack_require__(16);
6667  var Slider = __webpack_require__(17);
6668  var StepNumber = __webpack_require__(18);
6669
6670  /**
6671   * @constructor Graph3d
6672   * Graph3d displays data in 3d.
6673   *
6674   * Graph3d is developed in javascript as a Google Visualization Chart.
6675   *
6676   * @param {Element} container   The DOM element in which the Graph3d will
6677   *                              be created. Normally a div element.
6678   * @param {DataSet | DataView | Array} [data]
6679   * @param {Object} [options]
6680   */
6681  function Graph3d(container, data, options) {
6682    if (!(this instanceof Graph3d)) {
6683      throw new SyntaxError('Constructor must be called with the new operator');
6684    }
6685
6686    // create variables and set default values
6687    this.containerElement = container;
6688    this.width = '400px';
6689    this.height = '400px';
6690    this.margin = 10; // px
6691    this.defaultXCenter = '55%';
6692    this.defaultYCenter = '50%';
6693
6694    this.xLabel = 'x';
6695    this.yLabel = 'y';
6696    this.zLabel = 'z';
6697
6698    var passValueFn = function passValueFn(v) {
6699      return v;
6700    };
6701    this.xValueLabel = passValueFn;
6702    this.yValueLabel = passValueFn;
6703    this.zValueLabel = passValueFn;
6704
6705    this.filterLabel = 'time';
6706    this.legendLabel = 'value';
6707
6708    this.style = Graph3d.STYLE.DOT;
6709    this.showPerspective = true;
6710    this.showGrid = true;
6711    this.keepAspectRatio = true;
6712    this.showShadow = false;
6713    this.showGrayBottom = false; // TODO: this does not work correctly
6714    this.showTooltip = false;
6715    this.verticalRatio = 0.5; // 0.1 to 1.0, where 1.0 results in a 'cube'
6716
6717    this.animationInterval = 1000; // milliseconds
6718    this.animationPreload = false;
6719
6720    this.camera = new Camera();
6721    this.camera.setArmRotation(1.0, 0.5);
6722    this.camera.setArmLength(1.7);
6723    this.eye = new Point3d(0, 0, -1); // TODO: set eye.z about 3/4 of the width of the window?
6724
6725    this.dataTable = null; // The original data table
6726    this.dataPoints = null; // The table with point objects
6727
6728    // the column indexes
6729    this.colX = undefined;
6730    this.colY = undefined;
6731    this.colZ = undefined;
6732    this.colValue = undefined;
6733    this.colFilter = undefined;
6734
6735    this.xMin = 0;
6736    this.xStep = undefined; // auto by default
6737    this.xMax = 1;
6738    this.yMin = 0;
6739    this.yStep = undefined; // auto by default
6740    this.yMax = 1;
6741    this.zMin = 0;
6742    this.zStep = undefined; // auto by default
6743    this.zMax = 1;
6744    this.valueMin = 0;
6745    this.valueMax = 1;
6746    this.xBarWidth = 1;
6747    this.yBarWidth = 1;
6748    // TODO: customize axis range
6749
6750    // colors
6751    this.axisColor = '#4D4D4D';
6752    this.gridColor = '#D3D3D3';
6753    this.dataColor = {
6754      fill: '#7DC1FF',
6755      stroke: '#3267D2',
6756      strokeWidth: 1 // px
6757    };
6758
6759    // create a frame and canvas
6760    this.create();
6761
6762    // apply options (also when undefined)
6763    this.setOptions(options);
6764
6765    // apply data
6766    if (data) {
6767      this.setData(data);
6768    }
6769  }
6770
6771  // Extend Graph3d with an Emitter mixin
6772  Emitter(Graph3d.prototype);
6773
6774  /**
6775   * Calculate the scaling values, dependent on the range in x, y, and z direction
6776   */
6777  Graph3d.prototype._setScale = function () {
6778    this.scale = new Point3d(1 / (this.xMax - this.xMin), 1 / (this.yMax - this.yMin), 1 / (this.zMax - this.zMin));
6779
6780    // keep aspect ration between x and y scale if desired
6781    if (this.keepAspectRatio) {
6782      if (this.scale.x < this.scale.y) {
6783        //noinspection JSSuspiciousNameCombination
6784        this.scale.y = this.scale.x;
6785      } else {
6786        //noinspection JSSuspiciousNameCombination
6787        this.scale.x = this.scale.y;
6788      }
6789    }
6790
6791    // scale the vertical axis
6792    this.scale.z *= this.verticalRatio;
6793    // TODO: can this be automated? verticalRatio?
6794
6795    // determine scale for (optional) value
6796    this.scale.value = 1 / (this.valueMax - this.valueMin);
6797
6798    // position the camera arm
6799    var xCenter = (this.xMax + this.xMin) / 2 * this.scale.x;
6800    var yCenter = (this.yMax + this.yMin) / 2 * this.scale.y;
6801    var zCenter = (this.zMax + this.zMin) / 2 * this.scale.z;
6802    this.camera.setArmLocation(xCenter, yCenter, zCenter);
6803  };
6804
6805  /**
6806   * Convert a 3D location to a 2D location on screen
6807   * http://en.wikipedia.org/wiki/3D_projection
6808   * @param {Point3d} point3d   A 3D point with parameters x, y, z
6809   * @return {Point2d} point2d  A 2D point with parameters x, y
6810   */
6811  Graph3d.prototype._convert3Dto2D = function (point3d) {
6812    var translation = this._convertPointToTranslation(point3d);
6813    return this._convertTranslationToScreen(translation);
6814  };
6815
6816  /**
6817   * Convert a 3D location its translation seen from the camera
6818   * http://en.wikipedia.org/wiki/3D_projection
6819   * @param {Point3d} point3d    A 3D point with parameters x, y, z
6820   * @return {Point3d} translation A 3D point with parameters x, y, z This is
6821   *                   the translation of the point, seen from the
6822   *                   camera
6823   */
6824  Graph3d.prototype._convertPointToTranslation = function (point3d) {
6825    var ax = point3d.x * this.scale.x,
6826        ay = point3d.y * this.scale.y,
6827        az = point3d.z * this.scale.z,
6828        cx = this.camera.getCameraLocation().x,
6829        cy = this.camera.getCameraLocation().y,
6830        cz = this.camera.getCameraLocation().z,
6831
6832    // calculate angles
6833    sinTx = Math.sin(this.camera.getCameraRotation().x),
6834        cosTx = Math.cos(this.camera.getCameraRotation().x),
6835        sinTy = Math.sin(this.camera.getCameraRotation().y),
6836        cosTy = Math.cos(this.camera.getCameraRotation().y),
6837        sinTz = Math.sin(this.camera.getCameraRotation().z),
6838        cosTz = Math.cos(this.camera.getCameraRotation().z),
6839
6840    // calculate translation
6841    dx = cosTy * (sinTz * (ay - cy) + cosTz * (ax - cx)) - sinTy * (az - cz),
6842        dy = sinTx * (cosTy * (az - cz) + sinTy * (sinTz * (ay - cy) + cosTz * (ax - cx))) + cosTx * (cosTz * (ay - cy) - sinTz * (ax - cx)),
6843        dz = cosTx * (cosTy * (az - cz) + sinTy * (sinTz * (ay - cy) + cosTz * (ax - cx))) - sinTx * (cosTz * (ay - cy) - sinTz * (ax - cx));
6844
6845    return new Point3d(dx, dy, dz);
6846  };
6847
6848  /**
6849   * Convert a translation point to a point on the screen
6850   * @param {Point3d} translation   A 3D point with parameters x, y, z This is
6851   *                    the translation of the point, seen from the
6852   *                    camera
6853   * @return {Point2d} point2d    A 2D point with parameters x, y
6854   */
6855  Graph3d.prototype._convertTranslationToScreen = function (translation) {
6856    var ex = this.eye.x,
6857        ey = this.eye.y,
6858        ez = this.eye.z,
6859        dx = translation.x,
6860        dy = translation.y,
6861        dz = translation.z;
6862
6863    // calculate position on screen from translation
6864    var bx;
6865    var by;
6866    if (this.showPerspective) {
6867      bx = (dx - ex) * (ez / dz);
6868      by = (dy - ey) * (ez / dz);
6869    } else {
6870      bx = dx * -(ez / this.camera.getArmLength());
6871      by = dy * -(ez / this.camera.getArmLength());
6872    }
6873
6874    // shift and scale the point to the center of the screen
6875    // use the width of the graph to scale both horizontally and vertically.
6876    return new Point2d(this.xcenter + bx * this.frame.canvas.clientWidth, this.ycenter - by * this.frame.canvas.clientWidth);
6877  };
6878
6879  /**
6880   * Set the background styling for the graph
6881   * @param {string | {fill: string, stroke: string, strokeWidth: string}} backgroundColor
6882   */
6883  Graph3d.prototype._setBackgroundColor = function (backgroundColor) {
6884    var fill = 'white';
6885    var stroke = 'gray';
6886    var strokeWidth = 1;
6887
6888    if (typeof backgroundColor === 'string') {
6889      fill = backgroundColor;
6890      stroke = 'none';
6891      strokeWidth = 0;
6892    } else if (typeof backgroundColor === 'object') {
6893      if (backgroundColor.fill !== undefined) fill = backgroundColor.fill;
6894      if (backgroundColor.stroke !== undefined) stroke = backgroundColor.stroke;
6895      if (backgroundColor.strokeWidth !== undefined) strokeWidth = backgroundColor.strokeWidth;
6896    } else if (backgroundColor === undefined) {} else {
6897      throw 'Unsupported type of backgroundColor';
6898    }
6899
6900    this.frame.style.backgroundColor = fill;
6901    this.frame.style.borderColor = stroke;
6902    this.frame.style.borderWidth = strokeWidth + 'px';
6903    this.frame.style.borderStyle = 'solid';
6904  };
6905
6906  /// enumerate the available styles
6907  Graph3d.STYLE = {
6908    BAR: 0,
6909    BARCOLOR: 1,
6910    BARSIZE: 2,
6911    DOT: 3,
6912    DOTLINE: 4,
6913    DOTCOLOR: 5,
6914    DOTSIZE: 6,
6915    GRID: 7,
6916    LINE: 8,
6917    SURFACE: 9
6918  };
6919
6920  /**
6921   * Retrieve the style index from given styleName
6922   * @param {string} styleName  Style name such as 'dot', 'grid', 'dot-line'
6923   * @return {Number} styleNumber Enumeration value representing the style, or -1
6924   *                when not found
6925   */
6926  Graph3d.prototype._getStyleNumber = function (styleName) {
6927    switch (styleName) {
6928      case 'dot':
6929        return Graph3d.STYLE.DOT;
6930      case 'dot-line':
6931        return Graph3d.STYLE.DOTLINE;
6932      case 'dot-color':
6933        return Graph3d.STYLE.DOTCOLOR;
6934      case 'dot-size':
6935        return Graph3d.STYLE.DOTSIZE;
6936      case 'line':
6937        return Graph3d.STYLE.LINE;
6938      case 'grid':
6939        return Graph3d.STYLE.GRID;
6940      case 'surface':
6941        return Graph3d.STYLE.SURFACE;
6942      case 'bar':
6943        return Graph3d.STYLE.BAR;
6944      case 'bar-color':
6945        return Graph3d.STYLE.BARCOLOR;
6946      case 'bar-size':
6947        return Graph3d.STYLE.BARSIZE;
6948    }
6949
6950    return -1;
6951  };
6952
6953  /**
6954   * Determine the indexes of the data columns, based on the given style and data
6955   * @param {DataSet} data
6956   * @param {Number}  style
6957   */
6958  Graph3d.prototype._determineColumnIndexes = function (data, style) {
6959    if (this.style === Graph3d.STYLE.DOT || this.style === Graph3d.STYLE.DOTLINE || this.style === Graph3d.STYLE.LINE || this.style === Graph3d.STYLE.GRID || this.style === Graph3d.STYLE.SURFACE || this.style === Graph3d.STYLE.BAR) {
6960      // 3 columns expected, and optionally a 4th with filter values
6961      this.colX = 0;
6962      this.colY = 1;
6963      this.colZ = 2;
6964      this.colValue = undefined;
6965
6966      if (data.getNumberOfColumns() > 3) {
6967        this.colFilter = 3;
6968      }
6969    } else if (this.style === Graph3d.STYLE.DOTCOLOR || this.style === Graph3d.STYLE.DOTSIZE || this.style === Graph3d.STYLE.BARCOLOR || this.style === Graph3d.STYLE.BARSIZE) {
6970      // 4 columns expected, and optionally a 5th with filter values
6971      this.colX = 0;
6972      this.colY = 1;
6973      this.colZ = 2;
6974      this.colValue = 3;
6975
6976      if (data.getNumberOfColumns() > 4) {
6977        this.colFilter = 4;
6978      }
6979    } else {
6980      throw 'Unknown style "' + this.style + '"';
6981    }
6982  };
6983
6984  Graph3d.prototype.getNumberOfRows = function (data) {
6985    return data.length;
6986  };
6987
6988  Graph3d.prototype.getNumberOfColumns = function (data) {
6989    var counter = 0;
6990    for (var column in data[0]) {
6991      if (data[0].hasOwnProperty(column)) {
6992        counter++;
6993      }
6994    }
6995    return counter;
6996  };
6997
6998  Graph3d.prototype.getDistinctValues = function (data, column) {
6999    var distinctValues = [];
7000    for (var i = 0; i < data.length; i++) {
7001      if (distinctValues.indexOf(data[i][column]) == -1) {
7002        distinctValues.push(data[i][column]);
7003      }
7004    }
7005    return distinctValues;
7006  };
7007
7008  Graph3d.prototype.getColumnRange = function (data, column) {
7009    var minMax = { min: data[0][column], max: data[0][column] };
7010    for (var i = 0; i < data.length; i++) {
7011      if (minMax.min > data[i][column]) {
7012        minMax.min = data[i][column];
7013      }
7014      if (minMax.max < data[i][column]) {
7015        minMax.max = data[i][column];
7016      }
7017    }
7018    return minMax;
7019  };
7020
7021  /**
7022   * Initialize the data from the data table. Calculate minimum and maximum values
7023   * and column index values
7024   * @param {Array | DataSet | DataView} rawData   The data containing the items for the Graph.
7025   * @param {Number}     style   Style Number
7026   */
7027  Graph3d.prototype._dataInitialize = function (rawData, style) {
7028    var me = this;
7029
7030    // unsubscribe from the dataTable
7031    if (this.dataSet) {
7032      this.dataSet.off('*', this._onChange);
7033    }
7034
7035    if (rawData === undefined) return;
7036
7037    if (Array.isArray(rawData)) {
7038      rawData = new DataSet(rawData);
7039    }
7040
7041    var data;
7042    if (rawData instanceof DataSet || rawData instanceof DataView) {
7043      data = rawData.get();
7044    } else {
7045      throw new Error('Array, DataSet, or DataView expected');
7046    }
7047
7048    if (data.length == 0) return;
7049
7050    this.dataSet = rawData;
7051    this.dataTable = data;
7052
7053    // subscribe to changes in the dataset
7054    this._onChange = function () {
7055      me.setData(me.dataSet);
7056    };
7057    this.dataSet.on('*', this._onChange);
7058
7059    // _determineColumnIndexes
7060    // getNumberOfRows (points)
7061    // getNumberOfColumns (x,y,z,v,t,t1,t2...)
7062    // getDistinctValues (unique values?)
7063    // getColumnRange
7064
7065    // determine the location of x,y,z,value,filter columns
7066    this.colX = 'x';
7067    this.colY = 'y';
7068    this.colZ = 'z';
7069    this.colValue = 'style';
7070    this.colFilter = 'filter';
7071
7072    // check if a filter column is provided
7073    if (data[0].hasOwnProperty('filter')) {
7074      if (this.dataFilter === undefined) {
7075        this.dataFilter = new Filter(rawData, this.colFilter, this);
7076        this.dataFilter.setOnLoadCallback(function () {
7077          me.redraw();
7078        });
7079      }
7080    }
7081
7082    var withBars = this.style == Graph3d.STYLE.BAR || this.style == Graph3d.STYLE.BARCOLOR || this.style == Graph3d.STYLE.BARSIZE;
7083
7084    // determine barWidth from data
7085    if (withBars) {
7086      if (this.defaultXBarWidth !== undefined) {
7087        this.xBarWidth = this.defaultXBarWidth;
7088      } else {
7089        var dataX = this.getDistinctValues(data, this.colX);
7090        this.xBarWidth = dataX[1] - dataX[0] || 1;
7091      }
7092
7093      if (this.defaultYBarWidth !== undefined) {
7094        this.yBarWidth = this.defaultYBarWidth;
7095      } else {
7096        var dataY = this.getDistinctValues(data, this.colY);
7097        this.yBarWidth = dataY[1] - dataY[0] || 1;
7098      }
7099    }
7100
7101    // calculate minimums and maximums
7102    var xRange = this.getColumnRange(data, this.colX);
7103    if (withBars) {
7104      xRange.min -= this.xBarWidth / 2;
7105      xRange.max += this.xBarWidth / 2;
7106    }
7107    this.xMin = this.defaultXMin !== undefined ? this.defaultXMin : xRange.min;
7108    this.xMax = this.defaultXMax !== undefined ? this.defaultXMax : xRange.max;
7109    if (this.xMax <= this.xMin) this.xMax = this.xMin + 1;
7110    this.xStep = this.defaultXStep !== undefined ? this.defaultXStep : (this.xMax - this.xMin) / 5;
7111
7112    var yRange = this.getColumnRange(data, this.colY);
7113    if (withBars) {
7114      yRange.min -= this.yBarWidth / 2;
7115      yRange.max += this.yBarWidth / 2;
7116    }
7117    this.yMin = this.defaultYMin !== undefined ? this.defaultYMin : yRange.min;
7118    this.yMax = this.defaultYMax !== undefined ? this.defaultYMax : yRange.max;
7119    if (this.yMax <= this.yMin) this.yMax = this.yMin + 1;
7120    this.yStep = this.defaultYStep !== undefined ? this.defaultYStep 
7120: (this.yMax - this.yMin) / 5;
7121
7122    var zRange = this.getColumnRange(data, this.colZ);
7123    this.zMin = this.defaultZMin !== undefined ? this.defaultZMin : zRange.min;
7124    this.zMax = this.defaultZMax !== undefined ? this.defaultZMax : zRange.max;
7125    if (this.zMax <= this.zMin) this.zMax = this.zMin + 1;
7126    this.zStep = this.defaultZStep !== undefined ? this.defaultZStep : (this.zMax - this.zMin) / 5;
7127
7128    if (this.colValue !== undefined) {
7129      var valueRange = this.getColumnRange(data, this.colValue);
7130      this.valueMin = this.defaultValueMin !== undefined ? this.defaultValueMin : valueRange.min;
7131      this.valueMax = this.defaultValueMax !== undefined ? this.defaultValueMax : valueRange.max;
7132      if (this.valueMax <= this.valueMin) this.valueMax = this.valueMin + 1;
7133    }
7134
7135    // set the scale dependent on the ranges.
7136    this._setScale();
7137  };
7138
7139  /**
7140   * Filter the data based on the current filter
7141   * @param {Array} data
7142   * @return {Array} dataPoints   Array with point objects which can be drawn on screen
7143   */
7144  Graph3d.prototype._getDataPoints = function (data) {
7145    // TODO: store the created matrix dataPoints in the filters instead of reloading each time
7146    var x, y, i, z, obj, point;
7147
7148    var dataPoints = [];
7149
7150    if (this.style === Graph3d.STYLE.GRID || this.style === Graph3d.STYLE.SURFACE) {
7151      // copy all values from the google data table to a matrix
7152      // the provided values are supposed to form a grid of (x,y) positions
7153
7154      // create two lists with all present x and y values
7155      var dataX = [];
7156      var dataY = [];
7157      for (i = 0; i < this.getNumberOfRows(data); i++) {
7158        x = data[i][this.colX] || 0;
7159        y = data[i][this.colY] || 0;
7160
7161        if (dataX.indexOf(x) === -1) {
7162          dataX.push(x);
7163        }
7164        if (dataY.indexOf(y) === -1) {
7165          dataY.push(y);
7166        }
7167      }
7168
7169      var sortNumber = function sortNumber(a, b) {
7170        return a - b;
7171      };
7172      dataX.sort(sortNumber);
7173      dataY.sort(sortNumber);
7174
7175      // create a grid, a 2d matrix, with all values.
7176      var dataMatrix = []; // temporary data matrix
7177      for (i = 0; i < data.length; i++) {
7178        x = data[i][this.colX] || 0;
7179        y = data[i][this.colY] || 0;
7180        z = data[i][this.colZ] || 0;
7181
7182        var xIndex = dataX.indexOf(x); // TODO: implement Array().indexOf() for Internet Explorer
7183        var yIndex = dataY.indexOf(y);
7184
7185        if (dataMatrix[xIndex] === undefined) {
7186          dataMatrix[xIndex] = [];
7187        }
7188
7189        var point3d = new Point3d();
7190        point3d.x = x;
7191        point3d.y = y;
7192        point3d.z = z;
7193
7194        obj = {};
7195        obj.point = point3d;
7196        obj.trans = undefined;
7197        obj.screen = undefined;
7198        obj.bottom = new Point3d(x, y, this.zMin);
7199
7200        dataMatrix[xIndex][yIndex] = obj;
7201
7202        dataPoints.push(obj);
7203      }
7204
7205      // fill in the pointers to the neighbors.
7206      for (x = 0; x < dataMatrix.length; x++) {
7207        for (y = 0; y < dataMatrix[x].length; y++) {
7208          if (dataMatrix[x][y]) {
7209            dataMatrix[x][y].pointRight = x < dataMatrix.length - 1 ? dataMatrix[x + 1][y] : undefined;
7210            dataMatrix[x][y].pointTop = y < dataMatrix[x].length - 1 ? dataMatrix[x][y + 1] : undefined;
7211            dataMatrix[x][y].pointCross = x < dataMatrix.length - 1 && y < dataMatrix[x].length - 1 ? dataMatrix[x + 1][y + 1] : undefined;
7212          }
7213        }
7214      }
7215    } else {
7216      // 'dot', 'dot-line', etc.
7217      // copy all values from the google data table to a list with Point3d objects
7218      for (i = 0; i < data.length; i++) {
7219        point = new Point3d();
7220        point.x = data[i][this.colX] || 0;
7221        point.y = data[i][this.colY] || 0;
7222        point.z = data[i][this.colZ] || 0;
7223
7224        if (this.colValue !== undefined) {
7225          point.value = data[i][this.colValue] || 0;
7226        }
7227
7228        obj = {};
7229        obj.point = point;
7230        obj.bottom = new Point3d(point.x, point.y, this.zMin);
7231        obj.trans = undefined;
7232        obj.screen = undefined;
7233
7234        dataPoints.push(obj);
7235      }
7236    }
7237
7238    return dataPoints;
7239  };
7240
7241  /**
7242   * Create the main frame for the Graph3d.
7243   * This function is executed once when a Graph3d object is created. The frame
7244   * contains a canvas, and this canvas contains all objects like the axis and
7245   * nodes.
7246   */
7247  Graph3d.prototype.create = function () {
7248    // remove all elements from the container element.
7249    while (this.containerElement.hasChildNodes()) {
7250      this.containerElement.removeChild(this.containerElement.firstChild);
7251    }
7252
7253    this.frame = document.createElement('div');
7254    this.frame.style.position = 'relative';
7255    this.frame.style.overflow = 'hidden';
7256
7257    // create the graph canvas (HTML canvas element)
7258    this.frame.canvas = document.createElement('canvas');
7259    this.frame.canvas.style.position = 'relative';
7260    this.frame.appendChild(this.frame.canvas);
7261    //if (!this.frame.canvas.getContext) {
7262    {
7263      var noCanvas = document.createElement('DIV');
7264      noCanvas.style.color = 'red';
7265      noCanvas.style.fontWeight = 'bold';
7266      noCanvas.style.padding = '10px';
7267      noCanvas.innerHTML = 'Error: your browser does not support HTML canvas';
7268      this.frame.canvas.appendChild(noCanvas);
7269    }
7270
7271    this.frame.filter = document.createElement('div');
7272    this.frame.filter.style.position = 'absolute';
7273    this.frame.filter.style.bottom = '0px';
7274    this.frame.filter.style.left = '0px';
7275    this.frame.filter.style.width = '100%';
7276    this.frame.appendChild(this.frame.filter);
7277
7278    // add event listeners to handle moving and zooming the contents
7279    var me = this;
7280    var onmousedown = function onmousedown(event) {
7281      me._onMouseDown(event);
7282    };
7283    var ontouchstart = function ontouchstart(event) {
7284      me._onTouchStart(event);
7285    };
7286    var onmousewheel = function onmousewheel(event) {
7287      me._onWheel(event);
7288    };
7289    var ontooltip = function ontooltip(event) {
7290      me._onTooltip(event);
7291    };
7292    // TODO: these events are never cleaned up... can give a 'memory leakage'
7293
7294    util.addEventListener(this.frame.canvas, 'keydown', onkeydown);
7295    util.addEventListener(this.frame.canvas, 'mousedown', onmousedown);
7296    util.addEventListener(this.frame.canvas, 'touchstart', ontouchstart);
7297    util.addEventListener(this.frame.canvas, 'mousewheel', onmousewheel);
7298    util.addEventListener(this.frame.canvas, 'mousemove', ontooltip);
7299
7300    // add the new graph to the container element
7301    this.containerElement.appendChild(this.frame);
7302  };
7303
7304  /**
7305   * Set a new size for the graph
7306   * @param {string} width   Width in pixels or percentage (for example '800px'
7307   *             or '50%')
7308   * @param {string} height  Height in pixels or percentage  (for example '400px'
7309   *             or '30%')
7310   */
7311  Graph3d.prototype.setSize = function (width, height) {
7312    this.frame.style.width = width;
7313    this.frame.style.height = height;
7314
7315    this._resizeCanvas();
7316  };
7317
7318  /**
7319   * Resize the canvas to the current size of the frame
7320   */
7321  Graph3d.prototype._resizeCanvas = function () {
7322    this.frame.canvas.style.width = '100%';
7323    this.frame.canvas.style.height = '100%';
7324
7325    this.frame.canvas.width = this.frame.canvas.clientWidth;
7326    this.frame.canvas.height = this.frame.canvas.clientHeight;
7327
7328    // adjust with for margin
7329    this.frame.filter.style.width = this.frame.canvas.clientWidth - 2 * 10 + 'px';
7330  };
7331
7332  /**
7333   * Start animation
7334   */
7335  Graph3d.prototype.animationStart = function () {
7336    if (!this.frame.filter || !this.frame.filter.slider) throw 'No animation available';
7337
7338    this.frame.filter.slider.play();
7339  };
7340
7341  /**
7342   * Stop animation
7343   */
7344  Graph3d.prototype.animationStop = function () {
7345    if (!this.frame.filter || !this.frame.filter.slider) return;
7346
7347    this.frame.filter.slider.stop();
7348  };
7349
7350  /**
7351   * Resize the center position based on the current values in this.defaultXCenter
7352   * and this.defaultYCenter (which are strings with a percentage or a value
7353   * in pixels). The center positions are the variables this.xCenter
7354   * and this.yCenter
7355   */
7356  Graph3d.prototype._resizeCenter = function () {
7357    // calculate the horizontal center position
7358    if (this.defaultXCenter.charAt(this.defaultXCenter.length - 1) === '%') {
7359      this.xcenter = parseFloat(this.defaultXCenter) / 100 * this.frame.canvas.clientWidth;
7360    } else {
7361      this.xcenter = parseFloat(this.defaultXCenter); // supposed to be in px
7362    }
7363
7364    // calculate the vertical center position
7365    if (this.defaultYCenter.charAt(this.defaultYCenter.length - 1) === '%') {
7366      this.ycenter = parseFloat(this.defaultYCenter) / 100 * (this.frame.canvas.clientHeight - this.frame.filter.clientHeight);
7367    } else {
7368      this.ycenter = parseFloat(this.defaultYCenter); // supposed to be in px
7369    }
7370  };
7371
7372  /**
7373   * Set the rotation and distance of the camera
7374   * @param {Object} pos   An object with the camera position. The object
7375   *             contains three parameters:
7376   *             - horizontal {Number}
7377   *             The horizontal rotation, between 0 and 2*PI.
7378   *             Optional, can be left undefined.
7379   *             - vertical {Number}
7380   *             The vertical rotation, between 0 and 0.5*PI
7381   *             if vertical=0.5*PI, the graph is shown from the
7382   *             top. Optional, can be left undefined.
7383   *             - distance {Number}
7384   *             The (normalized) distance of the camera to the
7385   *             center of the graph, a value between 0.71 and 5.0.
7386   *             Optional, can be left undefined.
7387   */
7388  Graph3d.prototype.setCameraPosition = function (pos) {
7389    if (pos === undefined) {
7390      return;
7391    }
7392
7393    if (pos.horizontal !== undefined && pos.vertical !== undefined) {
7394      this.camera.setArmRotation(pos.horizontal, pos.vertical);
7395    }
7396
7397    if (pos.distance !== undefined) {
7398      this.camera.setArmLength(pos.distance);
7399    }
7400
7401    this.redraw();
7402  };
7403
7404  /**
7405   * Retrieve the current camera rotation
7406   * @return {object}   An object with parameters horizontal, vertical, and
7407   *          distance
7408   */
7409  Graph3d.prototype.getCameraPosition = function () {
7410    var pos = this.camera.getArmRotation();
7411    pos.distance = this.camera.getArmLength();
7412    return pos;
7413  };
7414
7415  /**
7416   * Load data into the 3D Graph
7417   */
7418  Graph3d.prototype._readData = function (data) {
7419    // read the data
7420    this._dataInitialize(data, this.style);
7421
7422    if (this.dataFilter) {
7423      // apply filtering
7424      this.dataPoints = this.dataFilter._getDataPoints();
7425    } else {
7426      // no filtering. load all data
7427      this.dataPoints = this._getDataPoints(this.dataTable);
7428    }
7429
7430    // draw the filter
7431    this._redrawFilter();
7432  };
7433
7434  /**
7435   * Replace the dataset of the Graph3d
7436   * @param {Array | DataSet | DataView} data
7437   */
7438  Graph3d.prototype.setData = function (data) {
7439    this._readData(data);
7440    this.redraw();
7441
7442    // start animation when option is true
7443    if (this.animationAutoStart && this.dataFilter) {
7444      this.animationStart();
7445    }
7446  };
7447
7448  /**
7449   * Update the options. Options will be merged with current options
7450   * @param {Object} options
7451   */
7452  Graph3d.prototype.setOptions = function (options) {
7453    var cameraPosition = undefined;
7454
7455    this.animationStop();
7456
7457    if (options !== undefined) {
7458      // retrieve parameter values
7459      if (options.width !== undefined) this.width = options.width;
7460      if (options.height !== undefined) this.height = options.height;
7461
7462      if (options.xCenter !== undefined) this.defaultXCenter = options.xCenter;
7463      if (options.yCenter !== undefined) this.defaultYCenter = options.yCenter;
7464
7465      if (options.filterLabel !== undefined) this.filterLabel = options.filterLabel;
7466      if (options.legendLabel !== undefined) this.legendLabel = options.legendLabel;
7467      if (options.xLabel !== undefined) this.xLabel = options.xLabel;
7468      if (options.yLabel !== undefined) this.yLabel = options.yLabel;
7469      if (options.zLabel !== undefined) this.zLabel = options.zLabel;
7470
7471      if (options.xValueLabel !== undefined) this.xValueLabel = options.xValueLabel;
7472      if (options.yValueLabel !== undefined) this.yValueLabel = options.yValueLabel;
7473      if (options.zValueLabel !== undefined) this.zValueLabel = options.zValueLabel;
7474
7475      if (options.style !== undefined) {
7476        var styleNumber = this._getStyleNumber(options.style);
7477        if (styleNumber !== -1) {
7478          this.style = styleNumber;
7479        }
7480      }
7481      if (options.showGrid !== undefined) this.showGrid = options.showGrid;
7482      if (options.showPerspective !== undefined) this.showPerspective = options.showPerspective;
7483      if (options.showShadow !== undefined) this.showShadow = options.showShadow;
7484      if (options.tooltip !== undefined) this.showTooltip = options.tooltip;
7485      if (options.showAnimationControls !== undefined) this.showAnimationControls = options.showAnimationControls;
7486      if (options.keepAspectRatio !== undefined) this.keepAspectRatio = options.keepAspectRatio;
7487      if (options.verticalRatio !== undefined) this.verticalRatio = options.verticalRatio;
7488
7489      if (options.animationInterval !== undefined) this.animationInterval = options.animationInterval;
7490      if (options.animationPreload !== undefined) this.animationPreload = options.animationPreload;
7491      if (options.animationAutoStart !== undefined) this.animationAutoStart = options.animationAutoStart;
7492
7493      if (options.xBarWidth !== undefined) this.defaultXBarWidth = options.xBarWidth;
7494      if (options.yBarWidth !== undefined) this.defaultYBarWidth = options.yBarWidth;
7495
7496      if (options.xMin !== undefined) this.defaultXMin = options.xMin;
7497      if (options.xStep !== undefined) this.defaultXStep = options.xStep;
7498      if (options.xMax !== undefined) this.defaultXMax = options.xMax;
7499      if (options.yMin !== undefined) this.defaultYMin = options.yMin;
7500      if (options.yStep !== undefined) this.defaultYStep = options.yStep;
7501      if (options.yMax !== undefined) this.defaultYMax = options.yMax;
7502      if (options.zMin !== undefined) this.defaultZMin = options.zMin;
7503      if (options.zStep !== undefined) this.defaultZStep = options.zStep;
7504      if (options.zMax !== undefined) this.defaultZMax = options.zMax;
7505      if (options.valueMin !== undefined) this.defaultValueMin = options.valueMin;
7506      if (options.valueMax !== undefined) this.defaultValueMax = options.valueMax;
7507      if (options.backgroundColor !== undefined) this._setBackgroundColor(options.backgroundColor);
7508
7509      if (options.cameraPosition !== undefined) cameraPosition = options.cameraPosition;
7510
7511      if (cameraPosition !== undefined) {
7512        this.camera.setArmRotation(cameraPosition.horizontal, cameraPosition.vertical);
7513        this.camera.setArmLength(cameraPosition.distance);
7514      }
7515
7516      // colors
7517      if (options.axisColor !== undefined) this.axisColor = options.axisColor;
7518      if (options.gridColor !== undefined) this.gridColor = options.gridColor;
7519      if (options.dataColor) {
7520        if (typeof options.dataColor === 'string') {
7521          this.dataColor.fill = options.dataColor;
7522          this.dataColor.stroke = options.dataColor;
7523        } else {
7524          if (options.dataColor.fill) {
7525            this.dataColor.fill = options.dataColor.fill;
7526          }
7527          if (options.dataColor.stroke) {
7528            this.dataColor.stroke = options.dataColor.stroke;
7529          }
7530          if (options.dataColor.strokeWidth !== undefined) {
7531            this.dataColor.strokeWidth = options.dataColor.strokeWidth;
7532          }
7533        }
7534      }
7535    }
7536
7537    this.setSize(this.width, this.height);
7538
7539    // re-load the data
7540    if (this.dataTable) {
7541      this.setData(this.dataTable);
7542    }
7543
7544    // start animation when option is true
7545    if (this.animationAutoStart && this.dataFilter) {
7546      this.animationStart();
7547    }
7548  };
7549
7550  /**
7551   * Redraw the Graph.
7552   */
7553  Graph3d.prototype.redraw = function () {
7554    if (this.dataPoints === undefined) {
7555      throw 'Error: graph data not initialized';
7556    }
7557
7558    this._resizeCanvas();
7559    this._resizeCenter();
7560    this._redrawSlider();
7561    this._redrawClear();
7562    this._redrawAxis();
7563
7564    if (this.style === Graph3d.STYLE.GRID || this.style === Graph3d.STYLE.SURFACE) {
7565      this._redrawDataGrid();
7566    } else if (this.style === Graph3d.STYLE.LINE) {
7567      this._redrawDataLine();
7568    } else if (this.style === Graph3d.STYLE.BAR || this.style === Graph3d.STYLE.BARCOLOR || this.style === Graph3d.STYLE.BARSIZE) {
7569      this._redrawDataBar();
7570    } else {
7571      // style is DOT, DOTLINE, DOTCOLOR, DOTSIZE
7572      this._redrawDataDot();
7573    }
7574
7575    this._redrawInfo();
7576    this._redrawLegend();
7577  };
7578
7579  /**
7580   * Clear the canvas before redrawing
7581   */
7582  Graph3d.prototype._redrawClear = function () {
7583    var canvas = this.frame.canvas;
7584    var ctx = canvas.getContext('2d');
7585
7586    ctx.clearRect(0, 0, canvas.width, canvas.height);
7587  };
7588
7589  /**
7590   * Redraw the legend showing the colors
7591   */
7592  Graph3d.prototype._redrawLegend = function () {
7593    var y;
7594
7595    if (this.style === Graph3d.STYLE.DOTCOLOR || this.style === Graph3d.STYLE.DOTSIZE) {
7596
7597      var dotSize = this.frame.clientWidth * 0.02;
7598
7599      var widthMin, widthMax;
7600      if (this.style === Graph3d.STYLE.DOTSIZE) {
7601        widthMin = dotSize / 2; // px
7602        widthMax = dotSize / 2 + dotSize * 2; // Todo: put this in one function
7603      } else {
7604        widthMin = 20; // px
7605        widthMax = 20; // px
7606      }
7607
7608      var height = Math.max(this.frame.clientHeight * 0.25, 100);
7609      var top = this.margin;
7610      var right = this.frame.clientWidth - this.margin;
7611      var left = right - widthMax;
7612      var bottom = top + height;
7613    }
7614
7615    var canvas = this.frame.canvas;
7616    var ctx = canvas.getContext('2d');
7617    ctx.lineWidth = 1;
7618    ctx.font = '14px arial'; // TODO: put in options
7619
7620    if (this.style === Graph3d.STYLE.DOTCOLOR) {
7621      // draw the color bar
7622      var ymin = 0;
7623      var ymax = height; // Todo: make height customizable
7624      for (y = ymin; y < ymax; y++) {
7625        var f = (y - ymin) / (ymax - ymin);
7626
7627        //var width = (dotSize / 2 + (1-f) * dotSize * 2); // Todo: put this in one function
7628        var hue = f * 240;
7629        var color = this._hsv2rgb(hue, 1, 1);
7630
7631        ctx.strokeStyle = color;
7632        ctx.beginPath();
7633        ctx.moveTo(left, top + y);
7634        ctx.lineTo(right, top + y);
7635        ctx.stroke();
7636      }
7637
7638      ctx.strokeStyle = this.axisColor;
7639      ctx.strokeRect(left, top, widthMax, height);
7640    }
7641
7642    if (this.style === Graph3d.STYLE.DOTSIZE) {
7643      // draw border around color bar
7644      ctx.strokeStyle = this.axisColor;
7645      ctx.fillStyle = this.dataColor.fill;
7646      ctx.beginPath();
7647      ctx.moveTo(left, top);
7648      ctx.lineTo(right, top);
7649      ctx.lineTo(right - widthMax + widthMin, bottom);
7650      ctx.lineTo(left, bottom);
7651      ctx.closePath();
7652      ctx.fill();
7653      ctx.stroke();
7654    }
7655
7656    if (this.style === Graph3d.STYLE.DOTCOLOR || this.style === Graph3d.STYLE.DOTSIZE) {
7657      // print values along the color bar
7658      var gridLineLen = 5; // px
7659      var step = new StepNumber(this.valueMin, this.valueMax, (this.valueMax - this.valueMin) / 5, true);
7660      step.start();
7661      if (step.getCurrent() < this.valueMin) {
7662        step.next();
7663      }
7664      while (!step.end()) {
7665        y = bottom - (step.getCurrent() - this.valueMin) / (this.valueMax - this.valueMin) * height;
7666
7667        ctx.beginPath();
7668        ctx.moveTo(left - gridLineLen, y);
7669        ctx.lineTo(left, y);
7670        ctx.stroke();
7671
7672        ctx.textAlign = 'right';
7673        ctx.textBaseline = 'middle';
7674        ctx.fillStyle = this.axisColor;
7675        ctx.fillText(step.getCurrent(), left - 2 * gridLineLen, y);
7676
7677        step.next();
7678      }
7679
7680      ctx.textAlign = 'right';
7681      ctx.textBaseline = 'top';
7682      var label = this.legendLabel;
7683      ctx.fillText(label, right, bottom + this.margin);
7684    }
7685  };
7686
7687  /**
7688   * Redraw the filter
7689   */
7690  Graph3d.prototype._redrawFilter = function () {
7691    this.frame.filter.innerHTML = '';
7692
7693    if (this.dataFilter) {
7694      var options = {
7695        'visible': this.showAnimationControls
7696      };
7697      var slider = new Slider(this.frame.filter, options);
7698      this.frame.filter.slider = slider;
7699
7700      // TODO: css here is not nice here...
7701      this.frame.filter.style.padding = '10px';
7702      //this.frame.filter.style.backgroundColor = '#EFEFEF';
7703
7704      slider.setValues(this.dataFilter.values);
7705      slider.setPlayInterval(this.animationInterval);
7706
7707      // create an event handler
7708      var me = this;
7709      var onchange = function onchange() {
7710        var index = slider.getIndex();
7711
7712        me.dataFilter.selectValue(index);
7713        me.dataPoints = me.dataFilter._getDataPoints();
7714
7715        me.redraw();
7716      };
7717      slider.setOnChangeCallback(onchange);
7718    } else {
7719      this.frame.filter.slider = undefined;
7720    }
7721  };
7722
7723  /**
7724   * Redraw the slider
7725   */
7726  Graph3d.prototype._redrawSlider = function () {
7727    if (this.frame.filter.slider !== undefined) {
7728      this.frame.filter.slider.redraw();
7729    }
7730  };
7731
7732  /**
7733   * Redraw common information
7734   */
7735  Graph3d.prototype._redrawInfo = function () {
7736    if (this.dataFilter) {
7737      var canvas = this.frame.canvas;
7738      var ctx = canvas.getContext('2d');
7739
7740      ctx.font = '14px arial'; // TODO: put in options
7741      ctx.lineStyle = 'gray';
7742      ctx.fillStyle = 'gray';
7743      ctx.textAlign = 'left';
7744      ctx.textBaseline = 'top';
7745
7746      var x = this.margin;
7747      var y = this.margin;
7748      ctx.fillText(this.dataFilter.getLabel() + ': ' + this.dataFilter.getSelectedValue(), x, y);
7749    }
7750  };
7751
7752  /**
7753   * Redraw the axis
7754   */
7755  Graph3d.prototype._redrawAxis = function () {
7756    var canvas = this.frame.canvas,
7757        ctx = canvas.getContext('2d'),
7758        from,
7759        to,
7760        step,
7761        prettyStep,
7762        text,
7763        xText,
7764        yText,
7765        zText,
7766        offset,
7767        xOffset,
7768        yOffset,
7769        xMin2d,
7770        xMax2d;
7771
7772    // TODO: get the actual rendered style of the containerElement
7773    //ctx.font = this.containerElement.style.font;
7774    ctx.font = 24 / this.camera.getArmLength() + 'px arial';
7775
7776    // calculate the length for the short grid lines
7777    var gridLenX = 0.025 / this.scale.x;
7778    var gridLenY = 0.025 / this.scale.y;
7779    var textMargin = 5 / this.camera.getArmLength(); // px
7780    var armAngle = this.camera.getArmRotation().horizontal;
7781
7782    // draw x-grid lines
7783    ctx.lineWidth = 1;
7784    prettyStep = this.defaultXStep === undefined;
7785    step = new StepNumber(this.xMin, this.xMax, this.xStep, prettyStep);
7786    step.start();
7787    if (step.getCurrent() < this.xMin) {
7788      step.next();
7789    }
7790    while (!step.end()) {
7791      var x = step.getCurrent();
7792
7793      if (this.showGrid) {
7794        from = this._convert3Dto2D(new Point3d(x, this.yMin, this.zMin));
7795        to = this._convert3Dto2D(new Point3d(x, this.yMax, this.zMin));
7796        ctx.strokeStyle = this.gridColor;
7797        ctx.beginPath();
7798        ctx.moveTo(from.x, from.y);
7799        ctx.lineTo(to.x, to.y);
7800        ctx.stroke();
7801      } else {
7802        from = this._convert3Dto2D(new Point3d(x, this.yMin, this.zMin));
7803        to = this._convert3Dto2D(new Point3d(x, this.yMin + gridLenX, this.zMin));
7804        ctx.strokeStyle = this.axisColor;
7805        ctx.beginPath();
7806        ctx.moveTo(from.x, from.y);
7807        ctx.lineTo(to.x, to.y);
7808        ctx.stroke();
7809
7810        from = this._convert3Dto2D(new Point3d(x, this.yMax, this.zMin));
7811        to = this._convert3Dto2D(new Point3d(x, this.yMax - gridLenX, this.zMin));
7812        ctx.strokeStyle = this.axisColor;
7813        ctx.beginPath();
7814        ctx.moveTo(from.x, from.y);
7815        ctx.lineTo(to.x, to.y);
7816        ctx.stroke();
7817      }
7818
7819      yText = Math.cos(armAngle) > 0 ? this.yMin : this.yMax;
7820      text = this._convert3Dto2D(new Point3d(x, yText, this.zMin));
7821      if (Math.cos(armAngle * 2) > 0) {
7822        ctx.textAlign = 'center';
7823        ctx.textBaseline = 'top';
7824        text.y += textMargin;
7825      } else if (Math.sin(armAngle * 2) < 0) {
7826        ctx.textAlign = 'right';
7827        ctx.textBaseline = 'middle';
7828      } else {
7829        ctx.textAlign = 'left';
7830        ctx.textBaseline = 'middle';
7831      }
7832      ctx.fillStyle = this.axisColor;
7833      ctx.fillText('  ' + this.xValueLabel(step.getCurrent()) + '  ', text.x, text.y);
7834
7835      step.next();
7836    }
7837
7838    // draw y-grid lines
7839    ctx.lineWidth = 1;
7840    prettyStep = this.defaultYStep === undefined;
7841    step = new StepNumber(this.yMin, this.yMax, this.yStep, prettyStep);
7842    step.start();
7843    if (step.getCurrent() < this.yMin) {
7844      step.next();
7845    }
7846    while (!step.end()) {
7847      if (this.showGrid) {
7848        from = this._convert3Dto2D(new Point3d(this.xMin, step.getCurrent(), this.zMin));
7849        to = this._convert3Dto2D(new Point3d(this.xMax, step.getCurrent(), this.zMin));
7850        ctx.strokeStyle = this.gridColor;
7851        ctx.beginPath();
7852        ctx.moveTo(from.x, from.y);
7853        ctx.lineTo(to.x, to.y);
7854        ctx.stroke();
7855      } else {
7856        from = this._convert3Dto2D(new Point3d(this.xMin, step.getCurrent(), this.zMin));
7857        to = this._convert3Dto2D(new Point3d(this.xMin + gridLenY, step.getCurrent(), this.zMin));
7858        ctx.strokeStyle = this.axisColor;
7859        ctx.beginPath();
7860        ctx.moveTo(from.x, from.y);
7861        ctx.lineTo(to.x, to.y);
7862        ctx.stroke();
7863
7864        from = this._convert3Dto2D(new Point3d(this.xMax, step.getCurrent(), this.zMin));
7865        to = this._convert3Dto2D(new Point3d(this.xMax - gridLenY, step.getCurrent(), this.zMin));
7866        ctx.strokeStyle = this.axisColor;
7867        ctx.beginPath();
7868        ctx.moveTo(from.x, from.y);
7869        ctx.lineTo(to.x, to.y);
7870        ctx.stroke();
7871      }
7872
7873      xText = Math.sin(armAngle) > 0 ? this.xMin : this.xMax;
7874      text = this._convert3Dto2D(new Point3d(xText, step.getCurrent(), this.zMin));
7875      if (Math.cos(armAngle * 2) < 0) {
7876        ctx.textAlign = 'center';
7877        ctx.textBaseline = 'top';
7878        text.y += textMargin;
7879      } else if (Math.sin(armAngle * 2) > 0) {
7880        ctx.textAlign = 'right';
7881        ctx.textBaseline = 'middle';
7882      } else {
7883        ctx.textAlign = 'left';
7884        ctx.textBaseline = 'middle';
7885      }
7886      ctx.fillStyle = this.axisColor;
7887      ctx.fillText('  ' + this.yValueLabel(step.getCurrent()) + '  ', text.x, text.y);
7888
7889      step.next();
7890    }
7891
7892    // draw z-grid lines and axis
7893    ctx.lineWidth = 1;
7894    prettyStep = this.defaultZStep === undefined;
7895    step = new StepNumber(this.zMin, this.zMax, this.zStep, prettyStep);
7896    step.start();
7897    if (step.getCurrent() < this.zMin) {
7898      step.next();
7899    }
7900    xText = Math.cos(armAngle) > 0 ? this.xMin : this.xMax;
7901    yText = Math.sin(armAngle) < 0 ? this.yMin : this.yMax;
7902    while (!step.end()) {
7903      // TODO: make z-grid lines really 3d?
7904      from = this._convert3Dto2D(new Point3d(xText, yText, step.getCurrent()));
7905      ctx.strokeStyle = this.axisColor;
7906      ctx.beginPath();
7907      ctx.moveTo(from.x, from.y);
7908      ctx.lineTo(from.x - textMargin, from.y);
7909      ctx.stroke();
7910
7911      ctx.textAlign = 'right';
7912      ctx.textBaseline = 'middle';
7913      ctx.fillStyle = this.axisColor;
7914      ctx.fillText(this.zValueLabel(step.getCurrent()) + ' ', from.x - 5, from.y);
7915
7916      step.next();
7917    }
7918    ctx.lineWidth = 1;
7919    from = this._convert3Dto2D(new Point3d(xText, yText, this.zMin));
7920    to = this._convert3Dto2D(new Point3d(xText, yText, this.zMax));
7921    ctx.strokeStyle = this.axisColor;
7922    ctx.beginPath();
7923    ctx.moveTo(from.x, from.y);
7924    ctx.lineTo(to.x, to.y);
7925    ctx.stroke();
7926
7927    // draw x-axis
7928    ctx.lineWidth = 1;
7929    // line at yMin
7930    xMin2d = this._convert3Dto2D(new Point3d(this.xMin, this.yMin, this.zMin));
7931    xMax2d = this._convert3Dto2D(new Point3d(this.xMax, this.yMin, this.zMin));
7932    ctx.strokeStyle = this.axisColor;
7933    ctx.beginPath();
7934    ctx.moveTo(xMin2d.x, xMin2d.y);
7935    ctx.lineTo(xMax2d.x, xMax2d.y);
7936    ctx.stroke();
7937    // line at ymax
7938    xMin2d = this._convert3Dto2D(new Point3d(this.xMin, this.yMax, this.zMin));
7939    xMax2d = this._convert3Dto2D(new Point3d(this.xMax, this.yMax, this.zMin));
7940    ctx.strokeStyle = this.axisColor;
7941    ctx.beginPath();
7942    ctx.moveTo(xMin2d.x, xMin2d.y);
7943    ctx.lineTo(xMax2d.x, xMax2d.y);
7944    ctx.stroke();
7945
7946    // draw y-axis
7947    ctx.lineWidth = 1;
7948    // line at xMin
7949    from = this._convert3Dto2D(new Point3d(this.xMin, this.yMin, this.zMin));
7950    to = this._convert3Dto2D(new Point3d(this.xMin, this.yMax, this.zMin));
7951    ctx.strokeStyle = this.axisColor;
7952    ctx.beginPath();
7953    ctx.moveTo(from.x, from.y);
7954    ctx.lineTo(to.x, to.y);
7955    ctx.stroke();
7956    // line at xMax
7957    from = this._convert3Dto2D(new Point3d(this.xMax, this.yMin, this.zMin));
7958    to = this._convert3Dto2D(new Point3d(this.xMax, this.yMax, this.zMin));
7959    ctx.strokeStyle = this.axisColor;
7960    ctx.beginPath();
7961    ctx.moveTo(from.x, from.y);
7962    ctx.lineTo(to.x, to.y);
7963    ctx.stroke();
7964
7965    // draw x-label
7966    var xLabel = this.xLabel;
7967    if (xLabel.length > 0) {
7968      yOffset = 0.1 / this.scale.y;
7969      xText = (this.xMin + this.xMax) / 2;
7970      yText = Math.cos(armAngle) > 0 ? this.yMin - yOffset : this.yMax + yOffset;
7971      text = this._convert3Dto2D(new Point3d(xText, yText, this.zMin));
7972      if (Math.cos(armAngle * 2) > 0) {
7973        ctx.textAlign = 'center';
7974        ctx.textBaseline = 'top';
7975      } else if (Math.sin(armAngle * 2) < 0) {
7976        ctx.textAlign = 'right';
7977        ctx.textBaseline = 'middle';
7978      } else {
7979        ctx.textAlign = 'left';
7980        ctx.textBaseline = 'middle';
7981      }
7982      ctx.fillStyle = this.axisColor;
7983      ctx.fillText(xLabel, text.x, text.y);
7984    }
7985
7986    // draw y-label
7987    var yLabel = this.yLabel;
7988    if (yLabel.length > 0) {
7989      xOffset = 0.1 / this.scale.x;
7990      xText = Math.sin(armAngle) > 0 ? this.xMin - xOffset : this.xMax + xOffset;
7991      yText = (this.yMin + this.yMax) / 2;
7992      text = this._convert3Dto2D(new Point3d(xText, yText, this.zMin));
7993      if (Math.cos(armAngle * 2) < 0) {
7994        ctx.textAlign = 'center';
7995        ctx.textBaseline = 'top';
7996      } else if (Math.sin(armAngle * 2) > 0) {
7997        ctx.textAlign = 'right';
7998        ctx.textBaseline = 'middle';
7999      } else {
8000        ctx.textAlign = 'left';
8001        ctx.textBaseline = 'middle';
8002      }
8003      ctx.fillStyle = this.axisColor;
8004      ctx.fillText(yLabel, text.x, text.y);
8005    }
8006
8007    // draw z-label
8008    var zLabel = this.zLabel;
8009    if (zLabel.length > 0) {
8010      offset = 30; // pixels.  // TODO: relate to the max width of the values on the z axis?
8011      xText = Math.cos(armAngle) > 0 ? this.xMin : this.xMax;
8012      yText = Math.sin(armAngle) < 0 ? this.yMin : this.yMax;
8013      zText = (this.zMin + this.zMax) / 2;
8014      text = this._convert3Dto2D(new Point3d(xText, yText, zText));
8015      ctx.textAlign = 'right';
8016      ctx.textBaseline = 'middle';
8017      ctx.fillStyle = this.axisColor;
8018      ctx.fillText(zLabel, text.x - offset, text.y);
8019    }
8020  };
8021
8022  /**
8023   * Calculate the color based on the given value.
8024   * @param {Number} H   Hue, a value be between 0 and 360
8025   * @param {Number} S   Saturation, a value between 0 and 1
8026   * @param {Number} V   Value, a value between 0 and 1
8027   */
8028  Graph3d.prototype._hsv2rgb = function (H, S, V) {
8029    var R, G, B, C, Hi, X;
8030
8031    C = V * S;
8032    Hi = Math.floor(H / 60); // hi = 0,1,2,3,4,5
8033    X = C * (1 - Math.abs(H / 60 % 2 - 1));
8034
8035    switch (Hi) {
8036      case 0:
8037        R = C;G = X;B = 0;break;
8038      case 1:
8039        R = X;G = C;B = 0;break;
8040      case 2:
8041        R = 0;G = C;B = X;break;
8042      case 3:
8043        R = 0;G = X;B = C;break;
8044      case 4:
8045        R = X;G = 0;B = C;break;
8046      case 5:
8047        R = C;G = 0;B = X;break;
8048
8049      default:
8050        R = 0;G = 0;B = 0;break;
8051    }
8052
8053    return 'RGB(' + parseInt(R * 255) + ',' + parseInt(G * 255) + ',' + parseInt(B * 255) + ')';
8054  };
8055
8056  /**
8057   * Draw all datapoints as a grid
8058   * This function can be used when the style is 'grid'
8059   */
8060  Graph3d.prototype._redrawDataGrid = function () {
8061    var canvas = this.frame.canvas,
8062        ctx = canvas.getContext('2d'),
8063        point,
8064        right,
8065        top,
8066        cross,
8067        i,
8068        topSideVisible,
8069        fillStyle,
8070        strokeStyle,
8071        lineWidth,
8072        h,
8073        s,
8074        v,
8075        zAvg;
8076
8077    ctx.lineJoin = 'round';
8078    ctx.lineCap = 'round';
8079
8080    if (this.dataPoints === undefined || this.dataPoints.length <= 0) return; // TODO: throw exception?
8081
8082    // calculate the translations and screen position of all points
8083    for (i = 0; i < this.dataPoints.length; i++) {
8084      var trans = this._convertPointToTranslation(this.dataPoints[i].point);
8085      var screen = this._convertTranslationToScreen(trans);
8086
8087      this.dataPoints[i].trans = trans;
8088      this.dataPoints[i].screen = screen;
8089
8090      // calculate the translation of the point at the bottom (needed for sorting)
8091      var transBottom = this._convertPointToTranslation(this.dataPoints[i].bottom);
8092      this.dataPoints[i].dist = this.showPerspective ? transBottom.length() : -transBottom.z;
8093    }
8094
8095    // sort the points on depth of their (x,y) position (not on z)
8096    var sortDepth = function sortDepth(a, b) {
8097      return b.dist - a.dist;
8098    };
8099    this.dataPoints.sort(sortDepth);
8100
8101    if (this.style === Graph3d.STYLE.SURFACE) {
8102      for (i = 0; i < this.dataPoints.length; i++) {
8103        point = this.dataPoints[i];
8104        right = this.dataPoints[i].pointRight;
8105        top = this.dataPoints[i].pointTop;
8106        cross = this.dataPoints[i].pointCross;
8107
8108        if (point !== undefined && right !== undefined && top !== undefined && cross !== undefined) {
8109
8110          if (this.showGrayBottom || this.showShadow) {
8111            // calculate the cross product of the two vectors from center
8112            // to left and right, in order to know whether we are looking at the
8113            // bottom or at the top side. We can also use the cross product
8114            // for calculating light intensity
8115            var aDiff = Point3d.subtract(cross.trans, point.trans);
8116            var bDiff = Point3d.subtract(top.trans, right.trans);
8117            var crossproduct = Point3d.crossProduct(aDiff, bDiff);
8118            var len = crossproduct.length();
8119            // FIXME: there is a bug with determining the surface side (shadow or colored)
8120
8121            topSideVisible = crossproduct.z > 0;
8122          } else {
8123            topSideVisible = true;
8124          }
8125
8126          if (topSideVisible) {
8127            // calculate Hue from the current value. At zMin the hue is 240, at zMax the hue is 0
8128            zAvg = (point.point.z + right.point.z + top.point.z + cross.point.z) / 4;
8129            h = (1 - (zAvg - this.zMin) * this.scale.z / this.verticalRatio) * 240;
8130            s = 1; // saturation
8131
8132            if (this.showShadow) {
8133              v = Math.min(1 + crossproduct.x / len / 2, 1); // value. TODO: scale
8134              fillStyle = this._hsv2rgb(h, s, v);
8135              strokeStyle = fillStyle;
8136            } else {
8137              v = 1;
8138              fillStyle = this._hsv2rgb(h, s, v);
8139              strokeStyle = this.axisColor; // TODO: should be customizable
8140            }
8141          } else {
8142            fillStyle = 'gray';
8143            strokeStyle = this.axisColor;
8144          }
8145
8146          ctx.lineWidth = this._getStrokeWidth(point);
8147          ctx.fillStyle = fillStyle;
8148          ctx.strokeStyle = strokeStyle;
8149          ctx.beginPath();
8150          ctx.moveTo(point.screen.x, point.screen.y);
8151          ctx.lineTo(right.screen.x, right.screen.y);
8152          ctx.lineTo(cross.screen.x, cross.screen.y);
8153          ctx.lineTo(top.screen.x, top.screen.y);
8154          ctx.closePath();
8155          ctx.fill();
8156          ctx.stroke(); // TODO: only draw stroke when strokeWidth > 0
8157        }
8158      }
8159    } else {
8160      // grid style
8161      for (i = 0; i < this.dataPoints.length; i++) {
8162        point = this.dataPoints[i];
8163        right = this.dataPoints[i].pointRight;
8164        top = this.dataPoints[i].pointTop;
8165
8166        if (point !== undefined && right !== undefined) {
8167          // calculate Hue from the current value. At zMin the hue is 240, at zMax the hue is 0
8168          zAvg = (point.point.z + right.point.z) / 2;
8169          h = (1 - (zAvg - this.zMin) * this.scale.z / this.verticalRatio) * 240;
8170
8171          ctx.lineWidth = this._getStrokeWidth(point) * 2;
8172          ctx.strokeStyle = this._hsv2rgb(h, 1, 1);
8173          ctx.beginPath();
8174          ctx.moveTo(point.screen.x, point.screen.y);
8175          ctx.lineTo(right.screen.x, right.screen.y);
8176          ctx.stroke();
8177        }
8178
8179        if (point !== undefined && top !== undefined) {
8180          // calculate Hue from the current value. At zMin the hue is 240, at zMax the hue is 0
8181          zAvg = (point.point.z + top.point.z) / 2;
8182          h = (1 - (zAvg - this.zMin) * this.scale.z / this.verticalRatio) * 240;
8183
8184          ctx.lineWidth = this._getStrokeWidth(point) * 2;
8185          ctx.strokeStyle = this._hsv2rgb(h, 1, 1);
8186          ctx.beginPath();
8187          ctx.moveTo(point.screen.x, point.screen.y);
8188          ctx.lineTo(top.screen.x, top.screen.y);
8189          ctx.stroke();
8190        }
8191      }
8192    }
8193  };
8194
8195  Graph3d.prototype._getStrokeWidth = function (point) {
8196    if (point !== undefined) {
8197      if (this.showPerspective) {
8198        return 1 / -point.trans.z * this.dataColor.strokeWidth;
8199      } else {
8200        return -(this.eye.z / this.camera.getArmLength()) * this.dataColor.strokeWidth;
8201      }
8202    }
8203
8204    return this.dataColor.strokeWidth;
8205  };
8206
8207  /**
8208   * Draw all datapoints as dots.
8209   * This function can be used when the style is 'dot' or 'dot-line'
8210   */
8211  Graph3d.prototype._redrawDataDot = function () {
8212    var canvas = this.frame.canvas;
8213    var ctx = canvas.getContext('2d');
8214    var i;
8215
8216    if (this.dataPoints === undefined || this.dataPoints.length <= 0) return; // TODO: throw exception?
8217
8218    // calculate the translations of all points
8219    for (i = 0; i < this.dataPoints.length; i++) {
8220      var trans = this._convertPointToTranslation(this.dataPoints[i].point);
8221      var screen = this._convertTranslationToScreen(trans);
8222      this.dataPoints[i].trans = trans;
8223      this.dataPoints[i].screen = screen;
8224
8225      // calculate the distance from the point at the bottom to the camera
8226      var transBottom = this._convertPointToTranslation(this.dataPoints[i].bottom);
8227      this.dataPoints[i].dist = this.showPerspective ? transBottom.length() : -transBottom.z;
8228    }
8229
8230    // order the translated points by depth
8231    var sortDepth = function sortDepth(a, b) {
8232      return b.dist - a.dist;
8233    };
8234    this.dataPoints.sort(sortDepth);
8235
8236    // draw the datapoints as colored circles
8237    var dotSize = this.frame.clientWidth * 0.02; // px
8238    for (i = 0; i < this.dataPoints.length; i++) {
8239      var point = this.dataPoints[i];
8240
8241      if (this.style === Graph3d.STYLE.DOTLINE) {
8242        // draw a vertical line from the bottom to the graph value
8243        //var from = this._convert3Dto2D(new Point3d(point.point.x, point.point.y, this.zMin));
8244        var from = this._convert3Dto2D(point.bottom);
8245        ctx.lineWidth = 1;
8246        ctx.strokeStyle = this.gridColor;
8247        ctx.beginPath();
8248        ctx.moveTo(from.x, from.y);
8249        ctx.lineTo(point.screen.x, point.screen.y);
8250        ctx.stroke();
8251      }
8252
8253      // calculate radius for the circle
8254      var size;
8255      if (this.style === Graph3d.STYLE.DOTSIZE) {
8256        size = dotSize / 2 + 2 * dotSize * (point.point.value - this.valueMin) / (this.valueMax - this.valueMin);
8257      } else {
8258        size = dotSize;
8259      }
8260
8261      var radius;
8262      if (this.showPerspective) {
8263        radius = size / -point.trans.z;
8264      } else {
8265        radius = size * -(this.eye.z / this.camera.getArmLength());
8266      }
8267      if (radius < 0) {
8268        radius = 0;
8269      }
8270
8271      var hue, color, borderColor;
8272      if (this.style === Graph3d.STYLE.DOTCOLOR) {
8273        // calculate the color based on the value
8274        hue = (1 - (point.point.value - this.valueMin) * this.scale.value) * 240;
8275        color = this._hsv2rgb(hue, 1, 1);
8276        borderColor = this._hsv2rgb(hue, 1, 0.8);
8277      } else if (this.style === Graph3d.STYLE.DOTSIZE) {
8278        color = this.dataColor.fill;
8279        borderColor = this.dataColor.stroke;
8280      } else {
8281        // calculate Hue from the current value. At zMin the hue is 240, at zMax the hue is 0
8282        hue = (1 - (point.point.z - this.zMin) * this.scale.z / this.verticalRatio) * 240;
8283        color = this._hsv2rgb(hue, 1, 1);
8284        borderColor = this._hsv2rgb(hue, 1, 0.8);
8285      }
8286
8287      // draw the circle
8288      ctx.lineWidth = this._getStrokeWidth(point);
8289      ctx.strokeStyle = borderColor;
8290      ctx.fillStyle = color;
8291      ctx.beginPath();
8292      ctx.arc(point.screen.x, point.screen.y, radius, 0, Math.PI * 2, true);
8293      ctx.fill();
8294      ctx.stroke();
8295    }
8296  };
8297
8298  /**
8299   * Draw all datapoints as bars.
8300   * This function can be used when the style is 'bar', 'bar-color', or 'bar-size'
8301   */
8302  Graph3d.prototype._redrawDataBar = function () {
8303    var canvas = this.frame.canvas;
8304    var ctx = canvas.getContext('2d');
8305    var i, j, surface, corners;
8306
8307    if (this.dataPoints === undefined || this.dataPoints.length <= 0) return; // TODO: throw exception?
8308
8309    // calculate the translations of all points
8310    for (i = 0; i < this.dataPoints.length; i++) {
8311      var trans = this._convertPointToTranslation(this.dataPoints[i].point);
8312      var screen = this._convertTranslationToScreen(trans);
8313      this.dataPoints[i].trans = trans;
8314      this.dataPoints[i].screen = screen;
8315
8316      // calculate the distance from the point at the bottom to the camera
8317      var transBottom = this._convertPointToTranslation(this.dataPoints[i].bottom);
8318      this.dataPoints[i].dist = this.showPerspective ? transBottom.length() : -transBottom.z;
8319    }
8320
8321    // order the translated points by depth
8322    var sortDepth = function sortDepth(a, b) {
8323      return b.dist - a.dist;
8324    };
8325    this.dataPoints.sort(sortDepth);
8326
8327    ctx.lineJoin = 'round';
8328    ctx.lineCap = 'round';
8329
8330    // draw the datapoints as bars
8331    var xWidth = this.xBarWidth / 2;
8332    var yWidth = this.yBarWidth / 2;
8333    for (i = 0; i < this.dataPoints.length; i++) {
8334      var point = this.dataPoints[i];
8335
8336      // determine color
8337      var hue, color, borderColor;
8338      if (this.style === Graph3d.STYLE.BARCOLOR) {
8339        // calculate the color based on the value
8340        hue = (1 - (point.point.value - this.valueMin) * this.scale.value) * 240;
8341        color = this._hsv2rgb(hue, 1, 1);
8342        borderColor = this._hsv2rgb(hue, 1, 0.8);
8343      } else if (this.style === Graph3d.STYLE.BARSIZE) {
8344        color = this.dataColor.fill;
8345        borderColor = this.dataColor.stroke;
8346      } else {
8347        // calculate Hue from the current value. At zMin the hue is 240, at zMax the hue is 0
8348        hue = (1 - (point.point.z - this.zMin) * this.scale.z / this.verticalRatio) * 240;
8349        color = this._hsv2rgb(hue, 1, 1);
8350        borderColor = this._hsv2rgb(hue, 1, 0.8);
8351      }
8352
8353      // calculate size for the bar
8354      if (this.style === Graph3d.STYLE.BARSIZE) {
8355        xWidth = this.xBarWidth / 2 * ((point.point.value - this.valueMin) / (this.valueMax - this.valueMin) * 0.8 + 0.2);
8356        yWidth = this.yBarWidth / 2 * ((point.point.value - this.valueMin) / (this.valueMax - this.valueMin) * 0.8 + 0.2);
8357      }
8358
8359      // calculate all corner points
8360      var me = this;
8361      var point3d = point.point;
8362      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) }];
8363      var bottom = [{ point: new Point3d(point3d.x - xWidth, point3d.y - yWidth, this.zMin) }, { point: new Point3d(point3d.x + xWidth, point3d.y - yWidth, this.zMin) }, { point: new Point3d(point3d.x + xWidth, point3d.y + yWidth, this.zMin) }, { point: new Point3d(point3d.x - xWidth, point3d.y + yWidth, this.zMin) }];
8364
8365      // calculate screen location of the points
8366      top.forEach(function (obj) {
8367        obj.screen = me._convert3Dto2D(obj.point);
8368      });
8369      bottom.forEach(function (obj) {
8370        obj.screen = me._convert3Dto2D(obj.point);
8371      });
8372
8373      // create five sides, calculate both corner points and center points
8374      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) }];
8375      point.surfaces = surfaces;
8376
8377      // calculate the distance of each of the surface centers to the camera
8378      for (j = 0; j < surfaces.length; j++) {
8379        surface = surfaces[j];
8380        var transCenter = this._convertPointToTranslation(surface.center);
8381        surface.dist = this.showPerspective ? transCenter.length() : -transCenter.z;
8382        // TODO: this dept calculation doesn't work 100% of the cases due to perspective,
8383        //     but the current solution is fast/simple and works in 99.9% of all cases
8384        //     the issue is visible in example 14, with graph.setCameraPosition({horizontal: 2.97, vertical: 0.5, distance: 0.9})
8385      }
8386
8387      // order the surfaces by their (translated) depth
8388      surfaces.sort(function (a, b) {
8389        var diff = b.dist - a.dist;
8390        if (diff) return diff;
8391
8392        // if equal depth, sort the top surface last
8393        if (a.corners === top) return 1;
8394        if (b.corners === top) return -1;
8395
8396        // both are equal
8397        return 0;
8398      });
8399
8400      // draw the ordered surfaces
8401      ctx.lineWidth = this._getStrokeWidth(point);
8402      ctx.strokeStyle = borderColor;
8403      ctx.fillStyle = color;
8404      // NOTE: we start at j=2 instead of j=0 as we don't need to draw the two surfaces at the backside
8405      for (j = 2; j < surfaces.length; j++) {
8406        surface = surfaces[j];
8407        corners = surface.corners;
8408        ctx.beginPath();
8409        ctx.moveTo(corners[3].screen.x, corners[3].screen.y);
8410        ctx.lineTo(corners[0].screen.x, corners[0].screen.y);
8411        ctx.lineTo(corners[1].screen.x, corners[1].screen.y);
8412        ctx.lineTo(corners[2].screen.x, corners[2].screen.y);
8413        ctx.lineTo(corners[3].screen.x, corners[3].screen.y);
8414        ctx.fill();
8415        ctx.stroke();
8416      }
8417    }
8418  };
8419
8420  /**
8421   * Draw a line through all datapoints.
8422   * This function can be used when the style is 'line'
8423   */
8424  Graph3d.prototype._redrawDataLine = function () {
8425    var canvas = this.frame.canvas,
8426        ctx = canvas.getContext('2d'),
8427        point,
8428        i;
8429
8430    if (this.dataPoints === undefined || this.dataPoints.length <= 0) return; // TODO: throw exception?
8431
8432    // calculate the translations of all points
8433    for (i = 0; i < this.dataPoints.length; i++) {
8434      var trans = this._convertPointToTranslation(this.dataPoints[i].point);
8435      var screen = this._convertTranslationToScreen(trans);
8436
8437      this.dataPoints[i].trans = trans;
8438      this.dataPoints[i].screen = screen;
8439    }
8440
8441    // start the line
8442    if (this.dataPoints.length > 0) {
8443      point = this.dataPoints[0];
8444
8445      ctx.lineWidth = this._getStrokeWidth(point);
8446      ctx.lineJoin = 'round';
8447      ctx.lineCap = 'round';
8448      ctx.strokeStyle = this.dataColor.stroke;
8449      ctx.beginPath();
8450      ctx.moveTo(point.screen.x, point.screen.y);
8451
8452      // draw the datapoints as colored circles
8453      for (i = 1; i < this.dataPoints.length; i++) {
8454        point = this.dataPoints[i];
8455        ctx.lineTo(point.screen.x, point.screen.y);
8456      }
8457
8458      // finish the line
8459      ctx.stroke();
8460    }
8461  };
8462
8463  /**
8464   * Start a moving operation inside the provided parent element
8465   * @param {Event}     event     The event that occurred (required for
8466   *                  retrieving the  mouse position)
8467   */
8468  Graph3d.prototype._onMouseDown = function (event) {
8469    event = event || window.event;
8470
8471    // check if mouse is still down (may be up when focus is lost for example
8472    // in an iframe)
8473    if (this.leftButtonDown) {
8474      this._onMouseUp(event);
8475    }
8476
8477    // only react on left mouse button down
8478    this.leftButtonDown = event.which ? event.which === 1 : event.button === 1;
8479    if (!this.leftButtonDown && !this.touchDown) return;
8480
8481    // get mouse position (different code for IE and all other browsers)
8482    this.startMouseX = getMouseX(event);
8483    this.startMouseY = getMouseY(event);
8484
8485    this.startStart = new Date(this.start);
8486    this.startEnd = new Date(this.end);
8487    this.startArmRotation = this.camera.getArmRotation();
8488
8489    this.frame.style.cursor = 'move';
8490
8491    // add event listeners to handle moving the contents
8492    // we store the function onmousemove and onmouseup in the graph, so we can
8493    // remove the eventlisteners lateron in the function mouseUp()
8494    var me = this;
8495    this.onmousemove = function (event) {
8496      me._onMouseMove(event);
8497    };
8498    this.onmouseup = function (event) {
8499      me._onMouseUp(event);
8500    };
8501    util.addEventListener(document, 'mousemove', me.onmousemove);
8502    util.addEventListener(document, 'mouseup', me.onmouseup);
8503    util.preventDefault(event);
8504  };
8505
8506  /**
8507   * Perform moving operating.
8508   * This function activated from within the funcion Graph.mouseDown().
8509   * @param {Event}   event  Well, eehh, the event
8510   */
8511  Graph3d.prototype._onMouseMove = function (event) {
8512    event = event || window.event;
8513
8514    // calculate change in mouse position
8515    var diffX = parseFloat(getMouseX(event)) - this.startMouseX;
8516    var diffY = parseFloat(getMouseY(event)) - this.startMouseY;
8517
8518    var horizontalNew = this.startArmRotation.horizontal + diffX / 200;
8519    var verticalNew = this.startArmRotation.vertical + diffY / 200;
8520
8521    var snapAngle = 4; // degrees
8522    var snapValue = Math.sin(snapAngle / 360 * 2 * Math.PI);
8523
8524    // snap horizontally to nice angles at 0pi, 0.5pi, 1pi, 1.5pi, etc...
8525    // the -0.001 is to take care that the vertical axis is always drawn at the left front corner
8526    if (Math.abs(Math.sin(horizontalNew)) < snapValue) {
8527      horizontalNew = Math.round(horizontalNew / Math.PI) * Math.PI - 0.001;
8528    }
8529    if (Math.abs(Math.cos(horizontalNew)) < snapValue) {
8530      horizontalNew = (Math.round(horizontalNew / Math.PI - 0.5) + 0.5) * Math.PI - 0.001;
8531    }
8532
8533    // snap vertically to nice angles
8534    if (Math.abs(Math.sin(verticalNew)) < snapValue) {
8535      verticalNew = Math.round(verticalNew / Math.PI) * Math.PI;
8536    }
8537    if (Math.abs(Math.cos(verticalNew)) < snapValue) {
8538      verticalNew = (Math.round(verticalNew / Math.PI - 0.5) + 0.5) * Math.PI;
8539    }
8540
8541    this.camera.setArmRotation(horizontalNew, verticalNew);
8542    this.redraw();
8543
8544    // fire a cameraPositionChange event
8545    var parameters = this.getCameraPosition();
8546    this.emit('cameraPositionChange', parameters);
8547
8548    util.preventDefault(event);
8549  };
8550
8551  /**
8552   * Stop moving operating.
8553   * This function activated from within the funcion Graph.mouseDown().
8554   * @param {event}  event   The event
8555   */
8556  Graph3d.prototype._onMouseUp = function (event) {
8557    this.frame.style.cursor = 'auto';
8558    this.leftButtonDown = false;
8559
8560    // remove event listeners here
8561    util.removeEventListener(document, 'mousemove', this.onmousemove);
8562    util.removeEventListener(document, 'mouseup', this.onmouseup);
8563    util.preventDefault(event);
8564  };
8565
8566  /**
8567   * After having moved the mouse, a tooltip should pop up when the mouse is resting on a data point
8568   * @param {Event}  event   A mouse move event
8569   */
8570  Graph3d.prototype._onTooltip = function (event) {
8571    var delay = 300; // ms
8572    var boundingRect = this.frame.getBoundingClientRect();
8573    var mouseX = getMouseX(event) - boundingRect.left;
8574    var mouseY = getMouseY(event) - boundingRect.top;
8575
8576    if (!this.showTooltip) {
8577      return;
8578    }
8579
8580    if (this.tooltipTimeout) {
8581      clearTimeout(this.tooltipTimeout);
8582    }
8583
8584    // (delayed) display of a tooltip only if no mouse button is down
8585    if (this.leftButtonDown) {
8586      this._hideTooltip();
8587      return;
8588    }
8589
8590    if (this.tooltip && this.tooltip.dataPoint) {
8591      // tooltip is currently visible
8592      var dataPoint = this._dataPointFromXY(mouseX, mouseY);
8593      if (dataPoint !== this.tooltip.dataPoint) {
8594        // datapoint changed
8595        if (dataPoint) {
8596          this._showTooltip(dataPoint);
8597        } else {
8598          this._hideTooltip();
8599        }
8600      }
8601    } else {
8602      // tooltip is currently not visible
8603      var me = this;
8604      this.tooltipTimeout = setTimeout(function () {
8605        me.tooltipTimeout = null;
8606
8607        // show a tooltip if we have a data point
8608        var dataPoint = me._dataPointFromXY(mouseX, mouseY);
8609        if (dataPoint) {
8610          me._showTooltip(dataPoint);
8611        }
8612      }, delay);
8613    }
8614  };
8615
8616  /**
8617   * Event handler for touchstart event on mobile devices
8618   */
8619  Graph3d.prototype._onTouchStart = function (event) {
8620    this.touchDown = true;
8621
8622    var me = this;
8623    this.ontouchmove = function (event) {
8624      me._onTouchMove(event);
8625    };
8626    this.ontouchend = function (event) {
8627      me._onTouchEnd(event);
8628    };
8629    util.addEventListener(document, 'touchmove', me.ontouchmove);
8630    util.addEventListener(document, 'touchend', me.ontouchend);
8631
8632    this._onMouseDown(event);
8633  };
8634
8635  /**
8636   * Event handler for touchmove event on mobile devices
8637   */
8638  Graph3d.prototype._onTouchMove = function (event) {
8639    this._onMouseMove(event);
8640  };
8641
8642  /**
8643   * Event handler for touchend event on mobile devices
8644   */
8645  Graph3d.prototype._onTouchEnd = function (event) {
8646    this.touchDown = false;
8647
8648    util.removeEventListener(document, 'touchmove', this.ontouchmove);
8649    util.removeEventListener(document, 'touchend', this.ontouchend);
8650
8651    this._onMouseUp(event);
8652  };
8653
8654  /**
8655   * Event handler for mouse wheel event, used to zoom the graph
8656   * Code from http://adomas.org/javascript-mouse-wheel/
8657   * @param {event}  event   The event
8658   */
8659  Graph3d.prototype._onWheel = function (event) {
8660    if (!event) /* For IE. */
8661      event = window.event;
8662
8663    // retrieve delta
8664    var delta = 0;
8665    if (event.wheelDelta) {
8666      /* IE/Opera. */
8667      delta = event.wheelDelta / 120;
8668    } else if (event.detail) {
8669      /* Mozilla case. */
8670      // In Mozilla, sign of delta is different than in IE.
8671      // Also, delta is multiple of 3.
8672      delta = -event.detail / 3;
8673    }
8674
8675    // If delta is nonzero, handle it.
8676    // Basically, delta is now positive if wheel was scrolled up,
8677    // and negative, if wheel was scrolled down.
8678    if (delta) {
8679      var oldLength = this.camera.getArmLength();
8680      var newLength = oldLength * (1 - delta / 10);
8681
8682      this.camera.setArmLength(newLength);
8683      this.redraw();
8684
8685      this._hideTooltip();
8686    }
8687
8688    // fire a cameraPositionChange event
8689    var parameters = this.getCameraPosition();
8690    this.emit('cameraPositionChange', parameters);
8691
8692    // Prevent default actions caused by mouse wheel.
8693    // That might be ugly, but we handle scrolls somehow
8694    // anyway, so don't bother here..
8695    util.preventDefault(event);
8696  };
8697
8698  /**
8699   * Test whether a point lies inside given 2D triangle
8700   * @param {Point2d} point
8701   * @param {Point2d[]} triangle
8702   * @return {boolean} Returns true if given point lies inside or on the edge of the triangle
8703   * @private
8704   */
8705  Graph3d.prototype._insideTriangle = function (point, triangle) {
8706    var a = triangle[0],
8707        b = triangle[1],
8708        c = triangle[2];
8709
8710    function sign(x) {
8711      return x > 0 ? 1 : x < 0 ? -1 : 0;
8712    }
8713
8714    var as = sign((b.x - a.x) * (point.y - a.y) - (b.y - a.y) * (point.x - a.x));
8715    var bs = sign((c.x - b.x) * (point.y - b.y) - (c.y - b.y) * (point.x - b.x));
8716    var cs = sign((a.x - c.x) * (point.y - c.y) - (a.y - c.y) * (point.x - c.x));
8717
8718    // each of the three signs must be either equal to each other or zero
8719    return (as == 0 || bs == 0 || as == bs) && (bs == 0 || cs == 0 || bs == cs) && (as == 0 || cs == 0 || as == cs);
8720  };
8721
8722  /**
8723   * Find a data point close to given screen position (x, y)
8724   * @param {Number} x
8725   * @param {Number} y
8726   * @return {Object | null} The closest data point or null if not close to any data point
8727   * @private
8728   */
8729  Graph3d.prototype._dataPointFromXY = function (x, y) {
8730    var i,
8731        distMax = 100,
8732        // px
8733    dataPoint = null,
8734        closestDataPoint = null,
8735        closestDist = null,
8736        center = new Point2d(x, y);
8737
8738    if (this.style === Graph3d.STYLE.BAR || this.style === Graph3d.STYLE.BARCOLOR || this.style === Graph3d.STYLE.BARSIZE) {
8739      // the data points are ordered from far away to closest
8740      for (i = this.dataPoints.length - 1; i >= 0; i--) {
8741        dataPoint = this.dataPoints[i];
8742        var surfaces = dataPoint.surfaces;
8743        if (surfaces) {
8744          for (var s = surfaces.length - 1; s >= 0; s--) {
8745            // split each surface in two triangles, and see if the center point is inside one of these
8746            var surface = surfaces[s];
8747            var corners = surface.corners;
8748            var triangle1 = [corners[0].screen, corners[1].screen, corners[2].screen];
8749            var triangle2 = [corners[2].screen, corners[3].screen, corners[0].screen];
8750            if (this._insideTriangle(center, triangle1) || this._insideTriangle(center, triangle2)) {
8751              // return immediately at the first hit
8752              return dataPoint;
8753            }
8754          }
8755        }
8756      }
8757    } else {
8758      // find the closest data point, using distance to the center of the point on 2d screen
8759      for (i = 0; i < this.dataPoints.length; i++) {
8760        dataPoint = this.dataPoints[i];
8761        var point = dataPoint.screen;
8762        if (point) {
8763          var distX = Math.abs(x - point.x);
8764          var distY = Math.abs(y - point.y);
8765          var dist = Math.sqrt(distX * distX + distY * distY);
8766
8767          if ((closestDist === null || dist < closestDist) && dist < distMax) {
8768            closestDist = dist;
8769            closestDataPoint = dataPoint;
8770          }
8771        }
8772      }
8773    }
8774
8775    return closestDataPoint;
8776  };
8777
8778  /**
8779   * Display a tooltip for given data point
8780   * @param {Object} dataPoint
8781   * @private
8782   */
8783  Graph3d.prototype._showTooltip = function (dataPoint) {
8784    var content, line, dot;
8785
8786    if (!this.tooltip) {
8787      content = document.createElement('div');
8788      content.style.position = 'absolute';
8789      content.style.padding = '10px';
8790      content.style.border = '1px solid #4d4d4d';
8791      content.style.color = '#1a1a1a';
8792      content.style.background = 'rgba(255,255,255,0.7)';
8793      content.style.borderRadius = '2px';
8794      content.style.boxShadow = '5px 5px 10px rgba(128,128,128,0.5)';
8795
8796      line = document.createElement('div');
8797      line.style.position = 'absolute';
8798      line.style.height = '40px';
8799      line.style.width = '0';
8800      line.style.borderLeft = '1px solid #4d4d4d';
8801
8802      dot = document.createElement('div');
8803      dot.style.position = 'absolute';
8804      dot.style.height = '0';
8805      dot.style.width = '0';
8806      dot.style.border = '5px solid #4d4d4d';
8807      dot.style.borderRadius = '5px';
8808
8809      this.tooltip = {
8810        dataPoint: null,
8811        dom: {
8812          content: content,
8813          line: line,
8814          dot: dot
8815        }
8816      };
8817    } else {
8818      content = this.tooltip.dom.content;
8819      line = this.tooltip.dom.line;
8820      dot = this.tooltip.dom.dot;
8821    }
8822
8823    this._hideTooltip();
8824
8825    this.tooltip.dataPoint = dataPoint;
8826    if (typeof this.showTooltip === 'function') {
8827      content.innerHTML = this.showTooltip(dataPoint.point);
8828    } else {
8829      content.innerHTML = '<table>' + '<tr><td>x:</td><td>' + dataPoint.point.x + '</td></tr>' + '<tr><td>y:</td><td>' + dataPoint.point.y + '</td></tr>' + '<tr><td>z:</td><td>' + dataPoint.point.z + '</td></tr>' + '</table>';
8830    }
8831
8832    content.style.left = '0';
8833    content.style.top = '0';
8834    this.frame.appendChild(content);
8835    this.frame.appendChild(line);
8836    this.frame.appendChild(dot);
8837
8838    // calculate sizes
8839    var contentWidth = content.offsetWidth;
8840    var contentHeight = content.offsetHeight;
8841    var lineHeight = line.offsetHeight;
8842    var dotWidth = dot.offsetWidth;
8843    var dotHeight = dot.offsetHeight;
8844
8845    var left = dataPoint.screen.x - contentWidth / 2;
8846    left = Math.min(Math.max(left, 10), this.frame.clientWidth - 10 - contentWidth);
8847
8848    line.style.left = dataPoint.screen.x + 'px';
8849    line.style.top = dataPoint.screen.y - lineHeight + 'px';
8850    content.style.left = left + 'px';
8851    content.style.top = dataPoint.screen.y - lineHeight - contentHeight + 'px';
8852    dot.style.left = dataPoint.screen.x - dotWidth / 2 + 'px';
8853    dot.style.top = dataPoint.screen.y - dotHeight / 2 + 'px';
8854  };
8855
8856  /**
8857   * Hide the tooltip when displayed
8858   * @private
8859   */
8860  Graph3d.prototype._hideTooltip = function () {
8861    if (this.tooltip) {
8862      this.tooltip.dataPoint = null;
8863
8864      for (var prop in this.tooltip.dom) {
8865        if (this.tooltip.dom.hasOwnProperty(prop)) {
8866          var elem = this.tooltip.dom[prop];
8867          if (elem && elem.parentNode) {
8868            elem.parentNode.removeChild(elem);
8869          }
8870        }
8871      }
8872    }
8873  };
8874
8875  /**--------------------------------------------------------------------------**/
8876
8877  /**
8878   * Get the horizontal mouse position from a mouse event
8879   * @param {Event} event
8880   * @return {Number} mouse x
8881   */
8882  function getMouseX(event) {
8883    if ('clientX' in event) return event.clientX;
8884    return event.targetTouches[0] && event.targetTouches[0].clientX || 0;
8885  }
8886
8887  /**
8888   * Get the vertical mouse position from a mouse event
8889   * @param {Event} event
8890   * @return {Number} mouse y
8891   */
8892  function getMouseY(event) {
8893    if ('clientY' in event) return event.clientY;
8894    return event.targetTouches[0] && event.targetTouches[0].clientY || 0;
8895  }
8896
8897  module.exports = Graph3d;
8898
8899  // use use defaults
8900
8901/***/ },
8902/* 12 */
8903/***/ function(module, exports) {
8904
8905  
8906  /**
8907   * Expose `Emitter`.
8908   */
8909
8910  module.exports = Emitter;
8911
8912  /**
8913   * Initialize a new `Emitter`.
8914   *
8915   * @api public
8916   */
8917
8918  function Emitter(obj) {
8919    if (obj) return mixin(obj);
8920  };
8921
8922  /**
8923   * Mixin the emitter properties.
8924   *
8925   * @param {Object} obj
8926   * @return {Object}
8927   * @api private
8928   */
8929
8930  function mixin(obj) {
8931    for (var key in Emitter.prototype) {
8932      obj[key] = Emitter.prototype[key];
8933    }
8934    return obj;
8935  }
8936
8937  /**
8938   * Listen on the given `event` with `fn`.
8939   *
8940   * @param {String} event
8941   * @param {Function} fn
8942   * @return {Emitter}
8943   * @api public
8944   */
8945
8946  Emitter.prototype.on =
8947  Emitter.prototype.addEventListener = function(event, fn){
8948    this._callbacks = this._callbacks || {};
8949    (this._callbacks[event] = this._callbacks[event] || [])
8950      .push(fn);
8951    return this;
8952  };
8953
8954  /**
8955   * Adds an `event` listener that will be invoked a single
8956   * time then automatically removed.
8957   *
8958   * @param {String} event
8959   * @param {Function} fn
8960   * @return {Emitter}
8961   * @api public
8962   */
8963
8964  Emitter.prototype.once = function(event, fn){
8965    var self = this;
8966    this._callbacks = this._callbacks || {};
8967
8968    function on() {
8969      self.off(event, on);
8970      fn.apply(this, arguments);
8971    }
8972
8973    on.fn = fn;
8974    this.on(event, on);
8975    return this;
8976  };
8977
8978  /**
8979   * Remove the given callback for `event` or all
8980   * registered callbacks.
8981   *
8982   * @param {String} event
8983   * @param {Function} fn
8984   * @return {Emitter}
8985   * @api public
8986   */
8987
8988  Emitter.prototype.off =
8989  Emitter.prototype.removeListener =
8990  Emitter.prototype.removeAllListeners =
8991  Emitter.prototype.removeEventListener = function(event, fn){
8992    this._callbacks = this._callbacks || {};
8993
8994    // all
8995    if (0 == arguments.length) {
8996      this._callbacks = {};
8997      return this;
8998    }
8999
9000    // specific event
9001    var callbacks = this._callbacks[event];
9002    if (!callbacks) return this;
9003
9004    // remove all handlers
9005    if (1 == arguments.length) {
9006      delete this._callbacks[event];
9007      return this;
9008    }
9009
9010    // remove specific handler
9011    var cb;
9012    for (var i = 0; i < callbacks.length; i++) {
9013      cb = callbacks[i];
9014      if (cb === fn || cb.fn === fn) {
9015        callbacks.splice(i, 1);
9016        break;
9017      }
9018    }
9019    return this;
9020  };
9021
9022  /**
9023   * Emit `event` with the given args.
9024   *
9025   * @param {String} event
9026   * @param {Mixed} ...
9027   * @return {Emitter}
9028   */
9029
9030  Emitter.prototype.emit = function(event){
9031    this._callbacks = this._callbacks || {};
9032    var args = [].slice.call(arguments, 1)
9033      , callbacks = this._callbacks[event];
9034
9035    if (callbacks) {
9036      callbacks = callbacks.slice(0);
9037      for (var i = 0, len = callbacks.length; i < len; ++i) {
9038        callbacks[i].apply(this, args);
9039      }
9040    }
9041
9042    return this;
9043  };
9044
9045  /**
9046   * Return array of callbacks for `event`.
9047   *
9048   * @param {String} event
9049   * @return {Array}
9050   * @api public
9051   */
9052
9053  Emitter.prototype.listeners = function(event){
9054    this._callbacks = this._callbacks || {};
9055    return this._callbacks[event] || [];
9056  };
9057
9058  /**
9059   * Check if this emitter has `event` handlers.
9060   *
9061   * @param {String} event
9062   * @return {Boolean}
9063   * @api public
9064   */
9065
9066  Emitter.prototype.hasListeners = function(event){
9067    return !! this.listeners(event).length;
9068  };
9069
9070
9071/***/ },
9072/* 13 */
9073/***/ function(module, exports) {
9074
9075  /**
9076   * @prototype Point3d
9077   * @param {Number} [x]
9078   * @param {Number} [y]
9079   * @param {Number} [z]
9080   */
9081  "use strict";
9082
9083  function Point3d(x, y, z) {
9084    this.x = x !== undefined ? x : 0;
9085    this.y = y !== undefined ? y : 0;
9086    this.z = z !== undefined ? z : 0;
9087  };
9088
9089  /**
9090   * Subtract the two provided points, returns a-b
9091   * @param {Point3d} a
9092   * @param {Point3d} b
9093   * @return {Point3d} a-b
9094   */
9095  Point3d.subtract = function (a, b) {
9096    var sub = new Point3d();
9097    sub.x = a.x - b.x;
9098    sub.y = a.y - b.y;
9099    sub.z = a.z - b.z;
9100    return sub;
9101  };
9102
9103  /**
9104   * Add the two provided points, returns a+b
9105   * @param {Point3d} a
9106   * @param {Point3d} b
9107   * @return {Point3d} a+b
9108   */
9109  Point3d.add = function (a, b) {
9110    var sum = new Point3d();
9111    sum.x = a.x + b.x;
9112    sum.y = a.y + b.y;
9113    sum.z = a.z + b.z;
9114    return sum;
9115  };
9116
9117  /**
9118   * Calculate the average of two 3d points
9119   * @param {Point3d} a
9120   * @param {Point3d} b
9121   * @return {Point3d} The average, (a+b)/2
9122   */
9123  Point3d.avg = function (a, b) {
9124    return new Point3d((a.x + b.x) / 2, (a.y + b.y) / 2, (a.z + b.z) / 2);
9125  };
9126
9127  /**
9128   * Calculate the cross product of the two provided points, returns axb
9129   * Documentation: http://en.wikipedia.org/wiki/Cross_product
9130   * @param {Point3d} a
9131   * @param {Point3d} b
9132   * @return {Point3d} cross product axb
9133   */
9134  Point3d.crossProduct = function (a, b) {
9135    var crossproduct = new Point3d();
9136
9137    crossproduct.x = a.y * b.z - a.z * b.y;
9138    crossproduct.y = a.z * b.x - a.x * b.z;
9139    crossproduct.z = a.x * b.y - a.y * b.x;
9140
9141    return crossproduct;
9142  };
9143
9144  /**
9145   * Rtrieve the length of the vector (or the distance from this point to the origin
9146   * @return {Number}  length
9147   */
9148  Point3d.prototype.length = function () {
9149    return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z);
9150  };
9151
9152  module.exports = Point3d;
9153
9154/***/ },
9155/* 14 */
9156/***/ function(module, exports) {
9157
9158  /**
9159   * @prototype Point2d
9160   * @param {Number} [x]
9161   * @param {Number} [y]
9162   */
9163  "use strict";
9164
9165  function Point2d(x, y) {
9166    this.x = x !== undefined ? x : 0;
9167    this.y = y !== undefined ? y : 0;
9168  }
9169
9170  module.exports = Point2d;
9171
9172/***/ },
9173/* 15 */
9174/***/ function(module, exports, __webpack_require__) {
9175
9176  'use strict';
9177
9178  var Point3d = __webpack_require__(13);
9179
9180  /**
9181   * @class Camera
9182   * The camera is mounted on a (virtual) camera arm. The camera arm can rotate
9183   * The camera is always looking in the direction of the origin of the arm.
9184   * This way, the camera always rotates around one fixed point, the location
9185   * of the camera arm.
9186   *
9187   * Documentation:
9188   *   http://en.wikipedia.org/wiki/3D_projection
9189   */
9190  function Camera() {
9191    this.armLocation = new Point3d();
9192    this.armRotation = {};
9193    this.armRotation.horizontal = 0;
9194    this.armRotation.vertical = 0;
9195    this.armLength = 1.7;
9196
9197    this.cameraLocation = new Point3d();
9198    this.cameraRotation = new Point3d(0.5 * Math.PI, 0, 0);
9199
9200    this.calculateCameraOrientation();
9201  }
9202
9203  /**
9204   * Set the location (origin) of the arm
9205   * @param {Number} x  Normalized value of x
9206   * @param {Number} y  Normalized value of y
9207   * @param {Number} z  Normalized value of z
9208   */
9209  Camera.prototype.setArmLocation = function (x, y, z) {
9210    this.armLocation.x = x;
9211    this.armLocation.y = y;
9212    this.armLocation.z = z;
9213
9214    this.calculateCameraOrientation();
9215  };
9216
9217  /**
9218   * Set the rotation of the camera arm
9219   * @param {Number} horizontal   The horizontal rotation, between 0 and 2*PI.
9220   *                Optional, can be left undefined.
9221   * @param {Number} vertical   The vertical rotation, between 0 and 0.5*PI
9222   *                if vertical=0.5*PI, the graph is shown from the
9223   *                top. Optional, can be left undefined.
9224   */
9225  Camera.prototype.setArmRotation = function (horizontal, vertical) {
9226    if (horizontal !== undefined) {
9227      this.armRotation.horizontal = horizontal;
9228    }
9229
9230    if (vertical !== undefined) {
9231      this.armRotation.vertical = vertical;
9232      if (this.armRotation.vertical < 0) this.armRotation.vertical = 0;
9233      if (this.armRotation.vertical > 0.5 * Math.PI) this.armRotation.vertical = 0.5 * Math.PI;
9234    }
9235
9236    if (horizontal !== undefined || vertical !== undefined) {
9237      this.calculateCameraOrientation();
9238    }
9239  };
9240
9241  /**
9242   * Retrieve the current arm rotation
9243   * @return {object}   An object with parameters horizontal and vertical
9244   */
9245  Camera.prototype.getArmRotation = function () {
9246    var rot = {};
9247    rot.horizontal = this.armRotation.horizontal;
9248    rot.vertical = this.armRotation.vertical;
9249
9250    return rot;
9251  };
9252
9253  /**
9254   * Set the (normalized) length of the camera arm.
9255   * @param {Number} length A length between 0.71 and 5.0
9256   */
9257  Camera.prototype.setArmLength = function (length) {
9258    if (length === undefined) return;
9259
9260    this.armLength = length;
9261
9262    // Radius must be larger than the corner of the graph,
9263    // which has a distance of sqrt(0.5^2+0.5^2) = 0.71 from the center of the
9264    // graph
9265    if (this.armLength < 0.71) this.armLength = 0.71;
9266    if (this.armLength > 5.0) this.armLength = 5.0;
9267
9268    this.calculateCameraOrientation();
9269  };
9270
9271  /**
9272   * Retrieve the arm length
9273   * @return {Number} length
9274   */
9275  Camera.prototype.getArmLength = function () {
9276    return this.armLength;
9277  };
9278
9279  /**
9280   * Retrieve the camera location
9281   * @return {Point3d} cameraLocation
9282   */
9283  Camera.prototype.getCameraLocation = function () {
9284    return this.cameraLocation;
9285  };
9286
9287  /**
9288   * Retrieve the camera rotation
9289   * @return {Point3d} cameraRotation
9290   */
9291  Camera.prototype.getCameraRotation = function () {
9292    return this.cameraRotation;
9293  };
9294
9295  /**
9296   * Calculate the location and rotation of the camera based on the
9297   * position and orientation of the camera arm
9298   */
9299  Camera.prototype.calculateCameraOrientation = function () {
9300    // calculate location of the camera
9301    this.cameraLocation.x = this.armLocation.x - this.armLength * Math.sin(this.armRotation.horizontal) * Math.cos(this.armRotation.vertical);
9302    this.cameraLocation.y = this.armLocation.y - this.armLength * Math.cos(this.armRotation.horizontal) * Math.cos(this.armRotation.vertical);
9303    this.cameraLocation.z = this.armLocation.z + this.armLength * Math.sin(this.armRotation.vertical);
9304
9305    // calculate rotation of the camera
9306    this.cameraRotation.x = Math.PI / 2 - this.armRotation.vertical;
9307    this.cameraRotation.y = 0;
9308    this.cameraRotation.z = -this.armRotation.horizontal;
9309  };
9310
9311  module.exports = Camera;
9312
9313/***/ },
9314/* 16 */
9315/***/ function(module, exports, __webpack_require__) {
9316
9317  'use strict';
9318
9319  var DataView = __webpack_require__(10);
9320
9321  /**
9322   * @class Filter
9323   *
9324   * @param {DataSet} data The google data table
9325   * @param {Number}  column             The index of the column to be filtered
9326   * @param {Graph} graph           The graph
9327   */
9328  function Filter(data, column, graph) {
9329    this.data = data;
9330    this.column = column;
9331    this.graph = graph; // the parent graph
9332
9333    this.index = undefined;
9334    this.value = undefined;
9335
9336    // read all distinct values and select the first one
9337    this.values = graph.getDistinctValues(data.get(), this.column);
9338
9339    // sort both numeric and string values correctly
9340    this.values.sort(function (a, b) {
9341      return a > b ? 1 : a < b ? -1 : 0;
9342    });
9343
9344    if (this.values.length > 0) {
9345      this.selectValue(0);
9346    }
9347
9348    // create an array with the filtered datapoints. this will be loaded afterwards
9349    this.dataPoints = [];
9350
9351    this.loaded = false;
9352    this.onLoadCallback = undefined;
9353
9354    if (graph.animationPreload) {
9355      this.loaded = false;
9356      this.loadInBackground();
9357    } else {
9358      this.loaded = true;
9359    }
9360  };
9361
9362  /**
9363   * Return the label
9364   * @return {string} label
9365   */
9366  Filter.prototype.isLoaded = function () {
9367    return this.loaded;
9368  };
9369
9370  /**
9371   * Return the loaded progress
9372   * @return {Number} percentage between 0 and 100
9373   */
9374  Filter.prototype.getLoadedProgress = function () {
9375    var len = this.values.length;
9376
9377    var i = 0;
9378    while (this.dataPoints[i]) {
9379      i++;
9380    }
9381
9382    return Math.round(i / len * 100);
9383  };
9384
9385  /**
9386   * Return the label
9387   * @return {string} label
9388   */
9389  Filter.prototype.getLabel = function () {
9390    return this.graph.filterLabel;
9391  };
9392
9393  /**
9394   * Return the columnIndex of the filter
9395   * @return {Number} columnIndex
9396   */
9397  Filter.prototype.getColumn = function () {
9398    return this.column;
9399  };
9400
9401  /**
9402   * Return the currently selected value. Returns undefined if there is no selection
9403   * @return {*} value
9404   */
9405  Filter.prototype.getSelectedValue = function () {
9406    if (this.index === undefined) return undefined;
9407
9408    return this.values[this.index];
9409  };
9410
9411  /**
9412   * Retrieve all values of the filter
9413   * @return {Array} values
9414   */
9415  Filter.prototype.getValues = function () {
9416    return this.values;
9417  };
9418
9419  /**
9420   * Retrieve one value of the filter
9421   * @param {Number}  index
9422   * @return {*} value
9423   */
9424  Filter.prototype.getValue = function (index) {
9425    if (index >= this.values.length) throw 'Error: index out of range';
9426
9427    return this.values[index];
9428  };
9429
9430  /**
9431   * Retrieve the (filtered) dataPoints for the currently selected filter index
9432   * @param {Number} [index] (optional)
9433   * @return {Array} dataPoints
9434   */
9435  Filter.prototype._getDataPoints = function (index) {
9436    if (index === undefined) index = this.index;
9437
9438    if (index === undefined) return [];
9439
9440    var dataPoints;
9441    if (this.dataPoints[index]) {
9442      dataPoints = this.dataPoints[index];
9443    } else {
9444      var f = {};
9445      f.column = this.column;
9446      f.value = this.values[index];
9447
9448      var dataView = new DataView(this.data, { filter: function filter(item) {
9449          return item[f.column] == f.value;
9450        } }).get();
9451      dataPoints = this.graph._getDataPoints(dataView);
9452
9453      this.dataPoints[index] = dataPoints;
9454    }
9455
9456    return dataPoints;
9457  };
9458
9459  /**
9460   * Set a callback function when the filter is fully loaded.
9461   */
9462  Filter.prototype.setOnLoadCallback = function (callback) {
9463    this.onLoadCallback = callback;
9464  };
9465
9466  /**
9467   * Add a value to the list with available values for this filter
9468   * No double entries will be created.
9469   * @param {Number} index
9470   */
9471  Filter.prototype.selectValue = function (index) {
9472    if (index >= this.values.length) throw 'Error: index out of range';
9473
9474    this.index = index;
9475    this.value = this.values[index];
9476  };
9477
9478  /**
9479   * Load all filtered rows in the background one by one
9480   * Start this method without providing an index!
9481   */
9482  Filter.prototype.loadInBackground = function (index) {
9483    if (index === undefined) index = 0;
9484
9485    var frame = this.graph.frame;
9486
9487    if (index < this.values.length) {
9488      var dataPointsTemp = this._getDataPoints(index);
9489      //this.graph.redrawInfo(); // TODO: not neat
9490
9491      // create a progress box
9492      if (frame.progress === undefined) {
9493        frame.progress = document.createElement('DIV');
9494        frame.progress.style.position = 'absolute';
9495        frame.progress.style.color = 'gray';
9496        frame.appendChild(frame.progress);
9497      }
9498      var progress = this.getLoadedProgress();
9499      frame.progress.innerHTML = 'Loading animation... ' + progress + '%';
9500      // TODO: this is no nice solution...
9501      frame.progress.style.bottom = 60 + 'px'; // TODO: use height of slider
9502      frame.progress.style.left = 10 + 'px';
9503
9504      var me = this;
9505      setTimeout(function () {
9506        me.loadInBackground(index + 1);
9507      }, 10);
9508      this.loaded = false;
9509    } else {
9510      this.loaded = true;
9511
9512      // remove the progress box
9513      if (frame.progress !== undefined) {
9514        frame.removeChild(frame.progress);
9515        frame.progress = undefined;
9516      }
9517
9518      if (this.onLoadCallback) this.onLoadCallback();
9519    }
9520  };
9521
9522  module.exports = Filter;
9523
9524/***/ },
9525/* 17 */
9526/***/ function(module, exports, __webpack_require__) {
9527
9528  'use strict';
9529
9530  var util = __webpack_require__(1);
9531
9532  /**
9533   * @constructor Slider
9534   *
9535   * An html slider control with start/stop/prev/next buttons
9536   * @param {Element} container  The element where the slider will be created
9537   * @param {Object} options   Available options:
9538   *                 {boolean} visible   If true (default) the
9539   *                           slider is visible.
9540   */
9541  function Slider(container, options) {
9542    if (container === undefined) {
9543      throw 'Error: No container element defined';
9544    }
9545    this.container = container;
9546    this.visible = options && options.visible != undefined ? options.visible : true;
9547
9548    if (this.visible) {
9549      this.frame = document.createElement('DIV');
9550      //this.frame.style.backgroundColor = '#E5E5E5';
9551      this.frame.style.width = '100%';
9552      this.frame.style.position = 'relative';
9553      this.container.appendChild(this.frame);
9554
9555      this.frame.prev = document.createElement('INPUT');
9556      this.frame.prev.type = 'BUTTON';
9557      this.frame.prev.value = 'Prev';
9558      this.frame.appendChild(this.frame.prev);
9559
9560      this.frame.play = document.createElement('INPUT');
9561      this.frame.play.type = 'BUTTON';
9562      this.frame.play.value = 'Play';
9563      this.frame.appendChild(this.frame.play);
9564
9565      this.frame.next = document.createElement('INPUT');
9566      this.frame.next.type = 'BUTTON';
9567      this.frame.next.value = 'Next';
9568      this.frame.appendChild(this.frame.next);
9569
9570      this.frame.bar = document.createElement('INPUT');
9571      this.frame.bar.type = 'BUTTON';
9572      this.frame.bar.style.position = 'absolute';
9573      this.frame.bar.style.border = '1px solid red';
9574      this.frame.bar.style.width = '100px';
9575      this.frame.bar.style.height = '6px';
9576      this.frame.bar.style.borderRadius = '2px';
9577      this.frame.bar.style.MozBorderRadius = '2px';
9578      this.frame.bar.style.border = '1px solid #7F7F7F';
9579      this.frame.bar.style.backgroundColor = '#E5E5E5';
9580      this.frame.appendChild(this.frame.bar);
9581
9582      this.frame.slide = document.createElement('INPUT');
9583      this.frame.slide.type = 'BUTTON';
9584      this.frame.slide.style.margin = '0px';
9585      this.frame.slide.value = ' ';
9586      this.frame.slide.style.position = 'relative';
9587      this.frame.slide.style.left = '-100px';
9588      this.frame.appendChild(this.frame.slide);
9589
9590      // create events
9591      var me = this;
9592      this.frame.slide.onmousedown = function (event) {
9593        me._onMouseDown(event);
9594      };
9595      this.frame.prev.onclick = function (event) {
9596        me.prev(event);
9597      };
9598      this.frame.play.onclick = function (event) {
9599        me.togglePlay(event);
9600      };
9601      this.frame.next.onclick = function (event) {
9602        me.next(event);
9603      };
9604    }
9605
9606    this.onChangeCallback = undefined;
9607
9608    this.values = [];
9609    this.index = undefined;
9610
9611    this.playTimeout = undefined;
9612    this.playInterval = 1000; // milliseconds
9613    this.playLoop = true;
9614  }
9615
9616  /**
9617   * Select the previous index
9618   */
9619  Slider.prototype.prev = function () {
9620    var index = this.getIndex();
9621    if (index > 0) {
9622      index--;
9623      this.setIndex(index);
9624    }
9625  };
9626
9627  /**
9628   * Select the next index
9629   */
9630  Slider.prototype.next = function () {
9631    var index = this.getIndex();
9632    if (index < this.values.length - 1) {
9633      index++;
9634      this.setIndex(index);
9635    }
9636  };
9637
9638  /**
9639   * Select the next index
9640   */
9641  Slider.prototype.playNext = function () {
9642    var start = new Date();
9643
9644    var index = this.getIndex();
9645    if (index < this.values.length - 1) {
9646      index++;
9647      this.setIndex(index);
9648    } else if (this.playLoop) {
9649      // jump to the start
9650      index = 0;
9651      this.setIndex(index);
9652    }
9653
9654    var end = new Date();
9655    var diff = end - start;
9656
9657    // calculate how much time it to to set the index and to execute the callback
9658    // function.
9659    var interval = Math.max(this.playInterval - diff, 0);
9660    // document.title = diff // TODO: cleanup
9661
9662    var me = this;
9663    this.playTimeout = setTimeout(function () {
9664      me.playNext();
9665    }, interval);
9666  };
9667
9668  /**
9669   * Toggle start or stop playing
9670   */
9671  Slider.prototype.togglePlay = function () {
9672    if (this.playTimeout === undefined) {
9673      this.play();
9674    } else {
9675      this.stop();
9676    }
9677  };
9678
9679  /**
9680   * Start playing
9681   */
9682  Slider.prototype.play = function () {
9683    // Test whether already playing
9684    if (this.playTimeout) return;
9685
9686    this.playNext();
9687
9688    if (this.frame) {
9689      this.frame.play.value = 'Stop';
9690    }
9691  };
9692
9693  /**
9694   * Stop playing
9695   */
9696  Slider.prototype.stop = function () {
9697    clearInterval(this.playTimeout);
9698    this.playTimeout = undefined;
9699
9700    if (this.frame) {
9701      this.frame.play.value = 'Play';
9702    }
9703  };
9704
9705  /**
9706   * Set a callback function which will be triggered when the value of the
9707   * slider bar has changed.
9708   */
9709  Slider.prototype.setOnChangeCallback = function (callback) {
9710    this.onChangeCallback = callback;
9711  };
9712
9713  /**
9714   * Set the interval for playing the list
9715   * @param {Number} interval   The interval in milliseconds
9716   */
9717  Slider.prototype.setPlayInterval = function (interval) {
9718    this.playInterval = interval;
9719  };
9720
9721  /**
9722   * Retrieve the current play interval
9723   * @return {Number} interval   The interval in milliseconds
9724   */
9725  Slider.prototype.getPlayInterval = function (interval) {
9726    return this.playInterval;
9727  };
9728
9729  /**
9730   * Set looping on or off
9731   * @pararm {boolean} doLoop  If true, the slider will jump to the start when
9732   *               the end is passed, and will jump to the end
9733   *               when the start is passed.
9734   */
9735  Slider.prototype.setPlayLoop = function (doLoop) {
9736    this.playLoop = doLoop;
9737  };
9738
9739  /**
9740   * Execute the onchange callback function
9741   */
9742  Slider.prototype.onChange = function () {
9743    if (this.onChangeCallback !== undefined) {
9744      this.onChangeCallback();
9745    }
9746  };
9747
9748  /**
9749   * redraw the slider on the correct place
9750   */
9751  Slider.prototype.redraw = function () {
9752    if (this.frame) {
9753      // resize the bar
9754      this.frame.bar.style.top = this.frame.clientHeight / 2 - this.frame.bar.offsetHeight / 2 + 'px';
9755      this.frame.bar.style.width = this.frame.clientWidth - this.frame.prev.clientWidth - this.frame.play.clientWidth - this.frame.next.clientWidth - 30 + 'px';
9756
9757      // position the slider button
9758      var left = this.indexToLeft(this.index);
9759      this.frame.slide.style.left = left + 'px';
9760    }
9761  };
9762
9763  /**
9764   * Set the list with values for the slider
9765   * @param {Array} values   A javascript array with values (any type)
9766   */
9767  Slider.prototype.setValues = function (values) {
9768    this.values = values;
9769
9770    if (this.values.length > 0) this.setIndex(0);else this.index = undefined;
9771  };
9772
9773  /**
9774   * Select a value by its index
9775   * @param {Number} index
9776   */
9777  Slider.prototype.setIndex = function (index) {
9778    if (index < this.values.length) {
9779      this.index = index;
9780
9781      this.redraw();
9782      this.onChange();
9783    } else {
9784      throw 'Error: index out of range';
9785    }
9786  };
9787
9788  /**
9789   * retrieve the index of the currently selected vaue
9790   * @return {Number} index
9791   */
9792  Slider.prototype.getIndex = function () {
9793    return this.index;
9794  };
9795
9796  /**
9797   * retrieve the currently selected value
9798   * @return {*} value
9799   */
9800  Slider.prototype.get = function () {
9801    return this.values[this.index];
9802  };
9803
9804  Slider.prototype._onMouseDown = function (event) {
9805    // only react on left mouse button down
9806    var leftButtonDown = event.which ? event.which === 1 : event.button === 1;
9807    if (!leftButtonDown) return;
9808
9809    this.startClientX = event.clientX;
9810    this.startSlideX = parseFloat(this.frame.slide.style.left);
9811
9812    this.frame.style.cursor = 'move';
9813
9814    // add event listeners to handle moving the contents
9815    // we store the function onmousemove and onmouseup in the graph, so we can
9816    // remove the eventlisteners lateron in the function mouseUp()
9817    var me = this;
9818    this.onmousemove = function (event) {
9819      me._onMouseMove(event);
9820    };
9821    this.onmouseup = function (event) {
9822      me._onMouseUp(event);
9823    };
9824    util.addEventListener(document, 'mousemove', this.onmousemove);
9825    util.addEventListener(document, 'mouseup', this.onmouseup);
9826    util.preventDefault(event);
9827  };
9828
9829  Slider.prototype.leftToIndex = function (left) {
9830    var width = parseFloat(this.frame.bar.style.width) - this.frame.slide.clientWidth - 10;
9831    var x = left - 3;
9832
9833    var index = Math.round(x / width * (this.values.length - 1));
9834    if (index < 0) index = 0;
9835    if (index > this.values.length - 1) index = this.values.length - 1;
9836
9837    return index;
9838  };
9839
9840  Slider.prototype.indexToLeft = function (index) {
9841    var width = parseFloat(this.frame.bar.style.width) - this.frame.slide.clientWidth - 10;
9842
9843    var x = index / (this.values.length - 1) * width;
9844    var left = x + 3;
9845
9846    return left;
9847  };
9848
9849  Slider.prototype._onMouseMove = function (event) {
9850    var diff = event.clientX - this.startClientX;
9851    var x = this.startSlideX + diff;
9852
9853    var index = this.leftToIndex(x);
9854
9855    this.setIndex(index);
9856
9857    util.preventDefault();
9858  };
9859
9860  Slider.prototype._onMouseUp = function (event) {
9861    this.frame.style.cursor = 'auto';
9862
9863    // remove event listeners
9864    util.removeEventListener(document, 'mousemove', this.onmousemove);
9865    util.removeEventListener(document, 'mouseup', this.onmouseup);
9866
9867    util.preventDefault();
9868  };
9869
9870  module.exports = Slider;
9871
9872/***/ },
9873/* 18 */
9874/***/ function(module, exports) {
9875
9876  /**
9877   * @prototype StepNumber
9878   * The class StepNumber is an iterator for Numbers. You provide a start and end
9879   * value, and a best step size. StepNumber itself rounds to fixed values and
9880   * a finds the step that best fits the provided step.
9881   *
9882   * If prettyStep is true, the step size is chosen as close as possible to the
9883   * provided step, but being a round value like 1, 2, 5, 10, 20, 50, ....
9884   *
9885   * Example usage:
9886   *   var step = new StepNumber(0, 10, 2.5, true);
9887   *   step.start();
9888   *   while (!step.end()) {
9889   *   alert(step.getCurrent());
9890   *   step.next();
9891   *   }
9892   *
9893   * Version: 1.0
9894   *
9895   * @param {Number} start     The start value
9896   * @param {Number} end     The end value
9897   * @param {Number} step    Optional. Step size. Must be a positive value.
9898   * @param {boolean} prettyStep Optional. If true, the step size is rounded
9899   *               To a pretty step size (like 1, 2, 5, 10, 20, 50, ...)
9900   */
9901  "use strict";
9902
9903  function StepNumber(start, end, step, prettyStep) {
9904    // set default values
9905    this._start = 0;
9906    this._end = 0;
9907    this._step = 1;
9908    this.prettyStep = true;
9909    this.precision = 5;
9910
9911    this._current = 0;
9912    this.setRange(start, end, step, prettyStep);
9913  };
9914
9915  /**
9916   * Set a new range: start, end and step.
9917   *
9918   * @param {Number} start     The start value
9919   * @param {Number} end     The end value
9920   * @param {Number} step    Optional. Step size. Must be a positive value.
9921   * @param {boolean} prettyStep Optional. If true, the step size is rounded
9922   *               To a pretty step size (like 1, 2, 5, 10, 20, 50, ...)
9923   */
9924  StepNumber.prototype.setRange = function (start, end, step, prettyStep) {
9925    this._start = start ? start : 0;
9926    this._end = end ? end : 0;
9927
9928    this.setStep(step, prettyStep);
9929  };
9930
9931  /**
9932   * Set a new step size
9933   * @param {Number} step    New step size. Must be a positive value
9934   * @param {boolean} prettyStep Optional. If true, the provided step is rounded
9935   *               to a pretty step size (like 1, 2, 5, 10, 20, 50, ...)
9936   */
9937  StepNumber.prototype.setStep = function (step, prettyStep) {
9938    if (step === undefined || step <= 0) return;
9939
9940    if (prettyStep !== undefined) this.prettyStep = prettyStep;
9941
9942    if (this.prettyStep === true) this._step = StepNumber.calculatePrettyStep(step);else this._step = step;
9943  };
9944
9945  /**
9946   * Calculate a nice step size, closest to the desired step size.
9947   * Returns a value in one of the ranges 1*10^n, 2*10^n, or 5*10^n, where n is an
9948   * integer Number. For example 1, 2, 5, 10, 20, 50, etc...
9949   * @param {Number}  step  Desired step size
9950   * @return {Number}     Nice step size
9951   */
9952  StepNumber.calculatePrettyStep = function (step) {
9953    var log10 = function log10(x) {
9954      return Math.log(x) / Math.LN10;
9955    };
9956
9957    // try three steps (multiple of 1, 2, or 5
9958    var step1 = Math.pow(10, Math.round(log10(step))),
9959        step2 = 2 * Math.pow(10, Math.round(log10(step / 2))),
9960        step5 = 5 * Math.pow(10, Math.round(log10(step / 5)));
9961
9962    // choose the best step (closest to minimum step)
9963    var prettyStep = step1;
9964    if (Math.abs(step2 - step) <= Math.abs(prettyStep - step)) prettyStep = step2;
9965    if (Math.abs(step5 - step) <= Math.abs(prettyStep - step)) prettyStep = step5;
9966
9967    // for safety
9968    if (prettyStep <= 0) {
9969      prettyStep = 1;
9970    }
9971
9972    return prettyStep;
9973  };
9974
9975  /**
9976   * returns the current value of the step
9977   * @return {Number} current value
9978   */
9979  StepNumber.prototype.getCurrent = function () {
9980    return parseFloat(this._current.toPrecision(this.precision));
9981  };
9982
9983  /**
9984   * returns the current step size
9985   * @return {Number} current step size
9986   */
9987  StepNumber.prototype.getStep = function () {
9988    return this._step;
9989  };
9990
9991  /**
9992   * Set the current value to the largest value smaller than start, which
9993   * is a multiple of the step size
9994   */
9995  StepNumber.prototype.start = function () {
9996    this._current = this._start - this._start % this._step;
9997  };
9998
9999  /**
10000   * Do a step, add the step size to the current value
10001   */
10002  StepNumber.prototype.next = function () {
10003    this._current += this._step;
10004  };
10005
10006  /**
10007   * Returns true whether the end is reached
10008   * @return {boolean}  True if the current value has passed the end value.
10009   */
10010  StepNumber.prototype.end = function () {
10011    return this._current > this._end;
10012  };
10013
10014  module.exports = StepNumber;
10015
10016/***/ },
10017/* 19 */
10018/***/ function(module, exports, __webpack_require__) {
10019
10020  'use strict';
10021
10022  var Emitter = __webpack_require__(12);
10023  var Hammer = __webpack_require__(20);
10024  var moment = __webpack_require__(2);
10025  var util = __webpack_require__(1);
10026  var DataSet = __webpack_require__(8);
10027  var DataView = __webpack_require__(10);
10028  var Range = __webpack_require__(24);
10029  var Core = __webpack_require__(28);
10030  var TimeAxis = __webpack_require__(39);
10031  var CurrentTime = __webpack_require__(44);
10032  var CustomTime = __webpack_require__(42);
10033  var ItemSet = __webpack_require__(29);
10034
10035  var Configurator = __webpack_require__(45);
10036  var Validator = __webpack_require__(47)['default'];
10037  var printStyle = __webpack_require__(47).printStyle;
10038  var allOptions = __webpack_require__(48).allOptions;
10039  var configureOptions = __webpack_require__(48).configureOptions;
10040
10041  /**
10042   * Create a timeline visualization
10043   * @param {HTMLElement} container
10044   * @param {vis.DataSet | vis.DataView | Array} [items]
10045   * @param {vis.DataSet | vis.DataView | Array} [groups]
10046   * @param {Object} [options]  See Timeline.setOptions for the available options.
10047   * @constructor
10048   * @extends Core
10049   */
10050  function Timeline(container, items, groups, options) {
10051    if (!(this instanceof Timeline)) {
10052      throw new SyntaxError('Constructor must be called with the new operator');
10053    }
10054
10055    // if the third element is options, the forth is groups (optionally);
10056    if (!(Array.isArray(groups) || groups instanceof DataSet || groups instanceof DataView) && groups instanceof Object) {
10057      var forthArgument = options;
10058      options = groups;
10059      groups = forthArgument;
10060    }
10061
10062    var me = this;
10063    this.defaultOptions = {
10064      start: null,
10065      end: null,
10066
10067      autoResize: true,
10068      throttleRedraw: 0, // ms
10069
10070      orientation: {
10071        axis: 'bottom', // axis orientation: 'bottom', 'top', or 'both'
10072        item: 'bottom' // not relevant
10073      },
10074
10075      moment: moment,
10076
10077      width: null,
10078      height: null,
10079      maxHeight: null,
10080      minHeight: null
10081    };
10082    this.options = util.deepExtend({}, this.defaultOptions);
10083
10084    // Create the DOM, props, and emitter
10085    this._create(container);
10086
10087    // all components listed here will be repainted automatically
10088    this.components = [];
10089
10090    this.body = {
10091      dom: this.dom,
10092      domProps: this.props,
10093      emitter: {
10094        on: this.on.bind(this),
10095        off: this.off.bind(this),
10096        emit: this.emit.bind(this)
10097      },
10098      hiddenDates: [],
10099      util: {
10100        getScale: function getScale() {
10101          return me.timeAxis.step.scale;
10102        },
10103        getStep: function getStep() {
10104          return me.timeAxis.step.step;
10105        },
10106
10107        toScreen: me._toScreen.bind(me),
10108        toGlobalScreen: me._toGlobalScreen.bind(me), // this refers to the root.width
10109        toTime: me._toTime.bind(me),
10110        toGlobalTime: me._toGlobalTime.bind(me)
10111      }
10112    };
10113
10114    // range
10115    this.range = new Range(this.body);
10116    this.components.push(this.range);
10117    this.body.range = this.range;
10118
10119    // time axis
10120    this.timeAxis = new TimeAxis(this.body);
10121    this.timeAxis2 = null; // used in case of orientation option 'both'
10122    this.components.push(this.timeAxis);
10123
10124    // current time bar
10125    this.currentTime = new CurrentTime(this.body);
10126    this.components.push(this.currentTime);
10127
10128    // item set
10129    this.itemSet = new ItemSet(this.body);
10130    this.components.push(this.itemSet);
10131
10132    this.itemsData = null; // DataSet
10133    this.groupsData = null; // DataSet
10134
10135    this.on('tap', function (event) {
10136      me.emit('click', me.getEventProperties(event));
10137    });
10138    this.on('doubletap', function (event) {
10139      me.emit('doubleClick', me.getEventProperties(event));
10140    });
10141    this.dom.root.oncontextmenu = function (event) {
10142      me.emit('contextmenu', me.getEventProperties(event));
10143    };
10144
10145    // apply options
10146    if (options) {
10147      this.setOptions(options);
10148    }
10149
10150    // IMPORTANT: THIS HAPPENS BEFORE SET ITEMS!
10151    if (groups) {
10152      this.setGroups(groups);
10153    }
10154
10155    // create itemset
10156    if (items) {
10157      this.setItems(items);
10158    } else {
10159      this._redraw();
10160    }
10161  }
10162
10163  // Extend the functionality from Core
10164  Timeline.prototype = new Core();
10165
10166  /**
10167   * Load a configurator
10168   * @return {Object}
10169   * @private
10170   */
10171  Timeline.prototype._createConfigurator = function () {
10172    return new Configurator(this, this.dom.container, configureOptions);
10173  };
10174
10175  /**
10176   * Force a redraw. The size of all items will be recalculated.
10177   * Can be useful to manually redraw when option autoResize=false and the window
10178   * has been resized, or when the items CSS has been changed.
10179   *
10180   * Note: this function will be overridden on construction with a trottled version
10181   */
10182  Timeline.prototype.redraw = function () {
10183    this.itemSet && this.itemSet.markDirty({ refreshItems: true });
10184    this._redraw();
10185  };
10186
10187  Timeline.prototype.setOptions = function (options) {
10188    // validate options
10189    var errorFound = Validator.validate(options, allOptions);
10190    if (errorFound === true) {
10191      console.log('%cErrors have been found in the supplied options object.', printStyle);
10192    }
10193
10194    Core.prototype.setOptions.call(this, options);
10195
10196    if ('type' in options) {
10197      if (options.type !== this.options.type) {
10198        this.options.type = options.type;
10199
10200        // force recreation of all items
10201        var itemsData = this.itemsData;
10202        if (itemsData) {
10203          var selection = this.getSelection();
10204          this.setItems(null); // remove all
10205          this.setItems(itemsData); // add all
10206          this.setSelection(selection); // restore selection
10207        }
10208      }
10209    }
10210  };
10211
10212  /**
10213   * Set items
10214   * @param {vis.DataSet | Array | null} items
10215   */
10216  Timeline.prototype.setItems = function (items) {
10217    var initialLoad = this.itemsData == null;
10218
10219    // convert to type DataSet when needed
10220    var newDataSet;
10221    if (!items) {
10222      newDataSet = null;
10223    } else if (items instanceof DataSet || items instanceof DataView) {
10224      newDataSet = items;
10225    } else {
10226      // turn an array into a dataset
10227      newDataSet = new DataSet(items, {
10228        type: {
10229          start: 'Date',
10230          end: 'Date'
10231        }
10232      });
10233    }
10234
10235    // set items
10236    this.itemsData = newDataSet;
10237    this.itemSet && this.itemSet.setItems(newDataSet);
10238
10239    if (initialLoad) {
10240      if (this.options.start != undefined || this.options.end != undefined) {
10241        if (this.options.start == undefined || this.options.end == undefined) {
10242          var range = this.getItemRange();
10243        }
10244
10245        var start = this.options.start != undefined ? this.options.start : range.min;
10246        var end = this.options.end != undefined ? this.options.end : range.max;
10247
10248        this.setWindow(start, end, { animation: false });
10249      } else {
10250        this.fit({ animation: false });
10251      }
10252    }
10253  };
10254
10255  /**
10256   * Set groups
10257   * @param {vis.DataSet | Array} groups
10258   */
10259  Timeline.prototype.setGroups = function (groups) {
10260    // convert to type DataSet when needed
10261    var newDataSet;
10262    if (!groups) {
10263      newDataSet = null;
10264    } else if (groups instanceof DataSet || groups instanceof DataView) {
10265      newDataSet = groups;
10266    } else {
10267      // turn an array into a dataset
10268      newDataSet = new DataSet(groups);
10269    }
10270
10271    this.groupsData = newDataSet;
10272    this.itemSet.setGroups(newDataSet);
10273  };
10274
10275  /**
10276   * Set both items and groups in one go
10277   * @param {{items: Array | vis.DataSet, groups: Array | vis.DataSet}} data
10278   */
10279  Timeline.prototype.setData = function (data) {
10280    if (data && data.groups) {
10281      this.setGroups(data.groups);
10282    }
10283
10284    if (data && data.items) {
10285      this.setItems(data.items);
10286    }
10287  };
10288
10289  /**
10290   * Set selected items by their id. Replaces the current selection
10291   * Unknown id's are silently ignored.
10292   * @param {string[] | string} [ids]  An array with zero or more id's of the items to be
10293   *                                selected. If ids is an empty array, all items will be
10294   *                                unselected.
10295   * @param {Object} [options]      Available options:
10296   *                                `focus: boolean`
10297   *                                    If true, focus will be set to the selected item(s)
10298   *                                `animation: boolean | {duration: number, easingFunction: string}`
10299   *                                    If true (default), the range is animated
10300   *                                    smoothly to the new window. An object can be
10301   *                                    provided to specify duration and easing function.
10302   *                                    Default duration is 500 ms, and default easing
10303   *                                    function is 'easeInOutQuad'.
10304   *                                    Only applicable when option focus is true.
10305   */
10306  Timeline.prototype.setSelection = function (ids, options) {
10307    this.itemSet && this.itemSet.setSelection(ids);
10308
10309    if (options && options.focus) {
10310      this.focus(ids, options);
10311    }
10312  };
10313
10314  /**
10315   * Get the selected items by their id
10316   * @return {Array} ids  The ids of the selected items
10317   */
10318  Timeline.prototype.getSelection = function () {
10319    return this.itemSet && this.itemSet.getSelection() || [];
10320  };
10321
10322  /**
10323   * Adjust the visible window such that the selected item (or multiple items)
10324   * are centered on screen.
10325   * @param {String | String[]} id     An item id or array with item ids
10326   * @param {Object} [options]      Available options:
10327   *                                `animation: boolean | {duration: number, easingFunction: string}`
10328   *                                    If true (default), the range is animated
10329   *                                    smoothly to the new window. An object can be
10330   *                                    provided to specify duration and easing function.
10331   *                                    Default duration is 500 ms, and default easing
10332   *                                    function is 'easeInOutQuad'.
10333   */
10334  Timeline.prototype.focus = function (id, options) {
10335    if (!this.itemsData || id == undefined) return;
10336
10337    var ids = Array.isArray(id) ? id : [id];
10338
10339    // get the specified item(s)
10340    var itemsData = this.itemsData.getDataSet().get(ids, {
10341      type: {
10342        start: 'Date',
10343        end: 'Date'
10344      }
10345    });
10346
10347    // calculate minimum start and maximum end of specified items
10348    var start = null;
10349    var end = null;
10350    itemsData.forEach(function (itemData) {
10351      var s = itemData.start.valueOf();
10352      var e = 'end' in itemData ? itemData.end.valueOf() : itemData.start.valueOf();
10353
10354      if (start === null || s < start) {
10355        start = s;
10356      }
10357
10358      if (end === null || e > end) {
10359        end = e;
10360      }
10361    });
10362
10363    if (start !== null && end !== null) {
10364      // calculate the new middle and interval for the window
10365      var middle = (start + end) / 2;
10366      var interval = Math.max(this.range.end - this.range.start, (end - start) * 1.1);
10367
10368      var animation = options && options.animation !== undefined ? options.animation : true;
10369      this.range.setRange(middle - interval / 2, middle + interval / 2, animation);
10370    }
10371  };
10372
10373  /**
10374   * Set Timeline window such that it fits all items
10375   * @param {Object} [options]  Available options:
10376   *                                `animation: boolean | {duration: number, easingFunction: string}`
10377   *                                    If true (default), the range is animated
10378   *                                    smoothly to the new window. An object can be
10379   *                                    provided to specify duration and easing function.
10380   *                                    Default duration is 500 ms, and default easing
10381   *                                    function is 'easeInOutQuad'.
10382   */
10383  Timeline.prototype.fit = function (options) {
10384    var animation = options && options.animation !== undefined ? options.animation : true;
10385    var range = this.getItemRange();
10386    this.range.setRange(range.min, range.max, animation);
10387  };
10388
10389  /**
10390   * Determine the range of the items, taking into account their actual width
10391   * and a margin of 10 pixels on both sides.
10392   * @return {{min: Date | null, max: Date | null}}
10393   */
10394  Timeline.prototype.getItemRange = function () {
10395    var _this = this;
10396
10397    // get a rough approximation for the range based on the items start and end dates
10398    var range = this.getDataRange();
10399    var min = range.min;
10400    var max = range.max;
10401    var minItem = null;
10402    var maxItem = null;
10403
10404    if (min != null && max != null) {
10405      var interval;
10406      var factor;
10407      var lhs;
10408      var rhs;
10409      var delta;
10410
10411      (function () {
10412        var getStart = function getStart(item) {
10413          return util.convert(item.data.start, 'Date').valueOf();
10414        };
10415
10416        var getEnd = function getEnd(item) {
10417          var end = item.data.end != undefined ? item.data.end : item.data.start;
10418          return util.convert(end, 'Date').valueOf();
10419        };
10420
10421        interval = max - min;
10422        // ms
10423        if (interval <= 0) {
10424          interval = 10;
10425        }
10426        factor = interval / _this.props.center.width;
10427
10428        // calculate the date of the left side and right side of the items given
10429        util.forEach(_this.itemSet.items, (function (item) {
10430          item.show();
10431
10432          var start = getStart(item);
10433          var end = getEnd(item);
10434
10435          var left = new Date(start - (item.getWidthLeft() + 10) * factor);
10436          var right = new Date(end + (item.getWidthRight() + 10) * factor);
10437
10438          if (left < min) {
10439            min = left;
10440            minItem = item;
10441          }
10442          if (right > max) {
10443            max = right;
10444            maxItem = item;
10445          }
10446        }).bind(_this));
10447
10448        if (minItem && maxItem) {
10449          lhs = minItem.getWidthLeft() + 10;
10450          rhs = maxItem.getWidthRight() + 10;
10451          delta = _this.props.center.width - lhs - rhs;
10452          // px
10453
10454          if (delta > 0) {
10455            min = getStart(minItem) - lhs * interval / delta; // ms
10456            max = getEnd(maxItem) + rhs * interval / delta; // ms
10457          }
10458        }
10459      })();
10460    }
10461
10462    return {
10463      min: min != null ? new Date(min) : null,
10464      max: max != null ? new Date(max) : null
10465    };
10466  };
10467
10468  /**
10469   * Calculate the data range of the items start and end dates
10470   * @returns {{min: Date | null, max: Date | null}}
10471   */
10472  Timeline.prototype.getDataRange = function () {
10473    var min = null;
10474    var max = null;
10475
10476    var dataset = this.itemsData && this.itemsData.getDataSet();
10477    if (dataset) {
10478      dataset.forEach(function (item) {
10479        var start = util.convert(item.start, 'Date').valueOf();
10480        var end = util.convert(item.end != undefined ? item.end : item.start, 'Date').valueOf();
10481        if (min === null || start < min) {
10482          min = start;
10483        }
10484        if (max === null || end > max) {
10485          max = start;
10486        }
10487      });
10488    }
10489
10490    return {
10491      min: min != null ? new Date(min) : null,
10492      max: max != null ? new Date(max) : null
10493    };
10494  };
10495
10496  /**
10497   * Generate Timeline related information from an event
10498   * @param {Event} event
10499   * @return {Object} An object with related information, like on which area
10500   *                  The event happened, whether clicked on an item, etc.
10501   */
10502  Timeline.prototype.getEventProperties = function (event) {
10503    var clientX = event.center ? event.center.x : event.clientX;
10504    var clientY = event.center ? event.center.y : event.clientY;
10505    var x = clientX - util.getAbsoluteLeft(this.dom.centerContainer);
10506    var y = clientY - util.getAbsoluteTop(this.dom.centerContainer);
10507
10508    var item = this.itemSet.itemFromTarget(event);
10509    var group = this.itemSet.groupFromTarget(event);
10510    var customTime = CustomTime.customTimeFromTarget(event);
10511
10512    var snap = this.itemSet.options.snap || null;
10513    var scale = this.body.util.getScale();
10514    var step = this.body.util.getStep();
10515    var time = this._toTime(x);
10516    var snappedTime = snap ? snap(time, scale, step) : time;
10517
10518    var element = util.getTarget(event);
10519    var what = null;
10520    if (item != null) {
10521      what = 'item';
10522    } else if (customTime != null) {
10523      what = 'custom-time';
10524    } else if (util.hasParent(element, this.timeAxis.dom.foreground)) {
10525      what = 'axis';
10526    } else if (this.timeAxis2 && util.hasParent(element, this.timeAxis2.dom.foreground)) {
10527      what = 'axis';
10528    } else if (util.hasParent(element, this.itemSet.dom.labelSet)) {
10529      what = 'group-label';
10530    } else if (util.hasParent(element, this.currentTime.bar)) {
10531      what = 'current-time';
10532    } else if (util.hasParent(element, this.dom.center)) {
10533      what = 'background';
10534    }
10535
10536    return {
10537      event: event,
10538      item: item ? item.id : null,
10539      group: group ? group.groupId : null,
10540      what: what,
10541      pageX: event.srcEvent ? event.srcEvent.pageX : event.pageX,
10542      pageY: event.srcEvent ? event.srcEvent.pageY : event.pageY,
10543      x: x,
10544      y: y,
10545      time: time,
10546      snappedTime: snappedTime
10547    };
10548  };
10549
10550  module.exports = Timeline;
10551
10552/***/ },
10553/* 20 */
10554/***/ function(module, exports, __webpack_require__) {
10555
10556  // Only load hammer.js when in a browser environment
10557  // (loading hammer.js in a node.js environment gives errors)
10558  'use strict';
10559
10560  if (typeof window !== 'undefined') {
10561    var propagating = __webpack_require__(21);
10562    var Hammer = window['Hammer'] || __webpack_require__(22);
10563    module.exports = propagating(Hammer, {
10564      preventDefault: 'mouse'
10565    });
10566  } else {
10567    module.exports = function () {
10568      throw Error('hammer.js is only available in a browser, not in node.js.');
10569    };
10570  }
10571
10572/***/ },
10573/* 21 */
10574/***/ function(module, exports, __webpack_require__) {
10575
10576  var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;'use strict';
10577
10578  (function (factory) {
10579    if (true) {
10580      // AMD. Register as an anonymous module.
10581      !(__WEBPACK_AMD_DEFINE_ARRAY__ = [], __WEBPACK_AMD_DEFINE_FACTORY__ = (factory), __WEBPACK_AMD_DEFINE_RESULT__ = (typeof __WEBPACK_AMD_DEFINE_FACTORY__ === 'function' ? (__WEBPACK_AMD_DEFINE_FACTORY__.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__)) : __WEBPACK_AMD_DEFINE_FACTORY__), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));
10582    } else if (typeof exports === 'object') {
10583      // Node. Does not work with strict CommonJS, but
10584      // only CommonJS-like environments that support module.exports,
10585      // like Node.
10586      module.exports = factory();
10587    } else {
10588      // Browser globals (root is window)
10589      window.propagating = factory();
10590    }
10591  }(function () {
10592    var _firstTarget = null; // singleton, will contain the target element where the touch event started
10593    var _processing = false; // singleton, true when a touch event is being handled
10594
10595    /**
10596     * Extend an Hammer.js instance with event propagation.
10597     *
10598     * Features:
10599     * - Events emitted by hammer will propagate in order from child to parent
10600     *   elements.
10601     * - Events are extended with a function `event.stopPropagation()` to stop
10602     *   propagation to parent elements.
10603     * - An option `preventDefault` to stop all default browser behavior.
10604     *
10605     * Usage:
10606     *   var hammer = propagatingHammer(new Hammer(element));
10607     *   var hammer = propagatingHammer(new Hammer(element), {preventDefault: true});
10608     *
10609     * @param {Hammer.Manager} hammer   An hammer instance.
10610     * @param {Object} [options]        Available options:
10611     *                                  - `preventDefault: true | 'mouse' | 'touch' | 'pen'`.
10612     *                                    Enforce preventing the default browser behavior.
10613     *                                    Cannot be set to `false`.
10614     * @return {Hammer.Manager} Returns the same hammer instance with extended
10615     *                          functionality
10616     */
10617    return function propagating(hammer, options) {
10618      var _options = options || {
10619        preventDefault: false
10620      };
10621
10622      if (hammer.Manager) {
10623        // This looks like the Hammer constructor.
10624        // Overload the constructors with our own.
10625        var Hammer = hammer;
10626
10627        var PropagatingHammer = function(element, options) {
10628          var o = Object.create(_options);
10629          if (options) Hammer.extend(o, options);
10630          return propagating(new Hammer(element, o), o);
10631        };
10632        Hammer.extend(PropagatingHammer, Hammer);
10633
10634        PropagatingHammer.Manager = function (element, options) {
10635          var o = Object.create(_options);
10636          if (options) Hammer.extend(o, options);
10637          return propagating(new Hammer.Manager(element, o), o);
10638        };
10639
10640        return PropagatingHammer;
10641      }
10642
10643      // create a wrapper object which will override the functions
10644      // `on`, `off`, `destroy`, and `emit` of the hammer instance
10645      var wrapper = Object.create(hammer);
10646
10647      // attach to DOM element
10648      var element = hammer.element;
10649      element.hammer = wrapper;
10650
10651      // register an event to catch the start of a gesture and store the
10652      // target in a singleton
10653      hammer.on('hammer.input', function (event) {
10654        if (_options.preventDefault === true || (_options.preventDefault === event.pointerType)) {
10655          event.preventDefault();
10656        }
10657        if (event.isFirst) {
10658          _firstTarget = event.target;
10659        }
10660      });
10661
10662      /** @type {Object.<String, Array.<function>>} */
10663      wrapper._handlers = {};
10664
10665      /**
10666       * Register a handler for one or multiple events
10667       * @param {String} events    A space separated string with events
10668       * @param {function}
10668 handler A callback function, called as handler(event)
10669       * @returns {Hammer.Manager} Returns the hammer instance
10670       */
10671      wrapper.on = function (events, handler) {
10672        // register the handler
10673        split(events).forEach(function (event) {
10674          var _handlers = wrapper._handlers[event];
10675          if (!_handlers) {
10676            wrapper._handlers[event] = _handlers = [];
10677
10678            // register the static, propagated handler
10679            hammer.on(event, propagatedHandler);
10680          }
10681          _handlers.push(handler);
10682        });
10683
10684        return wrapper;
10685      };
10686
10687      /**
10688       * Unregister a handler for one or multiple events
10689       * @param {String} events      A space separated string with events
10690       * @param {function} [handler] Optional. The registered handler. If not
10691       *                             provided, all handlers for given events
10692       *                             are removed.
10693       * @returns {Hammer.Manager}   Returns the hammer instance
10694       */
10695      wrapper.off = function (events, handler) {
10696        // unregister the handler
10697        split(events).forEach(function (event) {
10698          var _handlers = wrapper._handlers[event];
10699          if (_handlers) {
10700            _handlers = handler ? _handlers.filter(function (h) {
10701              return h !== handler;
10702            }) : [];
10703
10704            if (_handlers.length > 0) {
10705              wrapper._handlers[event] = _handlers;
10706            }
10707            else {
10708              // remove static, propagated handler
10709              hammer.off(event, propagatedHandler);
10710              delete wrapper._handlers[event];
10711            }
10712          }
10713        });
10714
10715        return wrapper;
10716      };
10717
10718      /**
10719       * Emit to the event listeners
10720       * @param {string} eventType
10721       * @param {Event} event
10722       */
10723      wrapper.emit = function(eventType, event) {
10724        _firstTarget = event.target;
10725        hammer.emit(eventType, event);
10726      };
10727
10728      wrapper.destroy = function () {
10729        // Detach from DOM element
10730        delete hammer.element.hammer;
10731
10732        // clear all handlers
10733        wrapper._handlers = {};
10734
10735        // call original hammer destroy
10736        hammer.destroy();
10737      };
10738
10739      // split a string with space separated words
10740      function split(events) {
10741        return events.match(/[^ ]+/g);
10742      }
10743
10744      /**
10745       * A static event handler, applying event propagation.
10746       * @param {Object} event
10747       */
10748      function propagatedHandler(event) {
10749        // let only a single hammer instance handle this event
10750        if (event.type !== 'hammer.input') {
10751          // it is possible that the same srcEvent is used with multiple hammer events,
10752          // we keep track on which events are handled in an object _handled
10753          if (!event.srcEvent._handled) {
10754            event.srcEvent._handled = {};
10755          }
10756
10757          if (event.srcEvent._handled[event.type]) {
10758            return;
10759          }
10760          else {
10761            event.srcEvent._handled[event.type] = true;
10762          }
10763        }
10764
10765        // attach a stopPropagation function to the event
10766        var stopped = false;
10767        event.stopPropagation = function () {
10768          stopped = true;
10769        };
10770
10771        // attach firstTarget property to the event
10772        event.firstTarget = _firstTarget;
10773
10774        // propagate over all elements (until stopped)
10775        var elem = _firstTarget;
10776        while (elem && !stopped) {
10777          var _handlers = elem.hammer && elem.hammer._handlers[event.type];
10778          if (_handlers) {
10779            for (var i = 0; i < _handlers.length && !stopped; i++) {
10780              _handlers[i](event);
10781            }
10782          }
10783
10784          elem = elem.parentNode;
10785        }
10786      }
10787
10788      return wrapper;
10789    };
10790  }));
10791
10792
10793/***/ },
10794/* 22 */
10795/***/ function(module, exports, __webpack_require__) {
10796
10797  var __WEBPACK_AMD_DEFINE_RESULT__;/*! Hammer.JS - v2.0.4 - 2014-09-28
10798   * http://hammerjs.github.io/
10799   *
10800   * Copyright (c) 2014 Jorik Tangelder;
10801   * Licensed under the MIT license */
10802  (function(window, document, exportName, undefined) {
10803    'use strict';
10804
10805  var VENDOR_PREFIXES = ['', 'webkit', 'moz', 'MS', 'ms', 'o'];
10806  var TEST_ELEMENT = document.createElement('div');
10807
10808  var TYPE_FUNCTION = 'function';
10809
10810  var round = Math.round;
10811  var abs = Math.abs;
10812  var now = Date.now;
10813
10814  /**
10815   * set a timeout with a given scope
10816   * @param {Function} fn
10817   * @param {Number} timeout
10818   * @param {Object} context
10819   * @returns {number}
10820   */
10821  function setTimeoutContext(fn, timeout, context) {
10822      return setTimeout(bindFn(fn, context), timeout);
10823  }
10824
10825  /**
10826   * if the argument is an array, we want to execute the fn on each entry
10827   * if it aint an array we don't want to do a thing.
10828   * this is used by all the methods that accept a single and array argument.
10829   * @param {*|Array} arg
10830   * @param {String} fn
10831   * @param {Object} [context]
10832   * @returns {Boolean}
10833   */
10834  function invokeArrayArg(arg, fn, context) {
10835      if (Array.isArray(arg)) {
10836          each(arg, context[fn], context);
10837          return true;
10838      }
10839      return false;
vendor: 28,175 bytes, lines 10840-11857
10840  }
10841
10842  /**
10843   * walk objects and arrays
10844   * @param {Object} obj
10845   * @param {Function} iterator
10846   * @param {Object} context
10847   */
10848  function each(obj, iterator, context) {
10849      var i;
10850
10851      if (!obj) {
10852          return;
10853      }
10854
10855      if (obj.forEach) {
10856          obj.forEach(iterator, context);
10857      } else if (obj.length !== undefined) {
10858          i = 0;
10859          while (i < obj.length) {
10860              iterator.call(context, obj[i], i, obj);
10861              i++;
10862          }
10863      } else {
10864          for (i in obj) {
10865              obj.hasOwnProperty(i) && iterator.call(context, obj[i], i, obj);
10866          }
10867      }
10868  }
10869
10870  /**
10871   * extend object.
10872   * means that properties in dest will be overwritten by the ones in src.
10873   * @param {Object} dest
10874   * @param {Object} src
10875   * @param {Boolean} [merge]
10876   * @returns {Object} dest
10877   */
10878  function extend(dest, src, merge) {
10879      var keys = Object.keys(src);
10880      var i = 0;
10881      while (i < keys.length) {
10882          if (!merge || (merge && dest[keys[i]] === undefined)) {
10883              dest[keys[i]] = src[keys[i]];
10884          }
10885          i++;
10886      }
10887      return dest;
10888  }
10889
10890  /**
10891   * merge the values from src in the dest.
10892   * means that properties that exist in dest will not be overwritten by src
10893   * @param {Object} dest
10894   * @param {Object} src
10895   * @returns {Object} dest
10896   */
10897  function merge(dest, src) {
10898      return extend(dest, src, true);
10899  }
10900
10901  /**
10902   * simple class inheritance
10903   * @param {Function} child
10904   * @param {Function} base
10905   * @param {Object} [properties]
10906   */
10907  function inherit(child, base, properties) {
10908      var baseP = base.prototype,
10909          childP;
10910
10911      childP = child.prototype = Object.create(baseP);
10912      childP.constructor = child;
10913      childP._super = baseP;
10914
10915      if (properties) {
10916          extend(childP, properties);
10917      }
10918  }
10919
10920  /**
10921   * simple function bind
10922   * @param {Function} fn
10923   * @param {Object} context
10924   * @returns {Function}
10925   */
10926  function bindFn(fn, context) {
10927      return function boundFn() {
10928          return fn.apply(context, arguments);
10929      };
10930  }
10931
10932  /**
10933   * let a boolean value also be a function that must return a boolean
10934   * this first item in args will be used as the context
10935   * @param {Boolean|Function} val
10936   * @param {Array} [args]
10937   * @returns {Boolean}
10938   */
10939  function boolOrFn(val, args) {
10940      if (typeof val == TYPE_FUNCTION) {
10941          return val.apply(args ? args[0] || undefined : undefined, args);
10942      }
10943      return val;
10944  }
10945
10946  /**
10947   * use the val2 when val1 is undefined
10948   * @param {*} val1
10949   * @param {*} val2
10950   * @returns {*}
10951   */
10952  function ifUndefined(val1, val2) {
10953      return (val1 === undefined) ? val2 : val1;
10954  }
10955
10956  /**
10957   * addEventListener with multiple events at once
10958   * @param {EventTarget} target
10959   * @param {String} types
10960   * @param {Function} handler
10961   */
10962  function addEventListeners(target, types, handler) {
10963      each(splitStr(types), function(type) {
10964          target.addEventListener(type, handler, false);
10965      });
10966  }
10967
10968  /**
10969   * removeEventListener with multiple events at once
10970   * @param {EventTarget} target
10971   * @param {String} types
10972   * @param {Function} handler
10973   */
10974  function removeEventListeners(target, types, handler) {
10975      each(splitStr(types), function(type) {
10976          target.removeEventListener(type, handler, false);
10977      });
10978  }
10979
10980  /**
10981   * find if a node is in the given parent
10982   * @method hasParent
10983   * @param {HTMLElement} node
10984   * @param {HTMLElement} parent
10985   * @return {Boolean} found
10986   */
10987  function hasParent(node, parent) {
10988      while (node) {
10989          if (node == parent) {
10990              return true;
10991          }
10992          node = node.parentNode;
10993      }
10994      return false;
10995  }
10996
10997  /**
10998   * small indexOf wrapper
10999   * @param {String} str
11000   * @param {String} find
11001   * @returns {Boolean} found
11002   */
11003  function inStr(str, find) {
11004      return str.indexOf(find) > -1;
11005  }
11006
11007  /**
11008   * split string on whitespace
11009   * @param {String} str
11010   * @returns {Array} words
11011   */
11012  function splitStr(str) {
11013      return str.trim().split(/\s+/g);
11014  }
11015
11016  /**
11017   * find if a array contains the object using indexOf or a simple polyFill
11018   * @param {Array} src
11019   * @param {String} find
11020   * @param {String} [findByKey]
11021   * @return {Boolean|Number} false when not found, or the index
11022   */
11023  function inArray(src, find, findByKey) {
11024      if (src.indexOf && !findByKey) {
11025          return src.indexOf(find);
11026      } else {
11027          var i = 0;
11028          while (i < src.length) {
11029              if ((findByKey && src[i][findByKey] == find) || (!findByKey && src[i] === find)) {
11030                  return i;
11031              }
11032              i++;
11033          }
11034          return -1;
11035      }
11036  }
11037
11038  /**
11039   * convert array-like objects to real arrays
11040   * @param {Object} obj
11041   * @returns {Array}
11042   */
11043  function toArray(obj) {
11044      return Array.prototype.slice.call(obj, 0);
11045  }
11046
11047  /**
11048   * unique array with objects based on a key (like 'id') or just by the array's value
11049   * @param {Array} src [{id:1},{id:2},{id:1}]
11050   * @param {String} [key]
11051   * @param {Boolean} [sort=False]
11052   * @returns {Array} [{id:1},{id:2}]
11053   */
11054  function uniqueArray(src, key, sort) {
11055      var results = [];
11056      var values = [];
11057      var i = 0;
11058
11059      while (i < src.length) {
11060          var val = key ? src[i][key] : src[i];
11061          if (inArray(values, val) < 0) {
11062              results.push(src[i]);
11063          }
11064          values[i] = val;
11065          i++;
11066      }
11067
11068      if (sort) {
11069          if (!key) {
11070              results = results.sort();
11071          } else {
11072              results = results.sort(function sortUniqueArray(a, b) {
11073                  return a[key] > b[key];
11074              });
11075          }
11076      }
11077
11078      return results;
11079  }
11080
11081  /**
11082   * get the prefixed property
11083   * @param {Object} obj
11084   * @param {String} property
11085   * @returns {String|Undefined} prefixed
11086   */
11087  function prefixed(obj, property) {
11088      var prefix, prop;
11089      var camelProp = property[0].toUpperCase() + property.slice(1);
11090
11091      var i = 0;
11092      while (i < VENDOR_PREFIXES.length) {
11093          prefix = VENDOR_PREFIXES[i];
11094          prop = (prefix) ? prefix + camelProp : property;
11095
11096          if (prop in obj) {
11097              return prop;
11098          }
11099          i++;
11100      }
11101      return undefined;
11102  }
11103
11104  /**
11105   * get a unique id
11106   * @returns {number} uniqueId
11107   */
11108  var _uniqueId = 1;
11109  function uniqueId() {
11110      return _uniqueId++;
11111  }
11112
11113  /**
11114   * get the window object of an element
11115   * @param {HTMLElement} element
11116   * @returns {DocumentView|Window}
11117   */
11118  function getWindowForElement(element) {
11119      var doc = element.ownerDocument;
11120      return (doc.defaultView || doc.parentWindow);
11121  }
11122
11123  var MOBILE_REGEX = /mobile|tablet|ip(ad|hone|od)|android/i;
11124
11125  var SUPPORT_TOUCH = ('ontouchstart' in window);
11126  var SUPPORT_POINTER_EVENTS = prefixed(window, 'PointerEvent') !== undefined;
11127  var SUPPORT_ONLY_TOUCH = SUPPORT_TOUCH && MOBILE_REGEX.test(navigator.userAgent);
11128
11129  var INPUT_TYPE_TOUCH = 'touch';
11130  var INPUT_TYPE_PEN = 'pen';
11131  var INPUT_TYPE_MOUSE = 'mouse';
11132  var INPUT_TYPE_KINECT = 'kinect';
11133
11134  var COMPUTE_INTERVAL = 25;
11135
11136  var INPUT_START = 1;
11137  var INPUT_MOVE = 2;
11138  var INPUT_END = 4;
11139  var INPUT_CANCEL = 8;
11140
11141  var DIRECTION_NONE = 1;
11142  var DIRECTION_LEFT = 2;
11143  var DIRECTION_RIGHT = 4;
11144  var DIRECTION_UP = 8;
11145  var DIRECTION_DOWN = 16;
11146
11147  var DIRECTION_HORIZONTAL = DIRECTION_LEFT | DIRECTION_RIGHT;
11148  var DIRECTION_VERTICAL = DIRECTION_UP | DIRECTION_DOWN;
11149  var DIRECTION_ALL = DIRECTION_HORIZONTAL | DIRECTION_VERTICAL;
11150
11151  var PROPS_XY = ['x', 'y'];
11152  var PROPS_CLIENT_XY = ['clientX', 'clientY'];
11153
11154  /**
11155   * create new input type manager
11156   * @param {Manager} manager
11157   * @param {Function} callback
11158   * @returns {Input}
11159   * @constructor
11160   */
11161  function Input(manager, callback) {
11162      var self = this;
11163      this.manager = manager;
11164      this.callback = callback;
11165      this.element = manager.element;
11166      this.target = manager.options.inputTarget;
11167
11168      // smaller wrapper around the handler, for the scope and the enabled state of the manager,
11169      // so when disabled the input events are completely bypassed.
11170      this.domHandler = function(ev) {
11171          if (boolOrFn(manager.options.enable, [manager])) {
11172              self.handler(ev);
11173          }
11174      };
11175
11176      this.init();
11177
11178  }
11179
11180  Input.prototype = {
11181      /**
11182       * should handle the inputEvent data and trigger the callback
11183       * @virtual
11184       */
11185      handler: function() { },
11186
11187      /**
11188       * bind the events
11189       */
11190      init: function() {
11191          this.evEl && addEventListeners(this.element, this.evEl, this.domHandler);
11192          this.evTarget && addEventListeners(this.target, this.evTarget, this.domHandler);
11193          this.evWin && addEventListeners(getWindowForElement(this.element), this.evWin, this.domHandler);
11194      },
11195
11196      /**
11197       * unbind the events
11198       */
11199      destroy: function() {
11200          this.evEl && removeEventListeners(this.element, this.evEl, this.domHandler);
11201          this.evTarget && removeEventListeners(this.target, this.evTarget, this.domHandler);
11202          this.evWin && removeEventListeners(getWindowForElement(this.element), this.evWin, this.domHandler);
11203      }
11204  };
11205
11206  /**
11207   * create new input type manager
11208   * called by the Manager constructor
11209   * @param {Hammer} manager
11210   * @returns {Input}
11211   */
11212  function createInputInstance(manager) {
11213      var Type;
11214      var inputClass = manager.options.inputClass;
11215
11216      if (inputClass) {
11217          Type = inputClass;
11218      } else if (SUPPORT_POINTER_EVENTS) {
11219          Type = PointerEventInput;
11220      } else if (SUPPORT_ONLY_TOUCH) {
11221          Type = TouchInput;
11222      } else if (!SUPPORT_TOUCH) {
11223          Type = MouseInput;
11224      } else {
11225          Type = TouchMouseInput;
11226      }
11227      return new (Type)(manager, inputHandler);
11228  }
11229
11230  /**
11231   * handle input events
11232   * @param {Manager} manager
11233   * @param {String} eventType
11234   * @param {Object} input
11235   */
11236  function inputHandler(manager, eventType, input) {
11237      var pointersLen = input.pointers.length;
11238      var changedPointersLen = input.changedPointers.length;
11239      var isFirst = (eventType & INPUT_START && (pointersLen - changedPointersLen === 0));
11240      var isFinal = (eventType & (INPUT_END | INPUT_CANCEL) && (pointersLen - changedPointersLen === 0));
11241
11242      input.isFirst = !!isFirst;
11243      input.isFinal = !!isFinal;
11244
11245      if (isFirst) {
11246          manager.session = {};
11247      }
11248
11249      // source event is the normalized value of the domEvents
11250      // like 'touchstart, mouseup, pointerdown'
11251      input.eventType = eventType;
11252
11253      // compute scale, rotation etc
11254      computeInputData(manager, input);
11255
11256      // emit secret event
11257      manager.emit('hammer.input', input);
11258
11259      manager.recognize(input);
11260      manager.session.prevInput = input;
11261  }
11262
11263  /**
11264   * extend the data with some usable properties like scale, rotate, velocity etc
11265   * @param {Object} manager
11266   * @param {Object} input
11267   */
11268  function computeInputData(manager, input) {
11269      var session = manager.session;
11270      var pointers = input.pointers;
11271      var pointersLength = pointers.length;
11272
11273      // store the first input to calculate the distance and direction
11274      if (!session.firstInput) {
11275          session.firstInput = simpleCloneInputData(input);
11276      }
11277
11278      // to compute scale and rotation we need to store the multiple touches
11279      if (pointersLength > 1 && !session.firstMultiple) {
11280          session.firstMultiple = simpleCloneInputData(input);
11281      } else if (pointersLength === 1) {
11282          session.firstMultiple = false;
11283      }
11284
11285      var firstInput = session.firstInput;
11286      var firstMultiple = session.firstMultiple;
11287      var offsetCenter = firstMultiple ? firstMultiple.center : firstInput.center;
11288
11289      var center = input.center = getCenter(pointers);
11290      input.timeStamp = now();
11291      input.deltaTime = input.timeStamp - firstInput.timeStamp;
11292
11293      input.angle = getAngle(offsetCenter, center);
11294      input.distance = getDistance(offsetCenter, center);
11295
11296      computeDeltaXY(session, input);
11297      input.offsetDirection = getDirection(input.deltaX, input.deltaY);
11298
11299      input.scale = firstMultiple ? getScale(firstMultiple.pointers, pointers) : 1;
11300      input.rotation = firstMultiple ? getRotation(firstMultiple.pointers, pointers) : 0;
11301
11302      computeIntervalInputData(session, input);
11303
11304      // find the correct target
11305      var target = manager.element;
11306      if (hasParent(input.srcEvent.target, target)) {
11307          target = input.srcEvent.target;
11308      }
11309      input.target = target;
11310  }
11311
11312  function computeDeltaXY(session, input) {
11313      var center = input.center;
11314      var offset = session.offsetDelta || {};
11315      var prevDelta = session.prevDelta || {};
11316      var prevInput = session.prevInput || {};
11317
11318      if (input.eventType === INPUT_START || prevInput.eventType === INPUT_END) {
11319          prevDelta = session.prevDelta = {
11320              x: prevInput.deltaX || 0,
11321              y: prevInput.deltaY || 0
11322          };
11323
11324          offset = session.offsetDelta = {
11325              x: center.x,
11326              y: center.y
11327          };
11328      }
11329
11330      input.deltaX = prevDelta.x + (center.x - offset.x);
11331      input.deltaY = prevDelta.y + (center.y - offset.y);
11332  }
11333
11334  /**
11335   * velocity is calculated every x ms
11336   * @param {Object} session
11337   * @param {Object} input
11338   */
11339  function computeIntervalInputData(session, input) {
11340      var last = session.lastInterval || input,
11341          deltaTime = input.timeStamp - last.timeStamp,
11342          velocity, velocityX, velocityY, direction;
11343
11344      if (input.eventType != INPUT_CANCEL && (deltaTime > COMPUTE_INTERVAL || last.velocity === undefined)) {
11345          var deltaX = last.deltaX - input.deltaX;
11346          var deltaY = last.deltaY - input.deltaY;
11347
11348          var v = getVelocity(deltaTime, deltaX, deltaY);
11349          velocityX = v.x;
11350          velocityY = v.y;
11351          velocity = (abs(v.x) > abs(v.y)) ? v.x : v.y;
11352          direction = getDirection(deltaX, deltaY);
11353
11354          session.lastInterval = input;
11355      } else {
11356          // use latest velocity info if it doesn't overtake a minimum period
11357          velocity = last.velocity;
11358          velocityX = last.velocityX;
11359          velocityY = last.velocityY;
11360          direction = last.direction;
11361      }
11362
11363      input.velocity = velocity;
11364      input.velocityX = velocityX;
11365      input.velocityY = velocityY;
11366      input.direction = direction;
11367  }
11368
11369  /**
11370   * create a simple clone from the input used for storage of firstInput and firstMultiple
11371   * @param {Object} input
11372   * @returns {Object} clonedInputData
11373   */
11374  function simpleCloneInputData(input) {
11375      // make a simple copy of the pointers because we will get a reference if we don't
11376      // we only need clientXY for the calculations
11377      var pointers = [];
11378      var i = 0;
11379      while (i < input.pointers.length) {
11380          pointers[i] = {
11381              clientX: round(input.pointers[i].clientX),
11382              clientY: round(input.pointers[i].clientY)
11383          };
11384          i++;
11385      }
11386
11387      return {
11388          timeStamp: now(),
11389          pointers: pointers,
11390          center: getCenter(pointers),
11391          deltaX: input.deltaX,
11392          deltaY: input.deltaY
11393      };
11394  }
11395
11396  /**
11397   * get the center of all the pointers
11398   * @param {Array} pointers
11399   * @return {Object} center contains `x` and `y` properties
11400   */
11401  function getCenter(pointers) {
11402      var pointersLength = pointers.length;
11403
11404      // no need to loop when only one touch
11405      if (pointersLength === 1) {
11406          return {
11407              x: round(pointers[0].clientX),
11408              y: round(pointers[0].clientY)
11409          };
11410      }
11411
11412      var x = 0, y = 0, i = 0;
11413      while (i < pointersLength) {
11414          x += pointers[i].clientX;
11415          y += pointers[i].clientY;
11416          i++;
11417      }
11418
11419      return {
11420          x: round(x / pointersLength),
11421          y: round(y / pointersLength)
11422      };
11423  }
11424
11425  /**
11426   * calculate the velocity between two points. unit is in px per ms.
11427   * @param {Number} deltaTime
11428   * @param {Number} x
11429   * @param {Number} y
11430   * @return {Object} velocity `x` and `y`
11431   */
11432  function getVelocity(deltaTime, x, y) {
11433      return {
11434          x: x / deltaTime || 0,
11435          y: y / deltaTime || 0
11436      };
11437  }
11438
11439  /**
11440   * get the direction between two points
11441   * @param {Number} x
11442   * @param {Number} y
11443   * @return {Number} direction
11444   */
11445  function getDirection(x, y) {
11446      if (x === y) {
11447          return DIRECTION_NONE;
11448      }
11449
11450      if (abs(x) >= abs(y)) {
11451          return x > 0 ? DIRECTION_LEFT : DIRECTION_RIGHT;
11452      }
11453      return y > 0 ? DIRECTION_UP : DIRECTION_DOWN;
11454  }
11455
11456  /**
11457   * calculate the absolute distance between two points
11458   * @param {Object} p1 {x, y}
11459   * @param {Object} p2 {x, y}
11460   * @param {Array} [props] containing x and y keys
11461   * @return {Number} distance
11462   */
11463  function getDistance(p1, p2, props) {
11464      if (!props) {
11465          props = PROPS_XY;
11466      }
11467      var x = p2[props[0]] - p1[props[0]],
11468          y = p2[props[1]] - p1[props[1]];
11469
11470      return Math.sqrt((x * x) + (y * y));
11471  }
11472
11473  /**
11474   * calculate the angle between two coordinates
11475   * @param {Object} p1
11476   * @param {Object} p2
11477   * @param {Array} [props] containing x and y keys
11478   * @return {Number} angle
11479   */
11480  function getAngle(p1, p2, props) {
11481      if (!props) {
11482          props = PROPS_XY;
11483      }
11484      var x = p2[props[0]] - p1[props[0]],
11485          y = p2[props[1]] - p1[props[1]];
11486      return Math.atan2(y, x) * 180 / Math.PI;
11487  }
11488
11489  /**
11490   * calculate the rotation degrees between two pointersets
11491   * @param {Array} start array of pointers
11492   * @param {Array} end array of pointers
11493   * @return {Number} rotation
11494   */
11495  function getRotation(start, end) {
11496      return getAngle(end[1], end[0], PROPS_CLIENT_XY) - getAngle(start[1], start[0], PROPS_CLIENT_XY);
11497  }
11498
11499  /**
11500   * calculate the scale factor between two pointersets
11501   * no scale is 1, and goes down to 0 when pinched together, and bigger when pinched out
11502   * @param {Array} start array of pointers
11503   * @param {Array} end array of pointers
11504   * @return {Number} scale
11505   */
11506  function getScale(start, end) {
11507      return getDistance(end[0], end[1], PROPS_CLIENT_XY) / getDistance(start[0], start[1], PROPS_CLIENT_XY);
11508  }
11509
11510  var MOUSE_INPUT_MAP = {
11511      mousedown: INPUT_START,
11512      mousemove: INPUT_MOVE,
11513      mouseup: INPUT_END
11514  };
11515
11516  var MOUSE_ELEMENT_EVENTS = 'mousedown';
11517  var MOUSE_WINDOW_EVENTS = 'mousemove mouseup';
11518
11519  /**
11520   * Mouse events input
11521   * @constructor
11522   * @extends Input
11523   */
11524  function MouseInput() {
11525      this.evEl = MOUSE_ELEMENT_EVENTS;
11526      this.evWin = MOUSE_WINDOW_EVENTS;
11527
11528      this.allow = true; // used by Input.TouchMouse to disable mouse events
11529      this.pressed = false; // mousedown state
11530
11531      Input.apply(this, arguments);
11532  }
11533
11534  inherit(MouseInput, Input, {
11535      /**
11536       * handle mouse events
11537       * @param {Object} ev
11538       */
11539      handler: function MEhandler(ev) {
11540          var eventType = MOUSE_INPUT_MAP[ev.type];
11541
11542          // on start we want to have the left mouse button down
11543          if (eventType & INPUT_START && ev.button === 0) {
11544              this.pressed = true;
11545          }
11546
11547          if (eventType & INPUT_MOVE && ev.which !== 1) {
11548              eventType = INPUT_END;
11549          }
11550
11551          // mouse must be down, and mouse events are allowed (see the TouchMouse input)
11552          if (!this.pressed || !this.allow) {
11553              return;
11554          }
11555
11556          if (eventType & INPUT_END) {
11557              this.pressed = false;
11558          }
11559
11560          this.callback(this.manager, eventType, {
11561              pointers: [ev],
11562              changedPointers: [ev],
11563              pointerType: INPUT_TYPE_MOUSE,
11564              srcEvent: ev
11565          });
11566      }
11567  });
11568
11569  var POINTER_INPUT_MAP = {
11570      pointerdown: INPUT_START,
11571      pointermove: INPUT_MOVE,
11572      pointerup: INPUT_END,
11573      pointercancel: INPUT_CANCEL,
11574      pointerout: INPUT_CANCEL
11575  };
11576
11577  // in IE10 the pointer types is defined as an enum
11578  var IE10_POINTER_TYPE_ENUM = {
11579      2: INPUT_TYPE_TOUCH,
11580      3: INPUT_TYPE_PEN,
11581      4: INPUT_TYPE_MOUSE,
11582      5: INPUT_TYPE_KINECT // see https://twitter.com/jacobrossi/status/480596438489890816
11583  };
11584
11585  var POINTER_ELEMENT_EVENTS = 'pointerdown';
11586  var POINTER_WINDOW_EVENTS = 'pointermove pointerup pointercancel';
11587
11588  // IE10 has prefixed support, and case-sensitive
11589  if (window.MSPointerEvent) {
11590      POINTER_ELEMENT_EVENTS = 'MSPointerDown';
11591      POINTER_WINDOW_EVENTS = 'MSPointerMove MSPointerUp MSPointerCancel';
11592  }
11593
11594  /**
11595   * Pointer events input
11596   * @constructor
11597   * @extends Input
11598   */
11599  function PointerEventInput() {
11600      this.evEl = POINTER_ELEMENT_EVENTS;
11601      this.evWin = POINTER_WINDOW_EVENTS;
11602
11603      Input.apply(this, arguments);
11604
11605      this.store = (this.manager.session.pointerEvents = []);
11606  }
11607
11608  inherit(PointerEventInput, Input, {
11609      /**
11610       * handle mouse events
11611       * @param {Object} ev
11612       */
11613      handler: function PEhandler(ev) {
11614          var store = this.store;
11615          var removePointer = false;
11616
11617          var eventTypeNormalized = ev.type.toLowerCase().replace('ms', '');
11618          var eventType = POINTER_INPUT_MAP[eventTypeNormalized];
11619          var pointerType = IE10_POINTER_TYPE_ENUM[ev.pointerType] || ev.pointerType;
11620
11621          var isTouch = (pointerType == INPUT_TYPE_TOUCH);
11622
11623          // get index of the event in the store
11624          var storeIndex = inArray(store, ev.pointerId, 'pointerId');
11625
11626          // start and mouse must be down
11627          if (eventType & INPUT_START && (ev.button === 0 || isTouch)) {
11628              if (storeIndex < 0) {
11629                  store.push(ev);
11630                  storeIndex = store.length - 1;
11631              }
11632          } else if (eventType & (INPUT_END | INPUT_CANCEL)) {
11633              removePointer = true;
11634          }
11635
11636          // it not found, so the pointer hasn't been down (so it's probably a hover)
11637          if (storeIndex < 0) {
11638              return;
11639          }
11640
11641          // update the event in the store
11642          store[storeIndex] = ev;
11643
11644          this.callback(this.manager, eventType, {
11645              pointers: store,
11646              changedPointers: [ev],
11647              pointerType: pointerType,
11648              srcEvent: ev
11649          });
11650
11651          if (removePointer) {
11652              // remove from the store
11653              store.splice(storeIndex, 1);
11654          }
11655      }
11656  });
11657
11658  var SINGLE_TOUCH_INPUT_MAP = {
11659      touchstart: INPUT_START,
11660      touchmove: INPUT_MOVE,
11661      touchend: INPUT_END,
11662      touchcancel: INPUT_CANCEL
11663  };
11664
11665  var SINGLE_TOUCH_TARGET_EVENTS = 'touchstart';
11666  var SINGLE_TOUCH_WINDOW_EVENTS = 'touchstart touchmove touchend touchcancel';
11667
11668  /**
11669   * Touch events input
11670   * @constructor
11671   * @extends Input
11672   */
11673  function SingleTouchInput() {
11674      this.evTarget = SINGLE_TOUCH_TARGET_EVENTS;
11675      this.evWin = SINGLE_TOUCH_WINDOW_EVENTS;
11676      this.started = false;
11677
11678      Input.apply(this, arguments);
11679  }
11680
11681  inherit(SingleTouchInput, Input, {
11682      handler: function TEhandler(ev) {
11683          var type = SINGLE_TOUCH_INPUT_MAP[ev.type];
11684
11685          // should we handle the touch events?
11686          if (type === INPUT_START) {
11687              this.started = true;
11688          }
11689
11690          if (!this.started) {
11691              return;
11692          }
11693
11694          var touches = normalizeSingleTouches.call(this, ev, type);
11695
11696          // when done, reset the started state
11697          if (type & (INPUT_END | INPUT_CANCEL) && touches[0].length - touches[1].length === 0) {
11698              this.started = false;
11699          }
11700
11701          this.callback(this.manager, type, {
11702              pointers: touches[0],
11703              changedPointers: touches[1],
11704              pointerType: INPUT_TYPE_TOUCH,
11705              srcEvent: ev
11706          });
11707      }
11708  });
11709
11710  /**
11711   * @this {TouchInput}
11712   * @param {Object} ev
11713   * @param {Number} type flag
11714   * @returns {undefined|Array} [all, changed]
11715   */
11716  function normalizeSingleTouches(ev, type) {
11717      var all = toArray(ev.touches);
11718      var changed = toArray(ev.changedTouches);
11719
11720      if (type & (INPUT_END | INPUT_CANCEL)) {
11721          all = uniqueArray(all.concat(changed), 'identifier', true);
11722      }
11723
11724      return [all, changed];
11725  }
11726
11727  var TOUCH_INPUT_MAP = {
11728      touchstart: INPUT_START,
11729      touchmove: INPUT_MOVE,
11730      touchend: INPUT_END,
11731      touchcancel: INPUT_CANCEL
11732  };
11733
11734  var TOUCH_TARGET_EVENTS = 'touchstart touchmove touchend touchcancel';
11735
11736  /**
11737   * Multi-user touch events input
11738   * @constructor
11739   * @extends Input
11740   */
11741  function TouchInput() {
11742      this.evTarget = TOUCH_TARGET_EVENTS;
11743      this.targetIds = {};
11744
11745      Input.apply(this, arguments);
11746  }
11747
11748  inherit(TouchInput, Input, {
11749      handler: function MTEhandler(ev) {
11750          var type = TOUCH_INPUT_MAP[ev.type];
11751          var touches = getTouches.call(this, ev, type);
11752          if (!touches) {
11753              return;
11754          }
11755
11756          this.callback(this.manager, type, {
11757              pointers: touches[0],
11758              changedPointers: touches[1],
11759              pointerType: INPUT_TYPE_TOUCH,
11760              srcEvent: ev
11761          });
11762      }
11763  });
11764
11765  /**
11766   * @this {TouchInput}
11767   * @param {Object} ev
11768   * @param {Number} type flag
11769   * @returns {undefined|Array} [all, changed]
11770   */
11771  function getTouches(ev, type) {
11772      var allTouches = toArray(ev.touches);
11773      var targetIds = this.targetIds;
11774
11775      // when there is only one touch, the process can be simplified
11776      if (type & (INPUT_START | INPUT_MOVE) && allTouches.length === 1) {
11777          targetIds[allTouches[0].identifier] = true;
11778          return [allTouches, allTouches];
11779      }
11780
11781      var i,
11782          targetTouches,
11783          changedTouches = toArray(ev.changedTouches),
11784          changedTargetTouches = [],
11785          target = this.target;
11786
11787      // get target touches from touches
11788      targetTouches = allTouches.filter(function(touch) {
11789          return hasParent(touch.target, target);
11790      });
11791
11792      // collect touches
11793      if (type === INPUT_START) {
11794          i = 0;
11795          while (i < targetTouches.length) {
11796              targetIds[targetTouches[i].identifier] = true;
11797              i++;
11798          }
11799      }
11800
11801      // filter changed touches to only contain touches that exist in the collected target ids
11802      i = 0;
11803      while (i < changedTouches.length) {
11804          if (targetIds[changedTouches[i].identifier]) {
11805              changedTargetTouches.push(changedTouches[i]);
11806          }
11807
11808          // cleanup removed touches
11809          if (type & (INPUT_END | INPUT_CANCEL)) {
11810              delete targetIds[changedTouches[i].identifier];
11811          }
11812          i++;
11813      }
11814
11815      if (!changedTargetTouches.length) {
11816          return;
11817      }
11818
11819      return [
11820          // merge targetTouches with changedTargetTouches so it contains ALL touches, including 'end' and 'cancel'
11821          uniqueArray(targetTouches.concat(changedTargetTouches), 'identifier', true),
11822          changedTargetTouches
11823      ];
11824  }
11825
11826  /**
11827   * Combined touch and mouse input
11828   *
11829   * Touch has a higher priority then mouse, and while touching no mouse events are allowed.
11830   * This because touch devices also emit mouse events while doing a touch.
11831   *
11832   * @constructor
11833   * @extends Input
11834   */
11835  function TouchMouseInput() {
11836      Input.apply(this, arguments);
11837
11838      var handler = bindFn(this.handler, this);
11839      this.touch = new TouchInput(this.manager, handler);
11840      this.mouse = new MouseInput(this.manager, handler);
11841  }
11842
11843  inherit(TouchMouseInput, Input, {
11844      /**
11845       * handle mouse and touch events
11846       * @param {Hammer} manager
11847       * @param {String} inputEvent
11848       * @param {Object} inputData
11849       */
11850      handler: function TMEhandler(manager, inputEvent, inputData) {
11851          var isTouch = (inputData.pointerType == INPUT_TYPE_TOUCH),
11852              isMouse = (inputData.pointerType == INPUT_TYPE_MOUSE);
11853
11854          // when we're in a touch event, so  block all upcoming mouse events
11855          // most mobile browser also emit mouseevents, right after touchstart
11856          if (isTouch) {
11857              this.mouse.allow = false;
vendor: 10,820 bytes, lines 11858-12208
11858          } else if (isMouse && !this.mouse.allow) {
11859              return;
11860          }
11861
11862          // reset the allowMouse when we're done
11863          if (inputEvent & (INPUT_END | INPUT_CANCEL)) {
11864              this.mouse.allow = true;
11865          }
11866
11867          this.callback(manager, inputEvent, inputData);
11868      },
11869
11870      /**
11871       * remove the event listeners
11872       */
11873      destroy: function destroy() {
11874          this.touch.destroy();
11875          this.mouse.destroy();
11876      }
11877  });
11878
11879  var PREFIXED_TOUCH_ACTION = prefixed(TEST_ELEMENT.style, 'touchAction');
11880  var NATIVE_TOUCH_ACTION = PREFIXED_TOUCH_ACTION !== undefined;
11881
11882  // magical touchAction value
11883  var TOUCH_ACTION_COMPUTE = 'compute';
11884  var TOUCH_ACTION_AUTO = 'auto';
11885  var TOUCH_ACTION_MANIPULATION = 'manipulation'; // not implemented
11886  var TOUCH_ACTION_NONE = 'none';
11887  var TOUCH_ACTION_PAN_X = 'pan-x';
11888  var TOUCH_ACTION_PAN_Y = 'pan-y';
11889
11890  /**
11891   * Touch Action
11892   * sets the touchAction property or uses the js alternative
11893   * @param {Manager} manager
11894   * @param {String} value
11895   * @constructor
11896   */
11897  function TouchAction(manager, value) {
11898      this.manager = manager;
11899      this.set(value);
11900  }
11901
11902  TouchAction.prototype = {
11903      /**
11904       * set the touchAction value on the element or enable the polyfill
11905       * @param {String} value
11906       */
11907      set: function(value) {
11908          // find out the touch-action by the event handlers
11909          if (value == TOUCH_ACTION_COMPUTE) {
11910              value = this.compute();
11911          }
11912
11913          if (NATIVE_TOUCH_ACTION) {
11914              this.manager.element.style[PREFIXED_TOUCH_ACTION] = value;
11915          }
11916          this.actions = value.toLowerCase().trim();
11917      },
11918
11919      /**
11920       * just re-set the touchAction value
11921       */
11922      update: function() {
11923          this.set(this.manager.options.touchAction);
11924      },
11925
11926      /**
11927       * compute the value for the touchAction property based on the recognizer's settings
11928       * @returns {String} value
11929       */
11930      compute: function() {
11931          var actions = [];
11932          each(this.manager.recognizers, function(recognizer) {
11933              if (boolOrFn(recognizer.options.enable, [recognizer])) {
11934                  actions = actions.concat(recognizer.getTouchAction());
11935              }
11936          });
11937          return cleanTouchActions(actions.join(' '));
11938      },
11939
11940      /**
11941       * this method is called on each input cycle and provides the preventing of the browser behavior
11942       * @param {Object} input
11943       */
11944      preventDefaults: function(input) {
11945          // not needed with native support for the touchAction property
11946          if (NATIVE_TOUCH_ACTION) {
11947              return;
11948          }
11949
11950          var srcEvent = input.srcEvent;
11951          var direction = input.offsetDirection;
11952
11953          // if the touch action did prevented once this session
11954          if (this.manager.session.prevented) {
11955              srcEvent.preventDefault();
11956              return;
11957          }
11958
11959          var actions = this.actions;
11960          var hasNone = inStr(actions, TOUCH_ACTION_NONE);
11961          var hasPanY = inStr(actions, TOUCH_ACTION_PAN_Y);
11962          var hasPanX = inStr(actions, TOUCH_ACTION_PAN_X);
11963
11964          if (hasNone ||
11965              (hasPanY && direction & DIRECTION_HORIZONTAL) ||
11966              (hasPanX && direction & DIRECTION_VERTICAL)) {
11967              return this.preventSrc(srcEvent);
11968          }
11969      },
11970
11971      /**
11972       * call preventDefault to prevent the browser's default behavior (scrolling in most cases)
11973       * @param {Object} srcEvent
11974       */
11975      preventSrc: function(srcEvent) {
11976          this.manager.session.prevented = true;
11977          srcEvent.preventDefault();
11978      }
11979  };
11980
11981  /**
11982   * when the touchActions are collected they are not a valid value, so we need to clean things up. *
11983   * @param {String} actions
11984   * @returns {*}
11985   */
11986  function cleanTouchActions(actions) {
11987      // none
11988      if (inStr(actions, TOUCH_ACTION_NONE)) {
11989          return TOUCH_ACTION_NONE;
11990      }
11991
11992      var hasPanX = inStr(actions, TOUCH_ACTION_PAN_X);
11993      var hasPanY = inStr(actions, TOUCH_ACTION_PAN_Y);
11994
11995      // pan-x and pan-y can be combined
11996      if (hasPanX && hasPanY) {
11997          return TOUCH_ACTION_PAN_X + ' ' + TOUCH_ACTION_PAN_Y;
11998      }
11999
12000      // pan-x OR pan-y
12001      if (hasPanX || hasPanY) {
12002          return hasPanX ? TOUCH_ACTION_PAN_X : TOUCH_ACTION_PAN_Y;
12003      }
12004
12005      // manipulation
12006      if (inStr(actions, TOUCH_ACTION_MANIPULATION)) {
12007          return TOUCH_ACTION_MANIPULATION;
12008      }
12009
12010      return TOUCH_ACTION_AUTO;
12011  }
12012
12013  /**
12014   * Recognizer flow explained; *
12015   * All recognizers have the initial state of POSSIBLE when a input session starts.
12016   * The definition of a input session is from the first input until the last input, with all it's movement in it. *
12017   * Example session for mouse-input: mousedown -> mousemove -> mouseup
12018   *
12019   * On each recognizing cycle (see Manager.recognize) the .recognize() method is executed
12020   * which determines with state it should be.
12021   *
12022   * If the recognizer has the state FAILED, CANCELLED or RECOGNIZED (equals ENDED), it is reset to
12023   * POSSIBLE to give it another change on the next cycle.
12024   *
12025   *               Possible
12026   *                  |
12027   *            +-----+---------------+
12028   *            |                     |
12029   *      +-----+-----+               |
12030   *      |           |               |
12031   *   Failed      Cancelled          |
12032   *                          +-------+------+
12033   *                          |              |
12034   *                      Recognized       Began
12035   *                                         |
12036   *                                      Changed
12037   *                                         |
12038   *                                  Ended/Recognized
12039   */
12040  var STATE_POSSIBLE = 1;
12041  var STATE_BEGAN = 2;
12042  var STATE_CHANGED = 4;
12043  var STATE_ENDED = 8;
12044  var STATE_RECOGNIZED = STATE_ENDED;
12045  var STATE_CANCELLED = 16;
12046  var STATE_FAILED = 32;
12047
12048  /**
12049   * Recognizer
12050   * Every recognizer needs to extend from this class.
12051   * @constructor
12052   * @param {Object} options
12053   */
12054  function Recognizer(options) {
12055      this.id = uniqueId();
12056
12057      this.manager = null;
12058      this.options = merge(options || {}, this.defaults);
12059
12060      // default is enable true
12061      this.options.enable = ifUndefined(this.options.enable, true);
12062
12063      this.state = STATE_POSSIBLE;
12064
12065      this.simultaneous = {};
12066      this.requireFail = [];
12067  }
12068
12069  Recognizer.prototype = {
12070      /**
12071       * @virtual
12072       * @type {Object}
12073       */
12074      defaults: {},
12075
12076      /**
12077       * set options
12078       * @param {Object} options
12079       * @return {Recognizer}
12080       */
12081      set: function(options) {
12082          extend(this.options, options);
12083
12084          // also update the touchAction, in case something changed about the directions/enabled state
12085          this.manager && this.manager.touchAction.update();
12086          return this;
12087      },
12088
12089      /**
12090       * recognize simultaneous with an other recognizer.
12091       * @param {Recognizer} otherRecognizer
12092       * @returns {Recognizer} this
12093       */
12094      recognizeWith: function(otherRecognizer) {
12095          if (invokeArrayArg(otherRecognizer, 'recognizeWith', this)) {
12096              return this;
12097          }
12098
12099          var simultaneous = this.simultaneous;
12100          otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this);
12101          if (!simultaneous[otherRecognizer.id]) {
12102              simultaneous[otherRecognizer.id] = otherRecognizer;
12103              otherRecognizer.recognizeWith(this);
12104          }
12105          return this;
12106      },
12107
12108      /**
12109       * drop the simultaneous link. it doesnt remove the link on the other recognizer.
12110       * @param {Recognizer} otherRecognizer
12111       * @returns {Recognizer} this
12112       */
12113      dropRecognizeWith: function(otherRecognizer) {
12114          if (invokeArrayArg(otherRecognizer, 'dropRecognizeWith', this)) {
12115              return this;
12116          }
12117
12118          otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this);
12119          delete this.simultaneous[otherRecognizer.id];
12120          return this;
12121      },
12122
12123      /**
12124       * recognizer can only run when an other is failing
12125       * @param {Recognizer} otherRecognizer
12126       * @returns {Recognizer} this
12127       */
12128      requireFailure: function(otherRecognizer) {
12129          if (invokeArrayArg(otherRecognizer, 'requireFailure', this)) {
12130              return this;
12131          }
12132
12133          var requireFail = this.requireFail;
12134          otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this);
12135          if (inArray(requireFail, otherRecognizer) === -1) {
12136              requireFail.push(otherRecognizer);
12137              otherRecognizer.requireFailure(this);
12138          }
12139          return this;
12140      },
12141
12142      /**
12143       * drop the requireFailure link. it does not remove the link on the other recognizer.
12144       * @param {Recognizer} otherRecognizer
12145       * @returns {Recognizer} this
12146       */
12147      dropRequireFailure: function(otherRecognizer) {
12148          if (invokeArrayArg(otherRecognizer, 'dropRequireFailure', this)) {
12149              return this;
12150          }
12151
12152          otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this);
12153          var index = inArray(this.requireFail, otherRecognizer);
12154          if (index > -1) {
12155              this.requireFail.splice(index, 1);
12156          }
12157          return this;
12158      },
12159
12160      /**
12161       * has require failures boolean
12162       * @returns {boolean}
12163       */
12164      hasRequireFailures: function() {
12165          return this.requireFail.length > 0;
12166      },
12167
12168      /**
12169       * if the recognizer can recognize simultaneous with an other recognizer
12170       * @param {Recognizer} otherRecognizer
12171       * @returns {Boolean}
12172       */
12173      canRecognizeWith: function(otherRecognizer) {
12174          return !!this.simultaneous[otherRecognizer.id];
12175      },
12176
12177      /**
12178       * You should use `tryEmit` instead of `emit` directly to check
12179       * that all the needed recognizers has failed before emitting.
12180       * @param {Object} input
12181       */
12182      emit: function(input) {
12183          var self = this;
12184          var state = this.state;
12185
12186          function emit(withState) {
12187              self.manager.emit(self.options.event + (withState ? stateStr(state) : ''), input);
12188          }
12189
12190          // 'panstart' and 'panmove'
12191          if (state < STATE_ENDED) {
12192              emit(true);
12193          }
12194
12195          emit(); // simple 'eventName' events
12196
12197          // panend and pancancel
12198          if (state >= STATE_ENDED) {
12199              emit(true);
12200          }
12201      },
12202
12203      /**
12204       * Check that all the require failure recognizers has failed,
12205       * if true, it emits a gesture event,
12206       * otherwise, setup the state to FAILED.
12207       * @param {Object} input
12208       */
vendor: 7,710 bytes, lines 12209-12477
12209      tryEmit: function(input) {
12210          if (this.canEmit()) {
12211              return this.emit(input);
12212          }
12213          // it's failing anyway
12214          this.state = STATE_FAILED;
12215      },
12216
12217      /**
12218       * can we emit?
12219       * @returns {boolean}
12220       */
12221      canEmit: function() {
12222          var i = 0;
12223          while (i < this.requireFail.length) {
12224              if (!(this.requireFail[i].state & (STATE_FAILED | STATE_POSSIBLE))) {
12225                  return false;
12226              }
12227              i++;
12228          }
12229          return true;
12230      },
12231
12232      /**
12233       * update the recognizer
12234       * @param {Object} inputData
12235       */
12236      recognize: function(inputData) {
12237          // make a new copy of the inputData
12238          // so we can change the inputData without messing up the other recognizers
12239          var inputDataClone = extend({}, inputData);
12240
12241          // is is enabled and allow recognizing?
12242          if (!boolOrFn(this.options.enable, [this, inputDataClone])) {
12243              this.reset();
12244              this.state = STATE_FAILED;
12245              return;
12246          }
12247
12248          // reset when we've reached the end
12249          if (this.state & (STATE_RECOGNIZED | STATE_CANCELLED | STATE_FAILED)) {
12250              this.state = STATE_POSSIBLE;
12251          }
12252
12253          this.state = this.process(inputDataClone);
12254
12255          // the recognizer has recognized a gesture
12256          // so trigger an event
12257          if (this.state & (STATE_BEGAN | STATE_CHANGED | STATE_ENDED | STATE_CANCELLED)) {
12258              this.tryEmit(inputDataClone);
12259          }
12260      },
12261
12262      /**
12263       * return the state of the recognizer
12264       * the actual recognizing happens in this method
12265       * @virtual
12266       * @param {Object} inputData
12267       * @returns {Const} STATE
12268       */
12269      process: function(inputData) { }, // jshint ignore:line
12270
12271      /**
12272       * return the preferred touch-action
12273       * @virtual
12274       * @returns {Array}
12275       */
12276      getTouchAction: function() { },
12277
12278      /**
12279       * called when the gesture isn't allowed to recognize
12280       * like when another is being recognized or it is disabled
12281       * @virtual
12282       */
12283      reset: function() { }
12284  };
12285
12286  /**
12287   * get a usable string, used as event postfix
12288   * @param {Const} state
12289   * @returns {String} state
12290   */
12291  function stateStr(state) {
12292      if (state & STATE_CANCELLED) {
12293          return 'cancel';
12294      } else if (state & STATE_ENDED) {
12295          return 'end';
12296      } else if (state & STATE_CHANGED) {
12297          return 'move';
12298      } else if (state & STATE_BEGAN) {
12299          return 'start';
12300      }
12301      return '';
12302  }
12303
12304  /**
12305   * direction cons to string
12306   * @param {Const} direction
12307   * @returns {String}
12308   */
12309  function directionStr(direction) {
12310      if (direction == DIRECTION_DOWN) {
12311          return 'down';
12312      } else if (direction == DIRECTION_UP) {
12313          return 'up';
12314      } else if (direction == DIRECTION_LEFT) {
12315          return 'left';
12316      } else if (direction == DIRECTION_RIGHT) {
12317          return 'right';
12318      }
12319      return '';
12320  }
12321
12322  /**
12323   * get a recognizer by name if it is bound to a manager
12324   * @param {Recognizer|String} otherRecognizer
12325   * @param {Recognizer} recognizer
12326   * @returns {Recognizer}
12327   */
12328  function getRecognizerByNameIfManager(otherRecognizer, recognizer) {
12329      var manager = recognizer.manager;
12330      if (manager) {
12331          return manager.get(otherRecognizer);
12332      }
12333      return otherRecognizer;
12334  }
12335
12336  /**
12337   * This recognizer is just used as a base for the simple attribute recognizers.
12338   * @constructor
12339   * @extends Recognizer
12340   */
12341  function AttrRecognizer() {
12342      Recognizer.apply(this, arguments);
12343  }
12344
12345  inherit(AttrRecognizer, Recognizer, {
12346      /**
12347       * @namespace
12348       * @memberof AttrRecognizer
12349       */
12350      defaults: {
12351          /**
12352           * @type {Number}
12353           * @default 1
12354           */
12355          pointers: 1
12356      },
12357
12358      /**
12359       * Used to check if it the recognizer receives valid input, like input.distance > 10.
12360       * @memberof AttrRecognizer
12361       * @param {Object} input
12362       * @returns {Boolean} recognized
12363       */
12364      attrTest: function(input) {
12365          var optionPointers = this.options.pointers;
12366          return optionPointers === 0 || input.pointers.length === optionPointers;
12367      },
12368
12369      /**
12370       * Process the input and return the state for the recognizer
12371       * @memberof AttrRecognizer
12372       * @param {Object} input
12373       * @returns {*} State
12374       */
12375      process: function(input) {
12376          var state = this.state;
12377          var eventType = input.eventType;
12378
12379          var isRecognized = state & (STATE_BEGAN | STATE_CHANGED);
12380          var isValid = this.attrTest(input);
12381
12382          // on cancel input and we've recognized before, return STATE_CANCELLED
12383          if (isRecognized && (eventType & INPUT_CANCEL || !isValid)) {
12384              return state | STATE_CANCELLED;
12385          } else if (isRecognized || isValid) {
12386              if (eventType & INPUT_END) {
12387                  return state | STATE_ENDED;
12388              } else if (!(state & STATE_BEGAN)) {
12389                  return STATE_BEGAN;
12390              }
12391              return state | STATE_CHANGED;
12392          }
12393          return STATE_FAILED;
12394      }
12395  });
12396
12397  /**
12398   * Pan
12399   * Recognized when the pointer is down and moved in the allowed direction.
12400   * @constructor
12401   * @extends AttrRecognizer
12402   */
12403  function PanRecognizer() {
12404      AttrRecognizer.apply(this, arguments);
12405
12406      this.pX = null;
12407      this.pY = null;
12408  }
12409
12410  inherit(PanRecognizer, AttrRecognizer, {
12411      /**
12412       * @namespace
12413       * @memberof PanRecognizer
12414       */
12415      defaults: {
12416          event: 'pan',
12417          threshold: 10,
12418          pointers: 1,
12419          direction: DIRECTION_ALL
12420      },
12421
12422      getTouchAction: function() {
12423          var direction = this.options.direction;
12424          var actions = [];
12425          if (direction & DIRECTION_HORIZONTAL) {
12426              actions.push(TOUCH_ACTION_PAN_Y);
12427          }
12428          if (direction & DIRECTION_VERTICAL) {
12429              actions.push(TOUCH_ACTION_PAN_X);
12430          }
12431          return actions;
12432      },
12433
12434      directionTest: function(input) {
12435          var options = this.options;
12436          var hasMoved = true;
12437          var distance = input.distance;
12438          var direction = input.direction;
12439          var x = input.deltaX;
12440          var y = input.deltaY;
12441
12442          // lock to axis?
12443          if (!(direction & options.direction)) {
12444              if (options.direction & DIRECTION_HORIZONTAL) {
12445                  direction = (x === 0) ? DIRECTION_NONE : (x < 0) ? DIRECTION_LEFT : DIRECTION_RIGHT;
12446                  hasMoved = x != this.pX;
12447                  distance = Math.abs(input.deltaX);
12448              } else {
12449                  direction = (y === 0) ? DIRECTION_NONE : (y < 0) ? DIRECTION_UP : DIRECTION_DOWN;
12450                  hasMoved = y != this.pY;
12451                  distance = Math.abs(input.deltaY);
12452              }
12453          }
12454          input.direction = direction;
12455          return hasMoved && distance > options.threshold && direction & options.direction;
12456      },
12457
12458      attrTest: function(input) {
12459          return AttrRecognizer.prototype.attrTest.call(this, input) &&
12460              (this.state & STATE_BEGAN || (!(this.state & STATE_BEGAN) && this.directionTest(input)));
12461      },
12462
12463      emit: function(input) {
12464          this.pX = input.deltaX;
12465          this.pY = input.deltaY;
12466
12467          var direction = directionStr(input.direction);
12468          if (direction) {
12469              this.manager.emit(this.options.event + direction, input);
12470          }
12471
12472          this._super.emit.call(this, input);
12473      }
12474  });
12475
12476  /**
12477   * Pinch
12478   * Recognized when two or more pointers are moving toward (zoom-in) or away from each other (zoom-out).
12479   * @constructor
12480   * @extends AttrRecognizer
12481   */
12482  function PinchRecognizer() {
12483      AttrRecognizer.apply(this, arguments);
12484  }
12485
12486  inherit(PinchRecognizer, AttrRecognizer, {
12487      /**
12488       * @namespace
12489       * @memberof PinchRecognizer
12490       */
12491      defaults: {
12492          event: 'pinch',
12493          threshold: 0,
12494          pointers: 2
12495      },
12496
12497      getTouchAction: function() {
12498          return [TOUCH_ACTION_NONE];
12499      },
12500
12501      attrTest: function(input) {
12502          return this._super.attrTest.call(this, input) &&
12503              (Math.abs(input.scale - 1) > this.options.threshold || this.state & STATE_BEGAN);
12504      },
12505
12506      emit: function(input) {
12507          this._super.emit.call(this, input);
12508          if (input.scale !== 1) {
12509              var inOut = input.scale < 1 ? 'in' : 'out';
12510              this.manager.emit(this.options.event + inOut, input);
12511          }
12512      }
12513  });
12514
12515  /**
12516   * Press
12517   * Recognized 
vendor: 6,473 bytes, lines 12517-12733
12517when the pointer is down for x ms without any movement.
12518   * @constructor
12519   * @extends Recognizer
12520   */
12521  function PressRecognizer() {
12522      Recognizer.apply(this, arguments);
12523
12524      this._timer = null;
12525      this._input = null;
12526  }
12527
12528  inherit(PressRecognizer, Recognizer, {
12529      /**
12530       * @namespace
12531       * @memberof PressRecognizer
12532       */
12533      defaults: {
12534          event: 'press',
12535          pointers: 1,
12536          time: 500, // minimal time of the pointer to be pressed
12537          threshold: 5 // a minimal movement is ok, but keep it low
12538      },
12539
12540      getTouchAction: function() {
12541          return [TOUCH_ACTION_AUTO];
12542      },
12543
12544      process: function(input) {
12545          var options = this.options;
12546          var validPointers = input.pointers.length === options.pointers;
12547          var validMovement = input.distance < options.threshold;
12548          var validTime = input.deltaTime > options.time;
12549
12550          this._input = input;
12551
12552          // we only allow little movement
12553          // and we've reached an end event, so a tap is possible
12554          if (!validMovement || !validPointers || (input.eventType & (INPUT_END | INPUT_CANCEL) && !validTime)) {
12555              this.reset();
12556          } else if (input.eventType & INPUT_START) {
12557              this.reset();
12558              this._timer = setTimeoutContext(function() {
12559                  this.state = STATE_RECOGNIZED;
12560                  this.tryEmit();
12561              }, options.time, this);
12562          } else if (input.eventType & INPUT_END) {
12563              return STATE_RECOGNIZED;
12564          }
12565          return STATE_FAILED;
12566      },
12567
12568      reset: function() {
12569          clearTimeout(this._timer);
12570      },
12571
12572      emit: function(input) {
12573          if (this.state !== STATE_RECOGNIZED) {
12574              return;
12575          }
12576
12577          if (input && (input.eventType & INPUT_END)) {
12578              this.manager.emit(this.options.event + 'up', input);
12579          } else {
12580              this._input.timeStamp = now();
12581              this.manager.emit(this.options.event, this._input);
12582          }
12583      }
12584  });
12585
12586  /**
12587   * Rotate
12588   * Recognized when two or more pointer are moving in a circular motion.
12589   * @constructor
12590   * @extends AttrRecognizer
12591   */
12592  function RotateRecognizer() {
12593      AttrRecognizer.apply(this, arguments);
12594  }
12595
12596  inherit(RotateRecognizer, AttrRecognizer, {
12597      /**
12598       * @namespace
12599       * @memberof RotateRecognizer
12600       */
12601      defaults: {
12602          event: 'rotate',
12603          threshold: 0,
12604          pointers: 2
12605      },
12606
12607      getTouchAction: function() {
12608          return [TOUCH_ACTION_NONE];
12609      },
12610
12611      attrTest: function(input) {
12612          return this._super.attrTest.call(this, input) &&
12613              (Math.abs(input.rotation) > this.options.threshold || this.state & STATE_BEGAN);
12614      }
12615  });
12616
12617  /**
12618   * Swipe
12619   * Recognized when the pointer is moving fast (velocity), with enough distance in the allowed direction.
12620   * @constructor
12621   * @extends AttrRecognizer
12622   */
12623  function SwipeRecognizer() {
12624      AttrRecognizer.apply(this, arguments);
12625  }
12626
12627  inherit(SwipeRecognizer, AttrRecognizer, {
12628      /**
12629       * @namespace
12630       * @memberof SwipeRecognizer
12631       */
12632      defaults: {
12633          event: 'swipe',
12634          threshold: 10,
12635          velocity: 0.65,
12636          direction: DIRECTION_HORIZONTAL | DIRECTION_VERTICAL,
12637          pointers: 1
12638      },
12639
12640      getTouchAction: function() {
12641          return PanRecognizer.prototype.getTouchAction.call(this);
12642      },
12643
12644      attrTest: function(input) {
12645          var direction = this.options.direction;
12646          var velocity;
12647
12648          if (direction & (DIRECTION_HORIZONTAL | DIRECTION_VERTICAL)) {
12649              velocity = input.velocity;
12650          } else if (direction & DIRECTION_HORIZONTAL) {
12651              velocity = input.velocityX;
12652          } else if (direction & DIRECTION_VERTICAL) {
12653              velocity = input.velocityY;
12654          }
12655
12656          return this._super.attrTest.call(this, input) &&
12657              direction & input.direction &&
12658              input.distance > this.options.threshold &&
12659              abs(velocity) > this.options.velocity && input.eventType & INPUT_END;
12660      },
12661
12662      emit: function(input) {
12663          var direction = directionStr(input.direction);
12664          if (direction) {
12665              this.manager.emit(this.options.event + direction, input);
12666          }
12667
12668          this.manager.emit(this.options.event, input);
12669      }
12670  });
12671
12672  /**
12673   * A tap is ecognized when the pointer is doing a small tap/click. Multiple taps are recognized if they occur
12674   * between the given interval and position. The delay option can be used to recognize multi-taps without firing
12675   * a single tap.
12676   *
12677   * The eventData from the emitted event contains the property `tapCount`, which contains the amount of
12678   * multi-taps being recognized.
12679   * @constructor
12680   * @extends Recognizer
12681   */
12682  function TapRecognizer() {
12683      Recognizer.apply(this, arguments);
12684
12685      // previous time and center,
12686      // used for tap counting
12687      this.pTime = false;
12688      this.pCenter = false;
12689
12690      this._timer = null;
12691      this._input = null;
12692      this.count = 0;
12693  }
12694
12695  inherit(TapRecognizer, Recognizer, {
12696      /**
12697       * @namespace
12698       * @memberof PinchRecognizer
12699       */
12700      defaults: {
12701          event: 'tap',
12702          pointers: 1,
12703          taps: 1,
12704          interval: 300, // max time between the multi-tap taps
12705          time: 250, // max time of the pointer to be down (like finger on the screen)
12706          threshold: 2, // a minimal movement is ok, but keep it low
12707          posThreshold: 10 // a multi-tap can be a bit off the initial position
12708      },
12709
12710      getTouchAction: function() {
12711          return [TOUCH_ACTION_MANIPULATION];
12712      },
12713
12714      process: function(input) {
12715          var options = this.options;
12716
12717          var validPointers = input.pointers.length === options.pointers;
12718          var validMovement = input.distance < options.threshold;
12719          var validTouchTime = input.deltaTime < options.time;
12720
12721          this.reset();
12722
12723          if ((input.eventType & INPUT_START) && (this.count === 0)) {
12724              return this.failTimeout();
12725          }
12726
12727          // we only allow little movement
12728          // and we've reached an end event, so a tap is possible
12729          if (validMovement && validTouchTime && validPointers) {
12730              if (input.eventType != INPUT_END) {
12731                  return this.failTimeout();
12732              }
12733
vendor: 10,716 bytes, lines 12734-13055
12734              var validInterval = this.pTime ? (input.timeStamp - this.pTime < options.interval) : true;
12735              var validMultiTap = !this.pCenter || getDistance(this.pCenter, input.center) < options.posThreshold;
12736
12737              this.pTime = input.timeStamp;
12738              this.pCenter = input.center;
12739
12740              if (!validMultiTap || !validInterval) {
12741                  this.count = 1;
12742              } else {
12743                  this.count += 1;
12744              }
12745
12746              this._input = input;
12747
12748              // if tap count matches we have recognized it,
12749              // else it has began recognizing...
12750              var tapCount = this.count % options.taps;
12751              if (tapCount === 0) {
12752                  // no failing requirements, immediately trigger the tap event
12753                  // or wait as long as the multitap interval to trigger
12754                  if (!this.hasRequireFailures()) {
12755                      return STATE_RECOGNIZED;
12756                  } else {
12757                      this._timer = setTimeoutContext(function() {
12758                          this.state = STATE_RECOGNIZED;
12759                          this.tryEmit();
12760                      }, options.interval, this);
12761                      return STATE_BEGAN;
12762                  }
12763              }
12764          }
12765          return STATE_FAILED;
12766      },
12767
12768      failTimeout: function() {
12769          this._timer = setTimeoutContext(function() {
12770              this.state = STATE_FAILED;
12771          }, this.options.interval, this);
12772          return STATE_FAILED;
12773      },
12774
12775      reset: function() {
12776          clearTimeout(this._timer);
12777      },
12778
12779      emit: function() {
12780          if (this.state == STATE_RECOGNIZED ) {
12781              this._input.tapCount = this.count;
12782              this.manager.emit(this.options.event, this._input);
12783          }
12784      }
12785  });
12786
12787  /**
12788   * Simple way to create an manager with a default set of recognizers.
12789   * @param {HTMLElement} element
12790   * @param {Object} [options]
12791   * @constructor
12792   */
12793  function Hammer(element, options) {
12794      options = options || {};
12795      options.recognizers = ifUndefined(options.recognizers, Hammer.defaults.preset);
12796      return new Manager(element, options);
12797  }
12798
12799  /**
12800   * @const {string}
12801   */
12802  Hammer.VERSION = '2.0.4';
12803
12804  /**
12805   * default settings
12806   * @namespace
12807   */
12808  Hammer.defaults = {
12809      /**
12810       * set if DOM events are being triggered.
12811       * But this is slower and unused by simple implementations, so disabled by default.
12812       * @type {Boolean}
12813       * @default false
12814       */
12815      domEvents: false,
12816
12817      /**
12818       * The value for the touchAction property/fallback.
12819       * When set to `compute` it will magically set the correct value based on the added recognizers.
12820       * @type {String}
12821       * @default compute
12822       */
12823      touchAction: TOUCH_ACTION_COMPUTE,
12824
12825      /**
12826       * @type {Boolean}
12827       * @default true
12828       */
12829      enable: true,
12830
12831      /**
12832       * EXPERIMENTAL FEATURE -- can be removed/changed
12833       * Change the parent input target element.
12834       * If Null, then it is being set the to main element.
12835       * @type {Null|EventTarget}
12836       * @default null
12837       */
12838      inputTarget: null,
12839
12840      /**
12841       * force an input class
12842       * @type {Null|Function}
12843       * @default null
12844       */
12845      inputClass: null,
12846
12847      /**
12848       * Default recognizer setup when calling `Hammer()`
12849       * When creating a new Manager these will be skipped.
12850       * @type {Array}
12851       */
12852      preset: [
12853          // RecognizerClass, options, [recognizeWith, ...], [requireFailure, ...]
12854          [RotateRecognizer, { enable: false }],
12855          [PinchRecognizer, { enable: false }, ['rotate']],
12856          [SwipeRecognizer,{ direction: DIRECTION_HORIZONTAL }],
12857          [PanRecognizer, { direction: DIRECTION_HORIZONTAL }, ['swipe']],
12858          [TapRecognizer],
12859          [TapRecognizer, { event: 'doubletap', taps: 2 }, ['tap']],
12860          [PressRecognizer]
12861      ],
12862
12863      /**
12864       * Some CSS properties can be used to improve the working of Hammer.
12865       * Add them to this method and they will be set when creating a new Manager.
12866       * @namespace
12867       */
12868      cssProps: {
12869          /**
12870           * Disables text selection to improve the dragging gesture. Mainly for desktop browsers.
12871           * @type {String}
12872           * @default 'none'
12873           */
12874          userSelect: 'none',
12875
12876          /**
12877           * Disable the Windows Phone grippers when pressing an element.
12878           * @type {String}
12879           * @default 'none'
12880           */
12881          touchSelect: 'none',
12882
12883          /**
12884           * Disables the default callout shown when you touch and hold a touch target.
12885           * On iOS, when you touch and hold a touch target such as a link, Safari displays
12886           * a callout containing information about the link. This property allows you to disable that callout.
12887           * @type {String}
12888           * @default 'none'
12889           */
12890          touchCallout: 'none',
12891
12892          /**
12893           * Specifies whether zooming is enabled. Used by IE10>
12894           * @type {String}
12895           * @default 'none'
12896           */
12897          contentZooming: 'none',
12898
12899          /**
12900           * Specifies that an entire element should be draggable instead of its contents. Mainly for desktop browsers.
12901           * @type {String}
12902           * @default 'none'
12903           */
12904          userDrag: 'none',
12905
12906          /**
12907           * Overrides the highlight color shown when the user taps a link or a JavaScript
12908           * clickable element in iOS. This property obeys the alpha value, if specified.
12909           * @type {String}
12910           * @default 'rgba(0,0,0,0)'
12911           */
12912          tapHighlightColor: 'rgba(0,0,0,0)'
12913      }
12914  };
12915
12916  var STOP = 1;
12917  var FORCED_STOP = 2;
12918
12919  /**
12920   * Manager
12921   * @param {HTMLElement} element
12922   * @param {Object} [options]
12923   * @constructor
12924   */
12925  function Manager(element, options) {
12926      options = options || {};
12927
12928      this.options = merge(options, Hammer.defaults);
12929      this.options.inputTarget = this.options.inputTarget || element;
12930
12931      this.handlers = {};
12932      this.session = {};
12933      this.recognizers = [];
12934
12935      this.element = element;
12936      this.input = createInputInstance(this);
12937      this.touchAction = new TouchAction(this, this.options.touchAction);
12938
12939      toggleCssProps(this, true);
12940
12941      each(options.recognizers, function(item) {
12942          var recognizer = this.add(new (item[0])(item[1]));
12943          item[2] && recognizer.recognizeWith(item[2]);
12944          item[3] && recognizer.requireFailure(item[3]);
12945      }, this);
12946  }
12947
12948  Manager.prototype = {
12949      /**
12950       * set options
12951       * @param {Object} options
12952       * @returns {Manager}
12953       */
12954      set: function(options) {
12955          extend(this.options, options);
12956
12957          // Options that need a little more setup
12958          if (options.touchAction) {
12959              this.touchAction.update();
12960          }
12961          if (options.inputTarget) {
12962              // Clean up existing event listeners and reinitialize
12963              this.input.destroy();
12964              this.input.target = options.inputTarget;
12965              this.input.init();
12966          }
12967          return this;
12968      },
12969
12970      /**
12971       * stop recognizing for this session.
12972       * This session will be discarded, when a new [input]start event is fired.
12973       * When forced, the recognizer cycle is stopped immediately.
12974       * @param {Boolean} [force]
12975       */
12976      stop: function(force) {
12977          this.session.stopped = force ? FORCED_STOP : STOP;
12978      },
12979
12980      /**
12981       * run the recognizers!
12982       * called by the inputHandler function on every movement of the pointers (touches)
12983       * it walks through all the recognizers and tries to detect the gesture that is being made
12984       * @param {Object} inputData
12985       */
12986      recognize: function(inputData) {
12987          var session = this.session;
12988          if (session.stopped) {
12989              return;
12990          }
12991
12992          // run the touch-action polyfill
12993          this.touchAction.preventDefaults(inputData);
12994
12995          var recognizer;
12996          var recognizers = this.recognizers;
12997
12998          // this holds the recognizer that is being recognized.
12999          // so the recognizer's state needs to be BEGAN, CHANGED, ENDED or RECOGNIZED
13000          // if no recognizer is detecting a thing, it is set to `null`
13001          var curRecognizer = session.curRecognizer;
13002
13003          // reset when the last recognizer is recognized
13004          // or when we're in a new session
13005          if (!curRecognizer || (curRecognizer && curRecognizer.state & STATE_RECOGNIZED)) {
13006              curRecognizer = session.curRecognizer = null;
13007          }
13008
13009          var i = 0;
13010          while (i < recognizers.length) {
13011              recognizer = recognizers[i];
13012
13013              // find out if we are allowed try to recognize the input for this one.
13014              // 1.   allow if the session is NOT forced stopped (see the .stop() method)
13015              // 2.   allow if we still haven't recognized a gesture in this session, or the this recognizer is the one
13016              //      that is being recognized.
13017              // 3.   allow if the recognizer is allowed to run simultaneous with the current recognized recognizer.
13018              //      this can be setup with the `recognizeWith()` method on the recognizer.
13019              if (session.stopped !== FORCED_STOP && ( // 1
13020                      !curRecognizer || recognizer == curRecognizer || // 2
13021                      recognizer.canRecognizeWith(curRecognizer))) { // 3
13022                  recognizer.recognize(inputData);
13023              } else {
13024                  recognizer.reset();
13025              }
13026
13027              // if the recognizer has been recognizing the input as a valid gesture, we want to store this one as the
13028              // current active recognizer. but only if we don't already have an active recognizer
13029              if (!curRecognizer && recognizer.state & (STATE_BEGAN | STATE_CHANGED | STATE_ENDED)) {
13030                  curRecognizer = session.curRecognizer = recognizer;
13031              }
13032              i++;
13033          }
13034      },
13035
13036      /**
13037       * get a recognizer by its event name.
13038       * @param {Recognizer|String} recognizer
13039       * @returns {Recognizer|Null}
13040       */
13041      get: function(recognizer) {
13042          if (recognizer instanceof Recognizer) {
13043              return recognizer;
13044          }
13045
13046          var recognizers = this.recognizers;
13047          for (var i = 0; i < recognizers.length; i++) {
13048              if (recognizers[i].options.event == recognizer) {
13049                  return recognizers[i];
13050              }
13051          }
13052          return null;
13053      },
13054
13055      /**
vendor: 5,886 bytes, lines 13056-13261
13056       * add a recognizer to the manager
13057       * existing recognizers with the same event name will be removed
13058       * @param {Recognizer} recognizer
13059       * @returns {Recognizer|Manager}
13060       */
13061      add: function(recognizer) {
13062          if (invokeArrayArg(recognizer, 'add', this)) {
13063              return this;
13064          }
13065
13066          // remove existing
13067          var existing = this.get(recognizer.options.event);
13068          if (existing) {
13069              this.remove(existing);
13070          }
13071
13072          this.recognizers.push(recognizer);
13073          recognizer.manager = this;
13074
13075          this.touchAction.update();
13076          return recognizer;
13077      },
13078
13079      /**
13080       * remove a recognizer by name or instance
13081       * @param {Recognizer|String} recognizer
13082       * @returns {Manager}
13083       */
13084      remove: function(recognizer) {
13085          if (invokeArrayArg(recognizer, 'remove', this)) {
13086              return this;
13087          }
13088
13089          var recognizers = this.recognizers;
13090          recognizer = this.get(recognizer);
13091          recognizers.splice(inArray(recognizers, recognizer), 1);
13092
13093          this.touchAction.update();
13094          return this;
13095      },
13096
13097      /**
13098       * bind event
13099       * @param {String} events
13100       * @param {Function} handler
13101       * @returns {EventEmitter} this
13102       */
13103      on: function(events, handler) {
13104          var handlers = this.handlers;
13105          each(splitStr(events), function(event) {
13106              handlers[event] = handlers[event] || [];
13107              handlers[event].push(handler);
13108          });
13109          return this;
13110      },
13111
13112      /**
13113       * unbind event, leave emit blank to remove all handlers
13114       * @param {String} events
13115       * @param {Function} [handler]
13116       * @returns {EventEmitter} this
13117       */
13118      off: function(events, handler) {
13119          var handlers = this.handlers;
13120          each(splitStr(events), function(event) {
13121              if (!handler) {
13122                  delete handlers[event];
13123              } else {
13124                  handlers[event].splice(inArray(handlers[event], handler), 1);
13125              }
13126          });
13127          return this;
13128      },
13129
13130      /**
13131       * emit event to the listeners
13132       * @param {String} event
13133       * @param {Object} data
13134       */
13135      emit: function(event, data) {
13136          // we also want to trigger dom events
13137          if (this.options.domEvents) {
13138              triggerDomEvent(event, data);
13139          }
13140
13141          // no handlers, so skip it all
13142          var handlers = this.handlers[event] && this.handlers[event].slice();
13143          if (!handlers || !handlers.length) {
13144              return;
13145          }
13146
13147          data.type = event;
13148          data.preventDefault = function() {
13149              data.srcEvent.preventDefault();
13150          };
13151
13152          var i = 0;
13153          while (i < handlers.length) {
13154              handlers[i](data);
13155              i++;
13156          }
13157      },
13158
13159      /**
13160       * destroy the manager and unbinds all events
13161       * it doesn't unbind dom events, that is the user own responsibility
13162       */
13163      destroy: function() {
13164          this.element && toggleCssProps(this, false);
13165
13166          this.handlers = {};
13167          this.session = {};
13168          this.input.destroy();
13169          this.element = null;
13170      }
13171  };
13172
13173  /**
13174   * add/remove the css properties as defined in manager.options.cssProps
13175   * @param {Manager} manager
13176   * @param {Boolean} add
13177   */
13178  function toggleCssProps(manager, add) {
13179      var element = manager.element;
13180      each(manager.options.cssProps, function(value, name) {
13181          element.style[prefixed(element.style, name)] = add ? value : '';
13182      });
13183  }
13184
13185  /**
13186   * trigger dom event
13187   * @param {String} event
13188   * @param {Object} data
13189   */
13190  function triggerDomEvent(event, data) {
13191      var gestureEvent = document.createEvent('Event');
13192      gestureEvent.initEvent(event, true, true);
13193      gestureEvent.gesture = data;
13194      data.target.dispatchEvent(gestureEvent);
13195  }
13196
13197  extend(Hammer, {
13198      INPUT_START: INPUT_START,
13199      INPUT_MOVE: INPUT_MOVE,
13200      INPUT_END: INPUT_END,
13201      INPUT_CANCEL: INPUT_CANCEL,
13202
13203      STATE_POSSIBLE: STATE_POSSIBLE,
13204      STATE_BEGAN: STATE_BEGAN,
13205      STATE_CHANGED: STATE_CHANGED,
13206      STATE_ENDED: STATE_ENDED,
13207      STATE_RECOGNIZED: STATE_RECOGNIZED,
13208      STATE_CANCELLED: STATE_CANCELLED,
13209      STATE_FAILED: STATE_FAILED,
13210
13211      DIRECTION_NONE: DIRECTION_NONE,
13212      DIRECTION_LEFT: DIRECTION_LEFT,
13213      DIRECTION_RIGHT: DIRECTION_RIGHT,
13214      DIRECTION_UP: DIRECTION_UP,
13215      DIRECTION_DOWN: DIRECTION_DOWN,
13216      DIRECTION_HORIZONTAL: DIRECTION_HORIZONTAL,
13217      DIRECTION_VERTICAL: DIRECTION_VERTICAL,
13218      DIRECTION_ALL: DIRECTION_ALL,
13219
13220      Manager: Manager,
13221      Input: Input,
13222      TouchAction: TouchAction,
13223
13224      TouchInput: TouchInput,
13225      MouseInput: MouseInput,
13226      PointerEventInput: PointerEventInput,
13227      TouchMouseInput: TouchMouseInput,
13228      SingleTouchInput: SingleTouchInput,
13229
13230      Recognizer: Recognizer,
13231      AttrRecognizer: AttrRecognizer,
13232      Tap: TapRecognizer,
13233      Pan: PanRecognizer,
13234      Swipe: SwipeRecognizer,
13235      Pinch: PinchRecognizer,
13236      Rotate: RotateRecognizer,
13237      Press: PressRecognizer,
13238
13239      on: addEventListeners,
13240      off: removeEventListeners,
13241      each: each,
13242      merge: merge,
13243      extend: extend,
13244      inherit: inherit,
13245      bindFn: bindFn,
13246      prefixed: prefixed
13247  });
13248
13249  if ("function" == TYPE_FUNCTION && __webpack_require__(23)) {
13250      !(__WEBPACK_AMD_DEFINE_RESULT__ = function() {
13251          return Hammer;
13252      }.call(exports, __webpack_require__, exports, module), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));
13253  } else if (typeof module != 'undefined' && module.exports) {
13254      module.exports = Hammer;
13255  } else {
13256      window[exportName] = Hammer;
13257  }
13258
13259  })(window, document, 'Hammer');
13260
13261
13262/***/ },
13263/* 23 */
13264/***/ function(module, exports) {
13265
13266  /* WEBPACK VAR INJECTION */(function(__webpack_amd_options__) {module.exports = __webpack_amd_options__;
13267
13268  /* WEBPACK VAR INJECTION */}.call(exports, {}))
13269
13270/***/ },
13271/* 24 */
13272/***/ function(module, exports, __webpack_require__) {
13273
13274  'use strict';
13275
13276  var util = __webpack_require__(1);
13277  var hammerUtil = __webpack_require__(25);
13278  var moment = __webpack_require__(2);
13279  var Component = __webpack_require__(26);
13280  var DateUtil = __webpack_require__(27);
13281
13282  /**
13283   * @constructor Range
13284   * A Range controls a numeric range with a start and end value.
13285   * The Range adjusts the range based on mouse events or programmatic changes,
13286   * and triggers events when the range is changing or has been changed.
13287   * @param {{dom: Object, domProps: Object, emitter: Emitter}} body
13288   * @param {Object} [options]    See description at Range.setOptions
13289   */
13290  function Range(body, options) {
13291    var now = moment().hours(0).minutes(0).seconds(0).milliseconds(0);
13292    this.start = now.clone().add(-3, 'days').valueOf(); // Number
13293    this.end = now.clone().add(4, 'days').valueOf(); // Number
13294
13295    this.body = body;
13296    this.deltaDifference = 0;
13297    this.scaleOffset = 0;
13298    this.startToFront = false;
13299    this.endToFront = true;
13300
13301    // default options
13302    this.defaultOptions = {
13303      start: null,
13304      end: null,
13305      moment: moment,
13306      direction: 'horizontal', // 'horizontal' or 'vertical'
13307      moveable: true,
13308      zoomable: true,
13309      min: null,
13310      max: null,
13311      zoomMin: 10, // milliseconds
13312      zoomMax: 1000 * 60 * 60 * 24 * 365 * 10000 // milliseconds
13313    };
13314    this.options = util.extend({}, this.defaultOptions);
13315
13316    this.props = {
13317      touch: {}
13318    };
13319    this.animationTimer = null;
13320
13321    // drag listeners for dragging
13322    this.body.emitter.on('panstart', this._onDragStart.bind(this));
13323    this.body.emitter.on('panmove', this._onDrag.bind(this));
13324    this.body.emitter.on('panend', this._onDragEnd.bind(this));
13325
13326    // mouse wheel for zooming
13327    this.body.emitter.on('mousewheel', this._onMouseWheel.bind(this));
13328
13329    // pinch to zoom
13330    this.body.emitter.on('touch', this._onTouch.bind(this));
13331    this.body.emitter.on('pinch', this._onPinch.bind(this));
13332
13333    this.setOptions(options);
13334  }
13335
13336  Range.prototype = new Component();
13337
13338  /**
13339   * Set options for the range controller
13340   * @param {Object} options      Available options:
13341   *                              {Number | Date | String} start  Start date for the range
13342   *                              {Number | Date | String} end    End date for the range
13343   *                              {Number} min    Minimum value for start
13344   *                              {Number} max    Maximum value for end
13345   *                              {Number} zoomMin    Set a minimum value for
13346   *                                                  (end - start).
13347   *                              {Number} zoomMax    Set a maximum value for
13348   *                                                  (end - start).
13349   *                              {Boolean} moveable Enable moving of the range
13350   *                                                 by dragging. True by default
13351   *                              {Boolean} zoomable Enable zooming of the range
13352   *                                                 by pinching/scrolling. True by default
13353   */
13354  Range.prototype.setOptions = function (options) {
13355    if (options) {
13356      // copy the options that we know
13357      var fields = ['direction', 'min', 'max', 'zoomMin', 'zoomMax', 'moveable', 'zoomable', 'moment', 'activate', 'hiddenDates', 'zoomKey'];
13358      util.selectiveExtend(fields, this.options, options);
13359
13360      if ('start' in options || 'end' in options) {
13361        // apply a new range. both start and end are optional
13362        this.setRange(options.start, options.end);
13363      }
13364    }
13365  };
13366
13367  /**
13368   * Test whether direction has a valid value
13369   * @param {String} direction    'horizontal' or 'vertical'
13370   */
13371  function validateDirection(direction) {
13372    if (direction != 'horizontal' && direction != 'vertical') {
13373      throw new TypeError('Unknown direction "' + direction + '". ' + 'Choose "horizontal" or "vertical".');
13374    }
13375  }
13376
13377  /**
13378   * Set a new start and end range
13379   * @param {Date | Number | String} [start]
13380   * @param {Date | Number | String} [end]
13381   * @param {boolean | {duration: number, easingFunction: string}} [animation=false]
13382   *                                    If true (default), the range is animated
13383   *                                    smoothly to the new window. An object can be
13384   *                                    provided to specify duration and easing function.
13385   *                                    Default duration is 500 ms, and default easing
13386   *                                    function is 'easeInOutQuad'.
13387   * @param {Boolean} [byUser=false]
13388   *
13389   */
13390  Range.prototype.setRange = function (start, end, animation, byUser) {
13391    if (byUser !== true) {
13392      byUser = false;
13393    }
13394    var finalStart = start != undefined ? util.convert(start, 'Date').valueOf() : null;
13395    var finalEnd = end != undefined ? util.convert(end, 'Date').valueOf() : null;
13396    this._cancelAnimation();
13397
13398    if (animation) {
13399      // true or an Object
13400      var me = this;
13401      var initStart = this.start;
13402      var initEnd = this.end;
13403      var duration = typeof animation === 'object' && 'duration' in animation ? animation.duration : 500;
13404      var easingName = typeof animation === 'object' && 'easingFunction' in animation ? animation.easingFunction : 'easeInOutQuad';
13405      var easingFunction = util.easingFunctions[easingName];
13406      if (!easingFunction) {
13407        throw new Error('Unknown easing function ' + JSON.stringify(easingName) + '. ' + 'Choose from: ' + Object.keys(util.easingFunctions).join(', '));
13408      }
13409
13410      var initTime = new Date().valueOf();
13411      var anyChanged = false;
13412
13413      var next = function next() {
13414        if (!me.props.touch.dragging) {
13415          var now = new Date().valueOf();
13416          var time = now - initTime;
13417          var ease = easingFunction(time / duration);
13418          var done = time > duration;
13419          var s = done || finalStart === null ? finalStart : initStart + (finalStart - initStart) * ease;
13420          var e = done || finalEnd === null ? finalEnd : initEnd + (finalEnd - initEnd) * ease;
13421
13422          changed = me._applyRange(s, e);
13423          DateUtil.updateHiddenDates(me.options.moment, me.body, me.options.hiddenDates);
13424          anyChanged = anyChanged || changed;
13425          if (changed) {
13426            me.body.emitter.emit('rangechange', { start: new Date(me.start), end: new Date(me.end), byUser: byUser });
13427          }
13428
13429          if (done) {
13430            if (anyChanged) {
13431              me.body.emitter.emit('rangechanged', { start: new Date(me.start), end: new Date(me.end), byUser: byUser });
13432            }
13433          } else {
13434            // animate with as high as possible frame rate, leave 20 ms in between
13435            // each to prevent the browser from blocking
13436            me.animationTimer = setTimeout(next, 20);
13437          }
13438        }
13439      };
13440
13441      return next();
13442    } else {
13443      var changed = this._applyRange(finalStart, finalEnd);
13444      DateUtil.updateHiddenDates(this.options.moment, this.body, this.options.hiddenDates);
13445      if (changed) {
13446        var params = { start: new Date(this.start), end: new Date(this.end), byUser: byUser };
13447        this.body.emitter.emit('rangechange', params);
13448        this.body.emitter.emit('rangechanged', params);
13449      }
13450    }
13451  };
13452
13453  /**
13454   * Stop an animation
13455   * @private
13456   */
13457  Range.prototype._cancelAnimation = function () {
13458    if (this.animationTimer) {
13459      clearTimeout(this.animationTimer);
13460      this.animationTimer = null;
13461    }
13462  };
13463
13464  /**
13465   * Set a new start and end range. This method is the same as setRange, but
13466   * does not trigger a range change and range changed event, and it returns
13467   * true when the range is changed
13468   * @param {Number} [start]
13469   * @param {Number} [end]
13470   * @return {Boolean} changed
13471   * @private
13472   */
13473  Range.prototype._applyRange = function (start, end) {
13474    var newStart = start != null ? util.convert(start, 'Date').valueOf() : this.start,
13475        newEnd = end != null ? util.convert(end, 'Date').valueOf() : this.end,
13476        max = this.options.max != null ? util.convert(this.options.max, 'Date').valueOf() : null,
13477        min = this.options.min != null ? util.convert(this.options.min, 'Date').valueOf() : null,
13478        diff;
13479
13480    // check for valid number
13481    if (isNaN(newStart) || newStart === null) {
13482      throw new Error('Invalid start "' + start + '"');
13483    }
13484    if (isNaN(newEnd) || newEnd === null) {
13485      throw new Error('Invalid end "' + end + '"');
13486    }
13487
13488    // prevent start < end
13489    if (newEnd < newStart) {
13490      newEnd = newStart;
13491    }
13492
13493    // prevent start < min
13494    if (min !== null) {
13495      if (newStart < min) {
13496        diff = min - newStart;
13497        newStart += diff;
13498        newEnd += diff;
13499
13500        // prevent end > max
13501        if (max != null) {
13502          if (newEnd > max) {
13503            newEnd = max;
13504          }
13505        }
13506      }
13507    }
13508
13509    // prevent end > max
13510    if (max !== null) {
13511      if (newEnd > max) {
13512        diff = newEnd - max;
13513        newStart -= diff;
13514        newEnd -= diff;
13515
13516        // prevent start < min
13517        if (min != null) {
13518          if (newStart < min) {
13519            newStart = min;
13520          }
13521        }
13522      }
13523    }
13524
13525    // prevent (end-start) < zoomMin
13526    if (this.options.zoomMin !== null) {
13527      var zoomMin = parseFloat(this.options.zoomMin);
13528      if (zoomMin < 0) {
13529        zoomMin = 0;
13530      }
13531      if (newEnd - newStart < zoomMin) {
vendor: 6,184 bytes, lines 13532-13721
13532        if (this.end - this.start === zoomMin && newStart > this.start && newEnd < this.end) {
13533          // ignore this action, we are already zoomed to the minimum
13534          newStart = this.start;
13535          newEnd = this.end;
13536        } else {
13537          // zoom to the minimum
13538          diff = zoomMin - (newEnd - newStart);
13539          newStart -= diff / 2;
13540          newEnd += diff / 2;
13541        }
13542      }
13543    }
13544
13545    // prevent (end-start) > zoomMax
13546    if (this.options.zoomMax !== null) {
13547      var zoomMax = parseFloat(this.options.zoomMax);
13548      if (zoomMax < 0) {
13549        zoomMax = 0;
13550      }
13551
13552      if (newEnd - newStart > zoomMax) {
13553        if (this.end - this.start === zoomMax && newStart < this.start && newEnd > this.end) {
13554          // ignore this action, we are already zoomed to the maximum
13555          newStart = this.start;
13556          newEnd = this.end;
13557        } else {
13558          // zoom to the maximum
13559          diff = newEnd - newStart - zoomMax;
13560          newStart += diff / 2;
13561          newEnd -= diff / 2;
13562        }
13563      }
13564    }
13565
13566    var changed = this.start != newStart || this.end != newEnd;
13567
13568    // 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)
13569    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)) {
13570      this.body.emitter.emit('checkRangedItems');
13571    }
13572
13573    this.start = newStart;
13574    this.end = newEnd;
13575    return changed;
13576  };
13577
13578  /**
13579   * Retrieve the current range.
13580   * @return {Object} An object with start and end properties
13581   */
13582  Range.prototype.getRange = function () {
13583    return {
13584      start: this.start,
13585      end: this.end
13586    };
13587  };
13588
13589  /**
13590   * Calculate the conversion offset and scale for current range, based on
13591   * the provided width
13592   * @param {Number} width
13593   * @returns {{offset: number, scale: number}} conversion
13594   */
13595  Range.prototype.conversion = function (width, totalHidden) {
13596    return Range.conversion(this.start, this.end, width, totalHidden);
13597  };
13598
13599  /**
13600   * Static method to calculate the conversion offset and scale for a range,
13601   * based on the provided start, end, and width
13602   * @param {Number} start
13603   * @param {Number} end
13604   * @param {Number} width
13605   * @returns {{offset: number, scale: number}} conversion
13606   */
13607  Range.conversion = function (start, end, width, totalHidden) {
13608    if (totalHidden === undefined) {
13609      totalHidden = 0;
13610    }
13611    if (width != 0 && end - start != 0) {
13612      return {
13613        offset: start,
13614        scale: width / (end - start - totalHidden)
13615      };
13616    } else {
13617      return {
13618        offset: 0,
13619        scale: 1
13620      };
13621    }
13622  };
13623
13624  /**
13625   * Start dragging horizontally or vertically
13626   * @param {Event} event
13627   * @private
13628   */
13629  Range.prototype._onDragStart = function (event) {
13630    this.deltaDifference = 0;
13631    this.previousDelta = 0;
13632
13633    // only allow dragging when configured as movable
13634    if (!this.options.moveable) return;
13635
13636    // only start dragging when the mouse is inside the current range
13637    if (!this._isInsideRange(event)) return;
13638
13639    // refuse to drag when we where pinching to prevent the timeline make a jump
13640    // when releasing the fingers in opposite order from the touch screen
13641    if (!this.props.touch.allowDragging) return;
13642
13643    this.props.touch.start = this.start;
13644    this.props.touch.end = this.end;
13645    this.props.touch.dragging = true;
13646
13647    if (this.body.dom.root) {
13648      this.body.dom.root.style.cursor = 'move';
13649    }
13650  };
13651
13652  /**
13653   * Perform dragging operation
13654   * @param {Event} event
13655   * @private
13656   */
13657  Range.prototype._onDrag = function (event) {
13658    if (!this.props.touch.dragging) return;
13659
13660    // only allow dragging when configured as movable
13661    if (!this.options.moveable) return;
13662
13663    // TODO: this may be redundant in hammerjs2
13664    // refuse to drag when we where pinching to prevent the timeline make a jump
13665    // when releasing the fingers in opposite order from the touch screen
13666    if (!this.props.touch.allowDragging) return;
13667
13668    var direction = this.options.direction;
13669    validateDirection(direction);
13670    var delta = direction == 'horizontal' ? event.deltaX : event.deltaY;
13671    delta -= this.deltaDifference;
13672    var interval = this.props.touch.end - this.props.touch.start;
13673
13674    // normalize dragging speed if cutout is in between.
13675    var duration = DateUtil.getHiddenDurationBetween(this.body.hiddenDates, this.start, this.end);
13676    interval -= duration;
13677
13678    var width = direction == 'horizontal' ? this.body.domProps.center.width : this.body.domProps.center.height;
13679    var diffRange = -delta / width * interval;
13680    var newStart = this.props.touch.start + diffRange;
13681    var newEnd = this.props.touch.end + diffRange;
13682
13683    // snapping times away from hidden zones
13684    var safeStart = DateUtil.snapAwayFromHidden(this.body.hiddenDates, newStart, this.previousDelta - delta, true);
13685    var safeEnd = DateUtil.snapAwayFromHidden(this.body.hiddenDates, newEnd, this.previousDelta - delta, true);
13686    if (safeStart != newStart || safeEnd != newEnd) {
13687      this.deltaDifference += delta;
13688      this.props.touch.start = safeStart;
13689      this.props.touch.end = safeEnd;
13690      this._onDrag(event);
13691      return;
13692    }
13693
13694    this.previousDelta = delta;
13695    this._applyRange(newStart, newEnd);
13696
13697    // fire a rangechange event
13698    this.body.emitter.emit('rangechange', {
13699      start: new Date(this.start),
13700      end: new Date(this.end),
13701      byUser: true
13702    });
13703  };
13704
13705  /**
13706   * Stop dragging operation
13707   * @param {event} event
13708   * @private
13709   */
13710  Range.prototype._onDragEnd = function (event) {
13711    if (!this.props.touch.dragging) return;
13712
13713    // only allow dragging when configured as movable
13714    if (!this.options.moveable) return;
13715
13716    // TODO: this may be redundant in hammerjs2
13717    // refuse to drag when we where pinching to prevent the timeline make a jump
13718    // when releasing the fingers in opposite order from the touch screen
13719    if (!this.props.touch.allowDragging) return;
13720
13721    this.props.touch.dragging = false;
13722    if (this.body.dom.root) {
13723      this.body.dom.root.style.cursor = 'auto';
13724    }
13725
13726    // fire a rangechanged event
13727    this.body.emitter.emit('rangechanged', {
13728      start: new Date(this.start),
13729      end: new Date(this.end),
13730      byUser: true
13731    });
13732  };
13733
13734  /**
13735   * Event handler for mouse wheel event, used to zoom
13736   * Code from http://adomas.org/javascript-mouse-wheel/
13737   * @param {Event} event
13738   * @private
13739   */
13740  Range.prototype._onMouseWheel = function (event) {
13741    // only allow zooming when configured as zoomable and moveable
13742    if (!(this.options.zoomable && this.options.moveable)) return;
13743
13744    // only zoom when the mouse is inside the current range
13745    if (!this._isInsideRange(event)) return;
13746
13747    // only zoom when the according key is pressed and the zoomKey option is set
13748    if (this.options.zoomKey && !event[this.options.zoomKey]) return;
13749
13750    // retrieve delta
13751    var delta = 0;
13752    if (event.wheelDelta) {
13753      /* IE/Opera. */
13754      delta = event.wheelDelta / 120;
13755    } else if (event.detail) {
13756      /* Mozilla case. */
13757      // In Mozilla, sign of delta is different than in IE.
13758      // Also, delta is multiple of 3.
13759      delta = -event.detail / 3;
13760    }
13761
13762    // If delta is nonzero, handle it.
13763    // Basically, delta is now positive if wheel was scrolled up,
13764    // and negative, if wheel was scrolled down.
13765    if (delta) {
13766      // perform the zoom action. Delta is normally 1 or -1
13767
13768      // adjust a negative delta such that zooming in with delta 0.1
13769      // equals zooming out with a delta -0.1
13770      var scale;
13771      if (delta < 0) {
13772        scale = 1 - delta / 5;
13773      } else {
13774        scale = 1 / (1 + delta / 5);
13775      }
13776
13777      // calculate center, the date to zoom around
13778      var pointer = getPointer({ x: event.clientX, y: event.clientY }, this.body.dom.center);
13779      var pointerDate = this._pointerToDate(pointer);
13780
13781      this.zoom(scale, pointerDate, delta);
13782    }
13783
13784    // Prevent default actions caused by mouse wheel
13785    // (else the page and timeline both zoom and scroll)
13786    event.preventDefault();
13787  };
13788
13789  /**
13790   * Start of a touch gesture
13791   * @private
13792   */
13793  Range.prototype._onTouch = function (event) {
13794    this.props.touch.start = this.start;
13795    this.props.touch.end = this.end;
13796    this.props.touch.allowDragging = true;
13797    this.props.touch.center = null;
13798    this.scaleOffset = 0;
13799    this.deltaDifference = 0;
13800  };
13801
13802  /**
13803   * Handle pinch event
13804   * @param {Event} event
13805   * @private
13806   */
13807  Range.prototype._onPinch = function (event) {
13808    // only allow zooming when configured as zoomable and moveable
13809    if (!(this.options.zoomable && this.options.moveable)) return;
13810
13811    this.props.touch.allowDragging = false;
13812
13813    if (!this.props.touch.center) {
13814      this.props.touch.center = getPointer(event.center, this.body.dom.center);
13815    }
13816
13817    var scale = 1 / (event.scale + this.scaleOffset);
13818    var centerDate = this._pointerToDate(this.props.touch.center);
13819
13820    var hiddenDuration = DateUtil.getHiddenDurationBetween(this.body.hiddenDates, this.start, this.end);
13821    var hiddenDurationBefore = DateUtil.getHiddenDurationBefore(this.options.moment, this.body.hiddenDates, this, centerDate);
13822    var hiddenDurationAfter = hiddenDuration - hiddenDurationBefore;
13823
13824    // calculate new start and end
13825    var newStart = centerDate - hiddenDurationBefore + (this.props.touch.start - (centerDate - hiddenDurationBefore)) * scale;
13826    var newEnd = centerDate + hiddenDurationAfter + (this.props.touch.end - (centerDate + hiddenDurationAfter)) * scale;
13827
13828    // snapping times away from hidden zones
13829    this.startToFront = 1 - scale <= 0; // used to do the right auto correction with periodic hidden times
13830    this.endToFront = scale - 1 <= 0; // used to do the right auto correction with periodic hidden times
13831
13832    var safeStart = DateUtil.snapAwayFromHidden(this.body.hiddenDates, newStart, 1 - scale, true);
13833    var safeEnd = DateUtil.snapAwayFromHidden(this.body.hiddenDates, newEnd, scale - 1, true);
13834    if (safeStart != newStart || safeEnd != newEnd) {
13835      this.props.touch.start = safeStart;
13836      this.props.touch.end = safeEnd;
13837      this.scaleOffset = 1 - event.scale;
13838      newStart = safeStart;
13839      newEnd = safeEnd;
13840    }
13841
13842    this.setRange(newStart, newEnd, false, true);
13843
13844    this.startToFront = false; // revert to default
13845    this.endToFront = true; // revert to default
13846  };
13847
13848  /**
13849   * Test whether the mouse from a mouse event is inside the visible window,
13850   * between the current start and end date
13851   * @param {Object} event
13852   * @return {boolean} Returns true when inside the visible window
13853   * @private
13854   */
13855  Range.prototype._isInsideRange = function (event) {
13856    // calculate the time where the mouse is, check whether inside
13857    // and no scroll action should happen.
13858    var clientX = event.center ? event.center.x : event.clientX;
13859    var x = clientX - util.getAbsoluteLeft(this.body.dom.centerContainer);
13860    var time = this.body.util.toTime(x);
13861
13862    return time >= this.start && time <= this.end;
13863  };
13864
13865  /**
13866   * Helper function to calculate the center date for zooming
13867   * @param {{x: Number, y: Number}} pointer
13868   * @return {number} date
13869   * @private
13870   */
13871  Range.prototype._pointerToDate = function (pointer) {
13872    var conversion;
13873    var direction = this.options.direction;
13874
13875    validateDirection(direction);
13876
13877    if (direction == 'horizontal') {
13878      return this.body.util.toTime(pointer.x).valueOf();
13879    } else {
13880      var height = this.body.domProps.center.height;
13881      conversion = this.conversion(height);
13882      return pointer.y / conversion.scale + conversion.offset;
13883    }
13884  };
13885
13886  /**
13887   * Get the pointer location relative to the location of the dom element
13888   * @param {{x: Number, y: Number}} touch
13889   * @param {Element} element   HTML DOM element
13890   * @return {{x: Number, y: Number}} pointer
13891   * @private
13892   */
13893  function getPointer(touch, element) {
13894    return {
13895      x: touch.x - util.getAbsoluteLeft(element),
13896      y: touch.y - util.getAbsoluteTop(element)
13897    };
13898  }
13899
13900  /**
13901   * Zoom the range the given scale in or out. Start and end date will
13902   * be adjusted, and the timeline will be redrawn. You can optionally give a
13903   * date around which to zoom.
13904   * For example, try scale = 0.9 or 1.1
13905   * @param {Number} scale      Scaling factor. Values above 1 will zoom out,
13906   *                            values below 1 will zoom in.
13907   * @param {Number} [center]   Value representing a date around which will
13908   *                            be zoomed.
13909   */
13910  Range.prototype.zoom = function (scale, center, delta) {
13911    // if centerDate is not provided, take it half between start Date and end Date
13912    if (center == null) {
13913      center = (this.start + this.end) / 2;
13914    }
13915
13916    var hiddenDuration = DateUtil.getHiddenDurationBetween(this.body.hiddenDates, this.start, this.end);
13917    var hiddenDurationBefore = DateUtil.getHiddenDurationBefore(this.options.moment, this.body.hiddenDates, this, center);
13918    var hiddenDurationAfter = hiddenDuration - hiddenDurationBefore;
13919
13920    // calculate new start and end
13921    var newStart = center - hiddenDurationBefore + (this.start - (center - hiddenDurationBefore)) * scale;
13922    var newEnd = center + hiddenDurationAfter + (this.end - (center + hiddenDurationAfter)) * scale;
13923
13924    // snapping times away from hidden zones
13925    this.startToFront = delta > 0 ? false : true; // used to do the right autocorrection with periodic hidden times
13926    this.endToFront = -delta > 0 ? false : true; // used to do the right autocorrection with periodic hidden times
13927    var safeStart = DateUtil.snapAwayFromHidden(this.body.hiddenDates, newStart, delta, true);
13928    var safeEnd = DateUtil.snapAwayFromHidden(this.body.hiddenDates, newEnd, -delta, true);
13929    if (safeStart != newStart || safeEnd != newEnd) {
13930      newStart = safeStart;
13931      newEnd = safeEnd;
13932    }
13933
13934    this.setRange(newStart, newEnd, false, true);
13935
13936    this.startToFront = false; // revert to default
13937    this.endToFront = true; // revert to default
13938  };
13939
13940  /**
13941   * Move the range with a given delta to the left or right. Start and end
13942   * value will be adjusted. For example, try delta = 0.1 or -0.1
13943   * @param {Number}  delta     Moving amount. Positive value will move right,
13944   *                            negative value will move left
13945   */
13946  Range.prototype.move = function (delta) {
13947    // zoom start Date and end Date relative to the centerDate
13948    var diff = this.end - this.start;
13949
13950    // apply new values
13951    var newStart = this.start + diff * delta;
13952    var newEnd = this.end + diff * delta;
13953
13954    // TODO: reckon with min and max range
13955
13956    this.start = newStart;
13957    this.end = newEnd;
13958  };
13959
13960  /**
13961   * Move the range to a new center point
13962   * @param {Number} moveTo      New center point of the range
13963   */
13964  Range.prototype.moveTo = function (moveTo) {
13965    var center = (this.start + this.end) / 2;
13966
13967    var diff = center - moveTo;
13968
13969    // calculate new start and end
13970    var newStart = this.start - diff;
13971    var newEnd = this.end - diff;
13972
13973    this.setRange(newStart, newEnd);
13974  };
13975
13976  module.exports = Range;
13977
13978/***/ },
13979/* 25 */
13980/***/ function(module, exports, __webpack_require__) {
13981
13982  'use strict';
13983
13984  var Hammer = __webpack_require__(20);
13985
13986  /**
13987   * Register a touch event, taking place before a gesture
13988   * @param {Hammer} hammer       A hammer instance
13989   * @param {function} callback   Callback, called as callback(event)
13990   */
13991  exports.onTouch = function (hammer, callback) {
13992    callback.inputHandler = function (event) {
13993      if (event.isFirst && !isTouching) {
13994        callback(event);
13995
13996        isTouching = true;
13997        setTimeout(function () {
13998          isTouching = false;
13999        }, 0);
14000      }
14001    };
14002
14003    hammer.on('hammer.input', callback.inputHandler);
14004  };
14005
14006  // isTouching is true while a touch action is being emitted
14007  // this is a hack to prevent `touch` from being fired twice
14008  var isTouching = false;
14009
14010  /**
14011   * Register a release event, taking place after a gesture
14012   * @param {Hammer} hammer       A hammer instance
14013   * @param {function} callback   Callback, called as callback(event)
14014   */
14015  exports.onRelease = function (hammer, callback) {
14016    callback.inputHandler = function (event) {
14017      if (event.isFinal && !isReleasing) {
14018        callback(event);
14019
14020        isReleasing = true;
14021        setTimeout(function () {
14022          isReleasing = false;
14023        }, 0);
14024      }
14025    };
14026
14027    return hammer.on('hammer.input', callback.inputHandler);
14028  };
14029
14030  // isReleasing is true while a release action is being emitted
14031  // this is a hack to prevent `release` from being fired twice
14032  var isReleasing = false;
14033
14034  /**
14035   * Unregister a touch event, taking place before a gesture
14036   * @param {Hammer} hammer       A hammer instance
14037   * @param {function} callback   Callback, called as callback(event)
14038   */
14039  exports.offTouch = function (hammer, callback) {
14040    hammer.off('hammer.input', callback.inputHandler);
14041  };
14042
14043  /**
14044   * Unregister a release event, taking place before a gesture
14045   * @param {Hammer} hammer       A hammer instance
14046   * @param {function} callback   Callback, called as callback(event)
14047   */
14048  exports.offRelease = exports.offTouch;
14049
14050/***/ },
14051/* 26 */
14052/***/ function(module, exports) {
14053
14054  /**
14055   * Prototype for visual components
14056   * @param {{dom: Object, domProps: Object, emitter: Emitter, range: Range}} [body]
14057   * @param {Object} [options]
14058   */
14059  "use strict";
14060
14061  function Component(body, options) {
14062    this.options = null;
14063    this.props = null;
14064  }
14065
14066  /**
14067   * Set options for the component. The new options will be merged into the
14068   * current options.
14069   * @param {Object} options
14070   */
14071  Component.prototype.setOptions = function (options) {
14072    if (options) {
14073      util.extend(this.options, options);
14074    }
14075  };
14076
14077  /**
14078   * Repaint the component
14079   * @return {boolean} Returns true if the component is resized
14080   */
14081  Component.prototype.redraw = function () {
14082    // should be implemented by the component
14083    return false;
14084  };
14085
14086  /**
14087   * Destroy the component. Cleanup DOM and event listeners
14088   */
14089  Component.prototype.destroy = function () {};
14090
14091  /**
14092   * Test whether the component is resized since the last time _isResized() was
14093   * called.
14094   * @return {Boolean} Returns true if the component is resized
14095   * @protected
14096   */
14097  Component.prototype._isResized = function () {
14098    var resized = this.props._previousWidth !== this.props.width || this.props._previousHeight !== this.props.height;
14099
14100    this.props._previousWidth = this.props.width;
14101    this.props._previousHeight = this.props.height;
14102
14103    return resized;
14104  };
14105
14106  module.exports = Component;
14107
14108  // should be implemented by the component
14109
14110/***/ },
14111/* 27 */
14112/***/ function(module, exports) {
14113
14114  
14115  /**
14116   * used in Core to convert the options into a volatile variable
14117   * 
14118   * @param {function} moment
14119   * @param {Object} body
14120   * @param {Array | Object} hiddenDates
14121   */
14122  "use strict";
14123
14124  exports.convertHiddenOptions = function (moment, body, hiddenDates) {
14125    if (hiddenDates && !Array.isArray(hiddenDates)) {
14126      return exports.convertHiddenOptions(moment, body, [hiddenDates]);
14127    }
14128
14129    body.hiddenDates = [];
14130    if (hiddenDates) {
14131      if (Array.isArray(hiddenDates) == true) {
14132        for (var i = 0; i < hiddenDates.length; i++) {
14133          if (hiddenDates[i].repeat === undefined) {
14134            var dateItem = {};
14135            dateItem.start = moment(hiddenDates[i].start).toDate().valueOf();
14136            dateItem.end = moment(hiddenDates[i].end).toDate().valueOf();
14137            body.hiddenDates.push(dateItem);
14138          }
14139        }
14140        body.hiddenDates.sort(function (a, b) {
14141          return a.start - b.start;
14142        }); // sort by start time
14143      }
14144    }
14145  };
14146
14147  /**
14148   * create new entrees for the repeating hidden dates
14149   * @param {function} moment
14150   * @param {Object} body
14151   * @param {Array | Object} hiddenDates
14152   */
14153  exports.updateHiddenDates = function (moment, body, hiddenDates) {
14154    if (hiddenDates && !Array.isArray(hiddenDates)) {
14155      return exports.updateHiddenDates(moment, body, [hiddenDates]);
14156    }
14157
14158    if (hiddenDates && body.domProps.centerContainer.width !== undefined) {
14159      exports.convertHiddenOptions(moment, body, hiddenDates);
14160
14161      var start = moment(body.range.start);
14162      var end = moment(body.range.end);
14163
14164      var totalRange = body.range.end - body.range.start;
14165      var pixelTime = totalRange / body.domProps.centerContainer.width;
14166
14167      for (var i = 0; i < hiddenDates.length; i++) {
14168        if (hiddenDates[i].repeat !== undefined) {
14169          var startDate = moment(hiddenDates[i].start);
14170          var endDate = moment(hiddenDates[i].end);
14171
14172          if (startDate._d == "Invalid Date") {
14173            throw new Error("Supplied start date is not valid: " + hiddenDates[i].start);
14174          }
14175          if (endDate._d == "Invalid Date") {
14176            throw new Error("Supplied end date is not valid: " + hiddenDates[i].end);
14177          }
14178
14179          var duration = endDate - startDate;
14180          if (duration >= 4 * pixelTime) {
14181
14182            var offset = 0;
14183            var runUntil = end.clone();
14184            switch (hiddenDates[i].repeat) {
14185              case "daily":
14186                // case of time
14187                if (startDate.day() != endDate.day()) {
14188                  offset = 1;
14189                }
14190                startDate.dayOfYear(start.dayOfYear());
14191                startDate.year(start.year());
14192                startDate.subtract(7, "days");
14193
14194                endDate.dayOfYear(start.dayOfYear());
14195                endDate.year(start.year());
14196                endDate.subtract(7 - offset, "days");
14197
14198                runUntil.add(1, "weeks");
14199                break;
14200              case "weekly":
14201                var dayOffset = endDate.diff(startDate, "days");
14202                var day = startDate.day();
14203
14204                // set the start date to the range.start
14205                startDate.date(start.date());
14206                startDate.month(start.month());
14207                startDate.year(start.year());
14208                endDate = startDate.clone();
14209
14210                // force
14211                startDate.day(day);
14212                endDate.day(day);
14213                endDate.add(dayOffset, "days");
14214
14215                startDate.subtract(1, "weeks");
14216                endDate.subtract(1, "weeks");
14217
14218                runUntil.add(1, "weeks");
14219                break;
14220              case "monthly":
14221                if (startDate.month() != endDate.month()) {
14222                  offset = 1;
14223                }
14224                startDate.month(start.month());
14225                startDate.year(start.year());
14226                startDate.subtract(1, "months");
14227
14228                endDate.month(start.month());
14229                endDate.year(start.year());
14230                endDate.subtract(1, "months");
14231                endDate.add(offset, "months");
14232
14233                runUntil.add(1, "months");
14234                break;
14235              case "yearly":
14236                if (startDate.year() != endDate.year()) {
14237                  offset = 1;
14238                }
14239                startDate.year(start.year());
14240                startDate.subtract(1, "years");
14241                endDate.year(start.year());
14242                endDate.subtract(1, "years");
14243                endDate.add(offset, "years");
14244
14245                runUntil.add(1, "years");
14246                break;
14247              default:
14248                console.log("Wrong repeat format, allowed are: daily, weekly, monthly, yearly. Given:", hiddenDates[i].repeat);
14249                return;
14250            }
14251            while (startDate < runUntil) {
14252              body.hiddenDates.push({ start: startDate.valueOf(), end: endDate.valueOf() });
14253              switch (hiddenDates[i].repeat) {
14254                case "daily":
14255                  startDate.add(1, "days");
14256                  endDate.add(1, "days");
14257                  break;
14258                case "weekly":
14259                  startDate.add(1, "weeks");
14260                  endDate.add(1, "weeks");
14261                  break;
14262                case "monthly":
14263                  startDate.add(1, "months");
14264                  endDate.add(1, "months");
14265                  break;
14266                case "yearly":
14267                  startDate.add(1, "y");
14268                  endDate.add(1, "y");
14269                  break;
14270                default:
14271                  console.log("Wrong repeat format, allowed are: daily, weekly, monthly, yearly. Given:", hiddenDates[i].repeat);
14272                  return;
14273              }
14274            }
14275            body.hiddenDates.push({ start: startDate.valueOf(), end: endDate.valueOf() });
14276          }
14277        }
14278      }
14279      // remove duplicates, merge where possible
14280      exports.removeDuplicates(body);
14281      // ensure the new positions are not on hidden dates
14282      var startHidden = exports.isHidden(body.range.start, body.hiddenDates);
14283      var endHidden = exports.isHidden(body.range.end, body.hiddenDates);
14284      var rangeStart = body.range.start;
14285      var rangeEnd = body.range.end;
14286      if (startHidden.hidden == true) {
14287        rangeStart = body.range.startToFront == true ? startHidden.startDate - 1 : startHidden.endDate + 1;
14288      }
14289      if (endHidden.hidden == true) {
14290        rangeEnd = body.range.endToFront == true ? endHidden.startDate - 1 : endHidden.endDate + 1;
14291      }
14292      if (startHidden.hidden == true || endHidden.hidden == true) {
14293        body.range._applyRange(rangeStart, rangeEnd);
14294      }
14295    }
14296  };
14297
14298  /**
14299   * remove duplicates from the hidden dates list. Duplicates are evil. They mess everything up.
14300   * Scales with N^2
14301   * @param body
14302   */
14303  exports.removeDuplicates = function (body) {
14304    var hiddenDates = body.hiddenDates;
14305    var safeDates = [];
14306    for (var i = 0; i < hiddenDates.length; i++) {
14307      for (var j = 0; j < hiddenDates.length; j++) {
14308        if (i != j && hiddenDates[j].remove != true && hiddenDates[i].remove != true) {
14309          // j inside i
14310          if (hiddenDates[j].start >= hiddenDates[i].start && hiddenDates[j].end <= hiddenDates[i].end) {
14311            hiddenDates[j].remove = true;
14312          }
14313          // j start inside i
14314          else if (hiddenDates[j].start >= hiddenDates[i].start && hiddenDates[j].start <= hiddenDates[i].end) {
14315            hiddenDates[i].end = hiddenDates[j].end;
14316            hiddenDates[j].remove = true;
14317          }
14318          // j end inside i
14319          else if (hiddenDates[j].end >= hiddenDates[i].start && hiddenDates[j].end <= hiddenDates[i].end) {
14320            hiddenDates[i].start = hiddenDates[j].start;
14321            hiddenDates[j].remove = true;
14322          }
14323        }
14324      }
14325    }
14326
14327    for (var i = 0; i < hiddenDates.length; i++) {
14328      if (hiddenDates[i].remove !== true) {
vendor: 2,331 bytes, lines 14329-14402
14329        safeDates.push(hiddenDates[i]);
14330      }
14331    }
14332
14333    body.hiddenDates = safeDates;
14334    body.hiddenDates.sort(function (a, b) {
14335      return a.start - b.start;
14336    }); // sort by start time
14337  };
14338
14339  exports.printDates = function (dates) {
14340    for (var i = 0; i < dates.length; i++) {
14341      console.log(i, new Date(dates[i].start), new Date(dates[i].end), dates[i].start, dates[i].end, dates[i].remove);
14342    }
14343  };
14344
14345  /**
14346   * Used in TimeStep to avoid the hidden times.
14347   * @param {function} moment
14348   * @param {TimeStep} timeStep
14349   * @param previousTime
14350   */
14351  exports.stepOverHiddenDates = function (moment, timeStep, previousTime) {
14352    var stepInHidden = false;
14353    var currentValue = timeStep.current.valueOf();
14354    for (var i = 0; i < timeStep.hiddenDates.length; i++) {
14355      var startDate = timeStep.hiddenDates[i].start;
14356      var endDate = timeStep.hiddenDates[i].end;
14357      if (currentValue >= startDate && currentValue < endDate) {
14358        stepInHidden = true;
14359        break;
14360      }
14361    }
14362
14363    if (stepInHidden == true && currentValue < timeStep._end.valueOf() && currentValue != previousTime) {
14364      var prevValue = moment(previousTime);
14365      var newValue = moment(endDate);
14366      //check if the next step should be major
14367      if (prevValue.year() != newValue.year()) {
14368        timeStep.switchedYear = true;
14369      } else if (prevValue.month() != newValue.month()) {
14370        timeStep.switchedMonth = true;
14371      } else if (prevValue.dayOfYear() != newValue.dayOfYear()) {
14372        timeStep.switchedDay = true;
14373      }
14374
14375      timeStep.current = newValue;
14376    }
14377  };
14378
14379  ///**
14380  // * Used in TimeStep to avoid the hidden times.
14381  // * @param timeStep
14382  // * @param previousTime
14383  // */
14384  //exports.checkFirstStep = function(timeStep) {
14385  //  var stepInHidden = false;
14386  //  var currentValue = timeStep.current.valueOf();
14387  //  for (var i = 0; i < timeStep.hiddenDates.length; i++) {
14388  //    var startDate = timeStep.hiddenDates[i].start;
14389  //    var endDate = timeStep.hiddenDates[i].end;
14390  //    if (currentValue >= startDate && currentValue < endDate) {
14391  //      stepInHidden = true;
14392  //      break;
14393  //    }
14394  //  }
14395  //
14396  //  if (stepInHidden == true && currentValue <= timeStep._end.valueOf()) {
14397  //    var newValue = moment(endDate);
14398  //    timeStep.current = newValue.toDate();
14399  //  }
14400  //};
14401
14402  /**
14403   * replaces the Core toScreen methods
14404   * @param Core
14405   * @param time
14406   * @param width
14407   * @returns {number}
14408   */
14409  exports.toScreen = function (Core, time, width) {
14410    if (Core.body.hiddenDates.length == 0) {
14411      var conversion = Core.range.conversion(width);
14412      return (time.valueOf() - conversion.offset) * conversion.scale;
14413    } else {
14414      var hidden = exports.isHidden(time, Core.body.hiddenDates);
14415      if (hidden.hidden == true) {
14416        time = hidden.startDate;
14417      }
14418
14419      var duration = exports.getHiddenDurationBetween(Core.body.hiddenDates, Core.range.start, Core.range.end);
14420      time = exports.correctTimeForHidden(Core.options.moment, Core.body.hiddenDates, Core.range, time);
14421
14422      var conversion = Core.range.conversion(width, duration);
14423      return (time.valueOf() - conversion.offset) * conversion.scale;
14424    }
14425  };
14426
14427  /**
14428   * Replaces the core toTime methods
14429   * @param body
14430   * @param range
14431   * @param x
14432   * @param width
14433   * @returns {Date}
14434   */
14435  exports.toTime = function (Core, x, width) {
14436    if (Core.body.hiddenDates.length == 0) {
14437      var conversion = Core.range.conversion(width);
14438      return new Date(x / conversion.scale + conversion.offset);
14439    } else {
14440      var hiddenDuration = exports.getHiddenDurationBetween(Core.body.hiddenDates, Core.range.start, Core.range.end);
14441      var totalDuration = Core.range.end - Core.range.start - hiddenDuration;
14442      var partialDuration = totalDuration * x / width;
14443      var accumulatedHiddenDuration = exports.getAccumulatedHiddenDuration(Core.body.hiddenDates, Core.range, partialDuration);
14444
14445      var newTime = new Date(accumulatedHiddenDuration + partialDuration + Core.range.start);
14446      return newTime;
14447    }
14448  };
14449
14450  /**
14451   * Support function
14452   *
14453   * @param hiddenDates
14454   * @param range
14455   * @returns {number}
14456   */
14457  exports.getHiddenDurationBetween = function (hiddenDates, start, end) {
14458    var duration = 0;
14459    for (var i = 0; i < hiddenDates.length; i++) {
14460      var startDate = hiddenDates[i].start;
14461      var endDate = hiddenDates[i].end;
14462      // if time after the cutout, and the
14463      if (startDate >= start && endDate < end) {
14464        duration += endDate - startDate;
14465      }
14466    }
14467    return duration;
14468  };
14469
14470  /**
14471   * Support function
14472   * @param moment
14473   * @param hiddenDates
14474   * @param range
14475   * @param time
14476   * @returns {{duration: number, time: *, offset: number}}
14477   */
14478  exports.correctTimeForHidden = function (moment, hiddenDates, range, time) {
14479    time = moment(time).toDate().valueOf();
14480    time -= exports.getHiddenDurationBefore(moment, hiddenDates, range, time);
14481    return time;
14482  };
14483
14484  exports.getHiddenDurationBefore = function (moment, hiddenDates, range, time) {
14485    var timeOffset = 0;
14486    time = moment(time).toDate().valueOf();
14487
14488    for (var i = 0; i < hiddenDates.length; i++) {
14489      var startDate = hiddenDates[i].start;
14490      var endDate = hiddenDates[i].end;
14491      // if time after the cutout, and the
14492      if (startDate >= range.start && endDate < range.end) {
14493        if (time >
14493= endDate) {
14494          timeOffset += endDate - startDate;
14495        }
14496      }
14497    }
14498    return timeOffset;
14499  };
14500
14501  /**
14502   * sum the duration from start to finish, including the hidden duration,
14503   * until the required amount has been reached, return the accumulated hidden duration
14504   * @param hiddenDates
14505   * @param range
14506   * @param time
14507   * @returns {{duration: number, time: *, offset: number}}
14508   */
14509  exports.getAccumulatedHiddenDuration = function (hiddenDates, range, requiredDuration) {
14510    var hiddenDuration = 0;
14511    var duration = 0;
14512    var previousPoint = range.start;
14513    //exports.printDates(hiddenDates)
14514    for (var i = 0; i < hiddenDates.length; i++) {
14515      var startDate = hiddenDates[i].start;
14516      var endDate = hiddenDates[i].end;
14517      // if time after the cutout, and the
14518      if (startDate >= range.start && endDate < range.end) {
14519        duration += startDate - previousPoint;
14520        previousPoint = endDate;
14521        if (duration >= requiredDuration) {
14522          break;
14523        } else {
14524          hiddenDuration += endDate - startDate;
14525        }
14526      }
14527    }
14528
14529    return hiddenDuration;
14530  };
14531
14532  /**
14533   * used to step over to either side of a hidden block. Correction is disabled on tablets, might be set to true
14534   * @param hiddenDates
14535   * @param time
14536   * @param direction
14537   * @param correctionEnabled
14538   * @returns {*}
14539   */
14540  exports.snapAwayFromHidden = function (hiddenDates, time, direction, correctionEnabled) {
14541    var isHidden = exports.isHidden(time, hiddenDates);
14542    if (isHidden.hidden == true) {
14543      if (direction < 0) {
14544        if (correctionEnabled == true) {
14545          return isHidden.startDate - (isHidden.endDate - time) - 1;
14546        } else {
14547          return isHidden.startDate - 1;
14548        }
14549      } else {
14550        if (correctionEnabled == true) {
14551          return isHidden.endDate + (time - isHidden.startDate) + 1;
14552        } else {
14553          return isHidden.endDate + 1;
14554        }
14555      }
14556    } else {
14557      return time;
14558    }
14559  };
14560
14561  /**
14562   * Check if a time is hidden
14563   *
14564   * @param time
14565   * @param hiddenDates
14566   * @returns {{hidden: boolean, startDate: Window.start, endDate: *}}
14567   */
14568  exports.isHidden = function (time, hiddenDates) {
14569    for (var i = 0; i < hiddenDates.length; i++) {
14570      var startDate = hiddenDates[i].start;
14571      var endDate = hiddenDates[i].end;
14572
14573      if (time >= startDate && time < endDate) {
14574        // if the start is entering a hidden zone
14575        return { hidden: true, startDate: startDate, endDate: endDate };
14576        break;
14577      }
14578    }
14579    return { hidden: false, startDate: startDate, endDate: endDate };
14580  };
14581
14582/***/ },
14583/* 28 */
14584/***/ function(module, exports, __webpack_require__) {
14585
14586  'use strict';
14587
14588  var Emitter = __webpack_require__(12);
14589  var Hammer = __webpack_require__(20);
14590  var hammerUtil = __webpack_require__(25);
14591  var util = __webpack_require__(1);
14592  var DataSet = __webpack_require__(8);
14593  var DataView = __webpack_require__(10);
14594  var Range = __webpack_require__(24);
14595  var ItemSet = __webpack_require__(29);
14596  var TimeAxis = __webpack_require__(39);
14597  var Activator = __webpack_require__(40);
14598  var DateUtil = __webpack_require__(27);
14599  var CustomTime = __webpack_require__(42);
14600
14601  /**
14602   * Create a timeline visualization
14603   * @constructor
14604   */
14605  function Core() {}
14606
14607  // turn Core into an event emitter
14608  Emitter(Core.prototype);
14609
14610  /**
14611   * Create the main DOM for the Core: a root panel containing left, right,
14612   * top, bottom, content, and background panel.
14613   * @param {Element} container  The container element where the Core will
14614   *                             be attached.
14615   * @protected
14616   */
14617  Core.prototype._create = function (container) {
14618    this.dom = {};
14619
14620    this.dom.container = container;
14621
14622    this.dom.root = document.createElement('div');
14623    this.dom.background = document.createElement('div');
14624    this.dom.backgroundVertical = document.createElement('div');
14625    this.dom.backgroundHorizontal = document.createElement('div');
14626    this.dom.centerContainer = document.createElement('div');
14627    this.dom.leftContainer = document.createElement('div');
14628    this.dom.rightContainer = document.createElement('div');
14629    this.dom.center = document.createElement('div');
14630    this.dom.left = document.createElement('div');
14631    this.dom.right = document.createElement('div');
14632    this.dom.top = document.createElement('div');
14633    this.dom.bottom = document.createElement('div');
14634    this.dom.shadowTop = document.createElement('div');
14635    this.dom.shadowBottom = document.createElement('div');
14636    this.dom.shadowTopLeft = document.createElement('div');
14637    this.dom.shadowBottomLeft = document.createElement('div');
14638    this.dom.shadowTopRight = document.createElement('div');
14639    this.dom.shadowBottomRight = document.createElement('div');
14640
14641    this.dom.root.className = 'vis-timeline';
14642    this.dom.background.className = 'vis-panel vis-background';
14643    this.dom.backgroundVertical.className = 'vis-panel vis-background vis-vertical';
14644    this.dom.backgroundHorizontal.className = 'vis-panel vis-background vis-horizontal';
14645    this.dom.centerContainer.className = 'vis-panel vis-center';
14646    this.dom.leftContainer.className = 'vis-panel vis-left';
14647    this.dom.rightContainer.className = 'vis-panel vis-right';
14648    this.dom.top.className = 'vis-panel vis-top';
14649    this.dom.bottom.className = 'vis-panel vis-bottom';
14650    this.dom.left.className = 'vis-content';
14651    this.dom.center.className = 'vis-content';
14652    this.dom.right.className = 'vis-content';
14653    this.dom.shadowTop.className = 'vis-shadow vis-top';
14654    this.dom.shadowBottom.className = 'vis-shadow vis-bottom';
14655    this.dom.shadowTopLeft.className = 'vis-shadow vis-top';
14656    this.dom.shadowBottomLeft.className = 'vis-shadow vis-bottom';
14657    this.dom.shadowTopRight.className = 'vis-shadow vis-top';
14658    this.dom.shadowBottomRight.className = 'vis-shadow vis-bottom';
14659
14660    this.dom.root.appendChild(this.dom.background);
14661    this.dom.root.appendChild(this.dom.backgroundVertical);
14662    this.dom.root.appendChild(this.dom.backgroundHorizontal);
14663    this.dom.root.appendChild(this.dom.centerContainer);
14664    this.dom.root.appendChild(this.dom.leftContainer);
14665    this.dom.root.appendChild(this.dom.rightContainer);
14666    this.dom.root.appendChild(this.dom.top);
14667    this.dom.root.appendChild(this.dom.bottom);
14668
14669    this.dom.centerContainer.appendChild(this.dom.center);
14670    this.dom.leftContainer.appendChild(this.dom.left);
14671    this.dom.rightContainer.appendChild(this.dom.right);
14672
14673    this.dom.centerContainer.appendChild(this.dom.shadowTop);
14674    this.dom.centerContainer.appendChild(this.dom.shadowBottom);
14675    this.dom.leftContainer.appendChild(this.dom.shadowTopLeft);
14676    this.dom.leftContainer.appendChild(this.dom.shadowBottomLeft);
14677    this.dom.rightContainer.appendChild(this.dom.shadowTopRight);
14678    this.dom.rightContainer.appendChild(this.dom.shadowBottomRight);
14679
14680    this.on('rangechange', (function () {
14681      this._redraw(); // this allows overriding the _redraw method
14682    }).bind(this));
14683    this.on('touch', this._onTouch.bind(this));
14684    this.on('pan', this._onDrag.bind(this));
14685
14686    var me = this;
14687    this.on('change', function (properties) {
14688      if (properties && properties.queue == true) {
14689        // redraw once on next tick
14690        if (!me._redrawTimer) {
14691          me._redrawTimer = setTimeout(function () {
14692            me._redrawTimer = null;
14693            me._redraw();
14694          }, 0);
14695        }
14696      } else {
14697        // redraw immediately
14698        me._redraw();
14699      }
14700    });
14701
14702    // create event listeners for all interesting events, these events will be
14703    // emitted via emitter
14704    this.hammer = new Hammer(this.dom.root);
14705    this.hammer.get('pinch').set({ enable: true });
14706    this.hammer.get('pan').set({ threshold: 5, direction: 30 }); // 30 is ALL_DIRECTIONS in hammer.
14707    this.listeners = {};
14708
14709    var events = ['tap', 'doubletap', 'press', 'pinch', 'pan', 'panstart', 'panmove', 'panend'
14710    // TODO: cleanup
14711    //'touch', 'pinch',
14712    //'tap', 'doubletap', 'hold',
14713    //'dragstart', 'drag', 'dragend',
14714    //'mousewheel', 'DOMMouseScroll' // DOMMouseScroll is needed for Firefox
14715    ];
14716    events.forEach(function (type) {
14717      var listener = function listener(event) {
14718        if (me.isActive()) {
14719          me.emit(type, event);
14720        }
14721      };
14722      me.hammer.on(type, listener);
14723      me.listeners[type] = listener;
14724    });
14725
14726    // emulate a touch event (emitted before the start of a pan, pinch, tap, or press)
14727    hammerUtil.onTouch(this.hammer, (function (event) {
14728      me.emit('touch', event);
14729    }).bind(this));
14730
14731    // emulate a release event (emitted after a pan, pinch, tap, or press)
14732    hammerUtil.onRelease(this.hammer, (function (event) {
14733      me.emit('release', event);
14734    }).bind(this));
14735
14736    function onMouseWheel(event) {
14737      if (me.isActive()) {
14738        me.emit('mousewheel', event);
14739      }
14740    }
14741    this.dom.root.addEventListener('mousewheel', onMouseWheel);
14742    this.dom.root.addEventListener('DOMMouseScroll', onMouseWheel);
14743
14744    // size properties of each of the panels
14745    this.props = {
14746      root: {},
14747      background: {},
14748      centerContainer: {},
14749      leftContainer: {},
14750      rightContainer: {},
14751      center: {},
14752      left: {},
14753      right: {},
14754      top: {},
14755      bottom: {},
14756      border: {},
14757      scrollTop: 0,
14758      scrollTopMin: 0
14759    };
14760
14761    this.customTimes = [];
14762
14763    // store state information needed for touch events
14764    this.touch = {};
14765
14766    this.redrawCount = 0;
14767
14768    // attach the root panel to the provided container
14769    if (!container) throw new Error('No container provided');
14770    container.appendChild(this.dom.root);
14771  };
14772
14773  /**
14774   * Set options. Options will be passed to all components loaded in the Timeline.
14775   * @param {Object} [options]
14776   *                           {String} orientation
14777   *                              Vertical orientation for the Timeline,
14778   *                              can be 'bottom' (default) or 'top'.
14779   *                           {String | Number} width
14780   *                              Width for the timeline, a number in pixels or
14781   *                              a css string like '1000px' or '75%'. '100%' by default.
14782   *                           {String | Number} height
14783   *                              Fixed height for the Timeline, a number in pixels or
14784   *                              a css string like '400px' or '75%'. If undefined,
14785   *                              The Timeline will automatically size such that
14786   *                              its contents fit.
14787   *                           {String | Number} minHeight
14788   *                              Minimum height for the Timeline, a number in pixels or
14789   *                              a css string like '400px' or '75%'.
14790   *                           {String | Number} maxHeight
14791   *                              Maximum height for the Timeline, a number in pixels or
14792   *                              a css string like '400px' or '75%'.
14793   *                           {Number | Date | String} start
14794   *                              Start date for the visible window
14795   *                           {Number | Date | String} end
14796   *                              End date for the visible window
14797   */
14798  Core.prototype.setOptions = function (options) {
14799    if (options) {
14800      // copy the known options
14801      var fields = ['width', 'height', 'minHeight', 'maxHeight', 'autoResize', 'start', 'end', 'clickToUse', 'dataAttributes', 'hiddenDates', 'locale', 'locales', 'moment', 'throttleRedraw'];
14802      util.selectiveExtend(fields, this.options, options);
14803
14804      if ('orientation' in options) {
14805        if (typeof options.orientation === 'string') {
14806          this.options.orientation = {
14807            item: options.orientation,
14808            axis: options.orientation
14809          };
14810        } else if (typeof options.orientation === 'object') {
14811          if ('item' in options.orientation) {
14812            this.options.orientation.item = options.orientation.item;
14813          }
14814          if ('axis' in options.orientation) {
14815            this.options.orientation.axis = options.orientation.axis;
14816          }
14817        }
14818      }
14819
14820      if (this.options.orientation.axis === 'both') {
14821        if (!this.timeAxis2) {
14822          var timeAxis2 = this.timeAxis2 = new TimeAxis(this.body);
14823          timeAxis2.setOptions = function (options) {
14824            var _options = options ? util.extend({}, options) : {};
14825            _options.orientation = 'top'; // override the orientation option, always top
14826            TimeAxis.prototype.setOptions.call(timeAxis2, _options);
14827          };
14828          this.components.push(timeAxis2);
14829        }
14830      } else {
14831        if (this.timeAxis2) {
14832          var index = this.components.indexOf(this.timeAxis2);
14833          if (index !== -1) {
14834            this.components.splice(index, 1);
14835          }
14836          this.timeAxis2.destroy();
14837          this.timeAxis2 = null;
14838        }
14839      }
14840
14841      // if the graph2d's drawPoints is a function delegate the callback to the onRender property
14842      if (typeof options.drawPoints == 'function') {
14843        options.drawPoints = {
14844          onRender: options.drawPoints
14845        };
14846      }
14847
14848      if ('hiddenDates' in this.options) {
14849        DateUtil.convertHiddenOptions(this.options.moment, this.body, this.options.hiddenDates);
14850      }
14851
14852      if ('clickToUse' in options) {
14853        if (options.clickToUse) {
14854          if (!this.activator) {
14855            this.activator = new Activator(this.dom.root);
14856          }
14857        } else {
14858          if (this.activator) {
14859            this.activator.destroy();
14860            delete this.activator;
14861          }
14862        }
14863      }
14864
14865      if ('showCustomTime' in options) {
14866        throw new Error('Option `showCustomTime` is deprecated. Create a custom time bar via timeline.addCustomTime(time [, id])');
14867      }
14868
14869      // enable/disable autoResize
14870      this._initAutoResize();
14871    }
14872
14873    // propagate options to all components
14874    this.components.forEach(function (component) {
14875      return component.setOptions(options);
14876    });
14877
14878    // enable/disable configure
14879    if ('configure' in options) {
14880      if (!this.configurator) {
14881        this.configurator = this._createConfigurator();
14882      }
14883
14884      this.configurator.setOptions(options.configure);
14885
14886      // collect the settings of all components, and pass them to the configuration system
14887      var appliedOptions = util.deepExtend({}, this.options);
14888      this.components.forEach(function (component) {
14889        util.deepExtend(appliedOptions, component.options);
14890      });
14891      this.configurator.setModuleOptions({ global: appliedOptions });
14892    }
14893
14894    // override redraw with a throttled version
14895    if (!this._origRedraw) {
14896      this._origRedraw = this._redraw.bind(this);
14897    }
14898    this._redraw = util.throttle(this._origRedraw, this.options.throttleRedraw);
14899
14900    // redraw everything
14901    this._redraw();
14902  };
14903
14904  /**
14905   * Returns true when the Timeline is active.
14906   * @returns {boolean}
14907   */
14908  Core.prototype.isActive = function () {
14909    return !this.activator || this.activator.active;
14910  };
14911
14912  /**
14913   * Destroy the Core, clean up all DOM elements and event listeners.
14914   */
14915  Core.prototype.destroy = function () {
14916    // unbind datasets
14917    this.setItems(null);
14918    this.setGroups(null);
14919
14920    // remove all event listeners
14921    this.off();
14922
14923    // stop checking for changed size
14924    this._stopAutoResize();
14925
14926    // remove from DOM
14927    if (this.dom.root.parentNode) {
14928      this.dom.root.parentNode.removeChild(this.dom.root);
14929    }
14930    this.dom = null;
14931
14932    // remove Activator
14933    if (this.activator) {
14934      this.activator.destroy();
14935      delete this.activator;
14936    }
14937
14938    // cleanup hammer touch events
14939    for (var event in this.listeners) {
14940      if (this.listeners.hasOwnProperty(event)) {
14941        delete this.listeners[event];
14942      }
14943    }
14944    this.listeners = null;
14945    this.hammer = null;
14946
14947    // give all components the opportunity to cleanup
14948    this.components.forEach(function (component) {
14949      return component.destroy();
14950    });
14951
14952    this.body = null;
14953  };
14954
14955  /**
14956   * Set a custom time bar
14957   * @param {Date} time
14958   * @param {number} [id=undefined] Optional id of the custom time bar to be adjusted.
14959   */
14960  Core.prototype.setCustomTime = function (time, id) {
14961    var customTimes = this.customTimes.filter(function (component) {
14962      return id === component.options.id;
14963    });
14964
14965    if (customTimes.length === 0) {
14966      throw new Error('No custom time bar found with id ' + JSON.stringify(id));
14967    }
14968
14969    if (customTimes.length > 0) {
14970      customTimes[0].setCustomTime(time);
14971    }
14972  };
14973
14974  /**
14975   * Retrieve the current custom time.
14976   * @param {number} [id=undefined]    Id of the custom time bar.
14977   * @return {Date | undefined} customTime
14978   */
14979  Core.prototype.getCustomTime = function (id) {
14980    var customTimes = this.customTimes.filter(function (component) {
14981      return component.options.id === id;
14982    });
14983
14984    if (customTimes.length === 0) {
14985      throw new Error('No custom time bar found with id ' + JSON.stringify(id));
14986    }
14987    return customTimes[0].getCustomTime();
14988  };
14989
14990  /**
14991   * Set a custom title for the custom time bar.
14992   * @param {String} [title] Custom title
14993   * @param {number} [id=undefined]    Id of the custom time bar.
14994   */
14995  Core.prototype.setCustomTimeTitle = function (title, id) {
14996    var customTimes = this.customTimes.filter(function (component) {
14997      return component.options.id === id;
14998    });
14999
15000    if (customTimes.length === 0) {
15001      throw new Error('No custom time bar found with id ' + JSON.stringify(id));
15002    }
15003    if (customTimes.length > 0) {
15004      return customTimes[0].setCustomTitle(title);
15005    }
15006  };
15007
15008  /**
15009   * Retrieve meta information from an event.
15010   * Should be overridden by classes extending Core
15011   * @param {Event} event
15012   * @return {Object} An object with related information.
15013   */
15014  Core.prototype.getEventProperties = function (event) {
15015    return { event: event };
15016  };
15017
15018  /**
15019   * Add custom vertical bar
15020   * @param {Date | String | Number} [time]  A Date, unix timestamp, or
15021   *                                         ISO date string. Time point where
15022   *                                         the new bar should be placed.
15023   *                                         If not provided, `new Date()` will
15024   *                                         be used.
15025   * @param {Number | String} [id=undefined] Id of the new bar. Optional
15026   * @return {Number | String}               Returns the id of the new bar
15027   */
15028  Core.prototype.addCustomTime = function (time, id) {
15029    var timestamp = time !== undefined ? util.convert(time, 'Date').valueOf() : new Date();
15030
15031    var exists = this.customTimes.some(function (customTime) {
15032      return customTime.options.id === id;
15033    });
15034    if (exists) {
15035      throw new Error('A custom time with id ' + JSON.stringify(id) + ' already exists');
15036    }
15037
15038    var customTime = new CustomTime(this.body, util.extend({}, this.options, {
15039      time: timestamp,
15040      id: id
15041    }));
15042
15043    this.customTimes.push(customTime);
15044    this.components.push(customTime);
15045    this._redraw();
15046
15047    return id;
15048  };
15049
15050  /**
15051   * Remove previously added custom bar
15052   * @param {int} id ID of the custom bar to be removed
15053   * @return {boolean} True if the bar exists and is removed, false otherwise
15054   */
15055  Core.prototype.removeCustomTime = function (id) {
15056    var customTimes = this.customTimes.filter(function (bar) {
15057      return bar.options.id === id;
15058    });
15059
15060    if (customTimes.length === 0) {
15061      throw new Error('No custom time bar found with id ' + JSON.stringify(id));
15062    }
15063
15064    customTimes.forEach((function (customTime) {
15065      this.customTimes.splice(this.customTimes.indexOf(customTime), 1);
15066      this.components.splice(this.components.indexOf(customTime), 1);
15067      customTime.destroy();
15068    }).bind(this));
15069  };
15070
15071  /**
15072   * Get the id's of the currently visible items.
15073   * @returns {Array} The ids of the visible items
15074   */
15075  Core.prototype.getVisibleItems = function () {
15076    return this.itemSet && this.itemSet.getVisibleItems() || [];
15077  };
15078
15079  /**
15080   * Set Core window such that it fits all items
15081   * @param {Object} [options]  Available options:
15082   *                                `animation: boolean | {duration: number, easingFunction: string}`
15083   *                                    If true (default), the range is animated
15084   *                                    smoothly to the new window. An object can be
15085   *                                    provided to specify duration and easing function.
15086   *                                    Default duration is 500 ms, and default easing
15087   *                                    function is 'easeInOutQuad'.
15088   */
15089  Core.prototype.fit = function (options) {
15090    var range = this.getDataRange();
15091
15092    // skip range set if there is no min and max date
15093    if (range.min === null && range.max === null) {
15094      return;
15095    }
15096
15097    // apply a margin of 1% left and right of the data
15098    var interval = range.max - range.min;
15099    var min = new Date(range.min.valueOf() - interval * 0.01);
15100    var max = new Date(range.max.valueOf() + interval * 0.01);
15101
15102    var animation = options && options.animation !== undefined ? options.animation : true;
15103    this.range.setRange(min, max, animation);
15104  };
15105
15106  /**
15107   * Calculate the data range of the items start and end dates
15108   * @returns {{min: Date | null, max: Date | null}}
15109   * @protected
15110   */
15111  Core.prototype.getDataRange = function () {
15112    // must be implemented by Timeline and Graph2d
15113    throw new Error('Cannot invoke abstract method getDataRange');
15114  };
15115
15116  /**
15117   * Set the visible window. Both parameters are optional, you can change only
15118   * start or only end. Syntax:
15119   *
15120   *     TimeLine.setWindow(start, end)
15121   *     TimeLine.setWindow(start, end, options)
15122   *     TimeLine.setWindow(range)
15123   *
15124   * Where start and end can be a Date, number, or string, and range is an
15125   * object with properties start and end.
15126   *
15127   * @param {Date | Number | String | Object} [start] Start date of visible window
15128   * @param {Date | Number | String} [end]            End date of visible window
15129   * @param {Object} [options]  Available options:
15130   *                                `animation: boolean | {duration: number, easingFunction: string}`
15131   *                                    If true (default), the range is animated
15132   *                                    smoothly to the new window. An object can be
15133   *                                    provided to specify duration and easing function.
15134   *                                    Default duration is 500 ms, and default easing
15135   *                                    function is 'easeInOutQuad'.
15136   */
15137  Core.prototype.setWindow = function (start, end, options) {
15138    var animation;
15139    if (arguments.length == 1) {
15140      var range = arguments[0];
15141      animation = range.animation !== undefined ? range.animation : true;
15142      this.range.setRange(range.start, range.end, animation);
15143    } else {
15144      animation = options && options.animation !== undefined ? options.animation : true;
15145      this.range.setRange(start, end, animation);
15146    }
15147  };
15148
15149  /**
15150   * Move the window such that given time is centered on screen.
15151   * @param {Date | Number | String} time
15152   * @param {Object} [options]  Available options:
15153   *                                `animation: boolean | {duration: number, easingFunction: string}`
15154   *                                    If true (default), the range is animated
15155   *                                    smoothly to the new window. An object can be
15156   *                                    provided to specify duration and easing function.
15157   *                                    Default duration is 500 ms, and default easing
15158   *                                    function is 'easeInOutQuad'.
15159   */
15160  Core.prototype.moveTo = function (time, options) {
15161    var interval = this.range.end - this.range.start;
15162    var t = util.convert(time, 'Date').valueOf();
15163
15164    var start = t - interval / 2;
15165    var end = t + interval / 2;
15166    var animation = options && options.animation !== undefined ? options.animation : true;
15167
15168    this.range.setRange(start, end, animation);
15169  };
15170
15171  /**
15172   * Get the visible window
15173   * @return {{start: Date, end: Date}}   Visible range
15174   */
15175  Core.prototype.getWindow = function () {
15176    var range = this.range.getRange();
15177    return {
15178      start: new Date(range.start),
15179      end: new Date(range.end)
15180    };
15181  };
15182
15183  /**
15184   * Force a redraw. Can be overridden by implementations of Core
15185   *
15186   * Note: this function will be overridden on construction with a trottled version
15187   */
15188  Core.prototype.redraw = function () {
15189    this._redraw();
15190  };
15191
15192  /**
15193   * Redraw for internal use. Redraws all components. See also the public
15194   * method redraw.
15195   * @protected
15196   */
15197  Core.prototype._redraw = function () {
15198    var resized = false;
15199    var options = this.options;
15200    var props = this.props;
15201    var dom = this.dom;
15202
15203    if (!dom) return; // when destroyed
15204
15205    DateUtil.updateHiddenDates(this.options.moment, this.body, this.options.hiddenDates);
15206
15207    // update class names
15208    if (options.orientation == 'top') {
15209      util.addClassName(dom.root, 'vis-top');
15210      util.removeClassName(dom.root, 'vis-bottom');
15211    } else {
15212      util.removeClassName(dom.root, 'vis-top');
15213      util.addClassName(dom.root, 'vis-bottom');
15214    }
15215
15216    // update root width and height options
15217    dom.root.style.maxHeight = util.option.asSize(options.maxHeight, '');
15218    dom.root.style.minHeight = util.option.asSize(options.minHeight, '');
15219    dom.root.style.width = util.option.asSize(options.width, '');
15220
15221    // calculate border widths
15222    props.border.left = (dom.centerContainer.offsetWidth - dom.centerContainer.clientWidth) / 2;
15223    props.border.right = props.border.left;
15224    props.border.top = (dom.centerContainer.offsetHeight - dom.centerContainer.clientHeight) / 2;
15225    props.border.bottom = props.border.top;
15226    var borderRootHeight = dom.root.offsetHeight - dom.root.clientHeight;
15227    var borderRootWidth = dom.root.offsetWidth - dom.root.clientWidth;
15228
15229    // workaround for a bug in IE: the clientWidth of an element with
15230    // a height:0px and overflow:hidden is not calculated and always has value 0
15231    if (dom.centerContainer.clientHeight === 0) {
15232      props.border.left = props.border.top;
15233      props.border.right = props.border.left;
15234    }
15235    if (dom.root.clientHeight === 0) {
15236      borderRootWidth = borderRootHeight;
15237    }
15238
15239    // calculate the heights. If any of the side panels is empty, we set the height to
15240    // minus the border width, such that the border will be invisible
15241    props.center.height = dom.center.offsetHeight;
15242    props.left.height = dom.left.offsetHeight;
15243    props.right.height = dom.right.offsetHeight;
15244    props.top.height = dom.top.clientHeight || -props.border.top;
15245    props.bottom.height = dom.bottom.clientHeight || -props.border.bottom;
15246
15247    // TODO: compensate borders when any of the panels is empty.
15248
15249    // apply auto height
15250    // TODO: only calculate autoHeight when needed (else we cause an extra reflow/repaint of the DOM)
15251    var contentHeight = Math.max(props.left.height, props.center.height, props.right.height);
15252    var autoHeight = props.top.height + contentHeight + props.bottom.height + borderRootHeight + props.border.top + props.border.bottom;
15253    dom.root.style.height = util.option.asSize(options.height, autoHeight + 'px');
15254
15255    // calculate heights of the content panels
15256    props.root.height = dom.root.offsetHeight;
15257    props.background.height = props.root.height - borderRootHeight;
15258    var containerHeight = props.root.height - props.top.height - props.bottom.height - borderRootHeight;
15259    props.centerContainer.height = containerHeight;
15260    props.leftContainer.height = containerHeight;
15261    props.rightContainer.height = props.leftContainer.height;
15262
15263    // calculate the widths of the panels
15264    props.root.width = dom.root.offsetWidth;
15265    props.background.width = props.root.width - borderRootWidth;
15266    props.left.width = dom.leftContainer.clientWidth || -props.border.left;
15267    props.leftContainer.width = props.left.width;
15268    props.right.width = dom.rightContainer.clientWidth || -props.border.right;
15269    props.rightContainer.width = props.right.width;
15270    var centerWidth = props.root.width - props.left.width - props.right.width - borderRootWidth;
15271    props.center.width = centerWidth;
15272    props.centerContainer.width = centerWidth;
15273    props.top.width = centerWidth;
15274    props.bottom.width = centerWidth;
15275
15276    // resize the panels
15277    dom.background.style.height = props.background.height + 'px';
15278    dom.backgroundVertical.style.height = props.background.height + 'px';
15279    dom.backgroundHorizontal.style.height = props.centerContainer.height + 'px';
15280    dom.centerContainer.style.height = props.centerContainer.height + 'px';
15281    dom.leftContainer.style.height = props.leftContainer.height + 'px';
15282    dom.rightContainer.style.height = props.rightContainer.height + 'px';
15283
15284    dom.background.style.width = props.background.width + 'px';
15285    dom.backgroundVertical.style.width = props.centerContainer.width + 'px';
15286    dom.backgroundHorizontal.style.width = props.background.width + 'px';
15287    dom.centerContainer.style.width = props.center.width + 'px';
15288    dom.top.style.width = props.top.width + 'px';
15289    dom.bottom.style.width = props.bottom.width + 'px';
15290
15291    // reposition the panels
15292    dom.background.style.left = '0';
15293    dom.background.style.top = '0';
15294    dom.backgroundVertical.style.left = props.left.width + props.border.left + 'px';
15295    dom.backgroundVertical.style.top = '0';
15296    dom.backgroundHorizontal.style.left = '0';
15297    dom.backgroundHorizontal.style.top = props.top.height + 'px';
15298    dom.centerContainer.style.left = props.left.width + 'px';
15299    dom.centerContainer.style.top = props.top.height + 'px';
15300    dom.leftContainer.style.left = '0';
15301    dom.leftContainer.style.top = props.top.height + 'px';
15302    dom.rightContainer.style.left = props.left.width + props.center.width + 'px';
15303    dom.rightContainer.style.top = props.top.height + 'px';
15304    dom.top.style.left = props.left.width + 'px';
15305    dom.top.style.top = '0';
15306    dom.bottom.style.left = props.left.width + 'px';
15307    dom.bottom.style.top = props.top.height + props.centerContainer.height + 'px';
15308
15309    // update the scrollTop, feasible range for the offset can be changed
15310    // when the height of the Core or of the contents of the center changed
15311    this._updateScrollTop();
15312
15313    // reposition the scrollable contents
15314    var offset = this.props.scrollTop;
15315    if (options.orientation.item != 'top') {
15316      offset += Math.max(this.props.centerContainer.height - this.props.center.height - this.props.border.top - this.props.border.bottom, 0);
15317    }
15318    dom.center.style.left = '0';
15319    dom.center.style.top = offset + 'px';
15320    dom.left.style.left = '0';
15321    dom.left.style.top = offset + 'px';
15322    dom.right.style.left = '0';
15323    dom.right.style.top = offset + 'px';
15324
15325    // show shadows when vertical scrolling is available
15326    var visibilityTop = this.props.scrollTop == 0 ? 'hidden' : '';
15327    var visibilityBottom = this.props.scrollTop == this.props.scrollTopMin ? 'hidden' : '';
15328    dom.shadowTop.style.visibility = visibilityTop;
15329    dom.shadowBottom.style.visibility = visibilityBottom;
15330    dom.shadowTopLeft.style.visibility = visibilityTop;
15331    dom.shadowBottomLeft.style.visibility = visibilityBottom;
15332    dom.shadowTopRight.style.visibility = visibilityTop;
15333    dom.shadowBottomRight.style.visibility = visibilityBottom;
15334
15335    // redraw all components
15336    this.components.forEach(function (component) {
15337      resized = component.redraw() || resized;
15338    });
15339    if (resized) {
15340      // keep repainting until all sizes are settled
15341      var MAX_REDRAWS = 3; // maximum number of consecutive redraws
15342      if (this.redrawCount < MAX_REDRAWS) {
15343        this.redrawCount++;
15344        this._redraw();
15345      } else {
15346        console.log('WARNING: infinite loop in redraw?');
15347      }
15348      this.redrawCount = 0;
15349    }
15350  };
15351
15352  // TODO: deprecated since version 1.1.0, remove some day
15353  Core.prototype.repaint = function () {
15354    throw new Error('Function repaint is deprecated. Use redraw instead.');
15355  };
15356
15357  /**
15358   * Set a current time. This can be used for example to ensure that a client's
15359   * time is synchronized with a shared server time.
15360   * Only applicable when option `showCurrentTime` is true.
15361   * @param {Date | String | Number} time     A Date, unix timestamp, or
15362   *                                          ISO date string.
15363   */
15364  Core.prototype.setCurrentTime = function (time) {
15365    if (!this.currentTime) {
15366      throw new Error('Option showCurrentTime must be true');
15367    }
15368
15369    this.currentTime.setCurrentTime(time);
15370  };
15371
15372  /**
15373   * Get the current time.
15374   * Only applicable when option `showCurrentTime` is true.
15375   * @return {Date} Returns the current time.
15376   */
15377  Core.prototype.getCurrentTime = function () {
15378    if (!this.currentTime) {
15379      throw new Error('Option showCurrentTime must be true');
15380    }
15381
15382    return this.currentTime.getCurrentTime();
15383  };
15384
15385  /**
15386   * Convert a position on screen (pixels) to a datetime
15387   * @param {int}     x    Position on the screen in pixels
15388   * @return {Date}   time The datetime the corresponds with given position x
15389   * @protected
15390   */
15391  // TODO: move this function to Range
15392  Core.prototype._toTime = function (x) {
15393    return DateUtil.toTime(this, x, this.props.center.width);
15394  };
15395
15396  /**
15397   * Convert a position on the global screen (pixels) to a datetime
15398   * @param {int}     x    Position on the screen in pixels
15399   * @return {Date}   time The datetime the corresponds with given position x
15400   * @protected
15401   */
15402  // TODO: move this function to Range
15403  Core.prototype._toGlobalTime = function (x) {
15404    return DateUtil.toTime(this, x, this.props.root.width);
15405    //var conversion = this.range.conversion(this.props.root.width);
15406    //return new Date(x / conversion.scale + conversion.offset);
15407  };
15408
15409  /**
15410   * Convert a datetime (Date object) into a position on the screen
15411   * @param {Date}   time A date
15412   * @return {int}   x    The position on the screen in pixels which corresponds
15413   *                      with the given date.
15414   * @protected
15415   */
15416  // TODO: move this function to Range
15417  Core.prototype._toScreen = function (time) {
15418    return DateUtil.toScreen(this, time, this.props.center.width);
15419  };
15420
15421  /**
15422   * Convert a datetime (Date object) into a position on the root
15423   * This is used to get the pixel density estimate for the screen, not the center panel
15424   * @param {Date}   time A date
15425   * @return {int}   x    The position on root in pixels which corresponds
15426   *                      with the given date.
15427   * @protected
15428   */
15429  // TODO: move this function to Range
15430  Core.prototype._toGlobalScreen = function (time) {
15431    return DateUtil.toScreen(this, time, this.props.root.width);
15432    //var conversion = this.range.conversion(this.props.root.width);
15433    //return (time.valueOf() - conversion.offset) * conversion.scale;
15434  };
15435
15436  /**
15437   * Initialize watching when option autoResize is true
15438   * @private
15439   */
15440  Core.prototype._initAutoResize = function () {
15441    if (this.options.autoResize == true) {
15442      this._startAutoResize();
15443    } else {
15444      this._stopAutoResize();
15445    }
15446  };
15447
15448  /**
15449   * Watch for changes in the size of the container. On resize, the Panel will
15450   * automatically redraw itself.
15451   * @private
15452   */
15453  Core.prototype._startAutoResize = function () {
15454    var me = this;
15455
15456    this._stopAutoResize();
15457
15458    this._onResize = function () {
15459      if (me.options.autoResize != true) {
15460        // stop watching when the option autoResize is changed to fal
15460se
15461        me._stopAutoResize();
15462        return;
15463      }
15464
15465      if (me.dom.root) {
15466        // check whether the frame is resized
15467        // Note: we compare offsetWidth here, not clientWidth. For some reason,
15468        // IE does not restore the clientWidth from 0 to the actual width after
15469        // changing the timeline's container display style from none to visible
15470        if (me.dom.root.offsetWidth != me.props.lastWidth || me.dom.root.offsetHeight != me.props.lastHeight) {
15471          me.props.lastWidth = me.dom.root.offsetWidth;
15472          me.props.lastHeight = me.dom.root.offsetHeight;
15473
15474          me.emit('change');
15475        }
15476      }
15477    };
15478
15479    // add event listener to window resize
15480    util.addEventListener(window, 'resize', this._onResize);
15481
15482    this.watchTimer = setInterval(this._onResize, 1000);
15483  };
15484
15485  /**
15486   * Stop watching for a resize of the frame.
15487   * @private
15488   */
15489  Core.prototype._stopAutoResize = function () {
15490    if (this.watchTimer) {
15491      clearInterval(this.watchTimer);
15492      this.watchTimer = undefined;
15493    }
15494
15495    // remove event listener on window.resize
15496    if (this._onResize) {
15497      util.removeEventListener(window, 'resize', this._onResize);
15498      this._onResize = null;
15499    }
15500  };
15501
15502  /**
15503   * Start moving the timeline vertically
15504   * @param {Event} event
15505   * @private
15506   */
15507  Core.prototype._onTouch = function (event) {
15508    this.touch.allowDragging = true;
15509    this.touch.initialScrollTop = this.props.scrollTop;
15510  };
15511
15512  /**
15513   * Start moving the timeline vertically
15514   * @param {Event} event
15515   * @private
15516   */
15517  Core.prototype._onPinch = function (event) {
15518    this.touch.allowDragging = false;
15519  };
15520
15521  /**
15522   * Move the timeline vertically
15523   * @param {Event} event
15524   * @private
15525   */
15526  Core.prototype._onDrag = function (event) {
15527    // refuse to drag when we where pinching to prevent the timeline make a jump
15528    // when releasing the fingers in opposite order from the touch screen
15529    if (!this.touch.allowDragging) return;
15530
15531    var delta = event.deltaY;
15532
15533    var oldScrollTop = this._getScrollTop();
15534    var newScrollTop = this._setScrollTop(this.touch.initialScrollTop + delta);
15535
15536    if (newScrollTop != oldScrollTop) {
15537      this._redraw(); // TODO: this causes two redraws when dragging, the other is triggered by rangechange already
15538      this.emit('verticalDrag');
15539    }
15540  };
15541
15542  /**
15543   * Apply a scrollTop
15544   * @param {Number} scrollTop
15545   * @returns {Number} scrollTop  Returns the applied scrollTop
15546   * @private
15547   */
15548  Core.prototype._setScrollTop = function (scrollTop) {
15549    this.props.scrollTop = scrollTop;
15550    this._updateScrollTop();
15551    return this.props.scrollTop;
15552  };
15553
15554  /**
15555   * Update the current scrollTop when the height of  the containers has been changed
15556   * @returns {Number} scrollTop  Returns the applied scrollTop
15557   * @private
15558   */
15559  Core.prototype._updateScrollTop = function () {
15560    // recalculate the scrollTopMin
15561    var scrollTopMin = Math.min(this.props.centerContainer.height - this.props.center.height, 0); // is negative or zero
15562    if (scrollTopMin != this.props.scrollTopMin) {
15563      // in case of bottom orientation, change the scrollTop such that the contents
15564      // do not move relative to the time axis at the bottom
15565      if (this.options.orientation.item != 'top') {
15566        this.props.scrollTop += scrollTopMin - this.props.scrollTopMin;
15567      }
15568      this.props.scrollTopMin = scrollTopMin;
15569    }
15570
15571    // limit the scrollTop to the feasible scroll range
15572    if (this.props.scrollTop > 0) this.props.scrollTop = 0;
15573    if (this.props.scrollTop < scrollTopMin) this.props.scrollTop = scrollTopMin;
15574
15575    return this.props.scrollTop;
15576  };
15577
15578  /**
15579   * Get the current scrollTop
15580   * @returns {number} scrollTop
15581   * @private
15582   */
15583  Core.prototype._getScrollTop = function () {
15584    return this.props.scrollTop;
15585  };
15586
15587  /**
15588   * Load a configurator
15589   * @return {Object}
15590   * @private
15591   */
15592  Core.prototype._createConfigurator = function () {
15593    throw new Error('Cannot invoke abstract method _createConfigurator');
15594  };
15595
15596  module.exports = Core;
15597
15598/***/ },
15599/* 29 */
15600/***/ function(module, exports, __webpack_require__) {
15601
15602  'use strict';
15603
15604  var Hammer = __webpack_require__(20);
15605  var util = __webpack_require__(1);
15606  var DataSet = __webpack_require__(8);
15607  var DataView = __webpack_require__(10);
15608  var TimeStep = __webpack_require__(30);
15609  var Component = __webpack_require__(26);
15610  var Group = __webpack_require__(31);
15611  var BackgroundGroup = __webpack_require__(35);
15612  var BoxItem = __webpack_require__(36);
15613  var PointItem = __webpack_require__(37);
15614  var RangeItem = __webpack_require__(33);
15615  var BackgroundItem = __webpack_require__(38);
15616
15617  var UNGROUPED = '__ungrouped__'; // reserved group id for ungrouped items
15618  var BACKGROUND = '__background__'; // reserved group id for background items without group
15619
15620  /**
15621   * An ItemSet holds a set of items and ranges which can be displayed in a
15622   * range. The width is determined by the parent of the ItemSet, and the height
15623   * is determined by the size of the items.
15624   * @param {{dom: Object, domProps: Object, emitter: Emitter, range: Range}} body
15625   * @param {Object} [options]      See ItemSet.setOptions for the available options.
15626   * @constructor ItemSet
15627   * @extends Component
15628   */
15629  function ItemSet(body, options) {
15630    this.body = body;
15631
15632    this.defaultOptions = {
15633      type: null, // 'box', 'point', 'range', 'background'
15634      orientation: {
15635        item: 'bottom' // item orientation: 'top' or 'bottom'
15636      },
15637      align: 'auto', // alignment of box items
15638      stack: true,
15639      groupOrderSwap: function groupOrderSwap(fromGroup, toGroup, groups) {
15640        var targetOrder = toGroup.order;
15641        toGroup.order = fromGroup.order;
15642        fromGroup.order = targetOrder;
15643      },
15644      groupOrder: 'order',
15645
15646      selectable: true,
15647      multiselect: false,
15648
15649      editable: {
15650        updateTime: false,
15651        updateGroup: false,
15652        add: false,
15653        remove: false
15654      },
15655
15656      groupEditable: {
15657        order: false,
15658        add: false,
15659        remove: false
15660      },
15661
15662      snap: TimeStep.snap,
15663
15664      onAdd: function onAdd(item, callback) {
15665        callback(item);
15666      },
15667      onUpdate: function onUpdate(item, callback) {
15668        callback(item);
15669      },
15670      onMove: function onMove(item, callback) {
15671        callback(item);
15672      },
15673      onRemove: function onRemove(item, callback) {
15674        callback(item);
15675      },
15676      onMoving: function onMoving(item, callback) {
15677        callback(item);
15678      },
15679      onAddGroup: function onAddGroup(item, callback) {
15680        callback(item);
15681      },
15682      onMoveGroup: function onMoveGroup(item, callback) {
15683        callback(item);
15684      },
15685      onRemoveGroup: function onRemoveGroup(item, callback) {
15686        callback(item);
15687      },
15688
15689      margin: {
15690        item: {
15691          horizontal: 10,
15692          vertical: 10
15693        },
15694        axis: 20
15695      }
15696    };
15697
15698    // options is shared by this ItemSet and all its items
15699    this.options = util.extend({}, this.defaultOptions);
15700
15701    // options for getting items from the DataSet with the correct type
15702    this.itemOptions = {
15703      type: { start: 'Date', end: 'Date' }
15704    };
15705
15706    this.conversion = {
15707      toScreen: body.util.toScreen,
15708      toTime: body.util.toTime
15709    };
15710    this.dom = {};
15711    this.props = {};
15712    this.hammer = null;
15713
15714    var me = this;
15715    this.itemsData = null; // DataSet
15716    this.groupsData = null; // DataSet
15717
15718    // listeners for the DataSet of the items
15719    this.itemListeners = {
15720      'add': function add(event, params, senderId) {
15721        me._onAdd(params.items);
15722      },
15723      'update': function update(event, params, senderId) {
15724        me._onUpdate(params.items);
15725      },
15726      'remove': function remove(event, params, senderId) {
15727        me._onRemove(params.items);
15728      }
15729    };
15730
15731    // listeners for the DataSet of the groups
15732    this.groupListeners = {
15733      'add': function add(event, params, senderId) {
15734        me._onAddGroups(params.items);
15735      },
15736      'update': function update(event, params, senderId) {
15737        me._onUpdateGroups(params.items);
15738      },
15739      'remove': function remove(event, params, senderId) {
15740        me._onRemoveGroups(params.items);
15741      }
15742    };
15743
15744    this.items = {}; // object with an Item for every data item
15745    this.groups = {}; // Group object for every group
15746    this.groupIds = [];
15747
15748    this.selection = []; // list with the ids of all selected nodes
15749    this.stackDirty = true; // if true, all items will be restacked on next redraw
15750
15751    this.touchParams = {}; // stores properties while dragging
15752    this.groupTouchParams = {};
15753    // create the HTML DOM
15754
15755    this._create();
15756
15757    this.setOptions(options);
15758  }
15759
15760  ItemSet.prototype = new Component();
15761
15762  // available item types will be registered here
15763  ItemSet.types = {
15764    background: BackgroundItem,
15765    box: BoxItem,
15766    range: RangeItem,
15767    point: PointItem
15768  };
15769
15770  /**
15771   * Create the HTML DOM for the ItemSet
15772   */
15773  ItemSet.prototype._create = function () {
15774    var frame = document.createElement('div');
15775    frame.className = 'vis-itemset';
15776    frame['timeline-itemset'] = this;
15777    this.dom.frame = frame;
15778
15779    // create background panel
15780    var background = document.createElement('div');
15781    background.className = 'vis-background';
15782    frame.appendChild(background);
15783    this.dom.background = background;
15784
15785    // create foreground panel
15786    var foreground = document.createElement('div');
15787    foreground.className = 'vis-foreground';
15788    frame.appendChild(foreground);
15789    this.dom.foreground = foreground;
15790
15791    // create axis panel
15792    var axis = document.createElement('div');
15793    axis.className = 'vis-axis';
15794    this.dom.axis = axis;
15795
15796    // create labelset
15797    var labelSet = document.createElement('div');
15798    labelSet.className = 'vis-labelset';
15799    this.dom.labelSet = labelSet;
15800
15801    // create ungrouped Group
15802    this._updateUngrouped();
15803
15804    // create background Group
15805    var backgroundGroup = new BackgroundGroup(BACKGROUND, null, this);
15806    backgroundGroup.show();
15807    this.groups[BACKGROUND] = backgroundGroup;
15808
15809    // attach event listeners
15810    // Note: we bind to the centerContainer for the case where the height
15811    //       of the center container is larger than of the ItemSet, so we
15812    //       can click in the empty area to create a new item or deselect an item.
15813    this.hammer = new Hammer(this.body.dom.centerContainer);
15814
15815    // drag items when selected
15816    this.hammer.on('hammer.input', (function (event) {
15817      if (event.isFirst) {
15818        this._onTouch(event);
15819      }
15820    }).bind(this));
15821    this.hammer.on('panstart', this._onDragStart.bind(this));
15822    this.hammer.on('panmove', this._onDrag.bind(this));
15823    this.hammer.on('panend', this._onDragEnd.bind(this));
15824    this.hammer.get('pan').set({ threshold: 5, direction: 30 }); // 30 is ALL_DIRECTIONS in hammer.
15825
15826    // single select (or unselect) when tapping an item
15827    this.hammer.on('tap', this._onSelectItem.bind(this));
15828
15829    // multi select when holding mouse/touch, or on ctrl+click
15830    this.hammer.on('press', this._onMultiSelectItem.bind(this));
15831
15832    // add item on doubletap
15833    this.hammer.on('doubletap', this._onAddItem.bind(this));
15834
15835    this.groupHammer = new Hammer(this.body.dom.leftContainer);
15836    this.groupHammer.on('panstart', this._onGroupDragStart.bind(this));
15837    this.groupHammer.on('panmove', this._onGroupDrag.bind(this));
15838    this.groupHammer.on('panend', this._onGroupDragEnd.bind(this));
15839    this.groupHammer.get('pan').set({ threshold: 5, direction: 30 });
15840
15841    // attach to the DOM
15842    this.show();
15843  };
15844
15845  /**
15846   * Set options for the ItemSet. Existing options will be extended/overwritten.
15847   * @param {Object} [options] The following options are available:
15848   *                           {String} type
15849   *                              Default type for the items. Choose from 'box'
15850   *                              (default), 'point', 'range', or 'background'.
15851   *                              The default style can be overwritten by
15852   *                              individual items.
15853   *                           {String} align
15854   *                              Alignment for the items, only applicable for
15855   *                              BoxItem. Choose 'center' (default), 'left', or
15856   *                              'right'.
15857   *                           {String} orientation.item
15858   *                              Orientation of the item set. Choose 'top' or
15859   *                              'bottom' (default).
15860   *                           {Function} groupOrder
15861   *                              A sorting function for ordering groups
15862   *                           {Boolean} stack
15863   *                              If true (default), items will be stacked on
15864   *                              top of each other.
15865   *                           {Number} margin.axis
15866   *                              Margin between the axis and the items in pixels.
15867   *                              Default is 20.
15868   *                           {Number} margin.item.horizontal
15869   *                              Horizontal margin between items in pixels.
15870   *                              Default is 10.
15871   *                           {Number} margin.item.vertical
15872   *                              Vertical Margin between items in pixels.
15873   *                              Default is 10.
15874   *                           {Number} margin.item
15875   *                              Margin between items in pixels in both horizontal
15876   *                              and vertical direction. Default is 10.
15877   *                           {Number} margin
15878   *                              Set margin for both axis and items in pixels.
15879   *                           {Boolean} selectable
15880   *                              If true (default), items can be selected.
15881   *                           {Boolean} multiselect
15882   *                              If true, multiple items can be selected.
15883   *                              False by default.
15884   *                           {Boolean} editable
15885   *                              Set all editable options to true or false
15886   *                           {Boolean} editable.updateTime
15887   *                              Allow dragging an item to an other moment in time
15888   *                           {Boolean} editable.updateGroup
15889   *                              Allow dragging an item to an other group
15890   *                           {Boolean} editable.add
15891   *                              Allow creating new items on double tap
15892   *                           {Boolean} editable.remove
15893   *                              Allow removing items by clicking the delete button
15894   *                              top right of a selected item.
15895   *                           {Function(item: Item, callback: Function)} onAdd
15896   *                              Callback function triggered when an item is about to be added:
15897   *                              when the user double taps an empty space in the Timeline.
15898   *                           {Function(item: Item, callback: Function)} onUpdate
15899   *                              Callback function fired when an item is about to be updated.
15900   *                              This function typically has to show a dialog where the user
15901   *                              change the item. If not implemented, nothing happens.
15902   *                           {Function(item: Item, callback: Function)} onMove
15903   *                              Fired when an item has been moved. If not implemented,
15904   *                              the move action will be accepted.
15905   *                           {Function(item: Item, callback: Function)} onRemove
15906   *                              Fired when an item is about to be deleted.
15907   *                              If not implemented, the item will be always removed.
15908   */
15909  ItemSet.prototype.setOptions = function (options) {
15910    if (options) {
15911      // copy all options that we know
15912      var fields = ['type', 'align', 'order', 'stack', 'selectable', 'multiselect', 'groupOrder', 'dataAttributes', 'template', 'groupTemplate', 'hide', 'snap', 'groupOrderSwap'];
15913      util.selectiveExtend(fields, this.options, options);
15914
15915      if ('orientation' in options) {
15916        if (typeof options.orientation === 'string') {
15917          this.options.orientation.item = options.orientation === 'top' ? 'top' : 'bottom';
15918        } else if (typeof options.orientation === 'object' && 'item' in options.orientation) {
15919          this.options.orientation.item = options.orientation.item;
15920        }
15921      }
15922
15923      if ('margin' in options) {
15924        if (typeof options.margin === 'number') {
15925          this.options.margin.axis = options.margin;
15926          this.options.margin.item.horizontal = options.margin;
15927          this.options.margin.item.vertical = options.margin;
15928        } else if (typeof options.margin === 'object') {
15929          util.selectiveExtend(['axis'], this.options.margin, options.margin);
15930          if ('item' in options.margin) {
15931            if (typeof options.margin.item === 'number') {
15932              this.options.margin.item.horizontal = options.margin.item;
15933              this.options.margin.item.vertical = options.margin.item;
15934            } else if (typeof options.margin.item === 'object') {
15935              util.selectiveExtend(['horizontal', 'vertical'], this.options.margin.item, options.margin.item);
15936            }
15937          }
15938        }
15939      }
15940
15941      if ('editable' in options) {
15942        if (typeof options.editable === 'boolean') {
15943          this.options.editable.updateTime = options.editable;
15944          this.options.editable.updateGroup = options.editable;
15945          this.options.editable.add = options.editable;
15946          this.options.editable.remove = options.editable;
15947        } else if (typeof options.editable === 'object') {
15948          util.selectiveExtend(['updateTime', 'updateGroup', 'add', 'remove'], this.options.editable, options.editable);
15949        }
15950      }
15951
15952      if ('groupEditable' in options) {
15953        if (typeof options.groupEditable === 'boolean') {
15954          this.options.groupEditable.order = options.groupEditable;
15955          this.options.groupEditable.add = options.groupEditable;
15956          this.options.groupEditable.remove = options.groupEditable;
15957        } else if (typeof options.groupEditable === 'object') {
15958          util.selectiveExtend(['order', 'add', 'remove'], this.options.groupEditable, options.groupEditable);
15959        }
15960      }
15961
15962      // callback functions
15963      var addCallback = (function (name) {
15964        var fn = options[name];
15965        if (fn) {
15966          if (!(fn instanceof Function)) {
15967            throw new Error('option ' + name + ' must be a function ' + n
15967ame + '(item, callback)');
15968          }
15969          this.options[name] = fn;
15970        }
15971      }).bind(this);
15972      ['onAdd', 'onUpdate', 'onRemove', 'onMove', 'onMoving', 'onAddGroup', 'onMoveGroup', 'onRemoveGroup'].forEach(addCallback);
15973
15974      // force the itemSet to refresh: options like orientation and margins may be changed
15975      this.markDirty();
15976    }
15977  };
15978
15979  /**
15980   * Mark the ItemSet dirty so it will refresh everything with next redraw.
15981   * Optionally, all items can be marked as dirty and be refreshed.
15982   * @param {{refreshItems: boolean}} [options]
15983   */
15984  ItemSet.prototype.markDirty = function (options) {
15985    this.groupIds = [];
15986    this.stackDirty = true;
15987
15988    if (options && options.refreshItems) {
15989      util.forEach(this.items, function (item) {
15990        item.dirty = true;
15991        if (item.displayed) item.redraw();
15992      });
15993    }
15994  };
15995
15996  /**
15997   * Destroy the ItemSet
15998   */
15999  ItemSet.prototype.destroy = function () {
16000    this.hide();
16001    this.setItems(null);
16002    this.setGroups(null);
16003
16004    this.hammer = null;
16005
16006    this.body = null;
16007    this.conversion = null;
16008  };
16009
16010  /**
16011   * Hide the component from the DOM
16012   */
16013  ItemSet.prototype.hide = function () {
16014    // remove the frame containing the items
16015    if (this.dom.frame.parentNode) {
16016      this.dom.frame.parentNode.removeChild(this.dom.frame);
16017    }
16018
16019    // remove the axis with dots
16020    if (this.dom.axis.parentNode) {
16021      this.dom.axis.parentNode.removeChild(this.dom.axis);
16022    }
16023
16024    // remove the labelset containing all group labels
16025    if (this.dom.labelSet.parentNode) {
16026      this.dom.labelSet.parentNode.removeChild(this.dom.labelSet);
16027    }
16028  };
16029
16030  /**
16031   * Show the component in the DOM (when not already visible).
16032   * @return {Boolean} changed
16033   */
16034  ItemSet.prototype.show = function () {
16035    // show frame containing the items
16036    if (!this.dom.frame.parentNode) {
16037      this.body.dom.center.appendChild(this.dom.frame);
16038    }
16039
16040    // show axis with dots
16041    if (!this.dom.axis.parentNode) {
16042      this.body.dom.backgroundVertical.appendChild(this.dom.axis);
16043    }
16044
16045    // show labelset containing labels
16046    if (!this.dom.labelSet.parentNode) {
16047      this.body.dom.left.appendChild(this.dom.labelSet);
16048    }
16049  };
16050
16051  /**
16052   * Set selected items by their id. Replaces the current selection
16053   * Unknown id's are silently ignored.
16054   * @param {string[] | string} [ids] An array with zero or more id's of the items to be
16055   *                                  selected, or a single item id. If ids is undefined
16056   *                                  or an empty array, all items will be unselected.
16057   */
16058  ItemSet.prototype.setSelection = function (ids) {
16059    var i, ii, id, item;
16060
16061    if (ids == undefined) ids = [];
16062    if (!Array.isArray(ids)) ids = [ids];
16063
16064    // unselect currently selected items
16065    for (i = 0, ii = this.selection.length; i < ii; i++) {
16066      id = this.selection[i];
16067      item = this.items[id];
16068      if (item) item.unselect();
16069    }
16070
16071    // select items
16072    this.selection = [];
16073    for (i = 0, ii = ids.length; i < ii; i++) {
16074      id = ids[i];
16075      item = this.items[id];
16076      if (item) {
16077        this.selection.push(id);
16078        item.select();
16079      }
16080    }
16081  };
16082
16083  /**
16084   * Get the selected items by their id
16085   * @return {Array} ids  The ids of the selected items
16086   */
16087  ItemSet.prototype.getSelection = function () {
16088    return this.selection.concat([]);
16089  };
16090
16091  /**
16092   * Get the id's of the currently visible items.
16093   * @returns {Array} The ids of the visible items
16094   */
16095  ItemSet.prototype.getVisibleItems = function () {
16096    var range = this.body.range.getRange();
16097    var left = this.body.util.toScreen(range.start);
16098    var right = this.body.util.toScreen(range.end);
16099
16100    var ids = [];
16101    for (var groupId in this.groups) {
16102      if (this.groups.hasOwnProperty(groupId)) {
16103        var group = this.groups[groupId];
16104        var rawVisibleItems = group.visibleItems;
16105
16106        // filter the "raw" set with visibleItems into a set which is really
16107        // visible by pixels
16108        for (var i = 0; i < rawVisibleItems.length; i++) {
16109          var item = rawVisibleItems[i];
16110          // TODO: also check whether visible vertically
16111          if (item.left < right && item.left + item.width > left) {
16112            ids.push(item.id);
16113          }
16114        }
16115      }
16116    }
16117
16118    return ids;
16119  };
16120
16121  /**
16122   * Deselect a selected item
16123   * @param {String | Number} id
16124   * @private
16125   */
16126  ItemSet.prototype._deselect = function (id) {
16127    var selection = this.selection;
16128    for (var i = 0, ii = selection.length; i < ii; i++) {
16129      if (selection[i] == id) {
16130        // non-strict comparison!
16131        selection.splice(i, 1);
16132        break;
16133      }
16134    }
16135  };
16136
16137  /**
16138   * Repaint the component
16139   * @return {boolean} Returns true if the component is resized
16140   */
16141  ItemSet.prototype.redraw = function () {
16142    var margin = this.options.margin,
16143        range = this.body.range,
16144        asSize = util.option.asSize,
16145        options = this.options,
16146        orientation = options.orientation.item,
16147        resized = false,
16148        frame = this.dom.frame;
16149
16150    // recalculate absolute position (before redrawing groups)
16151    this.props.top = this.body.domProps.top.height + this.body.domProps.border.top;
16152    this.props.left = this.body.domProps.left.width + this.body.domProps.border.left;
16153
16154    // update class name
16155    frame.className = 'vis-itemset';
16156
16157    // reorder the groups (if needed)
16158    resized = this._orderGroups() || resized;
16159
16160    // check whether zoomed (in that case we need to re-stack everything)
16161    // TODO: would be nicer to get this as a trigger from Range
16162    var visibleInterval = range.end - range.start;
16163    var zoomed = visibleInterval != this.lastVisibleInterval || this.props.width != this.props.lastWidth;
16164    if (zoomed) this.stackDirty = true;
16165    this.lastVisibleInterval = visibleInterval;
16166    this.props.lastWidth = this.props.width;
16167
16168    var restack = this.stackDirty;
16169    var firstGroup = this._firstGroup();
16170    var firstMargin = {
16171      item: margin.item,
16172      axis: margin.axis
16173    };
16174    var nonFirstMargin = {
16175      item: margin.item,
16176      axis: margin.item.vertical / 2
16177    };
16178    var height = 0;
16179    var minHeight = margin.axis + margin.item.vertical;
16180
16181    // redraw the background group
16182    this.groups[BACKGROUND].redraw(range, nonFirstMargin, restack);
16183
16184    // redraw all regular groups
16185    util.forEach(this.groups, function (group) {
16186      var groupMargin = group == firstGroup ? firstMargin : nonFirstMargin;
16187      var groupResized = group.redraw(range, groupMargin, restack);
16188      resized = groupResized || resized;
16189      height += group.height;
16190    });
16191    height = Math.max(height, minHeight);
16192    this.stackDirty = false;
16193
16194    // update frame height
16195    frame.style.height = asSize(height);
16196
16197    // calculate actual size
16198    this.props.width = frame.offsetWidth;
16199    this.props.height = height;
16200
16201    // reposition axis
16202    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);
16203    this.dom.axis.style.left = '0';
16204
16205    // check if this component is resized
16206    resized = this._isResized() || resized;
16207
16208    return resized;
16209  };
16210
16211  /**
16212   * Get the first group, aligned with the axis
16213   * @return {Group | null} firstGroup
16214   * @private
16215   */
16216  ItemSet.prototype._firstGroup = function () {
16217    var firstGroupIndex = this.options.orientation.item == 'top' ? 0 : this.groupIds.length - 1;
16218    var firstGroupId = this.groupIds[firstGroupIndex];
16219    var firstGroup = this.groups[firstGroupId] || this.groups[UNGROUPED];
16220
16221    return firstGroup || null;
16222  };
16223
16224  /**
16225   * Create or delete the group holding all ungrouped items. This group is used when
16226   * there are no groups specified.
16227   * @protected
16228   */
16229  ItemSet.prototype._updateUngrouped = function () {
16230    var ungrouped = this.groups[UNGROUPED];
16231    var background = this.groups[BACKGROUND];
16232    var item, itemId;
16233
16234    if (this.groupsData) {
16235      // remove the group holding all ungrouped items
16236      if (ungrouped) {
16237        ungrouped.hide();
16238        delete this.groups[UNGROUPED];
16239
16240        for (itemId in this.items) {
16241          if (this.items.hasOwnProperty(itemId)) {
16242            item = this.items[itemId];
16243            item.parent && item.parent.remove(item);
16244            var groupId = this._getGroupId(item.data);
16245            var group = this.groups[groupId];
16246            group && group.add(item) || item.hide();
16247          }
16248        }
16249      }
16250    } else {
16251      // create a group holding all (unfiltered) items
16252      if (!ungrouped) {
16253        var id = null;
16254        var data = null;
16255        ungrouped = new Group(id, data, this);
16256        this.groups[UNGROUPED] = ungrouped;
16257
16258        for (itemId in this.items) {
16259          if (this.items.hasOwnProperty(itemId)) {
16260            item = this.items[itemId];
16261            ungrouped.add(item);
16262          }
16263        }
16264
16265        ungrouped.show();
16266      }
16267    }
16268  };
16269
16270  /**
16271   * Get the element for the labelset
16272   * @return {HTMLElement} labelSet
16273   */
16274  ItemSet.prototype.getLabelSet = function () {
16275    return this.dom.labelSet;
16276  };
16277
16278  /**
16279   * Set items
16280   * @param {vis.DataSet | null} items
16281   */
16282  ItemSet.prototype.setItems = function (items) {
16283    var me = this,
16284        ids,
16285        oldItemsData = this.itemsData;
16286
16287    // replace the dataset
16288    if (!items) {
16289      this.itemsData = null;
16290    } else if (items instanceof DataSet || items instanceof DataView) {
16291      this.itemsData = items;
16292    } else {
16293      throw new TypeError('Data must be an instance of DataSet or DataView');
16294    }
16295
16296    if (oldItemsData) {
16297      // unsubscribe from old dataset
16298      util.forEach(this.itemListeners, function (callback, event) {
16299        oldItemsData.off(event, callback);
16300      });
16301
16302      // remove all drawn items
16303      ids = oldItemsData.getIds();
16304      this._onRemove(ids);
16305    }
16306
16307    if (this.itemsData) {
16308      // subscribe to new dataset
16309      var id = this.id;
16310      util.forEach(this.itemListeners, function (callback, event) {
16311        me.itemsData.on(event, callback, id);
16312      });
16313
16314      // add all new items
16315      ids = this.itemsData.getIds();
16316      this._onAdd(ids);
16317
16318      // update the group holding all ungrouped items
16319      this._updateUngrouped();
16320    }
16321  };
16322
16323  /**
16324   * Get the current items
16325   * @returns {vis.DataSet | null}
16326   */
16327  ItemSet.prototype.getItems = function () {
16328    return this.itemsData;
16329  };
16330
16331  /**
16332   * Set groups
16333   * @param {vis.DataSet} groups
16334   */
16335  ItemSet.prototype.setGroups = function (groups) {
16336    var me = this,
16337        ids;
16338
16339    // unsubscribe from current dataset
16340    if (this.groupsData) {
16341      util.forEach(this.groupListeners, function (callback, event) {
16342        me.groupsData.off(event, callback);
16343      });
16344
16345      // remove all drawn groups
16346      ids = this.groupsData.getIds();
16347      this.groupsData = null;
16348      this._onRemoveGroups(ids); // note: this will cause a redraw
16349    }
16350
16351    // replace the dataset
16352    if (!groups) {
16353      this.groupsData = null;
16354    } else if (groups instanceof DataSet || groups instanceof DataView) {
16355      this.groupsData = groups;
16356    } else {
16357      throw new TypeError('Data must be an instance of DataSet or DataView');
16358    }
16359
16360    if (this.groupsData) {
16361      // subscribe to new dataset
16362      var id = this.id;
16363      util.forEach(this.groupListeners, function (callback, event) {
16364        me.groupsData.on(event, callback, id);
16365      });
16366
16367      // draw all ms
16368      ids = this.groupsData.getIds();
16369      this._onAddGroups(ids);
16370    }
16371
16372    // update the group holding all ungrouped items
16373    this._updateUngrouped();
16374
16375    // update the order of all items in each group
16376    this._order();
16377
16378    this.body.emitter.emit('change', { queue: true });
16379  };
16380
16381  /**
16382   * Get the current groups
16383   * @returns {vis.DataSet | null} groups
16384   */
16385  ItemSet.prototype.getGroups = function () {
16386    return this.groupsData;
16387  };
16388
16389  /**
16390   * Remove an item by its id
16391   * @param {String | Number} id
16392   */
16393  ItemSet.prototype.removeItem = function (id) {
16394    var item = this.itemsData.get(id),
16395        dataset = this.itemsData.getDataSet();
16396
16397    if (item) {
16398      // confirm deletion
16399      this.options.onRemove(item, function (item) {
16400        if (item) {
16401          // remove by id here, it is possible that an item has no id defined
16402          // itself, so better not delete by the item itself
16403          dataset.remove(id);
16404        }
16405      });
16406    }
16407  };
16408
16409  /**
16410   * Get the time of an item based on it's data and options.type
16411   * @param {Object} itemData
16412   * @returns {string} Returns the type
16413   * @private
16414   */
16415  ItemSet.prototype._getType = function (itemData) {
16416    return itemData.type || this.options.type || (itemData.end ? 'range' : 'box');
16417  };
16418
16419  /**
16420   * Get the group id for an item
16421   * @param {Object} itemData
16422   * @returns {string} Returns the groupId
16423   * @private
16424   */
16425  ItemSet.prototype._getGroupId = function (itemData) {
16426    var type = this._getType(itemData);
16427    if (type == 'background' && itemData.group == undefined) {
16428      return BACKGROUND;
16429    } else {
16430      return this.groupsData ? itemData.group : UNGROUPED;
16431    }
16432  };
16433
16434  /**
16435   * Handle updated items
16436   * @param {Number[]} ids
16437   * @protected
16438   */
16439  ItemSet.prototype._onUpdate = function (ids) {
16440    var me = this;
16441
16442    ids.forEach((function (id) {
16443      var itemData = me.itemsData.get(id, me.itemOptions);
16444      var item = me.items[id];
16445      var type = me._getType(itemData);
16446
16447      var constructor = ItemSet.types[type];
16448      var selected;
16449
16450      if (item) {
16451        // update item
16452        if (!constructor || !(item instanceof constructor)) {
16453          // item type has changed, delete the item and recreate it
16454          selected = item.selected; // preserve selection of this item
16455          me._removeItem(item);
16456          item = null;
16457        } else {
16458          me._updateItem(item, itemData);
16459        }
16460      }
16461
16462      if (!item) {
16463        // create item
16464        if (constructor) {
16465          item = new constructor(itemData, me.conversion, me.options);
16466          item.id = id; // TODO: not so nice setting id afterwards
16467          me._addItem(item);
16468          if (selected) {
16469            this.selection.push(id);
16470            item.select();
16471          }
16472        } else if (type == 'rangeoverflow') {
16473          // TODO: deprecated since version 2.1.0 (or 3.0.0?). cleanup some day
16474          throw new TypeError('Item type "rangeoverflow" is deprecated. Use css styling instead: ' + '.vis-item.vis-range .vis-item-content {overflow: visible;}');
16475        } else {
16476          throw new TypeError('Unknown item type "' + type + '"');
16477        }
16478      }
16479    }).bind(this));
16480
16481    this._order();
16482    this.stackDirty = true; // force re-stacking of all items next redraw
16483    this.body.emitter.emit('change', { queue: true });
16484  };
16485
16486  /**
16487   * Handle added items
16488   * @param {Number[]} ids
16489   * @protected
16490   */
16491  ItemSet.prototype._onAdd = ItemSet.prototype._onUpdate;
16492
16493  /**
16494   * Handle removed items
16495   * @param {Number[]} ids
16496   * @protected
16497   */
16498  ItemSet.prototype._onRemove = function (ids) {
16499    var count = 0;
16500    var me = this;
16501    ids.forEach(function (id) {
16502      var item = me.items[id];
16503      if (item) {
16504        count++;
16505        me._removeItem(item);
16506      }
16507    });
16508
16509    if (count) {
16510      // update order
16511      this._order();
16512      this.stackDirty = true; // force re-stacking of all items next redraw
16513      this.body.emitter.emit('change', { queue: true });
16514    }
16515  };
16516
16517  /**
16518   * Update the order of item in all groups
16519   * @private
16520   */
16521  ItemSet.prototype._order = function () {
16522    // reorder the items in all groups
16523    // TODO: optimization: only reorder groups affected by the changed items
16524    util.forEach(this.groups, function (group) {
16525      group.order();
16526    });
16527  };
16528
16529  /**
16530   * Handle updated groups
16531   * @param {Number[]} ids
16532   * @private
16533   */
16534  ItemSet.prototype._onUpdateGroups = function (ids) {
16535    this._onAddGroups(ids);
16536  };
16537
16538  /**
16539   * Handle changed groups (added or updated)
16540   * @param {Number[]} ids
16541   * @private
16542   */
16543  ItemSet.prototype._onAddGroups = function (ids) {
16544    var me = this;
16545
16546    ids.forEach(function (id) {
16547      var groupData = me.groupsData.get(id);
16548      var group = me.groups[id];
16549
16550      if (!group) {
16551        // check for reserved ids
16552        if (id == UNGROUPED || id == BACKGROUND) {
16553          throw new Error('Illegal group id. ' + id + ' is a reserved id.');
16554        }
16555
16556        var groupOptions = Object.create(me.options);
16557        util.extend(groupOptions, {
16558          height: null
16559        });
16560
16561        group = new Group(id, groupData, me);
16562        me.groups[id] = group;
16563
16564        // add items with this groupId to the new group
16565        for (var itemId in me.items) {
16566          if (me.items.hasOwnProperty(itemId)) {
16567            var item = me.items[itemId];
16568            if (item.data.group == id) {
16569              group.add(item);
16570            }
16571          }
16572        }
16573
16574        group.order();
16575        group.show();
16576      } else {
16577        // update group
16578        group.setData(groupData);
16579      }
16580    });
16581
16582    this.body.emitter.emit('change', { queue: true });
16583  };
16584
16585  /**
16586   * Handle removed groups
16587   * @param {Number[]} ids
16588   * @private
16589   */
16590  ItemSet.prototype._onRemoveGroups = function (ids) {
16591    var groups = this.groups;
16592    ids.forEach(function (id) {
16593      var group = groups[id];
16594
16595      if (group) {
16596        group.hide();
16597        delete groups[id];
16598      }
16599    });
16600
16601    this.markDirty();
16602
16603    this.body.emitter.emit('change', { queue: true });
16604  };
16605
16606  /**
16607   * Reorder the groups if needed
16608   * @return {boolean} changed
16609   * @private
16610   */
16611  ItemSet.prototype._orderGroups = function () {
16612    if (this.groupsData) {
16613      // reorder the groups
16614      var groupIds = this.groupsData.getIds({
16615        order: this.options.groupOrder
16616      });
16617
16618      var changed = !util.equalArray(groupIds, this.groupIds);
16619      if (changed) {
16620        // hide all groups, removes them from the DOM
16621        var groups = this.groups;
16622        groupIds.forEach(function (groupId) {
16623          groups[groupId].hide();
16624        });
16625
16626        // show the groups again, attach them to the DOM in correct order
16627        groupIds.forEach(function (groupId) {
16628          groups[groupId].show();
16629        });
16630
16631        this.groupIds = groupIds;
16632      }
16633
16634      return changed;
16635    } else {
16636      return false;
16637    }
16638  };
16639
16640  /**
16641   * Add a new item
16642   * @param {Item} item
16643   * @private
16644   */
16645  ItemSet.prototype._addItem = function (item) {
16646    this.items[item.id] = item;
16647
16648    // add to group
16649    var groupId = this._getGroupId(item.data);
16650    var group = this.groups[groupId];
16651    if (group) group.add(item);
16652  };
16653
16654  /**
16655   * Update an existing item
16656   * @param {Item} item
16657   * @param {Object} itemData
16658   * @private
16659   */
16660  ItemSet.prototype._updateItem = function (item, itemData) {
16661    var oldGroupId = item.data.group;
16662    var oldSubGroupId = item.data.subgroup;
16663
16664    // update the items data (will redraw the item when displayed)
16665    item.setData(itemData);
16666
16667    // update group
16668    if (oldGroupId != item.data.group || oldSubGroupId != item.data.subgroup) {
16669      var oldGroup = this.groups[oldGroupId];
16670      if (oldGroup) oldGroup.remove(item);
16671
16672      var groupId = this._getGroupId(item.data);
16673      var group = this.groups[groupId];
16674      if (group) group.add(item);
16675    }
16676  };
16677
16678  /**
16679   * Delete an item from the ItemSet: remove it from the DOM, from the map
16680   * with items, and from the map with visible items, and from the selection
16681   * @param {Item} item
16682   * @private
16683   */
16684  ItemSet.prototype._removeItem = function (item) {
16685    // remove from DOM
16686    item.hide();
16687
16688    // remove from items
16689    delete this.items[item.id];
16690
16691    // remove from selection
16692    var index = this.selection.indexOf(item.id);
16693    if (index != -1) this.selection.splice(index, 1);
16694
16695    // remove from group
16696    item.parent && item.parent.remove(item);
16697  };
16698
16699  /**
16700   * Create an array containing all items being a range (having an end date)
16701   * @param array
16702   * @returns {Array}
16703   * @private
16704   */
16705  ItemSet.prototype._constructByEndArray = function (array) {
16706    var endArray = [];
16707
16708    for (var i = 0; i < array.length; i++) {
16709      if (array[i] instanceof RangeItem) {
16710        endArray.push(array[i]);
16711      }
16712    }
16713    return endArray;
16714  };
16715
16716  /**
16717   * Register the clicked item on touch, before dragStart is initiated.
16718   *
16719   * dragStart is initiated from a mousemove event, AFTER the mouse/touch is
16720   * already moving. Therefore, the mouse/touch can sometimes be above an other
16721   * DOM element than the item itself.
16722   *
16723   * @param {Event} event
16724   * @private
16725   */
16726  ItemSet.prototype._onTouch = function (event) {
16727    // store the touched item, used in _onDragStart
16728    this.touchParams.item = this.itemFromTarget(event);
16729    this.touchParams.dragLeftItem = event.target.dragLeftItem || false;
16730    this.touchParams.dragRightItem = event.target.dragRightItem || false;
16731    this.touchParams.itemProps = null;
16732  };
16733
16734  /**
16735   * Given an group id, returns the index it has.
16736   *
16737   * @param {Number} groupID
16738   * @private
16739   */
16740  ItemSet.prototype._getGroupIndex = function (groupId) {
16741    for (var i = 0; i < this.groupIds.length; i++) {
16742      if (groupId == this.groupIds[i]) return i;
16743    }
16744  };
16745
16746  /**
16747   * Start dragging the selected events
16748   * @param {Event} event
16749   * @private
16750   */
16751  ItemSet.prototype._onDragStart = function (event) {
16752    var item = this.touchParams.item || null;
16753    var me = this;
16754    var props;
16755
16756    if (item && item.selected) {
16757
16758      if (!this.options.editable.updateTime && !this.options.editable.updateGroup && !item.editable) {
16759        return;
16760      }
16761
16762      // override options.editable
16763      if (item.editable === false) {
16764        return;
16765      }
16766
16767      var dragLeftItem = this.touchParams.dragLeftItem;
16768      var dragRightItem = this.touchParams.dragRightItem;
16769
16770      if (dragLeftItem) {
16771        props = {
16772          item: dragLeftItem,
16773          initialX: event.center.x,
16774          dragLeft: true,
16775          data: this._cloneItemData(item.data)
16776        };
16777
16778        this.touchParams.itemProps = [props];
16779      } else if (dragRightItem) {
16780        props = {
16781          item: dragRightItem,
16782          initialX: event.center.x,
16783          dragRight: true,
16784          data: this._cloneItemData(item.data)
16785        };
16786
16787        this.touchParams.itemProps = [props];
16788      } else {
16789        this.touchParams.selectedItem = item;
16790
16791        var baseGroupIndex = this._getGroupIndex(item.data.group);
16792
16793        this.touchParams.itemProps = this.getSelection().map((function (id) {
16794          var item = me.items[id];
16795          var groupIndex = me._getGroupIndex(item.data.group);
16796          return {
16797            item: item,
16798            initialX: event.center.x,
16799            groupOffset: baseGroupIndex - groupIndex,
16800            data: this._cloneItemData(item.data)
16801          };
16802        }).bind(this));
16803      }
16804
16805      event.stopPropagation();
16806    } else if (this.options.editable.add && (event.srcEvent.ctrlKey || event.srcEvent.metaKey)) {
16807      // create a new range item when dragging with ctrl key down
16808      this._onDragStartAddItem(event);
16809    }
16810  };
16811
16812  /**
16813   * Start creating a new range item by dragging.
16814   * @param {Event} event
16815   * @private
16816   */
16817  ItemSet.prototype._onDragStartAddItem = function (event) {
16818    var snap = this.options.snap || null;
16819    var xAbs = util.getAbsoluteLeft(this.dom.frame);
16820    var x = event.center.x - xAbs - 10; // minus 10 to compensate for the drag starting as soon as you've moved 10px
16821    var time = this.body.util.toTime(x);
16822    var scale = this.body.util.getScale();
16823    var step = this.body.util.getStep();
16824    var start = snap ? snap(time, scale, step) : start;
16825    var end = start;
16826
16827    var itemData = {
16828      type: 'range',
16829      start: start,
16830      end: end,
16831      content: 'new item'
16832    };
16833
16834    var id = util.randomUUID();
16835    itemData[this.itemsData._fieldId] = id;
16836
16837    var group = this.groupFromTarget(event);
16838    if (group) {
16839      itemData.group = group.groupId;
16840    }
16841
16842    var newItem = new RangeItem(itemData, this.conversion, this.options);
16843    newItem.id = id; // TODO: not so nice setting id afterwards
16844    newItem.data = this._cloneItemData(itemData);
16845    this._addItem(newItem);
16846
16847    var props = {
16848      item: newItem,
16849      dragRight: true,
16850      initialX: event.center.x,
16851      data: newItem.data
16852    };
16853    this.touchParams.itemProps = [props];
16854
16855    event.stopPropagation();
16856  };
16857
16858  /**
16859   * Drag selected items
16860   * @param {Event} event
16861   * @private
16862   */
16863  ItemSet.prototype._onDrag = function (event) {
16864    if (this.touchParams.itemProps) {
16865      event.stopPropagation();
16866
16867      var me = this;
16868      var snap = this.options.snap || null;
16869      var xOffset = this.body.dom.root.offsetLeft + this.body.domProps.left.width;
16870      var scale = this.body.util.getScale();
16871      var step = this.body.util.getStep();
16872
16873      //only calculate the new group for the item that's actually dragged
16874      var selectedItem = this.touchParams.selectedItem;
16875      var updateGroupAllowed = me.options.editable.updateGroup;
16876      var newGroupBase = null;
16877      if (updateGroupAllowed && selectedItem) {
16878        if (selectedItem.data.group != undefined) {
16879          // drag from one group to another
16880          var group = me.groupFromTarget(event);
16881          if (group) {
16882            //we know the offset for all items, so the new group for all items
16883            //will be relative to this one.
16884            newGroupBase = this._getGroupIndex(group.groupId);
16885          }
16886        }
16887      }
16888
16889      // move
16890      this.touchParams.itemProps.forEach((function (props) {
16891        var current = me.body.util.toTime(event.center.x - xOffset);
16892        var initial = me.body.util.toTime(props.initialX - xOffset);
16893        var offset = current - initial; // ms
16894
16895        var itemData = this._cloneItemData(props.item.data); // clone the data
16896        if (props.item.editable === false) {
16897          return;
16898        }
16899
16900        var updateTimeAllowed = me.options.editable.updateTime || props.item.editable === true;
16901
16902        if (updateTimeAllowed) {
16903          if (props.dragLeft) {
16904            // drag left side of a range item
16905            if (itemData.start != undefined) {
16906              var initialStart = util.convert(props.data.start, 'Date');
16907              var start = new Date(initialStart.valueOf() + offset);
16908              // TODO: pass a Moment instead of a Date to snap(). (Breaking change)
16909              itemData.start = snap ? snap(start, scale, step) : start;
16910            }
16911          } else if (props.dragRight) {
16912            // drag right side of a range item
16913            if (itemData.end != undefined) {
16914              var initialEnd = util.convert(props.data.end, 'Date');
16915              var end = new Date(initialEnd.valueOf() + offset);
16916              // TODO: pass a Moment instead of a Date to snap(). (Breaking change)
16917              itemData.end = snap ? snap(end, scale, step) : end;
16918            }
16919          } else {
16920            // drag both start and end
16921            if (itemData.start != undefined) {
16922              var initialStart = util.convert(props.data.start, 'Date').valueOf();
16923              var start = new Date(initialStart + offset);
16924
16925              if (itemData.end != undefined) {
16926                var initialEnd = util.convert(props.data.end, 'Date');
16927                var duration = initialEnd.valueOf() - initialStart.valueOf();
16928
16929                // TODO: pass a Moment instead of a Date to snap(). (Breaking change)
16930                itemData.start = snap ? snap(start, scale, step) : start;
16931                itemData.end = new Date(itemData.start.valueOf() + duration);
16932              } else {
16933                // TODO: pass a Moment instead of a Date to snap(). (Breaking change)
16934                itemData.start = snap ? snap(start, scale, step) : start;
16935              }
16936            }
16937          }
16938        }
16939
16940        var updateGroupAllowed = me.options.editable.updateGroup || props.item.editable === true;
16941
16942        if (updateGroupAllowed && (!props.dragLeft && !props.dragRight) && newGroupBase != null) {
16943          if (itemData.group != undefined) {
16944            var newOffset = newGroupBase - props.groupOffset;
16945
16946            //make sure we stay in bounds
16947            newOffset = Math.max(0, newOffset);
16948            newOffset = Math.min(me.groupIds.length - 1, newOffset);
16949
16950            itemData.group = me.groupIds[newOffset];
16951          }
16952        }
16953
16954        // confirm moving the item
16955        itemData = this._cloneItemData(itemData); // convert start and end to the correct type
16956        me.options.onMoving(itemData, (function (itemData) {
16957          if (itemData) {
16958            props.item.setData(this._cloneItemData(itemData, 'Date'));
16959          }
16960        }).bind(this));
16961      }).bind(this));
16962
16963      this.stackDirty = true; // force re-stacking of all items next redraw
16964      this.body.emitter.emit('change');
16965    }
16966  };
16967
16968  /**
16969   * Move an item to another group
16970   * @param {Item} item
16971   * @param {String | Number} groupId
16972   * @private
16973   */
16974  ItemSet.prototype._moveToGroup = function (item, groupId) {
16975    var group = this.groups[groupId];
16976    if (group && group.groupId != item.data.group) {
16977      var oldGroup = item.parent;
16978      oldGroup.remove(item);
16979      oldGroup.order();
16980      group.add(item);
16981      group.order();
16982
16983      item.data.group = group.groupId;
16984    }
16985  };
16986
16987  /**
16988   * End of dragging selected items
16989   * @param {Event} event
16990   * @private
16991   */
16992  ItemSet.prototype._onDragEnd = function (event) {
16993    if (this.touchParams.itemProps) {
16994      event.stopPropagation();
16995
16996      var me = this;
16997      var dataset = this.itemsData.getDataSet();
16998      var itemProps = this.touchParams.itemProps;
16999      this.touchParams.itemProps = null;
17000
17001      itemProps.forEach((function (props) {
17002        var id = props.item.id;
17003        var exists = me.itemsData.get(id, me.itemOptions) != null;
17004
17005        if (!exists) {
17006          // add a new item
17007          me.options.onAdd(props.item.data, function (itemData) {
17008            me._removeItem(props.item); // remove temporary item
17009            if (itemData) {
17010              me.itemsData.getDataSet().add(itemData);
17011            }
17012
17013            // force re-stacking of all items next redraw
17014            me.stackDirty = true;
17015            me.body.emitter.emit('change');
17016          });
17017        } else {
17018          // update existing item
17019          var itemData = this._cloneItemData(props.item.data); // convert start and end to the correct type
17020          me.options.onMove(itemData, function (itemData) {
17021            if (itemData) {
17022              // apply changes
17023              itemData[dataset._fieldId] = id; // ensure the item contains its id (can be undefined)
17024              dataset.update(itemData);
17025            } else {
17026              // restore original values
17027              props.item.setData(props.data);
17028
17029              me.stackDirty = true; // force re-stacking of all items next redraw
17030              me.body.emitter.emit('change');
17031            }
17032          });
17033        }
17034      }).bind(this));
17035    }
17036  };
17037
17038  ItemSet.prototype._onGroupDragStart = function (event) {
17039    if (this.options.groupEditable.order) {
17040      this.groupTouchParams.group = this.groupFromTarget(event);
17041
17042      if (this.groupTouchParams.group) {
17043        event.stopPropagation();
17044
17045        this.groupTouchParams.originalOrder = this.groupsData.getIds({
17046          order: this.options.groupOrder
17047        });
17048      }
17049    }
17050  };
17051
17052  ItemSet.prototype._onGroupDrag = function (event) {
17053    if (this.options.groupEditable.order && this.groupTouchParams.group) {
17054      event.stopPropagation();
17055
17056      // drag from one group to another
17057      var group = this.groupFromTarget(event);
17058
17059      // try to avoid toggling when groups differ in height
17060      if (group && group.height != this.groupTouchParams.group.height) {
17061        var movingUp = group.top < this.groupTouchParams.group.top;
17062        var clientY = event.center ? event.center.y : event.clientY;
17063        var targetGroupTop = util.getAbsoluteTop(group.dom.foreground);
17064        var draggedGroupHeight = this.groupTouchParams.group.height;
17065        if (movingUp) {
17066          // skip swapping the groups when the dragged group is not below clientY afterwards
17067          if (targetGroupTop + draggedGroupHeight < clientY) {
17068            return;
17069          }
17070        } else {
17071          var targetGroupHeight = group.height;
17072          // skip swapping the groups when the dragged group is not below clientY afterwards
17073          if (targetGroupTop + targetGroupHeight - draggedGroupHeight > clientY) {
17074            return;
17075          }
17076        }
17077      }
17078
17079      if (group && group != this.groupTouchParams.group) {
17080        var groupsData = this.groupsData;
17081        var targetGroup = groupsData.get(group.groupId);
17082        var draggedGroup = groupsData.get(this.groupTouchParams.group.groupId);
17083
17084        // switch groups
17085        if (draggedGroup && targetGroup) {
17086          this.options.groupOrderSwap(draggedGroup, targetGroup, this.groupsData);
17087          this.groupsData.update(draggedGroup);
17088          this.groupsData.update(targetGroup);
17089        }
17090
17091        // fetch current order of groups
17092        var newOrder = this.groupsData.getIds({
17093          order: this.options.groupOrder
17094        });
17095
17096        // in case of changes since _onGroupDragStart
17097        if (!util.equalArray(newOrder, this.groupTouchParams.originalOrder)) {
17098          var groupsData = this.groupsData;
17099          var origOrder = this.groupTouchParams.originalOrder;
17100          var draggedId = this.groupTouchParams.group.groupId;
17101          var numGroups = Math.min(origOrder.length, newOrder.length);
17102          var curPos = 0;
17103          var newOffset = 0;
17104          var orgOffset = 0;
17105          while (curPos < numGroups) {
17106            // as long as the groups are where they should be step down along the groups order
17107            while (curPos + newOffset < numGroups && curPos + orgOffset < numGroups && newOrder[curPos + newOffset] == origOrder[curPos + orgOffset]) {
17108              curPos++;
17109            }
17110
17111            // all ok
17112            if (curPos + newOffset >= numGroups) {
17113              break;
17114            }
17115
17116            // not all ok
17117            // if dragged group was move upwards everything below should have an offset
17118            if (newOrder[curPos + newOffset] == draggedId) {
17119              newOffset = 1;
17120              continue;
17121            }
17122            // if dragged group was move downwards everything above should have an offset
17123            else if (origOrder[curPos + orgOffset] == draggedId) {
17124              orgOffset = 1;
17125              continue;
17126            }
17127            // found a group (apart from dragged group) that has the wrong position -> switch with the
17128            // group at the position where other one should be, fix index arrays and continue
17129            else {
17130              var slippedPosition = newOrder.indexOf(origOrder[curPos + orgOffset]);
17131              var switchGroup = groupsData.get(newOrder[curPos + newOffset]);
17132              var shouldBeGroup = groupsData.get(origOrder[curPos + orgOffset]);
17133              this.options.groupOrderSwap(switchGroup, shouldBeGroup, groupsData);
17134              groupsData.update(switchGroup);
17135              groupsData.update(shouldBeGroup);
17136
17137              var switchGroupId = newOrder[curPos + newOffset];
17138              newOrder[curPos + newOffset] = origOrder[curPos + orgOffset];
17139              newOrder[slippedPosition] = switchGroupId;
17140
17141              curPos++;
17142            }
17143          }
17144        }
17145      }
17146    }
17147  };
17148
17149  ItemSet.prototype._onGroupDragEnd = function (event) {
17150    if (this.options.groupEditable.order && this.groupTouchParams.group) {
17151      event.stopPropagation();
17152
17153      // update existing group
17154      var me = this;
17155      var id = me.groupTouchParams.group.groupId;
17156      var dataset = me.groupsData.getDataSet();
17157      var groupData = util.extend({}, dataset.get(id)); // clone the data
17158      me.options.onMoveGroup(groupData, function (groupData) {
17159        if (groupData) {
17160          // apply changes
17161          groupData[dataset._fieldId] = id; // ensure the group contains its id (can be undefined)
17162          dataset.update(groupData);
17163        } else {
17164
17165          // fetch current order of groups
17166          var newOrder = dataset.getIds({
17167            order: me.options.groupOrder
17168          });
17169
17170          // restore original order
17171          if (!util.equalArray(newOrder, me.groupTouchParams.originalOrder)) {
17172            var origOrder = me.groupTouchParams.originalOrder;
17173            var numGroups = Math.min(origOrder.length, newOrder.length);
17174            var curPos = 0;
17175            while (curPos < numGroups) {
17176              // as long as the groups are where they should be step down along the groups order
17177              while (curPos < numGroups && newOrder[curPos] == origOrder[curPos]) {
17178                curPos++;
17179              }
17180
17181              // all ok
17182              if (curPos >= numGroups) {
17183                break;
17184              }
17185
17186              // found a group that has the wrong position -> switch with the
17187              // group at the position where other one should be, fix index arrays and continue
17188              var slippedPosition = newOrder.indexOf(origOrder[curPos]);
17189              var switchGroup = dataset.get(newOrder[curPos]);
17190              var shouldBeGroup = dataset.get(origOrder[curPos]);
17191              me.options.groupOrderSwap(switchGroup, shouldBeGroup, dataset);
17192              groupsData.update(switchGroup);
17193              groupsData.update(shouldBeGroup);
17194
17195              var switchGroupId = newOrder[curPos];
17196              newOrder[curPos] = origOrder[curPos];
17197              newOrder[slippedPosition] = switchGroupId;
17198
17199              curPos++;
17200            }
17201          }
17202        }
17203      });
17204
17205      me.body.emitter.emit('groupDragged', { groupId: id });
17206    }
17207  };
17208
17209  /**
17210   * Handle selecting/deselecting an item when tapping it
17211   * @param {Event} event
17212   * @private
17213   */
17214  ItemSet.prototype._onSelectItem = function (event) {
17215    if (!this.options.selectable) return;
17216
17217    var ctrlKey = event.srcEvent && (event.srcEvent.ctrlKey || event.srcEvent.metaKey);
17218    var shiftKey = event.srcEvent && event.srcEvent.shiftKey;
17219    if (ctrlKey || shiftKey) {
17220      this._onMultiSelectItem(event);
17221      return;
17222    }
17223
17224    var oldSelection = this.getSelection();
17225
17226    var item = this.itemFromTarget(event);
17227    var selection = item ? [item.id] : [];
17228    this.setSelection(selection);
17229
17230    var newSelection = this.getSelection();
17231
17232    // emit a select event,
17233    // except when old selection is empty and new selection is still empty
17234    if (newSelection.length > 0 || oldSelection.length > 0) {
17235      this.body.emitter.emit('select', {
17236        items: newSelection,
17237        event: event
17238      });
17239    }
17240  };
17241
17242  /**
17243   * Handle creation and updates of an item on double tap
17244   * @param event
17245   * @private
17246   */
17247  ItemSet.prototype._onAddItem = function (event) {
17248    if (!this.options.selectable) return;
17249    if (!this.options.editable.add) return;
17250
17251    var me = this;
17252    var snap = this.options.snap || null;
17253    var item = this.itemFromTarget(event);
17254
17255    event.stopPropagation();
17256
17257    if (item) {
17258      // update item
17259
17260      // execute async handler to update the item (or cancel it)
17261      var itemData = me.itemsData.get(item.id); // get a clone of the data from the dataset
17262      this.options.onUpdate(itemData, function (itemData) {
17263        if (itemData) {
17264          me.itemsData.getDataSet().update(itemData);
17265        }
17266      });
17267    } else {
17268      // add item
17269      var xAbs = util.getAbsoluteLeft(this.dom.frame);
17270      var x = event.center.x - xAbs;
17271      var start = this.body.util.toTime(x);
17272      var scale = this.body.util.getScale();
17273      var step = this.body.util.getStep();
17274
17275      var newItemData = {
17276        start: snap ? snap(start, scale, step) : start,
17277        content: 'new item'
17278      };
17279
17280      // when default type is a range, add a default end date to the new item
17281      if (this.options.type === 'range') {
17282        var end = this.body.util.toTime(x + this.props.width / 5);
17283        newItemData.end = snap ? snap(end, scale, step) : end;
17284      }
17285
17286      newItemData[this.itemsData._fieldId] = util.randomUUID();
17287
17288      var group = this.groupFromTarget(event);
17289      if (group) {
17290        newItemData.group = group.groupId;
17291      }
17292
17293      // execute async handler to customize (or cancel) adding an item
17294      newItemData = this._cloneItemData(newItemData); // convert start and end to the correct type
17295      this.options.onAdd(newItemData, function (item) {
17296        if (item) {
17297          me.itemsData.getDataSet().add(item);
17298          // TODO: need to trigger a redraw?
17299        }
17300      });
17301    }
17302  };
17303
17304  /**
17305   * Handle selecting/deselecting multiple items when holding an item
17306   * @param {Event} event
17307   * @private
17308   */
17309  ItemSet.prototype._onMultiSelectItem = function (event) {
17310    if (!this.options.selectable) return;
17311
17312    var item = this.itemFromTarget(event);
17313
17314    if (item) {
17315      // multi select items (if allowed)
17316
17317      var selection = this.options.multiselect ? this.getSelection() // take current selection
17318      : []; // deselect current selection
17319
17320      var shiftKey = event.srcEvent && event.srcEvent.shiftKey || false;
17321
17322      if (shiftKey && this.options.multiselect) {
17323        // select all items between the old selection and the tapped item
17324
17325        // determine the selection range
17326        selection.push(item.id);
17327        var range = ItemSet._getItemRange(this.itemsData.get(selection, this.itemOptions));
17328
17329        // select all items within the selection range
17330        selection = [];
17331        for (var id in this.items) {
17332          if (this.items.hasOwnProperty(id)) {
17333            var _item = this.items[id];
17334            var start = _item.data.start;
17335            var end = _item.data.end !== undefined ? _item.data.end : start;
17336
17337            if (start >= range.min && end <= range.max && !(_item instanceof BackgroundItem)) {
17338              selection.push(_item.id); // do not use id but item.id, id itself is stringified
17339            }
17340          }
17341        }
17342      } else {
17343        // add/remove this item from the current selection
17344        var index = selection.indexOf(item.id);
17345        if (index == -1) {
17346          // item is not yet selected -> select it
17347          selection.push(item.id);
17348        } else {
17349          // item is already selected -> deselect it
17350          selection.splice(index, 1);
17351        }
17352      }
17353
17354      this.setSelection(selection);
17355
17356      this.body.emitter.emit('select', {
17357        items: this.getSelection(),
17358        event: event
17359      });
17360    }
17361  };
17362
17363  /**
17364   * Calculate the time range of a list of items
17365   * @param {Array.<Object>} itemsData
17366   * @return {{min: Date, max: Date}} Returns the range of the provided items
17367   * @private
17368   */
17369  ItemSet._getItemRange = function (itemsData) {
17370    var max = null;
17371    var min = null;
17372
17373    itemsData.forEach(function (data) {
17374      if (min == null || data.start < min) {
17375        min = data.start;
17376      }
17377
17378      if (data.end != undefined) {
17379        if (max == null || data.end > max) {
17380          max = data.end;
17381        }
17382      } else {
17383        if (max == null || data.start > max) {
17384          max = data.start;
17385        }
17386      }
17387    });
17388
17389    return {
17390      min: min,
17391      max: max
17392    };
17393  };
17394
17395  /**
17396   * Find an item from an event target:
17397   * searches for the attribute 'timeline-item' in the event target's element tree
17398   * @param {Event} event
17399   * @return {Item | null} item
17400   */
17401  ItemSet.prototype.itemFromTarget = function (event) {
17402    var target = event.target;
17403    while (target) {
17404      if (target.hasOwnProperty('timeline-item')) {
17405        return target['timeline-item'];
17406      }
17407      target = target.parentNode;
17408    }
17409
17410    return null;
17411  };
17412
17413  /**
17414   * Find the Group from an event target:
17415   * searches for the attribute 'timeline-group' in the event target's element tree
17416   * @param {Event} event
17417   * @return {Group | null} group
17418   */
17419  ItemSet.prototype.groupFromTarget = function (event) {
17420    var clientY = event.center ? event.center.y : event.clientY;
17421    for (var i = 0; i < this.groupIds.length; i++) {
17422      var groupId = this.groupIds[i];
17423      var group = this.groups[groupId];
17424      var foreground = group.dom.foreground;
17425      var top = util.getAbsoluteTop(foreground);
17426      if (clientY > top && clientY < top + foreground.offsetHeight) {
17427        return group;
17428      }
17429
17430      if (this.options.orientation.item === 'top') {
17431        if (i === this.groupIds.length - 1 && clientY > top) {
17432          return group;
17433        }
17434      } else {
17435        if (i === 0 && clientY < top + foreground.offset) {
17436          return group;
17437        }
17438      }
17439    }
17440
17441    return null;
17442  };
17443
17444  /**
17445   * Find the ItemSet from an event target:
17446   * searches for the attribute 'timeline-itemset' in the event target's element tree
17447   * @param {Event} event
17448   * @return {ItemSet | null} item
17449   */
17450  ItemSet.itemSetFromTarget = function (event) {
17451    var target = event.target;
17452    while (target) {
17453      if (target.hasOwnProperty('timeline-itemset')) {
17454        return target['timeline-itemset'];
17455      }
17456      target = target.parentNode;
17457    }
17458
17459    return null;
17460  };
17461
17462  /**
17463   * Clone the data of an item, and "normalize" it: convert the start and end date
17464   * to the type (Date, Moment, ...) configured in the DataSet. If not configured,
17465   * start and end are converted to Date.
17466   * @param {Object} itemData, typically `item.data`
17467   * @param {string} [type]  Optional Date type. If not provided, the type from the DataSet is taken
17468   * @return {Object} The cloned object
17469   * @private
17470   */
17471  ItemSet.prototype._cloneItemData = function (itemData, type) {
17472    var clone = util.extend({}, itemData);
17473
17474    if (!type) {
17475      // convert start and end date to the type (Date, Moment, ...) configured in the DataSet
17476      type = this.itemsData.getDataSet()._options.type;
17477    }
17478
17479    if (clone.start != undefined) {
17480      clone.start = util.convert(clone.start, type && type.start || 'Date');
17481    }
17482    if (clone.end != undefined) {
17483      clone.end = util.convert(clone.end, type && type.end || 'Date');
17484    }
17485
17486    return clone;
17487  };
17488
17489  module.exports = ItemSet;
17490
17491/***/ },
17492/* 30 */
17493/***/ function(module, exports, __webpack_require__) {
17494
17495  'use strict';
17496
17497  var moment = __webpack_require__(2);
17498  var DateUtil = __webpack_require__(27);
17499  var util = __webpack_require__(1);
17500
17501  /**
17502   * @constructor  TimeStep
17503   * The class TimeStep is an iterator for dates. You provide a start date and an
17504   * end date. The class itself determines the best scale (step size) based on the
17505   * provided start Date, end Date, and minimumStep.
17506   *
17507   * If minimumStep is provided, the step size is chosen as close as possible
17508   * to the minimumStep but larger than minimumStep. If minimumStep is not
17509   * provided, the scale is set to 1 DAY.
17510   * The minimumStep should correspond with the onscreen size of about 6 characters
17511   *
17512   * Alternatively, you can set a scale by hand.
17513   * After creation, you can initialize the class by executing first(). Then you
17514   * can iterate from the start date to the end date via next(). You can check if
17515   * the end date is reached with the function hasNext(). After each step, you can
17516   * retrieve the current date via getCurrent().
17517   * The TimeStep has scales ranging from milliseconds, seconds, minutes, hours,
17518   * days, to years.
17519   *
17520   * Version: 1.2
17521   *
17522   * @param {Date} [start]         The start date, for example new Date(2010, 9, 21)
17523   *                               or new Date(2010, 9, 21, 23, 45, 00)
17524   * @param {Date} [end]           The end date
17525   * @param {Number} [minimumStep] Optional. Minimum step size in milliseconds
17526   */
17527  function TimeStep(start, end, minimumStep, hiddenDates) {
17528    this.moment = moment;
17529
17530    // variables
17531    this.current = this.moment();
17532    this._start = this.moment();
17533    this._end = this.moment();
17534
17535    this.autoScale = true;
17536    this.scale = 'day';
17537    this.step = 1;
17538
17539    // initialize the range
17540    this.setRange(start, end, minimumStep);
17541
17542    // hidden Dates options
17543    this.switchedDay = false;
17544    this.switchedMonth = false;
17545    this.switchedYear = false;
17546    if (Array.isArray(hiddenDates)) {
17547      this.hiddenDates = hiddenDates;
17548    } else if (hiddenDates != undefined) {
17549      this.hiddenDates = [hiddenDates];
17550    } else {
17551      this.hiddenDates = [];
17552    }
17553
17554    this.format = TimeStep.FORMAT; // default formatting
17555  }
17556
17557  // Time formatting
17558  TimeStep.FORMAT = {
17559    minorLabels: {
17560      millisecond: 'SSS',
17561      second: 's',
17562      minute: 'HH:mm',
17563      hour: 'HH:mm',
17564      weekday: 'ddd D',
17565      day: 'D',
17566      month: 'MMM',
17567      year: 'YYYY'
17568    },
17569    majorLabels: {
17570      millisecond: 'HH:mm:ss',
17571      second: 'D MMMM HH:mm',
17572      minute: 'ddd D MMMM',
17573      hour: 'ddd D MMMM',
17574      weekday: 'MMMM YYYY',
17575      day: 'MMMM YYYY',
17576      month: 'YYYY',
17577      year: ''
17578    }
17579  };
17580
17581  /**
17582   * Set custom constructor function for moment. Can be used to set dates
17583   * to UTC or to set a utcOffset.
17584   * @param {function} moment
17585   */
17586  TimeStep.prototype.setMoment = function (moment) {
17587    this.moment = moment;
17588
17589    // update the date properties, can have a new utcOffset
17590    this.current = this.moment(this.current);
17591    this._start = this.moment(this._start);
17592    this._end = this.moment(this._end);
17593  };
17594
17595  /**
17596   * Set custom formatting for the minor an major labels of the TimeStep.
17597   * Both `minorLabels` and `majorLabels` are an Object with properties:
17598   * 'millisecond', 'second', 'minute', 'hour', 'weekday', 'day', 'month', 'year'.
17599   * @param {{minorLabels: Object, majorLabels: Object}} format
17600   */
17601  TimeStep.prototype.setFormat = function (format) {
17602    var defaultFormat = util.deepExtend({}, TimeStep.FORMAT);
17603    this.format = util.deepExtend(defaultFormat, format);
17604  };
17605
17606  /**
17607   * Set a new range
17608   * If minimumStep is provided, the step size is chosen as close as possible
17609   * to the minimumStep but larger than minimumStep. If minimumStep is not
17610   * provided, the scale is set to 1 DAY.
17611   * The minimumStep should correspond with the onscreen size of about 6 characters
17612   * @param {Date} [start]      The start date and time.
17613   * @param {Date} [end]        The end date and time.
17614   * @param {int} [minimumStep] Optional. Minimum step size in milliseconds
17615   */
17616  TimeStep.prototype.setRange = function (start, end, minimumStep) {
17617    if (!(start instanceof Date) || !(end instanceof Date)) {
17618      throw 'No legal start or end date in method setRange';
17619    }
17620
17621    this._start = start != undefined ? this.moment(start.valueOf()) : new Date();
17622    this._end = end != undefined ? this.moment(end.valueOf()) : new Date();
17623
17624    if (this.autoScale) {
17625      this.setMinimumStep(minimumStep);
17626    }
17627  };
17628
17629  /**
17630   * Set the range iterator to the start date.
17631   */
17632  TimeStep.prototype.start = function () {
17633    this.current = this._start.clone();
17634    this.roundToMinor();
17635  };
17636
17637  /**
17638   * Round the current date to the first minor date value
17639   * This must be executed once when the current date is set to start Date
17640   */
17641  TimeStep.prototype.roundToMinor = function () {
17642    // round to floor
17643    // IMPORTANT: we have no breaks in this switch! (this is no bug)
17644    // noinspection FallThroughInSwitchStatementJS
17645    switch (this.scale) {
17646      case 'year':
17647        this.current.year(this.step * Math.floor(this.current.year() / this.step));
17648        this.current.month(0);
17649      case 'month':
17650        this.current.date(1);
17651      case 'day': // intentional fall through
17652      case 'weekday':
17653        this.current.hours(0);
17654      case 'hour':
17655        this.current.minutes(0);
17656      case 'minute':
17657        this.current.seconds(0);
17658      case 'second':
17659        this.current.milliseconds(0);
17660        //case 'millisecond': // nothing to do for milliseconds
17661    }
17662
17663    if (this.step != 1) {
17664      // round down to the first minor value that is a multiple of the current step size
17665      switch (this.scale) {
17666        case 'millisecond':
17667          this.current.subtract(this.current.milliseconds() % this.step, 'milliseconds');break;
17668        case 'second':
17669          this.current.subtract(this.current.seconds() % this.step, 'seconds');break;
17670        case 'minute':
17671          this.current.subtract(this.current.minutes() % this.step, 'minutes');break;
17672        case 'hour':
17673          this.current.subtract(this.current.hours() % this.step, 'hours');break;
17674        case 'weekday': // intentional fall through
17675        case 'day':
17676          this.current.subtract((this.current.date() - 1) % this.step, 'day');break;
17677        case 'month':
17678          this.current.subtract(this.current.month() % this.step, 'month');break;
17679        case 'year':
17680          this.current.subtract(this.current.year() % this.step, 'year');break;
17681        default:
17682          break;
17683      }
17684    }
17685  };
17686
17687  /**
17688   * Check if the there is a next step
17689   * @return {boolean}  true if the current date has not passed the end date
17690   */
17691  TimeStep.prototype.hasNext = function () {
17692    return this.current.valueOf() <= this._end.valueOf();
17693  };
17694
17695  /**
17696   * Do the next step
17697   */
17698  TimeStep.prototype.next = function () {
17699    var prev = this.current.valueOf();
17700
17701    // Two cases, needed to prevent issues with switching daylight savings
17702    // (end of March and end of October)
17703    if (this.current.month() < 6) {
17704      switch (this.scale) {
17705        case 'millisecond':
17706          this.current.add(this.step, 'millisecond');break;
17707        case 'second':
17708          this.current.add(this.step, 'second');break;
17709        case 'minute':
17710          this.current.add(this.step, 'minute');break;
17711        case 'hour':
17712          this.current.add(this.step, 'hour');
17713          // in case of skipping an hour for daylight savings, adjust the hour again (else you get: 0h 5h 9h ... instead of 0h 4h 8h ...)
17714          // TODO: is this still needed now we use the function of moment.js?
17715          this.current.subtract(this.current.hours() % this.step, 'hour');
17716          break;
17717        case 'weekday': // intentional fall through
17718        case 'day':
17719          this.current.add(this.step, 'day');break;
17720        case 'month':
17721          this.current.add(this.step, 'month');break;
17722        case 'year':
17723          this.current.add(this.step, 'year');break;
17724        default:
17725          break;
17726      }
17727    } else {
17728      switch (this.scale) {
17729        case 'millisecond':
17730          this.current.add(this.step, 'millisecond');break;
17731        case 'second':
17732          this.current.add(this.step, 'second');break;
17733        case 'minute':
17734          this.current.add(this.step, 'minute');break;
17735        case 'hour':
17736          this.current.add(this.step, 'hour');break;
17737        case 'weekday': // intentional fall through
17738        case 'day':
17739          this.current.add(this.step, 'day');break;
17740        case 'month':
17741          this.current.add(this.step, 'month');break;
17742        case 'year':
17743          this.current.add(this.step, 'year');break;
17744        default:
17745          break;
17746      }
17747    }
17748
17749    if (this.step != 1) {
17750      // round down to the correct major value
17751      switch (this.scale) {
17752        case 'millisecond':
17753          if (this.current.milliseconds() < this.step) this.current.milliseconds(0);break;
17754        case 'second':
17755          if (this.current.seconds() < this.step) this.current.seconds(0);break;
17756        case 'minute':
17757          if (this.current.minutes() < this.step) this.current.minutes(0);break;
17758        case 'hour':
17759          if (this.current.hours() < this.step) this.current.hours(0);break;
17760        case 'weekday': // intentional fall through
17761        case 'day':
17762          if (this.current.date() < this.step + 1) this.current.date(1);break;
17763        case 'month':
17764          if (this.current.month() < this.step) this.current.month(0);break;
17765        case 'year':
17766          break; // nothing to do for year
17767        default:
17768          break;
17769      }
17770    }
17771
17772    // safety mechanism: if current time is still unchanged, move to the end
17773    if (this.current.valueOf() == prev) {
17774      this.current = this._end.clone();
17775    }
17776
17777    DateUtil.stepOverHiddenDates(this.moment, this, prev);
17778  };
17779
17780  /**
17781   * Get the current datetime
17782   * @return {Moment}  current The current date
17783   */
17784  TimeStep.prototype.getCurrent = function () {
17785    return this.current;
17786  };
17787
17788  /**
17789   * Set a custom scale. Autoscaling will be disabled.
17790   * For example setScale('minute', 5) will result
17791   * in minor steps of 5 minutes, and major steps of an hour.
17792   *
17793   * @param {{scale: string, step: number}} params
17794   *                               An object containing two properties:
17795   *                               - A string 'scale'. Choose from 'millisecond', 'second',
17796   *                                 'minute', 'hour', 'weekday', 'day', 'month', 'year'.
17797   *                               - A number 'step'. A step size, by default 1.
17798   *                                 Choose for example 1, 2, 5, or 10.
17799   */
17800  TimeStep.prototype.setScale = function (params) {
17801    if (params && typeof params.scale == 'string') {
17802      this.scale = params.scale;
17803      this.step = params.step > 0 ? params.step : 1;
17804      this.autoScale = false;
17805    }
17806  };
17807
17808  /**
17809   * Enable or disable autoscaling
17810   * @param {boolean} enable  If true, autoascaling is set true
17811   */
17812  TimeStep.prototype.setAutoScale = function (enable) {
17813    this.autoScale = enable;
17814  };
17815
17816  /**
17817   * Automatically determine the scale that bests fits the provided minimum step
17818   * @param {Number} [minimumStep]  The minimum step size in milliseconds
17819   */
17820  TimeStep.prototype.setMinimumStep = function (minimumStep) {
17821    if (minimumStep == undefined) {
17822      return;
17823    }
17824
17825    //var b = asc + ds;
17826
17827    var stepYear = 1000 * 60 * 60 * 24 * 30 * 12;
17828    var stepMonth = 1000 * 60 * 60 * 24 * 30;
17829    var stepDay = 1000 * 60 * 60 * 24;
17830    var stepHour = 1000 * 60 * 60;
17831    var stepMinute = 1000 * 60;
17832    var stepSecond = 1000;
17833    var stepMillisecond = 1;
17834
17835    // find the smallest step that is larger than the provided minimumStep
17836    if (stepYear * 1000 > minimumStep) {
17837      this.scale = 'year';this.step = 1000;
17838    }
17839    if (stepYear * 500 > minimumStep) {
17840      this.scale = 'year';this.step = 500;
17841    }
17842    if (stepYear * 100 > minimumStep) {
17843      this.scale = 'year';this.step = 100;
17844    }
17845    if (stepYear * 50 > minimumStep) {
17846      this.scale = 'year';this.step = 50;
17847    }
17848    if (stepYear * 10 > minimumStep) {
17849      this.scale = 'year';this.step = 10;
17850    }
17851    if (stepYear * 5 > minimumStep) {
17852      this.scale = 'year';this.step = 5;
17853    }
17854    if (stepYear > minimumStep) {
17855      this.scale = 'year';this.step = 1;
17856    }
17857    if (stepMonth * 3 > minimumStep) {
17858      this.scale = 'month';this.step = 3;
17859    }
17860    if (stepMonth > minimumStep) {
17861      this.scale = 'month';this.step = 1;
17862    }
17863    if (stepDay * 5 > minimumStep) {
17864      this.scale = 'day';this.step = 5;
17865    }
17866    if (stepDay * 2 > minimumStep) {
17867      this.scale = 'day';this.step = 2;
17868    }
17869    if (stepDay > minimumStep) {
17870      this.scale = 'day';this.step = 1;
17871    }
17872    if (stepDay / 2 > minimumStep) {
17873      this.scale = 'weekday';this.step = 1;
17874    }
17875    if (stepHour * 4 > minimumStep) {
17876      this.scale = 'hour';this.step = 4;
17877    }
17878    if (stepHour > minimumStep) {
17879      this.scale = 'hour';this.step = 1;
17880    }
17881    if (stepMinute * 15 > minimumStep) {
17882      this.scale = 'minute';this.step = 15;
17883    }
17884    if (stepMinute * 10 > minimumStep) {
17885      this.scale = 'minute';this.step = 10;
17886    }
17887    if (stepMinute * 5 > minimumStep) {
17888      this.scale = 'minute';this.step = 5;
17889    }
17890    if (stepMinute > minimumStep) {
17891      this.scale = 'minute';this.step = 1;
17892    }
17893    if (stepSecond * 15 > minimumStep) {
17894      this.scale = 'second';this.step = 15;
17895    }
17896    if (stepSecond * 10 > minimumStep) {
17897      this.scale = 'second';this.step = 10;
17898    }
17899    if (stepSecond * 5 > minimumStep) {
17900      this.scale = 'second';this.step = 5;
17901    }
17902    if (stepSecond > minimumStep) {
17903      this.scale = 'second';this.step = 1;
17904    }
17905    if (stepMillisecond * 200 > minimumStep) {
17906      this.scale = 'millisecond';this.step = 200;
17907    }
17908    if (stepMillisecond * 100 > minimumStep) {
17909      this.scale = 'millisecond';this.step = 100;
17910    }
17911    if (stepMillisecond * 50 > minimumStep) {
17912      this.scale = 'millisecond';this.step = 50;
17913    }
17914    if (stepMillisecond * 10 > minimumStep) {
17915      this.scale = 'millisecond';this.step = 10;
17916    }
17917    if (stepMillisecond * 5 > minimumStep) {
17918      this.scale = 'millisecond';this.step = 5;
17919    }
17920    if (stepMillisecond > minimumStep) {
17921      this.scale = 'millisecond';this.step = 1;
17922    }
17923  };
17924
17925  /**
17926   * Snap a date to a rounded value.
17927   * The snap intervals are dependent on the current scale and step.
17928   * Static function
17929   * @param {Date} date    the date to be snapped.
17930   * @param {string}
17930 scale Current scale, can be 'millisecond', 'second',
17931   *                       'minute', 'hour', 'weekday, 'day', 'month', 'year'.
17932   * @param {number} step  Current step (1, 2, 4, 5, ...
17933   * @return {Date} snappedDate
17934   */
17935  TimeStep.snap = function (date, scale, step) {
17936    var clone = moment(date);
17937
17938    if (scale == 'year') {
17939      var year = clone.year() + Math.round(clone.month() / 12);
17940      clone.year(Math.round(year / step) * step);
17941      clone.month(0);
17942      clone.date(0);
17943      clone.hours(0);
17944      clone.minutes(0);
17945      clone.seconds(0);
17946      clone.milliseconds(0);
17947    } else if (scale == 'month') {
17948      if (clone.date() > 15) {
17949        clone.date(1);
17950        clone.add(1, 'month');
17951        // important: first set Date to 1, after that change the month.
17952      } else {
17953        clone.date(1);
17954      }
17955
17956      clone.hours(0);
17957      clone.minutes(0);
17958      clone.seconds(0);
17959      clone.milliseconds(0);
17960    } else if (scale == 'day') {
17961      //noinspection FallthroughInSwitchStatementJS
17962      switch (step) {
17963        case 5:
17964        case 2:
17965          clone.hours(Math.round(clone.hours() / 24) * 24);break;
17966        default:
17967          clone.hours(Math.round(clone.hours() / 12) * 12);break;
17968      }
17969      clone.minutes(0);
17970      clone.seconds(0);
17971      clone.milliseconds(0);
17972    } else if (scale == 'weekday') {
17973      //noinspection FallthroughInSwitchStatementJS
17974      switch (step) {
17975        case 5:
17976        case 2:
17977          clone.hours(Math.round(clone.hours() / 12) * 12);break;
17978        default:
17979          clone.hours(Math.round(clone.hours() / 6) * 6);break;
17980      }
17981      clone.minutes(0);
17982      clone.seconds(0);
17983      clone.milliseconds(0);
17984    } else if (scale == 'hour') {
17985      switch (step) {
17986        case 4:
17987          clone.minutes(Math.round(clone.minutes() / 60) * 60);break;
17988        default:
17989          clone.minutes(Math.round(clone.minutes() / 30) * 30);break;
17990      }
17991      clone.seconds(0);
17992      clone.milliseconds(0);
17993    } else if (scale == 'minute') {
17994      //noinspection FallthroughInSwitchStatementJS
17995      switch (step) {
17996        case 15:
17997        case 10:
17998          clone.minutes(Math.round(clone.minutes() / 5) * 5);
17999          clone.seconds(0);
18000          break;
18001        case 5:
18002          clone.seconds(Math.round(clone.seconds() / 60) * 60);break;
18003        default:
18004          clone.seconds(Math.round(clone.seconds() / 30) * 30);break;
18005      }
18006      clone.milliseconds(0);
18007    } else if (scale == 'second') {
18008      //noinspection FallthroughInSwitchStatementJS
18009      switch (step) {
18010        case 15:
18011        case 10:
18012          clone.seconds(Math.round(clone.seconds() / 5) * 5);
18013          clone.milliseconds(0);
18014          break;
18015        case 5:
18016          clone.milliseconds(Math.round(clone.milliseconds() / 1000) * 1000);break;
18017        default:
18018          clone.milliseconds(Math.round(clone.milliseconds() / 500) * 500);break;
18019      }
18020    } else if (scale == 'millisecond') {
18021      var _step = step > 5 ? step / 2 : 1;
18022      clone.milliseconds(Math.round(clone.milliseconds() / _step) * _step);
18023    }
18024
18025    return clone;
18026  };
18027
18028  /**
18029   * Check if the current value is a major value (for example when the step
18030   * is DAY, a major value is each first day of the MONTH)
18031   * @return {boolean} true if current date is major, else false.
18032   */
18033  TimeStep.prototype.isMajor = function () {
18034    if (this.switchedYear == true) {
18035      this.switchedYear = false;
18036      switch (this.scale) {
18037        case 'year':
18038        case 'month':
18039        case 'weekday':
18040        case 'day':
18041        case 'hour':
18042        case 'minute':
18043        case 'second':
18044        case 'millisecond':
18045          return true;
18046        default:
18047          return false;
18048      }
18049    } else if (this.switchedMonth == true) {
18050      this.switchedMonth = false;
18051      switch (this.scale) {
18052        case 'weekday':
18053        case 'day':
18054        case 'hour':
18055        case 'minute':
18056        case 'second':
18057        case 'millisecond':
18058          return true;
18059        default:
18060          return false;
18061      }
18062    } else if (this.switchedDay == true) {
18063      this.switchedDay = false;
18064      switch (this.scale) {
18065        case 'millisecond':
18066        case 'second':
18067        case 'minute':
18068        case 'hour':
18069          return true;
18070        default:
18071          return false;
18072      }
18073    }
18074
18075    var date = this.moment(this.current);
18076    switch (this.scale) {
18077      case 'millisecond':
18078        return date.milliseconds() == 0;
18079      case 'second':
18080        return date.seconds() == 0;
18081      case 'minute':
18082        return date.hours() == 0 && date.minutes() == 0;
18083      case 'hour':
18084        return date.hours() == 0;
18085      case 'weekday': // intentional fall through
18086      case 'day':
18087        return date.date() == 1;
18088      case 'month':
18089        return date.month() == 0;
18090      case 'year':
18091        return false;
18092      default:
18093        return false;
18094    }
18095  };
18096
18097  /**
18098   * Returns formatted text for the minor axislabel, depending on the current
18099   * date and the scale. For example when scale is MINUTE, the current time is
18100   * formatted as "hh:mm".
18101   * @param {Date} [date] custom date. if not provided, current date is taken
18102   */
18103  TimeStep.prototype.getLabelMinor = function (date) {
18104    if (date == undefined) {
18105      date = this.current;
18106    }
18107
18108    var format = this.format.minorLabels[this.scale];
18109    return format && format.length > 0 ? this.moment(date).format(format) : '';
18110  };
18111
18112  /**
18113   * Returns formatted text for the major axis label, depending on the current
18114   * date and the scale. For example when scale is MINUTE, the major scale is
18115   * hours, and the hour will be formatted as "hh".
18116   * @param {Date} [date] custom date. if not provided, current date is taken
18117   */
18118  TimeStep.prototype.getLabelMajor = function (date) {
18119    if (date == undefined) {
18120      date = this.current;
18121    }
18122
18123    var format = this.format.majorLabels[this.scale];
18124    return format && format.length > 0 ? this.moment(date).format(format) : '';
18125  };
18126
18127  TimeStep.prototype.getClassName = function () {
18128    var _moment = this.moment;
18129    var m = this.moment(this.current);
18130    var current = m.locale ? m.locale('en') : m.lang('en'); // old versions of moment have .lang() function
18131    var step = this.step;
18132
18133    function even(value) {
18134      return value / step % 2 == 0 ? ' vis-even' : ' vis-odd';
18135    }
18136
18137    function today(date) {
18138      if (date.isSame(new Date(), 'day')) {
18139        return ' vis-today';
18140      }
18141      if (date.isSame(_moment().add(1, 'day'), 'day')) {
18142        return ' vis-tomorrow';
18143      }
18144      if (date.isSame(_moment().add(-1, 'day'), 'day')) {
18145        return ' vis-yesterday';
18146      }
18147      return '';
18148    }
18149
18150    function currentWeek(date) {
18151      return date.isSame(new Date(), 'week') ? ' vis-current-week' : '';
18152    }
18153
18154    function currentMonth(date) {
18155      return date.isSame(new Date(), 'month') ? ' vis-current-month' : '';
18156    }
18157
18158    function currentYear(date) {
18159      return date.isSame(new Date(), 'year') ? ' vis-current-year' : '';
18160    }
18161
18162    switch (this.scale) {
18163      case 'millisecond':
18164        return even(current.milliseconds()).trim();
18165
18166      case 'second':
18167        return even(current.seconds()).trim();
18168
18169      case 'minute':
18170        return even(current.minutes()).trim();
18171
18172      case 'hour':
18173        var hours = current.hours();
18174        if (this.step == 4) {
18175          hours = hours + '-h' + (hours + 4);
18176        }
18177        return 'vis-h' + hours + today(current) + even(current.hours());
18178
18179      case 'weekday':
18180        return 'vis-' + current.format('dddd').toLowerCase() + today(current) + currentWeek(current) + even(current.date());
18181
18182      case 'day':
18183        var day = current.date();
18184        var month = current.format('MMMM').toLowerCase();
18185        return 'vis-day' + day + ' vis-' + month + currentMonth(current) + even(day - 1);
18186
18187      case 'month':
18188        return 'vis-' + current.format('MMMM').toLowerCase() + currentMonth(current) + even(current.month());
18189
18190      case 'year':
18191        var year = current.year();
18192        return 'vis-year' + year + currentYear(current) + even(year);
18193
18194      default:
18195        return '';
18196    }
18197  };
18198
18199  module.exports = TimeStep;
18200
18201/***/ },
18202/* 31 */
18203/***/ function(module, exports, __webpack_require__) {
18204
18205  'use strict';
18206
18207  var util = __webpack_require__(1);
18208  var stack = __webpack_require__(32);
18209  var RangeItem = __webpack_require__(33);
18210
18211  /**
18212   * @constructor Group
18213   * @param {Number | String} groupId
18214   * @param {Object} data
18215   * @param {ItemSet} itemSet
18216   */
18217  function Group(groupId, data, itemSet) {
18218    this.groupId = groupId;
18219    this.subgroups = {};
18220    this.subgroupIndex = 0;
18221    this.subgroupOrderer = data && data.subgroupOrder;
18222    this.itemSet = itemSet;
18223
18224    this.dom = {};
18225    this.props = {
18226      label: {
18227        width: 0,
18228        height: 0
18229      }
18230    };
18231    this.className = null;
18232
18233    this.items = {}; // items filtered by groupId of this group
18234    this.visibleItems = []; // items currently visible in window
18235    this.orderedItems = {
18236      byStart: [],
18237      byEnd: []
18238    };
18239    this.checkRangedItems = false; // needed to refresh the ranged items if the window is programatically changed with NO overlap.
18240    var me = this;
18241    this.itemSet.body.emitter.on('checkRangedItems', function () {
18242      me.checkRangedItems = true;
18243    });
18244
18245    this._create();
18246
18247    this.setData(data);
18248  }
18249
18250  /**
18251   * Create DOM elements for the group
18252   * @private
18253   */
18254  Group.prototype._create = function () {
18255    var label = document.createElement('div');
18256    if (this.itemSet.options.groupEditable.order) {
18257      label.className = 'vis-label draggable';
18258    } else {
18259      label.className = 'vis-label';
18260    }
18261    this.dom.label = label;
18262
18263    var inner = document.createElement('div');
18264    inner.className = 'vis-inner';
18265    label.appendChild(inner);
18266    this.dom.inner = inner;
18267
18268    var foreground = document.createElement('div');
18269    foreground.className = 'vis-group';
18270    foreground['timeline-group'] = this;
18271    this.dom.foreground = foreground;
18272
18273    this.dom.background = document.createElement('div');
18274    this.dom.background.className = 'vis-group';
18275
18276    this.dom.axis = document.createElement('div');
18277    this.dom.axis.className = 'vis-group';
18278
18279    // create a hidden marker to detect when the Timelines container is attached
18280    // to the DOM, or the style of a parent of the Timeline is changed from
18281    // display:none is changed to visible.
18282    this.dom.marker = document.createElement('div');
18283    this.dom.marker.style.visibility = 'hidden';
18284    this.dom.marker.innerHTML = '?';
18285    this.dom.background.appendChild(this.dom.marker);
18286  };
18287
18288  /**
18289   * Set the group data for this group
18290   * @param {Object} data   Group data, can contain properties content and className
18291   */
18292  Group.prototype.setData = function (data) {
18293    // update contents
18294    var content;
18295    if (this.itemSet.options && this.itemSet.options.groupTemplate) {
18296      content = this.itemSet.options.groupTemplate(data);
18297    } else {
18298      content = data && data.content;
18299    }
18300
18301    if (content instanceof Element) {
18302      this.dom.inner.appendChild(content);
18303      while (this.dom.inner.firstChild) {
18304        this.dom.inner.removeChild(this.dom.inner.firstChild);
18305      }
18306      this.dom.inner.appendChild(content);
18307    } else if (content !== undefined && content !== null) {
18308      this.dom.inner.innerHTML = content;
18309    } else {
18310      this.dom.inner.innerHTML = this.groupId || ''; // groupId can be null
18311    }
18312
18313    // update title
18314    this.dom.label.title = data && data.title || '';
18315
18316    if (!this.dom.inner.firstChild) {
18317      util.addClassName(this.dom.inner, 'vis-hidden');
18318    } else {
18319      util.removeClassName(this.dom.inner, 'vis-hidden');
18320    }
18321
18322    // update className
18323    var className = data && data.className || null;
18324    if (className != this.className) {
18325      if (this.className) {
18326        util.removeClassName(this.dom.label, this.className);
18327        util.removeClassName(this.dom.foreground, this.className);
18328        util.removeClassName(this.dom.background, this.className);
18329        util.removeClassName(this.dom.axis, this.className);
18330      }
18331      util.addClassName(this.dom.label, className);
18332      util.addClassName(this.dom.foreground, className);
18333      util.addClassName(this.dom.background, className);
18334      util.addClassName(this.dom.axis, className);
18335      this.className = className;
18336    }
18337
18338    // update style
18339    if (this.style) {
18340      util.removeCssText(this.dom.label, this.style);
18341      this.style = null;
18342    }
18343    if (data && data.style) {
18344      util.addCssText(this.dom.label, data.style);
18345      this.style = data.style;
18346    }
18347  };
18348
18349  /**
18350   * Get the width of the group label
18351   * @return {number} width
18352   */
18353  Group.prototype.getLabelWidth = function () {
18354    return this.props.label.width;
18355  };
18356
18357  /**
18358   * Repaint this group
18359   * @param {{start: number, end: number}} range
18360   * @param {{item: {horizontal: number, vertical: number}, axis: number}} margin
18361   * @param {boolean} [restack=false]  Force restacking of all items
18362   * @return {boolean} Returns true if the group is resized
18363   */
18364  Group.prototype.redraw = function (range, margin, restack) {
18365    var resized = false;
18366
18367    // force recalculation of the height of the items when the marker height changed
18368    // (due to the Timeline being attached to the DOM or changed from display:none to visible)
18369    var markerHeight = this.dom.marker.clientHeight;
18370    if (markerHeight != this.lastMarkerHeight) {
18371      this.lastMarkerHeight = markerHeight;
18372
18373      util.forEach(this.items, function (item) {
18374        item.dirty = true;
18375        if (item.displayed) item.redraw();
18376      });
18377
18378      restack = true;
18379    }
18380
18381    // reposition visible items vertically
18382    if (typeof this.itemSet.options.order === 'function') {
18383      // a custom order function
18384
18385      if (restack) {
18386        // brute force restack of all items
18387
18388        // show all items
18389        var me = this;
18390        var limitSize = false;
18391        util.forEach(this.items, function (item) {
18392          if (!item.displayed) {
18393            item.redraw();
18394            me.visibleItems.push(item);
18395          }
18396          item.repositionX(limitSize);
18397        });
18398
18399        // order all items and force a restacking
18400        var customOrderedItems = this.orderedItems.byStart.slice().sort(function (a, b) {
18401          return me.itemSet.options.order(a.data, b.data);
18402        });
18403        stack.stack(customOrderedItems, margin, true /* restack=true */);
18404      }
18405
18406      this.visibleItems = this._updateVisibleItems(this.orderedItems, this.visibleItems, range);
18407    } else {
18408      // no custom order function, lazy stacking
18409      this.visibleItems = this._updateVisibleItems(this.orderedItems, this.visibleItems, range);
18410
18411      if (this.itemSet.options.stack) {
18412        // TODO: ugly way to access options...
18413        stack.stack(this.visibleItems, margin, restack);
18414      } else {
18415        // no stacking
18416        stack.nostack(this.visibleItems, margin, this.subgroups);
18417      }
18418    }
18419
18420    // recalculate the height of the group
18421    var height = this._calculateHeight(margin);
18422
18423    // calculate actual size and position
18424    var foreground = this.dom.foreground;
18425    this.top = foreground.offsetTop;
18426    this.left = foreground.offsetLeft;
18427    this.width = foreground.offsetWidth;
18428    resized = util.updateProperty(this, 'height', height) || resized;
18429
18430    // recalculate size of label
18431    resized = util.updateProperty(this.props.label, 'width', this.dom.inner.clientWidth) || resized;
18432    resized = util.updateProperty(this.props.label, 'height', this.dom.inner.clientHeight) || resized;
18433
18434    // apply new height
18435    this.dom.background.style.height = height + 'px';
18436    this.dom.foreground.style.height = height + 'px';
18437    this.dom.label.style.height = height + 'px';
18438
18439    // update vertical position of items after they are re-stacked and the height of the group is calculated
18440    for (var i = 0, ii = this.visibleItems.length; i < ii; i++) {
18441      var item = this.visibleItems[i];
18442      item.repositionY(margin);
18443    }
18444
18445    return resized;
18446  };
18447
18448  /**
18449   * recalculate the height of the group
18450   * @param {{item: {horizontal: number, vertical: number}, axis: number}} margin
18451   * @returns {number} Returns the height
18452   * @private
18453   */
18454  Group.prototype._calculateHeight = function (margin) {
18455    // recalculate the height of the group
18456    var height;
18457    var visibleItems = this.visibleItems;
18458    //var visibleSubgroups = [];
18459    //this.visibleSubgroups = 0;
18460    this.resetSubgroups();
18461    var me = this;
18462    if (visibleItems.length > 0) {
18463      var min = visibleItems[0].top;
18464      var max = visibleItems[0].top + visibleItems[0].height;
18465      util.forEach(visibleItems, function (item) {
18466        min = Math.min(min, item.top);
18467        max = Math.max(max, item.top + item.height);
18468        if (item.data.subgroup !== undefined) {
18469          me.subgroups[item.data.subgroup].height = Math.max(me.subgroups[item.data.subgroup].height, item.height);
18470          me.subgroups[item.data.subgroup].visible = true;
18471        }
18472      });
18473      if (min > margin.axis) {
18474        // there is an empty gap between the lowest item and the axis
18475        var offset = min - margin.axis;
18476        max -= offset;
18477        util.forEach(visibleItems, function (item) {
18478          item.top -= offset;
18479        });
18480      }
18481      height = max + margin.item.vertical / 2;
18482    } else {
18483      height = 0;
18484    }
18485    height = Math.max(height, this.props.label.height);
18486
18487    return height;
18488  };
18489
18490  /**
18491   * Show this group: attach to the DOM
18492   */
18493  Group.prototype.show = function () {
18494    if (!this.dom.label.parentNode) {
18495      this.itemSet.dom.labelSet.appendChild(this.dom.label);
18496    }
18497
18498    if (!this.dom.foreground.parentNode) {
18499      this.itemSet.dom.foreground.appendChild(this.dom.foreground);
18500    }
18501
18502    if (!this.dom.background.parentNode) {
18503      this.itemSet.dom.background.appendChild(this.dom.background);
18504    }
18505
18506    if (!this.dom.axis.parentNode) {
18507      this.itemSet.dom.axis.appendChild(this.dom.axis);
18508    }
18509  };
18510
18511  /**
18512   * Hide this group: remove from the DOM
18513   */
18514  Group.prototype.hide = function () {
18515    var label = this.dom.label;
18516    if (label.parentNode) {
18517      label.parentNode.removeChild(label);
18518    }
18519
18520    var foreground = this.dom.foreground;
18521    if (foreground.parentNode) {
18522      foreground.parentNode.removeChild(foreground);
18523    }
18524
18525    var background = this.dom.background;
18526    if (background.parentNode) {
18527      background.parentNode.removeChild(background);
18528    }
18529
18530    var axis = this.dom.axis;
18531    if (axis.parentNode) {
18532      axis.parentNode.removeChild(axis);
18533    }
18534  };
18535
18536  /**
18537   * Add an item to the group
18538   * @param {Item} item
18539   */
18540  Group.prototype.add = function (item) {
18541    this.items[item.id] = item;
18542    item.setParent(this);
18543
18544    // add to
18545    if (item.data.subgroup !== undefined) {
18546      if (this.subgroups[item.data.subgroup] === undefined) {
18547        this.subgroups[item.data.subgroup] = { height: 0, visible: false, index: this.subgroupIndex, items: [] };
18548        this.subgroupIndex++;
18549      }
18550      this.subgroups[item.data.subgroup].items.push(item);
18551    }
18552    this.orderSubgroups();
18553
18554    if (this.visibleItems.indexOf(item) == -1) {
18555      var range = this.itemSet.body.range; // TODO: not nice accessing the range like this
18556      this._checkIfVisible(item, this.visibleItems, range);
18557    }
18558  };
18559
18560  Group.prototype.orderSubgroups = function () {
18561    if (this.subgroupOrderer !== undefined) {
18562      var sortArray = [];
18563      if (typeof this.subgroupOrderer == 'string') {
18564        for (var subgroup in this.subgroups) {
18565          sortArray.push({ subgroup: subgroup, sortField: this.subgroups[subgroup].items[0].data[this.subgroupOrderer] });
18566        }
18567        sortArray.sort(function (a, b) {
18568          return a.sortField - b.sortField;
18569        });
18570      } else if (typeof this.subgroupOrderer == 'function') {
18571        for (var subgroup in this.subgroups) {
18572          sortArray.push(this.subgroups[subgroup].items[0].data);
18573        }
18574        sortArray.sort(this.subgroupOrderer);
18575      }
18576
18577      if (sortArray.length > 0) {
18578        for (var i = 0; i < sortArray.length; i++) {
18579          this.subgroups[sortArray[i].subgroup].index = i;
18580        }
18581      }
18582    }
18583  };
18584
18585  Group.prototype.resetSubgroups = function () {
18586    for (var subgroup in this.subgroups) {
18587      if (this.subgroups.hasOwnProperty(subgroup)) {
18588        this.subgroups[subgroup].visible = false;
18589      }
18590    }
18591  };
18592
18593  /**
18594   * Remove an item from the group
18595   * @param {Item} item
18596   */
18597  Group.prototype.remove = function (item) {
18598    delete this.items[item.id];
18599    item.setParent(null);
18600
18601    // remove from visible items
18602    var index = this.visibleItems.indexOf(item);
18603    if (index != -1) this.visibleItems.splice(index, 1);
18604
18605    if (item.data.subgroup !== undefined) {
18606      var subgroup = this.subgroups[item.data.subgroup];
18607      if (subgroup) {
18608        var itemIndex = subgroup.items.indexOf(item);
18609        subgroup.items.splice(itemIndex, 1);
18610        if (!subgroup.items.length) {
18611          delete this.subgroups[item.data.subgroup];
18612          this.subgroupIndex--;
18613        }
18614        this.orderSubgroups();
18615      }
18616    }
18617  };
18618
18619  /**
18620   * Remove an item from the corresponding DataSet
18621   * @param {Item} item
18622   */
18623  Group.prototype.removeFromDataSet = function (item) {
18624    this.itemSet.removeItem(item.id);
18625  };
18626
18627  /**
18628   * Reorder the items
18629   */
18630  Group.prototype.order = function () {
18631    var array = util.toArray(this.items);
18632    var startArray = [];
18633    var endArray = [];
18634
18635    for (var i = 0; i < array.length; i++) {
18636      if (array[i].data.end !== undefined) {
18637        endArray.push(array[i]);
18638      }
18639      startArray.push(array[i]);
18640    }
18641    this.orderedItems = {
18642      byStart: startArray,
18643      byEnd: endArray
18644    };
18645
18646    stack.orderByStart(this.orderedItems.byStart);
18647    stack.orderByEnd(this.orderedItems.byEnd);
18648  };
18649
18650  /**
18651   * Update the visible items
18652   * @param {{byStart: Item[], byEnd: Item[]}} orderedItems   All items ordered by start date and by end date
18653   * @param {Item[]} visibleItems                             The previously visible items.
18654   * @param {{start: number, end: number}} range              Visible range
18655   * @return {Item[]} visibleItems                            The new visible items.
18656   * @private
18657   */
18658  Group.prototype._updateVisibleItems = function (orderedItems, oldVisibleItems, range) {
18659    var visibleItems = [];
18660    var visibleItemsLookup = {}; // we keep this to quickly look up if an item already exists in the list without using indexOf on visibleItems
18661    var interval = (range.end - range.start) / 4;
18662    var lowerBound = range.start - interval;
18663    var upperBound = range.end + interval;
18664    var item, i;
18665
18666    // this function is used to do the binary search.
18667    var searchFunction = function searchFunction(value) {
18668      if (value < lowerBound) {
18669        return -1;
18670      } else if (value <= upperBound) {
18671        return 0;
18672      } else {
18673        return 1;
18674      }
18675    };
18676
18677    // first check if the items that were in view previously are still in view.
18678    // IMPORTANT: this handles the case for the items with startdate before the window and enddate after the window!
18679    // also cleans up invisible items.
18680    if (oldVisibleItems.length > 0) {
18681      for (i = 0; i < oldVisibleItems.length; i++) {
18682        this._checkIfVisibleWithReference(oldVisibleItems[i], visibleItems, visibleItemsLookup, range);
18683      }
18684    }
18685
18686    // we do a binary search for the items that have only start values.
18687    var initialPosByStart = util.binarySearchCustom(orderedItems.byStart, searchFunction, 'data', 'start');
18688
18689    // trace the visible items from the inital start pos both ways until an invisible item is found, we only look at the start values.
18690    this._traceVisible(initialPosByStart, orderedItems.byStart, visibleItems, visibleItemsLookup, function (item) {
18691      return item.data.start < lowerBound || item.data.start > upperBound;
18692    });
18693
18694    // if the window has changed programmatically without overlapping the old window, the ranged items with start < lowerBound and end > upperbound are not shown.
18695    // We therefore have to brute force check all items in the byEnd list
18696    if (this.checkRangedItems == true) {
18697      this.checkRangedItems = false;
18698      for (i = 0; i < orderedItems.byEnd.length; i++) {
18699        this._checkIfVisibleWithReference(orderedItems.byEnd[i], visibleItems, visibleItemsLookup, range);
18700      }
18701    } else {
18702      // we do a binary search for the items that have defined end times.
18703      var initialPosByEnd = util.binarySearchCustom(orderedItems.byEnd, searchFunction, 'data', 'end');
18704
18705      // trace the visible items from the inital start pos both ways until an invisible item is found, we only look at the end values.
18706      this._traceVisible(initialPosByEnd, orderedItems.byEnd, visibleItems, visibleItemsLookup, function (item) {
18707        return item.data.end < lowerBound || item.data.end > upperBound;
18708      });
18709    }
18710
18711    // finally, we reposition all the visible items.
18712    for (i = 0; i < visibleItems.length; i++) {
18713      item = visibleItems[i];
18714      if (!item.displayed) item.show();
18715      // reposition item horizontally
18716      item.repositionX();
18717    }
18718
18719    // debug
18720    //console.log("new line")
18721    //if (this.groupId == null) {
18722    //  for (i = 0; i < orderedItems.byStart.length; i++) {
18723    //    item = orderedItems.byStart[i].data;
18724    //    console.log('start',i,initialPosByStart, item.start.valueOf(), item.content, item.start >= lowerBound && item.start <= upperBound,i == initialPosByStart ? "<------------------- HEREEEE" : "")
18725    //  }
18726    //  for (i = 0; i < orderedItems.byEnd.length; i++) {
18727    //    item = orderedItems.byEnd[i].data;
18728    //    console.log('rangeEnd',i,initialPosByEnd, item.end.valueOf(), item.content, item.end >= range.start && item.end <= range.end,i == initialPosByEnd ? "<------------------- HEREEEE" : "")
18729    //  }
18730    //}
18731
18732    return visibleItems;
18733  };
18734
18735  Group.prototype._traceVisible = function (initialPos, items, visibleItems, visibleItemsLookup, breakCondition) {
18736    var item;
18737    var i;
18738
18739    if (initialPos != -1) {
18740      for (i = initialPos; i >= 0; i--) {
18741        item = items[i];
18742        if (breakCondition(item)) {
18743          break;
18744        } else {
18745          if (visibleItemsLookup[item.id] === undefined) {
18746            visibleItemsLookup[item.id] = true;
18747            visibleItems.push(item);
18748          }
18749        }
18750      }
18751
18752      for (i = initialPos + 1; i < items.length; i++) {
18753        item = items[i];
18754        if (breakCondition(item)) {
18755          break;
18756        } else {
18757          if (visibleItemsLookup[item.id] === undefined) {
18758            visibleItemsLookup[item.id] = true;
18759            visibleItems.push(item);
18760          }
18761        }
18762      }
18763    }
18764  };
18765
18766  /**
18767   * this function is very similar to the _checkIfInvisible() but it does not
18768   * return booleans, hides the item if it should not be seen and always adds to
18769   * the visibleItems.
18770   * this one is for brute forcing and hiding.
18771   *
18772   * @param {Item} item
18773   * @param {Array} visibleItems
18774   * @param {{start:number, end:number}} range
18775   * @private
18776   */
18777  Group.prototype._checkIfVisible = function (item, visibleItems, range) {
18778    if (item.isVisible(range)) {
18779      if (!item.displayed) item.show();
18780      // reposition item horizontally
18781      item.repositionX();
18782      visibleItems.push(item);
18783    } else {
18784      if (item.displayed) item.hide();
18785    }
18786  };
18787
18788  /**
18789   * this function is very similar to the _checkIfInvisible() but it does not
18790   * return booleans, hides the item if it should not be seen and always adds to
18791   * the visibleItems.
18792   * this one is for brute forcing and hiding.
18793   *
18794   * @param {Item} item
18795   * @param {Array} visibleItems
18796   * @param {{start:number, end:number}} range
18797   * @private
18798   */
18799  Group.prototype._checkIfVisibleWithReference = function (item, visibleItems, visibleItemsLookup, range) {
18800    if (item.isVisible(range)) {
18801      if (visibleItemsLookup[item.id] === undefined) {
18802        visibleItemsLookup[item.id] = true;
18803        visibleItems.push(item);
18804      }
18805    } else {
18806      if (item.displayed) item.hide();
18807    }
18808  };
18809
18810  module.exports = Group;
18811
18812/***/ },
18813/* 32 */
18814/***/ function(module, exports) {
18815
18816  // Utility functions for ordering and stacking of items
18817  'use strict';
18818
18819  var EPSILON = 0.001; // used when checking collisions, to prevent round-off errors
18820
18821  /**
18822   * Order items by their start data
18823   * @param {Item[]} items
18824   */
18825  exports.orderByStart = function (items) {
18826    items.sort(function (a, b) {
18827      return a.data.start - b.data.start;
18828    });
18829  };
18830
18831  /**
18832   * Order items by their end date. If they have no end date, their start date
18833   * is used.
18834   * @param {Item[]} items
18835   */
18836  exports.orderByEnd = function (items) {
18837    items.sort(function (a, b) {
18838      var aTime = 'end' in a.data ? a.data.end : a.data.start,
18839          bTime = 'end' in b.data ? b.data.end : b.data.start;
18840
18841      return aTime - bTime;
18842    });
18843  };
18844
18845  /**
18846   * Adjust vertical positions of the items such that they don't overlap each
18847   * other.
18848   * @param {Item[]} items
18849   *            All visible items
18850   * @param {{item: {horizontal: number, vertical: number}, axis: number}} margin
18851   *            Margins between items and between items and the axis.
18852   * @param {boolean} [force=false]
18853   *            If true, all items will be repositioned. If false (default), only
18854   *            items having a top===null will be re-stacked
18855   */
18856  exports.stack = function (items, margin, force) {
18857    var i, iMax;
18858
18859    if (force) {
18860      // reset top position of all items
18861      for (i = 0, iMax = items.length; i < iMax; i++) {
18862        items[i].top = null;
18863      }
18864    }
18865
18866    // calculate new, non-overlapping positions
18867    for (i = 0, iMax = items.length; i < iMax; i++) {
18868      var item = items[i];
18869      if (item.stack && item.top === null) {
18870        // initialize top position
18871        item.top = margin.axis;
18872
18873        do {
18874          // TODO: optimize checking for overlap. when there is a gap without items,
18875          //       you only need to check for items from the next item on, not from zero
18876          var collidingItem = null;
18877          for (var j = 0, jj = items.length; j < jj; j++) {
18878            var other = items[j];
18879            if (other.top !== null && other !== item && other.stack && exports.collision(item, other, margin.item)) {
18880              collidingItem = other;
18881              break;
18882            }
18883          }
18884
18885          if (collidingItem != null) {
18886            // There is a collision. Reposition the items above the colliding element
18887            item.top = collidingItem.top + collidingItem.height + margin.item.vertical;
18888          }
18889        } while (collidingItem);
18890      }
18891    }
18892  };
18893
18894  /**
18895   * Adjust vertical positions of the items without stacking them
18896   * @param {Item[]} items
18897   *            All visible items
18898   * @param {{item: {horizontal: number, vertical: number}, axis: number}} margin
18899   *            Margins between items and between items and the axis.
18900   */
18901  exports.nostack = function (items, margin, subgroups) {
18902    var i, iMax, newTop;
18903
18904    // reset top position of all items
18905    for (i = 0, iMax = items.length; i < iMax; i++) {
18906      if (items[i].data.subgroup !== undefined) {
18907        newTop = margin.axis;
18908        for (var subgroup in subgroups) {
18909          if (subgroups.hasOwnProperty(subgroup)) {
18910            if (subgroups[subgroup].visible == true && subgroups[subgroup].index < subgroups[items[i].data.subgroup].index) {
18911              newTop += subgroups[subgroup].height + margin.item.vertical;
18912            }
18913          }
18914        }
18915        items[i].top = newTop;
18916      } else {
18917        items[i].top = margin.axis;
18918      }
18919    }
18920  };
18921
18922  /**
18923   * Test if the two provided items collide
18924   * The items must have parameters left, width, top, and height.
18925   * @param {Item} a          The first item
18926   * @param {Item} b          The second item
18927   * @param {{horizontal: number, vertical: number}} margin
18928   *                          An object containing a horizontal and vertical
18929   *                          minimum required margin.
18930   * @return {boolean}        true if a and b collide, else false
18931   */
18932  exports.collision = function (a, b, margin) {
18933    return a.left - margin.horizontal + EPSILON < b.left + b.width && a.left + a.width + margin.horizontal - EPSILON >
18933 b.left && a.top - margin.vertical + EPSILON < b.top + b.height && a.top + a.height + margin.vertical - EPSILON > b.top;
18934  };
18935
18936/***/ },
18937/* 33 */
18938/***/ function(module, exports, __webpack_require__) {
18939
18940  'use strict';
18941
18942  var Hammer = __webpack_require__(20);
18943  var Item = __webpack_require__(34);
18944
18945  /**
18946   * @constructor RangeItem
18947   * @extends Item
18948   * @param {Object} data             Object containing parameters start, end
18949   *                                  content, className.
18950   * @param {{toScreen: function, toTime: function}} conversion
18951   *                                  Conversion functions from time to screen and vice versa
18952   * @param {Object} [options]        Configuration options
18953   *                                  // TODO: describe options
18954   */
18955  function RangeItem(data, conversion, options) {
18956    this.props = {
18957      content: {
18958        width: 0
18959      }
18960    };
18961    this.overflow = false; // if contents can overflow (css styling), this flag is set to true
18962
18963    // validate data
18964    if (data) {
18965      if (data.start == undefined) {
18966        throw new Error('Property "start" missing in item ' + data.id);
18967      }
18968      if (data.end == undefined) {
18969        throw new Error('Property "end" missing in item ' + data.id);
18970      }
18971    }
18972
18973    Item.call(this, data, conversion, options);
18974  }
18975
18976  RangeItem.prototype = new Item(null, null, null);
18977
18978  RangeItem.prototype.baseClassName = 'vis-item vis-range';
18979
18980  /**
18981   * Check whether this item is visible inside given range
18982   * @returns {{start: Number, end: Number}} range with a timestamp for start and end
18983   * @returns {boolean} True if visible
18984   */
18985  RangeItem.prototype.isVisible = function (range) {
18986    // determine visibility
18987    return this.data.start < range.end && this.data.end > range.start;
18988  };
18989
18990  /**
18991   * Repaint the item
18992   */
18993  RangeItem.prototype.redraw = function () {
18994    var dom = this.dom;
18995    if (!dom) {
18996      // create DOM
18997      this.dom = {};
18998      dom = this.dom;
18999
19000      // background box
19001      dom.box = document.createElement('div');
19002      // className is updated in redraw()
19003
19004      // frame box (to prevent the item contents from overflowing
19005      dom.frame = document.createElement('div');
19006      dom.frame.className = 'vis-item-overflow';
19007      dom.box.appendChild(dom.frame);
19008
19009      // contents box
19010      dom.content = document.createElement('div');
19011      dom.content.className = 'vis-item-content';
19012      dom.frame.appendChild(dom.content);
19013
19014      // attach this item as attribute
19015      dom.box['timeline-item'] = this;
19016
19017      this.dirty = true;
19018    }
19019
19020    // append DOM to parent DOM
19021    if (!this.parent) {
19022      throw new Error('Cannot redraw item: no parent attached');
19023    }
19024    if (!dom.box.parentNode) {
19025      var foreground = this.parent.dom.foreground;
19026      if (!foreground) {
19027        throw new Error('Cannot redraw item: parent has no foreground container element');
19028      }
19029      foreground.appendChild(dom.box);
19030    }
19031    this.displayed = true;
19032
19033    // Update DOM when item is marked dirty. An item is marked dirty when:
19034    // - the item is not yet rendered
19035    // - the item's data is changed
19036    // - the item is selected/deselected
19037    if (this.dirty) {
19038      this._updateContents(this.dom.content);
19039      this._updateTitle(this.dom.box);
19040      this._updateDataAttributes(this.dom.box);
19041      this._updateStyle(this.dom.box);
19042
19043      var editable = (this.options.editable.updateTime || this.options.editable.updateGroup || this.editable === true) && this.editable !== false;
19044
19045      // update class
19046      var className = (this.data.className ? ' ' + this.data.className : '') + (this.selected ? ' vis-selected' : '') + (editable ? ' vis-editable' : ' vis-readonly');
19047      dom.box.className = this.baseClassName + className;
19048
19049      // determine from css whether this box has overflow
19050      this.overflow = window.getComputedStyle(dom.frame).overflow !== 'hidden';
19051
19052      // recalculate size
19053      // turn off max-width to be able to calculate the real width
19054      // this causes an extra browser repaint/reflow, but so be it
19055      this.dom.content.style.maxWidth = 'none';
19056      this.props.content.width = this.dom.content.offsetWidth;
19057      this.height = this.dom.box.offsetHeight;
19058      this.dom.content.style.maxWidth = '';
19059
19060      this.dirty = false;
19061    }
19062
19063    this._repaintDeleteButton(dom.box);
19064    this._repaintDragLeft();
19065    this._repaintDragRight();
19066  };
19067
19068  /**
19069   * Show the item in the DOM (when not already visible). The items DOM will
19070   * be created when needed.
19071   */
19072  RangeItem.prototype.show = function () {
19073    if (!this.displayed) {
19074      this.redraw();
19075    }
19076  };
19077
19078  /**
19079   * Hide the item from the DOM (when visible)
19080   * @return {Boolean} changed
19081   */
19082  RangeItem.prototype.hide = function () {
19083    if (this.displayed) {
19084      var box = this.dom.box;
19085
19086      if (box.parentNode) {
19087        box.parentNode.removeChild(box);
19088      }
19089
19090      this.displayed = false;
19091    }
19092  };
19093
19094  /**
19095   * Reposition the item horizontally
19096   * @param {boolean} [limitSize=true] If true (default), the width of the range
19097   *                                   item will be limited, as the browser cannot
19098   *                                   display very wide divs. This means though
19099   *                                   that the applied left and width may
19100   *                                   not correspond to the ranges start and end
19101   * @Override
19102   */
19103  RangeItem.prototype.repositionX = function (limitSize) {
19104    var parentWidth = this.parent.width;
19105    var start = this.conversion.toScreen(this.data.start);
19106    var end = this.conversion.toScreen(this.data.end);
19107    var contentLeft;
19108    var contentWidth;
19109
19110    // limit the width of the range, as browsers cannot draw very wide divs
19111    if (limitSize === undefined || limitSize === true) {
19112      if (start < -parentWidth) {
19113        start = -parentWidth;
19114      }
19115      if (end > 2 * parentWidth) {
19116        end = 2 * parentWidth;
19117      }
19118    }
19119    var boxWidth = Math.max(end - start, 1);
19120
19121    if (this.overflow) {
19122      this.left = start;
19123      this.width = boxWidth + this.props.content.width;
19124      contentWidth = this.props.content.width;
19125
19126      // Note: The calculation of width is an optimistic calculation, giving
19127      //       a width which will not change when moving the Timeline
19128      //       So no re-stacking needed, which is nicer for the eye;
19129    } else {
19130      this.left = start;
19131      this.width = boxWidth;
19132      contentWidth = Math.min(end - start, this.props.content.width);
19133    }
19134
19135    this.dom.box.style.left = this.left + 'px';
19136    this.dom.box.style.width = boxWidth + 'px';
19137
19138    switch (this.options.align) {
19139      case 'left':
19140        this.dom.content.style.left = '0';
19141        break;
19142
19143      case 'right':
19144        this.dom.content.style.left = Math.max(boxWidth - contentWidth, 0) + 'px';
19145        break;
19146
19147      case 'center':
19148        this.dom.content.style.left = Math.max((boxWidth - contentWidth) / 2, 0) + 'px';
19149        break;
19150
19151      default:
19152        // 'auto'
19153        // when range exceeds left of the window, position the contents at the left of the visible area
19154        if (this.overflow) {
19155          if (end > 0) {
19156            contentLeft = Math.max(-start, 0);
19157          } else {
19158            contentLeft = -contentWidth; // ensure it's not visible anymore
19159          }
19160        } else {
19161          if (start < 0) {
19162            contentLeft = -start;
19163          } else {
19164            contentLeft = 0;
19165          }
19166        }
19167        this.dom.content.style.left = contentLeft + 'px';
19168    }
19169  };
19170
19171  /**
19172   * Reposition the item vertically
19173   * @Override
19174   */
19175  RangeItem.prototype.repositionY = function () {
19176    var orientation = this.options.orientation.item;
19177    var box = this.dom.box;
19178
19179    if (orientation == 'top') {
19180      box.style.top = this.top + 'px';
19181    } else {
19182      box.style.top = this.parent.height - this.top - this.height + 'px';
19183    }
19184  };
19185
19186  /**
19187   * Repaint a drag area on the left side of the range when the range is selected
19188   * @protected
19189   */
19190  RangeItem.prototype._repaintDragLeft = function () {
19191    if (this.selected && this.options.editable.updateTime && !this.dom.dragLeft) {
19192      // create and show drag area
19193      var dragLeft = document.createElement('div');
19194      dragLeft.className = 'vis-drag-left';
19195      dragLeft.dragLeftItem = this;
19196
19197      this.dom.box.appendChild(dragLeft);
19198      this.dom.dragLeft = dragLeft;
19199    } else if (!this.selected && this.dom.dragLeft) {
19200      // delete drag area
19201      if (this.dom.dragLeft.parentNode) {
19202        this.dom.dragLeft.parentNode.removeChild(this.dom.dragLeft);
19203      }
19204      this.dom.dragLeft = null;
19205    }
19206  };
19207
19208  /**
19209   * Repaint a drag area on the right side of the range when the range is selected
19210   * @protected
19211   */
19212  RangeItem.prototype._repaintDragRight = function () {
19213    if (this.selected && this.options.editable.updateTime && !this.dom.dragRight) {
19214      // create and show drag area
19215      var dragRight = document.createElement('div');
19216      dragRight.className = 'vis-drag-right';
19217      dragRight.dragRightItem = this;
19218
19219      this.dom.box.appendChild(dragRight);
19220      this.dom.dragRight = dragRight;
19221    } else if (!this.selected && this.dom.dragRight) {
19222      // delete drag area
19223      if (this.dom.dragRight.parentNode) {
19224        this.dom.dragRight.parentNode.removeChild(this.dom.dragRight);
19225      }
19226      this.dom.dragRight = null;
19227    }
19228  };
19229
19230  module.exports = RangeItem;
19231
19232/***/ },
19233/* 34 */
19234/***/ function(module, exports, __webpack_require__) {
19235
19236  'use strict';
19237
19238  var Hammer = __webpack_require__(20);
19239  var util = __webpack_require__(1);
19240
19241  /**
19242   * @constructor Item
19243   * @param {Object} data             Object containing (optional) parameters type,
19244   *                                  start, end, content, group, className.
19245   * @param {{toScreen: function, toTime: function}} conversion
19246   *                                  Conversion functions from time to screen and vice versa
19247   * @param {Object} options          Configuration options
19248   *                                  // TODO: describe available options
19249   */
19250  function Item(data, conversion, options) {
19251    this.id = null;
19252    this.parent = null;
19253    this.data = data;
19254    this.dom = null;
19255    this.conversion = conversion || {};
19256    this.options = options || {};
19257
19258    this.selected = false;
19259    this.displayed = false;
19260    this.dirty = true;
19261
19262    this.top = null;
19263    this.left = null;
19264    this.width = null;
19265    this.height = null;
19266
19267    this.editable = null;
19268    if (this.data && this.data.hasOwnProperty('editable') && typeof this.data.editable === 'boolean') {
19269      this.editable = data.editable;
19270    }
19271  }
19272
19273  Item.prototype.stack = true;
19274
19275  /**
19276   * Select current item
19277   */
19278  Item.prototype.select = function () {
19279    this.selected = true;
19280    this.dirty = true;
19281    if (this.displayed) this.redraw();
19282  };
19283
19284  /**
19285   * Unselect current item
19286   */
19287  Item.prototype.unselect = function () {
19288    this.selected = false;
19289    this.dirty = true;
19290    if (this.displayed) this.redraw();
19291  };
19292
19293  /**
19294   * Set data for the item. Existing data will be updated. The id should not
19295   * be changed. When the item is displayed, it will be redrawn immediately.
19296   * @param {Object} data
19297   */
19298  Item.prototype.setData = function (data) {
19299    var groupChanged = data.group != undefined && this.data.group != data.group;
19300    if (groupChanged) {
19301      this.parent.itemSet._moveToGroup(this, data.group);
19302    }
19303
19304    if (data.hasOwnProperty('editable') && typeof data.editable === 'boolean') {
19305      this.editable = data.editable;
19306    }
19307
19308    this.data = data;
19309    this.dirty = true;
19310    if (this.displayed) this.redraw();
19311  };
19312
19313  /**
19314   * Set a parent for the item
19315   * @param {ItemSet | Group} parent
19316   */
19317  Item.prototype.setParent = function (parent) {
19318    if (this.displayed) {
19319      this.hide();
19320      this.parent = parent;
19321      if (this.parent) {
19322        this.show();
19323      }
19324    } else {
19325      this.parent = parent;
19326    }
19327  };
19328
19329  /**
19330   * Check whether this item is visible inside given range
19331   * @returns {{start: Number, end: Number}} range with a timestamp for start and end
19332   * @returns {boolean} True if visible
19333   */
19334  Item.prototype.isVisible = function (range) {
19335    // Should be implemented by Item implementations
19336    return false;
19337  };
19338
19339  /**
19340   * Show the Item in the DOM (when not already visible)
19341   * @return {Boolean} changed
19342   */
19343  Item.prototype.show = function () {
19344    return false;
19345  };
19346
19347  /**
19348   * Hide the Item from the DOM (when visible)
19349   * @return {Boolean} changed
19350   */
19351  Item.prototype.hide = function () {
19352    return false;
19353  };
19354
19355  /**
19356   * Repaint the item
19357   */
19358  Item.prototype.redraw = function () {};
19359
19360  /**
19361   * Reposition the Item horizontally
19362   */
19363  Item.prototype.repositionX = function () {};
19364
19365  /**
19366   * Reposition the Item vertically
19367   */
19368  Item.prototype.repositionY = function () {};
19369
19370  /**
19371   * Repaint a delete button on the top right of the item when the item is selected
19372   * @param {HTMLElement} anchor
19373   * @protected
19374   */
19375  Item.prototype._repaintDeleteButton = function (anchor) {
19376    var editable = (this.options.editable.remove || this.data.editable === true) && this.data.editable !== false;
19377
19378    if (this.selected && editable && !this.dom.deleteButton) {
19379      // create and show button
19380      var me = this;
19381
19382      var deleteButton = document.createElement('div');
19383      deleteButton.className = 'vis-delete';
19384      deleteButton.title = 'Delete this item';
19385
19386      // TODO: be able to destroy the delete button
19387      new Hammer(deleteButton).on('tap', function (event) {
19388        event.stopPropagation();
19389        me.parent.removeFromDataSet(me);
19390      });
19391
19392      anchor.appendChild(deleteButton);
19393      this.dom.deleteButton = deleteButton;
19394    } else if (!this.selected && this.dom.deleteButton) {
19395      // remove button
19396      if (this.dom.deleteButton.parentNode) {
19397        this.dom.deleteButton.parentNode.removeChild(this.dom.deleteButton);
19398      }
19399      this.dom.deleteButton = null;
19400    }
19401  };
19402
19403  /**
19404   * Set HTML contents for the item
19405   * @param {Element} element   HTML element to fill with the contents
19406   * @private
19407   */
19408  Item.prototype._updateContents = function (element) {
19409    var content;
19410    if (this.options.template) {
19411      var itemData = this.parent.itemSet.itemsData.get(this.id); // get a clone of the data from the dataset
19412      content = this.options.template(itemData);
19413    } else {
19414      content = this.data.content;
19415    }
19416
19417    var changed = this._contentToString(this.content) !== this._contentToString(content);
19418    if (changed) {
19419      // only replace the content when changed
19420      if (content instanceof Element) {
19421        element.innerHTML = '';
19422        element.appendChild(content);
19423      } else if (content != undefined) {
19424        element.innerHTML = content;
19425      } else {
19426        if (!(this.data.type == 'background' && this.data.content === undefined)) {
19427          throw new Error('Property "content" missing in item ' + this.id);
19428        }
19429      }
19430
19431      this.content = content;
19432    }
19433  };
19434
19435  /**
19436   * Set HTML contents for the item
19437   * @param {Element} element   HTML element to fill with the contents
19438   * @private
19439   */
19440  Item.prototype._updateTitle = function (element) {
19441    if (this.data.title != null) {
19442      element.title = this.data.title || '';
19443    } else {
19444      element.removeAttribute('vis-title');
19445    }
19446  };
19447
19448  /**
19449   * Process dataAttributes timeline option and set as data- attributes on dom.content
19450   * @param {Element} element   HTML element to which the attributes will be attached
19451   * @private
19452   */
19453  Item.prototype._updateDataAttributes = function (element) {
19454    if (this.options.dataAttributes && this.options.dataAttributes.length > 0) {
19455      var attributes = [];
19456
19457      if (Array.isArray(this.options.dataAttributes)) {
19458        attributes = this.options.dataAttributes;
19459      } else if (this.options.dataAttributes == 'all') {
19460        attributes = Object.keys(this.data);
19461      } else {
19462        return;
19463      }
19464
19465      for (var i = 0; i < attributes.length; i++) {
19466        var name = attributes[i];
19467        var value = this.data[name];
19468
19469        if (value != null) {
19470          element.setAttribute('data-' + name, value);
19471        } else {
19472          element.removeAttribute('data-' + name);
19473        }
19474      }
19475    }
19476  };
19477
19478  /**
19479   * Update custom styles of the element
19480   * @param element
19481   * @private
19482   */
19483  Item.prototype._updateStyle = function (element) {
19484    // remove old styles
19485    if (this.style) {
19486      util.removeCssText(element, this.style);
19487      this.style = null;
19488    }
19489
19490    // append new styles
19491    if (this.data.style) {
19492      util.addCssText(element, this.data.style);
19493      this.style = this.data.style;
19494    }
19495  };
19496
19497  /**
19498   * Stringify the items contents
19499   * @param {string | Element | undefined} content
19500   * @returns {string | undefined}
19501   * @private
19502   */
19503  Item.prototype._contentToString = function (content) {
19504    if (typeof content === 'string') return content;
19505    if (content && 'outerHTML' in content) return content.outerHTML;
19506    return content;
19507  };
19508
19509  /**
19510   * Return the width of the item left from its start date
19511   * @return {number}
19512   */
19513  Item.prototype.getWidthLeft = function () {
19514    return 0;
19515  };
19516
19517  /**
19518   * Return the width of the item right from the max of its start and end date
19519   * @return {number}
19520   */
19521  Item.prototype.getWidthRight = function () {
19522    return 0;
19523  };
19524
19525  module.exports = Item;
19526
19527  // should be implemented by the item
19528
19529  // should be implemented by the item
19530
19531  // should be implemented by the item
19532
19533/***/ },
19534/* 35 */
19535/***/ function(module, exports, __webpack_require__) {
19536
19537  'use strict';
19538
19539  var util = __webpack_require__(1);
19540  var Group = __webpack_require__(31);
19541
19542  /**
19543   * @constructor BackgroundGroup
19544   * @param {Number | String} groupId
19545   * @param {Object} data
19546   * @param {ItemSet} itemSet
19547   */
19548  function BackgroundGroup(groupId, data, itemSet) {
19549    Group.call(this, groupId, data, itemSet);
19550
19551    this.width = 0;
19552    this.height = 0;
19553    this.top = 0;
19554    this.left = 0;
19555  }
19556
19557  BackgroundGroup.prototype = Object.create(Group.prototype);
19558
19559  /**
19560   * Repaint this group
19561   * @param {{start: number, end: number}} range
19562   * @param {{item: {horizontal: number, vertical: number}, axis: number}} margin
19563   * @param {boolean} [restack=false]  Force restacking of all items
19564   * @return {boolean} Returns true if the group is resized
19565   */
19566  BackgroundGroup.prototype.redraw = function (range, margin, restack) {
19567    var resized = false;
19568
19569    this.visibleItems = this._updateVisibleItems(this.orderedItems, this.visibleItems, range);
19570
19571    // calculate actual size
19572    this.width = this.dom.background.offsetWidth;
19573
19574    // apply new height (just always zero for BackgroundGroup
19575    this.dom.background.style.height = '0';
19576
19577    // update vertical position of items after they are re-stacked and the height of the group is calculated
19578    for (var i = 0, ii = this.visibleItems.length; i < ii; i++) {
19579      var item = this.visibleItems[i];
19580      item.repositionY(margin);
19581    }
19582
19583    return resized;
19584  };
19585
19586  /**
19587   * Show this group: attach to the DOM
19588   */
19589  BackgroundGroup.prototype.show = function () {
19590    if (!this.dom.background.parentNode) {
19591      this.itemSet.dom.background.appendChild(this.dom.background);
19592    }
19593  };
19594
19595  module.exports = BackgroundGroup;
19596
19597/***/ },
19598/* 36 */
19599/***/ function(module, exports, __webpack_require__) {
19600
19601  'use strict';
19602
19603  var Item = __webpack_require__(34);
19604  var util = __webpack_require__(1);
19605
19606  /**
19607   * @constructor BoxItem
19608   * @extends Item
19609   * @param {Object} data             Object containing parameters start
19610   *                                  content, className.
19611   * @param {{toScreen: function, toTime: function}} conversion
19612   *                                  Conversion functions from time to screen and vice versa
19613   * @param {Object} [options]        Configuration options
19614   *                                  // TODO: describe available options
19615   */
19616  function BoxItem(data, conversion, options) {
19617    this.props = {
19618      dot: {
19619        width: 0,
19620        height: 0
19621      },
19622      line: {
19623        width: 0,
19624        height: 0
19625      }
19626    };
19627
19628    // validate data
19629    if (data) {
19630      if (data.start == undefined) {
19631        throw new Error('Property "start" missing in item ' + data);
19632      }
19633    }
19634
19635    Item.call(this, data, conversion, options);
19636  }
19637
19638  BoxItem.prototype = new Item(null, null, null);
19639
19640  /**
19641   * Check whether this item is visible inside given range
19642   * @returns {{start: Number, end: Number}} range with a timestamp for start and end
19643   * @returns {boolean} True if visible
19644   */
19645  BoxItem.prototype.isVisible = function (range) {
19646    // determine visibility
19647    // TODO: account for the real width of the item. Right now we just add 1/4 to the window
19648    var interval = (range.end - range.start) / 4;
19649    return this.data.start > range.start - interval && this.data.start < range.end + interval;
19650  };
19651
19652  /**
19653   * Repaint the item
19654   */
19655  BoxItem.prototype.redraw = function () {
19656    var dom = this.dom;
19657    if (!dom) {
19658      // create DOM
19659      this.dom = {};
19660      dom = this.dom;
19661
19662      // create main box
19663      dom.box = document.createElement('DIV');
19664
19665      // contents box (inside the background box). used for making margins
19666      dom.content = document.createElement('DIV');
19667      dom.content.className = 'vis-item-content';
19668      dom.box.appendChild(dom.content);
19669
19670      // line to axis
19671      dom.line = document.createElement('DIV');
19672      dom.line.className = 'vis-line';
19673
19674      // dot on axis
19675      dom.dot = document.createElement('DIV');
19676      dom.dot.className = 'vis-dot';
19677
19678      // attach this item as attribute
19679      dom.box['timeline-item'] = this;
19680
19681      this.dirty = true;
19682    }
19683
19684    // append DOM to parent DOM
19685    if (!this.parent) {
19686      throw new Error('Cannot redraw item: no parent attached');
19687    }
19688    if (!dom.box.parentNode) {
19689      var foreground = this.parent.dom.foreground;
19690      if (!foreground) throw new Error('Cannot redraw item: parent has no foreground container element');
19691      foreground.appendChild(dom.box);
19692    }
19693    if (!dom.line.parentNode) {
19694      var background = this.parent.dom.background;
19695      if (!background) throw new Error('Cannot redraw item: parent has no background container element');
19696      background.appendChild(dom.line);
19697    }
19698    if (!dom.dot.parentNode) {
19699      var axis = this.parent.dom.axis;
19700      if (!background) throw new Error('Cannot redraw item: parent has no axis container element');
19701      axis.appendChild(dom.dot);
19702    }
19703    this.displayed = true;
19704
19705    // Update DOM when item is marked dirty. An item is marked dirty when:
19706    // - the item is not yet rendered
19707    // - the item's data is changed
19708    // - the item is selected/deselected
19709    if (this.dirty) {
19710      this._updateContents(this.dom.content);
19711      this._updateTitle(this.dom.box);
19712      this._updateDataAttributes(this.dom.box);
19713      this._updateStyle(this.dom.box);
19714
19715      var editable = (this.options.editable.updateTime || this.options.editable.updateGroup || this.editable === true) && this.editable !== false;
19716
19717      // update class
19718      var className = (this.data.className ? ' ' + this.data.className : '') + (this.selected ? ' vis-selected' : '') + (editable ? ' vis-editable' : ' vis-readonly');
19719      dom.box.className = 'vis-item vis-box' + className;
19720      dom.line.className = 'vis-item vis-line' + className;
19721      dom.dot.className = 'vis-item vis-dot' + className;
19722
19723      // recalculate size
19724      this.props.dot.height = dom.dot.offsetHeight;
19725      this.props.dot.width = dom.dot.offsetWidth;
19726      this.props.line.width = dom.line.offsetWidth;
19727      this.width = dom.box.offsetWidth;
19728      this.height = dom.box.offsetHeight;
19729
19730      this.dirty = false;
19731    }
19732
19733    this._repaintDeleteButton(dom.box);
19734  };
19735
19736  /**
19737   * Show the item in the DOM (when not already displayed). The items DOM will
19738   * be created when needed.
19739   */
19740  BoxItem.prototype.show = function () {
19741    if (!this.displayed) {
19742      this.redraw();
19743    }
19744  };
19745
19746  /**
19747   * Hide the item from the DOM (when visible)
19748   */
19749  BoxItem.prototype.hide = function () {
19750    if (this.displayed) {
19751      var dom = this.dom;
19752
19753      if (dom.box.parentNode) dom.box.parentNode.removeChild(dom.box);
19754      if (dom.line.parentNode) dom.line.parentNode.removeChild(dom.line);
19755      if (dom.dot.parentNode) dom.dot.parentNode.removeChild(dom.dot);
19756
19757      this.displayed = false;
19758    }
19759  };
19760
19761  /**
19762   * Reposition the item horizontally
19763   * @Override
19764   */
19765  BoxItem.prototype.repositionX = function () {
19766    var start = this.conversion.toScreen(this.data.start);
19767    var align = this.options.align;
19768    var left;
19769
19770    // calculate left position of the box
19771    if (align == 'right') {
19772      this.left = start - this.width;
19773    } else if (align == 'left') {
19774      this.left = start;
19775    } else {
19776      // default or 'center'
19777      this.left = start - this.width / 2;
19778    }
19779
19780    // reposition box
19781    this.dom.box.style.left = this.left + 'px';
19782
19783    // reposition line
19784    this.dom.line.style.left = start - this.props.line.width / 2 + 'px';
19785
19786    // reposition dot
19787    this.dom.dot.style.left = start - this.props.dot.width / 2 + 'px';
19788  };
19789
19790  /**
19791   * Reposition the item vertically
19792   * @Override
19793   */
19794  BoxItem.prototype.repositionY = function () {
19795    var orientation = this.options.orientation.item;
19796    var box = this.dom.box;
19797    var line = this.dom.line;
19798    var dot = this.dom.dot;
19799
19800    if (orientation == 'top') {
19801      box.style.top = (this.top || 0) + 'px';
19802
19803      line.style.top = '0';
19804      line.style.height = this.parent.top + this.top + 1 + 'px';
19805      line.style.bottom = '';
19806    } else {
19807      // orientation 'bottom'
19808      var itemSetHeight = this.parent.itemSet.props.height; // TODO: this is nasty
19809      var lineHeight = itemSetHeight - this.parent.top - this.parent.height + this.top;
19810
19811      box.style.top = (this.parent.height - this.top - this.height || 0) + 'px';
19812      line.style.top = itemSetHeight - lineHeight + 'px';
19813      line.style.bottom = '0';
19814    }
19815
19816    dot.style.top = -this.props.dot.height / 2 + 'px';
19817  };
19818
19819  /**
19820   * Return the width of the item left from its start date
19821   * @return {number}
19822   */
19823  BoxItem.prototype.getWidthLeft = function () {
19824    return this.width / 2;
19825  };
19826
19827  /**
19828   * Return the width of the item right from its start date
19829   * @return {number}
19830   */
19831  BoxItem.prototype.getWidthRight = function () {
19832    return this.width / 2;
19833  };
19834
19835  module.exports = BoxItem;
19836
19837/***/ },
19838/* 37 */
19839/***/ function(module, exports, __webpack_require__) {
19840
19841  'use strict';
19842
19843  var Item = __webpack_require__(34);
19844
19845  /**
19846   * @constructor PointItem
19847   * @extends Item
19848   * @param {Object} data             Object containing parameters start
19849   *                                  content, className.
19850   * @param {{toScreen: function, toTime: function}} conversion
19851   *                                  Conversion functions from time to screen and vice versa
19852   * @param {Object} [options]        Configuration options
19853   *                                  // TODO: describe available options
19854   */
19855  function PointItem(data, conversion, options) {
19856    this.props = {
19857      dot: {
19858        top: 0,
19859        width: 0,
19860        height: 0
19861      },
19862      content: {
19863        height: 0,
19864        marginLeft: 0
19865      }
19866    };
19867
19868    // validate data
19869    if (data) {
19870      if (data.start == undefined) {
19871        throw new Error('Property "start" missing in item ' + data);
19872      }
19873    }
19874
19875    Item.call(this, data, conversion, options);
19876  }
19877
19878  PointItem.prototype = new Item(null, null, null);
19879
19880  /**
19881   * Check whether this item is visible inside given range
19882   * @returns {{start: Number, end: Number}} range with a timestamp for start and end
19883   * @returns {boolean} True if visible
19884   */
19885  PointItem.prototype.isVisible = function (range) {
19886    // determine visibility
19887    // TODO: account for the real width of the item. Right now we just add 1/4 to the window
19888    var interval = (range.end - range.start) / 4;
19889    return this.data.start > range.start - interval && this.data.start < range.end + interval;
19890  };
19891
19892  /**
19893   * Repaint the item
19894   */
19895  PointItem.prototype.redraw = function () {
19896    var dom = this.dom;
19897    if (!dom) {
19898      // create DOM
19899      this.dom = {};
19900      dom = this.dom;
19901
19902      // background box
19903      dom.point = document.createElement('div');
19904      // className is updated in redraw()
19905
19906      // contents box, right from the dot
19907      dom.content = document.createElement('div');
19908      dom.content.className = 'vis-item-content';
19909      dom.point.appendChild(dom.content);
19910
19911      // dot at start
19912      dom.dot = document.createElement('div');
19913      dom.point.appendChild(dom.dot);
19914
19915      // attach this item as attribute
19916      dom.point['timeline-item'] = this;
19917
19918      this.dirty = true;
19919    }
19920
19921    // append DOM to parent DOM
19922    if (!this.parent) {
19923      throw new Error('Cannot redraw item: no parent attached');
19924    }
19925    if (!dom.point.parentNode) {
19926      var foreground = this.parent.dom.foreground;
19927      if (!foreground) {
19928        throw new Error('Cannot redraw item: parent has no foreground container element');
19929      }
19930      foreground.appendChild(dom.point);
19931    }
19932    this.displayed = true;
19933
19934    // Update DOM when item is marked dirty. An item is marked dirty when:
19935    // - the item is not yet rendered
19936    // - the item's data is changed
19937    // - the item is selected/deselected
19938    if (this.dirty) {
19939      this._updateContents(this.dom.content);
19940      this._updateTitle(this.dom.point);
19941      this._updateDataAttributes(this.dom.point);
19942      this._updateStyle(this.dom.point);
19943
19944      var editable = (this.options.editable.updateTime || this.options.editable.updateGroup || this.editable === true) && this.editable !== false;
19945
19946      // update class
19947      var className = (this.data.className ? ' ' + this.data.className : '') + (this.selected ? ' vis-selected' : '') + (editable ? ' vis-editable' : ' vis-readonly');
19948      dom.point.className = 'vis-item vis-point' + className;
19949      dom.dot.className = 'vis-item vis-dot' + className;
19950
19951      // recalculate size of dot and contents
19952      this.props.dot.width = dom.dot.offsetWidth;
19953      this.props.dot.height = dom.dot.offsetHeight;
19954      this.props.content.height = dom.content.offsetHeight;
19955
19956      // resize contents
19957      dom.content.style.marginLeft = 2 * this.props.dot.width + 'px';
19958      //dom.content.style.marginRight = ... + 'px'; // TODO: margin right
19959
19960      // recalculate size
19961      this.width = dom.point.offsetWidth;
19962      this.height = dom.point.offsetHeight;
19963
19964      // reposition the dot
19965      dom.dot.style.top = (this.height - this.props.dot.height) / 2 + 'px';
19966      dom.dot.style.left = this.props.dot.width / 2 + 'px';
19967
19968      this.dirty = false;
19969    }
19970
19971    this._repaintDeleteButton(dom.point);
19972  };
19973
19974  /**
19975   * Show the item in the DOM (when not already visible). The items DOM will
19976   * be created when needed.
19977   */
19978  PointItem.prototype.show = function () {
19979    if (!this.displayed) {
19980      this.redraw();
19981    }
19982  };
19983
19984  /**
19985   * Hide the item from the DOM (when visible)
19986   */
19987  PointItem.prototype.hide = function () {
19988    if (this.displayed) {
19989      if (this.dom.point.parentNode) {
19990        this.dom.point.parentNode.removeChild(this.dom.point);
19991      }
19992
19993      this.displayed = false;
19994    }
19995  };
19996
19997  /**
19998   * Reposition the item horizontally
19999   * @Override
20000   */
20001  PointItem.prototype.repositionX = function () {
20002    var start = this.conversion.toScreen(this.data.start);
20003
20004    this.left = start - this.props.dot.width;
20005
20006    // reposition point
20007    this.dom.point.style.left = this.left + 'px';
20008  };
20009
20010  /**
20011   * Reposition the item vertically
20012   * @Override
20013   */
20014  PointItem.prototype.repositionY = function () {
20015    var orientation = this.options.orientation.item;
20016    var point = this.dom.point;
20017
20018    if (orientation == 'top') {
20019      point.style.top = this.top + 'px';
20020    } else {
20021      point.style.top = this.parent.height - this.top - this.height + 'px';
20022    }
20023  };
20024
20025  /**
20026   * Return the width of the item left from its start date
20027   * @return {number}
20028   */
20029  PointItem.prototype.getWidthLeft = function () {
20030    return this.props.dot.width;
20031  };
20032
20033  /**
20034   * Return the width of the item right from  its start date
20035   * @return {number}
20036   */
20037  PointItem.prototype.getWidthRight = function () {
20038    return this.width - this.props.dot.width;
20039  };
20040
20041  module.exports = PointItem;
20042
20043/***/ },
20044/* 38 */
20045/***/ function(module, exports, __webpack_require__) {
20046
20047  'use strict';
20048
20049  var Hammer = __webpack_require__(20);
20050  var Item = __webpack_require__(34);
20051  var BackgroundGroup = __webpack_require__(35);
20052  var RangeItem = __webpack_require__(33);
20053
20054  /**
20055   * @constructor BackgroundItem
20056   * @extends Item
20057   * @param {Object} data             Object containing parameters start, end
20058   *                                  content, className.
20059   * @param {{toScreen: function, toTime: function}} conversion
20060   *                                  Conversion functions from time to screen and vice versa
20061   * @param {Object} [options]        Configuration options
20062   *                                  // TODO: describe options
20063   */
20064  // TODO: implement support for the BackgroundItem just having a start, then being displayed as a sort of an annotation
20065  function BackgroundItem(data, conversion, options) {
20066    this.props = {
20067      content: {
20068        width: 0
20069      }
20070    };
20071    this.overflow = false; // if contents can overflow (css styling), this flag is set to true
20072
20073    // validate data
20074    if (data) {
20075      if (data.start == undefined) {
20076        throw new Error('Property "start" missing in item ' + data.id);
20077      }
20078      if (data.end == undefined) {
20079        throw new Error('Property "end" missing in item ' + data.id);
20080      }
20081    }
20082
20083    Item.call(this, data, conversion, options);
20084  }
20085
20086  BackgroundItem.prototype = new Item(null, null, null);
20087
20088  BackgroundItem.prototype.baseClassName = 'vis-item vis-background';
20089  BackgroundItem.prototype.stack = false;
20090
20091  /**
20092   * Check whether this item is visible inside given range
20093   * @returns {{start: Number, end: Number}} range with a timestamp for start and end
20094   * @returns {boolean} True if visible
20095   */
20096  BackgroundItem.prototype.isVisible = function (range) {
20097    // determine visibility
20098    return this.data.start < range.end && this.data.end > range.start;
20099  };
20100
20101  /**
20102   * Repaint the item
20103   */
20104  BackgroundItem.prototype.redraw = function () {
20105    var dom = this.dom;
20106    if (!dom) {
20107      // create DOM
20108      this.dom = {};
20109      dom = this.dom;
20110
20111      // background box
20112      dom.box = document.createElement('div');
20113      // className is updated in redraw()
20114
20115      // frame box (to prevent the item contents from overflowing
20116      dom.frame = document.createElement('div');
20117      dom.frame.className = 'vis-item-overflow';
20118      dom.box.appendChild(dom.frame);
20119
20120      // contents box
20121      dom.content = document.createElement('div');
20122      dom.content.className = 'vis-item-content';
20123      dom.frame.appendChild(dom.content);
20124
20125      // Note: we do NOT attach this item as attribute to the DOM,
20126      //       such that background items cannot be selected
20127      //dom.box['timeline-item'] = this;
20128
20129      this.dirty = true;
20130    }
20131
20132    // append DOM to parent DOM
20133    if (!this.parent) {
20134      throw new Error('Cannot redraw item: no parent attached');
20135    }
20136    if (!dom.box.parentNode) {
20137      var background = this.parent.dom.background;
20138      if (!background) {
20139        throw new Error('Cannot redraw item: parent has no background container element');
20140      }
20141      background.appendChild(dom.box);
20142    }
20143    this.displayed = true;
20144
20145    // Update DOM when item is marked dirty. An item is marked dirty when:
20146    // - the item is not yet rendered
20147    // - the item's data is changed
20148    // - the item is selected/deselected
20149    if (this.dirty) {
20150      this._updateContents(this.dom.content);
20151      this._updateTitle(this.dom.content);
20152      this._updateDataAttributes(this.dom.content);
20153      this._updateStyle(this.dom.box);
20154
20155      // update class
20156      var className = (this.data.className ? ' ' + this.data.className : '') + (this.selected ? ' vis-selected' : '');
20157      dom.box.className = this.baseClassName + className;
20158
20159      // determine from css whether this box has overflow
20160      this.overflow = window.getComputedStyle(dom.content).overflow !== 'hidden';
20161
20162      // recalculate size
20163      this.props.content.width = this.dom.content.offsetWidth;
20164      this.height = 0; // set height zero, so this item will be ignored when stacking items
20165
20166      this.dirty = false;
20167    }
20168  };
20169
20170  /**
20171   * Show the item in the DOM (when not already visible). The items DOM will
20172   * be created when needed.
20173   */
20174  BackgroundItem.prototype.show = RangeItem.prototype.show;
20175
20176  /**
20177   * Hide the item from the DOM (when visible)
20178   * @return {Boolean} changed
20179   */
20180  BackgroundItem.prototype.hide = RangeItem.prototype.hide;
20181
20182  /**
20183   * Reposition the item horizontally
20184   * @Override
20185   */
20186  BackgroundItem.prototype.repositionX = RangeItem.prototype.repositionX;
20187
20188  /**
20189   * Reposition the item vertically
20190   * @Override
20191   */
20192  BackgroundItem.prototype.repositionY = function (margin) {
20193    var onTop = this.options.orientation.item === 'top';
20194    this.dom.content.style.top = onTop ? '' : '0';
20195    this.dom.content.style.bottom = onTop ? '0' : '';
20196    var height;
20197
20198    // special positioning for subgroups
20199    if (this.data.subgroup !== undefined) {
20200      // TODO: instead of calculating the top position of the subgroups here for every BackgroundItem, calculate the top of the subgroup once in Itemset
20201
20202      var itemSubgroup = this.data.subgroup;
20203      var subgroups = this.parent.subgroups;
20204      var subgroupIndex = subgroups[itemSubgroup].index;
20205      // if the orientation is top, we need to take the difference in height into account.
20206      if (onTop == true) {
20207        // the first subgroup will have to account for the distance from the top to the first item.
20208        height = this.parent.subgroups[itemSubgroup].height + margin.item.vertical;
20209        height += subgroupIndex == 0 ? margin.axis - 0.5 * margin.item.vertical : 0;
20210        var newTop = this.parent.top;
20211        for (var subgroup in subgroups) {
20212          if (subgroups.hasOwnProperty(subgroup)) {
20213            if (subgroups[subgroup].visible == true && subgroups[subgroup].index < subgroupIndex) {
20214              newTop += subgroups[subgroup].height + margin.item.vertical;
20215            }
20216          }
20217        }
20218
20219        // the others will have to be offset downwards with this same distance.
20220        newTop += subgroupIndex != 0 ? margin.axis - 0.5 * margin.item.vertical : 0;
20221        this.dom.box.style.top = newTop + 'px';
20222        this.dom.box.style.bottom = '';
20223      }
20224      // and when the orientation is bottom:
20225      else {
20226        var newTop = this.parent.top;
20227        var totalHeight = 0;
20228        for (var subgroup in subgroups) {
20229          if (subgroups.hasOwnProperty(subgroup)) {
20230            if (subgroups[subgroup].visible == true) {
20231              var newHeight = subgroups[subgroup].height + margin.item.vertical;
20232              totalHeight += newHeight;
20233              if (subgroups[subgroup].index > subgroupIndex) {
20234                newTop += newHeight;
20235              }
20236            }
20237          }
20238        }
20239        height = this.parent.subgroups[itemSubgroup].height + margin.item.vertical;
20240        this.dom.box.style.top = this.parent.height - totalHeight + newTop + 'px';
20241        this.dom.box.style.bottom = '';
20242      }
20243    }
20244    // and in the case of no subgroups:
20245    else {
20246      // we want backgrounds with groups to only show in groups.
20247      if (this.parent instanceof BackgroundGroup) {
20248        // if the item is not in a group:
20249        height = Math.max(this.parent.height, this.parent.itemSet.body.domProps.center.height, this.parent.itemSet.body.domProps.centerContainer.height);
20250        this.dom.box.style.top = onTop ? '0' : '';
20251        this.dom.box.style.bottom = onTop ? '' : '0';
20252      } else {
20253        height = this.parent.height;
20254        // same alignment for items when orientation is top or bottom
20255        this.dom.box.style.top = this.parent.top + 'px';
20256        this.dom.box.style.bottom = '';
20257      }
20258    }
20259    this.dom.box.style.height = height + 'px';
20260  };
20261
vendor: 26,125 bytes, lines 20262-21046
20262  module.exports = BackgroundItem;
20263
20264/***/ },
20265/* 39 */
20266/***/ function(module, exports, __webpack_require__) {
20267
20268  'use strict';
20269
20270  var util = __webpack_require__(1);
20271  var Component = __webpack_require__(26);
20272  var TimeStep = __webpack_require__(30);
20273  var DateUtil = __webpack_require__(27);
20274  var moment = __webpack_require__(2);
20275
20276  /**
20277   * A horizontal time axis
20278   * @param {{dom: Object, domProps: Object, emitter: Emitter, range: Range}} body
20279   * @param {Object} [options]        See TimeAxis.setOptions for the available
20280   *                                  options.
20281   * @constructor TimeAxis
20282   * @extends Component
20283   */
20284  function TimeAxis(body, options) {
20285    this.dom = {
20286      foreground: null,
20287      lines: [],
20288      majorTexts: [],
20289      minorTexts: [],
20290      redundant: {
20291        lines: [],
20292        majorTexts: [],
20293        minorTexts: []
20294      }
20295    };
20296    this.props = {
20297      range: {
20298        start: 0,
20299        end: 0,
20300        minimumStep: 0
20301      },
20302      lineTop: 0
20303    };
20304
20305    this.defaultOptions = {
20306      orientation: {
20307        axis: 'bottom'
20308      }, // axis orientation: 'top' or 'bottom'
20309      showMinorLabels: true,
20310      showMajorLabels: true,
20311      format: TimeStep.FORMAT,
20312      moment: moment,
20313      timeAxis: null
20314    };
20315    this.options = util.extend({}, this.defaultOptions);
20316
20317    this.body = body;
20318
20319    // create the HTML DOM
20320    this._create();
20321
20322    this.setOptions(options);
20323  }
20324
20325  TimeAxis.prototype = new Component();
20326
20327  /**
20328   * Set options for the TimeAxis.
20329   * Parameters will be merged in current options.
20330   * @param {Object} options  Available options:
20331   *                          {string} [orientation.axis]
20332   *                          {boolean} [showMinorLabels]
20333   *                          {boolean} [showMajorLabels]
20334   */
20335  TimeAxis.prototype.setOptions = function (options) {
20336    if (options) {
20337      // copy all options that we know
20338      util.selectiveExtend(['showMinorLabels', 'showMajorLabels', 'hiddenDates', 'timeAxis', 'moment'], this.options, options);
20339
20340      // deep copy the format options
20341      util.selectiveDeepExtend(['format'], this.options, options);
20342
20343      if ('orientation' in options) {
20344        if (typeof options.orientation === 'string') {
20345          this.options.orientation.axis = options.orientation;
20346        } else if (typeof options.orientation === 'object' && 'axis' in options.orientation) {
20347          this.options.orientation.axis = options.orientation.axis;
20348        }
20349      }
20350
20351      // apply locale to moment.js
20352      // TODO: not so nice, this is applied globally to moment.js
20353      if ('locale' in options) {
20354        if (typeof moment.locale === 'function') {
20355          // moment.js 2.8.1+
20356          moment.locale(options.locale);
20357        } else {
20358          moment.lang(options.locale);
20359        }
20360      }
20361    }
20362  };
20363
20364  /**
20365   * Create the HTML DOM for the TimeAxis
20366   */
20367  TimeAxis.prototype._create = function () {
20368    this.dom.foreground = document.createElement('div');
20369    this.dom.background = document.createElement('div');
20370
20371    this.dom.foreground.className = 'vis-time-axis vis-foreground';
20372    this.dom.background.className = 'vis-time-axis vis-background';
20373  };
20374
20375  /**
20376   * Destroy the TimeAxis
20377   */
20378  TimeAxis.prototype.destroy = function () {
20379    // remove from DOM
20380    if (this.dom.foreground.parentNode) {
20381      this.dom.foreground.parentNode.removeChild(this.dom.foreground);
20382    }
20383    if (this.dom.background.parentNode) {
20384      this.dom.background.parentNode.removeChild(this.dom.background);
20385    }
20386
20387    this.body = null;
20388  };
20389
20390  /**
20391   * Repaint the component
20392   * @return {boolean} Returns true if the component is resized
20393   */
20394  TimeAxis.prototype.redraw = function () {
20395    var props = this.props;
20396    var foreground = this.dom.foreground;
20397    var background = this.dom.background;
20398
20399    // determine the correct parent DOM element (depending on option orientation)
20400    var parent = this.options.orientation.axis == 'top' ? this.body.dom.top : this.body.dom.bottom;
20401    var parentChanged = foreground.parentNode !== parent;
20402
20403    // calculate character width and height
20404    this._calculateCharSize();
20405
20406    // TODO: recalculate sizes only needed when parent is resized or options is changed
20407    var showMinorLabels = this.options.showMinorLabels && this.options.orientation.axis !== 'none';
20408    var showMajorLabels = this.options.showMajorLabels && this.options.orientation.axis !== 'none';
20409
20410    // determine the width and height of the elemens for the axis
20411    props.minorLabelHeight = showMinorLabels ? props.minorCharHeight : 0;
20412    props.majorLabelHeight = showMajorLabels ? props.majorCharHeight : 0;
20413    props.height = props.minorLabelHeight + props.majorLabelHeight;
20414    props.width = foreground.offsetWidth;
20415
20416    props.minorLineHeight = this.body.domProps.root.height - props.majorLabelHeight - (this.options.orientation.axis == 'top' ? this.body.domProps.bottom.height : this.body.domProps.top.height);
20417    props.minorLineWidth = 1; // TODO: really calculate width
20418    props.majorLineHeight = props.minorLineHeight + props.majorLabelHeight;
20419    props.majorLineWidth = 1; // TODO: really calculate width
20420
20421    //  take foreground and background offline while updating (is almost twice as fast)
20422    var foregroundNextSibling = foreground.nextSibling;
20423    var backgroundNextSibling = background.nextSibling;
20424    foreground.parentNode && foreground.parentNode.removeChild(foreground);
20425    background.parentNode && background.parentNode.removeChild(background);
20426
20427    foreground.style.height = this.props.height + 'px';
20428
20429    this._repaintLabels();
20430
20431    // put DOM online again (at the same place)
20432    if (foregroundNextSibling) {
20433      parent.insertBefore(foreground, foregroundNextSibling);
20434    } else {
20435      parent.appendChild(foreground);
20436    }
20437    if (backgroundNextSibling) {
20438      this.body.dom.backgroundVertical.insertBefore(background, backgroundNextSibling);
20439    } else {
20440      this.body.dom.backgroundVertical.appendChild(background);
20441    }
20442
20443    return this._isResized() || parentChanged;
20444  };
20445
20446  /**
20447   * Repaint major and minor text labels and vertical grid lines
20448   * @private
20449   */
20450  TimeAxis.prototype._repaintLabels = function () {
20451    var orientation = this.options.orientation.axis;
20452
20453    // calculate range and step (step such that we have space for 7 characters per label)
20454    var start = util.convert(this.body.range.start, 'Number');
20455    var end = util.convert(this.body.range.end, 'Number');
20456    var timeLabelsize = this.body.util.toTime((this.props.minorCharWidth || 10) * 7).valueOf();
20457    var minimumStep = timeLabelsize - DateUtil.getHiddenDurationBefore(this.options.moment, this.body.hiddenDates, this.body.range, timeLabelsize);
20458    minimumStep -= this.body.util.toTime(0).valueOf();
20459
20460    var step = new TimeStep(new Date(start), new Date(end), minimumStep, this.body.hiddenDates);
20461    step.setMoment(this.options.moment);
20462    if (this.options.format) {
20463      step.setFormat(this.options.format);
20464    }
20465    if (this.options.timeAxis) {
20466      step.setScale(this.options.timeAxis);
20467    }
20468    this.step = step;
20469
20470    // Move all DOM elements to a "redundant" list, where they
20471    // can be picked for re-use, and clear the lists with lines and texts.
20472    // At the end of the function _repaintLabels, left over elements will be cleaned up
20473    var dom = this.dom;
20474    dom.redundant.lines = dom.lines;
20475    dom.redundant.majorTexts = dom.majorTexts;
20476    dom.redundant.minorTexts = dom.minorTexts;
20477    dom.lines = [];
20478    dom.majorTexts = [];
20479    dom.minorTexts = [];
20480
20481    var current;
20482    var next;
20483    var x;
20484    var xNext;
20485    var isMajor;
20486    var width;
20487    var line;
20488    var labelMinor;
20489    var xFirstMajorLabel = undefined;
20490    var max = 0;
20491    var className;
20492
20493    step.start();
20494    next = step.getCurrent();
20495    xNext = this.body.util.toScreen(next);
20496    while (step.hasNext() && max < 1000) {
20497      max++;
20498
20499      isMajor = step.isMajor();
20500      className = step.getClassName();
20501      labelMinor = step.getLabelMinor();
20502
20503      current = next;
20504      x = xNext;
20505
20506      step.next();
20507      next = step.getCurrent();
20508      xNext = this.body.util.toScreen(next);
20509
20510      width = xNext - x;
20511      var labelFits = (labelMinor.length + 1) * this.props.minorCharWidth < width;
20512
20513      if (this.options.showMinorLabels && labelFits) {
20514        this._repaintMinorText(x, labelMinor, orientation, className);
20515      }
20516
20517      if (isMajor && this.options.showMajorLabels) {
20518        if (x > 0) {
20519          if (xFirstMajorLabel == undefined) {
20520            xFirstMajorLabel = x;
20521          }
20522          this._repaintMajorText(x, step.getLabelMajor(), orientation, className);
20523        }
20524        line = this._repaintMajorLine(x, width, orientation, className);
20525      } else {
20526        if (labelFits) {
20527          line = this._repaintMinorLine(x, width, orientation, className);
20528        } else {
20529          if (line) {
20530            line.style.width = parseInt(line.style.width) + width + 'px';
20531          }
20532        }
20533      }
20534    }
20535
20536    // create a major label on the left when needed
20537    if (this.options.showMajorLabels) {
20538      var leftTime = this.body.util.toTime(0),
20539          leftText = step.getLabelMajor(leftTime),
20540          widthText = leftText.length * (this.props.majorCharWidth || 10) + 10; // upper bound estimation
20541
20542      if (xFirstMajorLabel == undefined || widthText < xFirstMajorLabel) {
20543        this._repaintMajorText(0, leftText, orientation, className);
20544      }
20545    }
20546
20547    // Cleanup leftover DOM elements from the redundant list
20548    util.forEach(this.dom.redundant, function (arr) {
20549      while (arr.length) {
20550        var elem = arr.pop();
20551        if (elem && elem.parentNode) {
20552          elem.parentNode.removeChild(elem);
20553        }
20554      }
20555    });
20556  };
20557
20558  /**
20559   * Create a minor label for the axis at position x
20560   * @param {Number} x
20561   * @param {String} text
20562   * @param {String} orientation   "top" or "bottom" (default)
20563   * @param {String} className
20564   * @return {Element} Returns the HTML element of the created label
20565   * @private
20566   */
20567  TimeAxis.prototype._repaintMinorText = function (x, text, orientation, className) {
20568    // reuse redundant label
20569    var label = this.dom.redundant.minorTexts.shift();
20570
20571    if (!label) {
20572      // create new label
20573      var content = document.createTextNode('');
20574      label = document.createElement('div');
20575      label.appendChild(content);
20576      this.dom.foreground.appendChild(label);
20577    }
20578    this.dom.minorTexts.push(label);
20579
20580    label.childNodes[0].nodeValue = text;
20581
20582    label.style.top = orientation == 'top' ? this.props.majorLabelHeight + 'px' : '0';
20583    label.style.left = x + 'px';
20584    label.className = 'vis-text vis-minor ' + className;
20585    //label.title = title;  // TODO: this is a heavy operation
20586
20587    return label;
20588  };
20589
20590  /**
20591   * Create a Major label for the axis at position x
20592   * @param {Number} x
20593   * @param {String} text
20594   * @param {String} orientation   "top" or "bottom" (default)
20595   * @param {String} className
20596   * @return {Element} Returns the HTML element of the created label
20597   * @private
20598   */
20599  TimeAxis.prototype._repaintMajorText = function (x, text, orientation, className) {
20600    // reuse redundant label
20601    var label = this.dom.redundant.majorTexts.shift();
20602
20603    if (!label) {
20604      // create label
20605      var content = document.createTextNode(text);
20606      label = document.createElement('div');
20607      label.appendChild(content);
20608      this.dom.foreground.appendChild(label);
20609    }
20610    this.dom.majorTexts.push(label);
20611
20612    label.childNodes[0].nodeValue = text;
20613    label.className = 'vis-text vis-major ' + className;
20614    //label.title = title; // TODO: this is a heavy operation
20615
20616    label.style.top = orientation == 'top' ? '0' : this.props.minorLabelHeight + 'px';
20617    label.style.left = x + 'px';
20618
20619    return label;
20620  };
20621
20622  /**
20623   * Create a minor line for the axis at position x
20624   * @param {Number} x
20625   * @param {Number} width
20626   * @param {String} orientation   "top" or "bottom" (default)
20627   * @param {String} className
20628   * @return {Element} Returns the created line
20629   * @private
20630   */
20631  TimeAxis.prototype._repaintMinorLine = function (x, width, orientation, className) {
20632    // reuse redundant line
20633    var line = this.dom.redundant.lines.shift();
20634    if (!line) {
20635      // create vertical line
20636      line = document.createElement('div');
20637      this.dom.background.appendChild(line);
20638    }
20639    this.dom.lines.push(line);
20640
20641    var props = this.props;
20642    if (orientation == 'top') {
20643      line.style.top = props.majorLabelHeight + 'px';
20644    } else {
20645      line.style.top = this.body.domProps.top.height + 'px';
20646    }
20647    line.style.height = props.minorLineHeight + 'px';
20648    line.style.left = x - props.minorLineWidth / 2 + 'px';
20649    line.style.width = width + 'px';
20650
20651    line.className = 'vis-grid vis-vertical vis-minor ' + className;
20652
20653    return line;
20654  };
20655
20656  /**
20657   * Create a Major line for the axis at position x
20658   * @param {Number} x
20659   * @param {Number} width
20660   * @param {String} orientation   "top" or "bottom" (default)
20661   * @param {String} className
20662   * @return {Element} Returns the created line
20663   * @private
20664   */
20665  TimeAxis.prototype._repaintMajorLine = function (x, width, orientation, className) {
20666    // reuse redundant line
20667    var line = this.dom.redundant.lines.shift();
20668    if (!line) {
20669      // create vertical line
20670      line = document.createElement('div');
20671      this.dom.background.appendChild(line);
20672    }
20673    this.dom.lines.push(line);
20674
20675    var props = this.props;
20676    if (orientation == 'top') {
20677      line.style.top = '0';
20678    } else {
20679      line.style.top = this.body.domProps.top.height + 'px';
20680    }
20681    line.style.left = x - props.majorLineWidth / 2 + 'px';
20682    line.style.height = props.majorLineHeight + 'px';
20683    line.style.width = width + 'px';
20684
20685    line.className = 'vis-grid vis-vertical vis-major ' + className;
20686
20687    return line;
20688  };
20689
20690  /**
20691   * Determine the size of text on the axis (both major and minor axis).
20692   * The size is calculated only once and then cached in this.props.
20693   * @private
20694   */
20695  TimeAxis.prototype._calculateCharSize = function () {
20696    // Note: We calculate char size with every redraw. Size may change, for
20697    // example when any of the timelines parents had display:none for example.
20698
20699    // determine the char width and height on the minor axis
20700    if (!this.dom.measureCharMinor) {
20701      this.dom.measureCharMinor = document.createElement('DIV');
20702      this.dom.measureCharMinor.className = 'vis-text vis-minor vis-measure';
20703      this.dom.measureCharMinor.style.position = 'absolute';
20704
20705      this.dom.measureCharMinor.appendChild(document.createTextNode('0'));
20706      this.dom.foreground.appendChild(this.dom.measureCharMinor);
20707    }
20708    this.props.minorCharHeight = this.dom.measureCharMinor.clientHeight;
20709    this.props.minorCharWidth = this.dom.measureCharMinor.clientWidth;
20710
20711    // determine the char width and height on the major axis
20712    if (!this.dom.measureCharMajor) {
20713      this.dom.measureCharMajor = document.createElement('DIV');
20714      this.dom.measureCharMajor.className = 'vis-text vis-major vis-measure';
20715      this.dom.measureCharMajor.style.position = 'absolute';
20716
20717      this.dom.measureCharMajor.appendChild(document.createTextNode('0'));
20718      this.dom.foreground.appendChild(this.dom.measureCharMajor);
20719    }
20720    this.props.majorCharHeight = this.dom.measureCharMajor.clientHeight;
20721    this.props.majorCharWidth = this.dom.measureCharMajor.clientWidth;
20722  };
20723
20724  module.exports = TimeAxis;
20725
20726/***/ },
20727/* 40 */
20728/***/ function(module, exports, __webpack_require__) {
20729
20730  'use strict';
20731
20732  var keycharm = __webpack_require__(41);
20733  var Emitter = __webpack_require__(12);
20734  var Hammer = __webpack_require__(20);
20735  var util = __webpack_require__(1);
20736
20737  /**
20738   * Turn an element into an clickToUse element.
20739   * When not active, the element has a transparent overlay. When the overlay is
20740   * clicked, the mode is changed to active.
20741   * When active, the element is displayed with a blue border around it, and
20742   * the interactive contents of the element can be used. When clicked outside
20743   * the element, the elements mode is changed to inactive.
20744   * @param {Element} container
20745   * @constructor
20746   */
20747  function Activator(container) {
20748    this.active = false;
20749
20750    this.dom = {
20751      container: container
20752    };
20753
20754    this.dom.overlay = document.createElement('div');
20755    this.dom.overlay.className = 'vis-overlay';
20756
20757    this.dom.container.appendChild(this.dom.overlay);
20758
20759    this.hammer = Hammer(this.dom.overlay);
20760    this.hammer.on('tap', this._onTapOverlay.bind(this));
20761
20762    // block all touch events (except tap)
20763    var me = this;
20764    var events = ['tap', 'doubletap', 'press', 'pinch', 'pan', 'panstart', 'panmove', 'panend'];
20765    events.forEach(function (event) {
20766      me.hammer.on(event, function (event) {
20767        event.stopPropagation();
20768      });
20769    });
20770
20771    // attach a click event to the window, in order to deactivate when clicking outside the timeline
20772    if (document && document.body) {
20773      this.onClick = function (event) {
20774        if (!_hasParent(event.target, container)) {
20775          me.deactivate();
20776        }
20777      };
20778      document.body.addEventListener('click', this.onClick);
20779    }
20780
20781    if (this.keycharm !== undefined) {
20782      this.keycharm.destroy();
20783    }
20784    this.keycharm = keycharm();
20785
20786    // keycharm listener only bounded when active)
20787    this.escListener = this.deactivate.bind(this);
20788  }
20789
20790  // turn into an event emitter
20791  Emitter(Activator.prototype);
20792
20793  // The currently active activator
20794  Activator.current = null;
20795
20796  /**
20797   * Destroy the activator. Cleans up all created DOM and event listeners
20798   */
20799  Activator.prototype.destroy = function () {
20800    this.deactivate();
20801
20802    // remove dom
20803    this.dom.overlay.parentNode.removeChild(this.dom.overlay);
20804
20805    // remove global event listener
20806    if (this.onClick) {
20807      document.body.removeEventListener('click', this.onClick);
20808    }
20809
20810    // cleanup hammer instances
20811    this.hammer.destroy();
20812    this.hammer = null;
20813    // FIXME: cleaning up hammer instances doesn't work (Timeline not removed from memory)
20814  };
20815
20816  /**
20817   * Activate the element
20818   * Overlay is hidden, element is decorated with a blue shadow border
20819   */
20820  Activator.prototype.activate = function () {
20821    // we allow only one active activator at a time
20822    if (Activator.current) {
20823      Activator.current.deactivate();
20824    }
20825    Activator.current = this;
20826
20827    this.active = true;
20828    this.dom.overlay.style.display = 'none';
20829    util.addClassName(this.dom.container, 'vis-active');
20830
20831    this.emit('change');
20832    this.emit('activate');
20833
20834    // ugly hack: bind ESC after emitting the events, as the Network rebinds all
20835    // keyboard events on a 'change' event
20836    this.keycharm.bind('esc', this.escListener);
20837  };
20838
20839  /**
20840   * Deactivate the element
20841   * Overlay is displayed on top of the element
20842   */
20843  Activator.prototype.deactivate = function () {
20844    this.active = false;
20845    this.dom.overlay.style.display = '';
20846    util.removeClassName(this.dom.container, 'vis-active');
20847    this.keycharm.unbind('esc', this.escListener);
20848
20849    this.emit('change');
20850    this.emit('deactivate');
20851  };
20852
20853  /**
20854   * Handle a tap event: activate the container
20855   * @param event
20856   * @private
20857   */
20858  Activator.prototype._onTapOverlay = function (event) {
20859    // activate the container
20860    this.activate();
20861    event.stopPropagation();
20862  };
20863
20864  /**
20865   * Test whether the element has the requested parent element somewhere in
20866   * its chain of parent nodes.
20867   * @param {HTMLElement} element
20868   * @param {HTMLElement} parent
20869   * @returns {boolean} Returns true when the parent is found somewhere in the
20870   *                    chain of parent nodes.
20871   * @private
20872   */
20873  function _hasParent(element, parent) {
20874    while (element) {
20875      if (element === parent) {
20876        return true;
20877      }
20878      element = element.parentNode;
20879    }
20880    return false;
20881  }
20882
20883  module.exports = Activator;
20884
20885/***/ },
20886/* 41 */
20887/***/ function(module, exports, __webpack_require__) {
20888
20889  var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;"use strict";
20890  /**
20891   * Created by Alex on 11/6/2014.
20892   */
20893
20894  // https://github.com/umdjs/umd/blob/master/returnExports.js#L40-L60
20895  // if the module has no dependencies, the above pattern can be simplified to
20896  (function (root, factory) {
20897    if (true) {
20898      // AMD. Register as an anonymous module.
20899      !(__WEBPACK_AMD_DEFINE_ARRAY__ = [], __WEBPACK_AMD_DEFINE_FACTORY__ = (factory), __WEBPACK_AMD_DEFINE_RESULT__ = (typeof __WEBPACK_AMD_DEFINE_FACTORY__ === 'function' ? (__WEBPACK_AMD_DEFINE_FACTORY__.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__)) : __WEBPACK_AMD_DEFINE_FACTORY__), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));
20900    } else if (typeof exports === 'object') {
20901      // Node. Does not work with strict CommonJS, but
20902      // only CommonJS-like environments that support module.exports,
20903      // like Node.
20904      module.exports = factory();
20905    } else {
20906      // Browser globals (root is window)
20907      root.keycharm = factory();
20908    }
20909  }(this, function () {
20910
20911    function keycharm(options) {
20912      var preventDefault = options && options.preventDefault || false;
20913
20914      var container = options && options.container || window;
20915
20916      var _exportFunctions = {};
20917      var _bound = {keydown:{}, keyup:{}};
20918      var _keys = {};
20919      var i;
20920
20921      // a - z
20922      for (i = 97; i <= 122; i++) {_keys[String.fromCharCode(i)] = {code:65 + (i - 97), shift: false};}
20923      // A - Z
20924      for (i = 65; i <= 90; i++) {_keys[String.fromCharCode(i)] = {code:i, shift: true};}
20925      // 0 - 9
20926      for (i = 0;  i <= 9;   i++) {_keys['' + i] = {code:48 + i, shift: false};}
20927      // F1 - F12
20928      for (i = 1;  i <= 12;   i++) {_keys['F' + i] = {code:111 + i, shift: false};}
20929      // num0 - num9
20930      for (i = 0;  i <= 9;   i++) {_keys['num' + i] = {code:96 + i, shift: false};}
20931
20932      // numpad misc
20933      _keys['num*'] = {code:106, shift: false};
20934      _keys['num+'] = {code:107, shift: false};
20935      _keys['num-'] = {code:109, shift: false};
20936      _keys['num/'] = {code:111, shift: false};
20937      _keys['num.'] = {code:110, shift: false};
20938      // arrows
20939      _keys['left']  = {code:37, shift: false};
20940      _keys['up']    = {code:38, shift: false};
20941      _keys['right'] = {code:39, shift: false};
20942      _keys['down']  = {code:40, shift: false};
20943      // extra keys
20944      _keys['space'] = {code:32, shift: false};
20945      _keys['enter'] = {code:13, shift: false};
20946      _keys['shift'] = {code:16, shift: undefined};
20947      _keys['esc']   = {code:27, shift: false};
20948      _keys['backspace'] = {code:8, shift: false};
20949      _keys['tab']       = {code:9, shift: false};
20950      _keys['ctrl']      = {code:17, shift: false};
20951      _keys['alt']       = {code:18, shift: false};
20952      _keys['delete']    = {code:46, shift: false};
20953      _keys['pageup']    = {code:33, shift: false};
20954      _keys['pagedown']  = {code:34, shift: false};
20955      // symbols
20956      _keys['=']     = {code:187, shift: false};
20957      _keys['-']     = {code:189, shift: false};
20958      _keys[']']     = {code:221, shift: false};
20959      _keys['[']     = {code:219, shift: false};
20960
20961
20962
20963      var down = function(event) {handleEvent(event,'keydown');};
20964      var up = function(event) {handleEvent(event,'keyup');};
20965
20966      // handle the actualy bound key with the event
20967      var handleEvent = function(event,type) {
20968        if (_bound[type][event.keyCode] !== undefined) {
20969          var bound = _bound[type][event.keyCode];
20970          for (var i = 0; i < bound.length; i++) {
20971            if (bound[i].shift === undefined) {
20972              bound[i].fn(event);
20973            }
20974            else if (bound[i].shift == true && event.shiftKey == true) {
20975              bound[i].fn(event);
20976            }
20977            else if (bound[i].shift == false && event.shiftKey == false) {
20978              bound[i].fn(event);
20979            }
20980          }
20981
20982          if (preventDefault == true) {
20983            event.preventDefault();
20984          }
20985        }
20986      };
20987
20988      // bind a key to a callback
20989      _exportFunctions.bind = function(key, callback, type) {
20990        if (type === undefined) {
20991          type = 'keydown';
20992        }
20993        if (_keys[key] === undefined) {
20994          throw new Error("unsupported key: " + key);
20995        }
20996        if (_bound[type][_keys[key].code] === undefined) {
20997          _bound[type][_keys[key].code] = [];
20998        }
20999        _bound[type][_keys[key].code].push({fn:callback, shift:_keys[key].shift});
21000      };
21001
21002
21003      // bind all keys to a call back (demo purposes)
21004      _exportFunctions.bindAll = function(callback, type) {
21005        if (type === undefined) {
21006          type = 'keydown';
21007        }
21008        for (var key in _keys) {
21009          if (_keys.hasOwnProperty(key)) {
21010            _exportFunctions.bind(key,callback,type);
21011          }
21012        }
21013      };
21014
21015      // get the key label from an event
21016      _exportFunctions.getKey = function(event) {
21017        for (var key in _keys) {
21018          if (_keys.hasOwnProperty(key)) {
21019            if (event.shiftKey == true && _keys[key].shift == true && event.keyCode == _keys[key].code) {
21020              return key;
21021            }
21022            else if (event.shiftKey == false && _keys[key].shift == false && event.keyCode == _keys[key].code) {
21023              return key;
21024            }
21025            else if (event.keyCode == _keys[key].code && key == 'shift') {
21026              return key;
21027            }
21028          }
21029        }
21030        return "unknown key, currently not supported";
21031      };
21032
21033      // unbind either a specific callback from a key or all of them (by leaving callback undefined)
21034      _exportFunctions.unbind = function(key, callback, type) {
21035        if (type === undefined) {
21036          type = 'keydown';
21037        }
21038        if (_keys[key] === undefined) {
21039          throw new Error("unsupported key: " + key);
21040        }
21041        if (callback !== undefined) {
21042          var newBindings = [];
21043          var bound = _bound[type][_keys[key].code];
21044          if (bound !== undefined) {
21045            for (var i = 0; i < bound.length; i++) {
21046              if (!(bound[i].fn == callback && bound[i].shift == _keys[key].shift)) {
21047                newBindings.push(_bound[type][_keys[key].code][i]);
21048              }
21049            }
21050          }
21051          _bound[type][_keys[key].code] = newBindings;
21052        }
21053        else {
21054          _bound[type][_keys[key].code] = [];
21055        }
21056      };
21057
21058      // reset all bound variables.
21059      _exportFunctions.reset = function() {
21060        _bound = {keydown:{}, keyup:{}};
21061      };
21062
21063      // unbind all listeners and reset all variables.
21064      _exportFunctions.destroy = function() {
21065        _bound = {keydown:{}, keyup:{}};
21066        container.removeEventListener('keydown', down, true);
21067        container.removeEventListener('keyup', up, true);
21068      };
21069
21070      // create listeners.
21071      container.addEventListener('keydown',down,true);
21072      container.addEventListener('keyup',up,true);
21073
21074      // return the public functions.
21075      return _exportFunctions;
21076    }
21077
21078    return keycharm;
21079  }));
21080
21081
21082
21083
21084/***/ },
21085/* 42 */
21086/***/ function(module, exports, __webpack_require__) {
21087
21088  'use strict';
21089
21090  var Hammer = __webpack_require__(20);
21091  var util = __webpack_require__(1);
21092  var Component = __webpack_require__(26);
21093  var moment = __webpack_require__(2);
21094  var locales = __webpack_require__(43);
21095
21096  /**
21097   * A custom time bar
21098   * @param {{range: Range, dom: Object}} body
21099   * @param {Object} [options]        Available parameters:
21100   *                                  {number | string} id
21101   *                                  {string} locales
21102   *                                  {string} locale
21103   * @constructor CustomTime
21104   * @extends Component
21105   */
21106
21107  function CustomTime(body, options) {
21108    this.body = body;
21109
21110    // default options
21111    this.defaultOptions = {
21112      moment: moment,
21113      locales: locales,
21114      locale: 'en',
21115      id: undefined,
21116      title: undefined
21117    };
21118    this.options = util.extend({}, this.defaultOptions);
21119
21120    if (options && options.time) {
21121      this.customTime = options.time;
21122    } else {
21123      this.customTime = new Date();
21124    }
21125
21126    this.eventParams = {}; // stores state parameters while dragging the bar
21127
21128    this.setOptions(options);
21129
21130    // create the DOM
21131    this._create();
21132  }
21133
21134  CustomTime.prototype = new Component();
21135
21136  /**
21137   * Set options for the component. Options will be merged in current options.
21138   * @param {Object} options  Available parameters:
21139   *                                  {number | string} id
21140   *                                  {string} locales
21141   *                                  {string} locale
21142   */
21143  CustomTime.prototype.setOptions = function (options) {
21144    if (options) {
21145      // copy all options that we know
21146      util.selectiveExtend(['moment', 'locale', 'locales', 'id'], this.options, options);
21147    }
21148  };
21149
21150  /**
21151   * Create the DOM for the custom time
21152   * @private
21153   */
21154  CustomTime.prototype._create = function () {
21155    var bar = document.createElement('div');
21156    bar['custom-time'] = this;
21157    bar.className = 'vis-custom-time ' + (this.options.id || '');
21158    bar.style.position = 'absolute';
21159    bar.style.top = '0px';
21160    bar.style.height = '100%';
21161    this.bar = bar;
21162
21163    var drag = document.createElement('div');
21164    drag.style.position = 'relative';
21165    drag.style.top = '0px';
21166    drag.style.left = '-10px';
21167    drag.style.height = '100%';
21168    drag.style.width = '20px';
21169    bar.appendChild(drag);
21170
21171    // attach event listeners
21172    this.hammer = new Hammer(drag);
21173    this.hammer.on('panstart', this._onDragStart.bind(this));
21174    this.hammer.on('panmove', this._onDrag.bind(this));
21175    this.hammer.on('panend', this._onDragEnd.bind(this));
21176    this.hammer.get('pan').set({ threshold: 5, direction: 30 }); // 30 is ALL_DIRECTIONS in hammer.
21177  };
21178
21179  /**
21180   * Destroy the CustomTime bar
21181   */
21182  CustomTime.prototype.destroy = function () {
21183    this.hide();
21184
21185    this.hammer.destroy();
21186    this.hammer = null;
21187
21188    this.body = null;
21189  };
21190
21191  /**
21192   * Repaint the component
21193   * @return {boolean} Returns true if the component is resized
21194   */
21195  CustomTime.prototype.redraw = function () {
21196    var parent = this.body.dom.backgroundVertical;
21197    if (this.bar.parentNode != parent) {
21198      // attach to the dom
21199      if (this.bar.parentNode) {
21200        this.bar.parentNode.removeChild(this.bar);
21201      }
21202      parent.appendChild(this.bar);
21203    }
21204
21205    var x = this.body.util.toScreen(this.customTime);
21206
21207    var locale = this.options.locales[this.options.locale];
21208    if (!locale) {
21209      if (!this.warned) {
21210        console.log('WARNING: options.locales[\'' + this.options.locale + '\'] not found. See http://visjs.org/docs/timeline.html#Localization');
21211        this.warned = true;
21212      }
21213      locale = this.options.locales['en']; // fall back on english when not available
21214    }
21215
21216    var title = this.options.title;
21217    // To hide the title completely use empty string ''.
21218    if (title === undefined) {
21219      title = locale.time + ': ' + this.options.moment(this.customTime).format('dddd, MMMM Do YYYY, H:mm:ss');
21220      title = title.charAt(0).toUpperCase() + title.substring(1);
21221    }
21222
21223    this.bar.style.left = x + 'px';
21224    this.bar.title = title;
21225
21226    return false;
21227  };
21228
21229  /**
21230   * Remove the CustomTime from the DOM
21231   */
21232  CustomTime.prototype.hide = function () {
21233    // remove the line from the DOM
21234    if (this.bar.parentNode) {
21235      this.bar.parentNode.removeChild(this.bar);
21236    }
21237  };
21238
21239  /**
21240   * Set custom time.
21241   * @param {Date | number | string} time
21242   */
21243  CustomTime.prototype.setCustomTime = function (time) {
21244    this.customTime = util.convert(time, 'Date');
21245    this.redraw();
21246  };
21247
21248  /**
21249   * Retrieve the current custom time.
21250   * @return {Date} customTime
21251   */
21252  CustomTime.prototype.getCustomTime = function () {
21253    return new Date(this.customTime.valueOf());
21254  };
21255
21256  /**
21257    * Set custom title.
21258    * @param {Date | number | string} title
21259    */
21260  CustomTime.prototype.setCustomTitle = function (title) {
21261    this.options.title = title;
21262  };
21263
21264  /**
21265   * Start moving horizontally
21266   * @param {Event} event
21267   * @private
21268   */
21269  CustomTime.prototype._onDragStart = function (event) {
21270    this.eventParams.dragging = true;
21271    this.eventParams.customTime = this.customTime;
21272
21273    event.stopPropagation();
21274  };
21275
21276  /**
21277   * Perform moving operating.
21278   * @param {Event} event
21279   * @private
21280   */
21281  CustomTime.prototype._onDrag = function (event) {
21282    if (!this.eventParams.dragging) return;
21283
21284    var x = this.body.util.toScreen(this.eventParams.customTime) + event.deltaX;
21285    var time = this.body.util.toTime(x);
21286
21287    this.setCustomTime(time);
21288
21289    // fire a timechange event
21290    this.body.emitter.emit('timechange', {
21291      id: this.options.id,
21292      time: new Date(this.customTime.valueOf())
21293    });
21294
21295    event.stopPropagation();
21296  };
21297
21298  /**
21299   * Stop moving operating.
21300   * @param {Event} event
21301   * @private
21302   */
21303  CustomTime.prototype._onDragEnd = function (event) {
21304    if (!this.eventParams.dragging) return;
21305
21306    // fire a timechanged event
21307    this.body.emitter.emit('timechanged', {
21308      id: this.options.id,
21309      time: new Date(this.customTime.valueOf())
21310    });
21311
21312    event.stopPropagation();
21313  };
21314
21315  /**
21316   * Find a custom time from an event target:
21317   * searches for the attribute 'custom-time' in the event target's element tree
21318   * @param {Event} event
21319   * @return {CustomTime | null} customTime
21320   */
21321  CustomTime.customTimeFromTarget = function (event) {
21322    var target = event.target;
21323    while (target) {
21324      if (target.hasOwnProperty('custom-time')) {
21325        return target['custom-time'];
21326      }
21327      target = target.parentNode;
21328    }
21329
21330    return null;
21331  };
21332
21333  module.exports = CustomTime;
21334
21335/***/ },
21336/* 43 */
21337/***/ function(module, exports) {
21338
21339  // English
21340  'use strict';
21341
21342  exports['en'] = {
21343    current: 'current',
21344    time: 'time'
21345  };
21346  exports['en_EN'] = exports['en'];
21347  exports['en_US'] = exports['en'];
21348
21349  // Dutch
21350  exports['nl'] = {
21351    current: 'huidige',
21352    time: 'tijd'
21353  };
21354  exports['nl_NL'] = exports['nl'];
21355  exports['nl_BE'] = exports['nl'];
21356
21357/***/ },
21358/* 44 */
21359/***/ function(module, exports, __webpack_require__) {
21360
21361  'use strict';
21362
21363  var util = __webpack_require__(1);
21364  var Component = __webpack_require__(26);
21365  var moment = __webpack_require__(2);
21366  var locales = __webpack_require__(43);
21367
21368  /**
21369   * A current time bar
21370   * @param {{range: Range, dom: Object, domProps: Object}} body
21371   * @param {Object} [options]        Available parameters:
21372   *                                  {Boolean} [showCurrentTime]
21373   * @constructor CurrentTime
21374   * @extends Component
21375   */
21376  function CurrentTime(body, options) {
21377    this.body = body;
21378
21379    // default options
21380    this.defaultOptions = {
21381      showCurrentTime: true,
21382
21383      moment: moment,
21384      locales: locales,
21385      locale: 'en'
21386    };
21387    this.options = util.extend({}, this.defaultOptions);
21388    this.offset = 0;
21389
21390    this._create();
21391
21392    this.setOptions(options);
21393  }
21394
21395  CurrentTime.prototype = new Component();
21396
21397  /**
21398   * Create the HTML DOM for the current time bar
21399   * @private
21400   */
21401  CurrentTime.prototype._create = function () {
21402    var bar = document.createElement('div');
21403    bar.className = 'vis-current-time';
21404    bar.style.position = 'absolute';
21405    bar.style.top = '0px';
21406    bar.style.height = '100%';
21407
21408    this.bar = bar;
21409  };
21410
21411  /**
21412   * Destroy the CurrentTime bar
21413   */
21414  CurrentTime.prototype.destroy = function () {
21415    this.options.showCurrentTime = false;
21416    this.redraw(); // will remove the bar from the DOM and stop refreshing
21417
21418    this.body = null;
21419  };
21420
21421  /**
21422   * Set options for the component. Options will be merged in current options.
21423   * @param {Object} options  Available parameters:
21424   *                          {boolean} [showCurrentTime]
21425   */
21426  CurrentTime.prototype.setOptions = function (options) {
21427    if (options) {
21428      // copy all options that we know
21429      util.selectiveExtend(['showCurrentTime', 'moment', 'locale', 'locales'], this.options, options);
21430    }
21431  };
21432
21433  /**
21434   * Repaint the component
21435   * @return {boolean} Returns true if the component is resized
21436   */
21437  CurrentTime.prototype.redraw = function () {
21438    if (this.options.showCurrentTime) {
21439      var parent = this.body.dom.backgroundVertical;
21440      if (this.bar.parentNode != parent) {
21441        // attach to the dom
21442        if (this.bar.parentNode) {
21443          this.bar.parentNode.removeChild(this.bar);
21444        }
21445        parent.appendChild(this.bar);
21446
21447        this.start();
21448      }
21449
21450      var now = this.options.moment(new Date().valueOf() + this.offset);
21451      var x = this.body.util.toScreen(now);
21452
21453      var locale = this.options.locales[this.options.locale];
21454      if (!locale) {
21455        if (!this.warned) {
21456          console.log('WARNING: options.locales[\'' + this.options.locale + '\'] not found. See http://visjs.org/docs/timeline.html#Localization');
21457          this.warned = true;
21458        }
21459        locale = this.options.locales['en']; // fall back on english when not available
21460      }
21461      var title = locale.current + ' ' + locale.time + ': ' + now.format('dddd, MMMM Do YYYY, H:mm:ss');
21462      title = title.charAt(0).toUpperCase() + title.substring(1);
21463
21464      this.bar.style.left = x + 'px';
21465      this.bar.title = title;
21466    } else {
21467      // remove the line from the DOM
21468      if (this.bar.parentNode) {
21469        this.bar.parentNode.removeChild(this.bar);
21470      }
21471      this.stop();
21472    }
21473
21474    return false;
21475  };
21476
21477  /**
21478   * Start auto refreshing the current time bar
21479   */
21480  CurrentTime.prototype.start = function () {
21481    var me = this;
21482
21483    function update() {
21484      me.stop();
21485
21486      // determine interval to refresh
21487      var scale = me.body.range.conversion(me.body.domProps.center.width).scale;
21488      var interval = 1 / scale / 10;
21489      if (interval < 30) interval = 30;
21490      if (interval > 1000) interval = 1000;
21491
21492      me.redraw();
21493
21494      // start a renderTimer to adjust for the new time
21495      me.currentTimeTimer = setTimeout(update, interval);
21496    }
21497
21498    update();
21499  };
21500
21501  /**
21502   * Stop auto refreshing the current time bar
21503   */
21504  CurrentTime.prototype.stop = function () {
21505    if (this.currentTimeTimer !== undefined) {
21506      clearTimeout(this.currentTimeTimer);
21507      delete this.currentTimeTimer;
21508    }
21509  };
21510
21511  /**
21512   * Set a current time. This can be used for example to ensure that a client's
21513   * time is synchronized with a shared server time.
21514   * @param {Date | String | Number} time     A Date, unix timestamp, or
21515   *                                          ISO date string.
21516   */
21517  CurrentTime.prototype.setCurrentTime = function (time) {
21518    var t = util.convert(time, 'Date').valueOf();
21519    var now = new Date().valueOf();
21520    this.offset = t - now;
21521    this.redraw();
21522  };
21523
21524  /**
21525   * Get the current time.
21526   * @return {Date} Returns the current time.
21527   */
21528  CurrentTime.prototype.getCurrentTime = function () {
21529    return new Date(new Date().valueOf() + this.offset);
21530  };
21531
21532  module.exports = CurrentTime;
21533
21534/***/ },
21535/* 45 */
21536/***/ function(module, exports, __webpack_require__) {
21537
21538  'use strict';
21539
21540  Object.defineProperty(exports, '__esModule', {
21541    value: true
21542  });
21543
21544  var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
21545
21546  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
21547
21548  function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
21549
21550  var _ColorPicker = __webpack_require__(46);
21551
21552  var _ColorPicker2 = _interopRequireDefault(_ColorPicker);
21553
21554  var util = __webpack_require__(1);
21555
21556  /**
21557   * 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.
21558   * Boolean options are recognised as Boolean
21559   * Number options should be written as array: [default value, min value, max value, stepsize]
21560   * Colors should be written as array: ['color', '#ffffff']
21561   * Strings with should be written as array: [option1, option2, option3, ..]
21562   *
21563   * The options are matched with their counterparts in each of the modules and the values used in the configuration are
21564   *
21565   * @param parentModule        | the location where parentModule.setOptions() can be called
21566   * @param defaultContainer    | the default container of the module
21567   * @param configureOptions    | the fully configured and predefined options set found in allOptions.js
21568   * @param pixelRatio          | canvas pixel ratio
21569   */
21570
21571  var Configurator = (function () {
21572    function Configurator(parentModule, defaultContainer, configureOptions) {
21573      var pixelRatio = arguments.length <= 3 || arguments[3] === undefined ? 1 : arguments[3];
21574
21575      _classCallCheck(this, Configurator);
21576
21577      this.parent = parentModule;
21578      this.changedOptions = [];
21579      this.container = defaultContainer;
21580      this.allowCreation = false;
21581
21582      this.options = {};
21583      this.initialized = false;
21584      this.popupCounter = 0;
21585      this.defaultOptions = {
21586        enabled: false,
21587        filter: true,
21588        container: undefined,
21589        showButton: true
21590      };
21591      util.extend(this.options, this.defaultOptions);
21592
21593      this.configureOptions = configureOptions;
21594      this.moduleOptions = {};
21595      this.domElements = [];
21596      this.popupDiv = {};
21597      this.popupLimit = 5;
21598      this.popupHistory = {};
21599      this.colorPicker = new _ColorPicker2['default'](pixelRatio);
21600      this.wrapper = undefined;
21601    }
21602
21603    _createClass(Configurator, [{
21604      key: 'setOptions',
21605
21606      /**
21607       * refresh all options.
21608       * Because all modules parse their options by themselves, we just use their options. We copy them here.
21609       *
21610       * @param options
21611       */
21612      value: function setOptions(options) {
21613        if (options !== undefined) {
21614          // reset the popup history because the indices may have been changed.
21615          this.popupHistory = {};
21616          this._removePopup();
21617
21618          var enabled = true;
21619          if (typeof options === 'string') {
21620            this.options.filter = options;
21621          } else if (options instanceof Array) {
21622            this.options.filter = options.join();
21623          } else if (typeof options === 'object') {
21624            if (options.container !== undefined) {
21625              this.options.container = options.container;
21626            }
21627            if (options.filter !== undefined) {
21628              this.options.filter = options.filter;
21629            }
21630            if (options.showButton !== undefined) {
21631              this.options.showButton = options.showButton;
21632            }
21633            if (options.enabled !== undefined) {
21634              enabled = options.enabled;
21635            }
21636          } else if (typeof options === 'boolean') {
21637            this.options.filter = true;
21638            enabled = options;
21639          } else if (typeof options === 'function') {
21640            this.options.filter = options;
21641            enabled = true;
21642          }
21643          if (this.options.filter === false) {
21644            enabled = false;
21645          }
21646
21647          this.options.enabled = enabled;
21648        }
21649        this._clean();
21650      }
21651    }, {
21652      key: 'setModuleOptions',
21653      value: function setModuleOptions(moduleOptions) {
21654        this.moduleOptions = moduleOptions;
21655        if (this.options.enabled === true) {
21656          this._clean();
21657          if (this.options.container !== undefined) {
21658            this.container = this.options.container;
21659          }
21660          this._create();
21661        }
21662      }
21663    }, {
21664      key: '_create',
21665
21666      /**
21667       * Create all DOM elements
21668       * @private
21669       */
21670      value: function _create() {
21671        var _this = this;
21672
21673        this._clean();
21674        this.changedOptions = [];
21675
21676        var filter = this.options.filter;
21677        var counter = 0;
21678        var show = false;
21679        for (var option in this.configureOptions) {
21680          if (this.configureOptions.hasOwnProperty(option)) {
21681            this.allowCreation = false;
21682            show = false;
21683            if (typeof filter === 'function') {
21684              show = filter(option, []);
21685              show = show || this._handleObject(this.configureOptions[option], [option], true);
21686            } else if (filter === true || filter.indexOf(option) !== -1) {
21687              show = true;
21688            }
21689
21690            if (show !== false) {
21691              this.allowCreation = true;
21692
21693              // linebreak between categories
21694              if (counter > 0) {
21695                this._makeItem([]);
21696              }
21697              // a header for the category
21698              this._makeHeader(option);
21699
21700              // get the suboptions
21701              this._handleObject(this.configureOptions[option], [option]);
21702            }
21703            counter++;
21704          }
21705        }
21706
21707        if (this.options.showButton === true) {
21708          (function () {
21709            var generateButton = document.createElement('div');
21710            generateButton.className = 'vis-configuration vis-config-button';
21711            generateButton.innerHTML = 'generate options';
21712            generateButton.onclick = function () {
21713              _this._printOptions();
21714            };
21715            generateButton.onmouseover = function () {
21716              generateButton.className = 'vis-configuration vis-config-button hover';
21717            };
21718            generateButton.onmouseout = function () {
21719              generateButton.className = 'vis-configuration vis-config-button';
21720            };
21721
21722            _this.optionsContainer = document.createElement('div');
21723            _this.optionsContainer.className = 'vis-configuration vis-config-option-container';
21724
21725            _this.domElements.push(_this.optionsContainer);
21726            _this.domElements.push(generateButton);
21727          })();
21728        }
21729
21730        this._push();
21731        this.colorPicker.insertTo(this.container);
21732      }
21733    }, {
21734      key: '_push',
21735
21736      /**
21737       * draw all DOM elements on the screen
21738       * @private
21739       */
21740      value: function _push() {
21741        this.wrapper = document.createElement('div');
21742        this.wrapper.className = 'vis-configuration-wrapper';
21743        this.container.appendChild(this.wrapper);
21744        for (var i = 0; i < this.domElements.length; i++) {
21745          this.wrapper.appendChild(this.domElements[i]);
21746        }
21747
21748        this._showPopupIfNeeded();
21749      }
21750    }, {
21751      key: '_clean',
21752
21753      /**
21754       * delete all DOM elements
21755       * @private
21756       */
21757      value: function _clean() {
21758        for (var i = 0; i < this.domElements.length; i++) {
21759          this.wrapper.removeChild(this.domElements[i]);
21760        }
21761
21762        if (this.wrapper !== undefined) {
21763          this.container.removeChild(this.wrapper);
21764          this.wrapper = undefined;
21765        }
21766        this.domElements = [];
21767
21768        this._removePopup();
21769      }
21770    }, {
21771      key: '_getValue',
21772
21773      /**
21774       * get the value from the actualOptions if it exists
21775       * @param {array} path    | where to look for the actual option
21776       * @returns {*}
21777       * @private
21778       */
21779      value: function _getValue(path) {
21780        var base = this.moduleOptions;
21781        for (var i = 0; i < path.length; i++) {
21782          if (base[path[i]] !== undefined) {
21783            base = base[path[i]];
21784          } else {
21785            base = undefined;
21786            break;
21787          }
21788        }
21789        return base;
21790      }
21791    }, {
21792      key: '_makeItem',
21793
21794      /**
21795       * all option elements are wrapped in an item
21796       * @param path
21797       * @param domElements
21798       * @private
21799       */
21800      value: function _makeItem(path) {
21801        var _arguments = arguments,
21802            _this2 = this;
21803
21804        if (this.allowCreation === true) {
21805          var _len, domElements, _key;
21806
21807          var _ret2 = (function () {
21808            var item = document.createElement('div');
21809            item.className = 'vis-configuration vis-config-item vis-config-s' + path.length;
21810
21811            for (_len = _arguments.length, domElements = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
21812              domElements[_key - 1] = _arguments[_key];
21813            }
21814
21815            domElements.forEach(function (element) {
21816              item.appendChild(element);
21817            });
21818            _this2.domElements.push(item);
21819            return {
21820              v: _this2.domElements.length
21821            };
21822          })();
21823
21824          if (typeof _ret2 === 'object') return _ret2.v;
21825        }
21826        return 0;
21827      }
21828    }, {
21829      key: '_makeHeader',
21830
21831      /**
21832       * header for major subjects
21833       * @param name
21834       * @private
21835       */
21836      value: function _makeHeader(name) {
21837        var div = document.createElement('div');
21838        div.className = 'vis-configuration vis-config-header';
21839        div.innerHTML = name;
21840        this._makeItem([], div);
21841      }
21842    }, {
21843      key: '_makeLabel',
21844
21845      /**
21846       * make a label, if it is an object label, it gets different styling.
21847       * @param name
21848       * @param path
21849       * @param objectLabel
21850       * @returns {HTMLElement}
21851       * @private
21852       */
21853      value: function _makeLabel(name, path) {
21854        var objectLabel = arguments.length <= 2 || arguments[2] === undefined ? false : arguments[2];
21855
21856        var div = document.createElement('div');
21857        div.className = 'vis-configuration vis-config-label vis-config-s' + path.length;
21858        if (objectLabel === true) {
21859          div.innerHTML = '<i><b>' + name + ':</b></i>';
21860        } else {
21861          div.innerHTML = name + ':';
21862        }
21863        return div;
21864      }
21865    }, {
21866      key: '_makeDropdown',
21867
21868      /**
21869       * make a dropdown list for multiple possible string optoins
21870       * @param arr
21871       * @param value
21872       * @param path
21873       * @private
21874       */
21875      value: function _makeDropdown(arr, value, path) {
21876        var select = document.createElement('select');
21877        select.className = 'vis-configuration vis-config-select';
21878        var selectedValue = 0;
21879        if (value !== undefined) {
21880          if (arr.indexOf(value) !== -1) {
21881            selectedValue = arr.indexOf(value);
21882          }
21883        }
21884
21885        for (var i = 0; i < arr.length; i++) {
21886          var option = document.createElement('option');
21887          option.value = arr[i];
21888          if (i === selectedValue) {
21889            option.selected = 'selected';
21890          }
21891          option.innerHTML = arr[i];
21892          select.appendChild(option);
21893        }
21894
21895        var me = this;
21896        select.onchange = function () {
21897          me._update(this.value, path);
21898        };
21899
21900        var label = this._makeLabel(path[path.length - 1], path);
21901        this._makeItem(path, label, select);
21902      }
21903    }, {
21904      key: '_makeRange',
21905
21906      /**
21907       * make a range object for numeric options
21908       * @param arr
21909       * @param value
21910       * @param path
21911       * @private
21912       */
21913      value: function _makeRange(arr, value, path) {
21914        var defaultValue = arr[0];
21915        var min = arr[1];
21916        var max = arr[2];
21917        var step = arr[3];
21918        var range = document.createElement('input');
21919        range.className = 'vis-configuration vis-config-range';
21920        try {
21921          range.type = 'range'; // not supported on IE9
21922          range.min = min;
21923          range.max = max;
21924        } catch (err) {}
21925        range.step = step;
21926
21927        // set up the popup settings in case they are needed.
21928        var popupString = '';
21929        var popupValue = 0;
21930
21931        if (value !== undefined) {
21932          var factor = 1.20;
21933          if (value < 0 && value * factor < min) {
21934            range.min = Math.ceil(value * factor);
21935            popupValue = range.min;
21936            popupString = 'range increased';
21937          } else if (value / factor < min) {
21938            range.min = Math.ceil(value / factor);
21939            popupValue = range.min;
21940            popupString = 'range increased';
21941          }
21942          if (value * factor > max && max !== 1) {
21943            range.max = Math.ceil(value * factor);
21944            popupValue = range.max;
21945            popupString = 'range increased';
21946          }
21947          range.value = value;
21948        } else {
21949          range.value = defaultValue;
21950        }
21951
21952        var input = document.createElement('input');
21953        input.className = 'vis-configuration vis-config-rangeinput';
21954        input.value = range.value;
21955
21956        var me = this;
21957        range.onchange = function () {
21958          input.value = this.value;me._update(Number(this.value), path);
21959        };
21960        range.oninput = function () {
21961          input.value = this.value;
21962        };
21963
21964        var label = this._makeLabel(path[path.length - 1], path);
21965        var itemIndex = this._makeItem(path, label, range, input);
21966
21967        // if a popup is needed AND it has not been shown for this value, show it.
21968        if (popupString !== '' && this.popupHistory[itemIndex] !== popupValue) {
21969          this.popupHistory[itemIndex] = popupValue;
21970          this._setupPopup(popupString, itemIndex);
21971        }
21972      }
21973    }, {
21974      key: '_setupPopup',
21975
21976      /**
21977       * prepare the popup
21978       * @param string
21979       * @param index
21980       * @private
21981       */
21982      value: function _setupPopup(string, index) {
21983        var _this3 = this;
21984
21985        if (this.initialized === true && this.allowCreation === true && this.popupCounter < this.popupLimit) {
21986          var div = document.createElement('div');
21987          div.id = 'vis-configuration-popup';
21988          div.className = 'vis-configuration-popup';
21989          div.innerHTML = string;
21990          div.onclick = function () {
21991            _this3._removePopup();
21992          };
21993          this.popupCounter += 1;
21994          this.popupDiv = { html: div, index: index };
21995        }
21996      }
21997    }, {
21998      key: '_removePopup',
21999
22000      /**
22001       * remove the popup from the dom
22002       * @private
22003       */
22004      value: function _removePopup() {
22005        if (this.popupDiv.html !== undefined) {
22006          this.popupDiv.html.parentNode.removeChild(this.popupDiv.html);
22007          clearTimeout(this.popupDiv.hideTimeout);
22008          clearTimeout(this.popupDiv.deleteTimeout);
22009          this.popupDiv = {};
22010        }
22011      }
22012    }, {
22013      key: '_showPopupIfNeeded',
22014
22015      /**
22016       * Show the popup if it is needed.
22017       * @private
22018       */
22019      value: function _showPopupIfNeeded() {
22020        var _this4 = this;
22021
22022        if (this.popupDiv.html !== undefined) {
22023          var correspondingElement = this.domElements[this.popupDiv.index];
22024          var rect = correspondingElement.getBoundingClientRect();
22025          this.popupDiv.html.style.left = rect.left + 'px';
22026          this.popupDiv.html.style.top = rect.top - 30 + 'px'; // 30 is the height;
22027          document.body.appendChild(this.popupDiv.html);
22028          this.popupDiv.hideTimeout = setTimeout(function () {
22029            _this4.popupDiv.html.style.opacity = 0;
22030          }, 1500);
22031          this.popupDiv.deleteTimeout = setTimeout(function () {
22032            _this4._removePopup();
22033          }, 1800);
22034        }
22035      }
22036    }, {
22037      key: '_makeCheckbox',
22038
22039      /**
22040       * make a checkbox for boolean options.
22041       * @param defaultValue
22042       * @param value
22043       * @param path
22044       * @private
22045       */
22046      value: function _makeCheckbox(defaultValue, value, path) {
22047        var checkbox = document.createElement('input');
22048        checkbox.type = 'checkbox';
22049        checkbox.className = 'vis-configuration vis-config-checkbox';
22050        checkbox.checked = defaultValue;
22051        if (value !== undefined) {
22052          checkbox.checked = value;
22053          if (value !== defaultValue) {
22054            if (typeof defaultValue === 'object') {
22055              if (value !== defaultValue.enabled) {
22056                this.changedOptions.push({ path: path, value: value });
22057              }
22058            } else {
22059              this.changedOptions.push({ path: path, value: value });
22060            }
22061          }
22062        }
22063
22064        var me = this;
22065        checkbox.onchange = function () {
22066          me._update(this.checked, path);
22067        };
22068
22069        var label = this._makeLabel(path[path.length - 1], path);
22070        this._makeItem(path, label, checkbox);
22071      }
22072    }, {
22073      key: '_makeTextInput',
22074
22075      /**
22076       * make a text input field for string options.
22077       * @param defaultValue
22078       * @param value
22079       * @param path
22080       * @private
22081       */
22082      value: function _makeTextInput(defaultValue, value, path) {
22083        var checkbox = document.createElement('input');
22084        checkbox.type = 'text';
22085        checkbox.className = 'vis-configuration vis-config-text';
22086        checkbox.value = value;
22087        if (value !== defaultValue) {
22088          this.changedOptions.push({ path: path, value: value });
22089        }
22090
22091        var me = this;
22092        checkbox.onchange = function () {
22093          me._update(this.value, path);
22094        };
22095
22096        var label = this._makeLabel(path[path.length - 1], path);
22097        this._makeItem(path, label, checkbox);
22098      }
22099    }, {
22100      key: '_makeColorField',
22101
22102      /**
22103       * make a color field with a color picker for color fields
22104       * @param arr
22105       * @param value
22106       * @param path
22107       * @private
22108       */
22109      value: function _makeColorField(arr, value, path) {
22110        var _this5 = this;
22111
22112        var defaultColor = arr[1];
22113        var div = document.createElement('div');
22114        value = value === undefined ? defaultColor : value;
22115
22116        if (value !== 'none') {
22117          div.className = 'vis-configuration vis-config-colorBlock';
22118          div.style.backgroundColor = value;
22119        } else {
22120          div.className = 'vis-configuration vis-config-colorBlock none';
22121        }
22122
22123        value = value === undefined ? defaultColor : value;
22124        div.onclick = function () {
22125          _this5._showColorPicker(value, div, path);
22126        };
22127
22128        var label = this._makeLabel(path[path.length - 1], path);
22129        this._makeItem(path, label, div);
22130      }
22131    }, {
22132      key: '_showColorPicker',
22133
22134      /**
22135       * used by the color buttons to call the color picker.
22136       * @param event
22137       * @param value
22138       * @param div
22139       * @param path
22140       * @private
22141       */
22142      value: function _showColorPicker(value, div, path) {
22143        var _this6 = this;
22144
22145        var rect = div.getBoundingClientRect();
22146        var bodyRect = document.body.getBoundingClientRect();
22147        var pickerX = rect.left + rect.width + 5;
22148        var pickerY = rect.top - bodyRect.top + rect.height + 2;
22149        this.colorPicker.show(pickerX, pickerY);
22150        this.colorPicker.setColor(value);
22151        this.colorPicker.setCallback(function (color) {
22152          var colorString = 'rgba(' + color.r + ',' + color.g + ',' + color.b + ',' + color.a + ')';
22153          div.style.backgroundColor = colorString;
22154          _this6._update(colorString, path);
22155        });
22156      }
22157    }, {
22158      key: '_handleObject',
22159
22160      /**
22161       * parse an object and draw the correct items
22162       * @param obj
22163       * @param path
22164       * @private
22165       */
22166      value: function _handleObject(obj) {
22167        var path = arguments.length <= 1 || arguments[1] === undefined ? [] : arguments[1];
22168        var checkOnly = arguments.length <= 2 || arguments[2] === undefined ? false : arguments[2];
22169
22170        var show = false;
22171        var filter = this.options.filter;
22172        var visibleInSet = false;
22173        for (var subObj in obj) {
22174          if (obj.hasOwnProperty(subObj)) {
22175            show = true;
22176            var item = obj[subObj];
22177            var newPath = util.copyAndExtendArray(path, subObj);
22178            if (typeof filter === 'function') {
22179              show = filter(subObj, path);
22180
22181              // if needed we must go deeper into the object.
22182              if (show === false) {
22183                if (!(item instanceof Array) && typeof item !== 'string' && typeof item !== 'boolean' && item instanceof Object) {
22184                  this.allowCreation = false;
22185                  show = this._handleObject(item, newPath, true);
22186                  this.allowCreation = checkOnly === false;
22187                }
22188              }
22189            }
22190
22191            if (show !== false) {
22192              visibleInSet = true;
22193              var value = this._getValue(newPath);
22194
22195              if (item instanceof Array) {
22196                this._handleArray(item, value, newPath);
22197              } else if (typeof item === 'string') {
22198                this._makeTextInput(item, value, newPath);
22199              } else if (typeof item === 'boolean') {
22200                this._makeCheckbox(item, value, newPath);
22201              } else if (item instanceof Object) {
22202                // collapse the physics options that are not enabled
22203                var draw = true;
22204                if (path.indexOf('physics') !== -1) {
22205                  if (this.moduleOptions.physics.solver !== subObj) {
22206                    draw = false;
22207                  }
22208                }
22209
22210                if (draw === true) {
22211                  // initially collapse options with an disabled enabled option.
22212                  if (item.enabled !== undefined) {
22213                    var enabledPath = util.copyAndExtendArray(newPath, 'enabled');
22214                    var enabledValue = this._getValue(enabledPath);
22215                    if (enabledValue === true) {
22216                      var label = this._makeLabel(subObj, newPath, true);
22217                      this._makeItem(newPath, label);
22218                      visibleInSet = this._handleObject(item, newPath) || visibleInSet;
22219                    } else {
22220                      this._makeCheckbox(item, enabledValue, newPath);
22221                    }
22222                  } else {
22223                    var label = this._makeLabel(subObj, newPath, true);
22224                    this._makeItem(newPath, label);
22225                    visibleInSet = this._handleObject(item, newPath) || visibleInSet;
22226                  }
22227                }
22228              } else {
22229                console.error('dont know how to handle', item, subObj, newPath);
22230              }
22231            }
22232          }
22233        }
22234        return visibleInSet;
22235      }
22236    }, {
22237      key: '_handleArray',
22238
22239      /**
22240       * handle the array type of option
22241       * @param optionName
22242       * @param arr
22243       * @param value
22244       * @param path
22245       * @private
22246       */
22247      value: function _handleArray(arr, value, path) {
22248        if (typeof arr[0] === 'string' && arr[0] === 'color') {
22249          this._makeColorField(arr, value, path);
22250          if (arr[1] !== value) {
22251            this.changedOptions.push({ path: path, value: value });
22252          }
22253        } else if (typeof arr[0] === 'string') {
22254          this._makeDropdown(arr, value, path);
22255          if (arr[0] !== value) {
22256            this.changedOptions.push({ path: path, value: value });
22257          }
22258        } else if (typeof arr[0] === 'number') {
22259          this._makeRange(arr, value, path);
22260          if (arr[0] !== value) {
22261            this.changedOptions.push({ path: path, value: Number(value) });
22262          }
22263        }
22264      }
22265    }, {
22266      key: '_update',
22267
22268      /**
22269       * called to update the network with the new settings.
22270       * @param value
22271       * @param path
22272       * @private
22273       */
22274      value: function _update(value, path) {
22275        var options = this._constructOptions(value, path);
22276
22277        if (this.parent.body && this.parent.body.emitter && this.parent.body.emitter.emit) {
22278          this.parent.body.emitter.emit('configChange', options);
22279        }
22280        this.initialized = true;
22281        this.parent.setOptions(options);
22282      }
22283    }, {
22284      key: '_constructOptions',
22285      value: function _constructOptions(value, path) {
22286        var optionsObj = arguments.length <= 2 || arguments[2] === undefined ? {} : arguments[2];
22287
22288        var pointer = optionsObj;
22289
22290        // when dropdown boxes can be string or boolean, we typecast it into correct types
22291        value = value === 'true' ? true : value;
22292        value = value === 'false' ? false : value;
22293
22294        for (var i = 0; i < path.length; i++) {
22295          if (path[i] !== 'global') {
22296            if (pointer[path[i]] === undefined) {
22297              pointer[path[i]] = {};
22298            }
22299            if (i !== path.length - 1) {
22300              pointer = pointer[path[i]];
22301            } else {
22302              pointer[path[i]] = value;
22303            }
22304          }
22305        }
22306        return optionsObj;
22307      }
22308    }, {
22309      key: '_printOptions',
22310      value: function _printOptions() {
22311        var options = this.getOptions();
22312        this.optionsContainer.innerHTML = '<pre>var options = ' + JSON.stringify(options, null, 2) + '</pre>';
22313      }
22314    }, {
22315      key: 'getOptions',
22316      value: function getOptions() {
22317        var options = {};
22318        for (var i = 0; i < this.changedOptions.length; i++) {
22319          this._constructOptions(this.changedOptions[i].value, this.changedOptions[i].path, options);
22320        }
22321        return options;
22322      }
22323    }]);
22324
22325    return Configurator;
22326  })();
22327
22328  exports['default'] = Configurator;
22329  module.exports = exports['default'];
22330
22331/***/ },
22332/* 46 */
22333/***/ function(module, exports, __webpack_require__) {
22334
22335  'use strict';
22336
22337  Object.defineProperty(exports, '__esModule', {
22338    value: true
22339  });
22340
22341  var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
22342
22343  function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
22344
22345  var Hammer = __webpack_require__(20);
22346  var hammerUtil = __webpack_require__(25);
22347  var util = __webpack_require__(1);
22348
22349  var ColorPicker = (function () {
22350    function ColorPicker() {
22351      var pixelRatio = arguments.length <= 0 || arguments[0] === undefined ? 1 : arguments[0];
22352
22353      _classCallCheck(this, ColorPicker);
22354
22355      this.pixelRatio = pixelRatio;
22356      this.generated = false;
22357      this.centerCoordinates = { x: 289 / 2, y: 289 / 2 };
22358      this.r = 289 * 0.49;
22359      this.color = { r: 255, g: 255, b: 255, a: 1.0 };
22360      this.hueCircle = undefined;
22361      this.initialColor = { r: 255, g: 255, b: 255, a: 1.0 };
22362      this.previousColor = undefined;
22363      this.applied = false;
22364
22365      // bound by
22366      this.updateCallback = function () {};
22367
22368      // create all DOM elements
22369      this._create();
22370    }
22371
22372    _createClass(ColorPicker, [{
22373      key: 'insertTo',
22374
22375      /**
22376       * this inserts the colorPicker into a div from the DOM
22377       * @param container
22378       */
22379      value: function insertTo(container) {
22380        if (this.hammer !== undefined) {
22381          this.hammer.destroy();
22382          this.hammer = undefined;
22383        }
22384        this.container = container;
22385        this.container.appendChild(this.frame);
22386        this._bindHammer();
22387
22388        this._setSize();
22389      }
22390    }, {
22391      key: 'setCallback',
22392
22393      /**
22394       * the callback is executed on apply and save. Bind it to the application
22395       * @param callback
22396       */
22397      value: function setCallback(callback) {
22398        if (typeof callback === 'function') {
22399          this.updateCallback = callback;
22400        } else {
22401          throw new Error('Function attempted to set as colorPicker callback is not a function.');
22402        }
22403      }
22404    }, {
22405      key: '_isColorString',
22406      value: function _isColorString(color) {
22407        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' };
22408        if (typeof color === 'string') {
22409          return htmlColors[color];
22410        }
22411      }
22412    }, {
22413      key: 'setColor',
22414
22415      /**
22416       * Set the color of the colorPicker
22417       * Supported formats:
22418       * 'red'                   --> HTML color string
22419       * '#ffffff'               --> hex string
22420       * 'rbg(255,255,255)'      --> rgb string
22421       * 'rgba(255,255,255,1.0)' --> rgba string
22422       * {r:255,g:255,b:255}     --> rgb object
22423       * {r:255,g:255,b:255,a:1.0} --> rgba object
22424       * @param color
22425       * @param setInitial
22426       */
22427      value: function setColor(color) {
22428        var setInitial = arguments.length <= 1 || arguments[1] === undefined ? true : arguments[1];
22429
22430        if (color === 'none') {
22431          return;
22432        }
22433
22434        var rgba = undefined;
22435
22436        // if a html color shorthand is used, convert to hex
22437        var htmlColor = this._isColorString(color);
22438        if (htmlColor !== undefined) {
22439          color = htmlColor;
22440        }
22441
22442        // check format
22443        if (util.isString(color) === true) {
22444          if (util.isValidRGB(color) === true) {
22445            var rgbaArray = color.substr(4).substr(0, color.length - 5).split(',');
22446            rgba = { r: rgbaArray[0], g: rgbaArray[1], b: rgbaArray[2], a: 1.0 };
22447          } else if (util.isValidRGBA(color) === true) {
22448            var rgbaArray = color.substr(5).substr(0, color.length - 6).split(',');
22449            rgba = { r: rgbaArray[0], g: rgbaArray[1], b: rgbaArray[2], a: rgbaArray[3] };
22450          } else if (util.isValidHex(color) === true) {
22451            var rgbObj = util.hexToRGB(color);
22452            rgba = { r: rgbObj.r, g: rgbObj.g, b: rgbObj.b, a: 1.0 };
22453          }
22454        } else {
22455          if (color instanceof Object) {
22456            if (color.r !== undefined && color.g !== undefined && color.b !== undefined) {
22457              var alpha = color.a !== undefined ? color.a : '1.0';
22458              rgba = { r: color.r, g: color.g, b: color.b, a: alpha };
22459            }
22460          }
22461        }
22462
22463        // set color
22464        if (rgba === undefined) {
22465          throw new Error('Unknown color passed to the colorPicker. Supported are strings: rgb, hex, rgba. Object: rgb ({r:r,g:g,b:b,[a:a]}). Supplied: ' + JSON.stringify(color));
22466        } else {
22467          this._setColor(rgba, setInitial);
22468        }
22469      }
22470    }, {
22471      key: 'show',
22472
22473      /**
22474       * this shows the color picker at a location. The hue circle is constructed once and stored.
22475       * @param x
22476       * @param y
22477       */
22478      value: function show(x, y) {
22479        this.applied = false;
22480        this.frame.style.display = 'block';
22481        this.frame.style.top = y + 'px';
22482        this.frame.style.left = x + 'px';
22483        this._generateHueCircle();
22484      }
22485    }, {
22486      key: '_hide',
22487
22488      // ------------------------------------------ PRIVATE ----------------------------- //
22489
22490      /**
22491       * Hide the picker. Is called by the cancel button.
22492       * Optional boolean to store the previous color for easy access later on.
22493       * @param storePrevious
22494       * @private
22495       */
22496      value: function _hide() {
22497        var storePrevious = arguments.length <= 0 || arguments[0] === undefined ? true : arguments[0];
22498
22499        // store the previous color for next time;
22500        if (storePrevious === true) {
22501          this.previousColor = util.extend({}, this.color);
22502        }
22503
22504        if (this.applied === true) {
22505          this.updateCallback(this.initialColor);
22506        }
22507
22508        this.frame.style.display = 'none';
22509      }
22510    }, {
22511      key: '_save',
22512
22513      /**
22514       * bound to the save button. Saves and hides.
22515       * @private
22516       */
22517      value: function _save() {
22518        this.updateCallback(this.color);
22519        this.applied = false;
22520        this._hide();
22521      }
22522    }, {
22523      key: '_apply',
22524
22525      /**
22526       * Bound to apply button. Saves but does not close. Is undone by the cancel button.
22527       * @private
22528       */
22529      value: function _apply() {
22530        this.applied = true;
22531        this.updateCallback(this.color);
22532        this._updatePicker(this.color);
22533      }
22534    }, {
22535      key: '_loadLast',
22536
22537      /**
22538       * load the color from the previous session.
22539       * @private
22540       */
22541      value: function _loadLast() {
22542        if (this.previousColor !== undefined) {
22543          this.setColor(this.previousColor, false);
22544        } else {
22545          alert('There is no last color to load...');
22546        }
22547      }
22548    }, {
22549      key: '_setColor',
22550
22551      /**
22552       * set the color, place the picker
22553       * @param rgba
22554       * @param setInitial
22555       * @private
22556       */
22557      value: function _setColor(rgba) {
22558        var setInitial = arguments.length <= 1 || arguments[1] === undefined ? true : arguments[1];
22559
22560        // store the initial color
22561        if (setInitial === true) {
22562          this.initialColor = util.extend({}, rgba);
22563        }
22564
22565        this.color = rgba;
22566        var hsv = util.RGBToHSV(rgba.r, rgba.g, rgba.b);
22567
22568        var angleConvert = 2 * Math.PI;
22569        var radius = this.r * hsv.s;
22570        var x = this.centerCoordinates.x + radius * Math.sin(angleConvert * hsv.h);
22571        var y = this.centerCoordinates.y + radius * Math.cos(angleConvert * hsv.h);
22572
22573        this.colorPickerSelector.style.left = x - 0.5 * this.colorPickerSelector.clientWidth + 'px';
22574        this.colorPickerSelector.style.top = y - 0.5 * this.colorPickerSelector.clientHeight + 'px';
22575
22576        this._updatePicker(rgba);
22577      }
22578    }, {
22579      key: '_setOpacity',
22580
22581      /**
22582       * bound to opacity control
22583       * @param value
22584       * @private
22585       */
22586      value: function _setOpacity(value) {
22587        this.color.a = value / 100;
22588        this._updatePicker(this.color);
22589      }
22590    }, {
22591      key: '_setBrightness',
22592
22593      /**
22594       * bound to brightness control
22595       * @param value
22596       * @private
22597       */
22598      value: function _setBrightness(value) {
22599        var hsv = util.RGBToHSV(this.color.r, this.color.g, this.color.b);
22600        hsv.v = value / 100;
22601        var rgba = util.HSVToRGB(hsv.h, hsv.s, hsv.v);
22602        rgba['a'] = this.color.a;
22603        this.color = rgba;
22604        this._updatePicker();
22605      }
22606    }, {
22607      key: '_updatePicker',
22608
22609      /**
22610       * update the colorpicker. A black circle overlays the hue circle to mimic the brightness decreasing.
22611       * @param rgba
22612       * @private
22613       */
22614      value: function _updatePicker() {
22615        var rgba = arguments.length <= 0 || arguments[0] === undefined ? this.color : arguments[0];
22616
22617        var hsv = util.RGBToHSV(rgba.r, rgba.g, rgba.b);
22618        var ctx = this.colorPickerCanvas.getContext('2d');
22619        if (this.pixelRation === undefined) {
22620          this.pixelRatio = (window.devicePixelRatio || 1) / (ctx.webkitBackingStorePixelRatio || ctx.mozBackingStorePixelRatio || ctx.msBackingStorePixelRatio || ctx.oBackingStorePixelRatio || ctx.backingStorePixelRatio || 1);
22621        }
22622        ctx.setTransform(this.pixelRatio, 0, 0, this.pixelRatio, 0, 0);
22623
22624        // clear the canvas
22625        var w = this.colorPickerCanvas.clientWidth;
22626        var h = this.colorPickerCanvas.clientHeight;
22627        ctx.clearRect(0, 0, w, h);
22628
22629        ctx.putImageData(this.hueCircle, 0, 0);
22630        ctx.fillStyle = 'rgba(0,0,0,' + (1 - hsv.v) + ')';
22631        ctx.circle(this.centerCoordinates.x, this.centerCoordinates.y, this.r);
22632        ctx.fill();
22633
22634        this.brightnessRange.value = 100 * hsv.v;
22635        this.opacityRange.value = 100 * rgba.a;
22636
22637        this.initialColorDiv.style.backgroundColor = 'rgba(' + this.initialColor.r + ',' + this.initialColor.g + ',' + this.initialColor.b + ',' + this.initialColor.a + ')';
22638        this.newColorDiv.style.backgroundColor = 'rgba(' + this.color.r + ',' + this.color.g + ',' + this.color.b + ',' + this.color.a + ')';
22639      }
22640    }, {
vendor: 5,055 bytes, lines 22641-22767
22641      key: '_setSize',
22642
22643      /**
22644       * used by create to set the size of the canvas.
22645       * @private
22646       */
22647      value: function _setSize() {
22648        this.colorPickerCanvas.style.width = '100%';
22649        this.colorPickerCanvas.style.height = '100%';
22650
22651        this.colorPickerCanvas.width = 289 * this.pixelRatio;
22652        this.colorPickerCanvas.height = 289 * this.pixelRatio;
22653      }
22654    }, {
22655      key: '_create',
22656
22657      /**
22658       * create all dom elements
22659       * TODO: cleanup, lots of similar dom elements
22660       * @private
22661       */
22662      value: function _create() {
22663        this.frame = document.createElement('div');
22664        this.frame.className = 'vis-color-picker';
22665
22666        this.colorPickerDiv = document.createElement('div');
22667        this.colorPickerSelector = document.createElement('div');
22668        this.colorPickerSelector.className = 'vis-selector';
22669        this.colorPickerDiv.appendChild(this.colorPickerSelector);
22670
22671        this.colorPickerCanvas = document.createElement('canvas');
22672        this.colorPickerDiv.appendChild(this.colorPickerCanvas);
22673
22674        if (!this.colorPickerCanvas.getContext) {
22675          var noCanvas = document.createElement('DIV');
22676          noCanvas.style.color = 'red';
22677          noCanvas.style.fontWeight = 'bold';
22678          noCanvas.style.padding = '10px';
22679          noCanvas.innerHTML = 'Error: your browser does not support HTML canvas';
22680          this.colorPickerCanvas.appendChild(noCanvas);
22681        } else {
22682          var ctx = this.colorPickerCanvas.getContext('2d');
22683          this.pixelRatio = (window.devicePixelRatio || 1) / (ctx.webkitBackingStorePixelRatio || ctx.mozBackingStorePixelRatio || ctx.msBackingStorePixelRatio || ctx.oBackingStorePixelRatio || ctx.backingStorePixelRatio || 1);
22684
22685          this.colorPickerCanvas.getContext('2d').setTransform(this.pixelRatio, 0, 0, this.pixelRatio, 0, 0);
22686        }
22687
22688        this.colorPickerDiv.className = 'vis-color';
22689
22690        this.opacityDiv = document.createElement('div');
22691        this.opacityDiv.className = 'vis-opacity';
22692
22693        this.brightnessDiv = document.createElement('div');
22694        this.brightnessDiv.className = 'vis-brightness';
22695
22696        this.arrowDiv = document.createElement('div');
22697        this.arrowDiv.className = 'vis-arrow';
22698
22699        this.opacityRange = document.createElement('input');
22700        try {
22701          this.opacityRange.type = 'range'; // Not supported on IE9
22702          this.opacityRange.min = '0';
22703          this.opacityRange.max = '100';
22704        } catch (err) {}
22705        this.opacityRange.value = '100';
22706        this.opacityRange.className = 'vis-range';
22707
22708        this.brightnessRange = document.createElement('input');
22709        try {
22710          this.brightnessRange.type = 'range'; // Not supported on IE9
22711          this.brightnessRange.min = '0';
22712          this.brightnessRange.max = '100';
22713        } catch (err) {}
22714        this.brightnessRange.value = '100';
22715        this.brightnessRange.className = 'vis-range';
22716
22717        this.opacityDiv.appendChild(this.opacityRange);
22718        this.brightnessDiv.appendChild(this.brightnessRange);
22719
22720        var me = this;
22721        this.opacityRange.onchange = function () {
22722          me._setOpacity(this.value);
22723        };
22724        this.opacityRange.oninput = function () {
22725          me._setOpacity(this.value);
22726        };
22727        this.brightnessRange.onchange = function () {
22728          me._setBrightness(this.value);
22729        };
22730        this.brightnessRange.oninput = function () {
22731          me._setBrightness(this.value);
22732        };
22733
22734        this.brightnessLabel = document.createElement('div');
22735        this.brightnessLabel.className = 'vis-label vis-brightness';
22736        this.brightnessLabel.innerHTML = 'brightness:';
22737
22738        this.opacityLabel = document.createElement('div');
22739        this.opacityLabel.className = 'vis-label vis-opacity';
22740        this.opacityLabel.innerHTML = 'opacity:';
22741
22742        this.newColorDiv = document.createElement('div');
22743        this.newColorDiv.className = 'vis-new-color';
22744        this.newColorDiv.innerHTML = 'new';
22745
22746        this.initialColorDiv = document.createElement('div');
22747        this.initialColorDiv.className = 'vis-initial-color';
22748        this.initialColorDiv.innerHTML = 'initial';
22749
22750        this.cancelButton = document.createElement('div');
22751        this.cancelButton.className = 'vis-button vis-cancel';
22752        this.cancelButton.innerHTML = 'cancel';
22753        this.cancelButton.onclick = this._hide.bind(this, false);
22754
22755        this.applyButton = document.createElement('div');
22756        this.applyButton.className = 'vis-button vis-apply';
22757        this.applyButton.innerHTML = 'apply';
22758        this.applyButton.onclick = this._apply.bind(this);
22759
22760        this.saveButton = document.createElement('div');
22761        this.saveButton.className = 'vis-button vis-save';
22762        this.saveButton.innerHTML = 'save';
22763        this.saveButton.onclick = this._save.bind(this);
22764
22765        this.loadButton = document.createElement('div');
22766        this.loadButton.className = 'vis-button vis-load';
22767        this.loadButton.innerHTML = 'load last';
22768        this.loadButton.onclick = this._loadLast.bind(this);
22769
22770        this.frame.appendChild(this.colorPickerDiv);
22771        this.frame.appendChild(this.arrowDiv);
22772        this.frame.appendChild(this.brightnessLabel);
22773        this.frame.appendChild(this.brightnessDiv);
22774        this.frame.appendChild(this.opacityLabel);
22775        this.frame.appendChild(this.opacityDiv);
22776        this.frame.appendChild(this.newColorDiv);
22777        this.frame.appendChild(this.initialColorDiv);
22778
22779        this.frame.appendChild(this.cancelButton);
22780        this.frame.appendChild(this.applyButton);
22781        this.frame.appendChild(this.saveButton);
22782        this.frame.appendChild(this.loadButton);
22783      }
22784    }, {
22785      key: '_bindHammer',
22786
22787      /**
22788       * bind hammer to the color picker
22789       * @private
22790       */
22791      value: function _bindHammer() {
22792        var _this = this;
22793
22794        this.drag = {};
22795        this.pinch = {};
22796        this.hammer = new Hammer(this.colorPickerCanvas);
22797        this.hammer.get('pinch').set({ enable: true });
22798
22799        hammerUtil.onTouch(this.hammer, function (event) {
22800          _this._moveSelector(event);
22801        });
22802        this.hammer.on('tap', function (event) {
22803          _this._moveSelector(event);
22804        });
22805        this.hammer.on('panstart', function (event) {
22806          _this._moveSelector(event);
22807        });
22808        this.hammer.on('panmove', function (event) {
22809          _this._moveSelector(event);
22810        });
22811        this.hammer.on('panend', function (event) {
22812          _this._moveSelector(event);
22813        });
22814      }
22815    }, {
22816      key: '_generateHueCircle',
22817
22818      /**
22819       * generate the hue circle. This is relatively heavy (200ms) and is done only once on the first time it is shown.
22820       * @private
22821       */
22822      value: function _generateHueCircle() {
22823        if (this.generated === false) {
22824          var ctx = this.colorPickerCanvas.getContext('2d');
22825          if (this.pixelRation === undefined) {
22826            this.pixelRatio = (window.devicePixelRatio || 1) / (ctx.webkitBackingStorePixelRatio || ctx.mozBackingStorePixelRatio || ctx.msBackingStorePixelRatio || ctx.oBackingStorePixelRatio || ctx.backingStorePixelRatio || 1);
22827          }
22828          ctx.setTransform(this.pixelRatio, 0, 0, this.pixelRatio, 0, 0);
22829
22830          // clear the canvas
22831          var w = this.colorPickerCanvas.clientWidth;
22832          var h = this.colorPickerCanvas.clientHeight;
22833          ctx.clearRect(0, 0, w, h);
22834
22835          // draw hue circle
22836          var x = undefined,
22837              y = undefined,
22838              hue = undefined,
22839              sat = undefined;
22840          this.centerCoordinates = { x: w * 0.5, y: h * 0.5 };
22841          this.r = 0.49 * w;
22842          var angleConvert = 2 * Math.PI / 360;
22843          var hfac = 1 / 360;
22844          var sfac = 1 / this.r;
22845          var rgb = undefined;
22846          for (hue = 0; hue < 360; hue++) {
22847            for (sat = 0; sat < this.r; sat++) {
22848              x = this.centerCoordinates.x + sat * Math.sin(angleConvert * hue);
22849              y = this.centerCoordinates.y + sat * Math.cos(angleConvert * hue);
22850              rgb = util.HSVToRGB(hue * hfac, sat * sfac, 1);
22851              ctx.fillStyle = 'rgb(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ')';
22852              ctx.fillRect(x - 0.5, y - 0.5, 2, 2);
22853            }
22854          }
22855          ctx.strokeStyle = 'rgba(0,0,0,1)';
22856          ctx.circle(this.centerCoordinates.x, this.centerCoordinates.y, this.r);
22857          ctx.stroke();
22858
22859          this.hueCircle = ctx.getImageData(0, 0, w, h);
22860        }
22861        this.generated = true;
22862      }
22863    }, {
22864      key: '_moveSelector',
22865
22866      /**
22867       * move the selector. This is called by hammer functions.
22868       *
22869       * @param event
22870       * @private
22871       */
22872      value: function _moveSelector(event) {
22873        var rect = this.colorPickerDiv.getBoundingClientRect();
22874        var left = event.center.x - rect.left;
22875        var top = event.center.y - rect.top;
22876
22877        var centerY = 0.5 * this.colorPickerDiv.clientHeight;
22878        var centerX = 0.5 * this.colorPickerDiv.clientWidth;
22879
22880        var x = left - centerX;
22881        var y = top - centerY;
22882
22883        var angle = Math.atan2(x, y);
22884        var radius = 0.98 * Math.min(Math.sqrt(x * x + y * y), centerX);
22885
22886        var newTop = Math.cos(angle) * radius + centerY;
22887        var newLeft = Math.sin(angle) * radius + centerX;
22888
22889        this.colorPickerSelector.style.top = newTop - 0.5 * this.colorPickerSelector.clientHeight + 'px';
22890        this.colorPickerSelector.style.left = newLeft - 0.5 * this.colorPickerSelector.clientWidth + 'px';
22891
22892        // set color
22893        var h = angle / (2 * Math.PI);
22894        h = h < 0 ? h + 1 : h;
22895        var s = radius / this.r;
22896        var hsv = util.RGBToHSV(this.color.r, this.color.g, this.color.b);
22897        hsv.h = h;
22898        hsv.s = s;
22899        var rgba = util.HSVToRGB(hsv.h, hsv.s, hsv.v);
22900        rgba['a'] = this.color.a;
22901        this.color = rgba;
22902
22903        // update previews
22904        this.initialColorDiv.style.backgroundColor = 'rgba(' + this.initialColor.r + ',' + this.initialColor.g + ',' + this.initialColor.b + ',' + this.initialColor.a + ')';
22905        this.newColorDiv.style.backgroundColor = 'rgba(' + this.color.r + ',' + this.color.g + ',' + this.color.b + ',' + this.color.a + ')';
22906      }
22907    }]);
22908
22909    return ColorPicker;
22910  })();
22911
22912  exports['default'] = ColorPicker;
22913  module.exports = exports['default'];
22914
22915/***/ },
22916/* 47 */
22917/***/ function(module, exports, __webpack_require__) {
22918
22919  'use strict';
22920
22921  Object.defineProperty(exports, '__esModule', {
22922    value: true
22923  });
22924
22925  var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
22926
22927  function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
22928
22929  var util = __webpack_require__(1);
22930
22931  var errorFound = false;
22932  var allOptions = undefined;
22933  var printStyle = 'background: #FFeeee; color: #dd0000';
22934  /**
22935   *  Used to validate options.
22936   */
22937
22938  var Validator = (function () {
22939    function Validator() {
22940      _classCallCheck(this, Validator);
22941    }
22942
22943    _createClass(Validator, null, [{
22944      key: 'validate',
22945
22946      /**
22947       * Main function to be called
22948       * @param options
22949       * @param subObject
22950       * @returns {boolean}
22951       */
22952      value: function validate(options, referenceOptions, subObject) {
22953        errorFound = false;
22954        allOptions = referenceOptions;
22955        var usedOptions = referenceOptions;
22956        if (subObject !== undefined) {
22957          usedOptions = referenceOptions[subObject];
22958        }
22959        Validator.parse(options, usedOptions, []);
22960        return errorFound;
22961      }
22962    }, {
22963      key: 'parse',
22964
22965      /**
22966       * Will traverse an object recursively and check every value
22967       * @param options
22968       * @param referenceOptions
22969       * @param path
22970       */
22971      value: function parse(options, referenceOptions, path) {
22972        for (var option in options) {
22973          if (options.hasOwnProperty(option)) {
22974            Validator.check(option, options, referenceOptions, path);
22975          }
22976        }
22977      }
22978    }, {
22979      key: 'check',
22980
22981      /**
22982       * Check every value. If the value is an object, call the parse function on that object.
22983       * @param option
22984       * @param options
22985       * @param referenceOptions
22986       * @param path
22987       */
22988      value: function check(option, options, referenceOptions, path) {
22989        if (referenceOptions[option] === undefined && referenceOptions.__any__ === undefined) {
22990          Validator.getSuggestion(option, referenceOptions, path);
22991        } else if (referenceOptions[option] === undefined && referenceOptions.__any__ !== undefined) {
22992          // __any__ is a wildcard. Any value is accepted and will be further analysed by reference.
22993          if (Validator.getType(options[option]) === 'object' && referenceOptions['__any__'].__type__ !== undefined) {
22994            // if the any subgroup is not a predefined object int he configurator we do not look deeper into the object.
22995            Validator.checkFields(option, options, referenceOptions, '__any__', referenceOptions['__any__'].__type__, path);
22996          } else {
22997            Validator.checkFields(option, options, referenceOptions, '__any__', referenceOptions['__any__'], path);
22998          }
22999        } else {
23000          // Since all options in the reference are objects, we can check whether they are supposed to be object to look for the __type__ field.
23001          if (referenceOptions[option].__type__ !== undefined) {
23002            // if this should be an object, we check if the correct type has been supplied to account for shorthand options.
23003            Validator.checkFields(option, options, referenceOptions, option, referenceOptions[option].__type__, path);
23004          } else {
23005            Validator.checkFields(option, options, referenceOptions, option, referenceOptions[option], path);
23006          }
23007        }
23008      }
23009    }, {
23010      key: 'checkFields',
23011
23012      /**
23013       *
23014       * @param {String}  option     | the option property
23015       * @param {Object}  options    | The supplied options object
23016       * @param {Object}  referenceOptions    | The reference options containing all options and their allowed formats
23017       * @param {String}  referenceOption     | Usually this is the same as option, except when handling an __any__ tag.
23018       * @param {String}  refOptionType       | This is the type object from the reference options
23019       * @param {Array}   path      | where in the object is the option
23020       */
23021      value: function checkFields(option, options, referenceOptions, referenceOption, refOptionObj, path) {
23022        var optionType = Validator.getType(options[option]);
23023        var refOptionType = refOptionObj[optionType];
23024        if (refOptionType !== undefined) {
23025          // if the type is correct, we check if it is supposed to be one of a few select values
23026          if (Validator.getType(refOptionType) === 'array') {
23027            if (refOptionType.indexOf(options[option]) === -1) {
23028              console.log('%cInvalid option detected in "' + option + '".' + ' Allowed values are:' + Validator.print(refOptionType) + ' not "' + options[option] + '". ' + Validator.printLocation(path, option), printStyle);
23029              errorFound = true;
23030            } else if (optionType === 'object' && referenceOption !== '__any__') {
23031              path = util.copyAndExtendArray(path, option);
23032              Validator.parse(options[option], referenceOptions[referenceOption], path);
23033            }
23034          } else if (optionType === 'object' && referenceOption !== '__any__') {
23035            path = util.copyAndExtendArray(path, option);
23036            Validator.parse(options[option], referenceOptions[referenceOption], path);
23037          }
23038        } else if (refOptionObj['any'] === undefined) {
23039          // type of the field is incorrect and the field cannot be any
23040          console.log('%cInvalid type received for "' + option + '". Expected: ' + Validator.print(Object.keys(refOptionObj)) + '. Received [' + optionType + '] "' + options[option] + '"' + Validator.printLocation(path, option), printStyle);
23041          errorFound = true;
23042        }
23043      }
23044    }, {
23045      key: 'getType',
23046      value: function getType(object) {
23047        var type = typeof object;
23048
23049        if (type === 'object') {
23050          if (object === null) {
23051            return 'null';
23052          }
23053          if (object instanceof Boolean) {
23054            return 'boolean';
23055          }
23056          if (object instanceof Number) {
23057            return 'number';
23058          }
23059          if (object instanceof String) {
23060            return 'string';
23061          }
23062          if (Array.isArray(object)) {
23063            return 'array';
23064          }
23065          if (object instanceof Date) {
23066            return 'date';
23067          }
23068          if (object.nodeType !== undefined) {
23069            return 'dom';
23070          }
23071          if (object._isAMomentObject === true) {
23072            return 'moment';
23073          }
23074          return 'object';
23075        } else if (type === 'number') {
23076          return 'number';
23077        } else if (type === 'boolean') {
23078          return 'boolean';
23079        } else if (type === 'string') {
23080          return 'string';
23081        } else if (type === undefined) {
23082          return 'undefined';
23083        }
23084        return type;
23085      }
23086    }, {
23087      key: 'getSuggestion',
23088      value: function getSuggestion(option, options, path) {
23089        var localSearch = Validator.findInOptions(option, options, path, false);
23090        var globalSearch = Validator.findInOptions(option, allOptions, [], true);
23091
23092        var localSearchThreshold = 8;
23093        var globalSearchThreshold = 4;
23094
23095        if (localSearch.indexMatch !== undefined) {
23096          console.log('%cUnknown option detected: "' + option + '" in ' + Validator.printLocation(localSearch.path, option, '') + 'Perhaps it was incomplete? Did you mean: "' + localSearch.indexMatch + '"?\n\n', printStyle);
23097        } else if (globalSearch.distance <= globalSearchThreshold && localSearch.distance > globalSearch.distance) {
23098          console.log('%cUnknown option detected: "' + option + '" in ' + Validator.printLocation(localSearch.path, option, '') + 'Perhaps it was misplaced? Matching option found at: ' + Validator.printLocation(globalSearch.path, globalSearch.closestMatch, ''), printStyle);
23099        } else if (localSearch.distance <= localSearchThreshold) {
23100          console.log('%cUnknown option detected: "' + option + '". Did you mean "' + localSearch.closestMatch + '"?' + Validator.printLocation(localSearch.path, option), printStyle);
23101        } else {
23102          console.log('%cUnknown option detected: "' + option + '". Did you mean one of these: ' + Validator.print(Object.keys(options)) + Validator.printLocation(path, option), printStyle);
23103        }
23104
23105        errorFound = true;
23106      }
23107    }, {
23108      key: 'findInOptions',
23109
23110      /**
23111       * traverse the options in search for a match.
23112       * @param option
23113       * @param options
23114       * @param path
23115       * @param recursive
23116       * @returns {{closestMatch: string, path: Array, distance: number}}
23117       */
23118      value: function findInOptions(option, options, path) {
23119        var recursive = arguments.length <= 3 || arguments[3] === undefined ? false : arguments[3];
23120
23121        var min = 1e9;
23122        var closestMatch = '';
23123        var closestMatchPath = [];
23124        var lowerCaseOption = option.toLowerCase();
23125        var indexMatch = undefined;
23126        for (var op in options) {
23127          var distance = undefined;
23128          if (options[op].__type__ !== undefined && recursive === true) {
23129            var result = Validator.findInOptions(option, options[op], util.copyAndExtendArray(path, op));
23130            if (min > result.distance) {
23131              closestMatch = result.closestMatch;
23132              closestMatchPath = result.path;
23133              min = result.distance;
23134              indexMatch = result.indexMatch;
23135            }
23136          } else {
23137            if (op.toLowerCase().indexOf(lowerCaseOption) !== -1) {
23138              indexMatch = op;
23139            }
23140            distance = Validator.levenshteinDistance(option, op);
23141            if (min > distance) {
23142              closestMatch = op;
23143              closestMatchPath = util.copyArray(path);
23144              min = distance;
23145            }
23146          }
23147        }
23148        return { closestMatch: closestMatch, path: closestMatchPath, distance: min, indexMatch: indexMatch };
23149      }
23150    }, {
23151      key: 'printLocation',
23152      value: function printLocation(path, option) {
23153        var prefix = arguments.length <= 2 || arguments[2] === undefined ? 'Problem value found at: \n' : arguments[2];
23154
23155        var str = '\n\n' + prefix + 'options = {\n';
23156        for (var i = 0; i < path.length; i++) {
23157          for (var j = 0; j < i + 1; j++) {
23158            str += '  ';
23159          }
23160          str += path[i] + ': {\n';
23161        }
23162        for (var j = 0; j < path.length + 1; j++) {
23163          str += '  ';
23164        }
23165        str += option + '\n';
23166        for (var i = 0; i < path.length + 1; i++) {
23167          for (var j = 0; j < path.length - i; j++) {
23168            str += '  ';
23169          }
23170          str += '}\n';
23171        }
23172        return str + '\n\n';
23173      }
23174    }, {
23175      key: 'print',
23176      value: function print(options) {
23177        return JSON.stringify(options).replace(/(\")|(\[)|(\])|(,"__type__")/g, '').replace(/(\,)/g, ', ');
23178      }
23179    }, {
23180      key: 'levenshteinDistance',
23181
23182      // Compute the edit distance between the two given strings
23183      // http://en.wikibooks.org/wiki/Algorithm_Implementation/Strings/Levenshtein_distance#JavaScript
23184      /*
23185       Copyright (c) 2011 Andrei Mackenzie
23186        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:
23187        The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
23188        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.
23189       */
23190      value: function levenshteinDistance(a, b) {
23191        if (a.length === 0) return b.length;
23192        if (b.length === 0) return a.length;
23193
23194        var matrix = [];
23195
23196        // increment along the first column of each row
23197        var i;
23198        for (i = 0; i <= b.length; i++) {
23199          matrix[i] = [i];
23200        }
23201
23202        // increment each column in the first row
23203        var j;
23204        for (j = 0; j <= a.length; j++) {
23205          matrix[0][j] = j;
23206        }
23207
23208        // Fill in the rest of the matrix
23209        for (i = 1; i <= b.length; i++) {
23210          for (j = 1; j <= a.length; j++) {
23211            if (b.charAt(i - 1) == a.charAt(j - 1)) {
23212              matrix[i][j] = matrix[i - 1][j - 1];
23213            } else {
23214              matrix[i][j] = Math.min(matrix[i - 1][j - 1] + 1, // substitution
23215              Math.min(matrix[i][j - 1] + 1, // insertion
23216              matrix[i - 1][j] + 1)); // deletion
23217            }
23218          }
23219        }
23220
23221        return matrix[b.length][a.length];
23222      }
23223    }]);
23224
23225    return Validator;
23226  })();
23227
23228  exports['default'] = Validator;
23229  exports.printStyle = printStyle;
23230
23231/***/ },
23232/* 48 */
23233/***/ function(module, exports) {
23234
23235  /**
23236   * This object contains all possible options. It will check if the types are correct, if required if the option is one
23237   * of the allowed values.
23238   *
23239   * __any__ means that the name of the property does not matter.
23240   * __type__ is a required field for all objects and contains the allowed types of all objects
23241   */
23242  'use strict';
23243
23244  Object.defineProperty(exports, '__esModule', {
23245    value: true
23246  });
23247  var string = 'string';
23248  var boolean = 'boolean';
23249  var number = 'number';
23250  var array = 'array';
23251  var date = 'date';
23252  var object = 'object'; // should only be in a __type__ property
23253  var dom = 'dom';
23254  var moment = 'moment';
23255  var any = 'any';
23256
23257  var allOptions = {
23258    configure: {
23259      enabled: { boolean: boolean },
23260      filter: { boolean: boolean, 'function': 'function' },
23261      container: { dom: dom },
23262      __type__: { object: object, boolean: boolean, 'function': 'function' }
23263    },
23264
23265    //globals :
23266    align: { string: string },
23267    autoResize: { boolean: boolean },
23268    throttleRedraw: { number: number },
23269    clickToUse: { boolean: boolean },
23270    dataAttributes: { string: string, array: array },
23271    editable: {
23272      add: { boolean: boolean, 'undefined': 'undefined' },
23273      remove: { boolean: boolean, 'undefined': 'undefined' },
23274      updateGroup: { boolean: boolean, 'undefined': 'undefined' },
23275      updateTime: { boolean: boolean, 'undefined': 'undefined' },
23276      __type__: { boolean: boolean, object: object }
23277    },
23278    end: { number: number, date: date, string: string, moment: moment },
23279    format: {
23280      minorLabels: {
23281        millisecond: { string: string, 'undefined': 'undefined' },
23282        second: { string: string, 'undefined': 'undefined' },
23283        minute: { string: string, 'undefined': 'undefined' },
23284        hour: { string: string, 'undefined': 'undefined' },
23285        weekday: { string: string, 'undefined': 'undefined' },
23286        day: { string: string, 'undefined': 'undefined' },
23287        month: { string: string, 'undefined': 'undefined' },
23288        year: { string: string, 'undefined': 'undefined' },
23289        __type__: { object: object }
23290      },
23291      majorLabels: {
23292        millisecond: { string: string, 'undefined': 'undefined' },
23293        second: { string: string, 'undefined': 'undefined' },
23294        minute: { string: string, 'undefined': 'undefined' },
23295        hour: { string: string, 'undefined': 'undefined' },
23296        weekday: { string: string, 'undefined': 'undefined' },
23297        day: { string: string, 'undefined': 'undefined' },
23298        month: { string: string, 'undefined': 'undefined' },
23299        year: { string: string, 'undefined': 'undefined' },
23300        __type__: { object: object }
23301      },
23302      __type__: { object: object }
23303    },
23304    moment: { 'function': 'function' },
23305    groupOrder: { string: string, 'function': 'function' },
23306    groupEditable: {
23307      add: { boolean: boolean, 'undefined': 'undefined' },
23308      remove: { boolean: boolean, 'undefined': 'undefined' },
23309      order: { boolean: boolean, 'undefined': 'undefined' },
23310      __type__: { boolean: boolean, object: object }
23311    },
23312    groupOrderSwap: { 'function': 'function' },
23313    height: { string: string, number: number },
23314    hiddenDates: {
23315      start: { date: date, number: number, string: string, moment: moment },
23316      end: { date: date, number: number, string: string, moment: moment },
23317      repeat: { string: string },
23318      __type__: { object: object, array: array }
23319    },
23320    locale: { string: string },
23321    locales: {
23322      __type__: { object: object }
23323    },
23324    margin: {
23325      axis: { number: number },
23326      item: {
23327        horizontal: { number: number, 'undefined': 'undefined' },
23328        vertical: { number: number, 'undefined': 'undefined' },
23329        __type__: { object: object, number: number }
23330      },
23331      __type__: { object: object, number: number }
23332    },
23333    max: { date: date, number: number, string: string, moment: moment },
23334    maxHeight: { number: number, string: string },
23335    min: { date: date, number: number, string: string, moment: moment },
23336    minHeight: { number: number, string: string },
23337    moveable: { boolean: boolean },
23338    multiselect: { boolean: boolean },
23339    onAdd: { 'function': 'function' },
23340    onUpdate: { 'function': 'function' },
23341    onMove: { 'function': 'function' },
23342    onMoving: { 'function': 'function' },
23343    onRemove: { 'function': 'function' },
23344    onAddGroup: { 'function': 'function' },
23345    onMoveGroup: { 'function': 'function' },
23346    onRemoveGroup: { 'function': 'function' },
23347    order: { 'function': 'function' },
23348    orientation: {
23349      axis: { string: string, 'undefined': 'undefined' },
23350      item: { string: string, 'undefined': 'undefined' },
23351      __type__: { string: string, object: object }
23352    },
23353    selectable: { boolean: boolean },
23354    showCurrentTime: { boolean: boolean },
23355    showMajorLabels: { boolean: boolean },
23356    showMinorLabels: { boolean: boolean },
23357    stack: { boolean: boolean },
23358    snap: { 'function': 'function', 'null': 'null' },
23359    start: { date: date, number: number, string: string, moment: moment },
23360    template: { 'function': 'function' },
23361    groupTemplate: { 'function': 'function' },
23362    timeAxis: {
23363      scale: { string: string, 'undefined': 'undefined' },
23364      step: { number: number, 'undefined': 'undefined' },
23365      __type__: { object: object }
23366    },
23367    type: { string: string },
23368    width: { string: string, number: number },
23369    zoomable: { boolean: boolean },
23370    zoomKey: { string: ['ctrlKey', 'altKey', 'metaKey', ''] },
23371    zoomMax: { number: number },
23372    zoomMin: { number: number },
23373
23374    __type__: { object: object }
23375  };
23376
23377  var configureOptions = {
23378    global: {
23379      align: ['center', 'left', 'right'],
23380      autoResize: true,
23381      throttleRedraw: [10, 0, 1000, 10],
23382      clickToUse: false,
23383      // dataAttributes: ['all'], // FIXME: can be 'all' or string[]
23384      editable: {
23385        add: false,
23386        remove: false,
23387        updateGroup: false,
23388        updateTime: false
23389      },
23390      end: '',
23391      format: {
23392        minorLabels: {
23393          millisecond: 'SSS',
23394          second: 's',
23395          minute: 'HH:mm',
23396          hour: 'HH:mm',
23397          weekday: 'ddd D',
23398          day: 'D',
23399          month: 'MMM',
23400          year: 'YYYY'
23401        },
23402        majorLabels: {
23403          millisecond: 'HH:mm:ss',
23404          second: 'D MMMM HH:mm',
23405          minute: 'ddd D MMMM',
23406          hour: 'ddd D MMMM',
23407          weekday: 'MMMM YYYY',
23408          day: 'MMMM YYYY',
23409          month: 'YYYY',
23410          year: ''
23411        }
23412      },
23413
23414      //groupOrder: {string, 'function': 'function'},
23415      groupsDraggable: false,
23416      height: '',
23417      //hiddenDates: {object, array},
23418      locale: '',
23419      margin: {
23420        axis: [20, 0, 100, 1],
23421        item: {
23422          horizontal: [10, 0, 100, 1],
23423          vertical: [10, 0, 100, 1]
23424        }
23425      },
23426      max: '',
23427      maxHeight: '',
23428      min: '',
23429      minHeight: '',
23430      moveable: false,
23431      multiselect: false,
23432      //onAdd: {'function': 'function'},
23433      //onUpdate: {'function': 'function'},
23434      //onMove: {'function': 'function'},
23435      //onMoving: {'function': 'function'},
23436      //onRename: {'function': 'function'},
23437      //order: {'function': 'function'},
23438      orientation: {
23439        axis: ['both', 'bottom', 'top'],
23440        item: ['bottom', 'top']
23441      },
23442      selectable: true,
23443      showCurrentTime: false,
23444      showMajorLabels: true,
23445      showMinorLabels: true,
23446      stack: true,
23447      //snap: {'function': 'function', nada},
23448      start: '',
23449      //template: {'function': 'function'},
23450      //timeAxis: {
23451      //  scale: ['millisecond', 'second', 'minute', 'hour', 'weekday', 'day', 'month', 'year'],
23452      //  step: [1, 1, 10, 1]
23453      //},
23454      type: ['box', 'point', 'range', 'background'],
23455      width: '100%',
23456      zoomable: true,
23457      zoomKey: ['ctrlKey', 'altKey', 'metaKey', ''],
23458      zoomMax: [315360000000000, 10, 315360000000000, 1],
23459      zoomMin: [10, 10, 315360000000000, 1]
23460    }
23461  };
23462
23463  exports.allOptions = allOptions;
23464  exports.configureOptions = configureOptions;
23465
23466/***/ },
23467/* 49 */
23468/***/ function(module, exports, __webpack_require__) {
23469
23470  'use strict';
23471
23472  var Emitter = __webpack_require__(12);
23473  var Hammer = __webpack_require__(20);
23474  var moment = __webpack_require__(2);
23475  var util = __webpack_require__(1);
23476  var DataSet = __webpack_require__(8);
23477  var DataView = __webpack_require__(10);
23478  var Range = __webpack_require__(24);
23479  var Core = __webpack_require__(28);
23480  var TimeAxis = __webpack_require__(39);
23481  var CurrentTime = __webpack_require__(44);
23482  var CustomTime = __webpack_require__(42);
23483  var LineGraph = __webpack_require__(50);
23484
23485  var Configurator = __webpack_require__(45);
23486  var Validator = __webpack_require__(47)['default'];
23487  var printStyle = __webpack_require__(47).printStyle;
23488  var allOptions = __webpack_require__(58).allOptions;
23489  var configureOptions = __webpack_require__(58).configureOptions;
23490
23491  /**
23492   * Create a timeline visualization
23493   * @param {HTMLElement} container
23494   * @param {vis.DataSet | Array} [items]
23495   * @param {Object} [options]  See Graph2d.setOptions for the available options.
23496   * @constructor
23497   * @extends Core
23498   */
23499  function Graph2d(container, items, groups, options) {
23500    // if the third element is options, the forth is groups (optionally);
23501    if (!(Array.isArray(groups) || groups instanceof DataSet) && groups instanceof Object) {
23502      var forthArgument = options;
23503      options = groups;
23504      groups = forthArgument;
23505    }
23506
23507    var me = this;
23508    this.defaultOptions = {
23509      start: null,
23510      end: null,
23511
23512      autoResize: true,
23513
23514      orientation: {
23515        axis: 'bottom', // axis orientation: 'bottom', 'top', or 'both'
23516        item: 'bottom' // not relevant for Graph2d
23517      },
23518
23519      moment: moment,
23520
23521      width: null,
23522      height: null,
23523      maxHeight: null,
23524      minHeight: null
23525    };
23526    this.options = util.deepExtend({}, this.defaultOptions);
23527
23528    // Create the DOM, props, and emitter
23529    this._create(container);
23530
23531    // all components listed here will be repainted automatically
23532    this.components = [];
23533
23534    this.body = {
23535      dom: this.dom,
23536      domProps: this.props,
23537      emitter: {
23538        on: this.on.bind(this),
23539        off: this.off.bind(this),
23540        emit: this.emit.bind(this)
23541      },
23542      hiddenDates: [],
23543      util: {
23544        toScreen: me._toScreen.bind(me),
23545        toGlobalScreen: me._toGlobalScreen.bind(me), // this refers to the root.width
23546        toTime: me._toTime.bind(me),
23547        toGlobalTime: me._toGlobalTime.bind(me)
23548      }
23549    };
23550
23551    // range
23552    this.range = new Range(this.body);
23553    this.components.push(this.range);
23554    this.body.range = this.range;
23555
23556    // time axis
23557    this.timeAxis = new TimeAxis(this.body);
23558    this.components.push(this.timeAxis);
23559    //this.body.util.snap = this.timeAxis.snap.bind(this.timeAxis);
23560
23561    // current time bar
23562    this.currentTime = new CurrentTime(this.body);
23563    this.components.push(this.currentTime);
23564
23565    // item set
23566    this.linegraph = new LineGraph(this.body);
23567    this.components.push(this.linegraph);
23568
23569    this.itemsData = null; // DataSet
23570    this.groupsData = null; // DataSet
23571
23572    this.on('tap', function (event) {
23573      me.emit('click', me.getEventProperties(event));
23574    });
23575    this.on('doubletap', function (event) {
23576      me.emit('doubleClick', me.getEventProperties(event));
23577    });
23578    this.dom.root.oncontextmenu = function (event) {
23579      me.emit('contextmenu', me.getEventProperties(event));
23580    };
23581
23582    // apply options
23583    if (options) {
23584      this.setOptions(options);
23585    }
23586
23587    // IMPORTANT: THIS HAPPENS BEFORE SET ITEMS!
23588    if (groups) {
23589      this.setGroups(groups);
23590    }
23591
23592    // create itemset
23593    if (items) {
23594      this.setItems(items);
23595    } else {
23596      this._redraw();
23597    }
23598  }
23599
23600  // Extend the functionality from Core
23601  Graph2d.prototype = new Core();
23602
23603  Graph2d.prototype.setOptions = function (options) {
23604    // validate options
23605    var errorFound = Validator.validate(options, allOptions);
23606    if (errorFound === true) {
23607      console.log('%cErrors have been found in the supplied options object.', printStyle);
23608    }
23609
23610    Core.prototype.setOptions.call(this, options);
23611  };
23612
23613  /**
23614   * Set items
23615   * @param {vis.DataSet | Array | null} items
23616   */
23617  Graph2d.prototype.setItems = function (items) {
23618    var initialLoad = this.itemsData == null;
23619
23620    // convert to type DataSet when needed
23621    var newDataSet;
23622    if (!items) {
23623      newDataSet = null;
23624    } else if (items instanceof DataSet || items instanceof DataView) {
23625      newDataSet = items;
23626    } else {
23627      // turn an array into a dataset
23628      newDataSet = new DataSet(items, {
23629        type: {
23630          start: 'Date',
23631          end: 'Date'
23632        }
23633      });
23634    }
23635
23636    // set items
23637    this.itemsData = newDataSet;
23638    this.linegraph && this.linegraph.setItems(newDataSet);
23639
23640    if (initialLoad) {
23641      if (this.options.start != undefined || this.options.end != undefined) {
23642        var start = this.options.start != undefined ? this.options.start : null;
23643        var end = this.options.end != undefined ? this.options.end : null;
23644
23645        this.setWindow(start, end, { animation: false });
23646      } else {
23647        this.fit({ animation: false });
23648      }
23649    }
23650  };
23651
23652  /**
23653   * Set groups
23654   * @param {vis.DataSet | Array} groups
23655   */
23656  Graph2d.prototype.setGroups = function (groups) {
23657    // convert to type DataSet when needed
23658    var newDataSet;
23659    if (!groups) {
23660      newDataSet = null;
23661    } else if (groups instanceof DataSet || groups instanceof DataView) {
23662      newDataSet = groups;
23663    } else {
23664      // turn an array into a dataset
23665      newDataSet = new DataSet(groups);
23666    }
23667
23668    this.groupsData = newDataSet;
23669    this.linegraph.setGroups(newDataSet);
23670  };
23671
23672  /**
vendor: 12,483 bytes, lines 23673-24043
23673   * 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).
23674   * @param groupId
23675   * @param width
23676   * @param height
23677   */
23678  Graph2d.prototype.getLegend = function (groupId, width, height) {
23679    if (width === undefined) {
23680      width = 15;
23681    }
23682    if (height === undefined) {
23683      height = 15;
23684    }
23685    if (this.linegraph.groups[groupId] !== undefined) {
23686      return this.linegraph.groups[groupId].getLegend(width, height);
23687    } else {
23688      return 'cannot find group:' + groupId;
23689    }
23690  };
23691
23692  /**
23693   * This checks if the visible option of the supplied group (by ID) is true or false.
23694   * @param groupId
23695   * @returns {*}
23696   */
23697  Graph2d.prototype.isGroupVisible = function (groupId) {
23698    if (this.linegraph.groups[groupId] !== undefined) {
23699      return this.linegraph.groups[groupId].visible && (this.linegraph.options.groups.visibility[groupId] === undefined || this.linegraph.options.groups.visibility[groupId] == true);
23700    } else {
23701      return false;
23702    }
23703  };
23704
23705  /**
23706   * Get the data range of the item set.
23707   * @returns {{min: Date, max: Date}} range  A range with a start and end Date.
23708   *                                          When no minimum is found, min==null
23709   *                                          When no maximum is found, max==null
23710   */
23711  Graph2d.prototype.getDataRange = function () {
23712    var min = null;
23713    var max = null;
23714
23715    // calculate min from start filed
23716    for (var groupId in this.linegraph.groups) {
23717      if (this.linegraph.groups.hasOwnProperty(groupId)) {
23718        if (this.linegraph.groups[groupId].visible == true) {
23719          for (var i = 0; i < this.linegraph.groups[groupId].itemsData.length; i++) {
23720            var item = this.linegraph.groups[groupId].itemsData[i];
23721            var value = util.convert(item.x, 'Date').valueOf();
23722            min = min == null ? value : min > value ? value : min;
23723            max = max == null ? value : max < value ? value : max;
23724          }
23725        }
23726      }
23727    }
23728
23729    return {
23730      min: min != null ? new Date(min) : null,
23731      max: max != null ? new Date(max) : null
23732    };
23733  };
23734
23735  /**
23736   * Generate Timeline related information from an event
23737   * @param {Event} event
23738   * @return {Object} An object with related information, like on which area
23739   *                  The event happened, whether clicked on an item, etc.
23740   */
23741  Graph2d.prototype.getEventProperties = function (event) {
23742    var clientX = event.center ? event.center.x : event.clientX;
23743    var clientY = event.center ? event.center.y : event.clientY;
23744    var x = clientX - util.getAbsoluteLeft(this.dom.centerContainer);
23745    var y = clientY - util.getAbsoluteTop(this.dom.centerContainer);
23746    var time = this._toTime(x);
23747
23748    var customTime = CustomTime.customTimeFromTarget(event);
23749
23750    var element = util.getTarget(event);
23751    var what = null;
23752    if (util.hasParent(element, this.timeAxis.dom.foreground)) {
23753      what = 'axis';
23754    } else if (this.timeAxis2 && util.hasParent(element, this.timeAxis2.dom.foreground)) {
23755      what = 'axis';
23756    } else if (util.hasParent(element, this.linegraph.yAxisLeft.dom.frame)) {
23757      what = 'data-axis';
23758    } else if (util.hasParent(element, this.linegraph.yAxisRight.dom.frame)) {
23759      what = 'data-axis';
23760    } else if (util.hasParent(element, this.linegraph.legendLeft.dom.frame)) {
23761      what = 'legend';
23762    } else if (util.hasParent(element, this.linegraph.legendRight.dom.frame)) {
23763      what = 'legend';
23764    } else if (customTime != null) {
23765      what = 'custom-time';
23766    } else if (util.hasParent(element, this.currentTime.bar)) {
23767      what = 'current-time';
23768    } else if (util.hasParent(element, this.dom.center)) {
23769      what = 'background';
23770    }
23771
23772    var value = [];
23773    var yAxisLeft = this.linegraph.yAxisLeft;
23774    var yAxisRight = this.linegraph.yAxisRight;
23775    if (!yAxisLeft.hidden) {
23776      value.push(yAxisLeft.screenToValue(y));
23777    }
23778    if (!yAxisRight.hidden) {
23779      value.push(yAxisRight.screenToValue(y));
23780    }
23781
23782    return {
23783      event: event,
23784      what: what,
23785      pageX: event.srcEvent ? event.srcEvent.pageX : event.pageX,
23786      pageY: event.srcEvent ? event.srcEvent.pageY : event.pageY,
23787      x: x,
23788      y: y,
23789      time: time,
23790      value: value
23791    };
23792  };
23793
23794  /**
23795   * Load a configurator
23796   * @return {Object}
23797   * @private
23798   */
23799  Graph2d.prototype._createConfigurator = function () {
23800    return new Configurator(this, this.dom.container, configureOptions);
23801  };
23802
23803  module.exports = Graph2d;
23804
23805/***/ },
23806/* 50 */
23807/***/ function(module, exports, __webpack_require__) {
23808
23809  'use strict';
23810
23811  var util = __webpack_require__(1);
23812  var DOMutil = __webpack_require__(7);
23813  var DataSet = __webpack_require__(8);
23814  var DataView = __webpack_require__(10);
23815  var Component = __webpack_require__(26);
23816  var DataAxis = __webpack_require__(51);
23817  var GraphGroup = __webpack_require__(53);
23818  var Legend = __webpack_require__(57);
23819  var BarFunctions = __webpack_require__(56);
23820  var LineFunctions = __webpack_require__(54);
23821
23822  var UNGROUPED = '__ungrouped__'; // reserved group id for ungrouped items
23823
23824  /**
23825   * This is the constructor of the LineGraph. It requires a Timeline body and options.
23826   *
23827   * @param body
23828   * @param options
23829   * @constructor
23830   */
23831  function LineGraph(body, options) {
23832    this.id = util.randomUUID();
23833    this.body = body;
23834
23835    this.defaultOptions = {
23836      yAxisOrientation: 'left',
23837      defaultGroup: 'default',
23838      sort: true,
23839      sampling: true,
23840      stack: false,
23841      graphHeight: '400px',
23842      shaded: {
23843        enabled: false,
23844        orientation: 'bottom' // top, bottom
23845      },
23846      style: 'line', // line, bar
23847      barChart: {
23848        width: 50,
23849        sideBySide: false,
23850        align: 'center' // left, center, right
23851      },
23852      interpolation: {
23853        enabled: true,
23854        parametrization: 'centripetal', // uniform (alpha = 0.0), chordal (alpha = 1.0), centripetal (alpha = 0.5)
23855        alpha: 0.5
23856      },
23857      drawPoints: {
23858        enabled: true,
23859        size: 6,
23860        style: 'square' // square, circle
23861      },
23862      dataAxis: {
23863        showMinorLabels: true,
23864        showMajorLabels: true,
23865        icons: false,
23866        width: '40px',
23867        visible: true,
23868        alignZeros: true,
23869        left: {
23870          range: { min: undefined, max: undefined },
23871          format: function format(value) {
23872            return value;
23873          },
23874          title: { text: undefined, style: undefined }
23875        },
23876        right: {
23877          range: { min: undefined, max: undefined },
23878          format: function format(value) {
23879            return value;
23880          },
23881          title: { text: undefined, style: undefined }
23882        }
23883      },
23884      legend: {
23885        enabled: false,
23886        icons: true,
23887        left: {
23888          visible: true,
23889          position: 'top-left' // top/bottom - left,right
23890        },
23891        right: {
23892          visible: true,
23893          position: 'top-right' // top/bottom - left,right
23894        }
23895      },
23896      groups: {
23897        visibility: {}
23898      }
23899    };
23900
23901    // options is shared by this ItemSet and all its items
23902    this.options = util.extend({}, this.defaultOptions);
23903    this.dom = {};
23904    this.props = {};
23905    this.hammer = null;
23906    this.groups = {};
23907    this.abortedGraphUpdate = false;
23908    this.updateSVGheight = false;
23909    this.updateSVGheightOnResize = false;
23910
23911    var me = this;
23912    this.itemsData = null; // DataSet
23913    this.groupsData = null; // DataSet
23914
23915    // listeners for the DataSet of the items
23916    this.itemListeners = {
23917      'add': function add(event, params, senderId) {
23918        me._onAdd(params.items);
23919      },
23920      'update': function update(event, params, senderId) {
23921        me._onUpdate(params.items);
23922      },
23923      'remove': function remove(event, params, senderId) {
23924        me._onRemove(params.items);
23925      }
23926    };
23927
23928    // listeners for the DataSet of the groups
23929    this.groupListeners = {
23930      'add': function add(event, params, senderId) {
23931        me._onAddGroups(params.items);
23932      },
23933      'update': function update(event, params, senderId) {
23934        me._onUpdateGroups(params.items);
23935      },
23936      'remove': function remove(event, params, senderId) {
23937        me._onRemoveGroups(params.items);
23938      }
23939    };
23940
23941    this.items = {}; // object with an Item for every data item
23942    this.selection = []; // list with the ids of all selected nodes
23943    this.lastStart = this.body.range.start;
23944    this.touchParams = {}; // stores properties while dragging
23945
23946    this.svgElements = {};
23947    this.setOptions(options);
23948    this.groupsUsingDefaultStyles = [0];
23949    this.COUNTER = 0;
23950    this.body.emitter.on('rangechanged', function () {
23951      me.lastStart = me.body.range.start;
23952      me.svg.style.left = util.option.asSize(-me.props.width);
23953      me.redraw.call(me, true);
23954    });
23955
23956    // create the HTML DOM
23957    this._create();
23958    this.framework = { svg: this.svg, svgElements: this.svgElements, options: this.options, groups: this.groups };
23959    this.body.emitter.emit('change');
23960  }
23961
23962  LineGraph.prototype = new Component();
23963
23964  /**
23965   * Create the HTML DOM for the ItemSet
23966   */
23967  LineGraph.prototype._create = function () {
23968    var frame = document.createElement('div');
23969    frame.className = 'vis-line-graph';
23970    this.dom.frame = frame;
23971
23972    // create svg element for graph drawing.
23973    this.svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
23974    this.svg.style.position = 'relative';
23975    this.svg.style.height = ('' + this.options.graphHeight).replace('px', '') + 'px';
23976    this.svg.style.display = 'block';
23977    frame.appendChild(this.svg);
23978
23979    // data axis
23980    this.options.dataAxis.orientation = 'left';
23981    this.yAxisLeft = new DataAxis(this.body, this.options.dataAxis, this.svg, this.options.groups);
23982
23983    this.options.dataAxis.orientation = 'right';
23984    this.yAxisRight = new DataAxis(this.body, this.options.dataAxis, this.svg, this.options.groups);
23985    delete this.options.dataAxis.orientation;
23986
23987    // legends
23988    this.legendLeft = new Legend(this.body, this.options.legend, 'left', this.options.groups);
23989    this.legendRight = new Legend(this.body, this.options.legend, 'right', this.options.groups);
23990
23991    this.show();
23992  };
23993
23994  /**
23995   * set the options of the LineGraph. the mergeOptions is used for subObjects that have an enabled element.
23996   * @param {object} options
23997   */
23998  LineGraph.prototype.setOptions = function (options) {
23999    if (options) {
24000      var fields = ['sampling', 'defaultGroup', 'stack', 'height', 'graphHeight', 'yAxisOrientation', 'style', 'barChart', 'dataAxis', 'sort', 'groups'];
24001      if (options.graphHeight === undefined && options.height !== undefined && this.body.domProps.centerContainer.height !== undefined) {
24002        this.updateSVGheight = true;
24003        this.updateSVGheightOnResize = true;
24004      } else if (this.body.domProps.centerContainer.height !== undefined && options.graphHeight !== undefined) {
24005        if (parseInt((options.graphHeight + '').replace('px', '')) < this.body.domProps.centerContainer.height) {
24006          this.updateSVGheight = true;
24007        }
24008      }
24009      util.selectiveDeepExtend(fields, this.options, options);
24010      util.mergeOptions(this.options, options, 'interpolation');
24011      util.mergeOptions(this.options, options, 'drawPoints');
24012      util.mergeOptions(this.options, options, 'shaded');
24013      util.mergeOptions(this.options, options, 'legend');
24014
24015      if (options.interpolation) {
24016        if (typeof options.interpolation == 'object') {
24017          if (options.interpolation.parametrization) {
24018            if (options.interpolation.parametrization == 'uniform') {
24019              this.options.interpolation.alpha = 0;
24020            } else if (options.interpolation.parametrization == 'chordal') {
24021              this.options.interpolation.alpha = 1.0;
24022            } else {
24023              this.options.interpolation.parametrization = 'centripetal';
24024              this.options.interpolation.alpha = 0.5;
24025            }
24026          }
24027        }
24028      }
24029
24030      if (this.yAxisLeft) {
24031        if (options.dataAxis !== undefined) {
24032          this.yAxisLeft.setOptions(this.options.dataAxis);
24033          this.yAxisRight.setOptions(this.options.dataAxis);
24034        }
24035      }
24036
24037      if (this.legendLeft) {
24038        if (options.legend !== undefined) {
24039          this.legendLeft.setOptions(this.options.legend);
24040          this.legendRight.setOptions(this.options.legend);
24041        }
24042      }
24043
24044      if (this.groups.hasOwnProperty(UNGROUPED)) {
24045        this.groups[UNGROUPED].setOptions(options);
24046      }
24047    }
24048
24049    // this is used to redraw the graph if the visibility of the groups is changed.
24050    if (this.dom.frame) {
24051      this.redraw(true);
24052    }
24053  };
24054
24055  /**
24056   * Hide the component from the DOM
24057   */
24058  LineGraph.prototype.hide = function () {
24059    // remove the frame containing the items
24060    if (this.dom.frame.parentNode) {
24061      this.dom.frame.parentNode.removeChild(this.dom.frame);
24062    }
24063  };
24064
24065  /**
24066   * Show the component in the DOM (when not already visible).
24067   * @return {Boolean} changed
24068   */
24069  LineGraph.prototype.show = function () {
24070    // show frame containing the items
24071    if (!this.dom.frame.parentNode) {
24072      this.body.dom.center.appendChild(this.dom.frame);
24073    }
24074  };
24075
24076  /**
24077   * Set items
24078   * @param {vis.DataSet | null} items
24079   */
24080  LineGraph.prototype.setItems = function (items) {
24081    var me = this,
24082        ids,
24083        oldItemsData = this.itemsData;
24084
24085    // replace the dataset
24086    if (!items) {
24087      this.itemsData = null;
24088    } else if (items instanceof DataSet || items instanceof DataView) {
24089      this.itemsData = items;
24090    } else {
24091      throw new TypeError('Data must be an instance of DataSet or DataView');
24092    }
24093
24094    if (oldItemsData) {
24095      // unsubscribe from old dataset
24096      util.forEach(this.itemListeners, function (callback, event) {
24097        oldItemsData.off(event, callback);
24098      });
24099
24100      // remove all drawn items
24101      ids = oldItemsData.getIds();
24102      this._onRemove(ids);
24103    }
24104
24105    if (this.itemsData) {
24106      // subscribe to new dataset
24107      var id = this.id;
24108      util.forEach(this.itemListeners, function (callback, event) {
24109        me.itemsData.on(event, callback, id);
24110      });
24111
24112      // add all new items
24113      ids = this.itemsData.getIds();
24114      this._onAdd(ids);
24115    }
24116    this._updateUngrouped();
24117    //this._updateGraph();
24118    this.redraw(true);
24119  };
24120
24121  /**
24122   * Set groups
24123   * @param {vis.DataSet} groups
24124   */
24125  LineGraph.prototype.setGroups = function (groups) {
24126    var me = this;
24127    var ids;
24128
24129    // unsubscribe from current dataset
24130    if (this.groupsData) {
24131      util.forEach(this.groupListeners, function (callback, event) {
24132        me.groupsData.off(event, callback);
24133      });
24134
24135      // remove all drawn groups
24136      ids = this.groupsData.getIds();
24137      this.groupsData = null;
24138      this._onRemoveGroups(ids); // note: this will cause a redraw
24139    }
24140
24141    // replace the dataset
24142    if (!groups) {
24143      this.groupsData = null;
24144    } else if (groups instanceof DataSet || groups instanceof DataView) {
24145      this.groupsData = groups;
24146    } else {
24147      throw new TypeError('Data must be an instance of DataSet or DataView');
24148    }
24149
24150    if (this.groupsData) {
24151      // subscribe to new dataset
24152      var id = this.id;
24153      util.forEach(this.groupListeners, function (callback, event) {
24154        me.groupsData.on(event, callback, id);
24155      });
24156
24157      // draw all ms
24158      ids = this.groupsData.getIds();
24159      this._onAddGroups(ids);
24160    }
24161    this._onUpdate();
24162  };
24163
24164  /**
24165   * Update the data
24166   * @param [ids]
24167   * @private
24168   */
24169  LineGraph.prototype._onUpdate = function (ids) {
24170    this._updateUngrouped();
24171    this._updateAllGroupData();
24172    //this._updateGraph();
24173    this.redraw(true);
24174  };
24175  LineGraph.prototype._onAdd = function (ids) {
24176    this._onUpdate(ids);
24177  };
24178  LineGraph.prototype._onRemove = function (ids) {
24179    this._onUpdate(ids);
24180  };
24181  LineGraph.prototype._onUpdateGroups = function (groupIds) {
24182    for (var i = 0; i < groupIds.length; i++) {
24183      var group = this.groupsData.get(groupIds[i]);
24184      this._updateGroup(group, groupIds[i]);
24185    }
24186
24187    //this._updateGraph();
24188    this.redraw(true);
24189  };
24190  LineGraph.prototype._onAddGroups = function (groupIds) {
24191    this._onUpdateGroups(groupIds);
24192  };
24193
24194  /**
24195   * this cleans the group out off the legends and the dataaxis, updates the ungrouped and updates the graph
24196   * @param {Array} groupIds
24197   * @private
24198   */
24199  LineGraph.prototype._onRemoveGroups = function (groupIds) {
24200    for (var i = 0; i < groupIds.length; i++) {
24201      if (this.groups.hasOwnProperty(groupIds[i])) {
24202        if (this.groups[groupIds[i]].options.yAxisOrientation == 'right') {
24203          this.yAxisRight.removeGroup(groupIds[i]);
24204          this.legendRight.removeGroup(groupIds[i]);
24205          this.legendRight.redraw();
24206        } else {
24207          this.yAxisLeft.removeGroup(groupIds[i]);
24208          this.legendLeft.removeGroup(groupIds[i]);
24209          this.legendLeft.redraw();
24210        }
24211        delete this.groups[groupIds[i]];
24212      }
24213    }
24214    this._updateUngrouped();
24215    //this._updateGraph();
24216    this.redraw(true);
24217  };
24218
24219  /**
24220   * update a group object with the group dataset entree
24221   *
24222   * @param group
24223   * @param groupId
24224   * @private
24225   */
24226  LineGraph.prototype._updateGroup = function (group, groupId) {
24227    if (!this.groups.hasOwnProperty(groupId)) {
24228      this.groups[groupId] = new GraphGroup(group, groupId, this.options, this.groupsUsingDefaultStyles);
24229      if (this.groups[groupId].options.yAxisOrientation == 'right') {
24230        this.yAxisRight.addGroup(groupId, this.groups[groupId]);
24231        this.legendRight.addGroup(groupId, this.groups[groupId]);
24232      } else {
24233        this.yAxisLeft.addGroup(groupId, this.groups[groupId]);
24234        this.legendLeft.addGroup(groupId, this.groups[groupId]);
24235      }
24236    } else {
24237      this.groups[groupId].update(group);
24238      if (this.groups[groupId].options.yAxisOrientation == 'right') {
24239        this.yAxisRight.updateGroup(groupId, this.groups[groupId]);
24240        this.legendRight.updateGroup(groupId, this.groups[groupId]);
24241      } else {
24242        this.yAxisLeft.updateGroup(groupId, this.groups[groupId]);
24243        this.legendLeft.updateGroup(groupId, this.groups[groupId]);
24244      }
24245    }
24246    this.legendLeft.redraw();
24247    this.legendRight.redraw();
24248  };
24249
24250  /**
24251   * this updates all groups, it is used when there is an update the the itemset.
24252   *
24253   * @private
24254   */
24255  LineGraph.prototype._updateAllGroupData = function () {
24256    if (this.itemsData != null) {
24257      var groupsContent = {};
24258      var groupId;
24259      for (groupId in this.groups) {
24260        if (this.groups.hasOwnProperty(groupId)) {
24261          groupsContent[groupId] = [];
24262        }
24263      }
24264      for (var itemId in this.itemsData._data) {
24265        if (this.itemsData._data.hasOwnProperty(itemId)) {
24266          var item = this.itemsData._data[itemId];
24267          if (groupsContent[item.group] === undefined) {
24268            throw new Error('Cannot find referenced group ' + item.group + '. Possible reason: items added before groups? Groups need to be added before items, as items refer to groups.');
24269          }
24270          item.x = util.convert(item.x, 'Date');
24271          groupsContent[item.group].push(item);
24272        }
24273      }
24274      for (groupId in this.groups) {
24275        if (this.groups.hasOwnProperty(groupId)) {
24276          this.groups[groupId].setItems(groupsContent[groupId]);
24277        }
24278      }
24279    }
24280  };
24281
24282  /**
24283   * Create or delete the group holding all ungrouped items. This group is used when
24284   * there are no groups specified. This anonymous group is called 'graph'.
24285   * @protected
24286   */
24287  LineGraph.prototype._updateUngrouped = function () {
24288    if (this.itemsData && this.itemsData != null) {
24289      var ungroupedCounter = 0;
24290      for (var itemId in this.itemsData._data) {
24291        if (this.itemsData._data.hasOwnProperty(itemId)) {
24292          var item = this.itemsData._data[itemId];
24293          if (item != undefined) {
24294            if (item.hasOwnProperty('group')) {
24295              if (item.group === undefined) {
24296                item.group = UNGROUPED;
24297              }
24298            } else {
24299              item.group = UNGROUPED;
24300            }
24301            ungroupedCounter = item.group == UNGROUPED ? ungroupedCounter + 1 : ungroupedCounter;
24302          }
24303        }
24304      }
24305
24306      if (ungroupedCounter == 0) {
24307        delete this.groups[UNGROUPED];
24308        this.legendLeft.removeGroup(UNGROUPED);
24309        this.legendRight.removeGroup(UNGROUPED);
24310        this.yAxisLeft.removeGroup(UNGROUPED);
24311        this.yAxisRight.removeGroup(UNGROUPED);
24312      } else {
24313        var group = { id: UNGROUPED, content: this.options.defaultGroup };
24314        this._updateGroup(group, UNGROUPED);
24315      }
24316    } else {
24317      delete this.groups[UNGROUPED];
24318      this.legendLeft.removeGroup(UNGROUPED);
24319      this.legendRight.removeGroup(UNGROUPED);
24320      this.yAxisLeft.removeGroup(UNGROUPED);
24321      this.yAxisRight.removeGroup(UNGROUPED);
24322    }
24323
24324    this.legendLeft.redraw();
24325    this.legendRight.redraw();
24326  };
24327
24328  /**
24329   * Redraw the component, mandatory function
24330   * @return {boolean} Returns true if the component is resized
24331   */
24332  LineGraph.prototype.redraw = function (forceGraphUpdate) {
24333    var resized = false;
24334
24335    // calculate actual size and position
24336    this.props.width = this.dom.frame.offsetWidth;
24337    this.props.height = this.body.domProps.centerContainer.height - this.body.domProps.border.top - this.body.domProps.border.bottom;
24338
24339    // update the graph if there is no lastWidth or with, used for the initial draw
24340    if (this.lastWidth === undefined && this.props.width) {
24341      forceGraphUpdate = true;
24342    }
24343
24344    // check if this component is resized
24345    resized = this._isResized() || resized;
24346
24347    // check whether zoomed (in that case we need to re-stack everything)
24348    var visibleInterval = this.body.range.end - this.body.range.start;
24349    var zoomed = visibleInterval != this.lastVisibleInterval;
24350    this.lastVisibleInterval = visibleInterval;
24351
24352    // the svg element is three times as big as the width, this allows for fully dragging left and right
24353    // without reloading the graph. the controls for this are bound to events in the constructor
24354    if (resized == true) {
24355      this.svg.style.width = util.option.asSize(3 * this.props.width);
24356      this.svg.style.left = util.option.asSize(-this.props.width);
24357
24358      // if the height of the graph is set as proportional, change the height of the svg
24359      if ((this.options.height + '').indexOf('%') != -1 || this.updateSVGheightOnResize == true) {
24360        this.updateSVGheight = true;
24361      }
24362    }
24363
24364    // update the height of the graph on each redraw of the graph.
24365    if (this.updateSVGheight == true) {
24366      if (this.options.graphHeight != this.props.height + 'px') {
24367        this.options.graphHeight = this.props.height + 'px';
24368        this.svg.style.height = this.props.height + 'px';
24369      }
24370      this.updateSVGheight = false;
24371    } else {
24372      this.svg.style.height = ('' + this.options.graphHeight).replace('px', '') + 'px';
24373    }
24374
24375    // zoomed is here to ensure that animations are shown correctly.
24376    if (resized == true || zoomed == true || this.abortedGraphUpdate == true || forceGraphUpdate == true) {
24377      resized = this._updateGraph() || resized;
24378    } else {
24379      // move the whole svg while dragging
24380      if (this.lastStart != 0) {
24381        var offset = this.body.range.start - this.lastStart;
24382        var range = this.body.range.end - this.body.range.start;
24383        if (this.props.width != 0) {
24384          var rangePerPixelInv = this.props.width / range;
24385          var xOffset = offset * rangePerPixelInv;
24386          this.svg.style.left = -this.props.width - xOffset + 'px';
24387        }
24388      }
24389    }
24390
24391    this.legendLeft.redraw();
24392    this.legendRight.redraw();
24393    return resized;
24394  };
24395
24396  /**
24397   * Update and redraw the graph.
24398   *
24399   */
24400  LineGraph.prototype._updateGraph = function () {
24401    // reset the svg elements
24402    DOMutil.prepareElements(this.svgElements);
24403    if (this.props.width != 0 && this.itemsData != null) {
24404      var group, i;
24405      var preprocessedGroupData = {};
24406      var processedGroupData = {};
24407      var groupRanges = {};
24408      var changeCalled = false;
24409
24410      // getting group Ids
24411      var groupIds = [];
24412      for (var groupId in this.groups) {
24413        if (this.groups.hasOwnProperty(groupId)) {
24414          group = this.groups[groupId];
24415          if (group.visible == true && (this.options.groups.visibility[groupId] === undefined || this.options.groups.visibility[groupId] == true)) {
24416            groupIds.push(groupId);
24417          }
24418        }
24419      }
24420      if (groupIds.length > 0) {
24421        // this is the range of the SVG canvas
24422        var minDate = this.body.util.toGlobalTime(-this.body.domProps.root.width);
24423        var maxDate = this.body.util.toGlobalTime(2 * this.body.domProps.root.width);
24424        var groupsData = {};
24425        // fill groups data, this only loads the data we require based on the timewindow
24426        this._getRelevantData(groupIds, groupsData, minDate, maxDate);
24427
24428        // apply sampling, if disabled, it will pass through this function.
24429        this._applySampling(groupIds, groupsData);
24430
24431        // we transform the X coordinates to detect collisions
24432        for (i = 0; i < groupIds.length; i++) {
24433          preprocessedGroupData[groupIds[i]] = this._convertXcoordinates(groupsData[groupIds[i]]);
24434        }
24435
24436        // now all needed data has been collected we start the processing.
24437        this._getYRanges(groupIds, preprocessedGroupData, groupRanges);
24438
24439        // update the Y axis first, we use this data to draw at the correct Y points
24440        // changeCalled is required to clean the SVG on a change emit.
24441        changeCalled = this._updateYAxis(groupIds, groupRanges);
24442        var MAX_CYCLES = 5;
24443        if (changeCalled == true && this.COUNTER < MAX_CYCLES) {
24444          DOMutil.cleanupElements(this.svgElements);
24445          this.abortedGraphUpdate = true;
24446          this.COUNTER++;
24447          this.body.emitter.emit('change');
24448          return true;
24449        } else {
24450          if (this.COUNTER > MAX_CYCLES) {
24451            console.log('WARNING: there may be an infinite loop in the _updateGraph emitter cycle.');
24452          }
24453          this.COUNTER = 0;
24454          this.abortedGraphUpdate = false;
24455
24456          // With the yAxis scaled correctly, use this to get the Y values of the points.
24457          for (i = 0; i < groupIds.length; i++) {
24458            group = this.groups[groupIds[i]];
24459            processedGroupData[groupIds[i]] = this._convertYcoordinates(groupsData[groupIds[i]], group);
24460          }
24461
24462          // draw the groups
24463          for (i = 0; i < groupIds.length; i++) {
24464            group = this.groups[groupIds[i]];
24465            if (group.options.style != 'bar') {
24466              // bar needs to be drawn enmasse
24467              group.draw(processedGroupData[groupIds[i]], group, this.framework);
24468            }
24469          }
24470          BarFunctions.draw(groupIds, processedGroupData, this.framework);
24471        }
24472      }
24473    }
24474
24475    // cleanup unused svg elements
24476    DOMutil.cleanupElements(this.svgElements);
24477    return false;
24478  };
24479
24480  /**
24481   * first select and preprocess the data from the datasets.
24482   * the groups have their preselection of data, we now loop over this data to see
24483   * what data we need to draw. Sorted data is much faster.
24484   * more optimization is possible by doing the sampling before and using the binary search
24485   * to find the end date to determine the increment.
24486   *
24487   * @param {array}  groupIds
24488   * @param {object} groupsData
24489   * @param {date}   minDate
24490   * @param {date}   maxDate
24491   * @private
24492   */
24493  LineGraph.prototype._getRelevantData = function (groupIds, groupsData, minDate, maxDate) {
24494    var group, i, j, item;
24495    if (groupIds.length > 0) {
24496      for (i = 0; i < groupIds.length; i++) {
24497        group = this.groups[groupIds[i]];
24498        groupsData[groupIds[i]] = [];
24499        var dataContainer = groupsData[groupIds[i]];
24500        // optimization for sorted data
24501        if (group.options.sort == true) {
24502          var guess = Math.max(0, util.binarySearchValue(group.itemsData, minDate, 'x', 'before'));
24503          for (j = guess; j < group.itemsData.length; j++) {
24504            item = group.itemsData[j];
24505            if (item !== undefined) {
24506              if (item.x > maxDate) {
24507                dataContainer.push(item);
24508                break;
24509              } else {
24510                dataContainer.push(item);
24511              }
24512            }
24513          }
24514        } else {
24515          for (j = 0; j < group.itemsData.length; j++) {
24516            item = group.itemsData[j];
24517            if (item !== undefined) {
24518              if (item.x > minDate && item.x < maxDate) {
24519                dataContainer.push(item);
24520              }
24521            }
24522          }
24523        }
24524      }
24525    }
24526  };
24527
24528  /**
24529   *
24530   * @param groupIds
24531   * @param groupsData
24532   * @private
24533   */
24534  LineGraph.prototype._applySampling = function (groupIds, groupsData) {
24535    var group;
24536    if (groupIds.length > 0) {
24537      for (var i = 0; i < groupIds.length; i++) {
24538        group = this.groups[groupIds[i]];
24539        if (group.options.sampling == true) {
24540          var dataContainer = groupsData[groupIds[i]];
24541          if (dataContainer.length > 0) {
24542            var increment = 1;
24543            var amountOfPoints = dataContainer.length;
24544
24545            // the global screen is used because changing the width of the yAxis may affect the increment, resulting in an endless loop
24546            // of width changing of the yAxis.
24547            var xDistance = this.body.util.toGlobalScreen(dataContainer[dataContainer.length - 1].x) - this.body.util.toGlobalScreen(dataContainer[0].x);
24548            var pointsPerPixel = amountOfPoints / xDistance;
24549            increment = Math.min(Math.ceil(0.2 * amountOfPoints), Math.max(1, Math.round(pointsPerPixel)));
24550
24551            var sampledData = [];
24552            for (var j = 0; j < amountOfPoints; j += increment) {
24553              sampledData.push(dataContainer[j]);
24554            }
24555            groupsData[groupIds[i]] = sampledData;
24556          }
24557        }
24558      }
24559    }
24560  };
24561
24562  /**
24563   *
24564   *
24565   * @param {array}  groupIds
24566   * @param {object} groupsData
24567   * @param {object} groupRanges  | this is being filled here
24568   * @private
24569   */
24570  LineGraph.prototype._getYRanges = function (groupIds, groupsData, groupRanges) {
24571    var groupData, group, i;
24572    var combinedDataLeft = [];
24573    var combinedDataRight = [];
24574    var options;
24575    if (groupIds.length > 0) {
24576      for (i = 0; i < groupIds.length; i++) {
24577        groupData = groupsData[groupIds[i]];
24578        options = this.groups[groupIds[i]].options;
24579        if (groupData.length > 0) {
24580          group = this.groups[groupIds[i]];
24581          // if bar graphs are stacked, their range need to be handled differently and accumulated over all groups.
24582          if (options.stack === true && options.style === 'bar') {
vendor: 12,409 bytes, lines 24583-24966
24583            if (options.yAxisOrientation === 'left') {
24584              combinedDataLeft = combinedDataLeft.concat(group.getData(groupData));
24585            } else {
24586              combinedDataRight = combinedDataRight.concat(group.getData(groupData));
24587            }
24588          } else {
24589            groupRanges[groupIds[i]] = group.getYRange(groupData, groupIds[i]);
24590          }
24591        }
24592      }
24593
24594      // if bar graphs are stacked, their range need to be handled differently and accumulated over all groups.
24595      BarFunctions.getStackedYRange(combinedDataLeft, groupRanges, groupIds, '__barStackLeft', 'left');
24596      BarFunctions.getStackedYRange(combinedDataRight, groupRanges, groupIds, '__barStackRight', 'right');
24597      // if line graphs are stacked, their range need to be handled differently and accumulated over all groups.
24598      //LineFunctions.getStackedYRange(combinedDataLeft , groupRanges, groupIds, '__lineStackLeft' , 'left' );
24599      //LineFunctions.getStackedYRange(combinedDataRight, groupRanges, groupIds, '__lineStackRight', 'right');
24600    }
24601  };
24602
24603  /**
24604   * this sets the Y ranges for the Y axis. It also determines which of the axis should be shown or hidden.
24605   * @param {Array} groupIds
24606   * @param {Object} groupRanges
24607   * @private
24608   */
24609  LineGraph.prototype._updateYAxis = function (groupIds, groupRanges) {
24610    var resized = false;
24611    var yAxisLeftUsed = false;
24612    var yAxisRightUsed = false;
24613    var minLeft = 1e9,
24614        minRight = 1e9,
24615        maxLeft = -1e9,
24616        maxRight = -1e9,
24617        minVal,
24618        maxVal;
24619    // if groups are present
24620    if (groupIds.length > 0) {
24621      // 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.
24622      for (var i = 0; i < groupIds.length; i++) {
24623        var group = this.groups[groupIds[i]];
24624        if (group && group.options.yAxisOrientation != 'right') {
24625          yAxisLeftUsed = true;
24626          minLeft = 1e9;
24627          maxLeft = -1e9;
24628        } else if (group && group.options.yAxisOrientation) {
24629          yAxisRightUsed = true;
24630          minRight = 1e9;
24631          maxRight = -1e9;
24632        }
24633      }
24634
24635      // if there are items:
24636      for (var i = 0; i < groupIds.length; i++) {
24637        if (groupRanges.hasOwnProperty(groupIds[i])) {
24638          if (groupRanges[groupIds[i]].ignore !== true) {
24639            minVal = groupRanges[groupIds[i]].min;
24640            maxVal = groupRanges[groupIds[i]].max;
24641
24642            if (groupRanges[groupIds[i]].yAxisOrientation != 'right') {
24643              yAxisLeftUsed = true;
24644              minLeft = minLeft > minVal ? minVal : minLeft;
24645              maxLeft = maxLeft < maxVal ? maxVal : maxLeft;
24646            } else {
24647              yAxisRightUsed = true;
24648              minRight = minRight > minVal ? minVal : minRight;
24649              maxRight = maxRight < maxVal ? maxVal : maxRight;
24650            }
24651          }
24652        }
24653      }
24654
24655      if (yAxisLeftUsed == true) {
24656        this.yAxisLeft.setRange(minLeft, maxLeft);
24657      }
24658      if (yAxisRightUsed == true) {
24659        this.yAxisRight.setRange(minRight, maxRight);
24660      }
24661    }
24662    resized = this._toggleAxisVisiblity(yAxisLeftUsed, this.yAxisLeft) || resized;
24663    resized = this._toggleAxisVisiblity(yAxisRightUsed, this.yAxisRight) || resized;
24664
24665    if (yAxisRightUsed == true && yAxisLeftUsed == true) {
24666      this.yAxisLeft.drawIcons = true;
24667      this.yAxisRight.drawIcons = true;
24668    } else {
24669      this.yAxisLeft.drawIcons = false;
24670      this.yAxisRight.drawIcons = false;
24671    }
24672    this.yAxisRight.master = !yAxisLeftUsed;
24673    if (this.yAxisRight.master == false) {
24674      if (yAxisRightUsed == true) {
24675        this.yAxisLeft.lineOffset = this.yAxisRight.width;
24676      } else {
24677        this.yAxisLeft.lineOffset = 0;
24678      }
24679
24680      resized = this.yAxisLeft.redraw() || resized;
24681      this.yAxisRight.stepPixels = this.yAxisLeft.stepPixels;
24682      this.yAxisRight.zeroCrossing = this.yAxisLeft.zeroCrossing;
24683      this.yAxisRight.amountOfSteps = this.yAxisLeft.amountOfSteps;
24684      resized = this.yAxisRight.redraw() || resized;
24685    } else {
24686      resized = this.yAxisRight.redraw() || resized;
24687    }
24688
24689    // clean the accumulated lists
24690    var tempGroups = ['__barStackLeft', '__barStackRight', '__lineStackLeft', '__lineStackRight'];
24691    for (var i = 0; i < tempGroups.length; i++) {
24692      if (groupIds.indexOf(tempGroups[i]) != -1) {
24693        groupIds.splice(groupIds.indexOf(tempGroups[i]), 1);
24694      }
24695    }
24696
24697    return resized;
24698  };
24699
24700  /**
24701   * This shows or hides the Y axis if needed. If there is a change, the changed event is emitted by the updateYAxis function
24702   *
24703   * @param {boolean} axisUsed
24704   * @returns {boolean}
24705   * @private
24706   * @param axis
24707   */
24708  LineGraph.prototype._toggleAxisVisiblity = function (axisUsed, axis) {
24709    var changed = false;
24710    if (axisUsed == false) {
24711      if (axis.dom.frame.parentNode && axis.hidden == false) {
24712        axis.hide();
24713        changed = true;
24714      }
24715    } else {
24716      if (!axis.dom.frame.parentNode && axis.hidden == true) {
24717        axis.show();
24718        changed = true;
24719      }
24720    }
24721    return changed;
24722  };
24723
24724  /**
24725   * This uses the DataAxis object to generate the correct X coordinate on the SVG window. It uses the
24726   * util function toScreen to get the x coordinate from the timestamp. It also pre-filters the data and get the minMax ranges for
24727   * the yAxis.
24728   *
24729   * @param datapoints
24730   * @returns {Array}
24731   * @private
24732   */
24733  LineGraph.prototype._convertXcoordinates = function (datapoints) {
24734    var extractedData = [];
24735    var xValue, yValue;
24736    var toScreen = this.body.util.toScreen;
24737
24738    for (var i = 0; i < datapoints.length; i++) {
24739      xValue = toScreen(datapoints[i].x) + this.props.width;
24740      yValue = datapoints[i].y;
24741      extractedData.push({ x: xValue, y: yValue });
24742    }
24743
24744    return extractedData;
24745  };
24746
24747  /**
24748   * This uses the DataAxis object to generate the correct X coordinate on the SVG window. It uses the
24749   * util function toScreen to get the x coordinate from the timestamp. It also pre-filters the data and get the minMax ranges for
24750   * the yAxis.
24751   *
24752   * @param datapoints
24753   * @param group
24754   * @returns {Array}
24755   * @private
24756   */
24757  LineGraph.prototype._convertYcoordinates = function (datapoints, group) {
24758    var extractedData = [];
24759    var xValue, yValue;
24760    var toScreen = this.body.util.toScreen;
24761    var axis = this.yAxisLeft;
24762    var svgHeight = Number(this.svg.style.height.replace('px', ''));
24763    if (group.options.yAxisOrientation == 'right') {
24764      axis = this.yAxisRight;
24765    }
24766
24767    for (var i = 0; i < datapoints.length; i++) {
24768      var labelValue = datapoints[i].label ? datapoints[i].label : null;
24769      xValue = toScreen(datapoints[i].x) + this.props.width;
24770      yValue = Math.round(axis.convertValue(datapoints[i].y));
24771      extractedData.push({ x: xValue, y: yValue, label: labelValue });
24772    }
24773
24774    group.setZeroPosition(Math.min(svgHeight, axis.convertValue(0)));
24775
24776    return extractedData;
24777  };
24778
24779  module.exports = LineGraph;
24780
24781/***/ },
24782/* 51 */
24783/***/ function(module, exports, __webpack_require__) {
24784
24785  'use strict';
24786
24787  var util = __webpack_require__(1);
24788  var DOMutil = __webpack_require__(7);
24789  var Component = __webpack_require__(26);
24790  var DataStep = __webpack_require__(52);
24791
24792  /**
24793   * A horizontal time axis
24794   * @param {Object} [options]        See DataAxis.setOptions for the available
24795   *                                  options.
24796   * @constructor DataAxis
24797   * @extends Component
24798   * @param body
24799   */
24800  function DataAxis(body, options, svg, linegraphOptions) {
24801    this.id = util.randomUUID();
24802    this.body = body;
24803
24804    this.defaultOptions = {
24805      orientation: 'left', // supported: 'left', 'right'
24806      showMinorLabels: true,
24807      showMajorLabels: true,
24808      icons: true,
24809      majorLinesOffset: 7,
24810      minorLinesOffset: 4,
24811      labelOffsetX: 10,
24812      labelOffsetY: 2,
24813      iconWidth: 20,
24814      width: '40px',
24815      visible: true,
24816      alignZeros: true,
24817      left: {
24818        range: { min: undefined, max: undefined },
24819        format: function format(value) {
24820          return value;
24821        },
24822        title: { text: undefined, style: undefined }
24823      },
24824      right: {
24825        range: { min: undefined, max: undefined },
24826        format: function format(value) {
24827          return value;
24828        },
24829        title: { text: undefined, style: undefined }
24830      }
24831    };
24832
24833    this.linegraphOptions = linegraphOptions;
24834    this.linegraphSVG = svg;
24835    this.props = {};
24836    this.DOMelements = { // dynamic elements
24837      lines: {},
24838      labels: {},
24839      title: {}
24840    };
24841
24842    this.dom = {};
24843
24844    this.range = { start: 0, end: 0 };
24845
24846    this.options = util.extend({}, this.defaultOptions);
24847    this.conversionFactor = 1;
24848
24849    this.setOptions(options);
24850    this.width = Number(('' + this.options.width).replace('px', ''));
24851    this.minWidth = this.width;
24852    this.height = this.linegraphSVG.offsetHeight;
24853    this.hidden = false;
24854
24855    this.stepPixels = 25;
24856    this.zeroCrossing = -1;
24857    this.amountOfSteps = -1;
24858
24859    this.lineOffset = 0;
24860    this.master = true;
24861    this.svgElements = {};
24862    this.iconsRemoved = false;
24863
24864    this.groups = {};
24865    this.amountOfGroups = 0;
24866
24867    // create the HTML DOM
24868    this._create();
24869
24870    var me = this;
24871    this.body.emitter.on('verticalDrag', function () {
24872      me.dom.lineContainer.style.top = me.body.domProps.scrollTop + 'px';
24873    });
24874  }
24875
24876  DataAxis.prototype = new Component();
24877
24878  DataAxis.prototype.addGroup = function (label, graphOptions) {
24879    if (!this.groups.hasOwnProperty(label)) {
24880      this.groups[label] = graphOptions;
24881    }
24882    this.amountOfGroups += 1;
24883  };
24884
24885  DataAxis.prototype.updateGroup = function (label, graphOptions) {
24886    this.groups[label] = graphOptions;
24887  };
24888
24889  DataAxis.prototype.removeGroup = function (label) {
24890    if (this.groups.hasOwnProperty(label)) {
24891      delete this.groups[label];
24892      this.amountOfGroups -= 1;
24893    }
24894  };
24895
24896  DataAxis.prototype.setOptions = function (options) {
24897    if (options) {
24898      var redraw = false;
24899      if (this.options.orientation != options.orientation && options.orientation !== undefined) {
24900        redraw = true;
24901      }
24902      var fields = ['orientation', 'showMinorLabels', 'showMajorLabels', 'icons', 'majorLinesOffset', 'minorLinesOffset', 'labelOffsetX', 'labelOffsetY', 'iconWidth', 'width', 'visible', 'left', 'right', 'alignZeros'];
24903      util.selectiveExtend(fields, this.options, options);
24904
24905      this.minWidth = Number(('' + this.options.width).replace('px', ''));
24906
24907      if (redraw === true && this.dom.frame) {
24908        this.hide();
24909        this.show();
24910      }
24911    }
24912  };
24913
24914  /**
24915   * Create the HTML DOM for the DataAxis
24916   */
24917  DataAxis.prototype._create = function () {
24918    this.dom.frame = document.createElement('div');
24919    this.dom.frame.style.width = this.options.width;
24920    this.dom.frame.style.height = this.height;
24921
24922    this.dom.lineContainer = document.createElement('div');
24923    this.dom.lineContainer.style.width = '100%';
24924    this.dom.lineContainer.style.height = this.height;
24925    this.dom.lineContainer.style.position = 'relative';
24926
24927    // create svg element for graph drawing.
24928    this.svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
24929    this.svg.style.position = 'absolute';
24930    this.svg.style.top = '0px';
24931    this.svg.style.height = '100%';
24932    this.svg.style.width = '100%';
24933    this.svg.style.display = 'block';
24934    this.dom.frame.appendChild(this.svg);
24935  };
24936
24937  DataAxis.prototype._redrawGroupIcons = function () {
24938    DOMutil.prepareElements(this.svgElements);
24939
24940    var x;
24941    var iconWidth = this.options.iconWidth;
24942    var iconHeight = 15;
24943    var iconOffset = 4;
24944    var y = iconOffset + 0.5 * iconHeight;
24945
24946    if (this.options.orientation === 'left') {
24947      x = iconOffset;
24948    } else {
24949      x = this.width - iconWidth - iconOffset;
24950    }
24951
24952    var groupArray = Object.keys(this.groups);
24953    groupArray.sort(function (a, b) {
24954      return a < b ? -1 : 1;
24955    });
24956
24957    for (var i = 0; i < groupArray.length; i++) {
24958      var groupId = groupArray[i];
24959      if (this.groups[groupId].visible === true && (this.linegraphOptions.visibility[groupId] === undefined || this.linegraphOptions.visibility[groupId] === true)) {
24960        this.groups[groupId].drawIcon(x, y, this.svgElements, this.svg, iconWidth, iconHeight);
24961        y += iconHeight + iconOffset;
24962      }
24963    }
24964
24965    DOMutil.cleanupElements(this.svgElements);
24966    this.iconsRemoved = false;
24967  };
24968
24969  DataAxis.prototype._cleanupIcons = function () {
24970    if (this.iconsRemoved === false) {
24971      DOMutil.prepareElements(this.svgElements);
24972      DOMutil.cleanupElements(this.svgElements);
24973      this.iconsRemoved = true;
24974    }
24975  };
24976
24977  /**
24978   * Create the HTML DOM for the DataAxis
24979   */
24980  DataAxis.prototype.show = function () {
24981    this.hidden = false;
24982    if (!this.dom.frame.parentNode) {
24983      if (this.options.orientation === 'left') {
24984        this.body.dom.left.appendChild(this.dom.frame);
24985      } else {
24986        this.body.dom.right.appendChild(this.dom.frame);
24987      }
24988    }
24989
24990    if (!this.dom.lineContainer.parentNode) {
24991      this.body.dom.backgroundHorizontal.appendChild(this.dom.lineContainer);
24992    }
24993  };
24994
24995  /**
24996   * Create the HTML DOM for the DataAxis
24997   */
24998  DataAxis.prototype.hide = function () {
24999    this.hidden = true;
25000    if (this.dom.frame.parentNode) {
25001      this.dom.frame.parentNode.removeChild(this.dom.frame);
25002    }
25003
25004    if (this.dom.lineContainer.parentNode) {
25005      this.dom.lineContainer.parentNode.removeChild(this.dom.lineContainer);
25006    }
25007  };
25008
25009  /**
25010   * Set a range (start and end)
25011   * @param end
25012   * @param start
25013   * @param end
25014   */
25015  DataAxis.prototype.setRange = function (start, end) {
25016    if (this.master === false && this.options.alignZeros === true && this.zeroCrossing != -1) {
25017      if (start > 0) {
25018        start = 0;
25019      }
25020    }
25021    this.range.start = start;
25022    this.range.end = end;
25023  };
25024
25025  /**
25026   * Repaint the component
25027   * @return {boolean} Returns true if the component is resized
25028   */
25029  DataAxis.prototype.redraw = function () {
25030    var resized = false;
25031    var activeGroups = 0;
25032
25033    // Make sure the line container adheres to the vertical scrolling.
25034    this.dom.lineContainer.style.top = this.body.domProps.scrollTop + 'px';
25035
25036    for (var groupId in this.groups) {
25037      if (this.groups.hasOwnProperty(groupId)) {
25038        if (this.groups[groupId].visible === true && (this.linegraphOptions.visibility[groupId] === undefined || this.linegraphOptions.visibility[groupId] === true)) {
25039          activeGroups++;
25040        }
25041      }
25042    }
25043    if (this.amountOfGroups === 0 || activeGroups === 0) {
25044      this.hide();
25045    } else {
25046      this.show();
25047      this.height = Number(this.linegraphSVG.style.height.replace('px', ''));
25048
25049      // svg offsetheight did not work in firefox and explorer...
25050      this.dom.lineContainer.style.height = this.height + 'px';
25051      this.width = this.options.visible === true ? Number(('' + this.options.width).replace('px', '')) : 0;
25052
25053      var props = this.props;
25054      var frame = this.dom.frame;
25055
25056      // update classname
25057      frame.className = 'vis-data-axis';
25058
25059      // calculate character width and height
25060      this._calculateCharSize();
25061
25062      var orientation = this.options.orientation;
25063      var showMinorLabels = this.options.showMinorLabels;
25064      var showMajorLabels = this.options.showMajorLabels;
25065
25066      // determine the width and height of the elements for the axis
25067      props.minorLabelHeight = showMinorLabels ? props.minorCharHeight : 0;
25068      props.majorLabelHeight = showMajorLabels ? props.majorCharHeight : 0;
25069
25070      props.minorLineWidth = this.body.dom.backgroundHorizontal.offsetWidth - this.lineOffset - this.width + 2 * this.options.minorLinesOffset;
25071      props.minorLineHeight = 1;
25072      props.majorLineWidth = this.body.dom.backgroundHorizontal.offsetWidth - this.lineOffset - this.width + 2 * this.options.majorLinesOffset;
25073      props.majorLineHeight = 1;
25074
25075      //  take frame offline while updating (is almost twice as fast)
25076      if (orientation === 'left') {
25077        frame.style.top = '0';
25078        frame.style.left = '0';
25079        frame.style.bottom = '';
25080        frame.style.width = this.width + 'px';
25081        frame.style.height = this.height + 'px';
25082        this.props.width = this.body.domProps.left.width;
25083        this.props.height = this.body.domProps.left.height;
25084      } else {
25085        // right
25086        frame.style.top = '';
25087        frame.style.bottom = '0';
25088        frame.style.left = '0';
25089        frame.style.width = this.width + 'px';
25090        frame.style.height = this.height + 'px';
25091        this.props.width = this.body.domProps.right.width;
25092        this.props.height = this.body.domProps.right.height;
25093      }
25094
25095      resized = this._redrawLabels();
25096      resized = this._isResized() || resized;
25097
25098      if (this.options.icons === true) {
25099        this._redrawGroupIcons();
25100      } else {
25101        this._cleanupIcons();
25102      }
25103
25104      this._redrawTitle(orientation);
25105    }
25106    return resized;
25107  };
25108
25109  /**
25110   * Repaint major and minor text labels and vertical grid lines
25111   * @private
25112   */
25113  DataAxis.prototype._redrawLabels = function () {
25114    var resized = false;
25115    DOMutil.prepareElements(this.DOMelements.lines);
25116    DOMutil.prepareElements(this.DOMelements.labels);
25117    var orientation = this.options['orientation'];
25118
25119    // get the range for the slaved axis
25120    var step;
25121    if (this.master === false) {
25122      var stepSize, rangeStart, rangeEnd, minimumStep;
25123      if (this.zeroCrossing !== -1 && this.options.alignZeros === true) {
25124        if (this.range.end > 0) {
25125          stepSize = this.range.end / this.zeroCrossing; // size of one step
25126          rangeStart = this.range.end - this.amountOfSteps * stepSize;
25127          rangeEnd = this.range.end;
25128        } else {
25129          // all of the range (including start) has to be done before the zero crossing.
25130          stepSize = -1 * this.range.start / (this.amountOfSteps - this.zeroCrossing); // absolute size of a step
25131          rangeStart = this.range.start;
25132          rangeEnd = this.range.start + stepSize * this.amountOfSteps;
25133        }
25134      } else {
25135        rangeStart = this.range.start;
25136        rangeEnd = this.range.end;
25137      }
25138      minimumStep = this.stepPixels;
25139    } else {
25140      // calculate range and step (step such that we have space for 7 characters per label)
25141      minimumStep = this.props.majorCharHeight;
25142      rangeStart = this.range.start;
25143      rangeEnd = this.range.end;
25144    }
25145
25146    this.step = step = new DataStep(rangeStart, rangeEnd, minimumStep, this.dom.frame.offsetHeight, this.options[this.options.orientation].range, this.options[this.options.orientation].format, this.master === false && this.options.alignZeros // does the step have to align zeros? only if not master and the options is on
25147    );
25148
25149    // the slave axis needs to use the same horizontal lines as the master axis.
25150    if (this.master === true) {
25151      this.stepPixels = this.dom.frame.offsetHeight / step.marginRange * step.step;
25152      this.amountOfSteps = Math.ceil(this.dom.frame.offsetHeight / this.stepPixels);
25153    } else {
25154      // align with zero
25155      if (this.options.alignZeros === true && this.zeroCrossing !== -1) {
25156        // distance is the amount of steps away from the zero crossing we are.
25157        var distance = (step.current - this.zeroCrossing * step.step) / step.step;
25158        this.step.shift(distance);
25159      }
25160    }
25161
25162    // value at the bottom of the SVG
25163    this.valueAtBottom = step.marginEnd;
25164
25165    this.maxLabelSize = 0;
25166    var y = 0; // init value
25167    var stepIndex = 0; // init value
25168    var isMajor = false; // init value
25169    while (stepIndex < this.amountOfSteps) {
25170      y = Math.round(stepIndex * this.stepPixels);
25171      isMajor = step.isMajor();
25172
25173      if (stepIndex > 0 && stepIndex !== this.amountOfSteps) {
25174        if (this.options['showMinorLabels'] && isMajor === false || this.master === false && this.options['showMinorLabels'] === true) {
25175          this._redrawLabel(y - 2, step.getCurrent(), orientation, 'vis-y-axis vis-minor', this.props.minorCharHeight);
25176        }
25177
25178        if (isMajor && this.options['showMajorLabels'] && this.master === true || this.options['showMinorLabels'] === false && this.master === false && isMajor === true) {
25179          if (y >= 0) {
25180            this._redrawLabel(y - 2, step.getCurrent(), orientation, 'vis-y-axis vis-major', this.props.majorCharHeight);
25181          }
25182          this._redrawLine(y, orientation, 'vis-grid vis-horizontal vis-major', this.options.majorLinesOffset, this.props.majorLineWidth);
25183        } else {
25184          this._redrawLine(y, orientation, 'vis-grid vis-horizontal vis-minor', this.options.minorLinesOffset, this.props.minorLineWidth);
25185        }
25186      }
25187
25188      // get zero crossing
25189      if (this.master === true && step.current === 0) {
25190        this.zeroCrossing = stepIndex;
25191      }
25192
25193      step.next();
25194      stepIndex += 1;
25195    }
25196
25197    // get zero crossing if it's the last step
25198    if (this.master === true && step.current === 0) {
25199      this.zeroCrossing = stepIndex;
25200    }
25201
25202    this.conversionFactor = this.stepPixels / step.step;
25203
25204    // Note that title is rotated, so we're using the height, not width!
25205    var titleWidth = 0;
25206    if (this.options[orientation].title !== undefined && this.options[orientation].title.text !== undefined) {
25207      titleWidth = this.props.titleCharHeight;
25208    }
25209    var offset = this.options.icons === true ? Math.max(this.options.iconWidth, titleWidth) + this.options.labelOffsetX + 15 : titleWidth + this.options.labelOffsetX + 15;
25210
25211    // this will resize the yAxis to accommodate the labels.
25212    if (this.maxLabelSize > this.width - offset && this.options.visible === true) {
25213      this.width = this.maxLabelSize + offset;
25214      this.options.width = this.width + 'px';
25215      DOMutil.cleanupElements(this.DOMelements.lines);
25216      DOMutil.cleanupElements(this.DOMelements.labels);
25217      this.redraw();
25218      resized = true;
25219    }
25220    // this will resize the yAxis if it is too big for the labels.
25221    else if (this.maxLabelSize < this.width - offset && this.options.visible === true && this.width > this.minWidth) {
25222      this.width = Math.max(this.minWidth, this.maxLabelSize + offset);
25223      this.options.width = this.width + 'px';
25224      DOMutil.cleanupElements(this.DOMelements.lines);
25225      DOMutil.cleanupElements(this.DOMelements.labels);
25226      this.redraw();
25227      resized = true;
25228    } else {
25229      DOMutil.cleanupElements(this.DOMelements.lines);
25230      DOMutil.cleanupElements(this.DOMelements.labels);
25231      resized = false;
25232    }
25233
25234    return resized;
25235  };
25236
25237  DataAxis.prototype.convertValue = function (value) {
25238    var invertedValue = this.valueAtBottom - value;
25239    var convertedValue = invertedValue * this.conversionFactor;
25240    return convertedValue;
25241  };
25242
25243  DataAxis.prototype.screenToValue = function (x) {
25244    return this.valueAtBottom - x / this.conversionFactor;
25245  };
25246
25247  /**
25248   * Create a label for the axis at position x
25249   * @private
25250   * @param y
25251   * @param text
25252   * @param orientation
25253   * @param className
25254   * @param characterHeight
25255   */
25256  DataAxis.prototype._redrawLabel = function (y, text, orientation, className, characterHeight) {
25257    // reuse redundant label
25258    var label = DOMutil.getDOMElement('div', this.DOMelements.labels, this.dom.frame); //this.dom.redundant.labels.shift();
25259    label.className = className;
25260    label.innerHTML = text;
25261    if (orientation === 'left') {
25262      label.style.left = '-' + this.options.labelOffsetX + 'px';
25263      label.style.textAlign = 'right';
25264    } else {
25265      label.style.right = '-' + this.options.labelOffsetX + 'px';
25266      label.style.textAlign = 'left';
25267    }
25268
25269    label.style.top = y - 0.5 * characterHeight + this.options.labelOffsetY + 'px';
25270
25271    text += '';
25272
25273    var largestWidth = Math.max(this.props.majorCharWidth, this.props.minorCharWidth);
25274    if (this.maxLabelSize < text.length * largestWidth) {
25275      this.maxLabelSize = text.length * largestWidth;
25276    }
25277  };
25278
25279  /**
25280   * Create a minor line for the axis at position y
25281   * @param y
25282   * @param orientation
25283   * @param className
25284   * @param offset
25285   * @param width
25286   */
25287  DataAxis.prototype._redrawLine = function (y, orientation, className, offset, width) {
25288    if (this.master === true) {
25289      var line = DOMutil.getDOMElement('div', this.DOMelements.lines, this.dom.lineContainer); //this.dom.redundant.lines.shift();
25290      line.className = className;
25291      line.innerHTML = '';
25292
25293      if (orientation === 'left') {
25294        line.style.left = this.width - offset + 'px';
25295      } else {
25296        line.style.right = this.width - offset + 'px';
25297      }
25298
25299      line.style.width = width + 'px';
25300      line.style.top = y + 'px';
25301    }
25302  };
25303
25304  /**
25305   * Create a title for the axis
25306   * @private
25307   * @param orientation
25308   */
25309  DataAxis.prototype._redrawTitle = function (orientation) {
25310    DOMutil.prepareElements(this.DOMelements.title);
25311
25312    // Check if the title is defined for this axes
25313    if (this.options[orientation].title !== undefined && this.options[orientation].title.text !== undefined) {
25314      var title = DOMutil.getDOMElement('div', this.DOMelements.title, this.dom.frame);
25315      title.className = 'vis-y-axis vis-title vis-' + orientation;
25316      title.innerHTML = this.options[orientation].title.text;
25317
25318      // Add style - if provided
25319      if (this.options[orientation].title.style !== undefined) {
25320        util.addCssText(title, this.options[orientation].title.style);
25321      }
25322
25323      if (orientation === 'left') {
25324        title.style.left = this.props.titleCharHeight + 'px';
25325      } else {
25326        title.style.right = this.props.titleCharHeight + 'px';
25327      }
25328
25329      title.style.width = this.height + 'px';
25330    }
25331
25332    // we need to clean up in case we did not use all elements.
25333    DOMutil.cleanupElements(this.DOMelements.title);
25334  };
25335
25336  /**
25337   * Determine the size of text on the axis (both major and minor axis).
25338   * The size is calculated only once and then cached in this.props.
25339   * @private
25340   */
25341  DataAxis.prototype._calculateCharSize = function () {
25342    // determine the char width and height on the minor axis
25343    if (!('minorCharHeight' in this.props)) {
25344      var textMinor = document.createTextNode('0');
25345      var measureCharMinor = document.createElement('div');
25346      measureCharMinor.className = 'vis-y-axis vis-minor vis-measure';
25347      measureCharMinor.appendChild(textMinor);
25348      this.dom.frame.appendChild(measureCharMinor);
25349
25350      this.props.minorCharHeight = measureCharMinor.clientHeight;
25351      this.props.minorCharWidth = measureCharMinor.clientWidth;
25352
25353      this.dom.frame.removeChild(measureCharMinor);
25354    }
25355
25356    if (!('majorCharHeight' in this.props)) {
25357      var textMajor = document.createTextNode('0');
25358      var measureCharMajor = document.createElement('div');
25359      measureCharMajor.className = 'vis-y-axis vis-major vis-measure';
25360      measureCharMajor.appendChild(textMajor);
25361      this.dom.frame.appendChild(measureCharMajor);
25362
25363      this.props.majorCharHeight = measureCharMajor.clientHeight;
25364      this.props.majorCharWidth = measureCharMajor.clientWidth;
25365
25366      this.dom.frame.removeChild(measureCharMajor);
25367    }
25368
25369    if (!('titleCharHeight' in this.props)) {
25370      var textTitle = document.createTextNode('0');
25371      var measureCharTitle = document.createElement('div');
25372      measureCharTitle.className = 'vis-y-axis vis-title vis-measure';
25373      measureCharTitle.appendChild(textTitle);
25374      this.dom.frame.appendChild(measureCharTitle);
25375
25376      this.props.titleCharHeight = measureCharTitle.clientHeight;
25377      this.props.titleCharWidth = measureCharTitle.clientWidth;
25378
25379      this.dom.frame.removeChild(measureCharTitle);
25380    }
25381  };
25382
25383  module.exports = DataAxis;
25384
25385/***/ },
25386/* 52 */
25387/***/ function(module, exports) {
25388
25389  /**
25390   * @constructor  DataStep
25391   * The class DataStep is an iterator for data for the lineGraph. You provide a start data point and an
25392   * end data point. The class itself determines the best scale (step size) based on the
25393   * provided start Date, end Date, and minimumStep.
25394   *
25395   * If minimumStep is provided, the step size is chosen as close as possible
25396   * to the minimumStep but larger than minimumStep. If minimumStep is not
25397   * provided, the scale is set to 1 DAY.
25398   * The minimumStep should correspond with the onscreen size of about 6 characters
25399   *
25400   * Alternatively, you can set a scale by hand.
25401   * After creation, you can initialize the class by executing first(). Then you
25402   * can iterate from the start date to the end date via next(). You can check if
25403   * the end date is reached with the function hasNext(). After each step, you can
25404   * retrieve the current date via getCurrent().
25405   * The DataStep has scales ranging from milliseconds, seconds, minutes, hours,
25406   * days, to years.
25407   *
25408   * Version: 1.2
25409   *
25410   * @param {Date} [start]         The start date, for example new Date(2010, 9, 21)
25411   *                               or new Date(2010, 9, 21, 23, 45, 00)
25412   * @param {Date} [end]           The end date
25413   * @param {Number} [minimumStep] Optional. Minimum step size in milliseconds
25414   */
25415  'use strict';
25416
25417  function DataStep(start, end, minimumStep, containerHeight, customRange, formattingFunction, alignZeros) {
25418    // variables
25419    this.current = 0;
25420
25421    this.autoScale = true;
25422    this.stepIndex = 0;
25423    this.step = 1;
25424    this.scale = 1;
25425    this.formattingFunction = formattingFunction;
25426
25427    this.marginStart;
25428    this.marginEnd;
25429    this.deadSpace = 0;
25430
25431    this.majorSteps = [1, 2, 5, 10];
25432    this.minorSteps = [0.25, 0.5, 1, 2];
25433
25434    this.alignZeros = alignZeros;
25435
25436    this.setRange(start, end, minimumStep, containerHeight, customRange);
25437  }
25438
25439  /**
25440   * Set a new range
25441   * If minimumStep is provided, the step size is chosen as close as possible
25442   * to the minimumStep but larger than minimumStep. If minimumStep is not
25443   * provided, the scale is set to 1 DAY.
25444   * The minimumStep should correspond with the onscreen size of about 6 characters
25445   * @param {Number} [start]      The start date and time.
25446   * @param {Number} [end]        The end date and time.
25447   * @param {Number} [minimumStep] Optional. Minimum step size in milliseconds
25448   */
25449  DataStep.prototype.setRange = function (start, end, minimumStep, containerHeight, customRange) {
25450    this._start = customRange.min === undefined ? start : customRange.min;
25451    this._end = customRange.max === undefined ? end : customRange.max;
25452    if (this._start === this._end) {
25453      this._start = customRange.min === undefined ? this._start - 0.75 : this._start;
25454      this._end = customRange.max === undefined ? this._end + 1 : this._end;;
25455    }
25456
25457    if (this.autoScale === true) {
25458      this.setMinimumStep(minimumStep, containerHeight);
25459    }
25460
25461    this.setFirst(customRange);
25462  };
25463
25464  /**
25465   * Automatically determine the scale that bests fits the provided minimum step
25466   * @param {Number} [minimumStep]  The minimum step size in pixels
25467   */
25468  DataStep.prototype.setMinimumStep = function (minimumStep, containerHeight) {
25469    // round to floor
25470    var range = this._end - this._start;
25471    var safeRange = range * 1.2;
25472    var minimumStepValue = minimumStep * (safeRange / containerHeight);
25473    var orderOfMagnitude = Math.round(Math.log(safeRange) / Math.LN10);
25474
25475    var minorStepIdx = -1;
25476    var magnitudefactor = Math.pow(10, orderOfMagnitude);
25477
25478    var start = 0;
25479    if (orderOfMagnitude < 0) {
25480      start = orderOfMagnitude;
25481    }
25482
25483    var solutionFound = false;
25484    for (var i = start; Math.abs(i) <= Math.abs(orderOfMagnitude); i++) {
25485      magnitudefactor = Math.pow(10, i);
25486      for (var j = 0; j < this.minorSteps.length; j++) {
25487        var stepSize = magnitudefactor * this.minorSteps[j];
25488        if (stepSize >= minimumStepValue) {
25489          solutionFound = true;
25490          minorStepIdx = j;
25491          break;
25492        }
25493      }
25494      if (solutionFound === true) {
25495        break;
25496      }
25497    }
25498    this.stepIndex = minorStepIdx;
25499    this.scale = magnitudefactor;
25500    this.step = magnitudefactor * this.minorSteps[minorStepIdx];
25501  };
25502
25503  /**
25504   * Round the current date to the first minor date value
25505   * This must be executed once when the current date is set to start Date
25506   */
25507  DataStep.prototype.setFirst = function (customRange) {
25508    if (customRange === undefined) {
25509      customRange = {};
25510    }
25511
25512    var niceStart = customRange.min === undefined ? this._start - this.scale * 2 * this.minorSteps[this.stepIndex] : customRange.min;
25513    var niceEnd = customRange.max === undefined ? this._end + this.scale * this.minorSteps[this.stepIndex] : customRange.max;
25514
25515    this.marginEnd = customRange.max === undefined ? this.roundToMinor(niceEnd) : customRange.max;
25516    this.marginStart = customRange.min === undefined ? this.roundToMinor(niceStart) : customRange.min;
25517
25518    // if we need to align the zero's we need to make sure that there is a zero to use.
25519    if (this.alignZeros === true && (this.marginEnd - this.marginStart) % this.step != 0) {
25520      this.marginEnd += this.marginEnd % this.step;
25521    }
25522
25523    this.deadSpace = this.roundToMinor(niceEnd) - niceEnd + this.roundToMinor(niceStart) - niceStart;
25524    this.marginRange = this.marginEnd - this.marginStart;
25525
25526    this.current = this.marginEnd;
25527  };
25528
25529  DataStep.prototype.roundToMinor = function (value) {
25530    var rounded = value - value % (this.scale * this.minorSteps[this.stepIndex]);
25531    if (value % (this.scale * this.minorSteps[this.stepIndex]) > 0.5 * (this.scale * this.minorSteps[this.stepIndex])) {
25532      return rounded + this.scale * this.minorSteps[this.stepIndex];
25533    } else {
25534      return rounded;
25535    }
25536  };
25537
25538  /**
25539   * Check if the there is a next step
25540   * @return {boolean}  true if the current date has not passed the end date
25541   */
25542  DataStep.prototype.hasNext = function () {
25543    return this.current >= this.marginStart;
25544  };
25545
25546  /**
25547   * Do the next step
25548   */
25549  DataStep.prototype.next = function () {
25550    var prev = this.current;
25551    this.current -= this.step;
25552
25553    // safety mechanism: if current time is still unchanged, move to the end
25554    if (this.current === prev) {
25555      this.current = this._end;
25556    }
25557  };
25558
25559  /**
25560   * Do the next step
25561   */
25562  DataStep.prototype.previous = function () {
25563    this.current += this.step;
25564    this.marginEnd += this.step;
25565    this.marginRange = this.marginEnd - this.marginStart;
25566  };
25567
25568  /**
25569   * Get the current datetime
25570   * @return {String}  current The current date
25571   */
25572  DataStep.prototype.getCurrent = function () {
vendor: 7,103 bytes, lines 25573-25770
25573    // prevent round-off errors when close to zero
25574    var current = Math.abs(this.current) < this.step / 2 ? 0 : this.current;
25575    var returnValue = current.toPrecision(5);
25576    if (typeof this.formattingFunction === 'function') {
25577      returnValue = this.formattingFunction(current);
25578    }
25579
25580    if (typeof returnValue === 'number') {
25581      return '' + returnValue;
25582    } else if (typeof returnValue === 'string') {
25583      return returnValue;
25584    } else {
25585      return current.toPrecision(5);
25586    }
25587  };
25588
25589  /**
25590   * Check if the current value is a major value (for example when the step
25591   * is DAY, a major value is each first day of the MONTH)
25592   * @return {boolean} true if current date is major, else false.
25593   */
25594  DataStep.prototype.isMajor = function () {
25595    return this.current % (this.scale * this.majorSteps[this.stepIndex]) === 0;
25596  };
25597
25598  DataStep.prototype.shift = function (steps) {
25599    if (steps < 0) {
25600      for (var i = 0; i < -steps; i++) {
25601        this.previous();
25602      }
25603    } else if (steps > 0) {
25604      for (var i = 0; i < steps; i++) {
25605        this.next();
25606      }
25607    }
25608  };
25609
25610  module.exports = DataStep;
25611
25612/***/ },
25613/* 53 */
25614/***/ function(module, exports, __webpack_require__) {
25615
25616  'use strict';
25617
25618  var util = __webpack_require__(1);
25619  var DOMutil = __webpack_require__(7);
25620  var Line = __webpack_require__(54);
25621  var Bar = __webpack_require__(56);
25622  var Points = __webpack_require__(55);
25623
25624  /**
25625   * /**
25626   * @param {object} group            | the object of the group from the dataset
25627   * @param {string} groupId          | ID of the group
25628   * @param {object} options          | the default options
25629   * @param {array} groupsUsingDefaultStyles  | this array has one entree.
25630   *                                            It is passed as an array so it is passed by reference.
25631   *                                            It enumerates through the default styles
25632   * @constructor
25633   */
25634  function GraphGroup(group, groupId, options, groupsUsingDefaultStyles) {
25635    this.id = groupId;
25636    var fields = ['sampling', 'style', 'sort', 'yAxisOrientation', 'barChart', 'drawPoints', 'shaded', 'interpolation'];
25637    this.options = util.selectiveBridgeObject(fields, options);
25638    this.usingDefaultStyle = group.className === undefined;
25639    this.groupsUsingDefaultStyles = groupsUsingDefaultStyles;
25640    this.zeroPosition = 0;
25641    this.update(group);
25642    if (this.usingDefaultStyle == true) {
25643      this.groupsUsingDefaultStyles[0] += 1;
25644    }
25645    this.itemsData = [];
25646    this.visible = group.visible === undefined ? true : group.visible;
25647  }
25648
25649  /**
25650   * this loads a reference to all items in this group into this group.
25651   * @param {array} items
25652   */
25653  GraphGroup.prototype.setItems = function (items) {
25654    if (items != null) {
25655      this.itemsData = items;
25656      if (this.options.sort == true) {
25657        this.itemsData.sort(function (a, b) {
25658          return a.x - b.x;
25659        });
25660      }
25661      // typecast all items to numbers. Takes around 10ms for 500.000 items
25662      for (var i = 0; i < this.itemsData.length; i++) {
25663        this.itemsData[i].y = Number(this.itemsData[i].y);
25664      }
25665    } else {
25666      this.itemsData = [];
25667    }
25668  };
25669
25670  /**
25671   * this is used for plotting barcharts, this way, we only have to calculate it once.
25672   * @param pos
25673   */
25674  GraphGroup.prototype.setZeroPosition = function (pos) {
25675    this.zeroPosition = pos;
25676  };
25677
25678  /**
25679   * set the options of the graph group over the default options.
25680   * @param options
25681   */
25682  GraphGroup.prototype.setOptions = function (options) {
25683    if (options !== undefined) {
25684      var fields = ['sampling', 'style', 'sort', 'yAxisOrientation', 'barChart', 'excludeFromLegend'];
25685      util.selectiveDeepExtend(fields, this.options, options);
25686
25687      // if the group's drawPoints is a function delegate the callback to the onRender property
25688      if (typeof options.drawPoints == 'function') {
25689        options.drawPoints = {
25690          onRender: options.drawPoints
25691        };
25692      }
25693
25694      util.mergeOptions(this.options, options, 'interpolation');
25695      util.mergeOptions(this.options, options, 'drawPoints');
25696      util.mergeOptions(this.options, options, 'shaded');
25697
25698      if (options.interpolation) {
25699        if (typeof options.interpolation == 'object') {
25700          if (options.interpolation.parametrization) {
25701            if (options.interpolation.parametrization == 'uniform') {
25702              this.options.interpolation.alpha = 0;
25703            } else if (options.interpolation.parametrization == 'chordal') {
25704              this.options.interpolation.alpha = 1.0;
25705            } else {
25706              this.options.interpolation.parametrization = 'centripetal';
25707              this.options.interpolation.alpha = 0.5;
25708            }
25709          }
25710        }
25711      }
25712    }
25713
25714    if (this.options.style == 'line') {
25715      this.type = new Line(this.id, this.options);
25716    } else if (this.options.style == 'bar') {
25717      this.type = new Bar(this.id, this.options);
25718    } else if (this.options.style == 'points') {
25719      this.type = new Points(this.id, this.options);
25720    }
25721  };
25722
25723  /**
25724   * this updates the current group class with the latest group dataset entree, used in _updateGroup in linegraph
25725   * @param group
25726   */
25727  GraphGroup.prototype.update = function (group) {
25728    this.group = group;
25729    this.content = group.content || 'graph';
25730    this.className = group.className || this.className || 'vis-graph-group' + this.groupsUsingDefaultStyles[0] % 10;
25731    this.visible = group.visible === undefined ? true : group.visible;
25732    this.style = group.style;
25733    this.setOptions(group.options);
25734  };
25735
25736  /**
25737   * draw the icon for the legend.
25738   *
25739   * @param x
25740   * @param y
25741   * @param JSONcontainer
25742   * @param SVGcontainer
25743   * @param iconWidth
25744   * @param iconHeight
25745   */
25746  GraphGroup.prototype.drawIcon = function (x, y, JSONcontainer, SVGcontainer, iconWidth, iconHeight) {
25747    var fillHeight = iconHeight * 0.5;
25748    var path, fillPath;
25749
25750    var outline = DOMutil.getSVGElement('rect', JSONcontainer, SVGcontainer);
25751    outline.setAttributeNS(null, 'x', x);
25752    outline.setAttributeNS(null, 'y', y - fillHeight);
25753    outline.setAttributeNS(null, 'width', iconWidth);
25754    outline.setAttributeNS(null, 'height', 2 * fillHeight);
25755    outline.setAttributeNS(null, 'class', 'vis-outline');
25756
25757    if (this.options.style == 'line') {
25758      path = DOMutil.getSVGElement('path', JSONcontainer, SVGcontainer);
25759      path.setAttributeNS(null, 'class', this.className);
25760      if (this.style !== undefined) {
25761        path.setAttributeNS(null, 'style', this.style);
25762      }
25763
25764      path.setAttributeNS(null, 'd', 'M' + x + ',' + y + ' L' + (x + iconWidth) + ',' + y + '');
25765      if (this.options.shaded.enabled == true) {
25766        fillPath = DOMutil.getSVGElement('path', JSONcontainer, SVGcontainer);
25767        if (this.options.shaded.orientation == 'top') {
25768          fillPath.setAttributeNS(null, 'd', 'M' + x + ', ' + (y - fillHeight) + 'L' + x + ',' + y + ' L' + (x + iconWidth) + ',' + y + ' L' + (x + iconWidth) + ',' + (y - fillHeight));
25769        } else {
25770          fillPath.setAttributeNS(null, 'd', 'M' + x + ',' + y + ' ' + '
25770L' + x + ',' + (y + fillHeight) + ' ' + 'L' + (x + iconWidth) + ',' + (y + fillHeight) + 'L' + (x + iconWidth) + ',' + y);
25771        }
25772        fillPath.setAttributeNS(null, 'class', this.className + ' vis-icon-fill');
25773      }
25774
25775      if (this.options.drawPoints.enabled == true) {
25776        var groupTemplate = {
25777          style: this.options.drawPoints.style,
25778          styles: this.options.drawPoints.styles,
25779          size: this.options.drawPoints.size,
25780          className: this.className
25781        };
25782        DOMutil.drawPoint(x + 0.5 * iconWidth, y, groupTemplate, JSONcontainer, SVGcontainer);
25783      }
25784    } else {
25785      var barWidth = Math.round(0.3 * iconWidth);
25786      var bar1Height = Math.round(0.4 * iconHeight);
25787      var bar2Height = Math.round(0.75 * iconHeight);
25788
25789      var offset = Math.round((iconWidth - 2 * barWidth) / 3);
25790
25791      DOMutil.drawBar(x + 0.5 * barWidth + offset, y + fillHeight - bar1Height - 1, barWidth, bar1Height, this.className + ' vis-bar', JSONcontainer, SVGcontainer, this.style);
25792      DOMutil.drawBar(x + 1.5 * barWidth + offset + 2, y + fillHeight - bar2Height - 1, barWidth, bar2Height, this.className + ' vis-bar', JSONcontainer, SVGcontainer, this.style);
25793    }
25794  };
25795
25796  /**
25797   * return the legend entree for this group.
25798   *
25799   * @param iconWidth
25800   * @param iconHeight
25801   * @returns {{icon: HTMLElement, label: (group.content|*|string), orientation: (.options.yAxisOr
25801ientation|*)}}
25802   */
25803  GraphGroup.prototype.getLegend = function (iconWidth, iconHeight) {
25804    var svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
25805    this.drawIcon(0, 0.5 * iconHeight, [], svg, iconWidth, iconHeight);
25806    return { icon: svg, label: this.content, orientation: this.options.yAxisOrientation };
25807  };
25808
25809  GraphGroup.prototype.getYRange = function (groupData) {
25810    return this.type.getYRange(groupData);
25811  };
25812
25813  GraphGroup.prototype.getData = function (groupData) {
25814    return this.type.getData(groupData);
25815  };
25816
25817  GraphGroup.prototype.draw = function (dataset, group, framework) {
25818    this.type.draw(dataset, group, framework);
25819  };
25820
25821  module.exports = GraphGroup;
25822
25823/***/ },
25824/* 54 */
25825/***/ function(module, exports, __webpack_require__) {
25826
25827  'use strict';
25828
25829  var DOMutil = __webpack_require__(7);
25830  var Points = __webpack_require__(55);
25831
25832  function Line(groupId, options) {
25833    this.groupId = groupId;
25834    this.options = options;
25835  }
25836
25837  Line.prototype.getData = function (groupData) {
25838    var combinedData = [];
25839    for (var j = 0; j < groupData.length; j++) {
25840      combinedData.push({
25841        x: groupData[j].x,
25842        y: groupData[j].y,
25843        groupId: this.groupId
25844      });
25845    }
25846    return combinedData;
25847  };
25848
25849  Line.prototype.getYRange = function (groupData) {
25850    var yMin = groupData[0].y;
25851    var yMax = groupData[0].y;
25852    for (var j = 0; j < groupData.length; j++) {
25853      yMin = yMin > groupData[j].y ? groupData[j].y : yMin;
25854      yMax = yMax < groupData[j].y ? groupData[j].y : yMax;
25855    }
25856    return { min: yMin, max: yMax, yAxisOrientation: this.options.yAxisOr
vendor: 9,780 bytes, lines 25856-26137
25856ientation };
25857  };
25858
25859  Line.getStackedYRange = function (combinedData, groupRanges, groupIds, groupLabel, orientation) {
25860    if (combinedData.length > 0) {
25861      // sort by time and by group
25862      combinedData.sort(function (a, b) {
25863        if (a.x === b.x) {
25864          return a.groupId < b.groupId ? -1 : 1;
25865        } else {
25866          return a.x - b.x;
25867        }
25868      });
25869      var intersections = {};
25870
25871      Line._getDataIntersections(intersections, combinedData);
25872      groupRanges[groupLabel] = Line._getStackedYRange(intersections, combinedData);
25873      groupRanges[groupLabel].yAxisOrientation = orientation;
25874      groupIds.push(groupLabel);
25875    }
25876  };
25877
25878  Line._getStackedYRange = function (intersections, combinedData) {
25879    var key;
25880    var yMin = combinedData[0].y;
25881    var yMax = combinedData[0].y;
25882    for (var i = 0; i < combinedData.length; i++) {
25883      key = combinedData[i].x;
25884      if (intersections[key] === undefined) {
25885        yMin = yMin > combinedData[i].y ? combinedData[i].y : yMin;
25886        yMax = yMax < combinedData[i].y ? combinedData[i].y : yMax;
25887      } else {
25888        if (combinedData[i].y < 0) {
25889          intersections[key].accumulatedNegative += combinedData[i].y;
25890        } else {
25891          intersections[key].accumulatedPositive += combinedData[i].y;
25892        }
25893      }
25894    }
25895    for (var xpos in intersections) {
25896      if (intersections.hasOwnProperty(xpos)) {
25897        yMin = yMin > intersections[xpos].accumulatedNegative ? intersections[xpos].accumulatedNegative : yMin;
25898        yMin = yMin > intersections[xpos].accumulatedPositive ? intersections[xpos].accumulatedPositive : yMin;
25899        yMax = yMax < intersections[xpos].accumulatedNegative ? intersections[xpos].accumulatedNegative : yMax;
25900        yMax = yMax < intersections[xpos].accumulatedPositive ? intersections[xpos].accumulatedPositive : yMax;
25901      }
25902    }
25903
25904    return { min: yMin, max: yMax };
25905  };
25906
25907  /**
25908   * Fill the intersections object with counters of how many datapoints share the same x coordinates
25909   * @param intersections
25910   * @param combinedData
25911   * @private
25912   */
25913  Line._getDataIntersections = function (intersections, combinedData) {
25914    // get intersections
25915    var coreDistance;
25916    for (var i = 0; i < combinedData.length; i++) {
25917      if (i + 1 < combinedData.length) {
25918        coreDistance = Math.abs(combinedData[i + 1].x - combinedData[i].x);
25919      }
25920      if (i > 0) {
25921        coreDistance = Math.min(coreDistance, Math.abs(combinedData[i - 1].x - combinedData[i].x));
25922      }
25923      if (coreDistance === 0) {
25924        if (intersections[combinedData[i].x] === undefined) {
25925          intersections[combinedData[i].x] = { amount: 0, resolved: 0, accumulatedPositive: 0, accumulatedNegative: 0 };
25926        }
25927        intersections[combinedData[i].x].amount += 1;
25928      }
25929    }
25930  };
25931
25932  /**
25933   * draw a line graph
25934   *
25935   * @param dataset
25936   * @param group
25937   */
25938  Line.prototype.draw = function (dataset, group, framework) {
25939    if (dataset != null) {
25940      if (dataset.length > 0) {
25941        var path, d;
25942        var svgHeight = Number(framework.svg.style.height.replace('px', ''));
25943        path = DOMutil.getSVGElement('path', framework.svgElements, framework.svg);
25944        path.setAttributeNS(null, 'class', group.className);
25945        if (group.style !== undefined) {
25946          path.setAttributeNS(null, 'style', group.style);
25947        }
25948
25949        // construct path from dataset
25950        if (group.options.interpolation.enabled == true) {
25951          d = Line._catmullRom(dataset, group);
25952        } else {
25953          d = Line._linear(dataset);
25954        }
25955
25956        // append with points for fill and finalize the path
25957        if (group.options.shaded.enabled == true) {
25958          var fillPath = DOMutil.getSVGElement('path', framework.svgElements, framework.svg);
25959          var dFill;
25960          if (group.options.shaded.orientation == 'top') {
25961            dFill = 'M' + dataset[0].x + ',' + 0 + ' ' + d + 'L' + dataset[dataset.length - 1].x + ',' + 0;
25962          } else {
25963            dFill = 'M' + dataset[0].x + ',' + svgHeight + ' ' + d + 'L' + dataset[dataset.length - 1].x + ',' + svgHeight;
25964          }
25965          fillPath.setAttributeNS(null, 'class', group.className + ' vis-fill');
25966          if (group.options.shaded.style !== undefined) {
25967            fillPath.setAttributeNS(null, 'style', group.options.shaded.style);
25968          }
25969          fillPath.setAttributeNS(null, 'd', dFill);
25970        }
25971        // copy properties to path for drawing.
25972        path.setAttributeNS(null, 'd', 'M' + d);
25973
25974        // draw points
25975        if (group.options.drawPoints.enabled == true) {
25976          Points.draw(dataset, group, framework);
25977        }
25978      }
25979    }
25980  };
25981
25982  /**
25983   * This uses an uniform parametrization of the interpolation algorithm:
25984   * 'On the Parameterization of Catmull-Rom Curves' by Cem Yuksel et al.
25985   * @param data
25986   * @returns {string}
25987   * @private
25988   */
25989  Line._catmullRomUniform = function (data) {
25990    // catmull rom
25991    var p0, p1, p2, p3, bp1, bp2;
25992    var d = Math.round(data[0].x) + ',' + Math.round(data[0].y) + ' ';
25993    var normalization = 1 / 6;
25994    var length = data.length;
25995    for (var i = 0; i < length - 1; i++) {
25996
25997      p0 = i == 0 ? data[0] : data[i - 1];
25998      p1 = data[i];
25999      p2 = data[i + 1];
26000      p3 = i + 2 < length ? data[i + 2] : p2;
26001
26002      // Catmull-Rom to Cubic Bezier conversion matrix
26003      //    0       1       0       0
26004      //  -1/6      1      1/6      0
26005      //    0      1/6      1     -1/6
26006      //    0       0       1       0
26007
26008      //    bp0 = { x: p1.x,                               y: p1.y };
26009      bp1 = { x: (-p0.x + 6 * p1.x + p2.x) * normalization, y: (-p0.y + 6 * p1.y + p2.y) * normalization };
26010      bp2 = { x: (p1.x + 6 * p2.x - p3.x) * normalization, y: (p1.y + 6 * p2.y - p3.y) * normalization };
26011      //    bp0 = { x: p2.x,                               y: p2.y };
26012
26013      d += 'C' + bp1.x + ',' + bp1.y + ' ' + bp2.x + ',' + bp2.y + ' ' + p2.x + ',' + p2.y + ' ';
26014    }
26015
26016    return d;
26017  };
26018
26019  /**
26020   * This uses either the chordal or centripetal parameterization of the catmull-rom algorithm.
26021   * By default, the centripetal parameterization is used because this gives the nicest results.
26022   * These parameterizations are relatively heavy because the distance between 4 points have to be calculated.
26023   *
26024   * One optimization can be used to reuse distances since this is a sliding window approach.
26025   * @param data
26026   * @param group
26027   * @returns {string}
26028   * @private
26029   */
26030  Line._catmullRom = function (data, group) {
26031    var alpha = group.options.interpolation.alpha;
26032    if (alpha == 0 || alpha === undefined) {
26033      return this._catmullRomUniform(data);
26034    } else {
26035      var p0, p1, p2, p3, bp1, bp2, d1, d2, d3, A, B, N, M;
26036      var d3powA, d2powA, d3pow2A, d2pow2A, d1pow2A, d1powA;
26037      var d = Math.round(data[0].x) + ',' + Math.round(data[0].y) + ' ';
26038      var length = data.length;
26039      for (var i = 0; i < length - 1; i++) {
26040
26041        p0 = i == 0 ? data[0] : data[i - 1];
26042        p1 = data[i];
26043        p2 = data[i + 1];
26044        p3 = i + 2 < length ? data[i + 2] : p2;
26045
26046        d1 = Math.sqrt(Math.pow(p0.x - p1.x, 2) + Math.pow(p0.y - p1.y, 2));
26047        d2 = Math.sqrt(Math.pow(p1.x - p2.x, 2) + Math.pow(p1.y - p2.y, 2));
26048        d3 = Math.sqrt(Math.pow(p2.x - p3.x, 2) + Math.pow(p2.y - p3.y, 2));
26049
26050        // Catmull-Rom to Cubic Bezier conversion matrix
26051
26052        // A = 2d1^2a + 3d1^a * d2^a + d3^2a
26053        // B = 2d3^2a + 3d3^a * d2^a + d2^2a
26054
26055        // [   0             1            0          0          ]
26056        // [   -d2^2a /N     A/N          d1^2a /N   0          ]
26057        // [   0             d3^2a /M     B/M        -d2^2a /M  ]
26058        // [   0             0            1          0          ]
26059
26060        d3powA = Math.pow(d3, alpha);
26061        d3pow2A = Math.pow(d3, 2 * alpha);
26062        d2powA = Math.pow(d2, alpha);
26063        d2pow2A = Math.pow(d2, 2 * alpha);
26064        d1powA = Math.pow(d1, alpha);
26065        d1pow2A = Math.pow(d1, 2 * alpha);
26066
26067        A = 2 * d1pow2A + 3 * d1powA * d2powA + d2pow2A;
26068        B = 2 * d3pow2A + 3 * d3powA * d2powA + d2pow2A;
26069        N = 3 * d1powA * (d1powA + d2powA);
26070        if (N > 0) {
26071          N = 1 / N;
26072        }
26073        M = 3 * d3powA * (d3powA + d2powA);
26074        if (M > 0) {
26075          M = 1 / M;
26076        }
26077
26078        bp1 = { x: (-d2pow2A * p0.x + A * p1.x + d1pow2A * p2.x) * N,
26079          y: (-d2pow2A * p0.y + A * p1.y + d1pow2A * p2.y) * N };
26080
26081        bp2 = { x: (d3pow2A * p1.x + B * p2.x - d2pow2A * p3.x) * M,
26082          y: (d3pow2A * p1.y + B * p2.y - d2pow2A * p3.y) * M };
26083
26084        if (bp1.x == 0 && bp1.y == 0) {
26085          bp1 = p1;
26086        }
26087        if (bp2.x == 0 && bp2.y == 0) {
26088          bp2 = p2;
26089        }
26090        d += 'C' + bp1.x + ',' + bp1.y + ' ' + bp2.x + ',' + bp2.y + ' ' + p2.x + ',' + p2.y + ' ';
26091      }
26092
26093      return d;
26094    }
26095  };
26096
26097  /**
26098   * this generates the SVG path for a linear drawing between datapoints.
26099   * @param data
26100   * @returns {string}
26101   * @private
26102   */
26103  Line._linear = function (data) {
26104    // linear
26105    var d = '';
26106    for (var i = 0; i < data.length; i++) {
26107      if (i == 0) {
26108        d += data[i].x + ',' + data[i].y;
26109      } else {
26110        d += ' ' + data[i].x + ',' + data[i].y;
26111      }
26112    }
26113    return d;
26114  };
26115
26116  module.exports = Line;
26117
26118/***/ },
26119/* 55 */
26120/***/ function(module, exports, __webpack_require__) {
26121
26122  'use strict';
26123
26124  var DOMutil = __webpack_require__(7);
26125
26126  function Points(groupId, options) {
26127    this.groupId = groupId;
26128    this.options = options;
26129  }
26130
26131  Points.prototype.getYRange = function (groupData) {
26132    var yMin = groupData[0].y;
26133    var yMax = groupData[0].y;
26134    for (var j = 0; j < groupData.length; j++) {
26135      yMin = yMin > groupData[j].y ? groupData[j].y : yMin;
26136      yMax = yMax < groupData[j].y ? groupData[j].y : yMax;
26137    }
26138    return { min: yMin, max: yMax, yAxisOrientation: this.options.yAxisOrientation };
26139  };
26140
26141  Points.prototype.draw = function (dataset, group, framework, offset) {
26142    Points.draw(dataset, group, framework, offset);
26143  };
26144
26145  /**
26146   * draw the data points
26147   *
26148   * @param {Array} dataset
26149   * @param {Object} JSONcontainer
26150   * @param {Object} svg            | SVG DOM element
26151   * @param {GraphGroup} group
26152   * @param {Number} [offset]
26153   */
26154  Points.draw = function (dataset, group, framework, offset) {
26155    offset = offset || 0;
26156    var callback = getCallback();
26157
26158    for (var i = 0; i < dataset.length; i++) {
26159      if (!callback) {
26160        // draw the point the simple way.
26161        DOMutil.drawPoint(dataset[i].x + offset, dataset[i].y, getGroupTemplate(), framework.svgElements, framework.svg, dataset[i].label);
26162      } else {
26163        var callbackResult = callback(dataset[i], group, framework); // result might be true, false or an object
26164        if (callbackResult === true || typeof callbackResult === 'object') {
26165          DOMutil.drawPoint(dataset[i].x + offset, dataset[i].y, getGroupTemplate(callbackResult), framework.svgElements, framework.svg, dataset[i].label);
26166        }
26167      }
26168    }
26169
26170    function getGroupTemplate(callbackResult) {
26171      callbackResult = typeof callbackResult === 'undefined' ? {} : callbackResult;
26172      return {
26173        style: callbackResult.style || group.options.drawPoints.style,
26174        styles: callbackResult.styles || group.options.drawPoints.styles,
26175        size: callbackResult.size || group.options.drawPoints.size,
26176        className: callbackResult.className || group.className
26177      };
26178    }
26179
26180    function getCallback() {
26181      var callback = undefined;
26182      // check for the graph2d onRender
26183      if (framework.options.drawPoints.onRender && typeof framework.options.drawPoints.onRender == 'function') {
26184        callback = framework.options.drawPoints.onRender;
26185      }
26186
26187      // override it with the group onRender if defined
26188      if (group.group.options && group.group.options.drawPoints && group.group.options.drawPoints.onRender && typeof group.group.options.drawPoints.onRender == 'function') {
26189        callback = group.group.options.drawPoints.onRender;
26190      }
26191
26192      return callback;
26193    }
26194  };
26195
26196  module.exports = Points;
26197
26198/***/ },
26199/* 56 */
26200/***/ function(module, exports, __webpack_require__) {
26201
26202  'use strict';
26203
26204  var DOMutil = __webpack_require__(7);
26205  var Points = __webpack_require__(55);
26206
26207  function Bargraph(groupId, options) {
26208    this.groupId = groupId;
26209    this.options = options;
26210  }
26211
26212  Bargraph.prototype.getYRange = function (groupData) {
26213    var yMin = groupData[0].y;
26214    var yMax = groupData[0].y;
26215    for (var j = 0; j < groupData.length; j++) {
26216      yMin = yMin > groupData[j].y ? groupData[j].y : yMin;
26217      yMax = yMax < groupData[j].y ? groupData[j].y : yMax;
26218    }
26219    return { min: yMin, max: yMax, yAxisOrientation: this.options.yAxisOr
vendor: 1,725 bytes, lines 26219-26284
26219ientation };
26220  };
26221
26222  Bargraph.prototype.getData = function (groupData) {
26223    var combinedData = [];
26224    for (var j = 0; j < groupData.length; j++) {
26225      combinedData.push({
26226        x: groupData[j].x,
26227        y: groupData[j].y,
26228        groupId: this.groupId
26229      });
26230    }
26231    return combinedData;
26232  };
26233
26234  /**
26235   * draw a bar graph
26236   *
26237   * @param groupIds
26238   * @param processedGroupData
26239   */
26240  Bargraph.draw = function (groupIds, processedGroupData, framework) {
26241    var combinedData = [];
26242    var intersections = {};
26243    var coreDistance;
26244    var key, drawData;
26245    var group;
26246    var i, j;
26247    var barPoints = 0;
26248
26249    // combine all barchart data
26250    for (i = 0; i < groupIds.length; i++) {
26251      group = framework.groups[groupIds[i]];
26252      if (group.options.style === 'bar') {
26253        if (group.visible === true && (framework.options.groups.visibility[groupIds[i]] === undefined || framework.options.groups.visibility[groupIds[i]] === true)) {
26254          for (j = 0; j < processedGroupData[groupIds[i]].length; j++) {
26255            combinedData.push({
26256              x: processedGroupData[groupIds[i]][j].x,
26257              y: processedGroupData[groupIds[i]][j].y,
26258              groupId: groupIds[i],
26259              label: processedGroupData[groupIds[i]][j].label
26260            });
26261            barPoints += 1;
26262          }
26263        }
26264      }
26265    }
26266
26267    if (barPoints === 0) {
26268      return;
26269    }
26270
26271    // sort by time and by group
26272    combinedData.sort(function (a, b) {
26273      if (a.x === b.x) {
26274        return a.groupId < b.groupId ? -1 : 1;
26275      } else {
26276        return a.x - b.x;
26277      }
26278    });
26279
26280    // get intersections
26281    Bargraph._getDataIntersections(intersections, combinedData);
26282
26283    // plot barchart
26284    for (i = 0;
26284 i < combinedData.length; i++) {
26285      group = framework.groups[combinedData[i].groupId];
26286      var minWidth = 0.1 * group.options.barChart.width;
26287
26288      key = combinedData[i].x;
26289      var heightOffset = 0;
26290      if (intersections[key] === undefined) {
26291        if (i + 1 < combinedData.length) {
26292          coreDistance = Math.abs(combinedData[i + 1].x - key);
26293        }
26294        if (i > 0) {
26295          coreDistance = Math.min(coreDistance, Math.abs(combinedData[i - 1].x - key));
26296        }
26297        drawData = Bargraph._getSafeDrawData(coreDistance, group, minWidth);
26298      } else {
26299        var nextKey = i + (intersections[key].amount - intersections[key].resolved);
26300        var prevKey = i - (intersections[key].resolved + 1);
26301        if (nextKey < combinedData.length) {
26302          coreDistance = Math.abs(combinedData[nextKey].x - key);
26303        }
26304        if (prevKey > 0) {
26305          coreDistance = Math.min(coreDistance, Math.abs(combinedData[prevKey].x - key));
26306        }
26307        drawData = Bargraph._getSafeDrawData(coreDistance, group, minWidth);
26308        intersections[key].resolved += 1;
26309
26310        if (group.options.stack === true) {
26311          if (combinedData[i].y < group.zeroPosition) {
26312            heightOffset = intersections[key].accumulatedNegative;
26313            intersections[key].accumulatedNegative += group.zeroPosition - combinedData[i].y;
26314          } else {
26315            heightOffset = intersections[key].accumulatedPositive;
26316            intersections[key].accumulatedPositive += group.zeroPosition - combinedData[i].y;
26317          }
26318        } else if (group.options.barChart.sideBySide === true) {
26319          drawData.width = drawData.width / intersections[key].amount;
26320          drawData.offset += intersections[key].resolved * drawData.width - 0.5 * drawData.width * (intersections[key].amount + 1);
26321          if (group.options.barChart.align === 'left') {
26322            drawData.offset -= 0.5 * drawData.width;
26323          } else if (group.options.barChart.align === 'right') {
26324            drawData.offset += 0.5 * drawData.width;
26325          }
26326        }
26327      }
26328      DOMutil.drawBar(combinedData[i].x + drawData.offset, combinedData[i].y - heightOffset, drawData.width, group.zeroPosition - combinedData[i].y, group.className + ' vis-bar', framework.svgElements, framework.svg, group.style);
26329      // draw points
26330      if (group.options.drawPoints.enabled === true) {
26331        var pointData = {
26332          x: combinedData[i].x + drawData.offset,
26333          y: combinedData[i].y - heightOffset,
26334          groupId: combinedData[i].groupId,
26335          label: combinedData[i].label
26336        };
26337        Points.draw([pointData], group, framework, drawData.offset);
26338        //DOMutil.drawPoint(combinedData[i].x + drawData.offset, combinedData[i].y, group, framework.svgElements, framework.svg);
26339      }
26340    }
26341  };
26342
26343  /**
26344   * Fill the intersections object with counters of how many datapoints share the same x coordinates
26345   * @param intersections
26346   * @param combinedData
26347   * @private
26348   */
26349  Bargraph._getDataIntersections = function (intersections, combinedData) {
26350    // get intersections
26351    var coreDistance;
26352    for (var i = 0;
vendor: 11,072 bytes, lines 26352-26673
26352 i < combinedData.length; i++) {
26353      if (i + 1 < combinedData.length) {
26354        coreDistance = Math.abs(combinedData[i + 1].x - combinedData[i].x);
26355      }
26356      if (i > 0) {
26357        coreDistance = Math.min(coreDistance, Math.abs(combinedData[i - 1].x - combinedData[i].x));
26358      }
26359      if (coreDistance === 0) {
26360        if (intersections[combinedData[i].x] === undefined) {
26361          intersections[combinedData[i].x] = { amount: 0, resolved: 0, accumulatedPositive: 0, accumulatedNegative: 0 };
26362        }
26363        intersections[combinedData[i].x].amount += 1;
26364      }
26365    }
26366  };
26367
26368  /**
26369   * Get the width and offset for bargraphs based on the coredistance between datapoints
26370   *
26371   * @param coreDistance
26372   * @param group
26373   * @param minWidth
26374   * @returns {{width: Number, offset: Number}}
26375   * @private
26376   */
26377  Bargraph._getSafeDrawData = function (coreDistance, group, minWidth) {
26378    var width, offset;
26379    if (coreDistance < group.options.barChart.width && coreDistance > 0) {
26380      width = coreDistance < minWidth ? minWidth : coreDistance;
26381
26382      offset = 0; // recalculate offset with the new width;
26383      if (group.options.barChart.align === 'left') {
26384        offset -= 0.5 * coreDistance;
26385      } else if (group.options.barChart.align === 'right') {
26386        offset += 0.5 * coreDistance;
26387      }
26388    } else {
26389      // default settings
26390      width = group.options.barChart.width;
26391      offset = 0;
26392      if (group.options.barChart.align === 'left') {
26393        offset -= 0.5 * group.options.barChart.width;
26394      } else if (group.options.barChart.align === 'right') {
26395        offset += 0.5 * group.options.barChart.width;
26396      }
26397    }
26398
26399    return { width: width, offset: offset };
26400  };
26401
26402  Bargraph.getStackedYRange = function (combinedData, groupRanges, groupIds, groupLabel, orientation) {
26403    if (combinedData.length > 0) {
26404      // sort by time and by group
26405      combinedData.sort(function (a, b) {
26406        if (a.x === b.x) {
26407          return a.groupId < b.groupId ? -1 : 1;
26408        } else {
26409          return a.x - b.x;
26410        }
26411      });
26412      var intersections = {};
26413
26414      Bargraph._getDataIntersections(intersections, combinedData);
26415      groupRanges[groupLabel] = Bargraph._getStackedYRange(intersections, combinedData);
26416      groupRanges[groupLabel].yAxisOrientation = orientation;
26417      groupIds.push(groupLabel);
26418    }
26419  };
26420
26421  Bargraph._getStackedYRange = function (intersections, combinedData) {
26422    var key;
26423    var yMin = combinedData[0].y;
26424    var yMax = combinedData[0].y;
26425    for (var i = 0; i < combinedData.length; i++) {
26426      key = combinedData[i].x;
26427      if (intersections[key] === undefined) {
26428        yMin = yMin > combinedData[i].y ? combinedData[i].y : yMin;
26429        yMax = yMax < combinedData[i].y ? combinedData[i].y : yMax;
26430      } else {
26431        if (combinedData[i].y < 0) {
26432          intersections[key].accumulatedNegative += combinedData[i].y;
26433        } else {
26434          intersections[key].accumulatedPositive += combinedData[i].y;
26435        }
26436      }
26437    }
26438    for (var xpos in intersections) {
26439      if (intersections.hasOwnProperty(xpos)) {
26440        yMin = yMin > intersections[xpos].accumulatedNegative ? intersections[xpos].accumulatedNegative : yMin;
26441        yMin = yMin > intersections[xpos].accumulatedPositive ? intersections[xpos].accumulatedPositive : yMin;
26442        yMax = yMax < intersections[xpos].accumulatedNegative ? intersections[xpos].accumulatedNegative : yMax;
26443        yMax = yMax < intersections[xpos].accumulatedPositive ? intersections[xpos].accumulatedPositive : yMax;
26444      }
26445    }
26446
26447    return { min: yMin, max: yMax };
26448  };
26449
26450  module.exports = Bargraph;
26451
26452/***/ },
26453/* 57 */
26454/***/ function(module, exports, __webpack_require__) {
26455
26456  'use strict';
26457
26458  var util = __webpack_require__(1);
26459  var DOMutil = __webpack_require__(7);
26460  var Component = __webpack_require__(26);
26461
26462  /**
26463   * Legend for Graph2d
26464   */
26465  function Legend(body, options, side, linegraphOptions) {
26466    this.body = body;
26467    this.defaultOptions = {
26468      enabled: true,
26469      icons: true,
26470      iconSize: 20,
26471      iconSpacing: 6,
26472      left: {
26473        visible: true,
26474        position: 'top-left' // top/bottom - left,center,right
26475      },
26476      right: {
26477        visible: true,
26478        position: 'top-left' // top/bottom - left,center,right
26479      }
26480    };
26481    this.side = side;
26482    this.options = util.extend({}, this.defaultOptions);
26483    this.linegraphOptions = linegraphOptions;
26484
26485    this.svgElements = {};
26486    this.dom = {};
26487    this.groups = {};
26488    this.amountOfGroups = 0;
26489    this._create();
26490
26491    this.setOptions(options);
26492  }
26493
26494  Legend.prototype = new Component();
26495
26496  Legend.prototype.clear = function () {
26497    this.groups = {};
26498    this.amountOfGroups = 0;
26499  };
26500
26501  Legend.prototype.addGroup = function (label, graphOptions) {
26502
26503    // Include a group only if the group option 'excludeFromLegend: false' is not set.
26504    if (graphOptions.options.excludeFromLegend != true) {
26505      if (!this.groups.hasOwnProperty(label)) {
26506        this.groups[label] = graphOptions;
26507      }
26508      this.amountOfGroups += 1;
26509    }
26510  };
26511
26512  Legend.prototype.updateGroup = function (label, graphOptions) {
26513    this.groups[label] = graphOptions;
26514  };
26515
26516  Legend.prototype.removeGroup = function (label) {
26517    if (this.groups.hasOwnProperty(label)) {
26518      delete this.groups[label];
26519      this.amountOfGroups -= 1;
26520    }
26521  };
26522
26523  Legend.prototype._create = function () {
26524    this.dom.frame = document.createElement('div');
26525    this.dom.frame.className = 'vis-legend';
26526    this.dom.frame.style.position = 'absolute';
26527    this.dom.frame.style.top = '10px';
26528    this.dom.frame.style.display = 'block';
26529
26530    this.dom.textArea = document.createElement('div');
26531    this.dom.textArea.className = 'vis-legend-text';
26532    this.dom.textArea.style.position = 'relative';
26533    this.dom.textArea.style.top = '0px';
26534
26535    this.svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
26536    this.svg.style.position = 'absolute';
26537    this.svg.style.top = 0 + 'px';
26538    this.svg.style.width = this.options.iconSize + 5 + 'px';
26539    this.svg.style.height = '100%';
26540
26541    this.dom.frame.appendChild(this.svg);
26542    this.dom.frame.appendChild(this.dom.textArea);
26543  };
26544
26545  /**
26546   * Hide the component from the DOM
26547   */
26548  Legend.prototype.hide = function () {
26549    // remove the frame containing the items
26550    if (this.dom.frame.parentNode) {
26551      this.dom.frame.parentNode.removeChild(this.dom.frame);
26552    }
26553  };
26554
26555  /**
26556   * Show the component in the DOM (when not already visible).
26557   * @return {Boolean} changed
26558   */
26559  Legend.prototype.show = function () {
26560    // show frame containing the items
26561    if (!this.dom.frame.parentNode) {
26562      this.body.dom.center.appendChild(this.dom.frame);
26563    }
26564  };
26565
26566  Legend.prototype.setOptions = function (options) {
26567    var fields = ['enabled', 'orientation', 'icons', 'left', 'right'];
26568    util.selectiveDeepExtend(fields, this.options, options);
26569  };
26570
26571  Legend.prototype.redraw = function () {
26572    var activeGroups = 0;
26573    var groupArray = Object.keys(this.groups);
26574    groupArray.sort(function (a, b) {
26575      return a < b ? -1 : 1;
26576    });
26577
26578    for (var i = 0; i < groupArray.length; i++) {
26579      var groupId = groupArray[i];
26580      if (this.groups[groupId].visible == true && (this.linegraphOptions.visibility[groupId] === undefined || this.linegraphOptions.visibility[groupId] == true)) {
26581        activeGroups++;
26582      }
26583    }
26584
26585    if (this.options[this.side].visible == false || this.amountOfGroups == 0 || this.options.enabled == false || activeGroups == 0) {
26586      this.hide();
26587    } else {
26588      this.show();
26589      if (this.options[this.side].position == 'top-left' || this.options[this.side].position == 'bottom-left') {
26590        this.dom.frame.style.left = '4px';
26591        this.dom.frame.style.textAlign = 'left';
26592        this.dom.textArea.style.textAlign = 'left';
26593        this.dom.textArea.style.left = this.options.iconSize + 15 + 'px';
26594        this.dom.textArea.style.right = '';
26595        this.svg.style.left = 0 + 'px';
26596        this.svg.style.right = '';
26597      } else {
26598        this.dom.frame.style.right = '4px';
26599        this.dom.frame.style.textAlign = 'right';
26600        this.dom.textArea.style.textAlign = 'right';
26601        this.dom.textArea.style.right = this.options.iconSize + 15 + 'px';
26602        this.dom.textArea.style.left = '';
26603        this.svg.style.right = 0 + 'px';
26604        this.svg.style.left = '';
26605      }
26606
26607      if (this.options[this.side].position == 'top-left' || this.options[this.side].position == 'top-right') {
26608        this.dom.frame.style.top = 4 - Number(this.body.dom.center.style.top.replace('px', '')) + 'px';
26609        this.dom.frame.style.bottom = '';
26610      } else {
26611        var scrollableHeight = this.body.domProps.center.height - this.body.domProps.centerContainer.height;
26612        this.dom.frame.style.bottom = 4 + scrollableHeight + Number(this.body.dom.center.style.top.replace('px', '')) + 'px';
26613        this.dom.frame.style.top = '';
26614      }
26615
26616      if (this.options.icons == false) {
26617        this.dom.frame.style.width = this.dom.textArea.offsetWidth + 10 + 'px';
26618        this.dom.textArea.style.right = '';
26619        this.dom.textArea.style.left = '';
26620        this.svg.style.width = '0px';
26621      } else {
26622        this.dom.frame.style.width = this.options.iconSize + 15 + this.dom.textArea.offsetWidth + 10 + 'px';
26623        this.drawLegendIcons();
26624      }
26625
26626      var content = '';
26627      for (var i = 0; i < groupArray.length; i++) {
26628        var groupId = groupArray[i];
26629        if (this.groups[groupId].visible == true && (this.linegraphOptions.visibility[groupId] === undefined || this.linegraphOptions.visibility[groupId] == true)) {
26630          content += this.groups[groupId].content + '<br />';
26631        }
26632      }
26633      this.dom.textArea.innerHTML = content;
26634      this.dom.textArea.style.lineHeight = 0.75 * this.options.iconSize + this.options.iconSpacing + 'px';
26635    }
26636  };
26637
26638  Legend.prototype.drawLegendIcons = function () {
26639    if (this.dom.frame.parentNode) {
26640      var groupArray = Object.keys(this.groups);
26641      groupArray.sort(function (a, b) {
26642        return a < b ? -1 : 1;
26643      });
26644
26645      DOMutil.prepareElements(this.svgElements);
26646      var padding = window.getComputedStyle(this.dom.frame).paddingTop;
26647      var iconOffset = Number(padding.replace('px', ''));
26648      var x = iconOffset;
26649      var iconWidth = this.options.iconSize;
26650      var iconHeight = 0.75 * this.options.iconSize;
26651      var y = iconOffset + 0.5 * iconHeight + 3;
26652
26653      this.svg.style.width = iconWidth + 5 + iconOffset + 'px';
26654
26655      for (var i = 0; i < groupArray.length; i++) {
26656        var groupId = groupArray[i];
26657        if (this.groups[groupId].visible == true && (this.linegraphOptions.visibility[groupId] === undefined || this.linegraphOptions.visibility[groupId] == true)) {
26658          this.groups[groupId].drawIcon(x, y, this.svgElements, this.svg, iconWidth, iconHeight);
26659          y += iconHeight + this.options.iconSpacing;
26660        }
26661      }
26662
26663      DOMutil.cleanupElements(this.svgElements);
26664    }
26665  };
26666
26667  module.exports = Legend;
26668
26669/***/ },
26670/* 58 */
26671/***/ function(module, exports) {
26672
26673  /**
26674   * This object contains all possible options. It will check if the types are correct, if required if the option is one
26675   * of the allowed values.
26676   *
26677   * __any__ means that the name of the property does not matter.
26678   * __type__ is a required field for all objects and contains the allowed types of all objects
26679   */
26680  'use strict';
26681
26682  Object.defineProperty(exports, '__esModule', {
26683    value: true
26684  });
26685  var string = 'string';
26686  var boolean = 'boolean';
26687  var number = 'number';
26688  var array = 'array';
26689  var date = 'date';
26690  var object = 'object'; // should only be in a __type__ property
26691  var dom = 'dom';
26692  var moment = 'moment';
26693  var any = 'any';
26694
26695  var allOptions = {
26696    configure: {
26697      enabled: { boolean: boolean },
26698      filter: { boolean: boolean, 'function': 'function' },
26699      container: { dom: dom },
26700      __type__: { object: object, boolean: boolean, 'function': 'function' }
26701    },
26702
26703    //globals :
26704    yAxisOrientation: { string: ['left', 'right'] },
26705    defaultGroup: { string: string },
26706    sort: { boolean: boolean },
26707    sampling: { boolean: boolean },
26708    stack: { boolean: boolean },
26709    graphHeight: { string: string, number: number },
26710    shaded: {
26711      enabled: { boolean: boolean },
26712      orientation: { string: ['bottom', 'top'] }, // top, bottom
26713      __type__: { boolean: boolean, object: object }
26714    },
26715    style: { string: ['line', 'bar', 'points'] }, // line, bar
26716    barChart: {
26717      width: { number: number },
26718      sideBySide: { boolean: boolean },
26719      align: { string: ['left', 'center', 'right'] },
26720      __type__: { object: object }
26721    },
26722    interpolation: {
26723      enabled: { boolean: boolean },
26724      parametrization: { string: ['centripetal', 'chordal', 'uniform'] }, // uniform (alpha = 0.0), chordal (alpha = 1.0), centripetal (alpha = 0.5)
26725      alpha: { number: number },
26726      __type__: { object: object, boolean: boolean }
26727    },
26728    drawPoints: {
26729      enabled: { boolean: boolean },
26730      onRender: { 'function': 'function' },
26731      size: { number: number },
26732      style: { string: ['square', 'circle'] }, // square, circle
26733      __type__: { object: object, boolean: boolean, 'function': 'function' }
26734    },
26735    dataAxis: {
26736      showMinorLabels: { boolean: boolean },
26737      showMajorLabels: { boolean: boolean },
26738      icons: { boolean: boolean },
26739      width: { string: string, number: number },
26740      visible: { boolean: boolean },
26741      alignZeros: { boolean: boolean },
26742      left: {
26743        range: { min: { number: number }, max: { number: number }, __type__: { object: object } },
26744        format: { 'function': 'function' },
26745        title: { text: { string: string, number: number }, style: { string: string }, __type__: { object: object } },
26746        __type__: { object: object }
26747      },
26748      right: {
26749        range: { min: { number: number }, max: { number: number }, __type__: { object: object } },
26750        format: { 'function': 'function' },
26751        title: { text: { string: string, number: number }, style: { string: string }, __type__: { object: object } },
26752        __type__: { object: object }
26753      },
26754      __type__: { object: object }
26755    },
26756    legend: {
26757      enabled: { boolean: boolean },
26758      icons: { boolean: boolean },
26759      left: {
26760        visible: { boolean: boolean },
26761        position: { string: ['top-right', 'bottom-right', 'top-left', 'bottom-left'] },
26762        __type__: { object: object }
26763      },
26764      right: {
26765        visible: { boolean: boolean },
26766        position: { string: ['top-right', 'bottom-right', 'top-left', 'bottom-left'] },
26767        __type__: { object: object }
26768      },
26769      __type__: { object: object, boolean: boolean }
26770    },
26771    groups: {
26772      visibility: { any: any },
26773      __type__: { object: object }
26774    },
26775
26776    autoResize: { boolean: boolean },
26777    throttleRedraw: { number: number },
26778    clickToUse: { boolean: boolean },
26779    end: { number: number, date: date, string: string, moment: moment },
26780    format: {
26781      minorLabels: {
26782        millisecond: { string: string, 'undefined': 'undefined' },
26783        second: { string: string, 'undefined': 'undefined' },
26784        minute: { string: string, 'undefined': 'undefined' },
26785        hour: { string: string, 'undefined': 'undefined' },
vendor: 8,388 bytes, lines 26786-27038
26786        weekday: { string: string, 'undefined': 'undefined' },
26787        day: { string: string, 'undefined': 'undefined' },
26788        month: { string: string, 'undefined': 'undefined' },
26789        year: { string: string, 'undefined': 'undefined' },
26790        __type__: { object: object }
26791      },
26792      majorLabels: {
26793        millisecond: { string: string, 'undefined': 'undefined' },
26794        second: { string: string, 'undefined': 'undefined' },
26795        minute: { string: string, 'undefined': 'undefined' },
26796        hour: { string: string, 'undefined': 'undefined' },
26797        weekday: { string: string, 'undefined': 'undefined' },
26798        day: { string: string, 'undefined': 'undefined' },
26799        month: { string: string, 'undefined': 'undefined' },
26800        year: { string: string, 'undefined': 'undefined' },
26801        __type__: { object: object }
26802      },
26803      __type__: { object: object }
26804    },
26805    moment: { 'function': 'function' },
26806    height: { string: string, number: number },
26807    hiddenDates: {
26808      start: { date: date, number: number, string: string, moment: moment },
26809      end: { date: date, number: number, string: string, moment: moment },
26810      repeat: { string: string },
26811      __type__: { object: object, array: array }
26812    },
26813    locale: { string: string },
26814    locales: {
26815      __any__: { any: any },
26816      __type__: { object: object }
26817    },
26818    max: { date: date, number: number, string: string, moment: moment },
26819    maxHeight: { number: number, string: string },
26820    min: { date: date, number: number, string: string, moment: moment },
26821    minHeight: { number: number, string: string },
26822    moveable: { boolean: boolean },
26823    multiselect: { boolean: boolean },
26824    orientation: { string: string },
26825    showCurrentTime: { boolean: boolean },
26826    showMajorLabels: { boolean: boolean },
26827    showMinorLabels: { boolean: boolean },
26828    start: { date: date, number: number, string: string, moment: moment },
26829    timeAxis: {
26830      scale: { string: string, 'undefined': 'undefined' },
26831      step: { number: number, 'undefined': 'undefined' },
26832      __type__: { object: object }
26833    },
26834    width: { string: string, number: number },
26835    zoomable: { boolean: boolean },
26836    zoomKey: { string: ['ctrlKey', 'altKey', 'metaKey', ''] },
26837    zoomMax: { number: number },
26838    zoomMin: { number: number },
26839    __type__: { object: object }
26840  };
26841
26842  var configureOptions = {
26843    global: {
26844      //yAxisOrientation: ['left','right'], // TDOO: enable as soon as Grahp2d doesn't crash when changing this on the fly
26845      sort: true,
26846      sampling: true,
26847      stack: false,
26848      shaded: {
26849        enabled: false,
26850        orientation: ['top', 'bottom'] // top, bottom
26851      },
26852      style: ['line', 'bar', 'points'], // line, bar
26853      barChart: {
26854        width: [50, 5, 100, 5],
26855        sideBySide: false,
26856        align: ['left', 'center', 'right'] // left, center, right
26857      },
26858      interpolation: {
26859        enabled: true,
26860        parametrization: ['centripetal', 'chordal', 'uniform'] // uniform (alpha = 0.0), chordal (alpha = 1.0), centripetal (alpha = 0.5)
26861      },
26862      drawPoints: {
26863        enabled: true,
26864        size: [6, 2, 30, 1],
26865        style: ['square', 'circle'] // square, circle
26866      },
26867      dataAxis: {
26868        showMinorLabels: true,
26869        showMajorLabels: true,
26870        icons: false,
26871        width: [40, 0, 200, 1],
26872        visible: true,
26873        alignZeros: true,
26874        left: {
26875          //range: {min:'undefined': 'undefined'ined,max:'undefined': 'undefined'ined},
26876          //format: function (value) {return value;},
26877          title: { text: '', style: '' }
26878        },
26879        right: {
26880          //range: {min:'undefined': 'undefined'ined,max:'undefined': 'undefined'ined},
26881          //format: function (value) {return value;},
26882          title: { text: '', style: '' }
26883        }
26884      },
26885      legend: {
26886        enabled: false,
26887        icons: true,
26888        left: {
26889          visible: true,
26890          position: ['top-right', 'bottom-right', 'top-left', 'bottom-left'] // top/bottom - left,right
26891        },
26892        right: {
26893          visible: true,
26894          position: ['top-right', 'bottom-right', 'top-left', 'bottom-left'] // top/bottom - left,right
26895        }
26896      },
26897
26898      autoResize: true,
26899      throttleRedraw: [10, 0, 1000, 10],
26900      clickToUse: false,
26901      end: '',
26902      format: {
26903        minorLabels: {
26904          millisecond: 'SSS',
26905          second: 's',
26906          minute: 'HH:mm',
26907          hour: 'HH:mm',
26908          weekday: 'ddd D',
26909          day: 'D',
26910          month: 'MMM',
26911          year: 'YYYY'
26912        },
26913        majorLabels: {
26914          millisecond: 'HH:mm:ss',
26915          second: 'D MMMM HH:mm',
26916          minute: 'ddd D MMMM',
26917          hour: 'ddd D MMMM',
26918          weekday: 'MMMM YYYY',
26919          day: 'MMMM YYYY',
26920          month: 'YYYY',
26921          year: ''
26922        }
26923      },
26924
26925      height: '',
26926      locale: '',
26927      max: '',
26928      maxHeight: '',
26929      min: '',
26930      minHeight: '',
26931      moveable: true,
26932      orientation: ['both', 'bottom', 'top'],
26933      showCurrentTime: false,
26934      showMajorLabels: true,
26935      showMinorLabels: true,
26936      start: '',
26937      width: '100%',
26938      zoomable: true,
26939      zoomKey: ['ctrlKey', 'altKey', 'metaKey', ''],
26940      zoomMax: [315360000000000, 10, 315360000000000, 1],
26941      zoomMin: [10, 10, 315360000000000, 1]
26942    }
26943  };
26944
26945  exports.allOptions = allOptions;
26946  exports.configureOptions = configureOptions;
26947
26948/***/ },
26949/* 59 */
26950/***/ function(module, exports, __webpack_require__) {
26951
26952  // Load custom shapes into CanvasRenderingContext2D
26953  'use strict';
26954
26955  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
26956
26957  var _modulesGroups = __webpack_require__(60);
26958
26959  var _modulesGroups2 = _interopRequireDefault(_modulesGroups);
26960
26961  var _modulesNodesHandler = __webpack_require__(61);
26962
26963  var _modulesNodesHandler2 = _interopRequireDefault(_modulesNodesHandler);
26964
26965  var _modulesEdgesHandler = __webpack_require__(81);
26966
26967  var _modulesEdgesHandler2 = _interopRequireDefault(_modulesEdgesHandler);
26968
26969  var _modulesPhysicsEngine = __webpack_require__(90);
26970
26971  var _modulesPhysicsEngine2 = _interopRequireDefault(_modulesPhysicsEngine);
26972
26973  var _modulesClustering = __webpack_require__(99);
26974
26975  var _modulesClustering2 = _interopRequireDefault(_modulesClustering);
26976
26977  var _modulesCanvasRenderer = __webpack_require__(101);
26978
26979  var _modulesCanvasRenderer2 = _interopRequireDefault(_modulesCanvasRenderer);
26980
26981  var _modulesCanvas = __webpack_require__(102);
26982
26983  var _modulesCanvas2 = _interopRequireDefault(_modulesCanvas);
26984
26985  var _modulesView = __webpack_require__(103);
26986
26987  var _modulesView2 = _interopRequireDefault(_modulesView);
26988
26989  var _modulesInteractionHandler = __webpack_require__(105);
26990
26991  var _modulesInteractionHandler2 = _interopRequireDefault(_modulesInteractionHandler);
26992
26993  var _modulesSelectionHandler = __webpack_require__(108);
26994
26995  var _modulesSelectionHandler2 = _interopRequireDefault(_modulesSelectionHandler);
26996
26997  var _modulesLayoutEngine = __webpack_require__(109);
26998
26999  var _modulesLayoutEngine2 = _interopRequireDefault(_modulesLayoutEngine);
27000
27001  var _modulesManipulationSystem = __webpack_require__(110);
27002
27003  var _modulesManipulationSystem2 = _interopRequireDefault(_modulesManipulationSystem);
27004
27005  var _sharedConfigurator = __webpack_require__(45);
27006
27007  var _sharedConfigurator2 = _interopRequireDefault(_sharedConfigurator);
27008
27009  var _sharedValidator = __webpack_require__(47);
27010
27011  var _sharedValidator2 = _interopRequireDefault(_sharedValidator);
27012
27013  var _optionsJs = __webpack_require__(111);
27014
27015  var _modulesKamadaKawaiJs = __webpack_require__(112);
27016
27017  var _modulesKamadaKawaiJs2 = _interopRequireDefault(_modulesKamadaKawaiJs);
27018
27019  __webpack_require__(114);
27020
27021  var Emitter = __webpack_require__(12);
27022  var Hammer = __webpack_require__(20);
27023  var util = __webpack_require__(1);
27024  var DataSet = __webpack_require__(8);
27025  var DataView = __webpack_require__(10);
27026  var dotparser = __webpack_require__(115);
27027  var gephiParser = __webpack_require__(116);
27028  var Images = __webpack_require__(117);
27029  var Activator = __webpack_require__(40);
27030  var locales = __webpack_require__(118);
27031
27032  /**
27033   * @constructor Network
27034   * Create a network visualization, displaying nodes and edges.
27035   *
27036   * @param {Element} container   The DOM element in which the Network will
27037   *                                  be created. Normally a div element.
27038   * @param {Object}
27038 data         An object containing parameters
27039   *                              {Array} nodes
27040   *                              {Array} edges
27041   * @param {Object} options      Options
27042   */
27043  function Network(container, data, options) {
27044    var _this = this;
27045
27046    if (!(this instanceof Network)) {
27047      throw new SyntaxError('Constructor must be called with the new operator');
27048    }
27049
27050    // set constant values
27051    this.options = {};
27052    this.defaultOptions = {
27053      locale: 'en',
27054      locales: locales,
27055      clickToUse: false
27056    };
27057    util.extend(this.options, this.defaultOptions);
27058
27059    // containers for nodes and edges
27060    this.body = {
27061      container: container,
27062      nodes: {},
27063      nodeIndices: [],
27064      edges: {},
27065      edgeIndices: [],
27066      emitter: {
27067        on: this.on.bind(this),
27068        off: this.off.bind(this),
27069        emit: this.emit.bind(this),
27070        once: this.once.bind(this)
27071      },
27072      eventListeners: {
27073        onTap: function onTap() {},
27074        onTouch: function onTouch() {},
27075        onDoubleTap: function onDoubleTap() {},
27076        onHold: function onHold() {},
27077        onDragStart: function onDragStart() {},
27078        onDrag: function onDrag() {},
27079        onDragEnd: function onDragEnd() {},
27080        onMouseWheel: function onMouseWheel() {},
27081        onPinch: function onPinch() {},
27082        onMouseMove: function onMouseMove() {},
27083        onRelease: function onRelease() {},
27084        onContext: function onContext() {}
27085      },
27086      data: {
27087        nodes: null, // A DataSet or DataView
27088        edges: null // A DataSet or DataView
27089      },
27090      functions: {
27091        createNode: function createNode() {},
27092        createEdge: function createEdge() {},
27093        getPointer: function getPointer() {}
27094      },
27095      modules: {},
27096      view: {
27097        scale: 1,
27098        translation: { x: 0, y: 0 }
27099      }
27100    };
27101
27102    // bind the event listeners
27103    this.bindEventListeners();
27104
27105    // setting up all modules
27106    this.images = new Images(function () {
27107      return _this.body.emitter.emit('_requestRedraw');
27108    }); // object with images
27109    this.groups = new _modulesGroups2['default'](); // object with groups
27110    this.canvas = new _modulesCanvas2['default'](this.body); // DOM handler
27111    this.selectionHandler = new _modulesSelectionHandler2['default'](this.body, this.canvas); // Selection handler
27112    this.interactionHandler = new _modulesInteractionHandler2['default'](this.body, this.canvas, this.selectionHandler); // Interaction handler handles all the hammer bindings (that are bound by canvas), key
27113    this.view = new _modulesView2['default'](this.body, this.canvas); // camera handler, does animations and zooms
27114    this.renderer = new _modulesCanvasRenderer2['default'](this.body, this.canvas); // renderer, starts renderloop, has events that modules can hook into
27115    this.physics = new _modulesPhysicsEngine2['default'](this.body); // physics engine, does all the simulations
27116    this.layoutEngine = new _modulesLayoutEngine2['default'](this.body); // layout engine for inital layout and hierarchical layout
27117    this.clustering = new _modulesClustering2['default'](this.body); // clustering api
27118    this.manipulation = new _modulesManipulationSystem2['default'](this.body, this.canvas, this.selectionHandler); // data manipulation system
27119
27120    this.nodesHandler = new _modulesNodesHandler2['default'](this.body, this.images, this.groups, this.layoutEngine); // Handle adding, deleting and updating of nodes as well as global options
27121    this.edgesHandler = new _modulesEdgesHandler2['default'](this.body, this.images, this.groups); // Handle adding, deleting and updating of edges as well as global options
27122
27123    this.body.modules['kamadaKawai'] = new _modulesKamadaKawaiJs2['default'](this.body, 150, 0.05); // Layouting algorithm.
27124    this.body.modules['clustering'] = this.clustering;
27125
27126    // create the DOM elements
27127    this.canvas._create();
27128
27129    // apply options
27130    this.setOptions(options);
27131
27132    // load data (the disable start variable will be the same as the enabled clustering)
27133    this.setData(data);
27134  }
27135
27136  // Extend Network with an Emitter mixin
27137  Emitter(Network.prototype);
27138
27139  /**
27140   * Set options
27141   * @param {Object} options
27142   */
27143  Network.prototype.setOptions = function (options) {
27144    var _this2 = this;
27145
27146    if (options !== undefined) {
27147
27148      var errorFound = _sharedValidator2['default'].validate(options, _optionsJs.allOptions);
27149      if (errorFound === true) {
27150        console.log('%cErrors have been found in the supplied options object.', _sharedValidator.printStyle);
27151      }
27152
27153      // copy the global fields over
27154      var fields = ['locale', 'locales', 'clickToUse'];
27155      util.selectiveDeepExtend(fields, this.options, options);
27156
27157      // the hierarchical system can adapt the edges and the physics to it's own options because not all c
27157ombinations work with the hierarichical system.
27158      options = this.layoutEngine.setOptions(options.layout, options);
27159
27160      this.canvas.setOptions(options); // options for canvas are in globals
27161
27162      // pass the options to the modules
27163      this.groups.setOptions(options.groups);
27164      this.nodesHandler.setOptions(options.nodes);
27165      this.edgesHandler.setOptions(options.edges);
27166      this.physics.setOptions(options.physics);
27167      this.manipulation.setOptions(options.manipulation, options, this.options); // manipulation uses the locales in the globals
27168
27169      this.interactionHandler.setOptions(options.interaction);
27170      this.renderer.setOptions(options.interaction); // options for rendering are in interaction
27171      this.selectionHandler.setOptions(options.interaction); // options for selection are in interaction
27172
27173      // reload the settings of the nodes to apply changes in groups that are not referenced by pointer.
27174      if (options.groups !== undefined) {
27175        this.body.emitter.emit('refreshNodes');
27176      }
27177      // these two do not have options at the moment, here for completeness
27178      //this.view.setOptions(options.view);
27179      //this.clustering.setOptions(options.clustering);
27180
27181      if ('configure' in options) {
27182        if (!this.configurator) {
27183          this.configurator = new _sharedConfigurator2['default'](this, this.body.container, _optionsJs.configureOptions, this.canvas.pixelRatio);
27184        }
27185
27186        this.configurator.setOptions(options.configure);
27187      }
27188
27189      // if the configuration system is enabled, copy all options and put them into the config system
27190      if (this.configurator && this.configurator.options.enabled === true) {
27191        var networkOptions = { nodes: {}, edges: {}, layout: {}, interaction: {}, manipulation: {}, physics: {}, global: {} };
27192        util.deepExtend(networkOptions.nodes, this.nodesHandler.options);
27193        util.deepExtend(networkOptions.edges, this.edgesHandler.options);
27194        util.deepExtend(networkOptions.layout, this.layoutEngine.options);
27195        // load the selectionHandler and render default options in to the interaction group
27196        util.deepExtend(networkOptions.interaction, this.selectionHandler.options);
27197        util.deepExtend(networkOptions.interaction, this.renderer.options);
27198
27199        util.deepExtend(networkOptions.interaction, this.interactionHandler.options);
27200        util.deepExtend(networkOptions.manipulation, this.manipulation.options);
27201        util.deepExtend(networkOptions.physics, this.physics.options);
27202
27203        // load globals into the global object
27204        util.deepExtend(networkOptions.global, this.canvas.options);
27205        util.deepExtend(networkOptions.global, this.options);
27206
27207        this.configurator.setModuleOptions(networkOptions);
27208      }
27209
27210      // handle network global options
27211      if (options.clickToUse !== undefined) {
27212        if (options.clickToUse === true) {
27213          if (this.activator === undefined) {
27214            this.activator = new Activator(this.canvas.frame);
27215            this.activator.on('change', function () {
27216              _this2.body.emitter.emit('activate');
27217            });
27218          }
27219        } else {
27220          if (this.activator !== undefined) {
27221            this.activator.destroy();
27222            delete this.activator;
27223          }
27224          this.body.emitter.emit('activate');
27225        }
27226      } else {
27227        this.body.emitter.emit('activate');
27228      }
27229
27230      this.canvas.setSize();
27231      // start the physics simulation. Can be safely called multiple times.
27232      this.body.emitter.emit('startSimulation');
27233    }
27234  };
27235
27236  /**
27237   * Update the this.body.nodeIndices with the most recent node index list
27238   * @private
27239   */
27240  Network.prototype._updateVisibleIndices = function () {
27241    var nodes = this.body.nodes;
27242    var edges = this.body.edges;
27243    this.body.nodeIndices = [];
27244    this.body.edgeIndices = [];
27245
27246    for (var nodeId in nodes) {
27247      if (nodes.hasOwnProperty(nodeId)) {
27248        if (nodes[nodeId].options.hidden === false) {
27249          this.body.nodeIndices.push(nodeId);
27250        }
27251      }
27252    }
27253
27254    for (var edgeId in edges) {
27255      if (edges.hasOwnProperty(edgeId)) {
27256        if (edges[edgeId].options.hidden === false) {
27257          this.body.edgeIndices.push(edgeId);
27258        }
27259      }
27260    }
27261  };
27262
27263  /**
27264   * Bind all events
27265   */
27266  Network.prototype.bindEventListeners = function () {
27267    var _this3 = this;
27268
27269    // this event will trigger a rebuilding of the cache everything. Used when nodes or edges have been added or removed.
27270    this.body.emitter.on('_dataChanged', function () {
27271      // update shortcut lists
27272      _this3._updateVisibleIndices();
27273      _this3.physics.updatePhysicsData();
27274      _this3.body.emitter.emit('_requestRedraw');
27275      // call the dataUpdated event because the only difference between the two is the updating of the indices
27276      _this3.body.emitter.emit('_dataUpdated');
27277    });
27278
27279    // this is called when options of EXISTING nodes or edges have changed.
27280    this.body.emitter.on('_dataUpdated', function () {
27281      // update values
27282      _this3._updateValueRange(_this3.body.nodes);
27283      _this3._updateValueRange(_this3.body.edges);
27284      // start simulation (can be called safely, even if already running)
27285      _this3.body.emitter.emit('startSimulation');
27286      _this3.body.emitter.emit('_requestRedraw');
27287    });
27288  };
27289
27290  /**
27291   * Set nodes and edges, and optionally options as well.
27292   *
27293   * @param {Object} data              Object containing parameters:
27294   *                                   {Array | DataSet | DataView} [nodes] Array with nodes
27295   *                                   {Array | DataSet | DataView} [edges] Array with edges
27296   *                                   {String} [dot] String containing data in DOT format
27297   *                                   {String} [gephi] String containing data in gephi JSON format
27298   *                                   {Options} [options] Object with options
27299   */
27300  Network.prototype.setData = function (data) {
27301    // reset the physics engine.
27302    this.body.emitter.emit('resetPhysics');
27303    this.body.emitter.emit('_resetData');
27304
27305    // unselect all to ensure no selections from old data are carried over.
27306    this.selectionHandler.unselectAll();
27307
27308    if (data && data.dot && (data.nodes || data.edges)) {
27309      throw new SyntaxError('Data must contain either parameter "dot" or ' + ' parameter pair "nodes" and "edges", but not both.');
27310    }
27311
27312    // set options
27313    this.setOptions(data && data.options);
27314    // set all data
27315    if (data && data.dot) {
27316      console.log('The dot property has been depricated. 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);');
27317      // parse DOT file
27318      var dotData = dotparser.DOTToGraph(data.dot);
27319      this.setData(dotData);
27320      return;
27321    } else if (data && data.gephi) {
27322      // parse DOT file
27323      console.log('The gephi property has been depricated. 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);');
27324      var gephiData = gephiParser.parseGephi(data.gephi);
27325      this.setData(gephiData);
27326      return;
27327    } else {
27328      this.nodesHandler.setData(data && data.nodes, true);
27329      this.edgesHandler.setData(data && data.edges, true);
27330    }
27331
27332    // emit change in data
27333    this.body.emitter.emit('_dataChanged');
27334
27335    // emit data loaded
27336    this.body.emitter.emit('_dataLoaded');
27337
27338    // find a stable position or start animating to a stable position
27339    this.body.emitter.emit('initPhysics');
27340  };
27341
27342  /**
27343   * Cleans up all bindings of the network, removing it fully from the memory IF the variable is set to null after calling this function.
27344   * var network = new vis.Network(..);
27345   * network.destroy();
27346   * network = null;
27347   */
27348  Network.prototype.destroy = function () {
27349    this.body.emitter.emit('destroy');
27350    // clear events
27351    this.body.emitter.off();
27352    this.off();
27353
27354    // delete modules
27355    delete this.groups;
27356    delete this.canvas;
27357    delete this.selectionHandler;
27358    delete this.interactionHandler;
27359    delete this.view;
27360    delete this.renderer;
27361    delete this.physics;
27362    delete this.layoutEngine;
27363    delete this.clustering;
27364    delete this.manipulation;
27365    delete this.nodesHandler;
27366    delete this.edgesHandler;
27367    delete this.configurator;
27368    delete this.images;
27369
27370    for (var nodeId in this.body.nodes) {
27371      delete this.body.nodes[nodeId];
27372    }
27373    for (var edgeId in this.body.edges) {
27374      delete this.body.edges[edgeId];
27375    }
27376
27377    // remove the container and everything inside it recursively
27378    util.recursiveDOMDelete(this.body.container);
27379  };
27380
27381  /**
27382   * Update the values of all object in the given array according to the current
27383   * value range of the objects in the array.
27384   * @param {Object} obj    An object containing a set of Edges or Nodes
27385   *                        The objects must have a method getValue() and
27386   *                        setValueRange(min, max).
27387   * @private
27388   */
27389  Network.prototype._updateValueRange = function (obj) {
27390    var id;
27391
27392    // determine the range of the objects
27393    var valueMin = undefined;
27394    var valueMax = undefined;
27395    var valueTotal = 0;
27396    for (id in obj) {
27397      if (obj.hasOwnProperty(id)) {
27398        var value = obj[id].getValue();
27399        if (value !== undefined) {
27400          valueMin = valueMin === undefined ? value : Math.min(value, valueMin);
27401          valueMax = valueMax === undefined ? value : Math.max(value, valueMax);
27402          valueTotal += value;
27403        }
27404      }
27405    }
27406
27407    // adjust the range of all objects
27408    if (valueMin !== undefined && valueMax !== undefined) {
27409      for (id in obj) {
27410        if (obj.hasOwnProperty(id)) {
27411          obj[id].setValueRange(valueMin, valueMax, valueTotal);
27412        }
27413      }
27414    }
27415  };
27416
27417  /**
27418   * Returns true when the Network is active.
27419   * @returns {boolean}
27420   */
27421  Network.prototype.isActive = function () {
27422    return !this.activator || this.activator.active;
27423  };
27424
27425  Network.prototype.setSize = function () {
27426    return this.canvas.setSize.apply(this.canvas, arguments);
27427  };
27428  Network.prototype.canvasToDOM = function () {
27429    return this.canvas.canvasToDOM.apply(this.canvas, arguments);
27430  };
27431  Network.prototype.DOMtoCanvas = function () {
27432    return this.canvas.DOMtoCanvas.apply(this.canvas, arguments);
27433  };
27434  Network.prototype.findNode = function () {
27435    return this.clustering.findNode.apply(this.clustering, arguments);
27436  };
27437  Network.prototype.isCluster = function () {
27438    return this.clustering.isCluster.apply(this.clustering, arguments);
27439  };
27440  Network.prototype.openCluster = function () {
27441    return this.clustering.openCluster.apply(this.clustering, arguments);
27442  };
27443  Network.prototype.cluster = function () {
27444    return this.clustering.cluster.apply(this.clustering, arguments);
27445  };
27446  Network.prototype.getNodesInCluster = function () {
27447    return this.clustering.getNodesInCluster.apply(this.clustering, arguments);
27448  };
27449  Network.prototype.clusterByConnection = function () {
27450    return this.clustering.clusterByConnection.apply(this.clustering, arguments);
27451  };
27452  Network.prototype.clusterByHubsize = function () {
27453    return this.clustering.clusterByHubsize.apply(this.clustering, arguments);
27454  };
27455  Network.prototype.clusterOutliers = function () {
27456    return this.clustering.clusterOutliers.apply(this.clustering, arguments);
27457  };
27458  Network.prototype.getSeed = function () {
27459    return this.layoutEngine.getSeed.apply(this.layoutEngine, arguments);
27460  };
27461  Network.prototype.enableEditMode = function () {
27462    return this.manipulation.enableEditMode.apply(this.manipulation, arguments);
27463  };
27464  Network.prototype.disableEditMode = function () {
27465    return this.manipulation.disableEditMode.apply(this.manipulation, arguments);
27466  };
27467  Network.prototype.addNodeMode = function () {
27468    return this.manipulation.addNodeMode.apply(this.manipulation, arguments);
27469  };
27470  Network.prototype.editNode = function () {
27471    return this.manipulation.editNode.apply(this.manipulation, arguments);
27472  };
27473  Network.prototype.editNodeMode = function () {
27474    console.log('Deprecated: Please use editNode instead of editNodeMode.');return this.manipulation.editNode.apply(this.manipulation, arguments);
27475  };
27476  Network.prototype.addEdgeMode = function () {
27477    return this.manipulation.addEdgeMode.apply(this.manipulation, arguments);
27478  };
27479  Network.prototype.editEdgeMode = function () {
27480    return this.manipulation.editEdgeMode.apply(this.manipulation, arguments);
27481  };
27482  Network.prototype.deleteSelected = function () {
27483    return this.manipulation.deleteSelected.apply(this.manipulation, arguments);
27484  };
27485  Network.prototype.getPositions = function () {
27486    return this.nodesHandler.getPositions.apply(this.nodesHandler, arguments);
27487  };
27488  Network.prototype.storePositions = function () {
27489    return this.nodesHandler.storePositions.apply(this.nodesHandler, arguments);
27490  };
27491  Network.prototype.moveNode = function () {
27492    return this.nodesHandler.moveNode.apply(this.nodesHandler, arguments);
27493  };
27494  Network.prototype.getBoundingBox = function () {
27495    return this.nodesHandler.getBoundingBox.apply(this.nodesHandler, arguments);
27496  };
27497  Network.prototype.getConnectedNodes = function (objectId) {
27498    if (this.body.nodes[objectId] !== undefined) {
27499      return this.nodesHandler.getConnectedNodes.apply(this.nodesHandler, arguments);
27500    } else {
27501      return this.edgesHandler.getConnectedNodes.apply(this.edgesHandler, arguments);
27502    }
27503  };
27504  Network.prototype.getConnectedEdges = function () {
27505    return this.nodesHandler.getConnectedEdges.apply(this.nodesHandler, arguments);
27506  };
27507  Network.prototype.startSimulation = function () {
27508    return this.physics.startSimulation.apply(this.physics, arguments);
27509  };
27510  Network.prototype.stopSimulation = function () {
27511    return this.physics.stopSimulation.apply(this.physics, arguments);
27512  };
27513  Network.prototype.stabilize = function () {
27514    return this.physics.stabilize.apply(this.physics, arguments);
27515  };
27516  Network.prototype.getSelection = function () {
27517    return this.selectionHandler.getSelection.apply(this.selectionHandler, arguments);
27518  };
27519  Network.prototype.getSelectedNodes = function () {
27520    return this.selectionHandler.getSelectedNodes.apply(this.selectionHandler, arguments);
27521  };
27522  Network.prototype.getSelectedEdges = function () {
27523    return this.selectionHandler.getSelectedEdges.apply(this.selectionHandler, arguments);
27524  };
27525  Network.prototype.getNodeAt = function () {
27526    var node = this.selectionHandler.getNodeAt.apply(this.selectionHandler, arguments);
27527    if (node !== undefined && node.id !== undefined) {
27528      return node.id;
27529    }
27530    return node;
27531  };
27532  Network.prototype.getEdgeAt = function () {
27533    var edge = this.selectionHandler.getEdgeAt.apply(this.selectionHandler, arguments);
27534    if (edge !== undefined && edge.id !== undefined) {
27535      return edge.id;
27536    }
27537    return edge;
27538  };
27539  Network.prototype.selectNodes = function () {
27540    return this.selectionHandler.selectNodes.apply(this.selectionHandler, arguments);
27541  };
27542  Network.prototype.selectEdges = function () {
27543    return this.selectionHandler.selectEdges.apply(this.selectionHandler, arguments);
27544  };
27545  Network.prototype.unselectAll = function () {
27546    this.selectionHandler.unselectAll.apply(this.selectionHandler, arguments);
27547    this.redraw();
27548  };
27549  Network.prototype.redraw = function () {
27550    return this.renderer.redraw.apply(this.renderer, arguments);
27551  };
27552  Network.prototype.getScale = function () {
27553    return this.view.getScale.apply(this.view, arguments);
27554  };
27555  Network.prototype.getViewPosition = function () {
27556    return this.view.getViewPosition.apply(this.view, arguments);
27557  };
27558  Network.prototype.fit = function () {
27559    return this.view.fit.apply(this.view, arguments);
27560  };
27561  Network.prototype.moveTo = function () {
27562    return this.view.moveTo.apply(this.view, arguments);
27563  };
27564  Network.prototype.focus = function () {
27565    return this.view.focus.apply(this.view, arguments);
27566  };
27567  Network.prototype.releaseNode = function () {
27568    return this.view.releaseNode.apply(this.view, arguments);
27569  };
27570  Network.prototype.getOptionsFromConfigurator = function () {
27571    var options = {};
27572    if (this.configurator) {
27573      options = this.configurator.getOptions.apply(this.configurator);
27574    }
27575    return options;
27576  };
27577
27578  module.exports = Network;
27579
27580/***/ },
27581/* 60 */
27582/***/ function(module, exports, __webpack_require__) {
27583
27584  "use strict";
27585
27586  Object.defineProperty(exports, "__esModule", {
27587    value: true
27588  });
27589
27590  var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
27591
27592  function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
27593
27594  var util = __webpack_require__(1);
27595
27596  /**
27597   * @class Groups
27598   * This class can store groups and options specific for groups.
27599   */
27600
27601  var Groups = (function () {
27602    function Groups() {
27603      _classCallCheck(this, Groups);
27604
27605      this.clear();
27606      this.defaultIndex = 0;
27607      this.groupsArray = [];
27608      this.groupIndex = 0;
27609
27610      this.defaultGroups = [{ border: "#2B7CE9", background: "#97C2FC", highlight: { border: "#2B7CE9", background: "#D2E5FF" }, hover: { border: "#2B7CE9", background: "#D2E5FF" } }, // 0: blue
27611      { border: "#FFA500", background: "#FFFF00", highlight: { border: "#FFA500", background: "#FFFFA3" }, hover: { border: "#FFA500", background: "#FFFFA3" } }, // 1: yellow
27612      { border: "#FA0A10", background: "#FB7E81", highlight: { border: "#FA0A10", background: "#FFAFB1" }, hover: { border: "#FA0A10", background: "#FFAFB1" } }, // 2: red
27613      { border: "#41A906", background: "#7BE141", highlight: { border: "#41A906", background: "#A1EC76" }, hover: { border: "#41A906", background: "#A1EC76" } }, // 3: green
27614      { border: "#E129F0", background: "#EB7DF4", highlight: { border: "#E129F0", background: "#F0B3F5" }, hover: { border: "#E129F0", background: "#F0B3F5" } }, // 4: magenta
27615      { border: "#7C29F0", background: "#AD85E4", highlight: { border: "#7C29F0", background: "#D3BDF0" }, hover: { border: "#7C29F0", background: "#D3BDF0" } }, // 5: purple
27616      { border: "#C37F00", background: "#FFA807", highlight: { border: "#C37F00", background: "#FFCA66" }, hover: { border: "#C37F00", background: "#FFCA66" } }, // 6: orange
27617      { border: "#4220FB", background: "#6E6EFD", highlight: { border: "#4220FB", background: "#9B9BFD" }, hover: { border: "#4220FB", background: "#9B9BFD" } }, // 7: darkblue
27618      { border: "#FD5A77", background: "#FFC0CB", highlight: { border: "#FD5A77", background: "#FFD1D9" }, hover: { border: "#FD5A77", background: "#FFD1D9" } }, // 8: pink
27619      { border: "#4AD63A", background: "#C2FABC", highlight: { border: "#4AD63A", background: "#E6FFE3" }, hover: { border: "#4AD63A", background: "#E6FFE3" } }, // 9: mint
27620
27621      { border: "#990000", background: "#EE0000", highlight: { border: "#BB0000", background: "#FF3333" }, hover: { border: "#BB0000", background: "#FF3333" } }, // 10:bright red
27622
27623      { border: "#FF6000", background: "#FF6000", highlight: { border: "#FF6000", background: "#FF6000" }, hover: { border: "#FF6000", background: "#FF6000" } }, // 12: real orange
27624      { border: "#97C2FC", background: "#2B7CE9", highlight: { border: "#D2E5FF", background: "#2B7CE9" }, hover: { border: "#D2E5FF", background: "#2B7CE9" } }, // 13: blue
27625      { border: "#399605", background: "#255C03", highlight: { border: "#399605", background: "#255C03" }, hover: { border: "#399605", background: "#255C03" } }, // 14: green
27626      { border: "#B70054", background: "#FF007E", highlight: { border: "#B70054", background: "#FF007E" }, hover: { border: "#B70054", background: "#FF007E" } }, // 15: magenta
27627      { border: "#AD85E4", background: "#7C29F0", highlight: { border: "#D3BDF0", background: "#7C29F0" }, hover: { border: "#D3BDF0", background: "#7C29F0" } }, // 16: purple
27628      { border: "#4557FA", background: "#000EA1", highlight: { border: "#6E6EFD", background: "#000EA1" }, hover: { border: "#6E6EFD", background: "#000EA1" } }, // 17: darkblue
27629      { border: "#FFC0CB", background: "#FD5A77", highlight: { border: "#FFD1D9", background: "#FD5A77" }, hover: { border: "#FFD1D9", background: "#FD5A77" } }, // 18: pink
27630      { border: "#C2FABC", background: "#74D66A", highlight: { border: "#E6FFE3", background: "#74D66A" }, hover: { border: "#E6FFE3", background: "#74D66A" } }, // 19: mint
27631
27632      { border: "#EE0000", background: "#990000", highlight: { border: "#FF3333", background: "#BB0000" }, hover: { border: "#FF3333", background: "#BB0000" } } // 20:bright red
27633      ];
27634
27635      this.options = {};
27636      this.defaultOptions = {
27637        useDefaultGroups: true
27638      };
27639      util.extend(this.options, this.defaultOptions);
27640    }
27641
27642    _createClass(Groups, [{
27643      key: "setOptions",
27644      value: function setOptions(options) {
27645        var optionFields = ["useDefaultGroups"];
27646
27647        if (options !== undefined) {
27648          for (var groupName in options) {
27649            if (options.hasOwnProperty(groupName)) {
27650              if (optionFields.indexOf(groupName) === -1) {
27651                var group = options[groupName];
27652                this.add(groupName, group);
27653              }
27654            }
27655          }
27656        }
27657      }
27658    }, {
27659      key: "clear",
27660
27661      /**
27662       * Clear all groups
27663       */
27664      value: function clear() {
27665        this.groups = {};
27666        this.groupsArray = [];
27667      }
27668    }, {
27669      key: "get",
27670
27671      /**
27672       * get group options of a groupname. If groupname is not found, a new group
27673       * is added.
27674       * @param {*} groupname        Can be a number, string, Date, etc.
27675       * @return {Object} group      The created group, containing all group options
27676       */
27677      value: function get(groupname) {
27678        var group = this.groups[groupname];
27679        if (group === undefined) {
27680          if (this.options.useDefaultGroups === false && this.groupsArray.length > 0) {
27681            // create new group
27682            var index = this.groupIndex % this.groupsArray.length;
27683            this.groupIndex++;
27684            group = {};
27685            group.color = this.groups[this.groupsArray[index]];
27686            this.groups[groupname] = group;
27687          } else {
27688            // create new group
27689            var index = this.defaultIndex % this.defaultGroups.length;
27690            this.defaultIndex++;
27691            group = {};
27692            group.color = this.defaultGroups[index];
27693            this.groups[groupname] = group;
27694          }
27695        }
27696
27697        return group;
27698      }
27699    }, {
27700      key: "add",
27701
27702      /**
27703       * Add a custom group style
27704       * @param {String} groupName
27705       * @param {Object} style       An object containing borderColor,
27706       *                             backgroundColor, etc.
27707       * @return {Object} group      The created group object
27708       */
27709      value: function add(groupName, style) {
27710        this.groups[groupName] = style;
27711        this.groupsArray.push(groupName);
27712        return style;
27713      }
27714    }]);
27715
27716    return Groups;
27717  })();
27718
27719  exports["default"] = Groups;
27720  module.exports = exports["default"];
27721
27722/***/ },
27723/* 61 */
27724/***/ function(module, exports, __webpack_require__) {
27725
27726  'use strict';
27727
27728  Object.defineProperty(exports, '__esModule', {
27729    value: true
27730  });
27731
27732  var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
27733
27734  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
27735
27736  function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
27737
27738  var _componentsNode = __webpack_require__(62);
27739
27740  var _componentsNode2 = _interopRequireDefault(_componentsNode);
27741
27742  var _componentsSharedLabel = __webpack_require__(63);
27743
27744  var _componentsSharedLabel2 = _interopRequireDefault(_componentsSharedLabel);
27745
27746  var util = __webpack_require__(1);
27747  var DataSet = __webpack_require__(8);
27748  var DataView = __webpack_require__(10);
27749
27750  var NodesHandler = (function () {
27751    function NodesHandler(body, images, groups, layoutEngine) {
27752      var _this = this;
27753
27754      _classCallCheck(this, NodesHandler);
27755
27756      this.body = body;
27757      this.images = images;
27758      this.groups = groups;
27759      this.layoutEngine = layoutEngine;
27760
27761      // create the node API in the body container
27762      this.body.functions.createNode = this.create.bind(this);
27763
27764      this.nodesListeners = {
27765        add: function add(event, params) {
27766          _this.add(params.items);
27767        },
27768        update: function update(event, params) {
27769          _this.update(params.items, params.data);
27770        },
27771        remove: function remove(event, params) {
27772          _this.remove(params.items);
27773        }
27774      };
27775
27776      this.options = {};
27777      this.defaultOptions = {
27778        borderWidth: 1,
27779        borderWidthSelected: 2,
27780        brokenImage: undefined,
27781        color: {
27782          border: '#2B7CE9',
27783          background: '#97C2FC',
27784          highlight: {
27785            border: '#2B7CE9',
27786            background: '#D2E5FF'
27787          },
27788          hover: {
27789            border: '#2B7CE9',
27790            background: '#D2E5FF'
27791          }
27792        },
27793        fixed: {
27794          x: false,
27795          y: false
27796        },
27797        font: {
27798          color: '#343434',
27799          size: 14, // px
27800          face: 'arial',
27801          background: 'none',
27802          strokeWidth: 0, // px
27803          strokeColor: '#ffffff',
27804          align: 'horizontal'
27805        },
27806        group: undefined,
27807        hidden: false,
27808        icon: {
27809          face: 'FontAwesome', //'FontAwesome',
27810          code: undefined, //'\uf007',
27811          size: 50, //50,
27812          color: '#2B7CE9' //'#aa00ff'
27813        },
27814        image: undefined, // --> URL
27815        label: undefined,
27816        labelHighlightBold: true,
27817        level: undefined,
27818        mass: 1,
27819        physics: true,
27820        scaling: {
27821          min: 10,
27822          max: 30,
27823          label: {
27824            enabled: false,
27825            min: 14,
27826            max: 30,
27827            maxVisible: 30,
27828            drawThreshold: 5
27829          },
27830          customScalingFunction: function customScalingFunction(min, max, total, value) {
27831            if (max === min) {
27832              return 0.5;
27833            } else {
27834              var scale = 1 / (max - min);
27835              return Math.max(0, (value - min) * scale);
27836            }
27837          }
27838        },
27839        shadow: {
27840          enabled: false,
27841          size: 10,
27842          x: 5,
27843          y: 5
27844        },
27845        shape: 'ellipse',
27846        shapeProperties: {
27847          borderDashes: false, // only for borders
27848          borderRadius: 6, // only for box shape
27849          useImageSize: false, // only for image and circularImage shapes
27850          useBorderWithImage: false // only for image shape
27851        },
27852        size: 25,
27853        title: undefined,
27854        value: undefined,
27855        x: undefined,
27856        y: undefined
27857      };
27858      util.extend(this.options, this.defaultOptions);
27859
27860      this.bindEventListeners();
27861    }
27862
27863    _createClass(NodesHandler, [{
27864      key: 'bindEventListeners',
27865      value: function bindEventListeners() {
27866        var _this2 = this;
27867
27868        // refresh the nodes. Used when reverting from hierarchical layout
27869        this.body.emitter.on('refreshNodes', this.refresh.bind(this));
27870        this.body.emitter.on('refresh', this.refresh.bind(this));
27871        this.body.emitter.on('destroy', function () {
27872          delete _this2.body.functions.createNode;
27873          delete _this2.nodesListeners.add;
27874          delete _this2.nodesListeners.update;
27875          delete _this2.nodesListeners.remove;
27876          delete _this2.nodesListeners;
27877        });
27878      }
27879    }, {
27880      key: 'setOptions',
27881      value: function setOptions(options) {
27882        if (options !== undefined) {
27883          _componentsNode2['default'].parseOptions(this.options, options);
27884
27885          // update the shape in all nodes
27886          if (options.shape !== undefined) {
27887            for (var nodeId in this.body.nodes) {
27888              if (this.body.nodes.hasOwnProperty(nodeId)) {
27889                this.body.nodes[nodeId].updateShape();
27890              }
27891            }
27892          }
27893
27894          // update the font in all nodes
27895          if (options.font !== undefined) {
27896            _componentsSharedLabel2['default'].parseOptions(this.options.font, options);
27897            for (var nodeId in this.body.nodes) {
27898              if (this.body.nodes.hasOwnProperty(nodeId)) {
27899                this.body.nodes[nodeId].updateLabelModule();
27900                this.body.nodes[nodeId]._reset();
27901              }
27902            }
27903          }
27904
27905          // update the shape size in all nodes
27906          if (options.size !== undefined) {
27907            for (var nodeId in this.body.nodes) {
27908              if (this.body.nodes.hasOwnProperty(nodeId)) {
27909                this.body.nodes[nodeId]._reset();
27910              }
27911            }
27912          }
27913
27914          // update the state of the letiables if needed
27915          if (options.hidden !== undefined || options.physics !== undefined) {
27916            this.body.emitter.emit('_dataChanged');
27917          }
27918        }
27919      }
27920    }, {
27921      key: 'setData',
27922
27923      /**
27924       * Set a data set with nodes for the network
27925       * @param {Array | DataSet | DataView} nodes         The data containing the nodes.
27926       * @private
27927       */
27928      value: function setData(nodes) {
27929        var _this3 = this;
27930
27931        var doNotEmit = arguments.length <= 1 || arguments[1] === undefined ? false : arguments[1];
27932
27933        var oldNodesData = this.body.data.nodes;
27934
27935        if (nodes instanceof DataSet || nodes instanceof DataView) {
27936          this.body.data.nodes = nodes;
27937        } else if (Array.isArray(nodes)) {
27938          this.body.data.nodes = new DataSet();
27939          this.body.data.nodes.add(nodes);
27940        } else if (!nodes) {
27941          this.body.data.nodes = new DataSet();
27942        } else {
27943          throw new TypeError('Array or DataSet expected');
27944        }
27945
27946        if (oldNodesData) {
27947          // unsubscribe from old dataset
27948          util.forEach(this.nodesListeners, function (callback, event) {
27949            oldNodesData.off(event, callback);
27950          });
27951        }
27952
27953        // remove drawn nodes
27954        this.body.nodes = {};
27955
27956        if (this.body.data.nodes) {
27957          (function () {
27958            // subscribe to new dataset
27959            var me = _this3;
27960            util.forEach(_this3.nodesListeners, function (callback, event) {
27961              me.body.data.nodes.on(event, callback);
27962            });
27963
27964            // draw all new nodes
27965            var ids = _this3.body.data.nodes.getIds();
27966            _this3.add(ids, true);
27967          })();
27968        }
27969
27970        if (doNotEmit === false) {
27971          this.body.emitter.emit('_dataChanged');
27972        }
27973      }
27974    }, {
27975      key: 'add',
27976
27977      /**
27978       * Add nodes
27979       * @param {Number[] | String[]} ids
27980       * @private
27981       */
27982      value: function add(ids) {
27983        var doNotEmit = arguments.length <= 1 || arguments[1] === undefined ? false : arguments[1];
27984
27985        var id = undefined;
27986        var newNodes = [];
27987        for (var i = 0; i < ids.length; i++) {
27988          id = ids[i];
27989          var properties = this.body.data.nodes.get(id);
27990          var node = this.create(properties);
27991          newNodes.push(node);
27992          this.body.nodes[id] = node; // note: this may replace an existing node
27993        }
27994
27995        this.layoutEngine.positionInitially(newNodes);
27996
27997        if (doNotEmit === false) {
27998          this.body.emitter.emit('_dataChanged');
27999        }
28000      }
28001    }, {
28002      key: 'update',
28003
28004      /**
28005       * Update existing nodes, or create them when not yet existing
28006       * @param {Number[] | String[]} ids
28007       * @private
28008       */
28009      value: function update(ids, changedData) {
28010        var nodes = this.body.nodes;
28011        var dataChanged = false;
28012        for (var i = 0; i < ids.length; i++) {
28013          var id = ids[i];
28014          var node = nodes[id];
28015          var data = changedData[i];
28016          if (node !== undefined) {
28017            // update node
28018            dataChanged = node.setOptions(data);
28019          } else {
28020            dataChanged = true;
28021            // create node
28022            node = this.create(data);
28023            nodes[id] = node;
28024          }
28025        }
28026        if (dataChanged === true) {
28027          this.body.emitter.emit('_dataChanged');
28028        } else {
28029          this.body.emitter.emit('_dataUpdated');
28030        }
28031      }
28032    }, {
28033      key: 'remove',
28034
28035      /**
28036       * Remove existing nodes. If nodes do not exist, the method will just ignore it.
28037       * @param {Number[] | String[]} ids
28038       * @private
28039       */
28040      value: function remove(ids) {
28041        var nodes = this.body.nodes;
28042
28043        for (var i = 0; i < ids.length; i++) {
28044          var id = ids[i];
28045          delete nodes[id];
28046        }
28047
28048        this.body.emitter.emit('_dataChanged');
28049      }
28050    }, {
28051      key: 'create',
28052
28053      /**
28054       * create a node
28055       * @param properties
28056       * @param constructorClass
28057       */
28058      value: function create(properties) {
28059        var constructorClass = arguments.length <= 1 || arguments[1] === undefined ? _componentsNode2['default'] : arguments[1];
28060
28061        return new constructorClass(properties, this.body, this.images, this.groups, this.options);
28062      }
28063    }, {
28064      key: 'refresh',
28065      value: function refresh() {
28066        var clearPositions = arguments.length <= 0 || arguments[0] === undefined ? false : arguments[0];
28067
28068        var nodes = this.body.nodes;
28069        for (var nodeId in nodes) {
28070          var node = undefined;
28071          if (nodes.hasOwnProperty(nodeId)) {
28072            node = nodes[nodeId];
28073          }
28074          var data = this.body.data.nodes._data[nodeId];
28075          if (node !== undefined && data !== undefined) {
28076            if (clearPositions === true) {
28077              node.setOptions({ x: null, y: null });
28078            }
28079            node.setOptions({ fixed: false });
28080            node.setOptions(data);
28081          }
28082        }
28083      }
28084    }, {
28085      key: 'getPositions',
28086
28087      /**
28088       * Returns the positions of the nodes.
28089       * @param ids  --> optional, can be array of nodeIds, can be string
28090       * @returns {{}}
28091       */
28092      value: function getPositions(ids) {
28093        var dataArray = {};
28094        if (ids !== undefined) {
28095          if (Array.isArray(ids) === true) {
28096            for (var i = 0; i < ids.length; i++) {
28097              if (this.body.nodes[ids[i]] !== undefined) {
28098                var node = this.body.nodes[ids[i]];
28099                dataArray[ids[i]] = { x: Math.round(node.x), y: Math.round(node.y) };
28100              }
28101            }
28102          } else {
28103            if (this.body.nodes[ids] !== undefined) {
28104              var node = this.body.nodes[ids];
28105              dataArray[ids] = { x: Math.round(node.x), y: Math.round(node.y) };
28106            }
28107          }
28108        } else {
28109          for (var i = 0; i < this.body.nodeIndices.length; i++) {
28110            var node = this.body.nodes[this.body.nodeIndices[i]];
28111            dataArray[this.body.nodeIndices[i]] = { x: Math.round(node.x), y: Math.round(node.y) };
28112          }
28113        }
28114        return dataArray;
28115      }
28116    }, {
28117      key: 'storePositions',
28118
28119      /**
28120       * Load the XY positions of the nodes into the dataset.
28121       */
28122      value: function storePositions() {
28123        // todo: add support for clusters and hierarchical.
28124        var dataArray = [];
28125        var dataset = this.body.data.nodes.getDataSet();
28126
28127        for (var nodeId in dataset._data) {
28128          if (dataset._data.hasOwnProperty(nodeId)) {
28129            var node = this.body.nodes[nodeId];
28130            if (dataset._data[nodeId].x != Math.round(node.x) || dataset._data[nodeId].y != Math.round(node.y)) {
28131              dataArray.push({ id: nodeId, x: Math.round(node.x), y: Math.round(node.y) });
28132            }
28133          }
28134        }
28135        dataset.update(dataArray);
28136      }
28137    }, {
28138      key: 'getBoundingBox',
28139
28140      /**
28141       * get the bounding box of a node.
28142       * @param nodeId
28143       * @returns {j|*}
28144       */
28145      value: function getBoundingBox(nodeId) {
28146        if (this.body.nodes[nodeId] !== undefined) {
28147          return this.body.nodes[nodeId].shape.boundingBox;
28148        }
28149      }
28150    }, {
28151      key: 'getConnectedNodes',
28152
28153      /**
28154       * Get the Ids of nodes connected to this node.
28155       * @param nodeId
28156       * @returns {Array}
28157       */
28158      value: function getConnectedNodes(nodeId) {
28159        var nodeList = [];
28160        if (this.body.nodes[nodeId] !== undefined) {
28161          var node = this.body.nodes[nodeId];
28162          var nodeObj = {}; // used to quickly check if node already exists
28163          for (var i = 0; i < node.edges.length; i++) {
28164            var edge = node.edges[i];
28165            if (edge.toId == nodeId) {
28166              // these are double equals since ids can be numeric or string
28167              if (nodeObj[edge.fromId] === undefined) {
28168                nodeList.push(edge.fromId);
28169                nodeObj[edge.fromId] = true;
28170              }
28171            } else if (edge.fromId == nodeId) {
28172              // these are double equals since ids can be numeric or string
28173              if (nodeObj[edge.toId] === undefined) {
28174                nodeList.push(edge.toId);
28175                nodeObj[edge.toId] = true;
28176              }
28177            }
28178          }
28179        }
28180        return nodeList;
28181      }
28182    }, {
28183      key: 'getConnectedEdges',
28184
28185      /**
28186       * Get the ids of the edges connected to this node.
28187       * @param nodeId
28188       * @returns {*}
28189       */
28190      value: function getConnectedEdges(nodeId) {
28191        var edgeList = [];
28192        if (this.body.nodes[nodeId] !== undefined) {
28193          var node = this.body.nodes[nodeId];
28194          for (var i = 0; i < node.edges.length; i++) {
28195            edgeList.push(node.edges[i].id);
28196          }
28197        } else {
28198          console.log('NodeId provided for getConnectedEdges does not exist. Provided: ', nodeId);
28199        }
28200        return edgeList;
28201      }
28202    }, {
28203      key: 'moveNode',
28204
28205      /**
28206       * Move a node.
28207       * @param String nodeId
28208       * @param Number x
28209       * @param Number y
28210       */
28211      value: function moveNode(nodeId, x, y) {
28212        var _this4 = this;
28213
28214        if (this.body.nodes[nodeId] !== undefined) {
28215          this.body.nodes[nodeId].x = Number(x);
28216          this.body.nodes[nodeId].y = Number(y);
28217          setTimeout(function () {
28218            _this4.body.emitter.emit('startSimulation');
28219          }, 0);
28220        } else {
28221          console.log('Node id supplied to moveNode does not exist. Provided: ', nodeId);
28222        }
28223      }
28224    }]);
28225
28226    return NodesHandler;
28227  })();
28228
28229  exports['default'] = NodesHandler;
28230  module.exports = exports['default'];
28231
28232/***/ },
28233/* 62 */
28234/***/ function(module, exports, __webpack_require__) {
28235
28236  'use strict';
28237
28238  Object.defineProperty(exports, '__esModule', {
28239    value: true
28240  });
28241
28242  var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
28243
28244  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
28245
28246  function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
28247
28248  var _sharedLabel = __webpack_require__(63);
28249
28250  var _sharedLabel2 = _interopRequireDefault(_sharedLabel);
28251
28252  var _nodesShapesBox = __webpack_require__(64);
28253
28254  var _nodesShapesBox2 = _interopRequireDefault(_nodesShapesBox);
28255
28256  var _nodesShapesCircle = __webpack_require__(66);
28257
28258  var _nodesShapesCircle2 = _interopRequireDefault(_nodesShapesCircle);
28259
28260  var _nodesShapesCircularImage = __webpack_require__(68);
28261
28262  var _nodesShapesCircularImage2 = _interopRequireDefault(_nodesShapesCircularImage);
28263
28264  var _nodesShapesDatabase = __webpack_require__(69);
28265
28266  var _nodesShapesDatabase2 = _interopRequireDefault(_nodesShapesDatabase);
28267
28268  var _nodesShapesDiamond = __webpack_require__(70);
28269
28270  var _nodesShapesDiamond2 = _interopRequireDefault(_nodesShapesDiamond);
28271
28272  var _nodesShapesDot = __webpack_require__(72);
28273
28274  var _nodesShapesDot2 = _interopRequireDefault(_nodesShapesDot);
28275
28276  var _nodesShapesEllipse = __webpack_require__(73);
28277
28278  var _nodesShapesEllipse2 = _interopRequireDefault(_nodesShapesEllipse);
28279
28280  var _nodesShapesIcon = __webpack_require__(74);
28281
28282  var _nodesShapesIcon2 = _interopRequireDefault(_nodesShapesIcon);
28283
28284  var _nodesShapesImage = __webpack_require__(75);
28285
28286  var _nodesShapesImage2 = _interopRequireDefault(_nodesShapesImage);
28287
28288  var _nodesShapesSquare = __webpack_require__(76);
28289
28290  var _nodesShapesSquare2 = _interopRequireDefault(_nodesShapesSquare);
28291
28292  var _nodesShapesStar = __webpack_require__(77);
28293
28294  var _nodesShapesStar2 = _interopRequireDefault(_nodesShapesStar);
28295
28296  var _nodesShapesText = __webpack_require__(78);
28297
28298  var _nodesShapesText2 = _interopRequireDefault(_nodesShapesText);
28299
28300  var _nodesShapesTriangle = __webpack_require__(79);
28301
28302  var _nodesShapesTriangle2 = _interopRequireDefault(_nodesShapesTriangle);
28303
28304  var _nodesShapesTriangleDown = __webpack_require__(80);
28305
28306  var _nodesShapesTriangleDown2 = _interopRequireDefault(_nodesShapesTriangleDown);
28307
28308  var _sharedValidator = __webpack_require__(47);
28309
28310  var _sharedValidator2 = _interopRequireDefault(_sharedValidator);
28311
28312  var util = __webpack_require__(1);
28313
28314  /**
28315   * @class Node
28316   * A node. A node can be connected to other nodes via one or multiple edges.
28317   * @param {object}
28317 options An object containing options for the node. All
28318   *                            options are optional, except for the id.
28319   *                              {number} id     Id of the node. Required
28320   *                              {string} label  Text label for the node
28321   *                              {number} x      Horizontal position of the node
28322   *                              {number} y      Vertical position of the node
28323   *                              {string} shape  Node shape, available:
28324   *                                              "database", "circle", "ellipse",
28325   *                                              "box", "image", "text", "dot",
28326   *                                              "star", "triangle", "triangleDown",
28327   *                                              "square", "icon"
28328   *                              {string} image  An image url
28329   *                              {string} title  An title text, can be HTML
28330   *                              {anytype} group A group name or number
28331   * @param {Network.Images} imagelist    A list with images. Only needed
28332   *                                            when the node has an image
28333   * @param {Network.Groups} grouplist    A list with groups. Needed for
28334   *                                            retrieving group options
28335   * @param {Object}               constants    An object with default values for
28336   *                                            example for the color
28337   *
28338   */
28339
28340  var Node = (function () {
28341    function Node(options, body, imagelist, grouplist, globalOptions) {
28342      _classCallCheck(this, Node);
28343
28344      this.options = util.bridgeObject(globalOptions);
28345      this.globalOptions = globalOptions;
28346      this.body = body;
28347
28348      this.edges = []; // all edges connected to this node
28349
28350      // set defaults for the options
28351      this.id = undefined;
28352      this.imagelist = imagelist;
28353      this.grouplist = grouplist;
28354
28355      // state options
28356      this.x = undefined;
28357      this.y = undefined;
28358      this.baseSize = this.options.size;
28359      this.baseFontSize = this.options.font.size;
28360      this.predefinedPosition = false; // used to check if initial fit should just take the range or approximate
28361      this.selected = false;
28362      this.hover = false;
28363
28364      this.labelModule = new _sharedLabel2['default'](this.body, this.options);
28365      this.setOptions(options);
28366    }
28367
28368    _createClass(Node, [{
28369      key: 'attachEdge',
28370
28371      /**
28372       * Attach a edge to the node
28373       * @param {Edge} edge
28374       */
28375      value: function attachEdge(edge) {
28376        if (this.edges.indexOf(edge) === -1) {
28377          this.edges.push(edge);
28378        }
28379      }
28380    }, {
28381      key: 'detachEdge',
28382
28383      /**
28384       * Detach a edge from the node
28385       * @param {Edge} edge
28386       */
28387      value: function detachEdge(edge) {
28388        var index = this.edges.indexOf(edge);
28389        if (index != -1) {
28390          this.edges.splice(index, 1);
28391        }
28392      }
28393    }, {
28394      key: 'setOptions',
28395
28396      /**
28397       * Set or overwrite options for the node
28398       * @param {Object} options an object with options
28399       * @param {Object} constants  and object with default, global options
28400       */
28401      value: function setOptions(options) {
28402        var currentShape = this.options.shape;
28403        if (!options) {
28404          return;
28405        }
28406        // basic options
28407        if (options.id !== undefined) {
28408          this.id = options.id;
28409        }
28410
28411        if (this.id === undefined) {
28412          throw 'Node must have an id';
28413        }
28414
28415        // set these options locally
28416        // clear x and y positions
28417        if (options.x !== undefined) {
28418          if (options.x === null) {
28419            this.x = undefined;this.predefinedPosition = false;
28420          } else {
28421            this.x = parseInt(options.x);this.predefinedPosition = true;
28422          }
28423        }
28424        if (options.y !== undefined) {
28425          if (options.y === null) {
28426            this.y = undefined;this.predefinedPosition = false;
28427          } else {
28428            this.y = parseInt(options.y);this.predefinedPosition = true;
28429          }
28430        }
28431        if (options.size !== undefined) {
28432          this.baseSize = options.size;
28433        }
28434        if (options.value !== undefined) {
28435          options.value = parseFloat(options.value);
28436        }
28437
28438        // copy group options
28439        if (typeof options.group === 'number' || typeof options.group === 'string' && options.group != '') {
28440          var groupObj = this.grouplist.get(options.group);
28441          util.deepExtend(this.options, groupObj);
28442          // the color object needs to be completely defined. Since groups can partially overwrite the colors, we parse it again, just in case.
28443          this.options.color = util.parseColor(this.options.color);
28444        }
28445
28446        // this transforms all shorthands into fully defined options
28447        Node.parseOptions(this.options, options, true, this.globalOptions);
28448
28449        // load the images
28450        if (this.options.image !== undefined) {
28451          if (this.imagelist) {
28452            this.imageObj = this.imagelist.load(this.options.image, this.options.brokenImage, this.id);
28453          } else {
28454            throw 'No imagelist provided';
28455          }
28456        }
28457
28458        this.updateLabelModule();
28459        this.updateShape(currentShape);
28460
28461        if (options.hidden !== undefined || options.physics !== undefined) {
28462          return true;
28463        }
28464        return false;
28465      }
28466    }, {
28467      key: 'updateLabelModule',
28468      value: function updateLabelModule() {
28469        if (this.options.label === undefined || this.options.label === null) {
28470          this.options.label = '';
28471        }
28472        this.labelModule.setOptions(this.options, true);
28473        if (this.labelModule.baseSize !== undefined) {
28474          this.baseFontSize = this.labelModule.baseSize;
28475        }
28476      }
28477    }, {
28478      key: 'updateShape',
28479      value: function updateShape(currentShape) {
28480        if (currentShape === this.options.shape && this.shape) {
28481          this.shape.setOptions(this.options, this.imageObj);
28482        } else {
28483          // choose draw method depending on the shape
28484          switch (this.options.shape) {
28485            case 'box':
28486              this.shape = new _nodesShapesBox2['default'](this.options, this.body, this.labelModule);
28487              break;
28488            case 'circle':
28489              this.shape = new _nodesShapesCircle2['default'](this.options, this.body, this.labelModule);
28490              break;
28491            case 'circularImage':
28492              this.shape = new _nodesShapesCircularImage2['default'](this.options, this.body, this.labelModule, this.imageObj);
28493              break;
28494            case 'database':
28495              this.shape = new _nodesShapesDatabase2['default'](this.options, this.body, this.labelModule);
28496              break;
28497            case 'diamond':
28498              this.shape = new _nodesShapesDiamond2['default'](this.options, this.body, this.labelModule);
28499              break;
28500            case 'dot':
28501              this.shape = new _nodesShapesDot2['default'](this.options, this.body, this.labelModule);
28502              break;
28503            case 'ellipse':
28504              this.shape = new _nodesShapesEllipse2['default'](this.options, this.body, this.labelModule);
28505              break;
28506            case 'icon':
28507              this.shape = new _nodesShapesIcon2['default'](this.options, this.body, this.labelModule);
28508              break;
28509            case 'image':
28510              this.shape = new _nodesShapesImage2['default'](this.options, this.body, this.labelModule, this.imageObj);
28511              break;
28512            case 'square':
28513              this.shape = new _nodesShapesSquare2['default'](this.options, this.body, this.labelModule);
28514              break;
28515            case 'star':
28516              this.shape = new _nodesShapesStar2['default'](this.options, this.body, this.labelModule);
28517              break;
28518            case 'text':
28519              this.shape = new _nodesShapesText2['default'](this.options, this.body, this.labelModule);
28520              break;
28521            case 'triangle':
28522              this.shape = new _nodesShapesTriangle2['default'](this.options, this.body, this.labelModule);
28523              break;
28524            case 'triangleDown':
28525              this.shape = new _nodesShapesTriangleDown2['default'](this.options, this.body, this.labelModule);
28526              break;
28527            default:
28528              this.shape = new _nodesShapesEllipse2['default'](this.options, this.body, this.labelModule);
28529              break;
28530          }
28531        }
28532        this._reset();
28533      }
28534    }, {
28535      key: 'select',
28536
28537      /**
28538       * select this node
28539       */
28540      value: function select() {
28541        this.selected = true;
28542        this._reset();
28543      }
28544    }, {
28545      key: 'unselect',
28546
28547      /**
28548       * unselect this node
28549       */
28550      value: function unselect() {
28551        this.selected = false;
28552        this._reset();
28553      }
28554    }, {
28555      key: '_reset',
28556
28557      /**
28558       * Reset the calculated size of the node, forces it to recalculate its size
28559       * @private
28560       */
28561      value: function _reset() {
28562        this.shape.width = undefined;
28563        this.shape.height = undefined;
28564      }
28565    }, {
28566      key: 'getTitle',
28567
28568      /**
28569       * get the title of this node.
28570       * @return {string} title    The title of the node, or undefined when no title
28571       *                           has been set.
28572       */
28573      value: function getTitle() {
28574        return this.options.title;
28575      }
28576    }, {
28577      key: 'distanceToBorder',
28578
28579      /**
28580       * Calculate the distance to the border of the Node
28581       * @param {CanvasRenderingContext2D}   ctx
28582       * @param {Number} angle        Angle in radians
28583       * @returns {number} distance   Distance to the border in pixels
28584       */
28585      value: function distanceToBorder(ctx, angle) {
28586        return this.shape.distanceToBorder(ctx, angle);
28587      }
28588    }, {
28589      key: 'isFixed',
28590
28591      /**
28592       * Check if this node has a fixed x and y position
28593       * @return {boolean}      true if fixed, false if not
28594       */
28595      value: function isFixed() {
28596        return this.options.fixed.x && this.options.fixed.y;
28597      }
28598    }, {
28599      key: 'isSelected',
28600
28601      /**
28602       * check if this node is selecte
28603       * @return {boolean} selected   True if node is selected, else false
28604       */
28605      value: function isSelected() {
28606        return this.selected;
28607      }
28608    }, {
28609      key: 'getValue',
28610
28611      /**
28612       * Retrieve the value of the node. Can be undefined
28613       * @return {Number} value
28614       */
28615      value: function getValue() {
28616        return this.options.value;
28617      }
28618    }, {
28619      key: 'setValueRange',
28620
28621      /**
28622       * Adjust the value range of the node. The node will adjust it's size
28623       * based on its value.
28624       * @param {Number} min
28625       * @param {Number} max
28626       */
28627      value: function setValueRange(min, max, total) {
28628        if (this.options.value !== undefined) {
28629          var scale = this.options.scaling.customScalingFunction(min, max, total, this.options.value);
28630          var sizeDiff = this.options.scaling.max - this.options.scaling.min;
28631          if (this.options.scaling.label.enabled === true) {
28632            var fontDiff = this.options.scaling.label.max - this.options.scaling.label.min;
28633            this.options.font.size = this.options.scaling.label.min + scale * fontDiff;
28634          }
28635          this.options.size = this.options.scaling.min + scale * sizeDiff;
28636        } else {
28637          this.options.size = this.baseSize;
28638          this.options.font.size = this.baseFontSize;
28639        }
28640      }
28641    }, {
28642      key: 'draw',
28643
28644      /**
28645       * Draw this node in the given canvas
28646       * The 2d context of a HTML canvas can be retrieved by canvas.getContext("2d");
28647       * @param {CanvasRenderingContext2D}   ctx
28648       */
28649      value: function draw(ctx) {
28650        this.shape.draw(ctx, this.x, this.y, this.selected, this.hover);
28651      }
28652    }, {
28653      key: 'updateBoundingBox',
28654
28655      /**
28656       * Update the bounding box of the shape
28657       */
28658      value: function updateBoundingBox(ctx) {
28659        this.shape.updateBoundingBox(this.x, this.y, ctx);
28660      }
28661    }, {
28662      key: 'resize',
28663
28664      /**
28665       * Recalculate the size of this node in the given canvas
28666       * The 2d context of a HTML canvas can be retrieved by canvas.getContext("2d");
28667       * @param {CanvasRenderingContext2D}   ctx
28668       */
28669      value: function resize(ctx) {
28670        this.shape.resize(ctx, this.selected);
28671      }
28672    }, {
28673      key: 'isOverlappingWith',
28674
28675      /**
28676       * Check if this object is overlapping with the provided object
28677       * @param {Object} obj   an object with parameters left, top, right, bottom
28678       * @return {boolean}     True if location is located on node
28679       */
28680      value: function isOverlappingWith(obj) {
28681        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;
28682      }
28683    }, {
28684      key: 'isBoundingBoxOverlappingWith',
28685
28686      /**
28687       * Check if this object is overlapping with the provided object
28688       * @param {Object} obj   an object with parameters left, top, right, bottom
28689       * @return {boolean}     True if location is located on node
28690       */
28691      value: function isBoundingBoxOverlappingWith(obj) {
28692        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;
28693      }
28694    }], [{
28695      key: 'parseOptions',
28696
28697      /**
28698       * This process all possible shorthands in the new options and makes sure that the parentOptions are fully defined.
28699       * Static so it can also be used by the handler.
28700       * @param parentOptions
28701       * @param newOptions
28702       */
28703      value: function parseOptions(parentOptions, newOptions) {
28704        var allowDeletion = arguments.length <= 2 || arguments[2] === undefined ? false : arguments[2];
28705        var globalOptions = arguments.length <= 3 || arguments[3] === undefined ? {} : arguments[3];
28706
28707        var fields = ['color', 'font', 'fixed', 'shadow'];
28708        util.selectiveNotDeepExtend(fields, parentOptions, newOptions, allowDeletion);
28709
28710        // merge the shadow options into the parent.
28711        util.mergeOptions(parentOptions, newOptions, 'shadow', allowDeletion, globalOptions);
28712
28713        // individual shape newOptions
28714        if (newOptions.color !== undefined && newOptions.color !== null) {
28715          var parsedColor = util.parseColor(newOptions.color);
28716          util.fillIfDefined(parentOptions.color, parsedColor);
28717        } else if (allowDeletion === true && newOptions.color === null) {
28718          parentOptions.color = Object.create(globalOptions.color); // this sets the pointer of the option back to the global option.
28719        }
28720
28721        // handle the fixed options
28722        if (newOptions.fixed !== undefined && newOptions.fixed !== null) {
28723          if (typeof newOptions.fixed === 'boolean') {
28724            parentOptions.fixed.x = newOptions.fixed;
28725            parentOptions.fixed.y = newOptions.fixed;
28726          } else {
28727            if (newOptions.fixed.x !== undefined && typeof newOptions.fixed.x === 'boolean') {
28728              parentOptions.fixed.x = newOptions.fixed.x;
28729            }
28730            if (newOptions.fixed.y !== undefined && typeof newOptions.fixed.y === 'boolean') {
28731              parentOptions.fixed.y = newOptions.fixed.y;
28732            }
28733          }
28734        }
28735
28736        // handle the font options
28737        if (newOptions.font !== undefined && newOptions.font !== null) {
28738          _sharedLabel2['default'].parseOptions(parentOptions.font, newOptions);
28739        } else if (allowDeletion === true && newOptions.font === null) {
28740          parentOptions.font = Object.create(globalOptions.font); // this sets the pointer of the option back to the global option.
28741        }
28742
28743        // handle the scaling options, specifically the label part
28744        if (newOptions.scaling !== undefined) {
28745          util.mergeOptions(parentOptions.scaling, newOptions.scaling, 'label', allowDeletion, globalOptions.scaling);
28746        }
28747      }
28748    }]);
28749
28750    return Node;
28751  })();
28752
28753  exports['default'] = Node;
28754  module.exports = exports['default'];
28755
28756/***/ },
28757/* 63 */
28758/***/ function(module, exports, __webpack_require__) {
28759
28760  'use strict';
28761
28762  Object.defineProperty(exports, '__esModule', {
28763    value: true
28764  });
28765
28766  var _slicedToArray = (function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i['return']) _i['return'](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); }
28766 else { throw new TypeError('Invalid attempt to destructure non-iterable instance'); } }; })();
28767
28768  var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
28769
28770  function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
28771
28772  var util = __webpack_require__(1);
28773
28774  var Label = (function () {
28775    function Label(body, options) {
28776      _classCallCheck(this, Label);
28777
28778      this.body = body;
28779
28780      this.pointToSelf = false;
28781      this.baseSize = undefined;
28782      this.fontOptions = {};
28783      this.setOptions(options);
28784      this.size = { top: 0, left: 0, width: 0, height: 0, yLine: 0 }; // could be cached
28785    }
28786
28787    _createClass(Label, [{
28788      key: 'setOptions',
28789      value: function setOptions(options) {
28790        var allowDeletion = arguments.length <= 1 || arguments[1] === undefined ? false : arguments[1];
28791
28792        this.nodeOptions = options;
28793
28794        // We want to keep the font options seperated from the node options.
28795        // The node options have to mirror the globals when they are not overruled.
28796        this.fontOptions = util.deepExtend({}, options.font, true);
28797
28798        if (options.label !== undefined) {
28799          this.labelDirty = true;
28800        }
28801
28802        if (options.font !== undefined) {
28803          Label.parseOptions(this.fontOptions, options, allowDeletion);
28804          if (typeof options.font === 'string') {
28805            this.baseSize = this.fontOptions.size;
28806          } else if (typeof options.font === 'object') {
28807            if (options.font.size !== undefined) {
28808              this.baseSize = options.font.size;
28809            }
28810          }
28811        }
28812      }
28813    }, {
28814      key: 'draw',
28815
28816      /**
28817       * Main function. This is called from anything that wants to draw a label.
28818       * @param ctx
28819       * @param x
28820       * @param y
28821       * @param selected
28822       * @param baseline
28823       */
28824      value: function draw(ctx, x, y, selected) {
28825        var baseline = arguments.length <= 4 || arguments[4] === undefined ? 'middle' : arguments[4];
28826
28827        // if no label, return
28828        if (this.nodeOptions.label === undefined) return;
28829
28830        // check if we have to render the label
28831        var viewFontSize = this.fontOptions.size * this.body.view.scale;
28832        if (this.nodeOptions.label && viewFontSize < this.nodeOptions.scaling.label.drawThreshold - 1) return;
28833
28834        // update the size cache if required
28835        this.calculateLabelSize(ctx, selected, x, y, baseline);
28836
28837        // create the fontfill background
28838        this._drawBackground(ctx);
28839        // draw text
28840        this._drawText(ctx, selected, x, y, baseline);
28841      }
28842    }, {
28843      key: '_drawBackground',
28844
28845      /**
28846       * Draws the label background
28847       * @param {CanvasRenderingContext2D} ctx
28848       * @private
28849       */
28850      value: function _drawBackground(ctx) {
28851        if (this.fontOptions.background !== undefined && this.fontOptions.background !== 'none') {
28852          ctx.fillStyle = this.fontOptions.background;
28853
28854          var lineMargin = 2;
28855
28856          switch (this.fontOptions.align) {
28857            case 'middle':
28858              ctx.fillRect(-this.size.width * 0.5, -this.size.height * 0.5, this.size.width, this.size.height);
28859              break;
28860            case 'top':
28861              ctx.fillRect(-this.size.width * 0.5, -(this.size.height + lineMargin), this.size.width, this.size.height);
28862              break;
28863            case 'bottom':
28864              ctx.fillRect(-this.size.width * 0.5, lineMargin, this.size.width, this.size.height);
28865              break;
28866            default:
28867              ctx.fillRect(this.size.left, this.size.top - 0.5 * lineMargin, this.size.width, this.size.height);
28868              break;
28869          }
28870        }
28871      }
28872    }, {
28873      key: '_drawText',
28874
28875      /**
28876       *
28877       * @param ctx
28878       * @param x
28879       * @param baseline
28880       * @private
28881       */
28882      value: function _drawText(ctx, selected, x, y) {
28883        var baseline = arguments.length <= 4 || arguments[4] === undefined ? 'middle' : arguments[4];
28884
28885        var fontSize = this.fontOptions.size;
28886        var viewFontSize = fontSize * this.body.view.scale;
28887        // this ensures that there will not be HUGE letters on screen by setting an upper limit on the visible text size (regardless of zoomLevel)
28888        if (viewFontSize >= this.nodeOptions.scaling.label.maxVisible) {
28889          fontSize = Number(this.nodeOptions.scaling.label.maxVisible) / this.body.view.scale;
28890        }
28891
28892        var yLine = this.size.yLine;
28893
28894        var _getColor2 = this._getColor(viewFontSize);
28895
28896        var _getColor22 = _slicedToArray(_getColor2, 2);
28897
28898        var fontColor = _getColor22[0];
28899        var strokeColor = _getColor22[1];
28900
28901        var _setAlignment2 = this._setAlignment(ctx, x, yLine, baseline);
28902
28903        var _setAlignment22 = _slicedToArray(_setAlignment2, 2);
28904
28905        x = _setAlignment22[0];
28906        yLine = _setAlignment22[1];
28907
28908        // configure context for drawing the text
28909        ctx.font = (selected && this.nodeOptions.labelHighlightBold ? 'bold ' : '') + fontSize + 'px ' + this.fontOptions.face;
28910        ctx.fillStyle = fontColor;
28911        ctx.textAlign = 'center';
28912
28913        // set the strokeWidth
28914        if (this.fontOptions.strokeWidth > 0) {
28915          ctx.lineWidth = this.fontOptions.strokeWidth;
28916          ctx.strokeStyle = strokeColor;
28917          ctx.lineJoin = 'round';
28918        }
28919
28920        // draw the text
28921        for (var i = 0; i < this.lineCount; i++) {
28922          if (this.fontOptions.strokeWidth > 0) {
28923            ctx.strokeText(this.lines[i], x, yLine);
28924          }
28925          ctx.fillText(this.lines[i], x, yLine);
28926          yLine += fontSize;
28927        }
28928      }
28929    }, {
28930      key: '_setAlignment',
28931      value: function _setAlignment(ctx, x, yLine, baseline) {
28932        // check for label alignment (for edges)
28933        // TODO: make alignment for nodes
28934        if (this.fontOptions.align !== 'horizontal' && this.pointToSelf === false) {
28935          x = 0;
28936          yLine = 0;
28937
28938          var lineMargin = 2;
28939          if (this.fontOptions.align === 'top') {
28940            ctx.textBaseline = 'alphabetic';
28941            yLine -= 2 * lineMargin; // distance from edge, required because we use alphabetic. Alphabetic has less difference between browsers
28942          } else if (this.fontOptions.align === 'bottom') {
28943            ctx.textBaseline = 'hanging';
28944            yLine += 2 * lineMargin; // distance from edge, required because we use hanging. Hanging has less difference between browsers
28945          } else {
28946            ctx.textBaseline = 'middle';
28947          }
28948        } else {
28949          ctx.textBaseline = baseline;
28950        }
28951
28952        return [x, yLine];
28953      }
28954    }, {
28955      key: '_getColor',
28956
28957      /**
28958       * fade in when relative scale is between threshold and threshold - 1.
28959       * If the relative scale would be smaller than threshold -1 the draw function would have returned before coming here.
28960       *
28961       * @param viewFontSize
28962       * @returns {*[]}
28963       * @private
28964       */
28965      value: function _getColor(viewFontSize) {
28966        var fontColor = this.fontOptions.color || '#000000';
28967        var strokeColor = this.fontOptions.strokeColor || '#ffffff';
28968        if (viewFontSize <= this.nodeOptions.scaling.label.drawThreshold) {
28969          var opacity = Math.max(0, Math.min(1, 1 - (this.nodeOptions.scaling.label.drawThreshold - viewFontSize)));
28970          fontColor = util.overrideOpacity(fontColor, opacity);
28971          strokeColor = util.overrideOpacity(strokeColor, opacity);
28972        }
28973        return [fontColor, strokeColor];
28974      }
28975    }, {
28976      key: 'getTextSize',
28977
28978      /**
28979       *
28980       * @param ctx
28981       * @param selected
28982       * @returns {{width: number, height: number}}
28983       */
28984      value: function getTextSize(ctx) {
28985        var selected = arguments.length <= 1 || arguments[1] === undefined ? false : arguments[1];
28986
28987        var size = {
28988          width: this._processLabel(ctx, selected),
28989          height: this.fontOptions.size * this.lineCount,
28990          lineCount: this.lineCount
28991        };
28992        return size;
28993      }
28994    }, {
28995      key: 'calculateLabelSize',
28996
28997      /**
28998       *
28999       * @param ctx
29000       * @param selected
29001       * @param x
29002       * @param y
29003       * @param baseline
29004       */
29005      value: function calculateLabelSize(ctx, selected) {
29006        var x = arguments.length <= 2 || arguments[2] === undefined ? 0 : arguments[2];
29007        var y = arguments.length <= 3 || arguments[3] === undefined ? 0 : arguments[3];
29008        var baseline = arguments.length <= 4 || arguments[4] === undefined ? 'middle' : arguments[4];
29009
29010        if (this.labelDirty === true) {
29011          this.size.width = this._processLabel(ctx, selected);
29012        }
29013        this.size.height = this.fontOptions.size * this.lineCount;
29014        this.size.left = x - this.size.width * 0.5;
29015        this.size.top = y - this.size.height * 0.5;
29016        this.size.yLine = y + (1 - this.lineCount) * 0.5 * this.fontOptions.size;
29017        if (baseline === 'hanging') {
29018          this.size.top += 0.5 * this.fontOptions.size;
29019          this.size.top += 4; // distance from node, required because we use hanging. Hanging has less difference between browsers
29020          this.size.yLine += 4; // distance from node
29021        }
29022
29023        this.labelDirty = false;
29024      }
29025    }, {
29026      key: '_processLabel',
29027
29028      /**
29029       * This calculates the width as well as explodes the label string and calculates the amount of lines.
29030       * @param ctx
29031       * @param selected
29032       * @returns {number}
29033       * @private
29034       */
29035      value: function _processLabel(ctx, selected) {
29036        var width = 0;
29037        var lines = [''];
29038        var lineCount = 0;
29039        if (this.nodeOptions.label !== undefined) {
29040          lines = String(this.nodeOptions.label).split('\n');
29041          lineCount = lines.length;
29042          ctx.font = (selected && this.nodeOptions.labelHighlightBold ? 'bold ' : '') + this.fontOptions.size + 'px ' + this.fontOptions.face;
29043          width = ctx.measureText(lines[0]).width;
29044          for (var i = 1; i < lineCount; i++) {
29045            var lineWidth = ctx.measureText(lines[i]).width;
29046            width = lineWidth > width ? lineWidth : width;
29047          }
29048        }
29049        this.lines = lines;
29050        this.lineCount = lineCount;
29051
29052        return width;
29053      }
29054    }], [{
29055      key: 'parseOptions',
29056      value: function parseOptions(parentOptions, newOptions) {
29057        var allowDeletion = arguments.length <= 2 || arguments[2] === undefined ? false : arguments[2];
29058
29059        if (typeof newOptions.font === 'string') {
29060          var newOptionsArray = newOptions.font.split(' ');
29061          parentOptions.size = newOptionsArray[0].replace('px', '');
29062          parentOptions.face = newOptionsArray[1];
29063          parentOptions.color = newOptionsArray[2];
29064        } else if (typeof newOptions.font === 'object') {
29065          util.fillIfDefined(parentOptions, newOptions.font, allowDeletion);
29066        }
29067        parentOptions.size = Number(parentOptions.size);
29068      }
29069    }]);
29070
29071    return Label;
29072  })();
29073
29074  exports['default'] = Label;
29075  module.exports = exports['default'];
29076
29077/***/ },
29078/* 64 */
29079/***/ function(module, exports, __webpack_require__) {
29080
29081  'use strict';
29082
29083  Object.defineProperty(exports, '__esModule', {
29084    value: true
29085  });
29086
29087  var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
29088
29089  var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; desc = parent = getter = undefined; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } };
29090
29091  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
29092
29093  function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
29094
29095  function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { c
29095onstructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) subClass.__proto__ = superClass; }
29096
29097  var _utilNodeBase = __webpack_require__(65);
29098
29099  var _utilNodeBase2 = _interopRequireDefault(_utilNodeBase);
29100
29101  var Box = (function (_NodeBase) {
29102    _inherits(Box, _NodeBase);
29103
29104    function Box(options, body, labelModule) {
29105      _classCallCheck(this, Box);
29106
29107      _get(Object.getPrototypeOf(Box.prototype), 'constructor', this).call(this, options, body, labelModule);
29108    }
29109
29110    _createClass(Box, [{
29111      key: 'resize',
29112      value: function resize(ctx, selected) {
29113        if (this.width === undefined) {
29114          var margin = 5;
29115          var textSize = this.labelModule.getTextSize(ctx, selected);
29116          this.width = textSize.width + 2 * margin;
29117          this.height = textSize.height + 2 * margin;
29118          this.radius = 0.5 * this.width;
29119        }
29120      }
29121    }, {
29122      key: 'draw',
29123      value: function draw(ctx, x, y, selected, hover) {
29124        this.resize(ctx, selected);
29125        this.left = x - this.width / 2;
29126        this.top = y - this.height / 2;
29127
29128        var borderWidth = this.options.borderWidth;
29129        var selectionLineWidth = this.options.borderWidthSelected || 2 * this.options.borderWidth;
29130
29131        ctx.strokeStyle = selected ? this.options.color.highlight.border : hover ? this.options.color.hover.border : this.options.color.border;
29132        ctx.lineWidth = selected ? selectionLineWidth : borderWidth;
29133        ctx.lineWidth /= this.body.view.scale;
29134        ctx.lineWidth = Math.min(this.width, ctx.lineWidth);
29135
29136        ctx.fillStyle = selected ? this.options.color.highlight.background : hover ? this.options.color.hover.background : this.options.color.background;
29137
29138        var borderRadius = this.options.shapeProperties.borderRadius; // only effective for box
29139        ctx.roundRect(this.left, this.top, this.width, this.height, borderRadius);
29140
29141        // draw shadow if enabled
29142        this.enableShadow(ctx);
29143        // draw the background
29144        ctx.fill();
29145        // disable shadows for other elements.
29146        this.disableShadow(ctx);
29147
29148        //draw dashed border if enabled, save and restore is required for firefox not to crash on unix.
29149        ctx.save();
29150        this.enableBorderDashes(ctx);
29151        //draw the border
29152        ctx.stroke();
29153        //disable dashed border for other elements
29154        this.disableBorderDashes(ctx);
29155        ctx.restore();
29156
29157        this.updateBoundingBox(x, y, ctx, selected);
29158        this.labelModule.draw(ctx, x, y, selected);
29159      }
29160    }, {
29161      key: 'updateBoundingBox',
29162      value: function updateBoundingBox(x, y, ctx, selected) {
29163        this.resize(ctx, selected);
29164        this.left = x - this.width * 0.5;
29165        this.top = y - this.height * 0.5;
29166
29167        this.boundingBox.left = this.left;
29168        this.boundingBox.top = this.top;
29169        this.boundingBox.bottom = this.top + this.height;
29170        this.boundingBox.right = this.left + this.width;
29171      }
29172    }, {
29173      key: 'distanceToBorder',
29174      value: function distanceToBorder(ctx, angle) {
29175        this.resize(ctx);
29176        var a = this.width / 2;
29177        var b = this.height / 2;
29178        var w = Math.sin(angle) * a;
29179        var h = Math.cos(angle) * b;
29180        return a * b / Math.sqrt(w * w + h * h);
29181      }
29182    }]);
29183
29184    return Box;
29185  })(_utilNodeBase2['default']);
29186
29187  exports['default'] = Box;
29188  module.exports = exports['default'];
29189
29190/***/ },
29191/* 65 */
29192/***/ function(module, exports) {
29193
29194  'use strict';
29195
29196  Object.defineProperty(exports, '__esModule', {
29197    value: true
29198  });
29199
29200  var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
29201
29202  function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
29203
29204  var NodeBase = (function () {
29205    function NodeBase(options, body, labelModule) {
29206      _classCallCheck(this, NodeBase);
29207
29208      this.body = body;
29209      this.labelModule = labelModule;
29210      this.setOptions(options);
29211      this.top = undefined;
29212      this.left = undefined;
29213      this.height = undefined;
29214      this.width = undefined;
29215      this.radius = undefined;
29216      this.boundingBox = { top: 0, left: 0, right: 0, bottom: 0 };
29217    }
29218
29219    _createClass(NodeBase, [{
29220      key: 'setOptions',
29221      value: function setOptions(options) {
29222        this.options = options;
29223      }
29224    }, {
29225      key: '_distanceToBorder',
29226      value: function _distanceToBorder(ctx, angle) {
29227        var borderWidth = 1;
29228        this.resize(ctx);
29229        return Math.min(Math.abs(this.width / 2 / Math.cos(angle)), Math.abs(this.height / 2 / Math.sin(angle))) + borderWidth;
29230      }
29231    }, {
29232      key: 'enableShadow',
29233      value: function enableShadow(ctx) {
29234        if (this.options.shadow.enabled === true) {
29235          ctx.shadowColor = 'rgba(0,0,0,0.5)';
29236          ctx.shadowBlur = this.options.shadow.size;
29237          ctx.shadowOffsetX = this.options.shadow.x;
29238          ctx.shadowOffsetY = this.options.shadow.y;
29239        }
29240      }
29241    }, {
29242      key: 'disableShadow',
29243      value: function disableShadow(ctx) {
29244        if (this.options.shadow.enabled === true) {
29245          ctx.shadowColor = 'rgba(0,0,0,0)';
29246          ctx.shadowBlur = 0;
29247          ctx.shadowOffsetX = 0;
29248          ctx.shadowOffsetY = 0;
29249        }
29250      }
29251    }, {
29252      key: 'enableBorderDashes',
29253      value: function enableBorderDashes(ctx) {
29254        if (this.options.shapeProperties.borderDashes !== false) {
29255          if (ctx.setLineDash !== undefined) {
29256            var dashes = this.options.shapeProperties.borderDashes;
29257            if (dashes === true) {
29258              dashes = [5, 15];
29259            }
29260            ctx.setLineDash(dashes);
29261          } else {
29262            console.warn('setLineDash is not supported in this browser. The dashed borders cannot be used.');
29263            this.options.shapeProperties.borderDashes = false;
29264          }
29265        }
29266      }
29267    }, {
29268      key: 'disableBorderDashes',
29269      value: function disableBorderDashes(ctx) {
29270        if (this.options.shapeProperties.borderDashes !== false) {
29271          if (ctx.setLineDash !== undefined) {
29272            ctx.setLineDash([0]);
29273          } else {
29274            console.warn('setLineDash is not supported in this browser. The dashed borders cannot be used.');
29275            this.options.shapeProperties.borderDashes = false;
29276          }
29277        }
29278      }
29279    }]);
29280
29281    return NodeBase;
29282  })();
29283
29284  exports['default'] = NodeBase;
29285  module.exports = exports['default'];
29286
29287/***/ },
29288/* 66 */
29289/***/ function(module, exports, __webpack_require__) {
29290
29291  'use strict';
29292
29293  Object.defineProperty(exports, '__esModule', {
29294    value: true
29295  });
29296
29297  var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
29298
29299  var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; desc = parent = getter = undefined; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } };
29300
29301  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
29302
29303  function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
29304
29305  function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) subClass.__proto__ = superClass; }
29306
29307  var _utilCircleImageBase = __webpack_require__(67);
29308
29309  var _utilCircleImageBase2 = _interopRequireDefault(_utilCircleImageBase);
29310
29311  var Circle = (function (_CircleImageBase) {
29312    _inherits(Circle, _CircleImageBase);
29313
29314    function Circle(options, body, labelModule) {
29315      _classCallCheck(this, Circle);
29316
29317      _get(Object.getPrototypeOf(Circle.prototype), 'constructor', this).call(this, options, body, labelModule);
29318    }
29319
29320    _createClass(Circle, [{
29321      key: 'resize',
29322      value: function resize(ctx, selected) {
29323        if (this.width === undefined) {
29324          var margin = 5;
29325          var textSize = this.labelModule.getTextSize(ctx, selected);
29326          var diameter = Math.max(textSize.width, textSize.height) + 2 * margin;
29327          this.options.size = diameter / 2;
29328
29329          this.width = diameter;
29330          this.height = diameter;
29331          this.radius = 0.5 * this.width;
29332        }
29333      }
29334    }, {
29335      key: 'draw',
29336      value: function draw(ctx, x, y, selected, hover) {
29337        this.resize(ctx, selected);
29338        this.left = x - this.width / 2;
29339        this.top = y - this.height / 2;
29340
29341        this._drawRawCircle(ctx, x, y, selected, hover, this.options.size);
29342
29343        this.boundingBox.top = y - this.options.size;
29344        this.boundingBox.left = x - this.options.size;
29345        this.boundingBox.right = x + this.options.size;
29346        this.boundingBox.bottom = y + this.options.size;
29347
29348        this.updateBoundingBox(x, y);
29349        this.labelModule.draw(ctx, x, y, selected);
29350      }
29351    }, {
29352      key: 'updateBoundingBox',
29353      value: function updateBoundingBox(x, y) {
29354        this.boundingBox.top = y - this.options.size;
29355        this.boundingBox.left = x - this.options.size;
29356        this.boundingBox.right = x + this.options.size;
29357        this.boundingBox.bottom = y + this.options.size;
29358      }
29359    }, {
29360      key: 'distanceToBorder',
29361      value: function distanceToBorder(ctx, angle) {
29362        this.resize(ctx);
29363        var a = this.width / 2;
29364        var b = this.height / 2;
29365        var w = Math.sin(angle) * a;
29366        var h = Math.cos(angle) * b;
29367        return a * b / Math.sqrt(w * w + h * h);
29368      }
29369    }]);
29370
29371    return Circle;
29372  })(_utilCircleImageBase2['default']);
29373
29374  exports['default'] = Circle;
29375  module.exports = exports['default'];
29376
29377/***/ },
29378/* 67 */
29379/***/ function(module, exports, __webpack_require__) {
29380
29381  'use strict';
29382
29383  Object.defineProperty(exports, '__esModule', {
29384    value: true
29385  });
29386
29387  var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
29388
29389  var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; desc = parent = getter = undefined; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } };
29390
29391  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
29392
29393  function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
29394
29395  function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) subClass.__proto__ = superClass; }
29396
29397  var _utilNodeBase = __webpack_require__(65);
29398
29399  var _utilNodeBase2 = _interopRequireDefault(_utilNodeBase);
29400
29401  var CircleImageBase = (function (_NodeBase) {
29402    _inherits(CircleImageBase, _NodeBase);
29403
29404    function CircleImageBase(options, body, labelModule) {
29405      _classCallCheck(this, CircleImageBase);
29406
29407      _get(Object.getPrototypeOf(CircleImageBase.prototype), 'constructor', this).call(this, options, body, labelModule);
29408      this.labelOffset = 0;
29409      this.imageLoaded = false;
29410    }
29411
29412    _createClass(CircleImageBase, [{
29413      key: 'setOptions',
29414      value: function setOptions(options, imageObj) {
29415        this.options = options;
29416        if (imageObj) {
29417          this.imageObj = imageObj;
29418        }
29419      }
29420    }, {
29421      key: '_resizeImage',
29422
29423      /**
29424       * This function resizes the image by the options size when the image has not yet loaded. If the image has loaded, we
29425       * force the update of the size again.
29426       *
29427       * @private
29428       */
29429      value: function _resizeImage() {
29430        var force = false;
29431        if (!this.imageObj.width || !this.imageObj.height) {
29432          // undefined or 0
29433          this.imageLoaded = false;
29434        } else if (this.imageLoaded === false) {
29435          this.imageLoaded = true;
29436          force = true;
29437        }
29438
29439        if (!this.width || !this.height || force === true) {
29440          // undefined or 0
29441          var width, height, ratio;
29442          if (this.imageObj.width && this.imageObj.height) {
29443            // not undefined or 0
29444            width = 0;
29445            height = 0;
29446          }
29447          if (this.options.shapeProperties.useImageSize === false) {
29448            if (this.imageObj.width > this.imageObj.height) {
29449              ratio = this.imageObj.width / this.imageObj.height;
29450              width = this.options.size * 2 * ratio || this.imageObj.width;
29451              height = this.options.size * 2 || this.imageObj.height;
29452            } else {
29453              if (this.imageObj.width && this.imageObj.height) {
29454                // not undefined or 0
29455                ratio = this.imageObj.height / this.imageObj.width;
29456              } else {
29457                ratio = 1;
29458              }
29459              width = this.options.size * 2;
29460              height = this.options.size * 2 * ratio;
29461            }
29462          } else {
29463            // when not using the size property, we use the image size
29464            width = this.imageObj.width;
29465            height = this.imageObj.height;
29466          }
29467          this.width = width;
29468          this.height = height;
29469          this.radius = 0.5 * this.width;
29470        }
29471      }
29472    }, {
29473      key: '_drawRawCircle',
29474      value: function _drawRawCircle(ctx, x, y, selected, hover, size) {
29475        var borderWidth = this.options.borderWidth;
29476        var selectionLineWidth = this.options.borderWidthSelected || 2 * this.options.borderWidth;
29477
29478        ctx.strokeStyle = selected ? this.options.color.highlight.border : hover ? this.options.color.hover.border : this.options.color.border;
29479
29480        ctx.lineWidth = selected ? selectionLineWidth : borderWidth;
29481        ctx.lineWidth *= this.networkScaleInv;
29482        ctx.lineWidth = Math.min(this.width, ctx.lineWidth);
29483        ctx.fillStyle = selected ? this.options.color.highlight.background : hover ? this.options.color.hover.background : this.options.color.background;
29484        ctx.circle(x, y, size);
29485
29486        // draw shadow if enabled
29487        this.enableShadow(ctx);
29488        // draw the background
29489        ctx.fill();
29490        // disable shadows for other elements.
29491        this.disableShadow(ctx);
29492
29493        //draw dashed border if enabled, save and restore is required for firefox not to crash on unix.
29494        ctx.save();
29495        this.enableBorderDashes(ctx);
29496        //draw the border
29497        ctx.stroke();
29498        //disable dashed border for other elements
29499        this.disableBorderDashes(ctx);
29500        ctx.restore();
29501      }
29502    }, {
29503      key: '_drawImageAtPosition',
29504      value: function _drawImageAtPosition(ctx) {
29505        if (this.imageObj.width != 0) {
29506          // draw the image
29507          ctx.globalAlpha = 1.0;
29508
29509          // draw shadow if enabled
29510          this.enableShadow(ctx);
29511
29512          // draw image
29513          ctx.drawImage(this.imageObj, this.left, this.top, this.width, this.height);
29514
29515          // disable shadows for other elements.
29516          this.disableShadow(ctx);
29517        }
29518      }
29519    }, {
29520      key: '_drawImageLabel',
29521      value: function _drawImageLabel(ctx, x, y, selected) {
29522        var yLabel;
29523        var offset = 0;
29524
29525        if (this.height !== undefined) {
29526          offset = this.height * 0.5;
29527          var labelDimensions = this.labelModule.getTextSize(ctx);
29528          if (labelDimensions.lineCount >= 1) {
29529            offset += labelDimensions.height / 2;
29530          }
29531        }
29532
29533        yLabel = y + offset;
29534
29535        if (this.options.label) {
29536          this.labelOffset = offset;
29537        }
29538        this.labelModule.draw(ctx, x, yLabel, selected, 'hanging');
29539      }
29540    }]);
29541
29542    return CircleImageBase;
29543  })(_utilNodeBase2['default']);
29544
29545  exports['default'] = CircleImageBase;
29546  module.exports = exports['default'];
29547
29548/***/ },
29549/* 68 */
29550/***/ function(module, exports, __webpack_require__) {
29551
29552  'use strict';
29553
29554  Object.defineProperty(exports, '__esModule', {
29555    value: true
29556  });
29557
29558  var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
29559
29560  var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; desc = parent = getter = undefined; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } };
29561
29562  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
29563
29564  function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
29565
29566  function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { c
29566onstructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) subClass.__proto__ = superClass; }
29567
29568  var _utilCircleImageBase = __webpack_require__(67);
29569
29570  var _utilCircleImageBase2 = _interopRequireDefault(_utilCircleImageBase);
29571
29572  var CircularImage = (function (_CircleImageBase) {
29573    _inherits(CircularImage, _CircleImageBase);
29574
29575    function CircularImage(options, body, labelModule, imageObj) {
29576      _classCallCheck(this, CircularImage);
29577
29578      _get(Object.getPrototypeOf(CircularImage.prototype), 'constructor', this).call(this, options, body, labelModule);
29579      this.imageObj = imageObj;
29580      this._swapToImageResizeWhenImageLoaded = true;
29581    }
29582
29583    _createClass(CircularImage, [{
29584      key: 'resize',
29585      value: function resize() {
29586        if (this.imageObj.src === undefined || this.imageObj.width === undefined || this.imageObj.height === undefined) {
29587          if (!this.width) {
29588            var diameter = this.options.size * 2;
29589            this.width = diameter;
29590            this.height = diameter;
29591            this._swapToImageResizeWhenImageLoaded = true;
29592            this.radius = 0.5 * this.width;
29593          }
29594        } else {
29595          if (this._swapToImageResizeWhenImageLoaded) {
29596            this.width = undefined;
29597            this.height = undefined;
29598            this._swapToImageResizeWhenImageLoaded = false;
29599          }
29600          this._resizeImage();
29601        }
29602      }
29603    }, {
29604      key: 'draw',
29605      value: function draw(ctx, x, y, selected, hover) {
29606        this.resize();
29607
29608        this.left = x - this.width / 2;
29609        this.top = y - this.height / 2;
29610
29611        var size = Math.min(0.5 * this.height, 0.5 * this.width);
29612
29613        // draw the backgroun circle. IMPORTANT: the stroke in this method is used by the clip method below.
29614        this._drawRawCircle(ctx, x, y, selected, hover, size);
29615
29616        // now we draw in the cicle, we save so we can revert the clip operation after drawing.
29617        ctx.save();
29618        // clip is used to use the stroke in drawRawCircle as an area that we can draw in.
29619        ctx.clip();
29620        // draw the image
29621        this._drawImageAtPosition(ctx);
29622        // restore so we can again draw on the full canvas
29623        ctx.restore();
29624
29625        this._drawImageLabel(ctx, x, y, selected);
29626
29627        this.updateBoundingBox(x, y);
29628      }
29629    }, {
29630      key: 'updateBoundingBox',
29631      value: function updateBoundingBox(x, y) {
29632        this.boundingBox.top = y - this.options.size;
29633        this.boundingBox.left = x - this.options.size;
29634        this.boundingBox.right = x + this.options.size;
29635        this.boundingBox.bottom = y + this.options.size;
29636        this.boundingBox.left = Math.min(this.boundingBox.left, this.labelModule.size.left);
29637        this.boundingBox.right = Math.max(this.boundingBox.right, this.labelModule.size.left + this.labelModule.size.width);
29638        this.boundingBox.bottom = Math.max(this.boundingBox.bottom, this.boundingBox.bottom + this.labelOffset);
29639      }
29640    }, {
29641      key: 'distanceToBorder',
29642      value: function distanceToBorder(ctx, angle) {
29643        this.resize(ctx);
29644        return this._distanceToBorder(ctx, angle);
29645      }
29646    }]);
29647
29648    return CircularImage;
29649  })(_utilCircleImageBase2['default']);
29650
29651  exports['default'] = CircularImage;
29652  module.exports = exports['default'];
29653
29654/***/ },
29655/* 69 */
29656/***/ function(module, exports, __webpack_require__) {
29657
29658  'use strict';
29659
29660  Object.defineProperty(exports, '__esModule', {
29661    value: true
29662  });
29663
29664  var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
29665
29666  var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; desc = parent = getter = undefined; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } };
29667
29668  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
29669
29670  function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
29671
29672  function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { c
29672onstructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) subClass.__proto__ = superClass; }
29673
29674  var _utilNodeBase = __webpack_require__(65);
29675
29676  var _utilNodeBase2 = _interopRequireDefault(_utilNodeBase);
29677
29678  var Database = (function (_NodeBase) {
29679    _inherits(Database, _NodeBase);
29680
29681    function Database(options, body, labelModule) {
29682      _classCallCheck(this, Database);
29683
29684      _get(Object.getPrototypeOf(Database.prototype), 'constructor', this).call(this, options, body, labelModule);
29685    }
29686
29687    _createClass(Database, [{
29688      key: 'resize',
29689      value: function resize(ctx, selected) {
29690        if (this.width === undefined) {
29691          var margin = 5;
29692          var textSize = this.labelModule.getTextSize(ctx, selected);
29693          var size = textSize.width + 2 * margin;
29694          this.width = size;
29695          this.height = size;
29696          this.radius = 0.5 * this.width;
29697        }
29698      }
29699    }, {
29700      key: 'draw',
29701      value: function draw(ctx, x, y, selected, hover) {
29702        this.resize(ctx, selected);
29703        this.left = x - this.width / 2;
29704        this.top = y - this.height / 2;
29705
29706        var borderWidth = this.options.borderWidth;
29707        var selectionLineWidth = this.options.borderWidthSelected || 2 * this.options.borderWidth;
29708
29709        ctx.strokeStyle = selected ? this.options.color.highlight.border : hover ? this.options.color.hover.border : this.options.color.border;
29710        ctx.lineWidth = this.selected ? selectionLineWidth : borderWidth;
29711        ctx.lineWidth *= this.networkScaleInv;
29712        ctx.lineWidth = Math.min(this.width, ctx.lineWidth);
29713
29714        ctx.fillStyle = selected ? this.options.color.highlight.background : hover ? this.options.color.hover.background : this.options.color.background;
29715        ctx.database(x - this.width / 2, y - this.height * 0.5, this.width, this.height);
29716
29717        // draw shadow if enabled
29718        this.enableShadow(ctx);
29719        // draw the background
29720        ctx.fill();
29721        // disable shadows for other elements.
29722        this.disableShadow(ctx);
29723
29724        //draw dashed border if enabled, save and restore is required for firefox not to crash on unix.
29725        ctx.save();
29726        this.enableBorderDashes(ctx);
29727        //draw the border
29728        ctx.stroke();
29729        //disable dashed border for other elements
29730        this.disableBorderDashes(ctx);
29731        ctx.restore();
29732
29733        this.updateBoundingBox(x, y, ctx, selected);
29734        this.labelModule.draw(ctx, x, y, selected);
29735      }
29736    }, {
29737      key: 'updateBoundingBox',
29738      value: function updateBoundingBox(x, y, ctx, selected) {
29739        this.resize(ctx, selected);
29740
29741        this.left = x - this.width * 0.5;
29742        this.top = y - this.height * 0.5;
29743
29744        this.boundingBox.left = this.left;
29745        this.boundingBox.top = this.top;
29746        this.boundingBox.bottom = this.top + this.height;
29747        this.boundingBox.right = this.left + this.width;
29748      }
29749    }, {
29750      key: 'distanceToBorder',
29751      value: function distanceToBorder(ctx, angle) {
29752        this.resize(ctx);
29753        var a = this.width / 2;
29754        var b = this.height / 2;
29755        var w = Math.sin(angle) * a;
29756        var h = Math.cos(angle) * b;
29757        return a * b / Math.sqrt(w * w + h * h);
29758      }
29759    }]);
29760
29761    return Database;
29762  })(_utilNodeBase2['default']);
29763
29764  exports['default'] = Database;
29765  module.exports = exports['default'];
29766
29767/***/ },
29768/* 70 */
29769/***/ function(module, exports, __webpack_require__) {
29770
29771  'use strict';
29772
29773  Object.defineProperty(exports, '__esModule', {
29774    value: true
29775  });
29776
29777  var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
29778
29779  var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; desc = parent = getter = undefined; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } };
29780
29781  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
29782
29783  function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
29784
29785  function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { c
29785onstructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) subClass.__proto__ = superClass; }
29786
29787  var _utilShapeBase = __webpack_require__(71);
29788
29789  var _utilShapeBase2 = _interopRequireDefault(_utilShapeBase);
29790
29791  var Diamond = (function (_ShapeBase) {
29792    _inherits(Diamond, _ShapeBase);
29793
29794    function Diamond(options, body, labelModule) {
29795      _classCallCheck(this, Diamond);
29796
29797      _get(Object.getPrototypeOf(Diamond.prototype), 'constructor', this).call(this, options, body, labelModule);
29798    }
29799
29800    _createClass(Diamond, [{
29801      key: 'resize',
29802      value: function resize(ctx) {
29803        this._resizeShape();
29804      }
29805    }, {
29806      key: 'draw',
29807      value: function draw(ctx, x, y, selected, hover) {
29808        this._drawShape(ctx, 'diamond', 4, x, y, selected, hover);
29809      }
29810    }, {
29811      key: 'distanceToBorder',
29812      value: function distanceToBorder(ctx, angle) {
29813        return this._distanceToBorder(ctx, angle);
29814      }
29815    }]);
29816
29817    return Diamond;
29818  })(_utilShapeBase2['default']);
29819
29820  exports['default'] = Diamond;
29821  module.exports = exports['default'];
29822
29823/***/ },
29824/* 71 */
29825/***/ function(module, exports, __webpack_require__) {
29826
29827  'use strict';
29828
29829  Object.defineProperty(exports, '__esModule', {
29830    value: true
29831  });
29832
29833  var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
29834
29835  var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; desc = parent = getter = undefined; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } };
29836
29837  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
29838
29839  function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
29840
29841  function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) subClass.__proto__ = superClass; }
29842
29843  var _utilNodeBase = __webpack_require__(65);
29844
29845  var _utilNodeBase2 = _interopRequireDefault(_utilNodeBase);
29846
29847  var ShapeBase = (function (_NodeBase) {
29848    _inherits(ShapeBase, _NodeBase);
29849
29850    function ShapeBase(options, body, labelModule) {
29851      _classCallCheck(this, ShapeBase);
29852
29853      _get(Object.getPrototypeOf(ShapeBase.prototype), 'constructor', this).call(this, options, body, labelModule);
29854    }
29855
29856    _createClass(ShapeBase, [{
29857      key: '_resizeShape',
29858      value: function _resizeShape() {
29859        if (this.width === undefined) {
29860          var size = 2 * this.options.size;
29861          this.width = size;
29862          this.height = size;
29863          this.radius = 0.5 * this.width;
29864        }
29865      }
29866    }, {
29867      key: '_drawShape',
29868      value: function _drawShape(ctx, shape, sizeMultiplier, x, y, selected, hover) {
29869        this._resizeShape();
29870
29871        this.left = x - this.width / 2;
29872        this.top = y - this.height / 2;
29873
29874        var borderWidth = this.options.borderWidth;
29875        var selectionLineWidth = this.options.borderWidthSelected || 2 * this.options.borderWidth;
29876
29877        ctx.strokeStyle = selected ? this.options.color.highlight.border : hover ? this.options.color.hover.border : this.options.color.border;
29878        ctx.lineWidth = selected ? selectionLineWidth : borderWidth;
29879        ctx.lineWidth /= this.body.view.scale;
29880        ctx.lineWidth = Math.min(this.width, ctx.lineWidth);
29881        ctx.fillStyle = selected ? this.options.color.highlight.background : hover ? this.options.color.hover.background : this.options.color.background;
29882        ctx[shape](x, y, this.options.size);
29883
29884        // draw shadow if enabled
29885        this.enableShadow(ctx);
29886        // draw the background
29887        ctx.fill();
29888        // disable shadows for other elements.
29889        this.disableShadow(ctx);
29890
29891        //draw dashed border if enabled, save and restore is required for firefox not to crash on unix.
29892        ctx.save();
29893        this.enableBorderDashes(ctx);
29894        //draw the border
29895        ctx.stroke();
29896        //disable dashed border for other elements
29897        this.disableBorderDashes(ctx);
29898        ctx.restore();
29899
29900        if (this.options.label !== undefined) {
29901          var yLabel = y + 0.5 * this.height + 3; // the + 3 is to offset it a bit below the node.
29902          this.labelModule.draw(ctx, x, yLabel, selected, 'hanging');
29903        }
29904
29905        this.updateBoundingBox(x, y);
29906      }
29907    }, {
29908      key: 'updateBoundingBox',
29909      value: function updateBoundingBox(x, y) {
29910        this.boundingBox.top = y - this.options.size;
29911        this.boundingBox.left = x - this.options.size;
29912        this.boundingBox.right = x + this.options.size;
29913        this.boundingBox.bottom = y + this.options.size;
29914
29915        if (this.options.label !== undefined && this.labelModule.size.width > 0) {
29916          this.boundingBox.left = Math.min(this.boundingBox.left, this.labelModule.size.left);
29917          this.boundingBox.right = Math.max(this.boundingBox.right, this.labelModule.size.left + this.labelModule.size.width);
29918          this.boundingBox.bottom = Math.max(this.boundingBox.bottom, this.boundingBox.bottom + this.labelModule.size.height + 3);
29919        }
29920      }
29921    }]);
29922
29923    return ShapeBase;
29924  })(_utilNodeBase2['default']);
29925
29926  exports['default'] = ShapeBase;
29927  module.exports = exports['default'];
29928
29929/***/ },
29930/* 72 */
29931/***/ function(module, exports, __webpack_require__) {
29932
29933  'use strict';
29934
29935  Object.defineProperty(exports, '__esModule', {
29936    value: true
29937  });
29938
29939  var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
29940
29941  var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; desc = parent = getter = undefined; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } };
29942
29943  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
29944
29945  function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
29946
29947  function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) subClass.__proto__ = superClass; }
29948
29949  var _utilShapeBase = __webpack_require__(71);
29950
29951  var _utilShapeBase2 = _interopRequireDefault(_utilShapeBase);
29952
29953  var Dot = (function (_ShapeBase) {
29954    _inherits(Dot, _ShapeBase);
29955
29956    function Dot(options, body, labelModule) {
29957      _classCallCheck(this, Dot);
29958
29959      _get(Object.getPrototypeOf(Dot.prototype), 'constructor', this).call(this, options, body, labelModule);
29960    }
29961
29962    _createClass(Dot, [{
29963      key: 'resize',
29964      value: function resize(ctx) {
29965        this._resizeShape();
29966      }
29967    }, {
29968      key: 'draw',
29969      value: function draw(ctx, x, y, selected, hover) {
29970        this._drawShape(ctx, 'circle', 2, x, y, selected, hover);
29971      }
29972    }, {
29973      key: 'distanceToBorder',
29974      value: function distanceToBorder(ctx, angle) {
29975        this.resize(ctx);
29976        return this.options.size + this.options.borderWidth;
29977      }
29978    }]);
29979
29980    return Dot;
29981  })(_utilShapeBase2['default']);
29982
29983  exports['default'] = Dot;
29984  module.exports = exports['default'];
29985
29986/***/ },
29987/* 73 */
29988/***/ function(module, exports, __webpack_require__) {
29989
29990  'use strict';
29991
29992  Object.defineProperty(exports, '__esModule', {
29993    value: true
29994  });
29995
29996  var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
29997
29998  var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; desc = parent = getter = undefined; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } };
29999
30000  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
30001
30002  function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
30003
30004  function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { c
30004onstructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) subClass.__proto__ = superClass; }
30005
30006  var _utilNodeBase = __webpack_require__(65);
30007
30008  var _utilNodeBase2 = _interopRequireDefault(_utilNodeBase);
30009
30010  var Ellipse = (function (_NodeBase) {
30011    _inherits(Ellipse, _NodeBase);
30012
30013    function Ellipse(options, body, labelModule) {
30014      _classCallCheck(this, Ellipse);
30015
30016      _get(Object.getPrototypeOf(Ellipse.prototype), 'constructor', this).call(this, options, body, labelModule);
30017    }
30018
30019    _createClass(Ellipse, [{
30020      key: 'resize',
30021      value: function resize(ctx, selected) {
30022        if (this.width === undefined) {
30023          var textSize = this.labelModule.getTextSize(ctx, selected);
30024
30025          this.width = textSize.width * 1.5;
30026          this.height = textSize.height * 2;
30027          if (this.width < this.height) {
30028            this.width = this.height;
30029          }
30030          this.radius = 0.5 * this.width;
30031        }
30032      }
30033    }, {
30034      key: 'draw',
30035      value: function draw(ctx, x, y, selected, hover) {
30036        this.resize(ctx, selected);
30037        this.left = x - this.width * 0.5;
30038        this.top = y - this.height * 0.5;
30039
30040        var borderWidth = this.options.borderWidth;
30041        var selectionLineWidth = this.options.borderWidthSelected || 2 * this.options.borderWidth;
30042
30043        ctx.strokeStyle = selected ? this.options.color.highlight.border : hover ? this.options.color.hover.border : this.options.color.border;
30044
30045        ctx.lineWidth = selected ? selectionLineWidth : borderWidth;
30046        ctx.lineWidth /= this.body.view.scale;
30047        ctx.lineWidth = Math.min(this.width, ctx.lineWidth);
30048
30049        ctx.fillStyle = selected ? this.options.color.highlight.background : hover ? this.options.color.hover.background : this.options.color.background;
30050        ctx.ellipse(this.left, this.top, this.width, this.height);
30051
30052        // draw shadow if enabled
30053        this.enableShadow(ctx);
30054        // draw the background
30055        ctx.fill();
30056        // disable shadows for other elements.
30057        this.disableShadow(ctx);
30058
30059        //draw dashed border if enabled, save and restore is required for firefox not to crash on unix.
30060        ctx.save();
30061        this.enableBorderDashes(ctx);
30062        //draw the border
30063        ctx.stroke();
30064        //disable dashed border for other elements
30065        this.disableBorderDashes(ctx);
30066        ctx.restore();
30067
30068        this.updateBoundingBox(x, y, ctx, selected);
30069        this.labelModule.draw(ctx, x, y, selected);
30070      }
30071    }, {
30072      key: 'updateBoundingBox',
30073      value: function updateBoundingBox(x, y, ctx, selected) {
30074        this.resize(ctx, selected); // just in case
30075
30076        this.left = x - this.width * 0.5;
30077        this.top = y - this.height * 0.5;
30078
30079        this.boundingBox.left = this.left;
30080        this.boundingBox.top = this.top;
30081        this.boundingBox.bottom = this.top + this.height;
30082        this.boundingBox.right = this.left + this.width;
30083      }
30084    }, {
30085      key: 'distanceToBorder',
30086      value: function distanceToBorder(ctx, angle) {
30087        this.resize(ctx);
30088        var a = this.width * 0.5;
30089        var b = this.height * 0.5;
30090        var w = Math.sin(angle) * a;
30091        var h = Math.cos(angle) * b;
30092        return a * b / Math.sqrt(w * w + h * h);
30093      }
30094    }]);
30095
30096    return Ellipse;
30097  })(_utilNodeBase2['default']);
30098
30099  exports['default'] = Ellipse;
30100  module.exports = exports['default'];
30101
30102/***/ },
30103/* 74 */
30104/***/ function(module, exports, __webpack_require__) {
30105
30106  'use strict';
30107
30108  Object.defineProperty(exports, '__esModule', {
30109    value: true
30110  });
30111
30112  var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
30113
30114  var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; desc = parent = getter = undefined; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } };
30115
30116  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
30117
30118  function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
30119
30120  function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { c
30120onstructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) subClass.__proto__ = superClass; }
30121
30122  var _utilNodeBase = __webpack_require__(65);
30123
30124  var _utilNodeBase2 = _interopRequireDefault(_utilNodeBase);
30125
30126  var Icon = (function (_NodeBase) {
30127    _inherits(Icon, _NodeBase);
30128
30129    function Icon(options, body, labelModule) {
30130      _classCallCheck(this, Icon);
30131
30132      _get(Object.getPrototypeOf(Icon.prototype), 'constructor', this).call(this, options, body, labelModule);
30133    }
30134
30135    _createClass(Icon, [{
30136      key: 'resize',
30137      value: function resize(ctx) {
30138        if (this.width === undefined) {
30139          var margin = 5;
30140          var iconSize = {
30141            width: Number(this.options.icon.size),
30142            height: Number(this.options.icon.size)
30143          };
30144          this.width = iconSize.width + 2 * margin;
30145          this.height = iconSize.height + 2 * margin;
30146          this.radius = 0.5 * this.width;
30147        }
30148      }
30149    }, {
30150      key: 'draw',
30151      value: function draw(ctx, x, y, selected, hover) {
30152        this.resize(ctx);
30153        this.options.icon.size = this.options.icon.size || 50;
30154
30155        this.left = x - this.width * 0.5;
30156        this.top = y - this.height * 0.5;
30157        this._icon(ctx, x, y, selected);
30158
30159        if (this.options.label !== undefined) {
30160          var iconTextSpacing = 5;
30161          this.labelModule.draw(ctx, x, y + this.height * 0.5 + iconTextSpacing, selected);
30162        }
30163
30164        this.updateBoundingBox(x, y);
30165      }
30166    }, {
30167      key: 'updateBoundingBox',
30168      value: function updateBoundingBox(x, y) {
30169        this.boundingBox.top = y - this.options.icon.size * 0.5;
30170        this.boundingBox.left = x - this.options.icon.size * 0.5;
30171        this.boundingBox.right = x + this.options.icon.size * 0.5;
30172        this.boundingBox.bottom = y + this.options.icon.size * 0.5;
30173
30174        if (this.options.label !== undefined && this.labelModule.size.width > 0) {
30175          var iconTextSpacing = 5;
30176          this.boundingBox.left = Math.min(this.boundingBox.left, this.labelModule.size.left);
30177          this.boundingBox.right = Math.max(this.boundingBox.right, this.labelModule.size.left + this.labelModule.size.width);
30178          this.boundingBox.bottom = Math.max(this.boundingBox.bottom, this.boundingBox.bottom + this.labelModule.size.height + iconTextSpacing);
30179        }
30180      }
30181    }, {
30182      key: '_icon',
30183      value: function _icon(ctx, x, y, selected) {
30184        var iconSize = Number(this.options.icon.size);
30185
30186        if (this.options.icon.code !== undefined) {
30187          ctx.font = (selected ? 'bold ' : '') + iconSize + 'px ' + this.options.icon.face;
30188
30189          // draw icon
30190          ctx.fillStyle = this.options.icon.color || 'black';
30191          ctx.textAlign = 'center';
30192          ctx.textBaseline = 'middle';
30193
30194          // draw shadow if enabled
30195          this.enableShadow(ctx);
30196          ctx.fillText(this.options.icon.code, x, y);
30197
30198          // disable shadows for other elements.
30199          this.disableShadow(ctx);
30200        } else {
30201          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.');
30202        }
30203      }
30204    }, {
30205      key: 'distanceToBorder',
30206      value: function distanceToBorder(ctx, angle) {
30207        return this._distanceToBorder(ctx, angle);
30208      }
30209    }]);
30210
30211    return Icon;
30212  })(_utilNodeBase2['default']);
30213
30214  exports['default'] = Icon;
30215  module.exports = exports['default'];
30216
30217/***/ },
30218/* 75 */
30219/***/ function(module, exports, __webpack_require__) {
30220
30221  'use strict';
30222
30223  Object.defineProperty(exports, '__esModule', {
30224    value: true
30225  });
30226
30227  var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
30228
30229  var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; desc = parent = getter = undefined; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } };
30230
30231  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
30232
30233  function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
30234
30235  function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { c
30235onstructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) subClass.__proto__ = superClass; }
30236
30237  var _utilCircleImageBase = __webpack_require__(67);
30238
30239  var _utilCircleImageBase2 = _interopRequireDefault(_utilCircleImageBase);
30240
30241  var Image = (function (_CircleImageBase) {
30242    _inherits(Image, _CircleImageBase);
30243
30244    function Image(options, body, labelModule, imageObj) {
30245      _classCallCheck(this, Image);
30246
30247      _get(Object.getPrototypeOf(Image.prototype), 'constructor', this).call(this, options, body, labelModule);
30248      this.imageObj = imageObj;
30249    }
30250
30251    _createClass(Image, [{
30252      key: 'resize',
30253      value: function resize() {
30254        this._resizeImage();
30255      }
30256    }, {
30257      key: 'draw',
30258      value: function draw(ctx, x, y, selected, hover) {
30259        this.resize();
30260        this.left = x - this.width / 2;
30261        this.top = y - this.height / 2;
30262
30263        if (this.options.shapeProperties.useBorderWithImage === true) {
30264          var borderWidth = this.options.borderWidth;
30265
30266          var selectionLineWidth = this.options.borderWidthSelected || 2 * this.options.borderWidth;
30267
30268          ctx.beginPath();
30269
30270          // setup the line properties.
30271          ctx.strokeStyle = selected ? this.options.color.highlight.border : hover ? this.options.color.hover.border : this.options.color.border;
30272          ctx.lineWidth = selected ? selectionLineWidth : borderWidth;
30273          ctx.lineWidth /= this.body.view.scale;
30274          ctx.lineWidth = Math.min(this.width, ctx.lineWidth);
30275
30276          // set a fillstyle
30277          ctx.fillStyle = selected ? this.options.color.highlight.background : hover ? this.options.color.hover.background : this.options.color.background;
30278
30279          // 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
30280          ctx.rect(this.left - 0.5 * ctx.lineWidth, this.top - 0.5 * ctx.lineWidth, this.width + ctx.lineWidth, this.height + ctx.lineWidth);
30281          ctx.fill();
30282
30283          //draw dashed border if enabled, save and restore is required for firefox not to crash on unix.
30284          ctx.save();
30285          this.enableBorderDashes(ctx);
30286          //draw the border
30287          ctx.stroke();
30288          //disable dashed border for other elements
30289          this.disableBorderDashes(ctx);
30290          ctx.restore();
30291
30292          ctx.closePath();
30293        }
30294
30295        this._drawImageAtPosition(ctx);
30296
30297        this._drawImageLabel(ctx, x, y, selected || hover);
30298
30299        this.updateBoundingBox(x, y);
30300      }
30301    }, {
30302      key: 'updateBoundingBox',
30303      value: function updateBoundingBox(x, y) {
30304        this.resize();
30305        this.left = x - this.width / 2;
30306        this.top = y - this.height / 2;
30307
30308        this.boundingBox.top = this.top;
30309        this.boundingBox.left = this.left;
30310        this.boundingBox.right = this.left + this.width;
30311        this.boundingBox.bottom = this.top + this.height;
30312
30313        if (this.options.label !== undefined && this.labelModule.size.width > 0) {
30314          this.boundingBox.left = Math.min(this.boundingBox.left, this.labelModule.size.left);
30315          this.boundingBox.right = Math.max(this.boundingBox.right, this.labelModule.size.left + this.labelModule.size.width);
30316          this.boundingBox.bottom = Math.max(this.boundingBox.bottom, this.boundingBox.bottom + this.labelOffset);
30317        }
30318      }
30319    }, {
30320      key: 'distanceToBorder',
30321      value: function distanceToBorder(ctx, angle) {
30322        this.resize(ctx);
30323        var a = this.width / 2;
30324        var b = this.height / 2;
30325        var w = Math.sin(angle) * a;
30326        var h = Math.cos(angle) * b;
30327        return a * b / Math.sqrt(w * w + h * h);
30328      }
30329    }]);
30330
30331    return Image;
30332  })(_utilCircleImageBase2['default']);
30333
30334  exports['default'] = Image;
30335  module.exports = exports['default'];
30336
30337/***/ },
30338/* 76 */
30339/***/ function(module, exports, __webpack_require__) {
30340
30341  'use strict';
30342
30343  Object.defineProperty(exports, '__esModule', {
30344    value: true
30345  });
30346
30347  var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
30348
30349  var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; desc = parent = getter = undefined; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } };
30350
30351  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
30352
30353  function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
30354
30355  function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { c
vendor: 6,091 bytes, lines 30355-30467
30355onstructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) subClass.__proto__ = superClass; }
30356
30357  var _utilShapeBase = __webpack_require__(71);
30358
30359  var _utilShapeBase2 = _interopRequireDefault(_utilShapeBase);
30360
30361  var Square = (function (_ShapeBase) {
30362    _inherits(Square, _ShapeBase);
30363
30364    function Square(options, body, labelModule) {
30365      _classCallCheck(this, Square);
30366
30367      _get(Object.getPrototypeOf(Square.prototype), 'constructor', this).call(this, options, body, labelModule);
30368    }
30369
30370    _createClass(Square, [{
30371      key: 'resize',
30372      value: function resize() {
30373        this._resizeShape();
30374      }
30375    }, {
30376      key: 'draw',
30377      value: function draw(ctx, x, y, selected, hover) {
30378        this._drawShape(ctx, 'square', 2, x, y, selected, hover);
30379      }
30380    }, {
30381      key: 'distanceToBorder',
30382      value: function distanceToBorder(ctx, angle) {
30383        return this._distanceToBorder(ctx, angle);
30384      }
30385    }]);
30386
30387    return Square;
30388  })(_utilShapeBase2['default']);
30389
30390  exports['default'] = Square;
30391  module.exports = exports['default'];
30392
30393/***/ },
30394/* 77 */
30395/***/ function(module, exports, __webpack_require__) {
30396
30397  'use strict';
30398
30399  Object.defineProperty(exports, '__esModule', {
30400    value: true
30401  });
30402
30403  var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
30404
30405  var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; desc = parent = getter = undefined; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } };
30406
30407  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
30408
30409  function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
30410
30411  function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) subClass.__proto__ = superClass; }
30412
30413  var _utilShapeBase = __webpack_require__(71);
30414
30415  var _utilShapeBase2 = _interopRequireDefault(_utilShapeBase);
30416
30417  var Star = (function (_ShapeBase) {
30418    _inherits(Star, _ShapeBase);
30419
30420    function Star(options, body, labelModule) {
30421      _classCallCheck(this, Star);
30422
30423      _get(Object.getPrototypeOf(Star.prototype), 'constructor', this).call(this, options, body, labelModule);
30424    }
30425
30426    _createClass(Star, [{
30427      key: 'resize',
30428      value: function resize(ctx) {
30429        this._resizeShape();
30430      }
30431    }, {
30432      key: 'draw',
30433      value: function draw(ctx, x, y, selected, hover) {
30434        this._drawShape(ctx, 'star', 4, x, y, selected, hover);
30435      }
30436    }, {
30437      key: 'distanceToBorder',
30438      value: function distanceToBorder(ctx, angle) {
30439        return this._distanceToBorder(ctx, angle);
30440      }
30441    }]);
30442
30443    return Star;
30444  })(_utilShapeBase2['default']);
30445
30446  exports['default'] = Star;
30447  module.exports = exports['default'];
30448
30449/***/ },
30450/* 78 */
30451/***/ function(module, exports, __webpack_require__) {
30452
30453  'use strict';
30454
30455  Object.defineProperty(exports, '__esModule', {
30456    value: true
30457  });
30458
30459  var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
30460
30461  var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; desc = parent = getter = undefined; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } };
30462
30463  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
30464
30465  function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
30466
30467  function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { c
30467onstructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) subClass.__proto__ = superClass; }
30468
30469  var _utilNodeBase = __webpack_require__(65);
30470
30471  var _utilNodeBase2 = _interopRequireDefault(_utilNodeBase);
30472
30473  var Text = (function (_NodeBase) {
30474    _inherits(Text, _NodeBase);
30475
30476    function Text(options, body, labelModule) {
30477      _classCallCheck(this, Text);
30478
30479      _get(Object.getPrototypeOf(Text.prototype), 'constructor', this).call(this, options, body, labelModule);
30480    }
30481
30482    _createClass(Text, [{
30483      key: 'resize',
30484      value: function resize(ctx, selected) {
30485        if (this.width === undefined) {
30486          var margin = 5;
30487          var textSize = this.labelModule.getTextSize(ctx, selected);
30488          this.width = textSize.width + 2 * margin;
30489          this.height = textSize.height + 2 * margin;
30490          this.radius = 0.5 * this.width;
30491        }
30492      }
30493    }, {
30494      key: 'draw',
30495      value: function draw(ctx, x, y, selected, hover) {
30496        this.resize(ctx, selected || hover);
30497        this.left = x - this.width / 2;
30498        this.top = y - this.height / 2;
30499
30500        // draw shadow if enabled
30501        this.enableShadow(ctx);
30502        this.labelModule.draw(ctx, x, y, selected || hover);
30503
30504        // disable shadows for other elements.
30505        this.disableShadow(ctx);
30506
30507        this.updateBoundingBox(x, y, ctx, selected);
30508      }
30509    }, {
30510      key: 'updateBoundingBox',
30511      value: function updateBoundingBox(x, y, ctx, selected) {
30512        this.resize(ctx, selected);
30513
30514        this.left = x - this.width / 2;
30515        this.top = y - this.height / 2;
30516
30517        this.boundingBox.top = this.top;
30518        this.boundingBox.left = this.left;
30519        this.boundingBox.right = this.left + this.width;
30520        this.boundingBox.bottom = this.top + this.height;
30521      }
30522    }, {
30523      key: 'distanceToBorder',
30524      value: function distanceToBorder(ctx, angle) {
30525        return this._distanceToBorder(ctx, angle);
30526      }
30527    }]);
30528
30529    return Text;
30530  })(_utilNodeBase2['default']);
30531
30532  exports['default'] = Text;
30533  module.exports = exports['default'];
30534
30535/***/ },
30536/* 79 */
30537/***/ function(module, exports, __webpack_require__) {
30538
30539  'use strict';
30540
30541  Object.defineProperty(exports, '__esModule', {
30542    value: true
30543  });
30544
30545  var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
30546
30547  var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; desc = parent = getter = undefined; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } };
30548
30549  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
30550
30551  function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
30552
30553  function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) subClass.__proto__ = superClass; }
30554
30555  var _utilShapeBase = __webpack_require__(71);
30556
30557  var _utilShapeBase2 = _interopRequireDefault(_utilShapeBase);
30558
vendor: 5,088 bytes, lines 30559-30666
30559  var Triangle = (function (_ShapeBase) {
30560    _inherits(Triangle, _ShapeBase);
30561
30562    function Triangle(options, body, labelModule) {
30563      _classCallCheck(this, Triangle);
30564
30565      _get(Object.getPrototypeOf(Triangle.prototype), 'constructor', this).call(this, options, body, labelModule);
30566    }
30567
30568    _createClass(Triangle, [{
30569      key: 'resize',
30570      value: function resize(ctx) {
30571        this._resizeShape();
30572      }
30573    }, {
30574      key: 'draw',
30575      value: function draw(ctx, x, y, selected, hover) {
30576        this._drawShape(ctx, 'triangle', 3, x, y, selected, hover);
30577      }
30578    }, {
30579      key: 'distanceToBorder',
30580      value: function distanceToBorder(ctx, angle) {
30581        return this._distanceToBorder(ctx, angle);
30582      }
30583    }]);
30584
30585    return Triangle;
30586  })(_utilShapeBase2['default']);
30587
30588  exports['default'] = Triangle;
30589  module.exports = exports['default'];
30590
30591/***/ },
30592/* 80 */
30593/***/ function(module, exports, __webpack_require__) {
30594
30595  'use strict';
30596
30597  Object.defineProperty(exports, '__esModule', {
30598    value: true
30599  });
30600
30601  var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
30602
30603  var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; desc = parent = getter = undefined; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } };
30604
30605  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
30606
30607  function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
30608
30609  function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) subClass.__proto__ = superClass; }
30610
30611  var _utilShapeBase = __webpack_require__(71);
30612
30613  var _utilShapeBase2 = _interopRequireDefault(_utilShapeBase);
30614
30615  var TriangleDown = (function (_ShapeBase) {
30616    _inherits(TriangleDown, _ShapeBase);
30617
30618    function TriangleDown(options, body, labelModule) {
30619      _classCallCheck(this, TriangleDown);
30620
30621      _get(Object.getPrototypeOf(TriangleDown.prototype), 'constructor', this).call(this, options, body, labelModule);
30622    }
30623
30624    _createClass(TriangleDown, [{
30625      key: 'resize',
30626      value: function resize(ctx) {
30627        this._resizeShape();
30628      }
30629    }, {
30630      key: 'draw',
30631      value: function draw(ctx, x, y, selected, hover) {
30632        this._drawShape(ctx, 'triangleDown', 3, x, y, selected, hover);
30633      }
30634    }, {
30635      key: 'distanceToBorder',
30636      value: function distanceToBorder(ctx, angle) {
30637        return this._distanceToBorder(ctx, angle);
30638      }
30639    }]);
30640
30641    return TriangleDown;
30642  })(_utilShapeBase2['default']);
30643
30644  exports['default'] = TriangleDown;
30645  module.exports = exports['default'];
30646
30647/***/ },
30648/* 81 */
30649/***/ function(module, exports, __webpack_require__) {
30650
30651  'use strict';
30652
30653  Object.defineProperty(exports, '__esModule', {
30654    value: true
30655  });
30656
30657  var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
30658
30659  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
30660
30661  function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
30662
30663  var _componentsEdge = __webpack_require__(82);
30664
30665  var _componentsEdge2 = _interopRequireDefault(_componentsEdge);
30666
30667  var _componentsSharedLabel = __webpack_require__(63);
30668
30669  var _componentsSharedLabel2 = _interopRequireDefault(_componentsSharedLabel);
30670
30671  var util = __webpack_require__(1);
30672  var DataSet = __webpack_require__(8);
30673  var DataView = __webpack_require__(10);
30674
30675  var EdgesHandler = (function () {
30676    function EdgesHandler(body, images, groups) {
30677      var _this = this;
30678
30679      _classCallCheck(this, EdgesHandler);
30680
30681      this.body = body;
30682      this.images = images;
30683      this.groups = groups;
30684
30685      // create the edge API in the body container
30686      this.body.functions.createEdge = this.create.bind(this);
30687
30688      this.edgesListeners = {
30689        add: function add(event, params) {
30690          _this.add(params.items);
30691        },
30692        update: function update(event, params) {
30693          _this.update(params.items);
30694        },
30695        remove: function remove(event, params) {
30696          _this.remove(params.items);
30697        }
30698      };
30699
30700      this.options = {};
30701      this.defaultOptions = {
30702        arrows: {
30703          to: { enabled: false, scaleFactor: 1 }, // boolean / {arrowScaleFactor:1} / {enabled: false, arrowScaleFactor:1}
30704          middle: { enabled: false, scaleFactor: 1 },
30705          from: { enabled: false, scaleFactor: 1 }
30706        },
30707        color: {
30708          color: '#848484',
30709          highlight: '#848484',
30710          hover: '#848484',
30711          inherit: 'from',
30712          opacity: 1.0
30713        },
30714        dashes: false,
30715        font: {
30716          color: '#343434',
30717          size: 14, // px
30718          face: 'arial',
30719          background: 'none',
30720          strokeWidth: 2, // px
30721          strokeColor: '#ffffff',
30722          align: 'horizontal'
30723        },
30724        hidden: false,
30725        hoverWidth: 1.5,
30726        label: undefined,
30727        labelHighlightBold: true,
30728        length: undefined,
30729        physics: true,
30730        scaling: {
30731          min: 1,
30732          max: 15,
30733          label: {
30734            enabled: true,
30735            min: 14,
30736            max: 30,
30737            maxVisible: 30,
30738            drawThreshold: 5
30739          },
30740          customScalingFunction: function customScalingFunction(min, max, total, value) {
30741            if (max === min) {
30742              return 0.5;
30743            } else {
30744              var scale = 1 / (max - min);
30745              return Math.max(0, (value - min) * scale);
30746            }
30747          }
30748        },
30749        selectionWidth: 1.5,
30750        selfReferenceSize: 20,
30751        shadow: {
30752          enabled: false,
30753          size: 10,
30754          x: 5,
30755          y: 5
30756        },
30757        smooth: {
30758          enabled: true,
30759          type: 'dynamic',
30760          forceDirection: 'none',
30761          roundness: 0.5
30762        },
30763        title: undefined,
30764        width: 1,
30765        value: undefined
30766      };
30767
30768      util.extend(this.options, this.defaultOptions);
30769
30770      this.bindEventListeners();
30771    }
30772
30773    _createClass(EdgesHandler, [{
30774      key: 'bindEventListeners',
30775      value: function bindEventListeners() {
30776        var _this2 = this;
30777
30778        // this allows external modules to force all dynamic curves to turn static.
30779        this.body.emitter.on('_forceDisableDynamicCurves', function (type) {
30780          if (type === 'dynamic') {
30781            type = 'continuous';
30782          }
30783          var emitChange = false;
30784          for (var edgeId in _this2.body.edges) {
30785            if (_this2.body.edges.hasOwnProperty(edgeId)) {
30786              var edge = _this2.body.edges[edgeId];
30787              var edgeData = _this2.body.data.edges._data[edgeId];
30788
30789              // only forcilby remove the smooth curve if the data has been set of the edge has the smooth curves defined.
30790              // this is because a change in the global would not affect these curves.
30791              if (edgeData !== undefined) {
30792                var edgeOptions = edgeData.smooth;
30793                if (edgeOptions !== undefined) {
30794                  if (edgeOptions.enabled === true && edgeOptions.type === 'dynamic') {
30795                    if (type === undefined) {
30796                      edge.setOptions({ smooth: false });
30797                    } else {
30798                      edge.setOptions({ smooth: { type: type } });
30799                    }
30800                    emitChange = true;
30801                  }
30802                }
30803              }
30804            }
30805          }
30806          if (emitChange === true) {
30807            _this2.body.emitter.emit('_dataChanged');
30808          }
30809        });
30810
30811        // this is called when options of EXISTING nodes or edges have changed.
30812        this.body.emitter.on('_dataUpdated', function () {
30813          _this2.reconnectEdges();
30814          _this2.markAllEdgesAsDirty();
30815        });
30816
30817        // refresh the edges. Used when reverting from hierarchical layout
30818        this.body.emitter.on('refreshEdges', this.refresh.bind(this));
30819        this.body.emitter.on('refresh', this.refresh.bind(this));
30820        this.body.emitter.on('destroy', function () {
30821          delete _this2.body.functions.createEdge;
30822          delete _this2.edgesListeners.add;
30823          delete _this2.edgesListeners.update;
30824          delete _this2.edgesListeners.remove;
30825          delete _this2.edgesListeners;
30826        });
30827      }
30828    }, {
30829      key: 'setOptions',
30830      value: function setOptions(options) {
30831        if (options !== undefined) {
30832          // use the parser from the Edge class to fill in all shorthand notations
30833          _componentsEdge2['default'].parseOptions(this.options, options);
30834
30835          // hanlde multiple input cases for color
30836          if (options.color !== undefined) {
30837            this.markAllEdgesAsDirty();
30838          }
30839
30840          // update smooth settings in all edges
30841          var dataChanged = false;
30842          if (options.smooth !== undefined) {
30843            for (var edgeId in this.body.edges) {
30844              if (this.body.edges.hasOwnProperty(edgeId)) {
30845                dataChanged = this.body.edges[edgeId].updateEdgeType() || dataChanged;
30846              }
30847            }
30848          }
30849
30850          // update fonts in all edges
30851          if (options.font !== undefined) {
30852            // use the parser from the Label class to fill in all shorthand notations
30853            _componentsSharedLabel2['default'].parseOptions(this.options.font, options);
30854            for (var edgeId in this.body.edges) {
30855              if (this.body.edges.hasOwnProperty(edgeId)) {
30856                this.body.edges[edgeId].updateLabelModule();
30857              }
30858            }
30859          }
30860
30861          // update the state of the variables if needed
30862          if (options.hidden !== undefined || options.physics !== undefined || dataChanged === true) {
30863            this.body.emitter.emit('_dataChanged');
30864          }
30865        }
30866      }
30867    }, {
30868      key: 'setData',
30869
30870      /**
30871       * Load edges by reading the data table
30872       * @param {Array | DataSet | DataView} edges    The data containing the edges.
30873       * @private
30874       * @private
30875       */
30876      value: function setData(edges) {
30877        var _this3 = this;
30878
30879        var doNotEmit = arguments.length <= 1 || arguments[1] === undefined ? false : arguments[1];
30880
30881        var oldEdgesData = this.body.data.edges;
30882
30883        if (edges instanceof DataSet || edges instanceof DataView) {
30884          this.body.data.edges = edges;
30885        } else if (Array.isArray(edges)) {
30886          this.body.data.edges = new DataSet();
30887          this.body.data.edges.add(edges);
30888        } else if (!edges) {
30889          this.body.data.edges = new DataSet();
30890        } else {
30891          throw new TypeError('Array or DataSet expected');
30892        }
30893
30894        // TODO: is this null or undefined or false?
30895        if (oldEdgesData) {
30896          // unsubscribe from old dataset
30897          util.forEach(this.edgesListeners, function (callback, event) {
30898            oldEdgesData.off(event, callback);
30899          });
30900        }
30901
30902        // remove drawn edges
30903        this.body.edges = {};
30904
30905        // TODO: is this null or undefined or false?
30906        if (this.body.data.edges) {
30907          // subscribe to new dataset
30908          util.forEach(this.edgesListeners, function (callback, event) {
30909            _this3.body.data.edges.on(event, callback);
30910          });
30911
30912          // draw all new nodes
30913          var ids = this.body.data.edges.getIds();
30914          this.add(ids, true);
30915        }
30916
30917        if (doNotEmit === false) {
30918          this.body.emitter.emit('_dataChanged');
30919        }
30920      }
30921    }, {
30922      key: 'add',
30923
30924      /**
30925       * Add edges
30926       * @param {Number[] | String[]} ids
30927       * @private
30928       */
30929      value: function add(ids) {
30930        var doNotEmit = arguments.length <= 1 || arguments[1] === undefined ? false : arguments[1];
30931
30932        var edges = this.body.edges;
30933        var edgesData = this.body.data.edges;
30934
30935        for (var i = 0; i < ids.length; i++) {
30936          var id = ids[i];
30937
30938          var oldEdge = edges[id];
30939          if (oldEdge) {
30940            oldEdge.disconnect();
30941          }
30942
30943          var data = edgesData.get(id, { 'showInternalIds': true });
30944          edges[id] = this.create(data);
30945        }
30946
30947        if (doNotEmit === false) {
30948          this.body.emitter.emit('_dataChanged');
30949        }
30950      }
30951    }, {
30952      key: 'update',
30953
30954      /**
30955       * Update existing edges, or create them when not yet existing
30956       * @param {Number[] | String[]} ids
30957       * @private
30958       */
30959      value: function update(ids) {
30960        var edges = this.body.edges;
30961        var edgesData = this.body.data.edges;
30962        var dataChanged = false;
30963        for (var i = 0; i < ids.length; i++) {
30964          var id = ids[i];
30965          var data = edgesData.get(id);
30966          var edge = edges[id];
30967          if (edge !== undefined) {
30968            // update edge
30969            edge.disconnect();
30970            dataChanged = edge.setOptions(data) || dataChanged; // if a support node is added, data can be changed.
30971            edge.connect();
30972          } else {
30973            // create edge
30974            this.body.edges[id] = this.create(data);
30975            dataChanged = true;
30976          }
30977        }
30978
30979        if (dataChanged === true) {
30980          this.body.emitter.emit('_dataChanged');
30981        } else {
30982          this.body.emitter.emit('_dataUpdated');
30983        }
30984      }
30985    }, {
30986      key: 'remove',
30987
30988      /**
30989       * Remove existing edges. Non existing ids will be ignored
30990       * @param {Number[] | String[]} ids
30991       * @private
30992       */
30993      value: function remove(ids) {
30994        var edges = this.body.edges;
30995        for (var i = 0; i < ids.length; i++) {
30996          var id = ids[i];
30997          var edge = edges[id];
30998          if (edge !== undefined) {
30999            edge.cleanup();
31000            edge.disconnect();
31001            delete edges[id];
31002          }
31003        }
31004
31005        this.body.emitter.emit('_dataChanged');
31006      }
31007    }, {
31008      key: 'refresh',
31009      value: function refresh() {
31010        var edges = this.body.edges;
31011        for (var edgeId in edges) {
31012          var edge = undefined;
31013          if (edges.hasOwnProperty(edgeId)) {
31014            edge = edges[edgeId];
31015          }
31016          var data = this.body.data.edges._data[edgeId];
31017          if (edge !== undefined && data !== undefined) {
31018            edge.setOptions(data);
31019          }
31020        }
31021      }
31022    }, {
31023      key: 'create',
31024      value: function create(properties) {
31025        return new _componentsEdge2['default'](properties, this.body, this.options);
31026      }
31027    }, {
31028      key: 'markAllEdgesAsDirty',
31029      value: function markAllEdgesAsDirty() {
31030        for (var edgeId in this.body.edges) {
31031          this.body.edges[edgeId].edgeType.colorDirty = true;
31032        }
31033      }
31034    }, {
31035      key: 'reconnectEdges',
31036
31037      /**
31038       * Reconnect all edges
31039       * @private
31040       */
31041      value: function reconnectEdges() {
31042        var id;
31043        var nodes = this.body.nodes;
31044        var edges = this.body.edges;
31045
31046        for (id in nodes) {
31047          if (nodes.hasOwnProperty(id)) {
31048            nodes[id].edges = [];
31049          }
31050        }
31051
31052        for (id in edges) {
31053          if (edges.hasOwnProperty(id)) {
31054            var edge = edges[id];
31055            edge.from = null;
31056            edge.to = null;
31057            edge.connect();
31058          }
31059        }
31060      }
31061    }, {
31062      key: 'getConnectedNodes',
31063      value: function getConnectedNodes(edgeId) {
31064        var nodeList = [];
31065        if (this.body.edges[edgeId] !== undefined) {
31066          var edge = this.body.edges[edgeId];
31067          if (edge.fromId) {
31068            nodeList.push(edge.fromId);
31069          }
31070          if (edge.toId) {
31071            nodeList.push(edge.toId);
31072          }
31073        }
31074        return nodeList;
31075      }
31076    }]);
31077
31078    return EdgesHandler;
31079  })();
31080
31081  exports['default'] = EdgesHandler;
31082  module.exports = exports['default'];
31083
31084/***/ },
31085/* 82 */
31086/***/ function(module, exports, __webpack_require__) {
31087
31088  'use strict';
31089
31090  Object.defineProperty(exports, '__esModule', {
31091    value: true
31092  });
31093
31094  var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
31095
31096  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
31097
31098  function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
31099
31100  var _sharedLabel = __webpack_require__(63);
31101
31102  var _sharedLabel2 = _interopRequireDefault(_sharedLabel);
31103
31104  var _edgesCubicBezierEdge = __webpack_require__(83);
31105
31106  var _edgesCubicBezierEdge2 = _interopRequireDefault(_edgesCubicBezierEdge);
31107
31108  var _edgesBezierEdgeDynamic = __webpack_require__(87);
31109
31110  var _edgesBezierEdgeDynamic2 = _interopRequireDefault(_edgesBezierEdgeDynamic);
31111
31112  var _edgesBezierEdgeStatic = __webpack_require__(88);
31113
31114  var _edgesBezierEdgeStatic2 = _interopRequireDefault(_edgesBezierEdgeStatic);
31115
31116  var _edgesStraightEdge = __webpack_require__(89);
31117
31118  var _edgesStraightEdge2 = _interopRequireDefault(_edgesStraightEdge);
31119
31120  var util = __webpack_require__(1);
31121
31122  /**
31123   * @class Edge
31124   *
31125   * A edge connects two nodes
31126   * @param {Object} properties     Object with options. Must contain
31127   *                                At least options from and to.
31128   *                                Available options: from (number),
31129   *                                to (number), label (string, color (string),
31130   *                                width (number), style (string),
31131   *                                length (number), title (string)
31132   * @param {Network} network       A Network object, used to find and edge to
31133   *                                nodes.
31134   * @param {Object} constants      An object with default values for
31135   *                                example for the color
31136   */
31137
31138  var Edge = (function () {
31139    function Edge(options, body, globalOptions) {
31140      _classCallCheck(this, Edge);
31141
31142      if (body === undefined) {
31143        throw 'No body provided';
31144      }
31145      this.options = util.bridgeObject(globalOptions);
31146      this.globalOptions = globalOptions;
31147      this.body = body;
31148
31149      // initialize variables
31150      this.id = undefined;
31151      this.fromId = undefined;
31152      this.toId = undefined;
31153      this.selected = false;
31154      this.hover = false;
31155      this.labelDirty = true;
31156      this.colorDirty = true;
31157
31158      this.baseWidth = this.options.width;
31159      this.baseFontSize = this.options.font.size;
31160
31161      this.from = undefined; // a node
31162      this.to = undefined; // a node
31163
31164      this.edgeType = undefined;
31165
31166      this.connected = false;
31167
31168      this.labelModule = new _sharedLabel2['default'](this.body, this.options);
31169
31170      this.setOptions(options);
31171    }
31172
31173    _createClass(Edge, [{
31174      key: 'setOptions',
31175
31176      /**
31177       * Set or overwrite options for the edge
31178       * @param {Object} options  an object with options
31179       * @param doNotEmit
31180       */
31181      value: function setOptions(options) {
31182        if (!options) {
31183          return;
31184        }
31185        this.colorDirty = true;
31186
31187        Edge.parseOptions(this.options, options, true, this.globalOptions);
31188
31189        if (options.id !== undefined) {
31190          this.id = options.id;
31191        }
31192        if (options.from !== undefined) {
31193          this.fromId = options.from;
31194        }
31195        if (options.to !== undefined) {
31196          this.toId = options.to;
31197        }
31198        if (options.title !== undefined) {
31199          this.title = options.title;
31200        }
31201        if (options.value !== undefined) {
31202          options.value = parseFloat(options.value);
31203        }
31204
31205        // update label Module
31206        this.updateLabelModule();
31207
31208        var dataChanged = this.updateEdgeType();
31209
31210        // if anything has been updates, reset the selection width and the hover width
31211        this._setInteractionWidths();
31212
31213        // A node is connected when it has a from and to node that both exist in the network.body.nodes.
31214        this.connect();
31215
31216        if (options.hidden !== undefined || options.physics !== undefined) {
31217          dataChanged = true;
31218        }
31219
31220        return dataChanged;
31221      }
31222    }, {
31223      key: 'updateLabelModule',
31224
31225      /**
31226       * update the options in the label module
31227       */
31228      value: function updateLabelModule() {
31229        this.labelModule.setOptions(this.options, true);
31230        if (this.labelModule.baseSize !== undefined) {
31231          this.baseFontSize = this.labelModule.baseSize;
31232        }
31233      }
31234    }, {
31235      key: 'updateEdgeType',
31236
31237      /**
31238       * update the edge type, set the options
31239       * @returns {boolean}
31240       */
31241      value: function updateEdgeType() {
31242        var dataChanged = false;
31243        var changeInType = true;
31244        var smooth = this.options.smooth;
31245        if (this.edgeType !== undefined) {
31246          if (this.edgeType instanceof _edgesBezierEdgeDynamic2['default'] && smooth.enabled === true && smooth.type === 'dynamic') {
31247            changeInType = false;
31248          }
31249          if (this.edgeType instanceof _edgesCubicBezierEdge2['default'] && smooth.enabled === true && smooth.type === 'cubicBezier') {
31250            changeInType = false;
31251          }
31252          if (this.edgeType instanceof _edgesBezierEdgeStatic2['default'] && smooth.enabled === true && smooth.type !== 'dynamic' && smooth.type !== 'cubicBezier') {
31253            changeInType = false;
31254          }
31255          if (this.edgeType instanceof _edgesStraightEdge2['default'] && smooth.enabled === false) {
31256            changeInType = false;
31257          }
31258
31259          if (changeInType === true) {
31260            dataChanged = this.cleanup();
31261          }
31262        }
31263
31264        if (changeInType === true) {
31265          if (this.options.smooth.enabled === true) {
31266            if (this.options.smooth.type === 'dynamic') {
31267              dataChanged = true;
31268              this.edgeType = new _edgesBezierEdgeDynamic2['default'](this.options, this.body, this.labelModule);
31269            } else if (this.options.smooth.type === 'cubicBezier') {
31270              this.edgeType = new _edgesCubicBezierEdge2['default'](this.options, this.body, this.labelModule);
31271            } else {
31272              this.edgeType = new _edgesBezierEdgeStatic2['default'](this.options, this.body, this.labelModule);
31273            }
31274          } else {
31275            this.edgeType = new _edgesStraightEdge2['default'](this.options, this.body, this.labelModule);
31276          }
31277        } else {
31278          // if nothing changes, we just set the options.
31279          this.edgeType.setOptions(this.options);
31280        }
31281
31282        return dataChanged;
31283      }
31284    }, {
31285      key: 'connect',
31286
31287      /**
31288       * Connect an edge to its nodes
31289       */
31290      value: function connect() {
31291        this.disconnect();
31292
31293        this.from = this.body.nodes[this.fromId] || undefined;
31294        this.to = this.body.nodes[this.toId] || undefined;
31295        this.connected = this.from !== undefined && this.to !== undefined;
31296
31297        if (this.connected === true) {
31298          this.from.attachEdge(this);
31299          this.to.attachEdge(this);
31300        } else {
31301          if (this.from) {
31302            this.from.detachEdge(this);
31303          }
31304          if (this.to) {
31305            this.to.detachEdge(this);
31306          }
31307        }
31308
31309        this.edgeType.connect();
31310      }
31311    }, {
31312      key: 'disconnect',
31313
31314      /**
31315       * Disconnect an edge from its nodes
31316       */
31317      value: function disconnect() {
31318        if (this.from) {
31319          this.from.detachEdge(this);
31320          this.from = undefined;
31321        }
31322        if (this.to) {
31323          this.to.detachEdge(this);
31324          this.to = undefined;
31325        }
31326
31327        this.connected = false;
31328      }
31329    }, {
31330      key: 'getTitle',
31331
31332      /**
31333       * get the title of this edge.
31334       * @return {string} title    The title of the edge, or undefined when no title
31335       *                           has been set.
31336       */
31337      value: function getTitle() {
31338        return this.title;
31339      }
31340    }, {
31341      key: 'isSelected',
31342
31343      /**
31344       * check if this node is selecte
31345       * @return {boolean} selected   True if node is selected, else false
31346       */
31347      value: function isSelected() {
31348        return this.selected;
31349      }
31350    }, {
31351      key: 'getValue',
31352
31353      /**
31354       * Retrieve the value of the edge. Can be undefined
31355       * @return {Number} value
31356       */
31357      value: function getValue() {
31358        return this.options.value;
31359      }
31360    }, {
31361      key: 'setValueRange',
31362
31363      /**
31364       * Adjust the value range of the edge. The edge will adjust it's width
31365       * based on its value.
31366       * @param {Number} min
31367       * @param {Number} max
31368       * @param total
31369       */
31370      value: function setValueRange(min, max, total) {
31371        if (this.options.value !== undefined) {
31372          var scale = this.options.scaling.customScalingFunction(min, max, total, this.options.value);
31373          var widthDiff = this.options.scaling.max - this.options.scaling.min;
31374          if (this.options.scaling.label.enabled === true) {
31375            var fontDiff = this.options.scaling.label.max - this.options.scaling.label.min;
31376            this.options.font.size = this.options.scaling.label.min + scale * fontDiff;
31377          }
31378          this.options.width = this.options.scaling.min + scale * widthDiff;
31379        } else {
31380          this.options.width = this.baseWidth;
31381          this.options.font.size = this.baseFontSize;
31382        }
31383
31384        this._setInteractionWidths();
31385      }
31386    }, {
31387      key: '_setInteractionWidths',
31388      value: function _setInteractionWidths() {
31389        if (typeof this.options.hoverWidth === 'function') {
31390          this.edgeType.hoverWidth = this.options.hoverWidth(this.options.width);
31391        } else {
31392          this.edgeType.hoverWidth = this.options.hoverWidth + this.options.width;
31393        }
31394
31395        if (typeof this.options.selectionWidth === 'function') {
31396          this.edgeType.selectionWidth = this.options.selectionWidth(this.options.width);
31397        } else {
31398          this.edgeType.selectionWidth = this.options.selectionWidth + this.options.width;
31399        }
31400      }
31401    }, {
31402      key: 'draw',
31403
31404      /**
31405       * Redraw a edge
31406       * Draw this edge in the given canvas
31407       * The 2d context of a HTML canvas can be retrieved by canvas.getContext("2d");
31408       * @param {CanvasRenderingContext2D}   ctx
31409       */
31410      value: function draw(ctx) {
31411        var via = this.edgeType.drawLine(ctx, this.selected, this.hover);
31412        this.drawArrows(ctx, via);
31413        this.drawLabel(ctx, via);
31414      }
31415    }, {
31416      key: 'drawArrows',
31417      value: function drawArrows(ctx, viaNode) {
31418        if (this.options.arrows.from.enabled === true) {
31419          this.edgeType.drawArrowHead(ctx, 'from', viaNode, this.selected, this.hover);
31420        }
31421        if (this.options.arrows.middle.enabled === true) {
31422          this.edgeType.drawArrowHead(ctx, 'middle', viaNode, this.selected, this.hover);
31423        }
31424        if (this.options.arrows.to.enabled === true) {
31425          this.edgeType.drawArrowHead(ctx, 'to', viaNode, this.selected, this.hover);
31426        }
31427      }
31428    }, {
31429      key: 'drawLabel',
31430      value: function drawLabel(ctx, viaNode) {
31431        if (this.options.label !== undefined) {
31432          // set style
31433          var node1 = this.from;
31434          var node2 = this.to;
31435          var selected = this.from.selected || this.to.selected || this.selected;
31436          if (node1.id != node2.id) {
31437            this.labelModule.pointToSelf = false;
31438            var point = this.edgeType.getPoint(0.5, viaNode);
31439            ctx.save();
31440
31441            // if the label has to be rotated:
31442            if (this.options.font.align !== 'horizontal') {
31443              this.labelModule.calculateLabelSize(ctx, selected, point.x, point.y);
31444              ctx.translate(point.x, this.labelModule.size.yLine);
31445              this._rotateForLabelAlignment(ctx);
31446            }
31447
31448            // draw the label
31449            this.labelModule.draw(ctx, point.x, point.y, selected);
31450            ctx.restore();
31451          } else {
31452            // Ignore the orientations.
31453            this.labelModule.pointToSelf = true;
31454            var x, y;
31455            var radius = this.options.selfReferenceSize;
31456            if (node1.shape.width > node1.shape.height) {
31457              x = node1.x + node1.shape.width * 0.5;
31458              y = node1.y - radius;
31459            } else {
31460              x = node1.x + radius;
31461              y = node1.y - node1.shape.height * 0.5;
31462            }
31463            point = this._pointOnCircle(x, y, radius, 0.125);
31464            this.labelModule.draw(ctx, point.x, point.y, selected);
31465          }
31466        }
31467      }
31468    }, {
31469      key: 'isOverlappingWith',
31470
31471      /**
31472       * Check if this object is overlapping with the provided object
31473       * @param {Object} obj   an object with parameters left, top
31474       * @return {boolean}     True if location is located on the edge
31475       */
31476      value: function isOverlappingWith(obj) {
31477        if (this.connected) {
31478          var distMax = 10;
31479          var xFrom = this.from.x;
31480          var yFrom = this.from.y;
31481          var xTo = this.to.x;
31482          var yTo = this.to.y;
31483          var xObj = obj.left;
31484          var yObj = obj.top;
31485
31486          var dist = this.edgeType.getDistanceToEdge(xFrom, yFrom, xTo, yTo, xObj, yObj);
31487
31488          return dist < distMax;
31489        } else {
31490          return false;
31491        }
31492      }
31493    }, {
31494      key: '_rotateForLabelAlignment',
31495
31496      /**
31497       * Rotates the canvas so the text is most readable
31498       * @param {CanvasRenderingContext2D} ctx
31499       * @private
31500       */
31501      value: function _rotateForLabelAlignment(ctx) {
31502        var dy = this.from.y - this.to.y;
31503        var dx = this.from.x - this.to.x;
31504        var angleInDegrees = Math.atan2(dy, dx);
31505
31506        // rotate so label it is readable
31507        if (angleInDegrees < -1 && dx < 0 || angleInDegrees > 0 && dx < 0) {
31508          angleInDegrees = angleInDegrees + Math.PI;
31509        }
31510
31511        ctx.rotate(angleInDegrees);
31512      }
31513    }, {
31514      key: '_pointOnCircle',
31515
31516      /**
31517       * Get a point on a circle
31518       * @param {Number} x
31519       * @param {Number} y
31520       * @param {Number} radius
31521       * @param {Number} percentage. Value between 0 (line start) and 1 (line end)
31522       * @return {Object} point
31523       * @private
31524       */
31525      value: function _pointOnCircle(x, y, radius, percentage) {
31526        var angle = percentage * 2 * Math.PI;
31527        return {
31528          x: x + radius * Math.cos(angle),
31529          y: y - radius * Math.sin(angle)
31530        };
31531      }
31532    }, {
31533      key: 'select',
31534      value: function select() {
31535        this.selected = true;
31536      }
31537    }, {
31538      key: 'unselect',
31539      value: function unselect() {
31540        this.selected = false;
31541      }
31542    }, {
31543      key: 'cleanup',
31544
31545      /**
31546       * cleans all required things on delete
31547       * @returns {*}
31548       */
31549      value: function cleanup() {
31550        return this.edgeType.cleanup();
31551      }
31552    }], [{
31553      key: 'parseOptions',
31554      value: function parseOptions(parentOptions, newOptions) {
31555        var allowDeletion = arguments.length <= 2 || arguments[2] === undefined ? false : arguments[2];
31556        var globalOptions = arguments.length <= 3 || arguments[3] === undefined ? {} : arguments[3];
31557
31558        var fields = ['id', 'from', 'hidden', 'hoverWidth', 'label', 'labelHighlightBold', 'length', 'line', 'opacity', 'physics', 'scaling', 'selectionWidth', 'selfReferenceSize', 'to', 'title', 'value', 'width'];
31559
31560        // only deep extend the items in the field array. These do not have shorthand.
31561        util.selectiveDeepExtend(fields, parentOptions, newOptions, allowDeletion);
31562
31563        util.mergeOptions(parentOptions, newOptions, 'smooth', allowDeletion, globalOptions);
31564        util.mergeOptions(parentOptions, newOptions, 'shadow', allowDeletion, globalOptions);
31565
31566        if (newOptions.dashes !== undefined && newOptions.dashes !== null) {
31567          parentOptions.dashes = newOptions.dashes;
31568        } else if (allowDeletion === true && newOptions.dashes === null) {
31569          parentOptions.dashes = Object.create(globalOptions.dashes); // this sets the pointer of the option back to the global option.
31570        }
31571
31572        // set the scaling newOptions
31573        if (newOptions.scaling !== undefined && newOptions.scaling !== null) {
31574          if (newOptions.scaling.min !== undefined) {
31575            parentOptions.scaling.min = newOptions.scaling.min;
31576          }
31577          if (newOptions.scaling.max !== undefined) {
31578            parentOptions.scaling.max = newOptions.scaling.max;
31579          }
31580          util.mergeOptions(parentOptions.scaling, newOptions.scaling, 'label', allowDeletion, globalOptions.scaling);
31581        } else if (allowDeletion === true && newOptions.scaling === null) {
31582          parentOptions.scaling = Object.create(globalOptions.scaling); // this sets the pointer of the option back to the global option.
31583        }
31584
31585        // hanlde multiple input cases for arrows
31586        if (newOptions.arrows !== undefined && newOptions.arrows !== null) {
31587          if (typeof newOptions.arrows === 'string') {
31588            var arrows = newOptions.arrows.toLowerCase();
31589            if (arrows.indexOf('to') != -1) {
31590              parentOptions.arrows.to.enabled = true;
31591            }
31592            if (arrows.indexOf('middle') != -1) {
31593              parentOptions.arrows.middle.enabled = true;
31594            }
31595            if (arrows.indexOf('from') != -1) {
31596              parentOptions.arrows.from.enabled = true;
31597            }
31598          } else if (typeof newOptions.arrows === 'object') {
31599            util.mergeOptions(parentOptions.arrows, newOptions.arrows, 'to', allowDeletion, globalOptions.arrows);
31600            util.mergeOptions(parentOptions.arrows, newOptions.arrows, 'middle', allowDeletion, globalOptions.arrows);
31601            util.mergeOptions(parentOptions.arrows, newOptions.arrows, 'from', allowDeletion, globalOptions.arrows);
31602          } else {
31603            throw new Error('The arrow newOptions can only be an object or a string. Refer to the documentation. You used:' + JSON.stringify(newOptions.arrows));
31604          }
31605        } else if (allowDeletion === true && newOptions.arrows === null) {
31606          parentOptions.arrows = Object.create(globalOptions.arrows); // this sets the pointer of the option back to the global option.
31607        }
31608
31609        // hanlde multiple input cases for color
31610        if (newOptions.color !== undefined && newOptions.color !== null) {
31611          if (util.isString(newOptions.color)) {
31612            parentOptions.color.color = newOptions.color;
31613            parentOptions.color.highlight = newOptions.color;
31614            parentOptions.color.hover = newOptions.color;
31615            parentOptions.color.inherit = false;
31616          } else {
31617            var colorsDefined = false;
31618            if (newOptions.color.color !== undefined) {
31619              parentOptions.color.color = newOptions.color.color;colorsDefined = true;
31620            }
31621            if (newOptions.color.highlight !== undefined) {
31622              parentOptions.color.highlight = newOptions.color.highlight;colorsDefined = true;
31623            }
31624            if (newOptions.color.hover !== undefined) {
31625              parentOptions.color.hover = newOptions.color.hover;colorsDefined = true;
31626            }
31627            if (newOptions.color.inherit !== undefined) {
31628              parentOptions.color.inherit = newOptions.color.inherit;
31629            }
31630            if (newOptions.color.opacity !== undefined) {
31631              parentOptions.color.opacity = Math.min(1, Math.max(0, newOptions.color.opacity));
31632            }
31633
31634            if (newOptions.color.inherit === undefined && colorsDefined === true) {
31635              parentOptions.color.inherit = false;
31636            }
31637          }
31638        } else if (allowDeletion === true && newOptions.color === null) {
31639          parentOptions.color = Object.create(globalOptions.color); // this sets the pointer of the option back to the global option.
31640        }
31641
31642        // handle the font settings
31643        if (newOptions.font !== undefined && newOptions.font !== null) {
31644          _sharedLabel2['default'].parseOptions(parentOptions.font, newOptions);
31645        } else if (allowDeletion === true && newOptions.font === null) {
31646          parentOptions.font = Object.create(globalOptions.font); // this sets the pointer of the option back to the global option.
31647        }
31648      }
31649    }]);
31650
31651    return Edge;
31652  })();
31653
31654  exports['default'] = Edge;
31655  module.exports = exports['default'];
31656
31657/***/ },
31658/* 83 */
31659/***/ function(module, exports, __webpack_require__) {
31660
31661  'use strict';
31662
31663  Object.defineProperty(exports, '__esModule', {
31664    value: true
31665  });
31666
31667  var _slicedToArray = (function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i['return']) _i['return'](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError('Invalid attempt to destructure non-iterable instance'); } }; })();
31668
31669  var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
31670
31671  var _get = function get(_x3, _x4, _x5) { var _again = true; _function: while (_again) { var object = _x3, property = _x4, receiver = _x5; desc = parent = getter = undefined; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x3 = parent; _x4 = property; _x5 = receiver; _again = true; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } };
31672
31673  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
31674
31675  function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
31676
31677  function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { c
31677onstructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) subClass.__proto__ = superClass; }
31678
31679  var _utilCubicBezierEdgeBase = __webpack_require__(84);
31680
31681  var _utilCubicBezierEdgeBase2 = _interopRequireDefault(_utilCubicBezierEdgeBase);
31682
31683  var CubicBezierEdge = (function (_CubicBezierEdgeBase) {
31684    _inherits(CubicBezierEdge, _CubicBezierEdgeBase);
31685
31686    function CubicBezierEdge(options, body, labelModule) {
31687      _classCallCheck(this, CubicBezierEdge);
31688
31689      _get(Object.getPrototypeOf(CubicBezierEdge.prototype), 'constructor', this).call(this, options, body, labelModule);
31690    }
31691
31692    _createClass(CubicBezierEdge, [{
31693      key: '_line',
31694
31695      /**
31696       * Draw a line between two nodes
31697       * @param {CanvasRenderingContext2D} ctx
31698       * @private
31699       */
31700      value: function _line(ctx) {
31701        // get the coordinates of the support points.
31702
31703        var _getViaCoordinates2 = this._getViaCoordinates();
31704
31705        var _getViaCoordinates22 = _slicedToArray(_getViaCoordinates2, 2);
31706
31707        var via1 = _getViaCoordinates22[0];
31708        var via2 = _getViaCoordinates22[1];
31709
31710        var returnValue = [via1, via2];
31711
31712        // start drawing the line.
31713        ctx.beginPath();
31714        ctx.moveTo(this.from.x, this.from.y);
31715
31716        // fallback to normal straight edges
31717        if (via1.x === undefined) {
31718          ctx.lineTo(this.to.x, this.to.y);
31719          returnValue = undefined;
31720        } else {
31721          ctx.bezierCurveTo(via1.x, via1.y, via2.x, via2.y, this.to.x, this.to.y);
31722        }
31723        // draw shadow if enabled
31724        this.enableShadow(ctx);
31725        ctx.stroke();
31726        this.disableShadow(ctx);
31727        return returnValue;
31728      }
31729    }, {
31730      key: '_getViaCoordinates',
31731      value: function _getViaCoordinates() {
31732        var dx = this.from.x - this.to.x;
31733        var dy = this.from.y - this.to.y;
31734
31735        var x1 = undefined,
31736            y1 = undefined,
31737            x2 = undefined,
31738            y2 = undefined;
31739        var roundness = this.options.smooth.roundness;;
31740
31741        // horizontal if x > y or if direction is forced or if direction is horizontal
31742        if ((Math.abs(dx) > Math.abs(dy) || this.options.smooth.forceDirection === true || this.options.smooth.forceDirection === 'horizontal') && this.options.smooth.forceDirection !== 'vertical') {
31743          y1 = this.from.y;
31744          y2 = this.to.y;
31745          x1 = this.from.x - roundness * dx;
31746          x2 = this.to.x + roundness * dx;
31747        } else {
31748          y1 = this.from.y - roundness * dy;
31749          y2 = this.to.y + roundness * dy;
31750          x1 = this.from.x;
31751          x2 = this.to.x;
31752        }
31753
31754        return [{ x: x1, y: y1 }, { x: x2, y: y2 }];
31755      }
31756    }, {
31757      key: '_findBorderPosition',
31758      value: function _findBorderPosition(nearNode, ctx) {
31759        return this._findBorderPositionBezier(nearNode, ctx);
31760      }
31761    }, {
31762      key: '_getDistanceToEdge',
31763      value: function _getDistanceToEdge(x1, y1, x2, y2, x3, y3) {
31764        var _ref = arguments.length <= 6 || arguments[6] === undefined ? this._getViaCoordinates() : arguments[6];
31765
31766        var _ref2 = _slicedToArray(_ref, 2);
31767
31768        var via1 = _ref2[0];
31769        var via2 = _ref2[1];
31770        // x3,y3 is the point
31771        return this._getDistanceToBezierEdge(x1, y1, x2, y2, x3, y3, via1, via2);
31772      }
31773    }, {
31774      key: 'getPoint',
31775
31776      /**
31777       * Combined function of pointOnLine and pointOnBezier. This gives the coordinates of a point on the line at a certain percentage of the way
31778       * @param percentage
31779       * @param via
31780       * @returns {{x: number, y: number}}
31781       * @private
31782       */
31783      value: function getPoint(percentage) {
31784        var _ref3 = arguments.length <= 1 || arguments[1] === undefined ? this._getViaCoordinates() : arguments[1];
31785
31786        var _ref32 = _slicedToArray(_ref3, 2);
31787
31788        var via1 = _ref32[0];
31789        var via2 = _ref32[1];
31790
31791        var t = percentage;
31792        var vec = [];
31793        vec[0] = Math.pow(1 - t, 3);
31794        vec[1] = 3 * t * Math.pow(1 - t, 2);
31795        vec[2] = 3 * Math.pow(t, 2) * (1 - t);
31796        vec[3] = Math.pow(t, 3);
31797        var x = vec[0] * this.from.x + vec[1] * via1.x + vec[2] * via2.x + vec[3] * this.to.x;
31798        var y = vec[0] * this.from.y + vec[1] * via1.y + vec[2] * via2.y + vec[3] * this.to.y;
31799
31800        return { x: x, y: y };
31801      }
31802    }]);
31803
31804    return CubicBezierEdge;
31805  })(_utilCubicBezierEdgeBase2['default']);
31806
31807  exports['default'] = CubicBezierEdge;
31808  module.exports = exports['default'];
31809
31810/***/ },
31811/* 84 */
31812/***/ function(module, exports, __webpack_require__) {
31813
31814  'use strict';
31815
31816  Object.defineProperty(exports, '__esModule', {
31817    value: true
31818  });
31819
31820  var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
31821
31822  var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; desc = parent = getter = undefined; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } };
31823
31824  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
31825
31826  function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
31827
31828  function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { c
31828onstructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) subClass.__proto__ = superClass; }
31829
31830  var _BezierEdgeBase2 = __webpack_require__(85);
31831
31832  var _BezierEdgeBase3 = _interopRequireDefault(_BezierEdgeBase2);
31833
31834  var CubicBezierEdgeBase = (function (_BezierEdgeBase) {
31835    _inherits(CubicBezierEdgeBase, _BezierEdgeBase);
31836
31837    function CubicBezierEdgeBase(options, body, labelModule) {
31838      _classCallCheck(this, CubicBezierEdgeBase);
31839
31840      _get(Object.getPrototypeOf(CubicBezierEdgeBase.prototype), 'constructor', this).call(this, options, body, labelModule);
31841    }
31842
31843    _createClass(CubicBezierEdgeBase, [{
31844      key: '_getDistanceToBezierEdge',
31845
31846      /**
31847       * Calculate the distance between a point (x3,y3) and a line segment from
31848       * (x1,y1) to (x2,y2).
31849       * http://stackoverflow.com/questions/849211/shortest-distancae-between-a-point-and-a-line-segment
31850       * https://en.wikipedia.org/wiki/B%C3%A9zier_curve
31851       * @param {number} x1 from x
31852       * @param {number} y1 from y
31853       * @param {number} x2 to x
31854       * @param {number} y2 to y
31855       * @param {number} x3 point to check x
31856       * @param {number} y3 point to check y
31857       * @private
31858       */
31859      value: function _getDistanceToBezierEdge(x1, y1, x2, y2, x3, y3, via1, via2) {
31860        // x3,y3 is the point
31861        var minDistance = 1e9;
31862        var distance = undefined;
31863        var i = undefined,
31864            t = undefined,
31865            x = undefined,
31866            y = undefined;
31867        var lastX = x1;
31868        var lastY = y1;
31869        var vec = [0, 0, 0, 0];
31870        for (i = 1; i < 10; i++) {
31871          t = 0.1 * i;
31872          vec[0] = Math.pow(1 - t, 3);
31873          vec[1] = 3 * t * Math.pow(1 - t, 2);
31874          vec[2] = 3 * Math.pow(t, 2) * (1 - t);
31875          vec[3] = Math.pow(t, 3);
31876          x = vec[0] * x1 + vec[1] * via1.x + vec[2] * via2.x + vec[3] * x2;
31877          y = vec[0] * y1 + vec[1] * via1.y + vec[2] * via2.y + vec[3] * y2;
31878          if (i > 0) {
31879            distance = this._getDistanceToLine(lastX, lastY, x, y, x3, y3);
31880            minDistance = distance < minDistance ? distance : minDistance;
31881          }
31882          lastX = x;
31883          lastY = y;
31884        }
31885
31886        return minDistance;
31887      }
31888    }]);
31889
31890    return CubicBezierEdgeBase;
31891  })(_BezierEdgeBase3['default']);
31892
31893  exports['default'] = CubicBezierEdgeBase;
31894  module.exports = exports['default'];
31895
31896/***/ },
31897/* 85 */
31898/***/ function(module, exports, __webpack_require__) {
31899
31900  'use strict';
31901
31902  Object.defineProperty(exports, '__esModule', {
31903    value: true
31904  });
31905
31906  var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
31907
31908  var _get = function get(_x2, _x3, _x4) { var _again = true; _function: while (_again) { var object = _x2, property = _x3, receiver = _x4; desc = parent = getter = undefined; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x2 = parent; _x3 = property; _x4 = receiver; _again = true; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } };
31909
31910  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
31911
31912  function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
31913
31914  function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { c
31914onstructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) subClass.__proto__ = superClass; }
31915
31916  var _EdgeBase2 = __webpack_require__(86);
31917
31918  var _EdgeBase3 = _interopRequireDefault(_EdgeBase2);
31919
31920  var BezierEdgeBase = (function (_EdgeBase) {
31921    _inherits(BezierEdgeBase, _EdgeBase);
31922
31923    function BezierEdgeBase(options, body, labelModule) {
31924      _classCallCheck(this, BezierEdgeBase);
31925
31926      _get(Object.getPrototypeOf(BezierEdgeBase.prototype), 'constructor', this).call(this, options, body, labelModule);
31927    }
31928
31929    _createClass(BezierEdgeBase, [{
31930      key: '_findBorderPositionBezier',
31931
31932      /**
31933       * This function uses binary search to look for the point where the bezier curve crosses the border of the node.
31934       *
31935       * @param nearNode
31936       * @param ctx
31937       * @param viaNode
31938       * @param nearNode
31939       * @param ctx
31940       * @param viaNode
31941       * @param nearNode
31942       * @param ctx
31943       * @param viaNode
31944       */
31945      value: function _findBorderPositionBezier(nearNode, ctx) {
31946        var viaNode = arguments.length <= 2 || arguments[2] === undefined ? this._getViaCoordinates() : arguments[2];
31947
31948        var maxIterations = 10;
31949        var iteration = 0;
31950        var low = 0;
31951        var high = 1;
31952        var pos, angle, distanceToBorder, distanceToPoint, difference;
31953        var threshold = 0.2;
31954        var node = this.to;
31955        var from = false;
31956        if (nearNode.id === this.from.id) {
31957          node = this.from;
31958          from = true;
31959        }
31960
31961        while (low <= high && iteration < maxIterations) {
31962          var middle = (low + high) * 0.5;
31963
31964          pos = this.getPoint(middle, viaNode);
31965          angle = Math.atan2(node.y - pos.y, node.x - pos.x);
31966          distanceToBorder = node.distanceToBorder(ctx, angle);
31967          distanceToPoint = Math.sqrt(Math.pow(pos.x - node.x, 2) + Math.pow(pos.y - node.y, 2));
31968          difference = distanceToBorder - distanceToPoint;
31969          if (Math.abs(difference) < threshold) {
31970            break; // found
31971          } else if (difference < 0) {
31972            // distance to nodes is larger than distance to border --> t needs to be bigger if we're looking at the to node.
31973            if (from === false) {
31974              low = middle;
31975            } else {
31976              high = middle;
31977            }
31978          } else {
31979            if (from === false) {
31980              high = middle;
31981            } else {
31982              low = middle;
31983            }
31984          }
31985
31986          iteration++;
31987        }
31988        pos.t = middle;
31989
31990        return pos;
31991      }
31992    }, {
31993      key: '_getDistanceToBezierEdge',
31994
31995      /**
31996       * Calculate the distance between a point (x3,y3) and a line segment from
31997       * (x1,y1) to (x2,y2).
31998       * http://stackoverflow.com/questions/849211/shortest-distancae-between-a-point-and-a-line-segment
31999       * @param {number} x1 from x
32000       * @param {number} y1 from y
32001       * @param {number} x2 to x
32002       * @param {number} y2 to y
32003       * @param {number} x3 point to check x
32004       * @param {number} y3 point to check y
32005       * @private
32006       */
32007      value: function _getDistanceToBezierEdge(x1, y1, x2, y2, x3, y3, via) {
32008        // x3,y3 is the point
32009        var minDistance = 1e9;
32010        var distance = undefined;
32011        var i = undefined,
32012            t = undefined,
32013            x = undefined,
32014            y = undefined;
32015        var lastX = x1;
32016        var lastY = y1;
32017        for (i = 1; i < 10; i++) {
32018          t = 0.1 * i;
32019          x = Math.pow(1 - t, 2) * x1 + 2 * t * (1 - t) * via.x + Math.pow(t, 2) * x2;
32020          y = Math.pow(1 - t, 2) * y1 + 2 * t * (1 - t) * via.y + Math.pow(t, 2) * y2;
32021          if (i > 0) {
32022            distance = this._getDistanceToLine(lastX, lastY, x, y, x3, y3);
32023            minDistance = distance < minDistance ? distance : minDistance;
32024          }
32025          lastX = x;
32026          lastY = y;
32027        }
32028
32029        return minDistance;
32030      }
32031    }]);
32032
32033    return BezierEdgeBase;
32034  })(_EdgeBase3['default']);
32035
32036  exports['default'] = BezierEdgeBase;
32037  module.exports = exports['default'];
32038
32039/***/ },
32040/* 86 */
32041/***/ function(module, exports, __webpack_require__) {
32042
32043  'use strict';
32044
32045  Object.defineProperty(exports, '__esModule', {
32046    value: true
32047  });
32048
32049  var _slicedToArray = (function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i['return']) _i['return'](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); }
32049 else { throw new TypeError('Invalid attempt to destructure non-iterable instance'); } }; })();
32050
32051  var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
32052
32053  function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
32054
32055  var util = __webpack_require__(1);
32056
32057  var EdgeBase = (function () {
32058    function EdgeBase(options, body, labelModule) {
32059      _classCallCheck(this, EdgeBase);
32060
32061      this.body = body;
32062      this.labelModule = labelModule;
32063      this.setOptions(options);
32064      this.colorDirty = true;
32065      this.color = {};
32066      this.selectionWidth = 2;
32067      this.hoverWidth = 1.5;
32068    }
32069
32070    _createClass(EdgeBase, [{
32071      key: 'connect',
32072      value: function connect() {
32073        this.from = this.body.nodes[this.options.from];
32074        this.to = this.body.nodes[this.options.to];
32075      }
32076    }, {
32077      key: 'cleanup',
32078      value: function cleanup() {
32079        return false;
32080      }
32081    }, {
32082      key: 'setOptions',
32083      value: function setOptions(options) {
32084        this.options = options;
32085        this.from = this.body.nodes[this.options.from];
32086        this.to = this.body.nodes[this.options.to];
32087        this.id = this.options.id;
32088      }
32089    }, {
32090      key: 'drawLine',
32091
32092      /**
32093       * Redraw a edge as a line
32094       * Draw this edge in the given canvas
32095       * The 2d context of a HTML canvas can be retrieved by canvas.getContext("2d");
32096       * @param {CanvasRenderingContext2D}   ctx
32097       * @private
32098       */
32099      value: function drawLine(ctx, selected, hover) {
32100        // set style
32101        ctx.strokeStyle = this.getColor(ctx, selected, hover);
32102        ctx.lineWidth = this.getLineWidth(selected, hover);
32103        var via = undefined;
32104        if (this.options.dashes !== false) {
32105          via = this._drawDashedLine(ctx);
32106        } else {
32107          via = this._drawLine(ctx);
32108        }
32109        return via;
32110      }
32111    }, {
32112      key: '_drawLine',
32113      value: function _drawLine(ctx) {
32114        var via = undefined;
32115        if (this.from != this.to) {
32116          // draw line
32117          via = this._line(ctx);
32118        } else {
32119          var _getCircleData2 = this._getCircleData(ctx);
32120
32121          var _getCircleData22 = _slicedToArray(_getCircleData2, 3);
32122
32123          var x = _getCircleData22[0];
32124          var y = _getCircleData22[1];
32125          var radius = _getCircleData22[2];
32126
32127          this._circle(ctx, x, y, radius);
32128        }
32129        return via;
32130      }
32131    }, {
32132      key: '_drawDashedLine',
32133      value: function _drawDashedLine(ctx) {
32134        var via = undefined;
32135        ctx.lineCap = 'round';
32136        var pattern = [5, 5];
32137        if (Array.isArray(this.options.dashes) === true) {
32138          pattern = this.options.dashes;
32139        }
32140
32141        // only firefox and chrome support this method, else we use the legacy one.
32142        if (ctx.setLineDash !== undefined) {
32143          ctx.save();
32144
32145          // set dash settings for chrome or firefox
32146          ctx.setLineDash(pattern);
32147          ctx.lineDashOffset = 0;
32148
32149          // draw the line
32150          if (this.from != this.to) {
32151            // draw line
32152            via = this._line(ctx);
32153          } else {
32154            var _getCircleData3 = this._getCircleData(ctx);
32155
32156            var _getCircleData32 = _slicedToArray(_getCircleData3, 3);
32157
32158            var x = _getCircleData32[0];
32159            var y = _getCircleData32[1];
32160            var radius = _getCircleData32[2];
32161
32162            this._circle(ctx, x, y, radius);
32163          }
32164
32165          // restore the dash settings.
32166          ctx.setLineDash([0]);
32167          ctx.lineDashOffset = 0;
32168          ctx.restore();
32169        } else {
32170          // unsupporting smooth lines
32171          if (this.from != this.to) {
32172            // draw line
32173            ctx.dashedLine(this.from.x, this.from.y, this.to.x, this.to.y, pattern);
32174          } else {
32175            var _getCircleData4 = this._getCircleData(ctx);
32176
32177            var _getCircleData42 = _slicedToArray(_getCircleData4, 3);
32178
32179            var x = _getCircleData42[0];
32180            var y = _getCircleData42[1];
32181            var radius = _getCircleData42[2];
32182
32183            this._circle(ctx, x, y, radius);
32184          }
32185          // draw shadow if enabled
32186          this.enableShadow(ctx);
32187
32188          ctx.stroke();
32189
32190          // disable shadows for other elements.
32191          this.disableShadow(ctx);
32192        }
32193        return via;
32194      }
32195    }, {
32196      key: 'findBorderPosition',
32197      value: function findBorderPosition(nearNode, ctx, options) {
32198        if (this.from != this.to) {
32199          return this._findBorderPosition(nearNode, ctx, options);
32200        } else {
32201          return this._findBorderPositionCircle(nearNode, ctx, options);
32202        }
32203      }
32204    }, {
32205      key: 'findBorderPositions',
32206      value: function findBorderPositions(ctx) {
32207        var from = {};
32208        var to = {};
32209        if (this.from != this.to) {
32210          from = this._findBorderPosition(this.from, ctx);
32211          to = this._findBorderPosition(this.to, ctx);
32212        } else {
32213          var _getCircleData5 = this._getCircleData(ctx);
32214
32215          var _getCircleData52 = _slicedToArray(_getCircleData5, 3);
32216
32217          var x = _getCircleData52[0];
32218          var y = _getCircleData52[1];
32219          var radius = _getCircleData52[2];
32220
32221          from = this._findBorderPositionCircle(this.from, ctx, { x: x, y: y, low: 0.25, high: 0.6, direction: -1 });
32222          to = this._findBorderPositionCircle(this.from, ctx, { x: x, y: y, low: 0.6, high: 0.8, direction: 1 });
32223        }
32224        return { from: from, to: to };
32225      }
32226    }, {
32227      key: '_getCircleData',
32228      value: function _getCircleData(ctx) {
32229        var x = undefined,
32230            y = undefined;
32231        var node = this.from;
32232        var radius = this.options.selfReferenceSize;
32233
32234        if (ctx !== undefined) {
32235          if (node.shape.width === undefined) {
32236            node.shape.resize(ctx);
32237          }
32238        }
32239
32240        // get circle coordinates
32241        if (node.shape.width > node.shape.height) {
32242          x = node.x + node.shape.width * 0.5;
32243          y = node.y - radius;
32244        } else {
32245          x = node.x + radius;
32246          y = node.y - node.shape.height * 0.5;
32247        }
32248        return [x, y, radius];
32249      }
32250    }, {
32251      key: '_pointOnCircle',
32252
32253      /**
32254       * Get a point on a circle
32255       * @param {Number} x
32256       * @param {Number} y
32257       * @param {Number} radius
32258       * @param {Number} percentage. Value between 0 (line start) and 1 (line end)
32259       * @return {Object} point
32260       * @private
32261       */
32262      value: function _pointOnCircle(x, y, radius, percentage) {
32263        var angle = percentage * 2 * Math.PI;
32264        return {
32265          x: x + radius * Math.cos(angle),
32266          y: y - radius * Math.sin(angle)
32267        };
32268      }
32269    }, {
32270      key: '_findBorderPositionCircle',
32271
32272      /**
32273       * This function uses binary search to look for the point where the circle crosses the border of the node.
32274       * @param node
32275       * @param ctx
32276       * @param options
32277       * @returns {*}
32278       * @private
32279       */
32280      value: function _findBorderPositionCircle(node, ctx, options) {
32281        var x = options.x;
32282        var y = options.y;
32283        var low = options.low;
32284        var high = options.high;
32285        var direction = options.direction;
32286
32287        var maxIterations = 10;
32288        var iteration = 0;
32289        var radius = this.options.selfReferenceSize;
32290        var pos = undefined,
32291            angle = undefined,
32292            distanceToBorder = undefined,
32293            distanceToPoint = undefined,
32294            difference = undefined;
32295        var threshold = 0.05;
32296        var middle = (low + high) * 0.5;
32297
32298        while (low <= high && iteration < maxIterations) {
32299          middle = (low + high) * 0.5;
32300
32301          pos = this._pointOnCircle(x, y, radius, middle);
32302          angle = Math.atan2(node.y - pos.y, node.x - pos.x);
32303          distanceToBorder = node.distanceToBorder(ctx, angle);
32304          distanceToPoint = Math.sqrt(Math.pow(pos.x - node.x, 2) + Math.pow(pos.y - node.y, 2));
32305          difference = distanceToBorder - distanceToPoint;
32306          if (Math.abs(difference) < threshold) {
32307            break; // found
32308          } else if (difference > 0) {
32309            // distance to nodes is larger than distance to border --> t needs to be bigger if we're looking at the to node.
32310            if (direction > 0) {
32311              low = middle;
32312            } else {
32313              high = middle;
32314            }
32315          } else {
32316            if (direction > 0) {
32317              high = middle;
32318            } else {
32319              low = middle;
32320            }
32321          }
32322          iteration++;
32323        }
32324        pos.t = middle;
32325
32326        return pos;
32327      }
32328    }, {
32329      key: 'getLineWidth',
32330
32331      /**
32332       * Get the line width of the edge. Depends on width and whether one of the
32333       * connected nodes is selected.
32334       * @return {Number} width
32335       * @private
32336       */
32337      value: function getLineWidth(selected, hover) {
32338        if (selected === true) {
32339          return Math.max(this.selectionWidth, 0.3 / this.body.view.scale);
32340        } else {
32341          if (hover === true) {
32342            return Math.max(this.hoverWidth, 0.3 / this.body.view.scale);
32343          } else {
32344            return Math.max(this.options.width, 0.3 / this.body.view.scale);
32345          }
32346        }
32347      }
32348    }, {
32349      key: 'getColor',
32350      value: function getColor(ctx, selected, hover) {
32351        var colorOptions = this.options.color;
32352        if (colorOptions.inherit !== false) {
32353          // when this is a loop edge, just use the 'from' method
32354          if (colorOptions.inherit === 'both' && this.from.id !== this.to.id) {
32355            var grd = ctx.createLinearGradient(this.from.x, this.from.y, this.to.x, this.to.y);
32356            var fromColor = undefined,
32357                toColor = undefined;
32358            fromColor = this.from.options.color.highlight.border;
32359            toColor = this.to.options.color.highlight.border;
32360
32361            if (this.from.selected === false && this.to.selected === false) {
32362              fromColor = util.overrideOpacity(this.from.options.color.border, this.options.color.opacity);
32363              toColor = util.overrideOpacity(this.to.options.color.border, this.options.color.opacity);
32364            } else if (this.from.selected === true && this.to.selected === false) {
32365              toColor = this.to.options.color.border;
32366            } else if (this.from.selected === false && this.to.selected === true) {
32367              fromColor = this.from.options.color.border;
32368            }
32369            grd.addColorStop(0, fromColor);
32370            grd.addColorStop(1, toColor);
32371
32372            // -------------------- this returns -------------------- //
32373            return grd;
32374          }
32375
32376          if (this.colorDirty === true) {
32377            if (colorOptions.inherit === 'to') {
32378              this.color.highlight = this.to.options.color.highlight.border;
32379              this.color.hover = this.to.options.color.hover.border;
32380              this.color.color = util.overrideOpacity(this.to.options.color.border, colorOptions.opacity);
32381            } else {
32382              // (this.options.color.inherit.source === "from") {
32383              this.color.highlight = this.from.options.color.highlight.border;
32384              this.color.hover = this.from.options.color.hover.border;
32385              this.color.color = util.overrideOpacity(this.from.options.color.border, colorOptions.opacity);
32386            }
32387          }
32388        } else if (this.colorDirty === true) {
32389          this.color.highlight = colorOptions.highlight;
32390          this.color.hover = colorOptions.hover;
32391          this.color.color = util.overrideOpacity(colorOptions.color, colorOptions.opacity);
32392        }
32393
32394        // if color inherit is on and gradients are used, the function has already returned by now.
32395        this.colorDirty = false;
32396
32397        if (selected === true) {
32398          return this.color.highlight;
32399        } else if (hover === true) {
32400          return this.color.hover;
32401        } else {
32402          return this.color.color;
32403        }
32404      }
32405    }, {
32406      key: '_circle',
32407
32408      /**
32409       * Draw a line from a node to itself, a circle
32410       * @param {CanvasRenderingContext2D} ctx
32411       * @param {Number} x
32412       * @param {Number} y
32413       * @param {Number} radius
32414       * @private
32415       */
32416      value: function _circle(ctx, x, y, radius) {
32417        // draw shadow if enabled
32418        this.enableShadow(ctx);
32419
32420        // draw a circle
32421        ctx.beginPath();
32422        ctx.arc(x, y, radius, 0, 2 * Math.PI, false);
32423        ctx.stroke();
32424
32425        // disable shadows for other elements.
32426        this.disableShadow(ctx);
32427      }
32428    }, {
32429      key: 'getDistanceToEdge',
32430
32431      /**
32432       * Calculate the distance between a point (x3,y3) and a line segment from
32433       * (x1,y1) to (x2,y2).
32434       * http://stackoverflow.com/questions/849211/shortest-distancae-between-a-point-and-a-line-segment
32435       * @param {number} x1
32436       * @param {number} y1
32437       * @param {number} x2
32438       * @param {number} y2
32439       * @param {number} x3
32440       * @param {number} y3
32441       * @private
32442       */
32443      value: function getDistanceToEdge(x1, y1, x2, y2, x3, y3, via) {
32444        // x3,y3 is the point
32445        var returnValue = 0;
32446        if (this.from != this.to) {
32447          returnValue = this._getDistanceToEdge(x1, y1, x2, y2, x3, y3, via);
32448        } else {
32449          var _getCircleData6 = this._getCircleData();
32450
32451          var _getCircleData62 = _slicedToArray(_getCircleData6, 3);
32452
32453          var x = _getCircleData62[0];
32454          var y = _getCircleData62[1];
32455          var radius = _getCircleData62[2];
32456
32457          var dx = x - x3;
32458          var dy = y - y3;
32459          returnValue = Math.abs(Math.sqrt(dx * dx + dy * dy) - radiu
32459s);
32460        }
32461
32462        if (this.labelModule.size.left < x3 && this.labelModule.size.left + this.labelModule.size.width > x3 && this.labelModule.size.top < y3 && this.labelModule.size.top + this.labelModule.size.height > y3) {
32463          return 0;
32464        } else {
32465          return returnValue;
32466        }
32467      }
32468    }, {
32469      key: '_getDistanceToLine',
32470      value: function _getDistanceToLine(x1, y1, x2, y2, x3, y3) {
32471        var px = x2 - x1;
32472        var py = y2 - y1;
32473        var something = px * px + py * py;
32474        var u = ((x3 - x1) * px + (y3 - y1) * py) / something;
32475
32476        if (u > 1) {
32477          u = 1;
32478        } else if (u < 0) {
32479          u = 0;
32480        }
32481
32482        var x = x1 + u * px;
32483        var y = y1 + u * py;
32484        var dx = x - x3;
32485        var dy = y - y3;
32486
32487        //# Note: If the actual distance does not matter,
32488        //# if you only want to compare what this function
32489        //# returns to other results of this function, you
32490        //# can just return the squared distance instead
32491        //# (i.e. remove the sqrt) to gain a little performance
32492
32493        return Math.sqrt(dx * dx + dy * dy);
32494      }
32495    }, {
32496      key: 'drawArrowHead',
32497
32498      /**
32499       *
32500       * @param ctx
32501       * @param position
32502       * @param viaNode
32503       */
32504      value: function drawArrowHead(ctx, position, viaNode, selected, hover) {
32505        // set style
32506        ctx.strokeStyle = this.getColor(ctx, selected, hover);
32507        ctx.fillStyle = ctx.strokeStyle;
32508        ctx.lineWidth = this.getLineWidth(selected, hover);
32509
32510        // set lets
32511        var angle = undefined;
32512        var length = undefined;
32513        var arrowPos = undefined;
32514        var node1 = undefined;
32515        var node2 = undefined;
32516        var guideOffset = undefined;
32517        var scaleFactor = undefined;
32518
32519        if (position === 'from') {
32520          node1 = this.from;
32521          node2 = this.to;
32522          guideOffset = 0.1;
32523          scaleFactor = this.options.arrows.from.scaleFactor;
32524        } else if (position === 'to') {
32525          node1 = this.to;
32526          node2 = this.from;
32527          guideOffset = -0.1;
32528          scaleFactor = this.options.arrows.to.scaleFactor;
32529        } else {
32530          node1 = this.to;
32531          node2 = this.from;
32532          scaleFactor = this.options.arrows.middle.scaleFactor;
32533        }
32534
32535        // if not connected to itself
32536        if (node1 != node2) {
32537          if (position !== 'middle') {
32538            // draw arrow head
32539            if (this.options.smooth.enabled === true) {
32540              arrowPos = this.findBorderPosition(node1, ctx, { via: viaNode });
32541              var guidePos = this.getPoint(Math.max(0.0, Math.min(1.0, arrowPos.t + guideOffset)), viaNode);
32542              angle = Math.atan2(arrowPos.y - guidePos.y, arrowPos.x - guidePos.x);
32543            } else {
32544              angle = Math.atan2(node1.y - node2.y, node1.x - node2.x);
32545              arrowPos = this.findBorderPosition(node1, ctx);
32546            }
32547          } else {
32548            angle = Math.atan2(node1.y - node2.y, node1.x - node2.x);
32549            arrowPos = this.getPoint(0.6, viaNode); // this is 0.6 to account for the size of the arrow.
32550          }
32551          // draw arrow at the end of the line
32552          length = (10 + 5 * this.options.width) * scaleFactor;
32553          ctx.arrow(arrowPos.x, arrowPos.y, angle, length);
32554
32555          // draw shadow if enabled
32556          this.enableShadow(ctx);
32557          ctx.fill();
32558
32559          // disable shadows for other elements.
32560          this.disableShadow(ctx);
32561          ctx.stroke();
32562        } else {
32563          // draw circle
32564          var _angle = undefined,
32565              point = undefined;
32566
32567          var _getCircleData7 = this._getCircleData(ctx);
32568
32569          var _getCircleData72 = _slicedToArray(_getCircleData7, 3);
32570
32571          var x = _getCircleData72[0];
32572          var y = _getCircleData72[1];
32573          var radius = _getCircleData72[2];
32574
32575          if (position === 'from') {
32576            point = this.findBorderPosition(this.from, ctx, { x: x, y: y, low: 0.25, high: 0.6, direction: -1 });
32577            _angle = point.t * -2 * Math.PI + 1.5 * Math.PI + 0.1 * Math.PI;
32578          } else if (position === 'to') {
32579            point = this.findBorderPosition(this.from, ctx, { x: x, y: y, low: 0.6, high: 1.0, direction: 1 });
32580            _angle = point.t * -2 * Math.PI + 1.5 * Math.PI - 1.1 * Math.PI;
32581          } else {
32582            point = this._pointOnCircle(x, y, radius, 0.175);
32583            _angle = 3.9269908169872414; // === 0.175 * -2 * Math.PI + 1.5 * Math.PI + 0.1 * Math.PI;
32584          }
32585
32586          // draw the arrowhead
32587          var _length = (10 + 5 * this.options.width) * scaleFactor;
32588          ctx.arrow(point.x, point.y, _angle, _length);
32589
32590          // draw shadow if enabled
32591          this.enableShadow(ctx);
32592          ctx.fill();
32593
32594          // disable shadows for other elements.
32595          this.disableShadow(ctx);
32596          ctx.stroke();
32597        }
32598      }
32599    }, {
32600      key: 'enableShadow',
32601      value: function enableShadow(ctx) {
32602        if (this.options.shadow.enabled === true) {
32603          ctx.shadowColor = 'rgba(0,0,0,0.5)';
32604          ctx.shadowBlur = this.options.shadow.size;
32605          ctx.shadowOffsetX = this.options.shadow.x;
32606          ctx.shadowOffsetY = this.options.shadow.y;
32607        }
32608      }
32609    }, {
32610      key: 'disableShadow',
32611      value: function disableShadow(ctx) {
32612        if (this.options.shadow.enabled === true) {
32613          ctx.shadowColor = 'rgba(0,0,0,0)';
32614          ctx.shadowBlur = 0;
32615          ctx.shadowOffsetX = 0;
32616          ctx.shadowOffsetY = 0;
32617        }
32618      }
32619    }]);
32620
32621    return EdgeBase;
32622  })();
32623
32624  exports['default'] = EdgeBase;
32625  module.exports = exports['default'];
32626
32627/***/ },
32628/* 87 */
32629/***/ function(module, exports, __webpack_require__) {
32630
32631  "use strict";
32632
32633  Object.defineProperty(exports, "__esModule", {
32634    value: true
32635  });
32636
32637  var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
32638
32639  var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; desc = parent = getter = undefined; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; continue _function; } } else if ("value" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } };
32640
32641  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
32642
32643  function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
32644
32645  function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) subClass.__proto__ = superClass; }
32646
32647  var _utilBezierEdgeBase = __webpack_require__(85);
32648
32649  var _utilBezierEdgeBase2 = _interopRequireDefault(_utilBezierEdgeBase);
32650
32651  var BezierEdgeDynamic = (function (_BezierEdgeBase) {
32652    _inherits(BezierEdgeDynamic, _BezierEdgeBase);
32653
32654    function BezierEdgeDynamic(options, body, labelModule) {
32655      var _this = this;
32656
32657      _classCallCheck(this, BezierEdgeDynamic);
32658
32659      //this.via = undefined; // Here for completeness but not allowed to defined before super() is invoked.
32660      _get(Object.getPrototypeOf(BezierEdgeDynamic.prototype), "constructor", this).call(this, options, body, labelModule); // --> this calls the setOptions below
32661      this._boundFunction = function () {
32662        _this.positionBezierNode();
32663      };
32664      this.body.emitter.on("_repositionBezierNodes", this._boundFunction);
32665    }
32666
32667    _createClass(BezierEdgeDynamic, [{
32668      key: "setOptions",
32669      value: function setOptions(options) {
32670        this.options = options;
32671        this.id = this.options.id;
32672        this.setupSupportNode();
32673
32674        // when we change the physics state of the edge, we reposition the support node.
32675        if (this.options.physics !== options.physics) {
32676          this.via.setOptions({ physics: this.options.physics });
32677          this.positionBezierNode();
32678        }
32679        this.connect();
32680      }
32681    }, {
32682      key: "connect",
32683      value: function connect() {
32684        this.from = this.body.nodes[this.options.from];
32685        this.to = this.body.nodes[this.options.to];
32686        if (this.from === undefined || this.to === undefined || this.options.physics === false) {
32687          this.via.setOptions({ physics: false });
32688        } else {
32689          // fix weird behaviour where a selfreferencing node has physics enabled
32690          if (this.from.id === this.to.id) {
32691            this.via.setOptions({ physics: false });
32692          } else {
32693            this.via.setOptions({ physics: true });
32694          }
32695        }
32696      }
32697    }, {
32698      key: "cleanup",
32699
32700      /**
32701       * remove the support nodes
32702       * @returns {boolean}
32703       */
32704      value: function cleanup() {
32705        this.body.emitter.off("_repositionBezierNodes", this._boundFunction);
32706        if (this.via !== undefined) {
32707          delete this.body.nodes[this.via.id];
32708          this.via = undefined;
32709          return true;
32710        }
32711        return false;
32712      }
32713    }, {
32714      key: "setupSupportNode",
32715
32716      /**
32717       * Bezier curves require an anchor point to calculate the smooth flow. These points are nodes. These nodes are invisible but
32718       * are used for the force calculation.
32719       *
32720       * The changed data is not called, if needed, it is returned by the main edge constructor.
32721       * @private
32722       */
32723      value: function setupSupportNode() {
32724        if (this.via === undefined) {
32725          var nodeId = "edgeId:" + this.id;
32726          var node = this.body.functions.createNode({
32727            id: nodeId,
32728            shape: "circle",
32729            physics: true,
32730            hidden: true
32731          });
32732          this.body.nodes[nodeId] = node;
32733          this.via = node;
32734          this.via.parentEdgeId = this.id;
32735          this.positionBezierNode();
32736        }
32737      }
32738    }, {
32739      key: "positionBezierNode",
32740      value: function positionBezierNode() {
32741        if (this.via !== undefined && this.from !== undefined && this.to !== undefined) {
32742          this.via.x = 0.5 * (this.from.x + this.to.x);
32743          this.via.y = 0.5 * (this.from.y + this.to.y);
32744        } else if (this.via !== undefined) {
32745          this.via.x = 0;
32746          this.via.y = 0;
32747        }
32748      }
32749    }, {
32750      key: "_line",
32751
32752      /**
32753       * Draw a line between two nodes
32754       * @param {CanvasRenderingContext2D} ctx
32755       * @private
32756       */
32757      value: function _line(ctx) {
32758        // draw a straight line
32759        ctx.beginPath();
32760        ctx.moveTo(this.from.x, this.from.y);
32761        ctx.quadraticCurveTo(this.via.x, this.via.y, this.to.x, this.to.y);
32762        // draw shadow if enabled
32763        this.enableShadow(ctx);
32764        ctx.stroke();
32765        this.disableShadow(ctx);
32766        return this.via;
32767      }
32768    }, {
32769      key: "getPoint",
32770
32771      /**
32772       * Combined function of pointOnLine and pointOnBezier. This gives the coordinates of a point on the line at a certain percentage of the way
32773       * @param percentage
32774       * @param via
32775       * @returns {{x: number, y: number}}
32776       * @private
32777       */
32778      value: function getPoint(percentage) {
32779        var t = percentage;
32780        var x = Math.pow(1 - t, 2) * this.from.x + 2 * t * (1 - t) * this.via.x + Math.pow(t, 2) * this.to.x;
32781        var y = Math.pow(1 - t, 2) * this.from.y + 2 * t * (1 - t) * this.via.y + Math.pow(t, 2) * this.to.y;
32782
32783        return { x: x, y: y };
32784      }
32785    }, {
32786      key: "_findBorderPosition",
32787      value: function _findBorderPosition(nearNode, ctx) {
32788        return this._findBorderPositionBezier(nearNode, ctx, this.via);
32789      }
32790    }, {
32791      key: "_getDistanceToEdge",
32792      value: function _getDistanceToEdge(x1, y1, x2, y2, x3, y3) {
32793        // x3,y3 is the point
32794        return this._getDistanceToBezierEdge(x1, y1, x2, y2, x3, y3, this.via);
32795      }
32796    }]);
32797
32798    return BezierEdgeDynamic;
32799  })(_utilBezierEdgeBase2["default"]);
32800
32801  exports["default"] = BezierEdgeDynamic;
32802  module.exports = exports["default"];
32803
32804/***/ },
32805/* 88 */
32806/***/ function(module, exports, __webpack_require__) {
32807
32808  'use strict';
32809
32810  Object.defineProperty(exports, '__esModule', {
32811    value: true
32812  });
32813
32814  var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
32815
32816  var _get = function get(_x4, _x5, _x6) { var _again = true; _function: while (_again) { var object = _x4, property = _x5, receiver = _x6; desc = parent = getter = undefined; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x4 = parent; _x5 = property; _x6 = receiver; _again = true; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } };
32817
32818  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
32819
32820  function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
32821
32822  function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { c
32822onstructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) subClass.__proto__ = superClass; }
32823
32824  var _utilBezierEdgeBase = __webpack_require__(85);
32825
32826  var _utilBezierEdgeBase2 = _interopRequireDefault(_utilBezierEdgeBase);
32827
32828  var BezierEdgeStatic = (function (_BezierEdgeBase) {
32829    _inherits(BezierEdgeStatic, _BezierEdgeBase);
32830
32831    function BezierEdgeStatic(options, body, labelModule) {
32832      _classCallCheck(this, BezierEdgeStatic);
32833
32834      _get(Object.getPrototypeOf(BezierEdgeStatic.prototype), 'constructor', this).call(this, options, body, labelModule);
32835    }
32836
32837    _createClass(BezierEdgeStatic, [{
32838      key: '_line',
32839
32840      /**
32841       * Draw a line between two nodes
32842       * @param {CanvasRenderingContext2D} ctx
32843       * @private
32844       */
32845      value: function _line(ctx) {
32846        // draw a straight line
32847        ctx.beginPath();
32848        ctx.moveTo(this.from.x, this.from.y);
32849        var via = this._getViaCoordinates();
32850        var returnValue = via;
32851
32852        // fallback to normal straight edges
32853        if (via.x === undefined) {
32854          ctx.lineTo(this.to.x, this.to.y);
32855          returnValue = undefined;
32856        } else {
32857          ctx.quadraticCurveTo(via.x, via.y, this.to.x, this.to.y);
32858        }
32859        // draw shadow if enabled
32860        this.enableShadow(ctx);
32861        ctx.stroke();
32862        this.disableShadow(ctx);
32863        return returnValue;
32864      }
32865    }, {
32866      key: '_getViaCoordinates',
32867      value: function _getViaCoordinates() {
32868        var xVia = undefined;
32869        var yVia = undefined;
32870        var factor = this.options.smooth.roundness;
32871        var type = this.options.smooth.type;
32872        var dx = Math.abs(this.from.x - this.to.x);
32873        var dy = Math.abs(this.from.y - this.to.y);
32874        if (type === 'discrete' || type === 'diagonalCross') {
32875          if (Math.abs(this.from.x - this.to.x) <= Math.abs(this.from.y - this.to.y)) {
32876            if (this.from.y >= this.to.y) {
32877              if (this.from.x <= this.to.x) {
32878                xVia = this.from.x + factor * dy;
32879                yVia = this.from.y - factor * dy;
32880              } else if (this.from.x > this.to.x) {
32881                xVia = this.from.x - factor * dy;
32882                yVia = this.from.y - factor * dy;
32883              }
32884            } else if (this.from.y < this.to.y) {
32885              if (this.from.x <= this.to.x) {
32886                xVia = this.from.x + factor * dy;
32887                yVia = this.from.y + factor * dy;
32888              } else if (this.from.x > this.to.x) {
32889                xVia = this.from.x - factor * dy;
32890                yVia = this.from.y + factor * dy;
32891              }
32892            }
32893            if (type === 'discrete') {
32894              xVia = dx < factor * dy ? this.from.x : xVia;
32895            }
32896          } else if (Math.abs(this.from.x - this.to.x) > Math.abs(this.from.y - this.to.y)) {
32897            if (this.from.y >= this.to.y) {
32898              if (this.from.x <= this.to.x) {
32899                xVia = this.from.x + factor * dx;
32900                yVia = this.from.y - factor * dx;
32901              } else if (this.from.x > this.to.x) {
32902                xVia = this.from.x - factor * dx;
32903                yVia = this.from.y - factor * dx;
32904              }
32905            } else if (this.from.y < this.to.y) {
32906              if (this.from.x <= this.to.x) {
32907                xVia = this.from.x + factor * dx;
32908                yVia = this.from.y + factor * dx;
32909              } else if (this.from.x > this.to.x) {
32910                xVia = this.from.x - factor * dx;
32911                yVia = this.from.y + factor * dx;
32912              }
32913            }
32914            if (type === 'discrete') {
32915              yVia = dy < factor * dx ? this.from.y : yVia;
32916            }
32917          }
32918        } else if (type === 'straightCross') {
32919          if (Math.abs(this.from.x - this.to.x) <= Math.abs(this.from.y - this.to.y)) {
32920            // up - down
32921            xVia = this.from.x;
32922            if (this.from.y < this.to.y) {
32923              yVia = this.to.y - (1 - factor) * dy;
32924            } else {
32925              yVia = this.to.y + (1 - factor) * dy;
32926            }
32927          } else if (Math.abs(this.from.x - this.to.x) >
32927 Math.abs(this.from.y - this.to.y)) {
32928            // left - right
32929            if (this.from.x < this.to.x) {
32930              xVia = this.to.x - (1 - factor) * dx;
32931            } else {
32932              xVia = this.to.x + (1 - factor) * dx;
32933            }
32934            yVia = this.from.y;
32935          }
32936        } else if (type === 'horizontal') {
32937          if (this.from.x < this.to.x) {
32938            xVia = this.to.x - (1 - factor) * dx;
32939          } else {
32940            xVia = this.to.x + (1 - factor) * dx;
32941          }
32942          yVia = this.from.y;
32943        } else if (type === 'vertical') {
32944          xVia = this.from.x;
32945          if (this.from.y < this.to.y) {
32946            yVia = this.to.y - (1 - factor) * dy;
32947          } else {
32948            yVia = this.to.y + (1 - factor) * dy;
32949          }
32950        } else if (type === 'curvedCW') {
32951          dx = this.to.x - this.from.x;
32952          dy = this.from.y - this.to.y;
32953          var radius = Math.sqrt(dx * dx + dy * dy);
32954          var pi = Math.PI;
32955
32956          var originalAngle = Math.atan2(dy, dx);
32957          var myAngle = (originalAngle + (factor * 0.5 + 0.5) * pi) % (2 * pi);
32958
32959          xVia = this.from.x + (factor * 0.5 + 0.5) * radius * Math.sin(myAngle);
32960          yVia = this.from.y + (factor * 0.5 + 0.5) * radius * Math.cos(myAngle);
32961        } else if (type === 'curvedCCW') {
32962          dx = this.to.x - this.from.x;
32963          dy = this.from.y - this.to.y;
32964          var radius = Math.sqrt(dx * dx + dy * dy);
32965          var pi = Math.PI;
32966
32967          var originalAngle = Math.atan2(dy, dx);
32968          var myAngle = (originalAngle + (-factor * 0.5 + 0.5) * pi) % (2 * pi);
32969
32970          xVia = this.from.x + (factor * 0.5 + 0.5) * radius * Math.sin(myAngle);
32971          yVia = this.from.y + (factor * 0.5 + 0.5) * radius * Math.cos(myAngle);
32972        } else {
32973          // continuous
32974          if (Math.abs(this.from.x - this.to.x) <= Math.abs(this.from.y - this.to.y)) {
32975            if (this.from.y >= this.to.y) {
32976              if (this.from.x <= this.to.x) {
32977                xVia = this.from.x + factor * dy;
32978                yVia = this.from.y - factor * dy;
32979                xVia = this.to.x < xVia ? this.to.x : xVia;
32980              } else if (this.from.x > this.to.x) {
32981                xVia = this.from.x - factor * dy;
32982                yVia = this.from.y - factor * dy;
32983                xVia = this.to.x > xVia ? this.to.x : xVia;
32984              }
32985            } else if (this.from.y < this.to.y) {
32986              if (this.from.x <= this.to.x) {
32987                xVia = this.from.x + factor * dy;
32988                yVia = this.from.y + factor * dy;
32989                xVia = this.to.x < xVia ? this.to.x : xVia;
32990              } else if (this.from.x > this.to.x) {
32991                xVia = this.from.x - factor * dy;
32992                yVia = this.from.y + factor * dy;
32993                xVia = this.to.x > xVia ? this.to.x : xVia;
32994              }
32995            }
32996          } else if (Math.abs(this.from.x - this.to.x) > Math.abs(this.from.y - this.to.y)) {
32997            if (this.from.y >= this.to.y) {
32998              if (this.from.x <= this.to.x) {
32999                xVia = this.from.x + factor * dx;
33000                yVia = this.from.y - factor * dx;
33001                yVia = this.to.y > yVia ? this.to.y : yVia;
33002              } else if (this.from.x > this.to.x) {
33003                xVia = this.from.x - factor * dx;
33004                yVia = this.from.y - factor * dx;
33005                yVia = this.to.y > yVia ? this.to.y : yVia;
33006              }
33007            } else if (this.from.y < this.to.y) {
33008              if (this.from.x <= this.to.x) {
33009                xVia = this.from.x + factor * dx;
33010                yVia = this.from.y + factor * dx;
33011                yVia = this.to.y < yVia ? this.to.y : yVia;
33012              } else if (this.from.x > this.to.x) {
33013                xVia = this.from.x - factor * dx;
33014                yVia = this.from.y + factor * dx;
33015                yVia = this.to.y < yVia ? this.to.y : yVia;
33016              }
33017            }
33018          }
33019        }
33020        return { x: xVia, y: yVia };
33021      }
33022    }, {
33023      key: '_findBorderPosition',
33024      value: function _findBorderPosition(nearNode, ctx) {
33025        var options = arguments.length <= 2 || arguments[2] === undefined ? {} : arguments[2];
33026
33027        return this._findBorderPositionBezier(nearNode, ctx, options.via);
33028      }
33029    }, {
33030      key: '_getDistanceToEdge',
33031      value: function _getDistanceToEdge(x1, y1, x2, y2, x3, y3) {
33032        var via = arguments.length <= 6 || arguments[6] === undefined ? this._getViaCoordinates() : arguments[6];
33033        // x3,y3 is the point
33034        return this._getDistanceToBezierEdge(x1, y1, x2, y2, x3, y3, via);
33035      }
33036    }, {
33037      key: 'getPoint',
33038
33039      /**
33040       * Combined function of pointOnLine and pointOnBezier. This gives the coordinates of a point on the line at a certain percentage of the way
33041       * @param percentage
33042       * @param via
33043       * @returns {{x: number, y: number}}
33044       * @private
33045       */
33046      value: function getPoint(percentage) {
33047        var via = arguments.length <= 1 || arguments[1] === undefined ? this._getViaCoordinates() : arguments[1];
33048
33049        var t = percentage;
33050        var x = Math.pow(1 - t, 2) * this.from.x + 2 * t * (1 - t) * via.x + Math.pow(t, 2) * this.to.x;
33051        var y = Math.pow(1 - t, 2) * this.from.y + 2 * t * (1 - t) * via.y + Math.pow(t, 2) * this.to.y;
33052
33053        return { x: x, y: y };
33054      }
33055    }]);
33056
33057    return BezierEdgeStatic;
33058  })(_utilBezierEdgeBase2['default']);
33059
33060  exports['default'] = BezierEdgeStatic;
33061  module.exports = exports['default'];
33062
33063/***/ },
33064/* 89 */
33065/***/ function(module, exports, __webpack_require__) {
33066
33067  'use strict';
33068
33069  Object.defineProperty(exports, '__esModule', {
33070    value: true
33071  });
33072
33073  var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
33074
33075  var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; desc = parent = getter = undefined; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } };
33076
33077  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
33078
33079  function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
33080
33081  function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) subClass.__proto__ = superClass; }
33082
33083  var _utilEdgeBase = __webpack_require__(86);
33084
33085  var _utilEdgeBase2 = _interopRequireDefault(_utilEdgeBase);
33086
33087  var StraightEdge = (function (_EdgeBase) {
33088    _inherits(StraightEdge, _EdgeBase);
33089
33090    function StraightEdge(options, body, labelModule) {
33091      _classCallCheck(this, StraightEdge);
33092
33093      _get(Object.getPrototypeOf(StraightEdge.prototype), 'constructor', this).call(this, options, body, labelModule);
33094    }
33095
33096    _createClass(StraightEdge, [{
33097      key: '_line',
33098
33099      /**
33100       * Draw a line between two nodes
33101       * @param {CanvasRenderingContext2D} ctx
33102       * @private
33103       */
33104      value: function _line(ctx) {
33105        // draw a straight line
33106        ctx.beginPath();
33107        ctx.moveTo(this.from.x, this.from.y);
33108        ctx.lineTo(this.to.x, this.to.y);
33109        // draw shadow if enabled
33110        this.enableShadow(ctx);
33111        ctx.stroke();
33112        this.disableShadow(ctx);
33113        return undefined;
33114      }
33115    }, {
33116      key: 'getPoint',
33117
33118      /**
33119       * Combined function of pointOnLine and pointOnBezier. This gives the coordinates of a point on the line at a certain percentage of the way
33120       * @param percentage
33121       * @param via
33122       * @returns {{x: number, y: number}}
33123       * @private
33124       */
33125      value: function getPoint(percentage) {
33126        return {
33127          x: (1 - percentage) * this.from.x + percentage * this.to.x,
33128          y: (1 - percentage) * this.from.y + percentage * this.to.y
33129        };
33130      }
33131    }, {
33132      key: '_findBorderPosition',
33133      value: function _findBorderPosition(nearNode, ctx) {
33134        var node1 = this.to;
33135        var node2 = this.from;
33136        if (nearNode.id === this.from.id) {
33137          node1 = this.from;
33138          node2 = this.to;
33139        }
33140
33141        var angle = Math.atan2(node1.y - node2.y, node1.x - node2.x);
33142        var dx = node1.x - node2.x;
33143        var dy = node1.y - node2.y;
33144        var edgeSegmentLength = Math.sqrt(dx * dx + dy * dy);
33145        var toBorderDist = nearNode.distanceToBorder(ctx, angle);
33146        var toBorderPoint = (edgeSegmentLength - toBorderDist) / edgeSegmentLength;
33147
33148        var borderPos = {};
33149        borderPos.x = (1 - toBorderPoint) * node2.x + toBorderPoint * node1.x;
33150        borderPos.y = (1 - toBorderPoint) * node2.y + toBorderPoint * node1.y;
33151
33152        return borderPos;
33153      }
33154    }, {
33155      key: '_getDistanceToEdge',
33156      value: function _getDistanceToEdge(x1, y1, x2, y2, x3, y3) {
33157        // x3,y3 is the point
33158        return this._getDistanceToLine(x1, y1, x2, y2, x3, y3);
33159      }
33160    }]);
33161
33162    return StraightEdge;
33163  })(_utilEdgeBase2['default']);
33164
33165  exports['default'] = StraightEdge;
33166  module.exports = exports['default'];
33167
33168/***/ },
33169/* 90 */
33170/***/ function(module, exports, __webpack_require__) {
33171
33172  'use strict';
33173
33174  Object.defineProperty(exports, '__esModule', {
33175    value: true
33176  });
33177
33178  var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
33179
33180  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
33181
33182  function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
33183
33184  var _componentsPhysicsBarnesHutSolver = __webpack_require__(91);
33185
33186  var _componentsPhysicsBarnesHutSolver2 = _interopRequireDefault(_componentsPhysicsBarnesHutSolver);
33187
33188  var _componentsPhysicsRepulsionSolver = __webpack_require__(92);
33189
33190  var _componentsPhysicsRepulsionSolver2 = _interopRequireDefault(_componentsPhysicsRepulsionSolver);
33191
33192  var _componentsPhysicsHierarchicalRepulsionSolver = __webpack_require__(93);
33193
33194  var _componentsPhysicsHierarchicalRepulsionSolver2 = _interopRequireDefault(_componentsPhysicsHierarchicalRepulsionSolver);
33195
33196  var _componentsPhysicsSpringSolver = __webpack_require__(94);
33197
33198  var _componentsPhysicsSpringSolver2 = _interopRequireDefault(_componentsPhysicsSpringSolver);
33199
33200  var _componentsPhysicsHierarchicalSpringSolver = __webpack_require__(95);
33201
33202  var _componentsPhysicsHierarchicalSpringSolver2 = _interopRequireDefault(_componentsPhysicsHierarchicalSpringSolver);
33203
33204  var _componentsPhysicsCentralGravitySolver = __webpack_require__(96);
33205
33206  var _componentsPhysicsCentralGravitySolver2 = _interopRequireDefault(_componentsPhysicsCentralGravitySolver);
33207
33208  var _componentsPhysicsFA2BasedRepulsionSolver = __webpack_require__(97);
33209
33210  var _componentsPhysicsFA2BasedRepulsionSolver2 = _interopRequireDefault(_componentsPhysicsFA2BasedRepulsionSolver);
33211
33212  var _componentsPhysicsFA2BasedCentralGravitySolver = __webpack_require__(98);
33213
33214  var _componentsPhysicsFA2BasedCentralGravitySolver2 = _interopRequireDefault(_componentsPhysicsFA2BasedCentralGravitySolver);
33215
33216  var util = __webpack_require__(1);
33217
33218  var PhysicsEngine = (function () {
33219    function PhysicsEngine(body) {
33220      _classCallCheck(this, PhysicsEngine);
33221
33222      this.body = body;
33223      this.physicsBody = { physicsNodeIndices: [], physicsEdgeIndices: [], forces: {}, velocities: {} };
33224
33225      this.physicsEnabled = true;
33226      this.simulationInterval = 1000 / 60;
33227      this.requiresTimeout = true;
33228      this.previousStates = {};
33229      this.referenceState = {};
33230      this.freezeCache = {};
33231      this.renderTimer = undefined;
33232
33233      // parameters for the adaptive timestep
33234      this.adaptiveTimestep = false;
33235      this.adaptiveTimestepEnabled = false;
33236      this.adaptiveCounter = 0;
33237      this.adaptiveInterval = 3;
33238
33239      this.stabilized = false;
33240      this.startedStabilization = false;
33241      this.stabilizationIterations = 0;
33242      this.ready = false; // will be set to true if the stabilize
33243
33244      // default options
33245      this.options = {};
33246      this.defaultOptions = {
33247        enabled: true,
33248        barnesHut: {
33249          theta: 0.5,
33250          gravitationalConstant: -2000,
33251          centralGravity: 0.3,
33252          springLength: 95,
33253          springConstant: 0.04,
33254          damping: 0.09,
33255          avoidOverlap: 0
33256        },
33257        forceAtlas2Based: {
33258          theta: 0.5,
33259          gravitationalConstant: -50,
33260          centralGravity: 0.01,
33261          springConstant: 0.08,
33262          springLength: 100,
33263          damping: 0.4,
33264          avoidOverlap: 0
33265        },
33266        repulsion: {
33267          centralGravity: 0.2,
33268          springLength: 200,
33269          springConstant: 0.05,
33270          nodeDistance: 100,
33271          damping: 0.09,
33272          avoidOverlap: 0
33273        },
33274        hierarchicalRepulsion: {
33275          centralGravity: 0.0,
33276          springLength: 100,
33277          springConstant: 0.01,
33278          nodeDistance: 120,
33279          damping: 0.09
33280        },
33281        maxVelocity: 50,
33282        minVelocity: 0.75, // px/s
33283        solver: 'barnesHut',
33284        stabilization: {
33285          enabled: true,
33286          iterations: 1000, // maximum number of iteration to stabilize
33287          updateInterval: 50,
33288          onlyDynamicEdges: false,
33289          fit: true
33290        },
33291        timestep: 0.5,
33292        adaptiveTimestep: true
33293      };
33294      util.extend(this.options, this.defaultOptions);
33295      this.timestep = 0.5;
33296      this.layoutFailed = false;
33297
33298      this.bindEventListeners();
33299    }
33300
33301    _createClass(PhysicsEngine, [{
33302      key: 'bindEventListeners',
33303      value: function bindEventListeners() {
33304        var _this = this;
33305
33306        this.body.emitter.on('initPhysics', function () {
33307          _this.initPhysics();
33308        });
33309        this.body.emitter.on('_layoutFailed', function () {
33310          _this.layoutFailed = true;
33311        });
33312        this.body.emitter.on('resetPhysics', function () {
33313          _this.stopSimulation();_this.ready = false;
33314        });
33315        this.body.emitter.on('disablePhysics', function () {
33316          _this.physicsEnabled = false;_this.stopSimulation();
33317        });
33318        this.body.emitter.on('restorePhysics', function () {
33319          _this.setOptions(_this.options);
33320          if (_this.ready === true) {
33321            _this.startSimulation();
33322          }
33323        });
33324        this.body.emitter.on('startSimulation', function () {
33325          if (_this.ready === true) {
33326            _this.startSimulation();
33327          }
33328        });
33329        this.body.emitter.on('stopSimulation', function () {
33330          _this.stopSimulation();
33331        });
33332        this.body.emitter.on('destroy', function () {
33333          _this.stopSimulation(false);
33334          _this.body.emitter.off();
33335        });
33336      }
33337    }, {
33338      key: 'setOptions',
33339
33340      /**
33341       * set the physics options
33342       * @param options
33343       */
33344      value: function setOptions(options) {
33345        if (options !== undefined) {
33346          if (options === false) {
33347            this.options.enabled = false;
33348            this.physicsEnabled = false;
33349            this.stopSimulation();
33350          } else {
33351            this.physicsEnabled = true;
33352            util.selectiveNotDeepExtend(['stabilization'], this.options, options);
33353            util.mergeOptions(this.options, options, 'stabilization');
33354
33355            if (options.enabled === undefined) {
33356              this.options.enabled = true;
33357            }
33358
33359            if (this.options.enabled === false) {
33360              this.physicsEnabled = false;
33361              this.stopSimulation();
33362            }
33363
33364            // set the timestep
33365            this.timestep = this.options.timestep;
33366          }
33367        }
33368        this.init();
33369      }
33370    }, {
33371      key: 'init',
33372
33373      /**
33374       * configure the engine.
33375       */
33376      value: function init() {
33377        var options;
33378        if (this.options.solver === 'forceAtlas2Based') {
33379          options = this.options.forceAtlas2Based;
33380          this.nodesSolver = new _componentsPhysicsFA2BasedRepulsionSolver2['default'](this.body, this.physicsBody, options);
33381          this.edgesSolver = new _componentsPhysicsSpringSolver2['default'](this.body, this.physicsBody, options);
33382          this.gravitySolver = new _componentsPhysicsFA2BasedCentralGravitySolver2['default'](this.body, this.physicsBody, options);
33383        } else if (this.options.solver === 'repulsion') {
33384          options = this.options.repulsion;
33385          this.nodesSolver = new _componentsPhysicsRepulsionSolver2['default'](this.body, this.physicsBody, options);
33386          this.edgesSolver = new _componentsPhysicsSpringSolver2['default'](this.body, this.physicsBody, options);
33387          this.gravitySolver = new _componentsPhysicsCentralGravitySolver2['default'](this.body, this.physicsBody, options);
33388        } else if (this.options.solver === 'hierarchicalRepulsion') {
33389          options = this.options.hierarchicalRepulsion;
33390          this.nodesSolver = new _componentsPhysicsHierarchicalRepulsionSolver2['default'](this.body, this.physicsBody, options);
33391          this.edgesSolver = new _componentsPhysicsHierarchicalSpringSolver2['default'](this.body, this.physicsBody, options);
33392          this.gravitySolver = new _componentsPhysicsCentralGravitySolver2['default'](this.body, this.physicsBody, options);
33393        } else {
33394          // barnesHut
33395          options = this.options.barnesHut;
33396          this.nodesSolver = new _componentsPhysicsBarnesHutSolver2['default'](this.body, this.physicsBody, options);
33397          this.edgesSolver = new _componentsPhysicsSpringSolver2['default'](this.body, this.physicsBody, options);
33398          this.gravitySolver = new _componentsPhysicsCentralGravitySolver2['default'](this.body, this.physicsBody, options);
33399        }
33400
33401        this.modelOptions = options;
33402      }
33403    }, {
33404      key: 'initPhysics',
33405
33406      /**
33407       * initialize the engine
33408       */
33409      value: function initPhysics() {
33410        if (this.physicsEnabled === true && this.options.enabled === true) {
33411          if (this.options.stabilization.enabled === true) {
33412            this.stabilize();
33413          } else {
33414            this.stabilized = false;
33415            this.ready = true;
33416            this.body.emitter.emit('fit', {}, this.layoutFailed); // if the layout failed, we use the approximation for the zoom
33417            this.startSimulation();
33418          }
33419        } else {
33420          this.ready = true;
33421          this.body.emitter.emit('fit');
33422        }
33423      }
33424    }, {
33425      key: 'startSimulation',
33426
33427      /**
33428       * Start the simulation
33429       */
33430      value: function startSimulation() {
33431        if (this.physicsEnabled === true && this.options.enabled === true) {
33432          this.stabilized = false;
33433
33434          // when visible, adaptivity is disabled.
33435          this.adaptiveTimestep = false;
33436
33437          // this sets the width of all nodes initially which could be required for the avoidOverlap
33438          this.body.emitter.emit('_resizeNodes');
33439          if (this.viewFunction === undefined) {
33440            this.viewFunction = this.simulationStep.bind(this);
33441            this.body.emitter.on('initRedraw', this.viewFunction);
33442            this.body.emitter.emit('_startRendering');
33443          }
33444        } else {
33445          this.body.emitter.emit('_redraw');
33446        }
33447      }
33448    }, {
33449      key: 'stopSimulation',
33450
33451      /**
33452       * Stop the simulation, force stabilization.
33453       */
33454      value: function stopSimulation() {
33455        var emit = arguments.length <= 0 || arguments[0] === undefined ? true : arguments[0];
33456
33457        this.stabilized = true;
33458        if (emit === true) {
33459          this._emitStabilized();
33460        }
33461        if (this.viewFunction !== undefined) {
33462          this.body.emitter.off('initRedraw', this.viewFunction);
33463          this.viewFunction = undefined;
33464          if (emit === true) {
33465            this.body.emitter.emit('_stopRendering');
33466          }
33467        }
33468      }
33469    }, {
33470      key: 'simulationStep',
33471
33472      /**
33473       * The viewFunction inserts this step into each renderloop. It calls the physics tick and handles the cleanup at stabilized.
33474       *
33475       */
33476      value: function simulationStep() {
33477        // check if the physics have settled
33478        var startTime = Date.now();
33479        this.physicsTick();
33480        var physicsTime = Date.now() - startTime;
33481
33482        // run double speed if it is a little graph
33483        if ((physicsTime < 0.4 * this.simulationInterval || this.runDoubleSpeed === true) && this.stabilized === false) {
33484          this.physicsTick();
33485
33486          // this makes sure there is no jitter. The decision is taken once to run it at double speed.
33487          this.runDoubleSpeed = true;
33488        }
33489
33490        if (this.stabilized === true) {
33491          this.stopSimulation();
33492        }
33493      }
33494    }, {
33495      key: '_emitStabilized',
33496
33497      /**
33498       * trigger the stabilized event.
33499       * @private
33500       */
33501      value: function _emitStabilized() {
33502        var _this2 = this;
33503
33504        var amountOfIterations = arguments.length <= 0 || arguments[0] === undefined ? this.stabilizationIterations : arguments[0];
33505
33506        if (this.stabilizationIterations > 1 || this.startedStabilization === true) {
33507          setTimeout(function () {
33508            _this2.body.emitter.emit('stabilized', { iterations: amountOfIterations });
33509            _this2.startedStabilization = false;
33510            _this2.stabilizationIterations = 0;
33511          }, 0);
33512        }
33513      }
33514    }, {
33515      key: 'physicsTick',
33516
33517      /**
33518       * A single simulation step (or 'tick') in the physics simulation
33519       *
33520       * @private
33521       */
33522      value: function physicsTick() {
33523        // this is here to ensure that there is no start event when the network is already stable.
33524        if (this.startedStabilization === false) {
33525          this.body.emitter.emit('startStabilizing');
33526          this.startedStabilization = true;
33527        }
33528
33529        if (this.stabilized === false) {
33530          // adaptivity means the timestep adapts to the situation, only applicable for stabilization
33531          if (this.adaptiveTimestep === true && this.adaptiveTimestepEnabled === true) {
33532            // this is the factor for increasing the timestep on success.
33533            var factor = 1.2;
33534
33535            // we assume the adaptive interval is
33536            if (this.adaptiveCounter % this.adaptiveInterval === 0) {
33537              // we leave the timestep stable for "interval" iterations.
33538              // first the big step and revert. Revert saves the reference state.
33539              this.timestep = 2 * this.timestep;
33540              this.calculateForces();
33541              this.moveNodes();
33542              this.revert();
33543
33544              // now the normal step. Since this is the last step, it is the more stable one and we will take this.
33545              this.timestep = 0.5 * this.timestep;
33546
33547              // since it's half the step, we do it twice.
33548              this.calculateForces();
33549              this.moveNodes();
33550              this.calculateForces();
33551              this.moveNodes();
33552
33553              // we compare the two steps. if it is acceptable we double the step.
33554              if (this._evaluateStepQuality() === true) {
33555                this.timestep = factor * this.timestep;
33556              } else {
33557                // if not, we decrease the step to a minimum of the options timestep.
33558                // if the decreased timestep is smaller than the options step, we do not reset the counter
33559                // we assume that the options timestep is stable enough.
33560                if (this.timestep / factor < this.options.timestep) {
33561                  this.timestep = this.options.timestep;
33562                } else {
33563                  // if the timestep was larger than 2 times the option one we check the adaptivity again to ensure
33564                  // that large instabilities do not form.
33565                  this.adaptiveCounter = -1; // check again next iteration
33566                  this.timestep = Math.max(this.options.timestep, this.timestep / factor);
33567                }
33568              }
33569            } else {
33570              // normal step, keeping timestep constant
33571              this.calculateForces();
33572              this.moveNodes();
33573            }
33574
33575            // increment the counter
33576            this.adaptiveCounter += 1;
33577          } else {
33578            // case for the static timestep, we reset it to the one in options and take a normal step.
33579            this.timestep = this.options.timestep;
33580            this.calculateForces();
33581            this.moveNodes();
33582          }
33583
33584          // determine if the network has stabilzied
33585          if (this.stabilized === true) {
33586            this.revert();
33587          }
33588
33589          this.stabilizationIterations++;
33590        }
33591      }
33592    }, {
33593      key: 'updatePhysicsData',
33594
33595      /**
33596       * 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.
33597       *
33598       * @private
33599       */
33600      value: function updatePhysicsData() {
33601        this.physicsBody.forces = {};
33602        this.physicsBody.physicsNodeIndices = [];
33603        this.physicsBody.physicsEdgeIndices = [];
33604        var nodes = this.body.nodes;
33605        var edges = this.body.edges;
33606
33607        // get node indices for physics
33608        for (var nodeId in nodes) {
33609          if (nodes.hasOwnProperty(nodeId)) {
33610            if (nodes[nodeId].options.physics === true) {
33611              this.physicsBody.physicsNodeIndices.push(nodeId);
33612            }
33613          }
33614        }
33615
33616        // get edge indices for physics
33617        for (var edgeId in edges) {
33618          if (edges.hasOwnProperty(edgeId)) {
33619            if (edges[edgeId].options.physics === true) {
33620              this.physicsBody.physicsEdgeIndices.push(edgeId);
33621            }
33622          }
33623        }
33624
33625        // get the velocity and the forces vector
33626        for (var i = 0; i < this.physicsBody.physicsNodeIndices.length; i++) {
33627          var nodeId = this.physicsBody.physicsNodeIndices[i];
33628          this.physicsBody.forces[nodeId] = { x: 0, y: 0 };
33629
33630          // forces can be reset because they are recalculated. Velocities have to persist.
33631          if (this.physicsBody.velocities[nodeId] === undefined) {
33632            this.physicsBody.velocities[nodeId] = { x: 0, y: 0 };
33633          }
33634        }
33635
33636        // clean deleted nodes from the velocity vector
33637        for (var nodeId in this.physicsBody.velocities) {
33638          if (nodes[nodeId] === undefined) {
33639            delete this.physicsBody.velocities[nodeId];
33640          }
33641        }
33642      }
33643    }, {
33644      key: 'revert',
33645
33646      /**
33647       * Revert the simulation one step. This is done so after stabilization, every new start of the simulation will also say stabilized.
33648       */
33649      value: function revert() {
33650        var nodeIds = Object.keys(this.previousStates);
33651        var nodes = this.body.nodes;
33652        var velocities = this.physicsBody.velocities;
33653        this.referenceState = {};
33654
33655        for (var i = 0; i < nodeIds.length; i++) {
33656          var nodeId = nodeIds[i];
33657          if (nodes[nodeId] !== undefined) {
33658            if (nodes[nodeId].options.physics === true) {
33659              this.referenceState[nodeId] = {
33660                positions: { x: nodes[nodeId].x, y: nodes[nodeId].y }
33661              };
33662              velocities[nodeId].x = this.previousStates[nodeId].vx;
33663              velocities[nodeId].y = this.previousStates[nodeId].vy;
33664              nodes[nodeId].x = this.previousStates[nodeId].x;
33665              nodes[nodeId].y = this.previousStates[nodeId].y;
33666            }
33667          } else {
33668            delete this.previousStates[nodeId];
33669          }
33670        }
33671      }
33672    }, {
33673      key: '_evaluateStepQuality',
33674
33675      /**
33676       * This compares the reference state to the current state
33677       */
33678      value: function _evaluateStepQuality() {
33679        var dx = undefined,
33680            dy = undefined,
33681            dpos = undefined;
33682        var nodes = this.body.nodes;
33683        var reference = this.referenceState;
33684        var posThreshold = 0.3;
33685
33686        for (var nodeId in this.referenceState) {
33687          if (this.referenceState.hasOwnProperty(nodeId) && nodes[nodeId] !== undefined) {
33688            dx = nodes[nodeId].x - reference[nodeId].positions.x;
33689            dy = nodes[nodeId].y - reference[nodeId].positions.y;
33690
33691            dpos = Math.sqrt(Math.pow(dx, 2) + Math.pow(dy, 2));
33692
33693            if (dpos > posThreshold) {
33694              return false;
33695            }
33696          }
33697        }
33698        return true;
33699      }
33700    }, {
33701      key: 'moveNodes',
33702
33703      /**
33704       * move the nodes one timestap and check if they are stabilized
33705       * @returns {boolean}
33706       */
33707      value: function moveNodes() {
33708        var nodeIndices = this.physicsBody.physicsNodeIndices;
33709        var maxVelocity = this.options.maxVelocity ? this.options.maxVelocity : 1e9;
33710        var maxNodeVelocity = 0;
33711        var averageNodeVelocity = 0;
33712
33713        // the velocity threshold (energy in the system) for the adaptivity toggle
33714        var velocityAdaptiveThreshold = 5;
33715
33716        for (var i = 0; i < nodeIndices.length; i++) {
33717          var nodeId = nodeIndices[i];
33718          var nodeVelocity = this._performStep(nodeId, maxVelocity);
33719          // stabilized is true if stabilized is true and velocity is smaller than vmin --> all nodes must be stabilized
33720          maxNodeVelocity = Math.max(maxNodeVelocity, nodeVelocity);
33721          averageNodeVelocity += nodeVelocity;
33722        }
33723
33724        // evaluating the stabilized and adaptiveTimestepEnabled conditions
33725        this.adaptiveTimestepEnabled = averageNodeVelocity / nodeIndices.length < velocityAdaptiveThreshold;
33726        this.stabilized = maxNodeVelocity < this.options.minVelocity;
33727      }
33728    }, {
33729      key: '_performStep',
33730
33731      /**
33732       * Perform the actual step
33733       *
33734       * @param nodeId
33735       * @param maxVelocity
33736       * @returns {number}
33737       * @private
33738       */
33739      value: function _performStep(nodeId, maxVelocity) {
33740        var node = this.body.nodes[nodeId];
33741        var timestep = this.timestep;
33742        var forces = this.physicsBody.forces;
33743        var velocities = this.physicsBody.velocities;
33744
33745        // store the state so we can revert
33746        this.previousStates[nodeId] = { x: node.x, y: node.y, vx: velocities[nodeId].x, vy: velocities[nodeId].y };
33747
33748        if (node.options.fixed.x === false) {
33749          var dx = this.modelOptions.damping * velocities[nodeId].x; // damping force
33750          var ax = (forces[nodeId].x - dx) / node.options.mass; // acceleration
33751          velocities[nodeId].x += ax * timestep; // velocity
33752          velocities[nodeId].x = Math.abs(velocities[nodeId].x) > maxVelocity ? velocities[nodeId].x > 0 ? maxVelocity : -maxVelocity : velocities[nodeId].x;
33753          node.x += velocities[nodeId].x * timestep; // position
33754        } else {
33755          forces[nodeId].x = 0;
33756          velocities[nodeId].x = 0;
33757        }
33758
33759        if (node.options.fixed.y === false) {
33760          var dy = this.modelOptions.damping * velocities[nodeId].y; // damping force
33761          var ay = (forces[nodeId].y - dy) / node.options.mass; // acceleration
33762          velocities[nodeId].y += ay * timestep; // velocity
33763          velocities[nodeId].y = Math.abs(velocities[nodeId].y) > maxVelocity ? velocities[nodeId].y > 0 ? maxVelocity : -maxVelocity : velocities[nodeId].y;
33764          node.y += velocities[nodeId].y * timestep; // position
33765        } else {
33766          forces[nodeId].y = 0;
33767          velocities[nodeId].y = 0;
33768        }
33769
33770        var totalVelocity = Math.sqrt(Math.pow(velocities[nodeId].x, 2) + Math.pow(velocities[nodeId].y, 2));
33771        return totalVelocity;
33772      }
33773    }, {
33774      key: 'calculateForces',
33775
33776      /**
33777       * calculate the forces for one physics iteration.
33778       */
33779      value: function calculateForces() {
33780        this.gravitySolver.solve();
33781        this.nodesSolver.solve();
33782        this.edgesSolver.solve();
33783      }
33784    }, {
33785      key: '_freezeNodes',
33786
33787      /**
33788       * When initializing and stabilizing, we can freeze nodes with a predefined position. This greatly speeds up stabilization
33789       * because only the supportnodes for the smoothCurves have to settle.
33790       *
33791       * @private
33792       */
33793      value: function _freezeNodes() {
33794        var nodes = this.body.nodes;
33795        for (var id in nodes) {
33796          if (nodes.hasOwnProperty(id)) {
33797            if (nodes[id].x && nodes[id].y) {
33798              this.freezeCache[id] = { x: nodes[id].options.fixed.x, y: nodes[id].options.fixed.y };
33799              nodes[id].options.fixed.x = true;
33800              nodes[id].options.fixed.y = true;
33801            }
33802          }
33803        }
33804      }
33805    }, {
33806      key: '_restoreFrozenNodes',
33807
33808      /**
33809       * Unfreezes the nodes that have been frozen by _freezeDefinedNodes.
33810       *
33811       * @private
33812       */
33813      value: function _restoreFrozenNodes() {
33814        var nodes = this.body.nodes;
33815        for (var id in nodes) {
33816          if (nodes.hasOwnProperty(id)) {
33817            if (this.freezeCache[id] !== undefined) {
33818              nodes[id].options.fixed.x = this.freezeCache[id].x;
33819              nodes[id].options.fixed.y = this.freezeCache[id].y;
33820            }
33821          }
33822        }
33823        this.freezeCache = {};
33824      }
33825    }, {
33826      key: 'stabilize',
33827
33828      /**
33829       * Find a stable position for all nodes
33830       * @private
33831       */
33832      value: function stabilize() {
33833        var _this3 = this;
33834
33835        var iterations = arguments.length <= 0 || arguments[0] === undefined ? this.options.stabilization.iterations : arguments[0];
33836
33837        if (typeof iterations !== 'number') {
33838          console.log('The stabilize method needs a numeric amount of iterations. Switching to default: ', this.options.stabilization.iterations);
33839          iterations = this.options.stabilization.iterations;
33840        }
33841
33842        if (this.physicsBody.physicsNodeIndices.length === 0) {
33843          this.ready = true;
33844          return;
33845        }
33846
33847        // enable adaptive timesteps
33848        this.adaptiveTimestep = true && this.options.adaptiveTimestep;
33849
33850        // this sets the width of all nodes initially which could be required for the avoidOverlap
33851        this.body.emitter.emit('_resizeNodes');
33852
33853        // stop the render loop
33854        this.stopSimulation();
33855
33856        // set stabilze to false
33857        this.stabilized = false;
33858
33859        // block redraw requests
33860        this.body.emitter.emit('_blockRedraw');
33861        this.targetIterations = iterations;
33862
33863        // start the stabilization
33864        if (this.options.stabilization.onlyDynamicEdges === true) {
33865          this._freezeNodes();
33866        }
33867        this.stabilizationIterations = 0;
33868
33869        setTimeout(function () {
33870          return _this3._stabilizationBatch();
33871        }, 0);
33872      }
33873    }, {
33874      key: '_stabilizationBatch',
33875
33876      /**
33877       * One batch of stabilization
33878       * @private
33879       */
33880      value: function _stabilizationBatch() {
33881        // this is here to ensure that there is at least one start event.
33882        if (this.startedStabilization === false) {
33883          this.body.emitter.emit('startStabilizing');
33884          this.startedStabilization = true;
33885        }
33886
33887        var count = 0;
33888        while (this.stabilized === false && count < this.options.stabilization.updateInterval && this.stabilizationIterations < this.targetIterations) {
33889          this.physicsTick();
33890          count++;
33891        }
33892
33893        if (this.stabilized === false && this.stabilizationIterations < this.targetIterations) {
33894          this.body.emitter.emit('stabilizationProgress', { iterations: this.stabilizationIterations, total: this.targetIterations });
33895          setTimeout(this._stabilizationBatch.bind(this), 0);
33896        } else {
33897          this._finalizeStabilization();
33898        }
33899      }
33900    }, {
33901      key: '_finalizeStabilization',
33902
33903      /**
33904       * Wrap up the stabilization, fit and emit the events.
33905       * @private
33906       */
33907      value: function _finalizeStabilization() {
33908        this.body.emitter.emit('_allowRedraw');
33909        if (this.options.stabilization.fit === true) {
33910          this.body.emitter.emit('fit');
33911        }
33912
33913        if (this.options.stabilization.onlyDynamicEdges === true) {
33914          this._restoreFrozenNodes();
33915        }
33916
33917        this.body.emitter.emit('stabilizationIterationsDone');
33918        this.body.emitter.emit('_requestRedraw');
33919
33920        if (this.stabilized === true) {
33921          this._emitStabilized();
33922        } else {
33923          this.startSimulation();
33924        }
33925
33926        this.ready = true;
33927      }
33928    }]);
33929
33930    return PhysicsEngine;
33931  })();
33932
33933  exports['default'] = PhysicsEngine;
33934  module.exports = exports['default'];
33935
33936/***/ },
33937/* 91 */
33938/***/ function(module, exports) {
33939
33940  "use strict";
33941
33942  Object.defineProperty(exports, "__esModule", {
33943    value: true
33944  });
33945
33946  var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
33947
33948  function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
33949
33950  var BarnesHutSolver = (function () {
33951    function BarnesHutSolver(body, physicsBody, options) {
33952      _classCallCheck(this, BarnesHutSolver);
33953
33954      this.body = body;
33955      this.physicsBody = physicsBody;
33956      this.barnesHutTree;
33957      this.setOptions(options);
33958      this.randomSeed = 5;
33959    }
33960
33961    _createClass(BarnesHutSolver, [{
33962      key: "setOptions",
33963      value: function setOptions(options) {
33964        this.options = options;
33965        this.thetaInversed = 1 / this.options.theta;
33966        this.overlapAvoidanceFactor = 1 - Math.max(0, Math.min(1, this.options.avoidOverlap)); // if 1 then min distance = 0.5, if 0.5 then min distance = 0.5 + 0.5*node.shape.radius
33967      }
33968    }, {
33969      key: "seededRandom",
33970      value: function seededRandom() {
33971        var x = Math.sin(this.randomSeed++) * 10000;
33972        return x - Math.floor(x);
33973      }
33974    }, {
33975      key: "solve",
33976
33977      /**
33978       * This function calculates the forces the nodes apply on eachother based on a gravitational model.
33979       * The Barnes Hut method is used to speed up this N-body simulation.
33980       *
33981       * @private
33982       */
33983      value: function solve() {
33984        if (this.options.gravitationalConstant !== 0 && this.physicsBody.physicsNodeIndices.length > 0) {
33985          var node = undefined;
33986          var nodes = this.body.nodes;
33987          var nodeIndices = this.physicsBody.physicsNodeIndices;
33988          var nodeCount = nodeIndices.length;
33989
33990          // create the tree
33991          var barnesHutTree = this._formBarnesHutTree(nodes, nodeIndices);
33992
33993          // for debugging
33994          this.barnesHutTree = barnesHutTree;
33995
33996          // place the nodes one by one recursively
33997          for (var i = 0; i < nodeCount; i++) {
33998            node = nodes[nodeIndices[i]];
33999            if (node.options.mass > 0) {
34000              // starting with root is irrelevant, it never passes the BarnesHutSolver condition
34001              this._getForceContribution(barnesHutTree.root.children.NW, node);
34002              this._getForceContribution(barnesHutTree.root.children.NE, node);
34003              this._getForceContribution(barnesHutTree.root.children.SW, node);
34004              this._getForceContribution(barnesHutTree.root.children.SE, node);
34005            }
34006          }
34007        }
34008      }
34009    }, {
34010      key: "_getForceContribution",
34011
34012      /**
34013       * This function traverses the barnesHutTree. It checks when it can approximate distant nodes with their center of mass.
34014       * If a region contains a single node, we check if it is not itself, then we apply the force.
34015       *
34016       * @param parentBranch
34017       * @param node
34018       * @private
34019       */
34020      value: function _getForceContribution(parentBranch, node) {
34021        // we get no force contribution from an empty region
34022        if (parentBranch.childrenCount > 0) {
34023          var dx = undefined,
34024              dy = undefined,
34025              distance = undefined;
34026
34027          // get the distance from the center of mass to the node.
34028          dx = parentBranch.centerOfMass.x - node.x;
34029          dy = parentBranch.centerOfMass.y - node.y;
34030          distance = Math.sqrt(dx * dx + dy * dy);
34031
34032          // BarnesHutSolver condition
34033          // original condition : s/d < theta = passed  ===  d/s > 1/theta = passed
34034          // calcSize = 1/s --> d * 1/s > 1/theta = passed
34035          if (distance * parentBranch.calcSize > this.thetaInversed) {
34036            this._calculateForces(distance, dx, dy, node, parentBranch);
34037          } else {
34038            // Did not pass the condition, go into children if available
34039            if (parentBranch.childrenCount === 4) {
34040              this._getForceContribution(parentBranch.children.NW, node);
34041              this._getForceContribution(parentBranch.children.NE, node);
34042              this._getForceContribution(parentBranch.children.SW, node);
34043              this._getForceContribution(parentBranch.children.SE, node);
34044            } else {
34045              // parentBranch must have only one node, if it was empty we wouldnt be here
34046              if (parentBranch.children.data.id != node.id) {
34047                // if it is not self
34048                this._calculateForces(distance, dx, dy, node, parentBranch);
34049              }
34050            }
34051          }
34052        }
34053      }
34054    }, {
34055      key: "_calculateForces",
34056
34057      /**
34058       * Calculate the forces based on the distance.
34059       *
34060       * @param distance
34061       * @param dx
34062       * @param dy
34063       * @param node
34064       * @param parentBranch
34065       * @private
34066       */
34067      value: function _calculateForces(distance, dx, dy, node, parentBranch) {
34068        if (distance === 0) {
34069          distance = 0.1;
34070          dx = distance;
34071        }
34072
34073        if (this.overlapAvoidanceFactor < 1) {
34074          distance = Math.max(0.1 + this.overlapAvoidanceFactor * node.shape.radius, distance - node.shape.radius);
34075        }
34076
34077        // the dividing by the distance cubed instead of squared allows us to get the fx and fy components without sines and cosines
34078        // it is shorthand for gravityforce with distance squared and fx = dx/distance * gravityForce
34079        var gravityForce = this.options.gravitationalConstant * parentBranch.mass * node.options.mass / Math.pow(distance, 3);
34080        var fx = dx * gravityForce;
34081        var fy = dy * gravityForce;
34082
34083        this.physicsBody.forces[node.id].x += fx;
34084        this.physicsBody.forces[node.id].y += fy;
34085      }
34086    }, {
34087      key: "_formBarnesHutTree",
34088
34089      /**
34090       * This function constructs the barnesHut tree recursively. It creates the root, splits it and starts placing the nodes.
34091       *
34092       * @param nodes
34093       * @param nodeIndices
34094       * @private
34095       */
34096      value: function _formBarnesHutTree(nodes, nodeIndices) {
34097        var node = undefined;
34098        var nodeCount = nodeIndices.length;
34099
34100        var minX = nodes[nodeIndices[0]].x;
34101        var minY = nodes[nodeIndices[0]].y;
34102        var maxX = nodes[nodeIndices[0]].x;
34103        var maxY = nodes[nodeIndices[0]].y;
34104
34105        // get the range of the nodes
34106        for (var i = 1; i < nodeCount; i++) {
34107          var x = nodes[nodeIndices[i]].x;
34108          var y = nodes[nodeIndices[i]].y;
34109          if (nodes[nodeIndices[i]].options.mass > 0) {
34110            if (x < minX) {
34111              minX = x;
34112            }
34113            if (x > maxX) {
34114              maxX = x;
34115            }
34116            if (y < minY) {
34117              minY = y;
34118            }
34119            if (y > maxY) {
34120              maxY = y;
34121            }
34122          }
34123        }
34124        // make the range a square
34125        var sizeDiff = Math.abs(maxX - minX) - Math.abs(maxY - minY); // difference between X and Y
34126        if (sizeDiff > 0) {
34127          minY -= 0.5 * sizeDiff;
34128          maxY += 0.5 * sizeDiff;
34129        } // xSize > ySize
34130        else {
34131          minX += 0.5 * sizeDiff;
34132          maxX -= 0.5 * sizeDiff;
34133        } // xSize < ySize
34134
34135        var minimumTreeSize = 1e-5;
34136        var rootSize = Math.max(minimumTreeSize, Math.abs(maxX - minX));
34137        var halfRootSize = 0.5 * rootSize;
34138        var centerX = 0.5 * (minX + maxX),
34139            centerY = 0.5 * (minY + maxY);
34140
34141        // construct the barnesHutTree
34142        var barnesHutTree = {
34143          root: {
34144            centerOfMass: { x: 0, y: 0 },
34145            mass: 0,
34146            range: {
34147              minX: centerX - halfRootSize, maxX: centerX + halfRootSize,
34148              minY: centerY - halfRootSize, maxY: centerY + halfRootSize
34149            },
34150            size: rootSize,
34151            calcSize: 1 / rootSize,
34152            children: { data: null },
34153            maxWidth: 0,
34154            level: 0,
34155            childrenCount: 4
34156          }
34157        };
34158        this._splitBranch(barnesHutTree.root);
34159
34160        // place the nodes one by one recursively
34161        for (var i = 0; i < nodeCount; i++) {
34162          node = nodes[nodeIndices[i]];
34163          if (node.options.mass > 0) {
34164            this._placeInTree(barnesHutTree.root, node);
34165          }
34166        }
34167
34168        // make global
34169        return barnesHutTree;
34170      }
34171    }, {
34172      key: "_updateBranchMass",
34173
34174      /**
34175       * this updates the mass of a branch. this is increased by adding a node.
34176       *
34177       * @param parentBranch
34178       * @param node
34179       * @private
34180       */
34181      value: function _updateBranchMass(parentBranch, node) {
34182        var totalMass = parentBranch.mass + node.options.mass;
34183        var totalMassInv = 1 / totalMass;
34184
34185        parentBranch.centerOfMass.x = parentBranch.centerOfMass.x * parentBranch.mass + node.x * node.options.mass;
34186        parentBranch.centerOfMass.x *= totalMassInv;
34187
34188        parentBranch.centerOfMass.y = parentBranch.centerOfMass.y * parentBranch.mass + node.y * node.options.mass;
34189        parentBranch.centerOfMass.y *= totalMassInv;
34190
34191        parentBranch.mass = totalMass;
34192        var biggestSize = Math.max(Math.max(node.height, node.radius), node.width);
34193        parentBranch.maxWidth = parentBranch.maxWidth < biggestSize ? biggestSize : parentBranch.maxWidth;
34194      }
34195    }, {
34196      key: "_placeInTree",
34197
34198      /**
34199       * determine in which branch the node will be placed.
34200       *
34201       * @param parentBranch
34202       * @param node
34203       * @param skipMassUpdate
34204       * @private
34205       */
34206      value: function _placeInTree(parentBranch, node, skipMassUpdate) {
34207        if (skipMassUpdate != true || skipMassUpdate === undefined) {
34208          // update the mass of the branch.
34209          this._updateBranchMass(parentBranch, node);
34210        }
34211
34212        if (parentBranch.children.NW.range.maxX > node.x) {
34213          // in NW or SW
34214          if (parentBranch.children.NW.range.maxY > node.y) {
34215            // in NW
34216            this._placeInRegion(parentBranch, node, "NW");
34217          } else {
34218            // in SW
34219            this._placeInRegion(parentBranch, node, "SW");
34220          }
34221        } else {
34222          // in NE or SE
34223          if (parentBranch.children.NW.range.maxY > node.y) {
34224            // in NE
34225            this._placeInRegion(parentBranch, node, "NE");
34226          } else {
34227            // in SE
34228            this._placeInRegion(parentBranch, node, "SE");
34229          }
34230        }
34231      }
34232    }, {
34233      key: "_placeInRegion",
34234
34235      /**
34236       * actually place the node in a region (or branch)
34237       *
34238       * @param parentBranch
34239       * @param node
34240       * @param region
34241       * @private
34242       */
34243      value: function _placeInRegion(parentBranch, node, region) {
34244        switch (parentBranch.children[region].childrenCount) {
34245          case 0:
34246            // place node here
34247            parentBranch.children[region].children.data = node;
34248            parentBranch.children[region].childrenCount = 1;
34249            this._updateBranchMass(parentBranch.children[region], node);
34250            break;
34251          case 1:
34252            // convert into children
34253            // if there are two nodes exactly overlapping (on init, on opening of cluster etc.)
34254            // we move one node a pixel and we do not put it in the tree.
34255            if (parentBranch.children[region].children.data.x === node.x && parentBranch.children[region].children.data.y === node.y) {
34256              node.x += this.seededRandom();
34257              node.y += this.seededRandom();
34258            } else {
34259              this._splitBranch(parentBranch.children[region]);
34260              this._placeInTree(parentBranch.children[region], node);
34261            }
34262            break;
34263          case 4:
34264            // place in branch
34265            this._placeInTree(parentBranch.children[region], node);
34266            break;
34267        }
34268      }
34269    }, {
34270      key: "_splitBranch",
34271
34272      /**
34273       * this function splits a branch into 4 sub branches. If the branch contained a node, we place it in the subbranch
34274       * after the split is complete.
34275       *
34276       * @param parentBranch
34277       * @private
34278       */
34279      value: function _splitBranch(parentBranch) {
34280        // if the branch is shaded with a node, replace the node in the new subset.
34281        var containedNode = null;
34282        if (parentBranch.childrenCount === 1) {
34283          containedNode = parentBranch.children.data;
34284          parentBranch.mass = 0;
34285          parentBranch.centerOfMass.x = 0;
34286          parentBranch.centerOfMass.y = 0;
34287        }
34288        parentBranch.childrenCount = 4;
34289        parentBranch.children.data = null;
34290        this._insertRegion(parentBranch, "NW");
34291        this._insertRegion(parentBranch, "NE");
34292        this._insertRegion(parentBranch, "SW");
34293        this._insertRegion(parentBranch, "SE");
34294
34295        if (containedNode != null) {
34296          this._placeInTree(parentBranch, containedNode);
34297        }
34298      }
34299    }, {
34300      key: "_insertRegion",
34301
34302      /**
34303       * This function subdivides the region into four new segments.
34304       * Specifically, this inserts a single new segment.
34305       * It fills the children section of the parentBranch
34306       *
34307       * @param parentBranch
34308       * @param region
34309       * @param parentRange
34310       * @private
34311       */
34312      value: function _insertRegion(parentBranch, region) {
34313        var minX = undefined,
34314            maxX = undefined,
34315            minY = undefined,
34316            maxY = undefined;
34317        var childSize = 0.5 * parentBranch.size;
34318        switch (region) {
34319          case "NW":
34320            minX = parentBranch.range.minX;
34321            maxX = parentBranch.range.minX + childSize;
34322            minY = parentBranch.range.minY;
34323            maxY = parentBranch.range.minY + childSize;
34324            break;
34325          case "NE":
34326            minX = parentBranch.range.minX + childSize;
34327            maxX = parentBranch.range.maxX;
34328            minY = parentBranch.range.minY;
34329            maxY = parentBranch.range.minY + childSize;
34330            break;
34331          case "SW":
34332            minX = parentBranch.range.minX;
34333            maxX = parentBranch.range.minX + childSize;
34334            minY = parentBranch.range.minY + childSize;
34335            maxY = parentBranch.range.maxY;
34336            break;
34337          case "SE":
34338            minX = parentBranch.range.minX + childSize;
34339            maxX = parentBranch.range.maxX;
34340            minY = parentBranch.range.minY + childSize;
34341            maxY = parentBranch.range.maxY;
34342            break;
34343        }
34344
34345        parentBranch.children[region] = {
34346          centerOfMass: { x: 0, y: 0 },
34347          mass: 0,
34348          range: { minX: minX, maxX: maxX, minY: minY, maxY: maxY },
34349          size: 0.5 * parentBranch.size,
34350          calcSize: 2 * parentBranch.calcSize,
34351          children: { data: null },
34352          maxWidth: 0,
34353          level: parentBranch.level + 1,
34354          childrenCount: 0
34355        };
34356      }
34357    }, {
34358      key: "_debug",
34359
34360      //---------------------------  DEBUGGING BELOW  ---------------------------//
34361
34362      /**
34363       * This function is for debugging purposed, it draws the tree.
34364       *
34365       * @param ctx
34366       * @param color
34367       * @private
34368       */
34369      value: function _debug(ctx, color) {
34370        if (this.barnesHutTree !== undefined) {
34371
34372          ctx.lineWidth = 1;
34373
34374          this._drawBranch(this.barnesHutTree.root, ctx, color);
34375        }
34376      }
34377    }, {
34378      key: "_drawBranch",
34379
34380      /**
34381       * This function is for debugging purposes. It draws the branches recursively.
34382       *
34383       * @param branch
34384       * @param ctx
34385       * @param color
34386       * @private
34387       */
34388      value: function _drawBranch(branch, ctx, color) {
34389        if (color === undefined) {
34390          color = "#FF0000";
34391        }
34392
34393        if (branch.childrenCount === 4) {
34394          this._drawBranch(branch.children.NW, ctx);
34395          this._drawBranch(branch.children.NE, ctx);
34396          this._drawBranch(branch.children.SE, ctx);
34397          this._drawBranch(branch.children.SW, ctx);
34398        }
34399        ctx.strokeStyle = color;
34400        ctx.beginPath();
34401        ctx.moveTo(branch.range.minX, branch.range.minY);
34402        ctx.lineTo(branch.range.maxX, branch.range.minY);
34403        ctx.stroke();
34404
34405        ctx.beginPath();
34406        ctx.moveTo(branch.range.maxX, branch.range.minY);
34407        ctx.lineTo(branch.range.maxX, branch.range.maxY);
34408        ctx.stroke();
34409
34410        ctx.beginPath();
34411        ctx.moveTo(branch.range.maxX, branch.range.maxY);
34412        ctx.lineTo(branch.range.minX, branch.range.maxY);
34413        ctx.stroke();
34414
34415        ctx.beginPath();
34416        ctx.moveTo(branch.range.minX, branch.range.maxY);
34417        ctx.lineTo(branch.range.minX, branch.range.minY);
34418        ctx.stroke();
34419
34420        /*
34421         if (branch.mass > 0) {
34422         ctx.circle(branch.centerOfMass.x, branch.centerOfMass.y, 3*branch.mass);
34423         ctx.stroke();
34424         }
34425         */
34426      }
34427    }]);
34428
34429    return BarnesHutSolver;
34430  })();
34431
34432  exports["default"] = BarnesHutSolver;
34433  module.exports = exports["default"];
34434
34435/***/ },
34436/* 92 */
34437/***/ function(module, exports) {
34438
34439  "use strict";
34440
34441  Object.defineProperty(exports, "__esModule", {
34442    value: true
34443  });
34444
34445  var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
34446
34447  function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
34448
34449  var RepulsionSolver = (function () {
34450    function RepulsionSolver(body, physicsBody, options) {
34451      _classCallCheck(this, RepulsionSolver);
34452
34453      this.body = body;
34454      this.physicsBody = physicsBody;
34455      this.setOptions(options);
34456    }
34457
34458    _createClass(RepulsionSolver, [{
34459      key: "setOptions",
34460      value: function setOptions(options) {
34461        this.options = options;
34462      }
34463    }, {
34464      key: "solve",
34465
34466      /**
34467       * Calculate the forces the nodes apply on each other based on a repulsion field.
34468       * This field is linearly approximated.
34469       *
34470       * @private
34471       */
34472      value: function solve() {
34473        var dx, dy, distance, fx, fy, repulsingForce, node1, node2;
34474
34475        var nodes = this.body.nodes;
34476        var nodeIndices = this.physicsBody.physicsNodeIndices;
34477        var forces = this.physicsBody.forces;
34478
34479        // repulsing forces between nodes
34480        var nodeDistance = this.options.nodeDistance;
34481
34482        // approximation constants
34483        var a = -2 / 3 / nodeDistance;
34484        var b = 4 / 3;
34485
34486        // we loop from i over all but the last entree in the array
34487        // j loops from i+1 to the last. This way we do not double count any of the indices, nor i === j
34488        for (var i = 0; i < nodeIndices.length - 1; i++) {
34489          node1 = nodes[nodeIndices[i]];
34490          for (var j = i + 1; j < nodeIndices.length; j++) {
34491            node2 = nodes[nodeIndices[j]];
34492
34493            dx = node2.x - node1.x;
34494            dy = node2.y - node1.y;
34495            distance = Math.sqrt(dx * dx + dy * dy);
34496
34497            // same condition as BarnesHutSolver, making sure nodes are never 100% overlapping.
34498            if (distance === 0) {
34499              distance = 0.1 * Math.random();
34500              dx = distance;
34501            }
34502
34503            if (distance < 2 * nodeDistance) {
34504              if (distance < 0.5 * nodeDistance) {
34505                repulsingForce = 1.0;
34506              } else {
34507                repulsingForce = a * distance + b; // linear approx of  1 / (1 + Math.exp((distance / nodeDistance - 1) * steepne
34507ss))
34508              }
34509              repulsingForce = repulsingForce / distance;
34510
34511              fx = dx * repulsingForce;
34512              fy = dy * repulsingForce;
34513
34514              forces[node1.id].x -= fx;
34515              forces[node1.id].y -= fy;
34516              forces[node2.id].x += fx;
34517              forces[node2.id].y += fy;
34518            }
34519          }
34520        }
34521      }
34522    }]);
34523
34524    return RepulsionSolver;
34525  })();
34526
34527  exports["default"] = RepulsionSolver;
34528  module.exports = exports["default"];
34529
34530/***/ },
34531/* 93 */
34532/***/ function(module, exports) {
34533
34534  "use strict";
34535
34536  Object.defineProperty(exports, "__esModule", {
34537    value: true
34538  });
34539
34540  var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
34541
34542  function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
34543
34544  var HierarchicalRepulsionSolver = (function () {
34545    function HierarchicalRepulsionSolver(body, physicsBody, options) {
34546      _classCallCheck(this, HierarchicalRepulsionSolver);
34547
34548      this.body = body;
34549      this.physicsBody = physicsBody;
34550      this.setOptions(options);
34551    }
34552
34553    _createClass(HierarchicalRepulsionSolver, [{
34554      key: "setOptions",
34555      value: function setOptions(options) {
34556        this.options = options;
34557      }
34558    }, {
34559      key: "solve",
34560
34561      /**
34562       * Calculate the forces the nodes apply on each other based on a repulsion field.
34563       * This field is linearly approximated.
34564       *
34565       * @private
34566       */
34567      value: function solve() {
34568        var dx, dy, distance, fx, fy, repulsingForce, node1, node2, i, j;
34569
34570        var nodes = this.body.nodes;
34571        var nodeIndices = this.physicsBody.physicsNodeIndices;
34572        var forces = this.physicsBody.forces;
34573
34574        // repulsing forces between nodes
34575        var nodeDistance = this.options.nodeDistance;
34576
34577        // we loop from i over all but the last entree in the array
34578        // j loops from i+1 to the last. This way we do not double count any of the indices, nor i === j
34579        for (i = 0; i < nodeIndices.length - 1; i++) {
34580          node1 = nodes[nodeIndices[i]];
34581          for (j = i + 1; j < nodeIndices.length; j++) {
34582            node2 = nodes[nodeIndices[j]];
34583
34584            // nodes only affect nodes on their level
34585            if (node1.level === node2.level) {
34586              dx = node2.x - node1.x;
34587              dy = node2.y - node1.y;
34588              distance = Math.sqrt(dx * dx + dy * dy);
34589
34590              var steepness = 0.05;
34591              if (distance < nodeDistance) {
34592                repulsingForce = -Math.pow(steepness * distance, 2) + Math.pow(steepness * nodeDistance, 2);
34593              } else {
34594                repulsingForce = 0;
34595              }
34596              // normalize force with
34597              if (distance === 0) {
34598                distance = 0.01;
34599              } else {
34600                repulsingForce = repulsingForce / distance;
34601              }
34602              fx = dx * repulsingForce;
34603              fy = dy * repulsingForce;
34604
34605              forces[node1.id].x -= fx;
34606              forces[node1.id].y -= fy;
34607              forces[node2.id].x += fx;
34608              forces[node2.id].y += fy;
34609            }
34610          }
34611        }
34612      }
34613    }]);
34614
34615    return HierarchicalRepulsionSolver;
34616  })();
34617
34618  exports["default"] = HierarchicalRepulsionSolver;
34619  module.exports = exports["default"];
34620
34621/***/ },
34622/* 94 */
34623/***/ function(module, exports) {
34624
34625  "use strict";
34626
34627  Object.defineProperty(exports, "__esModule", {
34628    value: true
34629  });
34630
34631  var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
34632
34633  function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
34634
34635  var SpringSolver = (function () {
34636    function SpringSolver(body, physicsBody, options) {
34637      _classCallCheck(this, SpringSolver);
34638
34639      this.body = body;
34640      this.physicsBody = physicsBody;
34641      this.setOptions(options);
34642    }
34643
34644    _createClass(SpringSolver, [{
34645      key: "setOptions",
34646      value: function setOptions(options) {
34647        this.options = options;
34648      }
34649    }, {
34650      key: "solve",
34651
34652      /**
34653       * This function calculates the springforces on the nodes, accounting for the support nodes.
34654       *
34655       * @private
34656       */
34657      value: function solve() {
34658        var edgeLength = undefined,
34659            edge = undefined;
34660        var edgeIndices = this.physicsBody.physicsEdgeIndices;
34661        var edges = this.body.edges;
34662        var node1 = undefined,
34663            node2 = undefined,
34664            node3 = undefined;
34665
34666        // forces caused by the edges, modelled as springs
34667        for (var i = 0; i < edgeIndices.length; i++) {
34668          edge = edges[edgeIndices[i]];
34669          if (edge.connected === true && edge.toId !== edge.fromId) {
34670            // only calculate forces if nodes are in the same sector
34671            if (this.body.nodes[edge.toId] !== undefined && this.body.nodes[edge.fromId] !== undefined) {
34672              if (edge.edgeType.via !== undefined) {
34673                edgeLength = edge.options.length === undefined ? this.options.springLength : edge.options.length;
34674                node1 = edge.to;
34675                node2 = edge.edgeType.via;
34676                node3 = edge.from;
34677
34678                this._calculateSpringForce(node1, node2, 0.5 * edgeLength);
34679                this._calculateSpringForce(node2, node3, 0.5 * edgeLength);
34680              } else {
34681                // the * 1.5 is here so the edge looks as large as a smooth edge. It does not initially because the smooth edges use
34682                // the support nodes which exert a repulsive force on the to and from nodes, making the edge appear larger.
34683                edgeLength = edge.options.length === undefined ? this.options.springLength * 1.5 : edge.options.length;
34684                this._calculateSpringForce(edge.from, edge.to, edgeLength);
34685              }
34686            }
34687          }
34688        }
34689      }
34690    }, {
34691      key: "_calculateSpringForce",
34692
34693      /**
34694       * This is the code actually performing the calculation for the function above.
34695       *
34696       * @param node1
34697       * @param node2
34698       * @param edgeLength
34699       * @private
34700       */
34701      value: function _calculateSpringForce(node1, node2, edgeLength) {
34702        var dx = node1.x - node2.x;
34703        var dy = node1.y - node2.y;
34704        var distance = Math.max(Math.sqrt(dx * dx + dy * dy), 0.01);
34705
34706        // the 1/distance is so the fx and fy can be calculated without sine or cosine.
34707        var springForce = this.options.springConstant * (edgeLength - distance) / distance;
34708
34709        var fx = dx * springForce;
34710        var fy = dy * springForce;
34711
34712        // handle the case where one node is not part of the physcis
34713        if (this.physicsBody.forces[node1.id] !== undefined) {
34714          this.physicsBody.forces[node1.id].x += fx;
34715          this.physicsBody.forces[node1.id].y += fy;
34716        }
34717
34718        if (this.physicsBody.forces[node2.id] !== undefined) {
34719          this.physicsBody.forces[node2.id].x -= fx;
34720          this.physicsBody.forces[node2.id].y -= fy;
34721        }
34722      }
34723    }]);
34724
34725    return SpringSolver;
34726  })();
34727
34728  exports["default"] = SpringSolver;
34729  module.exports = exports["default"];
34730
34731/***/ },
34732/* 95 */
34733/***/ function(module, exports) {
34734
34735  "use strict";
34736
34737  Object.defineProperty(exports, "__esModule", {
34738    value: true
34739  });
34740
34741  var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
34742
34743  function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
34744
34745  var HierarchicalSpringSolver = (function () {
34746    function HierarchicalSpringSolver(body, physicsBody, options) {
34747      _classCallCheck(this, HierarchicalSpringSolver);
34748
34749      this.body = body;
34750      this.physicsBody = physicsBody;
34751      this.setOptions(options);
34752    }
34753
34754    _createClass(HierarchicalSpringSolver, [{
34755      key: "setOptions",
34756      value: function setOptions(options) {
34757        this.options = options;
34758      }
34759    }, {
34760      key: "solve",
34761
34762      /**
34763       * This function calculates the springforces on the nodes, accounting for the support nodes.
34764       *
34765       * @private
34766       */
34767      value: function solve() {
34768        var edgeLength, edge;
34769        var dx, dy, fx, fy, springForce, distance;
34770        var edges = this.body.edges;
34771        var factor = 0.5;
34772
34773        var edgeIndices = this.physicsBody.physicsEdgeIndices;
34774        var nodeIndices = this.physicsBody.physicsNodeIndices;
34775        var forces = this.physicsBody.forces;
34776
34777        // initialize the spring force counters
34778        for (var i = 0; i < nodeIndices.length; i++) {
34779          var nodeId = nodeIndices[i];
34780          forces[nodeId].springFx = 0;
34781          forces[nodeId].springFy = 0;
34782        }
34783
34784        // forces caused by the edges, modelled as springs
34785        for (var i = 0; i < edgeIndices.length; i++) {
34786          edge = edges[edgeIndices[i]];
34787          if (edge.connected === true) {
34788            edgeLength = edge.options.length === undefined ? this.options.springLength : edge.options.length;
34789
34790            dx = edge.from.x - edge.to.x;
34791            dy = edge.from.y - edge.to.y;
34792            distance = Math.sqrt(dx * dx + dy * dy);
34793            distance = distance === 0 ? 0.01 : distance;
34794
34795            // the 1/distance is so the fx and fy can be calculated without sine or cosine.
34796            springForce = this.options.springConstant * (edgeLength - distance) / distance;
34797
34798            fx = dx * springForce;
34799            fy = dy * springForce;
34800
34801            if (edge.to.level != edge.from.level) {
34802              if (forces[edge.toId] !== undefined) {
34803                forces[edge.toId].springFx -= fx;
34804                forces[edge.toId].springFy -= fy;
34805              }
34806              if (forces[edge.fromId] !== undefined) {
34807                forces[edge.fromId].springFx += fx;
34808                forces[edge.fromId].springFy += fy;
34809              }
34810            } else {
34811              if (forces[edge.toId] !== undefined) {
34812                forces[edge.toId].x -= factor * fx;
34813                forces[edge.toId].y -= factor * fy;
34814              }
34815              if (forces[edge.fromId] !== undefined) {
34816                forces[edge.fromId].x += factor * fx;
34817                forces[edge.fromId].y += factor * fy;
34818              }
34819            }
34820          }
34821        }
34822
34823        // normalize spring forces
34824        var springForce = 1;
34825        var springFx, springFy;
34826        for (var i = 0; i < nodeIndices.length; i++) {
34827          var nodeId = nodeIndices[i];
34828          springFx = Math.min(springForce, Math.max(-springForce, forces[nodeId].springFx));
34829          springFy = Math.min(springForce, Math.max(-springForce, forces[nodeId].springFy));
34830
34831          forces[nodeId].x += springFx;
34832          forces[nodeId].y += springFy;
34833        }
34834
34835        // retain energy balance
34836        var totalFx = 0;
34837        var totalFy = 0;
34838        for (var i = 0; i < nodeIndices.length; i++) {
34839          var nodeId = nodeIndices[i];
34840          totalFx += forces[nodeId].x;
34841          totalFy += forces[nodeId].y;
34842        }
34843        var correctionFx = totalFx / nodeIndices.length;
34844        var correctionFy = totalFy / nodeIndices.length;
34845
34846        for (var i = 0; i < nodeIndices.length; i++) {
34847          var nodeId = nodeIndices[i];
34848          forces[nodeId].x -= correctionFx;
34849          forces[nodeId].y -= correctionFy;
34850        }
34851      }
34852    }]);
34853
34854    return HierarchicalSpringSolver;
34855  })();
34856
34857  exports["default"] = HierarchicalSpringSolver;
34858  module.exports = exports["default"];
34859
34860/***/ },
34861/* 96 */
34862/***/ function(module, exports) {
34863
34864  "use strict";
34865
34866  Object.defineProperty(exports, "__esModule", {
34867    value: true
34868  });
34869
34870  var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
34871
34872  function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
34873
34874  var CentralGravitySolver = (function () {
34875    function CentralGravitySolver(body, physicsBody, options) {
34876      _classCallCheck(this, CentralGravitySolver);
34877
34878      this.body = body;
34879      this.physicsBody = physicsBody;
34880      this.setOptions(options);
34881    }
34882
34883    _createClass(CentralGravitySolver, [{
34884      key: "setOptions",
34885      value: function setOptions(options) {
34886        this.options = options;
34887      }
34888    }, {
34889      key: "solve",
34890      value: function solve() {
34891        var dx = undefined,
34892            dy = undefined,
34893            distance = undefined,
34894            node = undefined;
34895        var nodes = this.body.nodes;
34896        var nodeIndices = this.physicsBody.physicsNodeIndices;
34897        var forces = this.physicsBody.forces;
34898
34899        for (var i = 0; i < nodeIndices.length; i++) {
34900          var nodeId = nodeIndices[i];
34901          node = nodes[nodeId];
34902          dx = -node.x;
34903          dy = -node.y;
34904          distance = Math.sqrt(dx * dx + dy * dy);
34905
34906          this._calculateForces(distance, dx, dy, forces, node);
34907        }
34908      }
34909    }, {
34910      key: "_calculateForces",
34911
34912      /**
34913       * Calculate the forces based on the distance.
34914       * @private
34915       */
34916      value: function _calculateForces(distance, dx, dy, forces, node) {
34917        var gravityForce = distance === 0 ? 0 : this.options.centralGravity / distance;
34918        forces[node.id].x = dx * gravityForce;
34919        forces[node.id].y = dy * gravityForce;
34920      }
34921    }]);
34922
34923    return CentralGravitySolver;
34924  })();
34925
34926  exports["default"] = CentralGravitySolver;
34927  module.exports = exports["default"];
34928
34929/***/ },
34930/* 97 */
34931/***/ function(module, exports, __webpack_require__) {
34932
34933  "use strict";
34934
34935  Object.defineProperty(exports, "__esModule", {
34936    value: true
34937  });
34938
34939  var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
34940
34941  var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; desc = parent = getter = undefined; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; continue _function; } } else if ("value" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } };
34942
34943  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
34944
34945  function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
34946
34947  function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { c
34947onstructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) subClass.__proto__ = superClass; }
34948
34949  var _BarnesHutSolver2 = __webpack_require__(91);
34950
34951  var _BarnesHutSolver3 = _interopRequireDefault(_BarnesHutSolver2);
34952
34953  var ForceAtlas2BasedRepulsionSolver = (function (_BarnesHutSolver) {
34954    _inherits(ForceAtlas2BasedRepulsionSolver, _BarnesHutSolver);
34955
34956    function ForceAtlas2BasedRepulsionSolver(body, physicsBody, options) {
34957      _classCallCheck(this, ForceAtlas2BasedRepulsionSolver);
34958
34959      _get(Object.getPrototypeOf(ForceAtlas2BasedRepulsionSolver.prototype), "constructor", this).call(this, body, physicsBody, options);
34960    }
34961
34962    _createClass(ForceAtlas2BasedRepulsionSolver, [{
34963      key: "_calculateForces",
34964
34965      /**
34966       * Calculate the forces based on the distance.
34967       *
34968       * @param distance
34969       * @param dx
34970       * @param dy
34971       * @param node
34972       * @param parentBranch
34973       * @private
34974       */
34975      value: function _calculateForces(distance, dx, dy, node, parentBranch) {
34976        if (distance === 0) {
34977          distance = 0.1 * Math.random();
34978          dx = distance;
34979        }
34980
34981        if (this.overlapAvoidanceFactor < 1) {
34982          distance = Math.max(0.1 + this.overlapAvoidanceFactor * node.shape.radius, distance - node.shape.radius);
34983        }
34984
34985        var degree = node.edges.length + 1;
34986        // the dividing by the distance cubed instead of squared allows us to get the fx and fy components without sines and cosines
34987        // it is shorthand for gravityforce with distance squared and fx = dx/distance * gravityForce
34988        var gravityForce = this.options.gravitationalConstant * parentBranch.mass * node.options.mass * degree / Math.pow(distance, 2);
34989        var fx = dx * gravityForce;
34990        var fy = dy * gravityForce;
34991
34992        this.physicsBody.forces[node.id].x += fx;
34993        this.physicsBody.forces[node.id].y += fy;
34994      }
34995    }]);
34996
34997    return ForceAtlas2BasedRepulsionSolver;
34998  })(_BarnesHutSolver3["default"]);
34999
35000  exports["default"] = ForceAtlas2BasedRepulsionSolver;
35001  module.exports = exports["default"];
35002
35003/***/ },
35004/* 98 */
35005/***/ function(module, exports, __webpack_require__) {
35006
35007  "use strict";
35008
35009  Object.defineProperty(exports, "__esModule", {
35010    value: true
35011  });
35012
35013  var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
35014
35015  var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; desc = parent = getter = undefined; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; continue _function; } } else if ("value" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } };
35016
35017  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
35018
35019  function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
35020
35021  function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) subClass.__proto__ = superClass; }
35022
35023  var _CentralGravitySolver2 = __webpack_require__(96);
35024
35025  var _CentralGravitySolver3 = _interopRequireDefault(_CentralGravitySolver2);
35026
35027  var ForceAtlas2BasedCentralGravitySolver = (function (_CentralGravitySolver) {
35028    _inherits(ForceAtlas2BasedCentralGravitySolver, _CentralGravitySolver);
35029
35030    function ForceAtlas2BasedCentralGravitySolver(body, physicsBody, options) {
35031      _classCallCheck(this, ForceAtlas2BasedCentralGravitySolver);
35032
35033      _get(Object.getPrototypeOf(ForceAtlas2BasedCentralGravitySolver.prototype), "constructor", this).call(this, body, physicsBody, options);
35034    }
35035
35036    _createClass(ForceAtlas2BasedCentralGravitySolver, [{
35037      key: "_calculateForces",
35038
35039      /**
35040       * Calculate the forces based on the distance.
35041       * @private
35042       */
35043      value: function _calculateForces(distance, dx, dy, forces, node) {
35044        if (distance > 0) {
35045          var degree = node.edges.length + 1;
35046          var gravityForce = this.options.centralGravity * degree * node.options.mass;
35047          forces[node.id].x = dx * gravityForce;
35048          forces[node.id].y = dy * gravityForce;
35049        }
35050      }
35051    }]);
35052
35053    return ForceAtlas2BasedCentralGravitySolver;
35054  })(_CentralGravitySolver3["default"]);
35055
35056  exports["default"] = ForceAtlas2BasedCentralGravitySolver;
35057  module.exports = exports["default"];
35058
35059/***/ },
35060/* 99 */
35061/***/ function(module, exports, __webpack_require__) {
35062
35063  'use strict';
35064
35065  Object.defineProperty(exports, '__esModule', {
35066    value: true
35067  });
35068
35069  var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
35070
35071  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
35072
35073  function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
35074
35075  var _componentsNodesCluster = __webpack_require__(100);
35076
35077  var _componentsNodesCluster2 = _interopRequireDefault(_componentsNodesCluster);
35078
35079  var util = __webpack_require__(1);
35080
35081  var ClusterEngine = (function () {
35082    function ClusterEngine(body) {
35083      var _this = this;
35084
35085      _classCallCheck(this, ClusterEngine);
35086
35087      this.body = body;
35088      this.clusteredNodes = {};
35089
35090      this.options = {};
35091      this.defaultOptions = {};
35092      util.extend(this.options, this.defaultOptions);
35093
35094      this.body.emitter.on('_resetData', function () {
35095        _this.clusteredNodes = {};
35096      });
35097    }
35098
35099    _createClass(ClusterEngine, [{
35100      key: 'setOptions',
35101      value: function setOptions(options) {
35102        if (options !== undefined) {}
35103      }
35104    }, {
35105      key: 'clusterByHubsize',
35106
35107      /**
35108      *
35109      * @param hubsize
35110      * @param options
35111      */
35112      value: function clusterByHubsize(hubsize, options) {
35113        if (hubsize === undefined) {
35114          hubsize = this._getHubSize();
35115        } else if (typeof hubsize === 'object') {
35116          options = this._checkOptions(hubsize);
35117          hubsize = this._getHubSize();
35118        }
35119
35120        var nodesToCluster = [];
35121        for (var i = 0; i < this.body.nodeIndices.length; i++) {
35122          var node = this.body.nodes[this.body.nodeIndices[i]];
35123          if (node.edges.length >= hubsize) {
35124            nodesToCluster.push(node.id);
35125          }
35126        }
35127
35128        for (var i = 0; i < nodesToCluster.length; i++) {
35129          this.clusterByConnection(nodesToCluster[i], options, true);
35130        }
35131
35132        this.body.emitter.emit('_dataChanged');
35133      }
35134    }, {
35135      key: 'cluster',
35136
35137      /**
35138      * loop over all nodes, check if they adhere to the condition and cluster if needed.
35139      * @param options
35140      * @param refreshData
35141      */
35142      value: function cluster() {
35143        var options = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0];
35144        var refreshData = arguments.length <= 1 || arguments[1] === undefined ? true : arguments[1];
35145
35146        if (options.joinCondition === undefined) {
35147          throw new Error('Cannot call clusterByNodeData without a joinCondition function in the options.');
35148        }
35149
35150        // check if the options object is fine, append if needed
35151        options = this._checkOptions(options);
35152
35153        var childNodesObj = {};
35154        var childEdgesObj = {};
35155
35156        // collect the nodes that will be in the cluster
35157        for (var i = 0; i < this.body.nodeIndices.length; i++) {
35158          var nodeId = this.body.nodeIndices[i];
35159          var node = this.body.nodes[nodeId];
35160          var clonedOptions = this._cloneOptions(node);
35161          if (options.joinCondition(clonedOptions) === true) {
35162            childNodesObj[nodeId] = this.body.nodes[nodeId];
35163
35164            // collect the nodes that will be in the cluster
35165            for (var _i = 0; _i < node.edges.length; _i++) {
35166              var edge = node.edges[_i];
35167              if (edge.hiddenByCluster !== true) {
35168                childEdgesObj[edge.id] = edge;
35169              }
35170            }
35171          }
35172        }
35173
35174        this._cluster(childNodesObj, childEdgesObj, options, refreshData);
35175      }
35176    }, {
35177      key: 'clusterByEdgeCount',
35178
35179      /**
35180       * Cluster all nodes in the network that have only X edges
35181       * @param edgeCount
35182       * @param options
35183       * @param refreshData
35184       */
35185      value: function clusterByEdgeCount(edgeCount, options) {
35186        var refreshData = arguments.length <= 2 || arguments[2] === undefined ? true : arguments[2];
35187
35188        options = this._checkOptions(options);
35189        var clusters = [];
35190        var usedNodes = {};
35191        var edge = undefined,
35192            edges = undefined,
35193            node = undefined,
35194            nodeId = undefined,
35195            relevantEdgeCount = undefined;
35196        // collect the nodes that will be in the cluster
35197        for (var i = 0; i < this.body.nodeIndices.length; i++) {
35198          var childNodesObj = {};
35199          var childEdgesObj = {};
35200          nodeId = this.body.nodeIndices[i];
35201
35202          // if this node is already used in another cluster this session, we do not have to re-evaluate it.
35203          if (usedNodes[nodeId] === undefined) {
35204            relevantEdgeCount = 0;
35205            node = this.body.nodes[nodeId];
35206            edges = [];
35207            for (var j = 0; j < node.edges.length; j++) {
35208              edge = node.edges[j];
35209              if (edge.hiddenByCluster !== true) {
35210                if (edge.toId !== edge.fromId) {
35211                  relevantEdgeCount++;
35212                }
35213                edges.push(edge);
35214              }
35215            }
35216
35217            // this node qualifies, we collect its neighbours to start the clustering process.
35218            if (relevantEdgeCount === edgeCount) {
35219              var gatheringSuccessful = true;
35220              for (var j = 0; j < edges.length; j++) {
35221                edge = edges[j];
35222                var childNodeId = this._getConnectedId(edge, nodeId);
35223                // add the nodes to the list by the join condition.
35224                if (options.joinCondition === undefined) {
35225                  childEdgesObj[edge.id] = edge;
35226                  childNodesObj[nodeId] = this.body.nodes[nodeId];
35227                  childNodesObj[childNodeId] = this.body.nodes[childNodeId];
35228                  usedNodes[nodeId] = true;
35229                } else {
35230                  var clonedOptions = this._cloneOptions(this.body.nodes[nodeId]);
35231                  if (options.joinCondition(clonedOptions) === true) {
35232                    childEdgesObj[edge.id] = edge;
35233                    childNodesObj[nodeId] = this.body.nodes[nodeId];
35234                    usedNodes[nodeId] = true;
35235                  } else {
35236                    // this node does not qualify after all.
35237                    gatheringSuccessful = false;
35238                    break;
35239                  }
35240                }
35241              }
35242
35243              // add to the cluster queue
35244              if (Object.keys(childNodesObj).length > 0 && Object.keys(childEdgesObj).length > 0 && gatheringSuccessful === true) {
35245                clusters.push({ nodes: childNodesObj, edges: childEdgesObj });
35246              }
35247            }
35248          }
35249        }
35250
35251        for (var i = 0; i < clusters.length; i++) {
35252          this._cluster(clusters[i].nodes, clusters[i].edges, options, false);
35253        }
35254
35255        if (refreshData === true) {
35256          this.body.emitter.emit('_dataChanged');
35257        }
35258      }
35259    }, {
35260      key: 'clusterOutliers',
35261
35262      /**
35263      * Cluster all nodes in the network that have only 1 edge
35264      * @param options
35265      * @param refreshData
35266      */
35267      value: function clusterOutliers(options) {
35268        var refreshData = arguments.length <= 1 || arguments[1] === undefined ? true : arguments[1];
35269
35270        this.clusterByEdgeCount(1, options, refreshData);
35271      }
35272    }, {
35273      key: 'clusterBridges',
35274
35275      /**
35276       * Cluster all nodes in the network that have only 2 edge
35277       * @param options
35278       * @param refreshData
35279       */
35280      value: function clusterBridges(options) {
35281        var refreshData = arguments.length <= 1 || arguments[1] === undefined ? true : arguments[1];
35282
35283        this.clusterByEdgeCount(2, options, refreshData);
35284      }
35285    }, {
35286      key: 'clusterByConnection',
35287
35288      /**
35289      * suck all connected nodes of a node into the node.
35290      * @param nodeId
35291      * @param options
35292      * @param refreshData
35293      */
35294      value: function clusterByConnection(nodeId, options) {
35295        var refreshData = arguments.length <= 2 || arguments[2] === undefined ? true : arguments[2];
35296
35297        // kill conditions
35298        if (nodeId === undefined) {
35299          throw new Error('No nodeId supplied to clusterByConnection!');
35300        }
35301        if (this.body.nodes[nodeId] === undefined) {
35302          throw new Error('The nodeId given to clusterByConnection does not exist!');
35303        }
35304
35305        var node = this.body.nodes[nodeId];
35306        options = this._checkOptions(options, node);
35307        if (options.clusterNodeProperties.x === undefined) {
35308          options.clusterNodeProperties.x = node.x;
35309        }
35310        if (options.clusterNodeProperties.y === undefined) {
35311          options.clusterNodeProperties.y = node.y;
35312        }
35313        if (options.clusterNodeProperties.fixed === undefined) {
35314          options.clusterNodeProperties.fixed = {};
35315          options.clusterNodeProperties.fixed.x = node.options.fixed.x;
35316          options.clusterNodeProperties.fixed.y = node.options.fixed.y;
35317        }
35318
35319        var childNodesObj = {};
35320        var childEdgesObj = {};
35321        var parentNodeId = node.id;
35322        var parentClonedOptions = this._cloneOptions(node);
35323        childNodesObj[parentNodeId] = node;
35324
35325        // collect the nodes that will be in the cluster
35326        for (var i = 0; i < node.edges.length; i++) {
35327          var edge = node.edges[i];
35328          if (edge.hiddenByCluster !== true) {
35329            var childNodeId = this._getConnectedId(edge, parentNodeId);
35330
35331            // if the child node is not in a cluster (may not be needed now with the edge.hiddenByCluster check)
35332            if (this.clusteredNodes[childNodeId] === undefined) {
35333              if (childNodeId !== parentNodeId) {
35334                if (options.joinCondition === undefined) {
35335                  childEdgesObj[edge.id] = edge;
35336                  childNodesObj[childNodeId] = this.body.nodes[childNodeId];
35337                } else {
35338                  // clone the options and insert some additional parameters that could be interesting.
35339                  var childClonedOptions = this._cloneOptions(this.body.nodes[childNodeId]);
35340                  if (options.joinCondition(parentClonedOptions, childClonedOptions) === true) {
35341                    childEdgesObj[edge.id] = edge;
35342                    childNodesObj[childNodeId] = this.body.nodes[childNodeId];
35343                  }
35344                }
35345              } else {
35346                // swallow the edge if it is self-referencing.
35347                childEdgesObj[edge.id] = edge;
35348              }
35349            }
35350          }
35351        }
35352
35353        this._cluster(childNodesObj, childEdgesObj, options, refreshData);
35354      }
35355    }, {
35356      key: '_cloneOptions',
35357
35358      /**
35359      * This returns a clone of the options or options of the edge or node to be used for construction of new edges or check functions for new nodes.
35360      * @param objId
35361      * @param type
35362      * @returns {{}}
35363      * @private
35364      */
35365      value: function _cloneOptions(item, type) {
35366        var clonedOptions = {};
35367        if (type === undefined || type === 'node') {
35368          util.deepExtend(clonedOptions, item.options, true);
35369          clonedOptions.x = item.x;
35370          clonedOptions.y = item.y;
35371          clonedOptions.amountOfConnections = item.edges.length;
35372        } else {
35373          util.deepExtend(clonedOptions, item.options, true);
35374        }
35375        return clonedOptions;
35376      }
35377    }, {
35378      key: '_createClusterEdges',
35379
35380      /**
35381      * This function creates the edges that will be attached to the cluster
35382      * It looks for edges that are connected to the nodes from the "outside' of the cluster.
35383      *
35384      * @param childNodesObj
35385      * @param newEdges
35386      * @param options
35387      * @private
35388      */
35389      value: function _createClusterEdges(childNodesObj, childEdgesObj, clusterNodeProperties, clusterEdgeProperties) {
35390        var edge = undefined,
35391            childNodeId = undefined,
35392            childNode = undefined,
35393            toId = undefined,
35394            fromId = undefined,
35395            otherNodeId = undefined;
35396
35397        // loop over all child nodes and their edges to find edges going out of the cluster
35398        // these edges will be replaced by clusterEdges.
35399        var childKeys = Object.keys(childNodesObj);
35400        var createEdges = [];
35401        for (var i = 0; i < childKeys.length; i++) {
35402          childNodeId = childKeys[i];
35403          childNode = childNodesObj[childNodeId];
35404
35405          // construct new edges from the cluster to others
35406          for (var j = 0; j < childNode.edges.length; j++) {
35407            edge = childNode.edges[j];
35408            // we only handle edges that are visible to the system, not the disabled ones from the clustering process.
35409            if (edge.hiddenByCluster !== true) {
35410              // self-referencing edges will be added to the "hidden" list
35411              if (edge.toId == edge.fromId) {
35412                childEdgesObj[edge.id] = edge;
35413              } else {
35414                // set up the from and to.
35415                if (edge.toId == childNodeId) {
35416                  // this is a double equals because ints and strings can be interchanged here.
35417                  toId = clusterNodeProperties.id;
35418                  fromId = edge.fromId;
35419                  otherNodeId = fromId;
35420                } else {
35421                  toId = edge.toId;
35422                  fromId = clusterNodeProperties.id;
35423                  otherNodeId = toId;
35424                }
35425              }
35426
35427              // Only edges from the cluster outwards are being replaced.
35428              if (childNodesObj[otherNodeId] === undefined) {
35429                createEdges.push({ edge: edge, fromId: fromId, toId: toId });
35430              }
35431            }
35432          }
35433        }
35434
35435        // here we actually create the replacement edges. We could not do this in the loop above as the creation process
35436        // would add an edge to the edges array we are iterating over.
35437        for (var j = 0; j < createEdges.length; j++) {
35438          var _edge = createEdges[j].edge;
35439          // copy the options of the edge we will replace
35440          var clonedOptions = this._cloneOptions(_edge, 'edge');
35441          // make sure the properties of clusterEdges are superimposed on it
35442          util.deepExtend(clonedOptions, clusterEdgeProperties);
35443
35444          // set up the edge
35445          clonedOptions.from = createEdges[j].fromId;
35446          clonedOptions.to = createEdges[j].toId;
35447          clonedOptions.id = 'clusterEdge:' + util.randomUUID();
35448          //clonedOptions.id = '(cf: ' + createEdges[j].fromId + " to: " + createEdges[j].toId + ")" + Math.random();
35449
35450          // create the edge and give a reference to the one it replaced.
35451          var newEdge = this.body.functions.createEdge(clonedOptions);
35452          newEdge.clusteringEdgeReplacingId = _edge.id;
35453
35454          // connect the edge.
35455          this.body.edges[newEdge.id] = newEdge;
35456          newEdge.connect();
35457
35458          // hide the replaced edge
35459          _edge.setOptions({ physics: false, hidden: true });
35460          _edge.hiddenByCluster = true;
35461        }
35462      }
35463    }, {
35464      key: '_checkOptions',
35465
35466      /**
35467      * This function checks the options that can be supplied to the different cluster functions
35468      * for certain fields and inserts defaults if needed
35469      * @param options
35470      * @returns {*}
35471      * @private
35472      */
35473      value: function _checkOptions() {
35474        var options = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0];
35475
35476        if (options.clusterEdgeProperties === undefined) {
35477          options.clusterEdgeProperties = {};
35478        }
35479        if (options.clusterNodeProperties === undefined) {
35480          options.clusterNodeProperties = {};
35481        }
35482
35483        return options;
35484      }
35485    }, {
35486      key: '_cluster',
35487
35488      /**
35489      *
35490      * @param {Object}    childNodesObj         | object with node objects, id as keys, same as childNodes except it also contains a source node
35491      * @param {Object}    childEdgesObj         | object with edge objects, id as keys
35492      * @param {Array}     options               | object with {clusterNodeProperties, clusterEdgeProperties, processProperties}
35493      * @param {Boolean}   refreshData | when true, do not wrap up
35494      * @private
35495      */
35496      value: function _cluster(childNodesObj, childEdgesObj, options) {
35497        var refreshData = arguments.length <= 3 || arguments[3] === undefined ? true : arguments[3];
35498
35499        // kill condition: no children so can't cluster or only one node in the cluster, dont bother
35500        if (Object.keys(childNodesObj).length < 2) {
35501          return;
35502        }
35503
35504        // check if this cluster call is not trying to cluster anything that is in another cluster.
35505        for (var nodeId in childNodesObj) {
35506          if (childNodesObj.hasOwnProperty(nodeId)) {
35507            if (this.clusteredNodes[nodeId] !== undefined) {
35508              return;
35509            }
35510          }
35511        }
35512
35513        var clusterNodeProperties = util.deepExtend({}, options.clusterNodeProperties);
35514
35515        // construct the clusterNodeProperties
35516        if (options.processProperties !== undefined) {
35517          // get the childNode options
35518          var childNodesOptions = [];
35519          for (var nodeId in childNodesObj) {
35520            if (childNodesObj.hasOwnProperty(nodeId)) {
35521              var clonedOptions = this._cloneOptions(childNodesObj[nodeId]);
35522              childNodesOptions.push(clonedOptions);
35523            }
35524          }
35525
35526          // get clusterproperties based on childNodes
35527          var childEdgesOptions = [];
35528          for (var edgeId in childEdgesObj) {
35529            if (childEdgesObj.hasOwnProperty(edgeId)) {
35530              // these cluster edges will be removed on creation of the cluster.
35531              if (edgeId.substr(0, 12) !== 'clusterEdge:') {
35532                var clonedOptions = this._cloneOptions(childEdgesObj[edgeId], 'edge');
35533                childEdgesOptions.push(clonedOptions);
35534              }
35535            }
35536          }
35537
35538          clusterNodeProperties = options.processProperties(clusterNodeProperties, childNodesOptions, childEdgesOptions);
35539          if (!clusterNodeProperties) {
35540            throw new Error('The processProperties function does not return properties!');
35541          }
35542        }
35543
35544        // check if we have an unique id;
35545        if (clusterNodeProperties.id === undefined) {
35546          clusterNodeProperties.id = 'cluster:' + util.randomUUID();
35547        }
35548        var clusterId = clusterNodeProperties.id;
35549
35550        if (clusterNodeProperties.label === undefined) {
35551          clusterNodeProperties.label = 'cluster';
35552        }
35553
35554        // give the clusterNode a postion if it does not have one.
35555        var pos = undefined;
35556        if (clusterNodeProperties.x === undefined) {
35557          pos = this._getClusterPosition(childNodesObj);
35558          clusterNodeProperties.x = pos.x;
35559        }
35560        if (clusterNodeProperties.y === undefined) {
35561          if (pos === undefined) {
35562            pos = this._getClusterPosition(childNodesObj);
35563          }
35564          clusterNodeProperties.y = pos.y;
35565        }
35566
35567        // force the ID to remain the same
35568        clusterNodeProperties.id = clusterId;
35569
35570        // create the clusterNode
35571        var clusterNode = this.body.functions.createNode(clusterNodeProperties, _componentsNodesCluster2['default']);
35572        clusterNode.isCluster = true;
35573        clusterNode.containedNodes = childNodesObj;
35574        clusterNode.containedEdges = childEdgesObj;
35575        // cache a copy from the cluster edge properties if we have to reconnect others later on
35576        clusterNode.clusterEdgeProperties = options.clusterEdgeProperties;
35577
35578        // finally put the cluster node into global
35579        this.body.nodes[clusterNodeProperties.id] = clusterNode;
35580
35581        // create the new edges that will connect to the cluster, all self-referencing edges will be added to childEdgesObject here.
35582        this._createClusterEdges(childNodesObj, childEdgesObj, clusterNodeProperties, options.clusterEdgeProperties);
35583
35584        // disable the childEdges
35585        for (var edgeId in childEdgesObj) {
35586          if (childEdgesObj.hasOwnProperty(edgeId)) {
35587            if (this.body.edges[edgeId] !== undefined) {
35588              var edge = this.body.edges[edgeId];
35589              edge.setOptions({ physics: false, hidden: true });
35590              edge.hiddenByCluster = true;
35591            }
35592          }
35593        }
35594
35595        // disable the childNodes
35596        for (var nodeId in childNodesObj) {
35597          if (childNodesObj.hasOwnProperty(nodeId)) {
35598            this.clusteredNodes[nodeId] = { clusterId: clusterNodeProperties.id, node: this.body.nodes[nodeId] };
35599            this.body.nodes[nodeId].setOptions({ hidden: true, physics: false });
35600          }
35601        }
35602
35603        // set ID to undefined so no duplicates arise
35604        clusterNodeProperties.id = undefined;
35605
35606        // wrap up
35607        if (refreshData === true) {
35608          this.body.emitter.emit('_dataChanged');
35609        }
35610      }
35611    }, {
35612      key: 'isCluster',
35613
35614      /**
35615      * Check if a node is a cluster.
35616      * @param nodeId
35617      * @returns {*}
35618      */
35619      value: function isCluster(nodeId) {
35620        if (this.body.nodes[nodeId] !== undefined) {
35621          return this.body.nodes[nodeId].isCluster === true;
35622        } else {
35623          console.log('Node does not exist.');
35624          return false;
35625        }
35626      }
35627    }, {
35628      key: '_getClusterPosition',
35629
35630      /**
35631      * get the position of the cluster node based on what's inside
35632      * @param {object} childNodesObj    | object with node objects, id as keys
35633      * @returns {{x: number, y: number}}
35634      * @private
35635      */
35636      value: function _getClusterPosition(childNodesObj) {
35637        var childKeys = Object.keys(childNodesObj);
35638        var minX = childNodesObj[childKeys[0]].x;
35639        var maxX = childNodesObj[childKeys[0]].x;
35640        var minY = childNodesObj[childKeys[0]].y;
35641        var maxY = childNodesObj[childKeys[0]].y;
35642        var node = undefined;
35643        for (var i = 1; i < childKeys.length; i++) {
35644          node = childNodesObj[childKeys[i]];
35645          minX = node.x < minX ? node.x : minX;
35646          maxX = node.x >
35646 maxX ? node.x : maxX;
35647          minY = node.y < minY ? node.y : minY;
35648          maxY = node.y > maxY ? node.y : maxY;
35649        }
35650
35651        return { x: 0.5 * (minX + maxX), y: 0.5 * (minY + maxY) };
35652      }
35653    }, {
35654      key: 'openCluster',
35655
35656      /**
35657      * Open a cluster by calling this function.
35658      * @param {String}  clusterNodeId | the ID of the cluster node
35659      * @param {Boolean} refreshData | wrap up afterwards if not true
35660      */
35661      value: function openCluster(clusterNodeId, options) {
35662        var refreshData = arguments.length <= 2 || arguments[2] === undefined ? true : arguments[2];
35663
35664        // kill conditions
35665        if (clusterNodeId === undefined) {
35666          throw new Error('No clusterNodeId supplied to openCluster.');
35667        }
35668        if (this.body.nodes[clusterNodeId] === undefined) {
35669          throw new Error('The clusterNodeId supplied to openCluster does not exist.');
35670        }
35671        if (this.body.nodes[clusterNodeId].containedNodes === undefined) {
35672          console.log('The node:' + clusterNodeId + ' is not a cluster.');
35673          return;
35674        }
35675        var clusterNode = this.body.nodes[clusterNodeId];
35676        var containedNodes = clusterNode.containedNodes;
35677        var containedEdges = clusterNode.containedEdges;
35678
35679        // allow the user to position the nodes after release.
35680        if (options !== undefined && options.releaseFunction !== undefined && typeof options.releaseFunction === 'function') {
35681          var positions = {};
35682          var clusterPosition = { x: clusterNode.x, y: clusterNode.y };
35683          for (var nodeId in containedNodes) {
35684            if (containedNodes.hasOwnProperty(nodeId)) {
35685              var containedNode = this.body.nodes[nodeId];
35686              positions[nodeId] = { x: containedNode.x, y: containedNode.y };
35687            }
35688          }
35689          var newPositions = options.releaseFunction(clusterPosition, positions);
35690
35691          for (var nodeId in containedNodes) {
35692            if (containedNodes.hasOwnProperty(nodeId)) {
35693              var containedNode = this.body.nodes[nodeId];
35694              if (newPositions[nodeId] !== undefined) {
35695                containedNode.x = newPositions[nodeId].x === undefined ? clusterNode.x : newPositions[nodeId].x;
35696                containedNode.y = newPositions[nodeId].y === undefined ? clusterNode.y : newPositions[nodeId].y;
35697              }
35698            }
35699          }
35700        } else {
35701          // copy the position from the cluster
35702          for (var nodeId in containedNodes) {
35703            if (containedNodes.hasOwnProperty(nodeId)) {
35704              var containedNode = this.body.nodes[nodeId];
35705              containedNode = containedNodes[nodeId];
35706              // inherit position
35707              containedNode.x = clusterNode.x;
35708              containedNode.y = clusterNode.y;
35709            }
35710          }
35711        }
35712
35713        // release nodes
35714        for (var nodeId in containedNodes) {
35715          if (containedNodes.hasOwnProperty(nodeId)) {
35716            var containedNode = this.body.nodes[nodeId];
35717
35718            // inherit speed
35719            containedNode.vx = clusterNode.vx;
35720            containedNode.vy = clusterNode.vy;
35721
35722            // we use these methods to avoid reinstantiating the shape, which happens with setOptions.
35723            containedNode.setOptions({ hidden: false, physics: true });
35724
35725            delete this.clusteredNodes[nodeId];
35726          }
35727        }
35728
35729        // copy the clusterNode edges because we cannot iterate over an object that we add or remove from.
35730        var edgesToBeDeleted = [];
35731        for (var i = 0; i < clusterNode.edges.length; i++) {
35732          edgesToBeDeleted.push(clusterNode.edges[i]);
35733        }
35734
35735        // actually handling the deleting.
35736        for (var i = 0; i < edgesToBeDeleted.length; i++) {
35737          var edge = edgesToBeDeleted[i];
35738
35739          var otherNodeId = this._getConnectedId(edge, clusterNodeId);
35740          // if the other node is in another cluster, we transfer ownership of this edge to the other cluster
35741          if (this.clusteredNodes[otherNodeId] !== undefined) {
35742            // transfer ownership:
35743            var otherCluster = this.body.nodes[this.clusteredNodes[otherNodeId].clusterId];
35744            var transferEdge = this.body.edges[edge.clusteringEdgeReplacingId];
35745            if (transferEdge !== undefined) {
35746              otherCluster.containedEdges[transferEdge.id] = transferEdge;
35747
35748              // delete local reference
35749              delete containedEdges[transferEdge.id];
35750
35751              // create new cluster edge from the otherCluster:
35752              // get to and from
35753              var fromId = transferEdge.fromId;
35754              var toId = transferEdge.toId;
35755              if (transferEdge.toId == otherNodeId) {
35756                toId = this.clusteredNodes[otherNodeId].clusterId;
35757              } else {
35758                fromId = this.clusteredNodes[otherNodeId].clusterId;
35759              }
35760
35761              // clone the options and apply the cluster options to them
35762              var clonedOptions = this._cloneOptions(transferEdge, 'edge');
35763              util.deepExtend(clonedOptions, otherCluster.clusterEdgeProperties);
35764
35765              // apply the edge specific options to it.
35766              var id = 'clusterEdge:' + util.randomUUID();
35767              util.deepExtend(clonedOptions, { from: fromId, to: toId, hidden: false, physics: true, id: id });
35768
35769              // create it
35770              var newEdge = this.body.functions.createEdge(clonedOptions);
35771              newEdge.clusteringEdgeReplacingId = transferEdge.id;
35772              this.body.edges[id] = newEdge;
35773              this.body.edges[id].connect();
35774            }
35775          } else {
35776            var replacedEdge = this.body.edges[edge.clusteringEdgeReplacingId];
35777            if (replacedEdge !== undefined) {
35778              replacedEdge.setOptions({ physics: true, hidden: false });
35779              replacedEdge.hiddenByCluster = false;
35780            }
35781          }
35782          edge.cleanup();
35783          // this removes the edge from node.edges, which is why edgeIds is formed
35784          edge.disconnect();
35785          delete this.body.edges[edge.id];
35786        }
35787
35788        // handle the releasing of the edges
35789        for (var edgeId in containedEdges) {
35790          if (containedEdges.hasOwnProperty(edgeId)) {
35791            var edge = containedEdges[edgeId];
35792            edge.setOptions({ physics: true, hidden: false });
35793            edge.hiddenByCluster = undefined;
35794            delete edge.hiddenByCluster;
35795          }
35796        }
35797
35798        // remove clusterNode
35799        delete this.body.nodes[clusterNodeId];
35800
35801        if (refreshData === true) {
35802          this.body.emitter.emit('_dataChanged');
35803        }
35804      }
35805    }, {
35806      key: 'getNodesInCluster',
35807      value: function getNodesInCluster(clusterId) {
35808        var nodesArray = [];
35809        if (this.isCluster(clusterId) === true) {
35810          var containedNodes = this.body.nodes[clusterId].containedNodes;
35811          for (var nodeId in containedNodes) {
35812            if (containedNodes.hasOwnProperty(nodeId)) {
35813              nodesArray.push(nodeId);
35814            }
35815          }
35816        }
35817
35818        return nodesArray;
35819      }
35820    }, {
35821      key: 'findNode',
35822
35823      /**
35824      * Get the stack clusterId's that a certain node resides in. cluster A -> cluster B -> cluster C -> node
35825      * @param nodeId
35826      * @returns {Array}
35827      * @private
35828      */
35829      value: function findNode(nodeId) {
35830        var stack = [];
35831        var max = 100;
35832        var counter = 0;
35833
35834        while (this.clusteredNodes[nodeId] !== undefined && counter < max) {
35835          stack.push(this.clusteredNodes[nodeId].node);
35836          nodeId = this.clusteredNodes[nodeId].clusterId;
35837          counter++;
35838        }
35839        stack.push(this.body.nodes[nodeId]);
35840        return stack;
35841      }
35842    }, {
35843      key: '_getConnectedId',
35844
35845      /**
35846      * Get the Id the node is connected to
35847      * @param edge
35848      * @param nodeId
35849      * @returns {*}
35850      * @private
35851      */
35852      value: function _getConnectedId(edge, nodeId) {
35853        if (edge.toId != nodeId) {
35854          return edge.toId;
35855        } else if (edge.fromId != nodeId) {
35856          return edge.fromId;
35857        } else {
35858          return edge.fromId;
35859        }
35860      }
35861    }, {
35862      key: '_getHubSize',
35863
35864      /**
35865      * We determine how many connections denote an important hub.
35866      * We take the mean + 2*std as the important hub size. (Assuming a normal distribution of data, ~2.2%)
35867      *
35868      * @private
35869      */
35870      value: function _getHubSize() {
35871        var average = 0;
35872        var averageSquared = 0;
35873        var hubCounter = 0;
35874        var largestHub = 0;
35875
35876        for (var i = 0; i < this.body.nodeIndices.length; i++) {
35877          var node = this.body.nodes[this.body.nodeIndices[i]];
35878          if (node.edges.length > largestHub) {
35879            largestHub = node.edges.length;
35880          }
35881          average += node.edges.length;
35882          averageSquared += Math.pow(node.edges.length, 2);
35883          hubCounter += 1;
35884        }
35885        average = average / hubCounter;
35886        averageSquared = averageSquared / hubCounter;
35887
35888        var variance = averageSquared - Math.pow(average, 2);
35889        var standardDeviation = Math.sqrt(variance);
35890
35891        var hubThreshold = Math.floor(average + 2 * standardDeviation);
35892
35893        // always have at least one to cluster
35894        if (hubThreshold > largestHub) {
35895          hubThreshold = largestHub;
35896        }
35897
35898        return hubThreshold;
35899      }
35900    }]);
35901
35902    return ClusterEngine;
35903  })();
35904
35905  exports['default'] = ClusterEngine;
35906  module.exports = exports['default'];
35907
35908/***/ },
35909/* 100 */
35910/***/ function(module, exports, __webpack_require__) {
35911
35912  'use strict';
35913
35914  Object.defineProperty(exports, '__esModule', {
35915    value: true
35916  });
35917
35918  var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; desc = parent = getter = undefined; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } };
35919
35920  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
35921
35922  function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
35923
35924  function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { c
35924onstructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) subClass.__proto__ = superClass; }
35925
35926  var _Node2 = __webpack_require__(62);
35927
35928  var _Node3 = _interopRequireDefault(_Node2);
35929
35930  /**
35931   *
35932   */
35933
35934  var Cluster = (function (_Node) {
35935    _inherits(Cluster, _Node);
35936
35937    function Cluster(options, body, imagelist, grouplist, globalOptions) {
35938      _classCallCheck(this, Cluster);
35939
35940      _get(Object.getPrototypeOf(Cluster.prototype), 'constructor', this).call(this, options, body, imagelist, grouplist, globalOptions);
35941
35942      this.isCluster = true;
35943      this.containedNodes = {};
35944      this.containedEdges = {};
35945    }
35946
35947    return Cluster;
35948  })(_Node3['default']);
35949
35950  exports['default'] = Cluster;
35951  module.exports = exports['default'];
35952
35953/***/ },
35954/* 101 */
35955/***/ function(module, exports, __webpack_require__) {
35956
35957  'use strict';
35958
35959  Object.defineProperty(exports, '__esModule', {
35960    value: true
35961  });
35962
35963  var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
35964
35965  function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
35966
35967  if (typeof window !== 'undefined') {
35968    window.requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame || window.msRequestAnimationFrame;
35969  }
35970
35971  var util = __webpack_require__(1);
35972
35973  var CanvasRenderer = (function () {
35974    function CanvasRenderer(body, canvas) {
35975      _classCallCheck(this, CanvasRenderer);
35976
35977      this.body = body;
35978      this.canvas = canvas;
35979
35980      this.redrawRequested = false;
35981      this.renderTimer = undefined;
35982      this.requiresTimeout = true;
35983      this.renderingActive = false;
35984      this.renderRequests = 0;
35985      this.pixelRatio = undefined;
35986      this.allowRedraw = true;
35987
35988      this.dragging = false;
35989      this.options = {};
35990      this.defaultOptions = {
35991        hideEdgesOnDrag: false,
35992        hideNodesOnDrag: false
35993      };
35994      util.extend(this.options, this.defaultOptions);
35995
35996      this._determineBrowserMethod();
35997      this.bindEventListeners();
35998    }
35999
36000    _createClass(CanvasRenderer, [{
36001      key: 'bindEventListeners',
36002      value: function bindEventListeners() {
36003        var _this = this;
36004
36005        this.body.emitter.on('dragStart', function () {
36006          _this.dragging = true;
36007        });
36008        this.body.emitter.on('dragEnd', function () {
36009          return _this.dragging = false;
36010        });
36011        this.body.emitter.on('_resizeNodes', function () {
36012          return _this._resizeNodes();
36013        });
36014        this.body.emitter.on('_redraw', function () {
36015          if (_this.renderingActive === false) {
36016            _this._redraw();
36017          }
36018        });
36019        this.body.emitter.on('_blockRedraw', function () {
36020          _this.allowRedraw = false;
36021        });
36022        this.body.emitter.on('_allowRedraw', function () {
36023          _this.allowRedraw = true;_this.redrawRequested = false;
36024        });
36025        this.body.emitter.on('_requestRedraw', this._requestRedraw.bind(this));
36026        this.body.emitter.on('_startRendering', function () {
36027          _this.renderRequests += 1;
36028          _this.renderingActive = true;
36029          _this._startRendering();
36030        });
36031        this.body.emitter.on('_stopRendering', function () {
36032          _this.renderRequests -= 1;
36033          _this.renderingActive = _this.renderRequests > 0;
36034          _this.renderTimer = undefined;
36035        });
36036        this.body.emitter.on('destroy', function () {
36037          _this.renderRequests = 0;
36038          _this.allowRedraw = false;
36039          _this.renderingActive = false;
36040          if (_this.requiresTimeout === true) {
36041            clearTimeout(_this.renderTimer);
36042          } else {
36043            cancelAnimationFrame(_this.renderTimer);
36044          }
36045          _this.body.emitter.off();
36046        });
36047      }
36048    }, {
36049      key: 'setOptions',
36050      value: function setOptions(options) {
36051        if (options !== undefined) {
36052          var fields = ['hideEdgesOnDrag', 'hideNodesOnDrag'];
36053          util.selectiveDeepExtend(fields, this.options, options);
36054        }
36055      }
36056    }, {
36057      key: '_startRendering',
36058      value: function _startRendering() {
36059        if (this.renderingActive === true) {
36060          if (this.renderTimer === undefined) {
36061            if (this.requiresTimeout === true) {
36062              this.renderTimer = window.setTimeout(this._renderStep.bind(this), this.simulationInterval); // wait this.renderTimeStep milliseconds and perform the animation step function
36063            } else {
36064              this.renderTimer = window.requestAnimationFrame(this._renderStep.bind(this)); // wait this.renderTimeStep milliseconds and perform the animation step function
36065            }
36066          }
36067        }
36068      }
36069    }, {
36070      key: '_renderStep',
36071      value: function _renderStep() {
36072        if (this.renderingActive === true) {
36073          // reset the renderTimer so a new scheduled animation step can be set
36074          this.renderTimer = undefined;
36075
36076          if (this.requiresTimeout === true) {
36077            // this schedules a new simulation step
36078            this._startRendering();
36079          }
36080
36081          this._redraw();
36082
36083          if (this.requiresTimeout === false) {
36084            // this schedules a new simulation step
36085            this._startRendering();
36086          }
36087        }
36088      }
36089    }, {
36090      key: 'redraw',
36091
36092      /**
36093       * Redraw the network with the current data
36094       * chart will be resized too.
36095       */
36096      value: function redraw() {
36097        this.body.emitter.emit('setSize');
36098        this._redraw();
36099      }
36100    }, {
36101      key: '_requestRedraw',
36102
36103      /**
36104       * Redraw the network with the current data
36105       * @param hidden | used to get the first estimate of the node sizes. only the nodes are drawn after which they are quickly drawn over.
36106       * @private
36107       */
36108      value: function _requestRedraw() {
36109        var _this2 = this;
36110
36111        if (this.redrawRequested !== true && this.renderingActive === false && this.allowRedraw === true) {
36112          this.redrawRequested = true;
36113          if (this.requiresTimeout === true) {
36114            window.setTimeout(function () {
36115              _this2._redraw(false);
36116            }, 0);
36117          } else {
36118            window.requestAnimationFrame(function () {
36119              _this2._redraw(false);
36120            });
36121          }
36122        }
36123      }
36124    }, {
36125      key: '_redraw',
36126      value: function _redraw() {
36127        var hidden = arguments.length <= 0 || arguments[0] === undefined ? false : arguments[0];
36128
36129        if (this.allowRedraw === true) {
36130          this.body.emitter.emit('initRedraw');
36131
36132          this.redrawRequested = false;
36133          var ctx = this.canvas.frame.canvas.getContext('2d');
36134
36135          // when the container div was hidden, this fixes it back up!
36136          if (this.canvas.frame.canvas.width === 0 || this.canvas.frame.canvas.height === 0) {
36137            this.canvas.setSize();
36138          }
36139
36140          this.pixelRatio = (window.devicePixelRatio || 1) / (ctx.webkitBackingStorePixelRatio || ctx.mozBackingStorePixelRatio || ctx.msBackingStorePixelRatio || ctx.oBackingStorePixelRatio || ctx.backingStorePixelRatio || 1);
36141
36142          ctx.setTransform(this.pixelRatio, 0, 0, this.pixelRatio, 0, 0);
36143
36144          // clear the canvas
36145          var w = this.canvas.frame.canvas.clientWidth;
36146          var h = this.canvas.frame.canvas.clientHeight;
36147          ctx.clearRect(0, 0, w, h);
36148
36149          // if the div is hidden, we stop the redraw here for performance.
36150          if (this.canvas.frame.clientWidth === 0) {
36151            return;
36152          }
36153
36154          // set scaling and translation
36155          ctx.save();
36156          ctx.translate(this.body.view.translation.x, this.body.view.translation.y);
36157          ctx.scale(this.body.view.scale, this.body.view.scale);
36158
36159          ctx.beginPath();
36160          this.body.emitter.emit('beforeDrawing', ctx);
36161          ctx.closePath();
36162
36163          if (hidden === false) {
36164            if (this.dragging === false || this.dragging === true && this.options.hideEdgesOnDrag === false) {
36165              this._drawEdges(ctx);
36166            }
36167          }
36168
36169          if (this.dragging === false || this.dragging === true && this.options.hideNodesOnDrag === false) {
36170            this._drawNodes(ctx, hidden);
36171          }
36172
36173          if (this.controlNodesActive === true) {
36174            this._drawControlNodes(ctx);
36175          }
36176
36177          ctx.beginPath();
36178          //this.physics.nodesSolver._debug(ctx,"#F00F0F");
36179          this.body.emitter.emit('afterDrawing', ctx);
36180          ctx.closePath();
36181          // restore original scaling and translation
36182          ctx.restore();
36183
36184          if (hidden === true) {
36185            ctx.clearRect(0, 0, w, h);
36186          }
36187        }
36188      }
36189    }, {
36190      key: '_resizeNodes',
36191
36192      /**
36193       * Redraw all nodes
36194       * The 2d context of a HTML canvas can be retrieved by canvas.getContext('2d');
36195       * @param {CanvasRenderingContext2D}   ctx
36196       * @param {Boolean} [alwaysShow]
36197       * @private
36198       */
36199      value: function _resizeNodes() {
36200        var ctx = this.canvas.frame.canvas.getContext('2d');
36201        if (this.pixelRatio === undefined) {
36202          this.pixelRatio = (window.devicePixelRatio || 1) / (ctx.webkitBackingStorePixelRatio || ctx.mozBackingStorePixelRatio || ctx.msBackingStorePixelRatio || ctx.oBackingStorePixelRatio || ctx.backingStorePixelRatio || 1);
36203        }
36204        ctx.setTransform(this.pixelRatio, 0, 0, this.pixelRatio, 0, 0);
36205        ctx.save();
36206        ctx.translate(this.body.view.translation.x, this.body.view.translation.y);
36207        ctx.scale(this.body.view.scale, this.body.view.scale);
36208
36209        var nodes = this.body.nodes;
36210        var node = undefined;
36211
36212        // resize all nodes
36213        for (var nodeId in nodes) {
36214          if (nodes.hasOwnProperty(nodeId)) {
36215            node = nodes[nodeId];
36216            node.resize(ctx);
36217            node.updateBoundingBox(ctx, node.selected);
36218          }
36219        }
36220
36221        // restore original scaling and translation
36222        ctx.restore();
36223      }
36224    }, {
36225      key: '_drawNodes',
36226
36227      /**
36228       * Redraw all nodes
36229       * The 2d context of a HTML canvas can be retrieved by canvas.getContext('2d');
36230       * @param {CanvasRenderingContext2D}   ctx
36231       * @param {Boolean} [alwaysShow]
36232       * @private
36233       */
36234      value: function _drawNodes(ctx) {
36235        var alwaysShow = arguments.length <= 1 || arguments[1] === undefined ? false : arguments[1];
36236
36237        var nodes = this.body.nodes;
36238        var nodeIndices = this.body.nodeIndices;
36239        var node = undefined;
36240        var selected = [];
36241        var margin = 20;
36242        var topLeft = this.canvas.DOMtoCanvas({ x: -margin, y: -margin });
36243        var bottomRight = this.canvas.DOMtoCanvas({
36244          x: this.canvas.frame.canvas.clientWidth + margin,
36245          y: this.canvas.frame.canvas.clientHeight + margin
36246        });
36247        var viewableArea = { top: topLeft.y, left: topLeft.x, bottom: bottomRight.y, right: bottomRight.x };
36248
36249        // draw unselected nodes;
36250        for (var i = 0; i < nodeIndices.length; i++) {
36251          node = nodes[nodeIndices[i]];
36252          // set selected nodes aside
36253          if (node.isSelected()) {
36254            selected.push(nodeIndices[i]);
36255          } else {
36256            if (alwaysShow === true) {
36257              node.draw(ctx);
36258            } else if (node.isBoundingBoxOverlappingWith(viewableArea) === true) {
36259              node.draw(ctx);
36260            } else {
36261              node.updateBoundingBox(ctx, node.selected);
36262            }
36263          }
36264        }
36265
36266        // draw the selected nodes on top
36267        for (var i = 0; i < selected.length; i++) {
36268          node = nodes[selected[i]];
36269          node.draw(ctx);
36270        }
36271      }
36272    }, {
36273      key: '_drawEdges',
36274
36275      /**
36276       * Redraw all edges
36277       * The 2d context of a HTML canvas can be retrieved by canvas.getContext('2d');
36278       * @param {CanvasRenderingContext2D}   ctx
36279       * @private
36280       */
36281      value: function _drawEdges(ctx) {
36282        var edges = this.body.edges;
36283        var edgeIndices = this.body.edgeIndices;
36284        var edge = undefined;
36285
36286        for (var i = 0; i < edgeIndices.length; i++) {
36287          edge = edges[edgeIndices[i]];
36288          if (edge.connected === true) {
36289            edge.draw(ctx);
36290          }
36291        }
36292      }
36293    }, {
36294      key: '_drawControlNodes',
36295
36296      /**
36297       * Redraw all edges
36298       * The 2d context of a HTML canvas can be retrieved by canvas.getContext('2d');
36299       * @param {CanvasRenderingContext2D}   ctx
36300       * @private
36301       */
36302      value: function _drawControlNodes(ctx) {
36303        var edges = this.body.edges;
36304        var edgeIndices = this.body.edgeIndices;
36305        var edge = undefined;
36306
36307        for (var i = 0; i < edgeIndices.length; i++) {
36308          edge = edges[edgeIndices[i]];
36309          edge._drawControlNodes(ctx);
36310        }
36311      }
36312    }, {
36313      key: '_determineBrowserMethod',
36314
36315      /**
36316       * Determine if the browser requires a setTimeout or a requestAnimationFrame. This was required because
36317       * some implementations (safari and IE9) did not support requestAnimationFrame
36318       * @private
36319       */
36320      value: function _determineBrowserMethod() {
36321        if (typeof window !== 'undefined') {
36322          var browserType = navigator.userAgent.toLowerCase();
36323          this.requiresTimeout = false;
36324          if (browserType.indexOf('msie 9.0') != -1) {
36325            // IE 9
36326            this.requiresTimeout = true;
36327          } else if (browserType.indexOf('safari') != -1) {
36328            // safari
36329            if (browserType.indexOf('chrome') <= -1) {
36330              this.requiresTimeout = true;
36331            }
36332          }
36333        } else {
36334          this.requiresTimeout = true;
36335        }
36336      }
36337    }]);
36338
36339    return CanvasRenderer;
36340  })();
36341
36342  exports['default'] = CanvasRenderer;
36343  module.exports = exports['default'];
36344
36345/***/ },
36346/* 102 */
36347/***/ function(module, exports, __webpack_require__) {
36348
36349  'use strict';
36350
36351  Object.defineProperty(exports, '__esModule', {
36352    value: true
36353  });
36354
36355  var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
36356
36357  function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
36358
36359  var Hammer = __webpack_require__(20);
36360  var hammerUtil = __webpack_require__(25);
36361
36362  var util = __webpack_require__(1);
36363
36364  /**
36365   * Create the main frame for the Network.
36366   * This function is executed once when a Network object is created. The frame
36367   * contains a canvas, and this canvas contains all objects like the axis and
36368   * nodes.
36369   * @private
36370   */
36371
36372  var Canvas = (function () {
36373    function Canvas(body) {
36374      _classCallCheck(this, Canvas);
36375
36376      this.body = body;
36377      this.pixelRatio = 1;
36378      this.resizeTimer = undefined;
36379      this.resizeFunction = this._onResize.bind(this);
36380      this.cameraState = {};
36381
36382      this.options = {};
36383      this.defaultOptions = {
36384        autoResize: true,
36385        height: '100%',
36386        width: '100%'
36387      };
36388      util.extend(this.options, this.defaultOptions);
36389
36390      this.bindEventListeners();
36391    }
36392
36393    _createClass(Canvas, [{
36394      key: 'bindEventListeners',
36395      value: function bindEventListeners() {
36396        var _this = this;
36397
36398        // bind the events
36399        this.body.emitter.once('resize', function (obj) {
36400          if (obj.width !== 0) {
36401            _this.body.view.translation.x = obj.width * 0.5;
36402          }
36403          if (obj.height !== 0) {
36404            _this.body.view.translation.y = obj.height * 0.5;
36405          }
36406        });
36407        this.body.emitter.on('setSize', this.setSize.bind(this));
36408        this.body.emitter.on('destroy', function () {
36409          _this.hammerFrame.destroy();
36410          _this.hammer.destroy();
36411          _this._cleanUp();
36412        });
36413      }
36414    }, {
36415      key: 'setOptions',
36416      value: function setOptions(options) {
36417        var _this2 = this;
36418
36419        if (options !== undefined) {
36420          var fields = ['width', 'height', 'autoResize'];
36421          util.selectiveDeepExtend(fields, this.options, options);
36422        }
36423
36424        if (this.options.autoResize === true) {
36425          // automatically adapt to a changing size of the browser.
36426          this._cleanUp();
36427          this.resizeTimer = setInterval(function () {
36428            var changed = _this2.setSize();
36429            if (changed === true) {
36430              _this2.body.emitter.emit('_requestRedraw');
36431            }
36432          }, 1000);
36433          this.resizeFunction = this._onResize.bind(this);
36434          util.addEventListener(window, 'resize', this.resizeFunction);
36435        }
36436      }
36437    }, {
36438      key: '_cleanUp',
36439      value: function _cleanUp() {
36440        // automatically adapt to a changing size of the browser.
36441        if (this.resizeTimer !== undefined) {
36442          clearInterval(this.resizeTimer);
36443        }
36444        util.removeEventListener(window, 'resize', this.resizeFunction);
36445        this.resizeFunction = undefined;
36446      }
36447    }, {
36448      key: '_onResize',
36449      value: function _onResize() {
36450        this.setSize();
36451        this.body.emitter.emit('_redraw');
36452      }
36453    }, {
36454      key: '_getCameraState',
36455
36456      /**
36457       * Get and store the cameraState
36458       * @private
36459       */
36460      value: function _getCameraState() {
36461        var pixelRatio = arguments.length <= 0 || arguments[0] === undefined ? this.pixelRatio : arguments[0];
36462
36463        this.cameraState.previousWidth = this.frame.canvas.width / pixelRatio;
36464        this.cameraState.previousHeight = this.frame.canvas.height / pixelRatio;
36465        this.cameraState.scale = this.body.view.scale;
36466        this.cameraState.position = this.DOMtoCanvas({ x: 0.5 * this.frame.canvas.width / pixelRatio, y: 0.5 * this.frame.canvas.height / pixelRatio });
36467      }
36468    }, {
36469      key: '_setCameraState',
36470
36471      /**
36472       * Set the cameraState
36473       * @private
36474       */
36475      value: function _setCameraState() {
36476        if (this.cameraState.scale !== undefined && this.frame.canvas.clientWidth !== 0 && this.frame.canvas.clientHeight !== 0 && this.pixelRatio !== 0 && this.cameraState.previousWidth > 0) {
36477
36478          this.body.view.scale = this.cameraState.scale * Math.min(this.frame.canvas.width / this.pixelRatio / this.cameraState.previousWidth, this.frame.canvas.height / this.pixelRatio / this.cameraState.previousHeight);
36479
36480          // this comes from the view module.
36481          var currentViewCenter = this.DOMtoCanvas({
36482            x: 0.5 * this.frame.canvas.clientWidth,
36483            y: 0.5 * this.frame.canvas.clientHeight
36484          });
36485
36486          var distanceFromCenter = { // offset from view, distance view has to change by these x and y to center the node
36487            x: currentViewCenter.x - this.cameraState.position.x,
36488            y: currentViewCenter.y - this.cameraState.position.y
36489          };
36490          this.body.view.translation.x += distanceFromCenter.x * this.body.view.scale;
36491          this.body.view.translation.y += distanceFromCenter.y * this.body.view.scale;
36492        }
36493      }
36494    }, {
36495      key: '_prepareValue',
36496      value: function _prepareValue(value) {
36497        if (typeof value === 'number') {
36498          return value + 'px';
36499        } else if (typeof value === 'string') {
36500          if (value.indexOf('%') !== -1 || value.indexOf('px') !== -1) {
36501            return value;
36502          } else if (value.indexOf('%') === -1) {
36503            return value + 'px';
36504          }
36505        }
36506        throw new Error('Could not use the value supplied for width or height:' + value);
36507      }
36508    }, {
36509      key: '_create',
36510
36511      /**
36512       * Create the HTML
36513       */
36514      value: function _create() {
36515        // remove all elements from the container element.
36516        while (this.body.container.hasChildNodes()) {
36517          this.body.container.removeChild(this.body.container.firstChild);
36518        }
36519
36520        this.frame = document.createElement('div');
36521        this.frame.className = 'vis-network';
36522        this.frame.style.position = 'relative';
36523        this.frame.style.overflow = 'hidden';
36524        this.frame.tabIndex = 900; // tab index is required for keycharm to bind keystrokes to the div instead of the window
36525
36526        //////////////////////////////////////////////////////////////////
36527
36528        this.frame.canvas = document.createElement('canvas');
36529        this.frame.canvas.style.position = 'relative';
36530        this.frame.appendChild(this.frame.canvas);
36531
36532        if (!this.frame.canvas.getContext) {
36533          var noCanvas = document.createElement('DIV');
36534          noCanvas.style.color = 'red';
36535          noCanvas.style.fontWeight = 'bold';
36536          noCanvas.style.padding = '10px';
36537          noCanvas.innerHTML = 'Error: your browser does not support HTML canvas';
36538          this.frame.canvas.appendChild(noCanvas);
36539        } else {
36540          var ctx = this.frame.canvas.getContext('2d');
36541          this.pixelRatio = (window.devicePixelRatio || 1) / (ctx.webkitBackingStorePixelRatio || ctx.mozBackingStorePixelRatio || ctx.msBackingStorePixelRatio || ctx.oBackingStorePixelRatio || ctx.backingStorePixelRatio || 1);
36542
36543          this.frame.canvas.getContext('2d').setTransform(this.pixelRatio, 0, 0, this.pixelRatio, 0, 0);
36544        }
36545
36546        // add the frame to the container element
36547        this.body.container.appendChild(this.frame);
36548
36549        this.body.view.scale = 1;
36550        this.body.view.translation = { x: 0.5 * this.frame.canvas.clientWidth, y: 0.5 * this.frame.canvas.clientHeight };
36551
36552        this._bindHammer();
36553      }
36554    }, {
36555      key: '_bindHammer',
36556
36557      /**
36558       * This function binds hammer, it can be repeated over and over due to the uniqueness check.
36559       * @private
36560       */
36561      value: function _bindHammer() {
36562        var _this3 = this;
36563
36564        if (this.hammer !== undefined) {
36565          this.hammer.destroy();
36566        }
36567        this.drag = {};
36568        this.pinch = {};
36569
36570        // init hammer
36571        this.hammer = new Hammer(this.frame.canvas);
36572        this.hammer.get('pinch').set({ enable: true });
36573        // enable to get better response, todo: test on mobile.
36574        this.hammer.get('pan').set({ threshold: 5, direction: 30 }); // 30 is ALL_DIRECTIONS in hammer.
36575
36576        hammerUtil.onTouch(this.hammer, function (event) {
36577          _this3.body.eventListeners.onTouch(event);
36578        });
36579        this.hammer.on('tap', function (event) {
36580          _this3.body.eventListeners.onTap(event);
36581        });
36582        this.hammer.on('doubletap', function (event) {
36583          _this3.body.eventListeners.onDoubleTap(event);
36584        });
36585        this.hammer.on('press', function (event) {
36586          _this3.body.eventListeners.onHold(event);
36587        });
36588        this.hammer.on('panstart', function (event) {
36589          _this3.body.eventListeners.onDragStart(event);
36590        });
36591        this.hammer.on('panmove', function (event) {
36592          _this3.body.eventListeners.onDrag(event);
36593        });
36594        this.hammer.on('panend', function (event) {
36595          _this3.body.eventListeners.onDragEnd(event);
36596        });
36597        this.hammer.on('pinch', function (event) {
36598          _this3.body.eventListeners.onPinch(event);
36599        });
36600
36601        // TODO: neatly cleanup these handlers when re-creating the Canvas, IF these are done with hammer, event.stopPropagation will not work?
36602        this.frame.canvas.addEventListener('mousewheel', function (event) {
36603          _this3.body.eventListeners.onMouseWheel(event);
36604        });
36605        this.frame.canvas.addEventListener('DOMMouseScroll', function (event) {
36606          _this3.body.eventListeners.onMouseWheel(event);
36607        });
36608
36609        this.frame.canvas.addEventListener('mousemove', function (event) {
36610          _this3.body.eventListeners.onMouseMove(event);
36611        });
36612        this.frame.canvas.addEventListener('contextmenu', function (event) {
36613          _this3.body.eventListeners.onContext(event);
36614        });
36615
36616        this.hammerFrame = new Hammer(this.frame);
36617        hammerUtil.onRelease(this.hammerFrame, function (event) {
36618          _this3.body.eventListeners.onRelease(event);
36619        });
36620      }
36621    }, {
36622      key: 'setSize',
36623
36624      /**
36625       * Set a new size for the network
36626       * @param {string} width   Width in pixels or percentage (for example '800px'
36627       *                         or '50%')
36628       * @param {string} height  Height in pixels or percentage  (for example '400px'
36629       *                         or '30%')
36630       */
36631      value: function setSize() {
36632        var width = arguments.length <= 0 || arguments[0] === undefined ? this.options.width : arguments[0];
36633        var height = arguments.length <= 1 || arguments[1] === undefined ? this.options.height : arguments[1];
36634
36635        width = this._prepareValue(width);
36636        height = this._prepareValue(height);
36637
36638        var emitEvent = false;
36639        var oldWidth = this.frame.canvas.width;
36640        var oldHeight = this.frame.canvas.height;
36641
36642        // update the pixelratio
36643        var ctx = this.frame.canvas.getContext('2d');
36644        var previousRation = this.pixelRatio; // we cache this because the camera state storage needs the old value
36645        this.pixelRatio = (window.devicePixelRatio || 1) / (ctx.webkitBackingStorePixelRatio || ctx.mozBackingStorePixelRatio || ctx.msBackingStorePixelRatio || ctx.oBackingStorePixelRatio || ctx.backingStorePixelRatio || 1);
36646
36647        if (width != this.options.width || height != this.options.height || this.frame.style.width != width || this.frame.style.height != height) {
36648          this._getCameraState(previousRation);
36649
36650          this.frame.style.width = width;
36651          this.frame.style.height = height;
36652
36653          this.frame.canvas.style.width = '100%';
36654          this.frame.canvas.style.height = '100%';
36655
36656          this.frame.canvas.width = Math.round(this.frame.canvas.clientWidth * this.pixelRatio);
36657          this.frame.canvas.height = Math.round(this.frame.canvas.clientHeight * this.pixelRatio);
36658
36659          this.options.width = width;
36660          this.options.height = height;
36661
36662          emitEvent = true;
36663        } else {
36664          // this would adapt the width of the canvas to the width from 100% if and only if
36665          // there is a change.
36666
36667          // store the camera if there is a change in size.
36668          if (this.frame.canvas.width != Math.round(this.frame.canvas.clientWidth * this.pixelRatio) || this.frame.canvas.height != Math.round(this.frame.canvas.clientHeight * this.pixelRatio)) {
36669            this._getCameraState(previousRation);
36670          }
36671
36672          if (this.frame.canvas.width != Math.round(this.frame.canvas.clientWidth * this.pixelRatio)) {
36673            this.frame.canvas.width = Math.round(this.frame.canvas.clientWidth * this.pixelRatio);
36674            emitEvent = true;
36675          }
36676          if (this.frame.canvas.height != Math.round(this.frame.canvas.clientHeight * this.pixelRatio)) {
36677            this.frame.canvas.height = Math.round(this.frame.canvas.clientHeight * this.pixelRatio);
36678            emitEvent = true;
36679          }
36680        }
36681
36682        if (emitEvent === true) {
36683          this.body.emitter.emit('resize', {
36684            width: Math.round(this.frame.canvas.width / this.pixelRatio),
36685            height: Math.round(this.frame.canvas.height / this.pixelRatio),
36686            oldWidth: Math.round(oldWidth / this.pixelRatio),
36687            oldHeight: Math.round(oldHeight / this.pixelRatio)
36688          });
36689
36690          // restore the camera on change.
36691          this._setCameraState();
36692        }
36693
36694        return emitEvent;
36695      }
36696    }, {
36697      key: '_XconvertDOMtoCanvas',
36698
36699      /**
36700       * Convert the X coordinate in DOM-space (coordinate point in browser relative to the container div) to
36701       * the X coordinate in canvas-space (the simulation sandbox, which the camera looks upon)
36702       * @param {number} x
36703       * @returns {number}
36704       * @private
36705       */
36706      value: function _XconvertDOMtoCanvas(x) {
36707        return (x - this.body.view.translation.x) / this.body.view.scale;
36708      }
36709    }, {
36710      key: '_XconvertCanvasToDOM',
36711
36712      /**
36713       * Convert the X coordinate in canvas-space (the simulation sandbox, which the camera looks upon) to
36714       * the X coordinate in DOM-space (coordinate point in browser relative to the container div)
36715       * @param {number} x
36716       * @returns {number}
36717       * @private
36718       */
36719      value: function _XconvertCanvasToDOM(x) {
36720        return x * this.body.view.scale + this.body.view.translation.x;
36721      }
36722    }, {
36723      key: '_YconvertDOMtoCanvas',
36724
36725      /**
36726       * Convert the Y coordinate in DOM-space (coordinate point in browser relative to the container div) to
36727       * the Y coordinate in canvas-space (the simulation sandbox, which the camera looks upon)
36728       * @param {number} y
36729       * @returns {number}
36730       * @private
36731       */
36732      value: function _YconvertDOMtoCanvas(y) {
36733        return (y - this.body.view.translation.y) / this.body.view.scale;
36734      }
36735    }, {
36736      key: '_YconvertCanvasToDOM',
36737
36738      /**
36739       * Convert the Y coordinate in canvas-space (the simulation sandbox, which the camera looks upon) to
36740       * the Y coordinate in DOM-space (coordinate point in browser relative to the container div)
36741       * @param {number} y
36742       * @returns {number}
36743       * @private
36744       */
36745      value: function _YconvertCanvasToDOM(y) {
36746        return y * this.body.view.scale + this.body.view.translation.y;
36747      }
36748    }, {
36749      key: 'canvasToDOM',
36750
36751      /**
36752       *
36753       * @param {object} pos   = {x: number, y: number}
36754       * @returns {{x: number, y: number}}
36755       * @constructor
36756       */
36757      value: function canvasToDOM(pos) {
36758        return { x: this._XconvertCanvasToDOM(pos.x), y: this._YconvertCanvasToDOM(pos.y) };
36759      }
36760    }, {
36761      key: 'DOMtoCanvas',
36762
36763      /**
36764       *
36765       * @param {object} pos   = {x: number, y: number}
36766       * @returns {{x: number, y: number}}
36767       * @constructor
36768       */
36769      value: function DOMtoCanvas(pos) {
36770        return { x: this._XconvertDOMtoCanvas(pos.x), y: this._YconvertDOMtoCanvas(pos.y) };
36771      }
36772    }]);
36773
36774    return Canvas;
36775  })();
36776
36777  exports['default'] = Canvas;
36778  module.exports = exports['default'];
36779
36780/***/ },
36781/* 103 */
36782/***/ function(module, exports, __webpack_require__) {
36783
36784  'use strict';
36785
36786  Object.defineProperty(exports, '__esModule', {
36787    value: true
36788  });
36789
36790  var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
36791
36792  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
36793
36794  function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
36795
36796  var _NetworkUtil = __webpack_require__(104);
36797
36798  var _NetworkUtil2 = _interopRequireDefault(_NetworkUtil);
36799
36800  var util = __webpack_require__(1);
36801
36802  var View = (function () {
36803    function View(body, canvas) {
36804      var _this = this;
36805
36806      _classCallCheck(this, View);
36807
36808      this.body = body;
36809      this.canvas = canvas;
36810
36811      this.animationSpeed = 1 / this.renderRefreshRate;
36812      this.animationEasingFunction = 'easeInOutQuint';
36813      this.easingTime = 0;
36814      this.sourceScale = 0;
36815      this.targetScale = 0;
36816      this.sourceTranslation = 0;
36817      this.targetTranslation = 0;
36818      this.lockedOnNodeId = undefined;
36819      this.lockedOnNodeOffset = undefined;
36820      this.touchTime = 0;
36821
36822      this.viewFunction = undefined;
36823
36824      this.body.emitter.on('fit', this.fit.bind(this));
36825      this.body.emitter.on('animationFinished', function () {
36826        _this.body.emitter.emit('_stopRendering');
36827      });
36828      this.body.emitter.on('unlockNode', this.releaseNode.bind(this));
36829    }
36830
36831    _createClass(View, [{
36832      key: 'setOptions',
36833      value: function setOptions() {
36834        var options = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0];
36835
36836        this.options = options;
36837      }
36838    }, {
36839      key: 'fit',
36840
36841      /**
36842       * This function zooms out to fit all data on screen based on amount of nodes
36843       * @param {Object} Options
36844       * @param {Boolean} [initialZoom]  | zoom based on fitted formula or range, true = fitted, default = false;
36845       */
36846      value: function fit() {
36847        var options = arguments.length <= 0 || arguments[0] === undefined ? { nodes: [] } : arguments[0];
36848        var initialZoom = arguments.length <= 1 || arguments[1] === undefined ? false : arguments[1];
36849
36850        var range = undefined;
36851        var zoomLevel = undefined;
36852        if (options.nodes === undefined || options.nodes.length === 0) {
36853          options.nodes = this.body.nodeIndices;
36854        }
36855
36856        if (initialZoom === true) {
36857          // check if more than half of the nodes have a predefined position. If so, we use the range, not the approximation.
36858          var positionDefined = 0;
36859          for (var nodeId in this.body.nodes) {
36860            if (this.body.nodes.hasOwnProperty(nodeId)) {
36861              var node = this.body.nodes[nodeId];
36862              if (node.predefinedPosition === true) {
36863                positionDefined += 1;
36864              }
36865            }
36866          }
36867          if (positionDefined > 0.5 * this.body.nodeIndices.length) {
36868            this.fit(options, false);
36869            return;
36870          }
36871
36872          range = _NetworkUtil2['default']._getRange(this.body.nodes, options.nodes);
36873
36874          var numberOfNodes = this.body.nodeIndices.length;
36875          zoomLevel = 12.662 / (numberOfNodes + 7.4147) + 0.0964822; // this is obtained from fitting a dataset from 5 points with scale levels that looked good.
36876
36877          // correct for larger canvasses.
36878          var factor = Math.min(this.canvas.frame.canvas.clientWidth / 600, this.canvas.frame.canvas.clientHeight / 600);
36879          zoomLevel *= factor;
36880        } else {
36881          this.body.emitter.emit('_resizeNodes');
36882          range = _NetworkUtil2['default']._getRange(this.body.nodes, options.nodes);
36883
36884          var xDistance = Math.abs(range.maxX - range.minX) * 1.1;
36885          var yDistance = Math.abs(range.maxY - range.minY) * 1.1;
36886
36887          var xZoomLevel = this.canvas.frame.canvas.clientWidth / xDistance;
36888          var yZoomLevel = this.canvas.frame.canvas.clientHeight / yDistance;
36889
36890          zoomLevel = xZoomLevel <= yZoomLevel ? xZoomLevel : yZoomLevel;
36891        }
36892
36893        if (zoomLevel > 1.0) {
36894          zoomLevel = 1.0;
36895        } else if (zoomLevel === 0) {
36896          zoomLevel = 1.0;
36897        }
36898
36899        var center = _NetworkUtil2['default']._findCenter(range);
36900        var animationOptions = { position: center, scale: zoomLevel, animation: options.animation };
36901        this.moveTo(animationOptions);
36902      }
36903    }, {
36904      key: 'focus',
36905
36906      // animation
36907
36908      /**
36909       * Center a node in view.
36910       *
36911       * @param {Number} nodeId
36912       * @param {Number} [options]
36913       */
36914      value: function focus(nodeId) {
36915        var options = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1];
36916
36917        if (this.body.nodes[nodeId] !== undefined) {
36918          var nodePosition = { x: this.body.nodes[nodeId].x, y: this.body.nodes[nodeId].y };
36919          options.position = nodePosition;
36920          options.lockedOnNode = nodeId;
36921
36922          this.moveTo(options);
36923        } else {
36924          console.log('Node: ' + nodeId + ' cannot be found.');
36925        }
36926      }
36927    }, {
36928      key: 'moveTo',
36929
36930      /**
36931       *
36932       * @param {Object} options  |  options.offset   = {x:Number, y:Number}   // offset from the center in DOM pixels
36933       *                          |  options.scale    = Number                 // scale to move to
36934       *                          |  options.position = {x:Number, y:Number}   // position to move to
36935       *                          |  options.animation = {duration:Number, easingFunction:String} || Boolean   // position to move to
36936       */
36937      value: function moveTo(options) {
36938        if (options === undefined) {
36939          options = {};
36940          return;
36941        }
36942        if (options.offset === undefined) {
36943          options.offset = { x: 0, y: 0 };
36944        }
36945        if (options.offset.x === undefined) {
36946          options.offset.x = 0;
36947        }
36948        if (options.offset.y === undefined) {
36949          options.offset.y = 0;
36950        }
36951        if (options.scale === undefined) {
36952          options.scale = this.body.view.scale;
36953        }
36954        if (options.position === undefined) {
36955          options.position = this.getViewPosition();
36956        }
36957        if (options.animation === undefined) {
36958          options.animation = { duration: 0 };
36959        }
36960        if (options.animation === false) {
36961          options.animation = { duration: 0 };
36962        }
36963        if (options.animation === true) {
36964          options.animation = {};
36965        }
36966        if (options.animation.duration === undefined) {
36967          options.animation.duration = 1000;
36968        } // default duration
36969        if (options.animation.easingFunction === undefined) {
36970          options.animation.easingFunction = 'easeInOutQuad';
36971        } // default easing function
36972
36973        this.animateView(options);
36974      }
36975    }, {
36976      key: 'animateView',
36977
36978      /**
36979       *
36980       * @param {Object} options  |  options.offset   = {x:Number, y:Number}   // offset from the center in DOM pixels
36981       *                          |  options.time     = Number                 // animation time in milliseconds
36982       *                          |  options.scale    = Number                 // scale to animate to
36983       *                          |  options.position = {x:Number, y:Number}   // position to animate to
36984       *                          |  options.easingFunction = String           // linear, easeInQuad, easeOutQuad, easeInOutQuad,
36985       *                                                                       // easeInCubic, easeOutCubic, easeInOutCubic,
36986       *                                                                       // easeInQuart, easeOutQuart, easeInOutQuart,
36987       *                                                                       // easeInQuint, easeOutQuint, easeInOutQuint
36988       */
36989      value: function animateView(options) {
36990        if (options === undefined) {
36991          return;
36992        }
36993        this.animationEasingFunction = options.animation.easingFunction;
36994        // release if something focussed on the node
36995        this.releaseNode();
36996        if (options.locked === true) {
36997          this.lockedOnNodeId = options.lockedOnNode;
36998          this.lockedOnNodeOffset = options.offset;
36999        }
37000
37001        // forcefully complete the old animation if it was still running
37002        if (this.easingTime != 0) {
37003          this._transitionRedraw(true); // by setting easingtime to 1, we finish the animation.
37004        }
37005
37006        this.sourceScale = this.body.view.scale;
37007        this.sourceTranslation = this.body.view.translation;
37008        this.targetScale = options.scale;
37009
37010        // set the scale so the viewCenter is based on the correct zoom level. This is overridden in the transitionRedraw
37011        // but at least then we'll have the target transition
37012        this.body.view.scale = this.targetScale;
37013        var viewCenter = this.canvas.DOMtoCanvas({ x: 0.5 * this.canvas.frame.canvas.clientWidth, y: 0.5 * this.canvas.frame.canvas.clientHeight });
37014
37015        var distanceFromCenter = { // offset from view, distance view has to change by these x and y to center the node
37016          x: viewCenter.x - options.position.x,
37017          y: viewCenter.y - options.position.y
37018        };
37019        this.targetTranslation = {
37020          x: this.sourceTranslation.x + distanceFromCenter.x * this.targetScale + options.offset.x,
37021          y: this.sourceTranslation.y + distanceFromCenter.y * this.targetScale + options.offset.y
37022        };
37023
37024        // if the time is set to 0, don't do an animation
37025        if (options.animation.duration === 0) {
37026          if (this.lockedOnNodeId != undefined) {
37027            this.viewFunction = this._lockedRedraw.bind(this);
37028            this.body.emitter.on('initRedraw', this.viewFunction);
37029          } else {
37030            this.body.view.scale = this.targetScale;
37031            this.body.view.translation = this.targetTranslation;
37032            this.body.emitter.emit('_requestRedraw');
37033          }
37034        } else {
37035          this.animationSpeed = 1 / (60 * options.animation.duration * 0.001) || 1 / 60; // 60 for 60 seconds, 0.001 for milli's
37036          this.animationEasingFunction = options.animation.easingFunction;
37037
37038          this.viewFunction = this._transitionRedraw.bind(this);
37039          this.body.emitter.on('initRedraw', this.viewFunction);
37040          this.body.emitter.emit('_startRendering');
37041        }
37042      }
37043    }, {
37044      key: '_lockedRedraw',
37045
37046      /**
37047       * used to animate smoothly by hijacking the redraw function.
37048       * @private
37049       */
37050      value: function _lockedRedraw() {
37051        var nodePosition = { x: this.body.nodes[this.lockedOnNodeId].x, y: this.body.nodes[this.lockedOnNodeId].y };
37052        var viewCenter = this.canvas.DOMtoCanvas({ x: 0.5 * this.canvas.frame.canvas.clientWidth, y: 0.5 * this.canvas.frame.canvas.clientHeight });
37053        var distanceFromCenter = { // offset from view, distance view has to change by these x and y to center the node
37054          x: viewCenter.x - nodePosition.x,
37055          y: viewCenter.y - nodePosition.y
37056        };
37057        var sourceTranslation = this.body.view.translation;
37058        var targetTranslation = {
37059          x: sourceTranslation.x + distanceFromCenter.x * this.body.view.scale + this.lockedOnNodeOffset.x,
37060          y: sourceTranslation.y + distanceFromCenter.y * this.body.view.scale + this.lockedOnNodeOffset.y
37061        };
37062
37063        this.body.view.translation = targetTranslation;
37064      }
37065    }, {
37066      key: 'releaseNode',
37067      value: function releaseNode() {
37068        if (this.lockedOnNodeId !== undefined && this.viewFunction !== undefined) {
37069          this.body.emitter.off('initRedraw', this.viewFunction);
37070          this.lockedOnNodeId = undefined;
37071          this.lockedOnNodeOffset = undefined;
37072        }
37073      }
37074    }, {
37075      key: '_transitionRedraw',
37076
37077      /**
37078       *
37079       * @param easingTime
37080       * @private
37081       */
37082      value: function _transitionRedraw() {
37083        var finished = arguments.length <= 0 || arguments[0] === undefined ? false : arguments[0];
37084
37085        this.easingTime += this.animationSpeed;
37086        this.easingTime = finished === true ? 1.0 : this.easingTime;
37087
37088        var progress = util.easingFunctions[this.animationEasingFunction](this.easingTime);
37089
37090        this.body.view.scale = this.sourceScale + (this.targetScale - this.sourceScale) * progress;
37091        this.body.view.translation = {
37092          x: this.sourceTranslation.x + (this.targetTranslation.x - this.sourceTranslation.x) * progress,
37093          y: this.sourceTranslation.y + (this.targetTranslation.y - this.sourceTranslation.y) * progress
37094        };
37095
37096        // cleanup
37097        if (this.easingTime >= 1.0) {
37098          this.body.emitter.off('initRedraw', this.viewFunction);
37099          this.easingTime = 0;
37100          if (this.lockedOnNodeId != undefined) {
37101            this.viewFunction = this._lockedRedraw.bind(this);
37102            this.body.emitter.on('initRedraw', this.viewFunction);
37103          }
37104          this.body.emitter.emit('animationFinished');
37105        }
37106      }
37107    }, {
37108      key: 'getScale',
37109      value: function getScale() {
37110        return this.body.view.scale;
37111      }
37112    }, {
37113      key: 'getViewPosition',
37114      value: function getViewPosition() {
37115        return this.canvas.DOMtoCanvas({ x: 0.5 * this.canvas.frame.canvas.clientWidth, y: 0.5 * this.canvas.frame.canvas.clientHeight });
37116      }
37117    }]);
37118
37119    return View;
37120  })();
37121
37122  exports['default'] = View;
37123  module.exports = exports['default'];
37124
37125/***/ },
37126/* 104 */
37127/***/ function(module, exports) {
37128
37129  "use strict";
37130
37131  Object.defineProperty(exports, "__esModule", {
37132    value: true
37133  });
37134
37135  var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
37136
37137  function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
37138
37139  var NetworkUtil = (function () {
37140    function NetworkUtil() {
37141      _classCallCheck(this, NetworkUtil);
37142    }
37143
37144    _createClass(NetworkUtil, null, [{
37145      key: "_getRange",
37146
37147      /**
37148       * Find the center position of the network considering the bounding boxes
37149       * @private
37150       */
37151      value: function _getRange(allNodes) {
37152        var specificNodes = arguments.length <= 1 || arguments[1] === undefined ? [] : arguments[1];
37153
37154        var minY = 1e9,
37155            maxY = -1e9,
37156            minX = 1e9,
37157            maxX = -1e9,
37158            node;
37159        if (specificNodes.length > 0) {
37160          for (var i = 0; i < specificNodes.length; i++) {
37161            node = allNodes[specificNodes[i]];
37162            if (minX > node.shape.boundingBox.left) {
37163              minX = node.shape.boundingBox.left;
37164            }
37165            if (maxX < node.shape.boundingBox.right) {
37166              maxX = node.shape.boundingBox.right;
37167            }
37168            if (minY > node.shape.boundingBox.top) {
37169              minY = node.shape.boundingBox.top;
37170            } // top is negative, bottom is positive
37171            if (maxY < node.shape.boundingBox.bottom) {
37172              maxY = node.shape.boundingBox.bottom;
37173            } // top is negative, bottom is positive
37174          }
37175        }
37176
37177        if (minX === 1e9 && maxX === -1e9 && minY === 1e9 && maxY === -1e9) {
37178          minY = 0, maxY = 0, minX = 0, maxX = 0;
37179        }
37180        return { minX: minX, maxX: maxX, minY: minY, maxY: maxY };
37181      }
37182    }, {
37183      key: "_getRangeCore",
37184
37185      /**
37186       * Find the center position of the network
37187       * @private
37188       */
37189      value: function _getRangeCore(allNodes) {
37190        var specificNodes = arguments.length <= 1 || arguments[1] === undefined ? [] : arguments[1];
37191
37192        var minY = 1e9,
37193            maxY = -1e9,
37194            minX = 1e9,
37195            maxX = -1e9,
37196            node;
37197        if (specificNodes.length > 0) {
37198          for (var i = 0; i < specificNodes.length; i++) {
37199            node = allNodes[specificNodes[i]];
37200            if (minX > node.x) {
37201              minX = node.x;
37202            }
37203            if (maxX < node.x) {
37204              maxX = node.x;
37205            }
37206            if (minY > node.y) {
37207              minY = node.y;
37208            } // top is negative, bottom is positive
37209            if (maxY < node.y) {
37210              maxY = node.y;
37211            } // top is negative, bottom is positive
37212          }
37213        }
37214
37215        if (minX === 1e9 && maxX === -1e9 && minY === 1e9 && maxY === -1e9) {
37216          minY = 0, maxY = 0, minX = 0, maxX = 0;
37217        }
37218        return { minX: minX, maxX: maxX, minY: minY, maxY: maxY };
37219      }
37220    }, {
37221      key: "_findCenter",
37222
37223      /**
37224       * @param {object} range = {minX: minX, maxX: maxX, minY: minY, maxY: maxY};
37225       * @returns {{x: number, y: number}}
37226       * @private
37227       */
37228      value: function _findCenter(range) {
37229        return { x: 0.5 * (range.maxX + range.minX),
37230          y: 0.5 * (range.maxY + range.minY) };
37231      }
37232    }]);
37233
37234    return NetworkUtil;
37235  })();
37236
37237  exports["default"] = NetworkUtil;
37238  module.exports = exports["default"];
37239
37240/***/ },
37241/* 105 */
37242/***/ function(module, exports, __webpack_require__) {
37243
37244  'use strict';
37245
37246  Object.defineProperty(exports, '__esModule', {
37247    value: true
37248  });
37249
37250  var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
37251
37252  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
37253
37254  function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
37255
37256  var _componentsNavigationHandler = __webpack_require__(106);
37257
37258  var _componentsNavigationHandler2 = _interopRequireDefault(_componentsNavigationHandler);
37259
37260  var _componentsPopup = __webpack_require__(107);
37261
37262  var _componentsPopup2 = _interopRequireDefault(_componentsPopup);
37263
37264  var util = __webpack_require__(1);
37265
37266  var InteractionHandler = (function () {
37267    function InteractionHandler(body, canvas, selectionHandler) {
37268      _classCallCheck(this, InteractionHandler);
37269
37270      this.body = body;
37271      this.canvas = canvas;
37272      this.selectionHandler = selectionHandler;
37273      this.navigationHandler = new _componentsNavigationHandler2['default'](body, canvas);
37274
37275      // bind the events from hammer to functions in this object
37276      this.body.eventListeners.onTap = this.onTap.bind(this);
37277      this.body.eventListeners.onTouch = this.onTouch.bind(this);
37278      this.body.eventListeners.onDoubleTap = this.onDoubleTap.bind(this);
37279      this.body.eventListeners.onHold = this.onHold.bind(this);
37280      this.body.eventListeners.onDragStart = this.onDragStart.bind(this);
37281      this.body.eventListeners.onDrag = this.onDrag.bind(this);
37282      this.body.eventListeners.onDragEnd = this.onDragEnd.bind(this);
37283      this.body.eventListeners.onMouseWheel = this.onMouseWheel.bind(this);
37284      this.body.eventListeners.onPinch = this.onPinch.bind(this);
37285      this.body.eventListeners.onMouseMove = this.onMouseMove.bind(this);
37286      this.body.eventListeners.onRelease = this.onRelease.bind(this);
37287      this.body.eventListeners.onContext = this.onContext.bind(this);
37288
37289      this.touchTime = 0;
37290      this.drag = {};
37291      this.pinch = {};
37292      this.popup = undefined;
37293      this.popupObj = undefined;
37294      this.popupTimer = undefined;
37295
37296      this.body.functions.getPointer = this.getPointer.bind(this);
37297
37298      this.options = {};
37299      this.defaultOptions = {
37300        dragNodes: true,
37301        dragView: true,
37302        hover: false,
37303        keyboard: {
37304          enabled: false,
37305          speed: { x: 10, y: 10, zoom: 0.02 },
37306          bindToWindow: true
37307        },
37308        navigationButtons: false,
37309        tooltipDelay: 300,
37310        zoomView: true
37311      };
37312      util.extend(this.options, this.defaultOptions);
37313
37314      this.bindEventListeners();
37315    }
37316
37317    _createClass(InteractionHandler, [{
37318      key: 'bindEventListeners',
37319      value: function bindEventListeners() {
37320        var _this = this;
37321
37322        this.body.emitter.on('destroy', function () {
37323          clearTimeout(_this.popupTimer);
37324          delete _this.body.functions.getPointer;
37325        });
37326      }
37327    }, {
37328      key: 'setOptions',
37329      value: function setOptions(options) {
37330        if (options !== undefined) {
37331          // extend all but the values in fields
37332          var fields = ['hideEdgesOnDrag', 'hideNodesOnDrag', 'keyboard', 'multiselect', 'selectable', 'selectConnectedEdges'];
37333          util.selectiveNotDeepExtend(fields, this.options, options);
37334
37335          // merge the keyboard options in.
37336          util.mergeOptions(this.options, options, 'keyboard');
37337
37338          if (options.tooltip) {
37339            util.extend(this.options.tooltip, options.tooltip);
37340            if (options.tooltip.color) {
37341              this.options.tooltip.color = util.parseColor(options.tooltip.color);
37342            }
37343          }
37344        }
37345
37346        this.navigationHandler.setOptions(this.options);
37347      }
37348    }, {
37349      key: 'getPointer',
37350
37351      /**
37352       * Get the pointer location from a touch location
37353       * @param {{x: Number, y: Number}} touch
37354       * @return {{x: Number, y: Number}} pointer
37355       * @private
37356       */
37357      value: function getPointer(touch) {
37358        return {
37359          x: touch.x - util.getAbsoluteLeft(this.canvas.frame.canvas),
37360          y: touch.y - util.getAbsoluteTop(this.canvas.frame.canvas)
37361        };
37362      }
37363    }, {
37364      key: 'onTouch',
37365
37366      /**
37367       * On start of a touch gesture, store the pointer
37368       * @param event
37369       * @private
37370       */
37371      value: function onTouch(event) {
37372        if (new Date().valueOf() - this.touchTime > 50) {
37373          this.drag.pointer = this.getPointer(event.center);
37374          this.drag.pinched = false;
37375          this.pinch.scale = this.body.view.scale;
37376          // to avoid double fireing of this event because we have two hammer instances. (on canvas and on frame)
37377          this.touchTime = new Date().valueOf();
37378        }
37379      }
37380    }, {
37381      key: 'onTap',
37382
37383      /**
37384       * handle tap/click event: select/unselect a node
37385       * @private
37386       */
37387      value: function onTap(event) {
37388        var pointer = this.getPointer(event.center);
37389        var multiselect = this.selectionHandler.options.multiselect && (event.changedPointers[0].ctrlKey || event.changedPointers[0].metaKey);
37390
37391        this.checkSelectionChanges(pointer, event, multiselect);
37392        this.selectionHandler._generateClickEvent('click', event, pointer);
37393      }
37394    }, {
37395      key: 'onDoubleTap',
37396
37397      /**
37398       * handle doubletap event
37399       * @private
37400       */
37401      value: function onDoubleTap(event) {
37402        var pointer = this.getPointer(event.center);
37403        this.selectionHandler._generateClickEvent('doubleClick', event, pointer);
37404      }
37405    }, {
37406      key: 'onHold',
37407
37408      /**
37409       * handle long tap event: multi select nodes
37410       * @private
37411       */
37412      value: function onHold(event) {
37413        var pointer = this.getPointer(event.center);
37414        var multiselect = this.selectionHandler.options.multiselect;
37415
37416        this.checkSelectionChanges(pointer, event, multiselect);
37417
37418        this.selectionHandler._generateClickEvent('click', event, pointer);
37419        this.selectionHandler._generateClickEvent('hold', event, pointer);
37420      }
37421    }, {
37422      key: 'onRelease',
37423
37424      /**
37425       * handle the release of the screen
37426       *
37427       * @private
37428       */
37429      value: function onRelease(event) {
37430        if (new Date().valueOf() - this.touchTime > 10) {
37431          var pointer = this.getPointer(event.center);
37432          this.selectionHandler._generateClickEvent('release', event, pointer);
37433          // to avoid double fireing of this event because we have two hammer instances. (on canvas and on frame)
37434          this.touchTime = new Date().valueOf();
37435        }
37436      }
37437    }, {
37438      key: 'onContext',
37439      value: function onContext(event) {
37440        var pointer = this.getPointer({ x: event.clientX, y: event.clientY });
37441        this.selectionHandler._generateClickEvent('oncontext', event, pointer);
37442      }
37443    }, {
37444      key: 'checkSelectionChanges',
37445
37446      /**
37447       *
37448       * @param pointer
37449       * @param add
37450       */
37451      value: function checkSelectionChanges(pointer, event) {
37452        var add = arguments.length <= 2 || arguments[2] === undefined ? false : arguments[2];
37453
37454        var previouslySelectedEdgeCount = this.selectionHandler._getSelectedEdgeCount();
37455        var previouslySelectedNodeCount = this.selectionHandler._getSelectedNodeCount();
37456        var previousSelection = this.selectionHandler.getSelection();
37457        var selected = undefined;
37458        if (add === true) {
37459          selected = this.selectionHandler.selectAdditionalOnPoint(pointer);
37460        } else {
37461          selected = this.selectionHandler.selectOnPoint(pointer);
37462        }
37463        var selectedEdgesCount = this.selectionHandler._getSelectedEdgeCount();
37464        var selectedNodesCount = this.selectionHandler._getSelectedNodeCount();
37465        var currentSelection = this.selectionHandler.getSelection();
37466
37467        var _determineIfDifferent2 = this._determineIfDifferent(previousSelection, currentSelection);
37468
37469        var nodesChanges = _determineIfDifferent2.nodesChanges;
37470        var edgesChanges = _determineIfDifferent2.edgesChanges;
37471
37472        var nodeSelected = false;
37473
37474        if (selectedNodesCount - previouslySelectedNodeCount > 0) {
37475          // node was selected
37476          this.selectionHandler._generateClickEvent('selectNode', event, pointer);
37477          selected = true;
37478          nodeSelected = true;
37479        } else if (selectedNodesCount - previouslySelectedNodeCount < 0) {
37480          // node was deselected
37481          this.selectionHandler._generateClickEvent('deselectNode', event, pointer, previousSelection);
37482          selected = true;
37483        } else if (selectedNodesCount === previouslySelectedNodeCount && nodesChanges === true) {
37484          this.selectionHandler._generateClickEvent('deselectNode', event, pointer, previousSelection);
37485          this.selectionHandler._generateClickEvent('selectNode', event, pointer);
37486          nodeSelected = true;
37487          selected = true;
37488        }
37489
37490        // handle the selected edges
37491        if (selectedEdgesCount - previouslySelectedEdgeCount > 0 && nodeSelected === false) {
37492          // edge was selected
37493          this.selectionHandler._generateClickEvent('selectEdge', event, pointer);
37494          selected = true;
37495        } else if (selectedEdgesCount - previouslySelectedEdgeCount < 0) {
37496          // edge was deselected
37497          this.selectionHandler._generateClickEvent('deselectEdge', event, pointer, previousSelection);
37498          selected = true;
37499        } else if (selectedEdgesCount === previouslySelectedEdgeCount && edgesChanges === true) {
37500          this.selectionHandler._generateClickEvent('deselectEdge', event, pointer, previousSelection);
37501          this.selectionHandler._generateClickEvent('selectEdge', event, pointer);
37502          selected = true;
37503        }
37504
37505        // fire the select event if anything has been selected or deselected
37506        if (selected === true) {
37507          // select or unselect
37508          this.selectionHandler._generateClickEvent('select', event, pointer);
37509        }
37510      }
37511    }, {
37512      key: '_determineIfDifferent',
37513
37514      /**
37515       * This function checks if the nodes and edges previously selected have changed.
37516       * @param previousSelection
37517       * @param currentSelection
37518       * @returns {{nodesChanges: boolean, edgesChanges: boolean}}
37519       * @private
37520       */
37521      value: function _determineIfDifferent(previousSelection, currentSelection) {
37522        var nodesChanges = false;
37523        var edgesChanges = false;
37524
37525        for (var i = 0; i < previousSelection.nodes.length; i++) {
37526          if (currentSelection.nodes.indexOf(previousSelection.nodes[i]) === -1) {
37527            nodesChanges = true;
37528          }
37529        }
37530        for (var i = 0; i < currentSelection.nodes.length; i++) {
37531          if (previousSelection.nodes.indexOf(previousSelection.nodes[i]) === -1) {
37532            nodesChanges = true;
37533          }
37534        }
37535        for (var i = 0; i < previousSelection.edges.length; i++) {
37536          if (currentSelection.edges.indexOf(previousSelection.edges[i]) === -1) {
37537            edgesChanges = true;
37538          }
37539        }
37540        for (var i = 0; i < currentSelection.edges.length; i++) {
37541          if (previousSelection.edges.indexOf(previousSelection.edges[i]) === -1) {
37542            edgesChanges = true;
37543          }
37544        }
37545
37546        return { nodesChanges: nodesChanges, edgesChanges: edgesChanges };
37547      }
37548    }, {
37549      key: 'onDragStart',
37550
37551      /**
37552       * This function is called by onDragStart.
37553       * It is separated out because we can then overload it for the datamanipulation system.
37554       *
37555       * @private
37556       */
37557      value: function onDragStart(event) {
37558        //in case the touch event was triggered on an external div, do the initial touch now.
37559        if (this.drag.pointer === undefined) {
37560          this.onTouch(event);
37561        }
37562
37563        // note: drag.pointer is set in onTouch to get the initial touch location
37564        var node = this.selectionHandler.getNodeAt(this.drag.pointer);
37565
37566        this.drag.dragging = true;
37567        this.drag.selection = [];
37568        this.drag.translation = util.extend({}, this.body.view.translation); // copy the object
37569        this.drag.nodeId = undefined;
37570
37571        if (node !== undefined && this.options.dragNodes === true) {
37572          this.drag.nodeId = node.id;
37573          // select the clicked node if not yet selected
37574          if (node.isSelected() === false) {
37575            this.selectionHandler.unselectAll();
37576            this.selectionHandler.selectObject(node);
37577          }
37578
37579          // after select to contain the node
37580          this.selectionHandler._generateClickEvent('dragStart', event, this.drag.pointer);
37581
37582          var selection = this.selectionHandler.selectionObj.nodes;
37583          // create an array with the selected nodes and their original location and status
37584          for (var nodeId in selection) {
37585            if (selection.hasOwnProperty(nodeId)) {
37586              var object = selection[nodeId];
37587              var s = {
37588                id: object.id,
37589                node: object,
37590
37591                // store original x, y, xFixed and yFixed, make the node temporarily Fixed
37592                x: object.x,
37593                y: object.y,
37594                xFixed: object.options.fixed.x,
37595                yFixed: object.options.fixed.y
37596              };
37597
37598              object.options.fixed.x = true;
37599              object.options.fixed.y = true;
37600
37601              this.drag.selection.push(s);
37602            }
37603          }
37604        } else {
37605          // fallback if no node is selected and thus the view is dragged.
37606          this.selectionHandler._generateClickEvent('dragStart', event, this.drag.pointer, undefined, true);
37607        }
37608      }
37609    }, {
37610      key: 'onDrag',
37611
37612      /**
37613       * handle drag event
37614       * @private
37615       */
37616      value: function onDrag(event) {
37617        var _this2 = this;
37618
37619        if (this.drag.pinched === true) {
37620          return;
37621        }
37622
37623        // remove the focus on node if it is focussed on by the focusOnNode
37624        this.body.emitter.emit('unlockNode');
37625
37626        var pointer = this.getPointer(event.center);
37627
37628        var selection = this.drag.selection;
37629        if (selection && selection.length && this.options.dragNodes === true) {
37630          (function () {
37631            _this2.selectionHandler._generateClickEvent('dragging', event, pointer);
37632
37633            // calculate delta's and new location
37634            var deltaX = pointer.x - _this2.drag.pointer.x;
37635            var deltaY = pointer.y - _this2.drag.pointer.y;
37636
37637            // update position of all selected nodes
37638            selection.forEach(function (selection) {
37639              var node = selection.node;
37640              // only move the node if it was not fixed initially
37641              if (selection.xFixed === false) {
37642                node.x = _this2.canvas._XconvertDOMtoCanvas(_this2.canvas._XconvertCanvasToDOM(selection.x) + deltaX);
37643              }
37644              // only move the node if it was not fixed initially
37645              if (selection.yFixed === false) {
37646                node.y = _this2.canvas._YconvertDOMtoCanvas(_this2.canvas._YconvertCanvasToDOM(selection.y) + deltaY);
37647              }
37648            });
37649
37650            // start the simulation of the physics
37651            _this2.body.emitter.emit('startSimulation');
37652          })();
37653        } else {
37654          // move the network
37655          if (this.options.dragView === true) {
37656            this.selectionHandler._generateClickEvent('dragging', event, pointer, undefined, true);
37657
37658            // if the drag was not started properly because the click started outside the network div, start it now.
37659            if (this.drag.pointer === undefined) {
37660              this.onDragStart(event);
37661              return;
37662            }
37663            var diffX = pointer.x - this.drag.pointer.x;
37664            var diffY = pointer.y - this.drag.pointer.y;
37665
37666            this.body.view.translation = { x: this.drag.translation.x + diffX, y: this.drag.translation.y + diffY };
37667            this.body.emitter.emit('_redraw');
37668          }
37669        }
37670      }
37671    }, {
37672      key: 'onDragEnd',
37673
37674      /**
37675       * handle drag start event
37676       * @private
37677       */
37678      value: function onDragEnd(event) {
37679        this.drag.dragging = false;
37680        var selection = this.drag.selection;
37681        if (selection && selection.length) {
37682          selection.forEach(function (s) {
37683            // restore original xFixed and yFixed
37684            s.node.options.fixed.x = s.xFixed;
37685            s.node.options.fixed.y = s.yFixed;
37686          });
37687          this.selectionHandler._generateClickEvent('dragEnd', event, this.getPointer(event.center));
37688          this.body.emitter.emit('startSimulation');
37689        } else {
37690          this.selectionHandler._generateClickEvent('dragEnd', event, this.getPointer(event.center), undefined, true);
37691          this.body.emitter.emit('_requestRedraw');
37692        }
37693      }
37694    }, {
37695      key: 'onPinch',
37696
37697      /**
37698       * Handle pinch event
37699       * @param event
37700       * @private
37701       */
37702      value: function onPinch(event) {
37703        var pointer = this.getPointer(event.center);
37704
37705        this.drag.pinched = true;
37706        if (this.pinch['scale'] === undefined) {
37707          this.pinch.scale = 1;
37708        }
37709
37710        // TODO: enabled moving while pinching?
37711        var scale = this.pinch.scale * event.scale;
37712        this.zoom(scale, pointer);
37713      }
37714    }, {
37715      key: 'zoom',
37716
37717      /**
37718       * Zoom the network in or out
37719       * @param {Number} scale a number around 1, and between 0.01 and 10
37720       * @param {{x: Number, y: Number}} pointer    Position on screen
37721       * @return {Number} appliedScale    scale is limited within the boundaries
37722       * @private
37723       */
37724      value: function zoom(scale, pointer) {
37725        if (this.options.zoomView === true) {
37726          var scaleOld = this.body.view.scale;
37727          if (scale < 0.00001) {
37728            scale = 0.00001;
37729          }
37730          if (scale > 10) {
37731            scale = 10;
37732          }
37733
37734          var preScaleDragPointer = undefined;
37735          if (this.drag !== undefined) {
37736            if (this.drag.dragging === true) {
37737              preScaleDragPointer = this.canvas.DOMtoCanvas(this.drag.pointer);
37738            }
37739          }
37740          // + this.canvas.frame.canvas.clientHeight / 2
37741          var translation = this.body.view.translation;
37742
37743          var scaleFrac = scale / scaleOld;
37744          var tx = (1 - scaleFrac) * pointer.x + translation.x * scaleFrac;
37745          var ty = (1 - scaleFrac) * pointer.y + translation.y * scaleFrac;
37746
37747          this.body.view.scale = scale;
37748          this.body.view.translation = { x: tx, y: ty };
37749
37750          if (preScaleDragPointer != undefined) {
37751            var postScaleDragPointer = this.canvas.canvasToDOM(preScaleDragPointer);
37752            this.drag.pointer.x = postScaleDragPointer.x;
37753            this.drag.pointer.y = postScaleDragPointer.y;
37754          }
37755
37756          this.body.emitter.emit('_requestRedraw');
37757
37758          if (scaleOld < scale) {
37759            this.body.emitter.emit('zoom', { direction: '+', scale: this.body.view.scale });
37760          } else {
37761            this.body.emitter.emit('zoom', { direction: '-', scale: this.body.view.scale });
37762          }
37763        }
37764      }
37765    }, {
37766      key: 'onMouseWheel',
37767
37768      /**
37769       * Event handler for mouse wheel event, used to zoom the timeline
37770       * See http://adomas.org/javascript-mouse-wheel/
37771       *     https://github.com/EightMedia/hammer.js/issues/256
37772       * @param {MouseEvent}  event
37773       * @private
37774       */
37775      value: function onMouseWheel(event) {
37776        // retrieve delta
37777        var delta = 0;
37778        if (event.wheelDelta) {
37779          /* IE/Opera. */
37780          delta = event.wheelDelta / 120;
37781        } else if (event.detail) {
37782          /* Mozilla case. */
37783          // In Mozilla, sign of delta is different than in IE.
37784          // Also, delta is multiple of 3.
37785          delta = -event.detail / 3;
37786        }
37787
37788        // If delta is nonzero, handle it.
37789        // Basically, delta is now positive if wheel was scrolled up,
37790        // and negative, if wheel was scrolled down.
37791        if (delta !== 0) {
37792
37793          // calculate the new scale
37794          var scale = this.body.view.scale;
37795          var zoom = delta / 10;
37796          if (delta < 0) {
37797            zoom = zoom / (1 - zoom);
37798          }
37799          scale *= 1 + zoom;
37800
37801          // calculate the pointer location
37802          var pointer = this.getPointer({ x: event.clientX, y: event.clientY });
37803
37804          // apply the new scale
37805          this.zoom(scale, pointer);
37806        }
37807
37808        // Prevent default actions caused by mouse wheel.
37809        event.preventDefault();
37810      }
37811    }, {
37812      key: 'onMouseMove',
37813
37814      /**
37815       * Mouse move handler for checking whether the title moves over a node with a title.
37816       * @param  {Event} event
37817       * @private
37818       */
37819      value: function onMouseMove(event) {
37820        var _this3 = this;
37821
37822        var pointer = this.getPointer({ x: event.clientX, y: event.clientY });
37823        var popupVisible = false;
37824
37825        // check if the previously selected node is still selected
37826        if (this.popup !== undefined) {
37827          if (this.popup.hidden === false) {
37828            this._checkHidePopup(pointer);
37829          }
37830
37831          // if the popup was not hidden above
37832          if (this.popup.hidden === false) {
37833            popupVisible = true;
37834            this.popup.setPosition(pointer.x + 3, pointer.y - 5);
37835            this.popup.show();
37836          }
37837        }
37838
37839        // if we bind the keyboard to the div, we have to highlight it to use it. This highlights it on mouse over.
37840        if (this.options.keyboard.bindToWindow === false && this.options.keyboard.enabled === true) {
37841          this.canvas.frame.focus();
37842        }
37843
37844        // start a timeout that will check if the mouse is positioned above an element
37845        if (popupVisible === false) {
37846          if (this.popupTimer !== undefined) {
37847            clearInterval(this.popupTimer); // stop any running calculationTimer
37848            this.popupTimer = undefined;
37849          }
37850          if (!this.drag.dragging) {
37851            this.popupTimer = setTimeout(function () {
37852              return _this3._checkShowPopup(pointer);
37853            }, this.options.tooltipDelay);
37854          }
37855        }
37856
37857        /**
37858        * Adding hover highlights
37859        */
37860        if (this.options.hover === true) {
37861          // adding hover highlights
37862          var obj = this.selectionHandler.getNodeAt(pointer);
37863          if (obj === undefined) {
37864            obj = this.selectionHandler.getEdgeAt(pointer);
37865          }
37866          this.selectionHandler.hoverObject(obj);
37867        }
37868      }
37869    }, {
37870      key: '_checkShowPopup',
37871
37872      /**
37873       * Check if there is an element on the given position in the network
37874       * (a node or edge). If so, and if this element has a title,
37875       * show a popup window with its title.
37876       *
37877       * @param {{x:Number, y:Number}} pointer
37878       * @private
37879       */
37880      value: function _checkShowPopup(pointer) {
37881        var x = this.canvas._XconvertDOMtoCanvas(pointer.x);
37882        var y = this.canvas._YconvertDOMtoCanvas(pointer.y);
37883        var pointerObj = {
37884          left: x,
37885          top: y,
37886          right: x,
37887          bottom: y
37888        };
37889
37890        var previousPopupObjId = this.popupObj === undefined ? undefined : this.popupObj.id;
37891        var nodeUnderCursor = false;
37892        var popupType = 'node';
37893
37894        // check if a node is under the cursor.
37895        if (this.popupObj === undefined) {
37896          // search the nodes for overlap, select the top one in case of multiple nodes
37897          var nodeIndices = this.body.nodeIndices;
37898          var nodes = this.body.nodes;
37899          var node = undefined;
37900          var overlappingNodes = [];
37901          for (var i = 0; i < nodeIndices.length; i++) {
37902            node = nodes[nodeIndices[i]];
37903            if (node.isOverlappingWith(pointerObj) === true) {
37904              if (node.getTitle() !== undefined) {
37905                overlappingNodes.push(nodeIndices[i]);
37906              }
37907            }
37908          }
37909
37910          if (overlappingNodes.length > 0) {
37911            // if there are overlapping nodes, select the last one, this is the one which is drawn on top of the others
37912            this.popupObj = nodes[overlappingNodes[overlappingNodes.length - 1]];
37913            // if you hover over a node, the title of the edge is not supposed to be shown.
37914            nodeUnderCursor = true;
37915          }
37916        }
37917
37918        if (this.popupObj === undefined && nodeUnderCursor === false) {
37919          // search the edges for overlap
37920          var edgeIndices = this.body.edgeIndices;
37921          var edges = this.body.edges;
37922          var edge = undefined;
37923          var overlappingEdges = [];
37924          for (var i = 0; i < edgeIndices.length; i++) {
37925            edge = edges[edgeIndices[i]];
37926            if (edge.isOverlappingWith(pointerObj) === true) {
37927              if (edge.connected === true && edge.getTitle() !== undefined) {
37928                overlappingEdges.push(edgeIndices[i]);
37929              }
37930            }
37931          }
37932
37933          if (overlappingEdges.length > 0) {
37934            this.popupObj = edges[overlappingEdges[overlappingEdges.length - 1]];
37935            popupType = 'edge';
37936          }
37937        }
37938
37939        if (this.popupObj !== undefined) {
37940          // show popup message window
37941          if (this.popupObj.id !== previousPopupObjId) {
37942            if (this.popup === undefined) {
37943              this.popup = new _componentsPopup2['default'](this.canvas.frame);
37944            }
37945
37946            this.popup.popupTargetType = popupType;
37947            this.popup.popupTargetId = this.popupObj.id;
37948
37949            // adjust a small offset such that the mouse cursor is located in the
37950            // bottom left location of the popup, and you can easily move over the
37951            // popup area
37952            this.popup.setPosition(pointer.x + 3, pointer.y - 5);
37953            this.popup.setText(this.popupObj.getTitle());
37954            this.popup.show();
37955            this.body.emitter.emit('showPopup', this.popupObj.id);
37956          }
37957        } else {
37958          if (this.popup !== undefined) {
37959            this.popup.hide();
37960            this.body.emitter.emit('hidePopup');
37961          }
37962        }
37963      }
37964    }, {
37965      key: '_checkHidePopup',
37966
37967      /**
37968       * Check if the popup must be hidden, which is the case when the mouse is no
37969       * longer hovering on the object
37970       * @param {{x:Number, y:Number}} pointer
37971       * @private
37972       */
37973      value: function _checkHidePopup(pointer) {
37974        var pointerObj = this.selectionHandler._pointerToPositionObject(pointer);
37975
37976        var stillOnObj = false;
37977        if (this.popup.popupTargetType === 'node') {
37978          if (this.body.nodes[this.popup.popupTargetId] !== undefined) {
37979            stillOnObj = this.body.nodes[this.popup.popupTargetId].isOverlappingWith(pointerObj);
37980
37981            // 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.
37982            // we initially only check stillOnObj because this is much faster.
37983            if (stillOnObj === true) {
37984              var overNode = this.selectionHandler.getNodeAt(pointer);
37985              stillOnObj = overNode.id === this.popup.popupTargetId;
37986            }
37987          }
37988        } else {
37989          if (this.selectionHandler.getNodeAt(pointer) === undefined) {
37990            if (this.body.edges[this.popup.popupTargetId] !== undefined) {
37991              stillOnObj = this.body.edges[this.popup.popupTargetId].isOverlappingWith(pointerObj);
37992            }
37993          }
37994        }
37995
37996        if (stillOnObj === false) {
37997          this.popupObj = undefined;
37998          this.popup.hide();
37999          this.body.emitter.emit('hidePopup');
38000        }
38001      }
38002    }]);
38003
38004    return InteractionHandler;
38005  })();
38006
38007  exports['default'] = InteractionHandler;
38008  module.exports = exports['default'];
38009
38010/***/ },
38011/* 106 */
38012/***/ function(module, exports, __webpack_require__) {
38013
38014  'use strict';
38015
38016  Object.defineProperty(exports, '__esModule', {
38017    value: true
38018  });
38019
38020  var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
38021
38022  function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
38023
38024  var util = __webpack_require__(1);
38025  var Hammer = __webpack_require__(20);
38026  var hammerUtil = __webpack_require__(25);
38027  var keycharm = __webpack_require__(41);
38028
38029  var NavigationHandler = (function () {
38030    function NavigationHandler(body, canvas) {
38031      var _this = this;
38032
38033      _classCallCheck(this, NavigationHandler);
38034
38035      this.body = body;
38036      this.canvas = canvas;
38037
38038      this.iconsCreated = false;
38039      this.navigationHammers = [];
38040      this.boundFunctions = {};
38041      this.touchTime = 0;
38042      this.activated = false;
38043
38044      this.body.emitter.on('activate', function () {
38045        _this.activated = true;_this.configureKeyboardBindings();
38046      });
38047      this.body.emitter.on('deactivate', function () {
38048        _this.activated = false;_this.configureKeyboardBindings();
38049      });
38050      this.body.emitter.on('destroy', function () {
38051        if (_this.keycharm !== undefined) {
38052          _this.keycharm.destroy();
38053        }
38054      });
38055
38056      this.options = {};
38057    }
38058
38059    _createClass(NavigationHandler, [{
38060      key: 'setOptions',
38061      value: function setOptions(options) {
38062        if (options !== undefined) {
38063          this.options = options;
38064          this.create();
38065        }
38066      }
38067    }, {
38068      key: 'create',
38069      value: function create() {
38070        if (this.options.navigationButtons === true) {
38071          if (this.iconsCreated === false) {
38072            this.loadNavigationElements();
38073          }
38074        } else if (this.iconsCreated === true) {
38075          this.cleanNavigation();
38076        }
38077
38078        this.configureKeyboardBindings();
38079      }
38080    }, {
38081      key: 'cleanNavigation',
38082      value: function cleanNavigation() {
38083        // clean hammer bindings
38084        if (this.navigationHammers.length != 0) {
38085          for (var i = 0; i < this.navigationHammers.length; i++) {
38086            this.navigationHammers[i].destroy();
38087          }
38088          this.navigationHammers = [];
38089        }
38090
38091        // clean up previous navigation items
38092        if (this.navigationDOM && this.navigationDOM['wrapper'] && this.navigationDOM['wrapper'].parentNode) {
38093          this.navigationDOM['wrapper'].parentNode.removeChild(this.navigationDOM['wrapper']);
38094        }
38095
38096        this.iconsCreated = false;
38097      }
38098    }, {
38099      key: 'loadNavigationElements',
38100
38101      /**
38102       * Creation of the navigation controls nodes. They are drawn over the rest of the nodes and are not affected by scale and translation
38103       * they have a triggerFunction which is called on click. If the position of the navigation controls is dependent
38104       * on this.frame.canvas.clientWidth or this.frame.canvas.clientHeight, we flag horizontalAlignLeft and verticalAlignTop false.
38105       * This means that the location will be corrected by the _relocateNavigation function on a size change of the canvas.
38106       *
38107       * @private
38108       */
38109      value: function loadNavigationElements() {
38110        var _this2 = this;
38111
38112        this.cleanNavigation();
38113
38114        this.navigationDOM = {};
38115        var navigationDivs = ['up', 'down', 'left', 'right', 'zoomIn', 'zoomOut', 'zoomExtends'];
38116        var navigationDivActions = ['_moveUp', '_moveDown', '_moveLeft', '_moveRight', '_zoomIn', '_zoomOut', '_fit'];
38117
38118        this.navigationDOM['wrapper'] = document.createElement('div');
38119        this.navigationDOM['wrapper'].className = 'vis-navigation';
38120        this.canvas.frame.appendChild(this.navigationDOM['wrapper']);
38121
38122        for (var i = 0; i < navigationDivs.length; i++) {
38123          this.navigationDOM[navigationDivs[i]] = document.createElement('div');
38124          this.navigationDOM[navigationDivs[i]].className = 'vis-button vis-' + navigationDivs[i];
38125          this.navigationDOM['wrapper'].appendChild(this.navigationDOM[navigationDivs[i]]);
38126
38127          var hammer = new Hammer(this.navigationDOM[navigationDivs[i]]);
38128          if (navigationDivActions[i] === '_fit') {
38129            hammerUtil.onTouch(hammer, this._fit.bind(this));
38130          } else {
38131            hammerUtil.onTouch(hammer, this.bindToRedraw.bind(this, navigationDivActions[i]));
38132          }
38133
38134          this.navigationHammers.push(hammer);
38135        }
38136
38137        // use a hammer for the release so we do not require the one used in the rest of the network
38138        // the one the rest uses can be overloaded by the manipulation system.
38139        var hammerFrame = new Hammer(this.canvas.frame);
38140        hammerUtil.onRelease(hammerFrame, function () {
38141          _this2._stopMovement();
38142        });
38143        this.navigationHammers.push(hammerFrame);
38144
38145        this.iconsCreated = true;
38146      }
38147    }, {
38148      key: 'bindToRedraw',
38149      value: function bindToRedraw(action) {
38150        if (this.boundFunctions[action] === undefined) {
38151          this.boundFunctions[action] = this[action].bind(this);
38152          this.body.emitter.on('initRedraw', this.boundFunctions[action]);
38153          this.body.emitter.emit('_startRendering');
38154        }
38155      }
38156    }, {
38157      key: 'unbindFromRedraw',
38158      value: function unbindFromRedraw(action) {
38159        if (this.boundFunctions[action] !== undefined) {
38160          this.body.emitter.off('initRedraw', this.boundFunctions[action]);
38161          this.body.emitter.emit('_stopRendering');
38162          delete this.boundFunctions[action];
38163        }
38164      }
38165    }, {
38166      key: '_fit',
38167
38168      /**
38169       * this stops all movement induced by the navigation buttons
38170       *
38171       * @private
38172       */
38173      value: function _fit() {
38174        if (new Date().valueOf() - this.touchTime > 700) {
38175          // TODO: fix ugly hack to avoid hammer's double fireing of event (because we use release?)
38176          this.body.emitter.emit('fit', { duration: 700 });
38177          this.touchTime = new Date().valueOf();
38178        }
38179      }
38180    }, {
38181      key: '_stopMovement',
38182
38183      /**
38184       * this stops all movement induced by the navigation buttons
38185       *
38186       * @private
38187       */
38188      value: function _stopMovement() {
38189        for (var boundAction in this.boundFunctions) {
38190          if (this.boundFunctions.hasOwnProperty(boundAction)) {
38191            this.body.emitter.off('initRedraw', this.boundFunctions[boundAction]);
38192            this.body.emitter.emit('_stopRendering');
38193          }
38194        }
38195        this.boundFunctions = {};
38196      }
38197    }, {
38198      key: '_moveUp',
38199      value: function _moveUp() {
38200        this.body.view.translation.y += this.options.keyboard.speed.y;
38201      }
38202    }, {
38203      key: '_moveDown',
38204      value: function _moveDown() {
38205        this.body.view.translation.y -= this.options.keyboard.speed.y;
38206      }
38207    }, {
38208      key: '_moveLeft',
38209      value: function _moveLeft() {
38210        this.body.view.translation.x += this.options.keyboard.speed.x;
38211      }
38212    }, {
38213      key: '_moveRight',
38214      value: function _moveRight() {
38215        this.body.view.translation.x -= this.options.keyboard.speed.x;
38216      }
38217    }, {
38218      key: '_zoomIn',
38219      value: function _zoomIn() {
38220        this.body.view.scale *= 1 + this.options.keyboard.speed.zoom;
38221        this.body.emitter.emit('zoom', { direction: '+', scale: this.body.view.scale });
38222      }
38223    }, {
38224      key: '_zoomOut',
38225      value: function _zoomOut() {
38226        this.body.view.scale /= 1 + this.options.keyboard.speed.zoom;
38227        this.body.emitter.emit('zoom', { direction: '-', scale: this.body.view.scale });
38228      }
38229    }, {
38230      key: 'configureKeyboardBindings',
38231
38232      /**
38233       * bind all keys using keycharm.
38234       */
38235      value: function configureKeyboardBindings() {
38236        var _this3 = this;
38237
38238        if (this.keycharm !== undefined) {
38239          this.keycharm.destroy();
38240        }
38241
38242        if (this.options.keyboard.enabled === true) {
38243          if (this.options.keyboard.bindToWindow === true) {
38244            this.keycharm = keycharm({ container: window, preventDefault: true });
38245          } else {
38246            this.keycharm = keycharm({ container: this.canvas.frame, preventDefault: true });
38247          }
38248
38249          this.keycharm.reset();
38250
38251          if (this.activated === true) {
38252            this.keycharm.bind('up', function () {
38253              _this3.bindToRedraw('_moveUp');
38254            }, 'keydown');
38255            this.keycharm.bind('down', function () {
38256              _this3.bindToRedraw('_moveDown');
38257            }, 'keydown');
38258            this.keycharm.bind('left', function () {
38259              _this3.bindToRedraw('_moveLeft');
38260            }, 'keydown');
38261            this.keycharm.bind('right', function () {
38262              _this3.bindToRedraw('_moveRight');
38263            }, 'keydown');
38264            this.keycharm.bind('=', function () {
38265              _this3.bindToRedraw('_zoomIn');
38266            }, 'keydown');
38267            this.keycharm.bind('num+', function () {
38268              _this3.bindToRedraw('_zoomIn');
38269            }, 'keydown');
38270            this.keycharm.bind('num-', function () {
38271              _this3.bindToRedraw('_zoomOut');
38272            }, 'keydown');
38273            this.keycharm.bind('-', function () {
38274              _this3.bindToRedraw('_zoomOut');
38275            }, 'keydown');
38276            this.keycharm.bind('[', function () {
38277              _this3.bindToRedraw('_zoomOut');
38278            }, 'keydown');
38279            this.keycharm.bind(']', function () {
38280              _this3.bindToRedraw('_zoomIn');
38281            }, 'keydown');
38282            this.keycharm.bind('pageup', function () {
38283              _this3.bindToRedraw('_zoomIn');
38284            }, 'keydown');
38285            this.keycharm.bind('pagedown', function () {
38286              _this3.bindToRedraw('_zoomOut');
38287            }, 'keydown');
38288
38289            this.keycharm.bind('up', function () {
38290              _this3.unbindFromRedraw('_moveUp');
38291            }, 'keyup');
38292            this.keycharm.bind('down', function () {
38293              _this3.unbindFromRedraw('_moveDown');
38294            }, 'keyup');
38295            this.keycharm.bind('left', function () {
38296              _this3.unbindFromRedraw('_moveLeft');
38297            }, 'keyup');
38298            this.keycharm.bind('right', function () {
38299              _this3.unbindFromRedraw('_moveRight');
38300            }, 'keyup');
38301            this.keycharm.bind('=', function () {
38302              _this3.unbindFromRedraw('_zoomIn');
38303            }, 'keyup');
38304            this.keycharm.bind('num+', function () {
38305              _this3.unbindFromRedraw('_zoomIn');
38306            }, 'keyup');
38307            this.keycharm.bind('num-', function () {
38308              _this3.unbindFromRedraw('_zoomOut');
38309            }, 'keyup');
38310            this.keycharm.bind('-', function () {
38311              _this3.unbindFromRedraw('_zoomOut');
38312            }, 'keyup');
38313            this.keycharm.bind('[', function () {
38314              _this3.unbindFromRedraw('_zoomOut');
38315            }, 'keyup');
38316            this.keycharm.bind(']', function () {
38317              _this3.unbindFromRedraw('_zoomIn');
38318            }, 'keyup');
38319            this.keycharm.bind('pageup', function () {
38320              _this3.unbindFromRedraw('_zoomIn');
38321            }, 'keyup');
38322            this.keycharm.bind('pagedown', function () {
38323              _this3.unbindFromRedraw('_zoomOut');
38324            }, 'keyup');
38325          }
38326        }
38327      }
38328    }]);
38329
38330    return NavigationHandler;
38331  })();
38332
38333  exports['default'] = NavigationHandler;
38334  module.exports = exports['default'];
38335
38336/***/ },
38337/* 107 */
38338/***/ function(module, exports) {
38339
38340  /**
38341   * Popup is a class to create a popup window with some text
38342   * @param {Element}  container     The container object.
38343   * @param {Number} [x]
38344   * @param {Number} [y]
38345   * @param {String} [text]
38346   * @param {Object} [style]     An object containing borderColor,
38347   *                             backgroundColor, etc.
38348   */
38349  'use strict';
38350
38351  Object.defineProperty(exports, '__esModule', {
38352    value: true
38353  });
38354
38355  var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
38356
38357  function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
38358
38359  var Popup = (function () {
38360    function Popup(container) {
38361      _classCallCheck(this, Popup);
38362
38363      this.container = container;
38364
38365      this.x = 0;
38366      this.y = 0;
38367      this.padding = 5;
38368      this.hidden = false;
38369
38370      // create the frame
38371      this.frame = document.createElement('div');
38372      this.frame.className = 'vis-network-tooltip';
38373      this.container.appendChild(this.frame);
38374    }
38375
38376    _createClass(Popup, [{
38377      key: 'setPosition',
38378
38379      /**
38380       * @param {number} x   Horizontal position of the popup window
38381       * @param {number} y   Vertical position of the popup window
38382       */
38383      value: function setPosition(x, y) {
38384        this.x = parseInt(x);
38385        this.y = parseInt(y);
38386      }
38387    }, {
38388      key: 'setText',
38389
38390      /**
38391       * Set the content for the popup window. This can be HTML code or text.
38392       * @param {string | Element} content
38393       */
38394      value: function setText(content) {
38395        if (content instanceof Element) {
38396          this.frame.innerHTML = '';
38397          this.frame.appendChild(content);
38398        } else {
38399          this.frame.innerHTML = content; // string containing text or HTML
38400        }
38401      }
38402    }, {
38403      key: 'show',
38404
38405      /**
38406       * Show the popup window
38407       * @param {boolean} [doShow]    Show or hide the window
38408       */
38409      value: function show(doShow) {
38410        if (doShow === undefined) {
38411          doShow = true;
38412        }
38413
38414        if (doShow === true) {
38415          var height = this.frame.clientHeight;
38416          var width = this.frame.clientWidth;
38417          var maxHeight = this.frame.parentNode.clientHeight;
38418          var maxWidth = this.frame.parentNode.clientWidth;
38419
38420          var top = this.y - height;
38421          if (top + height + this.padding > maxHeight) {
38422            top = maxHeight - height - this.padding;
38423          }
38424          if (top < this.padding) {
38425            top = this.padding;
38426          }
38427
38428          var left = this.x;
38429          if (left + width + this.padding > maxWidth) {
38430            left = maxWidth - width - this.padding;
38431          }
38432          if (left < this.padding) {
38433            left = this.padding;
38434          }
38435
38436          this.frame.style.left = left + 'px';
38437          this.frame.style.top = top + 'px';
38438          this.frame.style.visibility = 'visible';
38439          this.hidden = false;
38440        } else {
38441          this.hide();
38442        }
38443      }
38444    }, {
38445      key: 'hide',
38446
38447      /**
38448       * Hide the popup window
38449       */
38450      value: function hide() {
38451        this.hidden = true;
38452        this.frame.style.visibility = 'hidden';
38453      }
38454    }]);
38455
38456    return Popup;
38457  })();
38458
38459  exports['default'] = Popup;
38460  module.exports = exports['default'];
38461
38462/***/ },
38463/* 108 */
38464/***/ function(module, exports, __webpack_require__) {
38465
38466  "use strict";
38467
38468  Object.defineProperty(exports, "__esModule", {
38469    value: true
38470  });
38471
38472  var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
38473
38474  function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
38475
38476  var Node = __webpack_require__(62);
38477  var Edge = __webpack_require__(82);
38478  var util = __webpack_require__(1);
38479
38480  var SelectionHandler = (function () {
38481    function SelectionHandler(body, canvas) {
38482      var _this = this;
38483
38484      _classCallCheck(this, SelectionHandler);
38485
38486      this.body = body;
38487      this.canvas = canvas;
38488      this.selectionObj = { nodes: [], edges: [] };
38489      this.hoverObj = { nodes: {}, edges: {} };
38490
38491      this.options = {};
38492      this.defaultOptions = {
38493        multiselect: false,
38494        selectable: true,
38495        selectConnectedEdges: true,
38496        hoverConnectedEdges: true
38497      };
38498      util.extend(this.options, this.defaultOptions);
38499
38500      this.body.emitter.on("_dataChanged", function () {
38501        _this.updateSelection();
38502      });
38503    }
38504
38505    _createClass(SelectionHandler, [{
38506      key: "setOptions",
38507      value: function setOptions(options) {
38508        if (options !== undefined) {
38509          var fields = ["multiselect", "hoverConnectedEdges", "selectable", "selectConnectedEdges"];
38510          util.selectiveDeepExtend(fields, this.options, options);
38511        }
38512      }
38513    }, {
38514      key: "selectOnPoint",
38515
38516      /**
38517       * handles the selection part of the tap;
38518       *
38519       * @param {Object} pointer
38520       * @private
38521       */
38522      value: function selectOnPoint(pointer) {
38523        var selected = false;
38524        if (this.options.selectable === true) {
38525          var obj = this.getNodeAt(pointer) || this.getEdgeAt(pointer);
38526
38527          // unselect after getting the objects in order to restore width and height.
38528          this.unselectAll();
38529
38530          if (obj !== undefined) {
38531            selected = this.selectObject(obj);
38532          }
38533          this.body.emitter.emit("_requestRedraw");
38534        }
38535        return selected;
38536      }
38537    }, {
38538      key: "selectAdditionalOnPoint",
38539      value: function selectAdditionalOnPoint(pointer) {
38540        var selectionChanged = false;
38541        if (this.options.selectable === true) {
38542          var obj = this.getNodeAt(pointer) || this.getEdgeAt(pointer);
38543
38544          if (obj !== undefined) {
38545            selectionChanged = true;
38546            if (obj.isSelected() === true) {
38547              this.deselectObject(obj);
38548            } else {
38549              this.selectObject(obj);
38550            }
38551
38552            this.body.emitter.emit("_requestRedraw");
38553          }
38554        }
38555        return selectionChanged;
38556      }
38557    }, {
38558      key: "_generateClickEvent",
38559      value: function _generateClickEvent(eventType, event, pointer, oldSelection) {
38560        var emptySelection = arguments.length <= 4 || arguments[4] === undefined ? false : arguments[4];
38561
38562        var properties = undefined;
38563        if (emptySelection === true) {
38564          properties = { nodes: [], edges: [] };
38565        } else {
38566          properties = this.getSelection();
38567        }
38568        properties["pointer"] = {
38569          DOM: { x: pointer.x, y: pointer.y },
38570          canvas: this.canvas.DOMtoCanvas(pointer)
38571        };
38572        properties["event"] = event;
38573
38574        if (oldSelection !== undefined) {
38575          properties["previousSelection"] = oldSelection;
38576        }
38577        this.body.emitter.emit(eventType, properties);
38578      }
38579    }, {
38580      key: "selectObject",
38581      value: function selectObject(obj) {
38582        var highlightEdges = arguments.length <= 1 || arguments[1] === undefined ? this.options.selectConnectedEdges : arguments[1];
38583
38584        if (obj !== undefined) {
38585          if (obj instanceof Node) {
38586            if (highlightEdges === true) {
38587              this._selectConnectedEdges(obj);
38588            }
38589          }
38590          obj.select();
38591          this._addToSelection(obj);
38592          return true;
38593        }
38594        return false;
38595      }
38596    }, {
38597      key: "deselectObject",
38598      value: function deselectObject(obj) {
38599        if (obj.isSelected() === true) {
38600          obj.selected = false;
38601          this._removeFromSelection(obj);
38602        }
38603      }
38604    }, {
38605      key: "_getAllNodesOverlappingWith",
38606
38607      /**
38608       * retrieve all nodes overlapping with given object
38609       * @param {Object} object  An object with parameters left, top, right, bottom
38610       * @return {Number[]}   An array with id's of the overlapping nodes
38611       * @private
38612       */
38613      value: function _getAllNodesOverlappingWith(object) {
38614        var overlappingNodes = [];
38615        var nodes = this.body.nodes;
38616        for (var i = 0; i < this.body.nodeIndices.length; i++) {
38617          var nodeId = this.body.nodeIndices[i];
38618          if (nodes[nodeId].isOverlappingWith(object)) {
38619            overlappingNodes.push(nodeId);
38620          }
38621        }
38622        return overlappingNodes;
38623      }
38624    }, {
38625      key: "_pointerToPositionObject",
38626
38627      /**
38628       * Return a position object in canvasspace from a single point in screenspace
38629       *
38630       * @param pointer
38631       * @returns {{left: number, top: number, right: number, bottom: number}}
38632       * @private
38633       */
38634      value: function _pointerToPositionObject(pointer) {
38635        var canvasPos = this.canvas.DOMtoCanvas(pointer);
38636        return {
38637          left: canvasPos.x - 1,
38638          top: canvasPos.y + 1,
38639          right: canvasPos.x + 1,
38640          bottom: canvasPos.y - 1
38641        };
38642      }
38643    }, {
38644      key: "getNodeAt",
38645
38646      /**
38647       * Get the top node at the a specific point (like a click)
38648       *
38649       * @param {{x: Number, y: Number}} pointer
38650       * @return {Node | undefined} node
38651       * @private
38652       */
38653      value: function getNodeAt(pointer) {
38654        var returnNode = arguments.length <= 1 || arguments[1] === undefined ? true : arguments[1];
38655
38656        // we first check if this is an navigation controls element
38657        var positionObject = this._pointerToPositionObject(pointer);
38658        var overlappingNodes = this._getAllNodesOverlappingWith(positionObject);
38659        // if there are overlapping nodes, select the last one, this is the
38660        // one which is drawn on top of the others
38661        if (overlappingNodes.length > 0) {
38662          if (returnNode === true) {
38663            return this.body.nodes[overlappingNodes[overlappingNodes.length - 1]];
38664          } else {
38665            return overlappingNodes[overlappingNodes.length - 1];
38666          }
38667        } else {
38668          return undefined;
38669        }
38670      }
38671    }, {
38672      key: "_getEdgesOverlappingWith",
38673
38674      /**
38675       * retrieve all edges overlapping with given object, selector is around center
38676       * @param {Object} object  An object with parameters left, top, right, bottom
38677       * @return {Number[]}   An array with id's of the overlapping nodes
38678       * @private
38679       */
38680      value: function _getEdgesOverlappingWith(object, overlappingEdges) {
38681        var edges = this.body.edges;
38682        for (var i = 0; i < this.body.edgeIndices.length; i++) {
38683          var edgeId = this.body.edgeIndices[i];
38684          if (edges[edgeId].isOverlappingWith(object)) {
38685            overlappingEdges.push(edgeId);
38686          }
38687        }
38688      }
38689    }, {
38690      key: "_getAllEdgesOverlappingWith",
38691
38692      /**
38693       * retrieve all nodes overlapping with given object
38694       * @param {Object} object  An object with parameters left, top, right, bottom
38695       * @return {Number[]}   An array with id's of the overlapping nodes
38696       * @private
38697       */
38698      value: function _getAllEdgesOverlappingWith(object) {
38699        var overlappingEdges = [];
38700        this._getEdgesOverlappingWith(object, overlappingEdges);
38701        return overlappingEdges;
38702      }
38703    }, {
38704      key: "getEdgeAt",
38705
38706      /**
38707       * Place holder. To implement change the getNodeAt to a _getObjectAt. Have the _getObjectAt call
38708       * getNodeAt and _getEdgesAt, then priortize the selection to user preferences.
38709       *
38710       * @param pointer
38711       * @returns {undefined}
38712       * @private
38713       */
38714      value: function getEdgeAt(pointer) {
38715        var returnEdge = arguments.length <= 1 || arguments[1] === undefined ? true : arguments[1];
38716
38717        var positionObject = this._pointerToPositionObject(pointer);
38718        var overlappingEdges = this._getAllEdgesOverlappingWith(positionObject);
38719
38720        if (overlappingEdges.length > 0) {
38721          if (returnEdge === true) {
38722            return this.body.edges[overlappingEdges[overlappingEdges.length - 1]];
38723          } else {
38724            return overlappingEdges[overlappingEdges.length - 1];
38725          }
38726        } else {
38727          return undefined;
38728        }
38729      }
38730    }, {
38731      key: "_addToSelection",
38732
38733      /**
38734       * Add object to the selection array.
38735       *
38736       * @param obj
38737       * @private
38738       */
38739      value: function _addToSelection(obj) {
38740        if (obj instanceof Node) {
38741          this.selectionObj.nodes[obj.id] = obj;
38742        } else {
38743          this.selectionObj.edges[obj.id] = obj;
38744        }
38745      }
38746    }, {
38747      key: "_addToHover",
38748
38749      /**
38750       * Add object to the selection array.
38751       *
38752       * @param obj
38753       * @private
38754       */
38755      value: function _addToHover(obj) {
38756        if (obj instanceof Node) {
38757          this.hoverObj.nodes[obj.id] = obj;
38758        } else {
38759          this.hoverObj.edges[obj.id] = obj;
38760        }
38761      }
38762    }, {
38763      key: "_removeFromSelection",
38764
38765      /**
38766       * Remove a single option from selection.
38767       *
38768       * @param {Object} obj
38769       * @private
38770       */
38771      value: function _removeFromSelection(obj) {
38772        if (obj instanceof Node) {
38773          delete this.selectionObj.nodes[obj.id];
38774        } else {
38775          delete this.selectionObj.edges[obj.id];
38776        }
38777      }
38778    }, {
38779      key: "unselectAll",
38780
38781      /**
38782       * Unselect all. The selectionObj is useful for this.
38783       *
38784       * @private
38785       */
38786      value: function unselectAll() {
38787        for (var nodeId in this.selectionObj.nodes) {
38788          if (this.selectionObj.nodes.hasOwnProperty(nodeId)) {
38789            this.selectionObj.nodes[nodeId].unselect();
38790          }
38791        }
38792        for (var edgeId in this.selectionObj.edges) {
38793          if (this.selectionObj.edges.hasOwnProperty(edgeId)) {
38794            this.selectionObj.edges[edgeId].unselect();
38795          }
38796        }
38797
38798        this.selectionObj = { nodes: {}, edges: {} };
38799      }
38800    }, {
38801      key: "_getSelectedNodeCount",
38802
38803      /**
38804       * return the number of selected nodes
38805       *
38806       * @returns {number}
38807       * @private
38808       */
38809      value: function _getSelectedNodeCount() {
38810        var count = 0;
38811        for (var nodeId in this.selectionObj.nodes) {
38812          if (this.selectionObj.nodes.hasOwnProperty(nodeId)) {
38813            count += 1;
38814          }
38815        }
38816        return count;
38817      }
38818    }, {
38819      key: "_getSelectedNode",
38820
38821      /**
38822       * return the selected node
38823       *
38824       * @returns {number}
38825       * @private
38826       */
38827      value: function _getSelectedNode() {
38828        for (var nodeId in this.selectionObj.nodes) {
38829          if (this.selectionObj.nodes.hasOwnProperty(nodeId)) {
38830            return this.selectionObj.nodes[nodeId];
38831          }
38832        }
38833        return undefined;
38834      }
38835    }, {
38836      key: "_getSelectedEdge",
38837
38838      /**
38839       * return the selected edge
38840       *
38841       * @returns {number}
38842       * @private
38843       */
38844      value: function _getSelectedEdge() {
38845        for (var edgeId in this.selectionObj.edges) {
38846          if (this.selectionObj.edges.hasOwnProperty(edgeId)) {
38847            return this.selectionObj.edges[edgeId];
38848          }
38849        }
38850        return undefined;
38851      }
38852    }, {
38853      key: "_getSelectedEdgeCount",
38854
38855      /**
38856       * return the number of selected edges
38857       *
38858       * @returns {number}
38859       * @private
38860       */
38861      value: function _getSelectedEdgeCount() {
38862        var count = 0;
38863        for (var edgeId in this.selectionObj.edges) {
38864          if (this.selectionObj.edges.hasOwnProperty(edgeId)) {
38865            count += 1;
38866          }
38867        }
38868        return count;
38869      }
38870    }, {
38871      key: "_getSelectedObjectCount",
38872
38873      /**
38874       * return the number of selected objects.
38875       *
38876       * @returns {number}
38877       * @private
38878       */
38879      value: function _getSelectedObjectCount() {
38880        var count = 0;
38881        for (var nodeId in this.selectionObj.nodes) {
38882          if (this.selectionObj.nodes.hasOwnProperty(nodeId)) {
38883            count += 1;
38884          }
38885        }
38886        for (var edgeId in this.selectionObj.edges) {
38887          if (this.selectionObj.edges.hasOwnProperty(edgeId)) {
38888            count += 1;
38889          }
38890        }
38891        return count;
38892      }
38893    }, {
38894      key: "_selectionIsEmpty",
38895
38896      /**
38897       * Check if anything is selected
38898       *
38899       * @returns {boolean}
38900       * @private
38901       */
38902      value: function _selectionIsEmpty() {
38903        for (var nodeId in this.selectionObj.nodes) {
38904          if (this.selectionObj.nodes.hasOwnProperty(nodeId)) {
38905            return false;
38906          }
38907        }
38908        for (var edgeId in this.selectionObj.edges) {
38909          if (this.selectionObj.edges.hasOwnProperty(edgeId)) {
38910            return false;
38911          }
38912        }
38913        return true;
38914      }
38915    }, {
38916      key: "_clusterInSelection",
38917
38918      /**
38919       * check if one of the selected nodes is a cluster.
38920       *
38921       * @returns {boolean}
38922       * @private
38923       */
38924      value: function _clusterInSelection() {
38925        for (var nodeId in this.selectionObj.nodes) {
38926          if (this.selectionObj.nodes.hasOwnProperty(nodeId)) {
38927            if (this.selectionObj.nodes[nodeId].clusterSize > 1) {
38928              return true;
38929            }
38930          }
38931        }
38932        return false;
38933      }
38934    }, {
38935      key: "_selectConnectedEdges",
38936
38937      /**
38938       * select the edges connected to the node that is being selected
38939       *
38940       * @param {Node} node
38941       * @private
38942       */
38943      value: function _selectConnectedEdges(node) {
38944        for (var i = 0; i < node.edges.length; i++) {
38945          var edge = node.edges[i];
38946          edge.select();
38947          this._addToSelection(edge);
38948        }
38949      }
38950    }, {
38951      key: "_hoverConnectedEdges",
38952
38953      /**
38954       * select the edges connected to the node that is being selected
38955       *
38956       * @param {Node} node
38957       * @private
38958       */
38959      value: function _hoverConnectedEdges(node) {
38960        for (var i = 0; i < node.edges.length; i++) {
38961          var edge = node.edges[i];
38962          edge.hover = true;
38963          this._addToHover(edge);
38964        }
38965      }
38966    }, {
38967      key: "_unselectConnectedEdges",
38968
38969      /**
38970       * unselect the edges connected to the node that is being selected
38971       *
38972       * @param {Node} node
38973       * @private
38974       */
38975      value: function _unselectConnectedEdges(node) {
38976        for (var i = 0; i < node.edges.length; i++) {
38977          var edge = node.edges[i];
38978          edge.unselect();
38979          this._removeFromSelection(edge);
38980        }
38981      }
38982    }, {
38983      key: "blurObject",
38984
38985      /**
38986       * This is called when someone clicks on a node. either select or deselect it.
38987       * If there is an existing selection and we don't want to append to it, clear the existing selection
38988       *
38989       * @param {Node || Edge} object
38990       * @private
38991       */
38992      value: function blurObject(object) {
38993        if (object.hover === true) {
38994          object.hover = false;
38995          if (object instanceof Node) {
38996            this.body.emitter.emit("blurNode", { node: object.id });
38997          } else {
38998            this.body.emitter.emit("blurEdge", { edge: object.id });
38999          }
39000        }
39001      }
39002    }, {
39003      key: "hoverObject",
39004
39005      /**
39006       * This is called when someone clicks on a node. either select or deselect it.
39007       * If there is an existing selection and we don't want to append to it, clear the existing selection
39008       *
39009       * @param {Node || Edge} object
39010       * @private
39011       */
39012      value: function hoverObject(object) {
39013        var hoverChanged = false;
39014        // remove all node hover highlights
39015        for (var nodeId in this.hoverObj.nodes) {
39016          if (this.hoverObj.nodes.hasOwnProperty(nodeId)) {
39017            if (object === undefined || object instanceof Node && object.id != nodeId || object instanceof Edge) {
39018              this.blurObject(this.hoverObj.nodes[nodeId]);
39019              delete this.hoverObj.nodes[nodeId];
39020              hoverChanged = true;
39021            }
39022          }
39023        }
39024
39025        // removing all edge hover highlights
39026        for (var edgeId in this.hoverObj.edges) {
39027          if (this.hoverObj.edges.hasOwnProperty(edgeId)) {
39028            // if the hover has been changed here it means that the node has been hovered over or off
39029            // we then do not use the blurObject method here.
39030            if (hoverChanged === true) {
39031              this.hoverObj.edges[edgeId].hover = false;
39032              delete this.hoverObj.edges[edgeId];
39033            }
39034            // if the blur remains the same and the object is undefined (mouse off), we blur the edge
39035            else if (object === undefined) {
39036              this.blurObject(this.hoverObj.edges[edgeId]);
39037              delete this.hoverObj.edges[edgeId];
39038              hoverChanged = true;
39039            }
39040          }
39041        }
39042
39043        if (object !== undefined) {
39044          if (object.hover === false) {
39045            object.hover = true;
39046            this._addToHover(object);
39047            hoverChanged = true;
39048            if (object instanceof Node) {
39049              this.body.emitter.emit("hoverNode", { node: object.id });
39050            } else {
39051              this.body.emitter.emit("hoverEdge", { edge: object.id });
39052            }
39053          }
39054          if (object instanceof Node && this.options.hoverConnectedEdges === true) {
39055            this._hoverConnectedEdges(object);
39056          }
39057        }
39058
39059        if (hoverChanged === true) {
39060          this.body.emitter.emit("_requestRedraw");
39061        }
39062      }
39063    }, {
39064      key: "getSelection",
39065
39066      /**
39067       *
39068       * retrieve the currently selected objects
39069       * @return {{nodes: Array.<String>, edges: Array.<String>}} selection
39070       */
39071      value: function getSelection() {
39072        var nodeIds = this.getSelectedNodes();
39073        var edgeIds = this.getSelectedEdges();
39074        return { nodes: nodeIds, edges: edgeIds };
39075      }
39076    }, {
39077      key: "getSelectedNodes",
39078
39079      /**
39080       *
39081       * retrieve the currently selected nodes
39082       * @return {String[]} selection    An array with the ids of the
39083       *                                            selected nodes.
39084       */
39085      value: function getSelectedNodes() {
39086        var idArray = [];
39087        if (this.options.selectable === true) {
39088          for (var nodeId in this.selectionObj.nodes) {
39089            if (this.selectionObj.nodes.hasOwnProperty(nodeId)) {
39090              idArray.push(nodeId);
39091            }
39092          }
39093        }
39094        return idArray;
39095      }
39096    }, {
39097      key: "getSelectedEdges",
39098
39099      /**
39100       *
39101       * retrieve the currently selected edges
39102       * @return {Array} selection    An array with the ids of the
39103       *                                            selected nodes.
39104       */
39105      value: function getSelectedEdges() {
39106        var idArray = [];
39107        if (this.options.selectable === true) {
39108          for (var edgeId in this.selectionObj.edges) {
39109            if (this.selectionObj.edges.hasOwnProperty(edgeId)) {
39110              idArray.push(edgeId);
39111            }
39112          }
39113        }
39114        return idArray;
39115      }
39116    }, {
39117      key: "selectNodes",
39118
39119      /**
39120       * select zero or more nodes with the option to highlight edges
39121       * @param {Number[] | String[]} selection     An array with the ids of the
39122       *                                            selected nodes.
39123       * @param {boolean} [highlightEdges]
39124       */
39125      value: function selectNodes(selection) {
39126        var highlightEdges = arguments.length <= 1 || arguments[1] === undefined ? true : arguments[1];
39127
39128        var i = undefined,
39129            id = undefined;
39130
39131        if (!selection || selection.length === undefined) throw "Selection must be an array with ids";
39132
39133        // first unselect any selected node
39134        this.unselectAll();
39135
39136        for (i = 0; i < selection.length; i++) {
39137          id = selection[i];
39138
39139          var node = this.body.nodes[id];
39140          if (!node) {
39141            throw new RangeError("Node with id \"" + id + "\" not found");
39142          }
39143          this.selectObject(node, highlightEdges);
39144        }
39145        this.body.emitter.emit("_requestRedraw");
39146      }
39147    }, {
39148      key: "selectEdges",
39149
39150      /**
39151       * select zero or more edges
39152       * @param {Number[] | String[]} selection     An array with the ids of the
39153       *                                            selected nodes.
39154       */
39155      value: function selectEdges(selection) {
39156        var i = undefined,
39157            id = undefined;
39158
39159        if (!selection || selection.length === undefined) throw "Selection must be an array with ids";
39160
39161        // first unselect any selected objects
39162        this.unselectAll();
39163
39164        for (i = 0; i < selection.length; i++) {
39165          id = selection[i];
39166
39167          var edge = this.body.edges[id];
39168          if (!edge) {
39169            throw new RangeError("Edge with id \"" + id + "\" not found");
39170          }
39171          this.selectObject(edge);
39172        }
39173        this.body.emitter.emit("_requestRedraw");
39174      }
39175    }, {
39176      key: "updateSelection",
39177
39178      /**
39179       * Validate the selection: remove ids of nodes which no longer exist
39180       * @private
39181       */
39182      value: function updateSelection() {
39183        for (var nodeId in this.selectionObj.nodes) {
39184          if (this.selectionObj.nodes.hasOwnProperty(nodeId)) {
39185            if (!this.body.nodes.hasOwnProperty(nodeId)) {
39186              delete this.selectionObj.nodes[nodeId];
39187            }
39188          }
39189        }
39190        for (var edgeId in this.selectionObj.edges) {
39191          if (this.selectionObj.edges.hasOwnProperty(edgeId)) {
39192            if (!this.body.edges.hasOwnProperty(edgeId)) {
39193              delete this.selectionObj.edges[edgeId];
39194            }
39195          }
39196        }
39197      }
39198    }]);
39199
39200    return SelectionHandler;
39201  })();
39202
39203  exports["default"] = SelectionHandler;
39204  module.exports = exports["default"];
39205
39206/***/ },
39207/* 109 */
39208/***/ function(module, exports, __webpack_require__) {
39209
39210  'use strict';
39211
39212  Object.defineProperty(exports, '__esModule', {
39213    value: true
39214  });
39215
39216  var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
39217
39218  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
39219
39220  function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
39221
39222  var _NetworkUtil = __webpack_require__(104);
39223
39224  var _NetworkUtil2 = _interopRequireDefault(_NetworkUtil);
39225
39226  var util = __webpack_require__(1);
39227
39228  var LayoutEngine = (function () {
39229    function LayoutEngine(body) {
39230      _classCallCheck(this, LayoutEngine);
39231
39232      this.body = body;
39233
39234      this.initialRandomSeed = Math.round(Math.random() * 1000000);
39235      this.randomSeed = this.initialRandomSeed;
39236      this.options = {};
39237      this.optionsBackup = {};
39238
39239      this.defaultOptions = {
39240        randomSeed: undefined,
39241        improvedLayout: true,
39242        hierarchical: {
39243          enabled: false,
39244          levelSeparation: 150,
39245          direction: 'UD', // UD, DU, LR, RL
39246          sortMethod: 'hubsize' // hubsize, directed
39247        }
39248      };
39249      util.extend(this.options, this.defaultOptions);
39250
39251      this.hierarchicalLevels = {};
39252
39253      this.bindEventListeners();
39254    }
39255
39256    _createClass(LayoutEngine, [{
39257      key: 'bindEventListeners',
39258      value: function bindEventListeners() {
39259        var _this = this;
39260
39261        this.body.emitter.on('_dataChanged', function () {
39262          _this.setupHierarchicalLayout();
39263        });
39264        this.body.emitter.on('_dataLoaded', function () {
39265          _this.layoutNetwork();
39266        });
39267        this.body.emitter.on('_resetHierarchicalLayout', function () {
39268          _this.setupHierarchicalLayout();
39269        });
39270      }
39271    }, {
39272      key: 'setOptions',
39273      value: function setOptions(options, allOptions) {
39274        if (options !== undefined) {
39275          var prevHierarchicalState = this.options.hierarchical.enabled;
39276          util.selectiveDeepExtend(['randomSeed', 'improvedLayout'], this.options, options);
39277          util.mergeOptions(this.options, options, 'hierarchical');
39278          if (options.randomSeed !== undefined) {
39279            this.initialRandomSeed = options.randomSeed;
39280          }
39281
39282          if (this.options.hierarchical.enabled === true) {
39283            if (prevHierarchicalState === true) {
39284              // refresh the overridden options for nodes and edges.
39285              this.body.emitter.emit('refresh', true);
39286            }
39287
39288            // make sure the level seperation is the right way up
39289            if (this.options.hierarchical.direction === 'RL' || this.options.hierarchical.direction === 'DU') {
39290              if (this.options.hierarchical.levelSeparation > 0) {
39291                this.options.hierarchical.levelSeparation *= -1;
39292              }
39293            } else {
39294              if (this.options.hierarchical.levelSeparation < 0) {
39295                this.options.hierarchical.levelSeparation *= -1;
39296              }
39297            }
39298
39299            this.body.emitter.emit('_resetHierarchicalLayout');
39300            // because the hierarchical system needs it's own physics and smooth curve settings, we adapt the other options if needed.
39301            return this.adaptAllOptions(allOptions);
39302          } else {
39303            if (prevHierarchicalState === true) {
39304              // refresh the overridden options for nodes and edges.
39305              this.body.emitter.emit('refresh');
39306              return util.deepExtend(allOptions, this.optionsBackup);
39307            }
39308          }
39309        }
39310        return allOptions;
39311      }
39312    }, {
39313      key: 'adaptAllOptions',
39314      value: function adaptAllOptions(allOptions) {
39315        if (this.options.hierarchical.enabled === true) {
39316          // set the physics
39317          if (allOptions.physics === undefined || allOptions.physics === true) {
39318            allOptions.physics = { solver: 'hierarchicalRepulsion' };
39319            this.optionsBackup.physics = { solver: 'barnesHut' };
39320          } else if (typeof allOptions.physics === 'object') {
39321            this.optionsBackup.physics = { solver: 'barnesHut' };
39322            if (allOptions.physics.solver !== undefined) {
39323              this.optionsBackup.physics = { solver: allOptions.physics.solver };
39324            }
39325            allOptions.physics['solver'] = 'hierarchicalRepulsion';
39326          } else if (allOptions.physics !== false) {
39327            this.optionsBackup.physics = { solver: 'barnesHut' };
39328            allOptions.physics['solver'] = 'hierarchicalRepulsion';
39329          }
39330
39331          // get the type of static smooth curve in case it is required
39332          var type = 'horizontal';
39333          if (this.options.hierarchical.direction === 'RL' || this.options.hierarchical.direction === 'LR') {
39334            type = 'vertical';
39335          }
39336
39337          // disable smooth curves if nothing is defined. If smooth curves have been turned on, turn them into static smooth curves.
39338          if (allOptions.edges === undefined) {
39339            this.optionsBackup.edges = { smooth: { enabled: true, type: 'dynamic' } };
39340            allOptions.edges = { smooth: false };
39341          } else if (allOptions.edges.smooth === undefined) {
39342            this.optionsBackup.edges = { smooth: { enabled: true, type: 'dynamic' } };
39343            allOptions.edges.smooth = false;
39344          } else {
39345            if (typeof allOptions.edges.smooth === 'boolean') {
39346              this.optionsBackup.edges = { smooth: allOptions.edges.smooth };
39347              allOptions.edges.smooth = { enabled: allOptions.edges.smooth, type: type };
39348            } else {
39349              // allow custom types except for dynamic
39350              if (allOptions.edges.smooth.type !== undefined && allOptions.edges.smooth.type !== 'dynamic') {
39351                type = allOptions.edges.smooth.type;
39352              }
39353
39354              this.optionsBackup.edges = {
39355                smooth: allOptions.edges.smooth.enabled === undefined ? true : allOptions.edges.smooth.enabled,
39356                type: allOptions.edges.smooth.type === undefined ? 'dynamic' : allOptions.edges.smooth.type,
39357                roundness: allOptions.edges.smooth.roundness === undefined ? 0.5 : allOptions.edges.smooth.roundness,
39358                forceDirection: allOptions.edges.smooth.forceDirection === undefined ? false : allOptions.edges.smooth.forceDirection
39359              };
39360              allOptions.edges.smooth = {
39361                enabled: allOptions.edges.smooth.enabled === undefined ? true : allOptions.edges.smooth.enabled,
39362                type: type,
39363                roundness: allOptions.edges.smooth.roundness === undefined ? 0.5 : allOptions.edges.smooth.roundness,
39364                forceDirection: allOptions.edges.smooth.forceDirection === undefined ? false : allOptions.edges.smooth.forceDirection
39365              };
39366            }
39367          }
39368
39369          // force all edges into static smooth curves. Only applies to edges that do not use the global options for smooth.
39370          this.body.emitter.emit('_forceDisableDynamicCurves', type);
39371        }
39372        return allOptions;
39373      }
39374    }, {
39375      key: 'seededRandom',
39376      value: function seededRandom() {
39377        var x = Math.sin(this.randomSeed++) * 10000;
39378        return x - Math.floor(x);
39379      }
39380    }, {
39381      key: 'positionInitially',
39382      value: function positionInitially(nodesArray) {
39383        if (this.options.hierarchical.enabled !== true) {
39384          this.randomSeed = this.initialRandomSeed;
39385          for (var i = 0; i < nodesArray.length; i++) {
39386            var node = nodesArray[i];
39387            var radius = 10 * 0.1 * nodesArray.length + 10;
39388            var angle = 2 * Math.PI * this.seededRandom();
39389            if (node.x === undefined) {
39390              node.x = radius * Math.cos(angle);
39391            }
39392            if (node.y === undefined) {
39393              node.y = radius * Math.sin(angle);
39394            }
39395          }
39396        }
39397      }
39398    }, {
39399      key: 'layoutNetwork',
39400
39401      /**
39402       * Use KamadaKawai to position nodes. This is quite a heavy algorithm so if there are a lot of nodes we
39403       * cluster them first to reduce the amount.
39404       */
39405      value: function layoutNetwork() {
39406        if (this.options.hierarchical.enabled !== true && this.options.improvedLayout === true) {
39407          // first check if we should KamadaKawai to layout. The threshold is if less than half of the visible
39408          // nodes have predefined positions we use this.
39409          var positionDefined = 0;
39410          for (var i = 0; i < this.body.nodeIndices.length; i++) {
39411            var node = this.body.nodes[this.body.nodeIndices[i]];
39412            if (node.predefinedPosition === true) {
39413              positionDefined += 1;
39414            }
39415          }
39416
39417          // if less than half of the nodes have a predefined position we continue
39418          if (positionDefined < 0.5 * this.body.nodeIndices.length) {
39419            var MAX_LEVELS = 10;
39420            var level = 0;
39421            var clusterThreshold = 100;
39422            // if there are a lot of nodes, we cluster before we run the algorithm.
39423            if (this.body.nodeIndices.length > clusterThreshold) {
39424              var startLength = this.body.nodeIndices.length;
39425              while (this.body.nodeIndices.length > clusterThreshold) {
39426                //console.time("clustering")
39427                level += 1;
39428                var before = this.body.nodeIndices.length;
39429                // if there are many nodes we do a hubsize cluster
39430                if (level % 3 === 0) {
39431                  this.body.modules.clustering.clusterBridges();
39432                } else {
39433                  this.body.modules.clustering.clusterOutliers();
39434                }
39435                var after = this.body.nodeIndices.length;
39436                if (before == after && level % 3 !== 0 || level > MAX_LEVELS) {
39437                  this._declusterAll();
39438                  this.body.emitter.emit('_layoutFailed');
39439                  console.info('This network could not be positioned by this version of the improved layout algorithm. Please disable improvedLayout for better performance.');
39440                  return;
39441                }
39442                //console.timeEnd("clustering")
39443                //console.log(level,after)
39444              }
39445              // increase the size of the edges
39446              this.body.modules.kamadaKawai.setOptions({ springLength: Math.max(150, 2 * startLength) });
39447            }
39448
39449            // position the system for these nodes and edges
39450            this.body.modules.kamadaKawai.solve(this.body.nodeIndices, this.body.edgeIndices, true);
39451
39452            // shift to center point
39453            this._shiftToCenter();
39454
39455            // perturb the nodes a little bit to force the physics to kick in
39456            var offset = 70;
39457            for (var i = 0; i < this.body.nodeIndices.length; i++) {
39458              this.body.nodes[this.body.nodeIndices[i]].x += (0.5 - this.seededRandom()) * offset;
39459              this.body.nodes[this.body.nodeIndices[i]].y += (0.5 - this.seededRandom()) * offset;
39460            }
39461
39462            // uncluster all clusters
39463            this._declusterAll();
39464
39465            // reposition all bezier nodes.
39466            this.body.emitter.emit('_repositionBezierNodes');
39467          }
39468        }
39469      }
39470    }, {
39471      key: '_shiftToCenter',
39472
39473      /**
39474       * Move all the nodes towards to the center so gravitational pull wil not move the nodes away from view
39475       * @private
39476       */
39477      value: function _shiftToCenter() {
39478        var range = _NetworkUtil2['default']._getRangeCore(this.body.nodes, this.body.nodeIndices);
39479        var center = _NetworkUtil2['default']._findCenter(range);
39480        for (var i = 0; i < this.body.nodeIndices.length; i++) {
39481          this.body.nodes[this.body.nodeIndices[i]].x -= center.x;
39482          this.body.nodes[this.body.nodeIndices[i]].y -= center.y;
39483        }
39484      }
39485    }, {
39486      key: '_declusterAll',
39487      value: function _declusterAll() {
39488        var clustersPresent = true;
39489        while (clustersPresent === true) {
39490          clustersPresent = false;
39491          for (var i = 0; i < this.body.nodeIndices.length; i++) {
39492            if (this.body.nodes[this.body.nodeIndices[i]].isCluster === true) {
39493              clustersPresent = true;
39494              this.body.modules.clustering.openCluster(this.body.nodeIndices[i], {}, false);
39495            }
39496          }
39497          if (clustersPresent === true) {
39498            this.body.emitter.emit('_dataChanged');
39499          }
39500        }
39501      }
39502    }, {
39503      key: 'getSeed',
39504      value: function getSeed() {
39505        return this.initialRandomSeed;
39506      }
39507    }, {
39508      key: 'setupHierarchicalLayout',
39509
39510      /**
39511       * This is the main function to layout the nodes in a hierarchical way.
39512       * It checks if the node details are supplied correctly
39513       *
39514       * @private
39515       */
39516      value: function setupHierarchicalLayout() {
39517        if (this.options.hierarchical.enabled === true && this.body.nodeIndices.length > 0) {
39518          // get the size of the largest hubs and check if the user has defined a level for a node.
39519          var node = undefined,
39520              nodeId = undefined;
39521          var definedLevel = false;
39522          var undefinedLevel = false;
39523          this.hierarchicalLevels = {};
39524          this.nodeSpacing = 100;
39525
39526          for (nodeId in this.body.nodes) {
39527            if (this.body.nodes.hasOwnProperty(nodeId)) {
39528              node = this.body.nodes[nodeId];
39529              if (node.options.level !== undefined) {
39530                definedLevel = true;
39531                this.hierarchicalLevels[nodeId] = node.options.level;
39532              } else {
39533                undefinedLevel = true;
39534              }
39535            }
39536          }
39537
39538          // if the user defined some levels but not all, alert and run without hierarchical layout
39539          if (undefinedLevel === true && definedLevel === true) {
39540            throw new Error('To use the hierarchical layout, nodes require either no predefined levels or levels have to be defined for all nodes.');
39541            return;
39542          } else {
39543            // setup the system to use hierarchical method.
39544            //this._changeConstants();
39545
39546            // define levels if undefined by the users. Based on hubsize
39547            if (undefinedLevel === true) {
39548              if (this.options.hierarchical.sortMethod === 'hubsize') {
39549                this._determineLevelsByHubsize();
39550              } else if (this.options.hierarchical.sortMethod === 'directed' || 'direction') {
39551                this._determineLevelsDirected();
39552              }
39553            }
39554
39555            // check the distribution of the nodes per level.
39556            var distribution = this._getDistribution();
39557
39558            // place the nodes on the canvas.
39559            this._placeNodesByHierarchy(distribution);
39560          }
39561        }
39562      }
39563    }, {
39564      key: '_placeNodesByHierarchy',
39565
39566      /**
39567       * This function places the nodes on the canvas based on the hierarchial distribution.
39568       *
39569       * @param {Object} distribution | obtained by the function this._getDistribution()
39570       * @private
39571       */
39572      value: function _placeNodesByHierarchy(distribution) {
39573        var nodeId = undefined,
39574            node = undefined;
39575        this.positionedNodes = {};
39576        // start placing all the level 0 nodes first. Then recursively position their branches.
39577        for (var level in distribution) {
39578          if (distribution.hasOwnProperty(level)) {
39579            for (nodeId in distribution[level].nodes) {
39580              if (distribution[level].nodes.hasOwnProperty(nodeId)) {
39581
39582                node = distribution[level].nodes[nodeId];
39583
39584                if (this.options.hierarchical.direction === 'UD' || this.options.hierarchical.direction === 'DU') {
39585                  if (node.x === undefined) {
39586                    node.x = distribution[level].distance;
39587                  }
39588                  distribution[level].distance = node.x + this.nodeSpacing;
39589                } else {
39590                  if (node.y === undefined) {
39591                    node.y = distribution[level].distance;
39592                  }
39593                  distribution[level].distance = node.y + this.nodeSpacing;
39594                }
39595
39596                this.positionedNodes[nodeId] = true;
39597                this._placeBranchNodes(node.edges, node.id, distribution, level);
39598              }
39599            }
39600          }
39601        }
39602      }
39603    }, {
39604      key: '_getDistribution',
39605
39606      /**
39607       * This function get the distribution of levels based on hubsize
39608       *
39609       * @returns {Object}
39610       * @private
39611       */
39612      value: function _getDistribution() {
39613        var distribution = {};
39614        var nodeId = undefined,
39615            node = undefined;
39616
39617        // we fix Y because the hierarchy is vertical, we fix X so we do not give a node an x position for a second time.
39618        // the fix of X is removed after the x value has been set.
39619        for (nodeId in this.body.nodes) {
39620          if (this.body.nodes.hasOwnProperty(nodeId)) {
39621            node = this.body.nodes[nodeId];
39622            var level = this.hierarchicalLevels[nodeId] === undefined ? 0 : this.hierarchicalLevels[nodeId];
39623            if (this.options.hierarchical.direction === 'UD' || this.options.hierarchical.direction === 'DU') {
39624              node.y = this.options.hierarchical.levelSeparation * level;
39625              node.options.fixed.y = true;
39626            } else {
39627              node.x = this.options.hierarchical.levelSeparation * level;
39628              node.options.fixed.x = true;
39629            }
39630            if (distribution[level] === undefined) {
39631              distribution[level] = { amount: 0, nodes: {}, distance: 0 };
39632            }
39633            distribution[level].amount += 1;
39634            distribution[level].nodes[nodeId] = node;
39635          }
39636        }
39637        return distribution;
39638      }
39639    }, {
39640      key: '_getHubSize',
39641
39642      /**
39643       * Get the hubsize from all remaining unlevelled nodes.
39644       *
39645       * @returns {number}
39646       * @private
39647       */
39648      value: function _getHubSize() {
39649        var hubSize = 0;
39650        for (var nodeId in this.body.nodes) {
39651          if (this.body.nodes.hasOwnProperty(nodeId)) {
39652            var node = this.body.nodes[nodeId];
39653            if (this.hierarchicalLevels[nodeId] === undefined) {
39654              hubSize = node.edges.length < hubSize ? hubSize : node.edges.length;
39655            }
39656          }
39657        }
39658        return hubSize;
39659      }
39660    }, {
39661      key: '_determineLevelsByHubsize',
39662
39663      /**
39664       * this function allocates nodes in levels based on the recursive branching from the largest hubs.
39665       *
39666       * @param hubsize
39667       * @private
39668       */
39669      value: function _determineLevelsByHubsize() {
39670        var nodeId = undefined,
39671            node = undefined;
39672        var hubSize = 1;
39673
39674        while (hubSize > 0) {
39675          // determine hubs
39676          hubSize = this._getHubSize();
39677          if (hubSize === 0) break;
39678
39679          for (nodeId in this.body.nodes) {
39680            if (this.body.nodes.hasOwnProperty(nodeId)) {
39681              node = this.body.nodes[nodeId];
39682              if (node.edges.length === hubSize) {
39683                this._setLevelByHubsize(0, node);
39684              }
39685            }
39686          }
39687        }
39688      }
39689    }, {
39690      key: '_setLevelByHubsize',
39691
39692      /**
39693       * this function is called recursively to enumerate the barnches of the largest hubs and give each node a level.
39694       *
39695       * @param level
39696       * @param edges
39697       * @param parentId
39698       * @private
39699       */
39700      value: function _setLevelByHubsize(level, node) {
39701        if (this.hierarchicalLevels[node.id] !== undefined) return;
39702
39703        var childNode = undefined;
39704        this.hierarchicalLevels[node.id] = level;
39705        for (var i = 0; i < node.edges.length; i++) {
39706          if (node.edges[i].toId === node.id) {
39707            childNode = node.edges[i].from;
39708          } else {
39709            childNode = node.edges[i].to;
39710          }
39711          this._setLevelByHubsize(level + 1, childNode);
39712        }
39713      }
39714    }, {
39715      key: '_determineLevelsDirected',
39716
39717      /**
39718       * this function allocates nodes in levels based on the direction of the edges
39719       *
39720       * @param hubsize
39721       * @private
39722       */
39723      value: function _determineLevelsDirected() {
39724        var nodeId = undefined,
39725            node = undefined;
39726        var minLevel = 10000;
39727
39728        // set first node to source
39729        for (nodeId in this.body.nodes) {
39730          if (this.body.nodes.hasOwnProperty(nodeId)) {
39731            node = this.body.nodes[nodeId];
39732            this._setLevelDirected(minLevel, node);
39733          }
39734        }
39735
39736        // get the minimum level
39737        for (nodeId in this.body.nodes) {
39738          if (this.body.nodes.hasOwnProperty(nodeId)) {
39739            minLevel = this.hierarchicalLevels[nodeId] < minLevel ? this.hierarchicalLevels[nodeId] : minLevel;
39740          }
39741        }
39742
39743        // subtract the minimum from the set so we have a range starting from 0
39744        for (nodeId in this.body.nodes) {
39745          if (this.body.nodes.hasOwnProperty(nodeId)) {
39746            this.hierarchicalLevels[nodeId] -= minLevel;
39747          }
39748        }
39749      }
39750    }, {
39751      key: '_setLevelDirected',
39752
39753      /**
39754       * this function is called recursively to enumerate the branched of the first node and give each node a level based on edge direction
39755       *
39756       * @param level
39757       * @param edges
39758       * @param parentId
39759       * @private
39760       */
39761      value: function _setLevelDirected(level, node) {
39762        if (this.hierarchicalLevels[node.id] !== undefined) return;
39763
39764        var childNode = undefined;
39765        this.hierarchicalLevels[node.id] = level;
39766
39767        for (var i = 0; i < node.edges.length; i++) {
39768          if (node.edges[i].toId === node.id) {
39769            childNode = node.edges[i].from;
39770            this._setLevelDirected(level - 1, childNode);
39771          } else {
39772            childNode = node.edges[i].to;
39773            this._setLevelDirected(level + 1, childNode);
39774          }
39775        }
39776      }
39777    }, {
39778      key: '_placeBranchNodes',
39779
39780      /**
39781       * This is a recursively called function to enumerate the branches from the largest hubs and place the nodes
39782       * on a X position that ensures there will be no overlap.
39783       *
39784       * @param edges
39785       * @param parentId
39786       * @param distribution
39787       * @param parentLevel
39788       * @private
39789       */
39790      value: function _placeBranchNodes(edges, parentId, distribution, parentLevel) {
39791        for (var i = 0; i < edges.length; i++) {
39792          var childNode = undefined;
39793          var parentNode = undefined;
39794          if (edges[i].toId === parentId) {
39795            childNode = edges[i].from;
39796            parentNode = edges[i].to;
39797          } else {
39798            childNode = edges[i].to;
39799            parentNode = edges[i].from;
39800          }
39801          var childNodeLevel = this.hierarchicalLevels[childNode.id];
39802
39803          if (this.positionedNodes[childNode.id] === undefined) {
39804            // if a node is conneceted to another node on the same level (
39804or higher (means lower level))!, this is not handled here.
39805            if (childNodeLevel > parentLevel) {
39806              if (this.options.hierarchical.direction === 'UD' || this.options.hierarchical.direction === 'DU') {
39807                if (childNode.x === undefined) {
39808                  childNode.x = Math.max(distribution[childNodeLevel].distance, parentNode.x);
39809                }
39810                distribution[childNodeLevel].distance = childNode.x + this.nodeSpacing;
39811                this.positionedNodes[childNode.id] = true;
39812              } else {
39813                if (childNode.y === undefined) {
39814                  childNode.y = Math.max(distribution[childNodeLevel].distance, parentNode.y);
39815                }
39816                distribution[childNodeLevel].distance = childNode.y + this.nodeSpacing;
39817              }
39818              this.positionedNodes[childNode.id] = true;
39819
39820              if (childNode.edges.length > 1) {
39821                this._placeBranchNodes(childNode.edges, childNode.id, distribution, childNodeLevel);
39822              }
39823            }
39824          }
39825        }
39826      }
39827    }]);
39828
39829    return LayoutEngine;
39830  })();
39831
39832  exports['default'] = LayoutEngine;
39833  module.exports = exports['default'];
39834
39835/***/ },
39836/* 110 */
39837/***/ function(module, exports, __webpack_require__) {
39838
39839  'use strict';
39840
39841  Object.defineProperty(exports, '__esModule', {
39842    value: true
39843  });
39844
39845  var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
39846
39847  function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
39848
39849  var util = __webpack_require__(1);
39850  var Hammer = __webpack_require__(20);
39851  var hammerUtil = __webpack_require__(25);
39852
39853  /**
39854   * clears the toolbar div element of children
39855   *
39856   * @private
39857   */
39858
39859  var ManipulationSystem = (function () {
39860    function ManipulationSystem(body, canvas, selectionHandler) {
39861      var _this = this;
39862
39863      _classCallCheck(this, ManipulationSystem);
39864
39865      this.body = body;
39866      this.canvas = canvas;
39867      this.selectionHandler = selectionHandler;
39868
39869      this.editMode = false;
39870      this.manipulationDiv = undefined;
39871      this.editModeDiv = undefined;
39872      this.closeDiv = undefined;
39873
39874      this.manipulationHammers = [];
39875      this.temporaryUIFunctions = {};
39876      this.temporaryEventFunctions = [];
39877
39878      this.touchTime = 0;
39879      this.temporaryIds = { nodes: [], edges: [] };
39880      this.guiEnabled = false;
39881      this.inMode = false;
39882      this.selectedControlNode = undefined;
39883
39884      this.options = {};
39885      this.defaultOptions = {
39886        enabled: false,
39887        initiallyActive: false,
39888        addNode: true,
39889        addEdge: true,
39890        editNode: undefined,
39891        editEdge: true,
39892        deleteNode: true,
39893        deleteEdge: true,
39894        controlNodeStyle: {
39895          shape: 'dot',
39896          size: 6,
39897          color: { background: '#ff0000', border: '#3c3c3c', highlight: { background: '#07f968', border: '#3c3c3c' } },
39898          borderWidth: 2,
39899          borderWidthSelected: 2
39900        }
39901      };
39902      util.extend(this.options, this.defaultOptions);
39903
39904      this.body.emitter.on('destroy', function () {
39905        _this._clean();
39906      });
39907      this.body.emitter.on('_dataChanged', this._restore.bind(this));
39908      this.body.emitter.on('_resetData', this._restore.bind(this));
39909    }
39910
39911    _createClass(ManipulationSystem, [{
39912      key: '_restore',
39913
39914      /**
39915       * If something changes in the data during editing, switch back to the initial datamanipulation state and close all edit modes.
39916       * @private
39917       */
39918      value: function _restore() {
39919        if (this.inMode !== false) {
39920          if (this.options.initiallyActive === true) {
39921            this.enableEditMode();
39922          } else {
39923            this.disableEditMode();
39924          }
39925        }
39926      }
39927    }, {
39928      key: 'setOptions',
39929
39930      /**
39931       * Set the Options
39932       * @param options
39933       */
39934      value: function setOptions(options, allOptions, globalOptions) {
39935        if (allOptions !== undefined) {
39936          if (allOptions.locale !== undefined) {
39937            this.options.locale = allOptions.locale;
39938          } else {
39939            this.options.locale = globalOptions.locale;
39940          }
39941          if (allOptions.locales !== undefined) {
39942            this.options.locales = allOptions.locales;
39943          } else {
39944            this.options.locales = globalOptions.locales;
39945          }
39946        }
39947
39948        if (options !== undefined) {
39949          if (typeof options === 'boolean') {
39950            this.options.enabled = options;
39951          } else {
39952            this.options.enabled = true;
39953            util.deepExtend(this.options, options);
39954          }
39955          if (this.options.initiallyActive === true) {
39956            this.editMode = true;
39957          }
39958          this._setup();
39959        }
39960      }
39961    }, {
39962      key: 'toggleEditMode',
39963
39964      /**
39965       * Enable or disable edit-mode. Draws the DOM required and cleans up after itself.
39966       *
39967       * @private
39968       */
39969      value: function toggleEditMode() {
39970        if (this.editMode === true) {
39971          this.disableEditMode();
39972        } else {
39973          this.enableEditMode();
39974        }
39975      }
39976    }, {
39977      key: 'enableEditMode',
39978      value: function enableEditMode() {
39979        this.editMode = true;
39980
39981        this._clean();
39982        if (this.guiEnabled === true) {
39983          this.manipulationDiv.style.display = 'block';
39984          this.closeDiv.style.display = 'block';
39985          this.editModeDiv.style.display = 'none';
39986          this.showManipulatorToolbar();
39987        }
39988      }
39989    }, {
39990      key: 'disableEditMode',
39991      value: function disableEditMode() {
39992        this.editMode = false;
39993
39994        this._clean();
39995        if (this.guiEnabled === true) {
39996          this.manipulationDiv.style.display = 'none';
39997          this.closeDiv.style.display = 'none';
39998          this.editModeDiv.style.display = 'block';
39999          this._createEditButton();
40000        }
40001      }
40002    }, {
40003      key: 'showManipulatorToolbar',
40004
40005      /**
40006       * Creates the main toolbar. Removes functions bound to the select event. Binds all the buttons of the toolbar.
40007       *
40008       * @private
40009       */
40010      value: function showManipulatorToolbar() {
40011        // restore the state of any bound functions or events, remove control nodes, restore physics
40012        this._clean();
40013
40014        // reset global letiables
40015        this.manipulationDOM = {};
40016
40017        // if the gui is enabled, draw all elements.
40018        if (this.guiEnabled === true) {
40019          // a _restore will hide these menus
40020          this.editMode = true;
40021          this.manipulationDiv.style.display = 'block';
40022          this.closeDiv.style.display = 'block';
40023
40024          var selectedNodeCount = this.selectionHandler._getSelectedNodeCount();
40025          var selectedEdgeCount = this.selectionHandler._getSelectedEdgeCount();
40026          var selectedTotalCount = selectedNodeCount + selectedEdgeCount;
40027          var locale = this.options.locales[this.options.locale];
40028          var needSeperator = false;
40029
40030          if (this.options.addNode !== false) {
40031            this._createAddNodeButton(locale);
40032            needSeperator = true;
40033          }
40034          if (this.options.addEdge !== false) {
40035            if (needSeperator === true) {
40036              this._createSeperator(1);
40037            } else {
40038              needSeperator = true;
40039            }
40040            this._createAddEdgeButton(locale);
40041          }
40042
40043          if (selectedNodeCount === 1 && typeof this.options.editNode === 'function') {
40044            if (needSeperator === true) {
40045              this._createSeperator(2);
40046            } else {
40047              needSeperator = true;
40048            }
40049            this._createEditNodeButton(locale);
40050          } else if (selectedEdgeCount === 1 && selectedNodeCount === 0 && this.options.editEdge !== false) {
40051            if (needSeperator === true) {
40052              this._createSeperator(3);
40053            } else {
40054              needSeperator = true;
40055            }
40056            this._createEditEdgeButton(locale);
40057          }
40058
40059          // remove buttons
40060          if (selectedTotalCount !== 0) {
40061            if (selectedNodeCount > 0 && this.options.deleteNode !== false) {
40062              if (needSeperator === true) {
40063                this._createSeperator(4);
40064              }
40065              this._createDeleteButton(locale);
40066            } else if (selectedNodeCount === 0 && this.options.deleteEdge !== false) {
40067              if (needSeperator === true) {
40068                this._createSeperator(4);
40069              }
40070              this._createDeleteButton(locale);
40071            }
40072          }
40073
40074          // bind the close button
40075          this._bindHammerToDiv(this.closeDiv, this.toggleEditMode.bind(this));
40076
40077          // refresh this bar based on what has been selected
40078          this._temporaryBindEvent('select', this.showManipulatorToolbar.bind(this));
40079        }
40080
40081        // redraw to show any possible changes
40082        this.body.emitter.emit('_redraw');
40083      }
40084    }, {
40085      key: 'addNodeMode',
40086
40087      /**
40088       * Create the toolbar for adding Nodes
40089       *
40090       * @private
40091       */
40092      value: function addNodeMode() {
40093        // when using the gui, enable edit mode if it wasnt already.
40094        if (this.editMode !== true) {
40095          this.enableEditMode();
40096        }
40097
40098        // restore the state of any bound functions or events, remove control nodes, restore physics
40099        this._clean();
40100
40101        this.inMode = 'addNode';
40102        if (this.guiEnabled === true) {
40103          var locale = this.options.locales[this.options.locale];
40104          this.manipulationDOM = {};
40105          this._createBackButton(locale);
40106          this._createSeperator();
40107          this._createDescription(locale['addDescription'] || this.options.locales['en']['addDescription']);
40108
40109          // bind the close button
40110          this._bindHammerToDiv(this.closeDiv, this.toggleEditMode.bind(this));
40111        }
40112
40113        this._temporaryBindEvent('click', this._performAddNode.bind(this));
40114      }
40115    }, {
40116      key: 'editNode',
40117
40118      /**
40119       * call the bound function to handle the editing of the node. The node has to be selected.
40120       *
40121       * @private
40122       */
40123      value: function editNode() {
40124        var _this2 = this;
40125
40126        // when using the gui, enable edit mode if it wasnt already.
40127        if (this.editMode !== true) {
40128          this.enableEditMode();
40129        }
40130
40131        // restore the state of any bound functions or events, remove control nodes, restore physics
40132        this._clean();
40133        var node = this.selectionHandler._getSelectedNode();
40134        if (node !== undefined) {
40135          this.inMode = 'editNode';
40136          if (typeof this.options.editNode === 'function') {
40137            if (node.isCluster !== true) {
40138              var data = util.deepExtend({}, node.options, true);
40139              data.x = node.x;
40140              data.y = node.y;
40141
40142              if (this.options.editNode.length === 2) {
40143                this.options.editNode(data, function (finalizedData) {
40144                  if (finalizedData !== null && finalizedData !== undefined && _this2.inMode === 'editNode') {
40145                    // if for whatever reason the mode has changes (due to dataset change) disregard the callback) {
40146                    _this2.body.data.nodes.getDataSet().update(finalizedData);
40147                  }
40148                  _this2.showManipulatorToolbar();
40149                });
40150              } else {
40151                throw new Error('The function for edit does not support two arguments (data, callback)');
40152              }
40153            } else {
40154              alert(this.options.locales[this.options.locale]['editClusterError'] || this.options.locales['en']['editClusterError']);
40155            }
40156          } else {
40157            throw new Error('No function has been configured to handle the editing of nodes.');
40158          }
40159        } else {
40160          this.showManipulatorToolbar();
40161        }
40162      }
40163    }, {
40164      key: 'addEdgeMode',
40165
40166      /**
40167       * create the toolbar to connect nodes
40168       *
40169       * @private
40170       */
40171      value: function addEdgeMode() {
40172        // when using the gui, enable edit mode if it wasnt already.
40173        if (this.editMode !== true) {
40174          this.enableEditMode();
40175        }
40176
40177        // restore the state of any bound functions or events, remove control nodes, restore physics
40178        this._clean();
40179
40180        this.inMode = 'addEdge';
40181        if (this.guiEnabled === true) {
40182          var locale = this.options.locales[this.options.locale];
40183          this.manipulationDOM = {};
40184          this._createBackButton(locale);
40185          this._createSeperator();
40186          this._createDescription(locale['edgeDescription'] || this.options.locales['en']['edgeDescription']);
40187
40188          // bind the close button
40189          this._bindHammerToDiv(this.closeDiv, this.toggleEditMode.bind(this));
40190        }
40191
40192        // temporarily overload functions
40193        this._temporaryBindUI('onTouch', this._handleConnect.bind(this));
40194        this._temporaryBindUI('onDragEnd', this._finishConnect.bind(this));
40195        this._temporaryBindUI('onDrag', this._dragControlNode.bind(this));
40196        this._temporaryBindUI('onRelease', this._finishConnect.bind(this));
40197
40198        this._temporaryBindUI('onDragStart', function () {});
40199        this._temporaryBindUI('onHold', function () {});
40200      }
40201    }, {
40202      key: 'editEdgeMode',
40203
40204      /**
40205       * create the toolbar to edit edges
40206       *
40207       * @private
40208       */
40209      value: function editEdgeMode() {
40210        var _this3 = this;
40211
40212        // when using the gui, enable edit mode if it wasnt already.
40213        if (this.editMode !== true) {
40214          this.enableEditMode();
40215        }
40216
40217        // restore the state of any bound functions or events, remove control nodes, restore physics
40218        this._clean();
40219
40220        this.inMode = 'editEdge';
40221        if (this.guiEnabled === true) {
40222          var locale = this.options.locales[this.options.locale];
40223          this.manipulationDOM = {};
40224          this._createBackButton(locale);
40225          this._createSeperator();
40226          this._createDescription(locale['editEdgeDescription'] || this.options.locales['en']['editEdgeDescription']);
40227
40228          // bind the close button
40229          this._bindHammerToDiv(this.closeDiv, this.toggleEditMode.bind(this));
40230        }
40231
40232        this.edgeBeingEditedId = this.selectionHandler.getSelectedEdges()[0];
40233        if (this.edgeBeingEditedId !== undefined) {
40234          (function () {
40235            var edge = _this3.body.edges[_this3.edgeBeingEditedId];
40236
40237            // create control nodes
40238            var controlNodeFrom = _this3._getNewTargetNode(edge.from.x, edge.from.y);
40239            var controlNodeTo = _this3._getNewTargetNode(edge.to.x, edge.to.y);
40240
40241            _this3.temporaryIds.nodes.push(controlNodeFrom.id);
40242            _this3.temporaryIds.nodes.push(controlNodeTo.id);
40243
40244            _this3.body.nodes[controlNodeFrom.id] = controlNodeFrom;
40245            _this3.body.nodeIndices.push(controlNodeFrom.id);
40246            _this3.body.nodes[controlNodeTo.id] = controlNodeTo;
40247            _this3.body.nodeIndices.push(controlNodeTo.id);
40248
40249            // temporarily overload UI functions, cleaned up automatically because of _temporaryBindUI
40250            _this3._temporaryBindUI('onTouch', _this3._controlNodeTouch.bind(_this3)); // used to get the position
40251            _this3._temporaryBindUI('onTap', function () {}); // disabled
40252            _this3._temporaryBindUI('onHold', function () {}); // disabled
40253            _this3._temporaryBindUI('onDragStart', _this3._controlNodeDragStart.bind(_this3)); // used to select control node
40254            _this3._temporaryBindUI('onDrag', _this3._controlNodeDrag.bind(_this3)); // used to drag control node
40255            _this3._temporaryBindUI('onDragEnd', _this3._controlNodeDragEnd.bind(_this3)); // used to connect or revert control nodes
40256            _this3._temporaryBindUI('onMouseMove', function () {}); // disabled
40257
40258            // create function to position control nodes correctly on movement
40259            // automatically cleaned up because we use the temporary bind
40260            _this3._temporaryBindEvent('beforeDrawing', function (ctx) {
40261              var positions = edge.edgeType.findBorderPositions(ctx);
40262              if (controlNodeFrom.selected === false) {
40263                controlNodeFrom.x = positions.from.x;
40264                controlNodeFrom.y = positions.from.y;
40265              }
40266              if (controlNodeTo.selected === false) {
40267                controlNodeTo.x = positions.to.x;
40268                controlNodeTo.y = positions.to.y;
40269              }
40270            });
40271
40272            _this3.body.emitter.emit('_redraw');
40273          })();
40274        } else {
40275          this.showManipulatorToolbar();
40276        }
40277      }
40278    }, {
40279      key: 'deleteSelected',
40280
40281      /**
40282       * delete everything in the selection
40283       *
40284       * @private
40285       */
40286      value: function deleteSelected() {
40287        var _this4 = this;
40288
40289        // when using the gui, enable edit mode if it wasnt already.
40290        if (this.editMode !== true) {
40291          this.enableEditMode();
40292        }
40293
40294        // restore the state of any bound functions or events, remove control nodes, restore physics
40295        this._clean();
40296
40297        this.inMode = 'delete';
40298        var selectedNodes = this.selectionHandler.getSelectedNodes();
40299        var selectedEdges = this.selectionHandler.getSelectedEdges();
40300        var deleteFunction = undefined;
40301        if (selectedNodes.length > 0) {
40302          for (var i = 0; i < selectedNodes.length; i++) {
40303            if (this.body.nodes[selectedNodes[i]].isCluster === true) {
40304              alert(this.options.locales[this.options.locale]['deleteClusterError'] || this.options.locales['en']['deleteClusterError']);
40305              return;
40306            }
40307          }
40308
40309          if (typeof this.options.deleteNode === 'function') {
40310            deleteFunction = this.options.deleteNode;
40311          }
40312        } else if (selectedEdges.length > 0) {
40313          if (typeof this.options.deleteEdge === 'function') {
40314            deleteFunction = this.options.deleteEdge;
40315          }
40316        }
40317
40318        if (typeof deleteFunction === 'function') {
40319          var data = { nodes: selectedNodes, edges: selectedEdges };
40320          if (deleteFunction.length === 2) {
40321            deleteFunction(data, function (finalizedData) {
40322              if (finalizedData !== null && finalizedData !== undefined && _this4.inMode === 'delete') {
40323                // if for whatever reason the mode has changes (due to dataset change) disregard the callback) {
40324                _this4.body.data.edges.getDataSet().remove(finalizedData.edges);
40325                _this4.body.data.nodes.getDataSet().remove(finalizedData.nodes);
40326                _this4.body.emitter.emit('startSimulation');
40327                _this4.showManipulatorToolbar();
40328              } else {
40329                _this4.body.emitter.emit('startSimulation');
40330                _this4.showManipulatorToolbar();
40331              }
40332            });
40333          } else {
40334            throw new Error('The function for delete does not support two arguments (data, callback)');
40335          }
40336        } else {
40337          this.body.data.edges.getDataSet().remove(selectedEdges);
40338          this.body.data.nodes.getDataSet().remove(selectedNodes);
40339          this.body.emitter.emit('startSimulation');
40340          this.showManipulatorToolbar();
40341        }
40342      }
40343    }, {
40344      key: '_setup',
40345
40346      //********************************************** PRIVATE ***************************************//
40347
40348      /**
40349       * draw or remove the DOM
40350       * @private
40351       */
40352      value: function _setup() {
40353        if (this.options.enabled === true) {
40354          // Enable the GUI
40355          this.guiEnabled = true;
40356
40357          this._createWrappers();
40358          if (this.editMode === false) {
40359            this._createEditButton();
40360          } else {
40361            this.showManipulatorToolbar();
40362          }
40363        } else {
40364          this._removeManipulationDOM();
40365
40366          // disable the gui
40367          this.guiEnabled = false;
40368        }
40369      }
40370    }, {
40371      key: '_createWrappers',
40372
40373      /**
40374       * create the div overlays that contain the DOM
40375       * @private
40376       */
40377      value: function _createWrappers() {
40378        // load the manipulator HTML elements. All styling done in css.
40379        if (this.manipulationDiv === undefined) {
40380          this.manipulationDiv = document.createElement('div');
40381          this.manipulationDiv.className = 'vis-manipulation';
40382          if (this.editMode === true) {
40383            this.manipulationDiv.style.display = 'block';
40384          } else {
40385            this.manipulationDiv.style.display = 'none';
40386          }
40387          this.canvas.frame.appendChild(this.manipulationDiv);
40388        }
40389
40390        // container for the edit button.
40391        if (this.editModeDiv === undefined) {
40392          this.editModeDiv = document.createElement('div');
40393          this.editModeDiv.className = 'vis-edit-mode';
40394          if (this.editMode === true) {
40395            this.editModeDiv.style.display = 'none';
40396          } else {
40397            this.editModeDiv.style.display = 'block';
40398          }
40399          this.canvas.frame.appendChild(this.editModeDiv);
40400        }
40401
40402        // container for the close div button
40403        if (this.closeDiv === undefined) {
40404          this.closeDiv = document.createElement('div');
40405          this.closeDiv.className = 'vis-close';
40406          this.closeDiv.style.display = this.manipulationDiv.style.display;
40407          this.canvas.frame.appendChild(this.closeDiv);
40408        }
40409      }
40410    }, {
40411      key: '_getNewTargetNode',
40412
40413      /**
40414       * generate a new target node. Used for creating new edges and editing edges
40415       * @param x
40416       * @param y
40417       * @returns {*}
40418       * @private
40419       */
40420      value: function _getNewTargetNode(x, y) {
40421        var controlNodeStyle = util.deepExtend({}, this.options.controlNodeStyle);
40422
40423        controlNodeStyle.id = 'targetNode' + util.randomUUID();
40424        controlNodeStyle.hidden = false;
40425        controlNodeStyle.physics = false;
40426        controlNodeStyle.x = x;
40427        controlNodeStyle.y = y;
40428
40429        return this.body.functions.createNode(controlNodeStyle);
40430      }
40431    }, {
40432      key: '_createEditButton',
40433
40434      /**
40435       * Create the edit button
40436       */
40437      value: function _createEditButton() {
40438        // restore everything to it's original state (if applicable)
40439        this._clean();
40440
40441        // reset the manipulationDOM
40442        this.manipulationDOM = {};
40443
40444        // empty the editModeDiv
40445        util.recursiveDOMDelete(this.editModeDiv);
40446
40447        // create the contents for the editMode button
40448        var locale = this.options.locales[this.options.locale];
40449        var button = this._createButton('editMode', 'vis-button vis-edit vis-edit-mode', locale['edit'] || this.options.locales['en']['edit']);
40450        this.editModeDiv.appendChild(button);
40451
40452        // bind a hammer listener to the button, calling the function toggleEditMode.
40453        this._bindHammerToDiv(button, this.toggleEditMode.bind(this));
40454      }
40455    }, {
40456      key: '_clean',
40457
40458      /**
40459       * this function cleans up after everything this module does. Temporary elements, functions and events are removed, physics restored, hammers removed.
40460       * @private
40461       */
40462      value: function _clean() {
40463        // not in mode
40464        this.inMode = false;
40465
40466        // _clean the divs
40467        if (this.guiEnabled === true) {
40468          util.recursiveDOMDelete(this.editModeDiv);
40469          util.recursiveDOMDelete(this.manipulationDiv);
40470
40471          // removes all the bindings and overloads
40472          this._cleanManipulatorHammers();
40473        }
40474
40475        // remove temporary nodes and edges
40476        this._cleanupTemporaryNodesAndEdges();
40477
40478        // restore overloaded UI functions
40479        this._unbindTemporaryUIs();
40480
40481        // remove the temporaryEventFunctions
40482        this._unbindTemporaryEvents();
40483
40484        // restore the physics if required
40485        this.body.emitter.emit('restorePhysics');
40486      }
40487    }, {
40488      key: '_cleanManipulatorHammers',
40489
40490      /**
40491       * Each dom element has it's own hammer. They are stored in this.manipulationHammers. This cleans them up.
40492       * @private
40493       */
40494      value: function _cleanManipulatorHammers() {
40495        // _clean hammer bindings
40496        if (this.manipulationHammers.length != 0) {
40497          for (var i = 0; i < this.manipulationHammers.length; i++) {
40498            this.manipulationHammers[i].destroy();
40499          }
40500          this.manipulationHammers = [];
40501        }
40502      }
40503    }, {
40504      key: '_removeManipulationDOM',
40505
40506      /**
40507       * Remove all DOM elements created by this module.
40508       * @private
40509       */
40510      value: function _removeManipulationDOM() {
40511        // removes all the bindings and overloads
40512        this._clean();
40513
40514        // empty the manipulation divs
40515        util.recursiveDOMDelete(this.manipulationDiv);
40516        util.recursiveDOMDelete(this.editModeDiv);
40517        util.recursiveDOMDelete(this.closeDiv);
40518
40519        // remove the manipulation divs
40520        if (this.manipulationDiv) {
40521          this.canvas.frame.removeChild(this.manipulationDiv);
40522        }
40523        if (this.editModeDiv) {
40524          this.canvas.frame.removeChild(this.editModeDiv);
40525        }
40526        if (this.closeDiv) {
40527          this.canvas.frame.removeChild(this.manipulationDiv);
40528        }
40529
40530        // set the references to undefined
40531        this.manipulationDiv = undefined;
40532        this.editModeDiv = undefined;
40533        this.closeDiv = undefined;
40534      }
40535    }, {
40536      key: '_createSeperator',
40537
40538      /**
40539       * create a seperator line. the index is to differentiate in the manipulation dom
40540       * @param index
40541       * @private
40542       */
40543      value: function _createSeperator() {
40544        var index = arguments.length <= 0 || arguments[0] === undefined ? 1 : arguments[0];
40545
40546        this.manipulationDOM['seperatorLineDiv' + index] = document.createElement('div');
40547        this.manipulationDOM['seperatorLineDiv' + index].className = 'vis-separator-line';
40548        this.manipulationDiv.appendChild(this.manipulationDOM['seperatorLineDiv' + index]);
40549      }
40550    }, {
40551      key: '_createAddNodeButton',
40552
40553      // ----------------------    DOM functions for buttons    --------------------------//
40554
40555      value: function _createAddNodeButton(locale) {
40556        var button = this._createButton('addNode', 'vis-button vis-add', locale['addNode'] || this.options.locales['en']['addNode']);
40557        this.manipulationDiv.appendChild(button);
40558        this._bindHammerToDiv(button, this.addNodeMode.bind(this));
40559      }
40560    }, {
40561      key: '_createAddEdgeButton',
40562      value: function _createAddEdgeButton(locale) {
40563        var button = this._createButton('addEdge', 'vis-button vis-connect', locale['addEdge'] || this.options.locales['en']['addEdge']);
40564        this.manipulationDiv.appendChild(button);
40565        this._bindHammerToDiv(button, this.addEdgeMode.bind(this));
40566      }
40567    }, {
40568      key: '_createEditNodeButton',
40569      value: function _createEditNodeButton(locale) {
40570        var button = this._createButton('editNode', 'vis-button vis-edit', locale['editNode'] || this.options.locales['en']['editNode']);
40571        this.manipulationDiv.appendChild(button);
40572        this._bindHammerToDiv(button, this.editNode.bind(this));
40573      }
40574    }, {
40575      key: '_createEditEdgeButton',
40576      value: function _createEditEdgeButton(locale) {
40577        var button = this._createButton('editEdge', 'vis-button vis-edit', locale['editEdge'] || this.options.locales['en']['editEdge']);
40578        this.manipulationDiv.appendChild(button);
40579        this._bindHammerToDiv(button, this.editEdgeMode.bind(this));
40580      }
40581    }, {
40582      key: '_createDeleteButton',
40583      value: function _createDeleteButton(locale) {
40584        var button = this._createButton('delete', 'vis-button vis-delete', locale['del'] || this.options.locales['en']['del']);
40585        this.manipulationDiv.appendChild(button);
40586        this._bindHammerToDiv(button, this.deleteSelected.bind(this));
40587      }
40588    }, {
40589      key: '_createBackButton',
40590      value: function _createBackButton(locale) {
40591        var button = this._createButton('back', 'vis-button vis-back', locale['back'] || this.options.locales['en']['back']);
40592        this.manipulationDiv.appendChild(button);
40593        this._bindHammerToDiv(button, this.showManipulatorToolbar.bind(this));
40594      }
40595    }, {
40596      key: '_createButton',
40597      value: function _createButton(id, className, label) {
40598        var labelClassName = arguments.length <= 3 || arguments[3] === undefined ? 'vis-label' : arguments[3];
40599
40600        this.manipulationDOM[id + 'Div'] = document.createElement('div');
40601        this.manipulationDOM[id + 'Div'].className = className;
40602        this.manipulationDOM[id + 'Label'] = document.createElement('div');
40603        this.manipulationDOM[id + 'Label'].className = labelClassName;
40604        this.manipulationDOM[id + 'Label'].innerHTML = label;
40605        this.manipulationDOM[id + 'Div'].appendChild(this.manipulationDOM[id + 'Label']);
40606        return this.manipulationDOM[id + 'Div'];
40607      }
40608    }, {
40609      key: '_createDescription',
40610      value: function _createDescription(label) {
40611        this.manipulationDiv.appendChild(this._createButton('description', 'vis-button vis-none', label));
40612      }
40613    }, {
40614      key: '_temporaryBindEvent',
40615
40616      // -------------------------- End of DOM functions for buttons ------------------------------//
40617
40618      /**
40619       * this binds an event until cleanup by the clean functions.
40620       * @param event
40621       * @param newFunction
40622       * @private
40623       */
40624      value: function _temporaryBindEvent(event, newFunction) {
40625        this.temporaryEventFunctions.push({ event: event, boundFunction: newFunction });
40626        this.body.emitter.on(event, newFunction);
40627      }
40628    }, {
40629      key: '_temporaryBindUI',
40630
40631      /**
40632       * this overrides an UI function until cleanup by the clean function
40633       * @param UIfunctionName
40634       * @param newFunction
40635       * @private
40636       */
40637      value: function _temporaryBindUI(UIfunctionName, newFunction) {
40638        if (this.body.eventListeners[UIfunctionName] !== undefined) {
40639          this.temporaryUIFunctions[UIfunctionName] = this.body.eventListeners[UIfunctionName];
40640          this.body.eventListeners[UIfunctionName] = newFunction;
40641        } else {
40642          throw new Error('This UI function does not exist. Typo? You tried: ' + UIfunctionName + ' possible are: ' + JSON.stringify(Object.keys(this.body.eventListeners)));
40643        }
40644      }
40645    }, {
40646      key: '_unbindTemporaryUIs',
40647
40648      /**
40649       * Restore the overridden UI functions to their original state.
40650       *
40651       * @private
40652       */
40653      value: function _unbindTemporaryUIs() {
40654        for (var functionName in this.temporaryUIFunctions) {
40655          if (this.temporaryUIFunctions.hasOwnProperty(functionName)) {
40656            this.body.eventListeners[functionName] = this.temporaryUIFunctions[functionName];
40657            delete this.temporaryUIFunctions[functionName];
40658          }
40659        }
40660        this.temporaryUIFunctions = {};
40661      }
40662    }, {
40663      key: '_unbindTemporaryEvents',
40664
40665      /**
40666       * Unbind the events created by _temporaryBindEvent
40667       * @private
40668       */
40669      value: function _unbindTemporaryEvents() {
40670        for (var i = 0; i < this.temporaryEventFunctions.length; i++) {
40671          var eventName = this.temporaryEventFunctions[i].event;
40672          var boundFunction = this.temporaryEventFunctions[i].boundFunction;
40673          this.body.emitter.off(eventName, boundFunction);
40674        }
40675        this.temporaryEventFunctions = [];
40676      }
40677    }, {
40678      key: '_bindHammerToDiv',
40679
40680      /**
40681       * Bind an hammer instance to a DOM element.
40682       * @param domElement
40683       * @param funct
40684       */
40685      value: function _bindHammerToDiv(domElement, boundFunction) {
40686        var hammer = new Hammer(domElement, {});
40687        hammerUtil.onTouch(hammer, boundFunction);
40688        this.manipulationHammers.push(hammer);
40689      }
40690    }, {
40691      key: '_cleanupTemporaryNodesAndEdges',
40692
40693      /**
40694       * Neatly clean up temporary edges and nodes
40695       * @private
40696       */
40697      value: function _cleanupTemporaryNodesAndEdges() {
40698        // _clean temporary edges
40699        for (var i = 0; i < this.temporaryIds.edges.length; i++) {
40700          this.body.edges[this.temporaryIds.edges[i]].disconnect();
40701          delete this.body.edges[this.temporaryIds.edges[i]];
40702          var indexTempEdge = this.body.edgeIndices.indexOf(this.temporaryIds.edges[i]);
40703          if (indexTempEdge !== -1) {
40704            this.body.edgeIndices.splice(indexTempEdge, 1);
40705          }
40706        }
40707
40708        // _clean temporary nodes
40709        for (var i = 0; i < this.temporaryIds.nodes.length; i++) {
40710          delete this.body.nodes[this.temporaryIds.nodes[i]];
40711          var indexTempNode = this.body.nodeIndices.indexOf(this.temporaryIds.nodes[i]);
40712          if (indexTempNode !== -1) {
40713            this.body.nodeIndices.splice(indexTempNode, 1);
40714          }
40715        }
40716
40717        this.temporaryIds = { nodes: [], edges: [] };
40718      }
40719    }, {
40720      key: '_controlNodeTouch',
40721
40722      // ------------------------------------------ EDIT EDGE FUNCTIONS -----------------------------------------//
40723
40724      /**
40725       * the touch is used to get the position of the initial click
40726       * @param event
40727       * @private
40728       */
40729      value: function _controlNodeTouch(event) {
40730        this.selectionHandler.unselectAll();
40731        this.lastTouch = this.body.functions.getPointer(event.center);
40732        this.lastTouch.translation = util.extend({}, this.body.view.translation); // copy the object
40733      }
40734    }, {
40735      key: '_controlNodeDragStart',
40736
40737      /**
40738       * the drag start is used to mark one of the control nodes as selected.
40739       * @param event
40740       * @private
40741       */
40742      value: function _controlNodeDragStart(event) {
40743        var pointer = this.lastTouch;
40744        var pointerObj = this.selectionHandler._pointerToPositionObject(pointer);
40745        var from = this.body.nodes[this.temporaryIds.nodes[0]];
40746        var to = this.body.nodes[this.temporaryIds.nodes[1]];
40747        var edge = this.body.edges[this.edgeBeingEditedId];
40748        this.selectedControlNode = undefined;
40749
40750        var fromSelect = from.isOverlappingWith(pointerObj);
40751        var toSelect = to.isOverlappingWith(pointerObj);
40752
40753        if (fromSelect === true) {
40754          this.selectedControlNode = from;
40755          edge.edgeType.from = from;
40756        } else if (toSelect === true) {
40757          this.selectedControlNode = to;
40758          edge.edgeType.to = to;
40759        }
40760
40761        this.body.emitter.emit('_redraw');
40762      }
40763    }, {
40764      key: '_controlNodeDrag',
40765
40766      /**
40767       * dragging the control nodes or the canvas
40768       * @param event
40769       * @private
40770       */
40771      value: function _controlNodeDrag(event) {
40772        this.body.emitter.emit('disablePhysics');
40773        var pointer = this.body.functions.getPointer(event.center);
40774        var pos = this.canvas.DOMtoCanvas(pointer);
40775
40776        if (this.selectedControlNode !== undefined) {
40777          this.selectedControlNode.x = pos.x;
40778          this.selectedControlNode.y = pos.y;
40779        } else {
40780          // if the drag was not started properly because the click started outside the network div, start it now.
40781          var diffX = pointer.x - this.lastTouch.x;
40782          var diffY = pointer.y - this.lastTouch.y;
40783          this.body.view.translation = { x: this.lastTouch.translation.x + diffX, y: this.lastTouch.translation.y + diffY };
40784        }
40785        this.body.emitter.emit('_redraw');
40786      }
40787    }, {
40788      key: '_controlNodeDragEnd',
40789
40790      /**
40791       * connecting or restoring the control nodes.
40792       * @param event
40793       * @private
40794       */
40795      value: function _controlNodeDragEnd(event) {
40796        var pointer = this.body.functions.getPointer(event.center);
40797        var pointerObj = this.selectionHandler._pointerToPositionObject(pointer);
40798        var edge = this.body.edges[this.edgeBeingEditedId];
40799
40800        // if the node that was dragged is not a control node, return
40801        if (this.selectedControlNode === undefined) {
40802          return;
40803        }
40804
40805        var overlappingNodeIds = this.selectionHandler._getAllNodesOverlappingWith(pointerObj);
40806        var node = undefined;
40807        for (var i = overlappingNodeIds.length - 1; i >= 0; i--) {
40808          if (overlappingNodeIds[i] !== this.selectedControlNode.id) {
40809            node = this.body.nodes[overlappingNodeIds[i]];
40810            break;
40811          }
40812        }
40813
40814        // perform the connection
40815        if (node !== undefined && this.selectedControlNode !== undefined) {
40816          if (node.isCluster === true) {
40817            alert(this.options.locales[this.options.locale]['createEdgeError'] || this.options.locales['en']['createEdgeError']);
40818          } else {
40819            var from = this.body.nodes[this.temporaryIds.nodes[0]];
40820            if (this.selectedControlNode.id === from.id) {
40821              this._performEditEdge(node.id, edge.to.id);
40822            } else {
40823              this._performEditEdge(edge.from.id, node.id);
40824            }
40825          }
40826        } else {
40827          edge.updateEdgeType();
40828          this.body.emitter.emit('restorePhysics');
40829        }
40830        this.body.emitter.emit('_redraw');
40831      }
40832    }, {
40833      key: '_handleConnect',
40834
40835      // ------------------------------------ END OF EDIT EDGE FUNCTIONS -----------------------------------------//
40836
40837      // ------------------------------------------- ADD EDGE FUNCTIONS -----------------------------------------//
40838      /**
40839       * the function bound to the selection event. It checks if you want to connect a cluster and changes the description
40840       * to walk the user through the process.
40841       *
40842       * @private
40843       */
40844      value: function _handleConnect(event) {
40845        // check to avoid double fireing of this function.
40846        if (new Date().valueOf() - this.touchTime > 100) {
40847          this.lastTouch = this.body.functions.getPointer(event.center);
40848          this.lastTouch.translation = util.extend({}, this.body.view.translation); // copy the object
40849
40850          var pointer = this.lastTouch;
40851          var node = this.selectionHandler.getNodeAt(pointer);
40852
40853          if (node !== undefined) {
40854            if (node.isCluster === true) {
40855              alert(this.options.locales[this.options.locale]['createEdgeError'] || this.options.locales['en']['createEdgeError']);
40856            } else {
40857              // create a node the temporary line can look at
40858              var targetNode = this._getNewTargetNode(node.x, node.y);
40859              this.body.nodes[targetNode.id] = targetNode;
40860              this.body.nodeIndices.push(targetNode.id);
40861
40862              // create a temporary edge
40863              var connectionEdge = this.body.functions.createEdge({
40864                id: 'connectionEdge' + util.randomUUID(),
40865                from: node.id,
40866                to: targetNode.id,
40867                physics: false,
40868                smooth: {
40869                  enabled: true,
40870                  type: 'continuous',
40871                  roundness: 0.5
40872                }
40873              });
40874              this.body.edges[connectionEdge.id] = connectionEdge;
40875              this.body.edgeIndices.push(connectionEdge.id);
40876
40877              this.temporaryIds.nodes.push(targetNode.id);
40878              this.temporaryIds.edges.push(connectionEdge.id);
40879            }
40880          }
40881          this.touchTime = new Date().valueOf();
40882        }
40883      }
40884    }, {
40885      key: '_dragControlNode',
40886      value: function _dragControlNode(event) {
40887        var pointer = this.body.functions.getPointer(event.center);
40888        if (this.temporaryIds.nodes[0] !== undefined) {
40889          var targetNode = this.body.nodes[this.temporaryIds.nodes[0]]; // there is only one temp node in the add edge mode.
40890          targetNode.x = this.canvas._XconvertDOMtoCanvas(pointer.x);
40891          targetNode.y = this.canvas._YconvertDOMtoCanvas(pointer.y);
40892          this.body.emitter.emit('_redraw');
40893        } else {
40894          var diffX = pointer.x - this.lastTouch.x;
40895          var diffY = pointer.y - this.lastTouch.y;
40896          this.body.view.translation = { x: this.lastTouch.translation.x + diffX, y: this.lastTouch.translation.y + diffY };
40897        }
40898      }
40899    }, {
40900      key: '_finishConnect',
40901
40902      /**
40903       * Connect the new edge to the target if one exists, otherwise remove temp line
40904       * @param event
40905       * @private
40906       */
40907      value: function _finishConnect(event) {
40908        var pointer = this.body.functions.getPointer(event.center);
40909        var pointerObj = this.selectionHandler._pointerToPositionObject(pointer);
40910
40911        // remember the edge id
40912        var connectFromId = undefined;
40913        if (this.temporaryIds.edges[0] !== undefined) {
40914          connectFromId = this.body.edges[this.temporaryIds.edges[0]].fromId;
40915        }
40916
40917        // get the overlapping node but NOT the temporary node;
40918        var overlappingNodeIds = this.selectionHandler._getAllNodesOverlappingWith(pointerObj);
40919        var node = undefined;
40920        for (var i = overlappingNodeIds.length - 1; i >= 0; i--) {
40921          // if the node id is NOT a temporary node, accept the node.
40922          if (this.temporaryIds.nodes.indexOf(overlappingNodeIds[i]) === -1) {
40923            node = this.body.nodes[overlappingNodeIds[i]];
40924            break;
40925          }
40926        }
40927
40928        // clean temporary nodes and edges.
40929        this._cleanupTemporaryNodesAndEdges();
40930
40931        // perform the connection
40932        if (node !== undefined) {
40933          if (node.isCluster === true) {
40934            alert(this.options.locales[this.options.locale]['createEdgeError'] || this.options.locales['en']['createEdgeError']);
40935          } else {
40936            if (this.body.nodes[connectFromId] !== undefined && this.body.nodes[node.id] !== undefined) {
40937              this._performAddEdge(connectFromId, node.id);
40938            }
40939          }
40940        }
40941        this.body.emitter.emit('_redraw');
40942      }
40943    }, {
40944      key: '_performAddNode',
40945
40946      // --------------------------------------- END OF ADD EDGE FUNCTIONS -------------------------------------//
40947
40948      // ------------------------------ Performing all the actual data manipulation ------------------------//
40949
40950      /**
40951       * Adds a node on the specified location
40952       */
40953      value: function _performAddNode(clickData) {
40954        var _this5 = this;
40955
40956        var defaultData = {
40957          id: util.randomUUID(),
40958          x: clickData.pointer.canvas.x,
40959          y: clickData.pointer.canvas.y,
40960          label: 'new'
40961        };
40962
40963        if (typeof this.options.addNode === 'function') {
40964          if (this.options.addNode.length === 2) {
40965            this.options.addNode(defaultData, function (finalizedData) {
40966              if (finalizedData !== null && finalizedData !== undefined && _this5.inMode === 'addNode') {
40967                // if for whatever reason the mode has changes (due to dataset change) disregard the callback
40968                _this5.body.data.nodes.getDataSet().add(finalizedData);
40969                _this5.showManipulatorToolbar();
40970              }
40971            });
40972          } else {
40973            throw new Error('The function for add does not support two arguments (data,callback)');
40974            this.showManipulatorToolbar();
40975          }
40976        } else {
40977          this.body.data.nodes.getDataSet().add(defaultData);
40978          this.showManipulatorToolbar();
40979        }
40980      }
40981    }, {
40982      key: '_performAddEdge',
40983
40984      /**
40985       * connect two nodes with a new edge.
40986       *
40987       * @private
40988       */
40989      value: function _performAddEdge(sourceNodeId, targetNodeId) {
40990        var _this6 = this;
40991
40992        var defaultData = { from: sourceNodeId, to: targetNodeId };
40993        if (typeof this.options.addEdge === 'function') {
40994          if (this.options.addEdge.length === 2) {
40995            this.options.addEdge(defaultData, function (finalizedData) {
40996              if (finalizedData !== null && finalizedData !== undefined && _this6.inMode === 'addEdge') {
40997                // if for whatever reason the mode has changes (due to dataset change) disregard the callback
40998                _this6.body.data.edges.getDataSet().add(finalizedData);
40999                _this6.selectionHandler.unselectAll();
41000                _this6.showManipulatorToolbar();
41001              }
41002            });
41003          } else {
41004            throw new Error('The function for connect does not support two arguments (data,callback)');
41005          }
41006        } else {
41007          this.body.data.edges.getDataSet().add(defaultData);
41008          this.selectionHandler.unselectAll();
41009          this.showManipulatorToolbar();
41010        }
41011      }
41012    }, {
41013      key: '_performEditEdge',
41014
41015      /**
41016       * connect two nodes with a new edge.
41017       *
41018       * @private
41019       */
41020      value: function _performEditEdge(sourceNodeId, targetNodeId) {
41021        var _this7 = this;
41022
41023        var defaultData = { id: this.edgeBeingEditedId, from: sourceNodeId, to: targetNodeId };
41024        if (typeof this.options.editEdge === 'function') {
41025          if (this.options.editEdge.length === 2) {
41026            this.options.editEdge(defaultData, function (finalizedData) {
41027              if (finalizedData === null || finalizedData === undefined || _this7.inMode !== 'editEdge') {
41028                // if for whatever reason the mode has changes (due to dataset change) disregard the callback) {
41029                _this7.body.edges[defaultData.id].updateEdgeType();
41030                _this7.body.emitter.emit('_redraw');
41031              } else {
41032                _this7.body.data.edges.getDataSet().update(finalizedData);
41033                _this7.selectionHandler.unselectAll();
41034                _this7.showManipulatorToolbar();
41035              }
41036            });
41037          } else {
41038            throw new Error('The function for edit does not support two arguments (data, callback)');
41039          }
41040        } else {
41041          this.body.data.edges.getDataSet().update(defaultData);
41042          this.selectionHandler.unselectAll();
41043          this.showManipulatorToolbar();
41044        }
41045      }
41046    }]);
41047
41048    return ManipulationSystem;
41049  })();
41050
41051  exports['default'] = ManipulationSystem;
41052  module.exports = exports['default'];
41053
41054/***/ },
41055/* 111 */
41056/***/ function(module, exports) {
41057
41058  /**
41059   * This object contains all possible options. It will check if the types are correct, if required if the option is one
41060   * of the allowed values.
41061   *
41062   * __any__ means that the name of the property does not matter.
41063   * __type__ is a required field for all objects and contains the allowed types of all objects
41064   */
41065  'use strict';
41066
41067  Object.defineProperty(exports, '__esModule', {
41068    value: true
41069  });
41070  var string = 'string';
41071  var boolean = 'boolean';
41072  var number = 'number';
41073  var array = 'array';
41074  var object = 'object'; // should only be in a __type__ property
41075  var dom = 'dom';
41076  var any = 'any';
41077
41078  var allOptions = {
41079    configure: {
41080      enabled: { boolean: boolean },
41081      filter: { boolean: boolean, string: string, array: array, 'function': 'function' },
41082      container: { dom: dom },
41083      showButton: { boolean: boolean },
41084      __type__: { object: object, boolean: boolean, string: string, array: array, 'function': 'function' }
41085    },
41086    edges: {
41087      arrows: {
41088        to: { enabled: { boolean: boolean }, scaleFactor: { number: number }, __type__: { object: object, boolean: boolean } },
41089        middle: { enabled: { boolean: boolean }, scaleFactor: { number: number }, __type__: { object: object, boolean: boolean } },
41090        from: { enabled: { boolean: boolean }, scaleFactor: { number: number }, __type__: { object: object, boolean: boolean } },
41091        __type__: { string: ['from', 'to', 'middle'], object: object }
41092      },
41093      color: {
41094        color: { string: string },
41095        highlight: { string: string },
41096        hover: { string: string },
41097        inherit: { string: ['from', 'to', 'both'], boolean: boolean },
41098        opacity: { number: number },
41099        __type__: { object: object, string: string }
41100      },
41101      dashes: { boolean: boolean, array: array },
41102      font: {
41103        color: { string: string },
41104        size: { number: number }, // px
41105        face: { string: string },
41106        background: { string: string },
41107        strokeWidth: { number: number }, // px
41108        strokeColor: { string: string },
41109        align: { string: ['horizontal', 'top', 'middle', 'bottom'] },
41110        __type__: { object: object, string: string }
41111      },
41112      hidden: { boolean: boolean },
41113      hoverWidth: { 'function': 'function', number: number },
41114      label: { string: string, 'undefined': 'undefined' },
41115      labelHighlightBold: { boolean: boolean },
41116      length: { number: number, 'undefined': 'undefined' },
41117      physics: { boolean: boolean },
41118      scaling: {
41119        min: { number: number },
41120        max: { number: number },
41121        label: {
41122          enabled: { boolean: boolean },
41123          min: { number: number },
41124          max: { number: number },
41125          maxVisible: { number: number },
41126          drawThreshold: { number: number },
41127          __type__: { object: object, boolean: boolean }
41128        },
41129        customScalingFunction: { 'function': 'function' },
41130        __type__: { object: object }
41131      },
41132      selectionWidth: { 'function': 'function', number: number },
41133      selfReferenceSize: { number: number },
41134      shadow: {
41135        enabled: { boolean: boolean },
41136        size: { number: number },
41137        x: { number: number },
41138        y: { number: number },
41139        __type__: { object: object, boolean: boolean }
41140      },
41141      smooth: {
41142        enabled: { boolean: boolean },
41143        type: { string: ['dynamic', 'continuous', 'discrete', 'diagonalCross', 'straightCross', 'horizontal', 'vertical', 'curvedCW', 'curvedCCW', 'cubicBezier'] },
41144        roundness: { number: number },
41145        forceDirection: { string: ['horizontal', 'vertical', 'none'], boolean: boolean },
41146        __type__: { object: object, boolean: boolean }
41147      },
41148      title: { string: string, 'undefined': 'undefined' },
41149      width: { number: number },
41150      value: { number: number, 'undefined': 'undefined' },
41151      __type__: { object: object }
41152    },
41153    groups: {
41154      useDefaultGroups: { boolean: boolean },
41155      __any__: 'get from nodes, will be overwritten below',
41156      __type__: { object: object }
41157    },
41158    interaction: {
41159      dragNodes: { boolean: boolean },
41160      dragView: { boolean: boolean },
41161      hideEdgesOnDrag: { boolean: boolean },
41162      hideNodesOnDrag: { boolean: boolean },
41163      hover: { boolean: boolean },
41164      keyboard: {
41165        enabled: { boolean: boolean },
41166        speed: { x: { number: number }, y: { number: number }, zoom: { number: number }, __type__: { object: object } },
41167        bindToWindow: { boolean: boolean },
41168        __type__: { object: object, boolean: boolean }
41169      },
41170      multiselect: { boolean: boolean },
41171      navigationButtons: { boolean: boolean },
41172      selectable: { boolean: boolean },
41173      selectConnectedEdges: { boolean: boolean },
41174      hoverConnectedEdges: { boolean: boolean },
41175      tooltipDelay: { number: number },
41176      zoomView: { boolean: boolean },
41177      __type__: { object: object }
41178    },
41179    layout: {
41180      randomSeed: { 'undefined': 'undefined', number: number },
41181      improvedLayout: { boolean: boolean },
41182      hierarchical: {
41183        enabled: { boolean: boolean },
41184        levelSeparation: { number: number },
41185        direction: { string: ['UD', 'DU', 'LR', 'RL'] }, // UD, DU, LR, RL
41186        sortMethod: { string: ['hubsize', 'directed'] }, // hubsize, directed
41187        __type__: { object: object, boolean: boolean }
41188      },
41189      __type__: { object: object }
41190    },
41191    manipulation: {
41192      enabled: { boolean: boolean },
41193      initiallyActive: { boolean: boolean },
41194      addNode: { boolean: boolean, 'function': 'function' },
41195      addEdge: { boolean: boolean, 'function': 'function' },
41196      editNode: { 'function': 'function' },
41197      editEdge: { boolean: boolean, 'function': 'function' },
41198      deleteNode: { boolean: boolean, 'function': 'function' },
41199      deleteEdge: { boolean: boolean, 'function': 'function' },
41200      controlNodeStyle: 'get from nodes, will be overwritten below',
41201      __type__: { object: object, boolean: boolean }
41202    },
41203    nodes: {
41204      borderWidth: { number: number },
41205      borderWidthSelected: { number: number, 'undefined': 'undefined' },
41206      brokenImage: { string: string, 'undefined': 'undefined' },
41207      color: {
41208        border: { string: string },
41209        background: { string: string },
41210        highlight: {
41211          border: { string: string },
41212          background: { string: string },
41213          __type__: { object: object, string: string }
41214        },
41215        hover: {
41216          border: { string: string },
41217          background: { string: string },
41218          __type__: { object: object, string: string }
41219        },
41220        __type__: { object: object, string: string }
41221      },
41222      fixed: {
41223        x: { boolean: boolean },
41224        y: { boolean: boolean },
41225        __type__: { object: object, boolean: boolean }
41226      },
41227      font: {
41228        color: { string: string },
41229        size: { number: number }, // px
41230        face: { string: string },
41231        background: { string: string },
41232        strokeWidth: { number: number }, // px
41233        strokeColor: { string: string },
41234        __type__: { object: object, string: string }
41235      },
41236      group: { string: string, number: number, 'undefined': 'undefined' },
41237      hidden: { boolean: boolean },
41238      icon: {
41239        face: { string: string },
41240        code: { string: string }, //'\uf007',
41241        size: { number: number }, //50,
41242        color: { string: string },
41243        __type__: { object: object }
41244      },
41245      id: { string: string, number: number },
41246      image: { string: string, 'undefined': 'undefined' }, // --> URL
41247      label: { string: string, 'undefined': 'undefined' },
41248      labelHighlightBold: { boolean: boolean },
41249      level: { number: number, 'undefined': 'undefined' },
41250      mass: { number: number },
41251      physics: { boolean: boolean },
41252      scaling: {
41253        min: { number: number },
41254        max: { number: number },
41255        label: {
41256          enabled: { boolean: boolean },
41257          min: { number: number },
41258          max: { number: number },
41259          maxVisible: { number: number },
41260          drawThreshold: { number: number },
41261          __type__: { object: object, boolean: boolean }
41262        },
41263        customScalingFunction: { 'function': 'function' },
41264        __type__: { object: object }
41265      },
41266      shadow: {
41267        enabled: { boolean: boolean },
41268        size: { number: number },
41269        x: { number: number },
41270        y: { number: number },
41271        __type__: { object: object, boolean: boolean }
41272      },
41273      shape: { string: ['ellipse', 'circle', 'database', 'box', 'text', 'image', 'circularImage', 'diamond', 'dot', 'star', 'triangle', 'triangleDown', 'square', 'icon'] },
41274      shapeProperties: {
41275        borderDashes: { boolean: boolean, array: array },
41276        borderRadius: { number: number },
41277        useImageSize: { boolean: boolean },
41278        useBorderWithImage: { boolean: boolean },
41279        __type__: { object: object }
41280      },
41281      size: { number: number },
41282      title: { string: string, 'undefined': 'undefined' },
41283      value: { number: number, 'undefined': 'undefined' },
41284      x: { number: number },
41285      y: { number: number },
41286      __type__: { object: object }
41287    },
41288    physics: {
41289      enabled: { boolean: boolean },
41290      barnesHut: {
41291        gravitationalConstant: { number: number },
41292        centralGravity: { number: number },
41293        springLength: { number: number },
41294        springConstant: { number: number },
41295        damping: { number: number },
41296        avoidOverlap: { number: number },
41297        __type__: { object: object }
41298      },
41299      forceAtlas2Based: {
41300        gravitationalConstant: { number: number },
41301        centralGravity: { number: number },
41302        springLength: { number: number },
41303        springConstant: { number: number },
41304        damping: { number: number },
41305        avoidOverlap: { number: number },
41306        __type__: { object: object }
41307      },
41308      repulsion: {
41309        centralGravity: { number: number },
41310        springLength: { number: number },
41311        springConstant: { number: number },
41312        nodeDistance: { number: number },
41313        damping: { number: number },
41314        __type__: { object: object }
41315      },
41316      hierarchicalRepulsion: {
41317        centralGravity: { number: number },
41318        springLength: { number: number },
41319        springConstant: { number: number },
41320        nodeDistance: { number: number },
41321        damping: { number: number },
41322        __type__: { object: object }
41323      },
41324      maxVelocity: { number: number },
41325      minVelocity: { number: number }, // px/s
41326      solver: { string: ['barnesHut', 'repulsion', 'hierarchicalRepulsion', 'forceAtlas2Based'] },
41327      stabilization: {
41328        enabled: { boolean: boolean },
41329        iterations: { number: number }, // maximum number of iteration to stabilize
41330        updateInterval: { number: number },
41331        onlyDynamicEdges: { boolean: boolean },
41332        fit: { boolean: boolean },
41333        __type__: { object: object, boolean: boolean }
41334      },
41335      timestep: { number: number },
41336      adaptiveTimestep: { boolean: boolean },
41337      __type__: { object: object, boolean: boolean }
41338    },
41339
41340    //globals :
41341    autoResize: { boolean: boolean },
41342    clickToUse: { boolean: boolean },
41343    locale: { string: string },
41344    locales: {
41345      __any__: { any: any },
41346      __type__: { object: object }
41347    },
41348    height: { string: string },
41349    width: { string: string },
41350    __type__: { object: object }
41351  };
41352
41353  allOptions.groups.__any__ = allOptions.nodes;
41354  allOptions.manipulation.controlNodeStyle = allOptions.nodes;
41355
41356  var configureOptions = {
41357    nodes: {
41358      borderWidth: [1, 0, 10, 1],
41359      borderWidthSelected: [2, 0, 10, 1],
41360      color: {
41361        border: ['color', '#2B7CE9'],
41362        background: ['color', '#97C2FC'],
41363        highlight: {
41364          border: ['color', '#2B7CE9'],
41365          background: ['color', '#D2E5FF']
41366        },
41367        hover: {
41368          border: ['color', '#2B7CE9'],
41369          background: ['color', '#D2E5FF']
41370        }
41371      },
41372      fixed: {
41373        x: false,
41374        y: false
41375      },
41376      font: {
41377        color: ['color', '#343434'],
41378        size: [14, 0, 100, 1], // px
41379        face: ['arial', 'verdana', 'tahoma'],
41380        background: ['color', 'none'],
41381        strokeWidth: [0, 0, 50, 1], // px
41382        strokeColor: ['color', '#ffffff']
41383      },
41384      //group: 'string',
41385      hidden: false,
41386      labelHighlightBold: true,
41387      //icon: {
41388      //  face: 'string',  //'FontAwesome',
41389      //  code: 'string',  //'\uf007',
41390      //  size: [50, 0, 200, 1],  //50,
41391      //  color: ['color','#2B7CE9']   //'#aa00ff'
41392      //},
41393      //image: 'string', // --> URL
41394      physics: true,
41395      scaling: {
41396        min: [10, 0, 200, 1],
41397        max: [30, 0, 200, 1],
41398        label: {
41399          enabled: false,
41400          min: [14, 0, 200, 1],
41401          max: [30, 0, 200, 1],
41402          maxVisible: [30, 0, 200, 1],
41403          drawThreshold: [5, 0, 20, 1]
41404        }
41405      },
41406      shadow: {
41407        enabled: false,
41408        size: [10, 0, 20, 1],
41409        x: [5, -30, 30, 1],
41410        y: [5, -30, 30, 1]
41411      },
41412      shape: ['ellipse', 'box', 'circle', 'database', 'diamond', 'dot', 'square', 'star', 'text', 'triangle', 'triangleDown'],
41413      shapeProperties: {
41414        borderDashes: false,
41415        borderRadius: [6, 0, 20, 1],
41416        useImageSize: false
41417      },
41418      size: [25, 0, 200, 1]
41419    },
41420    edges: {
41421      arrows: {
41422        to: { enabled: false, scaleFactor: [1, 0, 3, 0.05] }, // boolean / {arrowScaleFactor:1} / {enabled: false, arrowScaleFactor:1}
41423        middle: { enabled: false, scaleFactor: [1, 0, 3, 0.05] },
41424        from: { enabled: false, scaleFactor: [1, 0, 3, 0.05] }
41425      },
41426      color: {
41427        color: ['color', '#848484'],
41428        highlight: ['color', '#848484'],
41429        hover: ['color', '#848484'],
41430        inherit: ['from', 'to', 'both', true, false],
41431        opacity: [1, 0, 1, 0.05]
41432      },
41433      dashes: false,
41434      font: {
41435        color: ['color', '#343434'],
41436        size: [14, 0, 100, 1], // px
41437        face: ['arial', 'verdana', 'tahoma'],
41438        background: ['color', 'none'],
41439        strokeWidth: [2, 0, 50, 1], // px
41440        strokeColor: ['color', '#ffffff'],
41441        align: ['horizontal', 'top', 'middle', 'bottom']
41442      },
41443      hidden: false,
41444      hoverWidth: [1.5, 0, 5, 0.1],
41445      labelHighlightBold: true,
41446      physics: true,
41447      scaling: {
41448        min: [1, 0, 100, 1],
41449        max: [15, 0, 100, 1],
41450        label: {
41451          enabled: true,
41452          min: [14, 0, 200, 1],
41453          max: [30, 0, 200, 1],
41454          maxVisible: [30, 0, 200, 1],
41455          drawThreshold: [5, 0, 20, 1]
41456        }
41457      },
41458      selectionWidth: [1.5, 0, 5, 0.1],
41459      selfReferenceSize: [20, 0, 200, 1],
41460      shadow: {
41461        enabled: false,
41462        size: [10, 0, 20, 1],
41463        x: [5, -30, 30, 1],
41464        y: [5, -30, 30, 1]
41465      },
41466      smooth: {
41467        enabled: true,
41468        type: ['dynamic', 'continuous', 'discrete', 'diagonalCross', 'straightCross', 'horizontal', 'vertical', 'curvedCW', 'curvedCCW', 'cubicBezier'],
41469        forceDirection: ['horizontal', 'vertical', 'none'],
41470        roundness: [0.5, 0, 1, 0.05]
41471      },
41472      width: [1, 0, 30, 1]
41473    },
41474    layout: {
41475      //randomSeed: [0, 0, 500, 1],
41476      //improvedLayout: true,
41477      hierarchical: {
41478        enabled: false,
41479        levelSeparation: [150, 20, 500, 5],
41480        direction: ['UD', 'DU', 'LR', 'RL'], // UD, DU, LR, RL
41481        sortMethod: ['hubsize', 'directed'] // hubsize, directed
41482      }
41483    },
41484    interaction: {
41485      dragNodes: true,
41486      dragView: true,
41487      hideEdgesOnDrag: false,
41488      hideNodesOnDrag: false,
41489      hover: false,
41490      keyboard: {
41491        enabled: false,
41492        speed: { x: [10, 0, 40, 1], y: [10, 0, 40, 1], zoom: [0.02, 0, 0.1, 0.005] },
41493        bindToWindow: true
41494      },
41495      multiselect: false,
41496      navigationButtons: false,
41497      selectable: true,
41498      selectConnectedEdges: true,
41499      hoverConnectedEdges: true,
41500      tooltipDelay: [300, 0, 1000, 25],
41501      zoomView: true
41502    },
41503    manipulation: {
41504      enabled: false,
41505      initiallyActive: false
41506    },
41507    physics: {
41508      enabled: true,
41509      barnesHut: {
41510        //theta: [0.5, 0.1, 1, 0.05],
41511        gravitationalConstant: [-2000, -30000, 0, 50],
41512        centralGravity: [0.3, 0, 10, 0.05],
41513        springLength: [95, 0, 500, 5],
41514        springConstant: [0.04, 0, 1.2, 0.005],
41515        damping: [0.09, 0, 1, 0.01],
41516        avoidOverlap: [0, 0, 1, 0.01]
41517      },
41518      forceAtlas2Based: {
41519        //theta: [0.5, 0.1, 1, 0.05],
41520        gravitationalConstant: [-50, -500, 0, 1],
41521        centralGravity: [0.01, 0, 1, 0.005],
41522        springLength: [95, 0, 500, 5],
41523        springConstant: [0.08, 0, 1.2, 0.005],
41524        damping: [0.4, 0, 1, 0.01],
41525        avoidOverlap: [0, 0, 1, 0.01]
41526      },
41527      repulsion: {
41528        centralGravity: [0.2, 0, 10, 0.05],
41529        springLength: [200, 0, 500, 5],
41530        springConstant: [0.05, 0, 1.2, 0.005],
41531        nodeDistance: [100, 0, 500, 5],
41532        damping: [0.09, 0, 1, 0.01]
41533      },
41534      hierarchicalRepulsion: {
41535        centralGravity: [0.2, 0, 10, 0.05],
41536        springLength: [100, 0, 500, 5],
41537        springConstant: [0.01, 0, 1.2, 0.005],
41538        nodeDistance: [120, 0, 500, 5],
41539        damping: [0.09, 0, 1, 0.01]
41540      },
41541      maxVelocity: [50, 0, 150, 1],
41542      minVelocity: [0.1, 0.01, 0.5, 0.01],
41543      solver: ['barnesHut', 'forceAtlas2Based', 'repulsion', 'hierarchicalRepulsion'],
41544      timestep: [0.5, 0.01, 1, 0.01]
41545    },
41546    global: {
41547      locale: ['en', 'nl']
41548    }
41549  };
41550
41551  exports.allOptions = allOptions;
41552  exports.configureOptions = configureOptions;
41553  //adaptiveTimestep: true
41554
41555/***/ },
41556/* 112 */
41557/***/ function(module, exports, __webpack_require__) {
41558
41559  /**
41560   * Created by Alex on 8/7/2015.
41561   */
41562
41563  // distance finding algorithm
41564  "use strict";
41565
41566  Object.defineProperty(exports, "__esModule", {
41567    value: true
41568  });
41569
41570  var _slicedToArray = (function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; })();
41571
41572  var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
41573
41574  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
41575
41576  function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
41577
41578  var _componentsAlgorithmsFloydWarshallJs = __webpack_require__(113);
41579
41580  var _componentsAlgorithmsFloydWarshallJs2 = _interopRequireDefault(_componentsAlgorithmsFloydWarshallJs);
41581
41582  /**
41583   * KamadaKawai positions the nodes initially based on
41584   *
41585   * "AN ALGORITHM FOR DRAWING GENERAL UNDIRECTED GRAPHS"
41586   * -- Tomihisa KAMADA and Satoru KAWAI in 1989
41587   *
41588   * Possible optimizations in the distance calculation can be implemented.
41589   */
41590
41591  var KamadaKawai = (function () {
41592    function KamadaKawai(body, edgeLength, edgeStrength) {
41593      _classCallCheck(this, KamadaKawai);
41594
41595      this.body = body;
41596      this.springLength = edgeLength;
41597      this.springConstant = edgeStrength;
41598      this.distanceSolver = new _componentsAlgorithmsFloydWarshallJs2["default"]();
41599    }
41600
41601    _createClass(KamadaKawai, [{
41602      key: "setOptions",
41603
41604      /**
41605       * Not sure if needed but can be used to update the spring length and spring constant
41606       * @param options
41607       */
41608      value: function setOptions(options) {
41609        if (options) {
41610          if (options.springLength) {
41611            this.springLength = options.springLength;
41612          }
41613          if (options.springConstant) {
41614            this.springConstant = options.springConstant;
41615          }
41616        }
41617      }
41618    }, {
41619      key: "solve",
41620
41621      /**
41622       * Position the system
41623       * @param nodesArray
41624       * @param edgesArray
41625       */
41626      value: function solve(nodesArray, edgesArray) {
41627        var ignoreClusters = arguments.length <= 2 || arguments[2] === undefined ? false : arguments[2];
41628
41629        // get distance matrix
41630        var D_matrix = this.distanceSolver.getDistances(this.body, nodesArray, edgesArray); // distance matrix
41631
41632        // get the L Matrix
41633        this._createL_matrix(D_matrix);
41634
41635        // get the K Matrix
41636        this._createK_matrix(D_matrix);
41637
41638        // calculate positions
41639        var threshold = 0.01;
41640        var innerThreshold = 1;
41641        var iterations = 0;
41642        var maxIterations = Math.max(1000, Math.min(10 * this.body.nodeIndices.length, 6000));
41643        var maxInnerIterations = 5;
41644
41645        var maxEnergy = 1e9;
41646        var highE_nodeId = 0,
41647            dE_dx = 0,
41648            dE_dy = 0,
41649            delta_m = 0,
41650            subIterations = 0;
41651
41652        while (maxEnergy > threshold && iterations < maxIterations) {
41653          iterations += 1;
41654
41655          var _getHighestEnergyNode2 = this._getHighestEnergyNode(ignoreClusters);
41656
41657          var _getHighestEnergyNode22 = _slicedToArray(_getHighestEnergyNode2, 4);
41658
41659          highE_nodeId = _getHighestEnergyNode22[0];
41660          maxEnergy = _getHighestEnergyNode22[1];
41661          dE_dx = _getHighestEnergyNode22[2];
41662          dE_dy = _getHighestEnergyNode22[3];
41663
41664          delta_m = maxEnergy;
41665          subIterations = 0;
41666          while (delta_m > innerThreshold && subIterations < maxInnerIterations) {
41667            subIterations += 1;
41668            this._moveNode(highE_nodeId, dE_dx, dE_dy);
41669
41670            var _getEnergy2 = this._getEnergy(highE_nodeId);
41671
41672            var _getEnergy22 = _slicedToArray(_getEnergy2, 3);
41673
41674            delta_m = _getEnergy22[0];
41675            dE_dx = _getEnergy22[1];
41676            dE_dy = _getEnergy22[2];
41677          }
41678        }
41679      }
41680    }, {
41681      key: "_getHighestEnergyNode",
41682
41683      /**
41684       * get the node with the highest energy
41685       * @returns {*[]}
41686       * @private
41687       */
41688      value: function _getHighestEnergyNode(ignoreClusters) {
41689        var nodesArray = this.body.nodeIndices;
41690        var nodes = this.body.nodes;
41691        var maxEnergy = 0;
41692        var maxEnergyNodeId = nodesArray[0];
41693        var dE_dx_max = 0,
41694            dE_dy_max = 0;
41695
41696        for (var nodeIdx = 0; nodeIdx < nodesArray.length; nodeIdx++) {
41697          var m = nodesArray[nodeIdx];
41698          // by not evaluating nodes with predefined positions we should only move nodes that have no positions.
41699          if (nodes[m].predefinedPosition === false || nodes[m].isCluster === true && ignoreClusters === true || nodes[m].options.fixed.x === true || nodes[m].options.fixed.y === true) {
41700            var _getEnergy3 = this._getEnergy(m);
41701
41702            var _getEnergy32 = _slicedToArray(_getEnergy3, 3);
41703
41704            var delta_m = _getEnergy32[0];
41705            var dE_dx = _getEnergy32[1];
41706            var dE_dy = _getEnergy32[2];
41707
41708            if (maxEnergy < delta_m) {
41709              maxEnergy = delta_m;
41710              maxEnergyNodeId = m;
41711              dE_dx_max = dE_dx;
41712              dE_dy_max = dE_dy;
41713            }
41714          }
41715        }
41716
41717        return [maxEnergyNodeId, maxEnergy, dE_dx_max, dE_dy_max];
41718      }
41719    }, {
41720      key: "_getEnergy",
41721
41722      /**
41723       * calculate the energy of a single node
41724       * @param m
41725       * @returns {*[]}
41726       * @private
41727       */
41728      value: function _getEnergy(m) {
41729        var nodesArray = this.body.nodeIndices;
41730        var nodes = this.body.nodes;
41731
41732        var x_m = nodes[m].x;
41733        var y_m = nodes[m].y;
41734        var dE_dx = 0;
41735        var dE_dy = 0;
41736        for (var iIdx = 0; iIdx < nodesArray.length; iIdx++) {
41737          var i = nodesArray[iIdx];
41738          if (i !== m) {
41739            var x_i = nodes[i].x;
41740            var y_i = nodes[i].y;
41741            var denominator = 1.0 / Math.sqrt(Math.pow(x_m - x_i, 2) + Math.pow(y_m - y_i, 2));
41742            dE_dx += this.K_matrix[m][i] * (x_m - x_i - this.L_matrix[m][i] * (x_m - x_i) * denominator);
41743            dE_dy += this.K_matrix[m][i] * (y_m - y_i - this.L_matrix[m][i] * (y_m - y_i) * denominator);
41744          }
41745        }
41746
41747        var delta_m = Math.sqrt(Math.pow(dE_dx, 2) + Math.pow(dE_dy, 2));
41748        return [delta_m, dE_dx, dE_dy];
41749      }
41750    }, {
41751      key: "_moveNode",
41752
41753      /**
41754       * move the node based on it's energy
41755       * the dx and dy are calculated from the linear system proposed by Kamada and Kawai
41756       * @param m
41757       * @param dE_dx
41758       * @param dE_dy
41759       * @private
41760       */
41761      value: function _moveNode(m, dE_dx, dE_dy) {
41762        var nodesArray = this.body.nodeIndices;
41763        var nodes = this.body.nodes;
41764        var d2E_dx2 = 0;
41765        var d2E_dxdy = 0;
41766        var d2E_dy2 = 0;
41767
41768        var x_m = nodes[m].x;
41769        var y_m = nodes[m].y;
41770        for (var iIdx = 0; iIdx < nodesArray.length; iIdx++) {
41771          var i = nodesArray[iIdx];
41772          if (i !== m) {
41773            var x_i = nodes[i].x;
41774            var y_i = nodes[i].y;
41775            var denominator = 1.0 / Math.pow(Math.pow(x_m - x_i, 2) + Math.pow(y_m - y_i, 2), 1.5);
41776            d2E_dx2 += this.K_matrix[m][i] * (1 - this.L_matrix[m][i] * Math.pow(y_m - y_i, 2) * denominator);
41777            d2E_dxdy += this.K_matrix[m][i] * (this.L_matrix[m][i] * (x_m - x_i) * (y_m - y_i) * denominator);
41778            d2E_dy2 += this.K_matrix[m][i] * (1 - this.L_matrix[m][i] * Math.pow(x_m - x_i, 2) * denominator);
41779          }
41780        }
41781        // make the variable names easier to make the solving of the linear system easier to read
41782        var A = d2E_dx2,
41783            B = d2E_dxdy,
41784            C = dE_dx,
41785            D = d2E_dy2,
41786            E = dE_dy;
41787
41788        // solve the linear system for dx and dy
41789        var dy = (C / A + E / B) / (B / A - D / B);
41790        var dx = -(B * dy + C) / A;
41791
41792        // move the node
41793        nodes[m].x += dx;
41794        nodes[m].y += dy;
41795      }
41796    }, {
41797      key: "_createL_matrix",
41798
41799      /**
41800       * Create the L matrix: edge length times shortest path
41801       * @param D_matrix
41802       * @private
41803       */
41804      value: function _createL_matrix(D_matrix) {
41805        var nodesArray = this.body.nodeIndices;
41806        var edgeLength = this.springLength;
41807
41808        this.L_matrix = [];
41809        for (var i = 0; i < nodesArray.length; i++) {
41810          this.L_matrix[nodesArray[i]] = {};
41811          for (var j = 0; j < nodesArray.length; j++) {
41812            this.L_matrix[nodesArray[i]][nodesArray[j]] = edgeLength * D_matrix[nodesArray[i]][nodesArray[j]];
41813          }
41814        }
41815      }
41816    }, {
41817      key: "_createK_matrix",
41818
41819      /**
41820       * Create the K matrix: spring constants times shortest path
41821       * @param D_matrix
41822       * @private
41823       */
41824      value: function _createK_matrix(D_matrix) {
41825        var nodesArray = this.body.nodeIndices;
41826        var edgeStrength = this.springConstant;
41827
41828        this.K_matrix = [];
41829        for (var i = 0; i < nodesArray.length; i++) {
41830          this.K_matrix[nodesArray[i]] = {};
41831          for (var j = 0; j < nodesArray.length; j++) {
41832            this.K_matrix[nodesArray[i]][nodesArray[j]] = edgeStrength * Math.pow(D_matrix[nodesArray[i]][nodesArray[j]], -2);
41833          }
41834        }
41835      }
41836    }]);
41837
41838    return KamadaKawai;
41839  })();
41840
41841  exports["default"] = KamadaKawai;
41842  module.exports = exports["default"];
41843
41844/***/ },
41845/* 113 */
41846/***/ function(module, exports) {
41847
41848  /**
41849   * Created by Alex on 10-Aug-15.
41850   */
41851
41852  "use strict";
41853
41854  Object.defineProperty(exports, "__esModule", {
41855    value: true
41856  });
41857
41858  var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
41859
41860  function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
41861
41862  var FloydWarshall = (function () {
41863    function FloydWarshall() {
41864      _classCallCheck(this, FloydWarshall);
41865    }
41866
41867    _createClass(FloydWarshall, [{
41868      key: "getDistances",
41869      value: function getDistances(body, nodesArray, edgesArray) {
41870        var D_matrix = {};
41871        var edges = body.edges;
41872
41873        // prepare matrix with large numbers
41874        for (var i = 0; i < nodesArray.length; i++) {
41875          D_matrix[nodesArray[i]] = {};
41876          D_matrix[nodesArray[i]] = {};
41877          for (var j = 0; j < nodesArray.length; j++) {
41878            D_matrix[nodesArray[i]][nodesArray[j]] = i == j ? 0 : 1e9;
41879            D_matrix[nodesArray[i]][nodesArray[j]] = i == j ? 0 : 1e9;
41880          }
41881        }
41882
41883        // put the weights for the edges in. This assumes unidirectionality.
41884        for (var i = 0; i < edgesArray.length; i++) {
41885          var edge = edges[edgesArray[i]];
41886          if (edge.connected === true) {
41887            // edge has to be connected if it counts to the distances.
41888            D_matrix[edge.fromId][edge.toId] = 1;
41889            D_matrix[edge.toId][edge.fromId] = 1;
41890          }
41891        }
41892
41893        var nodeCount = nodesArray.length;
41894
41895        // Adapted FloydWarshall based on unidirectionality to greatly reduce complexity.
41896        for (var k = 0; k < nodeCount;
41896 k++) {
41897          for (var i = 0; i < nodeCount - 1; i++) {
41898            for (var j = i + 1; j < nodeCount; j++) {
41899              D_matrix[nodesArray[i]][nodesArray[j]] = Math.min(D_matrix[nodesArray[i]][nodesArray[j]], D_matrix[nodesArray[i]][nodesArray[k]] + D_matrix[nodesArray[k]][nodesArray[j]]);
41900              D_matrix[nodesArray[j]][nodesArray[i]] = D_matrix[nodesArray[i]][nodesArray[j]];
41901            }
41902          }
41903        }
41904
41905        return D_matrix;
41906      }
41907    }]);
41908
41909    return FloydWarshall;
41910  })();
41911
41912  exports["default"] = FloydWarshall;
41913  module.exports = exports["default"];
41914
41915/***/ },
41916/* 114 */
41917/***/ function(module, exports) {
41918
41919  /**
41920   * Canvas shapes used by Network
41921   */
41922  'use strict';
41923
41924  if (typeof CanvasRenderingContext2D !== 'undefined') {
41925
41926    /**
41927     * Draw a circle shape
41928     */
41929    CanvasRenderingContext2D.prototype.circle = function (x, y, r) {
41930      this.beginPath();
41931      this.arc(x, y, r, 0, 2 * Math.PI, false);
41932      this.closePath();
41933    };
41934
41935    /**
41936     * Draw a square shape
41937     * @param {Number} x horizontal center
41938     * @param {Number} y vertical center
41939     * @param {Number} r   size, width and height of the square
41940     */
41941    CanvasRenderingContext2D.prototype.square = function (x, y, r) {
41942      this.beginPath();
41943      this.rect(x - r, y - r, r * 2, r * 2);
41944      this.closePath();
41945    };
41946
41947    /**
41948     * Draw a triangle shape
41949     * @param {Number} x horizontal center
41950     * @param {Number} y vertical center
41951     * @param {Number} r   radius, half the length of the sides of the triangle
41952     */
41953    CanvasRenderingContext2D.prototype.triangle = function (x, y, r) {
41954      // http://en.wikipedia.org/wiki/Equilateral_triangle
41955      this.beginPath();
41956
41957      // the change in radius and the offset is here to center the shape
41958      r *= 1.15;
41959      y += 0.275 * r;
41960
41961      var s = r * 2;
41962      var s2 = s / 2;
41963      var ir = Math.sqrt(3) / 6 * s; // radius of inner circle
41964      var h = Math.sqrt(s * s - s2 * s2); // height
41965
41966      this.moveTo(x, y - (h - ir));
41967      this.lineTo(x + s2, y + ir);
41968      this.lineTo(x - s2, y + ir);
41969      this.lineTo(x, y - (h - ir));
41970      this.closePath();
41971    };
41972
41973    /**
41974     * Draw a triangle shape in downward orientation
41975     * @param {Number} x horizontal center
41976     * @param {Number} y vertical center
41977     * @param {Number} r radius
41978     */
41979    CanvasRenderingContext2D.prototype.triangleDown = function (x, y, r) {
41980      // http://en.wikipedia.org/wiki/Equilateral_triangle
41981      this.beginPath();
41982
41983      // the change in radius and the offset is here to center the shape
41984      r *= 1.15;
41985      y -= 0.275 * r;
41986
41987      var s = r * 2;
41988      var s2 = s / 2;
41989      var ir = Math.sqrt(3) / 6 * s; // radius of inner circle
41990      var h = Math.sqrt(s * s - s2 * s2); // height
41991
41992      this.moveTo(x, y + (h - ir));
41993      this.lineTo(x + s2, y - ir);
41994      this.lineTo(x - s2, y - ir);
41995      this.lineTo(x, y + (h - ir));
41996      this.closePath();
41997    };
41998
41999    /**
42000     * Draw a star shape, a star with 5 points
42001     * @param {Number} x horizontal center
42002     * @param {Number} y vertical center
42003     * @param {Number} r   radius, half the length of the sides of the triangle
42004     */
42005    CanvasRenderingContext2D.prototype.star = function (x, y, r) {
42006      // http://www.html5canvastutorials.com/labs/html5-canvas-star-spinner/
42007      this.beginPath();
42008
42009      // the change in radius and the offset is here to center the shape
42010      r *= 0.82;
42011      y += 0.1 * r;
42012
42013      for (var n = 0; n < 10; n++) {
42014        var radius = n % 2 === 0 ? r * 1.3 : r * 0.5;
42015        this.lineTo(x + radius * Math.sin(n * 2 * Math.PI / 10), y - radius * Math.cos(n * 2 * Math.PI / 10));
42016      }
42017
42018      this.closePath();
42019    };
42020
42021    /**
42022     * Draw a Diamond shape
42023     * @param {Number} x horizontal center
42024     * @param {Number} y vertical center
42025     * @param {Number} r   radius, half the length of the sides of the triangle
42026     */
42027    CanvasRenderingContext2D.prototype.diamond = function (x, y, r) {
42028      // http://www.html5canvastutorials.com/labs/html5-canvas-star-spinner/
42029      this.beginPath();
42030
42031      this.lineTo(x, y + r);
42032      this.lineTo(x + r, y);
42033      this.lineTo(x, y - r);
42034      this.lineTo(x - r, y);
42035
42036      this.closePath();
42037    };
42038
42039    /**
42040     * http://stackoverflow.com/questions/1255512/how-to-draw-a-rounded-rectangle-on-html-canvas
42041     */
42042    CanvasRenderingContext2D.prototype.roundRect = function (x, y, w, h, r) {
42043      var r2d = Math.PI / 180;
42044      if (w - 2 * r < 0) {
42045        r = w / 2;
42046      } //ensure that the radius isn't too large for x
42047      if (h - 2 * r < 0) {
42048        r = h / 2;
42049      } //ensure that the radius isn't too large for y
42050      this.beginPath();
42051      this.moveTo(x + r, y);
42052      this.lineTo(x + w - r, y);
42053      this.arc(x + w - r, y + r, r, r2d * 270, r2d * 360, false);
42054      this.lineTo(x + w, y + h - r);
42055      this.arc(x + w - r, y + h - r, r, 0, r2d * 90, false);
42056      this.lineTo(x + r, y + h);
42057      this.arc(x + r, y + h - r, r, r2d * 90, r2d * 180, false);
42058      this.lineTo(x, y + r);
42059      this.arc(x + r, y + r, r, r2d * 180, r2d * 270, false);
42060      this.closePath();
42061    };
42062
42063    /**
42064     * http://stackoverflow.com/questions/2172798/how-to-draw-an-oval-in-html5-canvas
42065     */
42066    CanvasRenderingContext2D.prototype.ellipse = function (x, y, w, h) {
42067      var kappa = .5522848,
42068          ox = w / 2 * kappa,
42069          // control point offset horizontal
42070      oy = h / 2 * kappa,
42071          // control point offset vertical
42072      xe = x + w,
42073          // x-end
42074      ye = y + h,
42075          // y-end
42076      xm = x + w / 2,
42077          // x-middle
42078      ym = y + h / 2; // y-middle
42079
42080      this.beginPath();
42081      this.moveTo(x, ym);
42082      this.bezierCurveTo(x, ym - oy, xm - ox, y, xm, y);
42083      this.bezierCurveTo(xm + ox, y, xe, ym - oy, xe, ym);
42084      this.bezierCurveTo(xe, ym + oy, xm + ox, ye, xm, ye);
42085      this.bezierCurveTo(xm - ox, ye, x, ym + oy, x, ym);
42086      this.closePath();
42087    };
42088
42089    /**
42090     * http://stackoverflow.com/questions/2172798/how-to-draw-an-oval-in-html5-canvas
42091     */
42092    CanvasRenderingContext2D.prototype.database = function (x, y, w, h) {
42093      var f = 1 / 3;
42094      var wEllipse = w;
42095      var hEllipse = h * f;
42096
42097      var kappa = .5522848,
42098          ox = wEllipse / 2 * kappa,
42099          // control point offset horizontal
42100      oy = hEllipse / 2 * kappa,
42101          // control point offset vertical
42102      xe = x + wEllipse,
42103          // x-end
42104      ye = y + hEllipse,
42105          // y-end
42106      xm = x + wEllipse / 2,
42107          // x-middle
42108      ym = y + hEllipse / 2,
42109          // y-middle
42110      ymb = y + (h - hEllipse / 2),
42111          // y-midlle, bottom ellipse
42112      yeb = y + h; // y-end, bottom ellipse
42113
42114      this.beginPath();
42115      this.moveTo(xe, ym);
42116
42117      this.bezierCurveTo(xe, ym + oy, xm + ox, ye, xm, ye);
42118      this.bezierCurveTo(xm - ox, ye, x, ym + oy, x, ym);
42119
42120      this.bezierCurveTo(x, ym - oy, xm - ox, y, xm, y);
42121      this.bezierCurveTo(xm + ox, y, xe, ym - oy, xe, ym);
42122
42123      this.lineTo(xe, ymb);
42124
42125      this.bezierCurveTo(xe, ymb + oy, xm + ox, yeb, xm, yeb);
42126      this.bezierCurveTo(xm - ox, yeb, x, ymb + oy, x, ymb);
42127
42128      this.lineTo(x, ym);
42129    };
42130
42131    /**
42132     * Draw an arrow point (no line)
42133     */
42134    CanvasRenderingContext2D.prototype.arrow = function (x, y, angle, length) {
42135      // tail
42136      var xt = x - length * Math.cos(angle);
42137      var yt = y - length * Math.sin(angle);
42138
42139      // inner tail
42140      var xi = x - length * 0.9 * Math.cos(angle);
42141      var yi = y - length * 0.9 * Math.sin(angle);
42142
42143      // left
42144      var xl = xt + length / 3 * Math.cos(angle + 0.5 * Math.PI);
42145      var yl = yt + length / 3 * Math.sin(angle + 0.5 * Math.PI);
42146
42147      // right
42148      var xr = xt + length / 3 * Math.cos(angle - 0.5 * Math.PI);
42149      var yr = yt + length / 3 * Math.sin(angle - 0.5 * Math.PI);
42150
42151      this.beginPath();
42152      this.moveTo(x, y);
42153      this.lineTo(xl, yl);
42154      this.lineTo(xi, yi);
42155      this.lineTo(xr, yr);
42156      this.closePath();
42157    };
42158
42159    /**
42160     * Sets up the dashedLine functionality for drawing
42161     * Original code came from http://stackoverflow.com/questions/4576724/dotted-stroke-in-canvas
42162     * @author David Jordan
42163     * @date 2012-08-08
42164     */
42165    CanvasRenderingContext2D.prototype.dashedLine = function (x, y, x2, y2, pattern) {
42166      this.beginPath();
42167      this.moveTo(x, y);
42168
42169      var patternLength = pattern.length;
42170      var dx = x2 - x;
42171      var dy = y2 - y;
42172      var slope = dy / dx;
42173      var distRemaining = Math.sqrt(dx * dx + dy * dy);
42174      var patternIndex = 0;
42175      var draw = true;
42176      var xStep = 0;
42177      var dashLength = pattern[0];
42178
42179      while (distRemaining >= 0.1) {
42180        dashLength = pattern[patternIndex++ % patternLength];
42181        if (dashLength > distRemaining) {
42182          dashLength = distRemaining;
42183        }
42184
42185        xStep = Math.sqrt(dashLength * dashLength / (1 + slope * slope));
42186        xStep = dx < 0 ? -xStep : xStep;
42187        x += xStep;
42188        y += slope * xStep;
42189
42190        if (draw === true) {
42191          this.lineTo(x, y);
42192        } else {
42193          this.moveTo(x, y);
42194        }
42195
42196        distRemaining -= dashLength;
42197        draw = !draw;
42198      }
42199    };
42200  }
42201
42202/***/ },
42203/* 115 */
42204/***/ function(module, exports) {
42205
42206  /**
42207   * Parse a text source containing data in DOT language into a JSON object.
42208   * The object contains two lists: one with nodes and one with edges.
42209   *
42210   * DOT language reference: http://www.graphviz.org/doc/info/lang.html
42211   *
42212   * DOT language attributes: http://graphviz.org/content/attrs
42213   *
42214   * @param {String} data     Text containing a graph in DOT-notation
42215   * @return {Object} graph   An object containing two parameters:
42216   *                          {Object[]} nodes
42217   *                          {Object[]} edges
42218   */
42219  'use strict';
42220
42221  function parseDOT(data) {
42222    dot = data;
42223    return parseGraph();
42224  }
42225
42226  // mapping of attributes from DOT (the keys) to vis.js (the values)
42227  var NODE_ATTR_MAPPING = {
42228    'fontsize': 'font.size',
42229    'fontcolor': 'font.color',
42230    'labelfontcolor': 'font.color',
42231    'fontname': 'font.face',
42232    'color': ['color.border', 'color.background'],
42233    'fillcolor': 'color.background',
42234    'tooltip': 'title',
42235    'labeltooltip': 'title'
42236  };
42237  var EDGE_ATTR_MAPPING = Object.create(NODE_ATTR_MAPPING);
42238  EDGE_ATTR_MAPPING.color = 'color.color';
42239
42240  // token types enumeration
42241  var TOKENTYPE = {
42242    NULL: 0,
42243    DELIMITER: 1,
42244    IDENTIFIER: 2,
42245    UNKNOWN: 3
42246  };
42247
42248  // map with all delimiters
42249  var DELIMITERS = {
42250    '{': true,
42251    '}': true,
42252    '[': true,
42253    ']': true,
42254    ';': true,
42255    '=': true,
42256    ',': true,
42257
42258    '->': true,
42259    '--': true
42260  };
42261
42262  var dot = ''; // current dot file
42263  var index = 0; // current index in dot file
42264  var c = ''; // current token character in expr
42265  var token = ''; // current token
42266  var tokenType = TOKENTYPE.NULL; // type of the token
42267
42268  /**
42269   * Get the first character from the dot file.
42270   * The character is stored into the char c. If the end of the dot file is
42271   * reached, the function puts an empty string in c.
42272   */
42273  function first() {
42274    index = 0;
42275    c = dot.charAt(0);
42276  }
42277
42278  /**
42279   * Get the next character from the dot file.
42280   * The character is stored into the char c. If the end of the dot file is
42281   * reached, the function puts an empty string in c.
42282   */
42283  function next() {
42284    index++;
42285    c = dot.charAt(index);
42286  }
42287
42288  /**
42289   * Preview the next character from the dot file.
42290   * @return {String} cNext
42291   */
42292  function nextPreview() {
42293    return dot.charAt(index + 1);
42294  }
42295
42296  /**
42297   * Test whether given character is alphabetic or numeric
42298   * @param {String} c
42299   * @return {Boolean} isAlphaNumeric
42300   */
42301  var regexAlphaNumeric = /[a-zA-Z_0-9.:#]/;
42302  function isAlphaNumeric(c) {
42303    return regexAlphaNumeric.test(c);
42304  }
42305
42306  /**
42307   * Merge all options of object b into object b
42308   * @param {Object} a
42309   * @param {Object} b
42310   * @return {Object} a
42311   */
42312  function merge(a, b) {
42313    if (!a) {
42314      a = {};
42315    }
42316
42317    if (b) {
42318      for (var name in b) {
42319        if (b.hasOwnProperty(name)) {
42320          a[name] = b[name];
42321        }
42322      }
42323    }
42324    return a;
42325  }
42326
42327  /**
42328   * Set a value in an object, where the provided parameter name can be a
42329   * path with nested parameters. For example:
42330   *
42331   *     var obj = {a: 2};
42332   *     setValue(obj, 'b.c', 3);     // obj = {a: 2, b: {c: 3}}
42333   *
42334   * @param {Object} obj
42335   * @param {String} path  A parameter name or dot-separated parameter path,
42336   *                      like "color.highlight.border".
42337   * @param {*} value
42338   */
42339  function setValue(obj, path, value) {
42340    var keys = path.split('.');
42341    var o = obj;
42342    while (keys.length) {
42343      var key = keys.shift();
42344      if (keys.length) {
42345        // this isn't the end point
42346        if (!o[key]) {
42347          o[key] = {};
42348        }
42349        o = o[key];
42350      } else {
42351        // this is the end point
42352        o[key] = value;
42353      }
42354    }
42355  }
42356
42357  /**
42358   * Add a node to a graph object. If there is already a node with
42359   * the same id, their attributes will be merged.
42360   * @param {Object} graph
42361   * @param {Object} node
42362   */
42363  function addNode(graph, node) {
42364    var i, len;
42365    var current = null;
42366
42367    // find root graph (in case of subgraph)
42368    var graphs = [graph]; // list with all graphs from current graph to root graph
42369    var root = graph;
42370    while (root.parent) {
42371      graphs.push(root.parent);
42372      root = root.parent;
42373    }
42374
42375    // find existing node (at root level) by its id
42376    if (root.nodes) {
42377      for (i = 0, len = root.nodes.length; i < len; i++) {
42378        if (node.id === root.nodes[i].id) {
42379          current = root.nodes[i];
42380          break;
42381        }
42382      }
42383    }
42384
42385    if (!current) {
42386      // this is a new node
42387      current = {
42388        id: node.id
42389      };
42390      if (graph.node) {
42391        // clone default attributes
42392        current.attr = merge(current.attr, graph.node);
42393      }
42394    }
42395
42396    // add node to this (sub)graph and all its parent graphs
42397    for (i = graphs.length - 1; i >= 0; i--) {
42398      var g = graphs[i];
42399
42400      if (!g.nodes) {
42401        g.nodes = [];
42402      }
42403      if (g.nodes.indexOf(current) === -1) {
42404        g.nodes.push(current);
42405      }
42406    }
42407
42408    // merge attributes
42409    if (node.attr) {
42410      current.attr = merge(current.attr, node.attr);
42411    }
42412  }
42413
42414  /**
42415   * Add an edge to a graph object
42416   * @param {Object} graph
42417   * @param {Object} edge
42418   */
42419  function addEdge(graph, edge) {
42420    if (!graph.edges) {
42421      graph.edges = [];
42422    }
42423    graph.edges.push(edge);
42424    if (graph.edge) {
42425      var attr = merge({}, graph.edge); // clone default attributes
42426      edge.attr = merge(attr, edge.attr); // merge attributes
42427    }
42428  }
42429
42430  /**
42431   * Create an edge to a graph object
42432   * @param {Object} graph
42433   * @param {String | Number | Object} from
42434   * @param {String | Number | Object} to
42435   * @param {String} type
42436   * @param {Object | null} attr
42437   * @return {Object} edge
42438   */
42439  function createEdge(graph, from, to, type, attr) {
42440    var edge = {
42441      from: from,
42442      to: to,
42443      type: type
42444    };
42445
42446    if (graph.edge) {
42447      edge.attr = merge({}, graph.edge); // clone default attributes
42448    }
42449    edge.attr = merge(edge.attr || {}, attr); // merge attributes
42450
42451    return edge;
42452  }
42453
42454  /**
42455   * Get next token in the current dot file.
42456   * The token and token type are available as token and tokenType
42457   */
42458  function getToken() {
42459    tokenType = TOKENTYPE.NULL;
42460    token = '';
42461
42462    // skip over whitespaces
42463    while (c === ' ' || c === '\t' || c === '\n' || c === '\r') {
42464      // space, tab, enter
42465      next();
42466    }
42467
42468    do {
42469      var isComment = false;
42470
42471      // skip comment
42472      if (c === '#') {
42473        // find the previous non-space character
42474        var i = index - 1;
42475        while (dot.charAt(i) === ' ' || dot.charAt(i) === '\t') {
42476          i--;
42477        }
42478        if (dot.charAt(i) === '\n' || dot.charAt(i) === '') {
42479          // the # is at the start of a line, this is indeed a line comment
42480          while (c != '' && c != '\n') {
42481            next();
42482          }
42483          isComment = true;
42484        }
42485      }
42486      if (c === '/' && nextPreview() === '/') {
42487        // skip line comment
42488        while (c != '' && c != '\n') {
42489          next();
42490        }
42491        isComment = true;
42492      }
42493      if (c === '/' && nextPreview() === '*') {
42494        // skip block comment
42495        while (c != '') {
42496          if (c === '*' && nextPreview() === '/') {
42497            // end of block comment found. skip these last two characters
42498            next();
42499            next();
42500            break;
42501          } else {
42502            next();
42503          }
42504        }
42505        isComment = true;
42506      }
42507
42508      // skip over whitespaces
42509      while (c === ' ' || c === '\t' || c === '\n' || c === '\r') {
42510        // space, tab, enter
42511        next();
42512      }
42513    } while (isComment);
42514
42515    // check for end of dot file
42516    if (c === '') {
42517      // token is still empty
42518      tokenType = TOKENTYPE.DELIMITER;
42519      return;
42520    }
42521
42522    // check for delimiters consisting of 2 characters
42523    var c2 = c + nextPreview();
42524    if (DELIMITERS[c2]) {
42525      tokenType = TOKENTYPE.DELIMITER;
42526      token = c2;
42527      next();
42528      next();
42529      return;
42530    }
42531
42532    // check for delimiters consisting of 1 character
42533    if (DELIMITERS[c]) {
42534      tokenType = TOKENTYPE.DELIMITER;
42535      token = c;
42536      next();
42537      return;
42538    }
42539
42540    // check for an identifier (number or string)
42541    // TODO: more precise parsing of numbers/strings (and the port separator ':')
42542    if (isAlphaNumeric(c) || c === '-') {
42543      token += c;
42544      next();
42545
42546      while (isAlphaNumeric(c)) {
42547        token += c;
42548        next();
42549      }
42550      if (token === 'false') {
42551        token = false; // convert to boolean
42552      } else if (token === 'true') {
42553        token = true; // convert to boolean
42554      } else if (!isNaN(Number(token))) {
42555        token = Number(token); // convert to number
42556      }
42557      tokenType = TOKENTYPE.IDENTIFIER;
42558      return;
42559    }
42560
42561    // check for a string enclosed by double quotes
42562    if (c === '"') {
42563      next();
42564      while (c != '' && (c != '"' || c === '"' && nextPreview() === '"')) {
42565        token += c;
42566        if (c === '"') {
42567          // skip the escape character
42568          next();
42569        }
42570        next();
42571      }
42572      if (c != '"') {
42573        throw newSyntaxError('End of string " expected');
42574      }
42575      next();
42576      tokenType = TOKENTYPE.IDENTIFIER;
42577      return;
42578    }
42579
42580    // something unknown is found, wrong characters, a syntax error
42581    tokenType = TOKENTYPE.UNKNOWN;
42582    while (c != '') {
42583      token += c;
42584      next();
42585    }
42586    throw new SyntaxError('Syntax error in part "' + chop(token, 30) + '"');
42587  }
42588
42589  /**
42590   * Parse a graph.
42591   * @returns {Object} graph
42592   */
42593  function parseGraph() {
42594    var graph = {};
42595
42596    first();
42597    getToken();
42598
42599    // optional strict keyword
42600    if (token === 'strict') {
42601      graph.strict = true;
42602      getToken();
42603    }
42604
42605    // graph or digraph keyword
42606    if (token === 'graph' || token === 'digraph') {
42607      graph.type = token;
42608      getToken();
42609    }
42610
42611    // optional graph id
42612    if (tokenType === TOKENTYPE.IDENTIFIER) {
42613      graph.id = token;
42614      getToken();
42615    }
42616
42617    // open angle bracket
42618    if (token != '{') {
42619      throw newSyntaxError('Angle bracket { expected');
42620    }
42621    getToken();
42622
42623    // statements
42624    parseStatements(graph);
42625
42626    // close angle bracket
42627    if (token != '}') {
42628      throw newSyntaxError('Angle bracket } expected');
42629    }
42630    getToken();
42631
42632    // end of file
42633    if (token !== '') {
42634      throw newSyntaxError('End of file expected');
42635    }
42636    getToken();
42637
42638    // remove temporary default options
42639    delete graph.node;
42640    delete graph.edge;
42641    delete graph.graph;
42642
42643    return graph;
42644  }
42645
42646  /**
42647   * Parse a list with statements.
42648   * @param {Object} graph
42649   */
42650  function parseStatements(graph) {
42651    while (token !== '' && token != '}') {
42652      parseStatement(graph);
42653      if (token === ';') {
42654        getToken();
42655      }
42656    }
42657  }
42658
42659  /**
42660   * Parse a single statement. Can be a an attribute statement, node
42661   * statement, a series of node statements and edge statements, or a
42662   * parameter.
42663   * @param {Object} graph
42664   */
42665  function parseStatement(graph) {
42666    // parse subgraph
42667    var subgraph = parseSubgraph(graph);
42668    if (subgraph) {
42669      // edge statements
42670      parseEdge(graph, subgraph);
42671
42672      return;
42673    }
42674
42675    // parse an attribute statement
42676    var attr = parseAttributeStatement(graph);
42677    if (attr) {
42678      return;
42679    }
42680
42681    // parse node
42682    if (tokenType != TOKENTYPE.IDENTIFIER) {
42683      throw newSyntaxError('Identifier expected');
42684    }
42685    var id = token; // id can be a string or a number
42686    getToken();
42687
42688    if (token === '=') {
42689      // id statement
42690      getToken();
42691      if (tokenType != TOKENTYPE.IDENTIFIER) {
42692        throw newSyntaxError('Identifier expected');
42693      }
42694      graph[id] = token;
42695      getToken();
42696      // TODO: implement comma separated list with "a_list: ID=ID [','] [a_list] "
42697    } else {
42698      parseNodeStatement(graph, id);
42699    }
42700  }
42701
42702  /**
42703   * Parse a subgraph
42704   * @param {Object} graph    parent graph object
42705   * @return {Object | null} subgraph
42706   */
42707  function parseSubgraph(graph) {
42708    var subgraph = null;
42709
42710    // optional subgraph keyword
42711    if (token === 'subgraph') {
42712      subgraph = {};
42713      subgraph.type = 'subgraph';
42714      getToken();
42715
42716      // optional graph id
42717      if (tokenType === TOKENTYPE.IDENTIFIER) {
42718        subgraph.id = token;
42719        getToken();
42720      }
42721    }
42722
42723    // open angle bracket
42724    if (token === '{') {
42725      getToken();
42726
42727      if (!subgraph) {
42728        subgraph = {};
42729      }
42730      subgraph.parent = graph;
42731      subgraph.node = graph.node;
42732      subgraph.edge = graph.edge;
42733      subgraph.graph = graph.graph;
42734
42735      // statements
42736      parseStatements(subgraph);
42737
42738      // close angle bracket
42739      if (token != '}') {
42740        throw newSyntaxError('Angle bracket } expected');
42741      }
42742      getToken();
42743
42744      // remove temporary default options
42745      delete subgraph.node;
42746      delete subgraph.edge;
42747      delete subgraph.graph;
42748      delete subgraph.parent;
42749
42750      // register at the parent graph
42751      if (!graph.subgraphs) {
42752        graph.subgraphs = [];
42753      }
42754      graph.subgraphs.push(subgraph);
42755    }
42756
42757    return subgraph;
42758  }
42759
42760  /**
42761   * parse an attribute statement like "node [shape=circle fontSize=16]".
42762   * Available keywords are 'node', 'edge', 'graph'.
42763   * The previous list with default attributes will be replaced
42764   * @param {Object} graph
42765   * @returns {String | null} keyword Returns the name of the parsed attribute
42766   *                                  (node, edge, graph), or null if nothing
42767   *                                  is parsed.
42768   */
42769  function parseAttributeStatement(graph) {
42770    // attribute statements
42771    if (token === 'node') {
42772      getToken();
42773
42774      // node attributes
42775      graph.node = parseAttributeList();
42776      return 'node';
42777    } else if (token === 'edge') {
42778      getToken();
42779
42780      // edge attributes
42781      graph.edge = parseAttributeList();
42782      return 'edge';
42783    } else if (token === 'graph') {
42784      getToken();
42785
42786      // graph attributes
42787      graph.graph = parseAttributeList();
42788      return 'graph';
42789    }
42790
42791    return null;
42792  }
42793
42794  /**
42795   * parse a node statement
42796   * @param {Object} graph
42797   * @param {String | Number} id
42798   */
42799  function parseNodeStatement(graph, id) {
42800    // node statement
42801    var node = {
42802      id: id
42803    };
42804    var attr = parseAttributeList();
42805    if (attr) {
42806      node.attr = attr;
42807    }
42808    addNode(graph, node);
42809
42810    // edge statements
42811    parseEdge(graph, id);
42812  }
42813
42814  /**
42815   * Parse an edge or a series of edges
42816   * @param {Object} graph
42817   * @param {String | Number} from        Id of the from node
42818   */
42819  function parseEdge(graph, from) {
42820    while (token === '->' || token === '--') {
42821      var to;
42822      var type = token;
42823      getToken();
42824
42825      var subgraph = parseSubgraph(graph);
42826      if (subgraph) {
42827        to = subgraph;
42828      } else {
42829        if (tokenType != TOKENTYPE.IDENTIFIER) {
42830          throw newSyntaxError('Identifier or subgraph expected');
42831        }
42832        to = token;
42833        addNode(graph, {
42834          id: to
42835        });
42836        getToken();
42837      }
42838
42839      // parse edge attributes
42840      var attr = parseAttributeList();
42841
42842      // create edge
42843      var edge = createEdge(graph, from, to, type, attr);
42844      addEdge(graph, edge);
42845
42846      from = to;
42847    }
42848  }
42849
42850  /**
42851   * Parse a set with attributes,
42852   * for example [label="1.000", shape=solid]
42853   * @return {Object | null} attr
42854   */
42855  function parseAttributeList() {
42856    var attr = null;
42857
42858    while (token === '[') {
42859      getToken();
42860      attr = {};
42861      while (token !== '' && token != ']') {
42862        if (tokenType != TOKENTYPE.IDENTIFIER) {
42863          throw newSyntaxError('Attribute name expected');
42864        }
42865        var name = token;
42866
42867        getToken();
42868        if (token != '=') {
42869          throw newSyntaxError('Equal sign = expected');
42870        }
42871        getToken();
42872
42873        if (tokenType != TOKENTYPE.IDENTIFIER) {
42874          throw newSyntaxError('Attribute value expected');
42875        }
42876        var value = token;
42877        setValue(attr, name, value); // name can be a path
42878
42879        getToken();
42880        if (token == ',') {
42881          getToken();
42882        }
42883      }
42884
42885      if (token != ']') {
42886        throw newSyntaxError('Bracket ] expected');
42887      }
42888      getToken();
42889    }
42890
42891    return attr;
42892  }
42893
42894  /**
42895   * Create a syntax error with extra information on current token and index.
42896   * @param {String} message
42897   * @returns {SyntaxError} err
42898   */
42899  function newSyntaxError(message) {
42900    return new SyntaxError(message + ', got "' + chop(token, 30) + '" (char ' + index + ')');
42901  }
42902
42903  /**
42904   * Chop off text after a maximum length
42905   * @param {String} text
42906   * @param {Number} maxLength
42907   * @returns {String}
42908   */
42909  function chop(text, maxLength) {
42910    return text.length <= maxLength ? text : text.substr(0, 27) + '...';
42911  }
42912
42913  /**
42914   * Execute a function fn for each pair of elements in two arrays
42915   * @param {Array | *} array1
42916   * @param {Array | *} array2
42917   * @param {function} fn
42918   */
42919  function forEach2(array1, array2, fn) {
42920    if (Array.isArray(array1)) {
42921      array1.forEach(function (elem1) {
42922        if (Array.isArray(array2)) {
42923          array2.forEach(function (elem2) {
42924            fn(elem1, elem2);
42925          });
42926        } else {
42927          fn(elem1, array2);
42928        }
42929      });
42930    } else {
42931      if (Array.isArray(array2)) {
42932        array2.forEach(function (elem2) {
42933          fn(array1, elem2);
42934        });
42935      } else {
42936        fn(array1, array2);
42937      }
42938    }
42939  }
42940
42941  /**
42942   * Set a nested property on an object
42943   * When nested objects are missing, they will be created.
42944   * For example setProp({}, 'font.color', 'red') will return {font: {color: 'red'}}
42945   * @param {Object} object
42946   * @param {string} path   A dot separated string like 'font.color'
42947   * @param {*} value       Value for the property
42948   * @return {Object} Returns the original object, allows for chaining.
42949   */
42950  function setProp(object, path, value) {
42951    var names = path.split('.');
42952    var prop = names.pop();
42953
42954    // traverse over the nested objects
42955    var obj = object;
42956    for (var i = 0; i < names.length; i++) {
42957      var name = names[i];
42958      if (!(name in obj)) {
42959        obj[name] = {};
42960      }
42961      obj = obj[name];
42962    }
42963
42964    // set the property value
42965    obj[prop] = value;
42966
42967    return object;
42968  }
42969
42970  /**
42971   * Convert an object with DOT attributes to their vis.js equivalents.
42972   * @param {Object} attr     Object with DOT attributes
42973   * @param {Object} mapping
42974   * @return {Object}         Returns an object with vis.js attributes
42975   */
42976  function convertAttr(attr, mapping) {
42977    var converted = {};
42978
42979    for (var prop in attr) {
42980      if (attr.hasOwnProperty(prop)) {
42981        var visProp = mapping[prop];
42982        if (Array.isArray(visProp)) {
42983          visProp.forEach(function (visPropI) {
42984            setProp(converted, visPropI, attr[prop]);
42985          });
42986        } else if (typeof visProp === 'string') {
42987          setProp(converted, visProp, attr[prop]);
42988        } else {
42989          setProp(converted, prop, attr[prop]);
42990        }
42991      }
42992    }
42993
42994    return converted;
42995  }
42996
42997  /**
42998   * Convert a string containing a graph in DOT language into a map containing
42999   * with nodes and edges in the format of graph.
43000   * @param {String} data         Text containing a graph in DOT-notation
43001   * @return {Object} graphData
43002   */
43003  function DOTToGraph(data) {
43004    // parse the DOT file
43005    var dotData = parseDOT(data);
43006    var graphData = {
43007      nodes: [],
43008      edges: [],
43009      options: {}
43010    };
43011
43012    // copy the nodes
43013    if (dotData.nodes) {
43014      dotData.nodes.forEach(function (dotNode) {
43015        var graphNode = {
43016          id: dotNode.id,
43017          label: String(dotNode.label || dotNode.id)
43018        };
43019        merge(graphNode, convertAttr(dotNode.attr, NODE_ATTR_MAPPING));
43020        if (graphNode.image) {
43021          graphNode.shape = 'image';
43022        }
43023        graphData.nodes.push(graphNode);
43024      });
43025    }
43026
43027    // copy the edges
43028    if (dotData.edges) {
43029      /**
43030       * Convert an edge in DOT format to an edge with VisGraph format
43031       * @param {Object} dotEdge
43032       * @returns {Object} graphEdge
43033       */
43034      var convertEdge = function convertEdge(dotEdge) {
43035        var graphEdge = {
43036          from: dotEdge.from,
43037          to: dotEdge.to
43038        };
43039        merge(graphEdge, convertAttr(dotEdge.attr, EDGE_ATTR_MAPPING));
43040        graphEdge.arrows = dotEdge.type === '->' ? 'to' : undefined;
43041
43042        return graphEdge;
43043      };
43044
43045      dotData.edges.forEach(function (dotEdge) {
43046        var from, to;
43047        if (dotEdge.from instanceof Object) {
43048          from = dotEdge.from.nodes;
43049        } else {
43050          from = {
43051            id: dotEdge.from
43052          };
43053        }
43054
43055        // TODO: support of solid/dotted/dashed edges (attr = 'style')
43056        // TODO: support for attributes 'dir' and 'arrowhead' (edge arrows)
43057
43058        if (dotEdge.to instanceof Object) {
43059          to = dotEdge.to.nodes;
43060        } else {
43061          to = {
43062            id: dotEdge.to
43063          };
43064        }
43065
43066        if (dotEdge.from instanceof Object && dotEdge.from.edges) {
43067          dotEdge.from.edges.forEach(function (subEdge) {
43068            var graphEdge = convertEdge(subEdge);
43069            graphData.edges.push(graphEdge);
43070          });
43071        }
43072
43073        forEach2(from, to, function (from, to) {
43074          var subEdge = createEdge(graphData, from.id, to.id, dotEdge.type, dotEdge.attr);
43075          var graphEdge = convertEdge(subEdge);
43076          graphData.edges.push(graphEdge);
43077        });
43078
43079        if (dotEdge.to instanceof Object && dotEdge.to.edges) {
43080          dotEdge.to.edges.forEach(function (subEdge) {
43081            var graphEdge = convertEdge(subEdge);
43082            graphData.edges.push(graphEdge);
43083          });
43084        }
43085      });
43086    }
43087
43088    // copy the options
43089    if (dotData.attr) {
43090      graphData.options = dotData.attr;
43091    }
43092
43093    return graphData;
43094  }
43095
43096  // exports
43097  exports.parseDOT = parseDOT;
43098  exports.DOTToGraph = DOTToGraph;
43099
43100/***/ },
43101/* 116 */
43102/***/ function(module, exports) {
43103
43104  'use strict';
43105
43106  function parseGephi(gephiJSON, optionsObj) {
43107    var edges = [];
43108    var nodes = [];
43109    var options = {
43110      edges: {
43111        inheritColor: false
43112      },
43113      nodes: {
43114        fixed: false,
43115        parseColor: false
43116      }
43117    };
43118
43119    if (optionsObj !== undefined) {
43120      if (optionsObj.fixed !== undefined) {
43121        options.nodes.fixed = optionsObj.fixed;
43122      }
43123      if (optionsObj.parseColor !== undefined) {
43124        options.nodes.parseColor = optionsObj.parseColor;
43125      }
43126      if (optionsObj.inheritColor !== undefined) {
43127        options.edges.inheritColor = optionsObj.inheritColor;
43128      }
43129    }
43130
43131    var gEdges = gephiJSON.edges;
43132    var gNodes = gephiJSON.nodes;
43133    for (var i = 0; i < gEdges.length; i++) {
43134      var edge = {};
43135      var gEdge = gEdges[i];
43136      edge['id'] = gEdge.id;
43137      edge['from'] = gEdge.source;
43138      edge['to'] = gEdge.target;
43139      edge['attributes'] = gEdge.attributes;
43140      edge['label'] = gEdge.label;
43141      edge['title'] = gEdge.attributes !== undefined ? gEdge.attributes.title : undefined;
43142      if (gEdge['type'] === 'Directed') {
43143        edge['arrows'] = 'to';
43144      }
43145      //    edge['value'] = gEdge.attributes !== undefined ? gEdge.attributes.Weight : undefined;
43146      //    edge['width'] = edge['value'] !== undefined ? undefined : edgegEdge.size;
43147      if (gEdge.color && options.inheritColor === false) {
43148        edge['color'] = gEdge.color;
43149      }
43150      edges.push(edge);
43151    }
43152
43153    for (var i = 0; i < gNodes.length; i++) {
43154      var node = {};
43155      var gNode = gNodes[i];
43156      node['id'] = gNode.id;
43157      node['attributes'] = gNode.attributes;
43158      node['title'] = gNode.title;
43159      node['x'] = gNode.x;
43160      node['y'] = gNode.y;
43161      node['label'] = gNode.label;
43162      node['title'] = gNode.attributes !== undefined ? gNode.attributes.title : undefined;
43163      if (options.nodes.parseColor === true) {
43164        node['color'] = gNode.color;
43165      } else {
43166        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;
43167      }
43168      node['size'] = gNode.size;
43169      node['fixed'] = options.nodes.fixed && gNode.x !== undefined && gNode.y !== undefined;
43170      nodes.push(node);
43171    }
43172
43173    return { nodes: nodes, edges: edges };
43174  }
43175
43176  exports.parseGephi = parseGephi;
43177
43178/***/ },
43179/* 117 */
43180/***/ function(module, exports) {
43181
43182  /**
43183   * @class Images
43184   * This class loads images and keeps them stored.
43185   */
43186  "use strict";
43187
43188  Object.defineProperty(exports, "__esModule", {
43189      value: true
43190  });
43191
43192  var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
43193
43194  function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
43195
43196  var Images = (function () {
43197      function Images(callback) {
43198          _classCallCheck(this, Images);
43199
43200          this.images = {};
43201          this.imageBroken = {};
43202          this.callback = callback;
43203      }
43204
43205      _createClass(Images, [{
43206          key: "_addImageToCache",
43207
43208          /**
43209           * @param {string} url                      The Url to cache the image as 
43210            * @return {Image} imageToLoadBrokenUrlOn  The image object
43211           */
43212          value: function _addImageToCache(url, imageToCache) {
43213              // IE11 fix -- thanks dponch!
43214              if (imageToCache.width === 0) {
43215                  document.body.appendChild(imageToCache);
43216                  imageToCache.width = imageToCache.offsetWidth;
43217                  imageToCache.height = imageToCache.offsetHeight;
43218                  document.body.removeChild(imageToCache);
43219              }
43220
43221              this.images[url] = imageToCache;
43222          }
43223      }, {
43224          key: "_tryloadBrokenUrl",
43225
43226          /**
43227           * @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
43228           * @param {string} brokenUrl                Url the broken image to try and load
43229           * @return {Image} imageToLoadBrokenUrlOn   The image object
43230           */
43231          value: function _tryloadBrokenUrl(url, brokenUrl, imageToLoadBrokenUrlOn) {
43232              var _this = this;
43233
43234              //If any of the parameters aren't specified then exit the function because nothing constructive can be done
43235              if (url === undefined || brokenUrl === undefined || imageToLoadBrokenUrlOn === undefined) return;
43236
43237              //Clear the old subscription to the error event and put a new in place that only handle errors in loading the brokenImageUrl
43238              imageToLoadBrokenUrlOn.onerror = function () {
43239                  console.error("Could not load brokenImage:", brokenUrl);
43240                  //Add an empty image to the cache so that when subsequent load calls are made for the url we don't try load the image and broken image again
43241                  _this._addImageToCache(url, new Image());
43242              };
43243
43244              //Set the source of the image to the brokenUrl, this is actually what kicks off the loading of the broken image
43245              imageToLoadBrokenUrlOn.src = brokenUrl;
43246          }
43247      }, {
43248          key: "_redrawWithImage",
43249
43250          /**
43251           * @return {Image} imageToRedrawWith The images that will be passed to the callback when it is invoked
43252           */
43253          value: function _redrawWithImage(imageToRedrawWith) {
43254              if (this.callback) {
43255                  this.callback(imageToRedrawWith);
43256              }
43257          }
43258      }, {
43259          key: "load",
43260
43261          /**
43262           * @param {string} url          Url of the image
43263           * @param {string} brokenUrl    Url of an image to use if the url image is not found
43264           * @return {Image} img          The image object
43265           */
43266          value: function load(url, brokenUrl, id) {
43267              var _this2 = this;
43268
43269              //Try and get the image from the cache, if successful then return the cached image  
43270              var cachedImage = this.images[url];
43271              if (cachedImage) return cachedImage;
43272
43273              //Create a new image
43274              var img = new Image();
43275
43276              //Subscribe to the event that is raised if the image loads successfully
43277              img.onload = function () {
43278                  //Add the image to the cache and then request a redraw
43279                  _this2._addImageToCache(url, img);
43280                  _this2._redrawWithImage(img);
43281              };
43282
43283              //Subscribe to the event that is raised if the image fails to load
43284              img.onerror = function () {
43285                  console.error("Could not load image:", url);
43286                  //Try and load the image specified by the brokenUrl using
43287                  _this2._tryloadBrokenUrl(url, brokenUrl, img);
43288              };
43289
43290              //Set the source of the image to the url, this is actuall what kicks off the loading of the image
43291              img.src = url;
43292
43293              //Return the new image
43294              return img;
43295          }
43296      }]);
43297
43298      return Images;
43299  })();
43300
43301  exports["default"] = Images;
43302  module.exports = exports["default"];
43303
43304/***/ },
43305/* 118 */
43306/***/ function(module, exports) {
43307
43308  // English
43309  'use strict';
43310
43311  exports['en'] = {
43312    edit: 'Edit',
43313    del: 'Delete selected',
43314    back: 'Back',
43315    addNode: 'Add Node',
43316    addEdge: 'Add Edge',
43317    editNode: 'Edit Node',
43318    editEdge: 'Edit Edge',
43319    addDescription: 'Click in an empty space to place a new node.',
43320    edgeDescription: 'Click on a node and drag the edge to another node to connect them.',
43321    editEdgeDescription: 'Click on the control points and drag them to a node to connect to it.',
43322    createEdgeError: 'Cannot link edges to a cluster.',
43323    deleteClusterError: 'Clusters cannot be deleted.',
43324    editClusterError: 'Clusters cannot be edited.'
43325  };
43326  exports['en_EN'] = exports['en'];
43327  exports['en_US'] = exports['en'];
43328
43329  // German
43330  exports['de'] = {
43331    edit: 'Editieren',
43332    del: 'Lösche Auswahl',
43333    back: 'Zurück',
43334    addNode: 'Knoten hinzufügen',
43335    addEdge: 'Kante hinzufügen',
43336    editNode: 'Knoten editieren',
43337    editEdge: 'Kante editieren',
43338    addDescription: 'Klicke auf eine freie Stelle, um einen neuen Knoten zu plazieren.',
43339    edgeDescription: 'Klicke auf einen Knoten und ziehe die Kante zu einem anderen Knoten, um diese zu verbinden.',
43340    editEdgeDescription: 'Klicke auf die Verbindungspunkte und ziehe diese auf einen Knoten, um sie zu verbinden.',
43341    createEdgeError: 'Es ist nicht möglich, Kanten mit Clustern zu verbinden.',
43342    deleteClusterError: 'Cluster können nicht gelöscht werden.',
43343    editClusterError: 'Cluster können nicht editiert werden.'
43344  };
43345  exports['de_DE'] = exports['de'];
43346
43347  // Spanish
43348  exports['es'] = {
43349    edit: 'Editar',
43350    del: 'Eliminar selección',
43351    back: 'Átras',
43352    addNode: 'Añadir nodo',
43353    addEdge: 'Añadir arista',
43354    editNode: 'Editar nodo',
43355    editEdge: 'Editar arista',
43356    addDescription: 'Haga clic en un lugar vacío para colocar un nuevo nodo.',
43357    edgeDescription: 'Haga clic en un nodo y arrastre la arista hacia otro nodo para conectarlos.',
43358    editEdgeDescription: 'Haga clic en un punto de control y arrastrelo a un nodo para conectarlo.',
43359    createEdgeError: 'No se puede conectar una arista a un grupo.',
43360    deleteClusterError: 'No es posible eliminar grupos.',
43361    editClusterError: 'No es posible editar grupos.'
43362  };
43363  exports['es_ES'] = exports['es'];
43364
43365  // Dutch
43366  exports['nl'] = {
43367    edit: 'Wijzigen',
43368    del: 'Selectie verwijderen',
43369    back: 'Terug',
43370    addNode: 'Node toevoegen',
43371    addEdge: 'Link toevoegen',
43372    editNode: 'Node wijzigen',
43373    editEdge: 'Link wijzigen',
43374    addDescription: 'Klik op een leeg gebied om een nieuwe node te maken.',
43375    edgeDescription: 'Klik op een node en sleep de link naar een andere node om ze te verbinden.',
43376    editEdgeDescription: 'Klik op de verbindingspunten en sleep ze naar een node om daarmee te verbinden.',
43377    createEdgeError: 'Kan geen link maken naar een cluster.',
43378    deleteClusterError: 'Clusters kunnen niet worden verwijderd.',
43379    editClusterError: 'Clusters kunnen niet worden aangepast.'
43380  };
43381  exports['nl_NL'] = exports['nl'];
43382  exports['nl_BE'] = exports['nl'];
43383
43384/***/ }
43385/******/ ])
43386});
43387;

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.