1/** 2 * vis.js 3 * https://github.com/almende/vis 4 * 5 * A dynamic, browser-based visualization library. 6 * 7 * @version 4.21.0 8 * @date 2017-10-12 9 * 10 * @license 11 * Copyright (C) 2011-2017 Almende B.V, http://almende.com 12 * 13 * Vis.js is dual licensed under both 14 * 15 * * The Apache 2.0 License 16 * http://www.apache.org/licenses/LICENSE-2.0 17 * 18 * and 19 * 20 * * The MIT License 21 * http://opensource.org/licenses/MIT 22 * 23 * Vis.js may be distributed under either license . 24 */ 25 26"use strict"; 27 28(function webpackUniversalModuleDefinition(root, factory) { 29 if(typeof exports === 'object' && typeof module === 'object') 30 module.exports = factory(); 31 else if(typeof define === 'function' && define.amd) 32 define([], factory); 33 else if(typeof exports === 'object') 34 exports["vis"] = factory(); 35 else 36 root["vis"] = factory(); 37})(this, function() { 38return /******/ (function(modules) { // webpackBootstrap 39/******/ // The module cache 40/******/ var installedModules = {}; 41/******/ 42/******/ // The require function 43/******/ function __webpack_require__(moduleId) { 44/******/ 45/******/ // Check if module is in cache 46/******/ if(installedModules[moduleId]) { 47/******/ return installedModules[moduleId].exports; 48/******/ } 49/******/ // Create a new module (and put it into the cache) 50/******/ var module = installedModules[moduleId] = { 51/******/ i: moduleId, 52/******/ l: false, 53/******/ exports: {} 54/******/ }; 55/******/ 56/******/ // Execute the module function 57/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); 58/******/ 59/******/ // Flag the module as loaded 60/******/ module.l = true; 61/******/ 62/******/ // Return the exports of the module 63/******/ return module.exports; 64/******/ } 65/******/ 66/******/ 67/******/ // expose the modules object (__webpack_modules__) 68/******/ __webpack_require__.m = modules; 69/******/ 70/******/ // expose the module cache 71/******/ __webpack_require__.c = installedModules; 72/******/ 73/******/ // define getter function for harmony exports 74/******/ __webpack_require__.d = function(exports, name, getter) { 75/******/ if(!__webpack_require__.o(exports, name)) { 76/******/ Object.defineProperty(exports, name, { 77/******/ configurable: false, 78/******/ enumerable: true, 79/******/ get: getter 80/******/ }); 81/******/ } 82/******/ }; 83/******/ 84/******/ // getDefaultExport function for compatibility with non-harmony modules 85/******/ __webpack_require__.n = function(module) { 86/******/ var getter = module && module.__esModule ? 87/******/ function getDefault() { return module['default']; } : 88/******/ function getModuleExports() { return module; }; 89/******/ __webpack_require__.d(getter, 'a', getter); 90/******/ return getter; 91/******/ }; 92/******/ 93/******/ // Object.prototype.hasOwnProperty.call 94/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; 95/******/ 96/******/ // __webpack_public_path__ 97/******/ __webpack_require__.p = ""; 98/******/ 99/******/ // Load entry module and return exports 100/******/ return __webpack_require__(__webpack_require__.s = 123); 101/******/ }) 102/************************************************************************/ 103/******/ ([ 104/* 0 */ 105/***/ (function(module, exports, __webpack_require__) { 106 107"use strict"; 108 109 110exports.__esModule = true; 111 112exports.default = function (instance, Constructor) { 113 if (!(instance instanceof Constructor)) { 114 throw new TypeError("Cannot call a class as a function"); 115 } 116}; 117 118/***/ }), 119/* 1 */ 120/***/ (function(module, exports, __webpack_require__) { 121 122"use strict"; 123 124 125exports.__esModule = true; 126 127var _defineProperty = __webpack_require__(169); 128 129var _defineProperty2 = _interopRequireDefault(_defineProperty); 130 131function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 132 133exports.default = function () { 134 function defineProperties(target, props) { 135 for (var i = 0; i < props.length; i++) { 136 var descriptor = props[i]; 137 descriptor.enumerable = descriptor.enumerable || false; 138 descriptor.configurable = true; 139 if ("value" in descriptor) descriptor.writable = true; 140 (0, _defineProperty2.default)(target, descriptor.key, descriptor); 141 } 142 } 143 144 return function (Constructor, protoProps, staticProps) { 145 if (protoProps) defineProperties(Constructor.prototype, protoProps); 146 if (staticProps) defineProperties(Constructor, staticProps); 147 return Constructor; 148 }; 149}(); 150 151/***/ }), 152/* 2 */ 153/***/ (function(module, exports, __webpack_require__) { 154 155"use strict"; 156 157 158var _getIterator2 = __webpack_require__(77); 159 160var _getIterator3 = _interopRequireDefault(_getIterator2); 161 162var _create = __webpack_require__(29); 163 164var _create2 = _interopRequireDefault(_create); 165 166var _keys = __webpack_require__(8); 167 168var _keys2 = _interopRequireDefault(_keys); 169 170var _typeof2 = __webpack_require__(6); 171 172var _typeof3 = _interopRequireDefault(_typeof2); 173 174function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 175 176// utility functions 177 178// first check if moment.js is already loaded in the browser window, if so, 179// use this instance. Else, load via commonjs. 180 181 182var moment = __webpack_require__(9); 183var uuid = __webpack_require__(157); 184 185/** 186 * Test whether given object is a number 187 * @param {*} object 188 * @return {Boolean} isNumber 189 */ 190exports.isNumber = function (object) { 191 return object instanceof Number || typeof object == 'number'; 192}; 193 194/** 195 * Remove everything in the DOM object 196 * @param {Element} DOMobject 197 */ 198exports.recursiveDOMDelete = function (DOMobject) { 199 if (DOMobject) { 200 while (DOMobject.hasChildNodes() === true) { 201 exports.recursiveDOMDelete(DOMobject.firstChild); 202 DOMobject.removeChild(DOMobject.firstChild); 203 } 204 } 205}; 206 207/** 208 * this function gives you a range between 0 and 1 based on the min and max values in the set, the total sum of all values and the current value. 209 * 210 * @param {number} min 211 * @param {number} max 212 * @param {number} total 213 * @param {number} value 214 * @returns {number} 215 */ 216exports.giveRange = function (min, max, total, value) { 217 if (max == min) { 218 return 0.5; 219 } else { 220 var scale = 1 / (max - min); 221 return Math.max(0, (value - min) * scale); 222 } 223}; 224 225/** 226 * Test whether given object is a string 227 * @param {*} object 228 * @return {Boolean} isString 229 */ 230exports.isString = function (object) { 231 return object instanceof String || typeof object == 'string'; 232}; 233 234/** 235 * Test whether given object is a Date, or a String containing a Date 236 * @param {Date | String} object 237 * @return {Boolean} isDate 238 */ 239exports.isDate = function (object) { 240 if (object instanceof Date) { 241 return true; 242 } else if (exports.isString(object)) { 243 // test whether this string contains a date 244 var match = ASPDateRegex.exec(object); 245 if (match) { 246 return true; 247 } else if (!isNaN(Date.parse(object))) { 248 return true; 249 } 250 } 251 252 return false;
253}; 254 255/** 256 * Create a semi UUID 257 * source: http://stackoverflow.com/a/105074/1262753 258 * @return {string} uuid 259 */ 260exports.randomUUID = function () { 261 return uuid.v4(); 262}; 263 264/** 265 * assign all keys of an object that are not nested objects to a certain value (used for color objects). 266 * @param {object} obj 267 * @param {number} value 268 */ 269exports.assignAllKeys = function (obj, value) { 270 for (var prop in obj) { 271 if (obj.hasOwnProperty(prop)) { 272 if ((0, _typeof3['default'])(obj[prop]) !== 'object') { 273 obj[prop] = value; 274 } 275 } 276 } 277}; 278 279/** 280 * Copy property from b to a if property present in a. 281 * If property in b explicitly set to null, delete it if `allowDeletion` set. 282 * 283 * Internal helper routine, should not be exported. Not added to `exports` for that reason. 284 * 285 * @param {object} a target object 286 * @param {object} b source object 287 * @param {string} prop name of property to copy to a 288 * @param {boolean} allowDeletion if true, delete property in a if explicitly set to null in b 289 * @private 290 */ 291function copyOrDelete(a, b, prop, allowDeletion) { 292 var doDeletion = false; 293 if (allowDeletion === true) { 294 doDeletion = b[prop] === null && a[prop] !== undefined; 295 } 296 297 if (doDeletion) { 298 delete a[prop]; 299 } else { 300 a[prop] = b[prop]; // Remember, this is a reference copy! 301 } 302} 303 304/** 305 * Fill an object with a possibly partially defined other object. 306 * 307 * Only copies values for the properties already present in a. 308 * That means an object is not created on a property if only the b object has it. 309 * 310 * @param {object} a 311 * @param {object} b 312 * @param {boolean} [allowDeletion=false] if true, delete properties in a that are explicitly set to null in b 313 */ 314exports.fillIfDefined = function (a, b) { 315 var allowDeletion = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false; 316 317 // NOTE: iteration of properties of a 318 // NOTE: prototype properties iterated over as well 319 for (var prop in a) { 320 if (b[prop] !== undefined) { 321 if (b[prop] === null || (0, _typeof3['default'])(b[prop]) !== 'object') { 322 // Note: typeof null === 'object' 323 copyOrDelete(a, b, prop, allowDeletion); 324 } else { 325 if ((0, _typeof3['default'])(a[prop]) === 'object') { 326 exports.fillIfDefined(a[prop], b[prop], allowDeletion); 327 } 328 } 329 } 330 } 331}; 332 333/** 334 * Extend object a with the properties of object b or a series of objects 335 * Only properties with defined values are copied 336 * @param {Object} a 337 * @param {...Object} b 338 * @return {Object} a 339 */ 340exports.extend = function (a, b) { 341 // eslint-disable-line no-unused-vars 342 for (var i = 1; i < arguments.length; i++) { 343 var other = arguments[i]; 344 for (var prop in other) { 345 if (other.hasOwnProperty(prop)) { 346 a[prop] = other[prop]; 347 } 348 } 349 } 350 return a; 351}; 352 353/** 354 * Extend object a with selected properties of object b or a series of objects 355 * Only properties with defined values are copied 356 * @param {Array.<string>} props 357 * @param {Object} a 358 * @param {Object} b 359 * @return {Object} a 360 */ 361exports.selectiveExtend = function (props, a, b) { 362 // eslint-disable-line no-unused-vars 363 if (!Array.isArray(props)) { 364 throw new Error('Array with property names expected as first argument'); 365 } 366 367 for (var i = 2; i < arguments.length; i++) { 368 var other = arguments[i]; 369 370 for (var p = 0; p < props.length; p++) { 371 var prop = props[p]; 372 if (other && other.hasOwnProperty(prop)) { 373 a[prop] = other[prop]; 374 } 375 } 376 } 377 return a; 378}; 379 380/** 381 * Extend object a with selected properties of object b. 382 * Only properties with defined values are copied. 383 * 384 * **Note:** Previous version of this routine implied that multiple source objects 385 * could be used; however, the implementation was **wrong**. 386 * Since multiple (>1) sources weren't used anywhere in the `vis.js` code, 387 * this has been removed 388 * 389 * @param {Array.<string>} props names of first-level properties to copy over 390 * @param {object} a target object 391 * @param {object} b source object 392 * @param {boolean} [allowDeletion=false] if true, delete property in a if explicitly set to null in b 393 * @returns {Object} a 394 */ 395exports.selectiveDeepExtend = function (props, a, b) { 396 var allowDeletion = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false;
397 398 // TODO: add support for Arrays to deepExtend 399 if (Array.isArray(b)) { 400 throw new TypeError('Arrays are not supported by deepExtend'); 401 } 402 403 for (var p = 0; p < props.length; p++) { 404 var prop = props[p]; 405 if (b.hasOwnProperty(prop)) { 406 if (b[prop] && b[prop].constructor === Object) { 407 if (a[prop] === undefined) { 408 a[prop] = {}; 409 } 410 if (a[prop].constructor === Object) { 411 exports.deepExtend(a[prop], b[prop], false, allowDeletion); 412 } else { 413 copyOrDelete(a, b, prop, allowDeletion); 414 } 415 } else if (Array.isArray(b[prop])) { 416 throw new TypeError('Arrays are not supported by deepExtend'); 417 } else { 418 copyOrDelete(a, b, prop, allowDeletion); 419 } 420 } 421 } 422 return a; 423}; 424 425/** 426 * Extend object `a` with properties of object `b`, ignoring properties which are explicitly 427 * specified to be excluded. 428 * 429 * The properties of `b` are considered for copying. 430 * Properties which are themselves objects are are also extended. 431 * Only properties with defined values are copied 432 * 433 * @param {Array.<string>} propsToExclude names of properties which should *not* be copied 434 * @param {Object} a object to extend 435 * @param {Object} b object to take properties from for extension 436 * @param {boolean} [allowDeletion=false] if true, delete properties in a that are explicitly set to null in b 437 * @return {Object} a 438 */ 439exports.selectiveNotDeepExtend = function (propsToExclude, a, b) { 440 var allowDeletion = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false; 441 442 // TODO: add support for Arrays to deepExtend 443 // NOTE: array properties have an else-below; apparently, there is a problem here. 444 if (Array.isArray(b)) { 445 throw new TypeError('Arrays are not supported by deepExtend'); 446 } 447 448 for (var prop in b) { 449 if (!b.hasOwnProperty(prop)) continue; // Handle local properties only 450 if (propsToExclude.indexOf(prop) !== -1) continue; // In exclusion list, skip 451 452 if (b[prop] && b[prop].constructor === Object) { 453 if (a[prop] === undefined) { 454 a[prop] = {}; 455 } 456 if (a[prop].constructor === Object) { 457 exports.deepExtend(a[prop], b[prop]); // NOTE: allowDeletion not propagated! 458 } else { 459 copyOrDelete(a, b, prop, allowDeletion); 460 } 461 } else if (Array.isArray(b[prop])) { 462 a[prop] = []; 463 for (var i = 0; i < b[prop].length; i++) { 464 a[prop].push(b[prop][i]); 465 } 466 } else { 467 copyOrDelete(a, b, prop, allowDeletion); 468 } 469 } 470 471 return a; 472}; 473 474/** 475 * Deep extend an object a with the properties of object b 476 * 477 * @param {Object} a 478 * @param {Object} b 479 * @param {boolean} [protoExtend=false] If true, the prototype values will also be extended. 480 * (ie. the options objects that inherit from others will also get the inherited options) 481 * @param {boolean} [allowDeletion=false] If true, the values of fields that are null will be deleted 482 * @returns {Object} 483 */ 484exports.deepExtend = function (a, b) { 485 var protoExtend = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false; 486 var allowDeletion = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false; 487 488 for (var prop in b) { 489 if (b.hasOwnProperty(prop) || protoExtend === true) { 490 if (b[prop] && b[prop].constructor === Object) { 491 if (a[prop] === undefined) { 492 a[prop] = {}; 493 } 494 if (a[prop].constructor === Object) { 495 exports.deepExtend(a[prop], b[prop], protoExtend); // NOTE: allowDeletion not propagated! 496 } else { 497 copyOrDelete(a, b, prop, allowDeletion); 498 } 499 } else if (Array.isArray(b[prop])) { 500 a[prop] = []; 501 for (var i = 0; i < b[prop].length; i++) { 502 a[prop].push(b[prop][i]); 503 } 504 } else { 505 copyOrDelete(a, b, prop, allowDeletion); 506 } 507 } 508 } 509 return a; 510}; 511 512/** 513 * Test whether all elements in two arrays are equal. 514 * @param {Array} a 515 * @param {Array} b 516 * @return {boolean} Returns true if both arrays have the same length and same 517 * elements. 518 */ 519exports.equalArray = function (a, b) { 520 if (a.length != b.length) return false;
521 522 for (var i = 0, len = a.length; i < len; i++) { 523 if (a[i] != b[i]) return false; 524 } 525 526 return true; 527}; 528 529/** 530 * Convert an object to another type 531 * @param {boolean | number | string | Date | Moment | Null | undefined} object 532 * @param {string | undefined} type Name of the type. Available types: 533 * 'Boolean', 'Number', 'String', 534 * 'Date', 'Moment', ISODate', 'ASPDate'. 535 * @return {*} object 536 * @throws Error 537 */ 538exports.convert = function (object, type) { 539 var match; 540 541 if (object === undefined) { 542 return undefined; 543 } 544 if (object === null) { 545 return null; 546 } 547 548 if (!type) { 549 return object; 550 } 551 if (!(typeof type === 'string') && !(type instanceof String)) { 552 throw new Error('Type must be a string'); 553 } 554 555 //noinspection FallthroughInSwitchStatementJS 556 switch (type) { 557 case 'boolean': 558 case 'Boolean': 559 return Boolean(object); 560 561 case 'number': 562 case 'Number': 563 if (exports.isString(object) && !isNaN(Date.parse(object))) { 564 return moment(object).valueOf(); 565 } else { 566 return Number(object.valueOf()); 567 } 568 case 'string': 569 case 'String': 570 return String(object); 571 572 case 'Date': 573 if (exports.isNumber(object)) { 574 return new Date(object); 575 } 576 if (object instanceof Date) { 577 return new Date(object.valueOf()); 578 } else if (moment.isMoment(object)) { 579 return new Date(object.valueOf()); 580 } 581 if (exports.isString(object)) { 582 match = ASPDateRegex.exec(object); 583 if (match) { 584 // object is an ASP date 585 return new Date(Number(match[1])); // parse number 586 } else { 587 return moment(new Date(object)).toDate(); // parse string 588 } 589 } else { 590 throw new Error('Cannot convert object of type ' + exports.getType(object) + ' to type Date'); 591 } 592 593 case 'Moment': 594 if (exports.isNumber(object)) { 595 return moment(object); 596 } 597 if (object instanceof Date) { 598 return moment(object.valueOf()); 599 } else if (moment.isMoment(object)) { 600 return moment(object); 601 } 602 if (exports.isString(object)) { 603 match = ASPDateRegex.exec(object); 604 if (match) { 605 // object is an ASP date 606 return moment(Number(match[1])); // parse number 607 } else { 608 return moment(object); // parse string 609 } 610 } else { 611 throw new Error('Cannot convert object of type ' + exports.getType(object) + ' to type Date'); 612 } 613 614 case 'ISODate': 615 if (exports.isNumber(object)) { 616 return new Date(object); 617 } else if (object instanceof Date) { 618 return object.toISOString(); 619 } else if (moment.isMoment(object)) { 620 return object.toDate().toISOString(); 621 } else if (exports.isString(object)) { 622 match = ASPDateRegex.exec(object); 623 if (match) { 624 // object is an ASP date 625 return new Date(Number(match[1])).toISOString(); // parse number 626 } else { 627 return moment(object).format(); // ISO 8601 628 } 629 } else { 630 throw new Error('Cannot convert object of type ' + exports.getType(object) + ' to type ISODate'); 631 } 632 633 case 'ASPDate': 634 if (exports.isNumber(object)) { 635 return '/Date(' + object + ')/'; 636 } else if (object instanceof Date) { 637 return '/Date(' + object.valueOf() + ')/'; 638 } else if (exports.isString(object)) { 639 match = ASPDateRegex.exec(object); 640 var value; 641 if (match) { 642 // object is an ASP date 643 value = new Date(Number(match[1])).valueOf(); // parse number 644 } else { 645 value = new Date(object).valueOf(); // parse string 646 } 647 return '/Date(' + value + ')/'; 648 } else { 649 throw new Error('Cannot convert object of type ' + exports.getType(object) + ' to type ASPDate'); 650 } 651 652 default: 653 throw new Error('Unknown type "' + type + '"'); 654 } 655}; 656 657// parse ASP.Net Date pattern, 658// for example '/Date(1198908717056)/' or '/Date(1198908717056-0700)/' 659// code from http://momentjs.com/ 660var ASPDateRegex = /^\/?Date\((\-?\d+)/i; 661 662/** 663 * Get the type of an object, for example exports.getType([]) returns 'Array' 664 * @param {*} object 665 * @return {string} type 666 */ 667exports.getType = function (object) { 668 var type = typeof object === 'undefined' ? 'undefined' : (0, _typeof3['default'])(object); 669 670 if (type == 'object') { 671 if (object === null) { 672 return 'null';
673 } 674 if (object instanceof Boolean) { 675 return 'Boolean'; 676 } 677 if (object instanceof Number) { 678 return 'Number'; 679 } 680 if (object instanceof String) { 681 return 'String'; 682 } 683 if (Array.isArray(object)) { 684 return 'Array'; 685 } 686 if (object instanceof Date) { 687 return 'Date'; 688 } 689 return 'Object'; 690 } else if (type == 'number') { 691 return 'Number'; 692 } else if (type == 'boolean') { 693 return 'Boolean'; 694 } else if (type == 'string') { 695 return 'String'; 696 } else if (type === undefined) { 697 return 'undefined'; 698 } 699 700 return type; 701}; 702 703/** 704 * Used to extend an array and copy it. This is used to propagate paths recursively. 705 * 706 * @param {Array} arr 707 * @param {*} newValue 708 * @returns {Array} 709 */ 710exports.copyAndExtendArray = function (arr, newValue) { 711 var newArr = []; 712 for (var i = 0; i < arr.length; i++) { 713 newArr.push(arr[i]); 714 } 715 newArr.push(newValue); 716 return newArr; 717}; 718 719/** 720 * Used to extend an array and copy it. This is used to propagate paths recursively. 721 * 722 * @param {Array} arr 723 * @returns {Array} 724 */ 725exports.copyArray = function (arr) { 726 var newArr = []; 727 for (var i = 0; i < arr.length; i++) { 728 newArr.push(arr[i]); 729 } 730 return newArr; 731}; 732 733/** 734 * Retrieve the absolute left value of a DOM element 735 * @param {Element} elem A dom element, for example a div 736 * @return {number} left The absolute left position of this element 737 * in the browser page. 738 */ 739exports.getAbsoluteLeft = function (elem) { 740 return elem.getBoundingClientRect().left; 741}; 742 743exports.getAbsoluteRight = function (elem) { 744 return elem.getBoundingClientRect().right; 745}; 746 747/** 748 * Retrieve the absolute top value of a DOM element 749 * @param {Element} elem A dom element, for example a div 750 * @return {number} top The absolute top position of this element 751 * in the browser page. 752 */ 753exports.getAbsoluteTop = function (elem) { 754 return elem.getBoundingClientRect().top; 755}; 756 757/** 758 * add a className to the given elements style 759 * @param {Element} elem 760 * @param {string} classNames 761 */ 762exports.addClassName = function (elem, classNames) { 763 var classes = elem.className.split(' '); 764 var newClasses = classNames.split(' '); 765 classes = classes.concat(newClasses.filter(function (className) { 766 return classes.indexOf(className) < 0; 767 })); 768 elem.className = classes.join(' '); 769}; 770 771/** 772 * add a className to the given elements style 773 * @param {Element} elem 774 * @param {string} classNames 775 */ 776exports.removeClassName = function (elem, classNames) { 777 var classes = elem.className.split(' '); 778 var oldClasses = classNames.split(' '); 779 classes = classes.filter(function (className) { 780 return oldClasses.indexOf(className) < 0; 781 }); 782 elem.className = classes.join(' '); 783}; 784 785/** 786 * For each method for both arrays and objects. 787 * In case of an array, the built-in Array.forEach() is applied. (**No, it's not!**) 788 * In case of an Object, the method loops over all properties of the object. 789 * @param {Object | Array} object An Object or Array 790 * @param {function} callback Callback method, called for each item in 791 * the object or array with three parameters: 792 * callback(value, index, object) 793 */ 794exports.forEach = function (object, callback) { 795 var i, len; 796 if (Array.isArray(object)) { 797 // array 798 for (i = 0, len = object.length; i < len; i++) { 799 callback(object[i], i, object); 800 } 801 } else { 802 // object 803 for (i in object) { 804 if (object.hasOwnProperty(i)) { 805 callback(object[i], i, object); 806 } 807 } 808 } 809}; 810 811/** 812 * Convert an object into an array: all objects properties are put into the 813 * array. The resulting array is unordered. 814 * @param {Object} object 815 * @returns {Array} array 816 */ 817exports.toArray = function (object) { 818 var array = []; 819 820 for (var prop in object) { 821 if (object.hasOwnProperty(prop)) array.push(object[prop]); 822 } 823 824 return array; 825}; 826 827/** 828 * Update a property in an object 829 * @param {Object} object 830 * @param {string} key 831 * @param {*} value 832 * @return {Boolean} changed 833 */ 834exports.updateProperty = function (object, key, value) { 835 if (object[key] !== value) { 836 object[key] = value; 837 return true; 838 } else { 839 return false;
840 } 841}; 842 843/** 844 * Throttle the given function to be only executed once per animation frame 845 * @param {function} fn 846 * @returns {function} Returns the throttled function 847 */ 848exports.throttle = function (fn) { 849 var scheduled = false; 850 851 return function throttled() { 852 if (!scheduled) { 853 scheduled = true; 854 requestAnimationFrame(function () { 855 scheduled = false; 856 fn(); 857 }); 858 } 859 }; 860}; 861 862/** 863 * Add and event listener. Works for all browsers 864 * @param {Element} element An html element 865 * @param {string} action The action, for example "click", 866 * without the prefix "on" 867 * @param {function} listener The callback function to be executed 868 * @param {boolean} [useCapture] 869 */ 870exports.addEventListener = function (element, action, listener, useCapture) { 871 if (element.addEventListener) { 872 if (useCapture === undefined) useCapture = false; 873 874 if (action === "mousewheel" && navigator.userAgent.indexOf("Firefox") >= 0) { 875 action = "DOMMouseScroll"; // For Firefox 876 } 877 878 element.addEventListener(action, listener, useCapture); 879 } else { 880 element.attachEvent("on" + action, listener); // IE browsers 881 } 882}; 883 884/** 885 * Remove an event listener from an element 886 * @param {Element} element An html dom element 887 * @param {string} action The name of the event, for example "mousedown" 888 * @param {function} listener The listener function 889 * @param {boolean} [useCapture] 890 */ 891exports.removeEventListener = function (element, action, listener, useCapture) { 892 if (element.removeEventListener) { 893 // non-IE browsers 894 if (useCapture === undefined) useCapture = false; 895 896 if (action === "mousewheel" && navigator.userAgent.indexOf("Firefox") >= 0) { 897 action = "DOMMouseScroll"; // For Firefox 898 } 899 900 element.removeEventListener(action, listener, useCapture); 901 } else { 902 // IE browsers 903 element.detachEvent("on" + action, listener); 904 } 905}; 906 907/** 908 * Cancels the event if it is cancelable, without stopping further propagation of the event. 909 * @param {Event} event 910 */ 911exports.preventDefault = function (event) { 912 if (!event) event = window.event; 913 914 if (event.preventDefault) { 915 event.preventDefault(); // non-IE browsers 916 } else { 917 event.returnValue = false; // IE browsers 918 } 919}; 920 921/** 922 * Get HTML element which is the target of the event 923 * @param {Event} event 924 * @return {Element} target element 925 */ 926exports.getTarget = function (event) { 927 // code from http://www.quirksmode.org/js/events_properties.html 928 if (!event) { 929 event = window.event; 930 } 931 932 var target; 933 934 if (event.target) { 935 target = event.target; 936 } else if (event.srcElement) { 937 target = event.srcElement; 938 } 939 940 if (target.nodeType != undefined && target.nodeType == 3) { 941 // defeat Safari bug 942 target = target.parentNode; 943 } 944 945 return target; 946}; 947 948/** 949 * Check if given element contains given parent somewhere in the DOM tree 950 * @param {Element} element 951 * @param {Element} parent 952 * @returns {boolean} 953 */ 954exports.hasParent = function (element, parent) { 955 var e = element; 956 957 while (e) { 958 if (e === parent) { 959 return true; 960 } 961 e = e.parentNode; 962 } 963 964 return false; 965}; 966 967exports.option = {}; 968 969/** 970 * Convert a value into a boolean 971 * @param {Boolean | function | undefined} value 972 * @param {boolean} [defaultValue] 973 * @returns {Boolean} bool 974 */ 975exports.option.asBoolean = function (value, defaultValue) { 976 if (typeof value == 'function') { 977 value = value(); 978 } 979 980 if (value != null) { 981 return value != false; 982 } 983 984 return defaultValue || null; 985}; 986 987/** 988 * Convert a value into a number 989 * @param {Boolean | function | undefined} value 990 * @param {number} [defaultValue] 991 * @returns {number} number 992 */ 993exports.option.asNumber = function (value, defaultValue) { 994 if (typeof value == 'function') { 995 value = value(); 996 } 997 998 if (value != null) { 999 return Number(value) || defaultValue || null; 1000 } 1001 1002 return defaultValue || null; 1003}; 1004 1005/** 1006 * Convert a value into a string 1007 * @param {string | function | undefined} value 1008 * @param {string} [defaultValue] 1009 * @returns {String} str 1010 */ 1011exports.option.asString = function (value, defaultValue) { 1012 if (typeof value == 'function') { 1013 value = value(); 1014 } 1015 1016 if (value != null) { 1017 return String(value); 1018 } 1019 1020 return defaultValue || null; 1021}; 1022 1023/** 1024 * Convert a size or location into a string with pixels or a percentage 1025 * @param {string | number | function | undefined} value 1026 * @param {string} [defaultValue] 1027 * @returns {String} size 1028 */ 1029exports.option.asSize = function (value, defaultValue) { 1030 if (typeof value == 'function') { 1031 value = value(); 1032 } 1033 1034 if (exports.isString(value)) { 1035 return value; 1036 } else if (exports.isNumber(value)) { 1037 return value + 'px'; 1038 } else { 1039 return defaultValue || null; 1040 } 1041}; 1042 1043/** 1044 * Convert a value into a DOM element 1045 * @param {HTMLElement | function | undefined} value 1046 * @param {HTMLElement} [defaultValue] 1047 * @returns {HTMLElement | null} dom 1048 */ 1049exports.option.asElement = function (value, defaultValue) { 1050 if (typeof value == 'function') { 1051 value = value(); 1052 } 1053 1054 return value || defaultValue || null; 1055}; 1056 1057/** 1058 * http://stackoverflow.com/questions/5623838/rgb-to-hex-and-hex-to-rgb 1059 * 1060 * @param {string} hex 1061 * @returns {{r: *, g: *, b: *}} | 255 range 1062 */ 1063exports.hexToRGB = function (hex) { 1064 // Expand shorthand form (e.g. "03F") to full form (e.g. "0033FF") 1065 var shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i; 1066 hex = hex.replace(shorthandRegex, function (m, r, g, b) { 1067 return r + r + g + g + b + b; 1068 }); 1069 var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); 1070 return result ? { 1071 r: parseInt(result[1], 16), 1072 g: parseInt(result[2], 16), 1073 b: parseInt(result[3], 16) 1074 } : null; 1075}; 1076 1077/** 1078 * This function takes color in hex format or rgb() or rgba() format and overrides the opacity. Returns rgba() string. 1079 * @param {string} color 1080 * @param {number} opacity 1081 * @returns {String} 1082 */ 1083exports.overrideOpacity = function (color, opacity) { 1084 var rgb; 1085 if (color.indexOf("rgba") != -1) { 1086 return color; 1087 } else if (color.indexOf("rgb") != -1) { 1088 rgb = color.substr(color.indexOf("(") + 1).replace(")", "").split(","); 1089 return "rgba(" + rgb[0] + "," + rgb[1] + "," + rgb[2] + "," + opacity + ")"; 1090 } else { 1091 rgb = exports.hexToRGB(color); 1092 if (rgb == null) { 1093 return color; 1094 } else { 1095 return "rgba(" + rgb.r + "," + rgb.g + "," + rgb.b + "," + opacity + ")"; 1096 } 1097 } 1098}; 1099 1100/** 1101 * 1102 * @param {number} red 0 -- 255 1103 * @param {number} green 0 -- 255 1104 * @param {number} blue 0 -- 255 1105 * @returns {String} 1106 * @constructor 1107 */ 1108exports.RGBToHex = function (red, green, blue) { 1109 return "#" + ((1 << 24) + (red << 16) + (green << 8) + blue).toString(16).slice(1); 1110}; 1111 1112/** 1113 * Parse a color property into an object with border, background, and 1114 * highlight colors 1115 * @param {Object | String} color 1116 * @return {Object} colorObject 1117 */ 1118exports.parseColor = function (color) { 1119 var c; 1120 if (exports.isString(color) === true) { 1121 if (exports.isValidRGB(color) === true) { 1122 var rgb = color.substr(4).substr(0, color.length - 5).split(',').map(function (value) { 1123 return parseInt(value); 1124 }); 1125 color = exports.RGBToHex(rgb[0], rgb[1], rgb[2]); 1126 } 1127 if (exports.isValidHex(color) === true) { 1128 var hsv = exports.hexToHSV(color); 1129 var lighterColorHSV = { h: hsv.h, s: hsv.s * 0.8, v: Math.min(1, hsv.v * 1.02) }; 1130 var darkerColorHSV = { h: hsv.h, s: Math.min(1, hsv.s * 1.25), v: hsv.v * 0.8 }; 1131 var darkerColorHex = exports.HSVToHex(darkerColorHSV.h, darkerColorHSV.s, darkerColorHSV.v); 1132 var lighterColorHex = exports.HSVToHex(lighterColorHSV.h, lighterColorHSV.s, lighterColorHSV.v); 1133 c = { 1134 background: color, 1135 border: darkerColorHex, 1136 highlight: { 1137 background: lighterColorHex, 1138 border: darkerColorHex 1139 }, 1140 hover: { 1141 background: lighterColorHex, 1142 border: darkerColorHex 1143 } 1144 }; 1145 } else { 1146 c = { 1147 background: color, 1148 border: color, 1149 highlight: { 1150 background: color, 1151 border: color 1152 }, 1153 hover: { 1154 background: color, 1155 border: color 1156 } 1157 }; 1158 } 1159 } else { 1160 c = {}; 1161 c.background = color.background || undefined; 1162 c.border = color.border || undefined; 1163 1164 if (exports.isString(color.highlight)) { 1165 c.highlight = { 1166 border: color.highlight, 1167 background: color.highlight 1168 }; 1169 } else { 1170 c.highlight = {}; 1171 c.highlight.background = color.highlight && color.highlight.background || undefined; 1172 c.highlight.border = color.highlight && color.highlight.border || undefined; 1173 } 1174 1175 if (exports.isString(color.hover)) { 1176 c.hover = { 1177 border: color.hover, 1178 background: color.hover 1179 }; 1180 } else { 1181 c.hover = {}; 1182 c.hover.background = color.hover && color.hover.background || undefined; 1183 c.hover.border = color.hover && color.hover.border || undefined; 1184 } 1185 } 1186 1187 return c; 1188}; 1189 1190/** 1191 * http://www.javascripter.net/faq/rgb2hsv.htm 1192 * 1193 * @param {number} red 1194 * @param {number} green 1195 * @param {number} blue 1196 * @returns {{h: number, s: number, v: number}} 1197 * @constructor 1198 */ 1199exports.RGBToHSV = function (red, green, blue) { 1200 red = red / 255;
1200green = green / 255;blue = blue / 255; 1201 var minRGB = Math.min(red, Math.min(green, blue)); 1202 var maxRGB = Math.max(red, Math.max(green, blue)); 1203 1204 // Black-gray-white 1205 if (minRGB == maxRGB) { 1206 return { h: 0, s: 0, v: minRGB }; 1207 } 1208 1209 // Colors other than black-gray-white: 1210 var d = red == minRGB ? green - blue : blue == minRGB ? red - green : blue - red; 1211 var h = red == minRGB ? 3 : blue == minRGB ? 1 : 5; 1212 var hue = 60 * (h - d / (maxRGB - minRGB)) / 360; 1213 var saturation = (maxRGB - minRGB) / maxRGB; 1214 var value = maxRGB; 1215 return { h: hue, s: saturation, v: value }; 1216}; 1217 1218var cssUtil = { 1219 // split a string with css styles into an object with key/values 1220 split: function split(cssText) { 1221 var styles = {}; 1222 1223 cssText.split(';').forEach(function (style) { 1224 if (style.trim() != '') { 1225 var parts = style.split(':'); 1226 var key = parts[0].trim(); 1227 var value = parts[1].trim(); 1228 styles[key] = value; 1229 } 1230 }); 1231 1232 return styles; 1233 }, 1234 1235 // build a css text string from an object with key/values 1236 join: function join(styles) { 1237 return (0, _keys2['default'])(styles).map(function (key) { 1238 return key + ': ' + styles[key]; 1239 }).join('; '); 1240 } 1241}; 1242 1243/** 1244 * Append a string with css styles to an element 1245 * @param {Element} element 1246 * @param {string} cssText 1247 */ 1248exports.addCssText = function (element, cssText) { 1249 var currentStyles = cssUtil.split(element.style.cssText); 1250 var newStyles = cssUtil.split(cssText); 1251 var styles = exports.extend(currentStyles, newStyles); 1252 1253 element.style.cssText = cssUtil.join(styles); 1254}; 1255 1256/** 1257 * Remove a string with css styles from an element 1258 * @param {Element} element 1259 * @param {string} cssText 1260 */ 1261exports.removeCssText = function (element, cssText) { 1262 var styles = cssUtil.split(element.style.cssText); 1263 var removeStyles = cssUtil.split(cssText); 1264 1265 for (var key in removeStyles) { 1266 if (removeStyles.hasOwnProperty(key)) { 1267 delete styles[key]; 1268 } 1269 } 1270 1271 element.style.cssText = cssUtil.join(styles); 1272}; 1273 1274/** 1275 * https://gist.github.com/mjijackson/5311256 1276 * @param {number} h 1277 * @param {number} s 1278 * @param {number} v 1279 * @returns {{r: number, g: number, b: number}} 1280 * @constructor 1281 */ 1282exports.HSVToRGB = function (h, s, v) { 1283 var r, g, b; 1284 1285 var i = Math.floor(h * 6); 1286 var f = h * 6 - i; 1287 var p = v * (1 - s); 1288 var q = v * (1 - f * s); 1289 var t = v * (1 - (1 - f) * s); 1290 1291 switch (i % 6) { 1292 case 0: 1293 r = v, g = t, b = p;break; 1294 case 1: 1295 r = q, g = v, b = p;break; 1296 case 2: 1297 r = p, g = v, b = t;break; 1298 case 3: 1299 r = p, g = q, b = v;break; 1300 case 4: 1301 r = t, g = p, b = v;break; 1302 case 5: 1303 r = v, g = p, b = q;break; 1304 } 1305 1306 return { r: Math.floor(r * 255), g: Math.floor(g * 255), b: Math.floor(b * 255) }; 1307}; 1308 1309exports.HSVToHex = function (h, s, v) { 1310 var rgb = exports.HSVToRGB(h, s, v); 1311 return exports.RGBToHex(rgb.r, rgb.g, rgb.b); 1312}; 1313 1314exports.hexToHSV = function (hex) { 1315 var rgb = exports.hexToRGB(hex); 1316 return exports.RGBToHSV(rgb.r, rgb.g, rgb.b); 1317}; 1318 1319exports.isValidHex = function (hex) { 1320 var isOk = /(^#[0-9A-F]{6}$)|(^#[0-9A-F]{3}$)/i.test(hex); 1321 return isOk; 1322}; 1323 1324exports.isValidRGB = function (rgb) { 1325 rgb = rgb.replace(" ", ""); 1326 var isOk = /rgb\((\d{1,3}),(\d{1,3}),(\d{1,3})\)/i.test(rgb); 1327 return isOk; 1328}; 1329exports.isValidRGBA = function (rgba) { 1330 rgba = rgba.replace(" ", ""); 1331 var isOk = /rgba\((\d{1,3}),(\d{1,3}),(\d{1,3}),(.{1,3})\)/i.test(rgba); 1332 return isOk; 1333}; 1334 1335/** 1336 * This recursively redirects the prototype of JSON objects to the referenceObject 1337 * This is used for default options. 1338 * 1339 * @param {Array.<string>} fields 1340 * @param {Object} referenceObject 1341 * @returns {*} 1342 */ 1343exports.selectiveBridgeObject = function (fields, referenceObject) { 1344 if (referenceObject !== null && (typeof referenceObject === 'undefined' ? 'undefined' : (0, _typeof3['default'])(referenceObject)) === "object") { 1345 // !!! typeof null === 'object' 1346 var objectTo = (0, _create2['default'])(referenceObject); 1347 for (var i = 0; i < fields.length; i++) { 1348 if (referenceObject.hasOwnProperty(fields[i])) { 1349 if ((0, _typeof3['default'])(referenceObject[fields[i]]) == "object") {
1350 objectTo[fields[i]] = exports.bridgeObject(referenceObject[fields[i]]); 1351 } 1352 } 1353 } 1354 return objectTo; 1355 } else { 1356 return null; 1357 } 1358}; 1359 1360/** 1361 * This recursively redirects the prototype of JSON objects to the referenceObject 1362 * This is used for default options. 1363 * 1364 * @param {Object} referenceObject 1365 * @returns {*} 1366 */ 1367exports.bridgeObject = function (referenceObject) { 1368 if (referenceObject !== null && (typeof referenceObject === 'undefined' ? 'undefined' : (0, _typeof3['default'])(referenceObject)) === "object") { 1369 // !!! typeof null === 'object' 1370 var objectTo = (0, _create2['default'])(referenceObject); 1371 if (referenceObject instanceof Element) { 1372 // Avoid bridging DOM objects 1373 objectTo = referenceObject; 1374 } else { 1375 objectTo = (0, _create2['default'])(referenceObject); 1376 for (var i in referenceObject) { 1377 if (referenceObject.hasOwnProperty(i)) { 1378 if ((0, _typeof3['default'])(referenceObject[i]) == "object") { 1379 objectTo[i] = exports.bridgeObject(referenceObject[i]); 1380 } 1381 } 1382 } 1383 } 1384 return objectTo; 1385 } else { 1386 return null; 1387 } 1388}; 1389 1390/** 1391 * This method provides a stable sort implementation, very fast for presorted data 1392 * 1393 * @param {Array} a the array 1394 * @param {function} compare an order comparator 1395 * @returns {Array} 1396 */ 1397exports.insertSort = function (a, compare) { 1398 for (var i = 0; i < a.length; i++) { 1399 var k = a[i]; 1400 for (var j = i; j > 0 && compare(k, a[j - 1]) < 0; j--) { 1401 a[j] = a[j - 1]; 1402 } 1403 a[j] = k; 1404 } 1405 return a; 1406}; 1407 1408/** 1409 * This is used to set the options of subobjects in the options object. 1410 * 1411 * A requirement of these subobjects is that they have an 'enabled' element 1412 * which is optional for the user but mandatory for the program. 1413 * 1414 * The added value here of the merge is that option 'enabled' is set as required. 1415 * 1416 * 1417 * @param {object} mergeTarget | either this.options or the options used for the groups. 1418 * @param {object} options | options 1419 * @param {string} option | option key in the options argument 1420 * @param {object} globalOptions | global options, passed in to determine value of option 'enabled' 1421 */ 1422exports.mergeOptions = function (mergeTarget, options, option) { 1423 var globalOptions = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {}; 1424 1425 // Local helpers 1426 var isPresent = function isPresent(obj) { 1427 return obj !== null && obj !== undefined; 1428 }; 1429 1430 var isObject = function isObject(obj) { 1431 return obj !== null && (typeof obj === 'undefined' ? 'undefined' : (0, _typeof3['default'])(obj)) === 'object'; 1432 }; 1433 1434 // https://stackoverflow.com/a/34491287/1223531 1435 var isEmpty = function isEmpty(obj) { 1436 for (var x in obj) { 1437 if (obj.hasOwnProperty(x)) return false; 1438 } 1439 return true; 1440 }; 1441 1442 // Guards 1443 if (!isObject(mergeTarget)) { 1444 throw new Error('Parameter mergeTarget must be an object'); 1445 } 1446 1447 if (!isObject(options)) { 1448 throw new Error('Parameter options must be an object'); 1449 } 1450 1451 if (!isPresent(option)) { 1452 throw new Error('Parameter option must have a value'); 1453 } 1454 1455 if (!isObject(globalOptions)) { 1456 throw new Error('Parameter globalOptions must be an object'); 1457 } 1458 1459 // 1460 // Actual merge routine, separated from main logic 1461 // Only a single level of options is merged. Deeper levels are ref'd. This may actually be an issue. 1462 // 1463 var doMerge = function doMerge(target, options, option) { 1464 if (!isObject(target[option])) { 1465 target[option] = {}; 1466 } 1467 1468 var src = options[option]; 1469 var dst = target[option]; 1470 for (var prop in src) { 1471 if (src.hasOwnProperty(prop)) { 1472 dst[prop] = src[prop]; 1473 } 1474 } 1475 }; 1476 1477 // Local initialization 1478 var srcOption = options[option]; 1479 var globalPassed = isObject(globalOptions) && !isEmpty(globalOptions); 1480 var globalOption = globalPassed ? globalOptions[option] : undefined; 1481 var globalEnabled = globalOption ? globalOption.enabled : undefined; 1482 1483 ///////////////////////////////////////// 1484 // Main routine 1485 ///////////////////////////////////////// 1486 if (srcOption === undefined) { 1487 return; // Nothing to do 1488 } 1489 1490 if (typeof srcOption === 'boolean') { 1491 if (!isObject(mergeTarget[option])) { 1492 mergeTarget[option] = {}; 1493 } 1494 1495 mergeTarget[option].enabled = srcOption; 1496 return; 1497 } 1498 1499 if (srcOption === null && !isObject(mergeTarget[option])) { 1500 // If possible, explicit copy from globals 1501 if (isPresent(globalOption)) { 1502 mergeTarget[option] = (0, _create2['default'])(globalOption); 1503 } else { 1504 return; // Nothing to do 1505 } 1506 } 1507 1508 if (!isObject(srcOption)) { 1509 return; 1510 } 1511 1512 // 1513 // Ensure that 'enabled' is properly set. It is required internally 1514 // Note that the value from options will always overwrite the existing value 1515 // 1516 var enabled = true; // default value 1517 1518 if (srcOption.enabled !== undefined) { 1519 enabled = srcOption.enabled; 1520 } else { 1521 // Take from globals, if present 1522 if (globalEnabled !== undefined) { 1523 enabled = globalOption.enabled; 1524 } 1525 } 1526 1527 doMerge(mergeTarget, options, option); 1528 mergeTarget[option].enabled = enabled; 1529}; 1530 1531/** 1532 * This function does a binary search for a visible item in a sorted list. If we find a visible item, the code that uses 1533 * this function will then iterate in both directions over this sorted list to find all visible items. 1534 * 1535 * @param {Item[]} orderedItems | Items ordered by start 1536 * @param {function} comparator | -1 is lower, 0 is equal, 1 is higher 1537 * @param {string} field 1538 * @param {string} field2 1539 * @returns {number} 1540 * @private 1541 */ 1542exports.binarySearchCustom = function (orderedItems, comparator, field, field2) { 1543 var maxIterations = 10000; 1544 var iteration = 0; 1545 var low = 0; 1546 var high = orderedItems.length - 1; 1547 1548 while (low <= high && iteration < maxIterations) { 1549 var middle = Math.floor((low + high) / 2); 1550 1551 var item = orderedItems[middle]; 1552 var value = field2 === undefined ? item[field] : item[field][field2]; 1553 1554 var searchResult = comparator(value); 1555 if (searchResult == 0) { 1556 // jihaa, found a visible item! 1557 return middle; 1558 } else if (searchResult == -1) { 1559 // it is too small --> increase low 1560 low = middle + 1; 1561 } else { 1562 // it is too big --> decrease high 1563 high = middle - 1; 1564 } 1565 1566 iteration++; 1567 } 1568 1569 return -1; 1570}; 1571 1572/** 1573 * This function does a binary search for a specific value in a sorted array. If it does not exist but is in between of 1574 * two values, we return either the one before or the one after, depending on user input 1575 * If it is found, we return the index, else -1. 1576 * 1577 * @param {Array} orderedItems 1578 * @param {{start: number, end: number}} target 1579 * @param {string} field 1580 * @param {string} sidePreference 'before' or 'after' 1581 * @param {function} comparator an optional comparator, returning -1,0,1 for <,==,>. 1582 * @returns {number} 1583 * @private 1584 */ 1585exports.binarySearchValue = function (orderedItems, target, field, sidePreference, comparator) { 1586 var maxIterations = 10000; 1587 var iteration = 0; 1588 var low = 0; 1589 var high = orderedItems.length - 1; 1590 var prevValue, value, nextValue, middle; 1591 1592 comparator = comparator != undefined ? comparator : function (a, b) { 1593 return a == b ? 0 : a < b ? -1 : 1; 1594 }; 1595 1596 while (low <= high && iteration < maxIterations) { 1597 // get a new guess 1598 middle = Math.floor(0.5 * (high + low)); 1599 prevValue = orderedItems[Math.max(0, middle - 1)][field]; 1600 value = orderedItems[middle][field]; 1601 nextValue = orderedItems[Math.min(orderedItems.length - 1, middle + 1)][field]; 1602 1603 if (comparator(value, target) == 0) { 1604 // we found the target 1605 return middle; 1606 } else if (comparator(prevValue, target) < 0 && comparator(value, target) > 0) {
1607 // target is in between of the previous and the current 1608 return sidePreference == 'before' ? Math.max(0, middle - 1) : middle; 1609 } else if (comparator(value, target) < 0 && comparator(nextValue, target) > 0) { 1610 // target is in between of the current and the next 1611 return sidePreference == 'before' ? middle : Math.min(orderedItems.length - 1, middle + 1); 1612 } else { 1613 // didnt find the target, we need to change our boundaries. 1614 if (comparator(value, target) < 0) { 1615 // it is too small --> increase low 1616 low = middle + 1; 1617 } else { 1618 // it is too big --> decrease high 1619 high = middle - 1; 1620 } 1621 } 1622 iteration++; 1623 } 1624 1625 // didnt find anything. Return -1. 1626 return -1; 1627}; 1628 1629/* 1630 * Easing Functions - inspired from http://gizma.com/easing/ 1631 * only considering the t value for the range [0, 1] => [0, 1] 1632 * https://gist.github.com/gre/1650294 1633 */ 1634exports.easingFunctions = { 1635 // no easing, no acceleration 1636 linear: function linear(t) { 1637 return t; 1638 }, 1639 // accelerating from zero velocity 1640 easeInQuad: function easeInQuad(t) { 1641 return t * t; 1642 }, 1643 // decelerating to zero velocity 1644 easeOutQuad: function easeOutQuad(t) { 1645 return t * (2 - t); 1646 }, 1647 // acceleration until halfway, then deceleration 1648 easeInOutQuad: function easeInOutQuad(t) { 1649 return t < .5 ? 2 * t * t : -1 + (4 - 2 * t) * t; 1650 }, 1651 // accelerating from zero velocity 1652 easeInCubic: function easeInCubic(t) { 1653 return t * t * t; 1654 }, 1655 // decelerating to zero velocity 1656 easeOutCubic: function easeOutCubic(t) { 1657 return --t * t * t + 1; 1658 }, 1659 // acceleration until halfway, then deceleration 1660 easeInOutCubic: function easeInOutCubic(t) { 1661 return t < .5 ? 4 * t * t * t : (t - 1) * (2 * t - 2) * (2 * t - 2) + 1; 1662 }, 1663 // accelerating from zero velocity 1664 easeInQuart: function easeInQuart(t) { 1665 return t * t * t * t; 1666 }, 1667 // decelerating to zero velocity 1668 easeOutQuart: function easeOutQuart(t) { 1669 return 1 - --t * t * t * t; 1670 }, 1671 // acceleration until halfway, then deceleration 1672 easeInOutQuart: function easeInOutQuart(t) { 1673 return t < .5 ? 8 * t * t * t * t : 1 - 8 * --t * t * t * t; 1674 }, 1675 // accelerating from zero velocity 1676 easeInQuint: function easeInQuint(t) { 1677 return t * t * t * t * t; 1678 }, 1679 // decelerating to zero velocity 1680 easeOutQuint: function easeOutQuint(t) { 1681 return 1 + --t * t * t * t * t; 1682 }, 1683 // acceleration until halfway, then deceleration 1684 easeInOutQuint: function easeInOutQuint(t) { 1685 return t < .5 ? 16 * t * t * t * t * t : 1 + 16 * --t * t * t * t * t; 1686 } 1687}; 1688 1689exports.getScrollBarWidth = function () { 1690 var inner = document.createElement('p'); 1691 inner.style.width = "100%"; 1692 inner.style.height = "200px"; 1693 1694 var outer = document.createElement('div'); 1695 outer.style.position = "absolute"; 1696 outer.style.top = "0px"; 1697 outer.style.left = "0px"; 1698 outer.style.visibility = "hidden"; 1699 outer.style.width = "200px"; 1700 outer.style.height = "150px"; 1701 outer.style.overflow = "hidden"; 1702 outer.appendChild(inner); 1703 1704 document.body.appendChild(outer); 1705 var w1 = inner.offsetWidth; 1706 outer.style.overflow = 'scroll'; 1707 var w2 = inner.offsetWidth; 1708 if (w1 == w2) w2 = outer.clientWidth; 1709 1710 document.body.removeChild(outer); 1711 1712 return w1 - w2; 1713}; 1714 1715exports.topMost = function (pile, accessors) { 1716 var candidate = void 0; 1717 if (!Array.isArray(accessors)) { 1718 accessors = [accessors]; 1719 } 1720 var _iteratorNormalCompletion = true; 1721 var _didIteratorError = false; 1722 var _iteratorError = undefined; 1723 1724 try { 1725 for (var _iterator = (0, _getIterator3['default'])(pile), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { 1726 var member = _step.value; 1727 1728 if (member) { 1729 candidate = member[accessors[0]]; 1730 for (var i = 1; i < accessors.length; i++) { 1731 if (candidate) { 1732 candidate = candidate[accessors[i]]; 1733 } else { 1734 continue; 1735 } 1736 } 1737 if (typeof candidate != 'undefined') { 1738 break; 1739 } 1740 } 1741 } 1742 } catch (err) { 1743 _didIteratorError = true; 1744 _iteratorError = err; 1745 } finally { 1746 try { 1747 if (!_iteratorNormalCompletion && _iterator['return']) { 1748 _iterator['return'](); 1749 } 1750 } finally { 1751 if (_didIteratorError) { 1752 throw _iteratorError; 1753 } 1754 } 1755 } 1756 1757 return candidate; 1758}; 1759 1760/***/ }), 1761/* 3 */ 1762/***/ (function(module, exports, __webpack_require__) { 1763 1764module.exports = { "default": __webpack_require__(194), __esModule: true }; 1765 1766/***/ }), 1767/* 4 */ 1768/***/ (function(module, exports, __webpack_require__) { 1769 1770"use strict"; 1771 1772 1773exports.__esModule = true; 1774 1775var _typeof2 = __webpack_require__(6); 1776 1777var _typeof3 = _interopRequireDefault(_typeof2); 1778 1779function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 1780 1781exports.default = function (self, call) { 1782 if (!self) { 1783 throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); 1784 } 1785 1786 return call && ((typeof call === "undefined" ? "undefined" : (0, _typeof3.default)(call)) === "object" || typeof call === "function") ? call : self; 1787}; 1788 1789/***/ }), 1790/* 5 */ 1791/***/ (function(module, exports, __webpack_require__) { 1792 1793"use strict"; 1794 1795 1796exports.__esModule = true; 1797 1798var _setPrototypeOf = __webpack_require__(196); 1799 1800var _setPrototypeOf2 = _interopRequireDefault(_setPrototypeOf); 1801 1802var _create = __webpack_require__(29); 1803 1804var _create2 = _interopRequireDefault(_create); 1805 1806var _typeof2 = __webpack_require__(6); 1807 1808var _typeof3 = _interopRequireDefault(_typeof2); 1809 1810function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 1811 1812exports.default = function (subClass, superClass) { 1813 if (typeof superClass !== "function" && superClass !== null) { 1814 throw new TypeError("Super expression must either be null or a function, not " + (typeof superClass === "undefined" ? "undefined" : (0, _typeof3.default)(superClass))); 1815 } 1816 1817 subClass.prototype = (0, _create2.default)(superClass && superClass.prototype, { 1818 constructor: { 1819 value: subClass, 1820 enumerable: false, 1821 writable: true, 1822 configurable: true 1823 } 1824 }); 1825 if (superClass) _setPrototypeOf2.default ? (0, _setPrototypeOf2.default)(subClass, superClass) : subClass.__proto__ = superClass; 1826}; 1827 1828/***/ }), 1829/* 6 */ 1830/***/ (function(module, exports, __webpack_require__) { 1831 1832"use strict"; 1833 1834 1835exports.__esModule = true; 1836 1837var _iterator = __webpack_require__(142); 1838 1839var _iterator2 = _interopRequireDefault(_iterator); 1840 1841var _symbol = __webpack_require__(144); 1842 1843var _symbol2 = _interopRequireDefault(_symbol); 1844 1845var _typeof = typeof _symbol2.default === "function" && typeof _iterator2.default === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof _symbol2.default === "function" && obj.constructor === _symbol2.default && obj !== _symbol2.default.prototype ? "symbol" : typeof obj; }; 1846 1847function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 1848 1849exports.default = typeof _symbol2.default === "function" && _typeof(_iterator2.default) === "symbol" ? function (obj) { 1850 return typeof obj === "undefined" ? "undefined" : _typeof(obj); 1851} : function (obj) { 1852 return obj && typeof _symbol2.default === "function" && obj.constructor === _symbol2.default && obj !== _symbol2.default.prototype ? "symbol" : typeof obj === "undefined" ? "undefined" : _typeof(obj); 1853}; 1854 1855/***/ }), 1856/* 7 */
1857/***/ (function(module, exports) { 1858 1859var core = module.exports = { version: '2.5.1' }; 1860if (typeof __e == 'number') __e = core; // eslint-disable-line no-undef 1861 1862 1863/***/ }), 1864/* 8 */ 1865/***/ (function(module, exports, __webpack_require__) { 1866 1867module.exports = { "default": __webpack_require__(140), __esModule: true }; 1868 1869/***/ }), 1870/* 9 */ 1871/***/ (function(module, exports, __webpack_require__) { 1872 1873"use strict"; 1874 1875 1876// first check if moment.js is already loaded in the browser window, if so, 1877// use this instance. Else, load via commonjs. 1878module.exports = typeof window !== 'undefined' && window['moment'] || __webpack_require__(154); 1879 1880/***/ }), 1881/* 10 */ 1882/***/ (function(module, exports, __webpack_require__) { 1883 1884"use strict"; 1885 1886 1887/** 1888 * Setup a mock hammer.js object, for unit testing. 1889 * 1890 * Inspiration: https://github.com/uber/deck.gl/pull/658 1891 * 1892 * @returns {{on: noop, off: noop, destroy: noop, emit: noop, get: get}} 1893 */ 1894function hammerMock() { 1895 var noop = function noop() {}; 1896 1897 return { 1898 on: noop, 1899 off: noop, 1900 destroy: noop, 1901 emit: noop, 1902 1903 get: function get(m) { 1904 //eslint-disable-line no-unused-vars 1905 return { 1906 set: noop 1907 }; 1908 } 1909 }; 1910} 1911 1912if (typeof window !== 'undefined') { 1913 var propagating = __webpack_require__(175); 1914 var Hammer = window['Hammer'] || __webpack_require__(176); 1915 module.exports = propagating(Hammer, { 1916 preventDefault: 'mouse' 1917 }); 1918} else { 1919 module.exports = function () { 1920 // hammer.js is only available in a browser, not in node.js. Replacing it with a mock object. 1921 return hammerMock(); 1922 }; 1923} 1924 1925/***/ }), 1926/* 11 */ 1927/***/ (function(module, exports, __webpack_require__) { 1928 1929"use strict"; 1930 1931 1932var _stringify = __webpack_require__(19); 1933 1934var _stringify2 = _interopRequireDefault(_stringify); 1935 1936var _typeof2 = __webpack_require__(6); 1937 1938var _typeof3 = _interopRequireDefault(_typeof2); 1939 1940var _keys = __webpack_require__(8); 1941 1942var _keys2 = _interopRequireDefault(_keys); 1943 1944function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 1945 1946var util = __webpack_require__(2); 1947var Queue = __webpack_require__(43); 1948 1949/** 1950 * DataSet 1951 * // TODO: add a DataSet constructor DataSet(data, options) 1952 * 1953 * Usage: 1954 * var dataSet = new DataSet({ 1955 * fieldId: '_id', 1956 * type: { 1957 * // ... 1958 * } 1959 * }); 1960 * 1961 * dataSet.add(item); 1962 * dataSet.add(data); 1963 * dataSet.update(item); 1964 * dataSet.update(data); 1965 * dataSet.remove(id); 1966 * dataSet.remove(ids); 1967 * var data = dataSet.get(); 1968 * var data = dataSet.get(id); 1969 * var data = dataSet.get(ids); 1970 * var data = dataSet.get(ids, options, data); 1971 * dataSet.clear(); 1972 * 1973 * A data set can: 1974 * - add/remove/update data 1975 * - gives triggers upon changes in the data 1976 * - can import/export data in various data formats 1977 * 1978 * @param {Array} [data] Optional array with initial data 1979 * @param {Object} [options] Available options: 1980 * {string} fieldId Field name of the id in the 1981 * items, 'id' by default. 1982 * {Object.<string, string} type 1983 * A map with field names as key, 1984 * and the field type as value. 1985 * {Object} queue Queue changes to the DataSet, 1986 * flush them all at once. 1987 * Queue options: 1988 * - {number} delay Delay in ms, null by default 1989 * - {number} max Maximum number of entries in the queue, Infinity by default 1990 * @constructor DataSet 1991 */ 1992function DataSet(data, options) { 1993 // correctly read optional arguments 1994 if (data && !Array.isArray(data)) { 1995 options = data; 1996 data = null; 1997 } 1998 1999 this._options = options || {}; 2000 this._data = {}; // map with data indexed by id 2001 this.length = 0; // number of items in the DataSet 2002 this._fieldId = this._options.fieldId || 'id'; // name of the field containing id 2003 this._type = {}; // internal field types (NOTE: this can differ from this._options.type) 2004 2005 // all variants of a Date are internally stored as Date, so we can convert 2006 // from everything to everything (also from ISODate to Number for example) 2007 if (this._options.type) { 2008 var fields = (0, _keys2['default'])(this._options.type); 2009 for (var i = 0, len = fields.length; i < len; i++) { 2010 var field = fields[i]; 2011 var value = this._options.type[field]; 2012 if (value == 'Date' || value == 'ISODate' || value == 'ASPDate') { 2013 this._type[field] = 'Date'; 2014 } else { 2015 this._type[field] = value; 2016 } 2017 } 2018 } 2019 2020 this._subscribers = {}; // event subscribers 2021 2022 // add initial data when provided 2023 if (data) { 2024 this.add(data); 2025 } 2026 2027 this.setOptions(options); 2028} 2029 2030/** 2031 * @param {Object} options Available options: 2032 * {Object} queue Queue changes to the DataSet, 2033 * flush them all at once. 2034 * Queue options: 2035 * - {number}
2035 delay Delay in ms, null by default 2036 * - {number} max Maximum number of entries in the queue, Infinity by default 2037 */ 2038DataSet.prototype.setOptions = function (options) { 2039 if (options && options.queue !== undefined) { 2040 if (options.queue === false) { 2041 // delete queue if loaded 2042 if (this._queue) { 2043 this._queue.destroy(); 2044 delete this._queue; 2045 } 2046 } else { 2047 // create queue and update its options 2048 if (!this._queue) { 2049 this._queue = Queue.extend(this, { 2050 replace: ['add', 'update', 'remove'] 2051 }); 2052 } 2053 2054 if ((0, _typeof3['default'])(options.queue) === 'object') { 2055 this._queue.setOptions(options.queue); 2056 } 2057 } 2058 } 2059}; 2060 2061/** 2062 * Subscribe to an event, add an event listener 2063 * @param {string} event Event name. Available events: 'add', 'update', 2064 * 'remove' 2065 * @param {function} callback Callback method. Called with three parameters: 2066 * {string} event 2067 * {Object | null} params 2068 * {string | number} senderId 2069 */ 2070DataSet.prototype.on = function (event, callback) { 2071 var subscribers = this._subscribers[event]; 2072 if (!subscribers) { 2073 subscribers = []; 2074 this._subscribers[event] = subscribers; 2075 } 2076 2077 subscribers.push({ 2078 callback: callback 2079 }); 2080}; 2081 2082/** 2083 * Unsubscribe from an event, remove an event listener 2084 * @param {string} event 2085 * @param {function} callback 2086 */ 2087DataSet.prototype.off = function (event, callback) { 2088 var subscribers = this._subscribers[event]; 2089 if (subscribers) { 2090 this._subscribers[event] = subscribers.filter(function (listener) { 2091 return listener.callback != callback; 2092 }); 2093 } 2094}; 2095 2096/** 2097 * Trigger an event 2098 * @param {string} event 2099 * @param {Object | null} params 2100 * @param {string} [senderId] Optional id of the sender. 2101 * @private 2102 */ 2103DataSet.prototype._trigger = function (event, params, senderId) { 2104 if (event == '*') { 2105 throw new Error('Cannot trigger event *'); 2106 } 2107 2108 var subscribers = []; 2109 if (event in this._subscribers) { 2110 subscribers = subscribers.concat(this._subscribers[event]); 2111 } 2112 if ('*' in this._subscribers) { 2113 subscribers = subscribers.concat(this._subscribers['*']); 2114 } 2115 2116 for (var i = 0, len = subscribers.length; i < len; i++) { 2117 var subscriber = subscribers[i]; 2118 if (subscriber.callback) { 2119 subscriber.callback(event, params, senderId || null); 2120 } 2121 } 2122}; 2123 2124/** 2125 * Add data. 2126 * Adding an item will fail when there already is an item with the same id. 2127 * @param {Object | Array} data 2128 * @param {string} [senderId] Optional sender id 2129 * @return {Array.<string|number>} addedIds Array with the ids of the added items 2130 */ 2131DataSet.prototype.add = function (data, senderId) { 2132 var addedIds = [], 2133 id, 2134 me = this; 2135 2136 if (Array.isArray(data)) { 2137 // Array 2138 for (var i = 0, len = data.length; i < len; i++) { 2139 id = me._addItem(data[i]); 2140 addedIds.push(id); 2141 } 2142 } else if (data && (typeof data === 'undefined' ? 'undefined' : (0, _typeof3['default'])(data)) === 'object') { 2143 // Single item 2144 id = me._addItem(data); 2145 addedIds.push(id); 2146 } else { 2147 throw new Error('Unknown dataType'); 2148 } 2149 2150 if (addedIds.length) { 2151 this._trigger('add', { items: addedIds }, senderId); 2152 } 2153 2154 return addedIds; 2155}; 2156 2157/** 2158 * Update existing items. When an item does not exist, it will be created 2159 * @param {Object | Array} data 2160 * @param {string} [senderId] Optional sender id 2161 * @return {Array.<string|number>} updatedIds The ids of the added or updated items 2162 * @throws {Error} Unknown Datatype 2163 */ 2164DataSet.prototype.update = function (data, senderId) { 2165 var addedIds = []; 2166 var updatedIds = []; 2167 var oldData = []; 2168 var updatedData = []; 2169 var me = this; 2170 var fieldId = me._fieldId; 2171 2172 var addOrUpdate = function addOrUpdate(item) { 2173 var id = item[fieldId]; 2174 if (me._data[id]) { 2175 var oldItem = util.extend({}, me._data[id]); 2176 // update item 2177 id = me._updateItem(item); 2178 updatedIds.push(id); 2179 updatedData.push(item); 2180 oldData.push(oldItem); 2181 } else { 2182 // add new item 2183 id = me._addItem(item); 2184 addedIds.push(id); 2185 } 2186 }; 2187 2188 if (Array.isArray(data)) { 2189 // Array 2190 for (var i = 0, len = data.length; i < len; i++) { 2191 if (data[i] && (0, _typeof3['default'])(data[i]) === 'object') { 2192 addOrUpdate(data[i]); 2193 } else { 2194 console.warn('Ignoring input item, which is not an object at index ' + i); 2195 } 2196 } 2197 } else if (data && (typeof data === 'undefined' ? 'undefined' : (0, _typeof3['default'])(data)) === 'object') { 2198 // Single item 2199 addOrUpdate(data); 2200 } else { 2201 throw new Error('Unknown dataType'); 2202 } 2203 2204 if (addedIds.length) { 2205 this._trigger('add', { items: addedIds }, senderId); 2206 } 2207 if (updatedIds.length) { 2208 var props = { items: updatedIds, oldData: oldData, data: updatedData }; 2209 // TODO: remove deprecated property 'data' some day 2210 //Object.defineProperty(props, 'data', { 2211 // 'get': (function() { 2212 // console.warn('Property data is deprecated. Use DataSet.get(ids) to retrieve the new data, use the oldData property on this object to get the old data'); 2213 // return updatedData; 2214 // }).bind(this) 2215 //}); 2216 this._trigger('update', props, senderId); 2217 } 2218 2219 return addedIds.concat(updatedIds); 2220}; 2221 2222/** 2223 * Get a data item or multiple items. 2224 * 2225 * Usage: 2226 * 2227 * get() 2228 * get(options: Object) 2229 * 2230 * get(id: number | string) 2231 * get(id: number | string, options: Object) 2232 * 2233 * get(ids: number[] | string[]) 2234 * get(ids: number[] | string[], options: Object) 2235 * 2236 * Where: 2237 * 2238 * {number | string} id The id of an item 2239 * {number[] | string{}} ids An array with ids of items 2240 * {Object} options An Object with options. Available options: 2241 * {string} [returnType] Type of data to be returned. 2242 * Can be 'Array' (default) or 'Object'. 2243 * {Object.<string, string>} [type] 2244 * {string[]} [fields] field names to be returned 2245 * {function} [filter] filter items 2246 * {string | function} [order] Order the items by a field name or custom sort function. 2247 * @param {Array} args 2248 * @returns {DataSet} 2249 * @throws Error 2250 */ 2251DataSet.prototype.get = function (args) { 2252 // eslint-disable-line no-unused-vars 2253 var me = this; 2254 2255 // parse the arguments 2256 var id, ids, options; 2257 var firstType = util.getType(arguments[0]); 2258 if (firstType == 'String' || firstType == 'Number') { 2259 // get(id [, options]) 2260 id = arguments[0]; 2261 options = arguments[1]; 2262 } else if (firstType == 'Array') { 2263 // get(ids [, options]) 2264 ids = arguments[0]; 2265 options = arguments[1]; 2266 } else { 2267 // get([, options]) 2268 options = arguments[0]; 2269 } 2270 2271 // determine the return type 2272 var returnType; 2273 if (options && options.returnType) { 2274 var allowedValues = ['Array', 'Object']; 2275 returnType = allowedValues.indexOf(options.returnType) == -1 ? 'Array' : options.returnType; 2276 } else { 2277 returnType = 'Array'; 2278 } 2279 2280 // build options 2281 var type = options && options.type || this._options.type; 2282 var filter = options && options.filter; 2283 var items = [], 2284 item, 2285 itemIds, 2286 itemId, 2287 i, 2288 len; 2289 2290 // convert items 2291 if (id != undefined) { 2292 // return a single item 2293 item = me._getItem(id, type); 2294 if (item && filter && !filter(item)) { 2295 item = null; 2296 } 2297 } else if (ids != undefined) { 2298 // return a subset of items 2299 for (i = 0, len = ids.length; i < len; i++) { 2300 item = me._getItem(ids[i], type); 2301 if (!filter || filter(item)) { 2302 items.push(item); 2303 } 2304 } 2305 } else { 2306 // return all items 2307 itemIds = (0, _keys2['default'])(this._data); 2308 for (i = 0, len = itemIds.length; i < len; i++) { 2309 itemId = itemIds[i]; 2310 item = me._getItem(itemId, type); 2311 if (!filter || filter(item)) { 2312 items.push(item); 2313 } 2314 } 2315 } 2316 2317 // order the results 2318 if (options && options.order && id == undefined) { 2319 this._sort(items, options.order); 2320 } 2321 2322 // filter fields of the items 2323 if (options && options.fields) { 2324 var fields = options.fields; 2325 if (id != undefined) { 2326 item = this._filterFields(item, fields); 2327 } else { 2328 for (i = 0, len = items.length; i < len; i++) { 2329 items[i] = this._filterFields(items[i], fields); 2330 } 2331 } 2332 } 2333 2334 // return the results 2335 if (returnType == 'Object') { 2336 var result = {}, 2337 resultant; 2338 for (i = 0, len = items.length; i < len; i++) { 2339 resultant = items[i]; 2340 result[resultant.id] = resultant; 2341 } 2342 return result; 2343 } else { 2344 if (id != undefined) { 2345 // a single item 2346 return item; 2347 } else { 2348 // just return our array 2349 return items; 2350 } 2351 } 2352}; 2353 2354/** 2355 * Get ids of all items or from a filtered set of items. 2356 * @param {Object} [options] An Object with options. Available options: 2357 * {function} [filter] filter items 2358 * {string | function} [order] Order the items by 2359 * a field name or custom sort function. 2360 * @return {Array.<string|number>} ids 2361 */ 2362DataSet.prototype.getIds = function (options) { 2363 var data = this._data, 2364 filter = options && options.filter, 2365 order = options && options.order, 2366 type = options && options.type || this._options.type, 2367 itemIds = (0, _keys2['default'])(data), 2368 i, 2369 len, 2370 id, 2371 item, 2372 items, 2373 ids = []; 2374 2375 if (filter) { 2376 // get filtered items 2377 if (order) { 2378 // create ordered list 2379 items = []; 2380 for (i = 0, len = itemIds.length; i < len; i++) { 2381 id = itemIds[i]; 2382 item = this._getItem(id, type); 2383 if (filter(item)) { 2384 items.push(item); 2385 } 2386 } 2387 2388 this._sort(items, order); 2389 2390 for (i = 0, len = items.length; i < len; i++) { 2391 ids.push(items[i][this._fieldId]); 2392 } 2393 } else { 2394 // create unordered list 2395 for (i = 0, len = itemIds.length; i < len; i++) { 2396 id = itemIds[i]; 2397 item = this._getItem(id, type); 2398 if (filter(item)) { 2399 ids.push(item[this._fieldId]); 2400 } 2401 } 2402 } 2403 } else { 2404 // get all items 2405 if (order) { 2406 // create an ordered list 2407 items = []; 2408 for (i = 0, len = itemIds.length; i < len; i++) { 2409 id = itemIds[i]; 2410 items.push(data[id]); 2411 } 2412 2413 this._sort(items, order); 2414 2415 for (i = 0, len = items.length; i < len; i++) { 2416 ids.push(items[i][this._fieldId]); 2417 } 2418 } else { 2419 // create unordered list 2420 for (i = 0, len = itemIds.length; i < len; i++) { 2421 id = itemIds[i]; 2422 item = data[id]; 2423 ids.push(item[this._fieldId]); 2424 } 2425 } 2426 } 2427 2428 return ids; 2429}; 2430 2431/** 2432 * Returns the DataSet itself. Is overwritten for example by the DataView, 2433 * which returns the DataSet it is connected to instead. 2434 * @returns {DataSet} 2435 */ 2436DataSet.prototype.getDataSet = function () { 2437 return this; 2438}; 2439 2440/** 2441 * Execute a callback function for every item in the dataset. 2442 * @param {function} callback 2443 * @param {Object} [options] Available options: 2444 * {Object.<string, string>} [type] 2445 * {string[]} [fields] filter fields 2446 * {function} [filter] filter items 2447 * {string | function} [order] Order the items by 2448 * a field name or custom sort function. 2449 */ 2450DataSet.prototype.forEach = function (callback, options) { 2451 var filter = options && options.filter, 2452 type = options && options.type || this._options.type, 2453 data = this._data, 2454 itemIds = (0, _keys2['default'])(data), 2455 i, 2456 len, 2457 item, 2458 id; 2459 2460 if (options && options.order) {
2461 // execute forEach on ordered list 2462 var items = this.get(options); 2463 2464 for (i = 0, len = items.length; i < len; i++) { 2465 item = items[i]; 2466 id = item[this._fieldId]; 2467 callback(item, id); 2468 } 2469 } else { 2470 // unordered 2471 for (i = 0, len = itemIds.length; i < len; i++) { 2472 id = itemIds[i]; 2473 item = this._getItem(id, type); 2474 if (!filter || filter(item)) { 2475 callback(item, id); 2476 } 2477 } 2478 } 2479}; 2480 2481/** 2482 * Map every item in the dataset. 2483 * @param {function} callback 2484 * @param {Object} [options] Available options: 2485 * {Object.<string, string>} [type] 2486 * {string[]} [fields] filter fields 2487 * {function} [filter] filter items 2488 * {string | function} [order] Order the items by 2489 * a field name or custom sort function. 2490 * @return {Object[]} mappedItems 2491 */ 2492DataSet.prototype.map = function (callback, options) { 2493 var filter = options && options.filter, 2494 type = options && options.type || this._options.type, 2495 mappedItems = [], 2496 data = this._data, 2497 itemIds = (0, _keys2['default'])(data), 2498 i, 2499 len, 2500 id, 2501 item; 2502 2503 // convert and filter items 2504 for (i = 0, len = itemIds.length; i < len; i++) { 2505 id = itemIds[i]; 2506 item = this._getItem(id, type); 2507 if (!filter || filter(item)) { 2508 mappedItems.push(callback(item, id)); 2509 } 2510 } 2511 2512 // order items 2513 if (options && options.order) { 2514 this._sort(mappedItems, options.order); 2515 } 2516 2517 return mappedItems; 2518}; 2519 2520/** 2521 * Filter the fields of an item 2522 * @param {Object | null} item 2523 * @param {string[]} fields Field names 2524 * @return {Object | null} filteredItem or null if no item is provided 2525 * @private 2526 */ 2527DataSet.prototype._filterFields = function (item, fields) { 2528 if (!item) { 2529 // item is null 2530 return item; 2531 } 2532 2533 var filteredItem = {}, 2534 itemFields = (0, _keys2['default'])(item), 2535 len = itemFields.length, 2536 i, 2537 field; 2538 2539 if (Array.isArray(fields)) { 2540 for (i = 0; i < len; i++) { 2541 field = itemFields[i]; 2542 if (fields.indexOf(field) != -1) { 2543 filteredItem[field] = item[field]; 2544 } 2545 } 2546 } else { 2547 for (i = 0; i < len; i++) { 2548 field = itemFields[i]; 2549 if (fields.hasOwnProperty(field)) { 2550 filteredItem[fields[field]] = item[field]; 2551 } 2552 } 2553 } 2554 2555 return filteredItem; 2556}; 2557 2558/** 2559 * Sort the provided array with items 2560 * @param {Object[]} items 2561 * @param {string | function} order A field name or custom sort function. 2562 * @private 2563 */ 2564DataSet.prototype._sort = function (items, order) { 2565 if (util.isString(order)) { 2566 // order by provided field name 2567 var name = order; // field name 2568 items.sort(function (a, b) { 2569 var av = a[name]; 2570 var bv = b[name]; 2571 return av > bv ? 1 : av < bv ? -1 : 0; 2572 }); 2573 } else if (typeof order === 'function') { 2574 // order by sort function 2575 items.sort(order); 2576 } 2577 // TODO: extend order by an Object {field:string, direction:string} 2578 // where direction can be 'asc' or 'desc' 2579 else { 2580 throw new TypeError('Order must be a function or a string'); 2581 } 2582}; 2583 2584/** 2585 * Remove an object by pointer or by id 2586 * @param {string | number | Object | Array.<string|number>} id Object or id, or an array with 2587 * objects or ids to be removed 2588 * @param {string} [senderId] Optional sender id 2589 * @return {Array.<string|number>} removedIds 2590 */ 2591DataSet.prototype.remove = function (id, senderId) { 2592 var removedIds = [], 2593 removedItems = [], 2594 ids = [], 2595 i, 2596 len, 2597 itemId, 2598 item; 2599 2600 // force everything to be an array for simplicity 2601 ids = Array.isArray(id) ? id : [id]; 2602 2603 for (i = 0, len = ids.length; i < len; i++) { 2604 item = this._remove(ids[i]); 2605 if (item) { 2606 itemId = item[this._fieldId]; 2607 if (itemId != undefined) { 2608 removedIds.push(itemId); 2609 removedItems.push(item); 2610 } 2611 } 2612 } 2613 2614 if (removedIds.length) { 2615 this._trigger('remove', { items: removedIds, oldData: removedItems }, senderId); 2616 } 2617 2618 return removedIds; 2619}; 2620 2621/** 2622 * Remove an item by its id 2623 * @param {number | string | Object} id id or item 2624 * @returns {number | string | null} id 2625 * @private 2626 */ 2627DataSet.prototype._remove = function (id) { 2628 var item, ident; 2629 2630 // confirm the id to use based on the args type 2631 if (util.isNumber(id) || util.isString(id)) { 2632 ident = id; 2633 } else if (id && (typeof id === 'undefined' ? 'undefined' : (0, _typeof3['default'])(id)) === 'object') { 2634 ident = id[this._fieldId]; // look for the identifier field using _fieldId 2635 } 2636 2637 // do the remove if the item is found 2638 if (ident !== undefined && this._data[ident]) { 2639 item = this._data[ident]; 2640 delete this._data[ident]; 2641 this.length--; 2642 return item; 2643 } 2644 return null; 2645}; 2646 2647/** 2648 * Clear the data 2649 * @param {string} [senderId] Optional sender id 2650 * @return {Array.<string|number>} removedIds The ids of all removed items 2651 */ 2652DataSet.prototype.clear = function (senderId) { 2653 var i, len; 2654 var ids = (0, _keys2['default'])(this._data); 2655 var items = []; 2656 2657 for (i = 0, len = ids.length; i < len; i++) { 2658 items.push(this._data[ids[i]]); 2659 } 2660 2661 this._data = {}; 2662 this.length = 0; 2663 2664 this._trigger('remove', { items: ids, oldData: items }, senderId); 2665 2666 return ids; 2667}; 2668 2669/** 2670 * Find the item with maximum value of a specified field 2671 * @param {string} field 2672 * @return {Object | null} item Item containing max value, or null if no items 2673 */ 2674DataSet.prototype.max = function (field) { 2675 var data = this._data, 2676 itemIds = (0, _keys2['default'])(data), 2677 max = null, 2678 maxField = null, 2679 i, 2680 len; 2681 2682 for (i = 0, len = itemIds.length; i < len; i++) { 2683 var id = itemIds[i]; 2684 var item = data[id]; 2685 var itemField = item[field]; 2686 if (itemField != null && (!max || itemField > maxField)) { 2687 max = item; 2688 maxField = itemField; 2689 } 2690 } 2691 2692 return max; 2693}; 2694 2695/** 2696 * Find the item with minimum value of a specified field 2697 * @param {string} field 2698 * @return {Object | null} item Item containing max value, or null if no items 2699 */ 2700DataSet.prototype.min = function (field) { 2701 var data = this._data, 2702 itemIds = (0, _keys2['default'])(data), 2703 min = null, 2704 minField = null, 2705 i, 2706 len; 2707 2708 for (i = 0, len = itemIds.length; i < len; i++) { 2709 var id = itemIds[i]; 2710 var item = data[id]; 2711 var itemField = item[field];
2712 if (itemField != null && (!min || itemField < minField)) { 2713 min = item; 2714 minField = itemField; 2715 } 2716 } 2717 2718 return min; 2719}; 2720 2721/** 2722 * Find all distinct values of a specified field 2723 * @param {string} field 2724 * @return {Array} values Array containing all distinct values. If data items 2725 * do not contain the specified field are ignored. 2726 * The returned array is unordered. 2727 */ 2728DataSet.prototype.distinct = function (field) { 2729 var data = this._data; 2730 var itemIds = (0, _keys2['default'])(data); 2731 var values = []; 2732 var fieldType = this._options.type && this._options.type[field] || null; 2733 var count = 0; 2734 var i, j, len; 2735 2736 for (i = 0, len = itemIds.length; i < len; i++) { 2737 var id = itemIds[i]; 2738 var item = data[id]; 2739 var value = item[field]; 2740 var exists = false; 2741 for (j = 0; j < count; j++) { 2742 if (values[j] == value) { 2743 exists = true; 2744 break; 2745 } 2746 } 2747 if (!exists && value !== undefined) { 2748 values[count] = value; 2749 count++; 2750 } 2751 } 2752 2753 if (fieldType) { 2754 for (i = 0, len = values.length; i < len; i++) { 2755 values[i] = util.convert(values[i], fieldType); 2756 } 2757 } 2758 2759 return values; 2760}; 2761 2762/** 2763 * Add a single item. Will fail when an item with the same id already exists. 2764 * @param {Object} item 2765 * @return {string} id 2766 * @private 2767 */ 2768DataSet.prototype._addItem = function (item) { 2769 var id = item[this._fieldId]; 2770 2771 if (id != undefined) { 2772 // check whether this id is already taken 2773 if (this._data[id]) { 2774 // item already exists 2775 throw new Error('Cannot add item: item with id ' + id + ' already exists'); 2776 } 2777 } else { 2778 // generate an id 2779 id = util.randomUUID(); 2780 item[this._fieldId] = id; 2781 } 2782 2783 var d = {}, 2784 fields = (0, _keys2['default'])(item), 2785 i, 2786 len; 2787 for (i = 0, len = fields.length; i < len; i++) { 2788 var field = fields[i]; 2789 var fieldType = this._type[field]; // type may be undefined 2790 d[field] = util.convert(item[field], fieldType); 2791 } 2792 this._data[id] = d; 2793 this.length++; 2794 2795 return id; 2796}; 2797 2798/** 2799 * Get an item. Fields can be converted to a specific type 2800 * @param {string} id 2801 * @param {Object.<string, string>} [types] field types to convert 2802 * @return {Object | null} item 2803 * @private 2804 */ 2805DataSet.prototype._getItem = function (id, types) { 2806 var field, value, i, len; 2807 2808 // get the item from the dataset 2809 var raw = this._data[id]; 2810 if (!raw) { 2811 return null; 2812 } 2813 2814 // convert the items field types 2815 var converted = {}, 2816 fields = (0, _keys2['default'])(raw); 2817 2818 if (types) { 2819 for (i = 0, len = fields.length; i < len; i++) { 2820 field = fields[i]; 2821 value = raw[field]; 2822 converted[field] = util.convert(value, types[field]); 2823 } 2824 } else { 2825 // no field types specified, no converting needed 2826 for (i = 0, len = fields.length; i < len; i++) { 2827 field = fields[i]; 2828 value = raw[field]; 2829 converted[field] = value; 2830 } 2831 } 2832 2833 if (!converted[this._fieldId]) { 2834 converted[this._fieldId] = raw.id; 2835 } 2836 2837 return converted; 2838}; 2839 2840/** 2841 * Update a single item: merge with existing item. 2842 * Will fail when the item has no id, or when there does not exist an item 2843 * with the same id. 2844 * @param {Object} item 2845 * @return {string} id 2846 * @private 2847 */ 2848DataSet.prototype._updateItem = function (item) { 2849 var id = item[this._fieldId]; 2850 if (id == undefined) { 2851 throw new Error('Cannot update item: item has no id (item: ' + (0, _stringify2['default'])(item) + ')'); 2852 } 2853 var d = this._data[id]; 2854 if (!d) { 2855 // item doesn't exist 2856 throw new Error('Cannot update item: no item with id ' + id + ' found'); 2857 } 2858 2859 // merge with current item 2860 var fields = (0, _keys2['default'])(item); 2861 for (var i = 0, len = fields.length; i < len; i++) { 2862 var field = fields[i]; 2863 var fieldType = this._type[field]; // type may be undefined 2864 d[field] = util.convert(item[field], fieldType); 2865 } 2866 2867 return id; 2868}; 2869 2870module.exports = DataSet; 2871 2872/***/ }), 2873/* 12 */ 2874/***/ (function(module, exports, __webpack_require__) { 2875 2876"use strict"; 2877 2878 2879var _keys = __webpack_require__(8); 2880 2881var _keys2 = _interopRequireDefault(_keys); 2882 2883function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 2884 2885var util = __webpack_require__(2); 2886var DataSet = __webpack_require__(11); 2887 2888/** 2889 * DataView 2890 * 2891 * a dataview offers a filtered view on a dataset or an other dataview. 2892 * 2893 * @param {DataSet | DataView} data 2894 * @param {Object} [options] Available options: see method get 2895 * 2896 * @constructor DataView 2897 */ 2898function DataView(data, options) { 2899 this._data = null; 2900 this._ids = {}; // ids of the items currently in memory (just contains a boolean true) 2901 this.length = 0; // number of items in the DataView 2902 this._options = options || {}; 2903 this._fieldId = 'id'; // name of the field containing id 2904 this._subscribers = {}; // event subscribers 2905 2906 var me = this; 2907 this.listener = function () { 2908 me._onEvent.apply(me, arguments); 2909 }; 2910 2911 this.setData(data); 2912} 2913 2914// TODO: implement a function .config() to dynamically update things like configured filter 2915// and trigger changes accordingly 2916 2917/** 2918 * Set a data source for the view 2919 * @param {DataSet | DataView} data 2920 */ 2921DataView.prototype.setData = function (data) { 2922 var ids, id, i, len, items; 2923 2924 if (this._data) { 2925 // unsubscribe from current dataset 2926 if (this._data.off) { 2927 this._data.off('*', this.listener); 2928 } 2929 2930 // trigger a remove of all items in memory 2931 ids = this._data.getIds({ filter: this._options && this._options.filter }); 2932 items = []; 2933 2934 for (i = 0, len = ids.length; i < len; i++) { 2935 items.push(this._data._data[ids[i]]); 2936 } 2937 2938 this._ids = {}; 2939 this.length = 0; 2940 this._trigger('remove', { items: ids, oldData: items }); 2941 } 2942 2943 this._data = data; 2944 2945 if (this._data) { 2946 // update fieldId 2947 this._fieldId = this._options.fieldId || this._data && this._data.options && this._data.options.fieldId || 'id'; 2948 2949 // trigger an add of all added items 2950 ids = this._data.getIds({ filter: this._options && this._options.filter }); 2951 for (i = 0, len = ids.length; i < len; i++) { 2952 id = ids[i]; 2953 this._ids[id] = true; 2954 } 2955 this.length = ids.length; 2956 this._trigger('add', { items: ids }); 2957 2958 // subscribe to new dataset 2959 if (this._data.on) { 2960 this._data.on('*', this.listener); 2961 } 2962 } 2963}; 2964 2965/** 2966 * Refresh the DataView. Useful when the DataView has a filter function 2967 * containing a variable parameter. 2968 */ 2969DataView.prototype.refresh = function () { 2970 var id, i, len; 2971 var ids = this._data.getIds({ filter: this._options && this._options.filter }), 2972 oldIds = (0, _keys2['default'])(this._ids), 2973 newIds = {}, 2974 addedIds = [], 2975 removedIds = [], 2976 removedItems = []; 2977 2978 // check for additions 2979 for (i = 0, len = ids.length; i < len; i++) { 2980 id = ids[i]; 2981 newIds[id] = true; 2982 if (!this._ids[id]) { 2983 addedIds.push(id); 2984 this._ids[id] = true; 2985 } 2986 } 2987 2988 // check for removals 2989 for (i = 0, len = oldIds.length; i < len; i++) { 2990 id = oldIds[i]; 2991 if (!newIds[id]) { 2992 removedIds.push(id); 2993 removedItems.push(this._data._data[id]); 2994 delete this._ids[id]; 2995 } 2996 } 2997 2998 this.length += addedIds.length - removedIds.length; 2999 3000 // trigger events 3001 if (addedIds.length) { 3002 this._trigger('add', { items: addedIds }); 3003 } 3004 if (removedIds.length) { 3005 this._trigger('remove', { items: removedIds, oldData: removedItems }); 3006 } 3007}; 3008 3009/** 3010 * Get data from the data view 3011 * 3012 * Usage: 3013 * 3014 * get() 3015 * get(options: Object) 3016 * get(options: Object, data: Array | DataTable) 3017 * 3018 * get(id: Number) 3019 * get(id: Number, options: Object) 3020 * get(id: Number, options: Object, data: Array | DataTable) 3021 * 3022 * get(ids: Number[]) 3023 * get(ids: Number[], options: Object) 3024 * get(ids: Number[], options: Object, data: Array | DataTable) 3025 * 3026 * Where: 3027 * 3028 * {number | string} id The id of an item 3029 * {number[] | string{}} ids An array with ids of items 3030 * {Object} options An Object with options. Available options: 3031 * {string} [type] Type of data to be returned. Can 3032 * be 'DataTable' or 'Array' (default) 3033 * {Object.<string, string>} [convert] 3034 * {string[]} [fields] field names to be returned 3035 * {function} [filter] filter items 3036 * {string | function} [order] Order the items by 3037 * a field name or custom sort function. 3038 * {Array | DataTable} [data] If provided, items will be appended to this 3039 * array or table. Required in case of Google 3040 * DataTable. 3041 * @param {Array} args 3042 * @return {DataSet|DataView} 3043 */ 3044DataView.prototype.get = function (args) { 3045 // eslint-disable-line no-unused-vars 3046 var me = this; 3047 3048 // parse the arguments 3049 var ids, options, data; 3050 var firstType = util.getType(arguments[0]); 3051 if (firstType == 'String' || firstType == 'Number' || firstType == 'Array') { 3052 // get(id(s) [, options] [, data]) 3053 ids = arguments[0]; // can be a single id or an array with ids 3054 options = arguments[1]; 3055 data = arguments[2]; 3056 } else { 3057 // get([, options] [, data]) 3058 options = arguments[0]; 3059 data = arguments[1]; 3060 } 3061 3062 // extend the options with the default options and provided options 3063 var viewOptions = util.extend({}, this._options, options); 3064 3065 // create a combined filter method when needed 3066 if (this._options.filter && options && options.filter) { 3067 viewOptions.filter = function (item) { 3068 return me._options.filter(item) && options.filter(item); 3069 }; 3070 } 3071 3072 // build up the call to the linked data set 3073 var getArguments = []; 3074 if (ids != undefined) { 3075 getArguments.push(ids); 3076 } 3077 getArguments.push(viewOptions); 3078 getArguments.push(data); 3079 3080 return this._data && this._data.get.apply(this._data, getArguments); 3081}; 3082 3083/** 3084 * Get ids of all items or from a filtered set of items. 3085 * @param {Object} [options] An Object with options. Available options: 3086 * {function} [filter] filter items 3087 * {string | function} [order] Order the items by 3088 * a field name or custom sort function. 3089 * @return {Array.<string|number>} ids 3090 */ 3091DataView.prototype.getIds = function (options) { 3092 var ids; 3093 3094 if (this._data) { 3095 var defaultFilter = this._options.filter; 3096 var filter; 3097 3098 if (options && options.filter) { 3099 if (defaultFilter) { 3100 filter = function filter(item) { 3101 return defaultFilter(item) && options.filter(item); 3102 }; 3103 } else { 3104 filter = options.filter; 3105 } 3106 } else { 3107 filter = defaultFilter; 3108 } 3109 3110 ids = this._data.getIds({ 3111 filter: filter, 3112 order: options && options.order 3113 }); 3114 } else { 3115 ids = []; 3116 } 3117 3118 return ids; 3119}; 3120 3121/** 3122 * Map every item in the dataset. 3123 * @param {function} callback 3124 * @param {Object} [options] Available options: 3125 * {Object.<string, string>} [type] 3126 * {string[]} [fields] filter fields 3127 * {function} [filter] filter items 3128 * {string | function} [order] Order the items by 3129 * a field name or custom sort function. 3130 * @return {Object[]} mappedItems 3131 */ 3132DataView.prototype.map = function (callback, options) { 3133 var mappedItems = []; 3134 if (this._data) { 3135 var defaultFilter = this._options.filter; 3136 var filter; 3137 3138 if (options && options.filter) { 3139 if (defaultFilter) { 3140 filter = function filter(item) { 3141 return defaultFilter(item) && options.filter(item); 3142 }; 3143 } else { 3144 filter = options.filter; 3145 } 3146 } else { 3147 filter = defaultFilter; 3148 } 3149 3150 mappedItems = this._data.map(callback, { 3151 filter: filter, 3152 order: options && options.order 3153 }); 3154 } else { 3155 mappedItems = []; 3156 } 3157 3158 return mappedItems; 3159}; 3160 3161/** 3162 * Get the DataSet to which this DataView is connected. In case there is a chain 3163 * of multiple DataViews, the root DataSet of this chain is returned. 3164 * @return {DataSet} dataSet 3165 */ 3166DataView.prototype.getDataSet = function () { 3167 var dataSet = this; 3168 while (dataSet instanceof DataView) { 3169 dataSet = dataSet._data; 3170 } 3171 return dataSet || null; 3172}; 3173 3174/** 3175 * Event listener. Will propagate all events from the connected data set to 3176 * the subscribers of the DataView, but will filter the items and only trigger 3177 * when there are changes in the filtered data set. 3178 * @param {string} event 3179 * @param {Object | null} params 3180 * @param {string} senderId 3181 * @private 3182 */ 3183DataView.prototype._onEvent = function (event, params, senderId) { 3184 var i, len, id, item; 3185 var ids = params && params.items; 3186 var addedIds = [], 3187 updatedIds = [], 3188 removedIds = [], 3189 oldItems = [], 3190 updatedItems = [], 3191 removedItems = []; 3192 3193 if (ids && this._data) { 3194 switch (event) { 3195 case 'add': 3196 // filter the ids of the added items 3197 for (i = 0, len = ids.length; i < len; i++) { 3198 id = ids[i]; 3199 item = this.get(id); 3200 if (item) { 3201 this._ids[id] = true; 3202 addedIds.push(id); 3203 } 3204 } 3205 3206 break; 3207 3208 case 'update': 3209 // determine the event from the views viewpoint: an updated 3210 // item can be added, updated, or removed from this view. 3211 for (i = 0, len = ids.length; i < len; i++) { 3212 id = ids[i]; 3213 item = this.get(id); 3214 3215 if (item) { 3216 if (this._ids[id]) { 3217 updatedIds.push(id); 3218 updatedItems.push(params.data[i]); 3219 oldItems.push(params.oldData[i]); 3220 } else { 3221 this._ids[id] = true; 3222 addedIds.push(id); 3223 } 3224 } else { 3225 if (this._ids[id]) { 3226 delete this._ids[id]; 3227 removedIds.push(id); 3228 removedItems.push(params.oldData[i]); 3229 } else { 3230 // nothing interesting for me :-( 3231 } 3232 } 3233 } 3234 3235 break; 3236 3237 case 'remove': 3238 // filter the ids of the removed items 3239 for (i = 0, len = ids.length; i < len; i++) { 3240 id = ids[i]; 3241 if (this._ids[id]) { 3242 delete this._ids[id]; 3243 removedIds.push(id); 3244 removedItems.push(params.oldData[i]); 3245 } 3246 } 3247 3248 break; 3249 } 3250 3251 this.length += addedIds.length - removedIds.length; 3252 3253 if (addedIds.length) { 3254 this._trigger('add', { items: addedIds }, senderId); 3255 } 3256 if (updatedIds.length) { 3257 this._trigger('update', { items: updatedIds, oldData: oldItems, data: updatedItems }, senderId); 3258 } 3259 if (removedIds.length) { 3260 this._trigger('remove', { items: removedIds, oldData: removedItems }, senderId); 3261 } 3262 } 3263}; 3264 3265// copy subscription functionality from DataSet 3266DataView.prototype.on = DataSet.prototype.on; 3267DataView.prototype.off = DataSet.prototype.off; 3268DataView.prototype._trigger = DataSet.prototype._trigger; 3269 3270// TODO: make these functions deprecated (replaced with `on` and `off` since version 0.5) 3271DataView.prototype.subscribe = DataView.prototype.on; 3272DataView.prototype.unsubscribe = DataView.prototype.off; 3273 3274module.exports = DataView; 3275 3276/***/ }), 3277/* 13 */ 3278/***/ (function(module, exports, __webpack_require__) { 3279 3280var store = __webpack_require__(57)('wks'); 3281var uid = __webpack_require__(40); 3282var Symbol = __webpack_require__(18).Symbol; 3283var USE_SYMBOL = typeof Symbol == 'function'; 3284
3285var $exports = module.exports = function (name) { 3286 return store[name] || (store[name] = 3287 USE_SYMBOL && Symbol[name] || (USE_SYMBOL ? Symbol : uid)('Symbol.' + name)); 3288}; 3289 3290$exports.store = store; 3291 3292 3293/***/ }), 3294/* 14 */ 3295/***/ (function(module, exports, __webpack_require__) { 3296 3297"use strict"; 3298 3299 3300// DOM utility methods 3301 3302/** 3303 * this prepares the JSON container for allocating SVG elements 3304 * @param {Object} JSONcontainer 3305 * @private 3306 */ 3307exports.prepareElements = function (JSONcontainer) { 3308 // cleanup the redundant svgElements; 3309 for (var elementType in JSONcontainer) { 3310 if (JSONcontainer.hasOwnProperty(elementType)) { 3311 JSONcontainer[elementType].redundant = JSONcontainer[elementType].used; 3312 JSONcontainer[elementType].used = []; 3313 } 3314 } 3315}; 3316 3317/** 3318 * this cleans up all the unused SVG elements. By asking for the parentNode, we only need to supply the JSON container from 3319 * which to remove the redundant elements. 3320 * 3321 * @param {Object} JSONcontainer 3322 * @private 3323 */ 3324exports.cleanupElements = function (JSONcontainer) { 3325 // cleanup the redundant svgElements; 3326 for (var elementType in JSONcontainer) { 3327 if (JSONcontainer.hasOwnProperty(elementType)) { 3328 if (JSONcontainer[elementType].redundant) { 3329 for (var i = 0; i < JSONcontainer[elementType].redundant.length; i++) { 3330 JSONcontainer[elementType].redundant[i].parentNode.removeChild(JSONcontainer[elementType].redundant[i]); 3331 } 3332 JSONcontainer[elementType].redundant = []; 3333 } 3334 } 3335 } 3336}; 3337 3338/** 3339 * Ensures that all elements are removed first up so they can be recreated cleanly 3340 * @param {Object} JSONcontainer 3341 */ 3342exports.resetElements = function (JSONcontainer) { 3343 exports.prepareElements(JSONcontainer); 3344 exports.cleanupElements(JSONcontainer); 3345 exports.prepareElements(JSONcontainer); 3346}; 3347 3348/** 3349 * Allocate or generate an SVG element if needed. Store a reference to it in the JSON container and draw it in the svgContainer 3350 * the JSON container and the SVG container have to be supplied so other svg containers (like the legend) can use this. 3351 * 3352 * @param {string} elementType 3353 * @param {Object} JSONcontainer 3354 * @param {Object} svgContainer 3355 * @returns {Element} 3356 * @private 3357 */ 3358exports.getSVGElement = function (elementType, JSONcontainer, svgContainer) { 3359 var element; 3360 // allocate SVG element, if it doesnt yet exist, create one. 3361 if (JSONcontainer.hasOwnProperty(elementType)) { 3362 // this element has been created before 3363 // check if there is an redundant element 3364 if (JSONcontainer[elementType].redundant.length > 0) { 3365 element = JSONcontainer[elementType].redundant[0]; 3366 JSONcontainer[elementType].redundant.shift(); 3367 } else { 3368 // create a new element and add it to the SVG 3369 element = document.createElementNS('http://www.w3.org/2000/svg', elementType); 3370 svgContainer.appendChild(element); 3371 } 3372 } else { 3373 // create a new element and add it to the SVG, also create a new object in the svgElements to keep track of it. 3374 element = document.createElementNS('http://www.w3.org/2000/svg', elementType); 3375 JSONcontainer[elementType] = { used: [], redundant: [] }; 3376 svgContainer.appendChild(element); 3377 } 3378 JSONcontainer[elementType].used.push(element); 3379 return element; 3380}; 3381 3382/** 3383 * Allocate or generate an SVG element if needed. Store a reference to it in the JSON container and draw it in the svgContainer 3384 * the JSON container and the SVG container have to be supplied so other svg containers (like the legend) can use this. 3385 * 3386 * @param {string} elementType 3387 * @param {Object} JSONcontainer 3388 * @param {Element} DOMContainer 3389 * @param {Element} insertBefore 3390 * @returns {*} 3391 */ 3392exports.getDOMElement = function (elementType, JSONcontainer, DOMContainer, insertBefore) { 3393 var element; 3394 // allocate DOM element, if it doesnt yet exist, create one. 3395 if (JSONcontainer.hasOwnProperty(elementType)) { 3396 // this element has been created before 3397 // check if there is an redundant element 3398 if (JSONcontainer[elementType].redundant.length > 0) { 3399 element = JSONcontainer[elementType].redundant[0]; 3400 JSONcontainer[elementType].redundant.shift(); 3401 } else { 3402 // create a new element and add it to the SVG 3403 element = document.createElement(elementType); 3404 if (insertBefore !== undefined) { 3405 DOMContainer.insertBefore(element, insertBefore); 3406 } else { 3407 DOMContainer.appendChild(element); 3408 } 3409 } 3410 } else { 3411 // create a new element and add it to the SVG, also create a new object in the svgElements to keep track of it. 3412 element = document.createElement(elementType); 3413 JSONcontainer[elementType] = { used: [], redundant: [] }; 3414 if (insertBefore !== undefined) { 3415 DOMContainer.insertBefore(element, insertBefore); 3416 } else { 3417 DOMContainer.appendChild(element); 3418 } 3419 } 3420 JSONcontainer[elementType].used.push(element); 3421 return element; 3422}; 3423 3424/** 3425 * Draw a point object. This is a separate function because it can also be called by the legend. 3426 * The reason the JSONcontainer and the target SVG svgContainer have to be supplied is so the legend can use these functions 3427 * as well. 3428 * 3429 * @param {number} x 3430 * @param {number} y 3431 * @param {Object} groupTemplate: A template containing the necessary information to draw the datapoint e.g., {style: 'circle', size: 5, className: 'className' } 3432 * @param {Object} JSONcontainer 3433 * @param {Object} svgContainer 3434 * @param {Object} labelObj 3435 * @returns {vis.PointItem} 3436 */ 3437exports.drawPoint = function (x, y, groupTemplate, JSONcontainer, svgContainer, labelObj) { 3438 var point; 3439 if (groupTemplate.style == 'circle') { 3440 point = exports.getSVGElement('circle', JSONcontainer, svgContainer); 3441 point.setAttributeNS(null, "cx", x); 3442 point.setAttributeNS(null, "cy", y); 3443 point.setAttributeNS(null, "r", 0.5 * groupTemplate.size); 3444 } else { 3445 point = exports.getSVGElement('rect', JSONcontainer, svgContainer); 3446 point.setAttributeNS(null, "x", x - 0.5 * groupTemplate.size); 3447 point.setAttributeNS(null, "y", y - 0.5 * groupTemplate.size); 3448 point.setAttributeNS(null, "width", groupTemplate.size); 3449 point.setAttributeNS(null, "height", groupTemplate.size); 3450 } 3451 3452 if (groupTemplate.styles !== undefined) { 3453 point.setAttributeNS(null, "style", groupTemplate.styles); 3454 } 3455 point.setAttributeNS(null, "class", groupTemplate.className + " vis-point"); 3456 //handle label 3457 3458 3459 if (labelObj) { 3460 var label = exports.getSVGElement('text', JSONcontainer, svgContainer); 3461 if (labelObj.xOffset) { 3462 x = x + labelObj.xOffset; 3463 } 3464 3465 if (labelObj.yOffset) { 3466 y = y + labelObj.yOffset; 3467 } 3468 if (labelObj.content) { 3469 label.textContent = labelObj.content; 3470 } 3471 3472 if (labelObj.className) { 3473 label.setAttributeNS(null, "class", labelObj.className + " vis-label"); 3474 } 3475 label.setAttributeNS(null, "x", x); 3476 label.setAttributeNS(null, "y", y); 3477 } 3478 3479 return point; 3480}; 3481 3482/** 3483 * draw a bar SVG element centered on the X coordinate 3484 * 3485 * @param {number} x 3486 * @param {number} y 3487 * @param {number} width 3488 * @param {number} height 3489 * @param {string} className 3490 * @param {Object} JSONcontainer 3491 * @param {Object} svgContainer 3492 * @param {string} style 3493 */ 3494exports.drawBar = function (x, y, width, height, className, JSONcontainer, svgContainer, style) { 3495 if (height != 0) { 3496 if (height < 0) { 3497 height *= -1; 3498 y -= height; 3499 } 3500 var rect = exports.getSVGElement('rect', JSONcontainer, svgContainer); 3501 rect.setAttributeNS(null, "x", x - 0.5 * width); 3502 rect.setAttributeNS(null, "y", y); 3503 rect.setAttributeNS(null, "width", width); 3504 rect.setAttributeNS(null, "height", height); 3505 rect.setAttributeNS(null, "class", className); 3506 if (style) { 3507 rect.setAttributeNS(null, "style", style); 3508 } 3509 } 3510}; 3511 3512/***/ }), 3513/* 15 */ 3514/***/ (function(module, exports, __webpack_require__) { 3515 3516"use strict"; 3517 3518 3519Object.defineProperty(exports, "__esModule", { 3520 value: true 3521}); 3522exports.printStyle = undefined; 3523 3524var _stringify = __webpack_require__(19); 3525 3526var _stringify2 = _interopRequireDefault(_stringify); 3527 3528var _typeof2 = __webpack_require__(6); 3529 3530var _typeof3 = _interopRequireDefault(_typeof2); 3531 3532var _keys = __webpack_require__(8); 3533 3534var _keys2 = _interopRequireDefault(_keys); 3535 3536var _classCallCheck2 = __webpack_require__(0); 3537 3538var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); 3539 3540var _createClass2 = __webpack_require__(1); 3541 3542var _createClass3 = _interopRequireDefault(_createClass2); 3543 3544function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 3545 3546var util = __webpack_require__(2); 3547 3548var errorFound = false;
3549var allOptions = void 0; 3550var printStyle = 'background: #FFeeee; color: #dd0000'; 3551/** 3552 * Used to validate options. 3553 */ 3554 3555var Validator = function () { 3556 /** 3557 * @ignore 3558 */ 3559 function Validator() { 3560 (0, _classCallCheck3['default'])(this, Validator); 3561 } 3562 3563 /** 3564 * Main function to be called 3565 * @param {Object} options 3566 * @param {Object} referenceOptions 3567 * @param {Object} subObject 3568 * @returns {boolean} 3569 * @static 3570 */ 3571 3572 3573 (0, _createClass3['default'])(Validator, null, [{ 3574 key: 'validate', 3575 value: function validate(options, referenceOptions, subObject) { 3576 errorFound = false; 3577 allOptions = referenceOptions; 3578 var usedOptions = referenceOptions; 3579 if (subObject !== undefined) { 3580 usedOptions = referenceOptions[subObject]; 3581 } 3582 Validator.parse(options, usedOptions, []); 3583 return errorFound; 3584 } 3585 3586 /** 3587 * Will traverse an object recursively and check every value 3588 * @param {Object} options 3589 * @param {Object} referenceOptions 3590 * @param {array} path | where to look for the actual option 3591 * @static 3592 */ 3593 3594 }, { 3595 key: 'parse', 3596 value: function parse(options, referenceOptions, path) { 3597 for (var option in options) { 3598 if (options.hasOwnProperty(option)) { 3599 Validator.check(option, options, referenceOptions, path); 3600 } 3601 } 3602 } 3603 3604 /** 3605 * Check every value. If the value is an object, call the parse function on that object. 3606 * @param {string} option 3607 * @param {Object} options 3608 * @param {Object} referenceOptions 3609 * @param {array} path | where to look for the actual option 3610 * @static 3611 */ 3612 3613 }, { 3614 key: 'check', 3615 value: function check(option, options, referenceOptions, path) { 3616 if (referenceOptions[option] === undefined && referenceOptions.__any__ === undefined) { 3617 Validator.getSuggestion(option, referenceOptions, path); 3618 return; 3619 } 3620 3621 var referenceOption = option; 3622 var is_object = true; 3623 3624 if (referenceOptions[option] === undefined && referenceOptions.__any__ !== undefined) { 3625 // NOTE: This only triggers if the __any__ is in the top level of the options object. 3626 // THAT'S A REALLY BAD PLACE TO ALLOW IT!!!! 3627 // TODO: Examine if needed, remove if possible 3628 3629 // __any__ is a wildcard. Any value is accepted and will be further analysed by reference. 3630 referenceOption = '__any__'; 3631 3632 // if the any-subgroup is not a predefined object in the configurator, 3633 // we do not look deeper into the object. 3634 is_object = Validator.getType(options[option]) === 'object'; 3635 } else { 3636 // Since all options in the reference are objects, we can check whether 3637 // they are supposed to be the object to look for the __type__ field. 3638 // if this is an object, we check if the correct type has been supplied to account for shorthand options. 3639 } 3640 3641 var refOptionObj = referenceOptions[referenceOption]; 3642 if (is_object && refOptionObj.__type__ !== undefined) { 3643 refOptionObj = refOptionObj.__type__; 3644 } 3645 3646 Validator.checkFields(option, options, referenceOptions, referenceOption, refOptionObj, path); 3647 } 3648 3649 /** 3650 * 3651 * @param {string} option | the option property 3652 * @param {Object} options | The supplied options object 3653 * @param {Object} referenceOptions | The reference options containing all options and their allowed formats 3654 * @param {string} referenceOption | Usually this is the same as option, except when handling an __any__ tag. 3655 * @param {string} refOptionObj | This is the type object from the reference options 3656 * @param {Array} path | where in the object is the option 3657 * @static 3658 */ 3659 3660 }, { 3661 key: 'checkFields', 3662 value: function checkFields(option, options, referenceOptions, referenceOption, refOptionObj, path) { 3663 var log = function log(message) { 3664 console.log('%c' + message + Validator.printLocation(path, option), printStyle); 3665 }; 3666 3667 var optionType = Validator.getType(options[option]); 3668 var refOptionType = refOptionObj[optionType]; 3669 3670 if (refOptionType !== undefined) { 3671 // if the type is correct, we check if it is supposed to be one of a few select values 3672 if (Validator.getType(refOptionType) === 'array' && refOptionType.indexOf(options[option]) === -1) { 3673 log('Invalid option detected in "' + option + '".' + ' Allowed values are:' + Validator.print(refOptionType) + ' not "' + options[option] + '". '); 3674 errorFound = true; 3675 } else if (optionType === 'object' && referenceOption !== "__any__") { 3676 path = util.copyAndExtendArray(path, option); 3677 Validator.parse(options[option], referenceOptions[referenceOption], path); 3678 } 3679 } else if (refOptionObj['any'] === undefined) { 3680 // type of the field is incorrect and the field cannot be any 3681 log('Invalid type received for "' + option + '". Expected: ' + Validator.print((0, _keys2['default'])(refOptionObj)) + '. Received [' + optionType + '] "' + options[option] + '"'); 3682 errorFound = true; 3683 } 3684 } 3685 3686 /** 3687 * 3688 * @param {Object|boolean|number|string|Array.<number>|Date|Node|Moment|undefined|null} object 3689 * @returns {string} 3690 * @static 3691 */ 3692 3693 }, { 3694 key: 'getType', 3695 value: function getType(object) { 3696 var type = typeof object === 'undefined' ? 'undefined' : (0, _typeof3['default'])(object); 3697 3698 if (type === 'object') { 3699 if (object === null) { 3700 return 'null';
3701 } 3702 if (object instanceof Boolean) { 3703 return 'boolean'; 3704 } 3705 if (object instanceof Number) { 3706 return 'number'; 3707 } 3708 if (object instanceof String) { 3709 return 'string'; 3710 } 3711 if (Array.isArray(object)) { 3712 return 'array'; 3713 } 3714 if (object instanceof Date) { 3715 return 'date'; 3716 } 3717 if (object.nodeType !== undefined) { 3718 return 'dom'; 3719 } 3720 if (object._isAMomentObject === true) { 3721 return 'moment'; 3722 } 3723 return 'object'; 3724 } else if (type === 'number') { 3725 return 'number'; 3726 } else if (type === 'boolean') { 3727 return 'boolean'; 3728 } else if (type === 'string') { 3729 return 'string'; 3730 } else if (type === undefined) { 3731 return 'undefined'; 3732 } 3733 return type; 3734 } 3735 3736 /** 3737 * @param {string} option 3738 * @param {Object} options 3739 * @param {Array.<string>} path 3740 * @static 3741 */ 3742 3743 }, { 3744 key: 'getSuggestion', 3745 value: function getSuggestion(option, options, path) { 3746 var localSearch = Validator.findInOptions(option, options, path, false); 3747 var globalSearch = Validator.findInOptions(option, allOptions, [], true); 3748 3749 var localSearchThreshold = 8; 3750 var globalSearchThreshold = 4; 3751 3752 var msg = void 0; 3753 if (localSearch.indexMatch !== undefined) { 3754 msg = ' in ' + Validator.printLocation(localSearch.path, option, '') + 'Perhaps it was incomplete? Did you mean: "' + localSearch.indexMatch + '"?\n\n'; 3755 } else if (globalSearch.distance <= globalSearchThreshold && localSearch.distance > globalSearch.distance) { 3756 msg = ' in ' + Validator.printLocation(localSearch.path, option, '') + 'Perhaps it was misplaced? Matching option found at: ' + Validator.printLocation(globalSearch.path, globalSearch.closestMatch, ''); 3757 } else if (localSearch.distance <= localSearchThreshold) { 3758 msg = '. Did you mean "' + localSearch.closestMatch + '"?' + Validator.printLocation(localSearch.path, option); 3759 } else { 3760 msg = '. Did you mean one of these: ' + Validator.print((0, _keys2['default'])(options)) + Validator.printLocation(path, option); 3761 } 3762 3763 console.log('%cUnknown option detected: "' + option + '"' + msg, printStyle); 3764 errorFound = true; 3765 } 3766 3767 /** 3768 * traverse the options in search for a match. 3769 * @param {string} option 3770 * @param {Object} options 3771 * @param {Array} path | where to look for the actual option 3772 * @param {boolean} [recursive=false] 3773 * @returns {{closestMatch: string, path: Array, distance: number}} 3774 * @static 3775 */ 3776 3777 }, { 3778 key: 'findInOptions', 3779 value: function findInOptions(option, options, path) { 3780 var recursive = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false; 3781 3782 var min = 1e9; 3783 var closestMatch = ''; 3784 var closestMatchPath = []; 3785 var lowerCaseOption = option.toLowerCase(); 3786 var indexMatch = undefined; 3787 for (var op in options) { 3788 // eslint-disable-line guard-for-in 3789 var distance = void 0; 3790 if (options[op].__type__ !== undefined && recursive === true) { 3791 var result = Validator.findInOptions(option, options[op], util.copyAndExtendArray(path, op)); 3792 if (min > result.distance) { 3793 closestMatch = result.closestMatch; 3794 closestMatchPath = result.path; 3795 min = result.distance; 3796 indexMatch = result.indexMatch; 3797 } 3798 } else { 3799 if (op.toLowerCase().indexOf(lowerCaseOption) !== -1) { 3800 indexMatch = op; 3801 } 3802 distance = Validator.levenshteinDistance(option, op); 3803 if (min > distance) { 3804 closestMatch = op; 3805 closestMatchPath = util.copyArray(path); 3806 min = distance; 3807 } 3808 } 3809 } 3810 return { closestMatch: closestMatch, path: closestMatchPath, distance: min, indexMatch: indexMatch }; 3811 } 3812 3813 /** 3814 * @param {Array.<string>} path 3815 * @param {Object} option 3816 * @param {string} prefix 3817 * @returns {String} 3818 * @static 3819 */ 3820 3821 }, { 3822 key: 'printLocation', 3823 value: function printLocation(path, option) { 3824 var prefix = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 'Problem value found at: \n'; 3825 3826 var str = '\n\n' + prefix + 'options = {\n'; 3827 for (var i = 0; i < path.length; i++) { 3828 for (var j = 0; j < i + 1; j++) { 3829 str += ' '; 3830 } 3831 str += path[i] + ': {\n'; 3832 } 3833 for (var _j = 0; _j < path.length + 1; _j++) { 3834 str += ' '; 3835 } 3836 str += option + '\n'; 3837 for (var _i = 0; _i < path.length + 1; _i++) { 3838 for (var _j2 = 0; _j2 < path.length - _i; _j2++) { 3839 str += ' '; 3840 } 3841 str += '}\n'; 3842 } 3843 return str + '\n\n'; 3844 } 3845 3846 /** 3847 * @param {Object} options 3848 * @returns {String} 3849 * @static 3850 */ 3851 3852 }, { 3853 key: 'print', 3854 value: function print(options) { 3855 return (0, _stringify2['default'])(options).replace(/(\")|(\[)|(\])|(,"__type__")/g, "").replace(/(\,)/g, ', '); 3856 } 3857 3858 /**
3859 * Compute the edit distance between the two given strings 3860 * http://en.wikibooks.org/wiki/Algorithm_Implementation/Strings/Levenshtein_distance#JavaScript 3861 * 3862 * Copyright (c) 2011 Andrei Mackenzie 3863 * 3864 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 3865 * 3866 * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 3867 * 3868 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 3869 * 3870 * @param {string} a 3871 * @param {string} b 3872 * @returns {Array.<Array.<number>>}} 3873 * @static 3874 */ 3875 3876 }, { 3877 key: 'levenshteinDistance', 3878 value: function levenshteinDistance(a, b) { 3879 if (a.length === 0) return b.length; 3880 if (b.length === 0) return a.length; 3881 3882 var matrix = []; 3883 3884 // increment along the first column of each row 3885 var i; 3886 for (i = 0; i <= b.length; i++) { 3887 matrix[i] = [i]; 3888 } 3889 3890 // increment each column in the first row 3891 var j; 3892 for (j = 0; j <= a.length; j++) { 3893 matrix[0][j] = j; 3894 } 3895 3896 // Fill in the rest of the matrix 3897 for (i = 1; i <= b.length; i++) { 3898 for (j = 1; j <= a.length; j++) { 3899 if (b.charAt(i - 1) == a.charAt(j - 1)) { 3900 matrix[i][j] = matrix[i - 1][j - 1]; 3901 } else { 3902 matrix[i][j] = Math.min(matrix[i - 1][j - 1] + 1, // substitution 3903 Math.min(matrix[i][j - 1] + 1, // insertion 3904 matrix[i - 1][j] + 1)); // deletion 3905 } 3906 } 3907 } 3908 3909 return matrix[b.length][a.length]; 3910 } 3911 }]); 3912 return Validator; 3913}(); 3914 3915exports['default'] = Validator; 3916exports.printStyle = printStyle; 3917 3918/***/ }), 3919/* 16 */ 3920/***/ (function(module, exports, __webpack_require__) { 3921 3922"use strict"; 3923 3924 3925var util = __webpack_require__(2); 3926 3927/** 3928 * Prototype for visual components 3929 * @param {{dom: Object, domProps: Object, emitter: Emitter, range: Range}} [body] 3930 * @param {Object} [options] 3931 */ 3932function Component(body, options) { 3933 // eslint-disable-line no-unused-vars 3934 this.options = null; 3935 this.props = null; 3936} 3937 3938/** 3939 * Set options for the component. The new options will be merged into the 3940 * current options. 3941 * @param {Object} options 3942 */ 3943Component.prototype.setOptions = function (options) { 3944 if (options) { 3945 util.extend(this.options, options); 3946 } 3947}; 3948 3949/** 3950 * Repaint the component 3951 * @return {boolean} Returns true if the component is resized 3952 */ 3953Component.prototype.redraw = function () { 3954 // should be implemented by the component 3955 return false; 3956}; 3957 3958/** 3959 * Destroy the component. Cleanup DOM and event listeners 3960 */ 3961Component.prototype.destroy = function () { 3962 // should be implemented by the component 3963}; 3964 3965/** 3966 * Test whether the component is resized since the last time _isResized() was 3967 * called. 3968 * @return {Boolean} Returns true if the component is resized 3969 * @protected 3970 */ 3971Component.prototype._isResized = function () { 3972 var resized = this.props._previousWidth !== this.props.width || this.props._previousHeight !== this.props.height; 3973 3974 this.props._previousWidth = this.props.width; 3975 this.props._previousHeight = this.props.height; 3976 3977 return resized; 3978}; 3979 3980module.exports = Component; 3981 3982/***/ }), 3983/* 17 */ 3984/***/ (function(module, exports, __webpack_require__) { 3985 3986var global = __webpack_require__(18); 3987var core = __webpack_require__(7); 3988var ctx = __webpack_require__(80); 3989var hide = __webpack_require__(26); 3990var PROTOTYPE = 'prototype'; 3991
3992var $export = function (type, name, source) { 3993 var IS_FORCED = type & $export.F; 3994 var IS_GLOBAL = type & $export.G; 3995 var IS_STATIC = type & $export.S; 3996 var IS_PROTO = type & $export.P; 3997 var IS_BIND = type & $export.B; 3998 var IS_WRAP = type & $export.W; 3999 var exports = IS_GLOBAL ? core : core[name] || (core[name] = {}); 4000 var expProto = exports[PROTOTYPE]; 4001 var target = IS_GLOBAL ? global : IS_STATIC ? global[name] : (global[name] || {})[PROTOTYPE]; 4002 var key, own, out; 4003 if (IS_GLOBAL) source = name; 4004 for (key in source) { 4005 // contains in native 4006 own = !IS_FORCED && target && target[key] !== undefined; 4007 if (own && key in exports) continue; 4008 // export native or passed 4009 out = own ? target[key] : source[key]; 4010 // prevent global pollution for namespaces 4011 exports[key] = IS_GLOBAL && typeof target[key] != 'function' ? source[key] 4012 // bind timers to global for call from export context 4013 : IS_BIND && own ? ctx(out, global) 4014 // wrap global constructors for prevent change them in library 4015 : IS_WRAP && target[key] == out ? (function (C) { 4016 var F = function (a, b, c) { 4017 if (this instanceof C) { 4018 switch (arguments.length) { 4019 case 0: return new C(); 4020 case 1: return new C(a); 4021 case 2: return new C(a, b); 4022 } return new C(a, b, c); 4023 } return C.apply(this, arguments); 4024 }; 4025 F[PROTOTYPE] = C[PROTOTYPE]; 4026 return F; 4027 // make static versions for prototype methods 4028 })(out) : IS_PROTO && typeof out == 'function' ? ctx(Function.call, out) : out; 4029 // export proto methods to core.%CONSTRUCTOR%.methods.%NAME% 4030 if (IS_PROTO) { 4031 (exports.virtual || (exports.virtual = {}))[key] = out; 4032 // export proto methods to core.%CONSTRUCTOR%.prototype.%NAME% 4033 if (type & $export.R && expProto && !expProto[key]) hide(expProto, key, out); 4034 } 4035 } 4036}; 4037// type bitmap 4038$export.F = 1; // forced 4039$export.G = 2; // global 4040$export.S = 4; // static 4041$export.P = 8; // proto 4042$export.B = 16; // bind 4043$export.W = 32; // wrap 4044$export.U = 64; // safe 4045$export.R = 128; // real proto method for `library` 4046module.exports = $export; 4047 4048 4049/***/ }), 4050/* 18 */ 4051/***/ (function(module, exports) { 4052 4053// https://github.com/zloirock/core-js/issues/86#issuecomment-115759028 4054var global = module.exports = typeof window != 'undefined' && window.Math == Math 4055 ? window : typeof self != 'undefined' && self.Math == Math ? self 4056 // eslint-disable-next-line no-new-func 4057 : Function('return this')(); 4058if (typeof __g == 'number') __g = global; // eslint-disable-line no-undef 4059 4060 4061/***/ }), 4062/* 19 */ 4063/***/ (function(module, exports, __webpack_require__) { 4064 4065module.exports = { "default": __webpack_require__(160), __esModule: true }; 4066 4067/***/ }), 4068/* 20 */ 4069/***/ (function(module, exports, __webpack_require__) { 4070 4071var anObject = __webpack_require__(27); 4072var IE8_DOM_DEFINE = __webpack_require__(81); 4073var toPrimitive = __webpack_require__(53); 4074var dP = Object.defineProperty; 4075 4076exports.f = __webpack_require__(21) ? Object.defineProperty : function defineProperty(O, P, Attributes) { 4077 anObject(O); 4078 P = toPrimitive(P, true); 4079 anObject(Attributes); 4080 if (IE8_DOM_DEFINE) try { 4081 return dP(O, P, Attributes); 4082 } catch (e) { /* empty */ } 4083 if ('get' in Attributes || 'set' in Attributes) throw TypeError('Accessors not supported!'); 4084 if ('value' in Attributes) O[P] = Attributes.value; 4085 return O; 4086}; 4087 4088 4089/***/ }), 4090/* 21 */ 4091/***/ (function(module, exports, __webpack_require__) { 4092 4093// Thank's IE8 for his funny defineProperty 4094module.exports = !__webpack_require__(28)(function () { 4095 return Object.defineProperty({}, 'a', { get: function () { return 7; } }).a != 7; 4096}); 4097 4098 4099/***/ }), 4100/* 22 */ 4101/***/ (function(module, exports) { 4102 4103var hasOwnProperty = {}.hasOwnProperty; 4104module.exports = function (it, key) { 4105 return hasOwnProperty.call(it, key); 4106}; 4107 4108 4109/***/ }), 4110/* 23 */ 4111/***/ (function(module, exports, __webpack_require__) { 4112 4113"use strict"; 4114 4115 4116Object.defineProperty(exports, "__esModule", { 4117 value: true 4118}); 4119 4120var _typeof2 = __webpack_require__(6); 4121 4122var _typeof3 = _interopRequireDefault(_typeof2); 4123 4124var _classCallCheck2 = __webpack_require__(0); 4125 4126var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); 4127 4128var _createClass2 = __webpack_require__(1); 4129 4130var _createClass3 = _interopRequireDefault(_createClass2); 4131 4132function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 4133 4134/** 4135 * The Base class for all Nodes. 4136 */ 4137var NodeBase = function () { 4138 /** 4139 * @param {Object} options 4140 * @param {Object} body 4141 * @param {Label} labelModule 4142 */ 4143 function NodeBase(options, body, labelModule) { 4144 (0, _classCallCheck3['default'])(this, NodeBase); 4145 4146 this.body = body; 4147 this.labelModule = labelModule; 4148 this.setOptions(options); 4149 this.top = undefined; 4150 this.left = undefined; 4151 this.height = undefined; 4152 this.width = undefined; 4153 this.radius = undefined; 4154 this.margin = undefined; 4155 this.refreshNeeded = true; 4156 this.boundingBox = { top: 0, left: 0, right: 0, bottom: 0 }; 4157 } 4158 4159 /** 4160 * 4161 * @param {Object} options 4162 */ 4163 4164 4165 (0, _createClass3['default'])(NodeBase, [{ 4166 key: 'setOptions', 4167 value: function setOptions(options) { 4168 this.options = options; 4169 } 4170 4171 /** 4172 * 4173 * @param {Label} labelModule 4174 * @private 4175 */ 4176 4177 }, { 4178 key: '_setMargins', 4179 value: function _setMargins(labelModule) { 4180 this.margin = {}; 4181 if (this.options.margin) { 4182 if ((0, _typeof3['default'])(this.options.margin) == 'object') { 4183 this.margin.top = this.options.margin.top; 4184 this.margin.right = this.options.margin.right; 4185 this.margin.bottom = this.options.margin.bottom; 4186 this.margin.left = this.options.margin.left; 4187 } else { 4188 this.margin.top = this.options.margin; 4189 this.margin.right = this.options.margin; 4190 this.margin.bottom = this.options.margin; 4191 this.margin.left = this.options.margin; 4192 } 4193 } 4194 labelModule.adjustSizes(this.margin); 4195 } 4196 4197 /** 4198 * 4199 * @param {CanvasRenderingContext2D} ctx 4200 * @param {number} angle 4201 * @returns {number} 4202 * @private 4203 */ 4204 4205 }, { 4206 key: '_distanceToBorder', 4207 value: function _distanceToBorder(ctx, angle) { 4208 var borderWidth = this.options.borderWidth; 4209 this.resize(ctx); 4210 return Math.min(Math.abs(this.width / 2 / Math.cos(angle)), Math.abs(this.height / 2 / Math.sin(angle))) + borderWidth; 4211 } 4212 4213 /** 4214 * 4215 * @param {CanvasRenderingContext2D} ctx 4216 * @param {{toArrow: boolean, toArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), toArrowType: *, middleArrow: boolean, middleArrowScale: (number|allOptions.edges.arrows.middle.scaleFactor|{number}|Array), middleArrowType: (allOptions.edges.arrows.middle.type|{string}|string|*), fromArrow: boolean, fromArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), fromArrowType: *, arrowStrikethrough: (*|boolean|allOptions.edges.arrowStrikethrough|{boolean}), color: undefined, inheritsColor: (string|string|string|allOptions.edges.color.inherit|{string, boolean}|Array|*), opacity: *, hidden: *, length: *, shadow: *, shadowColor: *, shadowSize: *, shadowX: *, shadowY: *, dashes: (*|boolean|Array|allOptions.edges.dashes|{boolean, array}), width: *}} values 4217 */ 4218 4219 }, { 4220 key: 'enableShadow', 4221 value: function enableShadow(ctx, values) { 4222 if (values.shadow) { 4223 ctx.shadowColor = values.shadowColor; 4224 ctx.shadowBlur = values.shadowSize; 4225 ctx.shadowOffsetX = values.shadowX; 4226 ctx.shadowOffsetY = values.shadowY; 4227 } 4228 } 4229 4230 /** 4231 * 4232 * @param {CanvasRenderingContext2D} ctx 4233 * @param {{toArrow: boolean, toArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), toArrowType: *, middleArrow: boolean, middleArrowScale: (number|allOptions.edges.arrows.middle.scaleFactor|{number}|Array), middleArrowType: (allOptions.edges.arrows.middle.type|{string}|string|*), fromArrow: boolean, fromArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), fromArrowType: *, arrowStrikethrough: (*|boolean|allOptions.edges.arrowStrikethrough|{boolean}), color: undefined, inheritsColor: (string|string|string|allOptions.edges.color.inherit|{string, boolean}|Array|*), opacity: *, hidden: *, length: *, shadow: *, shadowColor: *, shadowSize: *, shadowX: *, shadowY: *, dashes: (*|boolean|Array|allOptions.edges.dashes|{boolean, array}), width: *}} values 4234 */ 4235 4236 }, { 4237 key: 'disableShadow', 4238 value: function disableShadow(ctx, values) { 4239 if (values.shadow) { 4240 ctx.shadowColor = 'rgba(0,0,0,0)'; 4241 ctx.shadowBlur = 0; 4242 ctx.shadowOffsetX = 0; 4243 ctx.shadowOffsetY = 0; 4244 } 4245 } 4246 4247 /** 4248 * 4249 * @param {CanvasRenderingContext2D} ctx 4250 * @param {{toArrow: boolean, toArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), toArrowType: *, middleArrow: boolean, middleArrowScale: (number|allOptions.edges.arrows.middle.scaleFactor|{number}|Array), middleArrowType: (allOptions.edges.arrows.middle.type|{string}|string|*), fromArrow: boolean, fromArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), fromArrowType: *, arrowStrikethrough: (*|boolean|allOptions.edges.arrowStrikethrough|{boolean}), color: undefined, inheritsColor: (string|string|string|allOptions.edges.color.inherit|{string, boolean}|Array|*), opacity: *, hidden: *, length: *, shadow: *, shadowColor: *, shadowSize: *, shadowX: *, shadowY: *, dashes: (*|boolean|Array|allOptions.edges.dashes|{boolean, array}), width: *}} values 4251 */ 4252 4253 }, { 4254 key: 'enableBorderDashes', 4255 value: function enableBorderDashes(ctx, values) { 4256 if (values.borderDashes !== false) { 4257 if (ctx.setLineDash !== undefined) { 4258 var dashes = values.borderDashes; 4259 if (dashes === true) { 4260 dashes = [5, 15]; 4261 } 4262 ctx.setLineDash(dashes); 4263 } else { 4264 console.warn("setLineDash is not supported in this browser. The dashed borders cannot be used."); 4265 this.options.shapeProperties.borderDashes = false;
4266 values.borderDashes = false; 4267 } 4268 } 4269 } 4270 4271 /** 4272 * 4273 * @param {CanvasRenderingContext2D} ctx 4274 * @param {{toArrow: boolean, toArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), toArrowType: *, middleArrow: boolean, middleArrowScale: (number|allOptions.edges.arrows.middle.scaleFactor|{number}|Array), middleArrowType: (allOptions.edges.arrows.middle.type|{string}|string|*), fromArrow: boolean, fromArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), fromArrowType: *, arrowStrikethrough: (*|boolean|allOptions.edges.arrowStrikethrough|{boolean}), color: undefined, inheritsColor: (string|string|string|allOptions.edges.color.inherit|{string, boolean}|Array|*), opacity: *, hidden: *, length: *, shadow: *, shadowColor: *, shadowSize: *, shadowX: *, shadowY: *, dashes: (*|boolean|Array|allOptions.edges.dashes|{boolean, array}), width: *}} values 4275 */ 4276 4277 }, { 4278 key: 'disableBorderDashes', 4279 value: function disableBorderDashes(ctx, values) { 4280 if (values.borderDashes !== false) { 4281 if (ctx.setLineDash !== undefined) { 4282 ctx.setLineDash([0]); 4283 } else { 4284 console.warn("setLineDash is not supported in this browser. The dashed borders cannot be used."); 4285 this.options.shapeProperties.borderDashes = false; 4286 values.borderDashes = false; 4287 } 4288 } 4289 } 4290 4291 /** 4292 * Determine if the shape of a node needs to be recalculated. 4293 * 4294 * @param {boolean} selected 4295 * @param {boolean} hover 4296 * @returns {boolean} 4297 * @protected 4298 */ 4299 4300 }, { 4301 key: 'needsRefresh', 4302 value: function needsRefresh(selected, hover) { 4303 if (this.refreshNeeded === true) { 4304 // This is probably not the best location to reset this member. 4305 // However, in the current logic, it is the most convenient one. 4306 this.refreshNeeded = false; 4307 return true; 4308 } 4309 4310 return this.width === undefined || this.labelModule.differentState(selected, hover); 4311 } 4312 4313 /** 4314 * 4315 * @param {CanvasRenderingContext2D} ctx 4316 * @param {{toArrow: boolean, toArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), toArrowType: *, middleArrow: boolean, middleArrowScale: (number|allOptions.edges.arrows.middle.scaleFactor|{number}|Array), middleArrowType: (allOptions.edges.arrows.middle.type|{string}|string|*), fromArrow: boolean, fromArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), fromArrowType: *, arrowStrikethrough: (*|boolean|allOptions.edges.arrowStrikethrough|{boolean}), color: undefined, inheritsColor: (string|string|string|allOptions.edges.color.inherit|{string, boolean}|Array|*), opacity: *, hidden: *, length: *, shadow: *, shadowColor: *, shadowSize: *, shadowX: *, shadowY: *, dashes: (*|boolean|Array|allOptions.edges.dashes|{boolean, array}), width: *}} values 4317 */ 4318 4319 }, { 4320 key: 'initContextForDraw', 4321 value: function initContextForDraw(ctx, values) { 4322 var borderWidth = values.borderWidth / this.body.view.scale; 4323 4324 ctx.lineWidth = Math.min(this.width, borderWidth); 4325 ctx.strokeStyle = values.borderColor; 4326 ctx.fillStyle = values.color; 4327 } 4328 4329 /** 4330 * 4331 * @param {CanvasRenderingContext2D} ctx 4332 * @param {{toArrow: boolean, toArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), toArrowType: *, middleArrow: boolean, middleArrowScale: (number|allOptions.edges.arrows.middle.scaleFactor|{number}|Array), middleArrowType: (allOptions.edges.arrows.middle.type|{string}|string|*), fromArrow: boolean, fromArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), fromArrowType: *, arrowStrikethrough: (*|boolean|allOptions.edges.arrowStrikethrough|{boolean}), color: undefined, inheritsColor: (string|string|string|allOptions.edges.color.inherit|{string, boolean}|Array|*), opacity: *, hidden: *, length: *, shadow: *, shadowColor: *, shadowSize: *, shadowX: *, shadowY: *, dashes: (*|boolean|Array|allOptions.edges.dashes|{boolean, array}), width: *}} values 4333 */ 4334 4335 }, { 4336 key: 'performStroke', 4337 value: function performStroke(ctx, values) { 4338 var borderWidth = values.borderWidth / this.body.view.scale; 4339 4340 //draw dashed border if enabled, save and restore is required for firefox not to crash on unix. 4341 ctx.save(); 4342 // if borders are zero width, they will be drawn with width 1 by default. This prevents that 4343 if (borderWidth > 0) { 4344 this.enableBorderDashes(ctx, values); 4345 //draw the border 4346 ctx.stroke(); 4347 //disable dashed border for other elements 4348 this.disableBorderDashes(ctx, values); 4349 }
4350 ctx.restore(); 4351 } 4352 4353 /** 4354 * 4355 * @param {CanvasRenderingContext2D} ctx 4356 * @param {{toArrow: boolean, toArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), toArrowType: *, middleArrow: boolean, middleArrowScale: (number|allOptions.edges.arrows.middle.scaleFactor|{number}|Array), middleArrowType: (allOptions.edges.arrows.middle.type|{string}|string|*), fromArrow: boolean, fromArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), fromArrowType: *, arrowStrikethrough: (*|boolean|allOptions.edges.arrowStrikethrough|{boolean}), color: undefined, inheritsColor: (string|string|string|allOptions.edges.color.inherit|{string, boolean}|Array|*), opacity: *, hidden: *, length: *, shadow: *, shadowColor: *, shadowSize: *, shadowX: *, shadowY: *, dashes: (*|boolean|Array|allOptions.edges.dashes|{boolean, array}), width: *}} values 4357 */ 4358 4359 }, { 4360 key: 'performFill', 4361 value: function performFill(ctx, values) { 4362 // draw shadow if enabled 4363 this.enableShadow(ctx, values); 4364 // draw the background 4365 ctx.fill(); 4366 // disable shadows for other elements. 4367 this.disableShadow(ctx, values); 4368 4369 this.performStroke(ctx, values); 4370 } 4371 4372 /** 4373 * 4374 * @param {number} margin 4375 * @private 4376 */ 4377 4378 }, { 4379 key: '_addBoundingBoxMargin', 4380 value: function _addBoundingBoxMargin(margin) { 4381 this.boundingBox.left -= margin; 4382 this.boundingBox.top -= margin; 4383 this.boundingBox.bottom += margin; 4384 this.boundingBox.right += margin; 4385 } 4386 4387 /** 4388 * Actual implementation of this method call. 4389 * 4390 * Doing it like this makes it easier to override 4391 * in the child classes. 4392 * 4393 * @param {number} x width 4394 * @param {number} y height 4395 * @param {CanvasRenderingContext2D} ctx 4396 * @param {boolean} selected 4397 * @param {boolean} hover 4398 * @private 4399 */ 4400 4401 }, { 4402 key: '_updateBoundingBox', 4403 value: function _updateBoundingBox(x, y, ctx, selected, hover) { 4404 if (ctx !== undefined) { 4405 this.resize(ctx, selected, hover); 4406 } 4407 4408 this.left = x - this.width / 2; 4409 this.top = y - this.height / 2; 4410 4411 this.boundingBox.left = this.left; 4412 this.boundingBox.top = this.top; 4413 this.boundingBox.bottom = this.top + this.height; 4414 this.boundingBox.right = this.left + this.width; 4415 } 4416 4417 /** 4418 * Default implementation of this method call. 4419 * This acts as a stub which can be overridden. 4420 * 4421 * @param {number} x width 4422 * @param {number} y height 4423 * @param {CanvasRenderingContext2D} ctx 4424 * @param {boolean} selected 4425 * @param {boolean} hover 4426 */ 4427 4428 }, { 4429 key: 'updateBoundingBox', 4430 value: function updateBoundingBox(x, y, ctx, selected, hover) { 4431 this._updateBoundingBox(x, y, ctx, selected, hover); 4432 } 4433 4434 /** 4435 * Determine the dimensions to use for nodes with an internal label 4436 * 4437 * Currently, these are: Circle, Ellipse, Database, Box 4438 * The other nodes have external labels, and will not call this method 4439 * 4440 * If there is no label, decent default values are supplied. 4441 * 4442 * @param {CanvasRenderingContext2D} ctx 4443 * @param {boolean} [selected] 4444 * @param {boolean} [hover] 4445 * @returns {{width:number, height:number}} 4446 */ 4447 4448 }, { 4449 key: 'getDimensionsFromLabel', 4450 value: function getDimensionsFromLabel(ctx, selected, hover) { 4451 // NOTE: previously 'textSize' was not put in 'this' for Ellipse 4452 // TODO: examine the consequences. 4453 this.textSize = this.labelModule.getTextSize(ctx, selected, hover); 4454 var width = this.textSize.width; 4455 var height = this.textSize.height; 4456 4457 var DEFAULT_SIZE = 14; 4458 if (width === 0) { 4459 // This happens when there is no label text set 4460 width = DEFAULT_SIZE; // use a decent default 4461 height = DEFAULT_SIZE; // if width zero, then height also always zero 4462 } 4463 4464 return { width: width, height: height }; 4465 } 4466 }]); 4467 return NodeBase; 4468}(); 4469 4470exports['default'] = NodeBase; 4471 4472/***/ }), 4473/* 24 */ 4474/***/ (function(module, exports, __webpack_require__) { 4475 4476"use strict"; 4477 4478 4479Object.defineProperty(exports, "__esModule", { 4480 value: true 4481}); 4482 4483var _getPrototypeOf = __webpack_require__(3); 4484 4485var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf); 4486 4487var _classCallCheck2 = __webpack_require__(0); 4488 4489var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); 4490 4491var _createClass2 = __webpack_require__(1); 4492 4493var _createClass3 = _interopRequireDefault(_createClass2); 4494 4495var _possibleConstructorReturn2 = __webpack_require__(4); 4496 4497var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2); 4498 4499var _inherits2 = __webpack_require__(5); 4500 4501var _inherits3 = _interopRequireDefault(_inherits2); 4502 4503var _NodeBase2 = __webpack_require__(23); 4504 4505var _NodeBase3 = _interopRequireDefault(_NodeBase2); 4506 4507function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 4508 4509/**
4510 * Base class for constructing Node/Cluster Shapes. 4511 * 4512 * @extends NodeBase 4513 */ 4514var ShapeBase = function (_NodeBase) { 4515 (0, _inherits3['default'])(ShapeBase, _NodeBase); 4516 4517 /** 4518 * @param {Object} options 4519 * @param {Object} body 4520 * @param {Label} labelModule 4521 */ 4522 function ShapeBase(options, body, labelModule) { 4523 (0, _classCallCheck3['default'])(this, ShapeBase); 4524 return (0, _possibleConstructorReturn3['default'])(this, (ShapeBase.__proto__ || (0, _getPrototypeOf2['default'])(ShapeBase)).call(this, options, body, labelModule)); 4525 } 4526 4527 /** 4528 * 4529 * @param {CanvasRenderingContext2D} ctx 4530 * @param {boolean} [selected] 4531 * @param {boolean} [hover] 4532 * @param {Object} [values={size: this.options.size}] 4533 */ 4534 4535 4536 (0, _createClass3['default'])(ShapeBase, [{ 4537 key: 'resize', 4538 value: function resize(ctx) { 4539 var selected = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : this.selected; 4540 var hover = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : this.hover; 4541 var values = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : { size: this.options.size }; 4542 4543 if (this.needsRefresh(selected, hover)) { 4544 this.labelModule.getTextSize(ctx, selected, hover); 4545 var size = 2 * values.size; 4546 this.width = size; 4547 this.height = size; 4548 this.radius = 0.5 * this.width; 4549 } 4550 } 4551 4552 /** 4553 * 4554 * @param {CanvasRenderingContext2D} ctx 4555 * @param {string} shape 4556 * @param {number} sizeMultiplier - Unused! TODO: Remove next major release 4557 * @param {number} x 4558 * @param {number} y 4559 * @param {boolean} selected 4560 * @param {boolean} hover 4561 * @param {{toArrow: boolean, toArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), toArrowType: *, middleArrow: boolean, middleArrowScale: (number|allOptions.edges.arrows.middle.scaleFactor|{number}|Array), middleArrowType: (allOptions.edges.arrows.middle.type|{string}|string|*), fromArrow: boolean, fromArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), fromArrowType: *, arrowStrikethrough: (*|boolean|allOptions.edges.arrowStrikethrough|{boolean}), color: undefined, inheritsColor: (string|string|string|allOptions.edges.color.inherit|{string, boolean}|Array|*), opacity: *, hidden: *, length: *, shadow: *, shadowColor: *, shadowSize: *, shadowX: *, shadowY: *, dashes: (*|boolean|Array|allOptions.edges.dashes|{boolean, array}), width: *}} values 4562 * @private 4563 */ 4564 4565 }, { 4566 key: '_drawShape', 4567 value: function _drawShape(ctx, shape, sizeMultiplier, x, y, selected, hover, values) { 4568 this.resize(ctx, selected, hover, values); 4569 this.left = x - this.width / 2; 4570 this.top = y - this.height / 2; 4571 4572 this.initContextForDraw(ctx, values); 4573 ctx[shape](x, y, values.size); 4574 this.performFill(ctx, values); 4575 4576 if (this.options.label !== undefined) { 4577 // Need to call following here in order to ensure value for `this.labelModule.size.height` 4578 this.labelModule.calculateLabelSize(ctx, selected, hover, x, y, 'hanging'); 4579 var yLabel = y + 0.5 * this.height + 0.5 * this.labelModule.size.height; 4580 this.labelModule.draw(ctx, x, yLabel, selected, hover, 'hanging'); 4581 } 4582 4583 this.updateBoundingBox(x, y); 4584 } 4585 4586 /** 4587 * 4588 * @param {number} x 4589 * @param {number} y 4590 */ 4591 4592 }, { 4593 key: 'updateBoundingBox', 4594 value: function updateBoundingBox(x, y) { 4595 this.boundingBox.top = y - this.options.size; 4596 this.boundingBox.left = x - this.options.size; 4597 this.boundingBox.right = x + this.options.size; 4598 this.boundingBox.bottom = y + this.options.size; 4599 4600 if (this.options.label !== undefined && this.labelModule.size.width > 0) { 4601 this.boundingBox.left = Math.min(this.boundingBox.left, this.labelModule.size.left); 4602 this.boundingBox.right = Math.max(this.boundingBox.right, this.labelModule.size.left + this.labelModule.size.width); 4603 this.boundingBox.bottom = Math.max(this.boundingBox.bottom, this.boundingBox.bottom + this.labelModule.size.height); 4604 } 4605 } 4606 }]); 4607 return ShapeBase; 4608}(_NodeBase3['default']); 4609 4610exports['default'] = ShapeBase; 4611 4612/***/ }), 4613/* 25 */ 4614/***/ (function(module, exports, __webpack_require__) { 4615 4616// to indexed object, toObject with fallback for non-array-like ES3 strings 4617var IObject = __webpack_require__(78); 4618var defined = __webpack_require__(51); 4619module.exports = function (it) { 4620 return IObject(defined(it)); 4621}; 4622 4623 4624/***/ }), 4625/* 26 */ 4626/***/ (function(module, exports, __webpack_require__) { 4627 4628var dP = __webpack_require__(20); 4629var createDesc = __webpack_require__(39); 4630module.exports = __webpack_require__(21) ? function (object, key, value) { 4631 return dP.f(object, key, createDesc(1, value)); 4632} : function (object, key, value) { 4633 object[key] = value; 4634 return object; 4635}; 4636 4637 4638/***/ }), 4639/* 27 */ 4640/***/ (function(module, exports, __webpack_require__) { 4641 4642var isObject = __webpack_require__(32); 4643module.exports = function (it) { 4644 if (!isObject(it)) throw TypeError(it + ' is not an object!'); 4645 return it; 4646}; 4647 4648 4649/***/ }), 4650/* 28 */
4651/***/ (function(module, exports) { 4652 4653module.exports = function (exec) { 4654 try { 4655 return !!exec(); 4656 } catch (e) { 4657 return true; 4658 } 4659}; 4660 4661 4662/***/ }), 4663/* 29 */ 4664/***/ (function(module, exports, __webpack_require__) { 4665 4666module.exports = { "default": __webpack_require__(138), __esModule: true }; 4667 4668/***/ }), 4669/* 30 */ 4670/***/ (function(module, exports, __webpack_require__) { 4671 4672"use strict"; 4673 4674 4675exports.__esModule = true; 4676 4677var _isIterable2 = __webpack_require__(188); 4678 4679var _isIterable3 = _interopRequireDefault(_isIterable2); 4680 4681var _getIterator2 = __webpack_require__(77); 4682 4683var _getIterator3 = _interopRequireDefault(_getIterator2); 4684 4685function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 4686 4687exports.default = function () { 4688 function sliceIterator(arr, i) { 4689 var _arr = []; 4690 var _n = true; 4691 var _d = false; 4692 var _e = undefined; 4693 4694 try { 4695 for (var _i = (0, _getIterator3.default)(arr), _s; !(_n = (_s = _i.next()).done); _n = true) { 4696 _arr.push(_s.value); 4697 4698 if (i && _arr.length === i) break; 4699 } 4700 } catch (err) { 4701 _d = true; 4702 _e = err; 4703 } finally { 4704 try { 4705 if (!_n && _i["return"]) _i["return"](); 4706 } finally { 4707 if (_d) throw _e; 4708 } 4709 } 4710 4711 return _arr; 4712 } 4713 4714 return function (arr, i) { 4715 if (Array.isArray(arr)) { 4716 return arr; 4717 } else if ((0, _isIterable3.default)(Object(arr))) { 4718 return sliceIterator(arr, i); 4719 } else { 4720 throw new TypeError("Invalid attempt to destructure non-iterable instance"); 4721 } 4722 }; 4723}(); 4724 4725/***/ }), 4726/* 31 */ 4727/***/ (function(module, exports) { 4728 4729module.exports = {}; 4730 4731 4732/***/ }), 4733/* 32 */ 4734/***/ (function(module, exports) { 4735 4736module.exports = function (it) { 4737 return typeof it === 'object' ? it !== null : typeof it === 'function'; 4738}; 4739 4740 4741/***/ }), 4742/* 33 */ 4743/***/ (function(module, exports, __webpack_require__) { 4744 4745// 19.1.2.14 / 15.2.3.14 Object.keys(O) 4746var $keys = __webpack_require__(84); 4747var enumBugKeys = __webpack_require__(58); 4748 4749module.exports = Object.keys || function keys(O) { 4750 return $keys(O, enumBugKeys); 4751}; 4752 4753 4754/***/ }), 4755/* 34 */ 4756/***/ (function(module, exports, __webpack_require__) { 4757 4758"use strict"; 4759 4760 4761/** 4762 * @prototype Point3d 4763 * @param {number} [x] 4764 * @param {number} [y] 4765 * @param {number} [z] 4766 */ 4767function Point3d(x, y, z) { 4768 this.x = x !== undefined ? x : 0; 4769 this.y = y !== undefined ? y : 0; 4770 this.z = z !== undefined ? z : 0; 4771} 4772 4773/** 4774 * Subtract the two provided points, returns a-b 4775 * @param {Point3d} a 4776 * @param {Point3d} b 4777 * @return {Point3d} a-b 4778 */ 4779Point3d.subtract = function (a, b) { 4780 var sub = new Point3d(); 4781 sub.x = a.x - b.x; 4782 sub.y = a.y - b.y; 4783 sub.z = a.z - b.z; 4784 return sub; 4785}; 4786 4787/** 4788 * Add the two provided points, returns a+b 4789 * @param {Point3d} a 4790 * @param {Point3d} b 4791 * @return {Point3d} a+b 4792 */ 4793Point3d.add = function (a, b) { 4794 var sum = new Point3d(); 4795 sum.x = a.x + b.x; 4796 sum.y = a.y + b.y; 4797 sum.z = a.z + b.z; 4798 return sum; 4799}; 4800 4801/** 4802 * Calculate the average of two 3d points 4803 * @param {Point3d} a 4804 * @param {Point3d} b 4805 * @return {Point3d} The average, (a+b)/2 4806 */ 4807Point3d.avg = function (a, b) { 4808 return new Point3d((a.x + b.x) / 2, (a.y + b.y) / 2, (a.z + b.z) / 2); 4809}; 4810 4811/** 4812 * Calculate the cross product of the two provided points, returns axb 4813 * Documentation: http://en.wikipedia.org/wiki/Cross_product 4814 * @param {Point3d} a 4815 * @param {Point3d} b 4816 * @return {Point3d} cross product axb 4817 */ 4818Point3d.crossProduct = function (a, b) { 4819 var crossproduct = new Point3d(); 4820 4821 crossproduct.x = a.y * b.z - a.z * b.y; 4822 crossproduct.y = a.z * b.x - a.x * b.z; 4823 crossproduct.z = a.x * b.y - a.y * b.x; 4824 4825 return crossproduct; 4826}; 4827 4828/** 4829 * Rtrieve the length of the vector (or the distance from this point to the origin 4830 * @return {number} length 4831 */ 4832Point3d.prototype.length = function () { 4833 return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z); 4834}; 4835 4836module.exports = Point3d; 4837 4838/***/ }), 4839/* 35 */ 4840/***/ (function(module, exports, __webpack_require__) { 4841 4842"use strict"; 4843var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__; 4844/** 4845 * Created by Alex on 11/6/2014. 4846 */ 4847 4848// https://github.com/umdjs/umd/blob/master/returnExports.js#L40-L60
vendor: 5,697 bytes, lines 4849-5003
4849// if the module has no dependencies, the above pattern can be simplified to 4850(function (root, factory) { 4851 if (true) { 4852 // AMD. Register as an anonymous module. 4853 !(__WEBPACK_AMD_DEFINE_ARRAY__ = [], __WEBPACK_AMD_DEFINE_FACTORY__ = (factory), 4854 __WEBPACK_AMD_DEFINE_RESULT__ = (typeof __WEBPACK_AMD_DEFINE_FACTORY__ === 'function' ? 4855 (__WEBPACK_AMD_DEFINE_FACTORY__.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__)) : __WEBPACK_AMD_DEFINE_FACTORY__), 4856 __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); 4857 } else if (typeof exports === 'object') { 4858 // Node. Does not work with strict CommonJS, but 4859 // only CommonJS-like environments that support module.exports, 4860 // like Node. 4861 module.exports = factory(); 4862 } else { 4863 // Browser globals (root is window) 4864 root.keycharm = factory(); 4865 } 4866}(this, function () { 4867 4868 function keycharm(options) { 4869 var preventDefault = options && options.preventDefault || false; 4870 4871 var container = options && options.container || window; 4872 4873 var _exportFunctions = {}; 4874 var _bound = {keydown:{}, keyup:{}}; 4875 var _keys = {}; 4876 var i; 4877 4878 // a - z 4879 for (i = 97; i <= 122; i++) {_keys[String.fromCharCode(i)] = {code:65 + (i - 97), shift: false};} 4880 // A - Z 4881 for (i = 65; i <= 90; i++) {_keys[String.fromCharCode(i)] = {code:i, shift: true};} 4882 // 0 - 9 4883 for (i = 0; i <= 9; i++) {_keys['' + i] = {code:48 + i, shift: false};} 4884 // F1 - F12 4885 for (i = 1; i <= 12; i++) {_keys['F' + i] = {code:111 + i, shift: false};} 4886 // num0 - num9 4887 for (i = 0; i <= 9; i++) {_keys['num' + i] = {code:96 + i, shift: false};} 4888 4889 // numpad misc 4890 _keys['num*'] = {code:106, shift: false}; 4891 _keys['num+'] = {code:107, shift: false}; 4892 _keys['num-'] = {code:109, shift: false}; 4893 _keys['num/'] = {code:111, shift: false}; 4894 _keys['num.'] = {code:110, shift: false}; 4895 // arrows 4896 _keys['left'] = {code:37, shift: false}; 4897 _keys['up'] = {code:38, shift: false}; 4898 _keys['right'] = {code:39, shift: false}; 4899 _keys['down'] = {code:40, shift: false}; 4900 // extra keys 4901 _keys['space'] = {code:32, shift: false}; 4902 _keys['enter'] = {code:13, shift: false}; 4903 _keys['shift'] = {code:16, shift: undefined}; 4904 _keys['esc'] = {code:27, shift: false}; 4905 _keys['backspace'] = {code:8, shift: false}; 4906 _keys['tab'] = {code:9, shift: false}; 4907 _keys['ctrl'] = {code:17, shift: false}; 4908 _keys['alt'] = {code:18, shift: false}; 4909 _keys['delete'] = {code:46, shift: false}; 4910 _keys['pageup'] = {code:33, shift: false}; 4911 _keys['pagedown'] = {code:34, shift: false}; 4912 // symbols 4913 _keys['='] = {code:187, shift: false}; 4914 _keys['-'] = {code:189, shift: false}; 4915 _keys[']'] = {code:221, shift: false}; 4916 _keys['['] = {code:219, shift: false}; 4917 4918 4919 4920 var down = function(event) {handleEvent(event,'keydown');}; 4921 var up = function(event) {handleEvent(event,'keyup');}; 4922 4923 // handle the actualy bound key with the event 4924 var handleEvent = function(event,type) { 4925 if (_bound[type][event.keyCode] !== undefined) { 4926 var bound = _bound[type][event.keyCode]; 4927 for (var i = 0; i < bound.length; i++) { 4928 if (bound[i].shift === undefined) { 4929 bound[i].fn(event); 4930 } 4931 else if (bound[i].shift == true && event.shiftKey == true) { 4932 bound[i].fn(event); 4933 } 4934 else if (bound[i].shift == false && event.shiftKey == false) { 4935 bound[i].fn(event); 4936 } 4937 } 4938 4939 if (preventDefault == true) { 4940 event.preventDefault(); 4941 } 4942 } 4943 }; 4944 4945 // bind a key to a callback 4946 _exportFunctions.bind = function(key, callback, type) { 4947 if (type === undefined) { 4948 type = 'keydown'; 4949 } 4950 if (_keys[key] === undefined) { 4951 throw new Error("unsupported key: " + key); 4952 } 4953 if (_bound[type][_keys[key].code] === undefined) { 4954 _bound[type][_keys[key].code] = []; 4955 } 4956 _bound[type][_keys[key].code].push({fn:callback, shift:_keys[key].shift}); 4957 }; 4958 4959 4960 // bind all keys to a call back (demo purposes) 4961 _exportFunctions.bindAll = function(callback, type) { 4962 if (type === undefined) { 4963 type = 'keydown'; 4964 } 4965 for (var key in _keys) { 4966 if (_keys.hasOwnProperty(key)) { 4967 _exportFunctions.bind(key,callback,type); 4968 } 4969 } 4970 }; 4971 4972 // get the key label from an event 4973 _exportFunctions.getKey = function(event) { 4974 for (var key in _keys) { 4975 if (_keys.hasOwnProperty(key)) { 4976 if (event.shiftKey == true && _keys[key].shift == true && event.keyCode == _keys[key].code) { 4977 return key; 4978 } 4979 else if (event.shiftKey == false && _keys[key].shift == false && event.keyCode == _keys[key].code) { 4980 return key; 4981 } 4982 else if (event.keyCode == _keys[key].code && key == 'shift') { 4983 return key; 4984 } 4985 } 4986 } 4987 return "unknown key, currently not supported"; 4988 }; 4989 4990 // unbind either a specific callback from a key or all of them (by leaving callback undefined) 4991 _exportFunctions.unbind = function(key, callback, type) { 4992 if (type === undefined) { 4993 type = 'keydown'; 4994 } 4995 if (_keys[key] === undefined) { 4996 throw new Error("unsupported key: " + key); 4997 } 4998 if (callback !== undefined) { 4999 var newBindings = []; 5000 var bound = _bound[type][_keys[key].code]; 5001 if (bound !== undefined) { 5002 for (var i = 0; i < bound.length; i++) { 5003 if (!(bound[i].fn == callback && bound[i].shift == _keys[key].shift)) {
5004 newBindings.push(_bound[type][_keys[key].code][i]); 5005 } 5006 } 5007 } 5008 _bound[type][_keys[key].code] = newBindings; 5009 } 5010 else { 5011 _bound[type][_keys[key].code] = []; 5012 } 5013 }; 5014 5015 // reset all bound variables. 5016 _exportFunctions.reset = function() { 5017 _bound = {keydown:{}, keyup:{}}; 5018 }; 5019 5020 // unbind all listeners and reset all variables. 5021 _exportFunctions.destroy = function() { 5022 _bound = {keydown:{}, keyup:{}}; 5023 container.removeEventListener('keydown', down, true); 5024 container.removeEventListener('keyup', up, true); 5025 }; 5026 5027 // create listeners. 5028 container.addEventListener('keydown',down,true); 5029 container.addEventListener('keyup',up,true); 5030 5031 // return the public functions. 5032 return _exportFunctions; 5033 } 5034 5035 return keycharm; 5036})); 5037 5038 5039 5040 5041/***/ }), 5042/* 36 */ 5043/***/ (function(module, exports, __webpack_require__) { 5044 5045"use strict"; 5046 5047 5048/** 5049 * used in Core to convert the options into a volatile variable 5050 * 5051 * @param {function} moment 5052 * @param {Object} body 5053 * @param {Array | Object} hiddenDates 5054 * @returns {number} 5055 */ 5056exports.convertHiddenOptions = function (moment, body, hiddenDates) { 5057 if (hiddenDates && !Array.isArray(hiddenDates)) { 5058 return exports.convertHiddenOptions(moment, body, [hiddenDates]); 5059 } 5060 5061 body.hiddenDates = []; 5062 if (hiddenDates) { 5063 if (Array.isArray(hiddenDates) == true) { 5064 for (var i = 0; i < hiddenDates.length; i++) { 5065 if (hiddenDates[i].repeat === undefined) { 5066 var dateItem = {}; 5067 dateItem.start = moment(hiddenDates[i].start).toDate().valueOf(); 5068 dateItem.end = moment(hiddenDates[i].end).toDate().valueOf(); 5069 body.hiddenDates.push(dateItem); 5070 } 5071 } 5072 body.hiddenDates.sort(function (a, b) { 5073 return a.start - b.start; 5074 }); // sort by start time 5075 } 5076 } 5077}; 5078 5079/** 5080 * create new entrees for the repeating hidden dates 5081 * 5082 * @param {function} moment 5083 * @param {Object} body 5084 * @param {Array | Object} hiddenDates 5085 * @returns {null} 5086 */ 5087exports.updateHiddenDates = function (moment, body, hiddenDates) { 5088 if (hiddenDates && !Array.isArray(hiddenDates)) { 5089 return exports.updateHiddenDates(moment, body, [hiddenDates]); 5090 } 5091 5092 if (hiddenDates && body.domProps.centerContainer.width !== undefined) { 5093 exports.convertHiddenOptions(moment, body, hiddenDates); 5094 5095 var start = moment(body.range.start); 5096 var end = moment(body.range.end); 5097 5098 var totalRange = body.range.end - body.range.start; 5099 var pixelTime = totalRange / body.domProps.centerContainer.width; 5100 5101 for (var i = 0; i < hiddenDates.length; i++) { 5102 if (hiddenDates[i].repeat !== undefined) { 5103 var startDate = moment(hiddenDates[i].start); 5104 var endDate = moment(hiddenDates[i].end); 5105 5106 if (startDate._d == "Invalid Date") { 5107 throw new Error("Supplied start date is not valid: " + hiddenDates[i].start); 5108 } 5109 if (endDate._d == "Invalid Date") { 5110 throw new Error("Supplied end date is not valid: " + hiddenDates[i].end); 5111 } 5112 5113 var duration = endDate - startDate; 5114 if (duration >= 4 * pixelTime) { 5115 5116 var offset = 0; 5117 var runUntil = end.clone(); 5118 switch (hiddenDates[i].repeat) { 5119 case "daily": 5120 // case of time 5121 if (startDate.day() != endDate.day()) { 5122 offset = 1; 5123 } 5124 startDate.dayOfYear(start.dayOfYear()); 5125 startDate.year(start.year()); 5126 startDate.subtract(7, 'days'); 5127 5128 endDate.dayOfYear(start.dayOfYear()); 5129 endDate.year(start.year()); 5130 endDate.subtract(7 - offset, 'days'); 5131 5132 runUntil.add(1, 'weeks'); 5133 break; 5134 case "weekly": 5135 var dayOffset = endDate.diff(startDate, 'days'); 5136 var day = startDate.day(); 5137 5138 // set the start date to the range.start 5139 startDate.date(start.date()); 5140 startDate.month(start.month()); 5141 startDate.year(start.year()); 5142 endDate = startDate.clone(); 5143 5144 // force 5145 startDate.day(day); 5146 endDate.day(day); 5147 endDate.add(dayOffset, 'days'); 5148 5149 startDate.subtract(1, 'weeks'); 5150 endDate.subtract(1, 'weeks'); 5151 5152 runUntil.add(1, 'weeks'); 5153 break; 5154 case "monthly": 5155 if (startDate.month() != endDate.month()) { 5156 offset = 1; 5157 } 5158 startDate.month(start.month()); 5159 startDate.year(start.year()); 5160 startDate.subtract(1, 'months'); 5161 5162 endDate.month(start.month()); 5163 endDate.year(start.year()); 5164 endDate.subtract(1, 'months'); 5165 endDate.add(offset, 'months'); 5166 5167 runUntil.add(1, 'months'); 5168 break; 5169 case "yearly": 5170 if (startDate.year() != endDate.year()) { 5171 offset = 1; 5172 } 5173 startDate.year(start.year()); 5174 startDate.subtract(1, 'years'); 5175 endDate.year(start.year()); 5176 endDate.subtract(1, 'years'); 5177 endDate.add(offset, 'years'); 5178 5179 runUntil.add(1, 'years'); 5180 break; 5181 default: 5182 console.log("Wrong repeat format, allowed are: daily, weekly, monthly, yearly. Given:", hiddenDates[i].repeat); 5183 return; 5184 } 5185 while (startDate < runUntil) { 5186 body.hiddenDates.push({ start: startDate.valueOf(), end: endDate.valueOf() }); 5187 switch (hiddenDates[i].repeat) { 5188 case "daily": 5189 startDate.add(1, 'days'); 5190 endDate.add(1, 'days'); 5191 break; 5192 case "weekly": 5193 startDate.add(1, 'weeks'); 5194 endDate.add(1, 'weeks'); 5195 break; 5196 case "monthly": 5197 startDate.add(1, 'months'); 5198 endDate.add(1, 'months'); 5199 break; 5200 case "yearly": 5201 startDate.add(1, 'y'); 5202 endDate.add(1, 'y'); 5203 break; 5204 default: 5205 console.log("Wrong repeat format, allowed are: daily, weekly, monthly, yearly. Given:", hiddenDates[i].repeat); 5206 return; 5207 } 5208 } 5209 body.hiddenDates.push({ start: startDate.valueOf(), end: endDate.valueOf() }); 5210 } 5211 } 5212 } 5213 // remove duplicates, merge where possible 5214 exports.removeDuplicates(body); 5215 // ensure the new positions are not on hidden dates 5216 var startHidden = exports.isHidden(body.range.start, body.hiddenDates); 5217 var endHidden = exports.isHidden(body.range.end, body.hiddenDates); 5218 var rangeStart = body.range.start; 5219 var rangeEnd = body.range.end; 5220 if (startHidden.hidden == true) { 5221 rangeStart = body.range.startToFront == true ? startHidden.startDate - 1 : startHidden.endDate + 1; 5222 } 5223 if (endHidden.hidden == true) { 5224 rangeEnd = body.range.endToFront == true ? endHidden.startDate - 1 : endHidden.endDate + 1; 5225 } 5226 if (startHidden.hidden == true || endHidden.hidden == true) { 5227 body.range._applyRange(rangeStart, rangeEnd); 5228 } 5229 } 5230}; 5231 5232/** 5233 * remove duplicates from the hidden dates list. Duplicates are evil. They mess everything up. 5234 * Scales with N^2 5235 * 5236 * @param {Object} body 5237 */ 5238exports.removeDuplicates = function (body) { 5239 var hiddenDates = body.hiddenDates; 5240 var safeDates = []; 5241 for (var i = 0; i < hiddenDates.length; i++) { 5242 for (var j = 0; j < hiddenDates.length; j++) { 5243 if (i != j && hiddenDates[j].remove != true && hiddenDates[i].remove != true) { 5244 // j inside i 5245 if (hiddenDates[j].start >= hiddenDates[i].start && hiddenDates[j].end <= hiddenDates[i].end) { 5246 hiddenDates[j].remove = true; 5247 } 5248 // j start inside i 5249 else if (hiddenDates[j].start >= hiddenDates[i].start && hiddenDates[j].start <= hiddenDates[i].end) { 5250 hiddenDates[i].end = hiddenDates[j].end; 5251 hiddenDates[j].remove = true; 5252 } 5253 // j end inside i 5254 else if (hiddenDates[j].end >= hiddenDates[i].start && hiddenDates[j].end <= hiddenDates[i].end) { 5255 hiddenDates[i].start = hiddenDates[j].start; 5256 hiddenDates[j].remove = true; 5257 } 5258 } 5259 } 5260 } 5261 5262 for (i = 0; i < hiddenDates.length; i++) { 5263 if (hiddenDates[i].remove !== true) {
5264 safeDates.push(hiddenDates[i]); 5265 } 5266 } 5267 5268 body.hiddenDates = safeDates; 5269 body.hiddenDates.sort(function (a, b) { 5270 return a.start - b.start; 5271 }); // sort by start time 5272}; 5273 5274exports.printDates = function (dates) { 5275 for (var i = 0; i < dates.length; i++) { 5276 console.log(i, new Date(dates[i].start), new Date(dates[i].end), dates[i].start, dates[i].end, dates[i].remove); 5277 } 5278}; 5279 5280/** 5281 * Used in TimeStep to avoid the hidden times. 5282 * @param {function} moment 5283 * @param {TimeStep} timeStep 5284 * @param {Date} previousTime 5285 */ 5286exports.stepOverHiddenDates = function (moment, timeStep, previousTime) { 5287 var stepInHidden = false; 5288 var currentValue = timeStep.current.valueOf(); 5289 for (var i = 0; i < timeStep.hiddenDates.length; i++) { 5290 var startDate = timeStep.hiddenDates[i].start; 5291 var endDate = timeStep.hiddenDates[i].end; 5292 if (currentValue >= startDate && currentValue < endDate) { 5293 stepInHidden = true; 5294 break; 5295 } 5296 } 5297 5298 if (stepInHidden == true && currentValue < timeStep._end.valueOf() && currentValue != previousTime) { 5299 var prevValue = moment(previousTime); 5300 var newValue = moment(endDate); 5301 //check if the next step should be major 5302 if (prevValue.year() != newValue.year()) { 5303 timeStep.switchedYear = true; 5304 } else if (prevValue.month() != newValue.month()) { 5305 timeStep.switchedMonth = true; 5306 } else if (prevValue.dayOfYear() != newValue.dayOfYear()) { 5307 timeStep.switchedDay = true; 5308 } 5309 5310 timeStep.current = newValue; 5311 } 5312}; 5313 5314///** 5315// * Used in TimeStep to avoid the hidden times. 5316// * @param timeStep 5317// * @param previousTime 5318// */ 5319//exports.checkFirstStep = function(timeStep) { 5320// var stepInHidden = false; 5321// var currentValue = timeStep.current.valueOf(); 5322// for (var i = 0; i < timeStep.hiddenDates.length; i++) { 5323// var startDate = timeStep.hiddenDates[i].start; 5324// var endDate = timeStep.hiddenDates[i].end; 5325// if (currentValue >= startDate && currentValue < endDate) { 5326// stepInHidden = true; 5327// break; 5328// } 5329// } 5330// 5331// if (stepInHidden == true && currentValue <= timeStep._end.valueOf()) { 5332// var newValue = moment(endDate); 5333// timeStep.current = newValue.toDate(); 5334// } 5335//}; 5336 5337/**
5338 * replaces the Core toScreen methods 5339 * 5340 * @param {vis.Core} Core 5341 * @param {Date} time 5342 * @param {number} width 5343 * @returns {number} 5344 */ 5345exports.toScreen = function (Core, time, width) { 5346 var conversion; 5347 if (Core.body.hiddenDates.length == 0) { 5348 conversion = Core.range.conversion(width); 5349 return (time.valueOf() - conversion.offset) * conversion.scale; 5350 } else { 5351 var hidden = exports.isHidden(time, Core.body.hiddenDates); 5352 if (hidden.hidden == true) { 5353 time = hidden.startDate; 5354 } 5355 5356 var duration = exports.getHiddenDurationBetween(Core.body.hiddenDates, Core.range.start, Core.range.end); 5357 if (time < Core.range.start) { 5358 conversion = Core.range.conversion(width, duration); 5359 var hiddenBeforeStart = exports.getHiddenDurationBeforeStart(Core.body.hiddenDates, time, conversion.offset); 5360 time = Core.options.moment(time).toDate().valueOf(); 5361 time = time + hiddenBeforeStart; 5362 return -(conversion.offset - time.valueOf()) * conversion.scale; 5363 } else if (time > Core.range.end) { 5364 var rangeAfterEnd = { start: Core.range.start, end: time }; 5365 time = exports.correctTimeForHidden(Core.options.moment, Core.body.hiddenDates, rangeAfterEnd, time); 5366 conversion = Core.range.conversion(width, duration); 5367 return (time.valueOf() - conversion.offset) * conversion.scale; 5368 } else { 5369 time = exports.correctTimeForHidden(Core.options.moment, Core.body.hiddenDates, Core.range, time); 5370 conversion = Core.range.conversion(width, duration); 5371 return (time.valueOf() - conversion.offset) * conversion.scale; 5372 } 5373 } 5374}; 5375 5376/** 5377 * Replaces the core toTime methods 5378 * 5379 * @param {vis.Core} Core 5380 * @param {number} x 5381 * @param {number} width 5382 * @returns {Date} 5383 */ 5384exports.toTime = function (Core, x, width) { 5385 if (Core.body.hiddenDates.length == 0) { 5386 var conversion = Core.range.conversion(width); 5387 return new Date(x / conversion.scale + conversion.offset); 5388 } else { 5389 var hiddenDuration = exports.getHiddenDurationBetween(Core.body.hiddenDates, Core.range.start, Core.range.end); 5390 var totalDuration = Core.range.end - Core.range.start - hiddenDuration; 5391 var partialDuration = totalDuration * x / width; 5392 var accumulatedHiddenDuration = exports.getAccumulatedHiddenDuration(Core.body.hiddenDates, Core.range, partialDuration); 5393 5394 return new Date(accumulatedHiddenDuration + partialDuration + Core.range.start); 5395 } 5396}; 5397 5398/** 5399 * Support function 5400 * 5401 * @param {Array.<{start: Window.start, end: *}>} hiddenDates 5402 * @param {number} start 5403 * @param {number} end 5404 * @returns {number} 5405 */ 5406exports.getHiddenDurationBetween = function (hiddenDates, start, end) { 5407 var duration = 0; 5408 for (var i = 0; i < hiddenDates.length; i++) { 5409 var startDate = hiddenDates[i].start; 5410 var endDate = hiddenDates[i].end; 5411 // if time after the cutout, and the 5412 if (startDate >= start && endDate < end) { 5413 duration += endDate - startDate; 5414 } 5415 } 5416 return duration; 5417}; 5418 5419/** 5420 * Support function 5421 * 5422 * @param {Array.<{start: Window.start, end: *}>} hiddenDates 5423 * @param {number} start 5424 * @param {number} end 5425 * @returns {number} 5426 */ 5427exports.getHiddenDurationBeforeStart = function (hiddenDates, start, end) { 5428 var duration = 0; 5429 for (var i = 0; i < hiddenDates.length; i++) { 5430 var startDate = hiddenDates[i].start; 5431 var endDate = hiddenDates[i].end; 5432 5433 if (startDate >= start && endDate <= end) { 5434 duration += endDate - startDate; 5435 } 5436 } 5437 return duration; 5438}; 5439 5440/** 5441 * Support function 5442 * @param {function} moment 5443 * @param {Array.<{start: Window.start, end: *}>} hiddenDates 5444 * @param {{start: number, end: number}} range 5445 * @param {Date} time 5446 * @returns {number} 5447 */ 5448exports.correctTimeForHidden = function (moment, hiddenDates, range, time) { 5449 time = moment(time).toDate().valueOf(); 5450 time -= exports.getHiddenDurationBefore(moment, hiddenDates, range, time); 5451 return time; 5452}; 5453 5454exports.getHiddenDurationBefore = function (moment, hiddenDates, range, time) { 5455 var timeOffset = 0; 5456 time = moment(time).toDate().valueOf(); 5457 5458 for (var i = 0; i < hiddenDates.length; i++) { 5459 var startDate = hiddenDates[i].start; 5460 var endDate = hiddenDates[i].end; 5461 // if time after the cutout, and the 5462 if (startDate >= range.start && endDate < range.end) { 5463 if (time >
5463= endDate) { 5464 timeOffset += endDate - startDate; 5465 } 5466 } 5467 } 5468 return timeOffset; 5469}; 5470 5471/** 5472 * sum the duration from start to finish, including the hidden duration, 5473 * until the required amount has been reached, return the accumulated hidden duration 5474 * @param {Array.<{start: Window.start, end: *}>} hiddenDates 5475 * @param {{start: number, end: number}} range 5476 * @param {number} [requiredDuration=0] 5477 * @returns {number} 5478 */ 5479exports.getAccumulatedHiddenDuration = function (hiddenDates, range, requiredDuration) { 5480 var hiddenDuration = 0; 5481 var duration = 0; 5482 var previousPoint = range.start; 5483 //exports.printDates(hiddenDates) 5484 for (var i = 0; i < hiddenDates.length; i++) { 5485 var startDate = hiddenDates[i].start; 5486 var endDate = hiddenDates[i].end; 5487 // if time after the cutout, and the 5488 if (startDate >= range.start && endDate < range.end) { 5489 duration += startDate - previousPoint; 5490 previousPoint = endDate; 5491 if (duration >= requiredDuration) { 5492 break; 5493 } else { 5494 hiddenDuration += endDate - startDate; 5495 } 5496 } 5497 } 5498 5499 return hiddenDuration; 5500}; 5501 5502/** 5503 * used to step over to either side of a hidden block. Correction is disabled on tablets, might be set to true 5504 * @param {Array.<{start: Window.start, end: *}>} hiddenDates 5505 * @param {Date} time 5506 * @param {number} direction 5507 * @param {boolean} correctionEnabled 5508 * @returns {Date|number} 5509 */ 5510exports.snapAwayFromHidden = function (hiddenDates, time, direction, correctionEnabled) { 5511 var isHidden = exports.isHidden(time, hiddenDates); 5512 if (isHidden.hidden == true) { 5513 if (direction < 0) { 5514 if (correctionEnabled == true) { 5515 return isHidden.startDate - (isHidden.endDate - time) - 1; 5516 } else { 5517 return isHidden.startDate - 1; 5518 } 5519 } else { 5520 if (correctionEnabled == true) { 5521 return isHidden.endDate + (time - isHidden.startDate) + 1; 5522 } else { 5523 return isHidden.endDate + 1; 5524 } 5525 } 5526 } else { 5527 return time; 5528 } 5529}; 5530 5531/** 5532 * Check if a time is hidden 5533 * 5534 * @param {Date} time 5535 * @param {Array.<{start: Window.start, end: *}>} hiddenDates 5536 * @returns {{hidden: boolean, startDate: Window.start, endDate: *}} 5537 */ 5538exports.isHidden = function (time, hiddenDates) { 5539 for (var i = 0; i < hiddenDates.length; i++) { 5540 var startDate = hiddenDates[i].start; 5541 var endDate = hiddenDates[i].end; 5542 5543 if (time >= startDate && time < endDate) { 5544 // if the start is entering a hidden zone 5545 return { hidden: true, startDate: startDate, endDate: endDate }; 5546 } 5547 } 5548 return { hidden: false, startDate: startDate, endDate: endDate }; 5549}; 5550 5551/***/ }), 5552/* 37 */ 5553/***/ (function(module, exports, __webpack_require__) { 5554 5555"use strict"; 5556 5557 5558/** 5559 * Register a touch event, taking place before a gesture 5560 * @param {Hammer} hammer A hammer instance 5561 * @param {function} callback Callback, called as callback(event) 5562 */ 5563exports.onTouch = function (hammer, callback) { 5564 callback.inputHandler = function (event) { 5565 if (event.isFirst) { 5566 callback(event); 5567 } 5568 }; 5569 5570 hammer.on('hammer.input', callback.inputHandler); 5571}; 5572 5573/** 5574 * Register a release event, taking place after a gesture 5575 * @param {Hammer} hammer A hammer instance 5576 * @param {function} callback Callback, called as callback(event) 5577 * @returns {*} 5578 */ 5579exports.onRelease = function (hammer, callback) { 5580 callback.inputHandler = function (event) { 5581 if (event.isFinal) { 5582 callback(event); 5583 } 5584 }; 5585 5586 return hammer.on('hammer.input', callback.inputHandler); 5587}; 5588 5589/** 5590 * Unregister a touch event, taking place before a gesture 5591 * @param {Hammer} hammer A hammer instance 5592 * @param {function} callback Callback, called as callback(event) 5593 */ 5594exports.offTouch = function (hammer, callback) { 5595 hammer.off('hammer.input', callback.inputHandler); 5596}; 5597 5598/** 5599 * Unregister a release event, taking place before a gesture 5600 * @param {Hammer} hammer A hammer instance 5601 * @param {function} callback Callback, called as callback(event) 5602 */ 5603exports.offRelease = exports.offTouch; 5604 5605/** 5606 * Hack the PinchRecognizer such that it doesn't prevent default behavior 5607 * for vertical panning. 5608 * 5609 * Yeah ... this is quite a hack ... see https://github.com/hammerjs/hammer.js/issues/932 5610 * 5611 * @param {Hammer.Pinch} pinchRecognizer 5612 * @return {Hammer.Pinch} returns the pinchRecognizer 5613 */ 5614exports.disablePreventDefaultVertically = function (pinchRecognizer) { 5615 var TOUCH_ACTION_PAN_Y = 'pan-y'; 5616 5617 pinchRecognizer.getTouchAction = function () { 5618 // default method returns [TOUCH_ACTION_NONE] 5619 return [TOUCH_ACTION_PAN_Y]; 5620 }; 5621 5622 return pinchRecognizer; 5623}; 5624 5625/***/ }), 5626/* 38 */ 5627/***/ (function(module, exports, __webpack_require__) { 5628 5629"use strict"; 5630 5631 5632var _typeof2 = __webpack_require__(6); 5633 5634var _typeof3 = _interopRequireDefault(_typeof2); 5635 5636var _keys = __webpack_require__(8); 5637 5638var _keys2 = _interopRequireDefault(_keys); 5639 5640function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 5641 5642var Hammer = __webpack_require__(10); 5643var util = __webpack_require__(2); 5644var moment = __webpack_require__(9); 5645 5646/** 5647 * @constructor Item 5648 * @param {Object} data Object containing (optional) parameters type, 5649 * start, end, content, group, className. 5650 * @param {{toScreen: function, toTime: function}} conversion 5651 * Conversion functions from time to screen and vice versa 5652 * @param {Object} options Configuration options 5653 * // TODO: describe available options 5654 */ 5655function Item(data, conversion, options) { 5656 this.id = null; 5657 this.parent = null; 5658 this.data = data; 5659 this.dom = null; 5660 this.conversion = conversion || {}; 5661 this.options = options || {}; 5662 this.selected = false;
5663 this.displayed = false; 5664 this.groupShowing = true; 5665 this.dirty = true; 5666 5667 this.top = null; 5668 this.right = null; 5669 this.left = null; 5670 this.width = null; 5671 this.height = null; 5672 5673 this.editable = null; 5674 this._updateEditStatus(); 5675} 5676 5677Item.prototype.stack = true; 5678 5679/** 5680 * Select current item 5681 */ 5682Item.prototype.select = function () { 5683 this.selected = true; 5684 this.dirty = true; 5685 if (this.displayed) this.redraw(); 5686}; 5687 5688/** 5689 * Unselect current item 5690 */ 5691Item.prototype.unselect = function () { 5692 this.selected = false; 5693 this.dirty = true; 5694 if (this.displayed) this.redraw(); 5695}; 5696 5697/** 5698 * Set data for the item. Existing data will be updated. The id should not 5699 * be changed. When the item is displayed, it will be redrawn immediately. 5700 * @param {Object} data 5701 */ 5702Item.prototype.setData = function (data) { 5703 var groupChanged = data.group != undefined && this.data.group != data.group; 5704 if (groupChanged && this.parent != null) { 5705 this.parent.itemSet._moveToGroup(this, data.group); 5706 } 5707 5708 if (this.parent) { 5709 this.parent.stackDirty = true; 5710 } 5711 5712 var subGroupChanged = data.subgroup != undefined && this.data.subgroup != data.subgroup; 5713 if (subGroupChanged && this.parent != null) { 5714 this.parent.changeSubgroup(this, this.data.subgroup, data.subgroup); 5715 } 5716 5717 this.data = data; 5718 this._updateEditStatus(); 5719 this.dirty = true; 5720 if (this.displayed) this.redraw(); 5721}; 5722 5723/** 5724 * Set a parent for the item 5725 * @param {Group} parent 5726 */ 5727Item.prototype.setParent = function (parent) { 5728 if (this.displayed) { 5729 this.hide(); 5730 this.parent = parent; 5731 if (this.parent) { 5732 this.show(); 5733 } 5734 } else { 5735 this.parent = parent; 5736 } 5737}; 5738 5739/** 5740 * Check whether this item is visible inside given range 5741 * @param {vis.Range} range with a timestamp for start and end 5742 * @returns {boolean} True if visible 5743 */ 5744Item.prototype.isVisible = function (range) { 5745 // eslint-disable-line no-unused-vars 5746 return false; 5747}; 5748 5749/** 5750 * Show the Item in the DOM (when not already visible) 5751 * @return {Boolean} changed 5752 */ 5753Item.prototype.show = function () { 5754 return false; 5755}; 5756 5757/** 5758 * Hide the Item from the DOM (when visible) 5759 * @return {Boolean} changed 5760 */ 5761Item.prototype.hide = function () { 5762 return false; 5763}; 5764 5765/** 5766 * Repaint the item 5767 */ 5768Item.prototype.redraw = function () { 5769 // should be implemented by the item 5770}; 5771 5772/** 5773 * Reposition the Item horizontally 5774 */ 5775Item.prototype.repositionX = function () { 5776 // should be implemented by the item 5777}; 5778 5779/** 5780 * Reposition the Item vertically 5781 */ 5782Item.prototype.repositionY = function () { 5783 // should be implemented by the item 5784}; 5785 5786/** 5787 * Repaint a drag area on the center of the item when the item is selected 5788 * @protected 5789 */ 5790Item.prototype._repaintDragCenter = function () { 5791 if (this.selected && this.options.editable.updateTime && !this.dom.dragCenter) { 5792 var me = this; 5793 // create and show drag area 5794 var dragCenter = document.createElement('div'); 5795 dragCenter.className = 'vis-drag-center'; 5796 dragCenter.dragCenterItem = this; 5797 var hammer = new Hammer(dragCenter); 5798 5799 hammer.on('tap', function (event) { 5800 me.parent.itemSet.body.emitter.emit('click', { 5801 event: event, 5802 item: me.id 5803 }); 5804 }); 5805 hammer.on('doubletap', function (event) { 5806 event.stopPropagation(); 5807 me.parent.itemSet._onUpdateItem(me); 5808 me.parent.itemSet.body.emitter.emit('doubleClick', { 5809 event: event, 5810 item: me.id 5811 }); 5812 }); 5813 5814 if (this.dom.box) { 5815 if (this.dom.dragLeft) { 5816 this.dom.box.insertBefore(dragCenter, this.dom.dragLeft); 5817 } else { 5818 this.dom.box.appendChild(dragCenter); 5819 } 5820 } else if (this.dom.point) { 5821 this.dom.point.appendChild(dragCenter); 5822 } 5823 5824 this.dom.dragCenter = dragCenter; 5825 } else if (!this.selected && this.dom.dragCenter) { 5826 // delete drag area 5827 if (this.dom.dragCenter.parentNode) { 5828 this.dom.dragCenter.parentNode.removeChild(this.dom.dragCenter); 5829 } 5830 this.dom.dragCenter = null; 5831 } 5832}; 5833 5834/** 5835 * Repaint a delete button on the top right of the item when the item is selected 5836 * @param {HTMLElement} anchor 5837 * @protected 5838 */ 5839Item.prototype._repaintDeleteButton = function (anchor) { 5840 var editable = (this.options.editable.overrideItems || this.editable == null) && this.options.editable.remove || !this.options.editable.overrideItems && this.editable != null && this.editable.remove; 5841 5842 if (this.selected && editable && !this.dom.deleteButton) { 5843 // create and show button 5844 var me = this; 5845 5846 var deleteButton = document.createElement('div'); 5847 5848 if (this.options.rtl) { 5849 deleteButton.className = 'vis-delete-rtl'; 5850 } else { 5851 deleteButton.className = 'vis-delete'; 5852 } 5853 deleteButton.title = 'Delete this item'; 5854
5855 // TODO: be able to destroy the delete button 5856 new Hammer(deleteButton).on('tap', function (event) { 5857 event.stopPropagation(); 5858 me.parent.removeFromDataSet(me); 5859 }); 5860 5861 anchor.appendChild(deleteButton); 5862 this.dom.deleteButton = deleteButton; 5863 } else if (!this.selected && this.dom.deleteButton) { 5864 // remove button 5865 if (this.dom.deleteButton.parentNode) { 5866 this.dom.deleteButton.parentNode.removeChild(this.dom.deleteButton); 5867 } 5868 this.dom.deleteButton = null; 5869 } 5870}; 5871 5872/** 5873 * Repaint a onChange tooltip on the top right of the item when the item is selected 5874 * @param {HTMLElement} anchor 5875 * @protected 5876 */ 5877Item.prototype._repaintOnItemUpdateTimeTooltip = function (anchor) { 5878 if (!this.options.tooltipOnItemUpdateTime) return; 5879 5880 var editable = (this.options.editable.updateTime || this.data.editable === true) && this.data.editable !== false; 5881 5882 if (this.selected && editable && !this.dom.onItemUpdateTimeTooltip) { 5883 var onItemUpdateTimeTooltip = document.createElement('div'); 5884 5885 onItemUpdateTimeTooltip.className = 'vis-onUpdateTime-tooltip'; 5886 anchor.appendChild(onItemUpdateTimeTooltip); 5887 this.dom.onItemUpdateTimeTooltip = onItemUpdateTimeTooltip; 5888 } else if (!this.selected && this.dom.onItemUpdateTimeTooltip) { 5889 // remove button 5890 if (this.dom.onItemUpdateTimeTooltip.parentNode) { 5891 this.dom.onItemUpdateTimeTooltip.parentNode.removeChild(this.dom.onItemUpdateTimeTooltip); 5892 } 5893 this.dom.onItemUpdateTimeTooltip = null; 5894 } 5895 5896 // position onChange tooltip 5897 if (this.dom.onItemUpdateTimeTooltip) { 5898 5899 // only show when editing 5900 this.dom.onItemUpdateTimeTooltip.style.visibility = this.parent.itemSet.touchParams.itemIsDragging ? 'visible' : 'hidden'; 5901 5902 // position relative to item's content 5903 if (this.options.rtl) { 5904 this.dom.onItemUpdateTimeTooltip.style.right = this.dom.content.style.right; 5905 } else { 5906 this.dom.onItemUpdateTimeTooltip.style.left = this.dom.content.style.left; 5907 } 5908 5909 // position above or below the item depending on the item's position in the window 5910 var tooltipOffset = 50; // TODO: should be tooltip height (depends on template) 5911 var scrollTop = this.parent.itemSet.body.domProps.scrollTop; 5912 5913 // TODO: this.top for orientation:true is actually the items distance from the bottom... 5914 // (should be this.bottom) 5915 var itemDistanceFromTop; 5916 if (this.options.orientation.item == 'top') { 5917 itemDistanceFromTop = this.top; 5918 } else { 5919 itemDistanceFromTop = this.parent.height - this.top - this.height; 5920 } 5921 var isCloseToTop = itemDistanceFromTop + this.parent.top - tooltipOffset < -scrollTop; 5922 5923 if (isCloseToTop) { 5924 this.dom.onItemUpdateTimeTooltip.style.bottom = ""; 5925 this.dom.onItemUpdateTimeTooltip.style.top = this.height + 2 + "px"; 5926 } else { 5927 this.dom.onItemUpdateTimeTooltip.style.top = ""; 5928 this.dom.onItemUpdateTimeTooltip.style.bottom = this.height + 2 + "px"; 5929 } 5930 5931 // handle tooltip content 5932 var content; 5933 var templateFunction; 5934 5935 if (this.options.tooltipOnItemUpdateTime && this.options.tooltipOnItemUpdateTime.template) { 5936 templateFunction = this.options.tooltipOnItemUpdateTime.template.bind(this); 5937 content = templateFunction(this.data); 5938 } else { 5939 content = 'start: ' + moment(this.data.start).format('MM/DD/YYYY hh:mm'); 5940 if (this.data.end) { 5941 content += '<br> end: ' + moment(this.data.end).format('MM/DD/YYYY hh:mm'); 5942 } 5943 } 5944 this.dom.onItemUpdateTimeTooltip.innerHTML = content; 5945 } 5946}; 5947 5948/** 5949 * Set HTML contents for the item 5950 * @param {Element} element HTML element to fill with the contents 5951 * @private 5952 */ 5953Item.prototype._updateContents = function (element) { 5954 var content; 5955 var changed; 5956 var templateFunction; 5957 var itemVisibleFrameContent; 5958 var visibleFrameTemplateFunction; 5959 var itemData = this.parent.itemSet.itemsData.get(this.id); // get a clone of the data from the dataset 5960 5961 var frameElement = this.dom.box || this.dom.point; 5962 var itemVisibleFrameContentElement = frameElement.getElementsByClassName('vis-item-visible-frame')[0]; 5963 5964 if (this.options.visibleFrameTemplate) { 5965 visibleFrameTemplateFunction = this.options.visibleFrameTemplate.bind(this); 5966 itemVisibleFrameContent = visibleFrameTemplateFunction(itemData, frameElement); 5967 } else { 5968 itemVisibleFrameContent = ''; 5969 } 5970 5971 if (itemVisibleFrameContentElement) { 5972 if (itemVisibleFrameContent instanceof Object && !(itemVisibleFrameContent instanceof Element)) { 5973 visibleFrameTemplateFunction(itemData, itemVisibleFrameContentElement); 5974 } else { 5975 changed = this._contentToString(this.itemVisibleFrameContent) !== this._contentToString(itemVisibleFrameContent); 5976 if (changed) { 5977 // only replace the content when changed 5978 if (itemVisibleFrameContent instanceof Element) { 5979 itemVisibleFrameContentElement.innerHTML = ''; 5980 itemVisibleFrameContentElement.appendChild(itemVisibleFrameContent); 5981 } else if (itemVisibleFrameContent != undefined) { 5982 itemVisibleFrameContentElement.innerHTML = itemVisibleFrameContent; 5983 } else { 5984 if (!(this.data.type == 'background' && this.data.content === undefined)) { 5985 throw new Error('Property "content" missing in item ' + this.id); 5986 } 5987 } 5988 5989 this.itemVisibleFrameContent = itemVisibleFrameContent; 5990 } 5991 } 5992 } 5993 5994 if (this.options.template) { 5995 templateFunction = this.options.template.bind(this); 5996 content = templateFunction(itemData, element, this.data); 5997 } else { 5998 content = this.data.content; 5999 } 6000 6001 if (content instanceof Object && !(content instanceof Element)) { 6002 templateFunction(itemData, element); 6003 } else { 6004 changed = this._contentToString(this.content) !== this._contentToString(content); 6005 if (changed) { 6006 // only replace the content when changed 6007 if (content instanceof Element) { 6008 element.innerHTML = ''; 6009 element.appendChild(content); 6010 } else if (content != undefined) { 6011 element.innerHTML = content; 6012 } else { 6013 if (!(this.data.type == 'background' && this.data.content === undefined)) { 6014 throw new Error('Property "content" missing in item ' + this.id); 6015 } 6016 } 6017 this.content = content; 6018 } 6019 } 6020}; 6021 6022/** 6023 * Process dataAttributes timeline option and set as data- attributes on dom.content 6024 * @param {Element} element HTML element to which the attributes will be attached 6025 * @private 6026 */ 6027Item.prototype._updateDataAttributes = function (element) { 6028 if (this.options.dataAttributes && this.options.dataAttributes.length > 0) { 6029 var attributes = []; 6030 6031 if (Array.isArray(this.options.dataAttributes)) { 6032 attributes = this.options.dataAttributes; 6033 } else if (this.options.dataAttributes == 'all') { 6034 attributes = (0, _keys2['default'])(this.data); 6035 } else { 6036 return; 6037 } 6038 6039 for (var i = 0; i < attributes.length; i++) { 6040 var name = attributes[i]; 6041 var value = this.data[name]; 6042 6043 if (value != null) { 6044 element.setAttribute('data-' + name, value); 6045 } else { 6046 element.removeAttribute('data-' + name); 6047 } 6048 } 6049 } 6050}; 6051 6052/** 6053 * Update custom styles of the element 6054 * @param {Element} element 6055 * @private 6056 */ 6057Item.prototype._updateStyle = function (element) { 6058 // remove old styles 6059 if (this.style) { 6060 util.removeCssText(element, this.style); 6061 this.style = null; 6062 } 6063 6064 // append new styles 6065 if (this.data.style) { 6066 util.addCssText(element, this.data.style); 6067 this.style = this.data.style; 6068 } 6069}; 6070 6071/** 6072 * Stringify the items contents 6073 * @param {string | Element | undefined} content 6074 * @returns {string | undefined} 6075 * @private 6076 */ 6077Item.prototype._contentToString = function (content) { 6078 if (typeof content === 'string') return content; 6079 if (content && 'outerHTML' in content) return content.outerHTML; 6080 return content; 6081}; 6082 6083/** 6084 * Update the editability of this item. 6085 */ 6086Item.prototype._updateEditStatus = function () { 6087 if (this.options) { 6088 if (typeof this.options.editable === 'boolean') { 6089 this.editable = { 6090 updateTime: this.options.editable, 6091 updateGroup: this.options.editable, 6092 remove: this.options.editable 6093 }; 6094 } else if ((0, _typeof3['default'])(this.options.editable) === 'object') { 6095 this.editable = {}; 6096 util.selectiveExtend(['updateTime', 'updateGroup', 'remove'], this.editable, this.options.editable); 6097 } 6098 } 6099 // Item data overrides, except if options.editable.overrideItems is set. 6100 if (!this.options || !this.options.editable || this.options.editable.overrideItems !== true) { 6101 if (this.data) { 6102 if (typeof this.data.editable === 'boolean') { 6103 this.editable = { 6104 updateTime: this.data.editable, 6105 updateGroup: this.data.editable, 6106 remove: this.data.editable 6107 }; 6108 } else if ((0, _typeof3['default'])(this.data.editable) === 'object') { 6109 // TODO: in vis.js 5.0, we should change this to not reset options from the timeline configuration. 6110 // Basically just remove the next line... 6111 this.editable = {}; 6112 util.selectiveExtend(['updateTime', 'updateGroup', 'remove'], this.editable, this.data.editable); 6113 } 6114 } 6115 } 6116}; 6117 6118/** 6119 * Return the width of the item left from its start date 6120 * @return {number} 6121 */ 6122Item.prototype.getWidthLeft = function () { 6123 return 0; 6124}; 6125 6126/** 6127 * Return the width of the item right from the max of its start and end date 6128 * @return {number} 6129 */ 6130Item.prototype.getWidthRight = function () { 6131 return 0; 6132}; 6133 6134/** 6135 * Return the title of the item 6136 * @return {string | undefined} 6137 */ 6138Item.prototype.getTitle = function () { 6139 return this.data.title; 6140}; 6141 6142module.exports = Item; 6143 6144/***/ }), 6145/* 39 */
6146/***/ (function(module, exports) { 6147 6148module.exports = function (bitmap, value) { 6149 return { 6150 enumerable: !(bitmap & 1), 6151 configurable: !(bitmap & 2), 6152 writable: !(bitmap & 4), 6153 value: value 6154 }; 6155}; 6156 6157 6158/***/ }), 6159/* 40 */ 6160/***/ (function(module, exports) { 6161 6162var id = 0; 6163var px = Math.random(); 6164module.exports = function (key) { 6165 return 'Symbol('.concat(key === undefined ? '' : key, ')_', (++id + px).toString(36)); 6166}; 6167 6168 6169/***/ }), 6170/* 41 */ 6171/***/ (function(module, exports, __webpack_require__) { 6172 6173// 7.1.13 ToObject(argument) 6174var defined = __webpack_require__(51); 6175module.exports = function (it) { 6176 return Object(defined(it)); 6177}; 6178 6179 6180/***/ }), 6181/* 42 */ 6182/***/ (function(module, exports) { 6183 6184exports.f = {}.propertyIsEnumerable; 6185 6186 6187/***/ }), 6188/* 43 */ 6189/***/ (function(module, exports, __webpack_require__) { 6190 6191"use strict"; 6192 6193 6194/** 6195 * A queue 6196 * @param {Object} options 6197 * Available options: 6198 * - delay: number When provided, the queue will be flushed 6199 * automatically after an inactivity of this delay 6200 * in milliseconds. 6201 * Default value is null. 6202 * - max: number When the queue exceeds the given maximum number 6203 * of entries, the queue is flushed automatically. 6204 * Default value of max is Infinity. 6205 * @constructor Queue 6206 */ 6207function Queue(options) { 6208 // options 6209 this.delay = null; 6210 this.max = Infinity; 6211 6212 // properties 6213 this._queue = []; 6214 this._timeout = null; 6215 this._extended = null; 6216 6217 this.setOptions(options); 6218} 6219 6220/** 6221 * Update the configuration of the queue 6222 * @param {Object} options 6223 * Available options: 6224 * - delay: number When provided, the queue will be flushed 6225 * automatically after an inactivity of this delay 6226 * in milliseconds. 6227 * Default value is null. 6228 * - max: number When the queue exceeds the given maximum number 6229 * of entries, the queue is flushed automatically. 6230 * Default value of max is Infinity. 6231 */ 6232Queue.prototype.setOptions = function (options) { 6233 if (options && typeof options.delay !== 'undefined') { 6234 this.delay = options.delay; 6235 } 6236 if (options && typeof options.max !== 'undefined') { 6237 this.max = options.max; 6238 } 6239 6240 this._flushIfNeeded(); 6241}; 6242 6243/** 6244 * Extend an object with queuing functionality. 6245 * The object will be extended with a function flush, and the methods provided 6246 * in options.replace will be replaced with queued ones. 6247 * @param {Object} object 6248 * @param {Object} options 6249 * Available options: 6250 * - replace: Array.<string> 6251 * A list with method names of the methods 6252 * on the object to be replaced with queued ones. 6253 * - delay: number When provided, the queue will be flushed 6254 * automatically after an inactivity of this delay 6255 * in milliseconds. 6256 * Default value is null. 6257 * - max: number When the queue exceeds the given maximum number 6258 * of entries, the queue is flushed automatically. 6259 * Default value of max is Infinity. 6260 * @return {Queue} Returns the created queue 6261 */ 6262Queue.extend = function (object, options) { 6263 var queue = new Queue(options); 6264 6265 if (object.flush !== undefined) { 6266 throw new Error('Target object already has a property flush'); 6267 } 6268 object.flush = function () { 6269 queue.flush(); 6270 }; 6271 6272 var methods = [{ 6273 name: 'flush', 6274 original: undefined 6275 }]; 6276 6277 if (options && options.replace) { 6278 for (var i = 0; i < options.replace.length; i++) { 6279 var name = options.replace[i]; 6280 methods.push({ 6281 name: name, 6282 original: object[name] 6283 }); 6284 queue.replace(object, name); 6285 } 6286 } 6287 6288 queue._extended = { 6289 object: object, 6290 methods: methods 6291 }; 6292 6293 return queue; 6294}; 6295 6296/** 6297 * Destroy the queue. The queue will first flush all queued actions, and in 6298 * case it has extended an object, will restore the original object. 6299 */ 6300Queue.prototype.destroy = function () { 6301 this.flush(); 6302 6303 if (this._extended) { 6304 var object = this._extended.object; 6305 var methods = this._extended.methods; 6306 for (var i = 0; i < methods.length; i++) { 6307 var method = methods[i]; 6308 if (method.original) { 6309 object[method.name] = method.original; 6310 } else { 6311 delete object[method.name]; 6312 } 6313 } 6314 this._extended = null; 6315 } 6316}; 6317 6318/**
6319 * Replace a method on an object with a queued version 6320 * @param {Object} object Object having the method 6321 * @param {string} method The method name 6322 */ 6323Queue.prototype.replace = function (object, method) { 6324 var me = this; 6325 var original = object[method]; 6326 if (!original) { 6327 throw new Error('Method ' + method + ' undefined'); 6328 } 6329 6330 object[method] = function () { 6331 // create an Array with the arguments 6332 var args = []; 6333 for (var i = 0; i < arguments.length; i++) { 6334 args[i] = arguments[i]; 6335 } 6336 6337 // add this call to the queue 6338 me.queue({ 6339 args: args, 6340 fn: original, 6341 context: this 6342 }); 6343 }; 6344}; 6345 6346/** 6347 * Queue a call 6348 * @param {function | {fn: function, args: Array} | {fn: function, args: Array, context: Object}} entry 6349 */ 6350Queue.prototype.queue = function (entry) { 6351 if (typeof entry === 'function') { 6352 this._queue.push({ fn: entry }); 6353 } else { 6354 this._queue.push(entry); 6355 } 6356 6357 this._flushIfNeeded(); 6358}; 6359 6360/** 6361 * Check whether the queue needs to be flushed 6362 * @private 6363 */ 6364Queue.prototype._flushIfNeeded = function () { 6365 // flush when the maximum is exceeded. 6366 if (this._queue.length > this.max) { 6367 this.flush(); 6368 } 6369 6370 // flush after a period of inactivity when a delay is configured 6371 clearTimeout(this._timeout); 6372 if (this.queue.length > 0 && typeof this.delay === 'number') { 6373 var me = this; 6374 this._timeout = setTimeout(function () { 6375 me.flush(); 6376 }, this.delay); 6377 } 6378}; 6379 6380/** 6381 * Flush all queued calls 6382 */ 6383Queue.prototype.flush = function () { 6384 while (this._queue.length > 0) { 6385 var entry = this._queue.shift(); 6386 entry.fn.apply(entry.context || entry.fn, entry.args || []); 6387 } 6388}; 6389 6390module.exports = Queue; 6391 6392/***/ }), 6393/* 44 */ 6394/***/ (function(module, exports) { 6395 6396 6397/** 6398 * Expose `Emitter`. 6399 */ 6400 6401module.exports = Emitter; 6402 6403/** 6404 * Initialize a new `Emitter`. 6405 * 6406 * @api public 6407 */ 6408 6409function Emitter(obj) { 6410 if (obj) return mixin(obj); 6411}; 6412 6413/** 6414 * Mixin the emitter properties. 6415 * 6416 * @param {Object} obj 6417 * @return {Object} 6418 * @api private 6419 */ 6420 6421function mixin(obj) { 6422 for (var key in Emitter.prototype) { 6423 obj[key] = Emitter.prototype[key]; 6424 } 6425 return obj; 6426} 6427 6428/** 6429 * Listen on the given `event` with `fn`. 6430 * 6431 * @param {String} event 6432 * @param {Function} fn 6433 * @return {Emitter} 6434 * @api public 6435 */ 6436 6437Emitter.prototype.on = 6438Emitter.prototype.addEventListener = function(event, fn){ 6439 this._callbacks = this._callbacks || {}; 6440 (this._callbacks[event] = this._callbacks[event] || []) 6441 .push(fn); 6442 return this; 6443}; 6444 6445/** 6446 * Adds an `event` listener that will be invoked a single 6447 * time then automatically removed. 6448 * 6449 * @param {String} event 6450 * @param {Function} fn 6451 * @return {Emitter} 6452 * @api public 6453 */ 6454 6455Emitter.prototype.once = function(event, fn){ 6456 var self = this; 6457 this._callbacks = this._callbacks || {}; 6458 6459 function on() { 6460 self.off(event, on); 6461 fn.apply(this, arguments); 6462 } 6463 6464 on.fn = fn; 6465 this.on(event, on); 6466 return this; 6467}; 6468 6469/** 6470 * Remove the given callback for `event` or all 6471 * registered callbacks. 6472 * 6473 * @param {String} event 6474 * @param {Function} fn 6475 * @return {Emitter} 6476 * @api public 6477 */ 6478 6479Emitter.prototype.off = 6480Emitter.prototype.removeListener = 6481Emitter.prototype.removeAllListeners = 6482Emitter.prototype.removeEventListener = function(event, fn){ 6483 this._callbacks = this._callbacks || {}; 6484 6485 // all 6486 if (0 == arguments.length) { 6487 this._callbacks = {}; 6488 return this; 6489 } 6490 6491 // specific event 6492 var callbacks = this._callbacks[event]; 6493 if (!callbacks) return this; 6494 6495 // remove all handlers 6496 if (1 == arguments.length) { 6497 delete this._callbacks[event]; 6498 return this; 6499 } 6500 6501 // remove specific handler 6502 var cb; 6503 for (var i = 0; i < callbacks.length; i++) { 6504 cb = callbacks[i]; 6505 if (cb === fn || cb.fn === fn) { 6506 callbacks.splice(i, 1); 6507 break; 6508 } 6509 } 6510 return this; 6511}; 6512 6513/** 6514 * Emit `event` with the given args. 6515 * 6516 * @param {String} event 6517 * @param {Mixed} ... 6518 * @return {Emitter} 6519 */ 6520 6521Emitter.prototype.emit = function(event){ 6522 this._callbacks = this._callbacks || {}; 6523 var args = [].slice.call(arguments, 1) 6524 , callbacks = this._callbacks[event]; 6525 6526 if (callbacks) { 6527 callbacks = callbacks.slice(0); 6528 for (var i = 0, len = callbacks.length; i < len; ++i) { 6529 callbacks[i].apply(this, args); 6530 } 6531 } 6532 6533 return this; 6534}; 6535 6536/** 6537 * Return array of callbacks for `event`. 6538 * 6539 * @param {String} event 6540 * @return {Array} 6541 * @api public 6542 */ 6543 6544Emitter.prototype.listeners = function(event){ 6545 this._callbacks = this._callbacks || {}; 6546 return this._callbacks[event] || []; 6547}; 6548 6549/** 6550 * Check if this emitter has `event` handlers. 6551 * 6552 * @param {String} event 6553 * @return {Boolean} 6554 * @api public 6555 */ 6556 6557Emitter.prototype.hasListeners = function(event){ 6558 return !! this.listeners(event).length; 6559}; 6560 6561 6562/***/ }), 6563/* 45 */ 6564/***/ (function(module, exports, __webpack_require__) { 6565 6566"use strict"; 6567 6568 6569var _typeof2 = __webpack_require__(6); 6570 6571var _typeof3 = _interopRequireDefault(_typeof2); 6572 6573function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 6574 6575var util = __webpack_require__(2); 6576var Component = __webpack_require__(16); 6577var TimeStep = __webpack_require__(66); 6578var DateUtil = __webpack_require__(36); 6579var moment = __webpack_require__(9); 6580 6581/** 6582 * A horizontal time axis 6583 * @param {{dom: Object, domProps: Object, emitter: Emitter, range: Range}} body 6584 * @param {Object} [options] See TimeAxis.setOptions for the available 6585 * options. 6586 * @constructor TimeAxis 6587 * @extends Component 6588 */ 6589function TimeAxis(body, options) { 6590 this.dom = { 6591 foreground: null, 6592 lines: [], 6593 majorTexts: [], 6594 minorTexts: [], 6595 redundant: { 6596 lines: [], 6597 majorTexts: [], 6598 minorTexts: [] 6599 } 6600 }; 6601 this.props = { 6602 range: { 6603 start: 0, 6604 end: 0, 6605 minimumStep: 0 6606 }, 6607 lineTop: 0 6608 }; 6609 6610 this.defaultOptions = { 6611 orientation: { 6612 axis: 'bottom' 6613 }, // axis orientation: 'top' or 'bottom' 6614 showMinorLabels: true, 6615 showMajorLabels: true, 6616 maxMinorChars: 7, 6617 format: TimeStep.FORMAT, 6618 moment: moment, 6619 timeAxis: null 6620 }; 6621 this.options = util.extend({}, this.defaultOptions); 6622 6623 this.body = body; 6624 6625 // create the HTML DOM 6626 this._create(); 6627 6628 this.setOptions(options); 6629} 6630 6631TimeAxis.prototype = new Component(); 6632 6633/** 6634 * Set options for the TimeAxis. 6635 * Parameters will be merged in current options. 6636 * @param {Object} options Available options: 6637 * {string} [orientation.axis] 6638 * {boolean} [showMinorLabels] 6639 * {boolean} [showMajorLabels] 6640 */ 6641TimeAxis.prototype.setOptions = function (options) { 6642 if (options) { 6643 // copy all options that we know 6644 util.selectiveExtend(['showMinorLabels', 'showMajorLabels', 'maxMinorChars', 'hiddenDates', 'timeAxis', 'moment', 'rtl'], this.options, options); 6645 6646 // deep copy the format options 6647 util.selectiveDeepExtend(['format'], this.options, options); 6648 6649 if ('orientation' in options) { 6650 if (typeof options.orientation === 'string') { 6651 this.options.orientation.axis = options.orientation; 6652 } else if ((0, _typeof3['default'])(options.orientation) === 'object' && 'axis' in options.orientation) { 6653 this.options.orientation.axis = options.orientation.axis; 6654 } 6655 } 6656 6657 // apply locale to moment.js 6658 // TODO: not so nice, this is applied globally to moment.js 6659 if ('locale' in options) { 6660 if (typeof moment.locale === 'function') { 6661 // moment.js 2.8.1+ 6662 moment.locale(options.locale); 6663 } else { 6664 moment.lang(options.locale); 6665 } 6666 } 6667 } 6668}; 6669 6670/** 6671 * Create the HTML DOM for the TimeAxis 6672 */ 6673TimeAxis.prototype._create = function () { 6674 this.dom.foreground = document.createElement('div'); 6675 this.dom.background = document.createElement('div'); 6676 6677 this.dom.foreground.className = 'vis-time-axis vis-foreground'; 6678 this.dom.background.className = 'vis-time-axis vis-background'; 6679}; 6680 6681/** 6682 * Destroy the TimeAxis 6683 */ 6684TimeAxis.prototype.destroy = function () { 6685 // remove from DOM 6686 if (this.dom.foreground.parentNode) { 6687 this.dom.foreground.parentNode.removeChild(this.dom.foreground); 6688 } 6689 if (this.dom.background.parentNode) { 6690 this.dom.background.parentNode.removeChild(this.dom.background); 6691 } 6692 6693 this.body = null; 6694}; 6695 6696/** 6697 * Repaint the component 6698 * @return {boolean} Returns true if the component is resized 6699 */ 6700TimeAxis.prototype.redraw = function () { 6701 var props = this.props; 6702 var foreground = this.dom.foreground; 6703 var background = this.dom.background; 6704 6705 // determine the correct parent DOM element (depending on option orientation) 6706 var parent = this.options.orientation.axis == 'top' ? this.body.dom.top : this.body.dom.bottom; 6707 var parentChanged = foreground.parentNode !== parent; 6708 6709 // calculate character width and height 6710 this._calculateCharSize(); 6711 6712 // TODO: recalculate sizes only needed when parent is resized or options is changed 6713 var showMinorLabels = this.options.showMinorLabels && this.options.orientation.axis !== 'none'; 6714 var showMajorLabels = this.options.showMajorLabels && this.options.orientation.axis !== 'none'; 6715 6716 // determine the width and height of the elemens for the axis 6717 props.minorLabelHeight = showMinorLabels ? props.minorCharHeight : 0; 6718 props.majorLabelHeight = showMajorLabels ? props.majorCharHeight : 0; 6719 props.height = props.minorLabelHeight + props.majorLabelHeight; 6720 props.width = foreground.offsetWidth; 6721 6722 props.minorLineHeight = this.body.domProps.root.height - props.majorLabelHeight - (this.options.orientation.axis == 'top' ? this.body.domProps.bottom.height : this.body.domProps.top.height); 6723 props.minorLineWidth = 1; // TODO: really calculate width 6724 props.majorLineHeight = props.minorLineHeight + props.majorLabelHeight; 6725 props.majorLineWidth = 1; // TODO: really calculate width 6726 6727 // take foreground and background offline while updating (is almost twice as fast) 6728 var foregroundNextSibling = foreground.nextSibling; 6729 var backgroundNextSibling = background.nextSibling; 6730 foreground.parentNode && foreground.parentNode.removeChild(foreground); 6731 background.parentNode && background.parentNode.removeChild(background); 6732 6733 foreground.style.height = this.props.height + 'px'; 6734 6735 this._repaintLabels(); 6736 6737 // put DOM online again (at the same place) 6738 if (foregroundNextSibling) { 6739 parent.insertBefore(foreground, foregroundNextSibling); 6740 } else { 6741 parent.appendChild(foreground); 6742 } 6743 if (backgroundNextSibling) { 6744 this.body.dom.backgroundVertical.insertBefore(background, backgroundNextSibling); 6745 } else { 6746 this.body.dom.backgroundVertical.appendChild(background); 6747 } 6748 return this._isResized() || parentChanged; 6749}; 6750 6751/** 6752 * Repaint major and minor text labels and vertical grid lines 6753 * @private 6754 */ 6755TimeAxis.prototype._repaintLabels = function () { 6756 var orientation = this.options.orientation.axis; 6757 6758 // calculate range and step (step such that we have space for 7 characters per label) 6759 var start = util.convert(this.body.range.start, 'Number'); 6760 var end = util.convert(this.body.range.end, 'Number'); 6761 var timeLabelsize = this.body.util.toTime((this.props.minorCharWidth || 10) * this.options.maxMinorChars).valueOf(); 6762 var minimumStep = timeLabelsize - DateUtil.getHiddenDurationBefore(this.options.moment, this.body.hiddenDates, this.body.range, timeLabelsize); 6763 minimumStep -= this.body.util.toTime(0).valueOf(); 6764 6765 var step = new TimeStep(new Date(start), new Date(end), minimumStep, this.body.hiddenDates, this.options); 6766 step.setMoment(this.options.moment); 6767 if (this.options.format) { 6768 step.setFormat(this.options.format); 6769 } 6770 if (this.options.timeAxis) { 6771 step.setScale(this.options.timeAxis); 6772 } 6773 this.step = step; 6774 6775 // Move all DOM elements to a "redundant" list, where they 6776 // can be picked for re-use, and clear the lists with lines and texts. 6777 // At the end of the function _repaintLabels, left over elements will be cleaned up 6778 var dom = this.dom; 6779 dom.redundant.lines = dom.lines; 6780 dom.redundant.majorTexts = dom.majorTexts; 6781 dom.redundant.minorTexts = dom.minorTexts; 6782 dom.lines = []; 6783 dom.majorTexts = []; 6784 dom.minorTexts = []; 6785 6786 var current; // eslint-disable-line no-unused-vars 6787 var next; 6788 var x; 6789 var xNext; 6790 var isMajor; 6791 var nextIsMajor; // eslint-disable-line no-unused-vars 6792 var showMinorGrid; 6793 var width = 0, 6794 prevWidth; 6795 var line; 6796 var labelMinor; 6797 var xFirstMajorLabel = undefined; 6798 var count = 0; 6799 var MAX = 1000; 6800 var className; 6801 6802 step.start(); 6803 next = step.getCurrent(); 6804 xNext = this.body.util.toScreen(next); 6805 while (step.hasNext() && count < MAX) { 6806 count++; 6807 6808 isMajor = step.isMajor(); 6809 className = step.getClassName(); 6810 labelMinor = step.getLabelMinor(); 6811 6812 current = next; 6813 x = xNext; 6814 6815 step.next(); 6816 next = step.getCurrent(); 6817 nextIsMajor = step.isMajor(); 6818 xNext = this.body.util.toScreen(next); 6819 6820 prevWidth = width; 6821 width = xNext - x; 6822 switch (step.scale) { 6823 case 'week': 6824 showMinorGrid = true;break; 6825 default: 6826 showMinorGrid = width >= prevWidth * 0.4;break; // prevent displaying of the 31th of the month on a scale of 5 days 6827 } 6828 6829 if (this.options.showMinorLabels && showMinorGrid) { 6830 var label = this._repaintMinorText(x, labelMinor, orientation, className); 6831 label.style.width = width + 'px'; // set width to prevent overflow 6832 } 6833 6834 if (isMajor && this.options.showMajorLabels) { 6835 if (x > 0) { 6836 if (xFirstMajorLabel == undefined) { 6837 xFirstMajorLabel = x; 6838 } 6839 label = this._repaintMajorText(x, step.getLabelMajor(), orientation, className); 6840 } 6841 line = this._repaintMajorLine(x, width, orientation, className); 6842 } else { 6843 // minor line 6844 if (showMinorGrid) { 6845 line = this._repaintMinorLine(x, width, orientation, className); 6846 } else { 6847 if (line) { 6848 // adjust the width of the previous grid 6849 line.style.width = parseInt(line.style.width) + width + 'px'; 6850 } 6851 } 6852 } 6853 } 6854 6855 if (count === MAX && !warnedForOverflow) { 6856 console.warn('Something is wrong with the Timeline scale. Limited drawing of grid lines to ' + MAX + ' lines.'); 6857 warnedForOverflow = true; 6858 } 6859 6860 // create a major label on the left when needed 6861 if (this.options.showMajorLabels) { 6862 var leftTime = this.body.util.toTime(0), 6863 leftText = step.getLabelMajor(leftTime), 6864 widthText = leftText.length * (this.props.majorCharWidth || 10) + 10; // upper bound estimation 6865 6866 if (xFirstMajorLabel == undefined || widthText < xFirstMajorLabel) { 6867 this._repaintMajorText(0, leftText, orientation, className); 6868 } 6869 } 6870 6871 // Cleanup leftover DOM elements from the redundant list 6872 util.forEach(this.dom.redundant, function (arr) { 6873 while (arr.length) { 6874 var elem = arr.pop(); 6875 if (elem && elem.parentNode) { 6876 elem.parentNode.removeChild(elem); 6877 } 6878 } 6879 }); 6880}; 6881 6882/** 6883 * Create a minor label for the axis at position x 6884 * @param {number} x 6885 * @param {string} text 6886 * @param {string} orientation "top" or "bottom" (default) 6887 * @param {string} className 6888 * @return {Element} Returns the HTML element of the created label 6889 * @private 6890 */ 6891TimeAxis.prototype._repaintMinorText = function (x, text, orientation, className) { 6892 // reuse redundant label 6893 var label = this.dom.redundant.minorTexts.shift(); 6894 6895 if (!label) { 6896 // create new label 6897 var content = document.createTextNode(''); 6898 label = document.createElement('div'); 6899 label.appendChild(content); 6900 this.dom.foreground.appendChild(label); 6901 } 6902 this.dom.minorTexts.push(label); 6903 label.innerHTML = text; 6904 6905 label.style.top = orientation == 'top' ? this.props.majorLabelHeight + 'px' : '0'; 6906 6907 if (this.options.rtl) { 6908 label.style.left = ""; 6909 label.style.right = x + 'px'; 6910 } else { 6911 label.style.left = x + 'px'; 6912 } 6913 label.className = 'vis-text vis-minor ' + className; 6914 //label.title = title; // TODO: this is a heavy operation 6915 6916 return label; 6917}; 6918 6919/** 6920 * Create a Major label for the axis at position x 6921 * @param {number} x 6922 * @param {string} text 6923 * @param {string} orientation "top" or "bottom" (default) 6924 * @param {string} className 6925 * @return {Element} Returns the HTML element of the created label 6926 * @private 6927 */ 6928TimeAxis.prototype._repaintMajorText = function (x, text, orientation, className) { 6929 // reuse redundant
6929label 6930 var label = this.dom.redundant.majorTexts.shift(); 6931 6932 if (!label) { 6933 // create label 6934 var content = document.createElement('div'); 6935 label = document.createElement('div'); 6936 label.appendChild(content); 6937 this.dom.foreground.appendChild(label); 6938 } 6939 6940 label.childNodes[0].innerHTML = text; 6941 label.className = 'vis-text vis-major ' + className; 6942 //label.title = title; // TODO: this is a heavy operation 6943 6944 label.style.top = orientation == 'top' ? '0' : this.props.minorLabelHeight + 'px'; 6945 if (this.options.rtl) { 6946 label.style.left = ""; 6947 label.style.right = x + 'px'; 6948 } else { 6949 label.style.left = x + 'px'; 6950 } 6951 6952 this.dom.majorTexts.push(label); 6953 return label; 6954}; 6955 6956/** 6957 * Create a minor line for the axis at position x 6958 * @param {number} x 6959 * @param {number} width 6960 * @param {string} orientation "top" or "bottom" (default) 6961 * @param {string} className 6962 * @return {Element} Returns the created line 6963 * @private 6964 */ 6965TimeAxis.prototype._repaintMinorLine = function (x, width, orientation, className) { 6966 // reuse redundant line 6967 var line = this.dom.redundant.lines.shift(); 6968 if (!line) { 6969 // create vertical line 6970 line = document.createElement('div'); 6971 this.dom.background.appendChild(line); 6972 } 6973 this.dom.lines.push(line); 6974 6975 var props = this.props; 6976 if (orientation == 'top') { 6977 line.style.top = props.majorLabelHeight + 'px'; 6978 } else { 6979 line.style.top = this.body.domProps.top.height + 'px'; 6980 } 6981 line.style.height = props.minorLineHeight + 'px'; 6982 if (this.options.rtl) { 6983 line.style.left = ""; 6984 line.style.right = x - props.minorLineWidth / 2 + 'px'; 6985 line.className = 'vis-grid vis-vertical-rtl vis-minor ' + className; 6986 } else { 6987 line.style.left = x - props.minorLineWidth / 2 + 'px'; 6988 line.className = 'vis-grid vis-vertical vis-minor ' + className; 6989 } 6990 line.style.width = width + 'px'; 6991 6992 return line; 6993}; 6994 6995/** 6996 * Create a Major line for the axis at position x 6997 * @param {number} x 6998 * @param {number} width 6999 * @param {string} orientation "top" or "bottom" (default) 7000 * @param {string} className 7001 * @return {Element} Returns the created line 7002 * @private 7003 */ 7004TimeAxis.prototype._repaintMajorLine = function (x, width, orientation, className) { 7005 // reuse redundant line 7006 var line = this.dom.redundant.lines.shift(); 7007 if (!line) { 7008 // create vertical line 7009 line = document.createElement('div'); 7010 this.dom.background.appendChild(line); 7011 } 7012 this.dom.lines.push(line); 7013 7014 var props = this.props; 7015 if (orientation == 'top') { 7016 line.style.top = '0'; 7017 } else { 7018 line.style.top = this.body.domProps.top.height + 'px'; 7019 } 7020 7021 if (this.options.rtl) { 7022 line.style.left = ""; 7023 line.style.right = x - props.majorLineWidth / 2 + 'px'; 7024 line.className = 'vis-grid vis-vertical-rtl vis-major ' + className; 7025 } else { 7026 line.style.left = x - props.majorLineWidth / 2 + 'px'; 7027 line.className = 'vis-grid vis-vertical vis-major ' + className; 7028 } 7029 7030 line.style.height = props.majorLineHeight + 'px'; 7031 line.style.width = width + 'px'; 7032 7033 return line; 7034}; 7035 7036/** 7037 * Determine the size of text on the axis (both major and minor axis). 7038 * The size is calculated only once and then cached in this.props. 7039 * @private 7040 */ 7041TimeAxis.prototype._calculateCharSize = function () { 7042 // Note: We calculate char size with every redraw. Size may change, for 7043 // example when any of the timelines parents had display:none for example. 7044 7045 // determine the char width and height on the minor axis 7046 if (!this.dom.measureCharMinor) { 7047 this.dom.measureCharMinor = document.createElement('DIV'); 7048 this.dom.measureCharMinor.className = 'vis-text vis-minor vis-measure'; 7049 this.dom.measureCharMinor.style.position = 'absolute'; 7050 7051 this.dom.measureCharMinor.appendChild(document.createTextNode('0')); 7052 this.dom.foreground.appendChild(this.dom.measureCharMinor); 7053 } 7054 this.props.minorCharHeight = this.dom.measureCharMinor.clientHeight; 7055 this.props.minorCharWidth = this.dom.measureCharMinor.clientWidth; 7056 7057 // determine the char width and height on the major axis 7058 if (!this.dom.measureCharMajor) { 7059 this.dom.measureCharMajor = document.createElement('DIV'); 7060 this.dom.measureCharMajor.className = 'vis-text vis-major vis-measure'; 7061 this.dom.measureCharMajor.style.position = 'absolute'; 7062 7063 this.dom.measureCharMajor.appendChild(document.createTextNode('0')); 7064 this.dom.foreground.appendChild(this.dom.measureCharMajor); 7065 } 7066 this.props.majorCharHeight = this.dom.measureCharMajor.clientHeight; 7067 this.props.majorCharWidth = this.dom.measureCharMajor.clientWidth; 7068}; 7069 7070var warnedForOverflow = false;
7071 7072module.exports = TimeAxis; 7073 7074/***/ }), 7075/* 46 */ 7076/***/ (function(module, exports, __webpack_require__) { 7077 7078"use strict"; 7079 7080 7081var Hammer = __webpack_require__(10); 7082var util = __webpack_require__(2); 7083var Component = __webpack_require__(16); 7084var moment = __webpack_require__(9); 7085var locales = __webpack_require__(98); 7086 7087/** 7088 * A custom time bar 7089 * @param {{range: Range, dom: Object}} body 7090 * @param {Object} [options] Available parameters: 7091 * {number | string} id 7092 * {string} locales 7093 * {string} locale 7094 * @constructor CustomTime 7095 * @extends Component 7096 */ 7097function CustomTime(body, options) { 7098 this.body = body; 7099 7100 // default options 7101 this.defaultOptions = { 7102 moment: moment, 7103 locales: locales, 7104 locale: 'en', 7105 id: undefined, 7106 title: undefined 7107 }; 7108 this.options = util.extend({}, this.defaultOptions); 7109 7110 if (options && options.time) { 7111 this.customTime = options.time; 7112 } else { 7113 this.customTime = new Date(); 7114 } 7115 7116 this.eventParams = {}; // stores state parameters while dragging the bar 7117 7118 this.setOptions(options); 7119 7120 // create the DOM 7121 this._create(); 7122} 7123 7124CustomTime.prototype = new Component(); 7125 7126/** 7127 * Set options for the component. Options will be merged in current options. 7128 * @param {Object} options Available parameters: 7129 * {number | string} id 7130 * {string} locales 7131 * {string} locale 7132 */ 7133CustomTime.prototype.setOptions = function (options) { 7134 if (options) { 7135 // copy all options that we know 7136 util.selectiveExtend(['moment', 'locale', 'locales', 'id'], this.options, options); 7137 } 7138}; 7139 7140/** 7141 * Create the DOM for the custom time 7142 * @private 7143 */ 7144CustomTime.prototype._create = function () { 7145 var bar = document.createElement('div'); 7146 bar['custom-time'] = this; 7147 bar.className = 'vis-custom-time ' + (this.options.id || ''); 7148 bar.style.position = 'absolute'; 7149 bar.style.top = '0px'; 7150 bar.style.height = '100%'; 7151 this.bar = bar; 7152 7153 var drag = document.createElement('div'); 7154 drag.style.position = 'relative'; 7155 drag.style.top = '0px'; 7156 drag.style.left = '-10px'; 7157 drag.style.height = '100%'; 7158 drag.style.width = '20px'; 7159 7160 /** 7161 * 7162 * @param {WheelEvent} e 7163 */ 7164 function onMouseWheel(e) { 7165 this.body.range._onMouseWheel(e); 7166 } 7167 7168 if (drag.addEventListener) { 7169 // IE9, Chrome, Safari, Opera 7170 drag.addEventListener("mousewheel", onMouseWheel.bind(this), false); 7171 // Firefox 7172 drag.addEventListener("DOMMouseScroll", onMouseWheel.bind(this), false); 7173 } else { 7174 // IE 6/7/8 7175 drag.attachEvent("onmousewheel", onMouseWheel.bind(this)); 7176 } 7177 7178 bar.appendChild(drag); 7179 // attach event listeners 7180 this.hammer = new Hammer(drag); 7181 this.hammer.on('panstart', this._onDragStart.bind(this)); 7182 this.hammer.on('panmove', this._onDrag.bind(this)); 7183 this.hammer.on('panend', this._onDragEnd.bind(this)); 7184 this.hammer.get('pan').set({ threshold: 5, direction: Hammer.DIRECTION_HORIZONTAL }); 7185}; 7186 7187/** 7188 * Destroy the CustomTime bar 7189 */ 7190CustomTime.prototype.destroy = function () { 7191 this.hide(); 7192 7193 this.hammer.destroy(); 7194 this.hammer = null; 7195 7196 this.body = null; 7197}; 7198 7199/** 7200 * Repaint the component 7201 * @return {boolean} Returns true if the component is resized 7202 */ 7203CustomTime.prototype.redraw = function () { 7204 var parent = this.body.dom.backgroundVertical; 7205 if (this.bar.parentNode != parent) { 7206 // attach to the dom 7207 if (this.bar.parentNode) { 7208 this.bar.parentNode.removeChild(this.bar); 7209 } 7210 parent.appendChild(this.bar); 7211 } 7212 7213 var x = this.body.util.toScreen(this.customTime); 7214 7215 var locale = this.options.locales[this.options.locale]; 7216 if (!locale) { 7217 if (!this.warned) { 7218 console.log('WARNING: options.locales[\'' + this.options.locale + '\'] not found. See http://visjs.org/docs/timeline/#Localization'); 7219 this.warned = true; 7220 } 7221 locale = this.options.locales['en']; // fall back on english when not available 7222 } 7223 7224 var title = this.options.title; 7225 // To hide the title completely use empty string ''. 7226 if (title === undefined) { 7227 title = locale.time + ': ' + this.options.moment(this.customTime).format('dddd, MMMM Do YYYY, H:mm:ss'); 7228 title = title.charAt(0).toUpperCase() + title.substring(1); 7229 } else if (typeof title === "function") { 7230 title = title.call(this.customTime); 7231 } 7232 7233 this.bar.style.left = x + 'px'; 7234 this.bar.title = title; 7235 7236 return false;
7237}; 7238 7239/** 7240 * Remove the CustomTime from the DOM 7241 */ 7242CustomTime.prototype.hide = function () { 7243 // remove the line from the DOM 7244 if (this.bar.parentNode) { 7245 this.bar.parentNode.removeChild(this.bar); 7246 } 7247}; 7248 7249/** 7250 * Set custom time. 7251 * @param {Date | number | string} time 7252 */ 7253CustomTime.prototype.setCustomTime = function (time) { 7254 this.customTime = util.convert(time, 'Date'); 7255 this.redraw(); 7256}; 7257 7258/** 7259 * Retrieve the current custom time. 7260 * @return {Date} customTime 7261 */ 7262CustomTime.prototype.getCustomTime = function () { 7263 return new Date(this.customTime.valueOf()); 7264}; 7265 7266/** 7267 * Set custom title. 7268 * @param {Date | number | string} title 7269 */ 7270CustomTime.prototype.setCustomTitle = function (title) { 7271 this.options.title = title; 7272}; 7273 7274/** 7275 * Start moving horizontally 7276 * @param {Event} event 7277 * @private 7278 */ 7279CustomTime.prototype._onDragStart = function (event) { 7280 this.eventParams.dragging = true; 7281 this.eventParams.customTime = this.customTime; 7282 7283 event.stopPropagation(); 7284}; 7285 7286/** 7287 * Perform moving operating. 7288 * @param {Event} event 7289 * @private 7290 */ 7291CustomTime.prototype._onDrag = function (event) { 7292 if (!this.eventParams.dragging) return; 7293 7294 var x = this.body.util.toScreen(this.eventParams.customTime) + event.deltaX; 7295 var time = this.body.util.toTime(x); 7296 7297 this.setCustomTime(time); 7298 7299 // fire a timechange event 7300 this.body.emitter.emit('timechange', { 7301 id: this.options.id, 7302 time: new Date(this.customTime.valueOf()), 7303 event: event 7304 }); 7305 7306 event.stopPropagation(); 7307}; 7308 7309/** 7310 * Stop moving operating. 7311 * @param {Event} event 7312 * @private 7313 */ 7314CustomTime.prototype._onDragEnd = function (event) { 7315 if (!this.eventParams.dragging) return; 7316 7317 // fire a timechanged event 7318 this.body.emitter.emit('timechanged', { 7319 id: this.options.id, 7320 time: new Date(this.customTime.valueOf()), 7321 event: event 7322 }); 7323 7324 event.stopPropagation(); 7325}; 7326 7327/** 7328 * Find a custom time from an event target: 7329 * searches for the attribute 'custom-time' in the event target's element tree 7330 * @param {Event} event 7331 * @return {CustomTime | null} customTime 7332 */ 7333CustomTime.customTimeFromTarget = function (event) { 7334 var target = event.target; 7335 while (target) { 7336 if (target.hasOwnProperty('custom-time')) { 7337 return target['custom-time']; 7338 } 7339 target = target.parentNode; 7340 } 7341 7342 return null; 7343}; 7344 7345module.exports = CustomTime; 7346 7347/***/ }), 7348/* 47 */ 7349/***/ (function(module, exports, __webpack_require__) { 7350 7351"use strict"; 7352 7353 7354Object.defineProperty(exports, "__esModule", { 7355 value: true 7356}); 7357 7358var _classCallCheck2 = __webpack_require__(0); 7359 7360var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); 7361 7362var _createClass2 = __webpack_require__(1); 7363 7364var _createClass3 = _interopRequireDefault(_createClass2); 7365 7366function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 7367 7368var util = __webpack_require__(2); 7369 7370var Label = __webpack_require__(117)['default']; 7371var ComponentUtil = __webpack_require__(48)['default']; 7372var Box = __webpack_require__(193)['default']; 7373var Circle = __webpack_require__(200)['default']; 7374var CircularImage = __webpack_require__(201)['default']; 7375var Database = __webpack_require__(202)['default']; 7376var Diamond = __webpack_require__(203)['default']; 7377var Dot = __webpack_require__(204)['default']; 7378var Ellipse = __webpack_require__(205)['default']; 7379var Icon = __webpack_require__(206)['default']; 7380var Image = __webpack_require__(207)['default']; 7381var Square = __webpack_require__(208)['default']; 7382var Hexagon = __webpack_require__(209)['default']; 7383var Star = __webpack_require__(210)['default']; 7384var Text = __webpack_require__(211)['default']; 7385var Triangle = __webpack_require__(212)['default']; 7386var TriangleDown = __webpack_require__(213)['default']; 7387 7388var _require = __webpack_require__(15), 7389 printStyle = _require.printStyle; 7390 7391/** 7392 * A node. A node can be connected to other nodes via one or multiple edges. 7393 */ 7394 7395 7396var Node = function () { 7397 /** 7398 * 7399 * @param {object} options An object containing options for the node. All 7400 * options are optional, except for the id. 7401 * {number} id Id of the node. Required 7402 * {string} label Text label for the node 7403 * {number} x Horizontal position of the node 7404 * {number} y Vertical position of the node 7405 * {string} shape Node shape 7406 * {string} image An image url 7407 * {string} title A title text, can be HTML 7408 * {anytype} group A group name or number 7409 * 7410 * @param {Object} body Shared state of current network instance 7411 * @param {Network.Images} imagelist A list with images. Only needed when the node has an image 7412 * @param {Groups} grouplist A list with groups. Needed for retrieving group options 7413 * @param {Object} globalOptions Current global node options; these serve as defaults for the node instance 7414 * @param {Object} defaultOptions Global default options for nodes; note that this is also the prototype 7415 * for parameter `globalOptions`. 7416 */ 7417 function Node(options, body, imagelist, grouplist, globalOptions, defaultOptions) { 7418 (0, _classCallCheck3['default'])(this, Node); 7419 7420 this.options = util.bridgeObject(globalOptions); 7421 this.globalOptions = globalOptions; 7422 this.defaultOptions = defaultOptions; 7423 this.body = body; 7424 7425 this.edges = []; // all edges connected to this node 7426 7427 // set defaults for the options 7428 this.id = undefined; 7429 this.imagelist = imagelist; 7430 this.grouplist = grouplist; 7431 7432 // state options 7433 this.x = undefined; 7434 this.y = undefined; 7435 this.baseSize = this.options.size; 7436 this.baseFontSize = this.options.font.size; 7437 this.predefinedPosition = false; // used to check if initial fit should just take the range or approximate 7438 this.selected = false;
7439 this.hover = false; 7440 7441 this.labelModule = new Label(this.body, this.options, false /* Not edge label */); 7442 this.setOptions(options); 7443 } 7444 7445 /** 7446 * Attach a edge to the node 7447 * @param {Edge} edge 7448 */ 7449 7450 7451 (0, _createClass3['default'])(Node, [{ 7452 key: 'attachEdge', 7453 value: function attachEdge(edge) { 7454 if (this.edges.indexOf(edge) === -1) { 7455 this.edges.push(edge); 7456 } 7457 } 7458 7459 /** 7460 * Detach a edge from the node 7461 * 7462 * @param {Edge} edge 7463 */ 7464 7465 }, { 7466 key: 'detachEdge', 7467 value: function detachEdge(edge) { 7468 var index = this.edges.indexOf(edge); 7469 if (index != -1) { 7470 this.edges.splice(index, 1); 7471 } 7472 } 7473 7474 /** 7475 * Set or overwrite options for the node 7476 * 7477 * @param {Object} options an object with options 7478 * @returns {null|boolean} 7479 */ 7480 7481 }, { 7482 key: 'setOptions', 7483 value: function setOptions(options) { 7484 var currentShape = this.options.shape; 7485 if (!options) { 7486 return; // Note that the return value will be 'undefined'! This is OK. 7487 } 7488 7489 // basic options 7490 if (options.id !== undefined) { 7491 this.id = options.id; 7492 } 7493 7494 if (this.id === undefined) { 7495 throw new Error("Node must have an id"); 7496 } 7497 7498 Node.checkMass(options, this.id); 7499 7500 // set these options locally 7501 // clear x and y positions 7502 if (options.x !== undefined) { 7503 if (options.x === null) { 7504 this.x = undefined;this.predefinedPosition = false; 7505 } else { 7506 this.x = parseInt(options.x);this.predefinedPosition = true; 7507 } 7508 } 7509 if (options.y !== undefined) { 7510 if (options.y === null) { 7511 this.y = undefined;this.predefinedPosition = false; 7512 } else { 7513 this.y = parseInt(options.y);this.predefinedPosition = true; 7514 } 7515 } 7516 if (options.size !== undefined) { 7517 this.baseSize = options.size; 7518 } 7519 if (options.value !== undefined) { 7520 options.value = parseFloat(options.value); 7521 } 7522 7523 // this transforms all shorthands into fully defined options 7524 Node.parseOptions(this.options, options, true, this.globalOptions, this.grouplist); 7525 7526 var pile = [options, this.options, this.defaultOptions]; 7527 this.chooser = ComponentUtil.choosify('node', pile); 7528 7529 this._load_images(); 7530 this.updateLabelModule(options); 7531 this.updateShape(currentShape); 7532 7533 return options.hidden !== undefined || options.physics !== undefined; 7534 } 7535 7536 /** 7537 * Load the images from the options, for the nodes that need them. 7538 * 7539 * TODO: The imageObj members should be moved to CircularImageBase. 7540 * It's the only place where they are required. 7541 * 7542 * @private 7543 */ 7544 7545 }, { 7546 key: '_load_images', 7547 value: function _load_images() { 7548 // Don't bother loading for nodes without images 7549 if (this.options.shape !== 'circularImage' && this.options.shape !== 'image') { 7550 return; 7551 } 7552 7553 if (this.options.image === undefined) { 7554 throw new Error("Option image must be defined for node type '" + this.options.shape + "'"); 7555 } 7556 7557 if (this.imagelist === undefined) { 7558 throw new Error("Internal Error: No images provided"); 7559 } 7560 7561 if (typeof this.options.image === 'string') { 7562 this.imageObj = this.imagelist.load(this.options.image, this.options.brokenImage, this.id); 7563 } else { 7564 if (this.options.image.unselected === undefined) { 7565 throw new Error("No unselected image provided"); 7566 } 7567 7568 this.imageObj = this.imagelist.load(this.options.image.unselected, this.options.brokenImage, this.id); 7569 7570 if (this.options.image.selected !== undefined) { 7571 this.imageObjAlt = this.imagelist.load(this.options.image.selected, this.options.brokenImage, this.id); 7572 } else { 7573 this.imageObjAlt = undefined; 7574 } 7575 } 7576 } 7577 7578 /** 7579 * Copy group option values into the node options. 7580 * 7581 * The group options override the global node options, so the copy of group options 7582 * must happen *after* the global node options have been set. 7583 * 7584 * This method must also be called also if the global node options have changed and the group options did not. 7585 * 7586 * @param {Object} parentOptions 7587 * @param {Object} newOptions new values for the options, currently only passed in for check 7588 * @param {Object} groupList 7589 */ 7590 7591 }, { 7592 key: 'getFormattingValues', 7593 7594 7595 /** 7596 * 7597 * @returns {{color: *, borderWidth: *, borderColor: *, size: *, borderDashes: (boolean|Array|allOptions.nodes.shapeProperties.borderDashes|{boolean, array}), borderRadius: (number|allOptions.nodes.shapeProperties.borderRadius|{number}|Array), shadow: *, shadowColor: *, shadowSize: *, shadowX: *, shadowY: *}} 7598 */ 7599 value: function getFormattingValues() { 7600 var values = { 7601 color: this.options.color.background, 7602 borderWidth: this.options.borderWidth, 7603 borderColor: this.options.color.border, 7604 size: this.options.size, 7605 borderDashes: this.options.shapeProperties.borderDashes, 7606 borderRadius: this.options.shapeProperties.borderRadius, 7607 shadow: this.options.shadow.enabled, 7608 shadowColor: this.options.shadow.color, 7609 shadowSize: this.options.shadow.size, 7610 shadowX: this.options.shadow.x, 7611 shadowY: this.options.shadow.y 7612 }; 7613 if (this.selected || this.hover) { 7614 if (this.chooser === true) { 7615 if (this.selected) { 7616 values.borderWidth *= 2; 7617 values.color = this.options.color.highlight.background; 7618 values.borderColor = this.options.color.highlight.border; 7619 values.shadow = this.options.shadow.enabled; 7620 } else if (this.hover) { 7621 values.color = this.options.color.hover.background; 7622 values.borderColor = this.options.color.hover.border; 7623 values.shadow = this.options.shadow.enabled; 7624 } 7625 } else if (typeof this.chooser === 'function') { 7626 this.chooser(values, this.options.id, this.selected, this.hover); 7627 if (values.shadow === false) { 7628 if (values.shadowColor !== this.options.shadow.color || values.shadowSize !== this.options.shadow.size || values.shadowX !== this.options.shadow.x || values.shadowY !== this.options.shadow.y) { 7629 values.shadow = true; 7630 } 7631 } 7632 } 7633 } else { 7634 values.shadow = this.options.shadow.enabled; 7635 } 7636 return values; 7637 } 7638 7639 /** 7640 * 7641 * @param {Object} options 7642 */ 7643 7644 }, { 7645 key: 'updateLabelModule', 7646 value: function updateLabelModule(options) { 7647 if (this.options.label === undefined || this.options.label === null) { 7648 this.options.label = ''; 7649 } 7650 7651 Node.updateGroupOptions(this.options, options, this.grouplist); 7652 7653 // 7654 // Note:The prototype chain for this.options is: 7655 // 7656 // this.options -> NodesHandler.options -> NodesHandler.defaultOptions 7657 // (also: this.globalOptions) 7658 // 7659 // Note that the prototypes are mentioned explicitly in the pile list below; 7660 // WE DON'T WANT THE ORDER OF THE PROTOTYPES!!!! At least, not for font handling of labels. 7661 // This is a good indication that the prototype usage of options is deficient. 7662 // 7663 var currentGroup = this.grouplist.get(this.options.group, false); 7664 var pile = [options, // new options 7665 this.options, // current node options, see comment above for prototype 7666 currentGroup, // group options, if any 7667 this.globalOptions, // Currently set global node options 7668 this.defaultOptions // Default global node options 7669 ]; 7670 this.labelModule.update(this.options, pile); 7671 7672 if (this.labelModule.baseSize !== undefined) { 7673 this.baseFontSize = this.labelModule.baseSize; 7674 } 7675 } 7676 7677 /** 7678 * 7679 * @param {string} currentShape 7680 */ 7681 7682 }, { 7683 key: 'updateShape', 7684 value: function updateShape(currentShape) { 7685 if (currentShape === this.options.shape && this.shape) { 7686 this.shape.setOptions(this.options, this.imageObj, this.imageObjAlt); 7687 } else {
7688 // choose draw method depending on the shape 7689 switch (this.options.shape) { 7690 case 'box': 7691 this.shape = new Box(this.options, this.body, this.labelModule); 7692 break; 7693 case 'circle': 7694 this.shape = new Circle(this.options, this.body, this.labelModule); 7695 break; 7696 case 'circularImage': 7697 this.shape = new CircularImage(this.options, this.body, this.labelModule, this.imageObj, this.imageObjAlt); 7698 break; 7699 case 'database': 7700 this.shape = new Database(this.options, this.body, this.labelModule); 7701 break; 7702 case 'diamond': 7703 this.shape = new Diamond(this.options, this.body, this.labelModule); 7704 break; 7705 case 'dot': 7706 this.shape = new Dot(this.options, this.body, this.labelModule); 7707 break; 7708 case 'ellipse': 7709 this.shape = new Ellipse(this.options, this.body, this.labelModule); 7710 break; 7711 case 'icon': 7712 this.shape = new Icon(this.options, this.body, this.labelModule); 7713 break; 7714 case 'image': 7715 this.shape = new Image(this.options, this.body, this.labelModule, this.imageObj, this.imageObjAlt); 7716 break; 7717 case 'square': 7718 this.shape = new Square(this.options, this.body, this.labelModule); 7719 break; 7720 case 'hexagon': 7721 this.shape = new Hexagon(this.options, this.body, this.labelModule); 7722 break; 7723 case 'star': 7724 this.shape = new Star(this.options, this.body, this.labelModule); 7725 break; 7726 case 'text': 7727 this.shape = new Text(this.options, this.body, this.labelModule); 7728 break; 7729 case 'triangle': 7730 this.shape = new Triangle(this.options, this.body, this.labelModule); 7731 break; 7732 case 'triangleDown': 7733 this.shape = new TriangleDown(this.options, this.body, this.labelModule); 7734 break; 7735 default: 7736 this.shape = new Ellipse(this.options, this.body, this.labelModule); 7737 break; 7738 } 7739 } 7740 this.needsRefresh(); 7741 } 7742 7743 /** 7744 * select this node 7745 */ 7746 7747 }, { 7748 key: 'select', 7749 value: function select() { 7750 this.selected = true; 7751 this.needsRefresh(); 7752 } 7753 7754 /** 7755 * unselect this node 7756 */ 7757 7758 }, { 7759 key: 'unselect', 7760 value: function unselect() { 7761 this.selected = false; 7762 this.needsRefresh(); 7763 } 7764 7765 /** 7766 * Reset the calculated size of the node, forces it to recalculate its size 7767 */ 7768 7769 }, { 7770 key: 'needsRefresh', 7771 value: function needsRefresh() { 7772 this.shape.refreshNeeded = true; 7773 } 7774 7775 /** 7776 * get the title of this node. 7777 * @return {string} title The title of the node, or undefined when no title 7778 * has been set. 7779 */ 7780 7781 }, { 7782 key: 'getTitle', 7783 value: function getTitle() { 7784 return this.options.title; 7785 } 7786 7787 /** 7788 * Calculate the distance to the border of the Node 7789 * @param {CanvasRenderingContext2D} ctx 7790 * @param {number} angle Angle in radians 7791 * @returns {number} distance Distance to the border in pixels 7792 */ 7793 7794 }, { 7795 key: 'distanceToBorder', 7796 value: function distanceToBorder(ctx, angle) { 7797 return this.shape.distanceToBorder(ctx, angle); 7798 } 7799 7800 /** 7801 * Check if this node has a fixed x and y position 7802 * @return {boolean} true if fixed, false if not 7803 */ 7804 7805 }, { 7806 key: 'isFixed', 7807 value: function isFixed() { 7808 return this.options.fixed.x && this.options.fixed.y; 7809 } 7810 7811 /** 7812 * check if this node is selecte 7813 * @return {boolean} selected True if node is selected, else false 7814 */ 7815 7816 }, { 7817 key: 'isSelected', 7818 value: function isSelected() { 7819 return this.selected; 7820 } 7821 7822 /** 7823 * Retrieve the value of the node. Can be undefined 7824 * @return {number} value 7825 */ 7826 7827 }, { 7828 key: 'getValue', 7829 value: function getValue() { 7830 return this.options.value; 7831 } 7832 7833 /** 7834 * Get the current dimensions of the label 7835 * 7836 * @return {rect} 7837 */ 7838 7839 }, { 7840 key: 'getLabelSize', 7841 value: function getLabelSize() { 7842 return this.labelModule.size(); 7843 } 7844 7845 /**
7846 * Adjust the value range of the node. The node will adjust it's size 7847 * based on its value. 7848 * @param {number} min 7849 * @param {number} max 7850 * @param {number} total 7851 */ 7852 7853 }, { 7854 key: 'setValueRange', 7855 value: function setValueRange(min, max, total) { 7856 if (this.options.value !== undefined) { 7857 var scale = this.options.scaling.customScalingFunction(min, max, total, this.options.value); 7858 var sizeDiff = this.options.scaling.max - this.options.scaling.min; 7859 if (this.options.scaling.label.enabled === true) { 7860 var fontDiff = this.options.scaling.label.max - this.options.scaling.label.min; 7861 this.options.font.size = this.options.scaling.label.min + scale * fontDiff; 7862 } 7863 this.options.size = this.options.scaling.min + scale * sizeDiff; 7864 } else { 7865 this.options.size = this.baseSize; 7866 this.options.font.size = this.baseFontSize; 7867 } 7868 7869 this.updateLabelModule(); 7870 } 7871 7872 /** 7873 * Draw this node in the given canvas 7874 * The 2d context of a HTML canvas can be retrieved by canvas.getContext("2d"); 7875 * @param {CanvasRenderingContext2D} ctx 7876 */ 7877 7878 }, { 7879 key: 'draw', 7880 value: function draw(ctx) { 7881 var values = this.getFormattingValues(); 7882 this.shape.draw(ctx, this.x, this.y, this.selected, this.hover, values); 7883 } 7884 7885 /** 7886 * Update the bounding box of the shape 7887 * @param {CanvasRenderingContext2D} ctx 7888 */ 7889 7890 }, { 7891 key: 'updateBoundingBox', 7892 value: function updateBoundingBox(ctx) { 7893 this.shape.updateBoundingBox(this.x, this.y, ctx); 7894 } 7895 7896 /** 7897 * Recalculate the size of this node in the given canvas 7898 * The 2d context of a HTML canvas can be retrieved by canvas.getContext("2d"); 7899 * @param {CanvasRenderingContext2D} ctx 7900 */ 7901 7902 }, { 7903 key: 'resize', 7904 value: function resize(ctx) { 7905 var values = this.getFormattingValues(); 7906 this.shape.resize(ctx, this.selected, this.hover, values); 7907 } 7908 7909 /** 7910 * Determine all visual elements of this node instance, in which the given 7911 * point falls within the bounding shape. 7912 * 7913 * @param {point} point 7914 * @returns {Array.<nodeClickItem|nodeLabelClickItem>} list with the items which are on the point 7915 */ 7916 7917 }, { 7918 key: 'getItemsOnPoint', 7919 value: function getItemsOnPoint(point) { 7920 var ret = []; 7921 7922 if (this.labelModule.visible()) { 7923 if (ComponentUtil.pointInRect(this.labelModule.getSize(), point)) { 7924 ret.push({ nodeId: this.id, labelId: 0 }); 7925 } 7926 } 7927 7928 if (ComponentUtil.pointInRect(this.shape.boundingBox, point)) { 7929 ret.push({ nodeId: this.id }); 7930 } 7931 7932 return ret; 7933 } 7934 7935 /** 7936 * Check if this object is overlapping with the provided object 7937 * @param {Object} obj an object with parameters left, top, right, bottom 7938 * @return {boolean} True if location is located on node 7939 */ 7940 7941 }, { 7942 key: 'isOverlappingWith', 7943 value: function isOverlappingWith(obj) { 7944 return this.shape.left < obj.right && this.shape.left + this.shape.width > obj.left && this.shape.top < obj.bottom && this.shape.top + this.shape.height > obj.top; 7945 } 7946 7947 /** 7948 * Check if this object is overlapping with the provided object 7949 * @param {Object} obj an object with parameters left, top, right, bottom 7950 * @return {boolean} True if location is located on node 7951 */ 7952 7953 }, { 7954 key: 'isBoundingBoxOverlappingWith', 7955 value: function isBoundingBoxOverlappingWith(obj) { 7956 return this.shape.boundingBox.left < obj.right && this.shape.boundingBox.right > obj.left && this.shape.boundingBox.top < obj.bottom && this.shape.boundingBox.bottom > obj.top; 7957 } 7958 7959 /** 7960 * Check valid values for mass 7961 * 7962 * The mass may not be negative or zero. If it is, reset to 1 7963 * 7964 * @param {object} options 7965 * @param {Node.id} id 7966 * @static 7967 */ 7968 7969 }], [{ 7970 key: 'updateGroupOptions', 7971 value: function updateGroupOptions(parentOptions, newOptions, groupList) { 7972 if (groupList === undefined) return; // No groups, nothing to do 7973 7974 var group = parentOptions.group; 7975 7976 // paranoia: the selected group is already merged into node options, check. 7977 if (newOptions !== undefined && newOptions.group !== undefined && group !== newOptions.group) { 7978 throw new Error("updateGroupOptions: group values in options don't match."); 7979 } 7980 7981 var hasGroup = typeof group === 'number' || typeof group === 'string' && group != ''; 7982 if (!hasGroup) return; // current node has no group, no need to merge 7983 7984 var groupObj = groupList.get(group); 7985 7986 // Skip merging of group font options into parent; these are required to be distinct for labels 7987 // TODO: It might not be a good idea either to merge the rest of the options, investigate this. 7988 util.selectiveNotDeepExtend(['font'], parentOptions, groupObj); 7989 7990 // the color object needs to be completely defined. 7991 // Since groups can partially overwrite the colors, we parse it again, just in case. 7992 parentOptions.color = util.parseColor(parentOptions.color); 7993 } 7994 7995 /** 7996 * This process all possible shorthands in the new options and makes sure that the parentOptions are fully defined. 7997 * Static so it can also be used by the handler. 7998 * 7999 * @param {Object} parentOptions 8000 * @param {Object} newOptions 8001 * @param {boolean} [allowDeletion=false] 8002 * @param {Object} [globalOptions={}] 8003 * @param {Object} [groupList] 8004 * @static 8005 */ 8006 8007 }, { 8008 key: 'parseOptions', 8009 value: function parseOptions(parentOptions, newOptions) { 8010 var allowDeletion = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
8011 var globalOptions = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {}; 8012 var groupList = arguments[4]; 8013 8014 8015 var fields = ['color', 'fixed', 'shadow']; 8016 util.selectiveNotDeepExtend(fields, parentOptions, newOptions, allowDeletion); 8017 8018 Node.checkMass(newOptions); 8019 8020 // merge the shadow options into the parent. 8021 util.mergeOptions(parentOptions, newOptions, 'shadow', globalOptions); 8022 8023 // individual shape newOptions 8024 if (newOptions.color !== undefined && newOptions.color !== null) { 8025 var parsedColor = util.parseColor(newOptions.color); 8026 util.fillIfDefined(parentOptions.color, parsedColor); 8027 } else if (allowDeletion === true && newOptions.color === null) { 8028 parentOptions.color = util.bridgeObject(globalOptions.color); // set the object back to the global options 8029 } 8030 8031 // handle the fixed options 8032 if (newOptions.fixed !== undefined && newOptions.fixed !== null) { 8033 if (typeof newOptions.fixed === 'boolean') { 8034 parentOptions.fixed.x = newOptions.fixed; 8035 parentOptions.fixed.y = newOptions.fixed; 8036 } else { 8037 if (newOptions.fixed.x !== undefined && typeof newOptions.fixed.x === 'boolean') { 8038 parentOptions.fixed.x = newOptions.fixed.x; 8039 } 8040 if (newOptions.fixed.y !== undefined && typeof newOptions.fixed.y === 'boolean') { 8041 parentOptions.fixed.y = newOptions.fixed.y; 8042 } 8043 } 8044 } 8045 8046 if (allowDeletion === true && newOptions.font === null) { 8047 parentOptions.font = util.bridgeObject(globalOptions.font); // set the object back to the global options 8048 } 8049 8050 Node.updateGroupOptions(parentOptions, newOptions, groupList); 8051 8052 // handle the scaling options, specifically the label part 8053 if (newOptions.scaling !== undefined) { 8054 util.mergeOptions(parentOptions.scaling, newOptions.scaling, 'label', globalOptions.scaling); 8055 } 8056 } 8057 }, { 8058 key: 'checkMass', 8059 value: function checkMass(options, id) { 8060 if (options.mass !== undefined && options.mass <= 0) { 8061 var strId = ''; 8062 if (id !== undefined) { 8063 strId = ' in node id: ' + id; 8064 } 8065 console.log('%cNegative or zero mass disallowed' + strId + ', setting mass to 1.', printStyle); 8066 options.mass = 1; 8067 } 8068 } 8069 }]); 8070 return Node; 8071}(); 8072 8073exports['default'] = Node; 8074 8075/***/ }), 8076/* 48 */ 8077/***/ (function(module, exports, __webpack_require__) { 8078 8079"use strict"; 8080 8081 8082Object.defineProperty(exports, "__esModule", { 8083 value: true 8084}); 8085 8086var _typeof2 = __webpack_require__(6); 8087 8088var _typeof3 = _interopRequireDefault(_typeof2); 8089 8090var _classCallCheck2 = __webpack_require__(0); 8091 8092var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); 8093 8094var _createClass2 = __webpack_require__(1); 8095 8096var _createClass3 = _interopRequireDefault(_createClass2); 8097 8098function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 8099 8100/** 8101 * Definitions for param's in jsdoc. 8102 * These are more or less global within Network. Putting them here until I can figure out 8103 * where to really put them 8104 * 8105 * @typedef {string|number} Id 8106 * @typedef {Id} NodeId 8107 * @typedef {Id} EdgeId 8108 * @typedef {Id} LabelId 8109 * 8110 * @typedef {{x: number, y: number}} point 8111 * @typedef {{left: number, top: number, width: number, height: number}} rect 8112 * @typedef {{x: number, y:number, angle: number}} rotationPoint 8113 * - point to rotate around and the angle in radians to rotate. angle == 0 means no rotation 8114 * @typedef {{nodeId:NodeId}} nodeClickItem 8115 * @typedef {{nodeId:NodeId, labelId:LabelId}} nodeLabelClickItem 8116 * @typedef {{edgeId:EdgeId}} edgeClickItem 8117 * @typedef {{edgeId:EdgeId, labelId:LabelId}} edgeLabelClickItem 8118 */ 8119 8120var util = __webpack_require__(2); 8121 8122/** 8123 * Helper functions for components 8124 * @class 8125 */ 8126 8127var ComponentUtil = function () { 8128 function ComponentUtil() { 8129 (0, _classCallCheck3['default'])(this, ComponentUtil); 8130 } 8131 8132 (0, _createClass3['default'])(ComponentUtil, null, [{ 8133 key: 'choosify', 8134 8135 /** 8136 * Determine values to use for (sub)options of 'chosen'. 8137 * 8138 * This option is either a boolean or an object whose values should be examined further. 8139 * The relevant structures are: 8140 * 8141 * - chosen: <boolean value> 8142 * - chosen: { subOption: <boolean or function> } 8143 * 8144 * Where subOption is 'node', 'edge' or 'label'. 8145 * 8146 * The intention of this method appears to be to set a specific priority to the options; 8147 * Since most properties are either bridged or merged into the local options objects, there 8148 * is not much point in handling them separately. 8149 * TODO: examine if 'most' in previous sentence can be replaced with 'all'. In that case, we 8150 * should be able to get rid of this method. 8151 * 8152 * @param {string} subOption option within object 'chosen' to consider; either 'node', 'edge' or 'label' 8153 * @param {Object} pile array of options objects to consider 8154 * 8155 * @return {boolean|function} value for passed subOption of 'chosen' to use 8156 */ 8157 value: function choosify(subOption, pile) { 8158 // allowed values for subOption 8159 var allowed = ['node', 'edge', 'label']; 8160 var value = true; 8161 8162 var chosen = util.topMost(pile, 'chosen'); 8163 if (typeof chosen === 'boolean') { 8164 value = chosen; 8165 } else if ((typeof chosen === 'undefined' ? 'undefined' : (0, _typeof3['default'])(chosen)) === 'object') { 8166 if (allowed.indexOf(subOption) === -1) { 8167 throw new Error('choosify: subOption \'' + subOption + '\' should be one of ' + "'" + allowed.join("', '") + "'"); 8168 } 8169 8170 var chosenEdge = util.topMost(pile, ['chosen', subOption]); 8171 if (typeof chosenEdge === 'boolean' || typeof chosenEdge === 'function') { 8172 value = chosenEdge; 8173 } 8174 } 8175 8176 return value; 8177 } 8178 8179 /** 8180 * Check if the point falls within the given rectangle. 8181 * 8182 * @param {rect} rect 8183 * @param {point} point 8184 * @param {rotationPoint} [rotationPoint] if specified, the rotation that applies to the rectangle. 8185 * @returns {boolean} true if point within rectangle, false otherwise 8186 * @static 8187 */ 8188 8189 }, { 8190 key: 'pointInRect', 8191 value: function pointInRect(rect, point, rotationPoint) { 8192 if (rect.width <= 0 || rect.height <= 0) { 8193 return false; // early out 8194 } 8195 8196 if (rotationPoint !== undefined) { 8197 // Rotate the point the same amount as the rectangle 8198 var tmp = { 8199 x: point.x - rotationPoint.x, 8200 y: point.y - rotationPoint.y 8201 }; 8202 8203 if (rotationPoint.angle !== 0) { 8204 // In order to get the coordinates the same, you need to 8205 // rotate in the reverse direction 8206 var angle = -rotationPoint.angle; 8207 8208 var tmp2 = { 8209 x: Math.cos(angle) * tmp.x - Math.sin(angle) * tmp.y,
vendor: 4,980 bytes, lines 8210-8368
8210 y: Math.sin(angle) * tmp.x + Math.cos(angle) * tmp.y 8211 }; 8212 point = tmp2; 8213 } else { 8214 point = tmp; 8215 } 8216 8217 // Note that if a rotation is specified, the rectangle coordinates 8218 // are **not* the full canvas coordinates. They are relative to the 8219 // rotationPoint. Hence, the point coordinates need not be translated 8220 // back in this case. 8221 } 8222 8223 var right = rect.x + rect.width; 8224 var bottom = rect.y + rect.width; 8225 8226 return rect.left < point.x && right > point.x && rect.top < point.y && bottom > point.y; 8227 } 8228 8229 /** 8230 * Check if given value is acceptable as a label text. 8231 * 8232 * @param {*} text value to check; can be anything at this point 8233 * @returns {boolean} true if valid label value, false otherwise 8234 */ 8235 8236 }, { 8237 key: 'isValidLabel', 8238 value: function isValidLabel(text) { 8239 // Note that this is quite strict: types that *might* be converted to string are disallowed 8240 return typeof text === 'string' && text !== ''; 8241 } 8242 }]); 8243 return ComponentUtil; 8244}(); 8245 8246exports['default'] = ComponentUtil; 8247 8248/***/ }), 8249/* 49 */ 8250/***/ (function(module, exports, __webpack_require__) { 8251 8252__webpack_require__(125); 8253var global = __webpack_require__(18); 8254var hide = __webpack_require__(26); 8255var Iterators = __webpack_require__(31); 8256var TO_STRING_TAG = __webpack_require__(13)('toStringTag'); 8257 8258var DOMIterables = ('CSSRuleList,CSSStyleDeclaration,CSSValueList,ClientRectList,DOMRectList,DOMStringList,' + 8259 'DOMTokenList,DataTransferItemList,FileList,HTMLAllCollection,HTMLCollection,HTMLFormElement,HTMLSelectElement,' + 8260 'MediaList,MimeTypeArray,NamedNodeMap,NodeList,PaintRequestList,Plugin,PluginArray,SVGLengthList,SVGNumberList,' + 8261 'SVGPathSegList,SVGPointList,SVGStringList,SVGTransformList,SourceBufferList,StyleSheetList,TextTrackCueList,' + 8262 'TextTrackList,TouchList').split(','); 8263 8264for (var i = 0; i < DOMIterables.length; i++) { 8265 var NAME = DOMIterables[i]; 8266 var Collection = global[NAME]; 8267 var proto = Collection && Collection.prototype; 8268 if (proto && !proto[TO_STRING_TAG]) hide(proto, TO_STRING_TAG, NAME); 8269 Iterators[NAME] = Iterators.Array; 8270} 8271 8272 8273/***/ }), 8274/* 50 */ 8275/***/ (function(module, exports) { 8276 8277var toString = {}.toString; 8278 8279module.exports = function (it) { 8280 return toString.call(it).slice(8, -1); 8281}; 8282 8283 8284/***/ }), 8285/* 51 */ 8286/***/ (function(module, exports) { 8287 8288// 7.2.1 RequireObjectCoercible(argument) 8289module.exports = function (it) { 8290 if (it == undefined) throw TypeError("Can't call method on " + it); 8291 return it; 8292}; 8293 8294 8295/***/ }), 8296/* 52 */ 8297/***/ (function(module, exports) { 8298 8299module.exports = true; 8300 8301 8302/***/ }), 8303/* 53 */ 8304/***/ (function(module, exports, __webpack_require__) { 8305 8306// 7.1.1 ToPrimitive(input [, PreferredType]) 8307var isObject = __webpack_require__(32); 8308// instead of the ES6 spec version, we didn't implement @@toPrimitive case 8309// and the second argument - flag - preferred type is a string 8310module.exports = function (it, S) { 8311 if (!isObject(it)) return it; 8312 var fn, val; 8313 if (S && typeof (fn = it.toString) == 'function' && !isObject(val = fn.call(it))) return val; 8314 if (typeof (fn = it.valueOf) == 'function' && !isObject(val = fn.call(it))) return val; 8315 if (!S && typeof (fn = it.toString) == 'function' && !isObject(val = fn.call(it))) return val; 8316 throw TypeError("Can't convert object to primitive value"); 8317}; 8318 8319 8320/***/ }), 8321/* 54 */ 8322/***/ (function(module, exports, __webpack_require__) { 8323 8324// 19.1.2.2 / 15.2.3.5 Object.create(O [, Properties]) 8325var anObject = __webpack_require__(27); 8326var dPs = __webpack_require__(130); 8327var enumBugKeys = __webpack_require__(58); 8328var IE_PROTO = __webpack_require__(56)('IE_PROTO'); 8329var Empty = function () { /* empty */ }; 8330var PROTOTYPE = 'prototype'; 8331 8332// Create object with fake `null` prototype: use iframe Object with cleared prototype 8333var createDict = function () { 8334 // Thrash, waste and sodomy: IE GC bug 8335 var iframe = __webpack_require__(82)('iframe'); 8336 var i = enumBugKeys.length; 8337 var lt = '<'; 8338 var gt = '>'; 8339 var iframeDocument; 8340 iframe.style.display = 'none'; 8341 __webpack_require__(134).appendChild(iframe); 8342 iframe.src = 'javascript:'; // eslint-disable-line no-script-url 8343 // createDict = iframe.contentWindow.Object; 8344 // html.removeChild(iframe); 8345 iframeDocument = iframe.contentWindow.document; 8346 iframeDocument.open(); 8347 iframeDocument.write(lt + 'script' + gt + 'document.F=Object' + lt + '/script' + gt); 8348 iframeDocument.close(); 8349 createDict = iframeDocument.F; 8350 while (i--) delete createDict[PROTOTYPE][enumBugKeys[i]]; 8351 return createDict(); 8352}; 8353 8354module.exports = Object.create || function create(O, Properties) { 8355 var result; 8356 if (O !== null) { 8357 Empty[PROTOTYPE] = anObject(O); 8358 result = new Empty(); 8359 Empty[PROTOTYPE] = null; 8360 // add "__proto__" for Object.getPrototypeOf polyfill 8361 result[IE_PROTO] = O; 8362 } else result = createDict(); 8363 return Properties === undefined ? result : dPs(result, Properties); 8364}; 8365 8366 8367/***/ }), 8368/* 55 */
8369/***/ (function(module, exports) { 8370 8371// 7.1.4 ToInteger 8372var ceil = Math.ceil; 8373var floor = Math.floor; 8374module.exports = function (it) { 8375 return isNaN(it = +it) ? 0 : (it > 0 ? floor : ceil)(it); 8376}; 8377 8378 8379/***/ }), 8380/* 56 */ 8381/***/ (function(module, exports, __webpack_require__) { 8382 8383var shared = __webpack_require__(57)('keys'); 8384var uid = __webpack_require__(40); 8385module.exports = function (key) { 8386 return shared[key] || (shared[key] = uid(key)); 8387}; 8388 8389 8390/***/ }), 8391/* 57 */ 8392/***/ (function(module, exports, __webpack_require__) { 8393 8394var global = __webpack_require__(18); 8395var SHARED = '__core-js_shared__'; 8396var store = global[SHARED] || (global[SHARED] = {}); 8397module.exports = function (key) { 8398 return store[key] || (store[key] = {}); 8399}; 8400 8401 8402/***/ }), 8403/* 58 */ 8404/***/ (function(module, exports) { 8405 8406// IE 8- don't enum bug keys 8407module.exports = ( 8408 'constructor,hasOwnProperty,isPrototypeOf,propertyIsEnumerable,toLocaleString,toString,valueOf' 8409).split(','); 8410 8411 8412/***/ }), 8413/* 59 */ 8414/***/ (function(module, exports, __webpack_require__) { 8415 8416var def = __webpack_require__(20).f; 8417var has = __webpack_require__(22); 8418var TAG = __webpack_require__(13)('toStringTag'); 8419 8420module.exports = function (it, tag, stat) { 8421 if (it && !has(it = stat ? it : it.prototype, TAG)) def(it, TAG, { configurable: true, value: tag }); 8422}; 8423 8424 8425/***/ }), 8426/* 60 */ 8427/***/ (function(module, exports, __webpack_require__) { 8428 8429"use strict"; 8430 8431var $at = __webpack_require__(135)(true); 8432 8433// 21.1.3.27 String.prototype[@@iterator]() 8434__webpack_require__(79)(String, 'String', function (iterated) { 8435 this._t = String(iterated); // target 8436 this._i = 0; // next index 8437// 21.1.5.2.1 %StringIteratorPrototype%.next() 8438}, function () { 8439 var O = this._t; 8440 var index = this._i; 8441 var point; 8442 if (index >= O.length) return { value: undefined, done: true }; 8443 point = $at(O, index); 8444 this._i += point.length; 8445 return { value: point, done: false }; 8446}); 8447 8448 8449/***/ }), 8450/* 61 */ 8451/***/ (function(module, exports, __webpack_require__) { 8452 8453exports.f = __webpack_require__(13); 8454 8455 8456/***/ }), 8457/* 62 */ 8458/***/ (function(module, exports, __webpack_require__) { 8459 8460var global = __webpack_require__(18); 8461var core = __webpack_require__(7); 8462var LIBRARY = __webpack_require__(52); 8463var wksExt = __webpack_require__(61); 8464var defineProperty = __webpack_require__(20).f; 8465module.exports = function (name) { 8466 var $Symbol = core.Symbol || (core.Symbol = LIBRARY ? {} : global.Symbol || {}); 8467 if (name.charAt(0) != '_' && !(name in $Symbol)) defineProperty($Symbol, name, { value: wksExt.f(name) }); 8468}; 8469 8470 8471/***/ }), 8472/* 63 */ 8473/***/ (function(module, exports) { 8474 8475exports.f = Object.getOwnPropertySymbols; 8476 8477 8478/***/ }), 8479/* 64 */ 8480/***/ (function(module, exports, __webpack_require__) { 8481 8482"use strict"; 8483 8484 8485var _keys = __webpack_require__(8); 8486 8487var _keys2 = _interopRequireDefault(_keys); 8488 8489var _stringify = __webpack_require__(19); 8490 8491var _stringify2 = _interopRequireDefault(_stringify); 8492 8493var _typeof2 = __webpack_require__(6); 8494 8495var _typeof3 = _interopRequireDefault(_typeof2); 8496 8497function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 8498 8499var util = __webpack_require__(2); 8500var moment = __webpack_require__(9); 8501var Component = __webpack_require__(16); 8502var DateUtil = __webpack_require__(36); 8503 8504/** 8505 * A Range controls a numeric range with a start and end value. 8506 * The Range adjusts the range based on mouse events or programmatic changes, 8507 * and triggers events when the range is changing or has been changed. 8508 * @param {{dom: Object, domProps: Object, emitter: Emitter}} body 8509 * @param {Object} [options] See description at Range.setOptions 8510 * @constructor Range 8511 * @extends Component 8512 */ 8513function Range(body, options) { 8514 var now = moment().hours(0).minutes(0).seconds(0).milliseconds(0); 8515 var start = now.clone().add(-3, 'days').valueOf(); 8516 var end = now.clone().add(3, 'days').valueOf(); 8517 this.millisecondsPerPixelCache = undefined; 8518 8519 if (options === undefined) { 8520 this.start = start; 8521 this.end = end; 8522 } else { 8523 this.start = options.start || start; 8524 this.end = options.end || end; 8525 } 8526 8527 this.rolling = false; 8528 8529 this.body = body; 8530 this.deltaDifference = 0; 8531 this.scaleOffset = 0; 8532 this.startToFront = false;
8533 this.endToFront = true; 8534 8535 // default options 8536 this.defaultOptions = { 8537 rtl: false, 8538 start: null, 8539 end: null, 8540 moment: moment, 8541 direction: 'horizontal', // 'horizontal' or 'vertical' 8542 moveable: true, 8543 zoomable: true, 8544 min: null, 8545 max: null, 8546 zoomMin: 10, // milliseconds 8547 zoomMax: 1000 * 60 * 60 * 24 * 365 * 10000, // milliseconds 8548 rollingMode: { 8549 follow: false, 8550 offset: 0.5 8551 } 8552 }; 8553 this.options = util.extend({}, this.defaultOptions); 8554 this.props = { 8555 touch: {} 8556 }; 8557 this.animationTimer = null; 8558 8559 // drag listeners for dragging 8560 this.body.emitter.on('panstart', this._onDragStart.bind(this)); 8561 this.body.emitter.on('panmove', this._onDrag.bind(this)); 8562 this.body.emitter.on('panend', this._onDragEnd.bind(this)); 8563 8564 // mouse wheel for zooming 8565 this.body.emitter.on('mousewheel', this._onMouseWheel.bind(this)); 8566 8567 // pinch to zoom 8568 this.body.emitter.on('touch', this._onTouch.bind(this)); 8569 this.body.emitter.on('pinch', this._onPinch.bind(this)); 8570 8571 // on click of rolling mode button 8572 this.body.dom.rollingModeBtn.addEventListener('click', this.startRolling.bind(this)); 8573 8574 this.setOptions(options); 8575} 8576 8577Range.prototype = new Component(); 8578 8579/** 8580 * Set options for the range controller 8581 * @param {Object} options Available options: 8582 * {number | Date | String} start Start date for the range 8583 * {number | Date | String} end End date for the range 8584 * {number} min Minimum value for start 8585 * {number} max Maximum value for end 8586 * {number} zoomMin Set a minimum value for 8587 * (end - start). 8588 * {number} zoomMax Set a maximum value for 8589 * (end - start). 8590 * {boolean} moveable Enable moving of the range 8591 * by dragging. True by default 8592 * {boolean} zoomable Enable zooming of the range 8593 * by pinching/scrolling. True by default 8594 */ 8595Range.prototype.setOptions = function (options) { 8596 if (options) { 8597 // copy the options that we know 8598 var fields = ['animation', 'direction', 'min', 'max', 'zoomMin', 'zoomMax', 'moveable', 'zoomable', 'moment', 'activate', 'hiddenDates', 'zoomKey', 'rtl', 'showCurrentTime', 'rollingMode', 'horizontalScroll']; 8599 util.selectiveExtend(fields, this.options, options); 8600 8601 if (options.rollingMode && options.rollingMode.follow) { 8602 this.startRolling(); 8603 } 8604 if ('start' in options || 'end' in options) { 8605 // apply a new range. both start and end are optional 8606 this.setRange(options.start, options.end); 8607 } 8608 } 8609}; 8610 8611/** 8612 * Test whether direction has a valid value 8613 * @param {string} direction 'horizontal' or 'vertical' 8614 */ 8615function validateDirection(direction) { 8616 if (direction != 'horizontal' && direction != 'vertical') { 8617 throw new TypeError('Unknown direction "' + direction + '". ' + 'Choose "horizontal" or "vertical".'); 8618 } 8619} 8620 8621/** 8622 * Start auto refreshing the current time bar 8623 */ 8624Range.prototype.startRolling = function () { 8625 var me = this; 8626 8627 /** 8628 * Updates the current time. 8629 */ 8630 function update() { 8631 me.stopRolling(); 8632 me.rolling = true; 8633 8634 var interval = me.end - me.start; 8635 var t = util.convert(new Date(), 'Date').valueOf(); 8636 8637 var start = t - interval * me.options.rollingMode.offset; 8638 var end = t + interval * (1 - me.options.rollingMode.offset); 8639 8640 var options = { 8641 animation: false 8642 }; 8643 me.setRange(start, end, options); 8644 8645 // determine interval to refresh 8646 var scale = me.conversion(me.body.domProps.center.width).scale; 8647 interval = 1 / scale / 10; 8648 if (interval < 30) interval = 30; 8649 if (interval > 1000) interval = 1000; 8650 8651 me.body.dom.rollingModeBtn.style.visibility = "hidden"; 8652 // start a renderTimer to adjust for the new time 8653 me.currentTimeTimer = setTimeout(update, interval); 8654 } 8655 8656 update(); 8657}; 8658 8659/** 8660 * Stop auto refreshing the current time bar 8661 */ 8662Range.prototype.stopRolling = function () { 8663 if (this.currentTimeTimer !== undefined) { 8664 clearTimeout(this.currentTimeTimer); 8665 this.rolling = false;
8666 this.body.dom.rollingModeBtn.style.visibility = "visible"; 8667 } 8668}; 8669 8670/** 8671 * Set a new start and end range 8672 * @param {Date | number | string} [start] 8673 * @param {Date | number | string} [end] 8674 * @param {Object} options Available options: 8675 * {boolean | {duration: number, easingFunction: string}} [animation=false] 8676 * If true, the range is animated 8677 * smoothly to the new window. An object can be 8678 * provided to specify duration and easing function. 8679 * Default duration is 500 ms, and default easing 8680 * function is 'easeInOutQuad'. 8681 * {boolean} [byUser=false] 8682 * {Event} event Mouse event 8683 * @param {Function} callback a callback function to be executed at the end of this function 8684 * @param {Function} frameCallback a callback function executed each frame of the range animation. 8685 * The callback will be passed three parameters: 8686 * {number} easeCoefficient an easing coefficent 8687 * {boolean} willDraw If true the caller will redraw after the callback completes 8688 * {boolean} done If true then animation is ending after the current frame 8689 */ 8690 8691Range.prototype.setRange = function (start, end, options, callback, frameCallback) { 8692 if (!options) { 8693 options = {}; 8694 } 8695 if (options.byUser !== true) { 8696 options.byUser = false; 8697 } 8698 var me = this; 8699 var finalStart = start != undefined ? util.convert(start, 'Date').valueOf() : null; 8700 var finalEnd = end != undefined ? util.convert(end, 'Date').valueOf() : null; 8701 this._cancelAnimation(); 8702 this.millisecondsPerPixelCache = undefined; 8703 8704 if (options.animation) { 8705 // true or an Object 8706 var initStart = this.start; 8707 var initEnd = this.end; 8708 var duration = (0, _typeof3['default'])(options.animation) === 'object' && 'duration' in options.animation ? options.animation.duration : 500; 8709 var easingName = (0, _typeof3['default'])(options.animation) === 'object' && 'easingFunction' in options.animation ? options.animation.easingFunction : 'easeInOutQuad'; 8710 var easingFunction = util.easingFunctions[easingName]; 8711 if (!easingFunction) { 8712 throw new Error('Unknown easing function ' + (0, _stringify2['default'])(easingName) + '. ' + 'Choose from: ' + (0, _keys2['default'])(util.easingFunctions).join(', ')); 8713 } 8714 8715 var initTime = new Date().valueOf(); 8716 var anyChanged = false; 8717 8718 var next = function next() { 8719 if (!me.props.touch.dragging) { 8720 var now = new Date().valueOf(); 8721 var time = now - initTime; 8722 var ease = easingFunction(time / duration); 8723 var done = time > duration; 8724 var s = done || finalStart === null ? finalStart : initStart + (finalStart - initStart) * ease; 8725 var e = done || finalEnd === null ? finalEnd : initEnd + (finalEnd - initEnd) * ease; 8726 8727 changed = me._applyRange(s, e); 8728 DateUtil.updateHiddenDates(me.options.moment, me.body, me.options.hiddenDates); 8729 anyChanged = anyChanged || changed; 8730 8731 var params = { 8732 start: new Date(me.start), 8733 end: new Date(me.end), 8734 byUser: options.byUser, 8735 event: options.event 8736 }; 8737 8738 if (frameCallback) { 8739 frameCallback(ease, changed, done); 8740 } 8741 8742 if (changed) { 8743 me.body.emitter.emit('rangechange', params); 8744 } 8745 8746 if (done) { 8747 if (anyChanged) { 8748 me.body.emitter.emit('rangechanged', params); 8749 if (callback) { 8750 return callback(); 8751 } 8752 } 8753 } else { 8754 // animate with as high as possible frame rate, leave 20 ms in between 8755 // each to prevent the browser from blocking 8756 me.animationTimer = setTimeout(next, 20); 8757 } 8758 } 8759 }; 8760 8761 return next(); 8762 } else { 8763 var changed = this._applyRange(finalStart, finalEnd); 8764 DateUtil.updateHiddenDates(this.options.moment, this.body, this.options.hiddenDates); 8765 if (changed) { 8766 var params = { 8767 start: new Date(this.start), 8768 end: new Date(this.end), 8769 byUser: options.byUser, 8770 event: options.event 8771 }; 8772 8773 this.body.emitter.emit('rangechange', params); 8774 clearTimeout(me.timeoutID); 8775 me.timeoutID = setTimeout(function () { 8776 me.body.emitter.emit('rangechanged', params); 8777 }, 200); 8778 if (callback) { 8779 return callback(); 8780 } 8781 } 8782 } 8783}; 8784 8785/** 8786 * Get the number of milliseconds per pixel. 8787 * 8788 * @returns {undefined|number} 8789 */ 8790Range.prototype.getMillisecondsPerPixel = function () { 8791 if (this.millisecondsPerPixelCache === undefined) {
8792 this.millisecondsPerPixelCache = (this.end - this.start) / this.body.dom.center.clientWidth; 8793 } 8794 return this.millisecondsPerPixelCache; 8795}; 8796 8797/** 8798 * Stop an animation 8799 * @private 8800 */ 8801Range.prototype._cancelAnimation = function () { 8802 if (this.animationTimer) { 8803 clearTimeout(this.animationTimer); 8804 this.animationTimer = null; 8805 } 8806}; 8807 8808/** 8809 * Set a new start and end range. This method is the same as setRange, but 8810 * does not trigger a range change and range changed event, and it returns 8811 * true when the range is changed 8812 * @param {number} [start] 8813 * @param {number} [end] 8814 * @return {boolean} changed 8815 * @private 8816 */ 8817Range.prototype._applyRange = function (start, end) { 8818 var newStart = start != null ? util.convert(start, 'Date').valueOf() : this.start, 8819 newEnd = end != null ? util.convert(end, 'Date').valueOf() : this.end, 8820 max = this.options.max != null ? util.convert(this.options.max, 'Date').valueOf() : null, 8821 min = this.options.min != null ? util.convert(this.options.min, 'Date').valueOf() : null, 8822 diff; 8823 8824 // check for valid number 8825 if (isNaN(newStart) || newStart === null) { 8826 throw new Error('Invalid start "' + start + '"'); 8827 } 8828 if (isNaN(newEnd) || newEnd === null) { 8829 throw new Error('Invalid end "' + end + '"'); 8830 } 8831 8832 // prevent end < start 8833 if (newEnd < newStart) { 8834 newEnd = newStart; 8835 } 8836 8837 // prevent start < min 8838 if (min !== null) { 8839 if (newStart < min) { 8840 diff = min - newStart; 8841 newStart += diff; 8842 newEnd += diff; 8843 8844 // prevent end > max 8845 if (max != null) { 8846 if (newEnd > max) { 8847 newEnd = max; 8848 } 8849 } 8850 } 8851 } 8852 8853 // prevent end > max 8854 if (max !== null) { 8855 if (newEnd > max) { 8856 diff = newEnd - max; 8857 newStart -= diff; 8858 newEnd -= diff; 8859 8860 // prevent start < min 8861 if (min != null) { 8862 if (newStart < min) { 8863 newStart = min; 8864 } 8865 } 8866 } 8867 } 8868 8869 // prevent (end-start) < zoomMin 8870 if (this.options.zoomMin !== null) { 8871 var zoomMin = parseFloat(this.options.zoomMin); 8872 if (zoomMin < 0) { 8873 zoomMin = 0; 8874 } 8875 if (newEnd - newStart < zoomMin) { 8876 // compensate for a scale of 0.5 ms 8877 var compensation = 0.5; 8878 if (this.end - this.start === zoomMin && newStart >= this.start - compensation && newEnd <= this.end) { 8879 // ignore this action, we are already zoomed to the minimum 8880 newStart = this.start; 8881 newEnd = this.end; 8882 } else { 8883 // zoom to the minimum 8884 diff = zoomMin - (newEnd - newStart); 8885 newStart -= diff / 2; 8886 newEnd += diff / 2; 8887 } 8888 } 8889 } 8890 8891 // prevent (end-start) > zoomMax 8892 if (this.options.zoomMax !== null) { 8893 var zoomMax = parseFloat(this.options.zoomMax); 8894 if (zoomMax < 0) { 8895 zoomMax = 0; 8896 } 8897 8898 if (newEnd - newStart > zoomMax) { 8899 if (this.end - this.start === zoomMax && newStart < this.start && newEnd > this.end) { 8900 // ignore this action, we are already zoomed to the maximum 8901 newStart = this.start; 8902 newEnd = this.end; 8903 } else { 8904 // zoom to the maximum 8905 diff = newEnd - newStart - zoomMax; 8906 newStart += diff / 2; 8907 newEnd -= diff / 2; 8908 } 8909 } 8910 } 8911 8912 var changed = this.start != newStart || this.end != newEnd; 8913 8914 // if the new range does NOT overlap with the old range, emit checkRangedItems to avoid not showing ranged items (ranged meaning has end time, not necessarily of type Range) 8915 if (!(newStart >= this.start && newStart <= this.end || newEnd >= this.start && newEnd <= this.end) && !(this.start >= newStart && this.start <= newEnd || this.end >= newStart && this.end <= newEnd)) { 8916 this.body.emitter.emit('checkRangedItems'); 8917 } 8918 8919 this.start = newStart; 8920 this.end = newEnd; 8921 return changed; 8922}; 8923 8924/** 8925 * Retrieve the current range. 8926 * @return {Object} An object with start and end properties 8927 */ 8928Range.prototype.getRange = function () { 8929 return { 8930 start: this.start, 8931 end: this.end 8932 }; 8933}; 8934 8935/** 8936 * Calculate the conversion offset and scale for current range, based on 8937 * the provided width 8938 * @param {number} width 8939 * @param {number} [totalHidden=0] 8940 * @returns {{offset: number, scale: number}} conversion 8941 */ 8942Range.prototype.conversion = function (width, totalHidden) { 8943 return Range.conversion(this.start, this.end, width, totalHidden); 8944}; 8945 8946/** 8947 * Static method to calculate the conversion offset and scale for a range, 8948 * based on the provided start, end, and width 8949 * @param {number} start 8950 * @param {number} end 8951 * @param {number} width 8952 * @param {number} [totalHidden=0] 8953 * @returns {{offset: number, scale: number}} conversion 8954 */ 8955Range.conversion = function (start, end, width, totalHidden) { 8956 if (totalHidden === undefined) { 8957 totalHidden = 0; 8958 } 8959 if (width != 0 && end - start != 0) { 8960 return { 8961 offset: start, 8962 scale: width / (end - start - totalHidden) 8963 }; 8964 } else { 8965 return { 8966 offset: 0, 8967 scale: 1 8968 }; 8969 } 8970}; 8971 8972/** 8973 * Start dragging horizontally or vertically 8974 * @param {Event} event 8975 * @private 8976 */ 8977Range.prototype._onDragStart = function (event) { 8978 this.deltaDifference = 0; 8979 this.previousDelta = 0; 8980 8981 // only allow dragging when configured as movable 8982 if (!this.options.moveable) return; 8983 8984 // only start dragging when the mouse is inside the current range 8985 if (!this._isInsideRange(event)) return; 8986 8987 // refuse to drag when we where pinching to prevent the timeline make a jump 8988 // when releasing the fingers in opposite order from the touch screen 8989 if (!this.props.touch.allowDragging) return; 8990 8991 this.stopRolling(); 8992 8993 this.props.touch.start = this.start; 8994 this.props.touch.end = this.end; 8995 this.props.touch.dragging = true; 8996 8997 if (this.body.dom.root) { 8998 this.body.dom.root.style.cursor = 'move'; 8999 } 9000}; 9001 9002/** 9003 * Perform dragging operation 9004 * @param {Event} event 9005 * @private 9006 */ 9007Range.prototype._onDrag = function (event) { 9008 if (!event) return; 9009 9010 if (!this.props.touch.dragging) return; 9011 9012 // only allow dragging when configured as movable 9013 if (!this.options.moveable) return; 9014 9015 // TODO: this may be redundant in hammerjs2 9016 // refuse to drag when we where pinching to prevent the timeline make a jump 9017 // when releasing the fingers in opposite order from the touch screen 9018 if (!this.props.touch.allowDragging) return; 9019 9020 var direction = this.options.direction; 9021 validateDirection(direction); 9022 var delta = direction == 'horizontal' ? event.deltaX : event.deltaY; 9023 delta -= this.deltaDifference; 9024 var interval = this.props.touch.end - this.props.touch.start; 9025 9026 // normalize dragging speed if cutout is in between. 9027 var duration = DateUtil.getHiddenDurationBetween(this.body.hiddenDates, this.start, this.end); 9028 interval -= duration; 9029 9030 var width = direction == 'horizontal' ? this.body.domProps.center.width : this.body.domProps.center.height; 9031 var diffRange; 9032 if (this.options.rtl) { 9033 diffRange = delta / width * interval; 9034 } else { 9035 diffRange = -delta / width * interval; 9036 } 9037 9038 var newStart = this.props.touch.start + diffRange; 9039 var newEnd = this.props.touch.end + diffRange; 9040 9041 // snapping times away from hidden zones 9042 var safeStart = DateUtil.snapAwayFromHidden(this.body.hiddenDates, newStart, this.previousDelta - delta, true); 9043 var safeEnd = DateUtil.snapAwayFromHidden(this.body.hiddenDates, newEnd, this.previousDelta - delta, true); 9044 if (safeStart != newStart || safeEnd != newEnd) { 9045 this.deltaDifference += delta; 9046 this.props.touch.start = safeStart; 9047 this.props.touch.end = safeEnd; 9048 this._onDrag(event); 9049 return; 9050 } 9051 9052 this.previousDelta = delta; 9053 this._applyRange(newStart, newEnd); 9054 9055 var startDate = new Date(this.start); 9056 var endDate = new Date(this.end); 9057 9058 // fire a rangechange event 9059 this.body.emitter.emit('rangechange', { 9060 start: startDate, 9061 end: endDate, 9062 byUser: true, 9063 event: event 9064 }); 9065 9066 // fire a panmove event 9067 this.body.emitter.emit('panmove'); 9068}; 9069 9070/** 9071 * Stop dragging operation 9072 * @param {event} event 9073 * @private 9074 */ 9075Range.prototype._onDragEnd = function (event) { 9076 if (!this.props.touch.dragging) return; 9077 9078 // only allow dragging when configured as movable 9079 if (!this.options.moveable) return; 9080 9081 // TODO: this may be redundant in hammerjs2 9082 // refuse to drag when we where pinching to prevent the timeline make a jump 9083 // when releasing the fingers in opposite order from the touch screen 9084 if (!this.props.touch.allowDragging) return; 9085 9086 this.props.touch.dragging = false;
9087 if (this.body.dom.root) { 9088 this.body.dom.root.style.cursor = 'auto'; 9089 } 9090 9091 // fire a rangechanged event 9092 this.body.emitter.emit('rangechanged', { 9093 start: new Date(this.start), 9094 end: new Date(this.end), 9095 byUser: true, 9096 event: event 9097 }); 9098}; 9099 9100/** 9101 * Event handler for mouse wheel event, used to zoom 9102 * Code from http://adomas.org/javascript-mouse-wheel/ 9103 * @param {Event} event 9104 * @private 9105 */ 9106Range.prototype._onMouseWheel = function (event) { 9107 // retrieve delta 9108 var delta = 0; 9109 if (event.wheelDelta) { 9110 /* IE/Opera. */ 9111 delta = event.wheelDelta / 120; 9112 } else if (event.detail) { 9113 /* Mozilla case. */ 9114 // In Mozilla, sign of delta is different than in IE. 9115 // Also, delta is multiple of 3. 9116 delta = -event.detail / 3; 9117 } 9118 9119 // don't allow zoom when the according key is pressed and the zoomKey option or not zoomable but movable 9120 if (this.options.zoomKey && !event[this.options.zoomKey] && this.options.zoomable || !this.options.zoomable && this.options.moveable) { 9121 return; 9122 } 9123 9124 // only allow zooming when configured as zoomable and moveable 9125 if (!(this.options.zoomable && this.options.moveable)) return; 9126 9127 // only zoom when the mouse is inside the current range 9128 if (!this._isInsideRange(event)) return; 9129 9130 // If delta is nonzero, handle it. 9131 // Basically, delta is now positive if wheel was scrolled up, 9132 // and negative, if wheel was scrolled down. 9133 if (delta) { 9134 // perform the zoom action. Delta is normally 1 or -1 9135 9136 // adjust a negative delta such that zooming in with delta 0.1 9137 // equals zooming out with a delta -0.1 9138 var scale; 9139 if (delta < 0) { 9140 scale = 1 - delta / 5; 9141 } else { 9142 scale = 1 / (1 + delta / 5); 9143 } 9144 9145 // calculate center, the date to zoom around 9146 var pointerDate; 9147 if (this.rolling) { 9148 pointerDate = this.start + (this.end - this.start) * this.options.rollingMode.offset; 9149 } else { 9150 var pointer = this.getPointer({ x: event.clientX, y: event.clientY }, this.body.dom.center); 9151 pointerDate = this._pointerToDate(pointer); 9152 } 9153 this.zoom(scale, pointerDate, delta, event); 9154 9155 // Prevent default actions caused by mouse wheel 9156 // (else the page and timeline both scroll) 9157 event.preventDefault(); 9158 } 9159}; 9160 9161/** 9162 * Start of a touch gesture 9163 * @param {Event} event 9164 * @private 9165 */ 9166Range.prototype._onTouch = function (event) { 9167 // eslint-disable-line no-unused-vars 9168 this.props.touch.start = this.start; 9169 this.props.touch.end = this.end; 9170 this.props.touch.allowDragging = true; 9171 this.props.touch.center = null; 9172 this.scaleOffset = 0; 9173 this.deltaDifference = 0; 9174 // Disable the browser default handling of this event. 9175 util.preventDefault(event); 9176}; 9177 9178/** 9179 * Handle pinch event 9180 * @param {Event} event 9181 * @private 9182 */ 9183Range.prototype._onPinch = function (event) { 9184 // only allow zooming when configured as zoomable and moveable 9185 if (!(this.options.zoomable && this.options.moveable)) return; 9186 9187 // Disable the browser default handling of this event. 9188 util.preventDefault(event); 9189 9190 this.props.touch.allowDragging = false; 9191 9192 if (!this.props.touch.center) { 9193 this.props.touch.center = this.getPointer(event.center, this.body.dom.center); 9194 } 9195 9196 this.stopRolling(); 9197 9198 var scale = 1 / (event.scale + this.scaleOffset); 9199 var centerDate = this._pointerToDate(this.props.touch.center); 9200 9201 var hiddenDuration = DateUtil.getHiddenDurationBetween(this.body.hiddenDates, this.start, this.end); 9202 var hiddenDurationBefore = DateUtil.getHiddenDurationBefore(this.options.moment, this.body.hiddenDates, this, centerDate); 9203 var hiddenDurationAfter = hiddenDuration - hiddenDurationBefore; 9204 9205 // calculate new start and end 9206 var newStart = centerDate - hiddenDurationBefore + (this.props.touch.start - (centerDate - hiddenDurationBefore)) * scale; 9207 var newEnd = centerDate + hiddenDurationAfter + (this.props.touch.end - (centerDate + hiddenDurationAfter)) * scale; 9208 9209 // snapping times away from hidden zones 9210 this.startToFront = 1 - scale <= 0; // used to do the right auto correction with periodic hidden times 9211 this.endToFront = scale - 1 <= 0; // used to do the right auto correction with periodic hidden times 9212 9213 var safeStart = DateUtil.snapAwayFromHidden(this.body.hiddenDates, newStart, 1 - scale, true); 9214 var safeEnd = DateUtil.snapAwayFromHidden(this.body.hiddenDates, newEnd, scale - 1, true); 9215 if (safeStart != newStart || safeEnd != newEnd) { 9216 this.props.touch.start = safeStart; 9217 this.props.touch.end = safeEnd; 9218 this.scaleOffset = 1 - event.scale; 9219 newStart = safeStart; 9220 newEnd = safeEnd; 9221 } 9222 9223 var options = { 9224 animation: false, 9225 byUser: true, 9226 event: event 9227 }; 9228 this.setRange(newStart, newEnd, options); 9229 9230 this.startToFront = false; // revert to default 9231 this.endToFront = true; // revert to default 9232}; 9233 9234/** 9235 * Test whether the mouse from a mouse event is inside the visible window, 9236 * between the current start and end date 9237 * @param {Object} event 9238 * @return {boolean} Returns true when inside the visible window 9239 * @private 9240 */ 9241Range.prototype._isInsideRange = function (event) { 9242 // calculate the time where the mouse is, check whether inside 9243 // and no scroll action should happen. 9244 var clientX = event.center ? event.center.x : event.clientX; 9245 var x; 9246 if (this.options.rtl) { 9247 x = clientX - util.getAbsoluteLeft(this.body.dom.centerContainer); 9248 } else { 9249 x = util.getAbsoluteRight(this.body.dom.centerContainer) - clientX; 9250 } 9251 var time = this.body.util.toTime(x); 9252 9253 return time >= this.start && time <= this.end; 9254}; 9255 9256/** 9257 * Helper function to calculate the center date for zooming 9258 * @param {{x: number, y: number}} pointer 9259 * @return {number} date 9260 * @private 9261 */ 9262Range.prototype._pointerToDate = function (pointer) { 9263 var conversion; 9264 var direction = this.options.direction; 9265 9266 validateDirection(direction); 9267 9268 if (direction == 'horizontal') { 9269 return this.body.util.toTime(pointer.x).valueOf(); 9270 } else { 9271 var height = this.body.domProps.center.height; 9272 conversion = this.conversion(height); 9273 return pointer.y / conversion.scale + conversion.offset; 9274 } 9275}; 9276 9277/** 9278 * Get the pointer location relative to the location of the dom element 9279 * @param {{x: number, y: number}} touch 9280 * @param {Element} element HTML DOM element 9281 * @return {{x: number, y: number}} pointer 9282 * @private 9283 */ 9284Range.prototype.getPointer = function (touch, element) { 9285 if (this.options.rtl) { 9286 return { 9287 x: util.getAbsoluteRight(element) - touch.x, 9288 y: touch.y - util.getAbsoluteTop(element) 9289 }; 9290 } else { 9291 return { 9292 x: touch.x - util.getAbsoluteLeft(element), 9293 y: touch.y - util.getAbsoluteTop(element) 9294 }; 9295 } 9296}; 9297 9298/** 9299 * Zoom the range the given scale in or out. Start and end date will
9300 * be adjusted, and the timeline will be redrawn. You can optionally give a 9301 * date around which to zoom. 9302 * For example, try scale = 0.9 or 1.1 9303 * @param {number} scale Scaling factor. Values above 1 will zoom out, 9304 * values below 1 will zoom in. 9305 * @param {number} [center] Value representing a date around which will 9306 * be zoomed. 9307 * @param {number} delta 9308 * @param {Event} event 9309 */ 9310Range.prototype.zoom = function (scale, center, delta, event) { 9311 // if centerDate is not provided, take it half between start Date and end Date 9312 if (center == null) { 9313 center = (this.start + this.end) / 2; 9314 } 9315 9316 var hiddenDuration = DateUtil.getHiddenDurationBetween(this.body.hiddenDates, this.start, this.end); 9317 var hiddenDurationBefore = DateUtil.getHiddenDurationBefore(this.options.moment, this.body.hiddenDates, this, center); 9318 var hiddenDurationAfter = hiddenDuration - hiddenDurationBefore; 9319 9320 // calculate new start and end 9321 var newStart = center - hiddenDurationBefore + (this.start - (center - hiddenDurationBefore)) * scale; 9322 var newEnd = center + hiddenDurationAfter + (this.end - (center + hiddenDurationAfter)) * scale; 9323 9324 // snapping times away from hidden zones 9325 this.startToFront = delta > 0 ? false : true; // used to do the right autocorrection with periodic hidden times 9326 this.endToFront = -delta > 0 ? false : true; // used to do the right autocorrection with periodic hidden times 9327 var safeStart = DateUtil.snapAwayFromHidden(this.body.hiddenDates, newStart, delta, true); 9328 var safeEnd = DateUtil.snapAwayFromHidden(this.body.hiddenDates, newEnd, -delta, true); 9329 if (safeStart != newStart || safeEnd != newEnd) { 9330 newStart = safeStart; 9331 newEnd = safeEnd; 9332 } 9333 9334 var options = { 9335 animation: false, 9336 byUser: true, 9337 event: event 9338 }; 9339 this.setRange(newStart, newEnd, options); 9340 9341 this.startToFront = false; // revert to default 9342 this.endToFront = true; // revert to default 9343}; 9344 9345/** 9346 * Move the range with a given delta to the left or right. Start and end 9347 * value will be adjusted. For example, try delta = 0.1 or -0.1 9348 * @param {number} delta Moving amount. Positive value will move right, 9349 * negative value will move left 9350 */ 9351Range.prototype.move = function (delta) { 9352 // zoom start Date and end Date relative to the centerDate 9353 var diff = this.end - this.start; 9354 9355 // apply new values 9356 var newStart = this.start + diff * delta; 9357 var newEnd = this.end + diff * delta; 9358 9359 // TODO: reckon with min and max range 9360 9361 this.start = newStart; 9362 this.end = newEnd; 9363}; 9364 9365/** 9366 * Move the range to a new center point 9367 * @param {number} moveTo New center point of the range 9368 */ 9369Range.prototype.moveTo = function (moveTo) { 9370 var center = (this.start + this.end) / 2; 9371 9372 var diff = center - moveTo; 9373 9374 // calculate new start and end 9375 var newStart = this.start - diff; 9376 var newEnd = this.end - diff; 9377 9378 var options = { 9379 animation: false, 9380 byUser: true, 9381 event: null 9382 }; 9383 this.setRange(newStart, newEnd, options); 9384}; 9385 9386module.exports = Range; 9387 9388/***/ }), 9389/* 65 */ 9390/***/ (function(module, exports, __webpack_require__) { 9391 9392"use strict"; 9393 9394 9395var _stringify = __webpack_require__(19); 9396 9397var _stringify2 = _interopRequireDefault(_stringify); 9398 9399var _typeof2 = __webpack_require__(6); 9400 9401var _typeof3 = _interopRequireDefault(_typeof2); 9402 9403function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 9404 9405var Emitter = __webpack_require__(44); 9406var Hammer = __webpack_require__(10); 9407var hammerUtil = __webpack_require__(37); 9408var util = __webpack_require__(2);
9409var TimeAxis = __webpack_require__(45); 9410var Activator = __webpack_require__(97); 9411var DateUtil = __webpack_require__(36); 9412var CustomTime = __webpack_require__(46); 9413 9414/** 9415 * Create a timeline visualization 9416 * @constructor Core 9417 */ 9418function Core() {} 9419 9420// turn Core into an event emitter 9421Emitter(Core.prototype); 9422 9423/** 9424 * Create the main DOM for the Core: a root panel containing left, right, 9425 * top, bottom, content, and background panel. 9426 * @param {Element} container The container element where the Core will 9427 * be attached. 9428 * @protected 9429 */ 9430Core.prototype._create = function (container) { 9431 this.dom = {}; 9432 9433 this.dom.container = container; 9434 9435 this.dom.root = document.createElement('div'); 9436 this.dom.background = document.createElement('div'); 9437 this.dom.backgroundVertical = document.createElement('div'); 9438 this.dom.backgroundHorizontal = document.createElement('div'); 9439 this.dom.centerContainer = document.createElement('div'); 9440 this.dom.leftContainer = document.createElement('div'); 9441 this.dom.rightContainer = document.createElement('div'); 9442 this.dom.center = document.createElement('div'); 9443 this.dom.left = document.createElement('div'); 9444 this.dom.right = document.createElement('div'); 9445 this.dom.top = document.createElement('div'); 9446 this.dom.bottom = document.createElement('div'); 9447 this.dom.shadowTop = document.createElement('div'); 9448 this.dom.shadowBottom = document.createElement('div'); 9449 this.dom.shadowTopLeft = document.createElement('div'); 9450 this.dom.shadowBottomLeft = document.createElement('div'); 9451 this.dom.shadowTopRight = document.createElement('div'); 9452 this.dom.shadowBottomRight = document.createElement('div'); 9453 this.dom.rollingModeBtn = document.createElement('div'); 9454 9455 this.dom.root.className = 'vis-timeline'; 9456 this.dom.background.className = 'vis-panel vis-background'; 9457 this.dom.backgroundVertical.className = 'vis-panel vis-background vis-vertical'; 9458 this.dom.backgroundHorizontal.className = 'vis-panel vis-background vis-horizontal'; 9459 this.dom.centerContainer.className = 'vis-panel vis-center'; 9460 this.dom.leftContainer.className = 'vis-panel vis-left'; 9461 this.dom.rightContainer.className = 'vis-panel vis-right'; 9462 this.dom.top.className = 'vis-panel vis-top'; 9463 this.dom.bottom.className = 'vis-panel vis-bottom'; 9464 this.dom.left.className = 'vis-content'; 9465 this.dom.center.className = 'vis-content'; 9466 this.dom.right.className = 'vis-content'; 9467 this.dom.shadowTop.className = 'vis-shadow vis-top'; 9468 this.dom.shadowBottom.className = 'vis-shadow vis-bottom'; 9469 this.dom.shadowTopLeft.className = 'vis-shadow vis-top'; 9470 this.dom.shadowBottomLeft.className = 'vis-shadow vis-bottom'; 9471 this.dom.shadowTopRight.className = 'vis-shadow vis-top'; 9472 this.dom.shadowBottomRight.className = 'vis-shadow vis-bottom'; 9473 this.dom.rollingModeBtn.className = 'vis-rolling-mode-btn'; 9474 9475 this.dom.root.appendChild(this.dom.background); 9476 this.dom.root.appendChild(this.dom.backgroundVertical); 9477 this.dom.root.appendChild(this.dom.backgroundHorizontal); 9478 this.dom.root.appendChild(this.dom.centerContainer); 9479 this.dom.root.appendChild(this.dom.leftContainer); 9480 this.dom.root.appendChild(this.dom.rightContainer); 9481 this.dom.root.appendChild(this.dom.top); 9482 this.dom.root.appendChild(this.dom.bottom); 9483 this.dom.root.appendChild(this.dom.bottom); 9484 this.dom.root.appendChild(this.dom.rollingModeBtn); 9485 9486 this.dom.centerContainer.appendChild(this.dom.center); 9487 this.dom.leftContainer.appendChild(this.dom.left); 9488 this.dom.rightContainer.appendChild(this.dom.right); 9489 this.dom.centerContainer.appendChild(this.dom.shadowTop); 9490 this.dom.centerContainer.appendChild(this.dom.shadowBottom); 9491 this.dom.leftContainer.appendChild(this.dom.shadowTopLeft); 9492 this.dom.leftContainer.appendChild(this.dom.shadowBottomLeft); 9493 this.dom.rightContainer.appendChild(this.dom.shadowTopRight); 9494 this.dom.rightContainer.appendChild(this.dom.shadowBottomRight); 9495 9496 // size properties of each of the panels 9497 this.props = { 9498 root: {}, 9499 background: {}, 9500 centerContainer: {}, 9501 leftContainer: {}, 9502 rightContainer: {}, 9503 center: {}, 9504 left: {}, 9505 right: {}, 9506 top: {}, 9507 bottom: {}, 9508 border: {}, 9509 scrollTop: 0, 9510 scrollTopMin: 0 9511 }; 9512 9513 this.on('rangechange', function () { 9514 if (this.initialDrawDone === true) { 9515 this._redraw(); 9516 } 9517 }.bind(this)); 9518 this.on('rangechanged', function () { 9519 if (!this.initialRangeChangeDone) { 9520 this.initialRangeChangeDone = true; 9521 } 9522 }.bind(this)); 9523 this.on('touch', this._onTouch.bind(this)); 9524 this.on('panmove', this._onDrag.bind(this)); 9525 9526 var me = this; 9527 this._origRedraw = this._redraw.bind(this); 9528 this._redraw = util.throttle(this._origRedraw); 9529 9530 this.on('_change', function (properties) { 9531 if (me.itemSet && me.itemSet.initialItemSetDrawn && properties && properties.queue == true) { 9532 me._redraw(); 9533 } else { 9534 me._origRedraw(); 9535 } 9536 }); 9537 9538 // create event listeners for all interesting events, these events will be 9539 // emitted via emitter 9540 this.hammer = new Hammer(this.dom.root); 9541 var pinchRecognizer = this.hammer.get('pinch').set({ enable: true }); 9542 hammerUtil.disablePreventDefaultVertically(pinchRecognizer); 9543 this.hammer.get('pan').set({ threshold: 5, direction: Hammer.DIRECTION_HORIZONTAL }); 9544 this.listeners = {}; 9545 9546 var events = ['tap', 'doubletap', 'press', 'pinch', 'pan', 'panstart', 'panmove', 'panend' 9547 // TODO: cleanup 9548 //'touch', 'pinch', 9549 //'tap', 'doubletap', 'hold', 9550 //'dragstart', 'drag', 'dragend', 9551 //'mousewheel', 'DOMMouseScroll' // DOMMouseScroll is needed for Firefox 9552 ];
9553 events.forEach(function (type) { 9554 var listener = function listener(event) { 9555 if (me.isActive()) { 9556 me.emit(type, event); 9557 } 9558 }; 9559 me.hammer.on(type, listener); 9560 me.listeners[type] = listener; 9561 }); 9562 9563 // emulate a touch event (emitted before the start of a pan, pinch, tap, or press) 9564 hammerUtil.onTouch(this.hammer, function (event) { 9565 me.emit('touch', event); 9566 }.bind(this)); 9567 9568 // emulate a release event (emitted after a pan, pinch, tap, or press) 9569 hammerUtil.onRelease(this.hammer, function (event) { 9570 me.emit('release', event); 9571 }.bind(this)); 9572 9573 /** 9574 * 9575 * @param {WheelEvent} event 9576 */ 9577 function onMouseWheel(event) { 9578 if (this.isActive()) { 9579 this.emit('mousewheel', event); 9580 } 9581 9582 // deltaX and deltaY normalization from jquery.mousewheel.js 9583 var deltaX = 0; 9584 var deltaY = 0; 9585 9586 // Old school scrollwheel delta 9587 if ('detail' in event) { 9588 deltaY = event.detail * -1; 9589 } 9590 if ('wheelDelta' in event) { 9591 deltaY = event.wheelDelta; 9592 } 9593 if ('wheelDeltaY' in event) { 9594 deltaY = event.wheelDeltaY; 9595 } 9596 if ('wheelDeltaX' in event) { 9597 deltaX = event.wheelDeltaX * -1; 9598 } 9599 9600 // Firefox < 17 horizontal scrolling related to DOMMouseScroll event 9601 if ('axis' in event && event.axis === event.HORIZONTAL_AXIS) { 9602 deltaX = deltaY * -1; 9603 deltaY = 0; 9604 } 9605 9606 // New school wheel delta (wheel event) 9607 if ('deltaY' in event) { 9608 deltaY = event.deltaY * -1; 9609 } 9610 if ('deltaX' in event) { 9611 deltaX = event.deltaX; 9612 } 9613 9614 // prevent scrolling when zoomKey defined or activated 9615 if (!this.options.zoomKey || event[this.options.zoomKey]) return; 9616 9617 // Prevent default actions caused by mouse wheel 9618 // (else the page and timeline both scroll) 9619 event.preventDefault(); 9620 9621 if (this.options.verticalScroll && Math.abs(deltaY) >= Math.abs(deltaX)) { 9622 var current = this.props.scrollTop; 9623 var adjusted = current + deltaY; 9624 9625 if (this.isActive()) { 9626 this._setScrollTop(adjusted); 9627 this._redraw(); 9628 this.emit('scroll', event); 9629 } 9630 } else if (this.options.horizontalScroll) { 9631 var delta = Math.abs(deltaX) >= Math.abs(deltaY) ? deltaX : deltaY; 9632 9633 // calculate a single scroll jump relative to the range scale 9634 var diff = delta / 120 * (this.range.end - this.range.start) / 20; 9635 // calculate new start and end 9636 var newStart = this.range.start + diff; 9637 var newEnd = this.range.end + diff; 9638 9639 var options = { 9640 animation: false, 9641 byUser: true, 9642 event: event 9643 }; 9644 this.range.setRange(newStart, newEnd, options); 9645 } 9646 } 9647 9648 if (this.dom.centerContainer.addEventListener) { 9649 // IE9, Chrome, Safari, Opera 9650 this.dom.centerContainer.addEventListener("mousewheel", onMouseWheel.bind(this), false); 9651 // Firefox 9652 this.dom.centerContainer.addEventListener("DOMMouseScroll", onMouseWheel.bind(this), false); 9653 } else { 9654 // IE 6/7/8 9655 this.dom.centerContainer.attachEvent("onmousewheel", onMouseWheel.bind(this)); 9656 } 9657 9658 /** 9659 * 9660 * @param {scroll} event 9661 */ 9662 function onMouseScrollSide(event) { 9663 if (!me.options.verticalScroll) return; 9664 event.preventDefault(); 9665 if (me.isActive()) { 9666 var adjusted = -event.target.scrollTop; 9667 me._setScrollTop(adjusted); 9668 me._redraw(); 9669 me.emit('scrollSide', event); 9670 } 9671 } 9672 9673 this.dom.left.parentNode.addEventListener('scroll', onMouseScrollSide.bind(this)); 9674 this.dom.right.parentNode.addEventListener('scroll', onMouseScrollSide.bind(this)); 9675 9676 var itemAddedToTimeline = false; 9677 9678 /** 9679 * 9680 * @param {dragover} event 9681 * @returns {boolean} 9682 */ 9683 function handleDragOver(event) { 9684 if (event.preventDefault) { 9685 event.preventDefault(); // Necessary. Allows us to drop. 9686 } 9687 9688 // make sure your target is a vis element 9689 if (!event.target.className.indexOf("vis") > -1) return; 9690 9691 // make sure only one item is added every time you're over the timeline 9692 if (itemAddedToTimeline) return; 9693 9694 event.dataTransfer.dropEffect = 'move'; 9695 itemAddedToTimeline = true; 9696 return false;
9697 } 9698 9699 /** 9700 * 9701 * @param {drop} event 9702 * @returns {boolean} 9703 */ 9704 function handleDrop(event) { 9705 // prevent redirect to blank page - Firefox 9706 if (event.preventDefault) { 9707 event.preventDefault(); 9708 } 9709 if (event.stopPropagation) { 9710 event.stopPropagation(); 9711 } 9712 // return when dropping non-vis items 9713 try { 9714 var itemData = JSON.parse(event.dataTransfer.getData("text")); 9715 if (!itemData || !itemData.content) return; 9716 } catch (err) { 9717 return false; 9718 } 9719 9720 itemAddedToTimeline = false; 9721 event.center = { 9722 x: event.clientX, 9723 y: event.clientY 9724 }; 9725 9726 if (itemData.target !== 'item') { 9727 me.itemSet._onAddItem(event); 9728 } else { 9729 me.itemSet._onDropObjectOnItem(event); 9730 } 9731 me.emit('drop', me.getEventProperties(event)); 9732 return false; 9733 } 9734 9735 this.dom.center.addEventListener('dragover', handleDragOver.bind(this), false); 9736 this.dom.center.addEventListener('drop', handleDrop.bind(this), false); 9737 9738 this.customTimes = []; 9739 9740 // store state information needed for touch events 9741 this.touch = {}; 9742 9743 this.redrawCount = 0; 9744 this.initialDrawDone = false; 9745 this.initialRangeChangeDone = false; 9746 9747 // attach the root panel to the provided container 9748 if (!container) throw new Error('No container provided'); 9749 container.appendChild(this.dom.root); 9750}; 9751 9752/** 9753 * Set options. Options will be passed to all components loaded in the Timeline. 9754 * @param {Object} [options] 9755 * {String} orientation 9756 * Vertical orientation for the Timeline, 9757 * can be 'bottom' (default) or 'top'. 9758 * {string | number} width 9759 * Width for the timeline, a number in pixels or 9760 * a css string like '1000px' or '75%'. '100%' by default. 9761 * {string | number} height 9762 * Fixed height for the Timeline, a number in pixels or 9763 * a css string like '400px' or '75%'. If undefined, 9764 * The Timeline will automatically size such that 9765 * its contents fit. 9766 * {string | number} minHeight 9767 * Minimum height for the Timeline, a number in pixels or 9768 * a css string like '400px' or '75%'. 9769 * {string | number} maxHeight 9770 * Maximum height for the Timeline, a number in pixels or 9771 * a css string like '400px' or '75%'. 9772 * {number | Date | string} start 9773 * Start date for the visible window 9774 * {number | Date | string} end 9775 * End date for the visible window 9776 */ 9777Core.prototype.setOptions = function (options) { 9778 if (options) { 9779 // copy the known options 9780 var fields = ['width', 'height', 'minHeight', 'maxHeight', 'autoResize', 'start', 'end', 'clickToUse', 'dataAttributes', 'hiddenDates', 'locale', 'locales', 'moment', 'rtl', 'zoomKey', 'horizontalScroll', 'verticalScroll']; 9781 util.selectiveExtend(fields, this.options, options); 9782 9783 this.dom.rollingModeBtn.style.visibility = 'hidden'; 9784 9785 if (this.options.rtl) { 9786 this.dom.container.style.direction = "rtl"; 9787 this.dom.backgroundVertical.className = 'vis-panel vis-background vis-vertical-rtl'; 9788 } 9789 9790 if (this.options.verticalScroll) { 9791 if (this.options.rtl) { 9792 this.dom.rightContainer.className = 'vis-panel vis-right vis-vertical-scroll'; 9793 } else { 9794 this.dom.leftContainer.className = 'vis-panel vis-left vis-vertical-scroll'; 9795 } 9796 } 9797 9798 if ((0, _typeof3['default'])(this.options.orientation) !== 'object') { 9799 this.options.orientation = { item: undefined, axis: undefined }; 9800 } 9801 if ('orientation' in options) { 9802 if (typeof options.orientation === 'string') { 9803 this.options.orientation = { 9804 item: options.orientation, 9805 axis: options.orientation 9806 }; 9807 } else if ((0, _typeof3['default'])(options.orientation) === 'object') { 9808 if ('item' in options.orientation) { 9809 this.options.orientation.item = options.orientation.item; 9810 } 9811 if ('axis' in options.orientation) { 9812 this.options.orientation.axis = options.orientation.axis; 9813 } 9814 } 9815 } 9816 9817 if (this.options.orientation.axis === 'both') { 9818 if (!this.timeAxis2) { 9819 var timeAxis2 = this.timeAxis2 = new TimeAxis(this.body); 9820 timeAxis2.setOptions = function (options) { 9821 var _options = options ? util.extend({}, options) : {}; 9822 _options.orientation = 'top'; // override the orientation option, always top 9823 TimeAxis.prototype.setOptions.call(timeAxis2, _options); 9824 }; 9825 this.components.push(timeAxis2); 9826 } 9827 } else { 9828 if (this.timeAxis2) { 9829 var index = this.components.indexOf(this.timeAxis2); 9830 if (index !== -1) { 9831 this.components.splice(index, 1); 9832 } 9833 this.timeAxis2.destroy(); 9834 this.timeAxis2 = null; 9835 } 9836 } 9837 9838 // if the graph2d's drawPoints is a function delegate the callback to the onRender property 9839 if (typeof options.drawPoints == 'function') { 9840 options.drawPoints = { 9841 onRender: options.drawPoints 9842 }; 9843 } 9844 9845 if ('hiddenDates' in this.options) { 9846 DateUtil.convertHiddenOptions(this.options.moment, this.body, this.options.hiddenDates); 9847 } 9848
9849 if ('clickToUse' in options) { 9850 if (options.clickToUse) { 9851 if (!this.activator) { 9852 this.activator = new Activator(this.dom.root); 9853 } 9854 } else { 9855 if (this.activator) { 9856 this.activator.destroy(); 9857 delete this.activator; 9858 } 9859 } 9860 } 9861 9862 if ('showCustomTime' in options) { 9863 throw new Error('Option `showCustomTime` is deprecated. Create a custom time bar via timeline.addCustomTime(time [, id])'); 9864 } 9865 9866 // enable/disable autoResize 9867 this._initAutoResize(); 9868 } 9869 9870 // propagate options to all components 9871 this.components.forEach(function (component) { 9872 return component.setOptions(options); 9873 }); 9874 9875 // enable/disable configure 9876 if ('configure' in options) { 9877 if (!this.configurator) { 9878 this.configurator = this._createConfigurator(); 9879 } 9880 9881 this.configurator.setOptions(options.configure); 9882 9883 // collect the settings of all components, and pass them to the configuration system 9884 var appliedOptions = util.deepExtend({}, this.options); 9885 this.components.forEach(function (component) { 9886 util.deepExtend(appliedOptions, component.options); 9887 }); 9888 this.configurator.setModuleOptions({ global: appliedOptions }); 9889 } 9890 9891 this._redraw(); 9892}; 9893 9894/** 9895 * Returns true when the Timeline is active. 9896 * @returns {boolean} 9897 */ 9898Core.prototype.isActive = function () { 9899 return !this.activator || this.activator.active; 9900}; 9901 9902/** 9903 * Destroy the Core, clean up all DOM elements and event listeners. 9904 */ 9905Core.prototype.destroy = function () { 9906 // unbind datasets 9907 this.setItems(null); 9908 this.setGroups(null); 9909 9910 // remove all event listeners 9911 this.off(); 9912 9913 // stop checking for changed size 9914 this._stopAutoResize(); 9915 9916 // remove from DOM 9917 if (this.dom.root.parentNode) { 9918 this.dom.root.parentNode.removeChild(this.dom.root); 9919 } 9920 this.dom = null; 9921 9922 // remove Activator 9923 if (this.activator) { 9924 this.activator.destroy(); 9925 delete this.activator; 9926 } 9927 9928 // cleanup hammer touch events 9929 for (var event in this.listeners) { 9930 if (this.listeners.hasOwnProperty(event)) { 9931 delete this.listeners[event]; 9932 } 9933 } 9934 this.listeners = null; 9935 this.hammer = null; 9936 9937 // give all components the opportunity to cleanup 9938 this.components.forEach(function (component) { 9939 return component.destroy(); 9940 }); 9941 9942 this.body = null; 9943}; 9944 9945/** 9946 * Set a custom time bar 9947 * @param {Date} time 9948 * @param {number} [id=undefined] Optional id of the custom time bar to be adjusted. 9949 */ 9950Core.prototype.setCustomTime = function (time, id) { 9951 var customTimes = this.customTimes.filter(function (component) { 9952 return id === component.options.id; 9953 }); 9954 9955 if (customTimes.length === 0) { 9956 throw new Error('No custom time bar found with id ' + (0, _stringify2['default'])(id)); 9957 } 9958 9959 if (customTimes.length > 0) { 9960 customTimes[0].setCustomTime(time); 9961 } 9962}; 9963 9964/** 9965 * Retrieve the current custom time. 9966 * @param {number} [id=undefined] Id of the custom time bar. 9967 * @return {Date | undefined} customTime 9968 */ 9969Core.prototype.getCustomTime = function (id) { 9970 var customTimes = this.customTimes.filter(function (component) { 9971 return component.options.id === id; 9972 }); 9973 9974 if (customTimes.length === 0) { 9975 throw new Error('No custom time bar found with id ' + (0, _stringify2['default'])(id)); 9976 } 9977 return customTimes[0].getCustomTime(); 9978}; 9979 9980/** 9981 * Set a custom title for the custom time bar. 9982 * @param {string} [title] Custom title 9983 * @param {number} [id=undefined] Id of the custom time bar. 9984 * @returns {*} 9985 */ 9986Core.prototype.setCustomTimeTitle = function (title, id) { 9987 var customTimes = this.customTimes.filter(function (component) { 9988 return component.options.id === id; 9989 }); 9990 9991 if (customTimes.length === 0) { 9992 throw new Error('No custom time bar found with id ' + (0, _stringify2['default'])(id)); 9993 } 9994 if (customTimes.length > 0) { 9995 return customTimes[0].setCustomTitle(title); 9996 } 9997}; 9998 9999/** 10000 * Retrieve meta information from an event. 10001 * Should be overridden by classes extending Core 10002 * @param {Event} event 10003 * @return {Object} An object with related information. 10004 */ 10005Core.prototype.getEventProperties = function (event) { 10006 return { event: event }; 10007}; 10008 10009/** 10010 * Add custom vertical bar 10011 * @param {Date | string | number} [time] A Date, unix timestamp, or 10012 * ISO date string. Time point where 10013 * the new bar should be placed. 10014 * If not provided, `new Date()` will 10015 * be used. 10016 * @param {number | string} [id=undefined] Id of the new bar. Optional 10017 * @return {number | string} Returns the id of the new bar 10018 */ 10019Core.prototype.addCustomTime = function (time, id) { 10020 var timestamp = time !== undefined ? util.convert(time, 'Date').valueOf() : new Date(); 10021 10022 var exists = this.customTimes.some(function (customTime) { 10023 return customTime.options.id === id; 10024 }); 10025 if (exists) { 10026 throw new Error('A custom time with id ' + (0, _stringify2['default'])(id) + ' already exists'); 10027 } 10028 10029 var customTime = new CustomTime(this.body, util.extend({}, this.options, { 10030 time: timestamp, 10031 id: id 10032 })); 10033 10034 this.customTimes.push(customTime); 10035 this.components.push(customTime); 10036 this._redraw(); 10037 10038 return id; 10039}; 10040 10041/** 10042 * Remove previously added custom bar 10043 * @param {int} id ID of the custom bar to be removed 10044 * [at]returns {boolean} True if the bar exists and is removed, false otherwise 10045 */ 10046Core.prototype.removeCustomTime = function (id) { 10047 var customTimes = this.customTimes.filter(function (bar) { 10048 return bar.options.id === id; 10049 }); 10050 10051 if (customTimes.length === 0) { 10052 throw new Error('No custom time bar found with id ' + (0, _stringify2['default'])(id)); 10053 } 10054
10055 customTimes.forEach(function (customTime) { 10056 this.customTimes.splice(this.customTimes.indexOf(customTime), 1); 10057 this.components.splice(this.components.indexOf(customTime), 1); 10058 customTime.destroy(); 10059 }.bind(this)); 10060}; 10061 10062/** 10063 * Get the id's of the currently visible items. 10064 * @returns {Array} The ids of the visible items 10065 */ 10066Core.prototype.getVisibleItems = function () { 10067 return this.itemSet && this.itemSet.getVisibleItems() || []; 10068}; 10069 10070/** 10071 * Set Core window such that it fits all items 10072 * @param {Object} [options] Available options: 10073 * `animation: boolean | {duration: number, easingFunction: string}` 10074 * If true (default), the range is animated 10075 * smoothly to the new window. An object can be 10076 * provided to specify duration and easing function. 10077 * Default duration is 500 ms, and default easing 10078 * function is 'easeInOutQuad'. 10079 * @param {function} [callback] a callback funtion to be executed at the end of this function 10080 */ 10081Core.prototype.fit = function (options, callback) { 10082 var range = this.getDataRange(); 10083 10084 // skip range set if there is no min and max date 10085 if (range.min === null && range.max === null) { 10086 return; 10087 } 10088 10089 // apply a margin of 1% left and right of the data 10090 var interval = range.max - range.min; 10091 var min = new Date(range.min.valueOf() - interval * 0.01); 10092 var max = new Date(range.max.valueOf() + interval * 0.01); 10093 var animation = options && options.animation !== undefined ? options.animation : true; 10094 this.range.setRange(min, max, { animation: animation }, callback); 10095}; 10096 10097/** 10098 * Calculate the data range of the items start and end dates 10099 * [at]returns {{min: [Date], max: [Date]}} 10100 * @protected 10101 */ 10102Core.prototype.getDataRange = function () { 10103 // must be implemented by Timeline and Graph2d 10104 throw new Error('Cannot invoke abstract method getDataRange'); 10105}; 10106 10107/** 10108 * Set the visible window. Both parameters are optional, you can change only 10109 * start or only end. Syntax: 10110 * 10111 * TimeLine.setWindow(start, end) 10112 * TimeLine.setWindow(start, end, options) 10113 * TimeLine.setWindow(range) 10114 * 10115 * Where start and end can be a Date, number, or string, and range is an 10116 * object with properties start and end. 10117 * 10118 * @param {Date | number | string | Object} [start] Start date of visible window 10119 * @param {Date | number | string} [end] End date of visible window 10120 * @param {Object} [options] Available options: 10121 * `animation: boolean | {duration: number, easingFunction: string}` 10122 * If true (default), the range is animated 10123 * smoothly to the new window. An object can be 10124 * provided to specify duration and easing function. 10125 * Default duration is 500 ms, and default easing 10126 * function is 'easeInOutQuad'. 10127 * @param {function} [callback] a callback funtion to be executed at the end of this function 10128 */ 10129Core.prototype.setWindow = function (start, end, options, callback) { 10130 if (typeof arguments[2] == "function") { 10131 callback = arguments[2]; 10132 options = {}; 10133 } 10134 var animation; 10135 var range; 10136 if (arguments.length == 1) { 10137 range = arguments[0]; 10138 animation = range.animation !== undefined ? range.animation : true; 10139 this.range.setRange(range.start, range.end, { animation: animation }); 10140 } else if (arguments.length == 2 && typeof arguments[1] == "function") { 10141 range = arguments[0]; 10142 callback = arguments[1]; 10143 animation = range.animation !== undefined ? range.animation : true; 10144 this.range.setRange(range.start, range.end, { animation: animation }, callback); 10145 } else { 10146 animation = options && options.animation !== undefined ? options.animation : true; 10147 this.range.setRange(start, end, { animation: animation }, callback); 10148 } 10149}; 10150 10151/** 10152 * Move the window such that given time is centered on screen. 10153 * @param {Date | number | string} time 10154 * @param {Object} [options] Available options: 10155 * `animation: boolean | {duration: number, easingFunction: string}` 10156 * If true (default), the range is animated 10157 * smoothly to the new window. An object can be 10158 * provided to specify duration and easing function. 10159 * Default duration is 500 ms, and default easing 10160 * function is 'easeInOutQuad'. 10161 * @param {function} [callback] a callback funtion to be executed at the end of this function 10162 */ 10163Core.prototype.moveTo = function (time, options, callback) { 10164 if (typeof arguments[1] == "function") { 10165 callback = arguments[1]; 10166 options = {}; 10167 } 10168 var interval = this.range.end - this.range.start; 10169 var t = util.convert(time, 'Date').valueOf(); 10170 10171 var start = t - interval / 2; 10172 var end = t + interval / 2; 10173 var animation = options && options.animation !== undefined ? options.animation : true; 10174 10175 this.range.setRange(start, end, { animation: animation }, callback); 10176}; 10177 10178/** 10179 * Get the visible window 10180 * @return {{start: Date, end: Date}} Visible range 10181 */ 10182Core.prototype.getWindow = function () { 10183 var range = this.range.getRange(); 10184 return { 10185 start: new Date(range.start), 10186 end: new Date(range.end) 10187 }; 10188}; 10189 10190/** 10191 * Zoom in the window such that given time is centered on screen. 10192 * @param {number} percentage - must be between [0..1] 10193 * @param {Object} [options] Available options: 10194 * `animation: boolean | {duration: number, easingFunction: string}` 10195 * If true (default), the range is animated 10196 * smoothly to the new window. An object can be 10197 * provided to specify duration and easing function. 10198 * Default duration is 500 ms, and default easing 10199 * function is 'easeInOutQuad'. 10200 * @param {function} [callback] a callback funtion to be executed at the end of this function 10201 */ 10202Core.prototype.zoomIn = function (percentage, options, callback) { 10203 if (!percentage || percentage < 0 || percentage > 1) return; 10204 if (typeof arguments[1] == "function") { 10205 callback = arguments[1]; 10206 options = {}; 10207 } 10208 var range = this.getWindow(); 10209 var start = range.start.valueOf(); 10210 var end = range.end.valueOf(); 10211 var interval = end - start; 10212 var newInterval = interval / (1 + percentage); 10213 var distance = (interval - newInterval) / 2; 10214 var newStart = start + distance; 10215 var newEnd = end - distance; 10216 10217 this.setWindow(newStart, newEnd, options, callback); 10218}; 10219 10220/** 10221 * Zoom out the window such that given time is centered on screen. 10222 * @param {number} percentage - must be between [0..1] 10223 * @param {Object} [options] Available options: 10224 * `animation: boolean | {duration: number, easingFunction: string}` 10225 * If true (default), the range is animated 10226 * smoothly to the new window. An object can be 10227 * provided to specify duration and easing function. 10228 * Default duration is 500 ms, and default easing 10229 * function is 'easeInOutQuad'. 10230 * @param {function} [callback] a callback funtion to be executed at the end of this function 10231 */ 10232Core.prototype.zoomOut = function (percentage, options, callback) { 10233 if (!percentage || percentage < 0 || percentage > 1) return; 10234 if (typeof arguments[1] == "function") { 10235 callback = arguments[1]; 10236 options = {}; 10237 } 10238 var range = this.getWindow(); 10239 var start = range.start.valueOf(); 10240 var end = range.end.valueOf(); 10241 var interval = end - start; 10242 var newStart = start - interval * percentage / 2; 10243 var newEnd = end + interval * percentage / 2; 10244 10245 this.setWindow(newStart, newEnd, options, callback); 10246}; 10247 10248/** 10249 * Force a redraw. Can be overridden by implementations of Core 10250 *
10251 * Note: this function will be overridden on construction with a trottled version 10252 */ 10253Core.prototype.redraw = function () { 10254 this._redraw(); 10255}; 10256 10257/** 10258 * Redraw for internal use. Redraws all components. See also the public 10259 * method redraw. 10260 * @protected 10261 */ 10262Core.prototype._redraw = function () { 10263 this.redrawCount++; 10264 var resized = false; 10265 var options = this.options; 10266 var props = this.props; 10267 var dom = this.dom; 10268 10269 if (!dom || !dom.container || dom.root.offsetWidth == 0) return; // when destroyed, or invisible 10270 10271 DateUtil.updateHiddenDates(this.options.moment, this.body, this.options.hiddenDates); 10272 10273 // update class names 10274 if (options.orientation == 'top') { 10275 util.addClassName(dom.root, 'vis-top'); 10276 util.removeClassName(dom.root, 'vis-bottom'); 10277 } else { 10278 util.removeClassName(dom.root, 'vis-top'); 10279 util.addClassName(dom.root, 'vis-bottom'); 10280 } 10281 10282 // update root width and height options 10283 dom.root.style.maxHeight = util.option.asSize(options.maxHeight, ''); 10284 dom.root.style.minHeight = util.option.asSize(options.minHeight, ''); 10285 dom.root.style.width = util.option.asSize(options.width, ''); 10286 10287 // calculate border widths 10288 props.border.left = (dom.centerContainer.offsetWidth - dom.centerContainer.clientWidth) / 2; 10289 props.border.right = props.border.left; 10290 props.border.top = (dom.centerContainer.offsetHeight - dom.centerContainer.clientHeight) / 2; 10291 props.border.bottom = props.border.top; 10292 props.borderRootHeight = dom.root.offsetHeight - dom.root.clientHeight; 10293 props.borderRootWidth = dom.root.offsetWidth - dom.root.clientWidth; 10294 10295 // workaround for a bug in IE: the clientWidth of an element with 10296 // a height:0px and overflow:hidden is not calculated and always has value 0 10297 if (dom.centerContainer.clientHeight === 0) { 10298 props.border.left = props.border.top; 10299 props.border.right = props.border.left; 10300 } 10301 if (dom.root.clientHeight === 0) { 10302 props.borderRootWidth = props.borderRootHeight; 10303 } 10304 10305 // calculate the heights. If any of the side panels is empty, we set the height to 10306 // minus the border width, such that the border will be invisible 10307 props.center.height = dom.center.offsetHeight; 10308 props.left.height = dom.left.offsetHeight; 10309 props.right.height = dom.right.offsetHeight; 10310 props.top.height = dom.top.clientHeight || -props.border.top; 10311 props.bottom.height = dom.bottom.clientHeight || -props.border.bottom; 10312 10313 // TODO: compensate borders when any of the panels is empty. 10314 10315 // apply auto height 10316 // TODO: only calculate autoHeight when needed (else we cause an extra reflow/repaint of the DOM) 10317 var contentHeight = Math.max(props.left.height, props.center.height, props.right.height); 10318 var autoHeight = props.top.height + contentHeight + props.bottom.height + props.borderRootHeight + props.border.top + props.border.bottom; 10319 dom.root.style.height = util.option.asSize(options.height, autoHeight + 'px'); 10320 10321 // calculate heights of the content panels 10322 props.root.height = dom.root.offsetHeight; 10323 props.background.height = props.root.height - props.borderRootHeight; 10324 var containerHeight = props.root.height - props.top.height - props.bottom.height - props.borderRootHeight; 10325 props.centerContainer.height = containerHeight; 10326 props.leftContainer.height = containerHeight; 10327 props.rightContainer.height = props.leftContainer.height; 10328 10329 // calculate the widths of the panels 10330 props.root.width = dom.root.offsetWidth; 10331 props.background.width = props.root.width - props.borderRootWidth; 10332 10333 if (!this.initialDrawDone) { 10334 props.scrollbarWidth = util.getScrollBarWidth(); 10335 } 10336 10337 if (options.verticalScroll) { 10338 if (options.rtl) { 10339 props.left.width = dom.leftContainer.clientWidth || -props.border.left; 10340 props.right.width = dom.rightContainer.clientWidth + props.scrollbarWidth || -props.border.right; 10341 } else { 10342 props.left.width = dom.leftContainer.clientWidth + props.scrollbarWidth || -props.border.left; 10343 props.right.width = dom.rightContainer.clientWidth || -props.border.right; 10344 } 10345 } else { 10346 props.left.width = dom.leftContainer.clientWidth || -props.border.left; 10347 props.right.width = dom.rightContainer.clientWidth || -props.border.right; 10348 } 10349 10350 this._setDOM(); 10351 10352 // update the scrollTop, feasible range for the offset can be changed 10353 // when the height of the Core or of the contents of the center changed 10354 var offset = this._updateScrollTop(); 10355 10356 // reposition the scrollable contents 10357 if (options.orientation.item != 'top') { 10358 offset += Math.max(props.centerContainer.height - props.center.height - props.border.top - props.border.bottom, 0); 10359 } 10360 dom.center.style.top = offset + 'px'; 10361 10362 // show shadows when vertical scrolling is available 10363 var visibilityTop = props.scrollTop == 0 ? 'hidden' : ''; 10364 var visibilityBottom = props.scrollTop == props.scrollTopMin ? 'hidden' : ''; 10365 dom.shadowTop.style.visibility = visibilityTop; 10366 dom.shadowBottom.style.visibility = visibilityBottom; 10367 dom.shadowTopLeft.style.visibility = visibilityTop; 10368 dom.shadowBottomLeft.style.visibility = visibilityBottom; 10369 dom.shadowTopRight.style.visibility = visibilityTop; 10370 dom.shadowBottomRight.style.visibility = visibilityBottom; 10371 10372 if (options.verticalScroll) { 10373 dom.rightContainer.className = 'vis-panel vis-right vis-vertical-scroll'; 10374 dom.leftContainer.className = 'vis-panel vis-left vis-vertical-scroll'; 10375 10376 dom.shadowTopRight.style.visibility = "hidden"; 10377 dom.shadowBottomRight.style.visibility = "hidden"; 10378 dom.shadowTopLeft.style.visibility = "hidden"; 10379 dom.shadowBottomLeft.style.visibility = "hidden"; 10380 10381 dom.left.style.top = '0px'; 10382 dom.right.style.top = '0px'; 10383 } 10384 10385 if (!options.verticalScroll || props.center.height < props.centerContainer.height) { 10386 dom.left.style.top = offset + 'px'; 10387 dom.right.style.top = offset + 'px'; 10388 dom.rightContainer.className = dom.rightContainer.className.replace(new RegExp('(?:^|\\s)' + 'vis-vertical-scroll' + '(?:\\s|$)'), ' '); 10389 dom.leftContainer.className = dom.leftContainer.className.replace(new RegExp('(?:^|\\s)' + 'vis-vertical-scroll' + '(?:\\s|$)'), ' '); 10390 props.left.width = dom.leftContainer.clientWidth || -props.border.left; 10391 props.right.width = dom.rightContainer.clientWidth || -props.border.right; 10392 this._setDOM(); 10393 } 10394 10395 // enable/disable vertical panning 10396 var contentsOverflow = props.center.height > props.centerContainer.height; 10397 this.hammer.get('pan').set({ 10398 direction: contentsOverflow ? Hammer.DIRECTION_ALL : Hammer.DIRECTION_HORIZONTAL 10399 }); 10400 10401 // redraw all components
10402 this.components.forEach(function (component) { 10403 resized = component.redraw() || resized; 10404 }); 10405 var MAX_REDRAW = 5; 10406 if (resized) { 10407 if (this.redrawCount < MAX_REDRAW) { 10408 this.body.emitter.emit('_change'); 10409 return; 10410 } else { 10411 console.log('WARNING: infinite loop in redraw?'); 10412 } 10413 } else { 10414 this.redrawCount = 0; 10415 } 10416 10417 //Emit public 'changed' event for UI updates, see issue #1592 10418 this.body.emitter.emit("changed"); 10419}; 10420 10421Core.prototype._setDOM = function () { 10422 var props = this.props; 10423 var dom = this.dom; 10424 10425 props.leftContainer.width = props.left.width; 10426 props.rightContainer.width = props.right.width; 10427 var centerWidth = props.root.width - props.left.width - props.right.width - props.borderRootWidth; 10428 props.center.width = centerWidth; 10429 props.centerContainer.width = centerWidth; 10430 props.top.width = centerWidth; 10431 props.bottom.width = centerWidth; 10432 10433 // resize the panels 10434 dom.background.style.height = props.background.height + 'px'; 10435 dom.backgroundVertical.style.height = props.background.height + 'px'; 10436 dom.backgroundHorizontal.style.height = props.centerContainer.height + 'px'; 10437 dom.centerContainer.style.height = props.centerContainer.height + 'px'; 10438 dom.leftContainer.style.height = props.leftContainer.height + 'px'; 10439 dom.rightContainer.style.height = props.rightContainer.height + 'px'; 10440 10441 dom.background.style.width = props.background.width + 'px'; 10442 dom.backgroundVertical.style.width = props.centerContainer.width + 'px'; 10443 dom.backgroundHorizontal.style.width = props.background.width + 'px'; 10444 dom.centerContainer.style.width = props.center.width + 'px'; 10445 dom.top.style.width = props.top.width + 'px'; 10446 dom.bottom.style.width = props.bottom.width + 'px'; 10447 10448 // reposition the panels 10449 dom.background.style.left = '0'; 10450 dom.background.style.top = '0'; 10451 dom.backgroundVertical.style.left = props.left.width + props.border.left + 'px'; 10452 dom.backgroundVertical.style.top = '0'; 10453 dom.backgroundHorizontal.style.left = '0'; 10454 dom.backgroundHorizontal.style.top = props.top.height + 'px'; 10455 dom.centerContainer.style.left = props.left.width + 'px'; 10456 dom.centerContainer.style.top = props.top.height + 'px'; 10457 dom.leftContainer.style.left = '0'; 10458 dom.leftContainer.style.top = props.top.height + 'px'; 10459 dom.rightContainer.style.left = props.left.width + props.center.width + 'px'; 10460 dom.rightContainer.style.top = props.top.height + 'px'; 10461 dom.top.style.left = props.left.width + 'px'; 10462 dom.top.style.top = '0'; 10463 dom.bottom.style.left = props.left.width + 'px'; 10464 dom.bottom.style.top = props.top.height + props.centerContainer.height + 'px'; 10465 dom.center.style.left = '0'; 10466 dom.left.style.left = '0'; 10467 dom.right.style.left = '0'; 10468}; 10469 10470// TODO: deprecated since version 1.1.0, remove some day 10471Core.prototype.repaint = function () { 10472 throw new Error('Function repaint is deprecated. Use redraw instead.'); 10473}; 10474 10475/** 10476 * Set a current time. This can be used for example to ensure that a client's 10477 * time is synchronized with a shared server time. 10478 * Only applicable when option `showCurrentTime` is true. 10479 * @param {Date | string | number} time A Date, unix timestamp, or 10480 * ISO date string. 10481 */ 10482Core.prototype.setCurrentTime = function (time) { 10483 if (!this.currentTime) { 10484 throw new Error('Option showCurrentTime must be true'); 10485 } 10486 10487 this.currentTime.setCurrentTime(time); 10488}; 10489 10490/** 10491 * Get the current time. 10492 * Only applicable when option `showCurrentTime` is true. 10493 * @return {Date} Returns the current time. 10494 */ 10495Core.prototype.getCurrentTime = function () { 10496 if (!this.currentTime) { 10497 throw new Error('Option showCurrentTime must be true'); 10498 } 10499 10500 return this.currentTime.getCurrentTime(); 10501}; 10502 10503/** 10504 * Convert a position on screen (pixels) to a datetime 10505 * @param {int} x Position on the screen in pixels 10506 * @return {Date} time The datetime the corresponds with given position x 10507 * @protected 10508 */ 10509// TODO: move this function to Range 10510Core.prototype._toTime = function (x) { 10511 return DateUtil.toTime(this, x, this.props.center.width); 10512}; 10513 10514/** 10515 * Convert a position on the global screen (pixels) to a datetime 10516 * @param {int} x Position on the screen in pixels 10517 * @return {Date} time The datetime the corresponds with given position x 10518 * @protected 10519 */ 10520// TODO: move this function to Range 10521Core.prototype._toGlobalTime = function (x) { 10522 return DateUtil.toTime(this, x, this.props.root.width); 10523 //var conversion = this.range.conversion(this.props.root.width); 10524 //return new Date(x / conversion.scale + conversion.offset); 10525}; 10526 10527/** 10528 * Convert a datetime (Date object) into a position on the screen 10529 * @param {Date} time A date 10530 * @return {int} x The position on the screen in pixels which corresponds 10531 * with the given date. 10532 * @protected 10533 */ 10534// TODO: move this function to Range 10535Core.prototype._toScreen = function (time) { 10536 return DateUtil.toScreen(this, time, this.props.center.width); 10537}; 10538 10539/** 10540 * Convert a datetime (Date object) into a position on the root 10541 * This is used to get the pixel density estimate for the screen, not the center panel 10542 * @param {Date} time A date 10543 * @return {int} x The position on root in pixels which corresponds 10544 * with the given date. 10545 * @protected 10546 */ 10547// TODO: move this function to Range 10548Core.prototype._toGlobalScreen = function (time) { 10549 return DateUtil.toScreen(this, time, this.props.root.width); 10550 //var conversion = this.range.conversion(this.props.root.width); 10551 //return (time.valueOf() - conversion.offset) * conversion.scale; 10552}; 10553 10554/** 10555 * Initialize watching when option autoResize is true 10556 * @private 10557 */ 10558Core.prototype._initAutoResize = function () { 10559 if (this.options.autoResize == true) { 10560 this._startAutoResize(); 10561 } else { 10562 this._stopAutoResize(); 10563 } 10564}; 10565 10566/** 10567 * Watch for changes in the size of the container. On resize, the Panel will 10568 * automatically redraw itself. 10569 * @private 10570 */ 10571Core.prototype._startAutoResize = function () { 10572 var me = this; 10573 10574 this._stopAutoResize(); 10575 10576 this._onResize = function () { 10577 if (me.options.autoResize != true) { 10578 // stop watching when the option autoResize is changed to fal
10578se 10579 me._stopAutoResize(); 10580 return; 10581 } 10582 10583 if (me.dom.root) { 10584 // check whether the frame is resized 10585 // Note: we compare offsetWidth here, not clientWidth. For some reason, 10586 // IE does not restore the clientWidth from 0 to the actual width after 10587 // changing the timeline's container display style from none to visible 10588 if (me.dom.root.offsetWidth != me.props.lastWidth || me.dom.root.offsetHeight != me.props.lastHeight) { 10589 me.props.lastWidth = me.dom.root.offsetWidth; 10590 me.props.lastHeight = me.dom.root.offsetHeight; 10591 me.props.scrollbarWidth = util.getScrollBarWidth(); 10592 10593 me.body.emitter.emit('_change'); 10594 } 10595 } 10596 }; 10597 10598 // add event listener to window resize 10599 util.addEventListener(window, 'resize', this._onResize); 10600 10601 //Prevent initial unnecessary redraw 10602 if (me.dom.root) { 10603 me.props.lastWidth = me.dom.root.offsetWidth; 10604 me.props.lastHeight = me.dom.root.offsetHeight; 10605 } 10606 10607 this.watchTimer = setInterval(this._onResize, 1000); 10608}; 10609 10610/** 10611 * Stop watching for a resize of the frame. 10612 * @private 10613 */ 10614Core.prototype._stopAutoResize = function () { 10615 if (this.watchTimer) { 10616 clearInterval(this.watchTimer); 10617 this.watchTimer = undefined; 10618 } 10619 10620 // remove event listener on window.resize 10621 if (this._onResize) { 10622 util.removeEventListener(window, 'resize', this._onResize); 10623 this._onResize = null; 10624 } 10625}; 10626 10627/** 10628 * Start moving the timeline vertically 10629 * @param {Event} event 10630 * @private 10631 */ 10632Core.prototype._onTouch = function (event) { 10633 // eslint-disable-line no-unused-vars 10634 this.touch.allowDragging = true; 10635 this.touch.initialScrollTop = this.props.scrollTop; 10636}; 10637 10638/** 10639 * Start moving the timeline vertically 10640 * @param {Event} event 10641 * @private 10642 */ 10643Core.prototype._onPinch = function (event) { 10644 // eslint-disable-line no-unused-vars 10645 this.touch.allowDragging = false; 10646}; 10647 10648/** 10649 * Move the timeline vertically 10650 * @param {Event} event 10651 * @private 10652 */ 10653Core.prototype._onDrag = function (event) { 10654 if (!event) return; 10655 // refuse to drag when we where pinching to prevent the timeline make a jump 10656 // when releasing the fingers in opposite order from the touch screen 10657 if (!this.touch.allowDragging) return; 10658 10659 var delta = event.deltaY; 10660 10661 var oldScrollTop = this._getScrollTop(); 10662 var newScrollTop = this._setScrollTop(this.touch.initialScrollTop + delta); 10663 10664 if (this.options.verticalScroll) { 10665 this.dom.left.parentNode.scrollTop = -this.props.scrollTop; 10666 this.dom.right.parentNode.scrollTop = -this.props.scrollTop; 10667 } 10668 10669 if (newScrollTop != oldScrollTop) { 10670 this.emit("verticalDrag"); 10671 } 10672}; 10673 10674/** 10675 * Apply a scrollTop 10676 * @param {number} scrollTop 10677 * @returns {number} scrollTop Returns the applied scrollTop 10678 * @private 10679 */ 10680Core.prototype._setScrollTop = function (scrollTop) { 10681 this.props.scrollTop = scrollTop; 10682 this._updateScrollTop(); 10683 return this.props.scrollTop; 10684}; 10685 10686/** 10687 * Update the current scrollTop when the height of the containers has been changed 10688 * @returns {number} scrollTop Returns the applied scrollTop 10689 * @private 10690 */ 10691Core.prototype._updateScrollTop = function () { 10692 // recalculate the scrollTopMin 10693 var scrollTopMin = Math.min(this.props.centerContainer.height - this.props.center.height, 0); // is negative or zero 10694 if (scrollTopMin != this.props.scrollTopMin) { 10695 // in case of bottom orientation, change the scrollTop such that the contents 10696 // do not move relative to the time axis at the bottom 10697 if (this.options.orientation.item != 'top') { 10698 this.props.scrollTop += scrollTopMin - this.props.scrollTopMin; 10699 } 10700 this.props.scrollTopMin = scrollTopMin; 10701 } 10702 10703 // limit the scrollTop to the feasible scroll range 10704 if (this.props.scrollTop > 0) this.props.scrollTop = 0; 10705 if (this.props.scrollTop < scrollTopMin) this.props.scrollTop = scrollTopMin; 10706 10707 if (this.options.verticalScroll) { 10708 this.dom.left.parentNode.scrollTop = -this.props.scrollTop; 10709 this.dom.right.parentNode.scrollTop = -this.props.scrollTop; 10710 } 10711 10712 return this.props.scrollTop; 10713}; 10714 10715/** 10716 * Get the current scrollTop 10717 * @returns {number} scrollTop 10718 * @private 10719 */ 10720Core.prototype._getScrollTop = function () { 10721 return this.props.scrollTop; 10722}; 10723 10724/** 10725 * Load a configurator 10726 * [at]returns {Object} 10727 * @private 10728 */ 10729Core.prototype._createConfigurator = function () {
10730 throw new Error('Cannot invoke abstract method _createConfigurator'); 10731}; 10732 10733module.exports = Core; 10734 10735/***/ }), 10736/* 66 */ 10737/***/ (function(module, exports, __webpack_require__) { 10738 10739"use strict"; 10740 10741 10742var moment = __webpack_require__(9); 10743var DateUtil = __webpack_require__(36); 10744var util = __webpack_require__(2); 10745 10746/** 10747 * The class TimeStep is an iterator for dates. You provide a start date and an 10748 * end date. The class itself determines the best scale (step size) based on the 10749 * provided start Date, end Date, and minimumStep. 10750 * 10751 * If minimumStep is provided, the step size is chosen as close as possible 10752 * to the minimumStep but larger than minimumStep. If minimumStep is not 10753 * provided, the scale is set to 1 DAY. 10754 * The minimumStep should correspond with the onscreen size of about 6 characters 10755 * 10756 * Alternatively, you can set a scale by hand. 10757 * After creation, you can initialize the class by executing first(). Then you 10758 * can iterate from the start date to the end date via next(). You can check if 10759 * the end date is reached with the function hasNext(). After each step, you can 10760 * retrieve the current date via getCurrent(). 10761 * The TimeStep has scales ranging from milliseconds, seconds, minutes, hours, 10762 * days, to years. 10763 * 10764 * Version: 1.2 10765 * 10766 * @param {Date} [start] The start date, for example new Date(2010, 9, 21) 10767 * or new Date(2010, 9, 21, 23, 45, 00) 10768 * @param {Date} [end] The end date 10769 * @param {number} [minimumStep] Optional. Minimum step size in milliseconds 10770 * @param {Date|Array.<Date>} [hiddenDates] Optional. 10771 * @param {{showMajorLabels: boolean}} [options] Optional. 10772 * @constructor TimeStep 10773 */ 10774function TimeStep(start, end, minimumStep, hiddenDates, options) { 10775 this.moment = moment; 10776 10777 // variables 10778 this.current = this.moment(); 10779 this._start = this.moment(); 10780 this._end = this.moment(); 10781 10782 this.autoScale = true; 10783 this.scale = 'day'; 10784 this.step = 1; 10785 10786 // initialize the range 10787 this.setRange(start, end, minimumStep); 10788 10789 // hidden Dates options 10790 this.switchedDay = false; 10791 this.switchedMonth = false; 10792 this.switchedYear = false; 10793 if (Array.isArray(hiddenDates)) { 10794 this.hiddenDates = hiddenDates; 10795 } else if (hiddenDates != undefined) { 10796 this.hiddenDates = [hiddenDates]; 10797 } else { 10798 this.hiddenDates = []; 10799 } 10800 10801 this.format = TimeStep.FORMAT; // default formatting 10802 this.options = options ? options : {}; 10803} 10804 10805// Time formatting 10806TimeStep.FORMAT = { 10807 minorLabels: { 10808 millisecond: 'SSS', 10809 second: 's', 10810 minute: 'HH:mm', 10811 hour: 'HH:mm', 10812 weekday: 'ddd D', 10813 day: 'D', 10814 week: 'w', 10815 month: 'MMM', 10816 year: 'YYYY' 10817 }, 10818 majorLabels: { 10819 millisecond: 'HH:mm:ss', 10820 second: 'D MMMM HH:mm', 10821 minute: 'ddd D MMMM', 10822 hour: 'ddd D MMMM', 10823 weekday: 'MMMM YYYY', 10824 day: 'MMMM YYYY', 10825 week: 'MMMM YYYY', 10826 month: 'YYYY', 10827 year: '' 10828 } 10829}; 10830 10831/** 10832 * Set custom constructor function for moment. Can be used to set dates 10833 * to UTC or to set a utcOffset. 10834 * @param {function} moment 10835 */ 10836TimeStep.prototype.setMoment = function (moment) { 10837 this.moment = moment; 10838 10839 // update the date properties, can have a new utcOffset 10840 this.current = this.moment(this.current.valueOf()); 10841 this._start = this.moment(this._start.valueOf()); 10842 this._end = this.moment(this._end.valueOf()); 10843}; 10844 10845/** 10846 * Set custom formatting for the minor an major labels of the TimeStep. 10847 * Both `minorLabels` and `majorLabels` are an Object with properties: 10848 * 'millisecond', 'second', 'minute', 'hour', 'weekday', 'day', 'week', 'month', 'year'. 10849 * @param {{minorLabels: Object, majorLabels: Object}} format 10850 */ 10851TimeStep.prototype.setFormat = function (format) { 10852 var defaultFormat = util.deepExtend({}, TimeStep.FORMAT); 10853 this.format = util.deepExtend(defaultFormat, format); 10854}; 10855 10856/** 10857 * Set a new range 10858 * If minimumStep is provided, the step size is chosen as close as possible 10859 * to the minimumStep but larger than minimumStep. If minimumStep is not 10860 * provided, the scale is set to 1 DAY. 10861 * The minimumStep should correspond with the onscreen size of about 6 characters 10862 * @param {Date} [start] The start date and time. 10863 * @param {Date} [end] The end date and time. 10864 * @param {int} [minimumStep] Optional. Minimum step size in milliseconds 10865 */ 10866TimeStep.prototype.setRange = function (start, end, minimumStep) { 10867 if (!(start instanceof Date) || !(end instanceof Date)) { 10868 throw "No legal start or end date in method setRange"; 10869 } 10870 10871 this._start = start != undefined ? this.moment(start.valueOf()) : new Date(); 10872 this._end = end != undefined ? this.moment(end.valueOf()) : new Date(); 10873 10874 if (this.autoScale) { 10875 this.setMinimumStep(minimumStep); 10876 } 10877}; 10878 10879/** 10880 * Set the range iterator to the start date. 10881 */ 10882TimeStep.prototype.start = function () { 10883 this.current = this._start.clone(); 10884 this.roundToMinor(); 10885}; 10886 10887/** 10888 * Round the current date to the first minor date value 10889 * This must be executed once when the current date is set to start Date 10890 */ 10891TimeStep.prototype.roundToMinor = function () { 10892 // round to floor 10893 // to prevent year & month scales rounding down to the first day of week we perform this separately 10894 if (this.scale == 'week') { 10895 this.current.weekday(0); 10896 } 10897 // IMPORTANT: we have no breaks in this switch! (this is no bug) 10898 // noinspection FallThroughInSwitchStatementJS 10899 switch (this.scale) { 10900 case 'year': 10901 this.current.year(this.step * Math.floor(this.current.year() / this.step)); 10902 this.current.month(0); 10903 case 'month': 10904 this.current.date(1); // eslint-disable-line no-fallthrough 10905 case 'week': // eslint-disable-line no-fallthrough 10906 case 'day': // eslint-disable-line no-fallthrough 10907 case 'weekday': 10908 this.current.hours(0); // eslint-disable-line no-fallthrough 10909 case 'hour': 10910 this.current.minutes(0); // eslint-disable-line no-fallthrough 10911 case 'minute': 10912 this.current.seconds(0); // eslint-disable-line no-fallthrough 10913 case 'second': 10914 this.current.milliseconds(0); // eslint-disable-line no-fallthrough 10915 //case 'millisecond': // nothing to do for milliseconds 10916 } 10917 10918 if (this.step != 1) { 10919 // round down to the first minor value that is a multiple of the current step size 10920 switch (this.scale) { 10921 case 'millisecond': 10922 this.current.subtract(this.current.milliseconds() % this.step, 'milliseconds');break; 10923 case 'second': 10924 this.current.subtract(this.current.seconds() % this.step, 'seconds');break; 10925 case 'minute': 10926 this.current.subtract(this.current.minutes() % this.step, 'minutes');break; 10927 case 'hour': 10928 this.current.subtract(this.current.hours() % this.step, 'hours');break; 10929 case 'weekday': // intentional fall through 10930 case 'day': 10931 this.current.subtract((this.current.date() - 1) % this.step, 'day');break; 10932 case 'week': 10933 this.current.subtract(this.current.week() % this.step, 'week');break; 10934 case 'month': 10935 this.current.subtract(this.current.month() % this.step, 'month');break; 10936 case 'year': 10937 this.current.subtract(this.current.year() % this.step, 'year');break; 10938 default: 10939 break; 10940 } 10941 } 10942}; 10943 10944/** 10945 * Check if the there is a next step 10946 * @return {boolean} true if the current date has not passed the end date 10947 */ 10948TimeStep.prototype.hasNext = function () { 10949 return this.current.valueOf() <= this._end.valueOf(); 10950}; 10951 10952/** 10953 * Do the next step 10954 */ 10955TimeStep.prototype.next = function () { 10956 var prev = this.current.valueOf(); 10957 10958 // Two cases, needed to prevent issues with switching daylight savings 10959 // (end of March and end of October) 10960 switch (this.scale) { 10961 case 'millisecond': 10962 this.current.add(this.step, 'millisecond');break; 10963 case 'second': 10964 this.current.add(this.step, 'second');break; 10965 case 'minute': 10966 this.current.add(this.step, 'minute');break; 10967 case 'hour': 10968 this.current.add(this.step, 'hour'); 10969 10970 if (this.current.month() < 6) { 10971 this.current.subtract(this.current.hours() % this.step, 'hour'); 10972 } else { 10973 if (this.current.hours() % this.step !== 0) { 10974 this.current.add(this.step - this.current.hours() % this.step, 'hour'); 10975 } 10976 } 10977 break; 10978 case 'weekday': // intentional fall through 10979 case 'day': 10980 this.current.add(this.step, 'day');break; 10981 case 'week': 10982 if (this.current.weekday() !== 0) { 10983 // we had a month break not correlating with a week's start before 10984 this.current.weekday(0); // switch back to week cycles 10985 this.current.add(this.step, 'week'); 10986 } else if (this.options.showMajorLabels === false) { 10987 this.current.add(this.step, 'week'); // the default case 10988 } else { 10989 // first day of the week 10990 var nextWeek = this.current.clone(); 10991 nextWeek.add(1, 'week'); 10992 if (nextWeek.isSame(this.current, 'month')) { 10993 // is the first day of the next week in the same month? 10994 this.current.add(this.step, 'week'); // the default case 10995 } else { 10996 // inject a step at each first day of the month 10997 this.current.add(this.step, 'week'); 10998 this.current.date(1); 10999 } 11000 } 11001 break; 11002 case 'month': 11003 this.current.add(this.step, 'month');break; 11004 case 'year': 11005 this.current.add(this.step, 'year');break; 11006 default: 11007 break; 11008 } 11009 11010 if (this.step != 1) { 11011 // round down to the correct major value 11012 switch (this.scale) { 11013 case 'millisecond': 11014 if (this.current.milliseconds() > 0 && this.current.milliseconds() < this.step) this.current.milliseconds(0);break; 11015 case 'second': 11016 if (this.current.seconds() > 0 && this.current.seconds() < this.step) this.current.seconds(0);break; 11017 case 'minute': 11018 if (this.current.minutes() > 0 && this.current.minutes() < this.step) this.current.minutes(0);break; 11019 case 'hour': 11020 if (this.current.hours() > 0 && this.current.hours() < this.step) this.current.hours(0);break; 11021 case 'weekday': // intentional fall through 11022 case 'day': 11023 if (this.current.date() < this.step + 1) this.current.date(1);break; 11024 case 'week': 11025 if (this.current.week() < this.step) this.current.week(1);break; // week numbering starts at 1, not 0 11026 case 'month': 11027 if (this.current.month() < this.step) this.current.month(0);break; 11028 case 'year': 11029 break; // nothing to do for year 11030 default: 11031 break; 11032 } 11033 } 11034 11035 // safety mechanism: if current time is still unchanged, move to the end 11036 if (this.current.valueOf() == prev) { 11037 this.current = this._end.clone(); 11038 } 11039 11040 // Reset switches for year, month and day. Will get set to true where appropriate in DateUtil.stepOverHiddenDates 11041 this.switchedDay = false;
11042 this.switchedMonth = false; 11043 this.switchedYear = false; 11044 11045 DateUtil.stepOverHiddenDates(this.moment, this, prev); 11046}; 11047 11048/** 11049 * Get the current datetime 11050 * @return {Moment} current The current date 11051 */ 11052TimeStep.prototype.getCurrent = function () { 11053 return this.current; 11054}; 11055 11056/** 11057 * Set a custom scale. Autoscaling will be disabled. 11058 * For example setScale('minute', 5) will result 11059 * in minor steps of 5 minutes, and major steps of an hour. 11060 * 11061 * @param {{scale: string, step: number}} params 11062 * An object containing two properties: 11063 * - A string 'scale'. Choose from 'millisecond', 'second', 11064 * 'minute', 'hour', 'weekday', 'day', 'week', 'month', 'year'. 11065 * - A number 'step'. A step size, by default 1. 11066 * Choose for example 1, 2, 5, or 10. 11067 */ 11068TimeStep.prototype.setScale = function (params) { 11069 if (params && typeof params.scale == 'string') { 11070 this.scale = params.scale; 11071 this.step = params.step > 0 ? params.step : 1; 11072 this.autoScale = false; 11073 } 11074}; 11075 11076/** 11077 * Enable or disable autoscaling 11078 * @param {boolean} enable If true, autoascaling is set true 11079 */ 11080TimeStep.prototype.setAutoScale = function (enable) { 11081 this.autoScale = enable; 11082}; 11083 11084/** 11085 * Automatically determine the scale that bests fits the provided minimum step 11086 * @param {number} [minimumStep] The minimum step size in milliseconds 11087 */ 11088TimeStep.prototype.setMinimumStep = function (minimumStep) { 11089 if (minimumStep == undefined) { 11090 return; 11091 } 11092 11093 //var b = asc + ds; 11094 11095 var stepYear = 1000 * 60 * 60 * 24 * 30 * 12; 11096 var stepMonth = 1000 * 60 * 60 * 24 * 30; 11097 var stepDay = 1000 * 60 * 60 * 24; 11098 var stepHour = 1000 * 60 * 60; 11099 var stepMinute = 1000 * 60; 11100 var stepSecond = 1000; 11101 var stepMillisecond = 1; 11102 11103 // find the smallest step that is larger than the provided minimumStep 11104 if (stepYear * 1000 > minimumStep) { 11105 this.scale = 'year';this.step = 1000; 11106 } 11107 if (stepYear * 500 > minimumStep) { 11108 this.scale = 'year';this.step = 500; 11109 } 11110 if (stepYear * 100 > minimumStep) { 11111 this.scale = 'year';this.step = 100; 11112 } 11113 if (stepYear * 50 > minimumStep) { 11114 this.scale = 'year';this.step = 50; 11115 } 11116 if (stepYear * 10 > minimumStep) { 11117 this.scale = 'year';this.step = 10; 11118 } 11119 if (stepYear * 5 > minimumStep) { 11120 this.scale = 'year';this.step = 5; 11121 } 11122 if (stepYear > minimumStep) { 11123 this.scale = 'year';this.step = 1; 11124 } 11125 if (stepMonth * 3 > minimumStep) { 11126 this.scale = 'month';this.step = 3; 11127 } 11128 if (stepMonth > minimumStep) { 11129 this.scale = 'month';this.step = 1; 11130 } 11131 if (stepDay * 5 > minimumStep) { 11132 this.scale = 'day';this.step = 5; 11133 } 11134 if (stepDay * 2 > minimumStep) { 11135 this.scale = 'day';this.step = 2; 11136 } 11137 if (stepDay > minimumStep) { 11138 this.scale = 'day';this.step = 1; 11139 } 11140 if (stepDay / 2 > minimumStep) { 11141 this.scale = 'weekday';this.step = 1; 11142 } 11143 if (stepHour * 4 > minimumStep) { 11144 this.scale = 'hour';this.step = 4; 11145 } 11146 if (stepHour > minimumStep) { 11147 this.scale = 'hour';this.step = 1; 11148 } 11149 if (stepMinute * 15 > minimumStep) { 11150 this.scale = 'minute';this.step = 15; 11151 } 11152 if (stepMinute * 10 > minimumStep) { 11153 this.scale = 'minute';this.step = 10; 11154 } 11155 if (stepMinute * 5 > minimumStep) { 11156 this.scale = 'minute';this.step = 5; 11157 } 11158 if (stepMinute > minimumStep) { 11159 this.scale = 'minute';this.step = 1; 11160 } 11161 if (stepSecond * 15 > minimumStep) { 11162 this.scale = 'second';this.step = 15; 11163 } 11164 if (stepSecond * 10 > minimumStep) { 11165 this.scale = 'second';this.step = 10; 11166 } 11167 if (stepSecond * 5 > minimumStep) { 11168 this.scale = 'second';this.step = 5; 11169 } 11170 if (stepSecond > minimumStep) { 11171 this.scale = 'second';this.step = 1; 11172 } 11173 if (stepMillisecond * 200 > minimumStep) { 11174 this.scale = 'millisecond';this.step = 200; 11175 } 11176 if (stepMillisecond * 100 > minimumStep) { 11177 this.scale = 'millisecond';this.step = 100; 11178 } 11179 if (stepMillisecond * 50 > minimumStep) { 11180 this.scale = 'millisecond';this.step = 50; 11181 } 11182 if (stepMillisecond * 10 > minimumStep) { 11183 this.scale = 'millisecond';this.step = 10; 11184 } 11185 if (stepMillisecond * 5 > minimumStep) { 11186 this.scale = 'millisecond';this.step = 5; 11187 } 11188 if (stepMillisecond > minimumStep) { 11189 this.scale = 'millisecond';this.step = 1; 11190 } 11191}; 11192 11193/** 11194 * Snap a date to a rounded value. 11195 * The snap intervals are dependent on the current scale and step. 11196 * Static function 11197 * @param {Date} date the date to be snapped. 11198 * @param {string}
11198 scale Current scale, can be 'millisecond', 'second', 11199 * 'minute', 'hour', 'weekday, 'day', 'week', 'month', 'year'. 11200 * @param {number} step Current step (1, 2, 4, 5, ... 11201 * @return {Date} snappedDate 11202 */ 11203TimeStep.snap = function (date, scale, step) { 11204 var clone = moment(date); 11205 11206 if (scale == 'year') { 11207 var year = clone.year() + Math.round(clone.month() / 12); 11208 clone.year(Math.round(year / step) * step); 11209 clone.month(0); 11210 clone.date(0); 11211 clone.hours(0); 11212 clone.minutes(0); 11213 clone.seconds(0); 11214 clone.milliseconds(0); 11215 } else if (scale == 'month') { 11216 if (clone.date() > 15) { 11217 clone.date(1); 11218 clone.add(1, 'month'); 11219 // important: first set Date to 1, after that change the month. 11220 } else { 11221 clone.date(1); 11222 } 11223 11224 clone.hours(0); 11225 clone.minutes(0); 11226 clone.seconds(0); 11227 clone.milliseconds(0); 11228 } else if (scale == 'week') { 11229 if (clone.weekday() > 2) { 11230 // doing it the momentjs locale aware way 11231 clone.weekday(0); 11232 clone.add(1, 'week'); 11233 } else { 11234 clone.weekday(0); 11235 } 11236 11237 clone.hours(0); 11238 clone.minutes(0); 11239 clone.seconds(0); 11240 clone.milliseconds(0); 11241 } else if (scale == 'day') { 11242 //noinspection FallthroughInSwitchStatementJS 11243 switch (step) { 11244 case 5: 11245 case 2: 11246 clone.hours(Math.round(clone.hours() / 24) * 24);break; 11247 default: 11248 clone.hours(Math.round(clone.hours() / 12) * 12);break; 11249 } 11250 clone.minutes(0); 11251 clone.seconds(0); 11252 clone.milliseconds(0); 11253 } else if (scale == 'weekday') { 11254 //noinspection FallthroughInSwitchStatementJS 11255 switch (step) { 11256 case 5: 11257 case 2: 11258 clone.hours(Math.round(clone.hours() / 12) * 12);break; 11259 default: 11260 clone.hours(Math.round(clone.hours() / 6) * 6);break; 11261 } 11262 clone.minutes(0); 11263 clone.seconds(0); 11264 clone.milliseconds(0); 11265 } else if (scale == 'hour') { 11266 switch (step) { 11267 case 4: 11268 clone.minutes(Math.round(clone.minutes() / 60) * 60);break; 11269 default: 11270 clone.minutes(Math.round(clone.minutes() / 30) * 30);break; 11271 } 11272 clone.seconds(0); 11273 clone.milliseconds(0); 11274 } else if (scale == 'minute') { 11275 //noinspection FallthroughInSwitchStatementJS 11276 switch (step) { 11277 case 15: 11278 case 10: 11279 clone.minutes(Math.round(clone.minutes() / 5) * 5); 11280 clone.seconds(0); 11281 break; 11282 case 5: 11283 clone.seconds(Math.round(clone.seconds() / 60) * 60);break; 11284 default: 11285 clone.seconds(Math.round(clone.seconds() / 30) * 30);break; 11286 } 11287 clone.milliseconds(0); 11288 } else if (scale == 'second') { 11289 //noinspection FallthroughInSwitchStatementJS 11290 switch (step) { 11291 case 15: 11292 case 10: 11293 clone.seconds(Math.round(clone.seconds() / 5) * 5); 11294 clone.milliseconds(0); 11295 break; 11296 case 5: 11297 clone.milliseconds(Math.round(clone.milliseconds() / 1000) * 1000);break; 11298 default: 11299 clone.milliseconds(Math.round(clone.milliseconds() / 500) * 500);break; 11300 } 11301 } else if (scale == 'millisecond') { 11302 var _step = step > 5 ? step / 2 : 1; 11303 clone.milliseconds(Math.round(clone.milliseconds() / _step) * _step); 11304 } 11305 11306 return clone; 11307}; 11308 11309/** 11310 * Check if the current value is a major value (for example when the step 11311 * is DAY, a major value is each first day of the MONTH) 11312 * @return {boolean} true if current date is major, else false. 11313 */ 11314TimeStep.prototype.isMajor = function () { 11315 if (this.switchedYear == true) { 11316 switch (this.scale) { 11317 case 'year': 11318 case 'month': 11319 case 'week': 11320 case 'weekday': 11321 case 'day': 11322 case 'hour': 11323 case 'minute': 11324 case 'second': 11325 case 'millisecond': 11326 return true; 11327 default: 11328 return false; 11329 } 11330 } else if (this.switchedMonth == true) { 11331 switch (this.scale) { 11332 case 'week': 11333 case 'weekday': 11334 case 'day': 11335 case 'hour': 11336 case 'minute': 11337 case 'second': 11338 case 'millisecond': 11339 return true; 11340 default: 11341 return false; 11342 } 11343 } else if (this.switchedDay == true) { 11344 switch (this.scale) { 11345 case 'millisecond': 11346 case 'second': 11347 case 'minute': 11348 case 'hour': 11349 return true; 11350 default: 11351 return false;
11352 } 11353 } 11354 11355 var date = this.moment(this.current); 11356 switch (this.scale) { 11357 case 'millisecond': 11358 return date.milliseconds() == 0; 11359 case 'second': 11360 return date.seconds() == 0; 11361 case 'minute': 11362 return date.hours() == 0 && date.minutes() == 0; 11363 case 'hour': 11364 return date.hours() == 0; 11365 case 'weekday': // intentional fall through 11366 case 'day': 11367 return date.date() == 1; 11368 case 'week': 11369 return date.date() == 1; 11370 case 'month': 11371 return date.month() == 0; 11372 case 'year': 11373 return false; 11374 default: 11375 return false; 11376 } 11377}; 11378 11379/** 11380 * Returns formatted text for the minor axislabel, depending on the current 11381 * date and the scale. For example when scale is MINUTE, the current time is 11382 * formatted as "hh:mm". 11383 * @param {Date} [date=this.current] custom date. if not provided, current date is taken 11384 * @returns {String} 11385 */ 11386TimeStep.prototype.getLabelMinor = function (date) { 11387 if (date == undefined) { 11388 date = this.current; 11389 } 11390 if (date instanceof Date) { 11391 date = this.moment(date); 11392 } 11393 11394 if (typeof this.format.minorLabels === "function") { 11395 return this.format.minorLabels(date, this.scale, this.step); 11396 } 11397 11398 var format = this.format.minorLabels[this.scale]; 11399 // noinspection FallThroughInSwitchStatementJS 11400 switch (this.scale) { 11401 case 'week': 11402 if (this.isMajor() && date.weekday() !== 0) { 11403 return ""; 11404 } 11405 default: 11406 // eslint-disable-line no-fallthrough 11407 return format && format.length > 0 ? this.moment(date).format(format) : ''; 11408 } 11409}; 11410 11411/** 11412 * Returns formatted text for the major axis label, depending on the current 11413 * date and the scale. For example when scale is MINUTE, the major scale is 11414 * hours, and the hour will be formatted as "hh". 11415 * @param {Date} [date=this.current] custom date. if not provided, current date is taken 11416 * @returns {String} 11417 */ 11418TimeStep.prototype.getLabelMajor = function (date) { 11419 if (date == undefined) { 11420 date = this.current; 11421 } 11422 if (date instanceof Date) { 11423 date = this.moment(date); 11424 } 11425 11426 if (typeof this.format.majorLabels === "function") { 11427 return this.format.majorLabels(date, this.scale, this.step); 11428 } 11429 11430 var format = this.format.majorLabels[this.scale]; 11431 return format && format.length > 0 ? this.moment(date).format(format) : ''; 11432}; 11433 11434TimeStep.prototype.getClassName = function () { 11435 var _moment = this.moment; 11436 var m = this.moment(this.current); 11437 var current = m.locale ? m.locale('en') : m.lang('en'); // old versions of moment have .lang() function 11438 var step = this.step; 11439 var classNames = []; 11440 11441 /** 11442 * 11443 * @param {number} value 11444 * @returns {String} 11445 */ 11446 function even(value) { 11447 return value / step % 2 == 0 ? ' vis-even' : ' vis-odd'; 11448 } 11449 11450 /** 11451 * 11452 * @param {Date} date 11453 * @returns {String} 11454 */ 11455 function today(date) { 11456 if (date.isSame(new Date(), 'day')) { 11457 return ' vis-today'; 11458 } 11459 if (date.isSame(_moment().add(1, 'day'), 'day')) { 11460 return ' vis-tomorrow'; 11461 } 11462 if (date.isSame(_moment().add(-1, 'day'), 'day')) { 11463 return ' vis-yesterday'; 11464 } 11465 return ''; 11466 } 11467 11468 /** 11469 * 11470 * @param {Date} date 11471 * @returns {String} 11472 */ 11473 function currentWeek(date) { 11474 return date.isSame(new Date(), 'week') ? ' vis-current-week' : ''; 11475 } 11476 11477 /** 11478 * 11479 * @param {Date} date 11480 * @returns {String} 11481 */ 11482 function currentMonth(date) { 11483 return date.isSame(new Date(), 'month') ? ' vis-current-month' : ''; 11484 } 11485 11486 /** 11487 * 11488 * @param {Date} date 11489 * @returns {String} 11490 */ 11491 function currentYear(date) { 11492 return date.isSame(new Date(), 'year') ? ' vis-current-year' : ''; 11493 } 11494 11495 switch (this.scale) { 11496 case 'millisecond': 11497 classNames.push(today(current)); 11498 classNames.push(even(current.milliseconds())); 11499 break; 11500 case 'second': 11501 classNames.push(today(current)); 11502 classNames.push(even(current.seconds())); 11503 break; 11504 case 'minute': 11505 classNames.push(today(current)); 11506 classNames.push(even(current.minutes())); 11507 break; 11508 case 'hour': 11509 classNames.push('vis-h' + current.hours() + (this.step == 4 ? '-h' + (current.hours() + 4) : '')); 11510 classNames.push(today(current)); 11511 classNames.push(even(current.hours())); 11512 break; 11513 case 'weekday': 11514 classNames.push('vis-' + current.format('dddd').toLowerCase()); 11515 classNames.push(today(current)); 11516 classNames.push(currentWeek(current)); 11517 classNames.push(even(current.date())); 11518 break; 11519 case 'day': 11520 classNames.push('vis-day' + current.date()); 11521 classNames.push('vis-' + current.format('MMMM').toLowerCase()); 11522 classNames.push(today(current)); 11523 classNames.push(currentMonth(current)); 11524 classNames.push(this.step <= 2 ? today(current) : ''); 11525 classNames.push(this.step <= 2 ? 'vis-' + current.format('dddd').toLowerCase() : ''); 11526 classNames.push(even(current.date() - 1)); 11527 break; 11528 case 'week': 11529 classNames.push('vis-week' + current.format('w')); 11530 classNames.push(currentWeek(current)); 11531 classNames.push(even(current.week())); 11532 break; 11533 case 'month': 11534 classNames.push('vis-' + current.format('MMMM').toLowerCase()); 11535 classNames.push(currentMonth(current)); 11536 classNames.push(even(current.month())); 11537 break; 11538 case 'year': 11539 classNames.push('vis-year' + current.year()); 11540 classNames.push(currentYear(current)); 11541 classNames.push(even(current.year())); 11542 break; 11543 } 11544 return classNames.filter(String).join(" "); 11545}; 11546 11547module.exports = TimeStep; 11548 11549/***/ }), 11550/* 67 */ 11551/***/ (function(module, exports, __webpack_require__) { 11552 11553"use strict"; 11554 11555 11556var util = __webpack_require__(2); 11557var Component = __webpack_require__(16); 11558var moment = __webpack_require__(9); 11559var locales = __webpack_require__(98); 11560 11561/** 11562 * A current time bar 11563 * @param {{range: Range, dom: Object, domProps: Object}} body 11564 * @param {Object} [options] Available parameters: 11565 * {Boolean} [showCurrentTime] 11566 * @constructor CurrentTime 11567 * @extends Component 11568 */ 11569function CurrentTime(body, options) { 11570 this.body = body; 11571 11572 // default options 11573 this.defaultOptions = { 11574 rtl: false, 11575 showCurrentTime: true, 11576 11577 moment: moment, 11578 locales: locales, 11579 locale: 'en' 11580 }; 11581 this.options = util.extend({}, this.defaultOptions); 11582 this.offset = 0; 11583 11584 this._create(); 11585 11586 this.setOptions(options); 11587} 11588 11589CurrentTime.prototype = new Component(); 11590 11591/** 11592 * Create the HTML DOM for the current time bar 11593 * @private 11594 */ 11595CurrentTime.prototype._create = function () { 11596 var bar = document.createElement('div'); 11597 bar.className = 'vis-current-time'; 11598 bar.style.position = 'absolute'; 11599 bar.style.top = '0px'; 11600 bar.style.height = '100%'; 11601 11602 this.bar = bar; 11603}; 11604 11605/** 11606 * Destroy the CurrentTime bar 11607 */ 11608CurrentTime.prototype.destroy = function () { 11609 this.options.showCurrentTime = false;
11610 this.redraw(); // will remove the bar from the DOM and stop refreshing 11611 11612 this.body = null; 11613}; 11614 11615/** 11616 * Set options for the component. Options will be merged in current options. 11617 * @param {Object} options Available parameters: 11618 * {boolean} [showCurrentTime] 11619 */ 11620CurrentTime.prototype.setOptions = function (options) { 11621 if (options) { 11622 // copy all options that we know 11623 util.selectiveExtend(['rtl', 'showCurrentTime', 'moment', 'locale', 'locales'], this.options, options); 11624 } 11625}; 11626 11627/** 11628 * Repaint the component 11629 * @return {boolean} Returns true if the component is resized 11630 */ 11631CurrentTime.prototype.redraw = function () { 11632 if (this.options.showCurrentTime) { 11633 var parent = this.body.dom.backgroundVertical; 11634 if (this.bar.parentNode != parent) { 11635 // attach to the dom 11636 if (this.bar.parentNode) { 11637 this.bar.parentNode.removeChild(this.bar); 11638 } 11639 parent.appendChild(this.bar); 11640 11641 this.start(); 11642 } 11643 11644 var now = this.options.moment(new Date().valueOf() + this.offset); 11645 var x = this.body.util.toScreen(now); 11646 11647 var locale = this.options.locales[this.options.locale]; 11648 if (!locale) { 11649 if (!this.warned) { 11650 console.log('WARNING: options.locales[\'' + this.options.locale + '\'] not found. See http://visjs.org/docs/timeline/#Localization'); 11651 this.warned = true; 11652 } 11653 locale = this.options.locales['en']; // fall back on english when not available 11654 } 11655 var title = locale.current + ' ' + locale.time + ': ' + now.format('dddd, MMMM Do YYYY, H:mm:ss'); 11656 title = title.charAt(0).toUpperCase() + title.substring(1); 11657 11658 if (this.options.rtl) { 11659 this.bar.style.right = x + 'px'; 11660 } else { 11661 this.bar.style.left = x + 'px'; 11662 } 11663 this.bar.title = title; 11664 } else { 11665 // remove the line from the DOM 11666 if (this.bar.parentNode) { 11667 this.bar.parentNode.removeChild(this.bar); 11668 } 11669 this.stop(); 11670 } 11671 11672 return false; 11673}; 11674 11675/** 11676 * Start auto refreshing the current time bar 11677 */ 11678CurrentTime.prototype.start = function () { 11679 var me = this; 11680 11681 /** 11682 * Updates the current time. 11683 */ 11684 function update() { 11685 me.stop(); 11686 11687 // determine interval to refresh 11688 var scale = me.body.range.conversion(me.body.domProps.center.width).scale; 11689 var interval = 1 / scale / 10; 11690 if (interval < 30) interval = 30; 11691 if (interval > 1000) interval = 1000; 11692 11693 me.redraw(); 11694 me.body.emitter.emit('currentTimeTick'); 11695 11696 // start a renderTimer to adjust for the new time 11697 me.currentTimeTimer = setTimeout(update, interval); 11698 } 11699 11700 update(); 11701}; 11702 11703/** 11704 * Stop auto refreshing the current time bar 11705 */ 11706CurrentTime.prototype.stop = function () { 11707 if (this.currentTimeTimer !== undefined) { 11708 clearTimeout(this.currentTimeTimer); 11709 delete this.currentTimeTimer; 11710 } 11711}; 11712 11713/** 11714 * Set a current time. This can be used for example to ensure that a client's 11715 * time is synchronized with a shared server time. 11716 * @param {Date | string | number} time A Date, unix timestamp, or 11717 * ISO date string. 11718 */ 11719CurrentTime.prototype.setCurrentTime = function (time) { 11720 var t = util.convert(time, 'Date').valueOf(); 11721 var now = new Date().valueOf(); 11722 this.offset = t - now; 11723 this.redraw(); 11724}; 11725 11726/** 11727 * Get the current time. 11728 * @return {Date} Returns the current time. 11729 */ 11730CurrentTime.prototype.getCurrentTime = function () { 11731 return new Date(new Date().valueOf() + this.offset); 11732}; 11733 11734module.exports = CurrentTime; 11735 11736/***/ }), 11737/* 68 */ 11738/***/ (function(module, exports, __webpack_require__) { 11739 11740"use strict"; 11741 11742 11743var _keys = __webpack_require__(8); 11744 11745var _keys2 = _interopRequireDefault(_keys); 11746 11747function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 11748 11749var util = __webpack_require__(2); 11750var stack = __webpack_require__(100); 11751 11752/** 11753 * @param {number | string} groupId 11754 * @param {Object} data 11755 * @param {ItemSet} itemSet 11756 * @constructor Group 11757 */ 11758function Group(groupId, data, itemSet) { 11759 this.groupId = groupId; 11760 this.subgroups = {}; 11761 this.subgroupStack = {}; 11762 this.subgroupStackAll = false; 11763 this.doInnerStack = false;
11764 this.subgroupIndex = 0; 11765 this.subgroupOrderer = data && data.subgroupOrder; 11766 this.itemSet = itemSet; 11767 this.isVisible = null; 11768 this.stackDirty = true; // if true, items will be restacked on next redraw 11769 11770 if (data && data.nestedGroups) { 11771 this.nestedGroups = data.nestedGroups; 11772 if (data.showNested == false) { 11773 this.showNested = false; 11774 } else { 11775 this.showNested = true; 11776 } 11777 } 11778 11779 if (data && data.subgroupStack) { 11780 if (typeof data.subgroupStack === "boolean") { 11781 this.doInnerStack = data.subgroupStack; 11782 this.subgroupStackAll = data.subgroupStack; 11783 } else { 11784 // We might be doing stacking on specific sub groups, but only 11785 // if at least one is set to do stacking 11786 for (var key in data.subgroupStack) { 11787 this.subgroupStack[key] = data.subgroupStack[key]; 11788 this.doInnerStack = this.doInnerStack || data.subgroupStack[key]; 11789 } 11790 } 11791 } 11792 11793 this.nestedInGroup = null; 11794 11795 this.dom = {}; 11796 this.props = { 11797 label: { 11798 width: 0, 11799 height: 0 11800 } 11801 }; 11802 this.className = null; 11803 11804 this.items = {}; // items filtered by groupId of this group 11805 this.visibleItems = []; // items currently visible in window 11806 this.itemsInRange = []; // items currently in range 11807 this.orderedItems = { 11808 byStart: [], 11809 byEnd: [] 11810 }; 11811 this.checkRangedItems = false; // needed to refresh the ranged items if the window is programatically changed with NO overlap. 11812 var me = this; 11813 this.itemSet.body.emitter.on("checkRangedItems", function () { 11814 me.checkRangedItems = true; 11815 }); 11816 11817 this._create(); 11818 11819 this.setData(data); 11820} 11821 11822/** 11823 * Create DOM elements for the group 11824 * @private 11825 */ 11826Group.prototype._create = function () { 11827 var label = document.createElement('div'); 11828 if (this.itemSet.options.groupEditable.order) { 11829 label.className = 'vis-label draggable'; 11830 } else { 11831 label.className = 'vis-label'; 11832 } 11833 this.dom.label = label; 11834 11835 var inner = document.createElement('div'); 11836 inner.className = 'vis-inner'; 11837 label.appendChild(inner); 11838 this.dom.inner = inner; 11839 11840 var foreground = document.createElement('div'); 11841 foreground.className = 'vis-group'; 11842 foreground['timeline-group'] = this; 11843 this.dom.foreground = foreground; 11844 11845 this.dom.background = document.createElement('div'); 11846 this.dom.background.className = 'vis-group'; 11847 11848 this.dom.axis = document.createElement('div'); 11849 this.dom.axis.className = 'vis-group'; 11850 11851 // create a hidden marker to detect when the Timelines container is attached 11852 // to the DOM, or the style of a parent of the Timeline is changed from 11853 // display:none is changed to visible. 11854 this.dom.marker = document.createElement('div'); 11855 this.dom.marker.style.visibility = 'hidden'; 11856 this.dom.marker.style.position = 'absolute'; 11857 this.dom.marker.innerHTML = ''; 11858 this.dom.background.appendChild(this.dom.marker); 11859}; 11860 11861/** 11862 * Set the group data for this group 11863 * @param {Object} data Group data, can contain properties content and className 11864 */ 11865Group.prototype.setData = function (data) { 11866 // update contents 11867 var content; 11868 var templateFunction; 11869 11870 if (this.itemSet.options && this.itemSet.options.groupTemplate) { 11871 templateFunction = this.itemSet.options.groupTemplate.bind(this); 11872 content = templateFunction(data, this.dom.inner); 11873 } else { 11874 content = data && data.content; 11875 } 11876 11877 if (content instanceof Element) { 11878 this.dom.inner.appendChild(content); 11879 while (this.dom.inner.firstChild) { 11880 this.dom.inner.removeChild(this.dom.inner.firstChild); 11881 } 11882 this.dom.inner.appendChild(content); 11883 } else if (content instanceof Object) { 11884 templateFunction(data, this.dom.inner); 11885 } else if (content !== undefined && content !== null) { 11886 this.dom.inner.innerHTML = content; 11887 } else { 11888 this.dom.inner.innerHTML = this.groupId || ''; // groupId can be null 11889 } 11890 11891 // update title 11892 this.dom.label.title = data && data.title || ''; 11893 if (!this.dom.inner.firstChild) { 11894 util.addClassName(this.dom.inner, 'vis-hidden'); 11895 } else { 11896 util.removeClassName(this.dom.inner, 'vis-hidden'); 11897 } 11898 11899 if (data && data.nestedGroups) { 11900 if (!this.nestedGroups || this.nestedGroups != data.nestedGroups) { 11901 this.nestedGroups = data.nestedGroups; 11902 } 11903 11904 if (data.showNested !== undefined || this.showNested === undefined) { 11905 if (data.showNested == false) { 11906 this.showNested = false;
11907 } else { 11908 this.showNested = true; 11909 } 11910 } 11911 11912 util.addClassName(this.dom.label, 'vis-nesting-group'); 11913 var collapsedDirClassName = this.itemSet.options.rtl ? 'collapsed-rtl' : 'collapsed'; 11914 if (this.showNested) { 11915 util.removeClassName(this.dom.label, collapsedDirClassName); 11916 util.addClassName(this.dom.label, 'expanded'); 11917 } else { 11918 util.removeClassName(this.dom.label, 'expanded'); 11919 util.addClassName(this.dom.label, collapsedDirClassName); 11920 } 11921 } else if (this.nestedGroups) { 11922 this.nestedGroups = null; 11923 collapsedDirClassName = this.itemSet.options.rtl ? 'collapsed-rtl' : 'collapsed'; 11924 util.removeClassName(this.dom.label, collapsedDirClassName); 11925 util.removeClassName(this.dom.label, 'expanded'); 11926 util.removeClassName(this.dom.label, 'vis-nesting-group'); 11927 } 11928 11929 if (data && data.nestedInGroup) { 11930 util.addClassName(this.dom.label, 'vis-nested-group'); 11931 if (this.itemSet.options && this.itemSet.options.rtl) { 11932 this.dom.inner.style.paddingRight = '30px'; 11933 } else { 11934 this.dom.inner.style.paddingLeft = '30px'; 11935 } 11936 } 11937 11938 // update className 11939 var className = data && data.className || null; 11940 if (className != this.className) { 11941 if (this.className) { 11942 util.removeClassName(this.dom.label, this.className); 11943 util.removeClassName(this.dom.foreground, this.className); 11944 util.removeClassName(this.dom.background, this.className); 11945 util.removeClassName(this.dom.axis, this.className); 11946 } 11947 util.addClassName(this.dom.label, className); 11948 util.addClassName(this.dom.foreground, className); 11949 util.addClassName(this.dom.background, className); 11950 util.addClassName(this.dom.axis, className); 11951 this.className = className; 11952 } 11953 11954 // update style 11955 if (this.style) { 11956 util.removeCssText(this.dom.label, this.style); 11957 this.style = null; 11958 } 11959 if (data && data.style) { 11960 util.addCssText(this.dom.label, data.style); 11961 this.style = data.style; 11962 } 11963}; 11964 11965/** 11966 * Get the width of the group label 11967 * @return {number} width 11968 */ 11969Group.prototype.getLabelWidth = function () { 11970 return this.props.label.width; 11971}; 11972 11973Group.prototype._didMarkerHeightChange = function () { 11974 var markerHeight = this.dom.marker.clientHeight; 11975 if (markerHeight != this.lastMarkerHeight) { 11976 this.lastMarkerHeight = markerHeight; 11977 var redrawQueue = {}; 11978 var redrawQueueLength = 0; 11979 11980 util.forEach(this.items, function (item, key) { 11981 item.dirty = true; 11982 if (item.displayed) { 11983 var returnQueue = true; 11984 redrawQueue[key] = item.redraw(returnQueue); 11985 redrawQueueLength = redrawQueue[key].length; 11986 } 11987 }); 11988 11989 var needRedraw = redrawQueueLength > 0; 11990 if (needRedraw) { 11991 // redraw all regular items 11992 for (var i = 0; i < redrawQueueLength; i++) { 11993 util.forEach(redrawQueue, function (fns) { 11994 fns[i](); 11995 }); 11996 } 11997 } 11998 return true; 11999 } 12000}; 12001 12002Group.prototype._calculateGroupSizeAndPosition = function () { 12003 var offsetTop = this.dom.foreground.offsetTop; 12004 var offsetLeft = this.dom.foreground.offsetLeft; 12005 var offsetWidth = this.dom.foreground.offsetWidth; 12006 this.top = offsetTop; 12007 this.right = offsetLeft; 12008 this.width = offsetWidth; 12009}; 12010 12011Group.prototype._redrawItems = function (forceRestack, lastIsVisible, margin, range) { 12012 var restack = forceRestack || this.stackDirty || this.isVisible && !lastIsVisible; 12013 12014 // if restacking, reposition visible items vertically 12015 if (restack) { 12016 var visibleSubgroups = {}; 12017 var subgroup = null; 12018 12019 if (typeof this.itemSet.options.order === 'function') { 12020 // a custom order function 12021 // brute force restack of all items 12022 12023 // show all items 12024 var me = this; 12025 var limitSize = false; 12026 12027 var redrawQueue = {}; 12028 var redrawQueueLength = 0; 12029 12030 util.forEach(this.items, function (item, key) { 12031 if (!item.displayed) { 12032 var returnQueue = true; 12033 redrawQueue[key] = item.redraw(returnQueue); 12034 redrawQueueLength = redrawQueue[key].length; 12035 me.visibleItems.push(item); 12036 } 12037 }); 12038 12039 var needRedraw = redrawQueueLength > 0; 12040 if (needRedraw) { 12041 // redraw all regular items 12042 for (var i = 0; i < redrawQueueLength; i++) { 12043 util.forEach(redrawQueue, function (fns) { 12044 fns[i](); 12045 }); 12046 } 12047 } 12048 12049 util.forEach(this.items, function (item) { 12050 item.repositionX(limitSize); 12051 }); 12052 12053 if (this.doInnerStack && this.itemSet.options.stackSubgroups) { 12054 // Order the items within each subgroup 12055 for (subgroup in this.subgroups) { 12056 visibleSubgroups[subgroup] = this.subgroups[subgroup].items.slice().sort(function (a, b) { 12057 return me.itemSet.options.order(a.data, b.data); 12058 }); 12059 } 12060 12061 stack.stackSubgroupsWithInnerStack(visibleSubgroups, margin, this.subgroups); 12062 } else { 12063 // order all items and force a restacking 12064 var customOrderedItems = this.orderedItems.byStart.slice().sort(function (a, b) { 12065 return me.itemSet.options.order(a.data, b.data); 12066 }); 12067 stack.stack(customOrderedItems, margin, true /* restack=true */); 12068 } 12069 12070 this.visibleItems = this._updateItemsInRange(this.orderedItems, this.visibleItems, range); 12071 } else { 12072 // no custom order function, lazy stacking 12073 this.visibleItems = this._updateItemsInRange(this.orderedItems, this.visibleItems, range); 12074 12075 if (this.itemSet.options.stack) { 12076 if (this.doInnerStack && this.itemSet.options.stackSubgroups) { 12077 for (subgroup in this.subgroups) { 12078 visibleSubgroups[subgroup] = this.subgroups[subgroup].items; 12079 } 12080 12081 stack.stackSubgroupsWithInnerStack(visibleSubgroups, margin, this.subgroups); 12082 } else { 12083 // TODO: ugly way to access options... 12084 stack.stack(this.visibleItems, margin, true /* restack=true */); 12085 } 12086 } else { 12087 // no stacking 12088 stack.nostack(this.visibleItems, margin, this.subgroups, this.itemSet.options.stackSubgroups); 12089 } 12090 } 12091 12092 this.stackDirty = false;
12093 } 12094}; 12095 12096Group.prototype._didResize = function (resized, height) { 12097 resized = util.updateProperty(this, 'height', height) || resized; 12098 // recalculate size of label 12099 var labelWidth = this.dom.inner.clientWidth; 12100 var labelHeight = this.dom.inner.clientHeight; 12101 resized = util.updateProperty(this.props.label, 'width', labelWidth) || resized; 12102 resized = util.updateProperty(this.props.label, 'height', labelHeight) || resized; 12103 return resized; 12104}; 12105 12106Group.prototype._applyGroupHeight = function (height) { 12107 this.dom.background.style.height = height + 'px'; 12108 this.dom.foreground.style.height = height + 'px'; 12109 this.dom.label.style.height = height + 'px'; 12110}; 12111 12112// update vertical position of items after they are re-stacked and the height of the group is calculated 12113Group.prototype._updateItemsVerticalPosition = function (margin) { 12114 for (var i = 0, ii = this.visibleItems.length; i < ii; i++) { 12115 var item = this.visibleItems[i]; 12116 item.repositionY(margin); 12117 if (!this.isVisible && this.groupId != "__background__") { 12118 if (item.displayed) item.hide(); 12119 } 12120 } 12121}; 12122 12123/** 12124 * Repaint this group 12125 * @param {{start: number, end: number}} range 12126 * @param {{item: {horizontal: number, vertical: number}, axis: number}} margin 12127 * @param {boolean} [forceRestack=false] Force restacking of all items 12128 * @param {boolean} [returnQueue=false] return the queue or if the group resized 12129 * @return {boolean} Returns true if the group is resized or the redraw queue if returnQueue=true 12130 */ 12131Group.prototype.redraw = function (range, margin, forceRestack, returnQueue) { 12132 var resized = false; 12133 var lastIsVisible = this.isVisible; 12134 var height; 12135 12136 var queue = [ 12137 // force recalculation of the height of the items when the marker height changed 12138 // (due to the Timeline being attached to the DOM or changed from display:none to visible) 12139 function () { 12140 forceRestack = this._didMarkerHeightChange.bind(this); 12141 }.bind(this), 12142 12143 // recalculate the height of the subgroups 12144 this._updateSubGroupHeights.bind(this, margin), 12145 12146 // calculate actual size and position 12147 this._calculateGroupSizeAndPosition.bind(this), 12148 12149 // check if group is visible 12150 function () { 12151 this.isVisible = this._isGroupVisible.bind(this)(range, margin); 12152 }.bind(this), 12153 12154 // redraw Items if needed 12155 function () { 12156 this._redrawItems.bind(this)(forceRestack, lastIsVisible, margin, range); 12157 }.bind(this), 12158 12159 // update subgroups 12160 this._updateSubgroupsSizes.bind(this), 12161 12162 // recalculate the height of the group 12163 function () { 12164 height = this._calculateHeight.bind(this)(margin); 12165 }.bind(this), 12166 12167 // calculate actual size and position again 12168 this._calculateGroupSizeAndPosition.bind(this), 12169 12170 // check if resized 12171 function () { 12172 resized = this._didResize.bind(this)(resized, height); 12173 }.bind(this), 12174 12175 // apply group height 12176 function () { 12177 this._applyGroupHeight.bind(this)(height); 12178 }.bind(this), 12179 12180 // update vertical position of items after they are re-stacked and the height of the group is calculated 12181 function () { 12182 this._updateItemsVerticalPosition.bind(this)(margin); 12183 }.bind(this), function () { 12184 if (!this.isVisible && this.height) { 12185 resized = false; 12186 } 12187 return resized; 12188 }]; 12189 12190 if (returnQueue) { 12191 return queue; 12192 } else { 12193 var result; 12194 queue.forEach(function (fn) { 12195 result = fn(); 12196 }); 12197 return result; 12198 } 12199}; 12200 12201/** 12202 * recalculate the height of the subgroups 12203 * 12204 * @param {{item: vis.Item}} margin 12205 * @private 12206 */ 12207Group.prototype._updateSubGroupHeights = function (margin) { 12208 if ((0, _keys2['default'])(this.subgroups).length > 0) { 12209 var me = this; 12210 12211 this.resetSubgroups(); 12212 12213 util.forEach(this.visibleItems, function (item) { 12214 if (item.data.subgroup !== undefined) { 12215 me.subgroups[item.data.subgroup].height = Math.max(me.subgroups[item.data.subgroup].height, item.height + margin.item.vertical); 12216 me.subgroups[item.data.subgroup].visible = true; 12217 } 12218 }); 12219 } 12220}; 12221 12222/** 12223 * check if group is visible 12224 * 12225 * @param {vis.Range} range 12226 * @param {{axis: vis.DataAxis}} margin 12227 * @returns {boolean} is visible 12228 * @private 12229 */ 12230Group.prototype._isGroupVisible = function (range, margin) { 12231 return this.top <= range.body.domProps.centerContainer.height - range.body.domProps.scrollTop + margin.axis && this.top + this.height + margin.axis >= -range.body.domProps.scrollTop; 12232}; 12233 12234/**
12235 * recalculate the height of the group 12236 * @param {{item: {horizontal: number, vertical: number}, axis: number}} margin 12237 * @returns {number} Returns the height 12238 * @private 12239 */ 12240Group.prototype._calculateHeight = function (margin) { 12241 // recalculate the height of the group 12242 var height; 12243 var itemsInRange = this.visibleItems; 12244 if (itemsInRange.length > 0) { 12245 var min = itemsInRange[0].top; 12246 var max = itemsInRange[0].top + itemsInRange[0].height; 12247 util.forEach(itemsInRange, function (item) { 12248 min = Math.min(min, item.top); 12249 max = Math.max(max, item.top + item.height); 12250 }); 12251 if (min > margin.axis) { 12252 // there is an empty gap between the lowest item and the axis 12253 var offset = min - margin.axis; 12254 max -= offset; 12255 util.forEach(itemsInRange, function (item) { 12256 item.top -= offset; 12257 }); 12258 } 12259 height = max + margin.item.vertical / 2; 12260 } else { 12261 height = 0; 12262 } 12263 height = Math.max(height, this.props.label.height); 12264 12265 return height; 12266}; 12267 12268/** 12269 * Show this group: attach to the DOM 12270 */ 12271Group.prototype.show = function () { 12272 if (!this.dom.label.parentNode) { 12273 this.itemSet.dom.labelSet.appendChild(this.dom.label); 12274 } 12275 12276 if (!this.dom.foreground.parentNode) { 12277 this.itemSet.dom.foreground.appendChild(this.dom.foreground); 12278 } 12279 12280 if (!this.dom.background.parentNode) { 12281 this.itemSet.dom.background.appendChild(this.dom.background); 12282 } 12283 12284 if (!this.dom.axis.parentNode) { 12285 this.itemSet.dom.axis.appendChild(this.dom.axis); 12286 } 12287}; 12288 12289/** 12290 * Hide this group: remove from the DOM 12291 */ 12292Group.prototype.hide = function () { 12293 var label = this.dom.label; 12294 if (label.parentNode) { 12295 label.parentNode.removeChild(label); 12296 } 12297 12298 var foreground = this.dom.foreground; 12299 if (foreground.parentNode) { 12300 foreground.parentNode.removeChild(foreground); 12301 } 12302 12303 var background = this.dom.background; 12304 if (background.parentNode) { 12305 background.parentNode.removeChild(background); 12306 } 12307 12308 var axis = this.dom.axis; 12309 if (axis.parentNode) { 12310 axis.parentNode.removeChild(axis); 12311 } 12312}; 12313 12314/** 12315 * Add an item to the group 12316 * @param {Item} item 12317 */ 12318Group.prototype.add = function (item) { 12319 this.items[item.id] = item; 12320 item.setParent(this); 12321 this.stackDirty = true; 12322 // add to 12323 if (item.data.subgroup !== undefined) { 12324 this._addToSubgroup(item); 12325 this.orderSubgroups(); 12326 } 12327 12328 if (this.visibleItems.indexOf(item) == -1) { 12329 var range = this.itemSet.body.range; // TODO: not nice accessing the range like this 12330 this._checkIfVisible(item, this.visibleItems, range); 12331 } 12332}; 12333 12334Group.prototype._addToSubgroup = function (item, subgroupId) { 12335 subgroupId = subgroupId || item.data.subgroup; 12336 if (subgroupId != undefined && this.subgroups[subgroupId] === undefined) { 12337 this.subgroups[subgroupId] = { 12338 height: 0, 12339 top: 0, 12340 start: item.data.start, 12341 end: item.data.end || item.data.start, 12342 visible: false, 12343 index: this.subgroupIndex, 12344 items: [], 12345 stack: this.subgroupStackAll || this.subgroupStack[subgroupId] || false 12346 }; 12347 this.subgroupIndex++; 12348 } 12349 12350 if (new Date(item.data.start) < new Date(this.subgroups[subgroupId].start)) { 12351 this.subgroups[subgroupId].start = item.data.start; 12352 } 12353 12354 var itemEnd = item.data.end || item.data.start; 12355 if (new Date(itemEnd) > new Date(this.subgroups[subgroupId].end)) { 12356 this.subgroups[subgroupId].end = itemEnd; 12357 } 12358 12359 this.subgroups[subgroupId].items.push(item); 12360}; 12361 12362Group.prototype._updateSubgroupsSizes = function () { 12363 var me = this; 12364 if (me.subgroups) { 12365 for (var subgroup in me.subgroups) { 12366 var initialEnd = me.subgroups[subgroup].items[0].data.end || me.subgroups[subgroup].items[0].data.start; 12367 var newStart = me.subgroups[subgroup].items[0].data.start; 12368 var newEnd = initialEnd - 1; 12369 12370 me.subgroups[subgroup].items.forEach(function (item) { 12371 if (new Date(item.data.start) < new Date(newStart)) { 12372 newStart = item.data.start; 12373 } 12374 12375 var itemEnd = item.data.end || item.data.start; 12376 if (new Date(itemEnd) > new Date(newEnd)) { 12377 newEnd = itemEnd; 12378 } 12379 }); 12380 12381 me.subgroups[subgroup].start = newStart; 12382 me.subgroups[subgroup].end = new Date(newEnd - 1); // -1 to compensate for colliding end to start subgroups; 12383 } 12384 } 12385}; 12386 12387Group.prototype.orderSubgroups = function () { 12388 if (this.subgroupOrderer !== undefined) { 12389 var sortArray = []; 12390 var subgroup; 12391 if (typeof this.subgroupOrderer == 'string') { 12392 for (subgroup in this.subgroups) { 12393 sortArray.push({ subgroup: subgroup, sortField: this.subgroups[subgroup].items[0].data[this.subgroupOrderer] }); 12394 } 12395 sortArray.sort(function (a, b) { 12396 return a.sortField - b.sortField; 12397 }); 12398 } else if (typeof this.subgroupOrderer == 'function') { 12399 for (subgroup in this.subgroups) { 12400 sortArray.push(this.subgroups[subgroup].items[0].data); 12401 } 12402 sortArray.sort(this.subgroupOrderer); 12403 } 12404 12405 if (sortArray.length > 0) { 12406 for (var i = 0; i < sortArray.length; i++) { 12407 this.subgroups[sortArray[i].subgroup].index = i; 12408 } 12409 } 12410 } 12411}; 12412 12413Group.prototype.resetSubgroups = function () { 12414 for (var subgroup in this.subgroups) { 12415 if (this.subgroups.hasOwnProperty(subgroup)) { 12416 this.subgroups[subgroup].visible = false;
12417 this.subgroups[subgroup].height = 0; 12418 } 12419 } 12420}; 12421 12422/** 12423 * Remove an item from the group 12424 * @param {Item} item 12425 */ 12426Group.prototype.remove = function (item) { 12427 delete this.items[item.id]; 12428 item.setParent(null); 12429 this.stackDirty = true; 12430 12431 // remove from visible items 12432 var index = this.visibleItems.indexOf(item); 12433 if (index != -1) this.visibleItems.splice(index, 1); 12434 12435 if (item.data.subgroup !== undefined) { 12436 this._removeFromSubgroup(item); 12437 this.orderSubgroups(); 12438 } 12439}; 12440 12441Group.prototype._removeFromSubgroup = function (item, subgroupId) { 12442 subgroupId = subgroupId || item.data.subgroup; 12443 if (subgroupId != undefined) { 12444 var subgroup = this.subgroups[subgroupId]; 12445 if (subgroup) { 12446 var itemIndex = subgroup.items.indexOf(item); 12447 // Check the item is actually in this subgroup. How should items not in the group be handled? 12448 if (itemIndex >= 0) { 12449 subgroup.items.splice(itemIndex, 1); 12450 if (!subgroup.items.length) { 12451 delete this.subgroups[subgroupId]; 12452 } else { 12453 this._updateSubgroupsSizes(); 12454 } 12455 } 12456 } 12457 } 12458}; 12459 12460/** 12461 * Remove an item from the corresponding DataSet 12462 * @param {Item} item 12463 */ 12464Group.prototype.removeFromDataSet = function (item) { 12465 this.itemSet.removeItem(item.id); 12466}; 12467 12468/** 12469 * Reorder the items 12470 */ 12471Group.prototype.order = function () { 12472 var array = util.toArray(this.items); 12473 var startArray = []; 12474 var endArray = []; 12475 12476 for (var i = 0; i < array.length; i++) { 12477 if (array[i].data.end !== undefined) { 12478 endArray.push(array[i]); 12479 } 12480 startArray.push(array[i]); 12481 } 12482 this.orderedItems = { 12483 byStart: startArray, 12484 byEnd: endArray 12485 }; 12486 12487 stack.orderByStart(this.orderedItems.byStart); 12488 stack.orderByEnd(this.orderedItems.byEnd); 12489}; 12490 12491/** 12492 * Update the visible items 12493 * @param {{byStart: Item[], byEnd: Item[]}} orderedItems All items ordered by start date and by end date 12494 * @param {Item[]} oldVisibleItems The previously visible items. 12495 * @param {{start: number, end: number}} range Visible range 12496 * @return {Item[]} visibleItems The new visible items. 12497 * @private 12498 */ 12499Group.prototype._updateItemsInRange = function (orderedItems, oldVisibleItems, range) { 12500 var visibleItems = []; 12501 var visibleItemsLookup = {}; // we keep this to quickly look up if an item already exists in the list without using indexOf on visibleItems 12502 12503 var interval = (range.end - range.start) / 4; 12504 var lowerBound = range.start - interval; 12505 var upperBound = range.end + interval; 12506 12507 // this function is used to do the binary search. 12508 var searchFunction = function searchFunction(value) { 12509 if (value < lowerBound) { 12510 return -1; 12511 } else if (value <= upperBound) { 12512 return 0; 12513 } else { 12514 return 1; 12515 } 12516 }; 12517 12518 // first check if the items that were in view previously are still in view. 12519 // IMPORTANT: this handles the case for the items with startdate before the window and enddate after the window! 12520 // also cleans up invisible items. 12521 if (oldVisibleItems.length > 0) { 12522 for (var i = 0; i < oldVisibleItems.length; i++) { 12523 this._checkIfVisibleWithReference(oldVisibleItems[i], visibleItems, visibleItemsLookup, range); 12524 } 12525 } 12526 12527 // we do a binary search for the items that have only start values. 12528 var initialPosByStart = util.binarySearchCustom(orderedItems.byStart, searchFunction, 'data', 'start'); 12529 12530 // trace the visible items from the inital start pos both ways until an invisible item is found, we only look at the start values. 12531 this._traceVisible(initialPosByStart, orderedItems.byStart, visibleItems, visibleItemsLookup, function (item) { 12532 return item.data.start < lowerBound || item.data.start > upperBound; 12533 }); 12534 12535 // if the window has changed programmatically without overlapping the old window, the ranged items with start < lowerBound and end > upperbound are not shown. 12536 // We therefore have to brute force check all items in the byEnd list 12537 if (this.checkRangedItems == true) { 12538 this.checkRangedItems = false; 12539 for (i = 0; i < orderedItems.byEnd.length; i++) { 12540 this._checkIfVisibleWithReference(orderedItems.byEnd[i], visibleItems, visibleItemsLookup, range); 12541 } 12542 } else { 12543 // we do a binary search for the items that have defined end times. 12544 var initialPosByEnd = util.binarySearchCustom(orderedItems.byEnd, searchFunction, 'data', 'end'); 12545 12546 // trace the visible items from the inital start pos both ways until an invisible item is found, we only look at the end values. 12547 this._traceVisible(initialPosByEnd, orderedItems.byEnd, visibleItems, visibleItemsLookup, function (item) { 12548 return item.data.end < lowerBound || item.data.end > upperBound; 12549 }); 12550 } 12551 12552 var redrawQueue = {}; 12553 var redrawQueueLength = 0; 12554 12555 for (i = 0; i < visibleItems.length; i++) { 12556 var item = visibleItems[i]; 12557 if (!item.displayed) { 12558 var returnQueue = true; 12559 redrawQueue[i] = item.redraw(returnQueue); 12560 redrawQueueLength = redrawQueue[i].length; 12561 } 12562 } 12563 12564 var needRedraw = redrawQueueLength > 0; 12565 if (needRedraw) { 12566 // redraw all regular items 12567 for (var j = 0; j < redrawQueueLength; j++) { 12568 util.forEach(redrawQueue, function (fns) { 12569 fns[j](); 12570 }); 12571 } 12572 } 12573 12574 for (i = 0; i < visibleItems.length; i++) { 12575 visibleItems[i].repositionX(); 12576 } 12577 return visibleItems; 12578}; 12579 12580Group.prototype._traceVisible = function (initialPos, items, visibleItems, visibleItemsLookup, breakCondition) { 12581 if (initialPos != -1) { 12582 var i, item; 12583 for (i = initialPos; i >= 0; i--) { 12584 item = items[i]; 12585 if (breakCondition(item)) { 12586 break; 12587 } else { 12588 if (visibleItemsLookup[item.id] === undefined) { 12589 visibleItemsLookup[item.id] = true; 12590 visibleItems.push(item); 12591 } 12592 } 12593 } 12594 12595 for (i = initialPos + 1; i < items.length; i++) { 12596 item = items[i]; 12597 if (breakCondition(item)) { 12598 break; 12599 } else { 12600 if (visibleItemsLookup[item.id] === undefined) { 12601 visibleItemsLookup[item.id] = true; 12602 visibleItems.push(item); 12603 } 12604 } 12605 } 12606 } 12607}; 12608 12609/** 12610 * this function is very similar to the _checkIfInvisible() but it does not 12611 * return booleans, hides the item if it should not be seen and always adds to 12612 * the visibleItems. 12613 * this one is for brute forcing and hiding. 12614 * 12615 * @param {Item} item 12616 * @param {Array} visibleItems 12617 * @param {{start:number, end:number}} range 12618 * @private 12619 */ 12620Group.prototype._checkIfVisible = function (item, visibleItems, range) { 12621 if (item.isVisible(range)) { 12622 if (!item.displayed) item.show(); 12623 // reposition item horizontally 12624 item.repositionX(); 12625 visibleItems.push(item); 12626 } else { 12627 if (item.displayed) item.hide(); 12628 } 12629}; 12630 12631/** 12632 * this function is very similar to the _checkIfInvisible() but it does not 12633 * return booleans, hides the item if it should not be seen and always adds to 12634 * the visibleItems. 12635 * this one is for brute forcing and hiding. 12636 * 12637 * @param {Item} item 12638 * @param {Array.<vis.Item>} visibleItems 12639 * @param {Object<number, boolean>} visibleItemsLookup 12640 * @param {{start:number, end:number}} range 12641 * @private 12642 */ 12643Group.prototype._checkIfVisibleWithReference = function (item, visibleItems, visibleItemsLookup, range) { 12644 if (item.isVisible(range)) { 12645 if (visibleItemsLookup[item.id] === undefined) { 12646 visibleItemsLookup[item.id] = true; 12647 visibleItems.push(item); 12648 } 12649 } else { 12650 if (item.displayed) item.hide(); 12651 } 12652}; 12653 12654Group.prototype.changeSubgroup = function (item, oldSubgroup, newSubgroup) { 12655 this._removeFromSubgroup(item, oldSubgroup); 12656 this._addToSubgroup(item, newSubgroup); 12657 this.orderSubgroups(); 12658}; 12659 12660module.exports = Group; 12661 12662/***/ }), 12663/* 69 */ 12664/***/ (function(module, exports, __webpack_require__) { 12665 12666"use strict"; 12667 12668 12669var _create = __webpack_require__(29); 12670 12671var _create2 = _interopRequireDefault(_create); 12672 12673function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 12674 12675var Group = __webpack_require__(68); 12676 12677/**
12678 * @constructor BackgroundGroup 12679 * @param {number | string} groupId 12680 * @param {Object} data 12681 * @param {ItemSet} itemSet 12682 * @extends Group 12683 */ 12684function BackgroundGroup(groupId, data, itemSet) { 12685 Group.call(this, groupId, data, itemSet); 12686 12687 this.width = 0; 12688 this.height = 0; 12689 this.top = 0; 12690 this.left = 0; 12691} 12692 12693BackgroundGroup.prototype = (0, _create2['default'])(Group.prototype); 12694 12695/** 12696 * Repaint this group 12697 * @param {{start: number, end: number}} range 12698 * @param {{item: {horizontal: number, vertical: number}, axis: number}} margin 12699 * @param {boolean} [forceRestack=false] Force restacking of all items 12700 * @return {boolean} Returns true if the group is resized 12701 */ 12702BackgroundGroup.prototype.redraw = function (range, margin, forceRestack) { 12703 // eslint-disable-line no-unused-vars 12704 var resized = false; 12705 12706 this.visibleItems = this._updateItemsInRange(this.orderedItems, this.visibleItems, range); 12707 12708 // calculate actual size 12709 this.width = this.dom.background.offsetWidth; 12710 12711 // apply new height (just always zero for BackgroundGroup 12712 this.dom.background.style.height = '0'; 12713 12714 // update vertical position of items after they are re-stacked and the height of the group is calculated 12715 for (var i = 0, ii = this.visibleItems.length; i < ii; i++) { 12716 var item = this.visibleItems[i]; 12717 item.repositionY(margin); 12718 } 12719 12720 return resized; 12721}; 12722 12723/** 12724 * Show this group: attach to the DOM 12725 */ 12726BackgroundGroup.prototype.show = function () { 12727 if (!this.dom.background.parentNode) { 12728 this.itemSet.dom.background.appendChild(this.dom.background); 12729 } 12730}; 12731 12732module.exports = BackgroundGroup; 12733 12734/***/ }), 12735/* 70 */ 12736/***/ (function(module, exports, __webpack_require__) { 12737 12738"use strict"; 12739 12740 12741var Item = __webpack_require__(38); 12742 12743/** 12744 * @constructor RangeItem 12745 * @extends Item 12746 * @param {Object} data Object containing parameters start, end 12747 * content, className. 12748 * @param {{toScreen: function, toTime: function}} conversion 12749 * Conversion functions from time to screen and vice versa 12750 * @param {Object} [options] Configuration options 12751 * // TODO: describe options 12752 */ 12753function RangeItem(data, conversion, options) { 12754 this.props = { 12755 content: { 12756 width: 0 12757 } 12758 }; 12759 this.overflow = false; // if contents can overflow (css styling), this flag is set to true 12760 this.options = options; 12761 // validate data 12762 if (data) { 12763 if (data.start == undefined) { 12764 throw new Error('Property "start" missing in item ' + data.id); 12765 } 12766 if (data.end == undefined) { 12767 throw new Error('Property "end" missing in item ' + data.id); 12768 } 12769 } 12770 12771 Item.call(this, data, conversion, options); 12772} 12773 12774RangeItem.prototype = new Item(null, null, null); 12775 12776RangeItem.prototype.baseClassName = 'vis-item vis-range'; 12777 12778/** 12779 * Check whether this item is visible inside given range 12780 * 12781 * @param {vis.Range} range with a timestamp for start and end 12782 * @returns {boolean} True if visible 12783 */ 12784RangeItem.prototype.isVisible = function (range) { 12785 // determine visibility 12786 return this.data.start < range.end && this.data.end > range.start; 12787}; 12788 12789RangeItem.prototype._createDomElement = function () { 12790 if (!this.dom) { 12791 // create DOM 12792 this.dom = {}; 12793 12794 // background box 12795 this.dom.box = document.createElement('div'); 12796 // className is updated in redraw() 12797 12798 // frame box (to prevent the item contents from overflowing) 12799 this.dom.frame = document.createElement('div'); 12800 this.dom.frame.className = 'vis-item-overflow'; 12801 this.dom.box.appendChild(this.dom.frame); 12802 12803 // visible frame box (showing the frame that is always visible) 12804 this.dom.visibleFrame = document.createElement('div'); 12805 this.dom.visibleFrame.className = 'vis-item-visible-frame'; 12806 this.dom.box.appendChild(this.dom.visibleFrame); 12807 12808 // contents box 12809 this.dom.content = document.createElement('div'); 12810 this.dom.content.className = 'vis-item-content'; 12811 this.dom.frame.appendChild(this.dom.content); 12812 12813 // attach this item as attribute 12814 this.dom.box['timeline-item'] = this; 12815 12816 this.dirty = true; 12817 } 12818}; 12819 12820RangeItem.prototype._appendDomElement = function () { 12821 if (!this.parent) { 12822 throw new Error('Cannot redraw item: no parent attached'); 12823 } 12824 if (!this.dom.box.parentNode) { 12825 var foreground = this.parent.dom.foreground; 12826 if (!foreground) { 12827 throw new Error('Cannot redraw item: parent has no foreground container element'); 12828 } 12829 foreground.appendChild(this.dom.box); 12830 } 12831 this.displayed = true; 12832}; 12833 12834RangeItem.prototype._updateDirtyDomComponents = function () { 12835 // update dirty DOM. An item is marked dirty when: 12836 // - the item is not yet rendered 12837 // - the item's data is changed 12838 // - the item is selected/deselected 12839 if (this.dirty) { 12840 this._updateContents(this.dom.content); 12841 this._updateDataAttributes(this.dom.box); 12842 this._updateStyle(this.dom.box); 12843 12844 var editable = this.editable.updateTime || this.editable.updateGroup; 12845 12846 // update class 12847 var className = (this.data.className ? ' ' + this.data.className : '') + (this.selected ? ' vis-selected' : '') + (editable ? ' vis-editable' : ' vis-readonly'); 12848 this.dom.box.className = this.baseClassName + className; 12849 12850 // turn off max-width to be able to calculate the real width 12851 // this causes an extra browser repaint/reflow, but so be it 12852 this.dom.content.style.maxWidth = 'none'; 12853 } 12854}; 12855 12856RangeItem.prototype._getDomComponentsSizes = function () { 12857 // determine from css whether this box has overflow 12858 this.overflow = window.getComputedStyle(this.dom.frame).overflow !== 'hidden'; 12859 return { 12860 content: { 12861 width: this.dom.content.offsetWidth 12862 }, 12863 box: { 12864 height: this.dom.box.offsetHeight 12865 } 12866 }; 12867}; 12868 12869RangeItem.prototype._updateDomComponentsSizes = function (sizes) { 12870 this.props.content.width = sizes.content.width; 12871 this.height = sizes.box.height; 12872 this.dom.content.style.maxWidth = ''; 12873 this.dirty = false;
12874}; 12875 12876RangeItem.prototype._repaintDomAdditionals = function () { 12877 this._repaintOnItemUpdateTimeTooltip(this.dom.box); 12878 this._repaintDeleteButton(this.dom.box); 12879 this._repaintDragCenter(); 12880 this._repaintDragLeft(); 12881 this._repaintDragRight(); 12882}; 12883 12884/** 12885 * Repaint the item 12886 * @param {boolean} [returnQueue=false] return the queue 12887 * @return {boolean} the redraw queue if returnQueue=true 12888 */ 12889RangeItem.prototype.redraw = function (returnQueue) { 12890 var sizes; 12891 var queue = [ 12892 // create item DOM 12893 this._createDomElement.bind(this), 12894 12895 // append DOM to parent DOM 12896 this._appendDomElement.bind(this), 12897 12898 // update dirty DOM 12899 this._updateDirtyDomComponents.bind(this), function () { 12900 if (this.dirty) { 12901 sizes = this._getDomComponentsSizes.bind(this)(); 12902 } 12903 }.bind(this), function () { 12904 if (this.dirty) { 12905 this._updateDomComponentsSizes.bind(this)(sizes); 12906 } 12907 }.bind(this), 12908 12909 // repaint DOM additionals 12910 this._repaintDomAdditionals.bind(this)]; 12911 12912 if (returnQueue) { 12913 return queue; 12914 } else { 12915 var result; 12916 queue.forEach(function (fn) { 12917 result = fn(); 12918 }); 12919 return result; 12920 } 12921}; 12922 12923/** 12924 * Show the item in the DOM (when not already visible). The items DOM will 12925 * be created when needed. 12926 */ 12927RangeItem.prototype.show = function () { 12928 if (!this.displayed) { 12929 this.redraw(); 12930 } 12931}; 12932 12933/** 12934 * Hide the item from the DOM (when visible) 12935 */ 12936RangeItem.prototype.hide = function () { 12937 if (this.displayed) { 12938 var box = this.dom.box; 12939 12940 if (box.parentNode) { 12941 box.parentNode.removeChild(box); 12942 } 12943 12944 this.displayed = false; 12945 } 12946}; 12947 12948/** 12949 * Reposition the item horizontally 12950 * @param {boolean} [limitSize=true] If true (default), the width of the range 12951 * item will be limited, as the browser cannot 12952 * display very wide divs. This means though 12953 * that the applied left and width may 12954 * not correspond to the ranges start and end 12955 * @Override 12956 */ 12957RangeItem.prototype.repositionX = function (limitSize) { 12958 var parentWidth = this.parent.width; 12959 var start = this.conversion.toScreen(this.data.start); 12960 var end = this.conversion.toScreen(this.data.end); 12961 var align = this.data.align === undefined ? this.options.align : this.data.align; 12962 var contentStartPosition; 12963 var contentWidth; 12964 12965 // limit the width of the range, as browsers cannot draw very wide divs 12966 // unless limitSize: false is explicitly set in item data 12967 if (this.data.limitSize !== false && (limitSize === undefined || limitSize === true)) { 12968 if (start < -parentWidth) { 12969 start = -parentWidth; 12970 } 12971 if (end > 2 * parentWidth) { 12972 end = 2 * parentWidth; 12973 } 12974 } 12975 12976 // add 0.5 to compensate floating-point values rounding 12977 var boxWidth = Math.max(end - start + 0.5, 1); 12978 12979 if (this.overflow) { 12980 if (this.options.rtl) { 12981 this.right = start; 12982 } else { 12983 this.left = start; 12984 } 12985 this.width = boxWidth + this.props.content.width; 12986 contentWidth = this.props.content.width; 12987 12988 // Note: The calculation of width is an optimistic calculation, giving 12989 // a width which will not change when moving the Timeline 12990 // So no re-stacking needed, which is nicer for the eye; 12991 } else { 12992 if (this.options.rtl) { 12993 this.right = start; 12994 } else { 12995 this.left = start; 12996 } 12997 this.width = boxWidth; 12998 contentWidth = Math.min(end - start, this.props.content.width); 12999 } 13000 13001 if (this.options.rtl) { 13002 this.dom.box.style.right = this.right + 'px'; 13003 } else { 13004 this.dom.box.style.left = this.left + 'px'; 13005 } 13006 this.dom.box.style.width = boxWidth + 'px'; 13007 13008 switch (align) { 13009 case 'left': 13010 if (this.options.rtl) { 13011 this.dom.content.style.right = '0'; 13012 } else { 13013 this.dom.content.style.left = '0'; 13014 } 13015 break; 13016 13017 case 'right': 13018 if (this.options.rtl) { 13019 this.dom.content.style.right = Math.max(boxWidth - contentWidth, 0) + 'px'; 13020 } else { 13021 this.dom.content.style.left = Math.max(boxWidth - contentWidth, 0) + 'px'; 13022 } 13023 break; 13024 13025 case 'center': 13026 if (this.options.rtl) { 13027 this.dom.content.style.right = Math.max((boxWidth - contentWidth) / 2, 0) + 'px'; 13028 } else { 13029 this.dom.content.style.left = Math.max((boxWidth - contentWidth) / 2, 0) + 'px'; 13030 } 13031 13032 break; 13033 13034 default: 13035 // 'auto' 13036 // when range exceeds left of the window, position the contents at the left of the visible area 13037 if (this.overflow) { 13038 if (end > 0) { 13039 contentStartPosition = Math.max(-start, 0); 13040 } else { 13041 contentStartPosition = -contentWidth; // ensure it's not visible anymore 13042 } 13043 } else { 13044 if (start < 0) { 13045 contentStartPosition = -start; 13046 } else { 13047 contentStartPosition = 0; 13048 } 13049 } 13050 if (this.options.rtl) { 13051 this.dom.content.style.right = contentStartPosition + 'px'; 13052 } else { 13053 this.dom.content.style.left = contentStartPosition + 'px'; 13054 this.dom.content.style.width = 'calc(100% - ' + contentStartPosition + 'px)'; 13055 } 13056 } 13057}; 13058 13059/** 13060 * Reposition the item vertically 13061 * @Override 13062 */ 13063RangeItem.prototype.repositionY = function () { 13064 var orientation = this.options.orientation.item; 13065 var box = this.dom.box; 13066 13067 if (orientation == 'top') { 13068 box.style.top = this.top + 'px'; 13069 } else { 13070 box.style.top = this.parent.height - this.top - this.height + 'px'; 13071 } 13072}; 13073 13074/** 13075 * Repaint a drag area on the left side of the range when the range is selected 13076 * @protected 13077 */ 13078RangeItem.prototype._repaintDragLeft = function () { 13079 if ((this.selected || this.options.itemsAlwaysDraggable.range) && this.options.editable.updateTime && !this.dom.dragLeft) { 13080 // create and show drag area 13081 var dragLeft = document.createElement('div'); 13082 dragLeft.className = 'vis-drag-left'; 13083 dragLeft.dragLeftItem = this; 13084 13085 this.dom.box.appendChild(dragLeft); 13086 this.dom.dragLeft = dragLeft; 13087 } else if (!this.selected && !this.options.itemsAlwaysDraggable.range && this.dom.dragLeft) { 13088 // delete drag area 13089 if (this.dom.dragLeft.parentNode) { 13090 this.dom.dragLeft.parentNode.removeChild(this.dom.dragLeft); 13091 } 13092 this.dom.dragLeft = null; 13093 } 13094}; 13095 13096/** 13097 * Repaint a drag area on the right side of the range when the range is selected 13098 * @protected 13099 */ 13100RangeItem.prototype._repaintDragRight = function () { 13101 if ((this.selected || this.options.itemsAlwaysDraggable.range) && this.options.editable.updateTime && !this.dom.dragRight) { 13102 // create and show drag area 13103 var dragRight = document.createElement('div'); 13104 dragRight.className = 'vis-drag-right'; 13105 dragRight.dragRightItem = this; 13106 13107 this.dom.box.appendChild(dragRight); 13108 this.dom.dragRight = dragRight; 13109 } else if (!this.selected && !this.options.itemsAlwaysDraggable.range && this.dom.dragRight) { 13110 // delete drag area 13111 if (this.dom.dragRight.parentNode) { 13112 this.dom.dragRight.parentNode.removeChild(this.dom.dragRight); 13113 } 13114 this.dom.dragRight = null; 13115 } 13116}; 13117 13118module.exports = RangeItem; 13119 13120/***/ }), 13121/* 71 */ 13122/***/ (function(module, exports, __webpack_require__) { 13123 13124"use strict"; 13125 13126 13127Object.defineProperty(exports, "__esModule", { 13128 value: true 13129}); 13130 13131var _stringify = __webpack_require__(19); 13132 13133var _stringify2 = _interopRequireDefault(_stringify); 13134 13135var _typeof2 = __webpack_require__(6); 13136 13137var _typeof3 = _interopRequireDefault(_typeof2); 13138 13139var _classCallCheck2 = __webpack_require__(0); 13140 13141var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); 13142 13143var _createClass2 = __webpack_require__(1); 13144 13145var _createClass3 = _interopRequireDefault(_createClass2); 13146 13147function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 13148 13149var util = __webpack_require__(2); 13150 13151var ColorPicker = __webpack_require__(179)['default']; 13152 13153/** 13154 * The way this works is for all properties of this.possible options, you can supply the property name in any form to list the options. 13155 * Boolean options are recognised as Boolean 13156 * Number options should be written as array: [default value, min value, max value, stepsize] 13157 * Colors should be written as array: ['color', '#ffffff'] 13158 * Strings with should be written as array: [option1, option2, option3, ..] 13159 * 13160 * The options are matched with their counterparts in each of the modules and the values used in the configuration are 13161 */ 13162 13163var Configurator = function () { 13164 /** 13165 * @param {Object} parentModule | the location where parentModule.setOptions() can be called 13166 * @param {Object} defaultContainer | the default container of the module 13167 * @param {Object} configureOptions | the fully configured and predefined options set found in allOptions.js 13168 * @param {number} pixelRatio | canvas pixel ratio 13169 */ 13170 function Configurator(parentModule, defaultContainer, configureOptions) { 13171 var pixelRatio = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 1; 13172 (0, _classCallCheck3['default'])(this, Configurator); 13173 13174 this.parent = parentModule; 13175 this.changedOptions = []; 13176 this.container = defaultContainer; 13177 this.allowCreation = false;
13178 13179 this.options = {}; 13180 this.initialized = false; 13181 this.popupCounter = 0; 13182 this.defaultOptions = { 13183 enabled: false, 13184 filter: true, 13185 container: undefined, 13186 showButton: true 13187 }; 13188 util.extend(this.options, this.defaultOptions); 13189 13190 this.configureOptions = configureOptions; 13191 this.moduleOptions = {}; 13192 this.domElements = []; 13193 this.popupDiv = {}; 13194 this.popupLimit = 5; 13195 this.popupHistory = {}; 13196 this.colorPicker = new ColorPicker(pixelRatio); 13197 this.wrapper = undefined; 13198 } 13199 13200 /** 13201 * refresh all options. 13202 * Because all modules parse their options by themselves, we just use their options. We copy them here. 13203 * 13204 * @param {Object} options 13205 */ 13206 13207 13208 (0, _createClass3['default'])(Configurator, [{ 13209 key: 'setOptions', 13210 value: function setOptions(options) { 13211 if (options !== undefined) { 13212 // reset the popup history because the indices may have been changed. 13213 this.popupHistory = {}; 13214 this._removePopup(); 13215 13216 var enabled = true; 13217 if (typeof options === 'string') { 13218 this.options.filter = options; 13219 } else if (options instanceof Array) { 13220 this.options.filter = options.join(); 13221 } else if ((typeof options === 'undefined' ? 'undefined' : (0, _typeof3['default'])(options)) === 'object') { 13222 if (options.container !== undefined) { 13223 this.options.container = options.container; 13224 } 13225 if (options.filter !== undefined) { 13226 this.options.filter = options.filter; 13227 } 13228 if (options.showButton !== undefined) { 13229 this.options.showButton = options.showButton; 13230 } 13231 if (options.enabled !== undefined) { 13232 enabled = options.enabled; 13233 } 13234 } else if (typeof options === 'boolean') { 13235 this.options.filter = true; 13236 enabled = options; 13237 } else if (typeof options === 'function') { 13238 this.options.filter = options; 13239 enabled = true; 13240 } 13241 if (this.options.filter === false) { 13242 enabled = false; 13243 } 13244 13245 this.options.enabled = enabled; 13246 } 13247 this._clean(); 13248 } 13249 13250 /** 13251 * 13252 * @param {Object} moduleOptions 13253 */ 13254 13255 }, { 13256 key: 'setModuleOptions', 13257 value: function setModuleOptions(moduleOptions) { 13258 this.moduleOptions = moduleOptions; 13259 if (this.options.enabled === true) { 13260 this._clean(); 13261 if (this.options.container !== undefined) { 13262 this.container = this.options.container; 13263 } 13264 this._create(); 13265 } 13266 } 13267 13268 /** 13269 * Create all DOM elements 13270 * @private 13271 */ 13272 13273 }, { 13274 key: '_create', 13275 value: function _create() { 13276 var _this = this; 13277 13278 this._clean(); 13279 this.changedOptions = []; 13280 13281 var filter = this.options.filter; 13282 var counter = 0; 13283 var show = false; 13284 for (var option in this.configureOptions) { 13285 if (this.configureOptions.hasOwnProperty(option)) { 13286 this.allowCreation = false; 13287 show = false; 13288 if (typeof filter === 'function') { 13289 show = filter(option, []); 13290 show = show || this._handleObject(this.configureOptions[option], [option], true); 13291 } else if (filter === true || filter.indexOf(option) !== -1) { 13292 show = true; 13293 } 13294 13295 if (show !== false) { 13296 this.allowCreation = true; 13297 13298 // linebreak between categories 13299 if (counter > 0) { 13300 this._makeItem([]); 13301 } 13302 // a header for the category 13303 this._makeHeader(option); 13304 13305 // get the sub options 13306 this._handleObject(this.configureOptions[option], [option]); 13307 } 13308 counter++; 13309 } 13310 } 13311 13312 if (this.options.showButton === true) { 13313 var generateButton = document.createElement('div'); 13314 generateButton.className = 'vis-configuration vis-config-button'; 13315 generateButton.innerHTML = 'generate options'; 13316 generateButton.onclick = function () { 13317 _this._printOptions(); 13318 }; 13319 generateButton.onmouseover = function () { 13320 generateButton.className = 'vis-configuration vis-config-button hover'; 13321 }; 13322 generateButton.onmouseout = function () { 13323 generateButton.className = 'vis-configuration vis-config-button'; 13324 }; 13325 13326 this.optionsContainer = document.createElement('div'); 13327 this.optionsContainer.className = 'vis-configuration vis-config-option-container'; 13328 13329 this.domElements.push(this.optionsContainer); 13330 this.domElements.push(generateButton); 13331 } 13332 13333 this._push(); 13334 //~ this.colorPicker.insertTo(this.container); 13335 } 13336 13337 /** 13338 * draw all DOM elements on the screen 13339 * @private 13340 */ 13341 13342 }, { 13343 key: '_push', 13344 value: function _push() { 13345 this.wrapper = document.createElement('div'); 13346 this.wrapper.className = 'vis-configuration-wrapper'; 13347 this.container.appendChild(this.wrapper); 13348 for (var i = 0; i < this.domElements.length; i++) { 13349 this.wrapper.appendChild(this.domElements[i]); 13350 } 13351 13352 this._showPopupIfNeeded(); 13353 } 13354 13355 /** 13356 * delete all DOM elements 13357 * @private 13358 */ 13359 13360 }, { 13361 key: '_clean', 13362 value: function _clean() { 13363 for (var i = 0; i < this.domElements.length; i++) { 13364 this.wrapper.removeChild(this.domElements[i]); 13365 } 13366 13367 if (this.wrapper !== undefined) { 13368 this.container.removeChild(this.wrapper); 13369 this.wrapper = undefined; 13370 } 13371 this.domElements = []; 13372 13373 this._removePopup(); 13374 } 13375 13376 /** 13377 * get the value from the actualOptions if it exists 13378 * @param {array} path | where to look for the actual option 13379 * @returns {*} 13380 * @private 13381 */ 13382 13383 }, { 13384 key: '_getValue', 13385 value: function _getValue(path) { 13386 var base = this.moduleOptions; 13387 for (var i = 0; i < path.length; i++) { 13388 if (base[path[i]] !== undefined) { 13389 base = base[path[i]]; 13390 } else { 13391 base = undefined; 13392 break; 13393 } 13394 } 13395 return base; 13396 } 13397 13398 /** 13399 * all option elements are wrapped in an item 13400 * @param {Array} path | where to look for the actual option 13401 * @param {Array.<Element>} domElements 13402 * @returns {number} 13403 * @private 13404 */ 13405 13406 }, { 13407 key: '_makeItem', 13408 value: function _makeItem(path) { 13409 if (this.allowCreation === true) { 13410 var item = document.createElement('div'); 13411 item.className = 'vis-configuration vis-config-item vis-config-s' + path.length; 13412 13413 for (var _len = arguments.length, domElements = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { 13414 domElements[_key - 1] = arguments[_key]; 13415 } 13416
13417 domElements.forEach(function (element) { 13418 item.appendChild(element); 13419 }); 13420 this.domElements.push(item); 13421 return this.domElements.length; 13422 } 13423 return 0; 13424 } 13425 13426 /** 13427 * header for major subjects 13428 * @param {string} name 13429 * @private 13430 */ 13431 13432 }, { 13433 key: '_makeHeader', 13434 value: function _makeHeader(name) { 13435 var div = document.createElement('div'); 13436 div.className = 'vis-configuration vis-config-header'; 13437 div.innerHTML = name; 13438 this._makeItem([], div); 13439 } 13440 13441 /** 13442 * make a label, if it is an object label, it gets different styling. 13443 * @param {string} name 13444 * @param {array} path | where to look for the actual option 13445 * @param {string} objectLabel 13446 * @returns {HTMLElement} 13447 * @private 13448 */ 13449 13450 }, { 13451 key: '_makeLabel', 13452 value: function _makeLabel(name, path) { 13453 var objectLabel = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false; 13454 13455 var div = document.createElement('div'); 13456 div.className = 'vis-configuration vis-config-label vis-config-s' + path.length; 13457 if (objectLabel === true) { 13458 div.innerHTML = '<i><b>' + name + ':</b></i>'; 13459 } else { 13460 div.innerHTML = name + ':'; 13461 } 13462 return div; 13463 } 13464 13465 /** 13466 * make a dropdown list for multiple possible string optoins 13467 * @param {Array.<number>} arr 13468 * @param {number} value 13469 * @param {array} path | where to look for the actual option 13470 * @private 13471 */ 13472 13473 }, { 13474 key: '_makeDropdown', 13475 value: function _makeDropdown(arr, value, path) { 13476 var select = document.createElement('select'); 13477 select.className = 'vis-configuration vis-config-select'; 13478 var selectedValue = 0; 13479 if (value !== undefined) { 13480 if (arr.indexOf(value) !== -1) { 13481 selectedValue = arr.indexOf(value); 13482 } 13483 } 13484 13485 for (var i = 0; i < arr.length; i++) { 13486 var option = document.createElement('option'); 13487 option.value = arr[i]; 13488 if (i === selectedValue) { 13489 option.selected = 'selected'; 13490 } 13491 option.innerHTML = arr[i]; 13492 select.appendChild(option); 13493 } 13494 13495 var me = this; 13496 select.onchange = function () { 13497 me._update(this.value, path); 13498 }; 13499 13500 var label = this._makeLabel(path[path.length - 1], path); 13501 this._makeItem(path, label, select); 13502 } 13503 13504 /** 13505 * make a range object for numeric options 13506 * @param {Array.<number>} arr 13507 * @param {number} value 13508 * @param {array} path | where to look for the actual option 13509 * @private 13510 */ 13511 13512 }, { 13513 key: '_makeRange', 13514 value: function _makeRange(arr, value, path) { 13515 var defaultValue = arr[0]; 13516 var min = arr[1]; 13517 var max = arr[2]; 13518 var step = arr[3]; 13519 var range = document.createElement('input'); 13520 range.className = 'vis-configuration vis-config-range'; 13521 try { 13522 range.type = 'range'; // not supported on IE9 13523 range.min = min; 13524 range.max = max; 13525 } 13526 // TODO: Add some error handling and remove this lint exception 13527 catch (err) {} // eslint-disable-line no-empty 13528 range.step = step; 13529 13530 // set up the popup settings in case they are needed. 13531 var popupString = ''; 13532 var popupValue = 0; 13533 13534 if (value !== undefined) { 13535 var factor = 1.20; 13536 if (value < 0 && value * factor < min) { 13537 range.min = Math.ceil(value * factor); 13538 popupValue = range.min; 13539 popupString = 'range increased'; 13540 } else if (value / factor < min) { 13541 range.min = Math.ceil(value / factor); 13542 popupValue = range.min; 13543 popupString = 'range increased'; 13544 } 13545 if (value * factor > max && max !== 1) { 13546 range.max = Math.ceil(value * factor); 13547 popupValue = range.max; 13548 popupString = 'range increased'; 13549 } 13550 range.value = value; 13551 } else { 13552 range.value = defaultValue; 13553 } 13554 13555 var input = document.createElement('input'); 13556 input.className = 'vis-configuration vis-config-rangeinput'; 13557 input.value = range.value; 13558 13559 var me = this; 13560 range.onchange = function () { 13561 input.value = this.value;me._update(Number(this.value), path); 13562 }; 13563 range.oninput = function () { 13564 input.value = this.value; 13565 }; 13566 13567 var label = this._makeLabel(path[path.length - 1], path); 13568 var itemIndex = this._makeItem(path, label, range, input); 13569
13570 // if a popup is needed AND it has not been shown for this value, show it. 13571 if (popupString !== '' && this.popupHistory[itemIndex] !== popupValue) { 13572 this.popupHistory[itemIndex] = popupValue; 13573 this._setupPopup(popupString, itemIndex); 13574 } 13575 } 13576 13577 /** 13578 * prepare the popup 13579 * @param {string} string 13580 * @param {number} index 13581 * @private 13582 */ 13583 13584 }, { 13585 key: '_setupPopup', 13586 value: function _setupPopup(string, index) { 13587 var _this2 = this; 13588 13589 if (this.initialized === true && this.allowCreation === true && this.popupCounter < this.popupLimit) { 13590 var div = document.createElement("div"); 13591 div.id = "vis-configuration-popup"; 13592 div.className = "vis-configuration-popup"; 13593 div.innerHTML = string; 13594 div.onclick = function () { 13595 _this2._removePopup(); 13596 }; 13597 this.popupCounter += 1; 13598 this.popupDiv = { html: div, index: index }; 13599 } 13600 } 13601 13602 /** 13603 * remove the popup from the dom 13604 * @private 13605 */ 13606 13607 }, { 13608 key: '_removePopup', 13609 value: function _removePopup() { 13610 if (this.popupDiv.html !== undefined) { 13611 this.popupDiv.html.parentNode.removeChild(this.popupDiv.html); 13612 clearTimeout(this.popupDiv.hideTimeout); 13613 clearTimeout(this.popupDiv.deleteTimeout); 13614 this.popupDiv = {}; 13615 } 13616 } 13617 13618 /** 13619 * Show the popup if it is needed. 13620 * @private 13621 */ 13622 13623 }, { 13624 key: '_showPopupIfNeeded', 13625 value: function _showPopupIfNeeded() { 13626 var _this3 = this; 13627 13628 if (this.popupDiv.html !== undefined) { 13629 var correspondingElement = this.domElements[this.popupDiv.index]; 13630 var rect = correspondingElement.getBoundingClientRect(); 13631 this.popupDiv.html.style.left = rect.left + "px"; 13632 this.popupDiv.html.style.top = rect.top - 30 + "px"; // 30 is the height; 13633 document.body.appendChild(this.popupDiv.html); 13634 this.popupDiv.hideTimeout = setTimeout(function () { 13635 _this3.popupDiv.html.style.opacity = 0; 13636 }, 1500); 13637 this.popupDiv.deleteTimeout = setTimeout(function () { 13638 _this3._removePopup(); 13639 }, 1800); 13640 } 13641 } 13642 13643 /** 13644 * make a checkbox for boolean options. 13645 * @param {number} defaultValue 13646 * @param {number} value 13647 * @param {array} path | where to look for the actual option 13648 * @private 13649 */ 13650 13651 }, { 13652 key: '_makeCheckbox', 13653 value: function _makeCheckbox(defaultValue, value, path) { 13654 var checkbox = document.createElement('input'); 13655 checkbox.type = 'checkbox'; 13656 checkbox.className = 'vis-configuration vis-config-checkbox'; 13657 checkbox.checked = defaultValue; 13658 if (value !== undefined) { 13659 checkbox.checked = value; 13660 if (value !== defaultValue) { 13661 if ((typeof defaultValue === 'undefined' ? 'undefined' : (0, _typeof3['default'])(defaultValue)) === 'object') { 13662 if (value !== defaultValue.enabled) { 13663 this.changedOptions.push({ path: path, value: value }); 13664 } 13665 } else { 13666 this.changedOptions.push({ path: path, value: value }); 13667 } 13668 } 13669 } 13670 13671 var me = this; 13672 checkbox.onchange = function () { 13673 me._update(this.checked, path); 13674 }; 13675 13676 var label = this._makeLabel(path[path.length - 1], path); 13677 this._makeItem(path, label, checkbox); 13678 } 13679 13680 /** 13681 * make a text input field for string options. 13682 * @param {number} defaultValue 13683 * @param {number} value 13684 * @param {array} path | where to look for the actual option 13685 * @private 13686 */ 13687 13688 }, { 13689 key: '_makeTextInput', 13690 value: function _makeTextInput(defaultValue, value, path) { 13691 var checkbox = document.createElement('input'); 13692 checkbox.type = 'text'; 13693 checkbox.className = 'vis-configuration vis-config-text'; 13694 checkbox.value = value; 13695 if (value !== defaultValue) { 13696 this.changedOptions.push({ path: path, value: value }); 13697 } 13698 13699 var me = this; 13700 checkbox.onchange = function () { 13701 me._update(this.value, path); 13702 }; 13703 13704 var label = this._makeLabel(path[path.length - 1], path); 13705 this._makeItem(path, label, checkbox); 13706 } 13707 13708 /** 13709 * make a color field with a color picker for color fields 13710 * @param {Array.<number>} arr 13711 * @param {number} value 13712 * @param {array} path | where to look for the actual option 13713 * @private 13714 */ 13715 13716 }, { 13717 key: '_makeColorField', 13718 value: function _makeColorField(arr, value, path) { 13719 var _this4 = this; 13720 13721 var defaultColor = arr[1]; 13722 var div = document.createElement('div'); 13723 value = value === undefined ? defaultColor : value; 13724 13725 if (value !== 'none') { 13726 div.className = 'vis-configuration vis-config-colorBlock'; 13727 div.style.backgroundColor = value; 13728 } else { 13729 div.className = 'vis-configuration vis-config-colorBlock none'; 13730 } 13731 13732 value = value === undefined ? defaultColor : value; 13733 div.onclick = function () { 13734 _this4._showColorPicker(value, div, path); 13735 }; 13736 13737 var label = this._makeLabel(path[path.length - 1], path); 13738 this._makeItem(path, label, div); 13739 } 13740 13741 /** 13742 * used by the color buttons to call the color picker. 13743 * @param {number} value 13744 * @param {HTMLElement} div 13745 * @param {array} path | where to look for the actual option 13746 * @private 13747 */ 13748 13749 }, { 13750 key: '_showColorPicker', 13751 value: function _showColorPicker(value, div, path) { 13752 var _this5 = this; 13753 13754 // clear the callback from this div 13755 div.onclick = function () {}; 13756 13757 this.colorPicker.insertTo(div); 13758 this.colorPicker.show(); 13759 13760 this.colorPicker.setColor(value); 13761 this.colorPicker.setUpdateCallback(function (color) { 13762 var colorString = 'rgba(' + color.r + ',' + color.g + ',' + color.b + ',' + color.a + ')'; 13763 div.style.backgroundColor = colorString; 13764 _this5._update(colorString, path); 13765 }); 13766 13767 // on close of the colorpicker, restore the callback. 13768 this.colorPicker.setCloseCallback(function () { 13769 div.onclick = function () { 13770 _this5._showColorPicker(value, div, path); 13771 }; 13772 }); 13773 } 13774 13775 /** 13776 * parse an object and draw the correct items 13777 * @param {Object} obj 13778 * @param {array} [path=[]] | where to look for the actual option 13779 * @param {boolean} [checkOnly=false] 13780 * @returns {boolean} 13781 * @private 13782 */ 13783 13784 }, { 13785 key: '_handleObject', 13786 value: function _handleObject(obj) { 13787 var path = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : []; 13788 var checkOnly = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
13789 13790 var show = false; 13791 var filter = this.options.filter; 13792 var visibleInSet = false; 13793 for (var subObj in obj) { 13794 if (obj.hasOwnProperty(subObj)) { 13795 show = true; 13796 var item = obj[subObj]; 13797 var newPath = util.copyAndExtendArray(path, subObj); 13798 if (typeof filter === 'function') { 13799 show = filter(subObj, path); 13800 13801 // if needed we must go deeper into the object. 13802 if (show === false) { 13803 if (!(item instanceof Array) && typeof item !== 'string' && typeof item !== 'boolean' && item instanceof Object) { 13804 this.allowCreation = false; 13805 show = this._handleObject(item, newPath, true); 13806 this.allowCreation = checkOnly === false; 13807 } 13808 } 13809 } 13810 13811 if (show !== false) { 13812 visibleInSet = true; 13813 var value = this._getValue(newPath); 13814 13815 if (item instanceof Array) { 13816 this._handleArray(item, value, newPath); 13817 } else if (typeof item === 'string') { 13818 this._makeTextInput(item, value, newPath); 13819 } else if (typeof item === 'boolean') { 13820 this._makeCheckbox(item, value, newPath); 13821 } else if (item instanceof Object) { 13822 // collapse the physics options that are not enabled 13823 var draw = true; 13824 if (path.indexOf('physics') !== -1) { 13825 if (this.moduleOptions.physics.solver !== subObj) { 13826 draw = false; 13827 } 13828 } 13829 13830 if (draw === true) { 13831 // initially collapse options with an disabled enabled option. 13832 if (item.enabled !== undefined) { 13833 var enabledPath = util.copyAndExtendArray(newPath, 'enabled'); 13834 var enabledValue = this._getValue(enabledPath); 13835 if (enabledValue === true) { 13836 var label = this._makeLabel(subObj, newPath, true); 13837 this._makeItem(newPath, label); 13838 visibleInSet = this._handleObject(item, newPath) || visibleInSet; 13839 } else { 13840 this._makeCheckbox(item, enabledValue, newPath); 13841 } 13842 } else { 13843 var _label = this._makeLabel(subObj, newPath, true); 13844 this._makeItem(newPath, _label); 13845 visibleInSet = this._handleObject(item, newPath) || visibleInSet; 13846 } 13847 } 13848 } else { 13849 console.error('dont know how to handle', item, subObj, newPath); 13850 } 13851 } 13852 } 13853 } 13854 return visibleInSet; 13855 } 13856 13857 /** 13858 * handle the array type of option 13859 * @param {Array.<number>} arr 13860 * @param {number} value 13861 * @param {array} path | where to look for the actual option 13862 * @private 13863 */ 13864 13865 }, { 13866 key: '_handleArray', 13867 value: function _handleArray(arr, value, path) { 13868 if (typeof arr[0] === 'string' && arr[0] === 'color') { 13869 this._makeColorField(arr, value, path); 13870 if (arr[1] !== value) { 13871 this.changedOptions.push({ path: path, value: value }); 13872 } 13873 } else if (typeof arr[0] === 'string') { 13874 this._makeDropdown(arr, value, path); 13875 if (arr[0] !== value) { 13876 this.changedOptions.push({ path: path, value: value }); 13877 } 13878 } else if (typeof arr[0] === 'number') { 13879 this._makeRange(arr, value, path); 13880 if (arr[0] !== value) { 13881 this.changedOptions.push({ path: path, value: Number(value) }); 13882 } 13883 } 13884 } 13885 13886 /** 13887 * called to update the network with the new settings. 13888 * @param {number} value 13889 * @param {array} path | where to look for the actual option 13890 * @private 13891 */ 13892 13893 }, { 13894 key: '_update', 13895 value: function _update(value, path) { 13896 var options = this._constructOptions(value, path); 13897 13898 if (this.parent.body && this.parent.body.emitter && this.parent.body.emitter.emit) { 13899 this.parent.body.emitter.emit("configChange", options); 13900 } 13901 this.initialized = true; 13902 this.parent.setOptions(options); 13903 } 13904 13905 /** 13906 * 13907 * @param {string|Boolean} value 13908 * @param {Array.<string>} path 13909 * @param {{}} optionsObj 13910 * @returns {{}} 13911 * @private 13912 */ 13913 13914 }, { 13915 key: '_constructOptions', 13916 value: function _constructOptions(value, path) { 13917 var optionsObj = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; 13918 13919 var pointer = optionsObj; 13920 13921 // when dropdown boxes can be string or boolean, we typecast it into correct types 13922 value = value === 'true' ? true : value; 13923 value = value === 'false' ? false : value; 13924 13925 for (var i = 0; i < path.length; i++) { 13926 if (path[i] !== 'global') { 13927 if (pointer[path[i]] === undefined) { 13928 pointer[path[i]] = {}; 13929 } 13930 if (i !== path.length - 1) { 13931 pointer = pointer[path[i]]; 13932 } else { 13933 pointer[path[i]] = value; 13934 } 13935 } 13936 } 13937 return optionsObj; 13938 } 13939 13940 /** 13941 * @private 13942 */ 13943 13944 }, { 13945 key: '_printOptions', 13946 value: function _printOptions() { 13947 var options = this.getOptions(); 13948 this.optionsContainer.innerHTML = '<pre>var options = ' + (0, _stringify2['default'])(options, null, 2) + '</pre>'; 13949 } 13950 13951 /** 13952 * 13953 * @returns {{}} options 13954 */ 13955 13956 }, { 13957 key: 'getOptions', 13958 value: function getOptions() { 13959 var options = {}; 13960 for (var i = 0; i < this.changedOptions.length; i++) { 13961 this._constructOptions(this.changedOptions[i].value, this.changedOptions[i].path, options); 13962 } 13963 return options; 13964 } 13965 }]); 13966 return Configurator; 13967}(); 13968 13969exports['default'] = Configurator; 13970 13971/***/ }), 13972/* 72 */ 13973/***/ (function(module, exports, __webpack_require__) { 13974 13975"use strict"; 13976 13977 13978var _typeof2 = __webpack_require__(6); 13979 13980var _typeof3 = _interopRequireDefault(_typeof2); 13981 13982function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 13983 13984var DOMutil = __webpack_require__(14); 13985 13986/** 13987 * 13988 * @param {number | string} groupId 13989 * @param {Object} options // TODO: Describe options 13990 * 13991 * @constructor Points 13992 */ 13993function Points(groupId, options) {} // eslint-disable-line no-unused-vars 13994 13995 13996/** 13997 * draw the data points 13998 * 13999 * @param {Array} dataset 14000 * @param {GraphGroup} group 14001 * @param {Object} framework | SVG DOM element 14002 * @param {number} [offset] 14003 */ 14004Points.draw = function (dataset, group, framework, offset) { 14005 offset = offset || 0; 14006 var callback = getCallback(framework, group); 14007 14008 for (var i = 0; i < dataset.length; i++) { 14009 if (!callback) { 14010 // draw the point the simple way. 14011 DOMutil.drawPoint(dataset[i].screen_x + offset, dataset[i].screen_y, getGroupTemplate(group), framework.svgElements, framework.svg, dataset[i].label); 14012 } else { 14013 var callbackResult = callback(dataset[i], group); // result might be true, false or an object 14014 if (callbackResult === true || (typeof callbackResult === 'undefined' ? 'undefined' : (0, _typeof3['default'])(callbackResult)) === 'object') { 14015 DOMutil.drawPoint(dataset[i].screen_x + offset, dataset[i].screen_y, getGroupTemplate(group, callbackResult), framework.svgElements, framework.svg, dataset[i].label); 14016 } 14017 } 14018 } 14019}; 14020 14021Points.drawIcon = function (group, x, y, iconWidth, iconHeight, framework) { 14022 var fillHeight = iconHeight * 0.5; 14023 14024 var outline = DOMutil.getSVGElement("rect", framework.svgElements, framework.svg); 14025 outline.setAttributeNS(null, "x", x); 14026 outline.setAttributeNS(null, "y", y - fillHeight); 14027 outline.setAttributeNS(null, "width", iconWidth); 14028 outline.setAttributeNS(null, "height", 2 * fillHeight); 14029 outline.setAttributeNS(null, "class", "vis-outline"); 14030 14031 //Don't call callback on icon 14032 DOMutil.drawPoint(x + 0.5 * iconWidth, y, getGroupTemplate(group), framework.svgElements, framework.svg); 14033}; 14034 14035/** 14036 * 14037 * @param {vis.Group} group 14038 * @param {any} callbackResult 14039 * @returns {{style: *, styles: (*|string), size: *, className: *}} 14040 */ 14041function getGroupTemplate(group, callbackResult) { 14042 callbackResult = typeof callbackResult === 'undefined' ? {} : callbackResult; 14043 return { 14044 style: callbackResult.style || group.options.drawPoints.style, 14045 styles: callbackResult.styles || group.options.drawPoints.styles, 14046 size: callbackResult.size || group.options.drawPoints.size, 14047 className: callbackResult.className || group.className 14048 }; 14049} 14050 14051/** 14052 * 14053 * @param {Object} framework | SVG DOM element 14054 * @param {vis.Group} group 14055 * @returns {function} 14056 */ 14057function getCallback(framework, group) { 14058 var callback = undefined; 14059 // check for the graph2d onRender 14060 if (framework.options && framework.options.drawPoints && framework.options.drawPoints.onRender && typeof framework.options.drawPoints.onRender == 'function') { 14061 callback = framework.options.drawPoints.onRender; 14062 } 14063 14064 // override it with the group onRender if defined 14065 if (group.group.options && group.group.options.drawPoints && group.group.options.drawPoints.onRender && typeof group.group.options.drawPoints.onRender == 'function') { 14066 callback = group.group.options.drawPoints.onRender; 14067 } 14068 return callback; 14069} 14070 14071module.exports = Points; 14072 14073/***/ }), 14074/* 73 */ 14075/***/ (function(module, exports, __webpack_require__) { 14076 14077"use strict"; 14078 14079 14080Object.defineProperty(exports, "__esModule", { 14081 value: true 14082}); 14083 14084var _getPrototypeOf = __webpack_require__(3); 14085 14086var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf); 14087 14088var _classCallCheck2 = __webpack_require__(0); 14089 14090var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); 14091 14092var _createClass2 = __webpack_require__(1); 14093 14094var _createClass3 = _interopRequireDefault(_createClass2); 14095 14096var _possibleConstructorReturn2 = __webpack_require__(4); 14097 14098var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2); 14099 14100var _inherits2 = __webpack_require__(5); 14101 14102var _inherits3 = _interopRequireDefault(_inherits2); 14103 14104var _NodeBase2 = __webpack_require__(23); 14105 14106var _NodeBase3 = _interopRequireDefault(_NodeBase2); 14107 14108function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 14109 14110/** 14111 * NOTE: This is a bad base class 14112 * 14113 * Child classes are: 14114 * 14115 * Image - uses *only* image methods 14116 * Circle - uses *only* _drawRawCircle 14117 * CircleImage - uses all 14118 * 14119 * TODO: Refactor, move _drawRawCircle to different module, derive Circle from NodeBase 14120 * Rename this to ImageBase 14121 * Consolidate common code in Image and CircleImage to base class 14122 * 14123 * @extends NodeBase 14124 */ 14125var CircleImageBase = function (_NodeBase) { 14126 (0, _inherits3['default'])(CircleImageBase, _NodeBase); 14127 14128 /** 14129 * @param {Object} options 14130 * @param {Object} body 14131 * @param {Label} labelModule 14132 */ 14133 function CircleImageBase(options, body, labelModule) { 14134 (0, _classCallCheck3['default'])(this, CircleImageBase); 14135 14136 var _this = (0, _possibleConstructorReturn3['default'])(this, (CircleImageBase.__proto__ || (0, _getPrototypeOf2['default'])(CircleImageBase)).call(this, options, body, labelModule)); 14137 14138 _this.labelOffset = 0; 14139 _this.selected = false;
14140 return _this; 14141 } 14142 14143 /** 14144 * 14145 * @param {Object} options 14146 * @param {Object} [imageObj] 14147 * @param {Object} [imageObjAlt] 14148 */ 14149 14150 14151 (0, _createClass3['default'])(CircleImageBase, [{ 14152 key: 'setOptions', 14153 value: function setOptions(options, imageObj, imageObjAlt) { 14154 this.options = options; 14155 14156 if (!(imageObj === undefined && imageObjAlt === undefined)) { 14157 this.setImages(imageObj, imageObjAlt); 14158 } 14159 } 14160 14161 /** 14162 * Set the images for this node. 14163 * 14164 * The images can be updated after the initial setting of options; 14165 * therefore, this method needs to be reentrant. 14166 * 14167 * For correct working in error cases, it is necessary to properly set 14168 * field 'nodes.brokenImage' in the options. 14169 * 14170 * @param {Image} imageObj required; main image to show for this node 14171 * @param {Image|undefined} imageObjAlt optional; image to show when node is selected 14172 */ 14173 14174 }, { 14175 key: 'setImages', 14176 value: function setImages(imageObj, imageObjAlt) { 14177 if (imageObjAlt && this.selected) { 14178 this.imageObj = imageObjAlt; 14179 this.imageObjAlt = imageObj; 14180 } else { 14181 this.imageObj = imageObj; 14182 this.imageObjAlt = imageObjAlt; 14183 } 14184 } 14185 14186 /** 14187 * Set selection and switch between the base and the selected image. 14188 * 14189 * Do the switch only if imageObjAlt exists. 14190 * 14191 * @param {boolean} selected value of new selected state for current node 14192 */ 14193 14194 }, { 14195 key: 'switchImages', 14196 value: function switchImages(selected) { 14197 var selection_changed = selected && !this.selected || !selected && this.selected; 14198 this.selected = selected; // Remember new selection 14199 14200 if (this.imageObjAlt !== undefined && selection_changed) { 14201 var imageTmp = this.imageObj; 14202 this.imageObj = this.imageObjAlt; 14203 this.imageObjAlt = imageTmp; 14204 } 14205 } 14206 14207 /** 14208 * Adjust the node dimensions for a loaded image. 14209 * 14210 * Pre: this.imageObj is valid 14211 */ 14212 14213 }, { 14214 key: '_resizeImage', 14215 value: function _resizeImage() { 14216 var width, height; 14217 14218 if (this.options.shapeProperties.useImageSize === false) { 14219 // Use the size property 14220 var ratio_width = 1; 14221 var ratio_height = 1; 14222 14223 // Only calculate the proper ratio if both width and height not zero 14224 if (this.imageObj.width && this.imageObj.height) { 14225 if (this.imageObj.width > this.imageObj.height) { 14226 ratio_width = this.imageObj.width / this.imageObj.height; 14227 } else { 14228 ratio_height = this.imageObj.height / this.imageObj.width; 14229 } 14230 } 14231 14232 width = this.options.size * 2 * ratio_width; 14233 height = this.options.size * 2 * ratio_height; 14234 } else { 14235 // Use the image size 14236 width = this.imageObj.width; 14237 height = this.imageObj.height; 14238 } 14239 14240 this.width = width; 14241 this.height = height; 14242 this.radius = 0.5 * this.width; 14243 } 14244 14245 /** 14246 * 14247 * @param {CanvasRenderingContext2D} ctx 14248 * @param {number} x width 14249 * @param {number} y height 14250 * @param {{toArrow: boolean, toArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), toArrowType: *, middleArrow: boolean, middleArrowScale: (number|allOptions.edges.arrows.middle.scaleFactor|{number}|Array), middleArrowType: (allOptions.edges.arrows.middle.type|{string}|string|*), fromArrow: boolean, fromArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), fromArrowType: *, arrowStrikethrough: (*|boolean|allOptions.edges.arrowStrikethrough|{boolean}), color: undefined, inheritsColor: (string|string|string|allOptions.edges.color.inherit|{string, boolean}|Array|*), opacity: *, hidden: *, length: *, shadow: *, shadowColor: *, shadowSize: *, shadowX: *, shadowY: *, dashes: (*|boolean|Array|allOptions.edges.dashes|{boolean, array}), width: *}} values 14251 * @private 14252 */ 14253 14254 }, { 14255 key: '_drawRawCircle', 14256 value: function _drawRawCircle(ctx, x, y, values) { 14257 this.initContextForDraw(ctx, values); 14258 ctx.circle(x, y, values.size); 14259 this.performFill(ctx, values); 14260 } 14261 14262 /** 14263 * 14264 * @param {CanvasRenderingContext2D} ctx 14265 * @param {{toArrow: boolean, toArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), toArrowType: *, middleArrow: boolean, middleArrowScale: (number|allOptions.edges.arrows.middle.scaleFactor|{number}|Array), middleArrowType: (allOptions.edges.arrows.middle.type|{string}|string|*), fromArrow: boolean, fromArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), fromArrowType: *, arrowStrikethrough: (*|boolean|allOptions.edges.arrowStrikethrough|{boolean}), color: undefined, inheritsColor: (string|string|string|allOptions.edges.color.inherit|{string, boolean}|Array|*), opacity: *, hidden: *, length: *, shadow: *, shadowColor: *, shadowSize: *, shadowX: *, shadowY: *, dashes: (*|boolean|Array|allOptions.edges.dashes|{boolean, array}), width: *}} values 14266 * @private 14267 */ 14268 14269 }, { 14270 key: '_drawImageAtPosition', 14271 value: function _drawImageAtPosition(ctx, values) { 14272 if (this.imageObj.width != 0) { 14273 // draw the image 14274 ctx.globalAlpha = 1.0; 14275 14276 // draw shadow if enabled 14277 this.enableShadow(ctx, values); 14278 14279 var factor = 1; 14280 if (this.options.shapeProperties.interpolation === true) { 14281 factor = this.imageObj.width / this.width / this.body.view.scale; 14282 } 14283 14284 this.imageObj.drawImageAtPosition(ctx, factor, this.left, this.top, this.width, this.height); 14285 14286 // disable shadows for other elements. 14287 this.disableShadow(ctx, values); 14288 } 14289 } 14290 14291 /** 14292 * 14293 * @param {CanvasRenderingContext2D} ctx 14294 * @param {number} x width 14295 * @param {number} y height 14296 * @param {boolean} selected 14297 * @param {boolean} hover 14298 * @private 14299 */ 14300 14301 }, { 14302 key: '_drawImageLabel', 14303 value: function _drawImageLabel(ctx, x, y, selected, hover) { 14304 var yLabel; 14305 var offset = 0; 14306 14307 if (this.height !== undefined) { 14308 offset = this.height * 0.5; 14309 var labelDimensions = this.labelModule.getTextSize(ctx, selected, hover); 14310 if (labelDimensions.lineCount >= 1) { 14311 offset += labelDimensions.height / 2; 14312 } 14313 } 14314 14315 yLabel = y + offset; 14316 14317 if (this.options.label) { 14318 this.labelOffset = offset; 14319 } 14320 this.labelModule.draw(ctx, x, yLabel, selected, hover, 'hanging'); 14321 } 14322 }]); 14323 return CircleImageBase; 14324}(_NodeBase3['default']); 14325 14326exports['default'] = CircleImageBase; 14327 14328/***/ }), 14329/* 74 */ 14330/***/ (function(module, exports, __webpack_require__) { 14331 14332"use strict"; 14333 14334 14335Object.defineProperty(exports, "__esModule", { 14336 value: true 14337}); 14338 14339var _stringify = __webpack_require__(19); 14340 14341var _stringify2 = _interopRequireDefault(_stringify); 14342 14343var _typeof2 = __webpack_require__(6); 14344 14345var _typeof3 = _interopRequireDefault(_typeof2); 14346 14347var _create = __webpack_require__(29); 14348 14349var _create2 = _interopRequireDefault(_create); 14350 14351var _classCallCheck2 = __webpack_require__(0); 14352 14353var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); 14354 14355var _createClass2 = __webpack_require__(1); 14356 14357var _createClass3 = _interopRequireDefault(_createClass2); 14358 14359function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 14360 14361var util = __webpack_require__(2); 14362var Label = __webpack_require__(117)['default']; 14363var ComponentUtil = __webpack_require__(48)['default']; 14364var CubicBezierEdge = __webpack_require__(215)['default']; 14365var BezierEdgeDynamic = __webpack_require__(217)['default']; 14366var BezierEdgeStatic = __webpack_require__(218)['default']; 14367var StraightEdge = __webpack_require__(219)['default']; 14368 14369/** 14370 * An edge connects two nodes and has a specific direction. 14371 */ 14372 14373var Edge = function () { 14374 /** 14375 * @param {Object} options values specific to this edge, must contain at least 'from' and 'to' 14376 * @param {Object} body shared state from Network instance 14377 * @param {Object} globalOptions options from the EdgesHandler instance 14378 * @param {Object} defaultOptions default options from the EdgeHandler instance. Value and reference are constant 14379 */ 14380 function Edge(options, body, globalOptions, defaultOptions) { 14381 (0, _classCallCheck3['default'])(this, Edge); 14382 14383 if (body === undefined) { 14384 throw new Error("No body provided"); 14385 } 14386 14387 // Since globalOptions is constant in values as well as reference, 14388 // Following needs to be done only once. 14389 14390 this.options = util.bridgeObject(globalOptions); 14391 this.globalOptions = globalOptions; 14392 this.defaultOptions = defaultOptions; 14393 this.body = body; 14394 14395 // initialize variables 14396 this.id = undefined; 14397 this.fromId = undefined; 14398 this.toId = undefined; 14399 this.selected = false;
14400 this.hover = false; 14401 this.labelDirty = true; 14402 14403 this.baseWidth = this.options.width; 14404 this.baseFontSize = this.options.font.size; 14405 14406 this.from = undefined; // a node 14407 this.to = undefined; // a node 14408 14409 this.edgeType = undefined; 14410 14411 this.connected = false; 14412 14413 this.labelModule = new Label(this.body, this.options, true /* It's an edge label */); 14414 this.setOptions(options); 14415 } 14416 14417 /** 14418 * Set or overwrite options for the edge 14419 * @param {Object} options an object with options 14420 * @returns {null|boolean} null if no options, boolean if date changed 14421 */ 14422 14423 14424 (0, _createClass3['default'])(Edge, [{ 14425 key: 'setOptions', 14426 value: function setOptions(options) { 14427 if (!options) { 14428 return; 14429 } 14430 14431 Edge.parseOptions(this.options, options, true, this.globalOptions); 14432 14433 if (options.id !== undefined) { 14434 this.id = options.id; 14435 } 14436 if (options.from !== undefined) { 14437 this.fromId = options.from; 14438 } 14439 if (options.to !== undefined) { 14440 this.toId = options.to; 14441 } 14442 if (options.title !== undefined) { 14443 this.title = options.title; 14444 } 14445 if (options.value !== undefined) { 14446 options.value = parseFloat(options.value); 14447 } 14448 14449 var pile = [options, this.options, this.defaultOptions]; 14450 this.chooser = ComponentUtil.choosify('edge', pile); 14451 14452 // update label Module 14453 this.updateLabelModule(options); 14454 14455 var dataChanged = this.updateEdgeType(); 14456 14457 // if anything has been updates, reset the selection width and the hover width 14458 this._setInteractionWidths(); 14459 14460 // A node is connected when it has a from and to node that both exist in the network.body.nodes. 14461 this.connect(); 14462 14463 if (options.hidden !== undefined || options.physics !== undefined) { 14464 dataChanged = true; 14465 } 14466 14467 return dataChanged; 14468 } 14469 14470 /** 14471 * 14472 * @param {Object} parentOptions 14473 * @param {Object} newOptions 14474 * @param {boolean} [allowDeletion=false] 14475 * @param {Object} [globalOptions={}] 14476 * @param {boolean} [copyFromGlobals=false] 14477 */ 14478 14479 }, { 14480 key: 'getFormattingValues', 14481 14482 14483 /** 14484 * 14485 * @returns {{toArrow: boolean, toArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), toArrowType: *, middleArrow: boolean, middleArrowScale: (number|allOptions.edges.arrows.middle.scaleFactor|{number}|Array), middleArrowType: (allOptions.edges.arrows.middle.type|{string}|string|*), fromArrow: boolean, fromArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), fromArrowType: *, arrowStrikethrough: (*|boolean|allOptions.edges.arrowStrikethrough|{boolean}), color: undefined, inheritsColor: (string|string|string|allOptions.edges.color.inherit|{string, boolean}|Array|*), opacity: *, hidden: *, length: *, shadow: *, shadowColor: *, shadowSize: *, shadowX: *, shadowY: *, dashes: (*|boolean|Array|allOptions.edges.dashes|{boolean, array}), width: *}} 14486 */ 14487 value: function getFormattingValues() { 14488 var toArrow = this.options.arrows.to === true || this.options.arrows.to.enabled === true; 14489 var fromArrow = this.options.arrows.from === true || this.options.arrows.from.enabled === true; 14490 var middleArrow = this.options.arrows.middle === true || this.options.arrows.middle.enabled === true; 14491 var inheritsColor = this.options.color.inherit; 14492 var values = { 14493 toArrow: toArrow, 14494 toArrowScale: this.options.arrows.to.scaleFactor, 14495 toArrowType: this.options.arrows.to.type, 14496 middleArrow: middleArrow, 14497 middleArrowScale: this.options.arrows.middle.scaleFactor, 14498 middleArrowType: this.options.arrows.middle.type, 14499 fromArrow: fromArrow, 14500 fromArrowScale: this.options.arrows.from.scaleFactor, 14501 fromArrowType: this.options.arrows.from.type, 14502 arrowStrikethrough: this.options.arrowStrikethrough, 14503 color: inheritsColor ? undefined : this.options.color.color, 14504 inheritsColor: inheritsColor, 14505 opacity: this.options.color.opacity, 14506 hidden: this.options.hidden, 14507 length: this.options.length, 14508 shadow: this.options.shadow.enabled, 14509 shadowColor: this.options.shadow.color, 14510 shadowSize: this.options.shadow.size, 14511 shadowX: this.options.shadow.x, 14512 shadowY: this.options.shadow.y, 14513 dashes: this.options.dashes, 14514 width: this.options.width 14515 }; 14516 if (this.selected || this.hover) { 14517 if (this.chooser === true) { 14518 if (this.selected) { 14519 var selectedWidth = this.options.selectionWidth; 14520 if (typeof selectedWidth === 'function') { 14521 values.width = selectedWidth(values.width); 14522 } else if (typeof selectedWidth === 'number') { 14523 values.width += selectedWidth; 14524 } 14525 values.width = Math.max(values.width, 0.3 / this.body.view.scale); 14526 values.color = this.options.color.highlight; 14527 values.shadow = this.options.shadow.enabled; 14528 } else if (this.hover) { 14529 var hoverWidth = this.options.hoverWidth; 14530 if (typeof hoverWidth === 'function') { 14531 values.width = hoverWidth(values.width); 14532 } else if (typeof hoverWidth === 'number') { 14533 values.width += hoverWidth; 14534 } 14535 values.width = Math.max(values.width, 0.3 / this.body.view.scale); 14536 values.color = this.options.color.hover; 14537 values.shadow = this.options.shadow.enabled; 14538 } 14539 } else if (typeof this.chooser === 'function') { 14540 this.chooser(values, this.options.id, this.selected, this.hover); 14541 if (values.color !== undefined) { 14542 values.inheritsColor = false;
14543 } 14544 if (values.shadow === false) { 14545 if (values.shadowColor !== this.options.shadow.color || values.shadowSize !== this.options.shadow.size || values.shadowX !== this.options.shadow.x || values.shadowY !== this.options.shadow.y) { 14546 values.shadow = true; 14547 } 14548 } 14549 } 14550 } else { 14551 values.shadow = this.options.shadow.enabled; 14552 values.width = Math.max(values.width, 0.3 / this.body.view.scale); 14553 } 14554 return values; 14555 } 14556 14557 /** 14558 * update the options in the label module 14559 * 14560 * @param {Object} options 14561 */ 14562 14563 }, { 14564 key: 'updateLabelModule', 14565 value: function updateLabelModule(options) { 14566 var pile = [options, this.options, this.globalOptions, // Currently set global edge options 14567 this.defaultOptions]; 14568 14569 this.labelModule.update(this.options, pile); 14570 14571 if (this.labelModule.baseSize !== undefined) { 14572 this.baseFontSize = this.labelModule.baseSize; 14573 } 14574 } 14575 14576 /** 14577 * update the edge type, set the options 14578 * @returns {boolean} 14579 */ 14580 14581 }, { 14582 key: 'updateEdgeType', 14583 value: function updateEdgeType() { 14584 var smooth = this.options.smooth; 14585 var dataChanged = false; 14586 var changeInType = true; 14587 if (this.edgeType !== undefined) { 14588 if (this.edgeType instanceof BezierEdgeDynamic && smooth.enabled === true && smooth.type === 'dynamic' || this.edgeType instanceof CubicBezierEdge && smooth.enabled === true && smooth.type === 'cubicBezier' || this.edgeType instanceof BezierEdgeStatic && smooth.enabled === true && smooth.type !== 'dynamic' && smooth.type !== 'cubicBezier' || this.edgeType instanceof StraightEdge && smooth.type.enabled === false) { 14589 changeInType = false;
14590 } 14591 if (changeInType === true) { 14592 dataChanged = this.cleanup(); 14593 } 14594 } 14595 if (changeInType === true) { 14596 if (smooth.enabled === true) { 14597 if (smooth.type === 'dynamic') { 14598 dataChanged = true; 14599 this.edgeType = new BezierEdgeDynamic(this.options, this.body, this.labelModule); 14600 } else if (smooth.type === 'cubicBezier') { 14601 this.edgeType = new CubicBezierEdge(this.options, this.body, this.labelModule); 14602 } else { 14603 this.edgeType = new BezierEdgeStatic(this.options, this.body, this.labelModule); 14604 } 14605 } else { 14606 this.edgeType = new StraightEdge(this.options, this.body, this.labelModule); 14607 } 14608 } else { 14609 // if nothing changes, we just set the options. 14610 this.edgeType.setOptions(this.options); 14611 } 14612 return dataChanged; 14613 } 14614 14615 /** 14616 * Connect an edge to its nodes 14617 */ 14618 14619 }, { 14620 key: 'connect', 14621 value: function connect() { 14622 this.disconnect(); 14623 14624 this.from = this.body.nodes[this.fromId] || undefined; 14625 this.to = this.body.nodes[this.toId] || undefined; 14626 this.connected = this.from !== undefined && this.to !== undefined; 14627 14628 if (this.connected === true) { 14629 this.from.attachEdge(this); 14630 this.to.attachEdge(this); 14631 } else { 14632 if (this.from) { 14633 this.from.detachEdge(this); 14634 } 14635 if (this.to) { 14636 this.to.detachEdge(this); 14637 } 14638 } 14639 14640 this.edgeType.connect(); 14641 } 14642 14643 /** 14644 * Disconnect an edge from its nodes 14645 */ 14646 14647 }, { 14648 key: 'disconnect', 14649 value: function disconnect() { 14650 if (this.from) { 14651 this.from.detachEdge(this); 14652 this.from = undefined; 14653 } 14654 if (this.to) { 14655 this.to.detachEdge(this); 14656 this.to = undefined; 14657 } 14658 14659 this.connected = false; 14660 } 14661 14662 /** 14663 * get the title of this edge. 14664 * @return {string} title The title of the edge, or undefined when no title 14665 * has been set. 14666 */ 14667 14668 }, { 14669 key: 'getTitle', 14670 value: function getTitle() { 14671 return this.title; 14672 } 14673 14674 /** 14675 * check if this node is selecte 14676 * @return {boolean} selected True if node is selected, else false 14677 */ 14678 14679 }, { 14680 key: 'isSelected', 14681 value: function isSelected() { 14682 return this.selected; 14683 } 14684 14685 /** 14686 * Retrieve the value of the edge. Can be undefined 14687 * @return {number} value 14688 */ 14689 14690 }, { 14691 key: 'getValue', 14692 value: function getValue() { 14693 return this.options.value; 14694 } 14695 14696 /** 14697 * Adjust the value range of the edge. The edge will adjust it's width 14698 * based on its value. 14699 * @param {number} min 14700 * @param {number} max 14701 * @param {number} total 14702 */ 14703 14704 }, { 14705 key: 'setValueRange', 14706 value: function setValueRange(min, max, total) { 14707 if (this.options.value !== undefined) { 14708 var scale = this.options.scaling.customScalingFunction(min, max, total, this.options.value); 14709 var widthDiff = this.options.scaling.max - this.options.scaling.min; 14710 if (this.options.scaling.label.enabled === true) { 14711 var fontDiff = this.options.scaling.label.max - this.options.scaling.label.min; 14712 this.options.font.size = this.options.scaling.label.min + scale * fontDiff; 14713 } 14714 this.options.width = this.options.scaling.min + scale * widthDiff; 14715 } else { 14716 this.options.width = this.baseWidth; 14717 this.options.font.size = this.baseFontSize; 14718 } 14719 14720 this._setInteractionWidths(); 14721 this.updateLabelModule(); 14722 } 14723 14724 /** 14725 * 14726 * @private 14727 */ 14728 14729 }, { 14730 key: '_setInteractionWidths', 14731 value: function _setInteractionWidths() { 14732 if (typeof this.options.hoverWidth === 'function') { 14733 this.edgeType.hoverWidth = this.options.hoverWidth(this.options.width); 14734 } else { 14735 this.edgeType.hoverWidth = this.options.hoverWidth + this.options.width; 14736 } 14737 if (typeof this.options.selectionWidth === 'function') { 14738 this.edgeType.selectionWidth = this.options.selectionWidth(this.options.width); 14739 } else { 14740 this.edgeType.selectionWidth = this.options.selectionWidth + this.options.width; 14741 } 14742 } 14743 14744 /** 14745 * Redraw a edge 14746 * Draw this edge in the given canvas 14747 * The 2d context of a HTML canvas can be retrieved by canvas.getContext("2d"); 14748 * @param {CanvasRenderingContext2D} ctx 14749 */ 14750 14751 }, { 14752 key: 'draw', 14753 value: function draw(ctx) { 14754 var values = this.getFormattingValues(); 14755 if (values.hidden) { 14756 return; 14757 } 14758 14759 // get the via node from the edge type 14760 var viaNode = this.edgeType.getViaNode(); 14761 var arrowData = {}; 14762 14763 // restore edge targets to defaults 14764 this.edgeType.fromPoint = this.edgeType.from; 14765 this.edgeType.toPoint = this.edgeType.to; 14766 14767 // from and to arrows give a different end point for edges. we set them here 14768 if (values.fromArrow) { 14769 arrowData.from = this.edgeType.getArrowData(ctx, 'from', viaNode, this.selected, this.hover, values); 14770 if (values.arrowStrikethrough === false) this.edgeType.fromPoint = arrowData.from.core; 14771 } 14772 if (values.toArrow) { 14773 arrowData.to = this.edgeType.getArrowData(ctx, 'to', viaNode, this.selected, this.hover, values); 14774 if (values.arrowStrikethrough === false) this.edgeType.toPoint = arrowData.to.core; 14775 } 14776 14777 // the middle arrow depends on the line, which can depend on the to and from arrows so we do this one lastly. 14778 if (values.middleArrow) { 14779 arrowData.middle = this.edgeType.getArrowData(ctx, 'middle', viaNode, this.selected, this.hover, values); 14780 } 14781 14782 // draw everything 14783 this.edgeType.drawLine(ctx, values, this.selected, this.hover, viaNode); 14784 this.drawArrows(ctx, arrowData, values); 14785 this.drawLabel(ctx, viaNode); 14786 } 14787 14788 /** 14789 * 14790 * @param {CanvasRenderingContext2D} ctx 14791 * @param {Object} arrowData 14792 * @param {{toArrow: boolean, toArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), toArrowType: *, middleArrow: boolean, middleArrowScale: (number|allOptions.edges.arrows.middle.scaleFactor|{number}|Array), middleArrowType: (allOptions.edges.arrows.middle.type|{string}|string|*), fromArrow: boolean, fromArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), fromArrowType: *, arrowStrikethrough: (*|boolean|allOptions.edges.arrowStrikethrough|{boolean}), color: undefined, inheritsColor: (string|string|string|allOptions.edges.color.inherit|{string, boolean}|Array|*), opacity: *, hidden: *, length: *, shadow: *, shadowColor: *, shadowSize: *, shadowX: *, shadowY: *, dashes: (*|boolean|Array|allOptions.edges.dashes|{boolean, array}), width: *}} values 14793 */ 14794 14795 }, { 14796 key: 'drawArrows', 14797 value: function drawArrows(ctx, arrowData, values) { 14798 if (values.fromArrow) { 14799 this.edgeType.drawArrowHead(ctx, values, this.selected, this.hover, arrowData.from); 14800 } 14801 if (values.middleArrow) { 14802 this.edgeType.drawArrowHead(ctx, values, this.selected, this.hover, arrowData.middle); 14803 } 14804 if (values.toArrow) { 14805 this.edgeType.drawArrowHead(ctx, values, this.selected, this.hover, arrowData.to); 14806 } 14807 } 14808 14809 /** 14810 * 14811 * @param {CanvasRenderingContext2D} ctx 14812 * @param {Node} viaNode 14813 */ 14814 14815 }, { 14816 key: 'drawLabel', 14817 value: function drawLabel(ctx, viaNode) { 14818 if (this.options.label !== undefined) { 14819 // set style 14820 var node1 = this.from; 14821 var node2 = this.to; 14822 14823 if (this.labelModule.differentState(this.selected, this.hover)) { 14824 this.labelModule.getTextSize(ctx, this.selected, this.hover); 14825 } 14826 14827 if (node1.id != node2.id) { 14828 this.labelModule.pointToSelf = false;
14829 var point = this.edgeType.getPoint(0.5, viaNode); 14830 ctx.save(); 14831 14832 var rotationPoint = this._getRotation(ctx); 14833 if (rotationPoint.angle != 0) { 14834 ctx.translate(rotationPoint.x, rotationPoint.y); 14835 ctx.rotate(rotationPoint.angle); 14836 } 14837 14838 // draw the label 14839 this.labelModule.draw(ctx, point.x, point.y, this.selected, this.hover); 14840 14841 /* 14842 // Useful debug code: draw a border around the label 14843 // This should **not** be enabled in production! 14844 var size = this.labelModule.getSize();; // ;; intentional so lint catches it 14845 ctx.strokeStyle = "#ff0000"; 14846 ctx.strokeRect(size.left, size.top, size.width, size.height); 14847 // End debug code 14848 */ 14849 14850 ctx.restore(); 14851 } else { 14852 // Ignore the orientations. 14853 this.labelModule.pointToSelf = true; 14854 var x, y; 14855 var radius = this.options.selfReferenceSize; 14856 if (node1.shape.width > node1.shape.height) { 14857 x = node1.x + node1.shape.width * 0.5; 14858 y = node1.y - radius; 14859 } else { 14860 x = node1.x + radius; 14861 y = node1.y - node1.shape.height * 0.5; 14862 } 14863 point = this._pointOnCircle(x, y, radius, 0.125); 14864 this.labelModule.draw(ctx, point.x, point.y, this.selected, this.hover); 14865 } 14866 } 14867 } 14868 14869 /** 14870 * Determine all visual elements of this edge instance, in which the given 14871 * point falls within the bounding shape. 14872 * 14873 * @param {point} point 14874 * @returns {Array.<edgeClickItem|edgeLabelClickItem>} list with the items which are on the point 14875 */ 14876 14877 }, { 14878 key: 'getItemsOnPoint', 14879 value: function getItemsOnPoint(point) { 14880 var ret = []; 14881 14882 if (this.labelModule.visible()) { 14883 var rotationPoint = this._getRotation(); 14884 if (ComponentUtil.pointInRect(this.labelModule.getSize(), point, rotationPoint)) { 14885 ret.push({ edgeId: this.id, labelId: 0 }); 14886 } 14887 } 14888 14889 var obj = { 14890 left: point.x, 14891 top: point.y 14892 }; 14893 14894 if (this.isOverlappingWith(obj)) { 14895 ret.push({ edgeId: this.id }); 14896 } 14897 14898 return ret; 14899 } 14900 14901 /** 14902 * Check if this object is overlapping with the provided object 14903 * @param {Object} obj an object with parameters left, top 14904 * @return {boolean} True if location is located on the edge 14905 */ 14906 14907 }, { 14908 key: 'isOverlappingWith', 14909 value: function isOverlappingWith(obj) { 14910 if (this.connected) { 14911 var distMax = 10; 14912 var xFrom = this.from.x; 14913 var yFrom = this.from.y; 14914 var xTo = this.to.x; 14915 var yTo = this.to.y; 14916 var xObj = obj.left; 14917 var yObj = obj.top; 14918 14919 var dist = this.edgeType.getDistanceToEdge(xFrom, yFrom, xTo, yTo, xObj, yObj); 14920 14921 return dist < distMax; 14922 } else { 14923 return false; 14924 } 14925 } 14926 14927 /** 14928 * Determine the rotation point, if any. 14929 * 14930 * @param {CanvasRenderingContext2D} [ctx] if passed, do a recalculation of the label size 14931 * @returns {rotationPoint} the point to rotate around and the angle in radians to rotate 14932 * @private 14933 */ 14934 14935 }, { 14936 key: '_getRotation', 14937 value: function _getRotation(ctx) { 14938 var viaNode = this.edgeType.getViaNode(); 14939 var point = this.edgeType.getPoint(0.5, viaNode); 14940 14941 if (ctx !== undefined) { 14942 this.labelModule.calculateLabelSize(ctx, this.selected, this.hover, point.x, point.y); 14943 } 14944 14945 var ret = { 14946 x: point.x, 14947 y: this.labelModule.size.yLine, 14948 angle: 0 14949 }; 14950 14951 if (!this.labelModule.visible()) { 14952 return ret; // Don't even bother doing the atan2, there's nothing to draw 14953 } 14954 14955 if (this.options.font.align === "horizontal") { 14956 return ret; // No need to calculate angle 14957 } 14958 14959 var dy = this.from.y - this.to.y; 14960 var dx = this.from.x - this.to.x; 14961 var angle = Math.atan2(dy, dx); // radians 14962 14963 // rotate so that label is readable 14964 if (angle < -1 && dx < 0 || angle > 0 && dx < 0) { 14965 angle += Math.PI; 14966 } 14967 ret.angle = angle; 14968 14969 return ret; 14970 } 14971 14972 /** 14973 * Get a point on a circle 14974 * @param {number} x 14975 * @param {number} y 14976 * @param {number} radius 14977 * @param {number} percentage Value between 0 (line start) and 1 (line end) 14978 * @return {Object} point 14979 * @private 14980 */ 14981 14982 }, { 14983 key: '_pointOnCircle', 14984 value: function _pointOnCircle(x, y, radius, percentage) { 14985 var angle = percentage * 2 * Math.PI; 14986 return { 14987 x: x + radius * Math.cos(angle),
14988 y: y - radius * Math.sin(angle) 14989 }; 14990 } 14991 14992 /** 14993 * Sets selected state to true 14994 */ 14995 14996 }, { 14997 key: 'select', 14998 value: function select() { 14999 this.selected = true; 15000 } 15001 15002 /** 15003 * Sets selected state to false 15004 */ 15005 15006 }, { 15007 key: 'unselect', 15008 value: function unselect() { 15009 this.selected = false; 15010 } 15011 15012 /** 15013 * cleans all required things on delete 15014 * @returns {*} 15015 */ 15016 15017 }, { 15018 key: 'cleanup', 15019 value: function cleanup() { 15020 return this.edgeType.cleanup(); 15021 } 15022 15023 /** 15024 * Remove edge from the list and perform necessary cleanup. 15025 */ 15026 15027 }, { 15028 key: 'remove', 15029 value: function remove() { 15030 this.cleanup(); 15031 this.disconnect(); 15032 delete this.body.edges[this.id]; 15033 } 15034 15035 /** 15036 * Check if both connecting nodes exist 15037 * @returns {boolean} 15038 */ 15039 15040 }, { 15041 key: 'endPointsValid', 15042 value: function endPointsValid() { 15043 return this.body.nodes[this.fromId] !== undefined && this.body.nodes[this.toId] !== undefined; 15044 } 15045 }], [{ 15046 key: 'parseOptions', 15047 value: function parseOptions(parentOptions, newOptions) { 15048 var allowDeletion = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false; 15049 var globalOptions = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {}; 15050 var copyFromGlobals = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : false; 15051 15052 var fields = ['arrowStrikethrough', 'id', 'from', 'hidden', 'hoverWidth', 'labelHighlightBold', 'length', 'line', 'opacity', 'physics', 'scaling', 'selectionWidth', 'selfReferenceSize', 'to', 'title', 'value', 'width', 'font', 'chosen', 'widthConstraint']; 15053 15054 // only deep extend the items in the field array. These do not have shorthand. 15055 util.selectiveDeepExtend(fields, parentOptions, newOptions, allowDeletion); 15056 15057 // Only copy label if it's a legal value. 15058 if (ComponentUtil.isValidLabel(newOptions.label)) { 15059 parentOptions.label = newOptions.label; 15060 } else { 15061 parentOptions.label = undefined; 15062 } 15063 15064 util.mergeOptions(parentOptions, newOptions, 'smooth', globalOptions); 15065 util.mergeOptions(parentOptions, newOptions, 'shadow', globalOptions); 15066 15067 if (newOptions.dashes !== undefined && newOptions.dashes !== null) { 15068 parentOptions.dashes = newOptions.dashes; 15069 } else if (allowDeletion === true && newOptions.dashes === null) { 15070 parentOptions.dashes = (0, _create2['default'])(globalOptions.dashes); // this sets the pointer of the option back to the global option. 15071 } 15072 15073 // set the scaling newOptions 15074 if (newOptions.scaling !== undefined && newOptions.scaling !== null) { 15075 if (newOptions.scaling.min !== undefined) { 15076 parentOptions.scaling.min = newOptions.scaling.min; 15077 } 15078 if (newOptions.scaling.max !== undefined) { 15079 parentOptions.scaling.max = newOptions.scaling.max; 15080 } 15081 util.mergeOptions(parentOptions.scaling, newOptions.scaling, 'label', globalOptions.scaling); 15082 } else if (allowDeletion === true && newOptions.scaling === null) { 15083 parentOptions.scaling = (0, _create2['default'])(globalOptions.scaling); // this sets the pointer of the option back to the global option. 15084 } 15085 15086 // handle multiple input cases for arrows 15087 if (newOptions.arrows !== undefined && newOptions.arrows !== null) { 15088 if (typeof newOptions.arrows === 'string') { 15089 var arrows = newOptions.arrows.toLowerCase(); 15090 parentOptions.arrows.to.enabled = arrows.indexOf("to") != -1; 15091 parentOptions.arrows.middle.enabled = arrows.indexOf("middle") != -1; 15092 parentOptions.arrows.from.enabled = arrows.indexOf("from") != -1; 15093 } else if ((0, _typeof3['default'])(newOptions.arrows) === 'object') { 15094 util.mergeOptions(parentOptions.arrows, newOptions.arrows, 'to', globalOptions.arrows); 15095 util.mergeOptions(parentOptions.arrows, newOptions.arrows, 'middle', globalOptions.arrows); 15096 util.mergeOptions(parentOptions.arrows, newOptions.arrows, 'from', globalOptions.arrows); 15097 } else { 15098 throw new Error("The arrow newOptions can only be an object or a string. Refer to the documentation. You used:" + (0, _stringify2['default'])(newOptions.arrows)); 15099 } 15100 } else if (allowDeletion === true && newOptions.arrows === null) { 15101 parentOptions.arrows = (0, _create2['default'])(globalOptions.arrows); // this sets the pointer of the option back to the global option. 15102 } 15103 15104 // handle multiple input cases for color 15105 if (newOptions.color !== undefined && newOptions.color !== null) { 15106 var fromColor = newOptions.color; 15107 var toColor = parentOptions.color; 15108 15109 // If passed, fill in values from default options - required in the case of no prototype bridging 15110 if (copyFromGlobals) { 15111 util.deepExtend(toColor, globalOptions.color, false, allowDeletion); 15112 } else { 15113 // Clear local properties - need to do it like this in order to retain prototype bridges 15114 for (var i in toColor) { 15115 if (toColor.hasOwnProperty(i)) { 15116 delete toColor[i]; 15117 } 15118 } 15119 } 15120 15121 if (util.isString(toColor)) { 15122 toColor.color = toColor; 15123 toColor.highlight = toColor; 15124 toColor.hover = toColor; 15125 toColor.inherit = false;
15126 if (fromColor.opacity === undefined) { 15127 toColor.opacity = 1.0; // set default 15128 } 15129 } else { 15130 var colorsDefined = false; 15131 if (fromColor.color !== undefined) { 15132 toColor.color = fromColor.color;colorsDefined = true; 15133 } 15134 if (fromColor.highlight !== undefined) { 15135 toColor.highlight = fromColor.highlight;colorsDefined = true; 15136 } 15137 if (fromColor.hover !== undefined) { 15138 toColor.hover = fromColor.hover;colorsDefined = true; 15139 } 15140 if (fromColor.inherit !== undefined) { 15141 toColor.inherit = fromColor.inherit; 15142 } 15143 if (fromColor.opacity !== undefined) { 15144 toColor.opacity = Math.min(1, Math.max(0, fromColor.opacity)); 15145 } 15146 15147 if (colorsDefined === true) { 15148 toColor.inherit = false; 15149 } else { 15150 if (toColor.inherit === undefined) { 15151 toColor.inherit = 'from'; // Set default 15152 } 15153 } 15154 } 15155 } else if (allowDeletion === true && newOptions.color === null) { 15156 parentOptions.color = util.bridgeObject(globalOptions.color); // set the object back to the global options 15157 } 15158 15159 if (allowDeletion === true && newOptions.font === null) { 15160 parentOptions.font = util.bridgeObject(globalOptions.font); // set the object back to the global options 15161 } 15162 } 15163 }]); 15164 return Edge; 15165}(); 15166 15167exports['default'] = Edge; 15168 15169/***/ }), 15170/* 75 */ 15171/***/ (function(module, exports, __webpack_require__) { 15172 15173"use strict"; 15174 15175 15176Object.defineProperty(exports, "__esModule", { 15177 value: true 15178}); 15179 15180var _getPrototypeOf = __webpack_require__(3); 15181 15182var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf); 15183 15184var _classCallCheck2 = __webpack_require__(0); 15185 15186var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); 15187 15188var _createClass2 = __webpack_require__(1); 15189 15190var _createClass3 = _interopRequireDefault(_createClass2); 15191 15192var _possibleConstructorReturn2 = __webpack_require__(4); 15193 15194var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2); 15195 15196var _inherits2 = __webpack_require__(5); 15197 15198var _inherits3 = _interopRequireDefault(_inherits2); 15199 15200var _EdgeBase2 = __webpack_require__(118); 15201 15202var _EdgeBase3 = _interopRequireDefault(_EdgeBase2); 15203 15204function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 15205 15206/** 15207 * The Base Class for all Bezier edges. Bezier curves are used to model smooth 15208 * gradual curves in paths between nodes. 15209 * 15210 * @extends EdgeBase 15211 */ 15212var BezierEdgeBase = function (_EdgeBase) { 15213 (0, _inherits3['default'])(BezierEdgeBase, _EdgeBase); 15214 15215 /** 15216 * @param {Object} options 15217 * @param {Object} body 15218 * @param {Label} labelModule 15219 */ 15220 function BezierEdgeBase(options, body, labelModule) { 15221 (0, _classCallCheck3['default'])(this, BezierEdgeBase); 15222 return (0, _possibleConstructorReturn3['default'])(this, (BezierEdgeBase.__proto__ || (0, _getPrototypeOf2['default'])(BezierEdgeBase)).call(this, options, body, labelModule)); 15223 } 15224 15225 /** 15226 * This function uses binary search to look for the point where the bezier curve crosses the border of the node. 15227 * 15228 * @param {Node} nearNode 15229 * @param {CanvasRenderingContext2D} ctx 15230 * @param {Node} viaNode 15231 * @returns {*} 15232 * @private 15233 */ 15234 15235 15236 (0, _createClass3['default'])(BezierEdgeBase, [{ 15237 key: '_findBorderPositionBezier', 15238 value: function _findBorderPositionBezier(nearNode, ctx) { 15239 var viaNode = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : this._getViaCoordinates(); 15240 15241 var maxIterations = 10; 15242 var iteration = 0; 15243 var low = 0; 15244 var high = 1; 15245 var pos, angle, distanceToBorder, distanceToPoint, difference; 15246 var threshold = 0.2; 15247 var node = this.to; 15248 var from = false; 15249 if (nearNode.id === this.from.id) { 15250 node = this.from; 15251 from = true; 15252 } 15253 15254 while (low <= high && iteration < maxIterations) { 15255 var middle = (low + high) * 0.5; 15256 15257 pos = this.getPoint(middle, viaNode); 15258 angle = Math.atan2(node.y - pos.y, node.x - pos.x); 15259 distanceToBorder = node.distanceToBorder(ctx, angle); 15260 distanceToPoint = Math.sqrt(Math.pow(pos.x - node.x, 2) + Math.pow(pos.y - node.y, 2)); 15261 difference = distanceToBorder - distanceToPoint; 15262 if (Math.abs(difference) < threshold) { 15263 break; // found 15264 } else if (difference < 0) { 15265 // distance to nodes is larger than distance to border --> t needs to be bigger if we're looking at the to node. 15266 if (from === false) { 15267 low = middle; 15268 } else { 15269 high = middle; 15270 } 15271 } else { 15272 if (from === false) { 15273 high = middle; 15274 } else { 15275 low = middle; 15276 } 15277 } 15278 15279 iteration++; 15280 } 15281 pos.t = middle; 15282 15283 return pos; 15284 } 15285 15286 /** 15287 * Calculate the distance between a point (x3,y3) and a line segment from 15288 * (x1,y1) to (x2,y2). 15289 * http://stackoverflow.com/questions/849211/shortest-distancae-between-a-point-and-a-line-segment 15290 * @param {number} x1 from x 15291 * @param {number} y1 from y 15292 * @param {number} x2 to x 15293 * @param {number} y2 to y 15294 * @param {number} x3 point to check x 15295 * @param {number} y3 point to check y 15296 * @param {Node} via 15297 * @returns {number} 15298 * @private 15299 */ 15300 15301 }, { 15302 key: '_getDistanceToBezierEdge', 15303 value: function _getDistanceToBezierEdge(x1, y1, x2, y2, x3, y3, via) { 15304 // x3,y3 is the point 15305 var minDistance = 1e9; 15306 var distance = void 0; 15307 var i = void 0, 15308 t = void 0, 15309 x = void 0, 15310 y = void 0; 15311 var lastX = x1; 15312 var lastY = y1; 15313 for (i = 1; i < 10; i++) { 15314 t = 0.1 * i; 15315 x = Math.pow(1 - t, 2) * x1 + 2 * t * (1 - t) * via.x + Math.pow(t, 2) * x2; 15316 y = Math.pow(1 - t, 2) * y1 + 2 * t * (1 - t) * via.y + Math.pow(t, 2) * y2; 15317 if (i > 0) { 15318 distance = this._getDistanceToLine(lastX, lastY, x, y, x3, y3); 15319 minDistance = distance < minDistance ? distance : minDistance; 15320 } 15321 lastX = x; 15322 lastY = y; 15323 } 15324 15325 return minDistance; 15326 } 15327 15328 /** 15329 * Draw a bezier curve between two nodes 15330 *
15331 * The method accepts zero, one or two control points. 15332 * Passing zero control points just draws a straight line 15333 * 15334 * @param {CanvasRenderingContext2D} ctx 15335 * @param {Object} values | options for shadow drawing 15336 * @param {Object|undefined} viaNode1 | first control point for curve drawing 15337 * @param {Object|undefined} viaNode2 | second control point for curve drawing 15338 * 15339 * @protected 15340 */ 15341 15342 }, { 15343 key: '_bezierCurve', 15344 value: function _bezierCurve(ctx, values, viaNode1, viaNode2) { 15345 var hasNode1 = viaNode1 !== undefined && viaNode1.x !== undefined; 15346 var hasNode2 = viaNode2 !== undefined && viaNode2.x !== undefined; 15347 15348 ctx.beginPath(); 15349 ctx.moveTo(this.fromPoint.x, this.fromPoint.y); 15350 15351 if (hasNode1 && hasNode2) { 15352 ctx.bezierCurveTo(viaNode1.x, viaNode1.y, viaNode2.x, viaNode2.y, this.toPoint.x, this.toPoint.y); 15353 } else if (hasNode1) { 15354 ctx.quadraticCurveTo(viaNode1.x, viaNode1.y, this.toPoint.x, this.toPoint.y); 15355 } else { 15356 // fallback to normal straight edge 15357 ctx.lineTo(this.toPoint.x, this.toPoint.y); 15358 } 15359 15360 // draw shadow if enabled 15361 this.enableShadow(ctx, values); 15362 ctx.stroke(); 15363 this.disableShadow(ctx, values); 15364 } 15365 15366 /** 15367 * 15368 * @returns {*|{x, y}|{x: undefined, y: undefined}} 15369 */ 15370 15371 }, { 15372 key: 'getViaNode', 15373 value: function getViaNode() { 15374 return this._getViaCoordinates(); 15375 } 15376 }]); 15377 return BezierEdgeBase; 15378}(_EdgeBase3['default']); 15379 15380exports['default'] = BezierEdgeBase; 15381 15382/***/ }), 15383/* 76 */ 15384/***/ (function(module, exports, __webpack_require__) { 15385 15386"use strict"; 15387 15388 15389Object.defineProperty(exports, "__esModule", { 15390 value: true 15391}); 15392 15393var _classCallCheck2 = __webpack_require__(0); 15394 15395var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); 15396 15397var _createClass2 = __webpack_require__(1); 15398 15399var _createClass3 = _interopRequireDefault(_createClass2); 15400 15401function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } 15402 15403var util = __webpack_require__(2); 15404 15405/** 15406 * Utility Class 15407 */ 15408 15409var NetworkUtil = function () { 15410 /** 15411 * @ignore 15412 */ 15413 function NetworkUtil() { 15414 (0, _classCallCheck3["default"])(this, NetworkUtil); 15415 } 15416 15417 /** 15418 * Find the center position of the network considering the bounding boxes 15419 * 15420 * @param {Array.<Node>} allNodes 15421 * @param {Array.<Node>} [specificNodes=[]] 15422 * @returns {{minX: number, maxX: number, minY: number, maxY: number}} 15423 * @static 15424 */ 15425 15426 15427 (0, _createClass3["default"])(NetworkUtil, null, [{ 15428 key: "getRange", 15429 value: function getRange(allNodes) { 15430 var specificNodes = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : []; 15431 15432 var minY = 1e9, 15433 maxY = -1e9, 15434 minX = 1e9, 15435 maxX = -1e9, 15436 node; 15437 if (specificNodes.length > 0) { 15438 for (var i = 0; i < specificNodes.length; i++) { 15439 node = allNodes[specificNodes[i]]; 15440 if (minX > node.shape.boundingBox.left) { 15441 minX = node.shape.boundingBox.left; 15442 } 15443 if (maxX < node.shape.boundingBox.right) { 15444 maxX = node.shape.boundingBox.right; 15445 } 15446 if (minY > node.shape.boundingBox.top) { 15447 minY = node.shape.boundingBox.top; 15448 } // top is negative, bottom is positive 15449 if (maxY < node.shape.boundingBox.bottom) { 15450 maxY = node.shape.boundingBox.bottom; 15451 } // top is negative, bottom is positive 15452 } 15453 } 15454 15455 if (minX === 1e9 && maxX === -1e9 && minY === 1e9 && maxY === -1e9) { 15456 minY = 0, maxY = 0, minX = 0, maxX = 0; 15457 } 15458 return { minX: minX, maxX: maxX, minY: minY, maxY: maxY }; 15459 } 15460 15461 /** 15462 * Find the center position of the network 15463 * 15464 * @param {Array.<Node>} allNodes 15465 * @param {Array.<Node>} [specificNodes=[]] 15466 * @returns {{minX: number, maxX: number, minY: number, maxY: number}} 15467 * @static 15468 */ 15469 15470 }, { 15471 key: "getRangeCore", 15472 value: function getRangeCore(allNodes) { 15473 var specificNodes = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : []; 15474 15475 var minY = 1e9, 15476 maxY = -1e9, 15477 minX = 1e9, 15478 maxX = -1e9, 15479 node; 15480 if (specificNodes.length > 0) { 15481 for (var i = 0; i < specificNodes.length; i++) { 15482 node = allNodes[specificNodes[i]]; 15483 if (minX > node.x) { 15484 minX = node.x; 15485 } 15486 if (maxX < node.x) { 15487 maxX = node.x; 15488 } 15489 if (minY > node.y) { 15490 minY = node.y; 15491 } // top is negative, bottom is positive 15492 if (maxY < node.y) { 15493 maxY = node.y; 15494 } // top is negative, bottom is positive 15495 } 15496 } 15497 15498 if (minX === 1e9 && maxX === -1e9 && minY === 1e9 && maxY === -1e9) { 15499 minY = 0, maxY = 0, minX = 0, maxX = 0; 15500 } 15501 return { minX: minX, maxX: maxX, minY: minY, maxY: maxY }; 15502 } 15503 15504 /** 15505 * @param {object} range = {minX: minX, maxX: maxX, minY: minY, maxY: maxY}; 15506 * @returns {{x: number, y: number}} 15507 * @static 15508 */ 15509 15510 }, { 15511 key: "findCenter", 15512 value: function findCenter(range) { 15513 return { x: 0.5 * (range.maxX + range.minX), 15514 y: 0.5 * (range.maxY + range.minY) }; 15515 } 15516 15517 /** 15518 * This returns a clone of the options or options of the edge or node to be used for c
vendor: 4,197 bytes, lines 15518-15643
15518onstruction of new edges or check functions for new nodes. 15519 * @param {vis.Item} item 15520 * @param {'node'|undefined} type 15521 * @returns {{}} 15522 * @static 15523 */ 15524 15525 }, { 15526 key: "cloneOptions", 15527 value: function cloneOptions(item, type) { 15528 var clonedOptions = {}; 15529 if (type === undefined || type === 'node') { 15530 util.deepExtend(clonedOptions, item.options, true); 15531 clonedOptions.x = item.x; 15532 clonedOptions.y = item.y; 15533 clonedOptions.amountOfConnections = item.edges.length; 15534 } else { 15535 util.deepExtend(clonedOptions, item.options, true); 15536 } 15537 return clonedOptions; 15538 } 15539 }]); 15540 return NetworkUtil; 15541}(); 15542 15543exports["default"] = NetworkUtil; 15544 15545/***/ }), 15546/* 77 */ 15547/***/ (function(module, exports, __webpack_require__) { 15548 15549module.exports = { "default": __webpack_require__(124), __esModule: true }; 15550 15551/***/ }), 15552/* 78 */ 15553/***/ (function(module, exports, __webpack_require__) { 15554 15555// fallback for non-array-like ES3 and non-enumerable old V8 strings 15556var cof = __webpack_require__(50); 15557// eslint-disable-next-line no-prototype-builtins 15558module.exports = Object('z').propertyIsEnumerable(0) ? Object : function (it) { 15559 return cof(it) == 'String' ? it.split('') : Object(it); 15560}; 15561 15562 15563/***/ }), 15564/* 79 */ 15565/***/ (function(module, exports, __webpack_require__) { 15566 15567"use strict"; 15568 15569var LIBRARY = __webpack_require__(52); 15570var $export = __webpack_require__(17); 15571var redefine = __webpack_require__(83); 15572var hide = __webpack_require__(26); 15573var has = __webpack_require__(22); 15574var Iterators = __webpack_require__(31); 15575var $iterCreate = __webpack_require__(129); 15576var setToStringTag = __webpack_require__(59); 15577var getPrototypeOf = __webpack_require__(85); 15578var ITERATOR = __webpack_require__(13)('iterator'); 15579var BUGGY = !([].keys && 'next' in [].keys()); // Safari has buggy iterators w/o `next` 15580var FF_ITERATOR = '@@iterator'; 15581var KEYS = 'keys'; 15582var VALUES = 'values'; 15583 15584var returnThis = function () { return this; }; 15585 15586module.exports = function (Base, NAME, Constructor, next, DEFAULT, IS_SET, FORCED) { 15587 $iterCreate(Constructor, NAME, next); 15588 var getMethod = function (kind) { 15589 if (!BUGGY && kind in proto) return proto[kind]; 15590 switch (kind) { 15591 case KEYS: return function keys() { return new Constructor(this, kind); }; 15592 case VALUES: return function values() { return new Constructor(this, kind); }; 15593 } return function entries() { return new Constructor(this, kind); }; 15594 }; 15595 var TAG = NAME + ' Iterator'; 15596 var DEF_VALUES = DEFAULT == VALUES; 15597 var VALUES_BUG = false; 15598 var proto = Base.prototype; 15599 var $native = proto[ITERATOR] || proto[FF_ITERATOR] || DEFAULT && proto[DEFAULT]; 15600 var $default = $native || getMethod(DEFAULT); 15601 var $entries = DEFAULT ? !DEF_VALUES ? $default : getMethod('entries') : undefined; 15602 var $anyNative = NAME == 'Array' ? proto.entries || $native : $native; 15603 var methods, key, IteratorPrototype; 15604 // Fix native 15605 if ($anyNative) { 15606 IteratorPrototype = getPrototypeOf($anyNative.call(new Base())); 15607 if (IteratorPrototype !== Object.prototype && IteratorPrototype.next) { 15608 // Set @@toStringTag to native iterators 15609 setToStringTag(IteratorPrototype, TAG, true); 15610 // fix for some old engines 15611 if (!LIBRARY && !has(IteratorPrototype, ITERATOR)) hide(IteratorPrototype, ITERATOR, returnThis); 15612 } 15613 } 15614 // fix Array#{values, @@iterator}.name in V8 / FF 15615 if (DEF_VALUES && $native && $native.name !== VALUES) { 15616 VALUES_BUG = true; 15617 $default = function values() { return $native.call(this); }; 15618 } 15619 // Define iterator 15620 if ((!LIBRARY || FORCED) && (BUGGY || VALUES_BUG || !proto[ITERATOR])) { 15621 hide(proto, ITERATOR, $default); 15622 } 15623 // Plug for library 15624 Iterators[NAME] = $default; 15625 Iterators[TAG] = returnThis; 15626 if (DEFAULT) { 15627 methods = { 15628 values: DEF_VALUES ? $default : getMethod(VALUES), 15629 keys: IS_SET ? $default : getMethod(KEYS), 15630 entries: $entries 15631 }; 15632 if (FORCED) for (key in methods) { 15633 if (!(key in proto)) redefine(proto, key, methods[key]); 15634 } else $export($export.P + $export.F * (BUGGY || VALUES_BUG), NAME, methods); 15635 } 15636 return methods; 15637}; 15638 15639 15640/***/ }), 15641/* 80 */ 15642/***/ (function(module, exports, __webpack_require__) { 15643
15644// optional / simple context binding 15645var aFunction = __webpack_require__(128); 15646module.exports = function (fn, that, length) { 15647 aFunction(fn); 15648 if (that === undefined) return fn; 15649 switch (length) { 15650 case 1: return function (a) { 15651 return fn.call(that, a); 15652 }; 15653 case 2: return function (a, b) { 15654 return fn.call(that, a, b); 15655 }; 15656 case 3: return function (a, b, c) { 15657 return fn.call(that, a, b, c); 15658 }; 15659 } 15660 return function (/* ...args */) { 15661 return fn.apply(that, arguments); 15662 }; 15663}; 15664 15665 15666/***/ }), 15667/* 81 */ 15668/***/ (function(module, exports, __webpack_require__) { 15669 15670module.exports = !__webpack_require__(21) && !__webpack_require__(28)(function () { 15671 return Object.defineProperty(__webpack_require__(82)('div'), 'a', { get: function () { return 7; } }).a != 7; 15672}); 15673 15674 15675/***/ }), 15676/* 82 */ 15677/***/ (function(module, exports, __webpack_require__) { 15678 15679var isObject = __webpack_require__(32); 15680var document = __webpack_require__(18).document; 15681// typeof document.createElement is 'object' in old IE 15682var is = isObject(document) && isObject(document.createElement); 15683module.exports = function (it) { 15684 return is ? document.createElement(it) : {}; 15685}; 15686 15687 15688/***/ }), 15689/* 83 */ 15690/***/ (function(module, exports, __webpack_require__) { 15691 15692module.exports = __webpack_require__(26); 15693 15694 15695/***/ }), 15696/* 84 */ 15697/***/ (function(module, exports, __webpack_require__) { 15698 15699var has = __webpack_require__(22); 15700var toIObject = __webpack_require__(25); 15701var arrayIndexOf = __webpack_require__(131)(false); 15702var IE_PROTO = __webpack_require__(56)('IE_PROTO'); 15703 15704module.exports = function (object, names) { 15705 var O = toIObject(object); 15706 var i = 0; 15707 var result = []; 15708 var key; 15709 for (key in O) if (key != IE_PROTO) has(O, key) && result.push(key); 15710 // Don't enum bug & hidden keys 15711 while (names.length > i) if (has(O, key = names[i++])) { 15712 ~arrayIndexOf(result, key) || result.push(key); 15713 } 15714 return result; 15715}; 15716 15717 15718/***/ }), 15719/* 85 */ 15720/***/ (function(module, exports, __webpack_require__) { 15721 15722// 19.1.2.9 / 15.2.3.2 Object.getPrototypeOf(O) 15723var has = __webpack_require__(22); 15724var toObject = __webpack_require__(41); 15725var IE_PROTO = __webpack_require__(56)('IE_PROTO'); 15726var ObjectProto = Object.prototype; 15727 15728module.exports = Object.getPrototypeOf || function (O) { 15729 O = toObject(O); 15730 if (has(O, IE_PROTO)) return O[IE_PROTO]; 15731 if (typeof O.constructor == 'function' && O instanceof O.constructor) { 15732 return O.constructor.prototype; 15733 } return O instanceof Object ? ObjectProto : null; 15734}; 15735 15736 15737/***/ }), 15738/* 86 */ 15739/***/ (function(module, exports, __webpack_require__) { 15740 15741// getting tag from 19.1.3.6 Object.prototype.toString() 15742var cof = __webpack_require__(50); 15743var TAG = __webpack_require__(13)('toStringTag'); 15744// ES3 wrong here 15745var ARG = cof(function () { return arguments; }()) == 'Arguments'; 15746 15747// fallback for IE11 Script Access Denied error 15748var tryGet = function (it, key) { 15749 try { 15750 return it[key]; 15751 } catch (e) { /* empty */ } 15752}; 15753 15754module.exports = function (it) { 15755 var O, T, B; 15756 return it === undefined ? 'Undefined' : it === null ? 'Null' 15757 // @@toStringTag case 15758 : typeof (T = tryGet(O = Object(it), TAG)) == 'string' ? T 15759 // builtinTag case 15760 : ARG ? cof(O) 15761 // ES3 arguments fallback 15762 : (B = cof(O)) == 'Object' && typeof O.callee == 'function' ? 'Arguments' : B; 15763}; 15764 15765 15766/***/ }), 15767/* 87 */ 15768/***/ (function(module, exports, __webpack_require__) { 15769 15770// most Object methods by ES6 should accept primitives 15771var $export = __webpack_require__(17); 15772var core = __webpack_require__(7); 15773var fails = __webpack_require__(28); 15774module.exports = function (KEY, exec) { 15775 var fn = (core.Object || {})[KEY] || Object[KEY]; 15776 var exp = {}; 15777 exp[KEY] = exec(fn); 15778 $export($export.S + $export.F * fails(function () { fn(1); }), 'Object', exp); 15779}; 15780 15781 15782/***/ }), 15783/* 88 */ 15784/***/ (function(module, exports, __webpack_require__) { 15785 15786// 19.1.2.7 / 15.2.3.4 Object.getOwnPropertyNames(O) 15787var $keys = __webpack_require__(84); 15788var hiddenKeys = __webpack_require__(58).concat('length', 'prototype'); 15789 15790exports.f = Object.getOwnPropertyNames || function getOwnPropertyNames(O) { 15791 return $keys(O, hiddenKeys); 15792}; 15793 15794 15795/***/ }), 15796/* 89 */ 15797/***/ (function(module, exports, __webpack_require__) { 15798 15799var pIE = __webpack_require__(42); 15800var createDesc = __webpack_require__(39); 15801var toIObject = __webpack_require__(25); 15802var toPrimitive = __webpack_require__(53); 15803var has = __webpack_require__(22); 15804var IE8_DOM_DEFINE = __webpack_require__(81); 15805var gOPD = Object.getOwnPropertyDescriptor; 15806 15807exports.f = __webpack_require__(21) ? gOPD : function getOwnPropertyDescriptor(O, P) { 15808 O = toIObject(O); 15809 P = toPrimitive(P, true); 15810 if (IE8_DOM_DEFINE) try { 15811 return gOPD(O, P); 15812 } catch (e) { /* empty */ } 15813 if (has(O, P)) return createDesc(!pIE.f.call(O, P), O[P]); 15814}; 15815 15816 15817/***/ }), 15818/* 90 */ 15819/***/ (function(module, exports, __webpack_require__) { 15820 15821module.exports = { "default": __webpack_require__(162), __esModule: true }; 15822 15823/***/ }), 15824/* 91 */ 15825/***/ (function(module, exports, __webpack_require__) { 15826 15827"use strict"; 15828 15829 15830/** 15831 * @prototype Point2d 15832 * @param {number} [x] 15833 * @param {number} [y] 15834 */ 15835function Point2d(x, y) { 15836 this.x = x !== undefined ? x : 0; 15837 this.y = y !== undefined ? y : 0; 15838} 15839 15840module.exports = Point2d; 15841 15842/***/ }), 15843/* 92 */ 15844/***/ (function(module, exports, __webpack_require__) { 15845 15846"use strict"; 15847 15848 15849var util = __webpack_require__(2); 15850 15851/** 15852 * An html slider control with start/stop/prev/next buttons 15853 * 15854 * @constructor Slider 15855 * @param {Element} container The element where the slider will be created 15856 * @param {Object} options Available options: 15857 * {boolean} visible If true (default) the 15858 * slider is visible. 15859 */ 15860function Slider(container, options) { 15861 if (container === undefined) { 15862 throw new Error('No container element defined'); 15863 } 15864 this.container = container; 15865 this.visible = options && options.visible != undefined ? options.visible : true; 15866 15867 if (this.visible) { 15868 this.frame = document.createElement('DIV'); 15869 //this.frame.style.backgroundColor = '#E5E5E5'; 15870 this.frame.style.width = '100%'; 15871 this.frame.style.position = 'relative'; 15872 this.container.appendChild(this.frame); 15873 15874 this.frame.prev = document.createElement('INPUT'); 15875 this.frame.prev.type = 'BUTTON'; 15876 this.frame.prev.value = 'Prev'; 15877 this.frame.appendChild(this.frame.prev); 15878 15879 this.frame.play = document.createElement('INPUT'); 15880 this.frame.play.type = 'BUTTON'; 15881 this.frame.play.value = 'Play'; 15882 this.frame.appendChild(this.frame.play); 15883 15884 this.frame.next = document.createElement('INPUT'); 15885 this.frame.next.type = 'BUTTON'; 15886 this.frame.next.value = 'Next'; 15887 this.frame.appendChild(this.frame.next); 15888 15889 this.frame.bar = document.createElement('INPUT'); 15890 this.frame.bar.type = 'BUTTON'; 15891 this.frame.bar.style.position = 'absolute'; 15892 this.frame.bar.style.border = '1px solid red'; 15893 this.frame.bar.style.width = '100px'; 15894 this.frame.bar.style.height = '6px'; 15895 this.frame.bar.style.borderRadius = '2px'; 15896 this.frame.bar.style.MozBorderRadius = '2px'; 15897 this.frame.bar.style.border = '1px solid #7F7F7F'; 15898 this.frame.bar.style.backgroundColor = '#E5E5E5'; 15899 this.frame.appendChild(this.frame.bar); 15900 15901 this.frame.slide = document.createElement('INPUT'); 15902 this.frame.slide.type = 'BUTTON'; 15903 this.frame.slide.style.margin = '0px'; 15904 this.frame.slide.value = ' '; 15905 this.frame.slide.style.position = 'relative'; 15906 this.frame.slide.style.left = '-100px'; 15907 this.frame.appendChild(this.frame.slide); 15908 15909 // create events 15910 var me = this; 15911 this.frame.slide.onmousedown = function (event) { 15912 me._onMouseDown(event); 15913 }; 15914 this.frame.prev.onclick = function (event) { 15915 me.prev(event); 15916 }; 15917 this.frame.play.onclick = function (event) { 15918 me.togglePlay(event); 15919 }; 15920 this.frame.next.onclick = function (event) { 15921 me.next(event); 15922 }; 15923 } 15924 15925 this.onChangeCallback = undefined; 15926 15927 this.values = []; 15928 this.index = undefined; 15929 15930 this.playTimeout = undefined; 15931 this.playInterval = 1000; // milliseconds 15932 this.playLoop = true; 15933} 15934 15935/** 15936 * Select the previous index 15937 */ 15938Slider.prototype.prev = function () { 15939 var index = this.getIndex(); 15940 if (index > 0) { 15941 index--; 15942 this.setIndex(index); 15943 } 15944}; 15945 15946/** 15947 * Select the next index 15948 */ 15949Slider.prototype.next = function () { 15950 var index = this.getIndex(); 15951 if (index < this.values.length - 1) { 15952 index++; 15953 this.setIndex(index); 15954 } 15955}; 15956 15957/** 15958 * Select the next index 15959 */ 15960Slider.prototype.playNext = function () { 15961 var start = new Date(); 15962 15963 var index = this.getIndex(); 15964 if (index < this.values.length - 1) { 15965 index++; 15966 this.setIndex(index); 15967 } else if (this.playLoop) { 15968 // jump to the start 15969 index = 0; 15970 this.setIndex(index); 15971 } 15972 15973 var end = new Date(); 15974 var diff = end - start; 15975 15976 // calculate how much time it to to set the index and to execute the callback 15977 // function. 15978 var interval = Math.max(this.playInterval - diff, 0); 15979 // document.title = diff // TODO: cleanup 15980 15981 var me = this; 15982 this.playTimeout = setTimeout(function () { 15983 me.playNext(); 15984 }, interval); 15985}; 15986 15987/** 15988 * Toggle start or stop playing 15989 */ 15990Slider.prototype.togglePlay = function () { 15991 if (this.playTimeout === undefined) { 15992 this.play(); 15993 } else { 15994 this.stop(); 15995 } 15996}; 15997 15998/** 15999 * Start playing 16000 */ 16001Slider.prototype.play = function () { 16002 // Test whether already playing 16003 if (this.playTimeout) return; 16004 16005 this.playNext(); 16006 16007 if (this.frame) { 16008 this.frame.play.value = 'Stop'; 16009 } 16010}; 16011 16012/** 16013 * Stop playing 16014 */ 16015Slider.prototype.stop = function () { 16016 clearInterval(this.playTimeout); 16017 this.playTimeout = undefined; 16018 16019 if (this.frame) { 16020 this.frame.play.value = 'Play'; 16021 } 16022}; 16023 16024/** 16025 * Set a callback function which will be triggered when the value of the 16026 * slider bar has changed. 16027 * 16028 * @param {function} callback 16029 */ 16030Slider.prototype.setOnChangeCallback = function (callback) { 16031 this.onChangeCallback = callback; 16032}; 16033 16034/** 16035 * Set the interval for playing the list 16036 * @param {number} interval The interval in milliseconds 16037 */ 16038Slider.prototype.setPlayInterval = function (interval) { 16039 this.playInterval = interval; 16040}; 16041 16042/** 16043 * Retrieve the current play interval 16044 * @return {number} interval The interval in milliseconds 16045 */ 16046Slider.prototype.getPlayInterval = function () { 16047 return this.playInterval; 16048}; 16049 16050/** 16051 * Set looping on or off 16052 * @param {boolean} doLoop If true, the slider will jump to the start when 16053 * the end is passed, and will jump to the end 16054 * when the start is passed. 16055 * 16056 */ 16057Slider.prototype.setPlayLoop = function (doLoop) { 16058 this.playLoop = doLoop; 16059}; 16060 16061/**
16062 * Execute the onchange callback function 16063 */ 16064Slider.prototype.onChange = function () { 16065 if (this.onChangeCallback !== undefined) { 16066 this.onChangeCallback(); 16067 } 16068}; 16069 16070/** 16071 * redraw the slider on the correct place 16072 */ 16073Slider.prototype.redraw = function () { 16074 if (this.frame) { 16075 // resize the bar 16076 this.frame.bar.style.top = this.frame.clientHeight / 2 - this.frame.bar.offsetHeight / 2 + 'px'; 16077 this.frame.bar.style.width = this.frame.clientWidth - this.frame.prev.clientWidth - this.frame.play.clientWidth - this.frame.next.clientWidth - 30 + 'px'; 16078 16079 // position the slider button 16080 var left = this.indexToLeft(this.index); 16081 this.frame.slide.style.left = left + 'px'; 16082 } 16083}; 16084 16085/** 16086 * Set the list with values for the slider 16087 * @param {Array} values A javascript array with values (any type) 16088 */ 16089Slider.prototype.setValues = function (values) { 16090 this.values = values; 16091 16092 if (this.values.length > 0) this.setIndex(0);else this.index = undefined; 16093}; 16094 16095/** 16096 * Select a value by its index 16097 * @param {number} index 16098 */ 16099Slider.prototype.setIndex = function (index) { 16100 if (index < this.values.length) { 16101 this.index = index; 16102 16103 this.redraw(); 16104 this.onChange(); 16105 } else { 16106 throw new Error('Index out of range'); 16107 } 16108}; 16109 16110/** 16111 * retrieve the index of the currently selected vaue 16112 * @return {number} index 16113 */ 16114Slider.prototype.getIndex = function () { 16115 return this.index; 16116}; 16117 16118/** 16119 * retrieve the currently selected value 16120 * @return {*} value 16121 */ 16122Slider.prototype.get = function () { 16123 return this.values[this.index]; 16124}; 16125 16126Slider.prototype._onMouseDown = function (event) { 16127 // only react on left mouse button down 16128 var leftButtonDown = event.which ? event.which === 1 : event.button === 1; 16129 if (!leftButtonDown) return; 16130 16131 this.startClientX = event.clientX; 16132 this.startSlideX = parseFloat(this.frame.slide.style.left); 16133 16134 this.frame.style.cursor = 'move'; 16135 16136 // add event listeners to handle moving the contents 16137 // we store the function onmousemove and onmouseup in the graph, so we can 16138 // remove the eventlisteners lateron in the function mouseUp() 16139 var me = this; 16140 this.onmousemove = function (event) { 16141 me._onMouseMove(event); 16142 }; 16143 this.onmouseup = function (event) { 16144 me._onMouseUp(event); 16145 }; 16146 util.addEventListener(document, 'mousemove', this.onmousemove); 16147 util.addEventListener(document, 'mouseup', this.onmouseup); 16148 util.preventDefault(event); 16149}; 16150 16151Slider.prototype.leftToIndex = function (left) { 16152 var width = parseFloat(this.frame.bar.style.width) - this.frame.slide.clientWidth - 10; 16153 var x = left - 3; 16154 16155 var index = Math.round(x / width * (this.values.length - 1)); 16156 if (index < 0) index = 0; 16157 if (index > this.values.length - 1) index = this.values.length - 1; 16158 16159 return index; 16160}; 16161 16162Slider.prototype.indexToLeft = function (index) { 16163 var width = parseFloat(this.frame.bar.style.width) - this.frame.slide.clientWidth - 10; 16164 16165 var x = index / (this.values.length - 1) * width; 16166 var left = x + 3; 16167 16168 return left; 16169}; 16170 16171Slider.prototype._onMouseMove = function (event) { 16172 var diff = event.clientX - this.startClientX; 16173 var x = this.startSlideX + diff; 16174 16175 var index = this.leftToIndex(x); 16176 16177 this.setIndex(index); 16178 16179 util.preventDefault(); 16180}; 16181 16182Slider.prototype._onMouseUp = function (event) { 16183 // eslint-disable-line no-unused-vars 16184 this.frame.style.cursor = 'auto'; 16185 16186 // remove event listeners 16187 util.removeEventListener(document, 'mousemove', this.onmousemove); 16188 util.removeEventListener(document, 'mouseup', this.onmouseup); 16189 16190 util.preventDefault(); 16191}; 16192 16193module.exports = Slider; 16194 16195/***/ }), 16196/* 93 */ 16197/***/ (function(module, exports, __webpack_require__) { 16198 16199"use strict"; 16200 16201 16202/** 16203 * @prototype StepNumber 16204 * The class StepNumber is an iterator for Numbers. You provide a start and end 16205 * value, and a best step size. StepNumber itself rounds to fixed values and 16206 * a finds the step that best fits the provided step. 16207 * 16208 * If prettyStep is true, the step size is chosen as close as possible to the 16209 * provided step, but being a round value like 1, 2, 5, 10, 20, 50, .... 16210 * 16211 * Example usage: 16212 * var step = new StepNumber(0, 10, 2.5, true); 16213 * step.start(); 16214 * while (!step.end()) { 16215 * alert(step.getCurrent()); 16216 * step.next(); 16217 * } 16218 * 16219 * Version: 1.0 16220 * 16221 * @param {number} start The start value 16222 * @param {number} end The end value 16223 * @param {number} step Optional. Step size. Must be a positive value. 16224 * @param {boolean} prettyStep Optional. If true, the step size is rounded 16225 * To a pretty step size (like 1, 2, 5, 10, 20, 50, ...) 16226 */ 16227function StepNumber(start, end, step, prettyStep) { 16228 // set default values 16229 this._start = 0; 16230 this._end = 0; 16231 this._step = 1; 16232 this.prettyStep = true; 16233 this.precision = 5; 16234 16235 this._current = 0; 16236 this.setRange(start, end, step, prettyStep); 16237} 16238 16239/** 16240 * Check for input values, to prevent disasters from happening 16241 * 16242 * Source: http://stackoverflow.com/a/1830844 16243 * 16244 * @param {string} n 16245 * @returns {boolean} 16246 */ 16247StepNumber.prototype.isNumeric = function (n) { 16248 return !isNaN(parseFloat(n)) && isFinite(n); 16249}; 16250 16251/** 16252 * Set a new range: start, end and step. 16253 * 16254 * @param {number} start The start value 16255 * @param {number} end The end value 16256 * @param {number} step Optional. Step size. Must be a positive value. 16257 * @param {boolean} prettyStep Optional. If true, the step size is rounded 16258 * To a pretty step size (like 1, 2, 5, 10, 20, 50, ...) 16259 */ 16260StepNumber.prototype.setRange = function (start, end, step, prettyStep) { 16261 if (!this.isNumeric(start)) { 16262 throw new Error('Parameter \'start\' is not numeric; value: ' + start); 16263 } 16264 if (!this.isNumeric(end)) { 16265 throw new Error('Parameter \'end\' is not numeric; value: ' + start); 16266 } 16267 if (!this.isNumeric(step)) { 16268 throw new Error('Parameter \'step\' is not numeric; value: ' + start); 16269 } 16270 16271 this._start = start ? start : 0; 16272 this._end = end ? end : 0; 16273 16274 this.setStep(step, prettyStep); 16275}; 16276 16277/** 16278 * Set a new step size 16279 * @param {number} step New step size. Must be a positive value 16280 * @param {boolean} prettyStep Optional. If true, the provided step is rounded 16281 * to a pretty step size (like 1, 2, 5, 10, 20, 50, ...) 16282 */ 16283StepNumber.prototype.setStep = function (step, prettyStep) { 16284 if (step === undefined || step <= 0) return; 16285 16286 if (prettyStep !== undefined) this.prettyStep = prettyStep; 16287 16288 if (this.prettyStep === true) this._step = StepNumber.calculatePrettyStep(step);else this._step = step; 16289}; 16290 16291/** 16292 * Calculate a nice step size, closest to the desired step size. 16293 * Returns a value in one of the ranges 1*10^n, 2*10^n, or 5*10^n, where n is an 16294 * integer Number. For example 1, 2, 5, 10, 20, 50, etc... 16295 * @param {number} step Desired step size 16296 * @return {number} Nice step size 16297 */ 16298StepNumber.calculatePrettyStep = function (step) { 16299 var log10 = function log10(x) { 16300 return Math.log(x) / Math.LN10; 16301 }; 16302 16303 // try three steps (multiple of 1, 2, or 5 16304 var step1 = Math.pow(10, Math.round(log10(step))), 16305 step2 = 2 * Math.pow(10, Math.round(log10(step / 2))), 16306 step5 = 5 * Math.pow(10, Math.round(log10(step / 5))); 16307 16308 // choose the best step (closest to minimum step) 16309 var prettyStep = step1; 16310 if (Math.abs(step2 - step) <= Math.abs(prettyStep - step)) prettyStep = step2; 16311 if (Math.abs(step5 - step) <= Math.abs(prettyStep - step)) prettyStep = step5; 16312 16313 // for safety 16314 if (prettyStep <= 0) { 16315 prettyStep = 1; 16316 } 16317 16318 return prettyStep; 16319}; 16320 16321/** 16322 * returns the current value of the step 16323 * @return {number} current value 16324 */ 16325StepNumber.prototype.getCurrent = function () { 16326 return parseFloat(this._current.toPrecision(this.precision)); 16327}; 16328 16329/** 16330 * returns the current step size 16331 * @return {number} current step size 16332 */ 16333StepNumber.prototype.getStep = function () { 16334 return this._step; 16335}; 16336 16337/** 16338 * Set the current to its starting value. 16339 * 16340 * By default, this will be the largest value smaller than start, which 16341 * is a multiple of the step size. 16342 * 16343 * Parameters checkFirst is optional, default false. 16344 * If set to true, move the current value one step if smaller than start. 16345 * 16346 * @param {boolean} [checkFirst=false] 16347 */ 16348StepNumber.prototype.start = function (checkFirst) { 16349 if (checkFirst === undefined) { 16350 checkFirst = false;
16351 } 16352 16353 this._current = this._start - this._start % this._step; 16354 16355 if (checkFirst) { 16356 if (this.getCurrent() < this._start) { 16357 this.next(); 16358 } 16359 } 16360}; 16361 16362/** 16363 * Do a step, add the step size to the current value 16364 */ 16365StepNumber.prototype.next = function () { 16366 this._current += this._step; 16367}; 16368 16369/** 16370 * Returns true whether the end is reached 16371 * @return {boolean} True if the current value has passed the end value. 16372 */ 16373StepNumber.prototype.end = function () { 16374 return this._current > this._end; 16375}; 16376 16377module.exports = StepNumber; 16378 16379/***/ }), 16380/* 94 */ 16381/***/ (function(module, exports, __webpack_require__) { 16382 16383"use strict"; 16384 16385 16386var _typeof2 = __webpack_require__(6); 16387 16388var _typeof3 = _interopRequireDefault(_typeof2); 16389 16390function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 16391 16392//////////////////////////////////////////////////////////////////////////////// 16393// This modules handles the options for Graph3d. 16394// 16395//////////////////////////////////////////////////////////////////////////////// 16396var util = __webpack_require__(2); 16397var Camera = __webpack_require__(95); 16398var Point3d = __webpack_require__(34); 16399 16400// enumerate the available styles 16401var STYLE = { 16402 BAR: 0, 16403 BARCOLOR: 1, 16404 BARSIZE: 2, 16405 DOT: 3, 16406 DOTLINE: 4, 16407 DOTCOLOR: 5, 16408 DOTSIZE: 6, 16409 GRID: 7, 16410 LINE: 8, 16411 SURFACE: 9 16412}; 16413 16414// The string representations of the styles 16415var STYLENAME = { 16416 'dot': STYLE.DOT, 16417 'dot-line': STYLE.DOTLINE, 16418 'dot-color': STYLE.DOTCOLOR, 16419 'dot-size': STYLE.DOTSIZE, 16420 'line': STYLE.LINE, 16421 'grid': STYLE.GRID, 16422 'surface': STYLE.SURFACE, 16423 'bar': STYLE.BAR, 16424 'bar-color': STYLE.BARCOLOR, 16425 'bar-size': STYLE.BARSIZE 16426}; 16427 16428/** 16429 * Field names in the options hash which are of relevance to the user. 16430 * 16431 * Specifically, these are the fields which require no special handling, 16432 * and can be directly copied over. 16433 */ 16434var OPTIONKEYS = ['width', 'height', 'filterLabel', 'legendLabel', 'xLabel', 'yLabel', 'zLabel', 'xValueLabel', 'yValueLabel', 'zValueLabel', 'showXAxis', 'showYAxis', 'showZAxis', 'showGrid', 'showPerspective', 'showShadow', 'keepAspectRatio', 'verticalRatio', 'dotSizeRatio', 'dotSizeMinFraction', 'dotSizeMaxFraction', 'showAnimationControls', 'animationInterval', 'animationPreload', 'animationAutoStart', 'axisColor', 'gridColor', 'xCenter', 'yCenter']; 16435 16436/** 16437 * Field names in the options hash which are of relevance to the user. 16438 * 16439 * Same as OPTIONKEYS, but internally these fields are stored with 16440 * prefix 'default' in the name. 16441 */ 16442var PREFIXEDOPTIONKEYS = ['xBarWidth', 'yBarWidth', 'valueMin', 'valueMax', 'xMin', 'xMax', 'xStep', 'yMin', 'yMax', 'yStep', 'zMin', 'zMax', 'zStep']; 16443 16444// Placeholder for DEFAULTS reference 16445var DEFAULTS = undefined; 16446 16447/** 16448 * Check if given hash is empty. 16449 * 16450 * Source: http://stackoverflow.com/a/679937 16451 * 16452 * @param {object} obj 16453 * @returns {boolean} 16454 */ 16455function isEmpty(obj) { 16456 for (var prop in obj) { 16457 if (obj.hasOwnProperty(prop)) return false; 16458 } 16459 16460 return true; 16461} 16462 16463/** 16464 * Make first letter of parameter upper case. 16465 * 16466 * Source: http://stackoverflow.com/a/1026087 16467 * 16468 * @param {string} str 16469 * @returns {string} 16470 */ 16471function capitalize(str) { 16472 if (str === undefined || str === "" || typeof str != "string") { 16473 return str; 16474 } 16475 16476 return str.charAt(0).toUpperCase() + str.slice(1); 16477} 16478 16479/** 16480 * Add a prefix to a field name, taking style guide into account 16481 * 16482 * @param {string} prefix 16483 * @param {string} fieldName 16484 * @returns {string} 16485 */ 16486function prefixFieldName(prefix, fieldName) { 16487 if (prefix === undefined || prefix === "") { 16488 return fieldName; 16489 } 16490 16491 return prefix + capitalize(fieldName); 16492} 16493 16494/** 16495 * Forcibly copy fields from src to dst in a controlled manner. 16496 * 16497 * A given field in dst will always be overwitten. If this field 16498 * is undefined or not present in src, the field in dst will 16499 * be explicitly set to undefined. 16500 * 16501 * The intention here is to be able to reset all option fields. 16502 * 16503 * Only the fields mentioned in array 'fields' will be handled. 16504 * 16505 * @param {object} src 16506 * @param {object} dst 16507 * @param {array<string>} fields array with names of fields to copy 16508 * @param {string} [prefix] prefix to use for the target fields. 16509 */ 16510function forceCopy(src, dst, fields, prefix) { 16511 var srcKey; 16512 var dstKey; 16513 16514 for (var i = 0; i < fields.length; ++i) { 16515 srcKey = fields[i]; 16516 dstKey = prefixFieldName(prefix, srcKey); 16517 16518 dst[dstKey] = src[srcKey]; 16519 } 16520} 16521 16522/** 16523 * Copy fields from src to dst in a safe and controlled manner. 16524 * 16525 * Only the fields mentioned in array 'fields' will be copied over, 16526 * and only if these are actually defined. 16527 * 16528 * @param {object} src 16529 * @param {object} dst 16530 * @param {array<string>}
16530 fields array with names of fields to copy 16531 * @param {string} [prefix] prefix to use for the target fields. 16532 */ 16533function safeCopy(src, dst, fields, prefix) { 16534 var srcKey; 16535 var dstKey; 16536 16537 for (var i = 0; i < fields.length; ++i) { 16538 srcKey = fields[i]; 16539 if (src[srcKey] === undefined) continue; 16540 16541 dstKey = prefixFieldName(prefix, srcKey); 16542 16543 dst[dstKey] = src[srcKey]; 16544 } 16545} 16546 16547/** 16548 * Initialize dst with the values in src. 16549 * 16550 * src is the hash with the default values. 16551 * A reference DEFAULTS to this hash is stored locally for 16552 * further handling. 16553 * 16554 * For now, dst is assumed to be a Graph3d instance. 16555 * @param {object} src 16556 * @param {object} dst 16557 */ 16558function setDefaults(src, dst) { 16559 if (src === undefined || isEmpty(src)) { 16560 throw new Error('No DEFAULTS passed'); 16561 } 16562 if (dst === undefined) { 16563 throw new Error('No dst passed'); 16564 } 16565 16566 // Remember defaults for future reference 16567 DEFAULTS = src; 16568 16569 // Handle the defaults which can be simply copied over 16570 forceCopy(src, dst, OPTIONKEYS); 16571 forceCopy(src, dst, PREFIXEDOPTIONKEYS, 'default'); 16572 16573 // Handle the more complex ('special') fields 16574 setSpecialSettings(src, dst); 16575 16576 // Following are internal fields, not part of the user settings 16577 dst.margin = 10; // px 16578 dst.showGrayBottom = false; // TODO: this does not work correctly 16579 dst.showTooltip = false; 16580 dst.onclick_callback = null; 16581 dst.eye = new Point3d(0, 0, -1); // TODO: set eye.z about 3/4 of the width of the window? 16582} 16583 16584/** 16585 * 16586 * @param {object} options 16587 * @param {object} dst 16588 */ 16589function setOptions(options, dst) { 16590 if (options === undefined) { 16591 return; 16592 } 16593 if (dst === undefined) { 16594 throw new Error('No dst passed'); 16595 } 16596 16597 if (DEFAULTS === undefined || isEmpty(DEFAULTS)) { 16598 throw new Error('DEFAULTS not set for module Settings'); 16599 } 16600 16601 // Handle the parameters which can be simply copied over 16602 safeCopy(options, dst, OPTIONKEYS); 16603 safeCopy(options, dst, PREFIXEDOPTIONKEYS, 'default'); 16604 16605 // Handle the more complex ('special') fields 16606 setSpecialSettings(options, dst); 16607} 16608 16609/** 16610 * Special handling for certain parameters 16611 * 16612 * 'Special' here means: setting requires more than a simple copy 16613 * 16614 * @param {object} src 16615 * @param {object} dst 16616 */ 16617function setSpecialSettings(src, dst) { 16618 if (src.backgroundColor !== undefined) { 16619 setBackgroundColor(src.backgroundColor, dst); 16620 } 16621 16622 setDataColor(src.dataColor, dst); 16623 setStyle(src.style, dst); 16624 setShowLegend(src.showLegend, dst); 16625 setCameraPosition(src.cameraPosition, dst); 16626 16627 // As special fields go, this is an easy one; just a translation of the name. 16628 // Can't use this.tooltip directly, because that field exists internally 16629 if (src.tooltip !== undefined) { 16630 dst.showTooltip = src.tooltip; 16631 } 16632 if (src.onclick != undefined) { 16633 dst.onclick_callback = src.onclick; 16634 } 16635 16636 if (src.tooltipStyle !== undefined) { 16637 util.selectiveDeepExtend(['tooltipStyle'], dst, src); 16638 } 16639} 16640 16641/** 16642 * Set the value of setting 'showLegend' 16643 * 16644 * This depends on the value of the style fields, so it must be called 16645 * after the style field has been initialized. 16646 * 16647 * @param {boolean} showLegend 16648 * @param {object} dst 16649 */ 16650function setShowLegend(showLegend, dst) { 16651 if (showLegend === undefined) { 16652 // If the default was auto, make a choice for this field 16653 var isAutoByDefault = DEFAULTS.showLegend === undefined; 16654 16655 if (isAutoByDefault) { 16656 // these styles default to having legends 16657 var isLegendGraphStyle = dst.style === STYLE.DOTCOLOR || dst.style === STYLE.DOTSIZE; 16658 16659 dst.showLegend = isLegendGraphStyle; 16660 } else { 16661 // Leave current value as is 16662 } 16663 } else { 16664 dst.showLegend = showLegend; 16665 } 16666} 16667 16668/** 16669 * Retrieve the style index from given styleName 16670 * @param {string} styleName Style name such as 'dot', 'grid', 'dot-line' 16671 * @return {number} styleNumber Enumeration value representing the style, or -1 16672 * when not found 16673 */ 16674function getStyleNumberByName(styleName) { 16675 var number = STYLENAME[styleName]; 16676 16677 if (number === undefined) { 16678 return -1; 16679 } 16680 16681 return number; 16682} 16683 16684/** 16685 * Check if given number is a valid style number. 16686 * 16687 * @param {string | number} style 16688 * @return {boolean} true if valid, false otherwise 16689 */ 16690function checkStyleNumber(style) {
16691 var valid = false; 16692 16693 for (var n in STYLE) { 16694 if (STYLE[n] === style) { 16695 valid = true; 16696 break; 16697 } 16698 } 16699 16700 return valid; 16701} 16702 16703/** 16704 * 16705 * @param {string | number} style 16706 * @param {Object} dst 16707 */ 16708function setStyle(style, dst) { 16709 if (style === undefined) { 16710 return; // Nothing to do 16711 } 16712 16713 var styleNumber; 16714 16715 if (typeof style === 'string') { 16716 styleNumber = getStyleNumberByName(style); 16717 16718 if (styleNumber === -1) { 16719 throw new Error('Style \'' + style + '\' is invalid'); 16720 } 16721 } else { 16722 // Do a pedantic check on style number value 16723 if (!checkStyleNumber(style)) { 16724 throw new Error('Style \'' + style + '\' is invalid'); 16725 } 16726 16727 styleNumber = style; 16728 } 16729 16730 dst.style = styleNumber; 16731} 16732 16733/** 16734 * Set the background styling for the graph 16735 * @param {string | {fill: string, stroke: string, strokeWidth: string}} backgroundColor 16736 * @param {Object} dst 16737 */ 16738function setBackgroundColor(backgroundColor, dst) { 16739 var fill = 'white'; 16740 var stroke = 'gray'; 16741 var strokeWidth = 1; 16742 16743 if (typeof backgroundColor === 'string') { 16744 fill = backgroundColor; 16745 stroke = 'none'; 16746 strokeWidth = 0; 16747 } else if ((typeof backgroundColor === 'undefined' ? 'undefined' : (0, _typeof3['default'])(backgroundColor)) === 'object') { 16748 if (backgroundColor.fill !== undefined) fill = backgroundColor.fill; 16749 if (backgroundColor.stroke !== undefined) stroke = backgroundColor.stroke; 16750 if (backgroundColor.strokeWidth !== undefined) strokeWidth = backgroundColor.strokeWidth; 16751 } else { 16752 throw new Error('Unsupported type of backgroundColor'); 16753 } 16754 16755 dst.frame.style.backgroundColor = fill; 16756 dst.frame.style.borderColor = stroke; 16757 dst.frame.style.borderWidth = strokeWidth + 'px'; 16758 dst.frame.style.borderStyle = 'solid'; 16759} 16760 16761/** 16762 * 16763 * @param {string | Object} dataColor 16764 * @param {Object} dst 16765 */ 16766function setDataColor(dataColor, dst) { 16767 if (dataColor === undefined) { 16768 return; // Nothing to do 16769 } 16770 16771 if (dst.dataColor === undefined) { 16772 dst.dataColor = {}; 16773 } 16774 16775 if (typeof dataColor === 'string') { 16776 dst.dataColor.fill = dataColor; 16777 dst.dataColor.stroke = dataColor; 16778 } else { 16779 if (dataColor.fill) { 16780 dst.dataColor.fill = dataColor.fill; 16781 } 16782 if (dataColor.stroke) { 16783 dst.dataColor.stroke = dataColor.stroke; 16784 } 16785 if (dataColor.strokeWidth !== undefined) { 16786 dst.dataColor.strokeWidth = dataColor.strokeWidth; 16787 } 16788 } 16789} 16790 16791/** 16792 * 16793 * @param {Object} cameraPosition 16794 * @param {Object} dst 16795 */ 16796function setCameraPosition(cameraPosition, dst) { 16797 var camPos = cameraPosition; 16798 if (camPos === undefined) { 16799 return; 16800 } 16801 16802 if (dst.camera === undefined) { 16803 dst.camera = new Camera(); 16804 } 16805 16806 dst.camera.setArmRotation(camPos.horizontal, camPos.vertical); 16807 dst.camera.setArmLength(camPos.distance); 16808} 16809 16810module.exports.STYLE = STYLE; 16811module.exports.setDefaults = setDefaults; 16812module.exports.setOptions = setOptions; 16813module.exports.setCameraPosition = setCameraPosition; 16814 16815/***/ }), 16816/* 95 */ 16817/***/ (function(module, exports, __webpack_require__) { 16818 16819"use strict"; 16820 16821 16822var _sign = __webpack_require__(165); 16823 16824var _sign2 = _interopRequireDefault(_sign); 16825 16826function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 16827 16828var Point3d = __webpack_require__(34); 16829 16830/** 16831 * The camera is mounted on a (virtual) camera arm. The camera arm can rotate 16832 * The camera is always looking in the direction of the origin of the arm. 16833 * This way, the camera always rotates around one fixed point, the location 16834 * of the camera arm. 16835 * 16836 * Documentation: 16837 * http://en.wikipedia.org/wiki/3D_projection 16838 * @class Camera 16839 */ 16840function Camera() { 16841 this.armLocation = new Point3d(); 16842 this.armRotation = {}; 16843 this.armRotation.horizontal = 0; 16844 this.armRotation.vertical = 0; 16845 this.armLength = 1.7; 16846 this.cameraOffset = new Point3d(); 16847 this.offsetMultiplier = 0.6; 16848 16849 this.cameraLocation = new Point3d(); 16850 this.cameraRotation = new Point3d(0.5 * Math.PI, 0, 0); 16851 16852 this.calculateCameraOrientation(); 16853} 16854 16855/** 16856 * Set offset camera in camera coordinates 16857 * @param {number} x offset by camera horisontal 16858 * @param {number} y offset by camera vertical 16859 */ 16860Camera.prototype.setOffset = function (x, y) { 16861 var abs = Math.abs, 16862 sign = _sign2['default'], 16863 mul = this.offsetMultiplier, 16864 border = this.armLength * mul; 16865 16866 if (abs(x) > border) { 16867 x = sign(x) * border; 16868 } 16869 if (abs(y) > border) { 16870 y = sign(y) * border; 16871 } 16872 this.cameraOffset.x = x;
16873 this.cameraOffset.y = y; 16874 this.calculateCameraOrientation(); 16875}; 16876 16877/** 16878 * Get camera offset by horizontal and vertical 16879 * @returns {number} 16880 */ 16881Camera.prototype.getOffset = function () { 16882 return this.cameraOffset; 16883}; 16884 16885/** 16886 * Set the location (origin) of the arm 16887 * @param {number} x Normalized value of x 16888 * @param {number} y Normalized value of y 16889 * @param {number} z Normalized value of z 16890 */ 16891Camera.prototype.setArmLocation = function (x, y, z) { 16892 this.armLocation.x = x; 16893 this.armLocation.y = y; 16894 this.armLocation.z = z; 16895 16896 this.calculateCameraOrientation(); 16897}; 16898 16899/** 16900 * Set the rotation of the camera arm 16901 * @param {number} horizontal The horizontal rotation, between 0 and 2*PI. 16902 * Optional, can be left undefined. 16903 * @param {number} vertical The vertical rotation, between 0 and 0.5*PI 16904 * if vertical=0.5*PI, the graph is shown from the 16905 * top. Optional, can be left undefined. 16906 */ 16907Camera.prototype.setArmRotation = function (horizontal, vertical) { 16908 if (horizontal !== undefined) { 16909 this.armRotation.horizontal = horizontal; 16910 } 16911 16912 if (vertical !== undefined) { 16913 this.armRotation.vertical = vertical; 16914 if (this.armRotation.vertical < 0) this.armRotation.vertical = 0; 16915 if (this.armRotation.vertical > 0.5 * Math.PI) this.armRotation.vertical = 0.5 * Math.PI; 16916 } 16917 16918 if (horizontal !== undefined || vertical !== undefined) { 16919 this.calculateCameraOrientation(); 16920 } 16921}; 16922 16923/** 16924 * Retrieve the current arm rotation 16925 * @return {object} An object with parameters horizontal and vertical 16926 */ 16927Camera.prototype.getArmRotation = function () { 16928 var rot = {}; 16929 rot.horizontal = this.armRotation.horizontal; 16930 rot.vertical = this.armRotation.vertical; 16931 16932 return rot; 16933}; 16934 16935/** 16936 * Set the (normalized) length of the camera arm. 16937 * @param {number} length A length between 0.71 and 5.0 16938 */ 16939Camera.prototype.setArmLength = function (length) { 16940 if (length === undefined) return; 16941 16942 this.armLength = length; 16943 16944 // Radius must be larger than the corner of the graph, 16945 // which has a distance of sqrt(0.5^2+0.5^2) = 0.71 from the center of the 16946 // graph 16947 if (this.armLength < 0.71) this.armLength = 0.71; 16948 if (this.armLength > 5.0) this.armLength = 5.0; 16949 16950 this.setOffset(this.cameraOffset.x, this.cameraOffset.y); 16951 this.calculateCameraOrientation(); 16952}; 16953 16954/** 16955 * Retrieve the arm length 16956 * @return {number} length 16957 */ 16958Camera.prototype.getArmLength = function () { 16959 return this.armLength; 16960}; 16961 16962/** 16963 * Retrieve the camera location 16964 * @return {Point3d} cameraLocation 16965 */ 16966Camera.prototype.getCameraLocation = function () { 16967 return this.cameraLocation; 16968}; 16969 16970/** 16971 * Retrieve the camera rotation 16972 * @return {Point3d} cameraRotation 16973 */ 16974Camera.prototype.getCameraRotation = function () { 16975 return this.cameraRotation; 16976}; 16977 16978/** 16979 * Calculate the location and rotation of the camera based on the 16980 * position and orientation of the camera arm 16981 */ 16982Camera.prototype.calculateCameraOrientation = function () { 16983 // calculate location of the camera 16984 this.cameraLocation.x = this.armLocation.x - this.armLength * Math.sin(this.armRotation.horizontal) * Math.cos(this.armRotation.vertical); 16985 this.cameraLocation.y = this.armLocation.y - this.armLength * Math.cos(this.armRotation.horizontal) * Math.cos(this.armRotation.vertical); 16986 this.cameraLocation.z = this.armLocation.z + this.armLength * Math.sin(this.armRotation.vertical); 16987 16988 // calculate rotation of the camera 16989 this.cameraRotation.x = Math.PI / 2 - this.armRotation.vertical; 16990 this.cameraRotation.y = 0; 16991 this.cameraRotation.z = -this.armRotation.horizontal; 16992 16993 var xa = this.cameraRotation.x; 16994 var za = this.cameraRotation.z; 16995 var dx = this.cameraOffset.x; 16996 var dy = this.cameraOffset.y; 16997 var sin = Math.sin, 16998 cos = Math.cos; 16999 17000 this.cameraLocation.x = this.cameraLocation.x + dx * cos(za) + dy * -sin(za) * cos(xa); 17001 this.cameraLocation.y = this.cameraLocation.y + dx * sin(za) + dy * cos(za) * cos(xa); 17002 this.cameraLocation.z = this.cameraLocation.z + dy * sin(xa); 17003}; 17004 17005module.exports = Camera; 17006 17007/***/ }), 17008/* 96 */ 17009/***/ (function(module, exports, __webpack_require__) { 17010 17011"use strict"; 17012 17013 17014var DataView = __webpack_require__(12); 17015 17016/** 17017 * @class Filter 17018 * 17019 * @param {DataGroup} dataGroup the data group 17020 * @param {number} column The index of the column to be filtered 17021 * @param {Graph3d} graph The graph 17022 */ 17023function Filter(dataGroup, column, graph) { 17024 this.dataGroup = dataGroup; 17025 this.column = column; 17026 this.graph = graph; // the parent graph 17027 17028 this.index = undefined; 17029 this.value = undefined; 17030 17031 // read all distinct values and select the first one 17032 this.values = dataGroup.getDistinctValues(this.column); 17033 17034 if (this.values.length > 0) { 17035 this.selectValue(0); 17036 } 17037 17038 // create an array with the filtered datapoints. this will be loaded afterwards 17039 this.dataPoints = []; 17040 17041 this.loaded = false;
17042 this.onLoadCallback = undefined; 17043 17044 if (graph.animationPreload) { 17045 this.loaded = false; 17046 this.loadInBackground(); 17047 } else { 17048 this.loaded = true; 17049 } 17050} 17051 17052/** 17053 * Return the label 17054 * @return {string} label 17055 */ 17056Filter.prototype.isLoaded = function () { 17057 return this.loaded; 17058}; 17059 17060/** 17061 * Return the loaded progress 17062 * @return {number} percentage between 0 and 100 17063 */ 17064Filter.prototype.getLoadedProgress = function () { 17065 var len = this.values.length; 17066 17067 var i = 0; 17068 while (this.dataPoints[i]) { 17069 i++; 17070 } 17071 17072 return Math.round(i / len * 100); 17073}; 17074 17075/** 17076 * Return the label 17077 * @return {string} label 17078 */ 17079Filter.prototype.getLabel = function () { 17080 return this.graph.filterLabel; 17081}; 17082 17083/** 17084 * Return the columnIndex of the filter 17085 * @return {number} columnIndex 17086 */ 17087Filter.prototype.getColumn = function () { 17088 return this.column; 17089}; 17090 17091/** 17092 * Return the currently selected value. Returns undefined if there is no selection 17093 * @return {*} value 17094 */ 17095Filter.prototype.getSelectedValue = function () { 17096 if (this.index === undefined) return undefined; 17097 17098 return this.values[this.index]; 17099}; 17100 17101/** 17102 * Retrieve all values of the filter 17103 * @return {Array} values 17104 */ 17105Filter.prototype.getValues = function () { 17106 return this.values; 17107}; 17108 17109/** 17110 * Retrieve one value of the filter 17111 * @param {number} index 17112 * @return {*} value 17113 */ 17114Filter.prototype.getValue = function (index) { 17115 if (index >= this.values.length) throw new Error('Index out of range'); 17116 17117 return this.values[index]; 17118}; 17119 17120/** 17121 * Retrieve the (filtered) dataPoints for the currently selected filter index 17122 * @param {number} [index] (optional) 17123 * @return {Array} dataPoints 17124 */ 17125Filter.prototype._getDataPoints = function (index) { 17126 if (index === undefined) index = this.index; 17127 17128 if (index === undefined) return []; 17129 17130 var dataPoints; 17131 if (this.dataPoints[index]) { 17132 dataPoints = this.dataPoints[index]; 17133 } else { 17134 var f = {}; 17135 f.column = this.column; 17136 f.value = this.values[index]; 17137 17138 var dataView = new DataView(this.dataGroup.getDataSet(), { filter: function filter(item) { 17139 return item[f.column] == f.value; 17140 } }).get(); 17141 dataPoints = this.dataGroup._getDataPoints(dataView); 17142 17143 this.dataPoints[index] = dataPoints; 17144 } 17145 17146 return dataPoints; 17147}; 17148 17149/** 17150 * Set a callback function when the filter is fully loaded. 17151 * 17152 * @param {function} callback 17153 */ 17154Filter.prototype.setOnLoadCallback = function (callback) { 17155 this.onLoadCallback = callback; 17156}; 17157 17158/** 17159 * Add a value to the list with available values for this filter 17160 * No double entries will be created. 17161 * @param {number} index 17162 */ 17163Filter.prototype.selectValue = function (index) { 17164 if (index >= this.values.length) throw new Error('Index out of range'); 17165 17166 this.index = index; 17167 this.value = this.values[index]; 17168}; 17169 17170/** 17171 * Load all filtered rows in the background one by one 17172 * Start this method without providing an index! 17173 * 17174 * @param {number} [index=0] 17175 */ 17176Filter.prototype.loadInBackground = function (index) { 17177 if (index === undefined) index = 0; 17178 17179 var frame = this.graph.frame; 17180 17181 if (index < this.values.length) { 17182 // create a progress box 17183 if (frame.progress === undefined) { 17184 frame.progress = document.createElement('DIV'); 17185 frame.progress.style.position = 'absolute'; 17186 frame.progress.style.color = 'gray'; 17187 frame.appendChild(frame.progress); 17188 } 17189 var progress = this.getLoadedProgress(); 17190 frame.progress.innerHTML = 'Loading animation... ' + progress + '%'; 17191 // TODO: this is no nice solution... 17192 frame.progress.style.bottom = 60 + 'px'; // TODO: use height of slider 17193 frame.progress.style.left = 10 + 'px'; 17194 17195 var me = this; 17196 setTimeout(function () { 17197 me.loadInBackground(index + 1); 17198 }, 10); 17199 this.loaded = false; 17200 } else { 17201 this.loaded = true; 17202 17203 // remove the progress box 17204 if (frame.progress !== undefined) { 17205 frame.removeChild(frame.progress); 17206 frame.progress = undefined; 17207 } 17208 17209 if (this.onLoadCallback) this.onLoadCallback(); 17210 } 17211}; 17212 17213module.exports = Filter; 17214 17215/***/ }), 17216/* 97 */ 17217/***/ (function(module, exports, __webpack_require__) { 17218 17219"use strict"; 17220 17221 17222var keycharm = __webpack_require__(35); 17223var Emitter = __webpack_require__(44); 17224var Hammer = __webpack_require__(10); 17225var util = __webpack_require__(2); 17226 17227/** 17228 * Turn an element into an clickToUse element. 17229 * When not active, the element has a transparent overlay. When the overlay is 17230 * clicked, the mode is changed to active. 17231 * When active, the element is displayed with a blue border around it, and 17232 * the interactive contents of the element can be used. When clicked outside 17233 * the element, the elements mode is changed to inactive. 17234 * @param {Element} container 17235 * @constructor Activator 17236 */ 17237function Activator(container) { 17238 this.active = false;
17239 17240 this.dom = { 17241 container: container 17242 }; 17243 17244 this.dom.overlay = document.createElement('div'); 17245 this.dom.overlay.className = 'vis-overlay'; 17246 17247 this.dom.container.appendChild(this.dom.overlay); 17248 17249 this.hammer = Hammer(this.dom.overlay); 17250 this.hammer.on('tap', this._onTapOverlay.bind(this)); 17251 17252 // block all touch events (except tap) 17253 var me = this; 17254 var events = ['tap', 'doubletap', 'press', 'pinch', 'pan', 'panstart', 'panmove', 'panend']; 17255 events.forEach(function (event) { 17256 me.hammer.on(event, function (event) { 17257 event.stopPropagation(); 17258 }); 17259 }); 17260 17261 // attach a click event to the window, in order to deactivate when clicking outside the timeline 17262 if (document && document.body) { 17263 this.onClick = function (event) { 17264 if (!_hasParent(event.target, container)) { 17265 me.deactivate(); 17266 } 17267 }; 17268 document.body.addEventListener('click', this.onClick); 17269 } 17270 17271 if (this.keycharm !== undefined) { 17272 this.keycharm.destroy(); 17273 } 17274 this.keycharm = keycharm(); 17275 17276 // keycharm listener only bounded when active) 17277 this.escListener = this.deactivate.bind(this); 17278} 17279 17280// turn into an event emitter 17281Emitter(Activator.prototype); 17282 17283// The currently active activator 17284Activator.current = null; 17285 17286/** 17287 * Destroy the activator. Cleans up all created DOM and event listeners 17288 */ 17289Activator.prototype.destroy = function () { 17290 this.deactivate(); 17291 17292 // remove dom 17293 this.dom.overlay.parentNode.removeChild(this.dom.overlay); 17294 17295 // remove global event listener 17296 if (this.onClick) { 17297 document.body.removeEventListener('click', this.onClick); 17298 } 17299 17300 // cleanup hammer instances 17301 this.hammer.destroy(); 17302 this.hammer = null; 17303 // FIXME: cleaning up hammer instances doesn't work (Timeline not removed from memory) 17304}; 17305 17306/** 17307 * Activate the element 17308 * Overlay is hidden, element is decorated with a blue shadow border 17309 */ 17310Activator.prototype.activate = function () { 17311 // we allow only one active activator at a time 17312 if (Activator.current) { 17313 Activator.current.deactivate(); 17314 } 17315 Activator.current = this; 17316 17317 this.active = true; 17318 this.dom.overlay.style.display = 'none'; 17319 util.addClassName(this.dom.container, 'vis-active'); 17320 17321 this.emit('change'); 17322 this.emit('activate'); 17323 17324 // ugly hack: bind ESC after emitting the events, as the Network rebinds all 17325 // keyboard events on a 'change' event 17326 this.keycharm.bind('esc', this.escListener); 17327}; 17328 17329/** 17330 * Deactivate the element 17331 * Overlay is displayed on top of the element 17332 */ 17333Activator.prototype.deactivate = function () { 17334 this.active = false; 17335 this.dom.overlay.style.display = ''; 17336 util.removeClassName(this.dom.container, 'vis-active'); 17337 this.keycharm.unbind('esc', this.escListener); 17338 17339 this.emit('change'); 17340 this.emit('deactivate'); 17341}; 17342 17343/** 17344 * Handle a tap event: activate the container 17345 * @param {Event} event The event 17346 * @private 17347 */ 17348Activator.prototype._onTapOverlay = function (event) { 17349 // activate the container 17350 this.activate(); 17351 event.stopPropagation(); 17352}; 17353 17354/** 17355 * Test whether the element has the requested parent element somewhere in 17356 * its chain of parent nodes. 17357 * @param {HTMLElement} element 17358 * @param {HTMLElement} parent 17359 * @returns {boolean} Returns true when the parent is found somewhere in the 17360 * chain of parent nodes. 17361 * @private 17362 */ 17363function _hasParent(element, parent) { 17364 while (element) { 17365 if (element === parent) { 17366 return true; 17367 } 17368 element = element.parentNode; 17369 } 17370 return false; 17371} 17372 17373module.exports = Activator; 17374 17375/***/ }), 17376/* 98 */ 17377/***/ (function(module, exports, __webpack_require__) { 17378 17379"use strict"; 17380 17381 17382// English 17383exports['en'] = { 17384 current: 'current', 17385 time: 'time' 17386}; 17387exports['en_EN'] = exports['en']; 17388exports['en_US'] = exports['en']; 17389 17390// Italiano 17391exports['it'] = { 17392 current: 'attuale', 17393 time: 'tempo' 17394}; 17395exports['it_IT'] = exports['it']; 17396exports['it_CH'] = exports['it']; 17397 17398// Dutch 17399exports['nl'] = { 17400 current: 'huidige', 17401 time: 'tijd' 17402}; 17403exports['nl_NL'] = exports['nl']; 17404exports['nl_BE'] = exports['nl']; 17405 17406// German 17407exports['de'] = { 17408 current: 'Aktuelle', 17409 time: 'Zeit' 17410}; 17411exports['de_DE'] = exports['de']; 17412 17413// French 17414exports['fr'] = { 17415 current: 'actuel', 17416 time: 'heure' 17417}; 17418exports['fr_FR'] = exports['fr']; 17419exports['fr_CA'] = exports['fr']; 17420exports['fr_BE'] = exports['fr']; 17421 17422// Espanol 17423exports['es'] = { 17424 current: 'corriente', 17425 time: 'hora' 17426}; 17427exports['es_ES'] = exports['es']; 17428 17429/***/ }), 17430/* 99 */ 17431/***/ (function(module, exports, __webpack_require__) { 17432 17433"use strict"; 17434 17435 17436var _create = __webpack_require__(29); 17437 17438var _create2 = _interopRequireDefault(_create); 17439 17440var _typeof2 = __webpack_require__(6); 17441 17442var _typeof3 = _interopRequireDefault(_typeof2); 17443 17444function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 17445 17446var Hammer = __webpack_require__(10); 17447var util = __webpack_require__(2); 17448var DataSet = __webpack_require__(11); 17449var DataView = __webpack_require__(12); 17450var TimeStep = __webpack_require__(66); 17451var Component = __webpack_require__(16); 17452var Group = __webpack_require__(68);
17453var BackgroundGroup = __webpack_require__(69); 17454var BoxItem = __webpack_require__(101); 17455var PointItem = __webpack_require__(102); 17456var RangeItem = __webpack_require__(70); 17457var BackgroundItem = __webpack_require__(103); 17458var Popup = __webpack_require__(104)['default']; 17459 17460var UNGROUPED = '__ungrouped__'; // reserved group id for ungrouped items 17461var BACKGROUND = '__background__'; // reserved group id for background items without group 17462 17463/** 17464 * An ItemSet holds a set of items and ranges which can be displayed in a 17465 * range. The width is determined by the parent of the ItemSet, and the height 17466 * is determined by the size of the items. 17467 * @param {{dom: Object, domProps: Object, emitter: Emitter, range: Range}} body 17468 * @param {Object} [options] See ItemSet.setOptions for the available options. 17469 * @constructor ItemSet 17470 * @extends Component 17471 */ 17472function ItemSet(body, options) { 17473 this.body = body; 17474 this.defaultOptions = { 17475 type: null, // 'box', 'point', 'range', 'background' 17476 orientation: { 17477 item: 'bottom' // item orientation: 'top' or 'bottom' 17478 }, 17479 align: 'auto', // alignment of box items 17480 stack: true, 17481 stackSubgroups: true, 17482 groupOrderSwap: function groupOrderSwap(fromGroup, toGroup, groups) { 17483 // eslint-disable-line no-unused-vars 17484 var targetOrder = toGroup.order; 17485 toGroup.order = fromGroup.order; 17486 fromGroup.order = targetOrder; 17487 }, 17488 groupOrder: 'order', 17489 17490 selectable: true, 17491 multiselect: false, 17492 itemsAlwaysDraggable: { 17493 item: false, 17494 range: false 17495 }, 17496 17497 editable: { 17498 updateTime: false, 17499 updateGroup: false, 17500 add: false, 17501 remove: false, 17502 overrideItems: false 17503 }, 17504 17505 groupEditable: { 17506 order: false, 17507 add: false, 17508 remove: false 17509 }, 17510 17511 snap: TimeStep.snap, 17512 17513 // Only called when `objectData.target === 'item'. 17514 onDropObjectOnItem: function onDropObjectOnItem(objectData, item, callback) { 17515 callback(item); 17516 }, 17517 onAdd: function onAdd(item, callback) { 17518 callback(item); 17519 }, 17520 onUpdate: function onUpdate(item, callback) { 17521 callback(item); 17522 }, 17523 onMove: function onMove(item, callback) { 17524 callback(item); 17525 }, 17526 onRemove: function onRemove(item, callback) { 17527 callback(item); 17528 }, 17529 onMoving: function onMoving(item, callback) { 17530 callback(item); 17531 }, 17532 onAddGroup: function onAddGroup(item, callback) { 17533 callback(item); 17534 }, 17535 onMoveGroup: function onMoveGroup(item, callback) { 17536 callback(item); 17537 }, 17538 onRemoveGroup: function onRemoveGroup(item, callback) { 17539 callback(item); 17540 }, 17541 17542 margin: { 17543 item: { 17544 horizontal: 10, 17545 vertical: 10 17546 }, 17547 axis: 20 17548 }, 17549 17550 showTooltips: true, 17551 17552 tooltip: { 17553 followMouse: false, 17554 overflowMethod: 'flip' 17555 }, 17556 17557 tooltipOnItemUpdateTime: false 17558 }; 17559 17560 // options is shared by this ItemSet and all its items 17561 this.options = util.extend({}, this.defaultOptions); 17562 this.options.rtl = options.rtl; 17563 17564 // options for getting items from the DataSet with the correct type 17565 this.itemOptions = { 17566 type: { start: 'Date', end: 'Date' } 17567 }; 17568 17569 this.conversion = { 17570 toScreen: body.util.toScreen, 17571 toTime: body.util.toTime 17572 }; 17573 this.dom = {}; 17574 this.props = {}; 17575 this.hammer = null; 17576 17577 var me = this; 17578 this.itemsData = null; // DataSet 17579 this.groupsData = null; // DataSet 17580 17581 // listeners for the DataSet of the items 17582 this.itemListeners = { 17583 'add': function add(event, params, senderId) { 17584 // eslint-disable-line no-unused-vars 17585 me._onAdd(params.items); 17586 }, 17587 'update': function update(event, params, senderId) { 17588 // eslint-disable-line no-unused-vars 17589 me._onUpdate(params.items); 17590 }, 17591 'remove': function remove(event, params, senderId) { 17592 // eslint-disable-line no-unused-vars 17593 me._onRemove(params.items); 17594 } 17595 }; 17596 17597 // listeners for the DataSet of the groups 17598 this.groupListeners = { 17599 'add': function add(event, params, senderId) { 17600 // eslint-disable-line no-unused-vars 17601 me._onAddGroups(params.items); 17602 17603 if (me.groupsData && me.groupsData.length > 0) { 17604 var groupsData = me.groupsData.getDataSet();
17605 groupsData.get().forEach(function (groupData) { 17606 if (groupData.nestedGroups) { 17607 if (groupData.showNested != false) { 17608 groupData.showNested = true; 17609 } 17610 var updatedGroups = []; 17611 groupData.nestedGroups.forEach(function (nestedGroupId) { 17612 var updatedNestedGroup = groupsData.get(nestedGroupId); 17613 if (!updatedNestedGroup) { 17614 return; 17615 } 17616 updatedNestedGroup.nestedInGroup = groupData.id; 17617 if (groupData.showNested == false) { 17618 updatedNestedGroup.visible = false; 17619 } 17620 updatedGroups = updatedGroups.concat(updatedNestedGroup); 17621 }); 17622 groupsData.update(updatedGroups, senderId); 17623 } 17624 }); 17625 } 17626 }, 17627 'update': function update(event, params, senderId) { 17628 // eslint-disable-line no-unused-vars 17629 me._onUpdateGroups(params.items); 17630 }, 17631 'remove': function remove(event, params, senderId) { 17632 // eslint-disable-line no-unused-vars 17633 me._onRemoveGroups(params.items); 17634 } 17635 }; 17636 17637 this.items = {}; // object with an Item for every data item 17638 this.groups = {}; // Group object for every group 17639 this.groupIds = []; 17640 17641 this.selection = []; // list with the ids of all selected nodes 17642 17643 this.popup = null; 17644 17645 this.touchParams = {}; // stores properties while dragging 17646 this.groupTouchParams = {}; 17647 // create the HTML DOM 17648 17649 this._create(); 17650 17651 this.setOptions(options); 17652} 17653 17654ItemSet.prototype = new Component(); 17655 17656// available item types will be registered here 17657ItemSet.types = { 17658 background: BackgroundItem, 17659 box: BoxItem, 17660 range: RangeItem, 17661 point: PointItem 17662}; 17663 17664/** 17665 * Create the HTML DOM for the ItemSet 17666 */ 17667ItemSet.prototype._create = function () { 17668 var frame = document.createElement('div'); 17669 frame.className = 'vis-itemset'; 17670 frame['timeline-itemset'] = this; 17671 this.dom.frame = frame; 17672 17673 // create background panel 17674 var background = document.createElement('div'); 17675 background.className = 'vis-background'; 17676 frame.appendChild(background); 17677 this.dom.background = background; 17678 17679 // create foreground panel 17680 var foreground = document.createElement('div'); 17681 foreground.className = 'vis-foreground'; 17682 frame.appendChild(foreground); 17683 this.dom.foreground = foreground; 17684 17685 // create axis panel 17686 var axis = document.createElement('div'); 17687 axis.className = 'vis-axis'; 17688 this.dom.axis = axis; 17689 17690 // create labelset 17691 var labelSet = document.createElement('div'); 17692 labelSet.className = 'vis-labelset'; 17693 this.dom.labelSet = labelSet; 17694 17695 // create ungrouped Group 17696 this._updateUngrouped(); 17697 17698 // create background Group 17699 var backgroundGroup = new BackgroundGroup(BACKGROUND, null, this); 17700 backgroundGroup.show(); 17701 this.groups[BACKGROUND] = backgroundGroup; 17702 17703 // attach event listeners 17704 // Note: we bind to the centerContainer for the case where the height 17705 // of the center container is larger than of the ItemSet, so we 17706 // can click in the empty area to create a new item or deselect an item. 17707 this.hammer = new Hammer(this.body.dom.centerContainer); 17708 17709 // drag items when selected 17710 this.hammer.on('hammer.input', function (event) { 17711 if (event.isFirst) { 17712 this._onTouch(event); 17713 } 17714 }.bind(this)); 17715 this.hammer.on('panstart', this._onDragStart.bind(this)); 17716 this.hammer.on('panmove', this._onDrag.bind(this)); 17717 this.hammer.on('panend', this._onDragEnd.bind(this)); 17718 this.hammer.get('pan').set({ threshold: 5, direction: Hammer.DIRECTION_HORIZONTAL }); 17719 17720 // single select (or unselect) when tapping an item 17721 this.hammer.on('tap', this._onSelectItem.bind(this)); 17722 17723 // multi select when holding mouse/touch, or on ctrl+click 17724 this.hammer.on('press', this._onMultiSelectItem.bind(this)); 17725 17726 // add item on doubletap 17727 this.hammer.on('doubletap', this._onAddItem.bind(this)); 17728 17729 if (this.options.rtl) { 17730 this.groupHammer = new Hammer(this.body.dom.rightContainer); 17731 } else { 17732 this.groupHammer = new Hammer(this.body.dom.leftContainer); 17733 } 17734 17735 this.groupHammer.on('tap', this._onGroupClick.bind(this)); 17736 this.groupHammer.on('panstart', this._onGroupDragStart.bind(this)); 17737 this.groupHammer.on('panmove', this._onGroupDrag.bind(this)); 17738 this.groupHammer.on('panend', this._onGroupDragEnd.bind(this)); 17739 this.groupHammer.get('pan').set({ threshold: 5, direction: Hammer.DIRECTION_VERTICAL }); 17740 17741 this.body.dom.centerContainer.addEventListener('mouseover', this._onMouseOver.bind(this)); 17742 this.body.dom.centerContainer.addEventListener('mouseout', this._onMouseOut.bind(this)); 17743 this.body.dom.centerContainer.addEventListener('mousemove', this._onMouseMove.bind(this)); 17744 // right-click on timeline 17745 this.body.dom.centerContainer.addEventListener('contextmenu', this._onDragEnd.bind(this)); 17746 17747 this.body.dom.centerContainer.addEventListener('mousewheel', this._onMouseWheel.bind(this)); 17748 17749 // attach to the DOM 17750 this.show(); 17751}; 17752 17753/** 17754 * Set options for the ItemSet. Existing options will be extended/overwritten. 17755 * @param {Object} [options] The following options are available: 17756 * {string} type 17757 * Default type for the items. Choose from 'box' 17758 * (default), 'point', 'range', or 'background'.
17759 * The default style can be overwritten by 17760 * individual items. 17761 * {string} align 17762 * Alignment for the items, only applicable for 17763 * BoxItem. Choose 'center' (default), 'left', or 17764 * 'right'. 17765 * {string} orientation.item 17766 * Orientation of the item set. Choose 'top' or 17767 * 'bottom' (default). 17768 * {Function} groupOrder 17769 * A sorting function for ordering groups 17770 * {boolean} stack 17771 * If true (default), items will be stacked on 17772 * top of each other. 17773 * {number} margin.axis 17774 * Margin between the axis and the items in pixels. 17775 * Default is 20. 17776 * {number} margin.item.horizontal 17777 * Horizontal margin between items in pixels. 17778 * Default is 10. 17779 * {number} margin.item.vertical 17780 * Vertical Margin between items in pixels. 17781 * Default is 10. 17782 * {number} margin.item 17783 * Margin between items in pixels in both horizontal 17784 * and vertical direction. Default is 10. 17785 * {number} margin 17786 * Set margin for both axis and items in pixels. 17787 * {boolean} selectable 17788 * If true (default), items can be selected. 17789 * {boolean} multiselect 17790 * If true, multiple items can be selected. 17791 * False by default. 17792 * {boolean} editable 17793 * Set all editable options to true or false 17794 * {boolean} editable.updateTime 17795 * Allow dragging an item to an other moment in time 17796 * {boolean} editable.updateGroup 17797 * Allow dragging an item to an other group 17798 * {boolean} editable.add 17799 * Allow creating new items on double tap 17800 * {boolean} editable.remove 17801 * Allow removing items by clicking the delete button 17802 * top right of a selected item. 17803 * {Function(item: Item, callback: Function)} onAdd 17804 * Callback function triggered when an item is about to be added: 17805 * when the user double taps an empty space in the Timeline. 17806 * {Function(item: Item, callback: Function)} onUpdate 17807 * Callback function fired when an item is about to be updated. 17808 * This function typically has to show a dialog where the user 17809 * change the item. If not implemented, nothing happens. 17810 * {Function(item: Item, callback: Function)} onMove 17811 * Fired when an item has been moved. If not implemented, 17812 * the move action will be accepted. 17813 * {Function(item: Item, callback: Function)} onRemove 17814 * Fired when an item is about to be deleted. 17815 * If not implemented, the item will be always removed. 17816 */ 17817ItemSet.prototype.setOptions = function (options) { 17818 if (options) { 17819 // copy all options that we know 17820 var fields = ['type', 'rtl', 'align', 'order', 'stack', 'stackSubgroups', 'selectable', 'multiselect', 'multiselectPerGroup', 'groupOrder', 'dataAttributes', 'template', 'groupTemplate', 'visibleFrameTemplate', 'hide', 'snap', 'groupOrderSwap', 'showTooltips', 'tooltip', 'tooltipOnItemUpdateTime']; 17821 util.selectiveExtend(fields, this.options, options); 17822 17823 if ('itemsAlwaysDraggable' in options) { 17824 if (typeof options.itemsAlwaysDraggable === 'boolean') { 17825 this.options.itemsAlwaysDraggable.item = options.itemsAlwaysDraggable; 17826 this.options.itemsAlwaysDraggable.range = false;
17827 } else if ((0, _typeof3['default'])(options.itemsAlwaysDraggable) === 'object') { 17828 util.selectiveExtend(['item', 'range'], this.options.itemsAlwaysDraggable, options.itemsAlwaysDraggable); 17829 // only allow range always draggable when item is always draggable as well 17830 if (!this.options.itemsAlwaysDraggable.item) { 17831 this.options.itemsAlwaysDraggable.range = false; 17832 } 17833 } 17834 } 17835 17836 if ('orientation' in options) { 17837 if (typeof options.orientation === 'string') { 17838 this.options.orientation.item = options.orientation === 'top' ? 'top' : 'bottom'; 17839 } else if ((0, _typeof3['default'])(options.orientation) === 'object' && 'item' in options.orientation) { 17840 this.options.orientation.item = options.orientation.item; 17841 } 17842 } 17843 17844 if ('margin' in options) { 17845 if (typeof options.margin === 'number') { 17846 this.options.margin.axis = options.margin; 17847 this.options.margin.item.horizontal = options.margin; 17848 this.options.margin.item.vertical = options.margin; 17849 } else if ((0, _typeof3['default'])(options.margin) === 'object') { 17850 util.selectiveExtend(['axis'], this.options.margin, options.margin); 17851 if ('item' in options.margin) { 17852 if (typeof options.margin.item === 'number') { 17853 this.options.margin.item.horizontal = options.margin.item; 17854 this.options.margin.item.vertical = options.margin.item; 17855 } else if ((0, _typeof3['default'])(options.margin.item) === 'object') { 17856 util.selectiveExtend(['horizontal', 'vertical'], this.options.margin.item, options.margin.item); 17857 } 17858 } 17859 } 17860 } 17861 17862 if ('editable' in options) { 17863 if (typeof options.editable === 'boolean') { 17864 this.options.editable.updateTime = options.editable; 17865 this.options.editable.updateGroup = options.editable; 17866 this.options.editable.add = options.editable; 17867 this.options.editable.remove = options.editable; 17868 this.options.editable.overrideItems = false; 17869 } else if ((0, _typeof3['default'])(options.editable) === 'object') { 17870 util.selectiveExtend(['updateTime', 'updateGroup', 'add', 'remove', 'overrideItems'], this.options.editable, options.editable); 17871 } 17872 } 17873 17874 if ('groupEditable' in options) { 17875 if (typeof options.groupEditable === 'boolean') { 17876 this.options.groupEditable.order = options.groupEditable; 17877 this.options.groupEditable.add = options.groupEditable; 17878 this.options.groupEditable.remove = options.groupEditable; 17879 } else if ((0, _typeof3['default'])(options.groupEditable) === 'object') { 17880 util.selectiveExtend(['order', 'add', 'remove'], this.options.groupEditable, options.groupEditable); 17881 } 17882 } 17883 17884 // callback functions 17885 var addCallback = function (name) { 17886 var fn = options[name]; 17887 if (fn) { 17888 if (!(fn instanceof Function)) { 17889 throw new Error('option ' + name + ' must be a function ' + n
17889ame + '(item, callback)'); 17890 } 17891 this.options[name] = fn; 17892 } 17893 }.bind(this); 17894 ['onDropObjectOnItem', 'onAdd', 'onUpdate', 'onRemove', 'onMove', 'onMoving', 'onAddGroup', 'onMoveGroup', 'onRemoveGroup'].forEach(addCallback); 17895 17896 // force the itemSet to refresh: options like orientation and margins may be changed 17897 this.markDirty(); 17898 } 17899}; 17900 17901/** 17902 * Mark the ItemSet dirty so it will refresh everything with next redraw. 17903 * Optionally, all items can be marked as dirty and be refreshed. 17904 * @param {{refreshItems: boolean}} [options] 17905 */ 17906ItemSet.prototype.markDirty = function (options) { 17907 this.groupIds = []; 17908 17909 if (options && options.refreshItems) { 17910 util.forEach(this.items, function (item) { 17911 item.dirty = true; 17912 if (item.displayed) item.redraw(); 17913 }); 17914 } 17915}; 17916 17917/** 17918 * Destroy the ItemSet 17919 */ 17920ItemSet.prototype.destroy = function () { 17921 this.hide(); 17922 this.setItems(null); 17923 this.setGroups(null); 17924 17925 this.hammer = null; 17926 17927 this.body = null; 17928 this.conversion = null; 17929}; 17930 17931/** 17932 * Hide the component from the DOM 17933 */ 17934ItemSet.prototype.hide = function () { 17935 // remove the frame containing the items 17936 if (this.dom.frame.parentNode) { 17937 this.dom.frame.parentNode.removeChild(this.dom.frame); 17938 } 17939 17940 // remove the axis with dots 17941 if (this.dom.axis.parentNode) { 17942 this.dom.axis.parentNode.removeChild(this.dom.axis); 17943 } 17944 17945 // remove the labelset containing all group labels 17946 if (this.dom.labelSet.parentNode) { 17947 this.dom.labelSet.parentNode.removeChild(this.dom.labelSet); 17948 } 17949}; 17950 17951/** 17952 * Show the component in the DOM (when not already visible). 17953 */ 17954ItemSet.prototype.show = function () { 17955 // show frame containing the items 17956 if (!this.dom.frame.parentNode) { 17957 this.body.dom.center.appendChild(this.dom.frame); 17958 } 17959 17960 // show axis with dots 17961 if (!this.dom.axis.parentNode) { 17962 this.body.dom.backgroundVertical.appendChild(this.dom.axis); 17963 } 17964 17965 // show labelset containing labels 17966 if (!this.dom.labelSet.parentNode) { 17967 if (this.options.rtl) { 17968 this.body.dom.right.appendChild(this.dom.labelSet); 17969 } else { 17970 this.body.dom.left.appendChild(this.dom.labelSet); 17971 } 17972 } 17973}; 17974 17975/** 17976 * Set selected items by their id. Replaces the current selection 17977 * Unknown id's are silently ignored. 17978 * @param {string[] | string} [ids] An array with zero or more id's of the items to be 17979 * selected, or a single item id. If ids is undefined 17980 * or an empty array, all items will be unselected. 17981 */ 17982ItemSet.prototype.setSelection = function (ids) { 17983 var i, ii, id, item; 17984 17985 if (ids == undefined) ids = []; 17986 if (!Array.isArray(ids)) ids = [ids]; 17987 17988 // unselect currently selected items 17989 for (i = 0, ii = this.selection.length; i < ii; i++) { 17990 id = this.selection[i]; 17991 item = this.items[id]; 17992 if (item) item.unselect(); 17993 } 17994 17995 // select items 17996 this.selection = []; 17997 for (i = 0, ii = ids.length; i < ii; i++) { 17998 id = ids[i]; 17999 item = this.items[id]; 18000 if (item) { 18001 this.selection.push(id); 18002 item.select(); 18003 } 18004 } 18005}; 18006 18007/** 18008 * Get the selected items by their id 18009 * @return {Array} ids The ids of the selected items 18010 */ 18011ItemSet.prototype.getSelection = function () { 18012 return this.selection.concat([]); 18013}; 18014 18015/** 18016 * Get the id's of the currently visible items. 18017 * @returns {Array} The ids of the visible items 18018 */ 18019ItemSet.prototype.getVisibleItems = function () { 18020 var range = this.body.range.getRange(); 18021 var right, left; 18022 18023 if (this.options.rtl) { 18024 right = this.body.util.toScreen(range.start); 18025 left = this.body.util.toScreen(range.end); 18026 } else { 18027 left = this.body.util.toScreen(range.start); 18028 right = this.body.util.toScreen(range.end); 18029 } 18030 18031 var ids = []; 18032 for (var groupId in this.groups) { 18033 if (this.groups.hasOwnProperty(groupId)) { 18034 var group = this.groups[groupId]; 18035 var rawVisibleItems = group.isVisible ? group.visibleItems : []; 18036 18037 // filter the "raw" set with visibleItems into a set which is really 18038 // visible by pixels 18039 for (var i = 0; i < rawVisibleItems.length; i++) { 18040 var item = rawVisibleItems[i]; 18041 // TODO: also check whether visible vertically 18042 if (this.options.rtl) { 18043 if (item.right < left && item.right + item.width > right) { 18044 ids.push(item.id); 18045 } 18046 } else { 18047 if (item.left < right && item.left + item.width > left) { 18048 ids.push(item.id); 18049 } 18050 } 18051 } 18052 } 18053 } 18054 18055 return ids; 18056}; 18057 18058/** 18059 * Deselect a selected item 18060 * @param {string | number} id 18061 * @private 18062 */ 18063ItemSet.prototype._deselect = function (id) { 18064 var selection = this.selection; 18065 for (var i = 0, ii = selection.length; i < ii; i++) { 18066 if (selection[i] == id) { 18067 // non-strict comparison! 18068 selection.splice(i, 1); 18069 break; 18070 } 18071 } 18072}; 18073 18074/** 18075 * Repaint the component 18076 * @return {boolean} Returns true if the component is resized 18077 */ 18078ItemSet.prototype.redraw = function () { 18079 var margin = this.options.margin, 18080 range = this.body.range, 18081 asSize = util.option.asSize, 18082 options = this.options, 18083 orientation = options.orientation.item, 18084 resized = false, 18085 frame = this.dom.frame; 18086 18087 // recalculate absolute position (before redrawing groups) 18088 this.props.top = this.body.domProps.top.height + this.body.domProps.border.top; 18089 18090 if (this.options.rtl) { 18091 this.props.right = this.body.domProps.right.width + this.body.domProps.border.right; 18092 } else { 18093 this.props.left = this.body.domProps.left.width + this.body.domProps.border.left; 18094 } 18095 18096 // update class name 18097 frame.className = 'vis-itemset'; 18098 18099 // reorder the groups (if needed) 18100 resized = this._orderGroups() || resized; 18101 18102 // check whether zoomed (in that case we need to re-stack everything) 18103 // TODO: would be nicer to get this as a trigger from Range 18104 var visibleInterval = range.end - range.start; 18105 var zoomed = visibleInterval != this.lastVisibleInterval || this.props.width != this.props.lastWidth; 18106 var scrolled = range.start != this.lastRangeStart; 18107 var changedStackOption = options.stack != this.lastStack; 18108 var changedStackSubgroupsOption = options.stackSubgroups != this.lastStackSubgroups; 18109 var forceRestack = zoomed || scrolled || changedStackOption || changedStackSubgroupsOption; 18110 this.lastVisibleInterval = visibleInterval; 18111 this.lastRangeStart = range.start; 18112 this.lastStack = options.stack; 18113 this.lastStackSubgroups = options.stackSubgroups; 18114 18115 this.props.lastWidth = this.props.width; 18116 18117 var firstGroup = this._firstGroup(); 18118 var firstMargin = { 18119 item: margin.item, 18120 axis: margin.axis 18121 }; 18122 var nonFirstMargin = { 18123 item: margin.item, 18124 axis: margin.item.vertical / 2 18125 }; 18126 var height = 0; 18127 var minHeight = margin.axis + margin.item.vertical; 18128 18129 // redraw the background group 18130 this.groups[BACKGROUND].redraw(range, nonFirstMargin, forceRestack); 18131 18132 var redrawQueue = {}; 18133 var redrawQueueLength = 0; 18134 18135 // collect redraw functions 18136 util.forEach(this.groups, function (group, key) { 18137 if (key === BACKGROUND) return; 18138 var groupMargin = group == firstGroup ? firstMargin : nonFirstMargin; 18139 var returnQueue = true; 18140 redrawQueue[key] = group.redraw(range, groupMargin, forceRestack, returnQueue); 18141 redrawQueueLength = redrawQueue[key].length; 18142 }); 18143 18144 var needRedraw = redrawQueueLength > 0; 18145 if (needRedraw) { 18146 var redrawResults = {}; 18147 18148 for (var i = 0; i < redrawQueueLength; i++) { 18149 util.forEach(redrawQueue, function (fns, key) { 18150 redrawResults[key] = fns[i](); 18151 }); 18152 } 18153 18154 // redraw all regular groups 18155 util.forEach(this.groups, function (group, key) { 18156 if (key === BACKGROUND) return; 18157 var groupResized = redrawResults[key]; 18158 resized = groupResized || resized; 18159 height += group.height; 18160 }); 18161 height = Math.max(height, minHeight); 18162 } 18163 18164 height = Math.max(height, minHeight); 18165 18166 // update frame height 18167 frame.style.height = asSize(height); 18168 18169 // calculate actual size 18170 this.props.width = frame.offsetWidth; 18171 this.props.height = height; 18172 18173 // reposition axis 18174 this.dom.axis.style.top = asSize(orientation == 'top' ? this.body.domProps.top.height + this.body.domProps.border.top : this.body.domProps.top.height + this.body.domProps.centerContainer.height); 18175 if (this.options.rtl) { 18176 this.dom.axis.style.right = '0'; 18177 } else { 18178 this.dom.axis.style.left = '0'; 18179 } 18180 18181 this.initialItemSetDrawn = true; 18182 // check if this component is resized 18183 resized = this._isResized() || resized; 18184 18185 return resized; 18186}; 18187 18188/** 18189 * Get the first group, aligned with the axis 18190 * @return {Group | null} firstGroup 18191 * @private 18192 */ 18193ItemSet.prototype._firstGroup = function () { 18194 var firstGroupIndex = this.options.orientation.item == 'top' ? 0 : this.groupIds.length - 1; 18195 var firstGroupId = this.groupIds[firstGroupIndex]; 18196 var firstGroup = this.groups[firstGroupId] || this.groups[UNGROUPED]; 18197 18198 return firstGroup || null; 18199}; 18200 18201/** 18202 * Create or delete the group holding all ungrouped items. This group is used when 18203 * there are no groups specified. 18204 * @protected 18205 */ 18206ItemSet.prototype._updateUngrouped = function () { 18207 var ungrouped = this.groups[UNGROUPED]; 18208 var item, itemId; 18209 18210 if (this.groupsData) { 18211 // remove the group holding all ungrouped items 18212 if (ungrouped) { 18213 ungrouped.hide(); 18214 delete this.groups[UNGROUPED]; 18215 18216 for (itemId in this.items) { 18217 if (this.items.hasOwnProperty(itemId)) { 18218 item = this.items[itemId]; 18219 item.parent && item.parent.remove(item); 18220 var groupId = this._getGroupId(item.data); 18221 var group = this.groups[groupId]; 18222 group && group.add(item) || item.hide(); 18223 } 18224 } 18225 } 18226 } else { 18227 // create a group holding all (unfiltered) items 18228 if (!ungrouped) { 18229 var id = null; 18230 var data = null; 18231 ungrouped = new Group(id, data, this); 18232 this.groups[UNGROUPED] = ungrouped; 18233 18234 for (itemId in this.items) { 18235 if (this.items.hasOwnProperty(itemId)) { 18236 item = this.items[itemId]; 18237 ungrouped.add(item); 18238 } 18239 } 18240 18241 ungrouped.show(); 18242 } 18243 } 18244}; 18245 18246/** 18247 * Get the element for the labelset 18248 * @return {HTMLElement} labelSet 18249 */ 18250ItemSet.prototype.getLabelSet = function () { 18251 return this.dom.labelSet; 18252}; 18253 18254/** 18255 * Set items 18256 * @param {vis.DataSet | null} items 18257 */ 18258ItemSet.prototype.setItems = function (items) { 18259 var me = this, 18260 ids, 18261 oldItemsData = this.itemsData; 18262 18263 // replace the dataset 18264 if (!items) { 18265 this.itemsData = null; 18266 } else if (items instanceof DataSet || items instanceof DataView) { 18267 this.itemsData = items; 18268 } else { 18269 throw new TypeError('Data must be an instance of DataSet or DataView'); 18270 } 18271 18272 if (oldItemsData) { 18273 // unsubscribe from old dataset 18274 util.forEach(this.itemListeners, function (callback, event) { 18275 oldItemsData.off(event, callback); 18276 }); 18277 18278 // remove all drawn items 18279 ids = oldItemsData.getIds(); 18280 this._onRemove(ids); 18281 } 18282 18283 if (this.itemsData) { 18284 // subscribe to new dataset 18285 var id = this.id; 18286 util.forEach(this.itemListeners, function (callback, event) { 18287 me.itemsData.on(event, callback, id); 18288 }); 18289 18290 // add all new items 18291 ids = this.itemsData.getIds(); 18292 this._onAdd(ids); 18293 18294 // update the group holding all ungrouped items 18295 this._updateUngrouped(); 18296 } 18297 18298 this.body.emitter.emit('_change', { queue: true }); 18299}; 18300 18301/** 18302 * Get the current items 18303 * @returns {vis.DataSet | null} 18304 */ 18305ItemSet.prototype.getItems = function () { 18306 return this.itemsData; 18307}; 18308 18309/** 18310 * Set groups 18311 * @param {vis.DataSet} groups 18312 */ 18313ItemSet.prototype.setGroups = function (groups) { 18314 var me = this, 18315 ids; 18316 18317 // unsubscribe from current dataset 18318 if (this.groupsData) { 18319 util.forEach(this.groupListeners, function (callback, event) { 18320 me.groupsData.off(event, callback); 18321 }); 18322 18323 // remove all drawn groups 18324 ids = this.groupsData.getIds(); 18325 this.groupsData = null; 18326 this._onRemoveGroups(ids); // note: this will cause a redraw 18327 } 18328 18329 // replace the dataset 18330 if (!groups) { 18331 this.groupsData = null; 18332 } else if (groups instanceof DataSet || groups instanceof DataView) { 18333 this.groupsData = groups; 18334 } else { 18335 throw new TypeError('Data must be an instance of DataSet or DataView'); 18336 } 18337 18338 if (this.groupsData) { 18339 // go over all groups nesting 18340 var groupsData = this.groupsData; 18341 if (this.groupsData instanceof DataView) { 18342 groupsData = this.groupsData.getDataSet(); 18343 } 18344
18345 groupsData.get().forEach(function (group) { 18346 if (group.nestedGroups) { 18347 group.nestedGroups.forEach(function (nestedGroupId) { 18348 var updatedNestedGroup = groupsData.get(nestedGroupId); 18349 updatedNestedGroup.nestedInGroup = group.id; 18350 if (group.showNested == false) { 18351 updatedNestedGroup.visible = false; 18352 } 18353 groupsData.update(updatedNestedGroup); 18354 }); 18355 } 18356 }); 18357 18358 // subscribe to new dataset 18359 var id = this.id; 18360 util.forEach(this.groupListeners, function (callback, event) { 18361 me.groupsData.on(event, callback, id); 18362 }); 18363 18364 // draw all ms 18365 ids = this.groupsData.getIds(); 18366 this._onAddGroups(ids); 18367 } 18368 18369 // update the group holding all ungrouped items 18370 this._updateUngrouped(); 18371 18372 // update the order of all items in each group 18373 this._order(); 18374 18375 this.body.emitter.emit('_change', { queue: true }); 18376}; 18377 18378/** 18379 * Get the current groups 18380 * @returns {vis.DataSet | null} groups 18381 */ 18382ItemSet.prototype.getGroups = function () { 18383 return this.groupsData; 18384}; 18385 18386/** 18387 * Remove an item by its id 18388 * @param {string | number} id 18389 */ 18390ItemSet.prototype.removeItem = function (id) { 18391 var item = this.itemsData.get(id), 18392 dataset = this.itemsData.getDataSet(); 18393 18394 if (item) { 18395 // confirm deletion 18396 this.options.onRemove(item, function (item) { 18397 if (item) { 18398 // remove by id here, it is possible that an item has no id defined 18399 // itself, so better not delete by the item itself 18400 dataset.remove(id); 18401 } 18402 }); 18403 } 18404}; 18405 18406/** 18407 * Get the time of an item based on it's data and options.type 18408 * @param {Object} itemData 18409 * @returns {string} Returns the type 18410 * @private 18411 */ 18412ItemSet.prototype._getType = function (itemData) { 18413 return itemData.type || this.options.type || (itemData.end ? 'range' : 'box'); 18414}; 18415 18416/** 18417 * Get the group id for an item 18418 * @param {Object} itemData 18419 * @returns {string} Returns the groupId 18420 * @private 18421 */ 18422ItemSet.prototype._getGroupId = function (itemData) { 18423 var type = this._getType(itemData); 18424 if (type == 'background' && itemData.group == undefined) { 18425 return BACKGROUND; 18426 } else { 18427 return this.groupsData ? itemData.group : UNGROUPED; 18428 } 18429}; 18430 18431/** 18432 * Handle updated items 18433 * @param {number[]} ids 18434 * @protected 18435 */ 18436ItemSet.prototype._onUpdate = function (ids) { 18437 var me = this; 18438 18439 ids.forEach(function (id) { 18440 var itemData = me.itemsData.get(id, me.itemOptions); 18441 var item = me.items[id]; 18442 var type = itemData ? me._getType(itemData) : null; 18443 18444 var constructor = ItemSet.types[type]; 18445 var selected; 18446 18447 if (item) { 18448 // update item 18449 if (!constructor || !(item instanceof constructor)) { 18450 // item type has changed, delete the item and recreate it 18451 selected = item.selected; // preserve selection of this item 18452 me._removeItem(item); 18453 item = null; 18454 } else { 18455 me._updateItem(item, itemData); 18456 } 18457 } 18458 18459 if (!item && itemData) { 18460 // create item 18461 if (constructor) { 18462 item = new constructor(itemData, me.conversion, me.options); 18463 item.id = id; // TODO: not so nice setting id afterwards 18464 18465 me._addItem(item); 18466 if (selected) { 18467 this.selection.push(id); 18468 item.select(); 18469 } 18470 } else if (type == 'rangeoverflow') { 18471 // TODO: deprecated since version 2.1.0 (or 3.0.0?). cleanup some day 18472 throw new TypeError('Item type "rangeoverflow" is deprecated. Use css styling instead: ' + '.vis-item.vis-range .vis-item-content {overflow: visible;}'); 18473 } else { 18474 throw new TypeError('Unknown item type "' + type + '"'); 18475 } 18476 } 18477 }.bind(this)); 18478 18479 this._order(); 18480 this.body.emitter.emit('_change', { queue: true }); 18481}; 18482 18483/** 18484 * Handle added items 18485 * @param {number[]} ids 18486 * @protected 18487 */ 18488ItemSet.prototype._onAdd = ItemSet.prototype._onUpdate; 18489 18490/** 18491 * Handle removed items 18492 * @param {number[]} ids 18493 * @protected 18494 */ 18495ItemSet.prototype._onRemove = function (ids) { 18496 var count = 0; 18497 var me = this; 18498 ids.forEach(function (id) { 18499 var item = me.items[id]; 18500 if (item) { 18501 count++; 18502 me._removeItem(item); 18503 } 18504 }); 18505 18506 if (count) { 18507 // update order 18508 this._order(); 18509 this.body.emitter.emit('_change', { queue: true }); 18510 } 18511}; 18512 18513/** 18514 * Update the order of item in all groups 18515 * @private 18516 */ 18517ItemSet.prototype._order = function () { 18518 // reorder the items in all groups 18519 // TODO: optimization: only reorder groups affected by the changed items 18520 util.forEach(this.groups, function (group) { 18521 group.order(); 18522 }); 18523}; 18524 18525/** 18526 * Handle updated groups 18527 * @param {number[]} ids 18528 * @private 18529 */ 18530ItemSet.prototype._onUpdateGroups = function (ids) { 18531 this._onAddGroups(ids); 18532}; 18533 18534/** 18535 * Handle changed groups (added or updated) 18536 * @param {number[]} ids 18537 * @private 18538 */ 18539ItemSet.prototype._onAddGroups = function (ids) { 18540 var me = this; 18541 18542 ids.forEach(function (id) { 18543 var groupData = me.groupsData.get(id); 18544 var group = me.groups[id]; 18545 18546 if (!group) { 18547 // check for reserved ids 18548 if (id == UNGROUPED || id == BACKGROUND) { 18549 throw new Error('Illegal group id. ' + id + ' is a reserved id.'); 18550 } 18551 18552 var groupOptions = (0, _create2['default'])(me.options); 18553 util.extend(groupOptions, { 18554 height: null 18555 }); 18556 18557 group = new Group(id, groupData, me); 18558 me.groups[id] = group; 18559 18560 // add items with this groupId to the new group 18561 for (var itemId in me.items) { 18562 if (me.items.hasOwnProperty(itemId)) { 18563 var item = me.items[itemId]; 18564 if (item.data.group == id) { 18565 group.add(item); 18566 } 18567 } 18568 } 18569 18570 group.order(); 18571 group.show(); 18572 } else { 18573 // update group 18574 group.setData(groupData); 18575 } 18576 }); 18577 18578 this.body.emitter.emit('_change', { queue: true }); 18579}; 18580 18581/** 18582 * Handle removed groups 18583 * @param {number[]} ids 18584 * @private 18585 */ 18586ItemSet.prototype._onRemoveGroups = function (ids) { 18587 var groups = this.groups; 18588 ids.forEach(function (id) { 18589 var group = groups[id]; 18590 18591 if (group) { 18592 group.hide(); 18593 delete groups[id]; 18594 } 18595 }); 18596 18597 this.markDirty(); 18598 18599 this.body.emitter.emit('_change', { queue: true }); 18600}; 18601 18602/** 18603 * Reorder the groups if needed 18604 * @return {boolean} changed 18605 * @private 18606 */ 18607ItemSet.prototype._orderGroups = function () { 18608 if (this.groupsData) { 18609 // reorder the groups 18610 var groupIds = this.groupsData.getIds({ 18611 order: this.options.groupOrder 18612 }); 18613 18614 groupIds = this._orderNestedGroups(groupIds); 18615 18616 var changed = !util.equalArray(groupIds, this.groupIds); 18617 if (changed) { 18618 // hide all groups, removes them from the DOM 18619 var groups = this.groups; 18620 groupIds.forEach(function (groupId) { 18621 groups[groupId].hide(); 18622 }); 18623 18624 // show the groups again, attach them to the DOM in correct order 18625 groupIds.forEach(function (groupId) { 18626 groups[groupId].show(); 18627 }); 18628 18629 this.groupIds = groupIds; 18630 } 18631 18632 return changed; 18633 } else { 18634 return false;
18635 } 18636}; 18637 18638/** 18639 * Reorder the nested groups 18640 * 18641 * @param {Array.<number>} groupIds 18642 * @returns {Array.<number>} 18643 * @private 18644 */ 18645ItemSet.prototype._orderNestedGroups = function (groupIds) { 18646 var newGroupIdsOrder = []; 18647 18648 groupIds.forEach(function (groupId) { 18649 var groupData = this.groupsData.get(groupId); 18650 if (!groupData.nestedInGroup) { 18651 newGroupIdsOrder.push(groupId); 18652 } 18653 if (groupData.nestedGroups) { 18654 var nestedGroups = this.groupsData.get({ 18655 filter: function filter(nestedGroup) { 18656 return nestedGroup.nestedInGroup == groupId; 18657 }, 18658 order: this.options.groupOrder 18659 }); 18660 var nestedGroupIds = nestedGroups.map(function (nestedGroup) { 18661 return nestedGroup.id; 18662 }); 18663 newGroupIdsOrder = newGroupIdsOrder.concat(nestedGroupIds); 18664 } 18665 }, this); 18666 return newGroupIdsOrder; 18667}; 18668 18669/** 18670 * Add a new item 18671 * @param {Item} item 18672 * @private 18673 */ 18674ItemSet.prototype._addItem = function (item) { 18675 this.items[item.id] = item; 18676 18677 // add to group 18678 var groupId = this._getGroupId(item.data); 18679 var group = this.groups[groupId]; 18680 18681 if (!group) { 18682 item.groupShowing = false; 18683 } else if (group && group.data && group.data.showNested) { 18684 item.groupShowing = true; 18685 } 18686 18687 if (group) group.add(item); 18688}; 18689 18690/** 18691 * Update an existing item 18692 * @param {Item} item 18693 * @param {Object} itemData 18694 * @private 18695 */ 18696ItemSet.prototype._updateItem = function (item, itemData) { 18697 // update the items data (will redraw the item when displayed) 18698 item.setData(itemData); 18699 18700 var groupId = this._getGroupId(item.data); 18701 var group = this.groups[groupId]; 18702 if (!group) { 18703 item.groupShowing = false; 18704 } else if (group && group.data && group.data.showNested) { 18705 item.groupShowing = true; 18706 } 18707}; 18708 18709/** 18710 * Delete an item from the ItemSet: remove it from the DOM, from the map 18711 * with items, and from the map with visible items, and from the selection 18712 * @param {Item} item 18713 * @private 18714 */ 18715ItemSet.prototype._removeItem = function (item) { 18716 // remove from DOM 18717 item.hide(); 18718 18719 // remove from items 18720 delete this.items[item.id]; 18721 18722 // remove from selection 18723 var index = this.selection.indexOf(item.id); 18724 if (index != -1) this.selection.splice(index, 1); 18725 18726 // remove from group 18727 item.parent && item.parent.remove(item); 18728}; 18729 18730/** 18731 * Create an array containing all items being a range (having an end date) 18732 * @param {Array.<Object>} array 18733 * @returns {Array} 18734 * @private 18735 */ 18736ItemSet.prototype._constructByEndArray = function (array) { 18737 var endArray = []; 18738 18739 for (var i = 0; i < array.length; i++) { 18740 if (array[i] instanceof RangeItem) { 18741 endArray.push(array[i]); 18742 } 18743 } 18744 return endArray; 18745}; 18746 18747/** 18748 * Register the clicked item on touch, before dragStart is initiated. 18749 * 18750 * dragStart is initiated from a mousemove event, AFTER the mouse/touch is 18751 * already moving. Therefore, the mouse/touch can sometimes be above an other 18752 * DOM element than the item itself. 18753 * 18754 * @param {Event} event 18755 * @private 18756 */ 18757ItemSet.prototype._onTouch = function (event) { 18758 // store the touched item, used in _onDragStart 18759 this.touchParams.item = this.itemFromTarget(event); 18760 this.touchParams.dragLeftItem = event.target.dragLeftItem || false; 18761 this.touchParams.dragRightItem = event.target.dragRightItem || false; 18762 this.touchParams.itemProps = null; 18763}; 18764 18765/** 18766 * Given an group id, returns the index it has. 18767 * 18768 * @param {number} groupId 18769 * @returns {number} index / groupId 18770 * @private 18771 */ 18772ItemSet.prototype._getGroupIndex = function (groupId) { 18773 for (var i = 0; i < this.groupIds.length; i++) { 18774 if (groupId == this.groupIds[i]) return i; 18775 } 18776}; 18777 18778/** 18779 * Start dragging the selected events 18780 * @param {Event} event 18781 * @private 18782 */ 18783ItemSet.prototype._onDragStart = function (event) { 18784 if (this.touchParams.itemIsDragging) { 18785 return; 18786 } 18787 var item = this.touchParams.item || null; 18788 var me = this; 18789 var props; 18790 18791 if (item && (item.selected || this.options.itemsAlwaysDraggable.item)) { 18792 18793 if (this.options.editable.overrideItems && !this.options.editable.updateTime && !this.options.editable.updateGroup) { 18794 return; 18795 } 18796 18797 // override options.editable 18798 if (item.editable != null && !item.editable.updateTime && !item.editable.updateGroup && !this.options.editable.overrideItems) { 18799 return; 18800 } 18801 18802 var dragLeftItem = this.touchParams.dragLeftItem; 18803 var dragRightItem = this.touchParams.dragRightItem; 18804 this.touchParams.itemIsDragging = true; 18805 this.touchParams.selectedItem = item; 18806 18807 if (dragLeftItem) { 18808 props = { 18809 item: dragLeftItem, 18810 initialX: event.center.x, 18811 dragLeft: true, 18812 data: this._cloneItemData(item.data) 18813 }; 18814 18815 this.touchParams.itemProps = [props]; 18816 } else if (dragRightItem) { 18817 props = { 18818 item: dragRightItem, 18819 initialX: event.center.x, 18820 dragRight: true, 18821 data: this._cloneItemData(item.data) 18822 }; 18823 18824 this.touchParams.itemProps = [props]; 18825 } else if (this.options.editable.add && (event.srcEvent.ctrlKey || event.srcEvent.metaKey)) { 18826 // create a new range item when dragging with ctrl key down 18827 this._onDragStartAddItem(event); 18828 } else { 18829 if (this.groupIds.length < 1) { 18830 // Mitigates a race condition if _onDragStart() is 18831 // called after markDirty() without redraw() being called between. 18832 this.redraw(); 18833 } 18834 18835 var baseGroupIndex = this._getGroupIndex(item.data.group); 18836 18837 var itemsToDrag = this.options.itemsAlwaysDraggable.item && !item.selected ? [item.id] : this.get
18837Selection(); 18838 18839 this.touchParams.itemProps = itemsToDrag.map(function (id) { 18840 var item = me.items[id]; 18841 var groupIndex = me._getGroupIndex(item.data.group); 18842 return { 18843 item: item, 18844 initialX: event.center.x, 18845 groupOffset: baseGroupIndex - groupIndex, 18846 data: this._cloneItemData(item.data) 18847 }; 18848 }.bind(this)); 18849 } 18850 18851 event.stopPropagation(); 18852 } else if (this.options.editable.add && (event.srcEvent.ctrlKey || event.srcEvent.metaKey)) { 18853 // create a new range item when dragging with ctrl key down 18854 this._onDragStartAddItem(event); 18855 } 18856}; 18857 18858/** 18859 * Start creating a new range item by dragging. 18860 * @param {Event} event 18861 * @private 18862 */ 18863ItemSet.prototype._onDragStartAddItem = function (event) { 18864 var xAbs; 18865 var x; 18866 var snap = this.options.snap || null; 18867 18868 if (this.options.rtl) { 18869 xAbs = util.getAbsoluteRight(this.dom.frame); 18870 x = xAbs - event.center.x + 10; // plus 10 to compensate for the drag starting as soon as you've moved 10px 18871 } else { 18872 xAbs = util.getAbsoluteLeft(this.dom.frame); 18873 x = event.center.x - xAbs - 10; // minus 10 to compensate for the drag starting as soon as you've moved 10px 18874 } 18875 18876 var time = this.body.util.toTime(x); 18877 var scale = this.body.util.getScale(); 18878 var step = this.body.util.getStep(); 18879 var start = snap ? snap(time, scale, step) : time; 18880 var end = start; 18881 18882 var itemData = { 18883 type: 'range', 18884 start: start, 18885 end: end, 18886 content: 'new item' 18887 }; 18888 18889 var id = util.randomUUID(); 18890 itemData[this.itemsData._fieldId] = id; 18891 18892 var group = this.groupFromTarget(event); 18893 if (group) { 18894 itemData.group = group.groupId; 18895 } 18896 var newItem = new RangeItem(itemData, this.conversion, this.options); 18897 newItem.id = id; // TODO: not so nice setting id afterwards 18898 newItem.data = this._cloneItemData(itemData); 18899 this._addItem(newItem); 18900 this.touchParams.selectedItem = newItem; 18901 18902 var props = { 18903 item: newItem, 18904 initialX: event.center.x, 18905 data: newItem.data 18906 }; 18907 18908 if (this.options.rtl) { 18909 props.dragLeft = true; 18910 } else { 18911 props.dragRight = true; 18912 } 18913 this.touchParams.itemProps = [props]; 18914 18915 event.stopPropagation(); 18916}; 18917 18918/** 18919 * Drag selected items 18920 * @param {Event} event 18921 * @private 18922 */ 18923ItemSet.prototype._onDrag = function (event) { 18924 if (this.touchParams.itemProps) { 18925 event.stopPropagation(); 18926 18927 var me = this; 18928 var snap = this.options.snap || null; 18929 var xOffset; 18930 18931 if (this.options.rtl) { 18932 xOffset = this.body.dom.root.offsetLeft + this.body.domProps.right.width; 18933 } else { 18934 xOffset = this.body.dom.root.offsetLeft + this.body.domProps.left.width; 18935 } 18936 18937 var scale = this.body.util.getScale(); 18938 var step = this.body.util.getStep(); 18939 18940 //only calculate the new group for the item that's actually dragged 18941 var selectedItem = this.touchParams.selectedItem; 18942 var updateGroupAllowed = (this.options.editable.overrideItems || selectedItem.editable == null) && this.options.editable.updateGroup || !this.options.editable.overrideItems && selectedItem.editable != null && selectedItem.editable.updateGroup; 18943 var newGroupBase = null; 18944 if (updateGroupAllowed && selectedItem) { 18945 if (selectedItem.data.group != undefined) { 18946 // drag from one group to another 18947 var group = me.groupFromTarget(event); 18948 if (group) { 18949 //we know the offset for all items, so the new group for all items 18950 //will be relative to this one. 18951 newGroupBase = this._getGroupIndex(group.groupId); 18952 } 18953 } 18954 } 18955 18956 // move 18957 this.touchParams.itemProps.forEach(function (props) { 18958 var current = me.body.util.toTime(event.center.x - xOffset); 18959 var initial = me.body.util.toTime(props.initialX - xOffset); 18960 var offset; 18961 var initialStart; 18962 var initialEnd; 18963 var start; 18964 var end; 18965 18966 if (this.options.rtl) { 18967 offset = -(current - initial); // ms 18968 } else { 18969 offset = current - initial; // ms 18970 } 18971 18972 var itemData = this._cloneItemData(props.item.data); // clone the data 18973 if (props.item.editable != null && !props.item.editable.updateTime && !props.item.editable.updateGroup && !me.options.editable.overrideItems) { 18974 return; 18975 } 18976 18977 var updateTimeAllowed = (this.options.editable.overrideItems || selectedItem.editable == null) && this.options.editable.updateTime || !this.options.editable.overrideItems && selectedItem.editable != null && selectedItem.editable.updateTime; 18978 if (updateTimeAllowed) { 18979 if (props.dragLeft) { 18980 // drag left side of a range item 18981 if (this.options.rtl) { 18982 if (itemData.end != undefined) { 18983 initialEnd = util.convert(props.data.end, 'Date'); 18984 end = new Date(initialEnd.valueOf() + offset); 18985 // TODO: pass a Moment instead of a Date to snap(). (Breaking change) 18986 itemData.end = snap ? snap(end, scale, step) : end; 18987 } 18988 } else { 18989 if (itemData.start != undefined) { 18990 initialStart = util.convert(props.data.start, 'Date'); 18991 start = new Date(initialStart.valueOf() + offset); 18992 // TODO: pass a Moment instead of a Date to snap(). (Breaking change) 18993 itemData.start = snap ? snap(start, scale, step) : start; 18994 } 18995 } 18996 } else if (props.dragRight) { 18997 // drag right side of a range item 18998 if (this.options.rtl) { 18999 if (itemData.start != undefined) { 19000 initialStart = util.convert(props.data.start, 'Date'); 19001 start = new Date(initialStart.valueOf() + offset); 19002 // TODO: pass a Moment instead of a Date to snap(). (Breaking change) 19003 itemData.start = snap ? snap(start, scale, step) : start; 19004 } 19005 } else { 19006 if (itemData.end != undefined) { 19007 initialEnd = util.convert(props.data.end, 'Date'); 19008 end = new Date(initialEnd.valueOf() + offset); 19009 // TODO: pass a Moment instead of a Date to snap(). (Breaking change) 19010 itemData.end = snap ? snap(end, scale, step) : end; 19011 } 19012 } 19013 } else { 19014 // drag both start and end 19015 if (itemData.start != undefined) { 19016 19017 initialStart = util.convert(props.data.start, 'Date').valueOf(); 19018 start = new Date(initialStart + offset); 19019 19020 if (itemData.end != undefined) { 19021 initialEnd = util.convert(props.data.end, 'Date'); 19022 var duration = initialEnd.valueOf() - initialStart.valueOf(); 19023 19024 // TODO: pass a Moment instead of a Date to snap(). (Breaking change) 19025 itemData.start = snap ? snap(start, scale, step) : start; 19026 itemData.end = new Date(itemData.start.valueOf() + duration); 19027 } else { 19028 // TODO: pass a Moment instead of a Date to snap(). (Breaking change) 19029 itemData.start = snap ? snap(start, scale, step) : start; 19030 } 19031 } 19032 } 19033 } 19034 19035 if (updateGroupAllowed && !props.dragLeft && !props.dragRight && newGroupBase != null) { 19036 if (itemData.group != undefined) { 19037 var newOffset = newGroupBase - props.groupOffset; 19038 19039 //make sure we stay in bounds 19040 newOffset = Math.max(0, newOffset); 19041 newOffset = Math.min(me.groupIds.length - 1, newOffset); 19042 itemData.group = me.groupIds[newOffset]; 19043 } 19044 } 19045 19046 // confirm moving the item 19047 itemData = this._cloneItemData(itemData); // convert start and end to the correct type 19048 me.options.onMoving(itemData, function (itemData) { 19049 if (itemData) { 19050 props.item.setData(this._cloneItemData(itemData, 'Date')); 19051 } 19052 }.bind(this)); 19053 }.bind(this)); 19054 19055 this.body.emitter.emit('_change'); 19056 } 19057}; 19058 19059/** 19060 * Move an item to another group 19061 * @param {Item} item 19062 * @param {string | number} groupId 19063 * @private 19064 */ 19065ItemSet.prototype._moveToGroup = function (item, groupId) { 19066 var group = this.groups[groupId]; 19067 if (group && group.groupId != item.data.group) { 19068 var oldGroup = item.parent; 19069 oldGroup.remove(item); 19070 oldGroup.order(); 19071 19072 item.data.group = group.groupId; 19073 19074 group.add(item); 19075 group.order(); 19076 } 19077}; 19078 19079/** 19080 * End of dragging selected items 19081 * @param {Event} event 19082 * @private 19083 */ 19084ItemSet.prototype._onDragEnd = function (event) { 19085 this.touchParams.itemIsDragging = false;
19086 if (this.touchParams.itemProps) { 19087 event.stopPropagation(); 19088 19089 var me = this; 19090 var dataset = this.itemsData.getDataSet(); 19091 var itemProps = this.touchParams.itemProps; 19092 this.touchParams.itemProps = null; 19093 19094 itemProps.forEach(function (props) { 19095 var id = props.item.id; 19096 var exists = me.itemsData.get(id, me.itemOptions) != null; 19097 19098 if (!exists) { 19099 // add a new item 19100 me.options.onAdd(props.item.data, function (itemData) { 19101 me._removeItem(props.item); // remove temporary item 19102 if (itemData) { 19103 me.itemsData.getDataSet().add(itemData); 19104 } 19105 19106 // force re-stacking of all items next redraw 19107 me.body.emitter.emit('_change'); 19108 }); 19109 } else { 19110 // update existing item 19111 var itemData = this._cloneItemData(props.item.data); // convert start and end to the correct type 19112 me.options.onMove(itemData, function (itemData) { 19113 if (itemData) { 19114 // apply changes 19115 itemData[dataset._fieldId] = id; // ensure the item contains its id (can be undefined) 19116 dataset.update(itemData); 19117 } else { 19118 // restore original values 19119 props.item.setData(props.data); 19120 19121 me.body.emitter.emit('_change'); 19122 } 19123 }); 19124 } 19125 }.bind(this)); 19126 } 19127}; 19128 19129ItemSet.prototype._onGroupClick = function (event) { 19130 var group = this.groupFromTarget(event); 19131 19132 if (!group || !group.nestedGroups) return; 19133 19134 var groupsData = this.groupsData.getDataSet(); 19135 19136 var nestingGroup = groupsData.get(group.groupId); 19137 if (nestingGroup.showNested == undefined) { 19138 nestingGroup.showNested = true; 19139 } 19140 nestingGroup.showNested = !nestingGroup.showNested; 19141 19142 var nestedGroups = groupsData.get(group.nestedGroups).map(function (nestedGroup) { 19143 nestedGroup.visible = nestingGroup.showNested; 19144 return nestedGroup; 19145 }); 19146 19147 groupsData.update(nestedGroups.concat(nestingGroup)); 19148 19149 if (nestingGroup.showNested) { 19150 util.removeClassName(group.dom.label, 'collapsed'); 19151 util.addClassName(group.dom.label, 'expanded'); 19152 } else { 19153 util.removeClassName(group.dom.label, 'expanded'); 19154 var collapsedDirClassName = this.options.rtl ? 'collapsed-rtl' : 'collapsed'; 19155 util.addClassName(group.dom.label, collapsedDirClassName); 19156 } 19157}; 19158 19159ItemSet.prototype._onGroupDragStart = function (event) { 19160 if (this.options.groupEditable.order) { 19161 this.groupTouchParams.group = this.groupFromTarget(event); 19162 19163 if (this.groupTouchParams.group) { 19164 event.stopPropagation(); 19165 19166 this.groupTouchParams.originalOrder = this.groupsData.getIds({ 19167 order: this.options.groupOrder 19168 }); 19169 } 19170 } 19171}; 19172 19173ItemSet.prototype._onGroupDrag = function (event) { 19174 if (this.options.groupEditable.order && this.groupTouchParams.group) { 19175 event.stopPropagation(); 19176 19177 var groupsData = this.groupsData; 19178 if (this.groupsData instanceof DataView) { 19179 groupsData = this.groupsData.getDataSet(); 19180 } 19181 // drag from one group to another 19182 var group = this.groupFromTarget(event); 19183 19184 // try to avoid toggling when groups differ in height 19185 if (group && group.height != this.groupTouchParams.group.height) { 19186 var movingUp = group.top < this.groupTouchParams.group.top; 19187 var clientY = event.center ? event.center.y : event.clientY; 19188 var targetGroupTop = util.getAbsoluteTop(group.dom.foreground); 19189 var draggedGroupHeight = this.groupTouchParams.group.height; 19190 if (movingUp) { 19191 // skip swapping the groups when the dragged group is not below clientY afterwards 19192 if (targetGroupTop + draggedGroupHeight < clientY) { 19193 return; 19194 } 19195 } else { 19196 var targetGroupHeight = group.height; 19197 // skip swapping the groups when the dragged group is not below clientY afterwards 19198 if (targetGroupTop + targetGroupHeight - draggedGroupHeight > clientY) { 19199 return; 19200 } 19201 } 19202 } 19203 19204 if (group && group != this.groupTouchParams.group) { 19205 var targetGroup = groupsData.get(group.groupId); 19206 var draggedGroup = groupsData.get(this.groupTouchParams.group.groupId); 19207 19208 // switch groups 19209 if (draggedGroup && targetGroup) { 19210 this.options.groupOrderSwap(draggedGroup, targetGroup, groupsData); 19211 groupsData.update(draggedGroup); 19212 groupsData.update(targetGroup); 19213 } 19214 19215 // fetch current order of groups 19216 var newOrder = groupsData.getIds({ 19217 order: this.options.groupOrder 19218 }); 19219 19220 // in case of changes since _onGroupDragStart 19221 if (!util.equalArray(newOrder, this.groupTouchParams.originalOrder)) { 19222 var origOrder = this.groupTouchParams.originalOrder; 19223 var draggedId = this.groupTouchParams.group.groupId; 19224 var numGroups = Math.min(origOrder.length, newOrder.length); 19225 var curPos = 0; 19226 var newOffset = 0; 19227 var orgOffset = 0; 19228 while (curPos < numGroups) { 19229 // as long as the groups are where they should be step down along the groups order 19230 while (curPos + newOffset < numGroups && curPos + orgOffset < numGroups && newOrder[curPos + newOffset] == origOrder[curPos + orgOffset]) { 19231 curPos++; 19232 } 19233 19234 // all ok 19235 if (curPos + newOffset >= numGroups) { 19236 break; 19237 } 19238 19239 // not all ok 19240 // if dragged group was move upwards everything below should have an offset 19241 if (newOrder[curPos + newOffset] == draggedId) { 19242 newOffset = 1; 19243 } 19244 // if dragged group was move downwards everything above should have an offset 19245 else if (origOrder[curPos + orgOffset] == draggedId) { 19246 orgOffset = 1; 19247 } 19248 // found a group (apart from dragged group) that has the wrong position -> switch with the 19249 // group at the position where other one should be, fix index arrays and continue 19250 else { 19251 var slippedPosition = newOrder.indexOf(origOrder[curPos + orgOffset]); 19252 var switchGroup = groupsData.get(newOrder[curPos + newOffset]); 19253 var shouldBeGroup = groupsData.get(origOrder[curPos + orgOffset]); 19254 this.options.groupOrderSwap(switchGroup, shouldBeGroup, groupsData); 19255 groupsData.update(switchGroup); 19256 groupsData.update(shouldBeGroup); 19257 19258 var switchGroupId = newOrder[curPos + newOffset]; 19259 newOrder[curPos + newOffset] = origOrder[curPos + orgOffset]; 19260 newOrder[slippedPosition] = switchGroupId; 19261 19262 curPos++; 19263 } 19264 } 19265 } 19266 } 19267 } 19268}; 19269 19270ItemSet.prototype._onGroupDragEnd = function (event) { 19271 if (this.options.groupEditable.order && this.groupTouchParams.group) { 19272 event.stopPropagation(); 19273 19274 // update existing group 19275 var me = this; 19276 var id = me.groupTouchParams.group.groupId; 19277 var dataset = me.groupsData.getDataSet(); 19278 var groupData = util.extend({}, dataset.get(id)); // clone the data 19279 me.options.onMoveGroup(groupData, function (groupData) { 19280 if (groupData) { 19281 // apply changes 19282 groupData[dataset._fieldId] = id; // ensure the group contains its id (can be undefined) 19283 dataset.update(groupData); 19284 } else { 19285 19286 // fetch current order of groups 19287 var newOrder = dataset.getIds({ 19288 order: me.options.groupOrder 19289 }); 19290 19291 // restore original order 19292 if (!util.equalArray(newOrder, me.groupTouchParams.originalOrder)) { 19293 var origOrder = me.groupTouchParams.originalOrder; 19294 var numGroups = Math.min(origOrder.length, newOrder.length); 19295 var curPos = 0; 19296 while (curPos < numGroups) { 19297 // as long as the groups are where they should be step down along the groups order 19298 while (curPos < numGroups && newOrder[curPos] == origOrder[curPos]) { 19299 curPos++; 19300 } 19301 19302 // all ok 19303 if (curPos >= numGroups) { 19304 break; 19305 } 19306 19307 // found a group that has the wrong position -> switch with the 19308 // group at the position where other one should be, fix index arrays and continue 19309 var slippedPosition = newOrder.indexOf(origOrder[curPos]); 19310 var switchGroup = dataset.get(newOrder[curPos]); 19311 var shouldBeGroup = dataset.get(origOrder[curPos]); 19312 me.options.groupOrderSwap(switchGroup, shouldBeGroup, dataset); 19313 dataset.update(switchGroup); 19314 dataset.update(shouldBeGroup); 19315 19316 var switchGroupId = newOrder[curPos]; 19317 newOrder[curPos] = origOrder[curPos]; 19318 newOrder[slippedPosition] = switchGroupId; 19319 19320 curPos++; 19321 } 19322 } 19323 } 19324 }); 19325 19326 me.body.emitter.emit('groupDragged', { groupId: id }); 19327 } 19328}; 19329 19330/** 19331 * Handle selecting/deselecting an item when tapping it 19332 * @param {Event} event 19333 * @private 19334 */ 19335ItemSet.prototype._onSelectItem = function (event) { 19336 if (!this.options.selectable) return; 19337 19338 var ctrlKey = event.srcEvent && (event.srcEvent.ctrlKey || event.srcEvent.metaKey); 19339 var shiftKey = event.srcEvent && event.srcEvent.shiftKey;
19340 if (ctrlKey || shiftKey) { 19341 this._onMultiSelectItem(event); 19342 return; 19343 } 19344 19345 var oldSelection = this.getSelection(); 19346 19347 var item = this.itemFromTarget(event); 19348 var selection = item ? [item.id] : []; 19349 this.setSelection(selection); 19350 19351 var newSelection = this.getSelection(); 19352 19353 // emit a select event, 19354 // except when old selection is empty and new selection is still empty 19355 if (newSelection.length > 0 || oldSelection.length > 0) { 19356 this.body.emitter.emit('select', { 19357 items: newSelection, 19358 event: event 19359 }); 19360 } 19361}; 19362 19363/** 19364 * Handle hovering an item 19365 * @param {Event} event 19366 * @private 19367 */ 19368ItemSet.prototype._onMouseOver = function (event) { 19369 var item = this.itemFromTarget(event); 19370 if (!item) return; 19371 19372 // Item we just left 19373 var related = this.itemFromRelatedTarget(event); 19374 if (item === related) { 19375 // We haven't changed item, just element in the item 19376 return; 19377 } 19378 19379 var title = item.getTitle(); 19380 if (this.options.showTooltips && title) { 19381 if (this.popup == null) { 19382 this.popup = new Popup(this.body.dom.root, this.options.tooltip.overflowMethod || 'flip'); 19383 } 19384 19385 this.popup.setText(title); 19386 var container = this.body.dom.centerContainer; 19387 this.popup.setPosition(event.clientX - util.getAbsoluteLeft(container) + container.offsetLeft, event.clientY - util.getAbsoluteTop(container) + container.offsetTop); 19388 this.popup.show(); 19389 } else { 19390 // Hovering over item without a title, hide popup 19391 // Needed instead of _just_ in _onMouseOut due to #2572 19392 if (this.popup != null) { 19393 this.popup.hide(); 19394 } 19395 } 19396 19397 this.body.emitter.emit('itemover', { 19398 item: item.id, 19399 event: event 19400 }); 19401}; 19402ItemSet.prototype._onMouseOut = function (event) { 19403 var item = this.itemFromTarget(event); 19404 if (!item) return; 19405 19406 // Item we are going to 19407 var related = this.itemFromRelatedTarget(event); 19408 if (item === related) { 19409 // We aren't changing item, just element in the item 19410 return; 19411 } 19412 19413 if (this.popup != null) { 19414 this.popup.hide(); 19415 } 19416 19417 this.body.emitter.emit('itemout', { 19418 item: item.id, 19419 event: event 19420 }); 19421}; 19422ItemSet.prototype._onMouseMove = function (event) { 19423 var item = this.itemFromTarget(event); 19424 if (!item) return; 19425 19426 if (this.options.showTooltips && this.options.tooltip.followMouse) { 19427 if (this.popup) { 19428 if (!this.popup.hidden) { 19429 var container = this.body.dom.centerContainer; 19430 this.popup.setPosition(event.clientX - util.getAbsoluteLeft(container) + container.offsetLeft, event.clientY - util.getAbsoluteTop(container) + container.offsetTop); 19431 this.popup.show(); // Redraw 19432 } 19433 } 19434 } 19435}; 19436 19437/** 19438 * Handle mousewheel 19439 * @param {Event} event The event 19440 * @private 19441 */ 19442ItemSet.prototype._onMouseWheel = function (event) { 19443 if (this.touchParams.itemIsDragging) { 19444 this._onDragEnd(event); 19445 } 19446}; 19447 19448/** 19449 * Handle updates of an item on double tap 19450 * @param {vis.Item} item The item 19451 * @private 19452 */ 19453ItemSet.prototype._onUpdateItem = function (item) { 19454 if (!this.options.selectable) return; 19455 if (!this.options.editable.add) return; 19456 19457 var me = this; 19458 19459 if (item) { 19460 // execute async handler to update the item (or cancel it) 19461 var itemData = me.itemsData.get(item.id); // get a clone of the data from the dataset 19462 this.options.onUpdate(itemData, function (itemData) { 19463 if (itemData) { 19464 me.itemsData.getDataSet().update(itemData); 19465 } 19466 }); 19467 } 19468}; 19469 19470/** 19471 * Handle drop event of data on item 19472 * Only called when `objectData.target === 'item'. 19473 * @param {Event} event The event 19474 * @private 19475 */ 19476ItemSet.prototype._onDropObjectOnItem = function (event) { 19477 var item = this.itemFromTarget(event); 19478 var objectData = JSON.parse(event.dataTransfer.getData("text")); 19479 this.options.onDropObjectOnItem(objectData, item); 19480}; 19481 19482/** 19483 * Handle creation of an item on double tap or drop of a drag event 19484 * @param {Event} event The event 19485 * @private 19486 */ 19487ItemSet.prototype._onAddItem = function (event) { 19488 if (!this.options.selectable) return; 19489 if (!this.options.editable.add) return; 19490 19491 var me = this; 19492 var snap = this.options.snap || null; 19493 var xAbs; 19494 var x; 19495 // add item 19496 if (this.options.rtl) {
19497 xAbs = util.getAbsoluteRight(this.dom.frame); 19498 x = xAbs - event.center.x; 19499 } else { 19500 xAbs = util.getAbsoluteLeft(this.dom.frame); 19501 x = event.center.x - xAbs; 19502 } 19503 // var xAbs = util.getAbsoluteLeft(this.dom.frame); 19504 // var x = event.center.x - xAbs; 19505 var start = this.body.util.toTime(x); 19506 var scale = this.body.util.getScale(); 19507 var step = this.body.util.getStep(); 19508 var end; 19509 19510 var newItemData; 19511 if (event.type == 'drop') { 19512 newItemData = JSON.parse(event.dataTransfer.getData("text")); 19513 newItemData.content = newItemData.content ? newItemData.content : 'new item'; 19514 newItemData.start = newItemData.start ? newItemData.start : snap ? snap(start, scale, step) : start; 19515 newItemData.type = newItemData.type || 'box'; 19516 newItemData[this.itemsData._fieldId] = newItemData.id || util.randomUUID(); 19517 19518 if (newItemData.type == 'range' && !newItemData.end) { 19519 end = this.body.util.toTime(x + this.props.width / 5); 19520 newItemData.end = snap ? snap(end, scale, step) : end; 19521 } 19522 } else { 19523 newItemData = { 19524 start: snap ? snap(start, scale, step) : start, 19525 content: 'new item' 19526 }; 19527 newItemData[this.itemsData._fieldId] = util.randomUUID(); 19528 19529 // when default type is a range, add a default end date to the new item 19530 if (this.options.type === 'range') { 19531 end = this.body.util.toTime(x + this.props.width / 5); 19532 newItemData.end = snap ? snap(end, scale, step) : end; 19533 } 19534 } 19535 19536 var group = this.groupFromTarget(event); 19537 if (group) { 19538 newItemData.group = group.groupId; 19539 } 19540 19541 // execute async handler to customize (or cancel) adding an item 19542 newItemData = this._cloneItemData(newItemData); // convert start and end to the correct type 19543 this.options.onAdd(newItemData, function (item) { 19544 if (item) { 19545 me.itemsData.getDataSet().add(item); 19546 if (event.type == 'drop') { 19547 me.setSelection([item.id]); 19548 } 19549 // TODO: need to trigger a redraw? 19550 } 19551 }); 19552}; 19553 19554/** 19555 * Handle selecting/deselecting multiple items when holding an item 19556 * @param {Event} event 19557 * @private 19558 */ 19559ItemSet.prototype._onMultiSelectItem = function (event) { 19560 if (!this.options.selectable) return; 19561 19562 var item = this.itemFromTarget(event); 19563 19564 if (item) { 19565 // multi select items (if allowed) 19566
19567 var selection = this.options.multiselect ? this.getSelection() // take current selection 19568 : []; // deselect current selection 19569 19570 var shiftKey = event.srcEvent && event.srcEvent.shiftKey || false; 19571 19572 if (shiftKey && this.options.multiselect) { 19573 // select all items between the old selection and the tapped item 19574 var itemGroup = this.itemsData.get(item.id).group; 19575 19576 // when filtering get the group of the last selected item 19577 var lastSelectedGroup = undefined; 19578 if (this.options.multiselectPerGroup) { 19579 if (selection.length > 0) { 19580 lastSelectedGroup = this.itemsData.get(selection[0]).group; 19581 } 19582 } 19583 19584 // determine the selection range 19585 if (!this.options.multiselectPerGroup || lastSelectedGroup == undefined || lastSelectedGroup == itemGroup) { 19586 selection.push(item.id); 19587 } 19588 var range = ItemSet._getItemRange(this.itemsData.get(selection, this.itemOptions)); 19589 19590 if (!this.options.multiselectPerGroup || lastSelectedGroup == itemGroup) { 19591 // select all items within the selection range 19592 selection = []; 19593 for (var id in this.items) { 19594 if (this.items.hasOwnProperty(id)) { 19595 var _item = this.items[id]; 19596 var start = _item.data.start; 19597 var end = _item.data.end !== undefined ? _item.data.end : start; 19598 19599 if (start >= range.min && end <= range.max && (!this.options.multiselectPerGroup || lastSelectedGroup == this.itemsData.get(_item.id).group) && !(_item instanceof BackgroundItem)) { 19600 selection.push(_item.id); // do not use id but item.id, id itself is stringified 19601 } 19602 } 19603 } 19604 } 19605 } else { 19606 // add/remove this item from the current selection 19607 var index = selection.indexOf(item.id); 19608 if (index == -1) { 19609 // item is not yet selected -> select it 19610 selection.push(item.id); 19611 } else { 19612 // item is already selected -> deselect it 19613 selection.splice(index, 1); 19614 } 19615 } 19616 19617 this.setSelection(selection); 19618 19619 this.body.emitter.emit('select', { 19620 items: this.getSelection(), 19621 event: event 19622 }); 19623 } 19624}; 19625 19626/** 19627 * Calculate the time range of a list of items 19628 * @param {Array.<Object>} itemsData 19629 * @return {{min: Date, max: Date}} Returns the range of the provided items 19630 * @private 19631 */ 19632ItemSet._getItemRange = function (itemsData) { 19633 var max = null; 19634 var min = null; 19635 19636 itemsData.forEach(function (data) { 19637 if (min == null || data.start < min) { 19638 min = data.start; 19639 } 19640 19641 if (data.end != undefined) { 19642 if (max == null || data.end > max) { 19643 max = data.end; 19644 } 19645 } else { 19646 if (max == null || data.start > max) { 19647 max = data.start; 19648 } 19649 } 19650 }); 19651 19652 return { 19653 min: min, 19654 max: max 19655 }; 19656}; 19657 19658/** 19659 * Find an item from an element: 19660 * searches for the attribute 'timeline-item' in the element's tree 19661 * @param {HTMLElement} element 19662 * @return {Item | null} item 19663 */ 19664ItemSet.prototype.itemFromElement = function (element) { 19665 var cur = element; 19666 while (cur) { 19667 if (cur.hasOwnProperty('timeline-item')) { 19668 return cur['timeline-item']; 19669 } 19670 cur = cur.parentNode; 19671 } 19672 19673 return null; 19674}; 19675 19676/** 19677 * Find an item from an event target: 19678 * searches for the attribute 'timeline-item' in the event target's element tree 19679 * @param {Event} event 19680 * @return {Item | null} item 19681 */ 19682ItemSet.prototype.itemFromTarget = function (event) { 19683 return this.itemFromElement(event.target); 19684}; 19685 19686/** 19687 * Find an item from an event's related target: 19688 * searches for the attribute 'timeline-item' in the related target's element tree 19689 * @param {Event} event 19690 * @return {Item | null} item 19691 */ 19692ItemSet.prototype.itemFromRelatedTarget = function (event) { 19693 return this.itemFromElement(event.relatedTarget); 19694}; 19695 19696/** 19697 * Find the Group from an event target: 19698 * searches for the attribute 'timeline-group' in the event target's element tree 19699 * @param {Event} event 19700 * @return {Group | null} group 19701 */ 19702ItemSet.prototype.groupFromTarget = function (event) { 19703 var clientY = event.center ? event.center.y : event.clientY; 19704 var groupIds = this.groupIds; 19705 19706 if (groupIds.length <= 0 && this.groupsData) { 19707 groupIds = this.groupsData.getIds({ 19708 order: this.options.groupOrder 19709 }); 19710 } 19711 19712 for (var i = 0; i < groupIds.length; i++) { 19713 var groupId = groupIds[i]; 19714 var group = this.groups[groupId]; 19715 var foreground = group.dom.foreground; 19716 var top = util.getAbsoluteTop(foreground); 19717 if (clientY > top && clientY < top + foreground.offsetHeight) { 19718 return group; 19719 } 19720 19721 if (this.options.orientation.item === 'top') { 19722 if (i === this.groupIds.length - 1 && clientY > top) { 19723 return group; 19724 } 19725 } else { 19726 if (i === 0 && clientY < top + foreground.offset) { 19727 return group; 19728 } 19729 } 19730 } 19731 19732 return null; 19733}; 19734 19735/** 19736 * Find the ItemSet from an event target: 19737 * searches for the attribute 'timeline-itemset' in the event target's element tree 19738 * @param {Event} event 19739 * @return {ItemSet | null} item 19740 */ 19741ItemSet.itemSetFromTarget = function (event) { 19742 var target = event.target; 19743 while (target) { 19744 if (target.hasOwnProperty('timeline-itemset')) { 19745 return target['timeline-itemset']; 19746 } 19747 target = target.parentNode; 19748 } 19749 19750 return null; 19751}; 19752 19753/** 19754 * Clone the data of an item, and "normalize" it: convert the start and end date 19755 * to the type (Date, Moment, ...) configured in the DataSet. If not configured, 19756 * start and end are converted to Date. 19757 * @param {Object} itemData, typically `item.data` 19758 * @param {string} [type] Optional Date type. If not provided, the type from the DataSet is taken 19759 * @return {Object} The cloned object 19760 * @private 19761 */ 19762ItemSet.prototype._cloneItemData = function (itemData, type) { 19763 var clone = util.extend({}, itemData); 19764 19765 if (!type) { 19766 // convert start and end date to the type (Date, Moment, ...) configured in the DataSet 19767 type = this.itemsData.getDataSet()._options.type; 19768 } 19769 19770 if (clone.start != undefined) { 19771 clone.start = util.convert(clone.start, type && type.start || 'Date'); 19772 } 19773 if (clone.end != undefined) { 19774 clone.end = util.convert(clone.end, type && type.end || 'Date'); 19775 } 19776 19777 return clone; 19778}; 19779 19780module.exports = ItemSet; 19781 19782/***/ }), 19783/* 100 */ 19784/***/ (function(module, exports, __webpack_require__) { 19785 19786"use strict"; 19787 19788 19789// Utility functions for ordering and stacking of items 19790var EPSILON = 0.001;
19790 // used when checking collisions, to prevent round-off errors 19791 19792/** 19793 * Order items by their start data 19794 * @param {Item[]} items 19795 */ 19796exports.orderByStart = function (items) { 19797 items.sort(function (a, b) { 19798 return a.data.start - b.data.start; 19799 }); 19800}; 19801 19802/** 19803 * Order items by their end date. If they have no end date, their start date 19804 * is used. 19805 * @param {Item[]} items 19806 */ 19807exports.orderByEnd = function (items) { 19808 items.sort(function (a, b) { 19809 var aTime = 'end' in a.data ? a.data.end : a.data.start, 19810 bTime = 'end' in b.data ? b.data.end : b.data.start; 19811 19812 return aTime - bTime; 19813 }); 19814}; 19815 19816/** 19817 * Adjust vertical positions of the items such that they don't overlap each 19818 * other. 19819 * @param {Item[]} items 19820 * All visible items 19821 * @param {{item: {horizontal: number, vertical: number}, axis: number}} margin 19822 * Margins between items and between items and the axis. 19823 * @param {boolean} [force=false] 19824 * If true, all items will be repositioned. If false (default), only 19825 * items having a top===null will be re-stacked 19826 */ 19827exports.stack = function (items, margin, force) { 19828 if (force) { 19829 // reset top position of all items 19830 for (var i = 0; i < items.length; i++) { 19831 items[i].top = null; 19832 } 19833 } 19834 19835 // calculate new, non-overlapping positions 19836 for (var i = 0; i < items.length; i++) { 19837 // eslint-disable-line no-redeclare 19838 var item = items[i]; 19839 if (item.stack && item.top === null) { 19840 // initialize top position 19841 item.top = margin.axis; 19842 19843 do { 19844 // TODO: optimize checking for overlap. when there is a gap without items, 19845 // you only need to check for items from the next item on, not from zero 19846 var collidingItem = null; 19847 for (var j = 0, jj = items.length; j < jj; j++) { 19848 var other = items[j]; 19849 if (other.top !== null && other !== item && other.stack && exports.collision(item, other, margin.item, other.options.rtl)) { 19850 collidingItem = other; 19851 break; 19852 } 19853 } 19854 19855 if (collidingItem != null) { 19856 // There is a collision. Reposition the items above the colliding element 19857 item.top = collidingItem.top + collidingItem.height + margin.item.vertical; 19858 } 19859 } while (collidingItem); 19860 } 19861 } 19862}; 19863 19864/** 19865 * Adjust vertical positions of the items within a single subgroup such that they 19866 * don't overlap each other. 19867 * @param {Item[]} items 19868 * All items withina subgroup 19869 * @param {{item: {horizontal: number, vertical: number}, axis: number}} margin 19870 * Margins between items and between items and the axis. 19871 * @param {subgroup} subgroup 19872 * The subgroup that is being stacked 19873 */ 19874exports.substack = function (items, margin, subgroup) { 19875 for (var i = 0; i < items.length; i++) { 19876 items[i].top = null; 19877 } 19878 19879 // Set the initial height 19880 var subgroupHeight = subgroup.height; 19881 19882 // calculate new, non-overlapping positions 19883 for (i = 0; i < items.length; i++) { 19884 var item = items[i]; 19885 19886 if (item.stack && item.top === null) { 19887 // initialize top position 19888 item.top = item.baseTop; //margin.axis + item.baseTop; 19889 19890 do { 19891 // TODO: optimize checking for overlap. when there is a gap without items, 19892 // you only need to check for items from the next item on, not from zero 19893 var collidingItem = null; 19894 for (var j = 0, jj = items.length; j < jj; j++) { 19895 var other = items[j]; 19896 if (other.top !== null && other !== item /*&& other.stack*/ && exports.collision(item, other, margin.item, other.options.rtl)) { 19897 collidingItem = other; 19898 break; 19899 } 19900 } 19901 19902 if (collidingItem != null) { 19903 // There is a collision. Reposition the items above the colliding element 19904 item.top = collidingItem.top + collidingItem.height + margin.item.vertical; // + item.baseTop; 19905 } 19906 19907 if (item.top + item.height > subgroupHeight) { 19908 subgroupHeight = item.top + item.height; 19909 } 19910 } while (collidingItem); 19911 } 19912 } 19913 19914 // Set the new height 19915 subgroup.height = subgroupHeight - subgroup.top + 0.5 * margin.item.vertical; 19916}; 19917 19918/** 19919 * Adjust vertical positions of the items without stacking them 19920 * @param {Item[]} items 19921 * All visible items 19922 * @param {{item: {horizontal: number, vertical: number}, axis: number}} margin 19923 * Margins between items and between items and the axis. 19924 * @param {subgroups[]} subgroups 19925 * All subgroups 19926 * @param {boolean} stackSubgroups 19927 */ 19928exports.nostack = function (items, margin, subgroups, stackSubgroups) { 19929 for (var i = 0; i < items.length; i++) { 19930 if (items[i].data.subgroup == undefined) { 19931 items[i].top = margin.item.vertical; 19932 } else if (items[i].data.subgroup !== undefined && stackSubgroups) { 19933 var newTop = 0; 19934 for (var subgroup in subgroups) { 19935 if (subgroups.hasOwnProperty(subgroup)) { 19936 if (subgroups[subgroup].visible == true && subgroups[subgroup].index < subgroups[items[i].data.subgroup].index) { 19937 newTop += subgroups[subgroup].height; 19938 subgroups[items[i].data.subgroup].top = newTop; 19939 } 19940 } 19941 } 19942 items[i].top = newTop + 0.5 * margin.item.vertical; 19943 } 19944 } 19945 if (!stackSubgroups) { 19946 exports.stackSubgroups(items, margin, subgroups); 19947 } 19948}; 19949 19950/** 19951 * Adjust vertical positions of the subgroups such that they don't overlap each 19952 * other. 19953 * @param {Array.<vis.Item>} items 19954 * @param {{item: {horizontal: number, vertical: number}, axis: number}} margin Margins between items and between items and the axis. 19955 * @param {subgroups[]} subgroups 19956 * All subgroups 19957 */ 19958exports.stackSubgroups = function (items, margin, subgroups) { 19959 for (var subgroup in subgroups) { 19960 if (subgroups.hasOwnProperty(subgroup)) { 19961 19962 subgroups[subgroup].top = 0; 19963 do { 19964 // TODO: optimize checking for overlap. when there is a gap without items, 19965 // you only need to check for items from the next item on, not from zero 19966 var collidingItem = null; 19967 for (var otherSubgroup in subgroups) { 19968 if (subgroups[otherSubgroup].top !== null && otherSubgroup !== subgroup && subgroups[subgroup].index > subgroups[otherSubgroup].index && exports.collisionByTimes(subgroups[subgroup], subgroups[otherSubgroup])) { 19969 collidingItem = subgroups[otherSubgroup]; 19970 break; 19971 } 19972 } 19973 19974 if (collidingItem != null) { 19975 // There is a collision. Reposition the subgroups above the colliding element
19976 subgroups[subgroup].top = collidingItem.top + collidingItem.height; 19977 } 19978 } while (collidingItem); 19979 } 19980 } 19981 for (var i = 0; i < items.length; i++) { 19982 if (items[i].data.subgroup !== undefined) { 19983 items[i].top = subgroups[items[i].data.subgroup].top + 0.5 * margin.item.vertical; 19984 } 19985 } 19986}; 19987 19988/** 19989 * Adjust vertical positions of the subgroups such that they don't overlap each 19990 * other, then stacks the contents of each subgroup individually. 19991 * @param {Item[]} subgroupItems 19992 * All the items in a subgroup 19993 * @param {{item: {horizontal: number, vertical: number}, axis: number}} margin 19994 * Margins between items and between items and the axis. 19995 * @param {subgroups[]} subgroups 19996 * All subgroups 19997 */ 19998exports.stackSubgroupsWithInnerStack = function (subgroupItems, margin, subgroups) { 19999 var doSubStack = false; 20000 20001 // Run subgroups in their order (if any) 20002 var subgroupOrder = []; 20003 20004 for (var subgroup in subgroups) { 20005 if (subgroups[subgroup].hasOwnProperty("index")) { 20006 subgroupOrder[subgroups[subgroup].index] = subgroup; 20007 } else { 20008 subgroupOrder.push(subgroup); 20009 } 20010 } 20011 20012 for (var j = 0; j < subgroupOrder.length; j++) { 20013 subgroup = subgroupOrder[j]; 20014 if (subgroups.hasOwnProperty(subgroup)) { 20015 20016 doSubStack = doSubStack || subgroups[subgroup].stack; 20017 subgroups[subgroup].top = 0; 20018 20019 for (var otherSubgroup in subgroups) { 20020 if (subgroups[otherSubgroup].visible && subgroups[subgroup].index > subgroups[otherSubgroup].index) { 20021 subgroups[subgroup].top += subgroups[otherSubgroup].height; 20022 } 20023 } 20024 20025 var items = subgroupItems[subgroup]; 20026 for (var i = 0; i < items.length; i++) { 20027 if (items[i].data.subgroup !== undefined) { 20028 items[i].top = subgroups[items[i].data.subgroup].top + 0.5 * margin.item.vertical; 20029 20030 if (subgroups[subgroup].stack) { 20031 items[i].baseTop = items[i].top; 20032 } 20033 } 20034 } 20035 20036 if (doSubStack && subgroups[subgroup].stack) { 20037 exports.substack(subgroupItems[subgroup], margin, subgroups[subgroup]); 20038 } 20039 } 20040 } 20041}; 20042 20043/** 20044 * Test if the two provided items collide 20045 * The items must have parameters left, width, top, and height. 20046 * @param {Item} a The first item 20047 * @param {Item} b The second item 20048 * @param {{horizontal: number, vertical: number}} margin 20049 * An object containing a horizontal and vertical 20050 * minimum required margin. 20051 * @param {boolean} rtl 20052 * @return {boolean} true if a and b collide, else false 20053 */ 20054exports.collision = function (a, b, margin, rtl) { 20055 if (rtl) { 20056 return a.right - margin.horizontal + EPSILON < b.right + b.width && a.right + a.width + margin.horizontal - EPSILON > b.right && a.top - margin.vertical + EPSILON < b.top + b.height && a.top + a.height + margin.vertical - EPSILON > b.top; 20057 } else { 20058 return a.left - margin.horizontal + EPSILON < b.left + b.width && a.left + a.width + margin.horizontal - EPSILON > b.left && a.top - margin.vertical + EPSILON < b.top + b.height && a.top + a.height + margin.vertical - EPSILON > b.top; 20059 } 20060}; 20061 20062/** 20063 * Test if the two provided objects collide 20064 * The objects must have parameters start, end, top, and height. 20065 * @param {Object} a The first Object 20066 * @param {Object} b The second Object 20067 * @return {boolean} true if a and b collide, else false 20068 */ 20069exports.collisionByTimes = function (a, b) { 20070 return a.start <= b.start && a.end >= b.start && a.top < b.top + b.height && a.top + a.height > b.top || b.start <= a.start && b.end >= a.start && b.top < a.top + a.height && b.top + b.height > a.top; 20071}; 20072 20073/***/ }), 20074/* 101 */ 20075/***/ (function(module, exports, __webpack_require__) { 20076 20077"use strict"; 20078 20079 20080var Item = __webpack_require__(38); 20081 20082/** 20083 * @constructor BoxItem 20084 * @extends Item 20085 * @param {Object} data Object containing parameters start 20086 * content, className. 20087 * @param {{toScreen: function, toTime: function}} conversion 20088 * Conversion functions from time to screen and vice versa 20089 * @param {Object} [options] Configuration options 20090 * // TODO: describe available options 20091 */ 20092function BoxItem(data, conversion, options) { 20093 this.props = { 20094 dot: { 20095 width: 0, 20096 height: 0 20097 }, 20098 line: { 20099 width: 0, 20100 height: 0 20101 } 20102 }; 20103 this.options = options; 20104 // validate data 20105 if (data) { 20106 if (data.start == undefined) { 20107 throw new Error('Property "start" missing in item ' + data); 20108 } 20109 } 20110 20111 Item.call(this, data, conversion, options); 20112} 20113 20114BoxItem.prototype = new Item(null, null, null); 20115 20116/** 20117 * Check whether this item is visible inside given range 20118 * @param {{start: number, end: number}} range with a timestamp for start and end 20119 * @returns {boolean} True if visible 20120 */ 20121BoxItem.prototype.isVisible = function (range) { 20122 // determine visibility 20123 var isVisible; 20124 var align = this.options.align; 20125 var widthInMs = this.width * range.getMillisecondsPerPixel(); 20126 20127 if (align == 'right') { 20128 isVisible = this.data.start.getTime() > range.start && this.data.start.getTime() - widthInMs < range.end; 20129 } else if (align == 'left') { 20130 isVisible = this.data.start.getTime() + widthInMs > range.start && this.data.start.getTime() < range.end; 20131 } else { 20132 // default or 'center' 20133 isVisible = this.data.start.getTime() + widthInMs / 2 > range.start && this.data.start.getTime() - widthInMs / 2 < range.end; 20134 } 20135 return isVisible; 20136}; 20137 20138BoxItem.prototype._createDomElement = function () { 20139 if (!this.dom) { 20140 // create DOM 20141 this.dom = {}; 20142 20143 // create main box 20144 this.dom.box = document.createElement('DIV'); 20145 20146 // contents box (inside the background box). used for making margins 20147 this.dom.content = document.createElement('DIV'); 20148 this.dom.content.className = 'vis-item-content'; 20149 this.dom.box.appendChild(this.dom.content); 20150 20151 // line to axis 20152 this.dom.line = document.createElement('DIV'); 20153 this.dom.line.className = 'vis-line'; 20154 20155 // dot on axis 20156 this.dom.dot = document.createElement('DIV'); 20157 this.dom.dot.className = 'vis-dot'; 20158 20159 // attach this item as attribute 20160 this.dom.box['timeline-item'] = this; 20161 20162 this.dirty = true; 20163 } 20164}; 20165 20166BoxItem.prototype._appendDomElement = function () { 20167 if (!this.parent) { 20168 throw new Error('Cannot redraw item: no parent attached'); 20169 } 20170 if (!this.dom.box.parentNode) { 20171 var foreground = this.parent.dom.foreground; 20172 if (!foreground) throw new Error('Cannot redraw item: parent has no foreground container element'); 20173 foreground.appendChild(this.dom.box); 20174 } 20175 if (!this.dom.line.parentNode) { 20176 var background = this.parent.dom.background; 20177 if (!background) throw new Error('Cannot redraw item: parent has no background container element'); 20178 background.appendChild(this.dom.line); 20179 } 20180 if (!this.dom.dot.parentNode) { 20181 var axis = this.parent.dom.axis; 20182 if (!background) throw new Error('Cannot redraw item: parent has no axis container element'); 20183 axis.appendChild(this.dom.dot); 20184 } 20185 this.displayed = true; 20186}; 20187 20188BoxItem.prototype._updateDirtyDomComponents = function () { 20189 // An item is marked dirty when: 20190 // - the item is not yet rendered 20191 // - the item's data is changed 20192 // - the item is selected/deselected 20193 if (this.dirty) { 20194 this._updateContents(this.dom.content); 20195 this._updateDataAttributes(this.dom.box); 20196 this._updateStyle(this.dom.box); 20197 20198 var editable = this.editable.updateTime || this.editable.updateGroup; 20199 20200 // update class 20201 var className = (this.data.className ? ' ' + this.data.className : '') + (this.selected ? ' vis-selected' : '') + (editable ? ' vis-editable' : ' vis-readonly'); 20202 this.dom.box.className = 'vis-item vis-box' + className; 20203 this.dom.line.className = 'vis-item vis-line' + className; 20204 this.dom.dot.className = 'vis-item vis-dot' + className; 20205 } 20206}; 20207 20208BoxItem.prototype._getDomComponentsSizes = function () { 20209 return { 20210 previous: { 20211 right: this.dom.box.style.right, 20212 left: this.dom.box.style.left 20213 }, 20214 dot: { 20215 height: this.dom.dot.offsetHeight, 20216 width: this.dom.dot.offsetWidth 20217 }, 20218 line: { 20219 width: this.dom.line.offsetWidth 20220 }, 20221 box: { 20222 width: this.dom.box.offsetWidth, 20223 height: this.dom.box.offsetHeight 20224 } 20225 }; 20226}; 20227 20228BoxItem.prototype._updateDomComponentsSizes = function (sizes) { 20229 if (this.options.rtl) { 20230 this.dom.box.style.right = "0px"; 20231 } else {
20232 this.dom.box.style.left = "0px"; 20233 } 20234 20235 // recalculate size 20236 this.props.dot.height = sizes.dot.height; 20237 this.props.dot.width = sizes.dot.width; 20238 this.props.line.width = sizes.line.width; 20239 this.width = sizes.box.width; 20240 this.height = sizes.box.height; 20241 20242 // restore previous position 20243 if (this.options.rtl) { 20244 this.dom.box.style.right = sizes.previous.right; 20245 } else { 20246 this.dom.box.style.left = sizes.previous.left; 20247 } 20248 20249 this.dirty = false; 20250}; 20251 20252BoxItem.prototype._repaintDomAdditionals = function () { 20253 this._repaintOnItemUpdateTimeTooltip(this.dom.box); 20254 this._repaintDragCenter(); 20255 this._repaintDeleteButton(this.dom.box); 20256}; 20257 20258/** 20259 * Repaint the item 20260 * @param {boolean} [returnQueue=false] return the queue 20261 * @return {boolean} the redraw queue if returnQueue=true 20262 */ 20263BoxItem.prototype.redraw = function (returnQueue) { 20264 var sizes; 20265 var queue = [ 20266 // create item DOM 20267 this._createDomElement.bind(this), 20268 20269 // append DOM to parent DOM 20270 this._appendDomElement.bind(this), 20271 20272 // update dirty DOM 20273 this._updateDirtyDomComponents.bind(this), function () { 20274 if (this.dirty) { 20275 sizes = this._getDomComponentsSizes(); 20276 } 20277 }.bind(this), function () { 20278 if (this.dirty) { 20279 this._updateDomComponentsSizes.bind(this)(sizes); 20280 } 20281 }.bind(this), 20282 20283 // repaint DOM additionals 20284 this._repaintDomAdditionals.bind(this)]; 20285 20286 if (returnQueue) { 20287 return queue; 20288 } else { 20289 var result; 20290 queue.forEach(function (fn) { 20291 result = fn(); 20292 }); 20293 return result; 20294 } 20295}; 20296 20297/** 20298 * Show the item in the DOM (when not already displayed). The items DOM will 20299 * be created when needed. 20300 */ 20301BoxItem.prototype.show = function () { 20302 if (!this.displayed) { 20303 this.redraw(); 20304 } 20305}; 20306 20307/** 20308 * Hide the item from the DOM (when visible) 20309 */ 20310BoxItem.prototype.hide = function () { 20311 if (this.displayed) { 20312 var dom = this.dom; 20313 20314 if (dom.box.parentNode) dom.box.parentNode.removeChild(dom.box); 20315 if (dom.line.parentNode) dom.line.parentNode.removeChild(dom.line); 20316 if (dom.dot.parentNode) dom.dot.parentNode.removeChild(dom.dot); 20317 20318 this.displayed = false; 20319 } 20320}; 20321 20322/** 20323 * Reposition the item horizontally 20324 * @Override 20325 */ 20326BoxItem.prototype.repositionX = function () { 20327 var start = this.conversion.toScreen(this.data.start); 20328 var align = this.options.align; 20329 20330 // calculate left position of the box 20331 if (align == 'right') { 20332 if (this.options.rtl) { 20333 this.right = start - this.width; 20334 20335 // reposition box, line, and dot 20336 this.dom.box.style.right = this.right + 'px'; 20337 this.dom.line.style.right = start - this.props.line.width + 'px'; 20338 this.dom.dot.style.right = start - this.props.line.width / 2 - this.props.dot.width / 2 + 'px'; 20339 } else { 20340 this.left = start - this.width; 20341 20342 // reposition box, line, and dot 20343 this.dom.box.style.left = this.left + 'px'; 20344 this.dom.line.style.left = start - this.props.line.width + 'px'; 20345 this.dom.dot.style.left = start - this.props.line.width / 2 - this.props.dot.width / 2 + 'px'; 20346 } 20347 } else if (align == 'left') { 20348 if (this.options.rtl) { 20349 this.right = start; 20350 20351 // reposition box, line, and dot 20352 this.dom.box.style.right = this.right + 'px'; 20353 this.dom.line.style.right = start + 'px'; 20354 this.dom.dot.style.right = start + this.props.line.width / 2 - this.props.dot.width / 2 + 'px'; 20355 } else { 20356 this.left = start; 20357 20358 // reposition box, line, and dot 20359 this.dom.box.style.left = this.left + 'px'; 20360 this.dom.line.style.left = start + 'px'; 20361 this.dom.dot.style.left = start + this.props.line.width / 2 - this.props.dot.width / 2 + 'px'; 20362 } 20363 } else { 20364 // default or 'center' 20365 if (this.options.rtl) { 20366 this.right = start - this.width / 2; 20367 20368 // reposition box, line, and dot 20369 this.dom.box.style.right = this.right + 'px'; 20370 this.dom.line.style.right = start - this.props.line.width + 'px'; 20371 this.dom.dot.style.right = start - this.props.dot.width / 2 + 'px'; 20372 } else { 20373 this.left = start - this.width / 2; 20374 20375 // reposition box, line, and dot 20376 this.dom.box.style.left = this.left + 'px'; 20377 this.dom.line.style.left = start - this.props.line.width / 2 + 'px'; 20378 this.dom.dot.style.left = start - this.props.dot.width / 2 + 'px'; 20379 } 20380 } 20381}; 20382 20383/** 20384 * Reposition the item vertically 20385 * @Override 20386 */ 20387BoxItem.prototype.repositionY = function () { 20388 var orientation = this.options.orientation.item; 20389 var box = this.dom.box; 20390 var line = this.dom.line; 20391 var dot = this.dom.dot; 20392 20393 if (orientation == 'top') { 20394 box.style.top = (this.top || 0) + 'px'; 20395 20396 line.style.top = '0'; 20397 line.style.height = this.parent.top + this.top + 1 + 'px'; 20398 line.style.bottom = ''; 20399 } else { 20400 // orientation 'bottom' 20401 var itemSetHeight = this.parent.itemSet.props.height; // TODO: this is nasty 20402 var lineHeight = itemSetHeight - this.parent.top - this.parent.height + this.top; 20403 20404 box.style.top = (this.parent.height - this.top - this.height || 0
20404) + 'px'; 20405 line.style.top = itemSetHeight - lineHeight + 'px'; 20406 line.style.bottom = '0'; 20407 } 20408 20409 dot.style.top = -this.props.dot.height / 2 + 'px'; 20410}; 20411 20412/** 20413 * Return the width of the item left from its start date 20414 * @return {number} 20415 */ 20416BoxItem.prototype.getWidthLeft = function () { 20417 return this.width / 2; 20418}; 20419 20420/** 20421 * Return the width of the item right from its start date 20422 * @return {number} 20423 */ 20424BoxItem.prototype.getWidthRight = function () { 20425 return this.width / 2; 20426}; 20427 20428module.exports = BoxItem; 20429 20430/***/ }), 20431/* 102 */ 20432/***/ (function(module, exports, __webpack_require__) { 20433 20434"use strict"; 20435 20436 20437var Item = __webpack_require__(38); 20438 20439/** 20440 * @constructor PointItem 20441 * @extends Item 20442 * @param {Object} data Object containing parameters start 20443 * content, className. 20444 * @param {{toScreen: function, toTime: function}} conversion 20445 * Conversion functions from time to screen and vice versa 20446 * @param {Object} [options] Configuration options 20447 * // TODO: describe available options 20448 */ 20449function PointItem(data, conversion, options) { 20450 this.props = { 20451 dot: { 20452 top: 0, 20453 width: 0, 20454 height: 0 20455 }, 20456 content: { 20457 height: 0, 20458 marginLeft: 0, 20459 marginRight: 0 20460 } 20461 }; 20462 this.options = options; 20463 // validate data 20464 if (data) { 20465 if (data.start == undefined) { 20466 throw new Error('Property "start" missing in item ' + data); 20467 } 20468 } 20469 20470 Item.call(this, data, conversion, options); 20471} 20472 20473PointItem.prototype = new Item(null, null, null); 20474 20475/** 20476 * Check whether this item is visible inside given range 20477 * @param {{start: number, end: number}} range with a timestamp for start and end 20478 * @returns {boolean} True if visible 20479 */ 20480PointItem.prototype.isVisible = function (range) { 20481 // determine visibility 20482 var widthInMs = this.width * range.getMillisecondsPerPixel(); 20483 20484 return this.data.start.getTime() + widthInMs > range.start && this.data.start < range.end; 20485}; 20486 20487PointItem.prototype._createDomElement = function () { 20488 if (!this.dom) { 20489 // create DOM 20490 this.dom = {}; 20491 20492 // background box 20493 this.dom.point = document.createElement('div'); 20494 // className is updated in redraw() 20495 20496 // contents box, right from the dot 20497 this.dom.content = document.createElement('div'); 20498 this.dom.content.className = 'vis-item-content'; 20499 this.dom.point.appendChild(this.dom.content); 20500 20501 // dot at start 20502 this.dom.dot = document.createElement('div'); 20503 this.dom.point.appendChild(this.dom.dot); 20504 20505 // attach this item as attribute 20506 this.dom.point['timeline-item'] = this; 20507 20508 this.dirty = true; 20509 } 20510}; 20511 20512PointItem.prototype._appendDomElement = function () { 20513 if (!this.parent) { 20514 throw new Error('Cannot redraw item: no parent attached'); 20515 } 20516 if (!this.dom.point.parentNode) { 20517 var foreground = this.parent.dom.foreground; 20518 if (!foreground) { 20519 throw new Error('Cannot redraw item: parent has no foreground container element'); 20520 } 20521 foreground.appendChild(this.dom.point); 20522 } 20523 this.displayed = true; 20524}; 20525 20526PointItem.prototype._updateDirtyDomComponents = function () { 20527 // An item is marked dirty when: 20528 // - the item is not yet rendered 20529 // - the item's data is changed 20530 // - the item is selected/deselected 20531 if (this.dirty) { 20532 this._updateContents(this.dom.content); 20533 this._updateDataAttributes(this.dom.point); 20534 this._updateStyle(this.dom.point); 20535 20536 var editable = this.editable.updateTime || this.editable.updateGroup; 20537 // update class 20538 var className = (this.data.className ? ' ' + this.data.className : '') + (this.selected ? ' vis-selected' : '') + (editable ? ' vis-editable' : ' vis-readonly'); 20539 this.dom.point.className = 'vis-item vis-point' + className; 20540 this.dom.dot.className = 'vis-item vis-dot' + className; 20541 } 20542}; 20543 20544PointItem.prototype._getDomComponentsSizes = function () { 20545 return { 20546 dot: { 20547 width: this.dom.dot.offsetWidth, 20548 height: this.dom.dot.offsetHeight 20549 }, 20550 content: { 20551 width: this.dom.content.offsetWidth, 20552 height: this.dom.content.offsetHeight 20553 }, 20554 point: { 20555 width: this.dom.point.offsetWidth, 20556 height: this.dom.point.offsetHeight 20557 } 20558 }; 20559}; 20560 20561PointItem.prototype._updateDomComponentsSizes = function (sizes) { 20562 // recalculate size of dot and contents 20563 this.props.dot.width = sizes.dot.width; 20564 this.props.dot.height = sizes.dot.height; 20565 this.props.content.height = sizes.content.height; 20566 20567 // resize contents 20568 if (this.options.rtl) { 20569 this.dom.content.style.marginRight = 2 * this.props.dot.width + 'px'; 20570 } else { 20571 this.dom.content.style.marginLeft = 2 * this.props.dot.width + 'px'; 20572 } 20573 //this.dom.content.style.marginRight = ... + 'px'; // TODO: margin right 20574 20575 // recalculate size 20576 this.width = sizes.point.width; 20577 this.height = sizes.point.height; 20578 20579 // reposition the dot 20580 this.dom.dot.style.top = (this.height - this.props.dot.height) / 2 + 'px'; 20581 if (this.options.rtl) { 20582 this.dom.dot.style.right = this.props.dot.width / 2 + 'px'; 20583 } else { 20584 this.dom.dot.style.left = this.props.dot.width / 2 + 'px'; 20585 } 20586 20587 this.dirty = false;
20588}; 20589 20590PointItem.prototype._repaintDomAdditionals = function () { 20591 this._repaintOnItemUpdateTimeTooltip(this.dom.point); 20592 this._repaintDragCenter(); 20593 this._repaintDeleteButton(this.dom.point); 20594}; 20595 20596/** 20597 * Repaint the item 20598 * @param {boolean} [returnQueue=false] return the queue 20599 * @return {boolean} the redraw queue if returnQueue=true 20600 */ 20601PointItem.prototype.redraw = function (returnQueue) { 20602 var sizes; 20603 var queue = [ 20604 // create item DOM 20605 this._createDomElement.bind(this), 20606 20607 // append DOM to parent DOM 20608 this._appendDomElement.bind(this), 20609 20610 // update dirty DOM 20611 this._updateDirtyDomComponents.bind(this), function () { 20612 if (this.dirty) { 20613 sizes = this._getDomComponentsSizes(); 20614 } 20615 }.bind(this), function () { 20616 if (this.dirty) { 20617 this._updateDomComponentsSizes.bind(this)(sizes); 20618 } 20619 }.bind(this), 20620 20621 // repaint DOM additionals 20622 this._repaintDomAdditionals.bind(this)]; 20623 20624 if (returnQueue) { 20625 return queue; 20626 } else { 20627 var result; 20628 queue.forEach(function (fn) { 20629 result = fn(); 20630 }); 20631 return result; 20632 } 20633}; 20634 20635/** 20636 * Show the item in the DOM (when not already visible). The items DOM will 20637 * be created when needed. 20638 */ 20639PointItem.prototype.show = function () { 20640 if (!this.displayed) { 20641 this.redraw(); 20642 } 20643}; 20644 20645/** 20646 * Hide the item from the DOM (when visible) 20647 */ 20648PointItem.prototype.hide = function () { 20649 if (this.displayed) { 20650 if (this.dom.point.parentNode) { 20651 this.dom.point.parentNode.removeChild(this.dom.point); 20652 } 20653 20654 this.displayed = false; 20655 } 20656}; 20657 20658/** 20659 * Reposition the item horizontally 20660 * @Override 20661 */ 20662PointItem.prototype.repositionX = function () { 20663 var start = this.conversion.toScreen(this.data.start); 20664 20665 if (this.options.rtl) { 20666 this.right = start - this.props.dot.width; 20667 20668 // reposition point 20669 this.dom.point.style.right = this.right + 'px'; 20670 } else { 20671 this.left = start - this.props.dot.width; 20672 20673 // reposition point 20674 this.dom.point.style.left = this.left + 'px'; 20675 } 20676}; 20677 20678/** 20679 * Reposition the item vertically 20680 * @Override 20681 */ 20682PointItem.prototype.repositionY = function () { 20683 var orientation = this.options.orientation.item; 20684 var point = this.dom.point; 20685 if (orientation == 'top') { 20686 point.style.top = this.top + 'px'; 20687 } else { 20688 point.style.top = this.parent.height - this.top - this.height + 'px'; 20689 } 20690}; 20691 20692/** 20693 * Return the width of the item left from its start date 20694 * @return {number} 20695 */ 20696PointItem.prototype.getWidthLeft = function () { 20697 return this.props.dot.width; 20698}; 20699 20700/** 20701 * Return the width of the item right from its start date 20702 * @return {number} 20703 */ 20704PointItem.prototype.getWidthRight = function () { 20705 return this.props.dot.width; 20706}; 20707 20708module.exports = PointItem; 20709 20710/***/ }), 20711/* 103 */ 20712/***/ (function(module, exports, __webpack_require__) { 20713 20714"use strict"; 20715 20716 20717var Item = __webpack_require__(38);
20718var BackgroundGroup = __webpack_require__(69); 20719var RangeItem = __webpack_require__(70); 20720 20721/** 20722 * @constructor BackgroundItem 20723 * @extends Item 20724 * @param {Object} data Object containing parameters start, end 20725 * content, className. 20726 * @param {{toScreen: function, toTime: function}} conversion 20727 * Conversion functions from time to screen and vice versa 20728 * @param {Object} [options] Configuration options 20729 * // TODO: describe options 20730 * // TODO: implement support for the BackgroundItem just having a start, then being displayed as a sort of an annotation 20731 */ 20732function BackgroundItem(data, conversion, options) { 20733 this.props = { 20734 content: { 20735 width: 0 20736 } 20737 }; 20738 this.overflow = false; // if contents can overflow (css styling), this flag is set to true 20739 20740 // validate data 20741 if (data) { 20742 if (data.start == undefined) { 20743 throw new Error('Property "start" missing in item ' + data.id); 20744 } 20745 if (data.end == undefined) { 20746 throw new Error('Property "end" missing in item ' + data.id); 20747 } 20748 } 20749 20750 Item.call(this, data, conversion, options); 20751} 20752 20753BackgroundItem.prototype = new Item(null, null, null); 20754 20755BackgroundItem.prototype.baseClassName = 'vis-item vis-background'; 20756 20757BackgroundItem.prototype.stack = false; 20758 20759/** 20760 * Check whether this item is visible inside given range 20761 * @param {vis.Range} range with a timestamp for start and end 20762 * @returns {boolean} True if visible 20763 */ 20764BackgroundItem.prototype.isVisible = function (range) { 20765 // determine visibility 20766 return this.data.start < range.end && this.data.end > range.start; 20767}; 20768 20769BackgroundItem.prototype._createDomElement = function () { 20770 if (!this.dom) { 20771 // create DOM 20772 this.dom = {}; 20773 20774 // background box 20775 this.dom.box = document.createElement('div'); 20776 // className is updated in redraw() 20777 20778 // frame box (to prevent the item contents from overflowing 20779 this.dom.frame = document.createElement('div'); 20780 this.dom.frame.className = 'vis-item-overflow'; 20781 this.dom.box.appendChild(this.dom.frame); 20782 20783 // contents box 20784 this.dom.content = document.createElement('div'); 20785 this.dom.content.className = 'vis-item-content'; 20786 this.dom.frame.appendChild(this.dom.content); 20787 20788 // Note: we do NOT attach this item as attribute to the DOM, 20789 // such that background items cannot be selected 20790 //this.dom.box['timeline-item'] = this; 20791 20792 this.dirty = true; 20793 } 20794}; 20795 20796BackgroundItem.prototype._appendDomElement = function () { 20797 if (!this.parent) { 20798 throw new Error('Cannot redraw item: no parent attached'); 20799 } 20800 if (!this.dom.box.parentNode) { 20801 var background = this.parent.dom.background; 20802 if (!background) { 20803 throw new Error('Cannot redraw item: parent has no background container element'); 20804 } 20805 background.appendChild(this.dom.box); 20806 } 20807 this.displayed = true; 20808}; 20809 20810BackgroundItem.prototype._updateDirtyDomComponents = function () { 20811 // update dirty DOM. An item is marked dirty when: 20812 // - the item is not yet rendered 20813 // - the item's data is changed 20814 // - the item is selected/deselected 20815 if (this.dirty) { 20816 this._updateContents(this.dom.content); 20817 this._updateDataAttributes(this.dom.content); 20818 this._updateStyle(this.dom.box); 20819 20820 // update class 20821 var className = (this.data.className ? ' ' + this.data.className : '') + (this.selected ? ' vis-selected' : ''); 20822 this.dom.box.className = this.baseClassName + className; 20823 } 20824}; 20825 20826BackgroundItem.prototype._getDomComponentsSizes = function () { 20827 // determine from css whether this box has overflow 20828 this.overflow = window.getComputedStyle(this.dom.content).overflow !== 'hidden'; 20829 return { 20830 content: { 20831 width: this.dom.content.offsetWidth 20832 } 20833 }; 20834}; 20835 20836BackgroundItem.prototype._updateDomComponentsSizes = function (sizes) { 20837 // recalculate size 20838 this.props.content.width = sizes.content.width; 20839 this.height = 0; // set height zero, so this item will be ignored when stacking items 20840 20841 this.dirty = false; 20842}; 20843 20844BackgroundItem.prototype._repaintDomAdditionals = function () {}; 20845 20846/** 20847 * Repaint the item 20848 * @param {boolean} [returnQueue=false] return the queue 20849 * @return {boolean} the redraw result or the redraw queue if returnQueue=true 20850 */
20851BackgroundItem.prototype.redraw = function (returnQueue) { 20852 var sizes; 20853 var queue = [ 20854 // create item DOM 20855 this._createDomElement.bind(this), 20856 20857 // append DOM to parent DOM 20858 this._appendDomElement.bind(this), this._updateDirtyDomComponents.bind(this), function () { 20859 if (this.dirty) { 20860 sizes = this._getDomComponentsSizes.bind(this)(); 20861 } 20862 }.bind(this), function () { 20863 if (this.dirty) { 20864 this._updateDomComponentsSizes.bind(this)(sizes); 20865 } 20866 }.bind(this), 20867 20868 // repaint DOM additionals 20869 this._repaintDomAdditionals.bind(this)]; 20870 20871 if (returnQueue) { 20872 return queue; 20873 } else { 20874 var result; 20875 queue.forEach(function (fn) { 20876 result = fn(); 20877 }); 20878 return result; 20879 } 20880}; 20881 20882/** 20883 * Show the item in the DOM (when not already visible). The items DOM will 20884 * be created when needed. 20885 */ 20886BackgroundItem.prototype.show = RangeItem.prototype.show; 20887 20888/** 20889 * Hide the item from the DOM (when visible) 20890 * @return {Boolean} changed 20891 */ 20892BackgroundItem.prototype.hide = RangeItem.prototype.hide; 20893 20894/** 20895 * Reposition the item horizontally 20896 * @Override 20897 */ 20898BackgroundItem.prototype.repositionX = RangeItem.prototype.repositionX; 20899 20900/** 20901 * Reposition the item vertically 20902 * @Override 20903 */ 20904BackgroundItem.prototype.repositionY = function (margin) { 20905 // eslint-disable-line no-unused-vars 20906 var height; 20907 var orientation = this.options.orientation.item; 20908 20909 // special positioning for subgroups 20910 if (this.data.subgroup !== undefined) { 20911 // TODO: instead of calculating the top position of the subgroups here for every BackgroundItem, calculate the top of the subgroup once in Itemset 20912 var itemSubgroup = this.data.subgroup; 20913 20914 this.dom.box.style.height = this.parent.subgroups[itemSubgroup].height + 'px'; 20915 20916 if (orientation == 'top') { 20917 this.dom.box.style.top = this.parent.top + this.parent.subgroups[itemSubgroup].top + 'px'; 20918 } else { 20919 this.dom.box.style.top = this.parent.top + this.parent.height - this.parent.subgroups[itemSubgroup].top - this.parent.subgroups[itemSubgroup].height + 'px'; 20920 } 20921 this.dom.box.style.bottom = ''; 20922 } 20923 // and in the case of no subgroups: 20924 else { 20925 // we want backgrounds with groups to only show in groups. 20926 if (this.parent instanceof BackgroundGroup) { 20927 // if the item is not in a group: 20928 height = Math.max(this.parent.height, this.parent.itemSet.body.domProps.center.height, this.parent.itemSet.body.domProps.centerContainer.height); 20929 this.dom.box.style.bottom = orientation == 'bottom' ? '0' : ''; 20930 this.dom.box.style.top = orientation == 'top' ? '0' : ''; 20931 } else { 20932 height = this.parent.height; 20933 // same alignment for items when orientation is top or bottom 20934 this.dom.box.style.top = this.parent.top + 'px'; 20935 this.dom.box.style.bottom = ''; 20936 } 20937 } 20938 this.dom.box.style.height = height + 'px'; 20939}; 20940 20941module.exports = BackgroundItem; 20942 20943/***/ }), 20944/* 104 */ 20945/***/ (function(module, exports, __webpack_require__) { 20946 20947"use strict"; 20948 20949 20950Object.defineProperty(exports, "__esModule", { 20951 value: true 20952}); 20953 20954var _classCallCheck2 = __webpack_require__(0); 20955 20956var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); 20957 20958var _createClass2 = __webpack_require__(1); 20959 20960var _createClass3 = _interopRequireDefault(_createClass2); 20961 20962function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 20963 20964/** 20965 * Popup is a class to create a popup window with some text 20966 */ 20967var Popup = function () { 20968 /** 20969 * @param {Element} container The container object. 20970 * @param {string} overflowMethod How the popup should act to overflowing ('flip' or 'cap') 20971 */ 20972 function Popup(container, overflowMethod) { 20973 (0, _classCallCheck3['default'])(this, Popup); 20974 20975 this.container = container; 20976 this.overflowMethod = overflowMethod || 'cap'; 20977 20978 this.x = 0; 20979 this.y = 0; 20980 this.padding = 5; 20981 this.hidden = false; 20982 20983 // create the frame 20984 this.frame = document.createElement('div'); 20985 this.frame.className = 'vis-tooltip'; 20986 this.container.appendChild(this.frame); 20987 } 20988 20989 /** 20990 * @param {number} x Horizontal position of the popup window 20991 * @param {number} y Vertical position of the popup window 20992 */ 20993 20994 20995 (0, _createClass3['default'])(Popup, [{ 20996 key: 'setPosition', 20997 value: function setPosition(x, y) { 20998 this.x = parseInt(x); 20999 this.y = parseInt(y); 21000 } 21001 21002 /** 21003 * Set the content for the popup window. This can be HTML code or text. 21004 * @param {string | Element} content 21005 */ 21006 21007 }, { 21008 key: 'setText', 21009 value: function setText(content) { 21010 if (content instanceof Element) { 21011 this.frame.innerHTML = ''; 21012 this.frame.appendChild(content); 21013 } else { 21014 this.frame.innerHTML = content; // string containing text or HTML 21015 } 21016 } 21017 21018 /** 21019 * Show the popup window 21020 * @param {boolean} [doShow] Show or hide the window 21021 */ 21022 21023 }, { 21024 key: 'show', 21025 value: function show(doShow) { 21026 if (doShow === undefined) { 21027 doShow = true; 21028 } 21029 21030 if (doShow === true) { 21031 var height = this.frame.clientHeight; 21032 var width = this.frame.clientWidth; 21033 var maxHeight = this.frame.parentNode.clientHeight; 21034 var maxWidth = this.frame.parentNode.clientWidth; 21035 21036 var left = 0, 21037 top = 0; 21038 21039 if (this.overflowMethod == 'flip') { 21040 var isLeft = false, 21041 isTop = true; // Where around the position it's located 21042 21043 if (this.y - height < this.padding) { 21044 isTop = false;
21045 } 21046 21047 if (this.x + width > maxWidth - this.padding) { 21048 isLeft = true; 21049 } 21050 21051 if (isLeft) { 21052 left = this.x - width; 21053 } else { 21054 left = this.x; 21055 } 21056 21057 if (isTop) { 21058 top = this.y - height; 21059 } else { 21060 top = this.y; 21061 } 21062 } else { 21063 top = this.y - height; 21064 if (top + height + this.padding > maxHeight) { 21065 top = maxHeight - height - this.padding; 21066 } 21067 if (top < this.padding) { 21068 top = this.padding; 21069 } 21070 21071 left = this.x; 21072 if (left + width + this.padding > maxWidth) { 21073 left = maxWidth - width - this.padding; 21074 } 21075 if (left < this.padding) { 21076 left = this.padding; 21077 } 21078 } 21079 21080 this.frame.style.left = left + "px"; 21081 this.frame.style.top = top + "px"; 21082 this.frame.style.visibility = "visible"; 21083 this.hidden = false; 21084 } else { 21085 this.hide(); 21086 } 21087 } 21088 21089 /** 21090 * Hide the popup window 21091 */ 21092 21093 }, { 21094 key: 'hide', 21095 value: function hide() { 21096 this.hidden = true; 21097 this.frame.style.left = "0"; 21098 this.frame.style.top = "0"; 21099 this.frame.style.visibility = "hidden"; 21100 } 21101 21102 /** 21103 * Remove the popup window 21104 */ 21105 21106 }, { 21107 key: 'destroy', 21108 value: function destroy() { 21109 this.frame.parentNode.removeChild(this.frame); // Remove element from DOM 21110 } 21111 }]); 21112 return Popup; 21113}(); 21114 21115exports['default'] = Popup; 21116 21117/***/ }), 21118/* 105 */ 21119/***/ (function(module, exports, __webpack_require__) { 21120 21121"use strict"; 21122 21123 21124Object.defineProperty(exports, "__esModule", { 21125 value: true 21126}); 21127/**
21128 * This object contains all possible options. It will check if the types are correct, if required if the option is one 21129 * of the allowed values. 21130 * 21131 * __any__ means that the name of the property does not matter. 21132 * __type__ is a required field for all objects and contains the allowed types of all objects 21133 */ 21134var string = 'string'; 21135var bool = 'boolean'; 21136var number = 'number'; 21137var array = 'array'; 21138var date = 'date'; 21139var object = 'object'; // should only be in a __type__ property 21140var dom = 'dom'; 21141var moment = 'moment'; 21142var any = 'any'; 21143 21144var allOptions = { 21145 configure: { 21146 enabled: { 'boolean': bool }, 21147 filter: { 'boolean': bool, 'function': 'function' }, 21148 container: { dom: dom }, 21149 __type__: { object: object, 'boolean': bool, 'function': 'function' } 21150 }, 21151 21152 //globals : 21153 align: { string: string }, 21154 rtl: { 'boolean': bool, 'undefined': 'undefined' }, 21155 rollingMode: { 21156 follow: { 'boolean': bool }, 21157 offset: { number: number, 'undefined': 'undefined' }, 21158 __type__: { object: object } 21159 }, 21160 verticalScroll: { 'boolean': bool, 'undefined': 'undefined' }, 21161 horizontalScroll: { 'boolean': bool, 'undefined': 'undefined' }, 21162 autoResize: { 'boolean': bool }, 21163 throttleRedraw: { number: number }, // TODO: DEPRICATED see https://github.com/almende/vis/issues/2511 21164 clickToUse: { 'boolean': bool }, 21165 dataAttributes: { string: string, array: array }, 21166 editable: { 21167 add: { 'boolean': bool, 'undefined': 'undefined' }, 21168 remove: { 'boolean': bool, 'undefined': 'undefined' }, 21169 updateGroup: { 'boolean': bool, 'undefined': 'undefined' }, 21170 updateTime: { 'boolean': bool, 'undefined': 'undefined' }, 21171 overrideItems: { 'boolean': bool, 'undefined': 'undefined' }, 21172 __type__: { 'boolean': bool, object: object } 21173 }, 21174 end: { number: number, date: date, string: string, moment: moment }, 21175 format: { 21176 minorLabels: { 21177 millisecond: { string: string, 'undefined': 'undefined' }, 21178 second: { string: string, 'undefined': 'undefined' }, 21179 minute: { string: string, 'undefined': 'undefined' }, 21180 hour: { string: string, 'undefined': 'undefined' },
21181 weekday: { string: string, 'undefined': 'undefined' }, 21182 day: { string: string, 'undefined': 'undefined' }, 21183 week: { string: string, 'undefined': 'undefined' }, 21184 month: { string: string, 'undefined': 'undefined' }, 21185 year: { string: string, 'undefined': 'undefined' }, 21186 __type__: { object: object, 'function': 'function' } 21187 }, 21188 majorLabels: { 21189 millisecond: { string: string, 'undefined': 'undefined' }, 21190 second: { string: string, 'undefined': 'undefined' }, 21191 minute: { string: string, 'undefined': 'undefined' }, 21192 hour: { string: string, 'undefined': 'undefined' }, 21193 weekday: { string: string, 'undefined': 'undefined' }, 21194 day: { string: string, 'undefined': 'undefined' }, 21195 week: { string: string, 'undefined': 'undefined' }, 21196 month: { string: string, 'undefined': 'undefined' }, 21197 year: { string: string, 'undefined': 'undefined' }, 21198 __type__: { object: object, 'function': 'function' } 21199 }, 21200 __type__: { object: object } 21201 }, 21202 moment: { 'function': 'function' }, 21203 groupOrder: { string: string, 'function': 'function' }, 21204 groupEditable: { 21205 add: { 'boolean': bool, 'undefined': 'undefined' }, 21206 remove: { 'boolean': bool, 'undefined': 'undefined' }, 21207 order: { 'boolean': bool, 'undefined': 'undefined' }, 21208 __type__: { 'boolean': bool, object: object } 21209 }, 21210 groupOrderSwap: { 'function': 'function' }, 21211 height: { string: string, number: number }, 21212 hiddenDates: { 21213 start: { date: date, number: number, string: string, moment: moment }, 21214 end: { date: date, number: number, string: string, moment: moment }, 21215 repeat: { string: string }, 21216 __type__: { object: object, array: array } 21217 }, 21218 itemsAlwaysDraggable: { 21219 item: { 'boolean': bool, 'undefined': 'undefined' }, 21220 range: { 'boolean': bool, 'undefined': 'undefined' }, 21221 __type__: { 'boolean': bool, object: object } 21222 }, 21223 limitSize: { 'boolean': bool }, 21224 locale: { string: string }, 21225 locales: { 21226 __any__: { any: any }, 21227 __type__: { object: object } 21228 }, 21229 margin: { 21230 axis: { number: number }, 21231 item: { 21232 horizontal: { number: number, 'undefined': 'undefined' }, 21233 vertical: { number: number, 'undefined': 'undefined' }, 21234 __type__: { object: object, number: number } 21235 }, 21236 __type__: { object: object, number: number } 21237 }, 21238 max: { date: date, number: number, string: string, moment: moment }, 21239 maxHeight: { number: number, string: string }, 21240 maxMinorChars: { number: number }, 21241 min: { date: date, number: number, string: string, moment: moment }, 21242 minHeight: { number: number, string: string }, 21243 moveable: { 'boolean': bool }, 21244 multiselect: { 'boolean': bool }, 21245 multiselectPerGroup: { 'boolean': bool }, 21246 onAdd: { 'function': 'function' }, 21247 onDropObjectOnItem: { 'function': 'function' }, 21248 onUpdate: { 'function': 'function' }, 21249 onMove: { 'function': 'function' }, 21250 onMoving: { 'function': 'function' }, 21251 onRemove: { 'function': 'function' }, 21252 onAddGroup: { 'function': 'function' }, 21253 onMoveGroup: { 'function': 'function' }, 21254 onRemoveGroup: { 'function': 'function' }, 21255 onInitialDrawComplete: { 'function': 'function' }, 21256 order: { 'function': 'function' }, 21257 orientation: { 21258 axis: { string: string, 'undefined': 'undefined' }, 21259 item: { string: string, 'undefined': 'undefined' }, 21260 __type__: { string: string, object: object } 21261 }, 21262 selectable: { 'boolean': bool }, 21263 showCurrentTime: { 'boolean': bool }, 21264 showMajorLabels: { 'boolean': bool }, 21265 showMinorLabels: { 'boolean': bool }, 21266 stack: { 'boolean': bool }, 21267 stackSubgroups: { 'boolean': bool }, 21268 snap: { 'function': 'function', 'null': 'null' }, 21269 start: { date: date, number: number, string: string, moment: moment }, 21270 template: { 'function': 'function' }, 21271 groupTemplate: { 'function': 'function' }, 21272 visibleFrameTemplate: { string: string, 'function': 'function' }, 21273 showTooltips: { 'boolean': bool }, 21274 tooltip: { 21275 followMouse: { 'boolean': bool }, 21276 overflowMethod: { 'string': ['cap', 'flip'] }, 21277 __type__: { object: object } 21278 }, 21279 tooltipOnItemUpdateTime: { 21280 template: { 'function': 'function' }, 21281 __type__: { 'boolean': bool, object: object } 21282 }, 21283 timeAxis: { 21284 scale: { string: string, 'undefined': 'undefined' }, 21285 step: { number: number, 'undefined': 'undefined' }, 21286 __type__: { object: object } 21287 }, 21288 type: { string: string },
21289 width: { string: string, number: number }, 21290 zoomable: { 'boolean': bool }, 21291 zoomKey: { string: ['ctrlKey', 'altKey', 'metaKey', ''] }, 21292 zoomMax: { number: number }, 21293 zoomMin: { number: number }, 21294 21295 __type__: { object: object } 21296}; 21297 21298var configureOptions = { 21299 global: { 21300 align: ['center', 'left', 'right'], 21301 direction: false, 21302 autoResize: true, 21303 clickToUse: false, 21304 // dataAttributes: ['all'], // FIXME: can be 'all' or string[] 21305 editable: { 21306 add: false, 21307 remove: false, 21308 updateGroup: false, 21309 updateTime: false 21310 }, 21311 end: '', 21312 format: { 21313 minorLabels: { 21314 millisecond: 'SSS', 21315 second: 's', 21316 minute: 'HH:mm', 21317 hour: 'HH:mm', 21318 weekday: 'ddd D', 21319 day: 'D', 21320 week: 'w', 21321 month: 'MMM', 21322 year: 'YYYY' 21323 }, 21324 majorLabels: { 21325 millisecond: 'HH:mm:ss', 21326 second: 'D MMMM HH:mm', 21327 minute: 'ddd D MMMM', 21328 hour: 'ddd D MMMM', 21329 weekday: 'MMMM YYYY', 21330 day: 'MMMM YYYY', 21331 week: 'MMMM YYYY', 21332 month: 'YYYY', 21333 year: '' 21334 } 21335 }, 21336 21337 //groupOrder: {string, 'function': 'function'}, 21338 groupsDraggable: false, 21339 height: '', 21340 //hiddenDates: {object, array}, 21341 locale: '', 21342 margin: { 21343 axis: [20, 0, 100, 1], 21344 item: { 21345 horizontal: [10, 0, 100, 1], 21346 vertical: [10, 0, 100, 1] 21347 } 21348 }, 21349 max: '', 21350 maxHeight: '', 21351 maxMinorChars: [7, 0, 20, 1], 21352 min: '', 21353 minHeight: '', 21354 moveable: false, 21355 multiselect: false, 21356 multiselectPerGroup: false, 21357 //onAdd: {'function': 'function'}, 21358 //onUpdate: {'function': 'function'}, 21359 //onMove: {'function': 'function'}, 21360 //onMoving: {'function': 'function'}, 21361 //onRename: {'function': 'function'}, 21362 //order: {'function': 'function'}, 21363 orientation: { 21364 axis: ['both', 'bottom', 'top'], 21365 item: ['bottom', 'top'] 21366 }, 21367 selectable: true, 21368 showCurrentTime: false, 21369 showMajorLabels: true, 21370 showMinorLabels: true, 21371 stack: true, 21372 stackSubgroups: true, 21373 //snap: {'function': 'function', nada}, 21374 start: '', 21375 //template: {'function': 'function'}, 21376 //timeAxis: { 21377 // scale: ['millisecond', 'second', 'minute', 'hour', 'weekday', 'day', 'week', 'month', 'year'], 21378 // step: [1, 1, 10, 1] 21379 //}, 21380 showTooltips: true, 21381 tooltip: { 21382 followMouse: false, 21383 overflowMethod: 'flip' 21384 }, 21385 tooltipOnItemUpdateTime: false, 21386 type: ['box', 'point', 'range', 'background'], 21387 width: '100%', 21388 zoomable: true, 21389 zoomKey: ['ctrlKey', 'altKey', 'metaKey', ''], 21390 zoomMax: [315360000000000, 10, 315360000000000, 1], 21391 zoomMin: [10, 10, 315360000000000, 1] 21392 } 21393}; 21394 21395exports.allOptions = allOptions; 21396exports.configureOptions = configureOptions; 21397 21398/***/ }), 21399/* 106 */ 21400/***/ (function(module, exports, __webpack_require__) { 21401 21402"use strict"; 21403 21404 21405var _typeof2 = __webpack_require__(6); 21406 21407var _typeof3 = _interopRequireDefault(_typeof2); 21408 21409function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 21410 21411var util = __webpack_require__(2); 21412var DOMutil = __webpack_require__(14); 21413var DataSet = __webpack_require__(11); 21414var DataView = __webpack_require__(12); 21415var Component = __webpack_require__(16); 21416var DataAxis = __webpack_require__(107); 21417var GraphGroup = __webpack_require__(109); 21418var Legend = __webpack_require__(112); 21419var Bars = __webpack_require__(110); 21420var Lines = __webpack_require__(111); 21421var Points = __webpack_require__(72); 21422 21423var UNGROUPED = '__ungrouped__'; // reserved group id for ungrouped items 21424 21425/** 21426 * This is the constructor of the LineGraph. It requires a Timeline body and options. 21427 * 21428 * @param {vis.Timeline.body} body 21429 * @param {Object} options 21430 * @constructor LineGraph 21431 * @extends Component 21432 */ 21433function LineGraph(body, options) { 21434 this.id = util.randomUUID(); 21435 this.body = body; 21436 21437 this.defaultOptions = { 21438 yAxisOrientation: 'left', 21439 defaultGroup: 'default', 21440 sort: true, 21441 sampling: true, 21442 stack: false, 21443 graphHeight: '400px', 21444 shaded: { 21445 enabled: false, 21446 orientation: 'bottom' // top, bottom, zero 21447 }, 21448 style: 'line', // line, bar 21449 barChart: { 21450 width: 50, 21451 sideBySide: false, 21452 align: 'center' // left, center, right 21453 }, 21454 interpolation: { 21455 enabled: true, 21456 parametrization: 'centripetal', // uniform (alpha = 0.0), chordal (alpha = 1.0), centripetal (alpha = 0.5) 21457 alpha: 0.5 21458 }, 21459 drawPoints: { 21460 enabled: true, 21461 size: 6, 21462 style: 'square' // square, circle 21463 }, 21464 dataAxis: {}, //Defaults are done on DataAxis level 21465 legend: {}, //Defaults are done on Legend level 21466 groups: { 21467 visibility: {} 21468 } 21469 }; 21470 21471 // options is shared by this lineGraph and all its items 21472 this.options = util.extend({}, this.defaultOptions); 21473 this.dom = {}; 21474 this.props = {}; 21475 this.hammer = null; 21476 this.groups = {}; 21477 this.abortedGraphUpdate = false;
vendor: 5,170 bytes, lines 21478-21621
21478 this.updateSVGheight = false; 21479 this.updateSVGheightOnResize = false; 21480 this.forceGraphUpdate = true; 21481 21482 var me = this; 21483 this.itemsData = null; // DataSet 21484 this.groupsData = null; // DataSet 21485 21486 // listeners for the DataSet of the items 21487 this.itemListeners = { 21488 'add': function add(event, params, senderId) { 21489 // eslint-disable-line no-unused-vars 21490 me._onAdd(params.items); 21491 }, 21492 'update': function update(event, params, senderId) { 21493 // eslint-disable-line no-unused-vars 21494 me._onUpdate(params.items); 21495 }, 21496 'remove': function remove(event, params, senderId) { 21497 // eslint-disable-line no-unused-vars 21498 me._onRemove(params.items); 21499 } 21500 }; 21501 21502 // listeners for the DataSet of the groups 21503 this.groupListeners = { 21504 'add': function add(event, params, senderId) { 21505 // eslint-disable-line no-unused-vars 21506 me._onAddGroups(params.items); 21507 }, 21508 'update': function update(event, params, senderId) { 21509 // eslint-disable-line no-unused-vars 21510 me._onUpdateGroups(params.items); 21511 }, 21512 'remove': function remove(event, params, senderId) { 21513 // eslint-disable-line no-unused-vars 21514 me._onRemoveGroups(params.items); 21515 } 21516 }; 21517 21518 this.items = {}; // object with an Item for every data item 21519 this.selection = []; // list with the ids of all selected nodes 21520 this.lastStart = this.body.range.start; 21521 this.touchParams = {}; // stores properties while dragging 21522 21523 this.svgElements = {}; 21524 this.setOptions(options); 21525 this.groupsUsingDefaultStyles = [0]; 21526 this.body.emitter.on('rangechanged', function () { 21527 me.lastStart = me.body.range.start; 21528 me.svg.style.left = util.option.asSize(-me.props.width); 21529 21530 me.forceGraphUpdate = true; 21531 //Is this local redraw necessary? (Core also does a change event!) 21532 me.redraw.call(me); 21533 }); 21534 21535 // create the HTML DOM 21536 this._create(); 21537 this.framework = { svg: this.svg, svgElements: this.svgElements, options: this.options, groups: this.groups }; 21538} 21539 21540LineGraph.prototype = new Component(); 21541 21542/** 21543 * Create the HTML DOM for the ItemSet 21544 */ 21545LineGraph.prototype._create = function () { 21546 var frame = document.createElement('div'); 21547 frame.className = 'vis-line-graph'; 21548 this.dom.frame = frame; 21549 21550 // create svg element for graph drawing. 21551 this.svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); 21552 this.svg.style.position = 'relative'; 21553 this.svg.style.height = ('' + this.options.graphHeight).replace('px', '') + 'px'; 21554 this.svg.style.display = 'block'; 21555 frame.appendChild(this.svg); 21556 21557 // data axis 21558 this.options.dataAxis.orientation = 'left'; 21559 this.yAxisLeft = new DataAxis(this.body, this.options.dataAxis, this.svg, this.options.groups); 21560 21561 this.options.dataAxis.orientation = 'right'; 21562 this.yAxisRight = new DataAxis(this.body, this.options.dataAxis, this.svg, this.options.groups); 21563 delete this.options.dataAxis.orientation; 21564 21565 // legends 21566 this.legendLeft = new Legend(this.body, this.options.legend, 'left', this.options.groups); 21567 this.legendRight = new Legend(this.body, this.options.legend, 'right', this.options.groups); 21568 21569 this.show(); 21570}; 21571 21572/** 21573 * set the options of the LineGraph. the mergeOptions is used for subObjects that have an enabled element. 21574 * @param {object} options 21575 */ 21576LineGraph.prototype.setOptions = function (options) { 21577 if (options) { 21578 var fields = ['sampling', 'defaultGroup', 'stack', 'height', 'graphHeight', 'yAxisOrientation', 'style', 'barChart', 'dataAxis', 'sort', 'groups']; 21579 if (options.graphHeight === undefined && options.height !== undefined) { 21580 this.updateSVGheight = true; 21581 this.updateSVGheightOnResize = true; 21582 } else if (this.body.domProps.centerContainer.height !== undefined && options.graphHeight !== undefined) { 21583 if (parseInt((options.graphHeight + '').replace("px", '')) < this.body.domProps.centerContainer.height) { 21584 this.updateSVGheight = true; 21585 } 21586 } 21587 util.selectiveDeepExtend(fields, this.options, options); 21588 util.mergeOptions(this.options, options, 'interpolation'); 21589 util.mergeOptions(this.options, options, 'drawPoints'); 21590 util.mergeOptions(this.options, options, 'shaded'); 21591 util.mergeOptions(this.options, options, 'legend'); 21592 21593 if (options.interpolation) { 21594 if ((0, _typeof3['default'])(options.interpolation) == 'object') { 21595 if (options.interpolation.parametrization) { 21596 if (options.interpolation.parametrization == 'uniform') { 21597 this.options.interpolation.alpha = 0; 21598 } else if (options.interpolation.parametrization == 'chordal') { 21599 this.options.interpolation.alpha = 1.0; 21600 } else { 21601 this.options.interpolation.parametrization = 'centripetal'; 21602 this.options.interpolation.alpha = 0.5; 21603 } 21604 } 21605 } 21606 } 21607 21608 if (this.yAxisLeft) { 21609 if (options.dataAxis !== undefined) { 21610 this.yAxisLeft.setOptions(this.options.dataAxis); 21611 this.yAxisRight.setOptions(this.options.dataAxis); 21612 } 21613 } 21614 21615 if (this.legendLeft) { 21616 if (options.legend !== undefined) { 21617 this.legendLeft.setOptions(this.options.legend); 21618 this.legendRight.setOptions(this.options.legend); 21619 } 21620 } 21621
21622 if (this.groups.hasOwnProperty(UNGROUPED)) { 21623 this.groups[UNGROUPED].setOptions(options); 21624 } 21625 } 21626 21627 // this is used to redraw the graph if the visibility of the groups is changed. 21628 if (this.dom.frame) { 21629 //not on initial run? 21630 this.forceGraphUpdate = true; 21631 this.body.emitter.emit("_change", { queue: true }); 21632 } 21633}; 21634 21635/** 21636 * Hide the component from the DOM 21637 */ 21638LineGraph.prototype.hide = function () { 21639 // remove the frame containing the items 21640 if (this.dom.frame.parentNode) { 21641 this.dom.frame.parentNode.removeChild(this.dom.frame); 21642 } 21643}; 21644 21645/** 21646 * Show the component in the DOM (when not already visible). 21647 */ 21648LineGraph.prototype.show = function () { 21649 // show frame containing the items 21650 if (!this.dom.frame.parentNode) { 21651 this.body.dom.center.appendChild(this.dom.frame); 21652 } 21653}; 21654 21655/** 21656 * Set items 21657 * @param {vis.DataSet | null} items 21658 */ 21659LineGraph.prototype.setItems = function (items) { 21660 var me = this, 21661 ids, 21662 oldItemsData = this.itemsData; 21663 21664 // replace the dataset 21665 if (!items) { 21666 this.itemsData = null; 21667 } else if (items instanceof DataSet || items instanceof DataView) { 21668 this.itemsData = items; 21669 } else { 21670 throw new TypeError('Data must be an instance of DataSet or DataView'); 21671 } 21672 21673 if (oldItemsData) { 21674 // unsubscribe from old dataset 21675 util.forEach(this.itemListeners, function (callback, event) { 21676 oldItemsData.off(event, callback); 21677 }); 21678 21679 // remove all drawn items 21680 ids = oldItemsData.getIds(); 21681 this._onRemove(ids); 21682 } 21683 21684 if (this.itemsData) { 21685 // subscribe to new dataset 21686 var id = this.id; 21687 util.forEach(this.itemListeners, function (callback, event) { 21688 me.itemsData.on(event, callback, id); 21689 }); 21690 21691 // add all new items 21692 ids = this.itemsData.getIds(); 21693 this._onAdd(ids); 21694 } 21695}; 21696 21697/** 21698 * Set groups 21699 * @param {vis.DataSet} groups 21700 */ 21701LineGraph.prototype.setGroups = function (groups) { 21702 var me = this; 21703 var ids; 21704 21705 // unsubscribe from current dataset 21706 if (this.groupsData) { 21707 util.forEach(this.groupListeners, function (callback, event) { 21708 me.groupsData.off(event, callback); 21709 }); 21710 21711 // remove all drawn groups 21712 ids = this.groupsData.getIds(); 21713 this.groupsData = null; 21714 for (var i = 0; i < ids.length; i++) { 21715 this._removeGroup(ids[i]); 21716 } 21717 } 21718 21719 // replace the dataset 21720 if (!groups) { 21721 this.groupsData = null; 21722 } else if (groups instanceof DataSet || groups instanceof DataView) { 21723 this.groupsData = groups; 21724 } else { 21725 throw new TypeError('Data must be an instance of DataSet or DataView'); 21726 } 21727 21728 if (this.groupsData) { 21729 // subscribe to new dataset 21730 var id = this.id; 21731 util.forEach(this.groupListeners, function (callback, event) { 21732 me.groupsData.on(event, callback, id); 21733 }); 21734 21735 // draw all ms 21736 ids = this.groupsData.getIds(); 21737 this._onAddGroups(ids); 21738 } 21739}; 21740 21741LineGraph.prototype._onUpdate = function (ids) { 21742 this._updateAllGroupData(ids); 21743}; 21744LineGraph.prototype._onAdd = function (ids) { 21745 this._onUpdate(ids); 21746}; 21747LineGraph.prototype._onRemove = function (ids) { 21748 this._onUpdate(ids); 21749}; 21750LineGraph.prototype._onUpdateGroups = function (groupIds) { 21751 this._updateAllGroupData(null, groupIds); 21752}; 21753LineGraph.prototype._onAddGroups = function (groupIds) { 21754 this._onUpdateGroups(groupIds); 21755}; 21756 21757/** 21758 * this cleans the group out off the legends and the dataaxis, updates the ungrouped and updates the graph 21759 * @param {Array} groupIds 21760 * @private 21761 */ 21762LineGraph.prototype._onRemoveGroups = function (groupIds) { 21763 for (var i = 0; i < groupIds.length; i++) { 21764 this._removeGroup(groupIds[i]); 21765 } 21766 this.forceGraphUpdate = true; 21767 this.body.emitter.emit("_change", { queue: true }); 21768}; 21769 21770/** 21771 * this cleans the group out off the legends and the dataaxis 21772 * @param {vis.GraphGroup.id} groupId 21773 * @private 21774 */ 21775LineGraph.prototype._removeGroup = function (groupId) { 21776 if (this.groups.hasOwnProperty(groupId)) {
21777 if (this.groups[groupId].options.yAxisOrientation == 'right') { 21778 this.yAxisRight.removeGroup(groupId); 21779 this.legendRight.removeGroup(groupId); 21780 this.legendRight.redraw(); 21781 } else { 21782 this.yAxisLeft.removeGroup(groupId); 21783 this.legendLeft.removeGroup(groupId); 21784 this.legendLeft.redraw(); 21785 } 21786 delete this.groups[groupId]; 21787 } 21788}; 21789 21790/** 21791 * update a group object with the group dataset entree 21792 * 21793 * @param {vis.GraphGroup} group 21794 * @param {vis.GraphGroup.id} groupId 21795 * @private 21796 */ 21797LineGraph.prototype._updateGroup = function (group, groupId) { 21798 if (!this.groups.hasOwnProperty(groupId)) { 21799 this.groups[groupId] = new GraphGroup(group, groupId, this.options, this.groupsUsingDefaultStyles); 21800 if (this.groups[groupId].options.yAxisOrientation == 'right') { 21801 this.yAxisRight.addGroup(groupId, this.groups[groupId]); 21802 this.legendRight.addGroup(groupId, this.groups[groupId]); 21803 } else { 21804 this.yAxisLeft.addGroup(groupId, this.groups[groupId]); 21805 this.legendLeft.addGroup(groupId, this.groups[groupId]); 21806 } 21807 } else { 21808 this.groups[groupId].update(group);
21809 if (this.groups[groupId].options.yAxisOrientation == 'right') { 21810 this.yAxisRight.updateGroup(groupId, this.groups[groupId]); 21811 this.legendRight.updateGroup(groupId, this.groups[groupId]); 21812 //If yAxisOrientation changed, clean out the group from the other axis. 21813 this.yAxisLeft.removeGroup(groupId); 21814 this.legendLeft.removeGroup(groupId); 21815 } else { 21816 this.yAxisLeft.updateGroup(groupId, this.groups[groupId]); 21817 this.legendLeft.updateGroup(groupId, this.groups[groupId]); 21818 //If yAxisOrientation changed, clean out the group from the other axis. 21819 this.yAxisRight.removeGroup(groupId); 21820 this.legendRight.removeGroup(groupId); 21821 } 21822 } 21823 this.legendLeft.redraw(); 21824 this.legendRight.redraw(); 21825}; 21826 21827/** 21828 * this updates all groups, it is used when there is an update the the itemset. 21829 * 21830 * @param {Array} ids 21831 * @param {Array} groupIds 21832 * @private 21833 */ 21834LineGraph.prototype._updateAllGroupData = function (ids, groupIds) { 21835 if (this.itemsData != null) { 21836 var groupsContent = {}; 21837 var items = this.itemsData.get(); 21838 var fieldId = this.itemsData._fieldId; 21839 var idMap = {}; 21840 if (ids) { 21841 ids.map(function (id) { 21842 idMap[id] = id; 21843 }); 21844 } 21845 21846 //pre-Determine array sizes, for more efficient memory claim 21847 var groupCounts = {}; 21848 for (var i = 0; i < items.length; i++) { 21849 var item = items[i]; 21850 var groupId = item.group; 21851 if (groupId === null || groupId === undefined) { 21852 groupId = UNGROUPED; 21853 } 21854 groupCounts.hasOwnProperty(groupId) ? groupCounts[groupId]++ : groupCounts[groupId] = 1; 21855 } 21856 21857 //Pre-load arrays from existing groups if items are not changed (not in ids) 21858 var existingItemsMap = {}; 21859 if (!groupIds && ids) { 21860 for (groupId in this.groups) { 21861 if (this.groups.hasOwnProperty(groupId)) { 21862 group = this.groups[groupId]; 21863 var existing_items = group.getItems(); 21864 21865 groupsContent[groupId] = existing_items.filter(function (item) { 21866 existingItemsMap[item[fieldId]] = item[fieldId]; 21867 return item[fieldId] !== idMap[item[fieldId]]; 21868 }); 21869 var newLength = groupCounts[groupId]; 21870 groupCounts[groupId] -= groupsContent[groupId].length; 21871 if (groupsContent[groupId].length < newLength) { 21872 groupsContent[groupId][newLength - 1] = {}; 21873 } 21874 } 21875 } 21876 } 21877 21878 //Now insert data into the arrays. 21879 for (i = 0; i < items.length; i++) { 21880 item = items[i]; 21881 groupId = item.group; 21882 if (groupId === null || groupId === undefined) { 21883 groupId = UNGROUPED; 21884 } 21885 if (!groupIds && ids && item[fieldId] !== idMap[item[fieldId]] && existingItemsMap.hasOwnProperty(item[fieldId])) { 21886 continue; 21887 } 21888 if (!groupsContent.hasOwnProperty(groupId)) { 21889 groupsContent[groupId] = new Array(groupCounts[groupId]); 21890 } 21891 //Copy data (because of unmodifiable DataView input. 21892 var extended = util.bridgeObject(item); 21893 extended.x = util.convert(item.x, 'Date'); 21894 extended.end = util.convert(item.end, 'Date'); 21895 extended.orginalY = item.y; //real Y 21896 extended.y = Number(item.y); 21897 extended[fieldId] = item[fieldId]; 21898 21899 var index = groupsContent[groupId].length - groupCounts[groupId]--; 21900 groupsContent[groupId][index] = extended; 21901 } 21902 21903 //Make sure all groups are present, to allow removal of old groups 21904 for (groupId in this.groups) { 21905 if (this.groups.hasOwnProperty(groupId)) { 21906 if (!groupsContent.hasOwnProperty(groupId)) { 21907 groupsContent[groupId] = new Array(0); 21908 } 21909 } 21910 } 21911 21912 //Update legendas, style and axis 21913 for (groupId in groupsContent) { 21914 if (groupsContent.hasOwnProperty(groupId)) { 21915 if (groupsContent[groupId].length == 0) { 21916 if (this.groups.hasOwnProperty(groupId)) { 21917 this._removeGroup(groupId); 21918 } 21919 } else { 21920 var group = undefined; 21921 if (this.groupsData != undefined) { 21922 group = this.groupsData.get(groupId); 21923 } 21924 if (group == undefined) { 21925 group = { id: groupId, content: this.options.defaultGroup + groupId }; 21926 } 21927 this._updateGroup(group, groupId); 21928 this.groups[groupId].setItems(groupsContent[groupId]); 21929 } 21930 } 21931 } 21932 this.forceGraphUpdate = true; 21933 this.body.emitter.emit("_change", { queue: true }); 21934 } 21935}; 21936 21937/** 21938 * Redraw the component, mandatory function 21939 * @return {boolean} Returns true if the component is resized 21940 */ 21941LineGraph.prototype.redraw = function () { 21942 var resized = false;
vendor: 19,626 bytes, lines 21943-22472
21943 21944 // calculate actual size and position 21945 this.props.width = this.dom.frame.offsetWidth; 21946 this.props.height = this.body.domProps.centerContainer.height - this.body.domProps.border.top - this.body.domProps.border.bottom; 21947 21948 // check if this component is resized 21949 resized = this._isResized() || resized; 21950 21951 // check whether zoomed (in that case we need to re-stack everything) 21952 var visibleInterval = this.body.range.end - this.body.range.start; 21953 var zoomed = visibleInterval != this.lastVisibleInterval; 21954 this.lastVisibleInterval = visibleInterval; 21955 21956 // the svg element is three times as big as the width, this allows for fully dragging left and right 21957 // without reloading the graph. the controls for this are bound to events in the constructor 21958 if (resized == true) { 21959 this.svg.style.width = util.option.asSize(3 * this.props.width); 21960 this.svg.style.left = util.option.asSize(-this.props.width); 21961 21962 // if the height of the graph is set as proportional, change the height of the svg 21963 if ((this.options.height + '').indexOf("%") != -1 || this.updateSVGheightOnResize == true) { 21964 this.updateSVGheight = true; 21965 } 21966 } 21967 21968 // update the height of the graph on each redraw of the graph. 21969 if (this.updateSVGheight == true) { 21970 if (this.options.graphHeight != this.props.height + 'px') { 21971 this.options.graphHeight = this.props.height + 'px'; 21972 this.svg.style.height = this.props.height + 'px'; 21973 } 21974 this.updateSVGheight = false; 21975 } else { 21976 this.svg.style.height = ('' + this.options.graphHeight).replace('px', '') + 'px'; 21977 } 21978 21979 // zoomed is here to ensure that animations are shown correctly. 21980 if (resized == true || zoomed == true || this.abortedGraphUpdate == true || this.forceGraphUpdate == true) { 21981 resized = this._updateGraph() || resized; 21982 this.forceGraphUpdate = false; 21983 } else { 21984 // move the whole svg while dragging 21985 if (this.lastStart != 0) { 21986 var offset = this.body.range.start - this.lastStart; 21987 var range = this.body.range.end - this.body.range.start; 21988 if (this.props.width != 0) { 21989 var rangePerPixelInv = this.props.width / range; 21990 var xOffset = offset * rangePerPixelInv; 21991 this.svg.style.left = -this.props.width - xOffset + 'px'; 21992 } 21993 } 21994 } 21995 this.legendLeft.redraw(); 21996 this.legendRight.redraw(); 21997 return resized; 21998}; 21999 22000LineGraph.prototype._getSortedGroupIds = function () { 22001 // getting group Ids 22002 var grouplist = []; 22003 for (var groupId in this.groups) { 22004 if (this.groups.hasOwnProperty(groupId)) { 22005 var group = this.groups[groupId]; 22006 if (group.visible == true && (this.options.groups.visibility[groupId] === undefined || this.options.groups.visibility[groupId] == true)) { 22007 grouplist.push({ id: groupId, zIndex: group.options.zIndex }); 22008 } 22009 } 22010 } 22011 util.insertSort(grouplist, function (a, b) { 22012 var az = a.zIndex; 22013 var bz = b.zIndex; 22014 if (az === undefined) az = 0; 22015 if (bz === undefined) bz = 0; 22016 return az == bz ? 0 : az < bz ? -1 : 1; 22017 }); 22018 var groupIds = new Array(grouplist.length); 22019 for (var i = 0; i < grouplist.length; i++) { 22020 groupIds[i] = grouplist[i].id; 22021 } 22022 return groupIds; 22023}; 22024 22025/** 22026 * Update and redraw the graph. 22027 * 22028 * @returns {boolean} 22029 * @private 22030 */ 22031LineGraph.prototype._updateGraph = function () { 22032 // reset the svg elements 22033 DOMutil.prepareElements(this.svgElements); 22034 if (this.props.width != 0 && this.itemsData != null) { 22035 var group, i; 22036 var groupRanges = {}; 22037 var changeCalled = false; 22038 // this is the range of the SVG canvas 22039 var minDate = this.body.util.toGlobalTime(-this.body.domProps.root.width); 22040 var maxDate = this.body.util.toGlobalTime(2 * this.body.domProps.root.width); 22041 22042 // getting group Ids 22043 var groupIds = this._getSortedGroupIds(); 22044 if (groupIds.length > 0) { 22045 var groupsData = {}; 22046 22047 // fill groups data, this only loads the data we require based on the timewindow 22048 this._getRelevantData(groupIds, groupsData, minDate, maxDate); 22049 22050 // apply sampling, if disabled, it will pass through this function. 22051 this._applySampling(groupIds, groupsData); 22052 22053 // we transform the X coordinates to detect collisions 22054 for (i = 0; i < groupIds.length; i++) { 22055 this._convertXcoordinates(groupsData[groupIds[i]]); 22056 } 22057 22058 // now all needed data has been collected we start the processing. 22059 this._getYRanges(groupIds, groupsData, groupRanges); 22060 22061 // update the Y axis first, we use this data to draw at the correct Y points 22062 changeCalled = this._updateYAxis(groupIds, groupRanges); 22063 22064 // at changeCalled, abort this update cycle as the graph needs another update with new Width input from the Redraw container. 22065 // Cleanup SVG elements on abort. 22066 if (changeCalled == true) { 22067 DOMutil.cleanupElements(this.svgElements); 22068 this.abortedGraphUpdate = true; 22069 return true; 22070 } 22071 this.abortedGraphUpdate = false; 22072 22073 // With the yAxis scaled correctly, use this to get the Y values of the points. 22074 var below = undefined; 22075 for (i = 0; i < groupIds.length; i++) { 22076 group = this.groups[groupIds[i]]; 22077 if (this.options.stack === true && this.options.style === 'line') { 22078 if (group.options.excludeFromStacking == undefined || !group.options.excludeFromStacking) { 22079 if (below != undefined) { 22080 this._stack(groupsData[group.id], groupsData[below.id]); 22081 if (group.options.shaded.enabled == true && group.options.shaded.orientation !== "group") { 22082 if (group.options.shaded.orientation == "top" && below.options.shaded.orientation !== "group") { 22083 below.options.shaded.orientation = "group"; 22084 below.options.shaded.groupId = group.id; 22085 } else { 22086 group.options.shaded.orientation = "group"; 22087 group.options.shaded.groupId = below.id; 22088 } 22089 } 22090 } 22091 below = group; 22092 } 22093 } 22094 this._convertYcoordinates(groupsData[groupIds[i]], group); 22095 } 22096 22097 //Precalculate paths and draw shading if appropriate. This will make sure the shading is always behind any lines. 22098 var paths = {}; 22099 for (i = 0; i < groupIds.length; i++) { 22100 group = this.groups[groupIds[i]]; 22101 if (group.options.style === 'line' && group.options.shaded.enabled == true) { 22102 var dataset = groupsData[groupIds[i]]; 22103 if (dataset == null || dataset.length == 0) { 22104 continue; 22105 } 22106 if (!paths.hasOwnProperty(groupIds[i])) { 22107 paths[groupIds[i]] = Lines.calcPath(dataset, group); 22108 } 22109 if (group.options.shaded.orientation === "group") { 22110 var subGroupId = group.options.shaded.groupId; 22111 if (groupIds.indexOf(subGroupId) === -1) { 22112 console.log(group.id + ": Unknown shading group target given:" + subGroupId); 22113 continue; 22114 } 22115 if (!paths.hasOwnProperty(subGroupId)) { 22116 paths[subGroupId] = Lines.calcPath(groupsData[subGroupId], this.groups[subGroupId]); 22117 } 22118 Lines.drawShading(paths[groupIds[i]], group, paths[subGroupId], this.framework); 22119 } else { 22120 Lines.drawShading(paths[groupIds[i]], group, undefined, this.framework); 22121 } 22122 } 22123 } 22124 22125 // draw the groups, calculating paths if still necessary. 22126 Bars.draw(groupIds, groupsData, this.framework); 22127 for (i = 0; i < groupIds.length; i++) { 22128 group = this.groups[groupIds[i]]; 22129 if (groupsData[groupIds[i]].length > 0) { 22130 switch (group.options.style) { 22131 case "line": 22132 if (!paths.hasOwnProperty(groupIds[i])) { 22133 paths[groupIds[i]] = Lines.calcPath(groupsData[groupIds[i]], group); 22134 } 22135 Lines.draw(paths[groupIds[i]], group, this.framework); 22136 // eslint-disable-line no-fallthrough 22137 case "point": 22138 // eslint-disable-line no-fallthrough 22139 case "points": 22140 if (group.options.style == "point" || group.options.style == "points" || group.options.drawPoints.enabled == true) { 22141 Points.draw(groupsData[groupIds[i]], group, this.framework); 22142 } 22143 break; 22144 case "bar": 22145 // bar needs to be drawn enmasse 22146 // eslint-disable-line no-fallthrough 22147 default: 22148 //do nothing... 22149 } 22150 } 22151 } 22152 } 22153 } 22154 22155 // cleanup unused svg elements 22156 DOMutil.cleanupElements(this.svgElements); 22157 return false; 22158}; 22159 22160LineGraph.prototype._stack = function (data, subData) { 22161 var index, dx, dy, subPrevPoint, subNextPoint; 22162 index = 0; 22163 // for each data point we look for a matching on in the set below 22164 for (var j = 0; j < data.length; j++) { 22165 subPrevPoint = undefined; 22166 subNextPoint = undefined; 22167 // we look for time matches or a before-after point 22168 for (var k = index; k < subData.length; k++) { 22169 // if times match exactly 22170 if (subData[k].x === data[j].x) { 22171 subPrevPoint = subData[k]; 22172 subNextPoint = subData[k]; 22173 index = k; 22174 break; 22175 } else if (subData[k].x > data[j].x) { 22176 // overshoot 22177 subNextPoint = subData[k]; 22178 if (k == 0) { 22179 subPrevPoint = subNextPoint; 22180 } else { 22181 subPrevPoint = subData[k - 1]; 22182 } 22183 index = k; 22184 break; 22185 } 22186 } 22187 // in case the last data point has been used, we assume it stays like this. 22188 if (subNextPoint === undefined) { 22189 subPrevPoint = subData[subData.length - 1]; 22190 subNextPoint = subData[subData.length - 1]; 22191 } 22192 // linear interpolation 22193 dx = subNextPoint.x - subPrevPoint.x; 22194 dy = subNextPoint.y - subPrevPoint.y; 22195 if (dx == 0) { 22196 data[j].y = data[j].orginalY + subNextPoint.y; 22197 } else { 22198 data[j].y = data[j].orginalY + dy / dx * (data[j].x - subPrevPoint.x) + subPrevPoint.y; // ax + b where b is data[j].y 22199 } 22200 } 22201}; 22202 22203/** 22204 * first select and preprocess the data from the datasets. 22205 * the groups have their preselection of data, we now loop over this data to see 22206 * what data we need to draw. Sorted data is much faster. 22207 * more optimization is possible by doing the sampling before and using the binary search 22208 * to find the end date to determine the increment. 22209 * 22210 * @param {array} groupIds 22211 * @param {object} groupsData 22212 * @param {date} minDate 22213 * @param {date} maxDate 22214 * @private 22215 */ 22216LineGraph.prototype._getRelevantData = function (groupIds, groupsData, minDate, maxDate) { 22217 var group, i, j, item; 22218 if (groupIds.length > 0) { 22219 for (i = 0; i < groupIds.length; i++) { 22220 group = this.groups[groupIds[i]]; 22221 var itemsData = group.getItems(); 22222 // optimization for sorted data 22223 if (group.options.sort == true) { 22224 var dateComparator = function dateComparator(a, b) { 22225 return a.getTime() == b.getTime() ? 0 : a < b ? -1 : 1; 22226 }; 22227 var first = Math.max(0, util.binarySearchValue(itemsData, minDate, 'x', 'before', dateComparator)); 22228 var last = Math.min(itemsData.length, util.binarySearchValue(itemsData, maxDate, 'x', 'after', dateComparator) + 1); 22229 if (last <= 0) { 22230 last = itemsData.length; 22231 } 22232 var dataContainer = new Array(last - first); 22233 for (j = first; j < last; j++) { 22234 item = group.itemsData[j]; 22235 dataContainer[j - first] = item; 22236 } 22237 groupsData[groupIds[i]] = dataContainer; 22238 } else { 22239 // If unsorted data, all data is relevant, just returning entire structure 22240 groupsData[groupIds[i]] = group.itemsData; 22241 } 22242 } 22243 } 22244}; 22245 22246/** 22247 * 22248 * @param {Array.<vis.GraphGroup.id>} groupIds 22249 * @param {vis.DataSet} groupsData 22250 * @private 22251 */ 22252LineGraph.prototype._applySampling = function (groupIds, groupsData) { 22253 var group; 22254 if (groupIds.length > 0) { 22255 for (var i = 0; i < groupIds.length; i++) { 22256 group = this.groups[groupIds[i]]; 22257 if (group.options.sampling == true) { 22258 var dataContainer = groupsData[groupIds[i]]; 22259 if (dataContainer.length > 0) { 22260 var increment = 1; 22261 var amountOfPoints = dataContainer.length; 22262 22263 // the global screen is used because changing the width of the yAxis may affect the increment, resulting in an endless loop 22264 // of width changing of the yAxis. 22265 //TODO: This assumes sorted data, but that's not guaranteed! 22266 var xDistance = this.body.util.toGlobalScreen(dataContainer[dataContainer.length - 1].x) - this.body.util.toGlobalScreen(dataContainer[0].x); 22267 var pointsPerPixel = amountOfPoints / xDistance; 22268 increment = Math.min(Math.ceil(0.2 * amountOfPoints), Math.max(1, Math.round(pointsPerPixel))); 22269 22270 var sampledData = new Array(amountOfPoints); 22271 for (var j = 0; j < amountOfPoints; j += increment) { 22272 var idx = Math.round(j / increment); 22273 sampledData[idx] = dataContainer[j]; 22274 } 22275 groupsData[groupIds[i]] = sampledData.splice(0, Math.round(amountOfPoints / increment)); 22276 } 22277 } 22278 } 22279 } 22280}; 22281 22282/** 22283 * 22284 * @param {Array.<vis.GraphGroup.id>} groupIds 22285 * @param {vis.DataSet} groupsData 22286 * @param {object} groupRanges | this is being filled here 22287 * @private 22288 */ 22289LineGraph.prototype._getYRanges = function (groupIds, groupsData, groupRanges) { 22290 var groupData, group, i; 22291 var combinedDataLeft = []; 22292 var combinedDataRight = []; 22293 var options; 22294 if (groupIds.length > 0) { 22295 for (i = 0; i < groupIds.length; i++) { 22296 groupData = groupsData[groupIds[i]]; 22297 options = this.groups[groupIds[i]].options; 22298 if (groupData.length > 0) { 22299 group = this.groups[groupIds[i]]; 22300 // if bar graphs are stacked, their range need to be handled differently and accumulated over all groups. 22301 if (options.stack === true && options.style === 'bar') { 22302 if (options.yAxisOrientation === 'left') { 22303 combinedDataLeft = combinedDataLeft.concat(groupData); 22304 } else { 22305 combinedDataRight = combinedDataRight.concat(groupData); 22306 } 22307 } else { 22308 groupRanges[groupIds[i]] = group.getYRange(groupData, groupIds[i]); 22309 } 22310 } 22311 } 22312 22313 // if bar graphs are stacked, their range need to be handled differently and accumulated over all groups. 22314 Bars.getStackedYRange(combinedDataLeft, groupRanges, groupIds, '__barStackLeft', 'left'); 22315 Bars.getStackedYRange(combinedDataRight, groupRanges, groupIds, '__barStackRight', 'right'); 22316 } 22317}; 22318 22319/** 22320 * this sets the Y ranges for the Y axis. It also determines which of the axis should be shown or hidden. 22321 * @param {Array.<vis.GraphGroup.id>} groupIds 22322 * @param {Object} groupRanges 22323 * @returns {boolean} resized 22324 * @private 22325 */ 22326LineGraph.prototype._updateYAxis = function (groupIds, groupRanges) { 22327 var resized = false; 22328 var yAxisLeftUsed = false; 22329 var yAxisRightUsed = false; 22330 var minLeft = 1e9, 22331 minRight = 1e9, 22332 maxLeft = -1e9, 22333 maxRight = -1e9, 22334 minVal, 22335 maxVal; 22336 // if groups are present 22337 if (groupIds.length > 0) { 22338 // this is here to make sure that if there are no items in the axis but there are groups, that there is no infinite draw/redraw loop. 22339 for (var i = 0; i < groupIds.length; i++) { 22340 var group = this.groups[groupIds[i]]; 22341 if (group && group.options.yAxisOrientation != 'right') { 22342 yAxisLeftUsed = true; 22343 minLeft = 1e9; 22344 maxLeft = -1e9; 22345 } else if (group && group.options.yAxisOrientation) { 22346 yAxisRightUsed = true; 22347 minRight = 1e9; 22348 maxRight = -1e9; 22349 } 22350 } 22351 22352 // if there are items: 22353 for (i = 0; i < groupIds.length; i++) { 22354 if (groupRanges.hasOwnProperty(groupIds[i])) { 22355 if (groupRanges[groupIds[i]].ignore !== true) { 22356 minVal = groupRanges[groupIds[i]].min; 22357 maxVal = groupRanges[groupIds[i]].max; 22358 22359 if (groupRanges[groupIds[i]].yAxisOrientation != 'right') { 22360 yAxisLeftUsed = true; 22361 minLeft = minLeft > minVal ? minVal : minLeft; 22362 maxLeft = maxLeft < maxVal ? maxVal : maxLeft; 22363 } else { 22364 yAxisRightUsed = true; 22365 minRight = minRight > minVal ? minVal : minRight; 22366 maxRight = maxRight < maxVal ? maxVal : maxRight; 22367 } 22368 } 22369 } 22370 } 22371 22372 if (yAxisLeftUsed == true) { 22373 this.yAxisLeft.setRange(minLeft, maxLeft); 22374 } 22375 if (yAxisRightUsed == true) { 22376 this.yAxisRight.setRange(minRight, maxRight); 22377 } 22378 } 22379 resized = this._toggleAxisVisiblity(yAxisLeftUsed, this.yAxisLeft) || resized; 22380 resized = this._toggleAxisVisiblity(yAxisRightUsed, this.yAxisRight) || resized; 22381 22382 if (yAxisRightUsed == true && yAxisLeftUsed == true) { 22383 this.yAxisLeft.drawIcons = true; 22384 this.yAxisRight.drawIcons = true; 22385 } else { 22386 this.yAxisLeft.drawIcons = false; 22387 this.yAxisRight.drawIcons = false; 22388 } 22389 this.yAxisRight.master = !yAxisLeftUsed; 22390 this.yAxisRight.masterAxis = this.yAxisLeft; 22391 22392 if (this.yAxisRight.master == false) { 22393 if (yAxisRightUsed == true) { 22394 this.yAxisLeft.lineOffset = this.yAxisRight.width; 22395 } else { 22396 this.yAxisLeft.lineOffset = 0; 22397 } 22398 22399 resized = this.yAxisLeft.redraw() || resized; 22400 resized = this.yAxisRight.redraw() || resized; 22401 } else { 22402 resized = this.yAxisRight.redraw() || resized; 22403 } 22404 22405 // clean the accumulated lists 22406 var tempGroups = ['__barStackLeft', '__barStackRight', '__lineStackLeft', '__lineStackRight']; 22407 for (i = 0; i < tempGroups.length; i++) { 22408 if (groupIds.indexOf(tempGroups[i]) != -1) { 22409 groupIds.splice(groupIds.indexOf(tempGroups[i]), 1); 22410 } 22411 } 22412 22413 return resized; 22414}; 22415 22416/** 22417 * This shows or hides the Y axis if needed. If there is a change, the changed event is emitted by the updateYAxis function 22418 * 22419 * @param {boolean} axisUsed 22420 * @param {vis.DataAxis} axis 22421 * @returns {boolean} 22422 * @private 22423 */ 22424LineGraph.prototype._toggleAxisVisiblity = function (axisUsed, axis) { 22425 var changed = false; 22426 if (axisUsed == false) { 22427 if (axis.dom.frame.parentNode && axis.hidden == false) { 22428 axis.hide(); 22429 changed = true; 22430 } 22431 } else { 22432 if (!axis.dom.frame.parentNode && axis.hidden == true) { 22433 axis.show(); 22434 changed = true; 22435 } 22436 } 22437 return changed; 22438}; 22439 22440/** 22441 * This uses the DataAxis object to generate the correct X coordinate on the SVG window. It uses the 22442 * util function toScreen to get the x coordinate from the timestamp. It also pre-filters the data and get the minMax ranges for 22443 * the yAxis. 22444 * 22445 * @param {Array.<Object>} datapoints 22446 * @private 22447 */ 22448LineGraph.prototype._convertXcoordinates = function (datapoints) { 22449 var toScreen = this.body.util.toScreen; 22450 for (var i = 0; i < datapoints.length; i++) { 22451 datapoints[i].screen_x = toScreen(datapoints[i].x) + this.props.width; 22452 datapoints[i].screen_y = datapoints[i].y; //starting point for range calculations 22453 if (datapoints[i].end != undefined) { 22454 datapoints[i].screen_end = toScreen(datapoints[i].end) + this.props.width; 22455 } else { 22456 datapoints[i].screen_end = undefined; 22457 } 22458 } 22459}; 22460 22461/** 22462 * This uses the DataAxis object to generate the correct X coordinate on the SVG window. It uses the 22463 * util function toScreen to get the x coordinate from the timestamp. It also pre-filters the data and get the minMax ranges for 22464 * the yAxis. 22465 * 22466 * @param {Array.<Object>} datapoints 22467 * @param {vis.GraphGroup} group 22468 * @private 22469 */ 22470LineGraph.prototype._convertYcoordinates = function (datapoints, group) { 22471 var axis = this.yAxisLeft; 22472 var svgHeight = Number(this.svg.style.height.replace('px', ''));
22473 if (group.options.yAxisOrientation == 'right') { 22474 axis = this.yAxisRight; 22475 } 22476 for (var i = 0; i < datapoints.length; i++) { 22477 datapoints[i].screen_y = Math.round(axis.convertValue(datapoints[i].y)); 22478 } 22479 group.setZeroPosition(Math.min(svgHeight, axis.convertValue(0))); 22480}; 22481 22482module.exports = LineGraph; 22483 22484/***/ }), 22485/* 107 */ 22486/***/ (function(module, exports, __webpack_require__) { 22487 22488"use strict"; 22489 22490 22491var _keys = __webpack_require__(8); 22492 22493var _keys2 = _interopRequireDefault(_keys); 22494 22495function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 22496 22497var util = __webpack_require__(2); 22498var DOMutil = __webpack_require__(14); 22499var Component = __webpack_require__(16); 22500var DataScale = __webpack_require__(108); 22501/** 22502 * A horizontal time axis 22503 * @param {Object} body 22504 * @param {Object} [options] See DataAxis.setOptions for the available 22505 * options. 22506 * @param {SVGElement} svg 22507 * @param {vis.LineGraph.options} linegraphOptions 22508 * @constructor DataAxis 22509 * @extends Component 22510 */ 22511function DataAxis(body, options, svg, linegraphOptions) { 22512 this.id = util.randomUUID(); 22513 this.body = body; 22514 22515 this.defaultOptions = { 22516 orientation: 'left', // supported: 'left', 'right' 22517 showMinorLabels: true, 22518 showMajorLabels: true, 22519 icons: false, 22520 majorLinesOffset: 7, 22521 minorLinesOffset: 4, 22522 labelOffsetX: 10, 22523 labelOffsetY: 2, 22524 iconWidth: 20, 22525 width: '40px', 22526 visible: true, 22527 alignZeros: true, 22528 left: { 22529 range: { min: undefined, max: undefined }, 22530 format: function format(value) { 22531 return '' + parseFloat(value.toPrecision(3)); 22532 }, 22533 title: { text: undefined, style: undefined } 22534 }, 22535 right: { 22536 range: { min: undefined, max: undefined }, 22537 format: function format(value) { 22538 return '' + parseFloat(value.toPrecision(3)); 22539 }, 22540 title: { text: undefined, style: undefined } 22541 } 22542 }; 22543 22544 this.linegraphOptions = linegraphOptions; 22545 this.linegraphSVG = svg; 22546 this.props = {}; 22547 this.DOMelements = { // dynamic elements 22548 lines: {}, 22549 labels: {}, 22550 title: {} 22551 }; 22552 22553 this.dom = {}; 22554 this.scale = undefined; 22555 this.range = { start: 0, end: 0 }; 22556 22557 this.options = util.extend({}, this.defaultOptions); 22558 this.conversionFactor = 1; 22559 22560 this.setOptions(options); 22561 this.width = Number(('' + this.options.width).replace("px", "")); 22562 this.minWidth = this.width; 22563 this.height = this.linegraphSVG.getBoundingClientRect().height; 22564 this.hidden = false; 22565 22566 this.stepPixels = 25; 22567 this.zeroCrossing = -1; 22568 this.amountOfSteps = -1; 22569 22570 this.lineOffset = 0; 22571 this.master = true; 22572 this.masterAxis = null; 22573 this.svgElements = {}; 22574 this.iconsRemoved = false; 22575 22576 this.groups = {}; 22577 this.amountOfGroups = 0; 22578 22579 // create the HTML DOM 22580 this._create(); 22581 this.framework = { svg: this.svg, svgElements: this.svgElements, options: this.options, groups: this.groups }; 22582 22583 var me = this; 22584 this.body.emitter.on("verticalDrag", function () { 22585 me.dom.lineContainer.style.top = me.body.domProps.scrollTop + 'px'; 22586 }); 22587} 22588 22589DataAxis.prototype = new Component(); 22590 22591DataAxis.prototype.addGroup = function (label, graphOptions) { 22592 if (!this.groups.hasOwnProperty(label)) { 22593 this.groups[label] = graphOptions; 22594 } 22595 this.amountOfGroups += 1; 22596}; 22597 22598DataAxis.prototype.updateGroup = function (label, graphOptions) { 22599 if (!this.groups.hasOwnProperty(label)) { 22600 this.amountOfGroups += 1; 22601 } 22602 this.groups[label] = graphOptions; 22603}; 22604 22605DataAxis.prototype.removeGroup = function (label) { 22606 if (this.groups.hasOwnProperty(label)) { 22607 delete this.groups[label]; 22608 this.amountOfGroups -= 1; 22609 } 22610}; 22611 22612DataAxis.prototype.setOptions = function (options) { 22613 if (options) { 22614 var redraw = false; 22615 if (this.options.orientation != options.orientation && options.orientation !== undefined) { 22616 redraw = true; 22617 } 22618 var fields = ['orientation', 'showMinorLabels', 'showMajorLabels', 'icons', 'majorLinesOffset', 'minorLinesOffset', 'labelOffsetX', 'labelOffsetY', 'iconWidth', 'width', 'visible', 'left', 'right', 'alignZeros']; 22619 util.selectiveDeepExtend(fields, this.options, options); 22620 22621 this.minWidth = Number(('' + this.options.width).replace("px", "")); 22622 if (redraw === true && this.dom.frame) { 22623 this.hide(); 22624 this.show(); 22625 } 22626 } 22627}; 22628 22629/** 22630 * Create the HTML DOM for the DataAxis 22631 */ 22632DataAxis.prototype._create = function () { 22633 this.dom.frame = document.createElement('div'); 22634 this.dom.frame.style.width = this.options.width; 22635 this.dom.frame.style.height = this.height; 22636 22637 this.dom.lineContainer = document.createElement('div'); 22638 this.dom.lineContainer.style.width = '100%'; 22639 this.dom.lineContainer.style.height = this.height; 22640 this.dom.lineContainer.style.position = 'relative'; 22641 22642 // create svg element for graph drawing. 22643 this.svg = document.createElementNS('http://www.w3.org/2000/svg', "svg"); 22644 this.svg.style.position = "absolute"; 22645 this.svg.style.top = '0px'; 22646 this.svg.style.height = '100%'; 22647 this.svg.style.width = '100%'; 22648 this.svg.style.display = "block"; 22649 this.dom.frame.appendChild(this.svg); 22650}; 22651 22652DataAxis.prototype._redrawGroupIcons = function () { 22653 DOMutil.prepareElements(this.svgElements); 22654 22655 var x; 22656 var iconWidth = this.options.iconWidth; 22657 var iconHeight = 15; 22658 var iconOffset = 4; 22659 var y = iconOffset + 0.5 * iconHeight; 22660 22661 if (this.options.orientation === 'left') { 22662 x = iconOffset; 22663 } else { 22664 x = this.width - iconWidth - iconOffset; 22665 } 22666 22667 var groupArray = (0, _keys2['default'])(this.groups); 22668 groupArray.sort(function (a, b) { 22669 return a < b ? -1 : 1; 22670 }); 22671 22672 for (var i = 0; i < groupArray.length; i++) { 22673 var groupId = groupArray[i]; 22674 if (this.groups[groupId].visible === true && (this.linegraphOptions.visibility[groupId] === undefined || this.linegraphOptions.visibility[groupId] === true)) { 22675 this.groups[groupId].getLegend(iconWidth, iconHeight, this.framework, x, y); 22676 y += iconHeight + iconOffset; 22677 } 22678 } 22679 22680 DOMutil.cleanupElements(this.svgElements); 22681 this.iconsRemoved = false;
22682}; 22683 22684DataAxis.prototype._cleanupIcons = function () { 22685 if (this.iconsRemoved === false) { 22686 DOMutil.prepareElements(this.svgElements); 22687 DOMutil.cleanupElements(this.svgElements); 22688 this.iconsRemoved = true; 22689 } 22690}; 22691 22692/** 22693 * Create the HTML DOM for the DataAxis 22694 */ 22695DataAxis.prototype.show = function () { 22696 this.hidden = false; 22697 if (!this.dom.frame.parentNode) { 22698 if (this.options.orientation === 'left') { 22699 this.body.dom.left.appendChild(this.dom.frame); 22700 } else { 22701 this.body.dom.right.appendChild(this.dom.frame); 22702 } 22703 } 22704 22705 if (!this.dom.lineContainer.parentNode) { 22706 this.body.dom.backgroundHorizontal.appendChild(this.dom.lineContainer); 22707 } 22708}; 22709 22710/** 22711 * Create the HTML DOM for the DataAxis 22712 */ 22713DataAxis.prototype.hide = function () { 22714 this.hidden = true; 22715 if (this.dom.frame.parentNode) { 22716 this.dom.frame.parentNode.removeChild(this.dom.frame); 22717 } 22718 22719 if (this.dom.lineContainer.parentNode) { 22720 this.dom.lineContainer.parentNode.removeChild(this.dom.lineContainer); 22721 } 22722}; 22723 22724/** 22725 * Set a range (start and end) 22726 * @param {number} start 22727 * @param {number} end 22728 */ 22729DataAxis.prototype.setRange = function (start, end) { 22730 this.range.start = start; 22731 this.range.end = end; 22732}; 22733 22734/** 22735 * Repaint the component 22736 * @return {boolean} Returns true if the component is resized 22737 */ 22738DataAxis.prototype.redraw = function () { 22739 var resized = false; 22740 var activeGroups = 0; 22741 22742 // Make sure the line container adheres to the vertical scrolling. 22743 this.dom.lineContainer.style.top = this.body.domProps.scrollTop + 'px'; 22744 22745 for (var groupId in this.groups) { 22746 if (this.groups.hasOwnProperty(groupId)) { 22747 if (this.groups[groupId].visible === true && (this.linegraphOptions.visibility[groupId] === undefined || this.linegraphOptions.visibility[groupId] === true)) { 22748 activeGroups++; 22749 } 22750 } 22751 } 22752 if (this.amountOfGroups === 0 || activeGroups === 0) { 22753 this.hide(); 22754 } else { 22755 this.show(); 22756 this.height = Number(this.linegraphSVG.style.height.replace("px", "")); 22757 22758 // svg offsetheight did not work in firefox and explorer... 22759 this.dom.lineContainer.style.height = this.height + 'px'; 22760 this.width = this.options.visible === true ? Number(('' + this.options.width).replace("px", "")) : 0; 22761 22762 var props = this.props; 22763 var frame = this.dom.frame; 22764 22765 // update classname 22766 frame.className = 'vis-data-axis'; 22767 22768 // calculate character width and height 22769 this._calculateCharSize(); 22770 22771 var orientation = this.options.orientation; 22772 var showMinorLabels = this.options.showMinorLabels; 22773 var showMajorLabels = this.options.showMajorLabels; 22774 22775 // determine the width and height of the elements for the axis 22776 props.minorLabelHeight = showMinorLabels ? props.minorCharHeight : 0; 22777 props.majorLabelHeight = showMajorLabels ? props.majorCharHeight : 0; 22778 22779 props.minorLineWidth = this.body.dom.backgroundHorizontal.offsetWidth - this.lineOffset - this.width + 2 * this.options.minorLinesOffset; 22780 props.minorLineHeight = 1; 22781 props.majorLineWidth = this.body.dom.backgroundHorizontal.offsetWidth - this.lineOffset - this.width + 2 * this.options.majorLinesOffset; 22782 props.majorLineHeight = 1; 22783 22784 // take frame offline while updating (is almost twice as fast) 22785 if (orientation === 'left') { 22786 frame.style.top = '0'; 22787 frame.style.left = '0'; 22788 frame.style.bottom = ''; 22789 frame.style.width = this.width + 'px'; 22790 frame.style.height = this.height + "px"; 22791 this.props.width = this.body.domProps.left.width; 22792 this.props.height = this.body.domProps.left.height; 22793 } else { 22794 // right 22795 frame.style.top = ''; 22796 frame.style.bottom = '0'; 22797 frame.style.left = '0'; 22798 frame.style.width = this.width + 'px'; 22799 frame.style.height = this.height + "px"; 22800 this.props.width = this.body.domProps.right.width; 22801 this.props.height = this.body.domProps.right.height; 22802 } 22803 22804 resized = this._redrawLabels(); 22805 resized = this._isResized() || resized; 22806 22807 if (this.options.icons === true) { 22808 this._redrawGroupIcons(); 22809 } else { 22810 this._cleanupIcons(); 22811 } 22812 22813 this._redrawTitle(orientation); 22814 } 22815 return resized; 22816}; 22817 22818/** 22819 * Repaint major and minor text labels and vertical grid lines 22820 * 22821 * @returns {boolean} 22822 * @private 22823 */ 22824DataAxis.prototype._redrawLabels = function () { 22825 var _this = this; 22826 22827 var resized = false;
22828 DOMutil.prepareElements(this.DOMelements.lines); 22829 DOMutil.prepareElements(this.DOMelements.labels); 22830 var orientation = this.options['orientation']; 22831 var customRange = this.options[orientation].range != undefined ? this.options[orientation].range : {}; 22832 22833 //Override range with manual options: 22834 var autoScaleEnd = true; 22835 if (customRange.max != undefined) { 22836 this.range.end = customRange.max; 22837 autoScaleEnd = false; 22838 } 22839 var autoScaleStart = true; 22840 if (customRange.min != undefined) { 22841 this.range.start = customRange.min; 22842 autoScaleStart = false; 22843 } 22844 22845 this.scale = new DataScale(this.range.start, this.range.end, autoScaleStart, autoScaleEnd, this.dom.frame.offsetHeight, this.props.majorCharHeight, this.options.alignZeros, this.options[orientation].format); 22846 22847 if (this.master === false && this.masterAxis != undefined) { 22848 this.scale.followScale(this.masterAxis.scale); 22849 } 22850 22851 //Is updated in side-effect of _redrawLabel(): 22852 this.maxLabelSize = 0; 22853 22854 var lines = this.scale.getLines(); 22855 lines.forEach(function (line) { 22856 var y = line.y; 22857 var isMajor = line.major; 22858 if (_this.options['showMinorLabels'] && isMajor === false) { 22859 _this._redrawLabel(y - 2, line.val, orientation, 'vis-y-axis vis-minor', _this.props.minorCharHeight); 22860 } 22861 if (isMajor) { 22862 if (y >= 0) { 22863 _this._redrawLabel(y - 2, line.val, orientation, 'vis-y-axis vis-major', _this.props.majorCharHeight); 22864 } 22865 } 22866 if (_this.master === true) { 22867 if (isMajor) { 22868 _this._redrawLine(y, orientation, 'vis-grid vis-horizontal vis-major', _this.options.majorLinesOffset, _this.props.majorLineWidth); 22869 } else { 22870 _this._redrawLine(y, orientation, 'vis-grid vis-horizontal vis-minor', _this.options.minorLinesOffset, _this.props.minorLineWidth); 22871 } 22872 } 22873 }); 22874 22875 // Note that title is rotated, so we're using the height, not width! 22876 var titleWidth = 0; 22877 if (this.options[orientation].title !== undefined && this.options[orientation].title.text !== undefined) { 22878 titleWidth = this.props.titleCharHeight; 22879 } 22880 var offset = this.options.icons === true ? Math.max(this.options.iconWidth, titleWidth) + this.options.labelOffsetX + 15 : titleWidth + this.options.labelOffsetX + 15; 22881 22882 // this will resize the yAxis to accommodate the labels. 22883 if (this.maxLabelSize > this.width - offset && this.options.visible === true) { 22884 this.width = this.maxLabelSize + offset; 22885 this.options.width = this.width + "px"; 22886 DOMutil.cleanupElements(this.DOMelements.lines); 22887 DOMutil.cleanupElements(this.DOMelements.labels); 22888 this.redraw(); 22889 resized = true; 22890 } 22891 // this will resize the yAxis if it is too big for the labels. 22892 else if (this.maxLabelSize < this.width - offset && this.options.visible === true && this.width > this.minWidth) { 22893 this.width = Math.max(this.minWidth, this.maxLabelSize + offset); 22894 this.options.width = this.width + "px"; 22895 DOMutil.cleanupElements(this.DOMelements.lines); 22896 DOMutil.cleanupElements(this.DOMelements.labels); 22897 this.redraw(); 22898 resized = true; 22899 } else { 22900 DOMutil.cleanupElements(this.DOMelements.lines); 22901 DOMutil.cleanupElements(this.DOMelements.labels); 22902 resized = false; 22903 } 22904 22905 return resized; 22906}; 22907 22908DataAxis.prototype.convertValue = function (value) { 22909 return this.scale.convertValue(value); 22910}; 22911 22912DataAxis.prototype.screenToValue = function (x) { 22913 return this.scale.screenToValue(x); 22914}; 22915 22916/** 22917 * Create a label for the axis at position x 22918 * 22919 * @param {number} y 22920 * @param {string} text 22921 * @param {'top'|'right'|'bottom'|'left'} orientation 22922 * @param {string} className 22923 * @param {number} characterHeight 22924 * @private 22925 */ 22926DataAxis.prototype._redrawLabel = function (y, text, orientation, className, characterHeight) { 22927 // reuse redundant label 22928 var label = DOMutil.getDOMElement('div', this.DOMelements.labels, this.dom.frame); //this.dom.redundant.labels.shift(); 22929 label.className = className; 22930 label.innerHTML = text; 22931 if (orientation === 'left') { 22932 label.style.left = '-' + this.options.labelOffsetX + 'px'; 22933 label.style.textAlign = "right"; 22934 } else { 22935 label.style.right = '-' + this.options.labelOffsetX + 'px'; 22936 label.style.textAlign = "left"; 22937 } 22938 22939 label.style.top = y - 0.5 * characterHeight + this.options.labelOffsetY + 'px'; 22940 22941 text += ''; 22942 22943 var largestWidth = Math.max(this.props.majorCharWidth, this.props.minorCharWidth); 22944 if (this.maxLabelSize < text.length * largestWidth) { 22945 this.maxLabelSize = text.length * largestWidth; 22946 } 22947}; 22948 22949/** 22950 * Create a minor line for the axis at position y 22951 * @param {number} y 22952 * @param {'top'|'right'|'bottom'|'left'} orientation 22953 * @param {string} className 22954 * @param {number} offset 22955 * @param {number} width 22956 */ 22957DataAxis.prototype._redrawLine = function (y, orientation, className, offset, width) { 22958 if (this.master === true) { 22959 var line = DOMutil.getDOMElement('div', this.DOMelements.lines, this.dom.lineContainer); //this.dom.redundant.lines.shift(); 22960 line.className = className; 22961 line.innerHTML = ''; 22962 22963 if (orientation === 'left') { 22964 line.style.left = this.width - offset + 'px'; 22965 } else { 22966 line.style.right = this.width - offset + 'px'; 22967 } 22968 22969 line.style.width = width + 'px'; 22970 line.style.top = y + 'px'; 22971 } 22972}; 22973 22974/** 22975 * Create a title for the axis 22976 * @private 22977 * @param {'top'|'right'|'bottom'|'left'} orientation 22978 */ 22979DataAxis.prototype._redrawTitle = function (orientation) { 22980 DOMutil.prepareElements(this.DOMelements.title); 22981 22982 // Check if the title is defined for this axes 22983 if (this.options[orientation].title !== undefined && this.options[orientation].title.text !== undefined) { 22984 var title = DOMutil.getDOMElement('div', this.DOMelements.title, this.dom.frame); 22985 title.className = 'vis-y-axis vis-title vis-' + orientation; 22986 title.innerHTML = this.options[orientation].title.text; 22987 22988 // Add style - if provided 22989 if (this.options[orientation].title.style !== undefined) { 22990 util.addCssText(title, this.options[orientation].title.style); 22991 } 22992 22993 if (orientation === 'left') { 22994 title.style.left = this.props.titleCharHeight + 'px'; 22995 } else { 22996 title.style.right = this.props.titleCharHeight + 'px'; 22997 } 22998 22999 title.style.width = this.height + 'px'; 23000 } 23001 23002 // we need to clean up in case we did not use all elements. 23003 DOMutil.cleanupElements(this.DOMelements.title); 23004}; 23005 23006/** 23007 * Determine the size of text on the axis (both major and minor axis). 23008 * The size is calculated only once and then cached in this.props. 23009 * @private 23010 */ 23011DataAxis.prototype._calculateCharSize = function () { 23012 // determine the char width and height on the minor axis 23013 if (!('minorCharHeight' in this.props)) { 23014 var textMinor = document.createTextNode('0'); 23015 var measureCharMinor = document.createElement('div'); 23016 measureCharMinor.className = 'vis-y-axis vis-minor vis-measure'; 23017 measureCharMinor.appendChild(textMinor); 23018 this.dom.frame.appendChild(measureCharMinor); 23019 23020 this.props.minorCharHeight = measureCharMinor.clientHeight; 23021 this.props.minorCharWidth = measureCharMinor.clientWidth; 23022 23023 this.dom.frame.removeChild(measureCharMinor); 23024 } 23025 23026 if (!('majorCharHeight' in this.props)) { 23027 var textMajor = document.createTextNode('0'); 23028 var measureCharMajor = document.createElement('div'); 23029 measureCharMajor.className = 'vis-y-axis vis-major vis-measure'; 23030 measureCharMajor.appendChild(textMajor); 23031 this.dom.frame.appendChild(measureCharMajor); 23032 23033 this.props.majorCharHeight = measureCharMajor.clientHeight; 23034 this.props.majorCharWidth = measureCharMajor.clientWidth; 23035 23036 this.dom.frame.removeChild(measureCharMajor); 23037 } 23038 23039 if (!('titleCharHeight' in this.props)) { 23040 var textTitle = document.createTextNode('0'); 23041 var measureCharTitle = document.createElement('div'); 23042 measureCharTitle.className = 'vis-y-axis vis-title vis-measure'; 23043 measureCharTitle.appendChild(textTitle); 23044 this.dom.frame.appendChild(measureCharTitle); 23045 23046 this.props.titleCharHeight = measureCharTitle.clientHeight; 23047 this.props.titleCharWidth = measureCharTitle.clientWidth; 23048 23049 this.dom.frame.removeChild(measureCharTitle); 23050 } 23051}; 23052 23053module.exports = DataAxis; 23054 23055/***/ }), 23056/* 108 */ 23057/***/ (function(module, exports, __webpack_require__) { 23058 23059"use strict"; 23060 23061 23062/** 23063 * 23064 * @param {number} start 23065 * @param {number} end 23066 * @param {boolean} autoScaleStart 23067 * @param {boolean} autoScaleEnd 23068 * @param {number} containerHeight 23069 * @param {number} majorCharHeight 23070 * @param {boolean} zeroAlign 23071 * @param {function} formattingFunction 23072 * @constructor DataScale 23073 */ 23074function DataScale(start, end, autoScaleStart, autoScaleEnd, containerHeight, majorCharHeight) { 23075 var zeroAlign = arguments.length > 6 && arguments[6] !== undefined ? arguments[6] : false;
23076 var formattingFunction = arguments.length > 7 && arguments[7] !== undefined ? arguments[7] : false; 23077 23078 this.majorSteps = [1, 2, 5, 10]; 23079 this.minorSteps = [0.25, 0.5, 1, 2]; 23080 this.customLines = null; 23081 23082 this.containerHeight = containerHeight; 23083 this.majorCharHeight = majorCharHeight; 23084 this._start = start; 23085 this._end = end; 23086 23087 this.scale = 1; 23088 this.minorStepIdx = -1; 23089 this.magnitudefactor = 1; 23090 this.determineScale(); 23091 23092 this.zeroAlign = zeroAlign; 23093 this.autoScaleStart = autoScaleStart; 23094 this.autoScaleEnd = autoScaleEnd; 23095 23096 this.formattingFunction = formattingFunction; 23097 23098 if (autoScaleStart || autoScaleEnd) { 23099 var me = this; 23100 var roundToMinor = function roundToMinor(value) { 23101 var rounded = value - value % (me.magnitudefactor * me.minorSteps[me.minorStepIdx]); 23102 if (value % (me.magnitudefactor * me.minorSteps[me.minorStepIdx]) > 0.5 * (me.magnitudefactor * me.minorSteps[me.minorStepIdx])) { 23103 return rounded + me.magnitudefactor * me.minorSteps[me.minorStepIdx]; 23104 } else { 23105 return rounded; 23106 } 23107 }; 23108 if (autoScaleStart) { 23109 this._start -= this.magnitudefactor * 2 * this.minorSteps[this.minorStepIdx]; 23110 this._start = roundToMinor(this._start); 23111 } 23112 23113 if (autoScaleEnd) { 23114 this._end += this.magnitudefactor * this.minorSteps[this.minorStepIdx]; 23115 this._end = roundToMinor(this._end); 23116 } 23117 this.determineScale(); 23118 } 23119} 23120 23121DataScale.prototype.setCharHeight = function (majorCharHeight) { 23122 this.majorCharHeight = majorCharHeight; 23123}; 23124 23125DataScale.prototype.setHeight = function (containerHeight) { 23126 this.containerHeight = containerHeight; 23127}; 23128 23129DataScale.prototype.determineScale = function () { 23130 var range = this._end - this._start; 23131 this.scale = this.containerHeight / range; 23132 var minimumStepValue = this.majorCharHeight / this.scale; 23133 var orderOfMagnitude = range > 0 ? Math.round(Math.log(range) / Math.LN10) : 0; 23134 23135 this.minorStepIdx = -1; 23136 this.magnitudefactor = Math.pow(10, orderOfMagnitude); 23137 23138 var start = 0; 23139 if (orderOfMagnitude < 0) { 23140 start = orderOfMagnitude; 23141 } 23142 23143 var solutionFound = false; 23144 for (var l = start; Math.abs(l) <= Math.abs(orderOfMagnitude); l++) { 23145 this.magnitudefactor = Math.pow(10, l); 23146 for (var j = 0; j < this.minorSteps.length; j++) { 23147 var stepSize = this.magnitudefactor * this.minorSteps[j]; 23148 if (stepSize >= minimumStepValue) { 23149 solutionFound = true; 23150 this.minorStepIdx = j; 23151 break; 23152 } 23153 } 23154 if (solutionFound === true) { 23155 break; 23156 } 23157 } 23158}; 23159 23160DataScale.prototype.is_major = function (value) { 23161 return value % (this.magnitudefactor * this.majorSteps[this.minorStepIdx]) === 0; 23162}; 23163 23164DataScale.prototype.getStep = function () { 23165 return this.magnitudefactor * this.minorSteps[this.minorStepIdx]; 23166}; 23167 23168DataScale.prototype.getFirstMajor = function () { 23169 var majorStep = this.magnitudefactor * this.majorSteps[this.minorStepIdx]; 23170 return this.convertValue(this._start + (majorStep - this._start % majorStep) % majorStep); 23171}; 23172 23173DataScale.prototype.formatValue = function (current) { 23174 var returnValue = current.toPrecision(5); 23175 if (typeof this.formattingFunction === 'function') { 23176 returnValue = this.formattingFunction(current); 23177 } 23178 23179 if (typeof returnValue === 'number') { 23180 return '' + returnValue; 23181 } else if (typeof returnValue === 'string') { 23182 return returnValue; 23183 } else { 23184 return current.toPrecision(5); 23185 } 23186}; 23187 23188DataScale.prototype.getLines = function () { 23189 var lines = []; 23190 var step = this.getStep(); 23191 var bottomOffset = (step - this._start % step) % step; 23192 for (var i = this._start + bottomOffset; this._end - i > 0.00001; i += step) { 23193 if (i != this._start) { 23194 //Skip the bottom line 23195 lines.push({ major: this.is_major(i), y: this.convertValue(i), val: this.formatValue(i) }); 23196 } 23197 } 23198 return lines; 23199}; 23200 23201DataScale.prototype.followScale = function (other) { 23202 var oldStepIdx = this.minorStepIdx; 23203 var oldStart = this._start; 23204 var oldEnd = this._end; 23205 23206 var me = this; 23207 var increaseMagnitude = function increaseMagnitude() { 23208 me.magnitudefactor *= 2; 23209 }; 23210 var decreaseMagnitude = function decreaseMagnitude() { 23211 me.magnitudefactor /= 2; 23212 }; 23213 23214 if (other.minorStepIdx <= 1 && this.minorStepIdx <= 1 || other.minorStepIdx > 1 && this.minorStepIdx > 1) { 23215 //easy, no need to change stepIdx nor multiplication factor 23216 } else if (other.minorStepIdx < this.minorStepIdx) { 23217 //I'm 5, they are 4 per major. 23218 this.minorStepIdx = 1; 23219 if (oldStepIdx == 2) { 23220 increaseMagnitude(); 23221 } else { 23222 increaseMagnitude(); 23223 increaseMagnitude(); 23224 } 23225 } else { 23226 //I'm 4, they are 5 per major 23227 this.minorStepIdx = 2; 23228 if (oldStepIdx == 1) { 23229 decreaseMagnitude(); 23230 } else { 23231 decreaseMagnitude(); 23232 decreaseMagnitude(); 23233 } 23234 } 23235 23236 //Get masters stats: 23237 var otherZero = other.convertValue(0);
vendor: 7,494 bytes, lines 23238-23460
23238 var otherStep = other.getStep() * other.scale; 23239 23240 var done = false; 23241 var count = 0; 23242 //Loop until magnitude is correct for given constrains. 23243 while (!done && count++ < 5) { 23244 23245 //Get my stats: 23246 this.scale = otherStep / (this.minorSteps[this.minorStepIdx] * this.magnitudefactor); 23247 var newRange = this.containerHeight / this.scale; 23248 23249 //For the case the magnitudefactor has changed: 23250 this._start = oldStart; 23251 this._end = this._start + newRange; 23252 23253 var myOriginalZero = this._end * this.scale; 23254 var majorStep = this.magnitudefactor * this.majorSteps[this.minorStepIdx]; 23255 var majorOffset = this.getFirstMajor() - other.getFirstMajor(); 23256 23257 if (this.zeroAlign) { 23258 var zeroOffset = otherZero - myOriginalZero; 23259 this._end += zeroOffset / this.scale; 23260 this._start = this._end - newRange; 23261 } else { 23262 if (!this.autoScaleStart) { 23263 this._start += majorStep - majorOffset / this.scale; 23264 this._end = this._start + newRange; 23265 } else { 23266 this._start -= majorOffset / this.scale; 23267 this._end = this._start + newRange; 23268 } 23269 } 23270 if (!this.autoScaleEnd && this._end > oldEnd + 0.00001) { 23271 //Need to decrease magnitude to prevent scale overshoot! (end) 23272 decreaseMagnitude(); 23273 done = false; 23274 continue; 23275 } 23276 if (!this.autoScaleStart && this._start < oldStart - 0.00001) { 23277 if (this.zeroAlign && oldStart >= 0) { 23278 console.warn("Can't adhere to given 'min' range, due to zeroalign"); 23279 } else { 23280 //Need to decrease magnitude to prevent scale overshoot! (start) 23281 decreaseMagnitude(); 23282 done = false; 23283 continue; 23284 } 23285 } 23286 if (this.autoScaleStart && this.autoScaleEnd && newRange < oldEnd - oldStart) { 23287 increaseMagnitude(); 23288 done = false; 23289 continue; 23290 } 23291 done = true; 23292 } 23293}; 23294 23295DataScale.prototype.convertValue = function (value) { 23296 return this.containerHeight - (value - this._start) * this.scale; 23297}; 23298 23299DataScale.prototype.screenToValue = function (pixels) { 23300 return (this.containerHeight - pixels) / this.scale + this._start; 23301}; 23302 23303module.exports = DataScale; 23304 23305/***/ }), 23306/* 109 */ 23307/***/ (function(module, exports, __webpack_require__) { 23308 23309"use strict"; 23310 23311 23312var _typeof2 = __webpack_require__(6); 23313 23314var _typeof3 = _interopRequireDefault(_typeof2); 23315 23316function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 23317 23318var util = __webpack_require__(2); 23319var Bars = __webpack_require__(110); 23320var Lines = __webpack_require__(111); 23321var Points = __webpack_require__(72); 23322 23323/** 23324 * /** 23325 * @param {object} group | the object of the group from the dataset 23326 * @param {string} groupId | ID of the group 23327 * @param {object} options | the default options 23328 * @param {array} groupsUsingDefaultStyles | this array has one entree. 23329 * It is passed as an array so it is passed by reference. 23330 * It enumerates through the default styles 23331 * @constructor GraphGroup 23332 */ 23333function GraphGroup(group, groupId, options, groupsUsingDefaultStyles) { 23334 this.id = groupId; 23335 var fields = ['sampling', 'style', 'sort', 'yAxisOrientation', 'barChart', 'drawPoints', 'shaded', 'interpolation', 'zIndex', 'excludeFromStacking', 'excludeFromLegend']; 23336 this.options = util.selectiveBridgeObject(fields, options); 23337 this.usingDefaultStyle = group.className === undefined; 23338 this.groupsUsingDefaultStyles = groupsUsingDefaultStyles; 23339 this.zeroPosition = 0; 23340 this.update(group); 23341 if (this.usingDefaultStyle == true) { 23342 this.groupsUsingDefaultStyles[0] += 1; 23343 } 23344 this.itemsData = []; 23345 this.visible = group.visible === undefined ? true : group.visible; 23346} 23347 23348/** 23349 * this loads a reference to all items in this group into this group. 23350 * @param {array} items 23351 */ 23352GraphGroup.prototype.setItems = function (items) { 23353 if (items != null) { 23354 this.itemsData = items; 23355 if (this.options.sort == true) { 23356 util.insertSort(this.itemsData, function (a, b) { 23357 return a.x > b.x ? 1 : -1; 23358 }); 23359 } 23360 } else { 23361 this.itemsData = []; 23362 } 23363}; 23364 23365GraphGroup.prototype.getItems = function () { 23366 return this.itemsData; 23367}; 23368 23369/** 23370 * this is used for barcharts and shading, this way, we only have to calculate it once. 23371 * @param {number} pos 23372 */ 23373GraphGroup.prototype.setZeroPosition = function (pos) { 23374 this.zeroPosition = pos; 23375}; 23376 23377/** 23378 * set the options of the graph group over the default options. 23379 * @param {Object} options 23380 */ 23381GraphGroup.prototype.setOptions = function (options) { 23382 if (options !== undefined) { 23383 var fields = ['sampling', 'style', 'sort', 'yAxisOrientation', 'barChart', 'zIndex', 'excludeFromStacking', 'excludeFromLegend']; 23384 util.selectiveDeepExtend(fields, this.options, options); 23385 23386 // if the group's drawPoints is a function delegate the callback to the onRender property 23387 if (typeof options.drawPoints == 'function') { 23388 options.drawPoints = { 23389 onRender: options.drawPoints 23390 }; 23391 } 23392 23393 util.mergeOptions(this.options, options, 'interpolation'); 23394 util.mergeOptions(this.options, options, 'drawPoints'); 23395 util.mergeOptions(this.options, options, 'shaded'); 23396 23397 if (options.interpolation) { 23398 if ((0, _typeof3['default'])(options.interpolation) == 'object') { 23399 if (options.interpolation.parametrization) { 23400 if (options.interpolation.parametrization == 'uniform') { 23401 this.options.interpolation.alpha = 0; 23402 } else if (options.interpolation.parametrization == 'chordal') { 23403 this.options.interpolation.alpha = 1.0; 23404 } else { 23405 this.options.interpolation.parametrization = 'centripetal'; 23406 this.options.interpolation.alpha = 0.5; 23407 } 23408 } 23409 } 23410 } 23411 } 23412}; 23413 23414/** 23415 * this updates the current group class with the latest group dataset entree, used in _updateGroup in linegraph 23416 * @param {vis.Group} group 23417 */ 23418GraphGroup.prototype.update = function (group) { 23419 this.group = group; 23420 this.content = group.content || 'graph'; 23421 this.className = group.className || this.className || 'vis-graph-group' + this.groupsUsingDefaultStyles[0] % 10; 23422 this.visible = group.visible === undefined ? true : group.visible; 23423 this.style = group.style; 23424 this.setOptions(group.options); 23425}; 23426 23427/** 23428 * return the legend entree for this group. 23429 * 23430 * @param {number} iconWidth 23431 * @param {number} iconHeight 23432 * @param {{svg: (*|Element), svgElements: Object, options: Object, groups: Array.<Object>}} framework 23433 * @param {number} x 23434 * @param {number} y 23435 * @returns {{icon: (*|Element), label: (*|string), orientation: *}} 23436 */ 23437GraphGroup.prototype.getLegend = function (iconWidth, iconHeight, framework, x, y) { 23438 if (framework == undefined || framework == null) { 23439 var svg = document.createElementNS('http://www.w3.org/2000/svg', "svg"); 23440 framework = { svg: svg, svgElements: {}, options: this.options, groups: [this] }; 23441 } 23442 if (x == undefined || x == null) { 23443 x = 0; 23444 } 23445 if (y == undefined || y == null) { 23446 y = 0.5 * iconHeight; 23447 } 23448 switch (this.options.style) { 23449 case "line": 23450 Lines.drawIcon(this, x, y, iconWidth, iconHeight, framework); 23451 break; 23452 case "points": //explicit no break 23453 case "point": 23454 Points.drawIcon(this, x, y, iconWidth, iconHeight, framework); 23455 break; 23456 case "bar": 23457 Bars.drawIcon(this, x, y, iconWidth, iconHeight, framework); 23458 break; 23459 } 23460 return { icon: framework.svg, label: this.content, orientation: this.options.yAxisOr
23460ientation }; 23461}; 23462 23463GraphGroup.prototype.getYRange = function (groupData) { 23464 var yMin = groupData[0].y; 23465 var yMax = groupData[0].y; 23466 for (var j = 0; j < groupData.length; j++) { 23467 yMin = yMin > groupData[j].y ? groupData[j].y : yMin; 23468 yMax = yMax < groupData[j].y ? groupData[j].y : yMax; 23469 } 23470 return { min: yMin, max: yMax, yAxisOrientation: this.options.yAxisOrientation }; 23471}; 23472 23473module.exports = GraphGroup; 23474 23475/***/ }), 23476/* 110 */ 23477/***/ (function(module, exports, __webpack_require__) { 23478 23479"use strict"; 23480 23481 23482var DOMutil = __webpack_require__(14); 23483var Points = __webpack_require__(72); 23484 23485/** 23486 * 23487 * @param {vis.GraphGroup.id} groupId 23488 * @param {Object} options // TODO: Describe options 23489 * @constructor Bargraph 23490 */ 23491function Bargraph(groupId, options) {// eslint-disable-line no-unused-vars 23492} 23493 23494Bargraph.drawIcon = function (group, x, y, iconWidth, iconHeight, framework) { 23495 var fillHeight = iconHeight * 0.5; 23496 var outline = DOMutil.getSVGElement("rect", framework.svgElements, framework.svg); 23497 outline.setAttributeNS(null, "x", x); 23498 outline.setAttributeNS(null, "y", y - fillHeight); 23499 outline.setAttributeNS(null, "width", iconWidth); 23500 outline.setAttributeNS(null, "height", 2 * fillHeight); 23501 outline.setAttributeNS(null, "class", "vis-outline"); 23502 23503 var barWidth = Math.round(0.3 * iconWidth); 23504 var originalWidth = group.options.barChart.width; 23505 var scale = originalWidth / barWidth; 23506 var bar1Height = Math.round(0.4 * iconHeight); 23507 var bar2Height = Math.round(0.75 * iconHeight); 23508 23509 var offset = Math.round((iconWidth - 2 * barWidth) / 3); 23510 23511 DOMutil.drawBar(x + 0.5 * barWidth + offset, y + fillHeight - bar1Height - 1, barWidth, bar1Height, group.className + ' vis-bar', framework.svgElements, framework.svg, group.style); 23512 DOMutil.drawBar(x + 1.5 * barWidth + offset + 2, y + fillHeight - bar2Height - 1, barWidth, bar2Height, group.className + ' vis-bar', framework.svgElements, framework.svg, group.style); 23513 23514 if (group.options.drawPoints.enabled == true) { 23515 var groupTemplate = { 23516 style: group.options.drawPoints.style, 23517 styles: group.options.drawPoints.styles, 23518 size: group.options.drawPoints.size / scale, 23519 className: group.className 23520 }; 23521 DOMutil.drawPoint(x + 0.5 * barWidth + offset, y + fillHeight - bar1Height - 1, groupTemplate, framework.svgElements, framework.svg); 23522 DOMutil.drawPoint(x + 1.5 * barWidth + offset + 2, y + fillHeight - bar2Height - 1, groupTemplate, framework.svgElements, framework.svg); 23523 } 23524}; 23525 23526/** 23527 * draw a bar graph 23528 * 23529 * @param {Array.<vis.GraphGroup.id>} groupIds 23530 * @param {Object} processedGroupData 23531 * @param {{svg: Object, svgElements: Array.<Object>, options: Object, groups: Array.<vis.Group>}} framework 23532 */ 23533Bargraph.draw = function (groupIds, processedGroupData, framework) { 23534 var combinedData = []; 23535 var intersections = {}; 23536 var coreDistance; 23537 var key, drawData; 23538 var group; 23539 var i, j; 23540 var barPoints = 0; 23541
vendor: 26,712 bytes, lines 23542-24279
23542 // combine all barchart data 23543 for (i = 0; i < groupIds.length; i++) { 23544 group = framework.groups[groupIds[i]]; 23545 if (group.options.style === 'bar') { 23546 if (group.visible === true && (framework.options.groups.visibility[groupIds[i]] === undefined || framework.options.groups.visibility[groupIds[i]] === true)) { 23547 for (j = 0; j < processedGroupData[groupIds[i]].length; j++) { 23548 combinedData.push({ 23549 screen_x: processedGroupData[groupIds[i]][j].screen_x, 23550 screen_end: processedGroupData[groupIds[i]][j].screen_end, 23551 screen_y: processedGroupData[groupIds[i]][j].screen_y, 23552 x: processedGroupData[groupIds[i]][j].x, 23553 end: processedGroupData[groupIds[i]][j].end, 23554 y: processedGroupData[groupIds[i]][j].y, 23555 groupId: groupIds[i], 23556 label: processedGroupData[groupIds[i]][j].label 23557 }); 23558 barPoints += 1; 23559 } 23560 } 23561 } 23562 } 23563 23564 if (barPoints === 0) { 23565 return; 23566 } 23567 23568 // sort by time and by group 23569 combinedData.sort(function (a, b) { 23570 if (a.screen_x === b.screen_x) { 23571 return a.groupId < b.groupId ? -1 : 1; 23572 } else { 23573 return a.screen_x - b.screen_x; 23574 } 23575 }); 23576 23577 // get intersections 23578 Bargraph._getDataIntersections(intersections, combinedData); 23579 23580 // plot barchart 23581 for (i = 0; i < combinedData.length; i++) { 23582 group = framework.groups[combinedData[i].groupId]; 23583 var minWidth = group.options.barChart.minWidth != undefined ? group.options.barChart.minWidth : 0.1 * group.options.barChart.width; 23584 23585 key = combinedData[i].screen_x; 23586 var heightOffset = 0; 23587 if (intersections[key] === undefined) { 23588 if (i + 1 < combinedData.length) { 23589 coreDistance = Math.abs(combinedData[i + 1].screen_x - key); 23590 } 23591 drawData = Bargraph._getSafeDrawData(coreDistance, group, minWidth); 23592 } else { 23593 var nextKey = i + (intersections[key].amount - intersections[key].resolved); 23594 if (nextKey < combinedData.length) { 23595 coreDistance = Math.abs(combinedData[nextKey].screen_x - key); 23596 } 23597 drawData = Bargraph._getSafeDrawData(coreDistance, group, minWidth); 23598 intersections[key].resolved += 1; 23599 23600 if (group.options.stack === true && group.options.excludeFromStacking !== true) { 23601 if (combinedData[i].screen_y < group.zeroPosition) { 23602 heightOffset = intersections[key].accumulatedNegative; 23603 intersections[key].accumulatedNegative += group.zeroPosition - combinedData[i].screen_y; 23604 } else { 23605 heightOffset = intersections[key].accumulatedPositive; 23606 intersections[key].accumulatedPositive += group.zeroPosition - combinedData[i].screen_y; 23607 } 23608 } else if (group.options.barChart.sideBySide === true) { 23609 drawData.width = drawData.width / intersections[key].amount; 23610 drawData.offset += intersections[key].resolved * drawData.width - 0.5 * drawData.width * (intersections[key].amount + 1); 23611 } 23612 } 23613 23614 var dataWidth = drawData.width; 23615 var start = combinedData[i].screen_x; 23616 23617 // are we drawing explicit boxes? (we supplied an end value) 23618 if (combinedData[i].screen_end != undefined) { 23619 dataWidth = combinedData[i].screen_end - combinedData[i].screen_x; 23620 start += dataWidth * 0.5; 23621 } else { 23622 start += drawData.offset; 23623 } 23624 23625 DOMutil.drawBar(start, combinedData[i].screen_y - heightOffset, dataWidth, group.zeroPosition - combinedData[i].screen_y, group.className + ' vis-bar', framework.svgElements, framework.svg, group.style); 23626 23627 // draw points 23628 if (group.options.drawPoints.enabled === true) { 23629 var pointData = { 23630 screen_x: combinedData[i].screen_x, 23631 screen_y: combinedData[i].screen_y - heightOffset, 23632 x: combinedData[i].x, 23633 y: combinedData[i].y, 23634 groupId: combinedData[i].groupId, 23635 label: combinedData[i].label 23636 }; 23637 Points.draw([pointData], group, framework, drawData.offset); 23638 //DOMutil.drawPoint(combinedData[i].x + drawData.offset, combinedData[i].y, group, framework.svgElements, framework.svg); 23639 } 23640 } 23641}; 23642 23643/** 23644 * Fill the intersections object with counters of how many datapoints share the same x coordinates 23645 * @param {Object} intersections 23646 * @param {Array.<Object>} combinedData 23647 * @private 23648 */ 23649Bargraph._getDataIntersections = function (intersections, combinedData) { 23650 // get intersections 23651 var coreDistance; 23652 for (var i = 0; i < combinedData.length; i++) { 23653 if (i + 1 < combinedData.length) { 23654 coreDistance = Math.abs(combinedData[i + 1].screen_x - combinedData[i].screen_x); 23655 } 23656 if (i > 0) { 23657 coreDistance = Math.min(coreDistance, Math.abs(combinedData[i - 1].screen_x - combinedData[i].screen_x)); 23658 } 23659 if (coreDistance === 0) { 23660 if (intersections[combinedData[i].screen_x] === undefined) { 23661 intersections[combinedData[i].screen_x] = { 23662 amount: 0, 23663 resolved: 0, 23664 accumulatedPositive: 0, 23665 accumulatedNegative: 0 23666 }; 23667 } 23668 intersections[combinedData[i].screen_x].amount += 1; 23669 } 23670 } 23671}; 23672 23673/** 23674 * Get the width and offset for bargraphs based on the coredistance between datapoints 23675 * 23676 * @param {number} coreDistance 23677 * @param {vis.Group} group 23678 * @param {number} minWidth 23679 * @returns {{width: number, offset: number}} 23680 * @private 23681 */ 23682Bargraph._getSafeDrawData = function (coreDistance, group, minWidth) { 23683 var width, offset; 23684 if (coreDistance < group.options.barChart.width && coreDistance > 0) { 23685 width = coreDistance < minWidth ? minWidth : coreDistance; 23686 23687 offset = 0; // recalculate offset with the new width; 23688 if (group.options.barChart.align === 'left') { 23689 offset -= 0.5 * coreDistance; 23690 } else if (group.options.barChart.align === 'right') { 23691 offset += 0.5 * coreDistance; 23692 } 23693 } else { 23694 // default settings 23695 width = group.options.barChart.width; 23696 offset = 0; 23697 if (group.options.barChart.align === 'left') { 23698 offset -= 0.5 * group.options.barChart.width; 23699 } else if (group.options.barChart.align === 'right') { 23700 offset += 0.5 * group.options.barChart.width; 23701 } 23702 } 23703 23704 return { width: width, offset: offset }; 23705}; 23706 23707Bargraph.getStackedYRange = function (combinedData, groupRanges, groupIds, groupLabel, orientation) { 23708 if (combinedData.length > 0) { 23709 // sort by time and by group 23710 combinedData.sort(function (a, b) { 23711 if (a.screen_x === b.screen_x) { 23712 return a.groupId < b.groupId ? -1 : 1; 23713 } else { 23714 return a.screen_x - b.screen_x; 23715 } 23716 }); 23717 var intersections = {}; 23718 23719 Bargraph._getDataIntersections(intersections, combinedData); 23720 groupRanges[groupLabel] = Bargraph._getStackedYRange(intersections, combinedData); 23721 groupRanges[groupLabel].yAxisOrientation = orientation; 23722 groupIds.push(groupLabel); 23723 } 23724}; 23725 23726Bargraph._getStackedYRange = function (intersections, combinedData) { 23727 var key; 23728 var yMin = combinedData[0].screen_y; 23729 var yMax = combinedData[0].screen_y; 23730 for (var i = 0; i < combinedData.length; i++) { 23731 key = combinedData[i].screen_x; 23732 if (intersections[key] === undefined) { 23733 yMin = yMin > combinedData[i].screen_y ? combinedData[i].screen_y : yMin; 23734 yMax = yMax < combinedData[i].screen_y ? combinedData[i].screen_y : yMax; 23735 } else { 23736 if (combinedData[i].screen_y < 0) { 23737 intersections[key].accumulatedNegative += combinedData[i].screen_y; 23738 } else { 23739 intersections[key].accumulatedPositive += combinedData[i].screen_y; 23740 } 23741 } 23742 } 23743 for (var xpos in intersections) { 23744 if (intersections.hasOwnProperty(xpos)) { 23745 yMin = yMin > intersections[xpos].accumulatedNegative ? intersections[xpos].accumulatedNegative : yMin; 23746 yMin = yMin > intersections[xpos].accumulatedPositive ? intersections[xpos].accumulatedPositive : yMin; 23747 yMax = yMax < intersections[xpos].accumulatedNegative ? intersections[xpos].accumulatedNegative : yMax; 23748 yMax = yMax < intersections[xpos].accumulatedPositive ? intersections[xpos].accumulatedPositive : yMax; 23749 } 23750 } 23751 23752 return { min: yMin, max: yMax }; 23753}; 23754 23755module.exports = Bargraph; 23756 23757/***/ }), 23758/* 111 */ 23759/***/ (function(module, exports, __webpack_require__) { 23760 23761"use strict"; 23762 23763 23764var DOMutil = __webpack_require__(14); 23765 23766/** 23767 * 23768 * @param {vis.GraphGroup.id} groupId 23769 * @param {Object} options // TODO: Describe options 23770 * @constructor Line 23771 */ 23772function Line(groupId, options) {// eslint-disable-line no-unused-vars 23773} 23774 23775Line.calcPath = function (dataset, group) { 23776 if (dataset != null) { 23777 if (dataset.length > 0) { 23778 var d = []; 23779 23780 // construct path from dataset 23781 if (group.options.interpolation.enabled == true) { 23782 d = Line._catmullRom(dataset, group); 23783 } else { 23784 d = Line._linear(dataset); 23785 } 23786 return d; 23787 } 23788 } 23789}; 23790 23791Line.drawIcon = function (group, x, y, iconWidth, iconHeight, framework) { 23792 var fillHeight = iconHeight * 0.5; 23793 var path, fillPath; 23794 23795 var outline = DOMutil.getSVGElement("rect", framework.svgElements, framework.svg); 23796 outline.setAttributeNS(null, "x", x); 23797 outline.setAttributeNS(null, "y", y - fillHeight); 23798 outline.setAttributeNS(null, "width", iconWidth); 23799 outline.setAttributeNS(null, "height", 2 * fillHeight); 23800 outline.setAttributeNS(null, "class", "vis-outline"); 23801 23802 path = DOMutil.getSVGElement("path", framework.svgElements, framework.svg); 23803 path.setAttributeNS(null, "class", group.className); 23804 if (group.style !== undefined) { 23805 path.setAttributeNS(null, "style", group.style); 23806 } 23807 23808 path.setAttributeNS(null, "d", "M" + x + "," + y + " L" + (x + iconWidth) + "," + y + ""); 23809 if (group.options.shaded.enabled == true) { 23810 fillPath = DOMutil.getSVGElement("path", framework.svgElements, framework.svg); 23811 if (group.options.shaded.orientation == 'top') { 23812 fillPath.setAttributeNS(null, "d", "M" + x + ", " + (y - fillHeight) + "L" + x + "," + y + " L" + (x + iconWidth) + "," + y + " L" + (x + iconWidth) + "," + (y - fillHeight)); 23813 } else { 23814 fillPath.setAttributeNS(null, "d", "M" + x + "," + y + " " + "L" + x + "," + (y + fillHeight) + " " + "L" + (x + iconWidth) + "," + (y + fillHeight) + "L" + (x + iconWidth) + "," + y); 23815 } 23816 fillPath.setAttributeNS(null, "class", group.className + " vis-icon-fill"); 23817 if (group.options.shaded.style !== undefined && group.options.shaded.style !== "") { 23818 fillPath.setAttributeNS(null, "style", group.options.shaded.style); 23819 } 23820 } 23821 23822 if (group.options.drawPoints.enabled == true) { 23823 var groupTemplate = { 23824 style: group.options.drawPoints.style, 23825 styles: group.options.drawPoints.styles, 23826 size: group.options.drawPoints.size, 23827 className: group.className 23828 }; 23829 DOMutil.drawPoint(x + 0.5 * iconWidth, y, groupTemplate, framework.svgElements, framework.svg); 23830 } 23831}; 23832 23833Line.drawShading = function (pathArray, group, subPathArray, framework) { 23834 // append shading to the path 23835 if (group.options.shaded.enabled == true) { 23836 var svgHeight = Number(framework.svg.style.height.replace('px', '')); 23837 var fillPath = DOMutil.getSVGElement('path', framework.svgElements, framework.svg); 23838 var type = "L"; 23839 if (group.options.interpolation.enabled == true) { 23840 type = "C"; 23841 } 23842 var dFill; 23843 var zero = 0; 23844 if (group.options.shaded.orientation == 'top') { 23845 zero = 0; 23846 } else if (group.options.shaded.orientation == 'bottom') { 23847 zero = svgHeight; 23848 } else { 23849 zero = Math.min(Math.max(0, group.zeroPosition), svgHeight); 23850 } 23851 if (group.options.shaded.orientation == 'group' && subPathArray != null && subPathArray != undefined) { 23852 dFill = 'M' + pathArray[0][0] + "," + pathArray[0][1] + " " + this.serializePath(pathArray, type, false) + ' L' + subPathArray[subPathArray.length - 1][0] + "," + subPathArray[subPathArray.length - 1][1] + " " + this.serializePath(subPathArray, type, true) + subPathArray[0][0] + "," + subPathArray[0][1] + " Z"; 23853 } else { 23854 dFill = 'M' + pathArray[0][0] + "," + pathArray[0][1] + " " + this.serializePath(pathArray, type, false) + ' V' + zero + ' H' + pathArray[0][0] + " Z"; 23855 } 23856 23857 fillPath.setAttributeNS(null, 'class', group.className + ' vis-fill'); 23858 if (group.options.shaded.style !== undefined) { 23859 fillPath.setAttributeNS(null, 'style', group.options.shaded.style); 23860 } 23861 fillPath.setAttributeNS(null, 'd', dFill); 23862 } 23863}; 23864 23865/** 23866 * draw a line graph 23867 * 23868 * @param {Array.<Object>} pathArray 23869 * @param {vis.Group} group 23870 * @param {{svg: Object, svgElements: Array.<Object>, options: Object, groups: Array.<vis.Group>}} framework 23871 */ 23872Line.draw = function (pathArray, group, framework) { 23873 if (pathArray != null && pathArray != undefined) { 23874 var path = DOMutil.getSVGElement('path', framework.svgElements, framework.svg); 23875 path.setAttributeNS(null, "class", group.className); 23876 if (group.style !== undefined) { 23877 path.setAttributeNS(null, "style", group.style); 23878 } 23879 23880 var type = "L"; 23881 if (group.options.interpolation.enabled == true) { 23882 type = "C"; 23883 } 23884 // copy properties to path for drawing. 23885 path.setAttributeNS(null, 'd', 'M' + pathArray[0][0] + "," + pathArray[0][1] + " " + this.serializePath(pathArray, type, false)); 23886 } 23887}; 23888 23889Line.serializePath = function (pathArray, type, inverse) { 23890 if (pathArray.length < 2) { 23891 //Too little data to create a path. 23892 return ""; 23893 } 23894 var d = type; 23895 var i; 23896 if (inverse) { 23897 for (i = pathArray.length - 2; i > 0; i--) { 23898 d += pathArray[i][0] + "," + pathArray[i][1] + " "; 23899 } 23900 } else { 23901 for (i = 1; i < pathArray.length; i++) { 23902 d += pathArray[i][0] + "," + pathArray[i][1] + " "; 23903 } 23904 } 23905 return d; 23906}; 23907 23908/** 23909 * This uses an uniform parametrization of the interpolation algorithm: 23910 * 'On the Parameterization of Catmull-Rom Curves' by Cem Yuksel et al. 23911 * @param {Array.<Object>} data 23912 * @returns {string} 23913 * @private 23914 */ 23915Line._catmullRomUniform = function (data) { 23916 // catmull rom 23917 var p0, p1, p2, p3, bp1, bp2; 23918 var d = []; 23919 d.push([Math.round(data[0].screen_x), Math.round(data[0].screen_y)]); 23920 var normalization = 1 / 6; 23921 var length = data.length; 23922 for (var i = 0; i < length - 1; i++) { 23923 23924 p0 = i == 0 ? data[0] : data[i - 1]; 23925 p1 = data[i]; 23926 p2 = data[i + 1]; 23927 p3 = i + 2 < length ? data[i + 2] : p2; 23928 23929 // Catmull-Rom to Cubic Bezier conversion matrix 23930 // 0 1 0 0 23931 // -1/6 1 1/6 0 23932 // 0 1/6 1 -1/6 23933 // 0 0 1 0 23934 23935 // bp0 = { x: p1.x, y: p1.y }; 23936 bp1 = { 23937 screen_x: (-p0.screen_x + 6 * p1.screen_x + p2.screen_x) * normalization, 23938 screen_y: (-p0.screen_y + 6 * p1.screen_y + p2.screen_y) * normalization 23939 }; 23940 bp2 = { 23941 screen_x: (p1.screen_x + 6 * p2.screen_x - p3.screen_x) * normalization, 23942 screen_y: (p1.screen_y + 6 * p2.screen_y - p3.screen_y) * normalization 23943 }; 23944 // bp0 = { x: p2.x, y: p2.y }; 23945 23946 d.push([bp1.screen_x, bp1.screen_y]); 23947 d.push([bp2.screen_x, bp2.screen_y]); 23948 d.push([p2.screen_x, p2.screen_y]); 23949 } 23950 23951 return d; 23952}; 23953 23954/** 23955 * This uses either the chordal or centripetal parameterization of the catmull-rom algorithm. 23956 * By default, the centripetal parameterization is used because this gives the nicest results. 23957 * These parameterizations are relatively heavy because the distance between 4 points have to be calculated. 23958 * 23959 * One optimization can be used to reuse distances since this is a sliding window approach. 23960 * @param {Array.<Object>} data 23961 * @param {vis.GraphGroup} group 23962 * @returns {string} 23963 * @private 23964 */ 23965Line._catmullRom = function (data, group) { 23966 var alpha = group.options.interpolation.alpha; 23967 if (alpha == 0 || alpha === undefined) { 23968 return this._catmullRomUniform(data); 23969 } else { 23970 var p0, p1, p2, p3, bp1, bp2, d1, d2, d3, A, B, N, M; 23971 var d3powA, d2powA, d3pow2A, d2pow2A, d1pow2A, d1powA; 23972 var d = []; 23973 d.push([Math.round(data[0].screen_x), Math.round(data[0].screen_y)]); 23974 var length = data.length; 23975 for (var i = 0; i < length - 1; i++) { 23976 23977 p0 = i == 0 ? data[0] : data[i - 1]; 23978 p1 = data[i]; 23979 p2 = data[i + 1]; 23980 p3 = i + 2 < length ? data[i + 2] : p2; 23981 23982 d1 = Math.sqrt(Math.pow(p0.screen_x - p1.screen_x, 2) + Math.pow(p0.screen_y - p1.screen_y, 2)); 23983 d2 = Math.sqrt(Math.pow(p1.screen_x - p2.screen_x, 2) + Math.pow(p1.screen_y - p2.screen_y, 2)); 23984 d3 = Math.sqrt(Math.pow(p2.screen_x - p3.screen_x, 2) + Math.pow(p2.screen_y - p3.screen_y, 2)); 23985 23986 // Catmull-Rom to Cubic Bezier conversion matrix 23987 23988 // A = 2d1^2a + 3d1^a * d2^a + d3^2a 23989 // B = 2d3^2a + 3d3^a * d2^a + d2^2a 23990 23991 // [ 0 1 0 0 ] 23992 // [ -d2^2a /N A/N d1^2a /N 0 ] 23993 // [ 0 d3^2a /M B/M -d2^2a /M ] 23994 // [ 0 0 1 0 ] 23995 23996 d3powA = Math.pow(d3, alpha); 23997 d3pow2A = Math.pow(d3, 2 * alpha); 23998 d2powA = Math.pow(d2, alpha); 23999 d2pow2A = Math.pow(d2, 2 * alpha); 24000 d1powA = Math.pow(d1, alpha); 24001 d1pow2A = Math.pow(d1, 2 * alpha); 24002 24003 A = 2 * d1pow2A + 3 * d1powA * d2powA + d2pow2A; 24004 B = 2 * d3pow2A + 3 * d3powA * d2powA + d2pow2A; 24005 N = 3 * d1powA * (d1powA + d2powA); 24006 if (N > 0) { 24007 N = 1 / N; 24008 } 24009 M = 3 * d3powA * (d3powA + d2powA); 24010 if (M > 0) { 24011 M = 1 / M; 24012 } 24013 24014 bp1 = { 24015 screen_x: (-d2pow2A * p0.screen_x + A * p1.screen_x + d1pow2A * p2.screen_x) * N, 24016 screen_y: (-d2pow2A * p0.screen_y + A * p1.screen_y + d1pow2A * p2.screen_y) * N 24017 }; 24018 24019 bp2 = { 24020 screen_x: (d3pow2A * p1.screen_x + B * p2.screen_x - d2pow2A * p3.screen_x) * M, 24021 screen_y: (d3pow2A * p1.screen_y + B * p2.screen_y - d2pow2A * p3.screen_y) * M 24022 }; 24023 24024 if (bp1.screen_x == 0 && bp1.screen_y == 0) { 24025 bp1 = p1; 24026 } 24027 if (bp2.screen_x == 0 && bp2.screen_y == 0) { 24028 bp2 = p2; 24029 } 24030 d.push([bp1.screen_x, bp1.screen_y]); 24031 d.push([bp2.screen_x, bp2.screen_y]); 24032 d.push([p2.screen_x, p2.screen_y]); 24033 } 24034 24035 return d; 24036 } 24037}; 24038 24039/** 24040 * this generates the SVG path for a linear drawing between datapoints. 24041 * @param {Array.<Object>} data 24042 * @returns {string} 24043 * @private 24044 */ 24045Line._linear = function (data) { 24046 // linear 24047 var d = []; 24048 for (var i = 0; i < data.length; i++) { 24049 d.push([data[i].screen_x, data[i].screen_y]); 24050 } 24051 return d; 24052}; 24053 24054module.exports = Line; 24055 24056/***/ }), 24057/* 112 */ 24058/***/ (function(module, exports, __webpack_require__) { 24059 24060"use strict"; 24061 24062 24063var _keys = __webpack_require__(8); 24064 24065var _keys2 = _interopRequireDefault(_keys); 24066 24067function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 24068 24069var util = __webpack_require__(2); 24070var DOMutil = __webpack_require__(14); 24071var Component = __webpack_require__(16); 24072 24073/** 24074 * Legend for Graph2d 24075 * 24076 * @param {vis.Graph2d.body} body 24077 * @param {vis.Graph2d.options} options 24078 * @param {number} side 24079 * @param {vis.LineGraph.options} linegraphOptions 24080 * @constructor Legend 24081 * @extends Component 24082 */ 24083function Legend(body, options, side, linegraphOptions) { 24084 this.body = body; 24085 this.defaultOptions = { 24086 enabled: false, 24087 icons: true, 24088 iconSize: 20, 24089 iconSpacing: 6, 24090 left: { 24091 visible: true, 24092 position: 'top-left' // top/bottom - left,center,right 24093 }, 24094 right: { 24095 visible: true, 24096 position: 'top-right' // top/bottom - left,center,right 24097 } 24098 }; 24099 24100 this.side = side; 24101 this.options = util.extend({}, this.defaultOptions); 24102 this.linegraphOptions = linegraphOptions; 24103 24104 this.svgElements = {}; 24105 this.dom = {}; 24106 this.groups = {}; 24107 this.amountOfGroups = 0; 24108 this._create(); 24109 this.framework = { svg: this.svg, svgElements: this.svgElements, options: this.options, groups: this.groups }; 24110 24111 this.setOptions(options); 24112} 24113 24114Legend.prototype = new Component(); 24115 24116Legend.prototype.clear = function () { 24117 this.groups = {}; 24118 this.amountOfGroups = 0; 24119}; 24120 24121Legend.prototype.addGroup = function (label, graphOptions) { 24122 24123 // Include a group only if the group option 'excludeFromLegend: false' is not set. 24124 if (graphOptions.options.excludeFromLegend != true) { 24125 if (!this.groups.hasOwnProperty(label)) { 24126 this.groups[label] = graphOptions; 24127 } 24128 this.amountOfGroups += 1; 24129 } 24130}; 24131 24132Legend.prototype.updateGroup = function (label, graphOptions) { 24133 this.groups[label] = graphOptions; 24134}; 24135 24136Legend.prototype.removeGroup = function (label) { 24137 if (this.groups.hasOwnProperty(label)) { 24138 delete this.groups[label]; 24139 this.amountOfGroups -= 1; 24140 } 24141}; 24142 24143Legend.prototype._create = function () { 24144 this.dom.frame = document.createElement('div'); 24145 this.dom.frame.className = 'vis-legend'; 24146 this.dom.frame.style.position = "absolute"; 24147 this.dom.frame.style.top = "10px"; 24148 this.dom.frame.style.display = "block"; 24149 24150 this.dom.textArea = document.createElement('div'); 24151 this.dom.textArea.className = 'vis-legend-text'; 24152 this.dom.textArea.style.position = "relative"; 24153 this.dom.textArea.style.top = "0px"; 24154 24155 this.svg = document.createElementNS('http://www.w3.org/2000/svg', "svg"); 24156 this.svg.style.position = 'absolute'; 24157 this.svg.style.top = 0 + 'px'; 24158 this.svg.style.width = this.options.iconSize + 5 + 'px'; 24159 this.svg.style.height = '100%'; 24160 24161 this.dom.frame.appendChild(this.svg); 24162 this.dom.frame.appendChild(this.dom.textArea); 24163}; 24164 24165/** 24166 * Hide the component from the DOM 24167 */ 24168Legend.prototype.hide = function () { 24169 // remove the frame containing the items 24170 if (this.dom.frame.parentNode) { 24171 this.dom.frame.parentNode.removeChild(this.dom.frame); 24172 } 24173}; 24174 24175/** 24176 * Show the component in the DOM (when not already visible). 24177 */ 24178Legend.prototype.show = function () { 24179 // show frame containing the items 24180 if (!this.dom.frame.parentNode) { 24181 this.body.dom.center.appendChild(this.dom.frame); 24182 } 24183}; 24184 24185Legend.prototype.setOptions = function (options) { 24186 var fields = ['enabled', 'orientation', 'icons', 'left', 'right']; 24187 util.selectiveDeepExtend(fields, this.options, options); 24188}; 24189 24190Legend.prototype.redraw = function () { 24191 var activeGroups = 0; 24192 var groupArray = (0, _keys2['default'])(this.groups); 24193 groupArray.sort(function (a, b) { 24194 return a < b ? -1 : 1; 24195 }); 24196 24197 for (var i = 0; i < groupArray.length; i++) { 24198 var groupId = groupArray[i]; 24199 if (this.groups[groupId].visible == true && (this.linegraphOptions.visibility[groupId] === undefined || this.linegraphOptions.visibility[groupId] == true)) { 24200 activeGroups++; 24201 } 24202 } 24203 24204 if (this.options[this.side].visible == false || this.amountOfGroups == 0 || this.options.enabled == false || activeGroups == 0) { 24205 this.hide(); 24206 } else { 24207 this.show(); 24208 if (this.options[this.side].position == 'top-left' || this.options[this.side].position == 'bottom-left') { 24209 this.dom.frame.style.left = '4px'; 24210 this.dom.frame.style.textAlign = "left"; 24211 this.dom.textArea.style.textAlign = "left"; 24212 this.dom.textArea.style.left = this.options.iconSize + 15 + 'px'; 24213 this.dom.textArea.style.right = ''; 24214 this.svg.style.left = 0 + 'px'; 24215 this.svg.style.right = ''; 24216 } else { 24217 this.dom.frame.style.right = '4px'; 24218 this.dom.frame.style.textAlign = "right"; 24219 this.dom.textArea.style.textAlign = "right"; 24220 this.dom.textArea.style.right = this.options.iconSize + 15 + 'px'; 24221 this.dom.textArea.style.left = ''; 24222 this.svg.style.right = 0 + 'px'; 24223 this.svg.style.left = ''; 24224 } 24225 24226 if (this.options[this.side].position == 'top-left' || this.options[this.side].position == 'top-right') { 24227 this.dom.frame.style.top = 4 - Number(this.body.dom.center.style.top.replace("px", "")) + 'px'; 24228 this.dom.frame.style.bottom = ''; 24229 } else { 24230 var scrollableHeight = this.body.domProps.center.height - this.body.domProps.centerContainer.height; 24231 this.dom.frame.style.bottom = 4 + scrollableHeight + Number(this.body.dom.center.style.top.replace("px", "")) + 'px'; 24232 this.dom.frame.style.top = ''; 24233 } 24234 24235 if (this.options.icons == false) { 24236 this.dom.frame.style.width = this.dom.textArea.offsetWidth + 10 + 'px'; 24237 this.dom.textArea.style.right = ''; 24238 this.dom.textArea.style.left = ''; 24239 this.svg.style.width = '0px'; 24240 } else { 24241 this.dom.frame.style.width = this.options.iconSize + 15 + this.dom.textArea.offsetWidth + 10 + 'px'; 24242 this.drawLegendIcons(); 24243 } 24244 24245 var content = ''; 24246 for (i = 0; i < groupArray.length; i++) { 24247 groupId = groupArray[i]; 24248 if (this.groups[groupId].visible == true && (this.linegraphOptions.visibility[groupId] === undefined || this.linegraphOptions.visibility[groupId] == true)) { 24249 content += this.groups[groupId].content + '<br />'; 24250 } 24251 } 24252 this.dom.textArea.innerHTML = content; 24253 this.dom.textArea.style.lineHeight = 0.75 * this.options.iconSize + this.options.iconSpacing + 'px'; 24254 } 24255}; 24256 24257Legend.prototype.drawLegendIcons = function () { 24258 if (this.dom.frame.parentNode) { 24259 var groupArray = (0, _keys2['default'])(this.groups); 24260 groupArray.sort(function (a, b) { 24261 return a < b ? -1 : 1; 24262 }); 24263 24264 // this resets the elements so the order is maintained 24265 DOMutil.resetElements(this.svgElements); 24266 24267 var padding = window.getComputedStyle(this.dom.frame).paddingTop; 24268 var iconOffset = Number(padding.replace('px', '')); 24269 var x = iconOffset; 24270 var iconWidth = this.options.iconSize; 24271 var iconHeight = 0.75 * this.options.iconSize; 24272 var y = iconOffset + 0.5 * iconHeight + 3; 24273 24274 this.svg.style.width = iconWidth + 5 + iconOffset + 'px'; 24275 24276 for (var i = 0; i < groupArray.length; i++) { 24277 var groupId = groupArray[i]; 24278 if (this.groups[groupId].visible == true && (this.linegraphOptions.visibility[groupId] === undefined || this.linegraphOptions.visibility[groupId] == true)) { 24279 this.groups[groupId].getLegend(iconWidth, iconHeight, this.framework, x, y);
24280 y += iconHeight + this.options.iconSpacing; 24281 } 24282 } 24283 } 24284}; 24285 24286module.exports = Legend; 24287 24288/***/ }), 24289/* 113 */ 24290/***/ (function(module, exports, __webpack_require__) { 24291 24292"use strict"; 24293 24294 24295Object.defineProperty(exports, "__esModule", { 24296 value: true 24297}); 24298/** 24299 * This object contains all possible options. It will check if the types are correct, if required if the option is one 24300 * of the allowed values. 24301 * 24302 * __any__ means that the name of the property does not matter. 24303 * __type__ is a required field for all objects and contains the allowed types of all objects 24304 */ 24305var string = 'string'; 24306var bool = 'boolean'; 24307var number = 'number'; 24308var array = 'array'; 24309var date = 'date'; 24310var object = 'object'; // should only be in a __type__ property 24311var dom = 'dom'; 24312var moment = 'moment'; 24313var any = 'any'; 24314 24315var allOptions = { 24316 configure: { 24317 enabled: { 'boolean': bool }, 24318 filter: { 'boolean': bool, 'function': 'function' }, 24319 container: { dom: dom }, 24320 __type__: { object: object, 'boolean': bool, 'function': 'function' } 24321 }, 24322 24323 //globals : 24324 yAxisOrientation: { string: ['left', 'right'] }, 24325 defaultGroup: { string: string }, 24326 sort: { 'boolean': bool }, 24327 sampling: { 'boolean': bool }, 24328 stack: { 'boolean': bool }, 24329 graphHeight: { string: string, number: number }, 24330 shaded: { 24331 enabled: { 'boolean': bool }, 24332 orientation: { string: ['bottom', 'top', 'zero', 'group'] }, // top, bottom, zero, group 24333 groupId: { object: object }, 24334 __type__: { 'boolean': bool, object: object } 24335 }, 24336 style: { string: ['line', 'bar', 'points'] }, // line, bar 24337 barChart: { 24338 width: { number: number }, 24339 minWidth: { number: number }, 24340 sideBySide: { 'boolean': bool }, 24341 align: { string: ['left', 'center', 'right'] }, 24342 __type__: { object: object } 24343 }, 24344 interpolation: { 24345 enabled: { 'boolean': bool }, 24346 parametrization: { string: ['centripetal', 'chordal', 'uniform'] }, // uniform (alpha = 0.0), chordal (alpha = 1.0), centripetal (alpha = 0.5) 24347 alpha: { number: number }, 24348 __type__: { object: object, 'boolean': bool } 24349 }, 24350 drawPoints: { 24351 enabled: { 'boolean': bool }, 24352 onRender: { 'function': 'function' }, 24353 size: { number: number }, 24354 style: { string: ['square', 'circle'] }, // square, circle 24355 __type__: { object: object, 'boolean': bool, 'function': 'function' } 24356 }, 24357 dataAxis: { 24358 showMinorLabels: { 'boolean': bool }, 24359 showMajorLabels: { 'boolean': bool }, 24360 icons: { 'boolean': bool }, 24361 width: { string: string, number: number }, 24362 visible: { 'boolean': bool }, 24363 alignZeros: { 'boolean': bool }, 24364 left: { 24365 range: { min: { number: number, 'undefined': 'undefined' }, max: { number: number, 'undefined': 'undefined' }, __type__: { object: object } }, 24366 format: { 'function': 'function' }, 24367 title: { text: { string: string, number: number, 'undefined': 'undefined' }, style: { string: string, 'undefined': 'undefined' }, __type__: { object: object } }, 24368 __type__: { object: object } 24369 }, 24370 right: { 24371 range: { min: { number: number, 'undefined': 'undefined' }, max: { number: number, 'undefined': 'undefined' }, __type__: { object: object } }, 24372 format: { 'function': 'function' }, 24373 title: { text: { string: string, number: number, 'undefined': 'undefined' }, style: { string: string, 'undefined': 'undefined' }, __type__: { object: object } }, 24374 __type__: { object: object } 24375 }, 24376 __type__: { object: object } 24377 }, 24378 legend: { 24379 enabled: { 'boolean': bool }, 24380 icons: { 'boolean': bool }, 24381 left: { 24382 visible: { 'boolean': bool }, 24383 position: { string: ['top-right', 'bottom-right', 'top-left', 'bottom-left'] }, 24384 __type__: { object: object } 24385 }, 24386 right: { 24387 visible: { 'boolean': bool }, 24388 position: { string: ['top-right', 'bottom-right', 'top-left', 'bottom-left'] }, 24389 __type__: { object: object } 24390 }, 24391 __type__: { object: object, 'boolean': bool } 24392 }, 24393 groups: { 24394 visibility: { any: any }, 24395 __type__: { object: object } 24396 }, 24397 24398 autoResize: { 'boolean': bool }, 24399 throttleRedraw: { number: number }, // TODO: DEPRICATED see https://github.com/almende/vis/issues/2511 24400 clickToUse: { 'boolean': bool }, 24401 end: { number: number, date: date, string: string, moment: moment }, 24402 format: { 24403 minorLabels: { 24404 millisecond: { string: string, 'undefined': 'undefined' }, 24405 second: { string: string, 'undefined': 'undefined' }, 24406 minute: { string: string, 'undefined': 'undefined' }, 24407 hour: { string: string, 'undefined': 'undefined' },
24408 weekday: { string: string, 'undefined': 'undefined' }, 24409 day: { string: string, 'undefined': 'undefined' }, 24410 month: { string: string, 'undefined': 'undefined' }, 24411 year: { string: string, 'undefined': 'undefined' }, 24412 __type__: { object: object } 24413 }, 24414 majorLabels: { 24415 millisecond: { string: string, 'undefined': 'undefined' }, 24416 second: { string: string, 'undefined': 'undefined' }, 24417 minute: { string: string, 'undefined': 'undefined' }, 24418 hour: { string: string, 'undefined': 'undefined' }, 24419 weekday: { string: string, 'undefined': 'undefined' }, 24420 day: { string: string, 'undefined': 'undefined' }, 24421 month: { string: string, 'undefined': 'undefined' }, 24422 year: { string: string, 'undefined': 'undefined' }, 24423 __type__: { object: object } 24424 }, 24425 __type__: { object: object } 24426 }, 24427 moment: { 'function': 'function' }, 24428 height: { string: string, number: number }, 24429 hiddenDates: { 24430 start: { date: date, number: number, string: string, moment: moment }, 24431 end: { date: date, number: number, string: string, moment: moment }, 24432 repeat: { string: string }, 24433 __type__: { object: object, array: array } 24434 }, 24435 locale: { string: string }, 24436 locales: { 24437 __any__: { any: any }, 24438 __type__: { object: object } 24439 }, 24440 max: { date: date, number: number, string: string, moment: moment }, 24441 maxHeight: { number: number, string: string }, 24442 maxMinorChars: { number: number }, 24443 min: { date: date, number: number, string: string, moment: moment }, 24444 minHeight: { number: number, string: string }, 24445 moveable: { 'boolean': bool }, 24446 multiselect: { 'boolean': bool }, 24447 orientation: { string: string }, 24448 showCurrentTime: { 'boolean': bool }, 24449 showMajorLabels: { 'boolean': bool }, 24450 showMinorLabels: { 'boolean': bool }, 24451 start: { date: date, number: number, string: string, moment: moment }, 24452 timeAxis: { 24453 scale: { string: string, 'undefined': 'undefined' }, 24454 step: { number: number, 'undefined': 'undefined' }, 24455 __type__: { object: object } 24456 }, 24457 width: { string: string, number: number }, 24458 zoomable: { 'boolean': bool }, 24459 zoomKey: { string: ['ctrlKey', 'altKey', 'metaKey', ''] }, 24460 zoomMax: { number: number }, 24461 zoomMin: { number: number }, 24462 zIndex: { number: number }, 24463 __type__: { object: object } 24464}; 24465
vendor: 4,605 bytes, lines 24466-24619
24466var configureOptions = { 24467 global: { 24468 //yAxisOrientation: ['left','right'], // TDOO: enable as soon as Grahp2d doesn't crash when changing this on the fly 24469 sort: true, 24470 sampling: true, 24471 stack: false, 24472 shaded: { 24473 enabled: false, 24474 orientation: ['zero', 'top', 'bottom', 'group'] // zero, top, bottom 24475 }, 24476 style: ['line', 'bar', 'points'], // line, bar 24477 barChart: { 24478 width: [50, 5, 100, 5], 24479 minWidth: [50, 5, 100, 5], 24480 sideBySide: false, 24481 align: ['left', 'center', 'right'] // left, center, right 24482 }, 24483 interpolation: { 24484 enabled: true, 24485 parametrization: ['centripetal', 'chordal', 'uniform'] // uniform (alpha = 0.0), chordal (alpha = 1.0), centripetal (alpha = 0.5) 24486 }, 24487 drawPoints: { 24488 enabled: true, 24489 size: [6, 2, 30, 1], 24490 style: ['square', 'circle'] // square, circle 24491 }, 24492 dataAxis: { 24493 showMinorLabels: true, 24494 showMajorLabels: true, 24495 icons: false, 24496 width: [40, 0, 200, 1], 24497 visible: true, 24498 alignZeros: true, 24499 left: { 24500 //range: {min:'undefined': 'undefined'ined,max:'undefined': 'undefined'ined}, 24501 //format: function (value) {return value;}, 24502 title: { text: '', style: '' } 24503 }, 24504 right: { 24505 //range: {min:'undefined': 'undefined'ined,max:'undefined': 'undefined'ined}, 24506 //format: function (value) {return value;}, 24507 title: { text: '', style: '' } 24508 } 24509 }, 24510 legend: { 24511 enabled: false, 24512 icons: true, 24513 left: { 24514 visible: true, 24515 position: ['top-right', 'bottom-right', 'top-left', 'bottom-left'] // top/bottom - left,right 24516 }, 24517 right: { 24518 visible: true, 24519 position: ['top-right', 'bottom-right', 'top-left', 'bottom-left'] // top/bottom - left,right 24520 } 24521 }, 24522 24523 autoResize: true, 24524 clickToUse: false, 24525 end: '', 24526 format: { 24527 minorLabels: { 24528 millisecond: 'SSS', 24529 second: 's', 24530 minute: 'HH:mm', 24531 hour: 'HH:mm', 24532 weekday: 'ddd D', 24533 day: 'D', 24534 month: 'MMM', 24535 year: 'YYYY' 24536 }, 24537 majorLabels: { 24538 millisecond: 'HH:mm:ss', 24539 second: 'D MMMM HH:mm', 24540 minute: 'ddd D MMMM', 24541 hour: 'ddd D MMMM', 24542 weekday: 'MMMM YYYY', 24543 day: 'MMMM YYYY', 24544 month: 'YYYY', 24545 year: '' 24546 } 24547 }, 24548 24549 height: '', 24550 locale: '', 24551 max: '', 24552 maxHeight: '', 24553 maxMinorChars: [7, 0, 20, 1], 24554 min: '', 24555 minHeight: '', 24556 moveable: true, 24557 orientation: ['both', 'bottom', 'top'], 24558 showCurrentTime: false, 24559 showMajorLabels: true, 24560 showMinorLabels: true, 24561 start: '', 24562 width: '100%', 24563 zoomable: true, 24564 zoomKey: ['ctrlKey', 'altKey', 'metaKey', ''], 24565 zoomMax: [315360000000000, 10, 315360000000000, 1], 24566 zoomMin: [10, 10, 315360000000000, 1], 24567 zIndex: 0 24568 } 24569}; 24570 24571exports.allOptions = allOptions; 24572exports.configureOptions = configureOptions; 24573 24574/***/ }), 24575/* 114 */ 24576/***/ (function(module, exports, __webpack_require__) { 24577 24578"use strict"; 24579 24580 24581var _create = __webpack_require__(29); 24582 24583var _create2 = _interopRequireDefault(_create); 24584 24585function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 24586 24587/** 24588 * Parse a text source containing data in DOT language into a JSON object. 24589 * The object contains two lists: one with nodes and one with edges. 24590 * 24591 * DOT language reference: http://www.graphviz.org/doc/info/lang.html 24592 * 24593 * DOT language attributes: http://graphviz.org/content/attrs 24594 * 24595 * @param {string} data Text containing a graph in DOT-notation 24596 * @return {Object} graph An object containing two parameters: 24597 * {Object[]} nodes 24598 * {Object[]} edges 24599 * 24600 * ------------------------------------------- 24601 * TODO 24602 * ==== 24603 * 24604 * For label handling, this is an incomplete implementation. From docs (quote #3015): 24605 * 24606 * > the escape sequences "\n", "\l" and "\r" divide the label into lines, centered, 24607 * > left-justified, and right-justified, respectively. 24608 * 24609 * Source: http://www.graphviz.org/content/attrs#kescString 24610 * 24611 * > As another aid for readability, dot allows double-quoted strings to span multiple physical 24612 * > lines using the standard C convention of a backslash immediately preceding a newline 24613 * > character 24614 * > In addition, double-quoted strings can be concatenated using a '+' operator. 24615 * > As HTML strings can contain newline characters, which are used solely for formatting, 24616 * > the language does not allow escaped newlines or concatenation operators to be used 24617 * > within them. 24618 * 24619 * - Currently, only '\\n' is handled
24620 * - Note that text explicitly says 'labels'; the dot parser currently handles escape 24621 * sequences in **all** strings. 24622 */ 24623function parseDOT(data) { 24624 dot = data; 24625 return parseGraph(); 24626} 24627 24628// mapping of attributes from DOT (the keys) to vis.js (the values) 24629var NODE_ATTR_MAPPING = { 24630 'fontsize': 'font.size', 24631 'fontcolor': 'font.color', 24632 'labelfontcolor': 'font.color', 24633 'fontname': 'font.face', 24634 'color': ['color.border', 'color.background'], 24635 'fillcolor': 'color.background', 24636 'tooltip': 'title', 24637 'labeltooltip': 'title' 24638}; 24639var EDGE_ATTR_MAPPING = (0, _create2['default'])(NODE_ATTR_MAPPING); 24640EDGE_ATTR_MAPPING.color = 'color.color'; 24641EDGE_ATTR_MAPPING.style = 'dashes'; 24642 24643// token types enumeration 24644var TOKENTYPE = { 24645 NULL: 0, 24646 DELIMITER: 1, 24647 IDENTIFIER: 2, 24648 UNKNOWN: 3 24649}; 24650 24651// map with all delimiters 24652var DELIMITERS = { 24653 '{': true, 24654 '}': true, 24655 '[': true, 24656 ']': true, 24657 ';': true, 24658 '=': true, 24659 ',': true, 24660 24661 '->': true, 24662 '--': true 24663}; 24664 24665var dot = ''; // current dot file 24666var index = 0; // current index in dot file 24667var c = ''; // current token character in expr 24668var token = ''; // current token 24669var tokenType = TOKENTYPE.NULL; // type of the token 24670 24671/** 24672 * Get the first character from the dot file. 24673 * The character is stored into the char c. If the end of the dot file is 24674 * reached, the function puts an empty string in c. 24675 */ 24676function first() { 24677 index = 0; 24678 c = dot.charAt(0); 24679} 24680 24681/** 24682 * Get the next character from the dot file. 24683 * The character is stored into the char c. If the end of the dot file is 24684 * reached, the function puts an empty string in c. 24685 */ 24686function next() { 24687 index++; 24688 c = dot.charAt(index); 24689} 24690 24691/** 24692 * Preview the next character from the dot file. 24693 * @return {string} cNext 24694 */ 24695function nextPreview() { 24696 return dot.charAt(index + 1); 24697} 24698 24699var regexAlphaNumeric = /[a-zA-Z_0-9.:#]/; 24700/** 24701 * Test whether given character is alphabetic or numeric 24702 * @param {string} c 24703 * @return {Boolean} isAlphaNumeric 24704 */ 24705function isAlphaNumeric(c) { 24706 return regexAlphaNumeric.test(c); 24707} 24708 24709/** 24710 * Merge all options of object b into object b 24711 * @param {Object} a 24712 * @param {Object} b 24713 * @return {Object} a 24714 */ 24715function merge(a, b) { 24716 if (!a) { 24717 a = {}; 24718 } 24719 24720 if (b) { 24721 for (var name in b) { 24722 if (b.hasOwnProperty(name)) { 24723 a[name] = b[name]; 24724 } 24725 } 24726 } 24727 return a; 24728} 24729 24730/** 24731 * Set a value in an object, where the provided parameter name can be a 24732 * path with nested parameters. For example: 24733 * 24734 * var obj = {a: 2}; 24735 * setValue(obj, 'b.c', 3); // obj = {a: 2, b: {c: 3}} 24736 * 24737 * @param {Object} obj 24738 * @param {string} path A parameter name or dot-separated parameter path, 24739 * like "color.highlight.border". 24740 * @param {*} value 24741 */ 24742function setValue(obj, path, value) { 24743 var keys = path.split('.'); 24744 var o = obj; 24745 while (keys.length) { 24746 var key = keys.shift(); 24747 if (keys.length) { 24748 // this isn't the end point 24749 if (!o[key]) { 24750 o[key] = {}; 24751 } 24752 o = o[key]; 24753 } else { 24754 // this is the end point 24755 o[key] = value; 24756 } 24757 } 24758} 24759 24760/** 24761 * Add a node to a graph object. If there is already a node with 24762 * the same id, their attributes will be merged. 24763 * @param {Object} graph 24764 * @param {Object} node 24765 */ 24766function addNode(graph, node) { 24767 var i, len; 24768 var current = null; 24769 24770 // find root graph (in case of subgraph) 24771 var graphs = [graph]; // list with all graphs from current graph to root graph 24772 var root = graph; 24773 while (root.parent) { 24774 graphs.push(root.parent); 24775 root = root.parent; 24776 } 24777 24778 // find existing node (at root level) by its id 24779 if (root.nodes) { 24780 for (i = 0, len = root.nodes.length; i < len; i++) { 24781 if (node.id === root.nodes[i].id) { 24782 current = root.nodes[i]; 24783 break; 24784 } 24785 } 24786 } 24787 24788 if (!current) { 24789 // this is a new node 24790 current = { 24791 id: node.id 24792 }; 24793 if (graph.node) { 24794 // clone default attributes 24795 current.attr = merge(current.attr, graph.node); 24796 } 24797 } 24798 24799 // add node to this (sub)graph and all its parent graphs 24800 for (i = graphs.length - 1; i >= 0; i--) { 24801 var g = graphs[i]; 24802 24803 if (!g.nodes) { 24804 g.nodes = []; 24805 } 24806 if (g.nodes.indexOf(current) === -1) { 24807 g.nodes.push(current); 24808 } 24809 } 24810 24811 // merge attributes 24812 if (node.attr) { 24813 current.attr = merge(current.attr, node.attr); 24814 } 24815} 24816 24817/** 24818 * Add an edge to a graph object 24819 * @param {Object} graph 24820 * @param {Object} edge 24821 */ 24822function addEdge(graph, edge) { 24823 if (!graph.edges) { 24824 graph.edges = []; 24825 } 24826 graph.edges.push(edge); 24827 if (graph.edge) { 24828 var attr = merge({}, graph.edge); // clone default attributes 24829 edge.attr = merge(attr, edge.attr); // merge attributes 24830 } 24831} 24832 24833/** 24834 * Create an edge to a graph object 24835 * @param {Object} graph 24836 * @param {string | number | Object} from 24837 * @param {string | number | Object} to 24838 * @param {string} type 24839 * @param {Object | null} attr 24840 * @return {Object} edge 24841 */ 24842function createEdge(graph, from, to, type, attr) { 24843 var edge = { 24844 from: from, 24845 to: to, 24846 type: type 24847 }; 24848 24849 if (graph.edge) { 24850 edge.attr = merge({}, graph.edge); // clone default attributes 24851 } 24852 edge.attr = merge(edge.attr || {}, attr); // merge attributes 24853 24854 return edge; 24855} 24856 24857/** 24858 * Get next token in the current dot file. 24859 * The token and token type are available as token and tokenType 24860 */ 24861function getToken() { 24862 tokenType = TOKENTYPE.NULL; 24863 token = ''; 24864
24865 // skip over whitespaces 24866 while (c === ' ' || c === '\t' || c === '\n' || c === '\r') { 24867 // space, tab, enter 24868 next(); 24869 } 24870 24871 do { 24872 var isComment = false; 24873 24874 // skip comment 24875 if (c === '#') { 24876 // find the previous non-space character 24877 var i = index - 1; 24878 while (dot.charAt(i) === ' ' || dot.charAt(i) === '\t') { 24879 i--; 24880 } 24881 if (dot.charAt(i) === '\n' || dot.charAt(i) === '') { 24882 // the # is at the start of a line, this is indeed a line comment 24883 while (c != '' && c != '\n') { 24884 next(); 24885 } 24886 isComment = true; 24887 } 24888 } 24889 if (c === '/' && nextPreview() === '/') { 24890 // skip line comment 24891 while (c != '' && c != '\n') { 24892 next(); 24893 } 24894 isComment = true; 24895 } 24896 if (c === '/' && nextPreview() === '*') { 24897 // skip block comment 24898 while (c != '') { 24899 if (c === '*' && nextPreview() === '/') { 24900 // end of block comment found. skip these last two characters 24901 next(); 24902 next(); 24903 break; 24904 } else { 24905 next(); 24906 } 24907 } 24908 isComment = true; 24909 } 24910 24911 // skip over whitespaces 24912 while (c === ' ' || c === '\t' || c === '\n' || c === '\r') { 24913 // space, tab, enter 24914 next(); 24915 } 24916 } while (isComment); 24917 24918 // check for end of dot file 24919 if (c === '') { 24920 // token is still empty 24921 tokenType = TOKENTYPE.DELIMITER; 24922 return; 24923 } 24924 24925 // check for delimiters consisting of 2 characters 24926 var c2 = c + nextPreview(); 24927 if (DELIMITERS[c2]) { 24928 tokenType = TOKENTYPE.DELIMITER; 24929 token = c2; 24930 next(); 24931 next(); 24932 return; 24933 } 24934 24935 // check for delimiters consisting of 1 character 24936 if (DELIMITERS[c]) { 24937 tokenType = TOKENTYPE.DELIMITER; 24938 token = c; 24939 next(); 24940 return; 24941 } 24942 24943 // check for an identifier (number or string) 24944 // TODO: more precise parsing of numbers/strings (and the port separator ':') 24945 if (isAlphaNumeric(c) || c === '-') { 24946 token += c; 24947 next(); 24948 24949 while (isAlphaNumeric(c)) { 24950 token += c; 24951 next(); 24952 } 24953 if (token === 'false') { 24954 token = false; // convert to boolean 24955 } else if (token === 'true') { 24956 token = true; // convert to boolean 24957 } else if (!isNaN(Number(token))) { 24958 token = Number(token); // convert to number 24959 } 24960 tokenType = TOKENTYPE.IDENTIFIER; 24961 return; 24962 } 24963 24964 // check for a string enclosed by double quotes 24965 if (c === '"') { 24966 next(); 24967 while (c != '' && (c != '"' || c === '"' && nextPreview() === '"')) { 24968 if (c === '"') { 24969 // skip the escape character 24970 token += c; 24971 next(); 24972 } else if (c === '\\' && nextPreview() === 'n') { 24973 // Honor a newline escape sequence 24974 token += '\n'; 24975 next(); 24976 } else { 24977 token += c; 24978 } 24979 next(); 24980 } 24981 if (c != '"') { 24982 throw newSyntaxError('End of string " expected'); 24983 } 24984 next(); 24985 tokenType = TOKENTYPE.IDENTIFIER; 24986 return; 24987 } 24988 24989 // something unknown is found, wrong characters, a syntax error 24990 tokenType = TOKENTYPE.UNKNOWN; 24991 while (c != '') { 24992 token += c; 24993 next(); 24994 } 24995 throw new SyntaxError('Syntax error in part "' + chop(token, 30) + '"'); 24996} 24997 24998/** 24999 * Parse a graph. 25000 * @returns {Object} graph 25001 */ 25002function parseGraph() { 25003 var graph = {}; 25004 25005 first(); 25006 getToken(); 25007 25008 // optional strict keyword 25009 if (token === 'strict') { 25010 graph.strict = true; 25011 getToken(); 25012 } 25013 25014 // graph or digraph keyword 25015 if (token === 'graph' || token === 'digraph') { 25016 graph.type = token; 25017 getToken(); 25018 } 25019 25020 // optional graph id 25021 if (tokenType === TOKENTYPE.IDENTIFIER) { 25022 graph.id = token; 25023 getToken(); 25024 } 25025 25026 // open angle bracket 25027 if (token != '{') { 25028 throw newSyntaxError('Angle bracket { expected'); 25029 } 25030 getToken(); 25031 25032 // statements 25033 parseStatements(graph); 25034 25035 // close angle bracket 25036 if (token != '}') { 25037 throw newSyntaxError('Angle bracket } expected'); 25038 } 25039 getToken(); 25040 25041 // end of file 25042 if (token !== '') { 25043 throw newSyntaxError('End of file expected'); 25044 } 25045 getToken(); 25046 25047 // remove temporary default options 25048 delete graph.node; 25049 delete graph.edge; 25050 delete graph.graph; 25051 25052 return graph; 25053} 25054 25055/** 25056 * Parse a list with statements. 25057 * @param {Object} graph 25058 */ 25059function parseStatements(graph) { 25060 while (token !== '' && token != '}') {
25061 parseStatement(graph); 25062 if (token === ';') { 25063 getToken(); 25064 } 25065 } 25066} 25067 25068/** 25069 * Parse a single statement. Can be a an attribute statement, node 25070 * statement, a series of node statements and edge statements, or a 25071 * parameter. 25072 * @param {Object} graph 25073 */ 25074function parseStatement(graph) { 25075 // parse subgraph 25076 var subgraph = parseSubgraph(graph); 25077 if (subgraph) { 25078 // edge statements 25079 parseEdge(graph, subgraph); 25080 25081 return; 25082 } 25083 25084 // parse an attribute statement 25085 var attr = parseAttributeStatement(graph); 25086 if (attr) { 25087 return; 25088 } 25089 25090 // parse node 25091 if (tokenType != TOKENTYPE.IDENTIFIER) { 25092 throw newSyntaxError('Identifier expected'); 25093 } 25094 var id = token; // id can be a string or a number 25095 getToken(); 25096 25097 if (token === '=') { 25098 // id statement 25099 getToken(); 25100 if (tokenType != TOKENTYPE.IDENTIFIER) { 25101 throw newSyntaxError('Identifier expected'); 25102 } 25103 graph[id] = token; 25104 getToken(); 25105 // TODO: implement comma separated list with "a_list: ID=ID [','] [a_list] " 25106 } else { 25107 parseNodeStatement(graph, id); 25108 } 25109} 25110 25111/** 25112 * Parse a subgraph 25113 * @param {Object} graph parent graph object 25114 * @return {Object | null} subgraph 25115 */ 25116function parseSubgraph(graph) { 25117 var subgraph = null; 25118 25119 // optional subgraph keyword 25120 if (token === 'subgraph') { 25121 subgraph = {}; 25122 subgraph.type = 'subgraph'; 25123 getToken(); 25124 25125 // optional graph id 25126 if (tokenType === TOKENTYPE.IDENTIFIER) { 25127 subgraph.id = token; 25128 getToken(); 25129 } 25130 } 25131 25132 // open angle bracket 25133 if (token === '{') { 25134 getToken(); 25135 25136 if (!subgraph) { 25137 subgraph = {}; 25138 } 25139 subgraph.parent = graph; 25140 subgraph.node = graph.node; 25141 subgraph.edge = graph.edge; 25142 subgraph.graph = graph.graph; 25143 25144 // statements 25145 parseStatements(subgraph); 25146 25147 // close angle bracket 25148 if (token != '}') { 25149 throw newSyntaxError('Angle bracket } expected'); 25150 } 25151 getToken(); 25152 25153 // remove temporary default options 25154 delete subgraph.node; 25155 delete subgraph.edge; 25156 delete subgraph.graph; 25157 delete subgraph.parent; 25158 25159 // register at the parent graph 25160 if (!graph.subgraphs) { 25161 graph.subgraphs = []; 25162 } 25163 graph.subgraphs.push(subgraph); 25164 } 25165 25166 return subgraph; 25167} 25168 25169/** 25170 * parse an attribute statement like "node [shape=circle fontSize=16]". 25171 * Available keywords are 'node', 'edge', 'graph'. 25172 * The previous list with default attributes will be replaced 25173 * @param {Object} graph 25174 * @returns {String | null} keyword Returns the name of the parsed attribute 25175 * (node, edge, graph), or null if nothing 25176 * is parsed. 25177 */ 25178function parseAttributeStatement(graph) { 25179 // attribute statements 25180 if (token === 'node') { 25181 getToken(); 25182 25183 // node attributes 25184 graph.node = parseAttributeList(); 25185 return 'node'; 25186 } else if (token === 'edge') { 25187 getToken(); 25188 25189 // edge attributes 25190 graph.edge = parseAttributeList(); 25191 return 'edge'; 25192 } else if (token === 'graph') { 25193 getToken(); 25194 25195 // graph attributes 25196 graph.graph = parseAttributeList(); 25197 return 'graph'; 25198 } 25199 25200 return null; 25201} 25202 25203/** 25204 * parse a node statement 25205 * @param {Object} graph 25206 * @param {string | number} id 25207 */ 25208function parseNodeStatement(graph, id) { 25209 // node statement 25210 var node = { 25211 id: id 25212 }; 25213 var attr = parseAttributeList(); 25214 if (attr) { 25215 node.attr = attr; 25216 } 25217 addNode(graph, node); 25218 25219 // edge statements 25220 parseEdge(graph, id); 25221} 25222 25223/** 25224 * Parse an edge or a series of edges 25225 * @param {Object} graph 25226 * @param {string | number} from Id of the from node 25227 */ 25228function parseEdge(graph, from) { 25229 while (token === '->' || token === '--') { 25230 var to; 25231 var type = token; 25232 getToken(); 25233 25234 var subgraph = parseSubgraph(graph); 25235 if (subgraph) { 25236 to = subgraph; 25237 } else { 25238 if (tokenType != TOKENTYPE.IDENTIFIER) { 25239 throw newSyntaxError('Identifier or subgraph expected'); 25240 } 25241 to = token; 25242 addNode(graph, { 25243 id: to 25244 }); 25245 getToken(); 25246 } 25247 25248 // parse edge attributes 25249 var attr = parseAttributeList(); 25250 25251 // create edge 25252 var edge = createEdge(graph, from, to, type, attr); 25253 addEdge(graph, edge); 25254 25255 from = to; 25256 } 25257} 25258 25259/** 25260 * Parse a set with attributes, 25261 * for example [label="1.000", shape=solid] 25262 * @return {Object | null} attr 25263 */ 25264function parseAttributeList() { 25265 var attr = null; 25266 25267 // edge styles of dot and vis 25268 var edgeStyles = { 25269 'dashed': true, 25270 'solid': false, 25271 'dotted': [1, 5] 25272 }; 25273 25274 while (token === '[') { 25275 getToken(); 25276 attr = {}; 25277 while (token !== '' && token != ']') { 25278 if (tokenType != TOKENTYPE.IDENTIFIER) { 25279 throw newSyntaxError('Attribute name expected'); 25280 } 25281 var name = token; 25282 25283 getToken(); 25284 if (token != '=') { 25285 throw newSyntaxError('Equal sign = expected'); 25286 } 25287 getToken(); 25288 25289 if (tokenType != TOKENTYPE.IDENTIFIER) { 25290 throw newSyntaxError('Attribute value expected'); 25291 } 25292 var value = token; 25293 25294 // convert from dot style to vis 25295 if (name === 'style') { 25296 value = edgeStyles[value]; 25297 } 25298 25299 setValue(attr, name, value); // name can be a path 25300 25301 getToken(); 25302 if (token == ',') { 25303 getToken(); 25304 } 25305 } 25306 25307 if (token != ']') { 25308 throw newSyntaxError('Bracket ] expected'); 25309 } 25310 getToken(); 25311 } 25312 25313 return attr; 25314} 25315 25316/** 25317 * Create a syntax error with extra information on current token and index. 25318 * @param {string} message 25319 * @returns {SyntaxError} err 25320 */ 25321function newSyntaxError(message) { 25322 return new SyntaxError(message + ', got "' + chop(token, 30) + '" (char ' + index + ')'); 25323} 25324 25325/** 25326 * Chop off text after a maximum length 25327 * @param {string} text 25328 * @param {number} maxLength 25329 * @returns {String} 25330 */ 25331function chop(text, maxLength) { 25332 return text.length <= maxLength ? text : text.substr(0, 27) + '...'; 25333} 25334 25335/** 25336 * Execute a function fn for each pair of elements in two arrays 25337 * @param {Array | *} array1 25338 * @param {Array | *} array2 25339 * @param {function} fn 25340 */ 25341function forEach2(array1, array2, fn) { 25342 if (Array.isArray(array1)) { 25343 array1.forEach(function (elem1) { 25344 if (Array.isArray(array2)) { 25345 array2.forEach(function (elem2) { 25346 fn(elem1, elem2); 25347 }); 25348 } else { 25349 fn(elem1, array2); 25350 } 25351 }); 25352 } else { 25353 if (Array.isArray(array2)) { 25354 array2.forEach(function (elem2) { 25355 fn(array1, elem2); 25356 }); 25357 } else { 25358 fn(array1, array2); 25359 } 25360 } 25361} 25362 25363/** 25364 * Set a nested property on an object 25365 * When nested objects are missing, they will be created. 25366 * For example setProp({}, 'font.color', 'red') will return {font: {color: 'red'}} 25367 * @param {Object} object 25368 * @param {string} path A dot separated string like 'font.color' 25369 * @param {*} value Value for the property 25370 * @return {Object} Returns the original object, allows for chaining. 25371 */ 25372function setProp(object, path, value) { 25373 var names = path.split('.'); 25374 var prop = names.pop(); 25375 25376 // traverse over the nested objects 25377 var obj = object; 25378 for (var i = 0; i < names.length; i++) { 25379 var name = names[i]; 25380 if (!(name in obj)) { 25381 obj[name] = {}; 25382 } 25383 obj = obj[name]; 25384 } 25385 25386 // set the property value 25387 obj[prop] = value; 25388 25389 return object; 25390} 25391
25392/** 25393 * Convert an object with DOT attributes to their vis.js equivalents. 25394 * @param {Object} attr Object with DOT attributes 25395 * @param {Object} mapping 25396 * @return {Object} Returns an object with vis.js attributes 25397 */ 25398function convertAttr(attr, mapping) { 25399 var converted = {}; 25400 25401 for (var prop in attr) { 25402 if (attr.hasOwnProperty(prop)) { 25403 var visProp = mapping[prop]; 25404 if (Array.isArray(visProp)) { 25405 visProp.forEach(function (visPropI) { 25406 setProp(converted, visPropI, attr[prop]); 25407 }); 25408 } else if (typeof visProp === 'string') { 25409 setProp(converted, visProp, attr[prop]); 25410 } else { 25411 setProp(converted, prop, attr[prop]); 25412 } 25413 } 25414 } 25415 25416 return converted; 25417} 25418 25419/** 25420 * Convert a string containing a graph in DOT language into a map containing 25421 * with nodes and edges in the format of graph. 25422 * @param {string} data Text containing a graph in DOT-notation 25423 * @return {Object} graphData 25424 */ 25425function DOTToGraph(data) { 25426 // parse the DOT file 25427 var dotData = parseDOT(data); 25428 var graphData = { 25429 nodes: [], 25430 edges: [], 25431 options: {} 25432 }; 25433 25434 // copy the nodes 25435 if (dotData.nodes) { 25436 dotData.nodes.forEach(function (dotNode) { 25437 var graphNode = { 25438 id: dotNode.id, 25439 label: String(dotNode.label || dotNode.id) 25440 }; 25441 merge(graphNode, convertAttr(dotNode.attr, NODE_ATTR_MAPPING)); 25442 if (graphNode.image) { 25443 graphNode.shape = 'image'; 25444 } 25445 graphData.nodes.push(graphNode); 25446 }); 25447 } 25448 25449 // copy the edges 25450 if (dotData.edges) { 25451 /** 25452 * Convert an edge in DOT format to an edge with VisGraph format 25453 * @param {Object} dotEdge 25454 * @returns {Object} graphEdge 25455 */ 25456 var convertEdge = function convertEdge(dotEdge) { 25457 var graphEdge = { 25458 from: dotEdge.from, 25459 to: dotEdge.to 25460 }; 25461 merge(graphEdge, convertAttr(dotEdge.attr, EDGE_ATTR_MAPPING)); 25462 graphEdge.arrows = dotEdge.type === '->' ? 'to' : undefined; 25463 25464 return graphEdge; 25465 }; 25466
25467 dotData.edges.forEach(function (dotEdge) { 25468 var from, to; 25469 if (dotEdge.from instanceof Object) { 25470 from = dotEdge.from.nodes; 25471 } else { 25472 from = { 25473 id: dotEdge.from 25474 }; 25475 } 25476 25477 // TODO: support for attributes 'dir' and 'arrowhead' (edge arrows) 25478 25479 if (dotEdge.to instanceof Object) { 25480 to = dotEdge.to.nodes; 25481 } else { 25482 to = { 25483 id: dotEdge.to 25484 }; 25485 } 25486 25487 if (dotEdge.from instanceof Object && dotEdge.from.edges) { 25488 dotEdge.from.edges.forEach(function (subEdge) { 25489 var graphEdge = convertEdge(subEdge); 25490 graphData.edges.push(graphEdge); 25491 }); 25492 } 25493 25494 forEach2(from, to, function (from, to) { 25495 var subEdge = createEdge(graphData, from.id, to.id, dotEdge.type, dotEdge.attr); 25496 var graphEdge = convertEdge(subEdge); 25497 graphData.edges.push(graphEdge); 25498 }); 25499 25500 if (dotEdge.to instanceof Object && dotEdge.to.edges) { 25501 dotEdge.to.edges.forEach(function (subEdge) { 25502 var graphEdge = convertEdge(subEdge); 25503 graphData.edges.push(graphEdge); 25504 }); 25505 } 25506 }); 25507 } 25508 25509 // copy the options 25510 if (dotData.attr) { 25511 graphData.options = dotData.attr; 25512 } 25513 25514 return graphData; 25515} 25516 25517// exports 25518exports.parseDOT = parseDOT; 25519exports.DOTToGraph = DOTToGraph; 25520 25521/***/ }), 25522/* 115 */ 25523/***/ (function(module, exports, __webpack_require__) { 25524 25525"use strict"; 25526 25527 25528/** 25529 * 25530 * @param {json} gephiJSON 25531 * @param {obj} optionsObj 25532 * @returns {{nodes: Array, edges: Array}} 25533 */ 25534function parseGephi(gephiJSON, optionsObj) { 25535 var edges = []; 25536 var nodes = []; 25537 var options = { 25538 edges: { 25539 inheritColor: false 25540 }, 25541 nodes: { 25542 fixed: false, 25543 parseColor: false 25544 } 25545 }; 25546 25547 if (optionsObj !== undefined) { 25548 if (optionsObj.fixed !== undefined) { 25549 options.nodes.fixed = optionsObj.fixed; 25550 } 25551 if (optionsObj.parseColor !== undefined) { 25552 options.nodes.parseColor = optionsObj.parseColor; 25553 } 25554 if (optionsObj.inheritColor !== undefined) { 25555 options.edges.inheritColor = optionsObj.inheritColor; 25556 } 25557 } 25558 25559 var gEdges = gephiJSON.edges; 25560 var gNodes = gephiJSON.nodes; 25561 for (var i = 0; i < gEdges.length; i++) { 25562 var edge = {}; 25563 var gEdge = gEdges[i]; 25564 edge['id'] = gEdge.id; 25565 edge['from'] = gEdge.source; 25566 edge['to'] = gEdge.target; 25567 edge['attributes'] = gEdge.attributes; 25568 edge['label'] = gEdge.label; 25569 edge['title'] = gEdge.attributes !== undefined ? gEdge.attributes.title : undefined; 25570 if (gEdge['type'] === 'Directed') { 25571 edge['arrows'] = 'to'; 25572 } 25573 // edge['value'] = gEdge.attributes !== undefined ? gEdge.attributes.Weight : undefined; 25574 // edge['width'] = edge['value'] !== undefined ? undefined : edgegEdge.size; 25575 if (gEdge.color && options.inheritColor === false) { 25576 edge['color'] = gEdge.color; 25577 } 25578 edges.push(edge); 25579 } 25580 25581 for (var j = 0; j < gNodes.length; j++) { 25582 var node = {}; 25583 var gNode = gNodes[j]; 25584 node['id'] = gNode.id; 25585 node['attributes'] = gNode.attributes; 25586 node['x'] = gNode.x; 25587 node['y'] = gNode.y; 25588 node['label'] = gNode.label; 25589 node['title'] = gNode.attributes !== undefined ? gNode.attributes.title : gNode.title; 25590 if (options.nodes.parseColor === true) { 25591 node['color'] = gNode.color; 25592 } else { 25593 node['color'] = gNode.color !== undefined ? { background: gNode.color, border: gNode.color, highlight: { background: gNode.color, border: gNode.color }, hover: { background: gNode.color, border: gNode.color } } : undefined; 25594 } 25595 node['size'] = gNode.size; 25596 node['fixed'] = options.nodes.fixed && gNode.x !== undefined && gNode.y !== undefined; 25597 nodes.push(node); 25598 } 25599 25600 return { nodes: nodes, edges: edges }; 25601} 25602 25603exports.parseGephi = parseGephi; 25604 25605/***/ }), 25606/* 116 */ 25607/***/ (function(module, exports, __webpack_require__) { 25608 25609"use strict"; 25610 25611 25612Object.defineProperty(exports, "__esModule", { 25613 value: true 25614}); 25615 25616var _classCallCheck2 = __webpack_require__(0); 25617 25618var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); 25619 25620var _createClass2 = __webpack_require__(1); 25621 25622var _createClass3 = _interopRequireDefault(_createClass2); 25623 25624var _CachedImage = __webpack_require__(185); 25625 25626var _CachedImage2 = _interopRequireDefault(_CachedImage); 25627 25628function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } 25629 25630/** 25631 * This callback is a callback that accepts an Image. 25632 * @callback ImageCallback 25633 * @param {Image} image 25634 */ 25635 25636/** 25637 * This class loads images and keeps them stored. 25638 * 25639 * @param {ImageCallback} callback 25640 */ 25641var Images = function () { 25642 /** 25643 * @param {ImageCallback} callback 25644 */ 25645 function Images(callback) { 25646 (0, _classCallCheck3["default"])(this, Images); 25647 25648 this.images = {}; 25649 this.imageBroken = {}; 25650 this.callback = callback; 25651 } 25652 25653 /** 25654 * @param {string} url The original Url that failed to load, if the broken image is successfully loaded it will be added to the cache using this Url as the key so that subsequent requests for this Url will return the broken image 25655 * @param {string} brokenUrl Url the broken image to try and load 25656 * @param {Image} imageToLoadBrokenUrlOn The image object 25657 */ 25658 25659 25660 (0, _createClass3["default"])(Images, [{ 25661 key: "_tryloadBrokenUrl", 25662 value: function _tryloadBrokenUrl(url, brokenUrl, imageToLoadBrokenUrlOn) { 25663 //If these parameters aren't specified then exit the function because nothing constructive can be done 25664 if (url === undefined || imageToLoadBrokenUrlOn === undefined) return; 25665 if (brokenUrl === undefined) { 25666 console.warn("No broken url image defined"); 25667 return; 25668 } 25669 25670 //Clear the old subscription to the error event and put a new in place that only handle errors in loading the brokenImageUrl 25671 imageToLoadBrokenUrlOn.onerror = function () { 25672 console.error("Could not load brokenImage:", brokenUrl); 25673 // cache item will contain empty image, this should be OK for default 25674 }; 25675 25676 //Set the source of the image to the brokenUrl, this is actually what kicks off the loading of the broken image 25677 imageToLoadBrokenUrlOn.image.src = brokenUrl; 25678 } 25679 25680 /** 25681 * 25682 * @param {vis.Image} imageToRedrawWith 25683 * @private 25684 */ 25685 25686 }, {
25687 key: "_redrawWithImage", 25688 value: function _redrawWithImage(imageToRedrawWith) { 25689 if (this.callback) { 25690 this.callback(imageToRedrawWith); 25691 } 25692 } 25693 25694 /** 25695 * @param {string} url Url of the image 25696 * @param {string} brokenUrl Url of an image to use if the url image is not found 25697 * @return {Image} img The image object 25698 */ 25699 25700 }, { 25701 key: "load", 25702 value: function load(url, brokenUrl) { 25703 var _this = this; 25704 25705 //Try and get the image from the cache, if successful then return the cached image 25706 var cachedImage = this.images[url]; 25707 if (cachedImage) return cachedImage; 25708 25709 //Create a new image 25710 var img = new _CachedImage2["default"](); 25711 25712 // Need to add to cache here, otherwise final return will spawn different copies of the same image, 25713 // Also, there will be multiple loads of the same image. 25714 this.images[url] = img; 25715 25716 //Subscribe to the event that is raised if the image loads successfully 25717 img.image.onload = function () { 25718 // Properly init the cached item and then request a redraw 25719 _this._fixImageCoordinates(img.image); 25720 img.init(); 25721 _this._redrawWithImage(img); 25722 }; 25723 25724 //Subscribe to the event that is raised if the image fails to load 25725 img.image.onerror = function () { 25726 console.error("Could not load image:", url); 25727 //Try and load the image specified by the brokenUrl using 25728 _this._tryloadBrokenUrl(url, brokenUrl, img); 25729 }; 25730 25731 //Set the source of the image to the url, this is what actually kicks off the loading of the image 25732 img.image.src = url; 25733 25734 //Return the new image 25735 return img; 25736 } 25737 25738 /** 25739 * IE11 fix -- thanks dponch! 25740 * 25741 * Local helper function 25742 * @param {vis.Image} imageToCache 25743 * @private 25744 */ 25745 25746 }, { 25747 key: "_fixImageCoordinates", 25748 value: function _fixImageCoordinates(imageToCache) { 25749 if (imageToCache.width === 0) { 25750 document.body.appendChild(imageToCache); 25751 imageToCache.width = imageToCache.offsetWidth; 25752 imageToCache.height = imageToCache.offsetHeight; 25753 document.body.removeChild(imageToCache); 25754 } 25755 } 25756 }]); 25757 return Images; 25758}(); 25759 25760exports["default"] = Images; 25761 25762/***/ }), 25763/* 117 */ 25764/***/ (function(module, exports, __webpack_require__) { 25765 25766"use strict"; 25767 25768 25769Object.defineProperty(exports, "__esModule", { 25770 value: true 25771}); 25772 25773var _slicedToArray2 = __webpack_require__(30); 25774 25775var _slicedToArray3 = _interopRequireDefault(_slicedToArray2); 25776 25777var _typeof2 = __webpack_require__(6); 25778 25779var _typeof3 = _interopRequireDefault(_typeof2); 25780 25781var _classCallCheck2 = __webpack_require__(0); 25782 25783var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); 25784 25785var _createClass2 = __webpack_require__(1); 25786 25787var _createClass3 = _interopRequireDefault(_createClass2); 25788 25789function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 25790 25791var util = __webpack_require__(2); 25792var ComponentUtil = __webpack_require__(48)['default']; 25793var LabelSplitter = __webpack_require__(191)['default']; 25794 25795/** 25796 * @typedef {'bold'|'ital'|'boldital'|'mono'|'normal'} MultiFontStyle 25797 * 25798 * The allowed specifiers of multi-fonts. 25799 */ 25800 25801/** 25802 * @typedef {{color:string, size:number, face:string, mod:string, vadjust:number}} MultiFontOptions 25803 * 25804 * The full set of options of a given multi-font. 25805 */ 25806 25807/** 25808 * @typedef {Array.<object>} Pile 25809 * 25810 * Sequence of option objects, the order is significant. 25811 * The sequence is used to determine the value of a given option. 25812 * 25813 * Usage principles: 25814 * 25815 * - All search is done in the sequence of the pile. 25816 * - As soon as a value is found, the searching stops. 25817 * - prototypes are totally ignored. The idea is to add option objects used as prototypes 25818 * to the pile, in the correct order. 25819 */ 25820 25821/** 25822 * List of special styles for multi-fonts 25823 * @private 25824 */ 25825var multiFontStyle = ['bold', 'ital', 'boldital', 'mono']; 25826 25827/** 25828 * A Label to be used for Nodes or Edges. 25829 */ 25830 25831var Label = function () { 25832 25833 /** 25834 * @param {Object} body 25835 * @param {Object} options 25836 * @param {boolean} [edgelabel=false] 25837 */ 25838 function Label(body, options) { 25839 var edgelabel = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
25840 (0, _classCallCheck3['default'])(this, Label); 25841 25842 this.body = body; 25843 this.pointToSelf = false; 25844 this.baseSize = undefined; 25845 this.fontOptions = {}; // instance variable containing the *instance-local* font options 25846 this.setOptions(options); 25847 this.size = { top: 0, left: 0, width: 0, height: 0, yLine: 0 }; 25848 this.isEdgeLabel = edgelabel; 25849 } 25850 25851 /** 25852 * @param {Object} options the options of the parent Node-instance 25853 */ 25854 25855 25856 (0, _createClass3['default'])(Label, [{ 25857 key: 'setOptions', 25858 value: function setOptions(options) { 25859 this.elementOptions = options; // Reference to the options of the parent Node-instance 25860 25861 this.initFontOptions(options.font); 25862 25863 if (ComponentUtil.isValidLabel(options.label)) { 25864 this.labelDirty = true; 25865 } else { 25866 // Bad label! Change the option value to prevent bad stuff happening 25867 options.label = ''; 25868 } 25869 25870 if (options.font !== undefined && options.font !== null) { 25871 // font options can be deleted at various levels 25872 if (typeof options.font === 'string') { 25873 this.baseSize = this.fontOptions.size; 25874 } else if ((0, _typeof3['default'])(options.font) === 'object') { 25875 var size = options.font.size; 25876 25877 if (size !== undefined) { 25878 this.baseSize = size; 25879 } 25880 } 25881 } 25882 } 25883 25884 /** 25885 * Init the font Options structure. 25886 * 25887 * Member fontOptions serves as an accumulator for the current font options. 25888 * As such, it needs to be completely separated from the node options. 25889 * 25890 * @param {Object} newFontOptions the new font options to process 25891 * @private 25892 */ 25893 25894 }, { 25895 key: 'initFontOptions', 25896 value: function initFontOptions(newFontOptions) { 25897 var _this = this; 25898 25899 // Prepare the multi-font option objects. 25900 // These will be filled in propagateFonts(), if required 25901 util.forEach(multiFontStyle, function (style) { 25902 _this.fontOptions[style] = {}; 25903 }); 25904 25905 // Handle shorthand option, if present 25906 if (Label.parseFontString(this.fontOptions, newFontOptions)) { 25907 this.fontOptions.vadjust = 0; 25908 return; 25909 } 25910 25911 // Copy over the non-multifont options, if specified 25912 util.forEach(newFontOptions, function (prop, n) { 25913 if (prop !== undefined && prop !== null && (typeof prop === 'undefined' ? 'undefined' : (0, _typeof3['default'])(prop)) !== 'object') { 25914 _this.fontOptions[n] = prop; 25915 } 25916 }); 25917 } 25918 25919 /** 25920 * If in-variable is a string, parse it as a font specifier. 25921 * 25922 * Note that following is not done here and have to be done after the call: 25923 * - No number conversion (size) 25924 * - Not all font options are set (vadjust, mod) 25925 * 25926 * @param {Object} outOptions out-parameter, object in which to store the parse results (if any) 25927 * @param {Object} inOptions font options to parse 25928 * @return {boolean} true if font parsed as string, false otherwise 25929 * @static 25930 */ 25931 25932 }, { 25933 key: 'constrain', 25934 25935 25936 /** 25937 * Set the width and height constraints based on 'nearest' value 25938 * 25939 * @param {Array} pile array of option objects to consider 25940 * @returns {object} the actual constraint values to use 25941 * @private 25942 */ 25943 value: function constrain(pile) { 25944 // NOTE: constrainWidth and constrainHeight never set! 25945 // NOTE: for edge labels, only 'maxWdt' set 25946 // Node labels can set all the fields 25947 var fontOptions = { 25948 constrainWidth: false, 25949 maxWdt: -1, 25950 minWdt: -1, 25951 constrainHeight: false, 25952 minHgt: -1, 25953 valign: 'middle' 25954 }; 25955 25956 var widthConstraint = util.topMost(pile, 'widthConstraint'); 25957 if (typeof widthConstraint === 'number') { 25958 fontOptions.maxWdt = Number(widthConstraint); 25959 fontOptions.minWdt = Number(widthConstraint); 25960 } else if ((typeof widthConstraint === 'undefined' ? 'undefined' : (0, _typeof3['default'])(widthConstraint)) === 'object') { 25961 var widthConstraintMaximum = util.topMost(pile, ['widthConstraint', 'maximum']); 25962 if (typeof widthConstraintMaximum === 'number') { 25963 fontOptions.maxWdt = Number(widthConstraintMaximum); 25964 } 25965 var widthConstraintMinimum = util.topMost(pile, ['widthConstraint', 'minimum']); 25966 if (typeof widthConstraintMinimum === 'number') { 25967 fontOptions.minWdt = Number(widthConstraintMinimum); 25968 } 25969 } 25970
25971 var heightConstraint = util.topMost(pile, 'heightConstraint'); 25972 if (typeof heightConstraint === 'number') { 25973 fontOptions.minHgt = Number(heightConstraint); 25974 } else if ((typeof heightConstraint === 'undefined' ? 'undefined' : (0, _typeof3['default'])(heightConstraint)) === 'object') { 25975 var heightConstraintMinimum = util.topMost(pile, ['heightConstraint', 'minimum']); 25976 if (typeof heightConstraintMinimum === 'number') { 25977 fontOptions.minHgt = Number(heightConstraintMinimum); 25978 } 25979 var heightConstraintValign = util.topMost(pile, ['heightConstraint', 'valign']); 25980 if (typeof heightConstraintValign === 'string') { 25981 if (heightConstraintValign === 'top' || heightConstraintValign === 'bottom') { 25982 fontOptions.valign = heightConstraintValign; 25983 } 25984 } 25985 } 25986 25987 return fontOptions; 25988 } 25989 25990 /** 25991 * Set options and update internal state 25992 * 25993 * @param {Object} options options to set 25994 * @param {Array} pile array of option objects to consider for option 'chosen' 25995 */ 25996 25997 }, { 25998 key: 'update', 25999 value: function update(options, pile) { 26000 this.setOptions(options, true); 26001 this.propagateFonts(pile); 26002 util.deepExtend(this.fontOptions, this.constrain(pile)); 26003 this.fontOptions.chooser = ComponentUtil.choosify('label', pile); 26004 } 26005 26006 /** 26007 * When margins are set in an element, adjust sizes is called to remove them 26008 * from the width/height constraints. This must be done prior to label sizing. 26009 * 26010 * @param {{top: number, right: number, bottom: number, left: number}} margins 26011 */ 26012 26013 }, { 26014 key: 'adjustSizes', 26015 value: function adjustSizes(margins) { 26016 var widthBias = margins ? margins.right + margins.left : 0; 26017 if (this.fontOptions.constrainWidth) { 26018 this.fontOptions.maxWdt -= widthBias; 26019 this.fontOptions.minWdt -= widthBias; 26020 } 26021 var heightBias = margins ? margins.top + margins.bottom : 0; 26022 if (this.fontOptions.constrainHeight) { 26023 this.fontOptions.minHgt -= heightBias; 26024 } 26025 } 26026 26027 ///////////////////////////////////////////////////////// 26028 // Methods for handling options piles 26029 // Eventually, these will be moved to a separate class 26030 ///////////////////////////////////////////////////////// 26031 26032 /** 26033 * Add the font members of the passed list of option objects to the pile. 26034 * 26035 * @param {Pile} dstPile pile of option objects add to 26036 * @param {Pile} srcPile pile of option objects to take font options from 26037 * @private 26038 */ 26039 26040 }, { 26041 key: 'addFontOptionsToPile', 26042 value: function addFontOptionsToPile(dstPile, srcPile) { 26043 for (var i = 0; i < srcPile.length; ++i) { 26044 this.addFontToPile(dstPile, srcPile[i]); 26045 } 26046 } 26047 26048 /** 26049 * Add given font option object to the list of objects (the 'pile') to consider for determining 26050 * multi-font option values. 26051 * 26052 * @param {Pile} pile pile of option objects to use 26053 * @param {object} options instance to add to pile 26054 * @private 26055 */ 26056 26057 }, { 26058 key: 'addFontToPile', 26059 value: function addFontToPile(pile, options) { 26060 if (options === undefined) return; 26061 if (options.font === undefined || options.font === null) return; 26062 26063 var item = options.font; 26064 pile.push(item); 26065 } 26066 26067 /** 26068 * Collect all own-property values from the font pile that aren't multi-font option objectss. 26069 * 26070 * @param {Pile} pile pile of option objects to use 26071 * @returns {object} object with all current own basic font properties 26072 * @private 26073 */ 26074 26075 }, { 26076 key: 'getBasicOptions', 26077 value: function getBasicOptions(pile) { 26078 var ret = {}; 26079 26080 // Scans the whole pile to get all options present 26081 for (var n = 0; n < pile.length; ++n) { 26082 var fontOptions = pile[n]; 26083 26084 // Convert shorthand if necessary 26085 var tmpShorthand = {}; 26086 if (Label.parseFontString(tmpShorthand, fontOptions)) { 26087 fontOptions = tmpShorthand; 26088 } 26089 26090 util.forEach(fontOptions, function (opt, name) { 26091 if (opt === undefined) return; // multi-font option need not be present 26092 if (ret.hasOwnProperty(name)) return; // Keep first value we encounter 26093 26094 if (multiFontStyle.indexOf(name) !== -1) { 26095 // Skip multi-font properties but we do need the structure 26096 ret[name] = {}; 26097 } else { 26098 ret[name] = opt; 26099 } 26100 }); 26101 } 26102 26103 return ret; 26104 } 26105 26106 /** 26107 * Return the value for given option for the given multi-font. 26108 * 26109 * All available option objects are trawled in the set order to construct the option values. 26110 * 26111 * --------------------------------------------------------------------- 26112 * ## Traversal of pile for multi-fonts 26113 * 26114 * The determination of multi-font option values is a special case, because any values n
26114ot 26115 * present in the multi-font options should by definition be taken from the main font options, 26116 * i.e. from the current 'parent' object of the multi-font option. 26117 * 26118 * ### Search order for multi-fonts 26119 * 26120 * 'bold' used as example: 26121 * 26122 * - search in option group 'bold' in local properties 26123 * - search in main font option group in local properties 26124 * 26125 * --------------------------------------------------------------------- 26126 * 26127 * @param {Pile} pile pile of option objects to use 26128 * @param {MultiFontStyle} multiName sub path for the multi-font 26129 * @param {string} option the option to search for, for the given multi-font 26130 * @returns {string|number} the value for the given option 26131 * @private 26132 */ 26133 26134 }, { 26135 key: 'getFontOption', 26136 value: function getFontOption(pile, multiName, option) { 26137 var multiFont = void 0; 26138 26139 // Search multi font in local properties 26140 for (var n = 0; n < pile.length; ++n) { 26141 var fontOptions = pile[n]; 26142 26143 if (fontOptions.hasOwnProperty(multiName)) { 26144 multiFont = fontOptions[multiName]; 26145 if (multiFont === undefined || multiFont === null) continue; 26146 26147 // Convert shorthand if necessary 26148 // TODO: inefficient to do this conversion every time; find a better way. 26149 var tmpShorthand = {}; 26150 if (Label.parseFontString(tmpShorthand, multiFont)) { 26151 multiFont = tmpShorthand; 26152 } 26153 26154 if (multiFont.hasOwnProperty(option)) { 26155 return multiFont[option]; 26156 } 26157 } 26158 } 26159 26160 // Option is not mentioned in the multi font options; take it from the parent font options. 26161 // These have already been converted with getBasicOptions(), so use the converted values. 26162 if (this.fontOptions.hasOwnProperty(option)) { 26163 return this.fontOptions[option]; 26164 } 26165 26166 // A value **must** be found; you should never get here. 26167 throw new Error("Did not find value for multi-font for property: '" + option + "'"); 26168 } 26169 26170 /** 26171 * Return all options values for the given multi-font. 26172 * 26173 * All available option objects are trawled in the set order to construct the option values. 26174 * 26175 * @param {Pile} pile pile of option objects to use 26176 * @param {MultiFontStyle} multiName sub path for the mod-font 26177 * @returns {MultiFontOptions} 26178 * @private 26179 */ 26180 26181 }, { 26182 key: 'getFontOptions', 26183 value: function getFontOptions(pile, multiName) { 26184 var result = {}; 26185 var optionNames = ['color', 'size', 'face', 'mod', 'vadjust']; // List of allowed options per multi-font 26186 26187 for (var i = 0; i < optionNames.length; ++i) { 26188 var mod = optionNames[i]; 26189 result[mod] = this.getFontOption(pile, multiName, mod); 26190 } 26191 26192 return result; 26193 } 26194 26195 ///////////////////////////////////////////////////////// 26196 // End methods for handling options piles 26197 ///////////////////////////////////////////////////////// 26198 26199 26200 /** 26201 * Collapse the font options for the multi-font to single objects, from 26202 * the chain of option objects passed (the 'pile'). 26203 * 26204 * @param {Pile} pile sequence of option objects to consider. 26205 * First item in list assumed to be the newly set options. 26206 */ 26207 26208 }, { 26209 key: 'propagateFonts', 26210 value: function propagateFonts(pile) { 26211 var _this2 = this; 26212 26213 var fontPile = []; // sequence of font objects to consider, order important 26214 26215 // Note that this.elementOptions is not used here. 26216 this.addFontOptionsToPile(fontPile, pile); 26217 this.fontOptions = this.getBasicOptions(fontPile); 26218 26219 // We set multifont values even if multi === false, for consistency (things break otherwise) 26220 26221 var _loop = function _loop(i) { 26222 var mod = multiFontStyle[i]; 26223 var modOptions = _this2.fontOptions[mod]; 26224 var tmpMultiFontOptions = _this2.getFontOptions(fontPile, mod); 26225 26226 // Copy over found values 26227 util.forEach(tmpMultiFontOptions, function (option, n) { 26228 modOptions[n] = option; 26229 }); 26230 26231 modOptions.size = Number(modOptions.size); 26232 modOptions.vadjust = Number(modOptions.vadjust); 26233 }; 26234 26235 for (var i = 0; i < multiFontStyle.length; ++i) { 26236 _loop(i); 26237 } 26238 } 26239 26240 /** 26241 * Main function. This is called from anything that wants to draw a label. 26242 * @param {CanvasRenderingContext2D} ctx 26243 * @param {number} x 26244 * @param {number} y 26245 * @param {boolean} selected 26246 * @param {boolean} hover 26247 * @param {string} [baseline='middle'] 26248 */ 26249 26250 }, { 26251 key: 'draw', 26252 value: function draw(ctx, x, y, selected, hover) { 26253 var baseline = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : 'middle'; 26254 26255 // if no label, return 26256 if (this.elementOptions.label === undefined) return; 26257 26258 // check if we have to render the label 26259 var viewFontSize = this.fontOptions.size * this.body.view.scale; 26260 if (this.elementOptions.label && viewFontSize < this.elementOptions.scaling.label.drawThreshold - 1) return; 26261 26262 // This ensures that there will not be HUGE letters on screen 26263 // by setting an upper limit on the visible text size (regardless of zoomLevel) 26264 if (viewFontSize >= this.elementOptions.scaling.label.maxVisible) { 26265 viewFontSize = Number(this.elementOptions.scaling.label.maxVisible) / this.body.view.scale; 26266 } 26267 26268 // update the size cache if required 26269 this.calculateLabelSize(ctx, selected, hover, x, y, baseline);
26270 this._drawBackground(ctx); 26271 this._drawText(ctx, x, this.size.yLine, baseline, viewFontSize); 26272 } 26273 26274 /** 26275 * Draws the label background 26276 * @param {CanvasRenderingContext2D} ctx 26277 * @private 26278 */ 26279 26280 }, { 26281 key: '_drawBackground', 26282 value: function _drawBackground(ctx) { 26283 if (this.fontOptions.background !== undefined && this.fontOptions.background !== "none") { 26284 ctx.fillStyle = this.fontOptions.background; 26285 var size = this.getSize(); 26286 ctx.fillRect(size.left, size.top, size.width, size.height); 26287 } 26288 } 26289 26290 /** 26291 * 26292 * @param {CanvasRenderingContext2D} ctx 26293 * @param {number} x 26294 * @param {number} y 26295 * @param {string} [baseline='middle'] 26296 * @param {number} viewFontSize 26297 * @private 26298 */ 26299 26300 }, { 26301 key: '_drawText', 26302 value: function _drawText(ctx, x, y) { 26303 var baseline = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 'middle'; 26304 var viewFontSize = arguments[4]; 26305 26306 var _setAlignment2 = this._setAlignment(ctx, x, y, baseline); 26307 26308 var _setAlignment3 = (0, _slicedToArray3['default'])(_setAlignment2, 2); 26309 26310 x = _setAlignment3[0]; 26311 y = _setAlignment3[1]; 26312 26313 26314 ctx.textAlign = 'left'; 26315 x = x - this.size.width / 2; // Shift label 1/2-distance to the left 26316 if (this.fontOptions.valign && this.size.height > this.size.labelHeight) { 26317 if (this.fontOptions.valign === 'top') { 26318 y -= (this.size.height - this.size.labelHeight) / 2; 26319 } 26320 if (this.fontOptions.valign === 'bottom') { 26321 y += (this.size.height - this.size.labelHeight) / 2; 26322 } 26323 } 26324 26325 // draw the text 26326 for (var i = 0; i < this.lineCount; i++) { 26327 var line = this.lines[i]; 26328 if (line && line.blocks) { 26329 var width = 0; 26330 if (this.isEdgeLabel || this.fontOptions.align === 'center') { 26331 width += (this.size.width - line.width) / 2; 26332 } else if (this.fontOptions.align === 'right') { 26333 width += this.size.width - line.width; 26334 } 26335 for (var j = 0; j < line.blocks.length; j++) { 26336 var block = line.blocks[j]; 26337 ctx.font = block.font; 26338 26339 var _getColor2 = this._getColor(block.color, viewFontSize, block.strokeColor), 26340 _getColor3 = (0, _slicedToArray3['default'])(_getColor2, 2), 26341 fontColor = _getColor3[0], 26342 strokeColor = _getColor3[1]; 26343 26344 if (block.strokeWidth > 0) { 26345 ctx.lineWidth = block.strokeWidth; 26346 ctx.strokeStyle = strokeColor; 26347 ctx.lineJoin = 'round'; 26348 } 26349 ctx.fillStyle = fontColor; 26350 26351 if (block.strokeWidth > 0) { 26352 ctx.strokeText(block.text, x + width, y + block.vadjust); 26353 } 26354 ctx.fillText(block.text, x + width, y + block.vadjust); 26355 width += block.width; 26356 } 26357 y += line.height; 26358 } 26359 } 26360 } 26361 26362 /** 26363 * 26364 * @param {CanvasRenderingContext2D} ctx 26365 * @param {number} x 26366 * @param {number} y 26367 * @param {string} baseline 26368 * @returns {Array.<number>} 26369 * @private 26370 */ 26371 26372 }, { 26373 key: '_setAlignment', 26374 value: function _setAlignment(ctx, x, y, baseline) { 26375 // check for label alignment (for edges) 26376 // TODO: make alignment for nodes 26377 if (this.isEdgeLabel && this.fontOptions.align !== 'horizontal' && this.pointToSelf === false) { 26378 x = 0; 26379 y = 0; 26380 26381 var lineMargin = 2; 26382 if (this.fontOptions.align === 'top') { 26383 ctx.textBaseline = 'alphabetic'; 26384 y -= 2 * lineMargin; // distance from edge, required because we use alphabetic. Alphabetic has less difference between browsers 26385 } else if (this.fontOptions.align === 'bottom') { 26386 ctx.textBaseline = 'hanging'; 26387 y += 2 * lineMargin; // distance from edge, required because we use hanging. Hanging has less difference between browsers 26388 } else { 26389 ctx.textBaseline = 'middle'; 26390 } 26391 } else { 26392 ctx.textBaseline = baseline; 26393 } 26394 return [x, y]; 26395 } 26396 26397 /** 26398 * fade in when relative scale is between threshold and threshold - 1. 26399 * If the relative scale would be smaller than threshold -1 the draw function would have returned before coming here. 26400 * 26401 * @param {string} color The font color to use 26402 * @param {number} viewFontSize 26403 * @param {string} initialStrokeColor 26404 * @returns {Array.<string>} An array containing the font color and stroke color 26405 * @private 26406 */ 26407 26408 }, { 26409 key: '_getColor', 26410 value: function _getColor(color, viewFontSize, initialStrokeColor) { 26411 var fontColor = color || '#000000'; 26412 var strokeColor = initialStrokeColor || '#ffffff'; 26413 if (viewFontSize <= this.elementOptions.scaling.label.drawThreshold) { 26414 var opacity = Math.max(0, Math.min(1, 1 - (this.elementOptions.scaling.label.drawThreshold - viewFontSize))); 26415 fontColor = util.overrideOpacity(fontColor, opacity); 26416 strokeColor = util.overrideOpacity(strokeColor, opacity); 26417 } 26418 return [fontColor, strokeColor]; 26419 } 26420 26421 /** 26422 * 26423 * @param {CanvasRenderingContext2D} ctx 26424 * @param {boolean} selected 26425 * @param {boolean} hover 26426 * @returns {{width: number, height: number}} 26427 */ 26428 26429 }, { 26430 key: 'getTextSize', 26431 value: function getTextSize(ctx) { 26432 var selected = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
26433 var hover = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false; 26434 26435 this._processLabel(ctx, selected, hover); 26436 return { 26437 width: this.size.width, 26438 height: this.size.height, 26439 lineCount: this.lineCount 26440 }; 26441 } 26442 26443 /** 26444 * Get the current dimensions of the label 26445 * 26446 * @return {rect} 26447 */ 26448 26449 }, { 26450 key: 'getSize', 26451 value: function getSize() { 26452 var lineMargin = 2; 26453 var x = this.size.left; // default values which might be overridden below 26454 var y = this.size.top - 0.5 * lineMargin; // idem 26455 26456 if (this.isEdgeLabel) { 26457 var x2 = -this.size.width * 0.5; 26458 26459 switch (this.fontOptions.align) { 26460 case 'middle': 26461 x = x2; 26462 y = -this.size.height * 0.5; 26463 break; 26464 case 'top': 26465 x = x2; 26466 y = -(this.size.height + lineMargin); 26467 break; 26468 case 'bottom': 26469 x = x2; 26470 y = lineMargin; 26471 break; 26472 } 26473 } 26474 26475 var ret = { 26476 left: x, 26477 top: y, 26478 width: this.size.width, 26479 height: this.size.height 26480 }; 26481 26482 return ret; 26483 } 26484 26485 /** 26486 * 26487 * @param {CanvasRenderingContext2D} ctx 26488 * @param {boolean} selected 26489 * @param {boolean} hover 26490 * @param {number} [x=0] 26491 * @param {number} [y=0] 26492 * @param {'middle'|'hanging'} [baseline='middle'] 26493 */ 26494 26495 }, { 26496 key: 'calculateLabelSize', 26497 value: function calculateLabelSize(ctx, selected, hover) { 26498 var x = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 0; 26499 var y = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : 0; 26500 var baseline = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : 'middle'; 26501 26502 this._processLabel(ctx, selected, hover); 26503 this.size.left = x - this.size.width * 0.5; 26504 this.size.top = y - this.size.height * 0.5; 26505 this.size.yLine = y + (1 - this.lineCount) * 0.5 * this.fontOptions.size; 26506 if (baseline === "hanging") { 26507 this.size.top += 0.5 * this.fontOptions.size; 26508 this.size.top += 4; // distance from node, required because we use hanging. Hanging has less difference between browsers 26509 this.size.yLine += 4; // distance from node 26510 } 26511 } 26512 26513 /** 26514 * 26515 * @param {CanvasRenderingContext2D} ctx 26516 * @param {boolean} selected 26517 * @param {boolean} hover 26518 * @param {string} mod 26519 * @returns {{color, size, face, mod, vadjust, strokeWidth: *, strokeColor: (*|string|allOptions.edges.font.strokeColor|{string}|allOptions.nodes.font.strokeColor|Array)}} 26520 */ 26521 26522 }, { 26523 key: 'getFormattingValues', 26524 value: function getFormattingValues(ctx, selected, hover, mod) { 26525 var getValue = function getValue(fontOptions, mod, option) { 26526 if (mod === "normal") { 26527 if (option === 'mod') return ""; 26528 return fontOptions[option]; 26529 } 26530 26531 if (fontOptions[mod][option] !== undefined) { 26532 // Grumbl leaving out test on undefined equals false for "" 26533 return fontOptions[mod][option]; 26534 } else { 26535 // Take from parent font option 26536 return fontOptions[option]; 26537 } 26538 }; 26539 26540 var values = { 26541 color: getValue(this.fontOptions, mod, 'color'), 26542 size: getValue(this.fontOptions, mod, 'size'), 26543 face: getValue(this.fontOptions, mod, 'face'), 26544 mod: getValue(this.fontOptions, mod, 'mod'), 26545 vadjust: getValue(this.fontOptions, mod, 'vadjust'), 26546 strokeWidth: this.fontOptions.strokeWidth, 26547 strokeColor: this.fontOptions.strokeColor 26548 }; 26549 if (selected || hover) { 26550 if (mod === "normal" && this.fontOptions.chooser === true && this.elementOptions.labelHighlightBold) { 26551 values.mod = 'bold'; 26552 } else { 26553 if (typeof this.fontOptions.chooser === 'function') { 26554 this.fontOptions.chooser(values, this.elementOptions.id, selected, hover); 26555 } 26556 } 26557 } 26558 26559 var fontString = ""; 26560 if (values.mod !== undefined && values.mod !== "") { 26561 // safeguard for undefined - this happened 26562 fontString += values.mod + " "; 26563 } 26564 fontString += values.size + "px " + values.face; 26565 26566 ctx.font = fontString.replace(/"/g, ""); 26567 values.font = ctx.font; 26568 values.height = values.size; 26569 return values; 26570 } 26571 26572 /** 26573 * 26574 * @param {boolean} selected 26575 * @param {boolean} hover 26576 * @returns {boolean} 26577 */ 26578 26579 }, { 26580 key: 'differentState', 26581 value: function differentState(selected, hover) { 26582 return selected !== this.selectedState || hover !== this.hoverState; 26583 } 26584 26585 /** 26586 * This explodes the passed text into lines and determines the width, height and number of lines. 26587 * 26588 * @param {CanvasRenderingContext2D} ctx 26589 * @param {boolean} selected 26590 * @param {boolean} hover 26591 * @param {string} inText the text to explode 26592 * @returns {{width, height, lines}|*} 26593 * @private 26594 */ 26595 26596 }, { 26597 key: '_processLabelText', 26598 value: function _processLabelText(ctx, selected, hover, inText) { 26599 var splitter = new LabelSplitter(ctx, this, selected, hover); 26600 return splitter.process(inText); 26601 } 26602 26603 /**
26604 * This explodes the label string into lines and sets the width, height and number of lines. 26605 * @param {CanvasRenderingContext2D} ctx 26606 * @param {boolean} selected 26607 * @param {boolean} hover 26608 * @private 26609 */ 26610 26611 }, { 26612 key: '_processLabel', 26613 value: function _processLabel(ctx, selected, hover) { 26614 26615 if (this.labelDirty === false && !this.differentState(selected, hover)) return; 26616 26617 var state = this._processLabelText(ctx, selected, hover, this.elementOptions.label); 26618 26619 if (this.fontOptions.minWdt > 0 && state.width < this.fontOptions.minWdt) { 26620 state.width = this.fontOptions.minWdt; 26621 } 26622 26623 this.size.labelHeight = state.height; 26624 if (this.fontOptions.minHgt > 0 && state.height < this.fontOptions.minHgt) { 26625 state.height = this.fontOptions.minHgt; 26626 } 26627 26628 this.lines = state.lines; 26629 this.lineCount = state.lines.length; 26630 this.size.width = state.width; 26631 this.size.height = state.height; 26632 this.selectedState = selected; 26633 this.hoverState = hover; 26634 26635 this.labelDirty = false; 26636 } 26637 26638 /** 26639 * Check if this label is visible 26640 * 26641 * @return {boolean} true if this label will be show, false otherwise 26642 */ 26643 26644 }, { 26645 key: 'visible', 26646 value: function visible() { 26647 if (this.size.width === 0 || this.size.height === 0 || this.elementOptions.label === undefined) { 26648 return false; // nothing to display 26649 } 26650 26651 var viewFontSize = this.fontOptions.size * this.body.view.scale; 26652 if (viewFontSize < this.elementOptions.scaling.label.drawThreshold - 1) { 26653 return false; // Too small or too far away to show 26654 } 26655 26656 return true; 26657 } 26658 }], [{ 26659 key: 'parseFontString', 26660 value: function parseFontString(outOptions, inOptions) { 26661 if (!inOptions || typeof inOptions !== 'string') return false; 26662 26663 var newOptionsArray = inOptions.split(" "); 26664 26665 outOptions.size = newOptionsArray[0].replace("px", ''); 26666 outOptions.face = newOptionsArray[1]; 26667 outOptions.color = newOptionsArray[2]; 26668 26669 return true; 26670 } 26671 }]); 26672 return Label; 26673}(); 26674 26675exports['default'] = Label; 26676 26677/***/ }), 26678/* 118 */ 26679/***/ (function(module, exports, __webpack_require__) { 26680 26681"use strict"; 26682 26683 26684Object.defineProperty(exports, "__esModule", { 26685 value: true 26686}); 26687 26688var _slicedToArray2 = __webpack_require__(30); 26689 26690var _slicedToArray3 = _interopRequireDefault(_slicedToArray2); 26691 26692var _classCallCheck2 = __webpack_require__(0); 26693 26694var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); 26695 26696var _createClass2 = __webpack_require__(1); 26697 26698var _createClass3 = _interopRequireDefault(_createClass2); 26699 26700function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } 26701 26702var util = __webpack_require__(2); 26703var EndPoints = __webpack_require__(119)["default"]; 26704 26705/** 26706 * The Base Class for all edges. 26707 * 26708 */ 26709 26710var EdgeBase = function () { 26711 /** 26712 * @param {Object} options 26713 * @param {Object} body 26714 * @param {Label} labelModule 26715 */ 26716 function EdgeBase(options, body, labelModule) { 26717 (0, _classCallCheck3["default"])(this, EdgeBase); 26718 26719 this.body = body; 26720 this.labelModule = labelModule; 26721 this.options = {}; 26722 this.setOptions(options); 26723 this.colorDirty = true; 26724 this.color = {}; 26725 this.selectionWidth = 2; 26726 this.hoverWidth = 1.5; 26727 this.fromPoint = this.from; 26728 this.toPoint = this.to; 26729 } 26730 26731 /** 26732 * Connects a node to itself 26733 */ 26734 26735 26736 (0, _createClass3["default"])(EdgeBase, [{ 26737 key: "connect", 26738 value: function connect() { 26739 this.from = this.body.nodes[this.options.from]; 26740 this.to = this.body.nodes[this.options.to]; 26741 } 26742 26743 /** 26744 * 26745 * @returns {boolean} always false 26746 */ 26747 26748 }, { 26749 key: "cleanup", 26750 value: function cleanup() { 26751 return false; 26752 } 26753 26754 /** 26755 * 26756 * @param {Object} options 26757 */ 26758 26759 }, { 26760 key: "setOptions", 26761 value: function setOptions(options) { 26762 this.options = options; 26763 this.from = this.body.nodes[this.options.from]; 26764 this.to = this.body.nodes[this.options.to]; 26765 this.id = this.options.id; 26766 } 26767 26768 /** 26769 * Redraw a edge as a line 26770 * Draw this edge in the given canvas 26771 * The 2d context of a HTML canvas can be retrieved by canvas.getContext("2d"); 26772 * 26773 * @param {CanvasRenderingContext2D} ctx 26774 * @param {Array} values 26775 * @param {boolean} selected 26776 * @param {boolean} hover 26777 * @param {Node} viaNode 26778 * @private 26779 */ 26780 26781 }, { 26782 key: "drawLine", 26783 value: function drawLine(ctx, values, selected, hover, viaNode) { 26784 // set style 26785 ctx.strokeStyle = this.getColor(ctx, values, selected, hover); 26786 ctx.lineWidth = values.width; 26787 26788 if (values.dashes !== false) { 26789 this._drawDashedLine(ctx, values, viaNode); 26790 } else { 26791 this._drawLine(ctx, values, viaNode); 26792 } 26793 } 26794 26795 /** 26796 * 26797 * @param {CanvasRenderingContext2D} ctx 26798 * @param {Array} values 26799 * @param {Node} viaNode 26800 * @param {{x: number, y: number}} [fromPoint] 26801 * @param {{x: number, y: number}} [toPoint] 26802 * @private 26803 */ 26804 26805 }, { 26806 key: "_drawLine", 26807 value: function _drawLine(ctx, values, viaNode, fromPoint, toPoint) { 26808 if (this.from != this.to) { 26809 // draw line 26810 this._line(ctx, values, viaNode, fromPoint, toPoint); 26811 } else { 26812 var _getCircleData2 = this._getCircleData(ctx), 26813 _getCircleData3 = (0, _slicedToArray3["default"])(_getCircleData2, 3), 26814 x = _getCircleData3[0], 26815 y = _getCircleData3[1], 26816 radius = _getCircleData3[2]; 26817 26818 this._circle(ctx, values, x, y, radius); 26819 } 26820 } 26821 26822 /** 26823 * 26824 * @param {CanvasRenderingContext2D} ctx 26825 * @param {Array} values 26826 * @param {Node} viaNode 26827 * @param {{x: number, y: number}} [fromPoint] TODO: Remove in next major release 26828 * @param {{x: number, y: number}} [toPoint] TODO: Remove in next major release 26829 * @private 26830 */ 26831 26832 }, { 26833 key: "_drawDashedLine", 26834 value: function _drawDashedLine(ctx, values, viaNode, fromPoint, toPoint) { 26835 // eslint-disable-line no-unused-vars 26836 ctx.lineCap = 'round'; 26837 var pattern = [5, 5]; 26838 if (Array.isArray(values.dashes) === true) { 26839 pattern = values.dashes; 26840 } 26841 26842 // only firefox and chrome support this method, else we use the legacy one. 26843 if (ctx.setLineDash !== undefined) { 26844 ctx.save(); 26845 26846 // set dash settings for chrome or firefox 26847 ctx.setLineDash(pattern); 26848 ctx.lineDashOffset = 0; 26849 26850 // draw the line 26851 if (this.from != this.to) { 26852 // draw line 26853 this._line(ctx, values, viaNode); 26854 } else { 26855 var _getCircleData4 = this._getCircleData(ctx), 26856 _getCircleData5 = (0, _slicedToArray3["default"])(_getCircleData4, 3), 26857 x = _getCircleData5[0], 26858 y = _getCircleData5[1], 26859 radius = _getCircleData5[2]; 26860 26861 this._circle(ctx, values, x, y, radius); 26862 } 26863 26864 // restore the dash settings. 26865 ctx.setLineDash([0]); 26866 ctx.lineDashOffset = 0;
26867 ctx.restore(); 26868 } else { 26869 // unsupporting smooth lines 26870 if (this.from != this.to) { 26871 // draw line 26872 ctx.dashedLine(this.from.x, this.from.y, this.to.x, this.to.y, pattern); 26873 } else { 26874 var _getCircleData6 = this._getCircleData(ctx), 26875 _getCircleData7 = (0, _slicedToArray3["default"])(_getCircleData6, 3), 26876 _x = _getCircleData7[0], 26877 _y = _getCircleData7[1], 26878 _radius = _getCircleData7[2]; 26879 26880 this._circle(ctx, values, _x, _y, _radius); 26881 } 26882 // draw shadow if enabled 26883 this.enableShadow(ctx, values); 26884 26885 ctx.stroke(); 26886 26887 // disable shadows for other elements. 26888 this.disableShadow(ctx, values); 26889 } 26890 } 26891 26892 /** 26893 * 26894 * @param {Node} nearNode 26895 * @param {CanvasRenderingContext2D} ctx 26896 * @param {Object} options 26897 * @returns {{x: number, y: number}} 26898 */ 26899 26900 }, { 26901 key: "findBorderPosition", 26902 value: function findBorderPosition(nearNode, ctx, options) { 26903 if (this.from != this.to) { 26904 return this._findBorderPosition(nearNode, ctx, options); 26905 } else { 26906 return this._findBorderPositionCircle(nearNode, ctx, options); 26907 } 26908 } 26909 26910 /** 26911 * 26912 * @param {CanvasRenderingContext2D} ctx 26913 * @returns {{from: ({x: number, y: number, t: number}|*), to: ({x: number, y: number, t: number}|*)}} 26914 */ 26915 26916 }, { 26917 key: "findBorderPositions", 26918 value: function findBorderPositions(ctx) { 26919 var from = {}; 26920 var to = {}; 26921 if (this.from != this.to) { 26922 from = this._findBorderPosition(this.from, ctx); 26923 to = this._findBorderPosition(this.to, ctx); 26924 } else { 26925 var _getCircleData$slice = this._getCircleData(ctx).slice(0, 2), 26926 _getCircleData$slice2 = (0, _slicedToArray3["default"])(_getCircleData$slice, 2), 26927 x = _getCircleData$slice2[0], 26928 y = _getCircleData$slice2[1]; 26929 26930 from = this._findBorderPositionCircle(this.from, ctx, { x: x, y: y, low: 0.25, high: 0.6, direction: -1 }); 26931 to = this._findBorderPositionCircle(this.from, ctx, { x: x, y: y, low: 0.6, high: 0.8, direction: 1 }); 26932 } 26933 return { from: from, to: to }; 26934 } 26935 26936 /** 26937 * 26938 * @param {CanvasRenderingContext2D} ctx 26939 * @returns {Array.<number>} x, y, radius 26940 * @private 26941 */ 26942 26943 }, { 26944 key: "_getCircleData", 26945 value: function _getCircleData(ctx) { 26946 var x = void 0, 26947 y = void 0; 26948 var node = this.from; 26949 var radius = this.options.selfReferenceSize; 26950 26951 if (ctx !== undefined) { 26952 if (node.shape.width === undefined) { 26953 node.shape.resize(ctx); 26954 } 26955 } 26956 26957 // get circle coordinates 26958 if (node.shape.width > node.shape.height) { 26959 x = node.x + node.shape.width * 0.5; 26960 y = node.y - radius; 26961 } else { 26962 x = node.x + radius; 26963 y = node.y - node.shape.height * 0.5; 26964 } 26965 return [x, y, radius]; 26966 } 26967 26968 /** 26969 * Get a point on a circle 26970 * @param {number} x 26971 * @param {number} y 26972 * @param {number} radius 26973 * @param {number} percentage - Value between 0 (line start) and 1 (line end) 26974 * @return {Object} point 26975 * @private 26976 */ 26977 26978 }, { 26979 key: "_pointOnCircle", 26980 value: function _pointOnCircle(x, y, radius, percentage) { 26981 var angle = percentage * 2 * Math.PI; 26982 return { 26983 x: x + radius * Math.cos(angle), 26984 y: y - radius * Math.sin(angle) 26985 }; 26986 } 26987 26988 /** 26989 * This function uses binary search to look for the point where the circle crosses the border of the node. 26990 * @param {Node} node 26991 * @param {CanvasRenderingContext2D} ctx 26992 * @param {Object} options 26993 * @returns {*} 26994 * @private 26995 */ 26996 26997 }, { 26998 key: "_findBorderPositionCircle", 26999 value: function _findBorderPositionCircle(node, ctx, options) { 27000 var x = options.x; 27001 var y = options.y; 27002 var low = options.low; 27003 var high = options.high; 27004 var direction = options.direction; 27005 27006 var maxIterations = 10; 27007 var iteration = 0; 27008 var radius = this.options.selfReferenceSize; 27009 var pos = void 0, 27010 angle = void 0, 27011 distanceToBorder = void 0, 27012 distanceToPoint = void 0, 27013 difference = void 0; 27014 var threshold = 0.05; 27015 var middle = (low + high) * 0.5; 27016 27017 while (low <= high && iteration < maxIterations) { 27018 middle = (low + high) * 0.5; 27019 27020 pos = this._pointOnCircle(x, y, radius, middle); 27021 angle = Math.atan2(node.y - pos.y, node.x - pos.x); 27022 distanceToBorder = node.distanceToBorder(ctx, angle); 27023 distanceToPoint = Math.sqrt(Math.pow(pos.x - node.x, 2) + Math.pow(pos.y - node.y, 2)); 27024 difference = distanceToBorder - distanceToPoint; 27025 if (Math.abs(difference) < threshold) { 27026 break; // found 27027 } else if (difference > 0) { 27028 // distance to nodes is larger than distance to border --> t needs to be bigger if we're looking at the to node. 27029 if (direction > 0) { 27030 low = middle; 27031 } else { 27032 high = middle; 27033 } 27034 } else { 27035 if (direction > 0) { 27036 high = middle; 27037 } else { 27038 low = middle; 27039 } 27040 } 27041 iteration++; 27042 } 27043 pos.t = middle; 27044 27045 return pos; 27046 } 27047 27048 /** 27049 * Get the line width of the edge. Depends on width and whether one of the 27050 * connected nodes is selected. 27051 * @param {boolean} selected 27052 * @param {boolean} hover 27053 * @returns {number} width 27054 * @private 27055 */ 27056 27057 }, { 27058 key: "getLineWidth", 27059 value: function getLineWidth(selected, hover) { 27060 if (selected === true) { 27061 return Math.max(this.selectionWidth, 0.3 / this.body.view.scale); 27062 } else { 27063 if (hover === true) { 27064 return Math.max(this.hoverWidth, 0.3 / this.body.view.scale); 27065 } else { 27066 return Math.max(this.options.width, 0.3 / this.body.view.scale); 27067 } 27068 } 27069 } 27070 27071 /** 27072 * 27073 * @param {CanvasRenderingContext2D} ctx 27074 * @param {{toArrow: boolean, toArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), toArrowType: *, middleArrow: boolean, middleArrowScale: (number|allOptions.edges.arrows.middle.scaleFactor|{number}|Array), middleArrowType: (allOptions.edges.arrows.middle.type|{string}|string|*), fromArrow: boolean, fromArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), fromArrowType: *, arrowStrikethrough: (*|boolean|allOptions.edges.arrowStrikethrough|{boolean}), color: undefined, inheritsColor: (string|string|string|allOptions.edges.color.inherit|{string, boolean}|Array|*), opacity: *, hidden: *, length: *, shadow: *, shadowColor: *, shadowSize: *, shadowX: *, shadowY: *, dashes: (*|boolean|Array|allOptions.edges.dashes|{boolean, array}), width: *}} values 27075 * @param {boolean} selected - Unused 27076 * @param {boolean} hover - Unused 27077 * @returns {string} 27078 */ 27079 27080 }, { 27081 key: "getColor", 27082 value: function getColor(ctx, values, selected, hover) { 27083 // eslint-disable-line no-unused-vars 27084 if (values.inheritsColor !== false) { 27085 // when this is a loop edge, just use the 'from' method 27086 if (values.inheritsColor === 'both' && this.from.id !== this.to.id) { 27087 var grd = ctx.createLinearGradient(this.from.x, this.from.y, this.to.x, this.to.y); 27088 var fromColor = void 0, 27089 toColor = void 0; 27090 fromColor = this.from.options.color.highlight.border; 27091 toColor = this.to.options.color.highlight.border; 27092 27093 if (this.from.selected === false && this.to.selected === false) { 27094 fromColor = util.overrideOpacity(this.from.options.color.border, values.opacity); 27095 toColor = util.overrideOpacity(this.to.options.color.border, values.opacity); 27096 } else if (this.from.selected === true && this.to.selected === false) { 27097 toColor = this.to.options.color.border; 27098 } else if (this.from.selected === false && this.to.selected === true) { 27099 fromColor = this.from.options.color.border; 27100 } 27101 grd.addColorStop(0, fromColor); 27102 grd.addColorStop(1, toColor); 27103 27104 // -------------------- this returns -------------------- // 27105 return grd; 27106 } 27107 27108 if (values.inheritsColor === "to") { 27109 return util.overrideOpacity(this.to.options.color.border, values.opacity); 27110 } else { 27111 // "from" 27112 return util.overrideOpacity(this.from.options.color.border, values.opacity); 27113 } 27114 } else { 27115 return util.overrideOpacity(values.color, values.opacity); 27116 } 27117 } 27118 27119 /** 27120 * Draw a line from a node to itself, a circle 27121 * 27122 * @param {CanvasRenderingContext2D} ctx 27123 * @param {Array} values 27124 * @param {number} x 27125 * @param {number} y 27126 * @param {number} radius 27127 * @private 27128 */ 27129 27130 }, { 27131 key: "_circle", 27132 value: function _circle(ctx, values, x, y, radius) { 27133 // draw shadow if enabled 27134 this.enableShadow(ctx, values); 27135 27136 // draw a circle 27137 ctx.beginPath(); 27138 ctx.arc(x, y, radius, 0, 2 * Math.PI, false); 27139 ctx.stroke(); 27140 27141 // disable shadows for other elements. 27142 this.disableShadow(ctx, values); 27143 } 27144 27145 /** 27146 * Calculate the distance between a point (x3,y3) and a line segment from (x1,y1) to (x2,y2). 27147 * (x3,y3) is the point. 27148 * 27149 * http://stackoverflow.com/questions/849211/shortest-distancae-between-a-point-and-a-line-segment 27150 * 27151 * @param {number} x1 27152 * @param {number} y1 27153 * @param {number} x2 27154 * @param {number} y2 27155 * @param {number} x3 27156 * @param {number} y3 27157 * @param {Node} via 27158 * @param {Array} values 27159 * @returns {number} 27160 */ 27161 27162 }, { 27163 key: "getDistanceToEdge", 27164 value: function getDistanceToEdge(x1, y1, x2, y2, x3, y3, via, values) { 27165 // eslint-disable-line no-unused-vars 27166 var returnValue = 0; 27167 if (this.from != this.to) { 27168 returnValue = this._getDistanceToEdge(x1, y1, x2, y2, x3, y3, via); 27169 } else { 27170 var _getCircleData8 = this._getCircleData(undefined), 27171 _getCircleData9 = (0, _slicedToArray3["default"])(_getCircleData8, 3), 27172 x = _getCircleData9[0], 27173 y = _getCircleData9[1], 27174 radius = _getCircleData9[2]; 27175 27176 var dx = x - x3; 27177 var dy = y - y3; 27178 returnValue = Math.abs(Math.sqrt(dx * dx + dy * dy) - radiu
27178s); 27179 } 27180 27181 return returnValue; 27182 } 27183 27184 /** 27185 * 27186 * @param {number} x1 27187 * @param {number} y1 27188 * @param {number} x2 27189 * @param {number} y2 27190 * @param {number} x3 27191 * @param {number} y3 27192 * @returns {number} 27193 * @private 27194 */ 27195 27196 }, { 27197 key: "_getDistanceToLine", 27198 value: function _getDistanceToLine(x1, y1, x2, y2, x3, y3) { 27199 var px = x2 - x1; 27200 var py = y2 - y1; 27201 var something = px * px + py * py; 27202 var u = ((x3 - x1) * px + (y3 - y1) * py) / something; 27203 27204 if (u > 1) { 27205 u = 1; 27206 } else if (u < 0) { 27207 u = 0; 27208 } 27209 27210 var x = x1 + u * px; 27211 var y = y1 + u * py; 27212 var dx = x - x3; 27213 var dy = y - y3; 27214 27215 //# Note: If the actual distance does not matter, 27216 //# if you only want to compare what this function 27217 //# returns to other results of this function, you 27218 //# can just return the squared distance instead 27219 //# (i.e. remove the sqrt) to gain a little performance 27220 27221 return Math.sqrt(dx * dx + dy * dy); 27222 } 27223 27224 /** 27225 * @param {CanvasRenderingContext2D} ctx 27226 * @param {string} position 27227 * @param {Node} viaNode 27228 * @param {boolean} selected 27229 * @param {boolean} hover 27230 * @param {Array} values 27231 * @returns {{point: *, core: {x: number, y: number}, angle: *, length: number, type: *}} 27232 */ 27233 27234 }, { 27235 key: "getArrowData", 27236 value: function getArrowData(ctx, position, viaNode, selected, hover, values) { 27237 // set lets 27238 var angle = void 0; 27239 var arrowPoint = void 0; 27240 var node1 = void 0; 27241 var node2 = void 0; 27242 var guideOffset = void 0; 27243 var scaleFactor = void 0; 27244 var type = void 0; 27245 var lineWidth = values.width; 27246 27247 if (position === 'from') { 27248 node1 = this.from; 27249 node2 = this.to; 27250 guideOffset = 0.1; 27251 scaleFactor = values.fromArrowScale; 27252 type = values.fromArrowType; 27253 } else if (position === 'to') { 27254 node1 = this.to; 27255 node2 = this.from; 27256 guideOffset = -0.1; 27257 scaleFactor = values.toArrowScale; 27258 type = values.toArrowType; 27259 } else { 27260 node1 = this.to; 27261 node2 = this.from; 27262 scaleFactor = values.middleArrowScale; 27263 type = values.middleArrowType; 27264 } 27265 27266 // if not connected to itself 27267 if (node1 != node2) { 27268 if (position !== 'middle') { 27269 // draw arrow head 27270 if (this.options.smooth.enabled === true) { 27271 arrowPoint = this.findBorderPosition(node1, ctx, { via: viaNode }); 27272 var guidePos = this.getPoint(Math.max(0.0, Math.min(1.0, arrowPoint.t + guideOffset)), viaNode); 27273 angle = Math.atan2(arrowPoint.y - guidePos.y, arrowPoint.x - guidePos.x); 27274 } else { 27275 angle = Math.atan2(node1.y - node2.y, node1.x - node2.x); 27276 arrowPoint = this.findBorderPosition(node1, ctx); 27277 } 27278 } else { 27279 angle = Math.atan2(node1.y - node2.y, node1.x - node2.x); 27280 arrowPoint = this.getPoint(0.5, viaNode); // this is 0.6 to account for the size of the arrow. 27281 } 27282 } else { 27283 // draw circle 27284 var _getCircleData10 = this._getCircleData(ctx), 27285 _getCircleData11 = (0, _slicedToArray3["default"])(_getCircleData10, 3), 27286 x = _getCircleData11[0], 27287 y = _getCircleData11[1], 27288 radius = _getCircleData11[2]; 27289 27290 if (position === 'from') { 27291 arrowPoint = this.findBorderPosition(this.from, ctx, { x: x, y: y, low: 0.25, high: 0.6, direction: -1 }); 27292 angle = arrowPoint.t * -2 * Math.PI + 1.5 * Math.PI + 0.1 * Math.PI; 27293 } else if (position === 'to') { 27294 arrowPoint = this.findBorderPosition(this.from, ctx, { x: x, y: y, low: 0.6, high: 1.0, direction: 1 }); 27295 angle = arrowPoint.t * -2 * Math.PI + 1.5 * Math.PI - 1.1 * Math.PI; 27296 } else { 27297 arrowPoint = this._pointOnCircle(x, y, radius, 0.175); 27298 angle = 3.9269908169872414; // === 0.175 * -2 * Math.PI + 1.5 * Math.PI + 0.1 * Math.PI; 27299 } 27300 } 27301 27302 if (position === 'middle' && scaleFactor < 0) lineWidth *= -1; // reversed middle arrow 27303 var length = 15 * scaleFactor + 3 * lineWidth; // 3* lineWidth is the width of the edge. 27304 27305 var xi = arrowPoint.x - length * 0.9 * Math.cos(angle); 27306 var yi = arrowPoint.y - length * 0.9 * Math.sin(angle); 27307 var arrowCore = { x: xi, y: yi }; 27308 27309 return { point: arrowPoint, core: arrowCore, angle: angle, length: length, type: type }; 27310 } 27311 27312 /** 27313 * 27314 * @param {CanvasRenderingContext2D} ctx 27315 * @param {{toArrow: boolean, toArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), toArrowType: *, middleArrow: boolean, middleArrowScale: (number|allOptions.edges.arrows.middle.scaleFactor|{number}|Array), middleArrowType: (allOptions.edges.arrows.middle.type|{string}|string|*), fromArrow: boolean, fromArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), fromArrowType: *, arrowStrikethrough: (*|boolean|allOptions.edges.arrowStrikethrough|{boolean}), color: undefined, inheritsColor: (string|string|string|allOptions.edges.color.inherit|{string, boolean}|Array|*), opacity: *, hidden: *, length: *, shadow: *, shadowColor: *, shadowSize: *, shadowX: *, shadowY: *, dashes: (*|boolean|Array|allOptions.edges.dashes|{boolean, array}), width: *}} values 27316 * @param {boolean} selected 27317 * @param {boolean} hover 27318 * @param {Object} arrowData 27319 */ 27320 27321 }, { 27322 key: "drawArrowHead", 27323 value: function drawArrowHead(ctx, values, selected, hover, arrowData) { 27324 // set style 27325 ctx.strokeStyle = this.getColor(ctx, values, selected, hover); 27326 ctx.fillStyle = ctx.strokeStyle; 27327 ctx.lineWidth = values.width; 27328 27329 EndPoints.draw(ctx, arrowData); 27330 27331 // draw shadow if enabled 27332 this.enableShadow(ctx, values); 27333 ctx.fill(); 27334 // disable shadows for other elements. 27335 this.disableShadow(ctx, values); 27336 } 27337 27338 /** 27339 * 27340 * @param {CanvasRenderingContext2D} ctx 27341 * @param {{toArrow: boolean, toArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), toArrowType: *, middleArrow: boolean, middleArrowScale: (number|allOptions.edges.arrows.middle.scaleFactor|{number}|Array), middleArrowType: (allOptions.edges.arrows.middle.type|{string}|string|*), fromArrow: boolean, fromArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), fromArrowType: *, arrowStrikethrough: (*|boolean|allOptions.edges.arrowStrikethrough|{boolean}), color: undefined, inheritsColor: (string|string|string|allOptions.edges.color.inherit|{string, boolean}|Array|*), opacity: *, hidden: *, length: *, shadow: *, shadowColor: *, shadowSize: *, shadowX: *, shadowY: *, dashes: (*|boolean|Array|allOptions.edges.dashes|{boolean, array}), width: *}} values 27342 */ 27343 27344 }, { 27345 key: "enableShadow", 27346 value: function enableShadow(ctx, values) { 27347 if (values.shadow === true) { 27348 ctx.shadowColor = values.shadowColor; 27349 ctx.shadowBlur = values.shadowSize; 27350 ctx.shadowOffsetX = values.shadowX; 27351 ctx.shadowOffsetY = values.shadowY; 27352 } 27353 } 27354 27355 /** 27356 * 27357 * @param {CanvasRenderingContext2D} ctx 27358 * @param {{toArrow: boolean, toArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), toArrowType: *, middleArrow: boolean, middleArrowScale: (number|allOptions.edges.arrows.middle.scaleFactor|{number}|Array), middleArrowType: (allOptions.edges.arrows.middle.type|{string}|string|*), fromArrow: boolean, fromArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), fromArrowType: *, arrowStrikethrough: (*|boolean|allOptions.edges.arrowStrikethrough|{boolean}), color: undefined, inheritsColor: (string|string|string|allOptions.edges.color.inherit|{string, boolean}|Array|*), opacity: *, hidden: *, length: *, shadow: *, shadowColor: *, shadowSize: *, shadowX: *, shadowY: *, dashes: (*|boolean|Array|allOptions.edges.dashes|{boolean, array}), width: *}} values 27359 */ 27360 27361 }, { 27362 key: "disableShadow", 27363 value: function disableShadow(ctx, values) { 27364 if (values.shadow === true) { 27365 ctx.shadowColor = 'rgba(0,0,0,0)'; 27366 ctx.shadowBlur = 0; 27367 ctx.shadowOffsetX = 0; 27368 ctx.shadowOffsetY = 0; 27369 } 27370 } 27371 }]); 27372 return EdgeBase; 27373}(); 27374 27375exports["default"] = EdgeBase; 27376 27377/***/ }), 27378/* 119 */ 27379/***/ (function(module, exports, __webpack_require__) { 27380 27381"use strict"; 27382 27383 27384Object.defineProperty(exports, "__esModule", { 27385 value: true 27386}); 27387 27388var _getPrototypeOf = __webpack_require__(3); 27389 27390var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf); 27391 27392var _possibleConstructorReturn2 = __webpack_require__(4); 27393 27394var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2); 27395 27396var _inherits2 = __webpack_require__(5); 27397 27398var _inherits3 = _interopRequireDefault(_inherits2); 27399 27400var _classCallCheck2 = __webpack_require__(0); 27401 27402var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); 27403 27404var _createClass2 = __webpack_require__(1); 27405 27406var _createClass3 = _interopRequireDefault(_createClass2); 27407 27408function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 27409 27410/** ============================================================================ 27411 * Location of all the endpoint drawing routines. 27412 * 27413 * Every endpoint has its own drawing routine, which contains an endpoint definition. 27414 * 27415 * The endpoint definitions must have the following properies: 27416 * 27417 * - (0,0) is the connection point to the node it attaches to 27418 * - The endpoints are orientated to the positive x-direction 27419 * - The length of the endpoint is at most 1 27420 * 27421 * As long as the endpoint classes remain simple and not too numerous, they will 27422 * be contained within this module. 27423 * All classes here except `EndPoints` should be considered as private to this module. 27424 * 27425 * ----------------------------------------------------------------------------- 27426 * ### Further Actions 27427 * 27428 * After adding a new endpoint here, you also need to do the following things: 27429 * 27430 * - Add the new endpoint name to `network/options.js` in array `endPoints`. 27431 * - Add the new endpoint name to the documentation. 27432 * Scan for 'arrows.to.type` and add it to the description. 27433 * - Add the endpoint to the examples. At the very least, add it to example 27434 * `edgeStyles/arrowTypes`.
27435 * ============================================================================= */ 27436 27437// NOTE: When a typedef is isolated in a separate comment block, an actual description is generated for it, 27438// using the rest of the commenting in the code block. Usage of typedef in other comments then 27439// link to there. TIL. 27440// 27441// Also noteworthy, all typedef's set up in this manner are collected in a single, global page 'global.html'. 27442// In other words, it doesn't matter *where* the typedef's are defined in the code. 27443// 27444// 27445// TODO: add descriptive commenting to given typedef's 27446 27447/** 27448 * @typedef {{type:string, point:Point, angle:number, length:number}} ArrowData 27449 * 27450 * Object containing instantiation data for a given endpoint. 27451 */ 27452 27453/** 27454 * @typedef {{x:number, y:number}} Point 27455 * 27456 * A point in view-coordinates. 27457 */ 27458 27459/** 27460 * Common methods for endpoints 27461 * 27462 * @class 27463 */ 27464var EndPoint = function () { 27465 function EndPoint() { 27466 (0, _classCallCheck3['default'])(this, EndPoint); 27467 } 27468 27469 (0, _createClass3['default'])(EndPoint, null, [{ 27470 key: 'transform', 27471 27472 27473 /** 27474 * Apply transformation on points for display. 27475 * 27476 * The following is done: 27477 * - rotate by the specified angle 27478 * - multiply the (normalized) coordinates by the passed length 27479 * - offset by the target coordinates 27480 * 27481 * @param {Array<Point>} points 27482 * @param {ArrowData} arrowData 27483 * @static 27484 */ 27485 value: function transform(points, arrowData) { 27486 if (!(points instanceof Array)) { 27487 points = [points]; 27488 } 27489 27490 var x = arrowData.point.x; 27491 var y = arrowData.point.y; 27492 var angle = arrowData.angle; 27493 var length = arrowData.length; 27494 27495 for (var i = 0; i < points.length; ++i) { 27496 var p = points[i]; 27497 var xt = p.x * Math.cos(angle) - p.y * Math.sin(angle); 27498 var yt = p.x * Math.sin(angle) + p.y * Math.cos(angle); 27499 27500 p.x = x + length * xt; 27501 p.y = y + length * yt; 27502 } 27503 } 27504 27505 /** 27506 * Draw a closed path using the given real coordinates. 27507 * 27508 * @param {CanvasRenderingContext2D} ctx 27509 * @param {Array.<Point>} points 27510 * @static 27511 */ 27512 27513 }, { 27514 key: 'drawPath', 27515 value: function drawPath(ctx, points) { 27516 ctx.beginPath(); 27517 ctx.moveTo(points[0].x, points[0].y); 27518 for (var i = 1; i < points.length; ++i) { 27519 ctx.lineTo(points[i].x, points[i].y); 27520 } 27521 ctx.closePath(); 27522 } 27523 }]); 27524 return EndPoint; 27525}(); 27526 27527/** 27528 * Drawing methods for the arrow endpoint. 27529 * @extends EndPoint 27530 */ 27531 27532 27533var Arrow = function (_EndPoint) { 27534 (0, _inherits3['default'])(Arrow, _EndPoint); 27535 27536 function Arrow() { 27537 (0, _classCallCheck3['default'])(this, Arrow); 27538 return (0, _possibleConstructorReturn3['default'])(this, (Arrow.__proto__ || (0, _getPrototypeOf2['default'])(Arrow)).apply(this, arguments)); 27539 } 27540 27541 (0, _createClass3['default'])(Arrow, null, [{ 27542 key: 'draw', 27543 27544 27545 /** 27546 * Draw this shape at the end of a line. 27547 * 27548 * @param {CanvasRenderingContext2D} ctx 27549 * @param {ArrowData} arrowData 27550 * @static 27551 */ 27552 value: function draw(ctx, arrowData) { 27553 // Normalized points of closed path, in the order that they should be drawn. 27554 // (0, 0) is the attachment point, and the point around which should be rotated 27555 var points = [{ x: 0, y: 0 }, { x: -1, y: 0.3 }, { x: -0.9, y: 0 }, { x: -1, y: -0.3 }]; 27556 27557 EndPoint.transform(points, arrowData); 27558 EndPoint.drawPath(ctx, points); 27559 } 27560 }]); 27561 return Arrow; 27562}(EndPoint); 27563 27564/** 27565 * Drawing methods for the circle endpoint. 27566 */ 27567 27568 27569var Circle = function () { 27570 function Circle() { 27571 (0, _classCallCheck3['default'])(this, Circle); 27572 } 27573 27574 (0, _createClass3['default'])(Circle, null, [{ 27575 key: 'draw', 27576 27577 27578 /** 27579 * Draw this shape at the end of a line. 27580 * 27581 * @param {CanvasRenderingContext2D} ctx 27582 * @param {ArrowData} arrowData 27583 * @static 27584 */ 27585 value: function draw(ctx, arrowData) { 27586 var point = { x: -0.4, y: 0 }; 27587 27588 EndPoint.transform(point, arrowData); 27589 ctx.circle(point.x, point.y, arrowData.length * 0.4); 27590 } 27591 }]); 27592 return Circle; 27593}(); 27594 27595/** 27596 * Drawing methods for the bar endpoint. 27597 */ 27598 27599 27600var Bar = function () { 27601 function Bar() { 27602 (0, _classCallCheck3['default'])(this, Bar); 27603 } 27604 27605 (0, _createClass3['default'])(Bar, null, [{ 27606 key: 'draw', 27607 27608 27609 /** 27610 * Draw this shape at the end of a line. 27611 * 27612 * @param {CanvasRenderingContext2D} ctx 27613 * @param {ArrowData} arrowData 27614 * @static 27615 */ 27616 value: function draw(ctx, arrowData) { 27617 /* 27618 var points = [ 27619 {x:0, y:0.5}, 27620 {x:0, y:-0.5} 27621 ]; 27622 27623 EndPoint.transform(points, arrowData); 27624 ctx.beginPath(); 27625 ctx.moveTo(points[0].x, points[0].y); 27626 ctx.lineTo(points[1].x, points[1].y); 27627 ctx.stroke(); 27628 */ 27629 27630 var points = [{ x: 0, y: 0.5 }, { x: 0, y: -0.5 }, { x: -0.15, y: -0.5 }, { x: -0.15, y: 0.5 }]; 27631 27632 EndPoint.transform(points, arrowData); 27633 EndPoint.drawPath(ctx, points); 27634 } 27635 }]); 27636 return Bar; 27637}(); 27638 27639/** 27640 * Drawing methods for the endpoints. 27641 */ 27642 27643 27644var EndPoints = function () { 27645 function EndPoints() { 27646 (0, _classCallCheck3['default'])(this, EndPoints); 27647 } 27648 27649 (0, _createClass3['default'])(EndPoints, null, [{ 27650 key: 'draw', 27651 27652 27653 /** 27654 * Draw an endpoint 27655 * 27656 * @param {CanvasRenderingContext2D} ctx 27657 * @param {ArrowData} arrowData 27658 * @static 27659 */ 27660 value: function draw(ctx, arrowData) { 27661 var type; 27662 if (arrowData.type) { 27663 type = arrowData.type.toLowerCase(); 27664 } 27665 27666 switch (type) { 27667 case 'circle': 27668 Circle.draw(ctx, arrowData); 27669 break; 27670 case 'bar': 27671 Bar.draw(ctx, arrowData); 27672 break; 27673 case 'arrow': // fall-through 27674 default: 27675 Arrow.draw(ctx, arrowData); 27676 } 27677 } 27678 }]); 27679 return EndPoints; 27680}(); 27681 27682exports['default'] = EndPoints; 27683 27684/***/ }), 27685/* 120 */ 27686/***/ (function(module, exports, __webpack_require__) { 27687 27688"use strict"; 27689 27690 27691Object.defineProperty(exports, "__esModule", { 27692 value: true 27693}); 27694 27695var _classCallCheck2 = __webpack_require__(0); 27696 27697var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); 27698 27699var _createClass2 = __webpack_require__(1); 27700 27701var _createClass3 = _interopRequireDefault(_createClass2); 27702 27703function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } 27704 27705/** 27706 * Barnes Hut Solver 27707 */ 27708var BarnesHutSolver = function () { 27709 /** 27710 * @param {Object} body 27711 * @param {{physicsNodeIndices: Array, physicsEdgeIndices: Array, forces: {}, velocities: {}}} physicsBody 27712 * @param {Object} options 27713 */ 27714 function BarnesHutSolver(body, physicsBody, options) { 27715 (0, _classCallCheck3["default"])(this, BarnesHutSolver); 27716 27717 this.body = body; 27718 this.physicsBody = physicsBody; 27719 this.barnesHutTree; 27720 this.setOptions(options); 27721 this.randomSeed = 5; 27722 27723 // debug: show grid 27724 // this.body.emitter.on("afterDrawing", (ctx) => {this._debug(ctx,'#ff0000')}) 27725 } 27726 27727 /** 27728 * 27729 * @param {Object} options 27730 */ 27731 27732 27733 (0, _createClass3["default"])(BarnesHutSolver, [{ 27734 key: "setOptions", 27735 value: function setOptions(options) { 27736 this.options = options; 27737 this.thetaInversed = 1 / this.options.theta; 27738 27739 // if 1 then min distance = 0.5, if 0.5 then min distance = 0.5 + 0.5*node.shape.radiu
27739s 27740 this.overlapAvoidanceFactor = 1 - Math.max(0, Math.min(1, this.options.avoidOverlap)); 27741 } 27742 27743 /** 27744 * 27745 * @returns {number} random integer 27746 */ 27747 27748 }, { 27749 key: "seededRandom", 27750 value: function seededRandom() { 27751 var x = Math.sin(this.randomSeed++) * 10000; 27752 return x - Math.floor(x); 27753 } 27754 27755 /** 27756 * This function calculates the forces the nodes apply on each other based on a gravitational model. 27757 * The Barnes Hut method is used to speed up this N-body simulation. 27758 * 27759 * @private 27760 */ 27761 27762 }, { 27763 key: "solve", 27764 value: function solve() { 27765 if (this.options.gravitationalConstant !== 0 && this.physicsBody.physicsNodeIndices.length > 0) { 27766 var node = void 0; 27767 var nodes = this.body.nodes; 27768 var nodeIndices = this.physicsBody.physicsNodeIndices; 27769 var nodeCount = nodeIndices.length; 27770 27771 // create the tree 27772 var barnesHutTree = this._formBarnesHutTree(nodes, nodeIndices); 27773 27774 // for debugging 27775 this.barnesHutTree = barnesHutTree; 27776 27777 // place the nodes one by one recursively 27778 for (var i = 0; i < nodeCount; i++) { 27779 node = nodes[nodeIndices[i]]; 27780 if (node.options.mass > 0) { 27781 // starting with root is irrelevant, it never passes the BarnesHutSolver condition 27782 this._getForceContributions(barnesHutTree.root, node); 27783 } 27784 } 27785 } 27786 } 27787 27788 /** 27789 * @param {Object} parentBranch 27790 * @param {Node} node 27791 * @private 27792 */ 27793 27794 }, { 27795 key: "_getForceContributions", 27796 value: function _getForceContributions(parentBranch, node) { 27797 this._getForceContribution(parentBranch.children.NW, node); 27798 this._getForceContribution(parentBranch.children.NE, node); 27799 this._getForceContribution(parentBranch.children.SW, node); 27800 this._getForceContribution(parentBranch.children.SE, node); 27801 } 27802 27803 /** 27804 * This function traverses the barnesHutTree. It checks when it can approximate distant nodes with their center of mass. 27805 * If a region contains a single node, we check if it is not itself, then we apply the force. 27806 * 27807 * @param {Object} parentBranch 27808 * @param {Node} node 27809 * @private 27810 */ 27811 27812 }, { 27813 key: "_getForceContribution", 27814 value: function _getForceContribution(parentBranch, node) { 27815 // we get no force contribution from an empty region 27816 if (parentBranch.childrenCount > 0) { 27817 var dx = void 0, 27818 dy = void 0, 27819 distance = void 0; 27820 27821 // get the distance from the center of mass to the node. 27822 dx = parentBranch.centerOfMass.x - node.x; 27823 dy = parentBranch.centerOfMass.y - node.y; 27824 distance = Math.sqrt(dx * dx + dy * dy); 27825 27826 // BarnesHutSolver condition 27827 // original condition : s/d < theta = passed === d/s > 1/theta = passed 27828 // calcSize = 1/s --> d * 1/s > 1/theta = passed 27829 if (distance * parentBranch.calcSize > this.thetaInversed) { 27830 this._calculateForces(distance, dx, dy, node, parentBranch); 27831 } else { 27832 // Did not pass the condition, go into children if available 27833 if (parentBranch.childrenCount === 4) { 27834 this._getForceContributions(parentBranch, node); 27835 } else { 27836 // parentBranch must have only one node, if it was empty we wouldnt be here 27837 if (parentBranch.children.data.id != node.id) { 27838 // if it is not self 27839 this._calculateForces(distance, dx, dy, node, parentBranch); 27840 } 27841 } 27842 } 27843 } 27844 } 27845 27846 /** 27847 * Calculate the forces based on the distance. 27848 * 27849 * @param {number} distance 27850 * @param {number} dx 27851 * @param {number} dy 27852 * @param {Node} node 27853 * @param {Object} parentBranch 27854 * @private 27855 */ 27856 27857 }, { 27858 key: "_calculateForces", 27859 value: function _calculateForces(distance, dx, dy, node, parentBranch) { 27860 if (distance === 0) { 27861 distance = 0.1; 27862 dx = distance; 27863 } 27864 27865 if (this.overlapAvoidanceFactor < 1 && node.shape.radius) { 27866 distance = Math.max(0.1 + this.overlapAvoidanceFactor * node.shape.radiu
27866s, distance - node.shape.radius); 27867 } 27868 27869 // the dividing by the distance cubed instead of squared allows us to get the fx and fy components without sines and cosines 27870 // it is shorthand for gravityforce with distance squared and fx = dx/distance * gravityForce 27871 var gravityForce = this.options.gravitationalConstant * parentBranch.mass * node.options.mass / Math.pow(distance, 3); 27872 var fx = dx * gravityForce; 27873 var fy = dy * gravityForce; 27874 27875 this.physicsBody.forces[node.id].x += fx; 27876 this.physicsBody.forces[node.id].y += fy; 27877 } 27878 27879 /** 27880 * This function constructs the barnesHut tree recursively. It creates the root, splits it and starts placing the nodes. 27881 * 27882 * @param {Array.<Node>} nodes 27883 * @param {Array.<number>} nodeIndices 27884 * @returns {{root: {centerOfMass: {x: number, y: number}, mass: number, range: {minX: number, maxX: number, minY: number, maxY: number}, size: number, calcSize: number, children: {data: null}, maxWidth: number, level: number, childrenCount: number}}} BarnesHutTree 27885 * @private 27886 */ 27887 27888 }, { 27889 key: "_formBarnesHutTree", 27890 value: function _formBarnesHutTree(nodes, nodeIndices) { 27891 var node = void 0; 27892 var nodeCount = nodeIndices.length; 27893 27894 var minX = nodes[nodeIndices[0]].x; 27895 var minY = nodes[nodeIndices[0]].y; 27896 var maxX = nodes[nodeIndices[0]].x; 27897 var maxY = nodes[nodeIndices[0]].y; 27898 27899 // get the range of the nodes 27900 for (var i = 1; i < nodeCount; i++) { 27901 var _node = nodes[nodeIndices[i]]; 27902 var x = _node.x; 27903 var y = _node.y; 27904 if (_node.options.mass > 0) { 27905 if (x < minX) { 27906 minX = x; 27907 } 27908 if (x > maxX) { 27909 maxX = x; 27910 } 27911 if (y < minY) { 27912 minY = y; 27913 } 27914 if (y > maxY) { 27915 maxY = y; 27916 } 27917 } 27918 } 27919 // make the range a square 27920 var sizeDiff = Math.abs(maxX - minX) - Math.abs(maxY - minY); // difference between X and Y 27921 if (sizeDiff > 0) { 27922 minY -= 0.5 * sizeDiff; 27923 maxY += 0.5 * sizeDiff; 27924 } // xSize > ySize 27925 else { 27926 minX += 0.5 * sizeDiff; 27927 maxX -= 0.5 * sizeDiff; 27928 } // xSize < ySize 27929 27930 27931 var minimumTreeSize = 1e-5; 27932 var rootSize = Math.max(minimumTreeSize, Math.abs(maxX - minX)); 27933 var halfRootSize = 0.5 * rootSize; 27934 var centerX = 0.5 * (minX + maxX), 27935 centerY = 0.5 * (minY + maxY); 27936 27937 // construct the barnesHutTree 27938 var barnesHutTree = { 27939 root: { 27940 centerOfMass: { x: 0, y: 0 }, 27941 mass: 0, 27942 range: { 27943 minX: centerX - halfRootSize, maxX: centerX + halfRootSize, 27944 minY: centerY - halfRootSize, maxY: centerY + halfRootSize 27945 }, 27946 size: rootSize, 27947 calcSize: 1 / rootSize, 27948 children: { data: null }, 27949 maxWidth: 0, 27950 level: 0, 27951 childrenCount: 4 27952 } 27953 }; 27954 this._splitBranch(barnesHutTree.root); 27955 27956 // place the nodes one by one recursively 27957 for (var _i = 0; _i < nodeCount; _i++) { 27958 node = nodes[nodeIndices[_i]]; 27959 if (node.options.mass > 0) { 27960 this._placeInTree(barnesHutTree.root, node); 27961 } 27962 } 27963 27964 // make global 27965 return barnesHutTree; 27966 } 27967 27968 /** 27969 * this updates the mass of a branch. this is increased by adding a node. 27970 * 27971 * @param {Object} parentBranch 27972 * @param {Node} node 27973 * @private 27974 */ 27975 27976 }, { 27977 key: "_updateBranchMass", 27978 value: function _updateBranchMass(parentBranch, node) { 27979 var centerOfMass = parentBranch.centerOfMass; 27980 var totalMass = parentBranch.mass + node.options.mass; 27981 var totalMassInv = 1 / totalMass; 27982 27983 centerOfMass.x = centerOfMass.x * parentBranch.mass + node.x * node.options.mass; 27984 centerOfMass.x *= totalMassInv; 27985 27986 centerOfMass.y = centerOfMass.y * parentBranch.mass + node.y * node.options.mass; 27987 centerOfMass.y *= totalMassInv; 27988 27989 parentBranch.mass = totalMass; 27990 var biggestSize = Math.max(Math.max(node.height, node.radiu
27990s), node.width); 27991 parentBranch.maxWidth = parentBranch.maxWidth < biggestSize ? biggestSize : parentBranch.maxWidth; 27992 } 27993 27994 /** 27995 * determine in which branch the node will be placed. 27996 * 27997 * @param {Object} parentBranch 27998 * @param {Node} node 27999 * @param {boolean} skipMassUpdate 28000 * @private 28001 */ 28002 28003 }, { 28004 key: "_placeInTree", 28005 value: function _placeInTree(parentBranch, node, skipMassUpdate) { 28006 if (skipMassUpdate != true || skipMassUpdate === undefined) { 28007 // update the mass of the branch. 28008 this._updateBranchMass(parentBranch, node); 28009 } 28010 28011 var range = parentBranch.children.NW.range; 28012 var region = void 0; 28013 if (range.maxX > node.x) { 28014 // in NW or SW 28015 if (range.maxY > node.y) { 28016 region = "NW"; 28017 } else { 28018 region = "SW"; 28019 } 28020 } else { 28021 // in NE or SE 28022 if (range.maxY > node.y) { 28023 region = "NE"; 28024 } else { 28025 region = "SE"; 28026 } 28027 } 28028 28029 this._placeInRegion(parentBranch, node, region); 28030 } 28031 28032 /** 28033 * actually place the node in a region (or branch) 28034 * 28035 * @param {Object} parentBranch 28036 * @param {Node} node 28037 * @param {'NW'| 'NE' | 'SW' | 'SE'} region 28038 * @private 28039 */ 28040 28041 }, { 28042 key: "_placeInRegion", 28043 value: function _placeInRegion(parentBranch, node, region) { 28044 var children = parentBranch.children[region]; 28045 28046 switch (children.childrenCount) { 28047 case 0: 28048 // place node here 28049 children.children.data = node; 28050 children.childrenCount = 1; 28051 this._updateBranchMass(children, node); 28052 break; 28053 case 1: 28054 // convert into children 28055 // if there are two nodes exactly overlapping (on init, on opening of cluster etc.) 28056 // we move one node a little bit and we do not put it in the tree. 28057 if (children.children.data.x === node.x && children.children.data.y === node.y) { 28058 node.x += this.seededRandom(); 28059 node.y += this.seededRandom(); 28060 } else { 28061 this._splitBranch(children); 28062 this._placeInTree(children, node); 28063 } 28064 break; 28065 case 4: 28066 // place in branch 28067 this._placeInTree(children, node); 28068 break; 28069 } 28070 } 28071 28072 /** 28073 * this function splits a branch into 4 sub branches. If the branch contained a node, we place it in the subbranch 28074 * after the split is complete. 28075 * 28076 * @param {Object} parentBranch 28077 * @private 28078 */ 28079 28080 }, { 28081 key: "_splitBranch", 28082 value: function _splitBranch(parentBranch) { 28083 // if the branch is shaded with a node, replace the node in the new subset. 28084 var containedNode = null; 28085 if (parentBranch.childrenCount === 1) { 28086 containedNode = parentBranch.children.data; 28087 parentBranch.mass = 0; 28088 parentBranch.centerOfMass.x = 0; 28089 parentBranch.centerOfMass.y = 0; 28090 } 28091 parentBranch.childrenCount = 4; 28092 parentBranch.children.data = null; 28093 this._insertRegion(parentBranch, "NW"); 28094 this._insertRegion(parentBranch, "NE"); 28095 this._insertRegion(parentBranch, "SW"); 28096 this._insertRegion(parentBranch, "SE"); 28097 28098 if (containedNode != null) { 28099 this._placeInTree(parentBranch, containedNode); 28100 } 28101 } 28102 28103 /** 28104 * This function subdivides the region into four new segments. 28105 * Specifically, this inserts a single new segment. 28106 * It fills the children section of the parentBranch 28107 * 28108 * @param {Object} parentBranch 28109 * @param {'NW'| 'NE' | 'SW' | 'SE'} region 28110 * @private 28111 */ 28112 28113 }, { 28114 key: "_insertRegion", 28115 value: function _insertRegion(parentBranch, region) { 28116 var minX = void 0, 28117 maxX = void 0, 28118 minY = void 0, 28119 maxY = void 0; 28120 var childSize = 0.5 * parentBranch.size; 28121 switch (region) { 28122 case "NW": 28123 minX = parentBranch.range.minX; 28124 maxX = parentBranch.range.minX + childSize; 28125 minY = parentBranch.range.minY; 28126 maxY = parentBranch.range.minY + childSize; 28127 break; 28128 case "NE": 28129 minX = parentBranch.range.minX + childSize; 28130 maxX = parentBranch.range.maxX; 28131 minY = parentBranch.range.minY; 28132 maxY = parentBranch.range.minY + childSize; 28133 break; 28134 case "SW": 28135 minX = parentBranch.range.minX; 28136 maxX = parentBranch.range.minX + childSize; 28137 minY = parentBranch.range.minY + childSize; 28138 maxY = parentBranch.range.maxY; 28139 break; 28140 case "SE": 28141 minX = parentBranch.range.minX + childSize; 28142 maxX = parentBranch.range.maxX; 28143 minY = parentBranch.range.minY + childSize; 28144 maxY = parentBranch.range.maxY; 28145 break; 28146 } 28147 28148 parentBranch.children[region] = { 28149 centerOfMass: { x: 0, y: 0 }, 28150 mass: 0, 28151 range: { minX: minX, maxX: maxX, minY: minY, maxY: maxY }, 28152 size: 0.5 * parentBranch.size, 28153 calcSize: 2 * parentBranch.calcSize, 28154 children: { data: null }, 28155 maxWidth: 0,
28156 level: parentBranch.level + 1, 28157 childrenCount: 0 28158 }; 28159 } 28160 28161 //--------------------------- DEBUGGING BELOW ---------------------------// 28162 28163 28164 /** 28165 * This function is for debugging purposed, it draws the tree. 28166 * 28167 * @param {CanvasRenderingContext2D} ctx 28168 * @param {string} color 28169 * @private 28170 */ 28171 28172 }, { 28173 key: "_debug", 28174 value: function _debug(ctx, color) { 28175 if (this.barnesHutTree !== undefined) { 28176 28177 ctx.lineWidth = 1; 28178 28179 this._drawBranch(this.barnesHutTree.root, ctx, color); 28180 } 28181 } 28182 28183 /** 28184 * This function is for debugging purposes. It draws the branches recursively. 28185 * 28186 * @param {Object} branch 28187 * @param {CanvasRenderingContext2D} ctx 28188 * @param {string} color 28189 * @private 28190 */ 28191 28192 }, { 28193 key: "_drawBranch", 28194 value: function _drawBranch(branch, ctx, color) { 28195 if (color === undefined) { 28196 color = "#FF0000"; 28197 } 28198 28199 if (branch.childrenCount === 4) { 28200 this._drawBranch(branch.children.NW, ctx); 28201 this._drawBranch(branch.children.NE, ctx); 28202 this._drawBranch(branch.children.SE, ctx); 28203 this._drawBranch(branch.children.SW, ctx); 28204 } 28205 ctx.strokeStyle = color; 28206 ctx.beginPath(); 28207 ctx.moveTo(branch.range.minX, branch.range.minY); 28208 ctx.lineTo(branch.range.maxX, branch.range.minY); 28209 ctx.stroke(); 28210 28211 ctx.beginPath(); 28212 ctx.moveTo(branch.range.maxX, branch.range.minY); 28213 ctx.lineTo(branch.range.maxX, branch.range.maxY); 28214 ctx.stroke(); 28215 28216 ctx.beginPath(); 28217 ctx.moveTo(branch.range.maxX, branch.range.maxY); 28218 ctx.lineTo(branch.range.minX, branch.range.maxY); 28219 ctx.stroke(); 28220 28221 ctx.beginPath(); 28222 ctx.moveTo(branch.range.minX, branch.range.maxY); 28223 ctx.lineTo(branch.range.minX, branch.range.minY); 28224 ctx.stroke(); 28225 28226 /* 28227 if (branch.mass > 0) { 28228 ctx.circle(branch.centerOfMass.x, branch.centerOfMass.y, 3*branch.mass); 28229 ctx.stroke(); 28230 } 28231 */ 28232 } 28233 }]); 28234 return BarnesHutSolver; 28235}(); 28236 28237exports["default"] = BarnesHutSolver; 28238 28239/***/ }), 28240/* 121 */ 28241/***/ (function(module, exports, __webpack_require__) { 28242 28243"use strict"; 28244 28245 28246Object.defineProperty(exports, "__esModule", { 28247 value: true 28248}); 28249 28250var _classCallCheck2 = __webpack_require__(0); 28251 28252var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); 28253 28254var _createClass2 = __webpack_require__(1); 28255 28256var _createClass3 = _interopRequireDefault(_createClass2); 28257 28258function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } 28259 28260/** 28261 * Central Gravity Solver 28262 */ 28263var CentralGravitySolver = function () { 28264 /** 28265 * @param {Object} body 28266 * @param {{physicsNodeIndices: Array, physicsEdgeIndices: Array, forces: {}, velocities: {}}} physicsBody 28267 * @param {Object} options 28268 */ 28269 function CentralGravitySolver(body, physicsBody, options) { 28270 (0, _classCallCheck3["default"])(this, CentralGravitySolver); 28271 28272 this.body = body; 28273 this.physicsBody = physicsBody; 28274 this.setOptions(options); 28275 } 28276 28277 /** 28278 * 28279 * @param {Object} options 28280 */ 28281 28282 28283 (0, _createClass3["default"])(CentralGravitySolver, [{ 28284 key: "setOptions", 28285 value: function setOptions(options) { 28286 this.options = options; 28287 } 28288 28289 /** 28290 * Calculates forces for each node 28291 */ 28292 28293 }, { 28294 key: "solve", 28295 value: function solve() { 28296 var dx = void 0, 28297 dy = void 0, 28298 distance = void 0, 28299 node = void 0; 28300 var nodes = this.body.nodes; 28301 var nodeIndices = this.physicsBody.physicsNodeIndices; 28302 var forces = this.physicsBody.forces; 28303 28304 for (var i = 0; i < nodeIndices.length; i++) { 28305 var nodeId = nodeIndices[i]; 28306 node = nodes[nodeId]; 28307 dx = -node.x; 28308 dy = -node.y; 28309 distance = Math.sqrt(dx * dx + dy * dy); 28310 28311 this._calculateForces(distance, dx, dy, forces, node); 28312 } 28313 } 28314 28315 /** 28316 * Calculate the forces based on the distance. 28317 * @param {number} distance 28318 * @param {number} dx 28319 * @param {number} dy 28320 * @param {Object<Node.id, vis.Node>} forces 28321 * @param {Node} node 28322 * @private 28323 */ 28324 28325 }, { 28326 key: "_calculateForces", 28327 value: function _calculateForces(distance, dx, dy, forces, node) { 28328 var gravityForce = distance === 0 ? 0 : this.options.centralGravity / distance; 28329 forces[node.id].x = dx * gravityForce; 28330 forces[node.id].y = dy * gravityForce; 28331 } 28332 }]); 28333 return CentralGravitySolver; 28334}(); 28335 28336exports["default"] = CentralGravitySolver; 28337 28338/***/ }), 28339/* 122 */ 28340/***/ (function(module, exports, __webpack_require__) { 28341 28342"use strict"; 28343 28344 28345Object.defineProperty(exports, "__esModule", { 28346 value: true 28347}); 28348/**
28349 * This object contains all possible options. It will check if the types are correct, if required if the option is one 28350 * of the allowed values. 28351 * 28352 * __any__ means that the name of the property does not matter. 28353 * __type__ is a required field for all objects and contains the allowed types of all objects 28354 */ 28355var string = 'string'; 28356var bool = 'boolean'; 28357var number = 'number'; 28358var array = 'array'; 28359var object = 'object'; // should only be in a __type__ property 28360var dom = 'dom'; 28361var any = 'any'; 28362 28363// List of endpoints 28364var endPoints = ['arrow', 'circle', 'bar']; 28365 28366var allOptions = { 28367 configure: { 28368 enabled: { boolean: bool }, 28369 filter: { boolean: bool, string: string, array: array, 'function': 'function' }, 28370 container: { dom: dom }, 28371 showButton: { boolean: bool }, 28372 __type__: { object: object, boolean: bool, string: string, array: array, 'function': 'function' } 28373 }, 28374 edges: { 28375 arrows: { 28376 to: { enabled: { boolean: bool }, scaleFactor: { number: number }, type: { string: endPoints }, __type__: { object: object, boolean: bool } }, 28377 middle: { enabled: { boolean: bool }, scaleFactor: { number: number }, type: { string: endPoints }, __type__: { object: object, boolean: bool } }, 28378 from: { enabled: { boolean: bool }, scaleFactor: { number: number }, type: { string: endPoints }, __type__: { object: object, boolean: bool } }, 28379 __type__: { string: ['from', 'to', 'middle'], object: object } 28380 }, 28381 arrowStrikethrough: { boolean: bool }, 28382 chosen: { 28383 label: { boolean: bool, 'function': 'function' }, 28384 edge: { boolean: bool, 'function': 'function' }, 28385 __type__: { object: object, boolean: bool } 28386 }, 28387 color: { 28388 color: { string: string }, 28389 highlight: { string: string }, 28390 hover: { string: string }, 28391 inherit: { string: ['from', 'to', 'both'], boolean: bool }, 28392 opacity: { number: number }, 28393 __type__: { object: object, string: string } 28394 }, 28395 dashes: { boolean: bool, array: array }, 28396 font: { 28397 color: { string: string }, 28398 size: { number: number }, // px 28399 face: { string: string }, 28400 background: { string: string }, 28401 strokeWidth: { number: number }, // px 28402 strokeColor: { string: string }, 28403 align: { string: ['horizontal', 'top', 'middle', 'bottom'] }, 28404 vadjust: { number: number }, 28405 multi: { boolean: bool, string: string }, 28406 bold: { 28407 color: { string: string }, 28408 size: { number: number }, // px 28409 face: { string: string }, 28410 mod: { string: string }, 28411 vadjust: { number: number }, 28412 __type__: { object: object, string: string } 28413 }, 28414 boldital: { 28415 color: { string: string }, 28416 size: { number: number }, // px 28417 face: { string: string }, 28418 mod: { string: string }, 28419 vadjust: { number: number }, 28420 __type__: { object: object, string: string } 28421 }, 28422 ital: { 28423 color: { string: string }, 28424 size: { number: number }, // px 28425 face: { string: string }, 28426 mod: { string: string }, 28427 vadjust: { number: number }, 28428 __type__: { object: object, string: string } 28429 }, 28430 mono: { 28431 color: { string: string }, 28432 size: { number: number }, // px 28433 face: { string: string }, 28434 mod: { string: string }, 28435 vadjust: { number: number }, 28436 __type__: { object: object, string: string } 28437 }, 28438 __type__: { object: object, string: string } 28439 }, 28440 hidden: { boolean: bool }, 28441 hoverWidth: { 'function': 'function', number: number }, 28442 label: { string: string, 'undefined': 'undefined' }, 28443 labelHighlightBold: { boolean: bool }, 28444 length: { number: number, 'undefined': 'undefined' }, 28445 physics: { boolean: bool }, 28446 scaling: { 28447 min: { number: number }, 28448 max: { number: number }, 28449 label: { 28450 enabled: { boolean: bool }, 28451 min: { number: number }, 28452 max: { number: number }, 28453 maxVisible: { number: number }, 28454 drawThreshold: { number: number }, 28455 __type__: { object: object, boolean: bool } 28456 }, 28457 customScalingFunction: { 'function': 'function' }, 28458 __type__: { object: object } 28459 }, 28460 selectionWidth: { 'function': 'function', number: number }, 28461 selfReferenceSize: { number: number }, 28462 shadow: { 28463 enabled: { boolean: bool }, 28464 color: { string: string }, 28465 size: { number: number }, 28466 x: { number: number }, 28467 y: { number: number }, 28468 __type__: { object: object, boolean: bool } 28469 }, 28470 smooth: { 28471 enabled: { boolean: bool }, 28472 type: { string: ['dynamic', 'continuous', 'discrete', 'diagonalCross', 'straightCross', 'horizontal', 'vertical', 'curvedCW', 'curvedCCW', 'cubicBezier'] }, 28473 roundness: { number: number },
28474 forceDirection: { string: ['horizontal', 'vertical', 'none'], boolean: bool }, 28475 __type__: { object: object, boolean: bool } 28476 }, 28477 title: { string: string, 'undefined': 'undefined' }, 28478 width: { number: number }, 28479 widthConstraint: { 28480 maximum: { number: number }, 28481 __type__: { object: object, boolean: bool, number: number } 28482 }, 28483 value: { number: number, 'undefined': 'undefined' }, 28484 __type__: { object: object } 28485 }, 28486 groups: { 28487 useDefaultGroups: { boolean: bool }, 28488 __any__: 'get from nodes, will be overwritten below', 28489 __type__: { object: object } 28490 }, 28491 interaction: { 28492 dragNodes: { boolean: bool }, 28493 dragView: { boolean: bool }, 28494 hideEdgesOnDrag: { boolean: bool }, 28495 hideNodesOnDrag: { boolean: bool }, 28496 hover: { boolean: bool }, 28497 keyboard: { 28498 enabled: { boolean: bool }, 28499 speed: { x: { number: number }, y: { number: number }, zoom: { number: number }, __type__: { object: object } }, 28500 bindToWindow: { boolean: bool }, 28501 __type__: { object: object, boolean: bool } 28502 }, 28503 multiselect: { boolean: bool }, 28504 navigationButtons: { boolean: bool }, 28505 selectable: { boolean: bool }, 28506 selectConnectedEdges: { boolean: bool }, 28507 hoverConnectedEdges: { boolean: bool }, 28508 tooltipDelay: { number: number }, 28509 zoomView: { boolean: bool }, 28510 __type__: { object: object } 28511 }, 28512 layout: { 28513 randomSeed: { 'undefined': 'undefined', number: number }, 28514 improvedLayout: { boolean: bool }, 28515 hierarchical: { 28516 enabled: { boolean: bool }, 28517 levelSeparation: { number: number }, 28518 nodeSpacing: { number: number }, 28519 treeSpacing: { number: number }, 28520 blockShifting: { boolean: bool }, 28521 edgeMinimization: { boolean: bool }, 28522 parentCentralization: { boolean: bool }, 28523 direction: { string: ['UD', 'DU', 'LR', 'RL'] }, // UD, DU, LR, RL 28524 sortMethod: { string: ['hubsize', 'directed'] }, // hubsize, directed 28525 __type__: { object: object, boolean: bool } 28526 }, 28527 __type__: { object: object } 28528 }, 28529 manipulation: { 28530 enabled: { boolean: bool }, 28531 initiallyActive: { boolean: bool }, 28532 addNode: { boolean: bool, 'function': 'function' }, 28533 addEdge: { boolean: bool, 'function': 'function' }, 28534 editNode: { 'function': 'function' }, 28535 editEdge: { 28536 editWithoutDrag: { 'function': 'function' }, 28537 __type__: { object: object, boolean: bool, 'function': 'function' } 28538 }, 28539 deleteNode: { boolean: bool, 'function': 'function' }, 28540 deleteEdge: { boolean: bool, 'function': 'function' }, 28541 controlNodeStyle: 'get from nodes, will be overwritten below', 28542 __type__: { object: object, boolean: bool } 28543 }, 28544 nodes: { 28545 borderWidth: { number: number }, 28546 borderWidthSelected: { number: number, 'undefined': 'undefined' }, 28547 brokenImage: { string: string, 'undefined': 'undefined' }, 28548 chosen: { 28549 label: { boolean: bool, 'function': 'function' }, 28550 node: { boolean: bool, 'function': 'function' }, 28551 __type__: { object: object, boolean: bool } 28552 }, 28553 color: { 28554 border: { string: string }, 28555 background: { string: string }, 28556 highlight: { 28557 border: { string: string }, 28558 background: { string: string }, 28559 __type__: { object: object, string: string } 28560 }, 28561 hover: { 28562 border: { string: string }, 28563 background: { string: string }, 28564 __type__: { object: object, string: string } 28565 }, 28566 __type__: { object: object, string: string } 28567 }, 28568 fixed: { 28569 x: { boolean: bool }, 28570 y: { boolean: bool }, 28571 __type__: { object: object, boolean: bool } 28572 }, 28573 font: { 28574 align: { string: string }, 28575 color: { string: string }, 28576 size: { number: number }, // px 28577 face: { string: string }, 28578 background: { string: string }, 28579 strokeWidth: { number: number }, // px 28580 strokeColor: { string: string }, 28581 vadjust: { number: number }, 28582 multi: { boolean: bool, string: string }, 28583 bold: { 28584 color: { string: string }, 28585 size: { number: number }, // px 28586 face: { string: string }, 28587 mod: { string: string }, 28588 vadjust: { number: number }, 28589 __type__: { object: object, string: string } 28590 }, 28591 boldital: { 28592 color: { string: string }, 28593 size: { number: number }, // px 28594 face: { string: string }, 28595 mod: { string: string }, 28596 vadjust: { number: number }, 28597 __type__: { object: object, string: string } 28598 }, 28599 ital: { 28600 color: { string: string }, 28601 size: { number: number }, // px 28602 face: { string: string }, 28603 mod: { string: string }, 28604 vadjust: { number: number }, 28605 __type__: { object: object, string: string } 28606 }, 28607 mono: { 28608 color: { string: string }, 28609 size: { number: number }, // px 28610 face: { string: string }, 28611 mod: { string: string }, 28612 vadjust: { number: number }, 28613 __type__: { object: object, string: string } 28614 }, 28615 __type__: { object: object, string: string } 28616 },
28617 group: { string: string, number: number, 'undefined': 'undefined' }, 28618 heightConstraint: { 28619 minimum: { number: number }, 28620 valign: { string: string }, 28621 __type__: { object: object, boolean: bool, number: number } 28622 }, 28623 hidden: { boolean: bool }, 28624 icon: { 28625 face: { string: string }, 28626 code: { string: string }, //'\uf007', 28627 size: { number: number }, //50, 28628 color: { string: string }, 28629 __type__: { object: object } 28630 }, 28631 id: { string: string, number: number }, 28632 image: { 28633 selected: { string: string, 'undefined': 'undefined' }, // --> URL 28634 unselected: { string: string, 'undefined': 'undefined' }, // --> URL 28635 __type__: { object: object, string: string } 28636 }, 28637 label: { string: string, 'undefined': 'undefined' }, 28638 labelHighlightBold: { boolean: bool }, 28639 level: { number: number, 'undefined': 'undefined' }, 28640 margin: { 28641 top: { number: number }, 28642 right: { number: number }, 28643 bottom: { number: number }, 28644 left: { number: number }, 28645 __type__: { object: object, number: number } 28646 }, 28647 mass: { number: number }, 28648 physics: { boolean: bool }, 28649 scaling: { 28650 min: { number: number }, 28651 max: { number: number }, 28652 label: { 28653 enabled: { boolean: bool }, 28654 min: { number: number }, 28655 max: { number: number }, 28656 maxVisible: { number: number }, 28657 drawThreshold: { number: number }, 28658 __type__: { object: object, boolean: bool } 28659 }, 28660 customScalingFunction: { 'function': 'function' }, 28661 __type__: { object: object } 28662 }, 28663 shadow: { 28664 enabled: { boolean: bool }, 28665 color: { string: string }, 28666 size: { number: number }, 28667 x: { number: number }, 28668 y: { number: number }, 28669 __type__: { object: object, boolean: bool } 28670 }, 28671 shape: { string: ['ellipse', 'circle', 'database', 'box', 'text', 'image', 'circularImage', 'diamond', 'dot', 'star', 'triangle', 'triangleDown', 'square', 'icon', 'hexagon'] }, 28672 shapeProperties: { 28673 borderDashes: { boolean: bool, array: array }, 28674 borderRadius: { number: number }, 28675 interpolation: { boolean: bool }, 28676 useImageSize: { boolean: bool }, 28677 useBorderWithImage: { boolean: bool }, 28678 __type__: { object: object } 28679 }, 28680 size: { number: number }, 28681 title: { string: string, dom: dom, 'undefined': 'undefined' }, 28682 value: { number: number, 'undefined': 'undefined' }, 28683 widthConstraint: { 28684 minimum: { number: number }, 28685 maximum: { number: number }, 28686 __type__: { object: object, boolean: bool, number: number } 28687 }, 28688 x: { number: number }, 28689 y: { number: number }, 28690 __type__: { object: object } 28691 }, 28692 physics: { 28693 enabled: { boolean: bool }, 28694 barnesHut: { 28695 gravitationalConstant: { number: number }, 28696 centralGravity: { number: number }, 28697 springLength: { number: number }, 28698 springConstant: { number: number }, 28699 damping: { number: number }, 28700 avoidOverlap: { number: number }, 28701 __type__: { object: object } 28702 }, 28703 forceAtlas2Based: { 28704 gravitationalConstant: { number: number }, 28705 centralGravity: { number: number }, 28706 springLength: { number: number }, 28707 springConstant: { number: number }, 28708 damping: { number: number }, 28709 avoidOverlap: { number: number }, 28710 __type__: { object: object } 28711 }, 28712 repulsion: { 28713 centralGravity: { number: number }, 28714 springLength: { number: number }, 28715 springConstant: { number: number }, 28716 nodeDistance: { number: number }, 28717 damping: { number: number }, 28718 __type__: { object: object } 28719 }, 28720 hierarchicalRepulsion: { 28721 centralGravity: { number: number }, 28722 springLength: { number: number }, 28723 springConstant: { number: number }, 28724 nodeDistance: { number: number }, 28725 damping: { number: number }, 28726 __type__: { object: object } 28727 }, 28728 maxVelocity: { number: number }, 28729 minVelocity: { number: number }, // px/s 28730 solver: { string: ['barnesHut', 'repulsion', 'hierarchicalRepulsion', 'forceAtlas2Based'] }, 28731 stabilization: { 28732 enabled: { boolean: bool }, 28733 iterations: { number: number }, // maximum number of iteration to stabilize 28734 updateInterval: { number: number }, 28735 onlyDynamicEdges: { boolean: bool }, 28736 fit: { boolean: bool }, 28737 __type__: { object: object, boolean: bool } 28738 }, 28739 timestep: { number: number }, 28740 adaptiveTimestep: { boolean: bool }, 28741 __type__: { object: object, boolean: bool } 28742 }, 28743 28744 //globals : 28745 autoResize: { boolean: bool }, 28746 clickToUse: { boolean: bool }, 28747 locale: { string: string }, 28748 locales: { 28749 __any__: { any: any }, 28750 __type__: { object: object } 28751 }, 28752 height: { string: string },
28753 width: { string: string }, 28754 __type__: { object: object } 28755}; 28756 28757allOptions.groups.__any__ = allOptions.nodes; 28758allOptions.manipulation.controlNodeStyle = allOptions.nodes; 28759 28760var configureOptions = { 28761 nodes: { 28762 borderWidth: [1, 0, 10, 1], 28763 borderWidthSelected: [2, 0, 10, 1], 28764 color: { 28765 border: ['color', '#2B7CE9'], 28766 background: ['color', '#97C2FC'], 28767 highlight: { 28768 border: ['color', '#2B7CE9'], 28769 background: ['color', '#D2E5FF'] 28770 }, 28771 hover: { 28772 border: ['color', '#2B7CE9'], 28773 background: ['color', '#D2E5FF'] 28774 } 28775 }, 28776 fixed: { 28777 x: false, 28778 y: false 28779 }, 28780 font: { 28781 color: ['color', '#343434'], 28782 size: [14, 0, 100, 1], // px 28783 face: ['arial', 'verdana', 'tahoma'], 28784 background: ['color', 'none'], 28785 strokeWidth: [0, 0, 50, 1], // px 28786 strokeColor: ['color', '#ffffff'] 28787 }, 28788 //group: 'string', 28789 hidden: false, 28790 labelHighlightBold: true, 28791 //icon: { 28792 // face: 'string', //'FontAwesome', 28793 // code: 'string', //'\uf007', 28794 // size: [50, 0, 200, 1], //50, 28795 // color: ['color','#2B7CE9'] //'#aa00ff' 28796 //}, 28797 //image: 'string', // --> URL 28798 physics: true, 28799 scaling: { 28800 min: [10, 0, 200, 1], 28801 max: [30, 0, 200, 1], 28802 label: { 28803 enabled: false, 28804 min: [14, 0, 200, 1], 28805 max: [30, 0, 200, 1], 28806 maxVisible: [30, 0, 200, 1], 28807 drawThreshold: [5, 0, 20, 1] 28808 } 28809 }, 28810 shadow: { 28811 enabled: false, 28812 color: 'rgba(0,0,0,0.5)', 28813 size: [10, 0, 20, 1], 28814 x: [5, -30, 30, 1], 28815 y: [5, -30, 30, 1] 28816 }, 28817 shape: ['ellipse', 'box', 'circle', 'database', 'diamond', 'dot', 'square', 'star', 'text', 'triangle', 'triangleDown', 'hexagon'], 28818 shapeProperties: { 28819 borderDashes: false, 28820 borderRadius: [6, 0, 20, 1], 28821 interpolation: true, 28822 useImageSize: false 28823 }, 28824 size: [25, 0, 200, 1] 28825 }, 28826 edges: { 28827 arrows: { 28828 to: { enabled: false, scaleFactor: [1, 0, 3, 0.05], type: 'arrow' }, 28829 middle: { enabled: false, scaleFactor: [1, 0, 3, 0.05], type: 'arrow' }, 28830 from: { enabled: false, scaleFactor: [1, 0, 3, 0.05], type: 'arrow' } 28831 }, 28832 arrowStrikethrough: true, 28833 color: { 28834 color: ['color', '#848484'], 28835 highlight: ['color', '#848484'], 28836 hover: ['color', '#848484'], 28837 inherit: ['from', 'to', 'both', true, false], 28838 opacity: [1, 0, 1, 0.05] 28839 }, 28840 dashes: false, 28841 font: { 28842 color: ['color', '#343434'], 28843 size: [14, 0, 100, 1], // px 28844 face: ['arial', 'verdana', 'tahoma'], 28845 background: ['color', 'none'], 28846 strokeWidth: [2, 0, 50, 1], // px 28847 strokeColor: ['color', '#ffffff'], 28848 align: ['horizontal', 'top', 'middle', 'bottom'] 28849 }, 28850 hidden: false, 28851 hoverWidth: [1.5, 0, 5, 0.1], 28852 labelHighlightBold: true, 28853 physics: true, 28854 scaling: { 28855 min: [1, 0, 100, 1], 28856 max: [15, 0, 100, 1], 28857 label: { 28858 enabled: true, 28859 min: [14, 0, 200, 1], 28860 max: [30, 0, 200, 1], 28861 maxVisible: [30, 0, 200, 1], 28862 drawThreshold: [5, 0, 20, 1] 28863 } 28864 }, 28865 selectionWidth: [1.5, 0, 5, 0.1], 28866 selfReferenceSize: [20, 0, 200, 1], 28867 shadow: { 28868 enabled: false, 28869 color: 'rgba(0,0,0,0.5)', 28870 size: [10, 0, 20, 1], 28871 x: [5, -30, 30, 1], 28872 y: [5, -30, 30, 1] 28873 }, 28874 smooth: { 28875 enabled: true, 28876 type: ['dynamic', 'continuous', 'discrete', 'diagonalCross', 'straightCross', 'horizontal', 'vertical', 'curvedCW', 'curvedCCW', 'cubicBezier'], 28877 forceDirection: ['horizontal', 'vertical', 'none'], 28878 roundness: [0.5, 0, 1, 0.05] 28879 }, 28880 width: [1, 0, 30, 1] 28881 }, 28882 layout: { 28883 //randomSeed: [0, 0, 500, 1], 28884 //improvedLayout: true, 28885 hierarchical: { 28886 enabled: false, 28887 levelSeparation: [150, 20, 500, 5], 28888 nodeSpacing: [100, 20, 500, 5], 28889 treeSpacing: [200, 20, 500, 5], 28890 blockShifting: true, 28891 edgeMinimization: true, 28892 parentCentralization: true, 28893 direction: ['UD', 'DU', 'LR', 'RL'], // UD, DU, LR, RL 28894 sortMethod: ['hubsize', 'directed'] // hubsize, directed 28895 } 28896 }, 28897 interaction: { 28898 dragNodes: true, 28899 dragView: true, 28900 hideEdgesOnDrag: false, 28901 hideNodesOnDrag: false, 28902 hover: false, 28903 keyboard: { 28904 enabled: false, 28905 speed: { x: [10, 0, 40, 1], y: [10, 0, 40, 1], zoom: [0.02, 0, 0.1, 0.005] }, 28906 bindToWindow: true 28907 }, 28908 multiselect: false,
28909 navigationButtons: false, 28910 selectable: true, 28911 selectConnectedEdges: true, 28912 hoverConnectedEdges: true, 28913 tooltipDelay: [300, 0, 1000, 25], 28914 zoomView: true 28915 }, 28916 manipulation: { 28917 enabled: false, 28918 initiallyActive: false 28919 }, 28920 physics: { 28921 enabled: true, 28922 barnesHut: { 28923 //theta: [0.5, 0.1, 1, 0.05], 28924 gravitationalConstant: [-2000, -30000, 0, 50], 28925 centralGravity: [0.3, 0, 10, 0.05], 28926 springLength: [95, 0, 500, 5], 28927 springConstant: [0.04, 0, 1.2, 0.005], 28928 damping: [0.09, 0, 1, 0.01], 28929 avoidOverlap: [0, 0, 1, 0.01] 28930 }, 28931 forceAtlas2Based: { 28932 //theta: [0.5, 0.1, 1, 0.05], 28933 gravitationalConstant: [-50, -500, 0, 1], 28934 centralGravity: [0.01, 0, 1, 0.005], 28935 springLength: [95, 0, 500, 5], 28936 springConstant: [0.08, 0, 1.2, 0.005], 28937 damping: [0.4, 0, 1, 0.01], 28938 avoidOverlap: [0, 0, 1, 0.01] 28939 }, 28940 repulsion: { 28941 centralGravity: [0.2, 0, 10, 0.05], 28942 springLength: [200, 0, 500, 5], 28943 springConstant: [0.05, 0, 1.2, 0.005], 28944 nodeDistance: [100, 0, 500, 5], 28945 damping: [0.09, 0, 1, 0.01] 28946 }, 28947 hierarchicalRepulsion: { 28948 centralGravity: [0.2, 0, 10, 0.05], 28949 springLength: [100, 0, 500, 5], 28950 springConstant: [0.01, 0, 1.2, 0.005], 28951 nodeDistance: [120, 0, 500, 5], 28952 damping: [0.09, 0, 1, 0.01] 28953 }, 28954 maxVelocity: [50, 0, 150, 1], 28955 minVelocity: [0.1, 0.01, 0.5, 0.01], 28956 solver: ['barnesHut', 'forceAtlas2Based', 'repulsion', 'hierarchicalRepulsion'], 28957 timestep: [0.5, 0.01, 1, 0.01] 28958 //adaptiveTimestep: true 28959 } 28960}; 28961 28962exports.allOptions = allOptions; 28963exports.configureOptions = configureOptions; 28964 28965/***/ }), 28966/* 123 */ 28967/***/ (function(module, exports, __webpack_require__) { 28968 28969"use strict"; 28970 28971 28972var util = __webpack_require__(2); 28973 28974// Graph3d 28975util.extend(exports, __webpack_require__(159)); 28976 28977// Timeline & Graph2d 28978util.extend(exports, __webpack_require__(177)); 28979 28980// Network 28981util.extend(exports, __webpack_require__(181)); 28982 28983/***/ }), 28984/* 124 */ 28985/***/ (function(module, exports, __webpack_require__) { 28986 28987__webpack_require__(49); 28988__webpack_require__(60); 28989module.exports = __webpack_require__(136); 28990 28991 28992/***/ }), 28993/* 125 */ 28994/***/ (function(module, exports, __webpack_require__) { 28995 28996"use strict"; 28997 28998var addToUnscopables = __webpack_require__(126); 28999var step = __webpack_require__(127); 29000var Iterators = __webpack_require__(31); 29001var toIObject = __webpack_require__(25); 29002 29003// 22.1.3.4 Array.prototype.entries() 29004// 22.1.3.13 Array.prototype.keys() 29005// 22.1.3.29 Array.prototype.values() 29006// 22.1.3.30 Array.prototype[@@iterator]() 29007module.exports = __webpack_require__(79)(Array, 'Array', function (iterated, kind) { 29008 this._t = toIObject(iterated); // target 29009 this._i = 0; // next index 29010 this._k = kind; // kind 29011// 22.1.5.2.1 %ArrayIteratorPrototype%.next() 29012}, function () { 29013 var O = this._t; 29014 var kind = this._k; 29015 var index = this._i++; 29016 if (!O || index >= O.length) { 29017 this._t = undefined; 29018 return step(1); 29019 } 29020 if (kind == 'keys') return step(0, index); 29021 if (kind == 'values') return step(0, O[index]); 29022 return step(0, [index, O[index]]); 29023}, 'values'); 29024 29025// argumentsList[@@iterator] is %ArrayProto_values% (9.4.4.6, 9.4.4.7) 29026Iterators.Arguments = Iterators.Array; 29027 29028addToUnscopables('keys'); 29029addToUnscopables('values'); 29030addToUnscopables('entries'); 29031 29032 29033/***/ }), 29034/* 126 */ 29035/***/ (function(module, exports) { 29036 29037module.exports = function () { /* empty */ }; 29038 29039 29040/***/ }), 29041/* 127 */ 29042/***/ (function(module, exports) { 29043 29044module.exports = function (done, value) { 29045 return { value: value, done: !!done }; 29046}; 29047 29048 29049/***/ }), 29050/* 128 */ 29051/***/ (function(module, exports) { 29052 29053module.exports = function (it) { 29054 if (typeof it != 'function') throw TypeError(it + ' is not a function!'); 29055 return it; 29056}; 29057 29058 29059/***/ }), 29060/* 129 */ 29061/***/ (function(module, exports, __webpack_require__) { 29062 29063"use strict"; 29064 29065var create = __webpack_require__(54); 29066var descriptor = __webpack_require__(39); 29067var setToStringTag = __webpack_require__(59); 29068var IteratorPrototype = {}; 29069 29070// 25.1.2.1.1 %IteratorPrototype%[@@iterator]() 29071__webpack_require__(26)(IteratorPrototype, __webpack_require__(13)('iterator'), function () { return this; }); 29072 29073module.exports = function (Constructor, NAME, next) { 29074 Constructor.prototype = create(IteratorPrototype, { next: descriptor(1, next) }); 29075 setToStringTag(Constructor, NAME + ' Iterator'); 29076}; 29077 29078 29079/***/ }), 29080/* 130 */ 29081/***/ (function(module, exports, __webpack_require__) { 29082 29083var dP = __webpack_require__(20); 29084var anObject = __webpack_require__(27); 29085var getKeys = __webpack_require__(33); 29086 29087module.exports = __webpack_require__(21) ? Object.defineProperties : function defineProperties(O, Properties) { 29088 anObject(O); 29089 var keys = getKeys(Properties); 29090 var length = keys.length; 29091 var i = 0; 29092 var P; 29093 while (length > i) dP.f(O, P = keys[i++], Properties[P]); 29094 return O; 29095}; 29096 29097 29098/***/ }), 29099/* 131 */ 29100/***/ (function(module, exports, __webpack_require__) { 29101 29102// false -> Array#indexOf 29103// true -> Array#includes 29104var toIObject = __webpack_require__(25);
vendor: 12,517 bytes, lines 29105-29496
29105var toLength = __webpack_require__(132); 29106var toAbsoluteIndex = __webpack_require__(133); 29107module.exports = function (IS_INCLUDES) { 29108 return function ($this, el, fromIndex) { 29109 var O = toIObject($this); 29110 var length = toLength(O.length); 29111 var index = toAbsoluteIndex(fromIndex, length); 29112 var value; 29113 // Array#includes uses SameValueZero equality algorithm 29114 // eslint-disable-next-line no-self-compare 29115 if (IS_INCLUDES && el != el) while (length > index) { 29116 value = O[index++]; 29117 // eslint-disable-next-line no-self-compare 29118 if (value != value) return true; 29119 // Array#indexOf ignores holes, Array#includes - not 29120 } else for (;length > index; index++) if (IS_INCLUDES || index in O) { 29121 if (O[index] === el) return IS_INCLUDES || index || 0; 29122 } return !IS_INCLUDES && -1; 29123 }; 29124}; 29125 29126 29127/***/ }), 29128/* 132 */ 29129/***/ (function(module, exports, __webpack_require__) { 29130 29131// 7.1.15 ToLength 29132var toInteger = __webpack_require__(55); 29133var min = Math.min; 29134module.exports = function (it) { 29135 return it > 0 ? min(toInteger(it), 0x1fffffffffffff) : 0; // pow(2, 53) - 1 == 9007199254740991 29136}; 29137 29138 29139/***/ }), 29140/* 133 */ 29141/***/ (function(module, exports, __webpack_require__) { 29142 29143var toInteger = __webpack_require__(55); 29144var max = Math.max; 29145var min = Math.min; 29146module.exports = function (index, length) { 29147 index = toInteger(index); 29148 return index < 0 ? max(index + length, 0) : min(index, length); 29149}; 29150 29151 29152/***/ }), 29153/* 134 */ 29154/***/ (function(module, exports, __webpack_require__) { 29155 29156var document = __webpack_require__(18).document; 29157module.exports = document && document.documentElement; 29158 29159 29160/***/ }), 29161/* 135 */ 29162/***/ (function(module, exports, __webpack_require__) { 29163 29164var toInteger = __webpack_require__(55); 29165var defined = __webpack_require__(51); 29166// true -> String#at 29167// false -> String#codePointAt 29168module.exports = function (TO_STRING) { 29169 return function (that, pos) { 29170 var s = String(defined(that)); 29171 var i = toInteger(pos); 29172 var l = s.length; 29173 var a, b; 29174 if (i < 0 || i >= l) return TO_STRING ? '' : undefined; 29175 a = s.charCodeAt(i); 29176 return a < 0xd800 || a > 0xdbff || i + 1 === l || (b = s.charCodeAt(i + 1)) < 0xdc00 || b > 0xdfff 29177 ? TO_STRING ? s.charAt(i) : a 29178 : TO_STRING ? s.slice(i, i + 2) : (a - 0xd800 << 10) + (b - 0xdc00) + 0x10000; 29179 }; 29180}; 29181 29182 29183/***/ }), 29184/* 136 */ 29185/***/ (function(module, exports, __webpack_require__) { 29186 29187var anObject = __webpack_require__(27); 29188var get = __webpack_require__(137); 29189module.exports = __webpack_require__(7).getIterator = function (it) { 29190 var iterFn = get(it); 29191 if (typeof iterFn != 'function') throw TypeError(it + ' is not iterable!'); 29192 return anObject(iterFn.call(it)); 29193}; 29194 29195 29196/***/ }), 29197/* 137 */ 29198/***/ (function(module, exports, __webpack_require__) { 29199 29200var classof = __webpack_require__(86); 29201var ITERATOR = __webpack_require__(13)('iterator'); 29202var Iterators = __webpack_require__(31); 29203module.exports = __webpack_require__(7).getIteratorMethod = function (it) { 29204 if (it != undefined) return it[ITERATOR] 29205 || it['@@iterator'] 29206 || Iterators[classof(it)]; 29207}; 29208 29209 29210/***/ }), 29211/* 138 */ 29212/***/ (function(module, exports, __webpack_require__) { 29213 29214__webpack_require__(139); 29215var $Object = __webpack_require__(7).Object; 29216module.exports = function create(P, D) { 29217 return $Object.create(P, D); 29218}; 29219 29220 29221/***/ }), 29222/* 139 */ 29223/***/ (function(module, exports, __webpack_require__) { 29224 29225var $export = __webpack_require__(17); 29226// 19.1.2.2 / 15.2.3.5 Object.create(O [, Properties]) 29227$export($export.S, 'Object', { create: __webpack_require__(54) }); 29228 29229 29230/***/ }), 29231/* 140 */ 29232/***/ (function(module, exports, __webpack_require__) { 29233 29234__webpack_require__(141); 29235module.exports = __webpack_require__(7).Object.keys; 29236 29237 29238/***/ }), 29239/* 141 */ 29240/***/ (function(module, exports, __webpack_require__) { 29241 29242// 19.1.2.14 Object.keys(O) 29243var toObject = __webpack_require__(41); 29244var $keys = __webpack_require__(33); 29245 29246__webpack_require__(87)('keys', function () { 29247 return function keys(it) { 29248 return $keys(toObject(it)); 29249 }; 29250}); 29251 29252 29253/***/ }), 29254/* 142 */ 29255/***/ (function(module, exports, __webpack_require__) { 29256 29257module.exports = { "default": __webpack_require__(143), __esModule: true }; 29258 29259/***/ }), 29260/* 143 */ 29261/***/ (function(module, exports, __webpack_require__) { 29262 29263__webpack_require__(60); 29264__webpack_require__(49); 29265module.exports = __webpack_require__(61).f('iterator'); 29266 29267 29268/***/ }), 29269/* 144 */ 29270/***/ (function(module, exports, __webpack_require__) { 29271 29272module.exports = { "default": __webpack_require__(145), __esModule: true }; 29273 29274/***/ }), 29275/* 145 */ 29276/***/ (function(module, exports, __webpack_require__) { 29277 29278__webpack_require__(146); 29279__webpack_require__(151); 29280__webpack_require__(152); 29281__webpack_require__(153); 29282module.exports = __webpack_require__(7).Symbol; 29283 29284 29285/***/ }), 29286/* 146 */ 29287/***/ (function(module, exports, __webpack_require__) { 29288 29289"use strict"; 29290 29291// ECMAScript 6 symbols shim 29292var global = __webpack_require__(18); 29293var has = __webpack_require__(22); 29294var DESCRIPTORS = __webpack_require__(21); 29295var $export = __webpack_require__(17); 29296var redefine = __webpack_require__(83); 29297var META = __webpack_require__(147).KEY; 29298var $fails = __webpack_require__(28); 29299var shared = __webpack_require__(57); 29300var setToStringTag = __webpack_require__(59); 29301var uid = __webpack_require__(40); 29302var wks = __webpack_require__(13); 29303var wksExt = __webpack_require__(61); 29304var wksDefine = __webpack_require__(62); 29305var enumKeys = __webpack_require__(148); 29306var isArray = __webpack_require__(149); 29307var anObject = __webpack_require__(27); 29308var toIObject = __webpack_require__(25); 29309var toPrimitive = __webpack_require__(53); 29310var createDesc = __webpack_require__(39); 29311var _create = __webpack_require__(54); 29312var gOPNExt = __webpack_require__(150); 29313var $GOPD = __webpack_require__(89); 29314var $DP = __webpack_require__(20); 29315var $keys = __webpack_require__(33); 29316var gOPD = $GOPD.f; 29317var dP = $DP.f; 29318var gOPN = gOPNExt.f; 29319var $Symbol = global.Symbol; 29320var $JSON = global.JSON; 29321var _stringify = $JSON && $JSON.stringify; 29322var PROTOTYPE = 'prototype'; 29323var HIDDEN = wks('_hidden'); 29324var TO_PRIMITIVE = wks('toPrimitive'); 29325var isEnum = {}.propertyIsEnumerable; 29326var SymbolRegistry = shared('symbol-registry'); 29327var AllSymbols = shared('symbols'); 29328var OPSymbols = shared('op-symbols'); 29329var ObjectProto = Object[PROTOTYPE]; 29330var USE_NATIVE = typeof $Symbol == 'function'; 29331var QObject = global.QObject; 29332// Don't use setters in Qt Script, https://github.com/zloirock/core-js/issues/173 29333var setter = !QObject || !QObject[PROTOTYPE] || !QObject[PROTOTYPE].findChild; 29334 29335// fallback for old Android, https://code.google.com/p/v8/issues/detail?id=687 29336var setSymbolDesc = DESCRIPTORS && $fails(function () { 29337 return _create(dP({}, 'a', { 29338 get: function () { return dP(this, 'a', { value: 7 }).a; } 29339 })).a != 7; 29340}) ? function (it, key, D) { 29341 var protoDesc = gOPD(ObjectProto, key); 29342 if (protoDesc) delete ObjectProto[key]; 29343 dP(it, key, D); 29344 if (protoDesc && it !== ObjectProto) dP(ObjectProto, key, protoDesc); 29345} : dP; 29346 29347var wrap = function (tag) { 29348 var sym = AllSymbols[tag] = _create($Symbol[PROTOTYPE]); 29349 sym._k = tag; 29350 return sym; 29351}; 29352 29353var isSymbol = USE_NATIVE && typeof $Symbol.iterator == 'symbol' ? function (it) { 29354 return typeof it == 'symbol'; 29355} : function (it) { 29356 return it instanceof $Symbol; 29357}; 29358 29359var $defineProperty = function defineProperty(it, key, D) { 29360 if (it === ObjectProto) $defineProperty(OPSymbols, key, D); 29361 anObject(it); 29362 key = toPrimitive(key, true); 29363 anObject(D); 29364 if (has(AllSymbols, key)) { 29365 if (!D.enumerable) { 29366 if (!has(it, HIDDEN)) dP(it, HIDDEN, createDesc(1, {})); 29367 it[HIDDEN][key] = true; 29368 } else { 29369 if (has(it, HIDDEN) && it[HIDDEN][key]) it[HIDDEN][key] = false; 29370 D = _create(D, { enumerable: createDesc(0, false) }); 29371 } return setSymbolDesc(it, key, D); 29372 } return dP(it, key, D); 29373}; 29374var $defineProperties = function defineProperties(it, P) { 29375 anObject(it); 29376 var keys = enumKeys(P = toIObject(P)); 29377 var i = 0; 29378 var l = keys.length; 29379 var key; 29380 while (l > i) $defineProperty(it, key = keys[i++], P[key]); 29381 return it; 29382}; 29383var $create = function create(it, P) { 29384 return P === undefined ? _create(it) : $defineProperties(_create(it), P); 29385}; 29386var $propertyIsEnumerable = function propertyIsEnumerable(key) { 29387 var E = isEnum.call(this, key = toPrimitive(key, true)); 29388 if (this === ObjectProto && has(AllSymbols, key) && !has(OPSymbols, key)) return false; 29389 return E || !has(this, key) || !has(AllSymbols, key) || has(this, HIDDEN) && this[HIDDEN][key] ? E : true; 29390}; 29391var $getOwnPropertyDescriptor = function getOwnPropertyDescriptor(it, key) { 29392 it = toIObject(it); 29393 key = toPrimitive(key, true); 29394 if (it === ObjectProto && has(AllSymbols, key) && !has(OPSymbols, key)) return; 29395 var D = gOPD(it, key); 29396 if (D && has(AllSymbols, key) && !(has(it, HIDDEN) && it[HIDDEN][key])) D.enumerable = true; 29397 return D; 29398}; 29399var $getOwnPropertyNames = function getOwnPropertyNames(it) { 29400 var names = gOPN(toIObject(it)); 29401 var result = []; 29402 var i = 0; 29403 var key; 29404 while (names.length > i) { 29405 if (!has(AllSymbols, key = names[i++]) && key != HIDDEN && key != META) result.push(key); 29406 } return result; 29407}; 29408var $getOwnPropertySymbols = function getOwnPropertySymbols(it) { 29409 var IS_OP = it === ObjectProto; 29410 var names = gOPN(IS_OP ? OPSymbols : toIObject(it)); 29411 var result = []; 29412 var i = 0; 29413 var key; 29414 while (names.length > i) { 29415 if (has(AllSymbols, key = names[i++]) && (IS_OP ? has(ObjectProto, key) : true)) result.push(AllSymbols[key]); 29416 } return result; 29417}; 29418 29419// 19.4.1.1 Symbol([description]) 29420if (!USE_NATIVE) { 29421 $Symbol = function Symbol() { 29422 if (this instanceof $Symbol) throw TypeError('Symbol is not a constructor!'); 29423 var tag = uid(arguments.length > 0 ? arguments[0] : undefined); 29424 var $set = function (value) { 29425 if (this === ObjectProto) $set.call(OPSymbols, value); 29426 if (has(this, HIDDEN) && has(this[HIDDEN], tag)) this[HIDDEN][tag] = false; 29427 setSymbolDesc(this, tag, createDesc(1, value)); 29428 }; 29429 if (DESCRIPTORS && setter) setSymbolDesc(ObjectProto, tag, { configurable: true, set: $set }); 29430 return wrap(tag); 29431 }; 29432 redefine($Symbol[PROTOTYPE], 'toString', function toString() { 29433 return this._k; 29434 }); 29435 29436 $GOPD.f = $getOwnPropertyDescriptor; 29437 $DP.f = $defineProperty; 29438 __webpack_require__(88).f = gOPNExt.f = $getOwnPropertyNames; 29439 __webpack_require__(42).f = $propertyIsEnumerable; 29440 __webpack_require__(63).f = $getOwnPropertySymbols; 29441 29442 if (DESCRIPTORS && !__webpack_require__(52)) { 29443 redefine(ObjectProto, 'propertyIsEnumerable', $propertyIsEnumerable, true); 29444 } 29445 29446 wksExt.f = function (name) { 29447 return wrap(wks(name)); 29448 }; 29449} 29450 29451$export($export.G + $export.W + $export.F * !USE_NATIVE, { Symbol: $Symbol }); 29452 29453for (var es6Symbols = ( 29454 // 19.4.2.2, 19.4.2.3, 19.4.2.4, 19.4.2.6, 19.4.2.8, 19.4.2.9, 19.4.2.10, 19.4.2.11, 19.4.2.12, 19.4.2.13, 19.4.2.14 29455 'hasInstance,isConcatSpreadable,iterator,match,replace,search,species,split,toPrimitive,toStringTag,unscopables' 29456).split(','), j = 0; es6Symbols.length > j;)wks(es6Symbols[j++]); 29457 29458for (var wellKnownSymbols = $keys(wks.store), k = 0; wellKnownSymbols.length > k;) wksDefine(wellKnownSymbols[k++]); 29459 29460$export($export.S + $export.F * !USE_NATIVE, 'Symbol', { 29461 // 19.4.2.1 Symbol.for(key) 29462 'for': function (key) { 29463 return has(SymbolRegistry, key += '') 29464 ? SymbolRegistry[key] 29465 : SymbolRegistry[key] = $Symbol(key); 29466 }, 29467 // 19.4.2.5 Symbol.keyFor(sym) 29468 keyFor: function keyFor(sym) { 29469 if (!isSymbol(sym)) throw TypeError(sym + ' is not a symbol!'); 29470 for (var key in SymbolRegistry) if (SymbolRegistry[key] === sym) return key; 29471 }, 29472 useSetter: function () { setter = true; }, 29473 useSimple: function () { setter = false; } 29474}); 29475 29476$export($export.S + $export.F * !USE_NATIVE, 'Object', { 29477 // 19.1.2.2 Object.create(O [, Properties]) 29478 create: $create, 29479 // 19.1.2.4 Object.defineProperty(O, P, Attributes) 29480 defineProperty: $defineProperty, 29481 // 19.1.2.3 Object.defineProperties(O, Properties) 29482 defineProperties: $defineProperties, 29483 // 19.1.2.6 Object.getOwnPropertyDescriptor(O, P) 29484 getOwnPropertyDescriptor: $getOwnPropertyDescriptor, 29485 // 19.1.2.7 Object.getOwnPropertyNames(O) 29486 getOwnPropertyNames: $getOwnPropertyNames, 29487 // 19.1.2.8 Object.getOwnPropertySymbols(O) 29488 getOwnPropertySymbols: $getOwnPropertySymbols 29489}); 29490 29491// 24.3.2 JSON.stringify(value [, replacer [, space]]) 29492$JSON && $export($export.S + $export.F * (!USE_NATIVE || $fails(function () { 29493 var S = $Symbol(); 29494 // MS Edge converts symbol values to JSON as {} 29495 // WebKit converts symbol values to JSON as null 29496 // V8 throws on boxed symbols
vendor: 4,237 bytes, lines 29497-29643
29497 return _stringify([S]) != '[null]' || _stringify({ a: S }) != '{}' || _stringify(Object(S)) != '{}'; 29498})), 'JSON', { 29499 stringify: function stringify(it) { 29500 if (it === undefined || isSymbol(it)) return; // IE8 returns string on undefined 29501 var args = [it]; 29502 var i = 1; 29503 var replacer, $replacer; 29504 while (arguments.length > i) args.push(arguments[i++]); 29505 replacer = args[1]; 29506 if (typeof replacer == 'function') $replacer = replacer; 29507 if ($replacer || !isArray(replacer)) replacer = function (key, value) { 29508 if ($replacer) value = $replacer.call(this, key, value); 29509 if (!isSymbol(value)) return value; 29510 }; 29511 args[1] = replacer; 29512 return _stringify.apply($JSON, args); 29513 } 29514}); 29515 29516// 19.4.3.4 Symbol.prototype[@@toPrimitive](hint) 29517$Symbol[PROTOTYPE][TO_PRIMITIVE] || __webpack_require__(26)($Symbol[PROTOTYPE], TO_PRIMITIVE, $Symbol[PROTOTYPE].valueOf); 29518// 19.4.3.5 Symbol.prototype[@@toStringTag] 29519setToStringTag($Symbol, 'Symbol'); 29520// 20.2.1.9 Math[@@toStringTag] 29521setToStringTag(Math, 'Math', true); 29522// 24.3.3 JSON[@@toStringTag] 29523setToStringTag(global.JSON, 'JSON', true); 29524 29525 29526/***/ }), 29527/* 147 */ 29528/***/ (function(module, exports, __webpack_require__) { 29529 29530var META = __webpack_require__(40)('meta'); 29531var isObject = __webpack_require__(32); 29532var has = __webpack_require__(22); 29533var setDesc = __webpack_require__(20).f; 29534var id = 0; 29535var isExtensible = Object.isExtensible || function () { 29536 return true; 29537}; 29538var FREEZE = !__webpack_require__(28)(function () { 29539 return isExtensible(Object.preventExtensions({})); 29540}); 29541var setMeta = function (it) { 29542 setDesc(it, META, { value: { 29543 i: 'O' + ++id, // object ID 29544 w: {} // weak collections IDs 29545 } }); 29546}; 29547var fastKey = function (it, create) { 29548 // return primitive with prefix 29549 if (!isObject(it)) return typeof it == 'symbol' ? it : (typeof it == 'string' ? 'S' : 'P') + it; 29550 if (!has(it, META)) { 29551 // can't set metadata to uncaught frozen object 29552 if (!isExtensible(it)) return 'F'; 29553 // not necessary to add metadata 29554 if (!create) return 'E'; 29555 // add missing metadata 29556 setMeta(it); 29557 // return object ID 29558 } return it[META].i; 29559}; 29560var getWeak = function (it, create) { 29561 if (!has(it, META)) { 29562 // can't set metadata to uncaught frozen object 29563 if (!isExtensible(it)) return true; 29564 // not necessary to add metadata 29565 if (!create) return false; 29566 // add missing metadata 29567 setMeta(it); 29568 // return hash weak collections IDs 29569 } return it[META].w; 29570}; 29571// add metadata on freeze-family methods calling 29572var onFreeze = function (it) { 29573 if (FREEZE && meta.NEED && isExtensible(it) && !has(it, META)) setMeta(it); 29574 return it; 29575}; 29576var meta = module.exports = { 29577 KEY: META, 29578 NEED: false, 29579 fastKey: fastKey, 29580 getWeak: getWeak, 29581 onFreeze: onFreeze 29582}; 29583 29584 29585/***/ }), 29586/* 148 */ 29587/***/ (function(module, exports, __webpack_require__) { 29588 29589// all enumerable object keys, includes symbols 29590var getKeys = __webpack_require__(33); 29591var gOPS = __webpack_require__(63); 29592var pIE = __webpack_require__(42); 29593module.exports = function (it) { 29594 var result = getKeys(it); 29595 var getSymbols = gOPS.f; 29596 if (getSymbols) { 29597 var symbols = getSymbols(it); 29598 var isEnum = pIE.f; 29599 var i = 0; 29600 var key; 29601 while (symbols.length > i) if (isEnum.call(it, key = symbols[i++])) result.push(key); 29602 } return result; 29603}; 29604 29605 29606/***/ }), 29607/* 149 */ 29608/***/ (function(module, exports, __webpack_require__) { 29609 29610// 7.2.2 IsArray(argument) 29611var cof = __webpack_require__(50); 29612module.exports = Array.isArray || function isArray(arg) { 29613 return cof(arg) == 'Array'; 29614}; 29615 29616 29617/***/ }), 29618/* 150 */ 29619/***/ (function(module, exports, __webpack_require__) { 29620 29621// fallback for IE11 buggy Object.getOwnPropertyNames with iframe and window 29622var toIObject = __webpack_require__(25); 29623var gOPN = __webpack_require__(88).f; 29624var toString = {}.toString; 29625 29626var windowNames = typeof window == 'object' && window && Object.getOwnPropertyNames 29627 ? Object.getOwnPropertyNames(window) : []; 29628 29629var getWindowNames = function (it) { 29630 try { 29631 return gOPN(it); 29632 } catch (e) { 29633 return windowNames.slice(); 29634 } 29635}; 29636 29637module.exports.f = function getOwnPropertyNames(it) { 29638 return windowNames && toString.call(it) == '[object Window]' ? getWindowNames(it) : gOPN(toIObject(it)); 29639}; 29640 29641 29642/***/ }), 29643/* 151 */
vendor: 6,171 bytes, lines 29644-29902
29644/***/ (function(module, exports) { 29645 29646 29647 29648/***/ }), 29649/* 152 */ 29650/***/ (function(module, exports, __webpack_require__) { 29651 29652__webpack_require__(62)('asyncIterator'); 29653 29654 29655/***/ }), 29656/* 153 */ 29657/***/ (function(module, exports, __webpack_require__) { 29658 29659__webpack_require__(62)('observable'); 29660 29661 29662/***/ }), 29663/* 154 */ 29664/***/ (function(module, exports, __webpack_require__) { 29665 29666/* WEBPACK VAR INJECTION */(function(module) {var require;//! moment.js 29667//! version : 2.19.1 29668//! authors : Tim Wood, Iskren Chernev, Moment.js contributors 29669//! license : MIT 29670//! momentjs.com 29671 29672;(function (global, factory) { 29673 true ? module.exports = factory() : 29674 typeof define === 'function' && define.amd ? define(factory) : 29675 global.moment = factory() 29676}(this, (function () { 'use strict'; 29677 29678var hookCallback; 29679 29680function hooks () { 29681 return hookCallback.apply(null, arguments); 29682} 29683 29684// This is done to register the method called with moment() 29685// without creating circular dependencies. 29686function setHookCallback (callback) { 29687 hookCallback = callback; 29688} 29689 29690function isArray(input) { 29691 return input instanceof Array || Object.prototype.toString.call(input) === '[object Array]'; 29692} 29693 29694function isObject(input) { 29695 // IE8 will treat undefined and null as object if it wasn't for 29696 // input != null 29697 return input != null && Object.prototype.toString.call(input) === '[object Object]'; 29698} 29699 29700function isObjectEmpty(obj) { 29701 if (Object.getOwnPropertyNames) { 29702 return (Object.getOwnPropertyNames(obj).length === 0); 29703 } else { 29704 var k; 29705 for (k in obj) { 29706 if (obj.hasOwnProperty(k)) { 29707 return false; 29708 } 29709 } 29710 return true; 29711 } 29712} 29713 29714function isUndefined(input) { 29715 return input === void 0; 29716} 29717 29718function isNumber(input) { 29719 return typeof input === 'number' || Object.prototype.toString.call(input) === '[object Number]'; 29720} 29721 29722function isDate(input) { 29723 return input instanceof Date || Object.prototype.toString.call(input) === '[object Date]'; 29724} 29725 29726function map(arr, fn) { 29727 var res = [], i; 29728 for (i = 0; i < arr.length; ++i) { 29729 res.push(fn(arr[i], i)); 29730 } 29731 return res; 29732} 29733 29734function hasOwnProp(a, b) { 29735 return Object.prototype.hasOwnProperty.call(a, b); 29736} 29737 29738function extend(a, b) { 29739 for (var i in b) { 29740 if (hasOwnProp(b, i)) { 29741 a[i] = b[i]; 29742 } 29743 } 29744 29745 if (hasOwnProp(b, 'toString')) { 29746 a.toString = b.toString; 29747 } 29748 29749 if (hasOwnProp(b, 'valueOf')) { 29750 a.valueOf = b.valueOf; 29751 } 29752 29753 return a; 29754} 29755 29756function createUTC (input, format, locale, strict) { 29757 return createLocalOrUTC(input, format, locale, strict, true).utc(); 29758} 29759 29760function defaultParsingFlags() { 29761 // We need to deep clone this object. 29762 return { 29763 empty : false, 29764 unusedTokens : [], 29765 unusedInput : [], 29766 overflow : -2, 29767 charsLeftOver : 0, 29768 nullInput : false, 29769 invalidMonth : null, 29770 invalidFormat : false, 29771 userInvalidated : false, 29772 iso : false, 29773 parsedDateParts : [], 29774 meridiem : null, 29775 rfc2822 : false, 29776 weekdayMismatch : false 29777 }; 29778} 29779 29780function getParsingFlags(m) { 29781 if (m._pf == null) { 29782 m._pf = defaultParsingFlags(); 29783 } 29784 return m._pf; 29785} 29786 29787var some; 29788if (Array.prototype.some) { 29789 some = Array.prototype.some; 29790} else { 29791 some = function (fun) { 29792 var t = Object(this); 29793 var len = t.length >>> 0; 29794 29795 for (var i = 0; i < len; i++) { 29796 if (i in t && fun.call(this, t[i], i, t)) { 29797 return true; 29798 } 29799 } 29800 29801 return false; 29802 }; 29803} 29804 29805function isValid(m) { 29806 if (m._isValid == null) { 29807 var flags = getParsingFlags(m); 29808 var parsedParts = some.call(flags.parsedDateParts, function (i) { 29809 return i != null; 29810 }); 29811 var isNowValid = !isNaN(m._d.getTime()) && 29812 flags.overflow < 0 && 29813 !flags.empty && 29814 !flags.invalidMonth && 29815 !flags.invalidWeekday && 29816 !flags.weekdayMismatch && 29817 !flags.nullInput && 29818 !flags.invalidFormat && 29819 !flags.userInvalidated && 29820 (!flags.meridiem || (flags.meridiem && parsedParts)); 29821 29822 if (m._strict) { 29823 isNowValid = isNowValid && 29824 flags.charsLeftOver === 0 && 29825 flags.unusedTokens.length === 0 && 29826 flags.bigHour === undefined; 29827 } 29828 29829 if (Object.isFrozen == null || !Object.isFrozen(m)) { 29830 m._isValid = isNowValid; 29831 } 29832 else { 29833 return isNowValid; 29834 } 29835 } 29836 return m._isValid; 29837} 29838 29839function createInvalid (flags) { 29840 var m = createUTC(NaN); 29841 if (flags != null) { 29842 extend(getParsingFlags(m), flags); 29843 } 29844 else { 29845 getParsingFlags(m).userInvalidated = true; 29846 } 29847 29848 return m; 29849} 29850 29851// Plugins that add properties should also add the key here (null value), 29852// so we can properly clone ourselves. 29853var momentProperties = hooks.momentProperties = []; 29854 29855function copyConfig(to, from) { 29856 var i, prop, val; 29857 29858 if (!isUndefined(from._isAMomentObject)) { 29859 to._isAMomentObject = from._isAMomentObject; 29860 } 29861 if (!isUndefined(from._i)) { 29862 to._i = from._i; 29863 } 29864 if (!isUndefined(from._f)) { 29865 to._f = from._f; 29866 } 29867 if (!isUndefined(from._l)) { 29868 to._l = from._l; 29869 } 29870 if (!isUndefined(from._strict)) { 29871 to._strict = from._strict; 29872 } 29873 if (!isUndefined(from._tzm)) { 29874 to._tzm = from._tzm; 29875 } 29876 if (!isUndefined(from._isUTC)) { 29877 to._isUTC = from._isUTC; 29878 } 29879 if (!isUndefined(from._offset)) { 29880 to._offset = from._offset; 29881 } 29882 if (!isUndefined(from._pf)) { 29883 to._pf = getParsingFlags(from); 29884 } 29885 if (!isUndefined(from._locale)) { 29886 to._locale = from._locale; 29887 } 29888 29889 if (momentProperties.length > 0) { 29890 for (i = 0; i < momentProperties.length; i++) { 29891 prop = momentProperties[i]; 29892 val = from[prop]; 29893 if (!isUndefined(val)) { 29894 to[prop] = val; 29895 } 29896 } 29897 } 29898 29899 return to; 29900} 29901 29902var updateInProgress = false;
vendor: 60,756 bytes, lines 29903-31940
29903 29904// Moment prototype object 29905function Moment(config) { 29906 copyConfig(this, config); 29907 this._d = new Date(config._d != null ? config._d.getTime() : NaN); 29908 if (!this.isValid()) { 29909 this._d = new Date(NaN); 29910 } 29911 // Prevent infinite loop in case updateOffset creates new moment 29912 // objects. 29913 if (updateInProgress === false) { 29914 updateInProgress = true; 29915 hooks.updateOffset(this); 29916 updateInProgress = false; 29917 } 29918} 29919 29920function isMoment (obj) { 29921 return obj instanceof Moment || (obj != null && obj._isAMomentObject != null); 29922} 29923 29924function absFloor (number) { 29925 if (number < 0) { 29926 // -0 -> 0 29927 return Math.ceil(number) || 0; 29928 } else { 29929 return Math.floor(number); 29930 } 29931} 29932 29933function toInt(argumentForCoercion) { 29934 var coercedNumber = +argumentForCoercion, 29935 value = 0; 29936 29937 if (coercedNumber !== 0 && isFinite(coercedNumber)) { 29938 value = absFloor(coercedNumber); 29939 } 29940 29941 return value; 29942} 29943 29944// compare two arrays, return the number of differences 29945function compareArrays(array1, array2, dontConvert) { 29946 var len = Math.min(array1.length, array2.length), 29947 lengthDiff = Math.abs(array1.length - array2.length), 29948 diffs = 0, 29949 i; 29950 for (i = 0; i < len; i++) { 29951 if ((dontConvert && array1[i] !== array2[i]) || 29952 (!dontConvert && toInt(array1[i]) !== toInt(array2[i]))) { 29953 diffs++; 29954 } 29955 } 29956 return diffs + lengthDiff; 29957} 29958 29959function warn(msg) { 29960 if (hooks.suppressDeprecationWarnings === false && 29961 (typeof console !== 'undefined') && console.warn) { 29962 console.warn('Deprecation warning: ' + msg); 29963 } 29964} 29965 29966function deprecate(msg, fn) { 29967 var firstTime = true; 29968 29969 return extend(function () { 29970 if (hooks.deprecationHandler != null) { 29971 hooks.deprecationHandler(null, msg); 29972 } 29973 if (firstTime) { 29974 var args = []; 29975 var arg; 29976 for (var i = 0; i < arguments.length; i++) { 29977 arg = ''; 29978 if (typeof arguments[i] === 'object') { 29979 arg += '\n[' + i + '] '; 29980 for (var key in arguments[0]) { 29981 arg += key + ': ' + arguments[0][key] + ', '; 29982 } 29983 arg = arg.slice(0, -2); // Remove trailing comma and space 29984 } else { 29985 arg = arguments[i]; 29986 } 29987 args.push(arg); 29988 } 29989 warn(msg + '\nArguments: ' + Array.prototype.slice.call(args).join('') + '\n' + (new Error()).stack); 29990 firstTime = false; 29991 } 29992 return fn.apply(this, arguments); 29993 }, fn); 29994} 29995 29996var deprecations = {}; 29997 29998function deprecateSimple(name, msg) { 29999 if (hooks.deprecationHandler != null) { 30000 hooks.deprecationHandler(name, msg); 30001 } 30002 if (!deprecations[name]) { 30003 warn(msg); 30004 deprecations[name] = true; 30005 } 30006} 30007 30008hooks.suppressDeprecationWarnings = false; 30009hooks.deprecationHandler = null; 30010 30011function isFunction(input) { 30012 return input instanceof Function || Object.prototype.toString.call(input) === '[object Function]'; 30013} 30014 30015function set (config) { 30016 var prop, i; 30017 for (i in config) { 30018 prop = config[i]; 30019 if (isFunction(prop)) { 30020 this[i] = prop; 30021 } else { 30022 this['_' + i] = prop; 30023 } 30024 } 30025 this._config = config; 30026 // Lenient ordinal parsing accepts just a number in addition to 30027 // number + (possibly) stuff coming from _dayOfMonthOrdinalParse. 30028 // TODO: Remove "ordinalParse" fallback in next major release. 30029 this._dayOfMonthOrdinalParseLenient = new RegExp( 30030 (this._dayOfMonthOrdinalParse.source || this._ordinalParse.source) + 30031 '|' + (/\d{1,2}/).source); 30032} 30033 30034function mergeConfigs(parentConfig, childConfig) { 30035 var res = extend({}, parentConfig), prop; 30036 for (prop in childConfig) { 30037 if (hasOwnProp(childConfig, prop)) { 30038 if (isObject(parentConfig[prop]) && isObject(childConfig[prop])) { 30039 res[prop] = {}; 30040 extend(res[prop], parentConfig[prop]); 30041 extend(res[prop], childConfig[prop]); 30042 } else if (childConfig[prop] != null) { 30043 res[prop] = childConfig[prop]; 30044 } else { 30045 delete res[prop]; 30046 } 30047 } 30048 } 30049 for (prop in parentConfig) { 30050 if (hasOwnProp(parentConfig, prop) && 30051 !hasOwnProp(childConfig, prop) && 30052 isObject(parentConfig[prop])) { 30053 // make sure changes to properties don't modify parent config 30054 res[prop] = extend({}, res[prop]); 30055 } 30056 } 30057 return res; 30058} 30059 30060function Locale(config) { 30061 if (config != null) { 30062 this.set(config); 30063 } 30064} 30065 30066var keys; 30067 30068if (Object.keys) { 30069 keys = Object.keys; 30070} else { 30071 keys = function (obj) { 30072 var i, res = []; 30073 for (i in obj) { 30074 if (hasOwnProp(obj, i)) { 30075 res.push(i); 30076 } 30077 } 30078 return res; 30079 }; 30080} 30081 30082var defaultCalendar = { 30083 sameDay : '[Today at] LT', 30084 nextDay : '[Tomorrow at] LT', 30085 nextWeek : 'dddd [at] LT', 30086 lastDay : '[Yesterday at] LT', 30087 lastWeek : '[Last] dddd [at] LT', 30088 sameElse : 'L' 30089}; 30090 30091function calendar (key, mom, now) { 30092 var output = this._calendar[key] || this._calendar['sameElse']; 30093 return isFunction(output) ? output.call(mom, now) : output; 30094} 30095 30096var defaultLongDateFormat = { 30097 LTS : 'h:mm:ss A', 30098 LT : 'h:mm A', 30099 L : 'MM/DD/YYYY', 30100 LL : 'MMMM D, YYYY', 30101 LLL : 'MMMM D, YYYY h:mm A', 30102 LLLL : 'dddd, MMMM D, YYYY h:mm A' 30103}; 30104 30105function longDateFormat (key) { 30106 var format = this._longDateFormat[key], 30107 formatUpper = this._longDateFormat[key.toUpperCase()]; 30108 30109 if (format || !formatUpper) { 30110 return format; 30111 } 30112 30113 this._longDateFormat[key] = formatUpper.replace(/MMMM|MM|DD|dddd/g, function (val) { 30114 return val.slice(1); 30115 }); 30116 30117 return this._longDateFormat[key]; 30118} 30119 30120var defaultInvalidDate = 'Invalid date'; 30121 30122function invalidDate () { 30123 return this._invalidDate; 30124} 30125 30126var defaultOrdinal = '%d'; 30127var defaultDayOfMonthOrdinalParse = /\d{1,2}/; 30128 30129function ordinal (number) { 30130 return this._ordinal.replace('%d', number); 30131} 30132 30133var defaultRelativeTime = { 30134 future : 'in %s', 30135 past : '%s ago', 30136 s : 'a few seconds', 30137 ss : '%d seconds', 30138 m : 'a minute', 30139 mm : '%d minutes', 30140 h : 'an hour', 30141 hh : '%d hours', 30142 d : 'a day', 30143 dd : '%d days', 30144 M : 'a month', 30145 MM : '%d months', 30146 y : 'a year', 30147 yy : '%d years' 30148}; 30149 30150function relativeTime (number, withoutSuffix, string, isFuture) { 30151 var output = this._relativeTime[string]; 30152 return (isFunction(output)) ? 30153 output(number, withoutSuffix, string, isFuture) : 30154 output.replace(/%d/i, number); 30155} 30156 30157function pastFuture (diff, output) { 30158 var format = this._relativeTime[diff > 0 ? 'future' : 'past']; 30159 return isFunction(format) ? format(output) : format.replace(/%s/i, output); 30160} 30161 30162var aliases = {}; 30163 30164function addUnitAlias (unit, shorthand) { 30165 var lowerCase = unit.toLowerCase(); 30166 aliases[lowerCase] = aliases[lowerCase + 's'] = aliases[shorthand] = unit; 30167} 30168 30169function normalizeUnits(units) { 30170 return typeof units === 'string' ? aliases[units] || aliases[units.toLowerCase()] : undefined; 30171} 30172 30173function normalizeObjectUnits(inputObject) { 30174 var normalizedInput = {}, 30175 normalizedProp, 30176 prop; 30177 30178 for (prop in inputObject) { 30179 if (hasOwnProp(inputObject, prop)) { 30180 normalizedProp = normalizeUnits(prop); 30181 if (normalizedProp) { 30182 normalizedInput[normalizedProp] = inputObject[prop]; 30183 } 30184 } 30185 } 30186 30187 return normalizedInput; 30188} 30189 30190var priorities = {}; 30191 30192function addUnitPriority(unit, priority) { 30193 priorities[unit] = priority; 30194} 30195 30196function getPrioritizedUnits(unitsObj) { 30197 var units = []; 30198 for (var u in unitsObj) { 30199 units.push({unit: u, priority: priorities[u]}); 30200 } 30201 units.sort(function (a, b) { 30202 return a.priority - b.priority; 30203 }); 30204 return units; 30205} 30206 30207function zeroFill(number, targetLength, forceSign) { 30208 var absNumber = '' + Math.abs(number), 30209 zerosToFill = targetLength - absNumber.length, 30210 sign = number >= 0; 30211 return (sign ? (forceSign ? '+' : '') : '-') + 30212 Math.pow(10, Math.max(0, zerosToFill)).toString().substr(1) + absNumber; 30213} 30214 30215var formattingTokens = /(\[[^\[]*\])|(\\)?([Hh]mm(ss)?|Mo|MM?M?M?|Do|DDDo|DD?D?D?|ddd?d?|do?|w[o|w]?|W[o|W]?|Qo?|YYYYYY|YYYYY|YYYY|YY|gg(ggg?)?|GG(GGG?)?|e|E|a|A|hh?|HH?|kk?|mm?|ss?|S{1,9}|x|X|zz?|ZZ?|.)/g; 30216 30217var localFormattingTokens = /(\[[^\[]*\])|(\\)?(LTS|LT|LL?L?L?|l{1,4})/g; 30218 30219var formatFunctions = {}; 30220 30221var formatTokenFunctions = {}; 30222 30223// token: 'M' 30224// padded: ['MM', 2] 30225// ordinal: 'Mo' 30226// callback: function () { this.month() + 1 } 30227function addFormatToken (token, padded, ordinal, callback) { 30228 var func = callback; 30229 if (typeof callback === 'string') { 30230 func = function () { 30231 return this[callback](); 30232 }; 30233 } 30234 if (token) { 30235 formatTokenFunctions[token] = func; 30236 } 30237 if (padded) { 30238 formatTokenFunctions[padded[0]] = function () { 30239 return zeroFill(func.apply(this, arguments), padded[1], padded[2]); 30240 }; 30241 } 30242 if (ordinal) { 30243 formatTokenFunctions[ordinal] = function () { 30244 return this.localeData().ordinal(func.apply(this, arguments), token); 30245 }; 30246 } 30247} 30248 30249function removeFormattingTokens(input) { 30250 if (input.match(/\[[\s\S]/)) { 30251 return input.replace(/^\[|\]$/g, ''); 30252 } 30253 return input.replace(/\\/g, ''); 30254} 30255 30256function makeFormatFunction(format) { 30257 var array = format.match(formattingTokens), i, length; 30258 30259 for (i = 0, length = array.length; i < length; i++) { 30260 if (formatTokenFunctions[array[i]]) { 30261 array[i] = formatTokenFunctions[array[i]]; 30262 } else { 30263 array[i] = removeFormattingTokens(array[i]); 30264 } 30265 } 30266 30267 return function (mom) { 30268 var output = '', i; 30269 for (i = 0; i < length; i++) { 30270 output += isFunction(array[i]) ? array[i].call(mom, format) : array[i]; 30271 } 30272 return output; 30273 }; 30274} 30275 30276// format date using native date object 30277function formatMoment(m, format) { 30278 if (!m.isValid()) { 30279 return m.localeData().invalidDate(); 30280 } 30281 30282 format = expandFormat(format, m.localeData()); 30283 formatFunctions[format] = formatFunctions[format] || makeFormatFunction(format); 30284 30285 return formatFunctions[format](m); 30286} 30287 30288function expandFormat(format, locale) { 30289 var i = 5; 30290 30291 function replaceLongDateFormatTokens(input) { 30292 return locale.longDateFormat(input) || input; 30293 } 30294 30295 localFormattingTokens.lastIndex = 0; 30296 while (i >= 0 && localFormattingTokens.test(format)) { 30297 format = format.replace(localFormattingTokens, replaceLongDateFormatTokens); 30298 localFormattingTokens.lastIndex = 0; 30299 i -= 1; 30300 } 30301 30302 return format; 30303} 30304 30305var match1 = /\d/; // 0 - 9 30306var match2 = /\d\d/; // 00 - 99 30307var match3 = /\d{3}/; // 000 - 999 30308var match4 = /\d{4}/; // 0000 - 9999 30309var match6 = /[+-]?\d{6}/; // -999999 - 999999 30310var match1to2 = /\d\d?/; // 0 - 99 30311var match3to4 = /\d\d\d\d?/; // 999 - 9999 30312var match5to6 = /\d\d\d\d\d\d?/; // 99999 - 999999 30313var match1to3 = /\d{1,3}/; // 0 - 999 30314var match1to4 = /\d{1,4}/; // 0 - 9999 30315var match1to6 = /[+-]?\d{1,6}/; // -999999 - 999999 30316 30317var matchUnsigned = /\d+/; // 0 - inf 30318var matchSigned = /[+-]?\d+/; // -inf - inf 30319 30320var matchOffset = /Z|[+-]\d\d:?\d\d/gi; // +00:00 -00:00 +0000 -0000 or Z 30321var matchShortOffset = /Z|[+-]\d\d(?::?\d\d)?/gi; // +00 -00 +00:00 -00:00 +0000 -0000 or Z 30322 30323var matchTimestamp = /[+-]?\d+(\.\d{1,3})?/; // 123456789 123456789.123 30324 30325// any word (or two) characters or numbers including two/three word month in arabic. 30326// includes scottish gaelic two word and hyphenated months 30327var matchWord = /[0-9]*['a-z\u00A0-\u05FF\u0700-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+|[\u0600-\u06FF\/]+(\s*?[\u0600-\u06FF]+){1,2}/i; 30328 30329 30330var regexes = {}; 30331 30332function addRegexToken (token, regex, strictRegex) { 30333 regexes[token] = isFunction(regex) ? regex : function (isStrict, localeData) { 30334 return (isStrict && strictRegex) ? strictRegex : regex; 30335 }; 30336} 30337 30338function getParseRegexForToken (token, config) { 30339 if (!hasOwnProp(regexes, token)) { 30340 return new RegExp(unescapeFormat(token)); 30341 } 30342 30343 return regexes[token](config._strict, config._locale); 30344} 30345 30346// Code from http://stackoverflow.com/questions/3561493/is-there-a-regexp-escape-function-in-javascript 30347function unescapeFormat(s) { 30348 return regexEscape(s.replace('\\', '').replace(/\\(\[)|\\(\])|\[([^\]\[]*)\]|\\(.)/g, function (matched, p1, p2, p3, p4) { 30349 return p1 || p2 || p3 || p4; 30350 })); 30351} 30352 30353function regexEscape(s) { 30354 return s.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&'); 30355} 30356 30357var tokens = {}; 30358 30359function addParseToken (token, callback) { 30360 var i, func = callback; 30361 if (typeof token === 'string') { 30362 token = [token]; 30363 } 30364 if (isNumber(callback)) { 30365 func = function (input, array) { 30366 array[callback] = toInt(input); 30367 }; 30368 } 30369 for (i = 0; i < token.length; i++) { 30370 tokens[token[i]] = func; 30371 } 30372} 30373 30374function addWeekParseToken (token, callback) { 30375 addParseToken(token, function (input, array, config, token) { 30376 config._w = config._w || {}; 30377 callback(input, config._w, config, token); 30378 }); 30379} 30380 30381function addTimeToArrayFromToken(token, input, config) { 30382 if (input != null && hasOwnProp(tokens, token)) { 30383 tokens[token](input, config._a, config, token); 30384 } 30385} 30386 30387var YEAR = 0; 30388var MONTH = 1; 30389var DATE = 2; 30390var HOUR = 3; 30391var MINUTE = 4; 30392var SECOND = 5; 30393var MILLISECOND = 6; 30394var WEEK = 7; 30395var WEEKDAY = 8; 30396 30397// FORMATTING 30398 30399addFormatToken('Y', 0, 0, function () { 30400 var y = this.year(); 30401 return y <= 9999 ? '' + y : '+' + y; 30402}); 30403 30404addFormatToken(0, ['YY', 2], 0, function () { 30405 return this.year() % 100; 30406}); 30407 30408addFormatToken(0, ['YYYY', 4], 0, 'year'); 30409addFormatToken(0, ['YYYYY', 5], 0, 'year'); 30410addFormatToken(0, ['YYYYYY', 6, true], 0, 'year'); 30411 30412// ALIASES 30413 30414addUnitAlias('year', 'y'); 30415 30416// PRIORITIES 30417 30418addUnitPriority('year', 1); 30419 30420// PARSING 30421 30422addRegexToken('Y', matchSigned); 30423addRegexToken('YY', match1to2, match2); 30424addRegexToken('YYYY', match1to4, match4); 30425addRegexToken('YYYYY', match1to6, match6); 30426addRegexToken('YYYYYY', match1to6, match6); 30427 30428addParseToken(['YYYYY', 'YYYYYY'], YEAR); 30429addParseToken('YYYY', function (input, array) { 30430 array[YEAR] = input.length === 2 ? hooks.parseTwoDigitYear(input) : toInt(input); 30431}); 30432addParseToken('YY', function (input, array) { 30433 array[YEAR] = hooks.parseTwoDigitYear(input); 30434}); 30435addParseToken('Y', function (input, array) { 30436 array[YEAR] = parseInt(input, 10); 30437}); 30438 30439// HELPERS 30440 30441function daysInYear(year) { 30442 return isLeapYear(year) ? 366 : 365; 30443} 30444 30445function isLeapYear(year) { 30446 return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0; 30447} 30448 30449// HOOKS 30450 30451hooks.parseTwoDigitYear = function (input) { 30452 return toInt(input) + (toInt(input) > 68 ? 1900 : 2000); 30453}; 30454 30455// MOMENTS 30456 30457var getSetYear = makeGetSet('FullYear', true); 30458 30459function getIsLeapYear () { 30460 return isLeapYear(this.year()); 30461} 30462 30463function makeGetSet (unit, keepTime) { 30464 return function (value) { 30465 if (value != null) { 30466 set$1(this, unit, value); 30467 hooks.updateOffset(this, keepTime); 30468 return this; 30469 } else { 30470 return get(this, unit); 30471 } 30472 }; 30473} 30474 30475function get (mom, unit) { 30476 return mom.isValid() ? 30477 mom._d['get' + (mom._isUTC ? 'UTC' : '') + unit]() : NaN; 30478} 30479 30480function set$1 (mom, unit, value) { 30481 if (mom.isValid() && !isNaN(value)) { 30482 if (unit === 'FullYear' && isLeapYear(mom.year())) { 30483 mom._d['set' + (mom._isUTC ? 'UTC' : '') + unit](value, mom.month(), daysInMonth(value, mom.month())); 30484 } 30485 else { 30486 mom._d['set' + (mom._isUTC ? 'UTC' : '') + unit](value); 30487 } 30488 } 30489} 30490 30491// MOMENTS 30492 30493function stringGet (units) { 30494 units = normalizeUnits(units); 30495 if (isFunction(this[units])) { 30496 return this[units](); 30497 } 30498 return this; 30499} 30500 30501 30502function stringSet (units, value) { 30503 if (typeof units === 'object') { 30504 units = normalizeObjectUnits(units); 30505 var prioritized = getPrioritizedUnits(units); 30506 for (var i = 0; i < prioritized.length; i++) { 30507 this[prioritized[i].unit](units[prioritized[i].unit]); 30508 } 30509 } else { 30510 units = normalizeUnits(units); 30511 if (isFunction(this[units])) { 30512 return this[units](value); 30513 } 30514 } 30515 return this; 30516} 30517 30518function mod(n, x) { 30519 return ((n % x) + x) % x; 30520} 30521 30522var indexOf; 30523 30524if (Array.prototype.indexOf) { 30525 indexOf = Array.prototype.indexOf; 30526} else { 30527 indexOf = function (o) { 30528 // I know 30529 var i; 30530 for (i = 0; i < this.length; ++i) { 30531 if (this[i] === o) { 30532 return i; 30533 } 30534 } 30535 return -1; 30536 }; 30537} 30538 30539function daysInMonth(year, month) { 30540 if (isNaN(year) || isNaN(month)) { 30541 return NaN; 30542 } 30543 var modMonth = mod(month, 12); 30544 year += (month - modMonth) / 12; 30545 return modMonth === 1 ? (isLeapYear(year) ? 29 : 28) : (31 - modMonth % 7 % 2); 30546} 30547 30548// FORMATTING 30549 30550addFormatToken('M', ['MM', 2], 'Mo', function () { 30551 return this.month() + 1; 30552}); 30553 30554addFormatToken('MMM', 0, 0, function (format) { 30555 return this.localeData().monthsShort(this, format); 30556}); 30557 30558addFormatToken('MMMM', 0, 0, function (format) { 30559 return this.localeData().months(this, format); 30560}); 30561 30562// ALIASES 30563 30564addUnitAlias('month', 'M'); 30565 30566// PRIORITY 30567 30568addUnitPriority('month', 8); 30569 30570// PARSING 30571 30572addRegexToken('M', match1to2); 30573addRegexToken('MM', match1to2, match2); 30574addRegexToken('MMM', function (isStrict, locale) { 30575 return locale.monthsShortRegex(isStrict); 30576}); 30577addRegexToken('MMMM', function (isStrict, locale) { 30578 return locale.monthsRegex(isStrict); 30579}); 30580 30581addParseToken(['M', 'MM'], function (input, array) { 30582 array[MONTH] = toInt(input) - 1; 30583}); 30584 30585addParseToken(['MMM', 'MMMM'], function (input, array, config, token) { 30586 var month = config._locale.monthsParse(input, token, config._strict); 30587 // if we didn't find a month name, mark the date as invalid. 30588 if (month != null) { 30589 array[MONTH] = month; 30590 } else { 30591 getParsingFlags(config).invalidMonth = input; 30592 } 30593}); 30594 30595// LOCALES 30596 30597var MONTHS_IN_FORMAT = /D[oD]?(\[[^\[\]]*\]|\s)+MMMM?/; 30598var defaultLocaleMonths = 'January_February_March_April_May_June_July_August_September_October_November_December'.split('_'); 30599function localeMonths (m, format) { 30600 if (!m) { 30601 return isArray(this._months) ? this._months : 30602 this._months['standalone']; 30603 } 30604 return isArray(this._months) ? this._months[m.month()] : 30605 this._months[(this._months.isFormat || MONTHS_IN_FORMAT).test(format) ? 'format' : 'standalone'][m.month()]; 30606} 30607 30608var defaultLocaleMonthsShort = 'Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec'.split('_'); 30609function localeMonthsShort (m, format) { 30610 if (!m) { 30611 return isArray(this._monthsShort) ? this._monthsShort : 30612 this._monthsShort['standalone']; 30613 } 30614 return isArray(this._monthsShort) ? this._monthsShort[m.month()] : 30615 this._monthsShort[MONTHS_IN_FORMAT.test(format) ? 'format' : 'standalone'][m.month()]; 30616} 30617 30618function handleStrictParse(monthName, format, strict) { 30619 var i, ii, mom, llc = monthName.toLocaleLowerCase(); 30620 if (!this._monthsParse) { 30621 // this is not used 30622 this._monthsParse = []; 30623 this._longMonthsParse = []; 30624 this._shortMonthsParse = []; 30625 for (i = 0; i < 12; ++i) { 30626 mom = createUTC([2000, i]); 30627 this._shortMonthsParse[i] = this.monthsShort(mom, '').toLocaleLowerCase(); 30628 this._longMonthsParse[i] = this.months(mom, '').toLocaleLowerCase(); 30629 } 30630 } 30631 30632 if (strict) { 30633 if (format === 'MMM') { 30634 ii = indexOf.call(this._shortMonthsParse, llc); 30635 return ii !== -1 ? ii : null; 30636 } else { 30637 ii = indexOf.call(this._longMonthsParse, llc); 30638 return ii !== -1 ? ii : null; 30639 } 30640 } else { 30641 if (format === 'MMM') { 30642 ii = indexOf.call(this._shortMonthsParse, llc); 30643 if (ii !== -1) { 30644 return ii; 30645 } 30646 ii = indexOf.call(this._longMonthsParse, llc); 30647 return ii !== -1 ? ii : null; 30648 } else { 30649 ii = indexOf.call(this._longMonthsParse, llc); 30650 if (ii !== -1) { 30651 return ii; 30652 } 30653 ii = indexOf.call(this._shortMonthsParse, llc); 30654 return ii !== -1 ? ii : null; 30655 } 30656 } 30657} 30658 30659function localeMonthsParse (monthName, format, strict) { 30660 var i, mom, regex; 30661 30662 if (this._monthsParseExact) { 30663 return handleStrictParse.call(this, monthName, format, strict); 30664 } 30665 30666 if (!this._monthsParse) { 30667 this._monthsParse = []; 30668 this._longMonthsParse = []; 30669 this._shortMonthsParse = []; 30670 } 30671 30672 // TODO: add sorting 30673 // Sorting makes sure if one month (or abbr) is a prefix of another 30674 // see sorting in computeMonthsParse 30675 for (i = 0; i < 12; i++) { 30676 // make the regex if we don't have it already 30677 mom = createUTC([2000, i]); 30678 if (strict && !this._longMonthsParse[i]) { 30679 this._longMonthsParse[i] = new RegExp('^' + this.months(mom, '').replace('.', '') + '$', 'i'); 30680 this._shortMonthsParse[i] = new RegExp('^' + this.monthsShort(mom, '').replace('.', '') + '$', 'i'); 30681 } 30682 if (!strict && !this._monthsParse[i]) { 30683 regex = '^' + this.months(mom, '') + '|^' + this.monthsShort(mom, ''); 30684 this._monthsParse[i] = new RegExp(regex.replace('.', ''), 'i'); 30685 } 30686 // test the regex 30687 if (strict && format === 'MMMM' && this._longMonthsParse[i].test(monthName)) { 30688 return i; 30689 } else if (strict && format === 'MMM' && this._shortMonthsParse[i].test(monthName)) { 30690 return i; 30691 } else if (!strict && this._monthsParse[i].test(monthName)) { 30692 return i; 30693 } 30694 } 30695} 30696 30697// MOMENTS 30698 30699function setMonth (mom, value) { 30700 var dayOfMonth; 30701 30702 if (!mom.isValid()) { 30703 // No op 30704 return mom; 30705 } 30706 30707 if (typeof value === 'string') { 30708 if (/^\d+$/.test(value)) { 30709 value = toInt(value); 30710 } else { 30711 value = mom.localeData().monthsParse(value); 30712 // TODO: Another silent failure? 30713 if (!isNumber(value)) { 30714 return mom; 30715 } 30716 } 30717 } 30718 30719 dayOfMonth = Math.min(mom.date(), daysInMonth(mom.year(), value)); 30720 mom._d['set' + (mom._isUTC ? 'UTC' : '') + 'Month'](value, dayOfMonth); 30721 return mom; 30722} 30723 30724function getSetMonth (value) { 30725 if (value != null) { 30726 setMonth(this, value); 30727 hooks.updateOffset(this, true); 30728 return this; 30729 } else { 30730 return get(this, 'Month'); 30731 } 30732} 30733 30734function getDaysInMonth () { 30735 return daysInMonth(this.year(), this.month()); 30736} 30737 30738var defaultMonthsShortRegex = matchWord; 30739function monthsShortRegex (isStrict) { 30740 if (this._monthsParseExact) { 30741 if (!hasOwnProp(this, '_monthsRegex')) { 30742 computeMonthsParse.call(this); 30743 } 30744 if (isStrict) { 30745 return this._monthsShortStrictRegex; 30746 } else { 30747 return this._monthsShortRegex; 30748 } 30749 } else { 30750 if (!hasOwnProp(this, '_monthsShortRegex')) { 30751 this._monthsShortRegex = defaultMonthsShortRegex; 30752 } 30753 return this._monthsShortStrictRegex && isStrict ? 30754 this._monthsShortStrictRegex : this._monthsShortRegex; 30755 } 30756} 30757 30758var defaultMonthsRegex = matchWord; 30759function monthsRegex (isStrict) { 30760 if (this._monthsParseExact) { 30761 if (!hasOwnProp(this, '_monthsRegex')) { 30762 computeMonthsParse.call(this); 30763 } 30764 if (isStrict) { 30765 return this._monthsStrictRegex; 30766 } else { 30767 return this._monthsRegex; 30768 } 30769 } else { 30770 if (!hasOwnProp(this, '_monthsRegex')) { 30771 this._monthsRegex = defaultMonthsRegex; 30772 } 30773 return this._monthsStrictRegex && isStrict ? 30774 this._monthsStrictRegex : this._monthsRegex; 30775 } 30776} 30777 30778function computeMonthsParse () { 30779 function cmpLenRev(a, b) { 30780 return b.length - a.length; 30781 } 30782 30783 var shortPieces = [], longPieces = [], mixedPieces = [], 30784 i, mom; 30785 for (i = 0; i < 12; i++) { 30786 // make the regex if we don't have it already 30787 mom = createUTC([2000, i]); 30788 shortPieces.push(this.monthsShort(mom, '')); 30789 longPieces.push(this.months(mom, '')); 30790 mixedPieces.push(this.months(mom, '')); 30791 mixedPieces.push(this.monthsShort(mom, '')); 30792 } 30793 // Sorting makes sure if one month (or abbr) is a prefix of another it 30794 // will match the longer piece. 30795 shortPieces.sort(cmpLenRev); 30796 longPieces.sort(cmpLenRev); 30797 mixedPieces.sort(cmpLenRev); 30798 for (i = 0; i < 12; i++) { 30799 shortPieces[i] = regexEscape(shortPieces[i]); 30800 longPieces[i] = regexEscape(longPieces[i]); 30801 } 30802 for (i = 0; i < 24; i++) { 30803 mixedPieces[i] = regexEscape(mixedPieces[i]); 30804 } 30805 30806 this._monthsRegex = new RegExp('^(' + mixedPieces.join('|') + ')', 'i'); 30807 this._monthsShortRegex = this._monthsRegex; 30808 this._monthsStrictRegex = new RegExp('^(' + longPieces.join('|') + ')', 'i'); 30809 this._monthsShortStrictRegex = new RegExp('^(' + shortPieces.join('|') + ')', 'i'); 30810} 30811 30812function createDate (y, m, d, h, M, s, ms) { 30813 // can't just apply() to create a date: 30814 // https://stackoverflow.com/q/181348 30815 var date = new Date(y, m, d, h, M, s, ms); 30816 30817 // the date constructor remaps years 0-99 to 1900-1999 30818 if (y < 100 && y >= 0 && isFinite(date.getFullYear())) { 30819 date.setFullYear(y); 30820 } 30821 return date; 30822} 30823 30824function createUTCDate (y) { 30825 var date = new Date(Date.UTC.apply(null, arguments)); 30826 30827 // the Date.UTC function remaps years 0-99 to 1900-1999 30828 if (y < 100 && y >= 0 && isFinite(date.getUTCFullYear())) { 30829 date.setUTCFullYear(y); 30830 } 30831 return date; 30832} 30833 30834// start-of-first-week - start-of-year 30835function firstWeekOffset(year, dow, doy) { 30836 var // first-week day -- which january is always in the first week (4 for iso, 1 for other) 30837 fwd = 7 + dow - doy, 30838 // first-week day local weekday -- which local weekday is fwd 30839 fwdlw = (7 + createUTCDate(year, 0, fwd).getUTCDay() - dow) % 7; 30840 30841 return -fwdlw + fwd - 1; 30842} 30843 30844// https://en.wikipedia.org/wiki/ISO_week_date#Calculating_a_date_given_the_year.2C_week_number_and_weekday 30845function dayOfYearFromWeeks(year, week, weekday, dow, doy) { 30846 var localWeekday = (7 + weekday - dow) % 7, 30847 weekOffset = firstWeekOffset(year, dow, doy), 30848 dayOfYear = 1 + 7 * (week - 1) + localWeekday + weekOffset, 30849 resYear, resDayOfYear; 30850 30851 if (dayOfYear <= 0) { 30852 resYear = year - 1; 30853 resDayOfYear = daysInYear(resYear) + dayOfYear; 30854 } else if (dayOfYear > daysInYear(year)) { 30855 resYear = year + 1; 30856 resDayOfYear = dayOfYear - daysInYear(year); 30857 } else { 30858 resYear = year; 30859 resDayOfYear = dayOfYear; 30860 } 30861 30862 return { 30863 year: resYear, 30864 dayOfYear: resDayOfYear 30865 }; 30866} 30867 30868function weekOfYear(mom, dow, doy) { 30869 var weekOffset = firstWeekOffset(mom.year(), dow, doy), 30870 week = Math.floor((mom.dayOfYear() - weekOffset - 1) / 7) + 1, 30871 resWeek, resYear; 30872 30873 if (week < 1) { 30874 resYear = mom.year() - 1; 30875 resWeek = week + weeksInYear(resYear, dow, doy); 30876 } else if (week > weeksInYear(mom.year(), dow, doy)) { 30877 resWeek = week - weeksInYear(mom.year(), dow, doy); 30878 resYear = mom.year() + 1; 30879 } else { 30880 resYear = mom.year(); 30881 resWeek = week; 30882 } 30883 30884 return { 30885 week: resWeek, 30886 year: resYear 30887 }; 30888} 30889 30890function weeksInYear(year, dow, doy) { 30891 var weekOffset = firstWeekOffset(year, dow, doy), 30892 weekOffsetNext = firstWeekOffset(year + 1, dow, doy); 30893 return (daysInYear(year) - weekOffset + weekOffsetNext) / 7; 30894} 30895 30896// FORMATTING 30897 30898addFormatToken('w', ['ww', 2], 'wo', 'week'); 30899addFormatToken('W', ['WW', 2], 'Wo', 'isoWeek'); 30900 30901// ALIASES 30902 30903addUnitAlias('week', 'w'); 30904addUnitAlias('isoWeek', 'W'); 30905 30906// PRIORITIES 30907 30908addUnitPriority('week', 5); 30909addUnitPriority('isoWeek', 5); 30910 30911// PARSING 30912 30913addRegexToken('w', match1to2); 30914addRegexToken('ww', match1to2, match2); 30915addRegexToken('W', match1to2); 30916addRegexToken('WW', match1to2, match2); 30917 30918addWeekParseToken(['w', 'ww', 'W', 'WW'], function (input, week, config, token) { 30919 week[token.substr(0, 1)] = toInt(input); 30920}); 30921 30922// HELPERS 30923 30924// LOCALES 30925 30926function localeWeek (mom) { 30927 return weekOfYear(mom, this._week.dow, this._week.doy).week; 30928} 30929 30930var defaultLocaleWeek = { 30931 dow : 0, // Sunday is the first day of the week. 30932 doy : 6 // The week that contains Jan 1st is the first week of the year. 30933}; 30934 30935function localeFirstDayOfWeek () { 30936 return this._week.dow; 30937} 30938 30939function localeFirstDayOfYear () { 30940 return this._week.doy; 30941} 30942 30943// MOMENTS 30944 30945function getSetWeek (input) { 30946 var week = this.localeData().week(this); 30947 return input == null ? week : this.add((input - week) * 7, 'd'); 30948} 30949 30950function getSetISOWeek (input) { 30951 var week = weekOfYear(this, 1, 4).week; 30952 return input == null ? week : this.add((input - week) * 7, 'd'); 30953} 30954 30955// FORMATTING 30956 30957addFormatToken('d', 0, 'do', 'day'); 30958 30959addFormatToken('dd', 0, 0, function (format) { 30960 return this.localeData().weekdaysMin(this, format); 30961}); 30962 30963addFormatToken('ddd', 0, 0, function (format) { 30964 return this.localeData().weekdaysShort(this, format); 30965}); 30966 30967addFormatToken('dddd', 0, 0, function (format) { 30968 return this.localeData().weekdays(this, format); 30969}); 30970 30971addFormatToken('e', 0, 0, 'weekday'); 30972addFormatToken('E', 0, 0, 'isoWeekday'); 30973 30974// ALIASES 30975 30976addUnitAlias('day', 'd'); 30977addUnitAlias('weekday', 'e'); 30978addUnitAlias('isoWeekday', 'E'); 30979 30980// PRIORITY 30981addUnitPriority('day', 11); 30982addUnitPriority('weekday', 11); 30983addUnitPriority('isoWeekday', 11); 30984 30985// PARSING 30986 30987addRegexToken('d', match1to2); 30988addRegexToken('e', match1to2); 30989addRegexToken('E', match1to2); 30990addRegexToken('dd', function (isStrict, locale) { 30991 return locale.weekdaysMinRegex(isStrict); 30992}); 30993addRegexToken('ddd', function (isStrict, locale) { 30994 return locale.weekdaysShortRegex(isStrict); 30995}); 30996addRegexToken('dddd', function (isStrict, locale) { 30997 return locale.weekdaysRegex(isStrict); 30998}); 30999 31000addWeekParseToken(['dd', 'ddd', 'dddd'], function (input, week, config, token) { 31001 var weekday = config._locale.weekdaysParse(input, token, config._strict); 31002 // if we didn't get a weekday name, mark the date as invalid 31003 if (weekday != null) { 31004 week.d = weekday; 31005 } else { 31006 getParsingFlags(config).invalidWeekday = input; 31007 } 31008}); 31009 31010addWeekParseToken(['d', 'e', 'E'], function (input, week, config, token) { 31011 week[token] = toInt(input); 31012}); 31013 31014// HELPERS 31015 31016function parseWeekday(input, locale) { 31017 if (typeof input !== 'string') { 31018 return input; 31019 } 31020 31021 if (!isNaN(input)) { 31022 return parseInt(input, 10); 31023 } 31024 31025 input = locale.weekdaysParse(input); 31026 if (typeof input === 'number') { 31027 return input; 31028 } 31029 31030 return null; 31031} 31032 31033function parseIsoWeekday(input, locale) { 31034 if (typeof input === 'string') { 31035 return locale.weekdaysParse(input) % 7 || 7; 31036 } 31037 return isNaN(input) ? null : input; 31038} 31039 31040// LOCALES 31041 31042var defaultLocaleWeekdays = 'Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday'.split('_'); 31043function localeWeekdays (m, format) { 31044 if (!m) { 31045 return isArray(this._weekdays) ? this._weekdays : 31046 this._weekdays['standalone']; 31047 } 31048 return isArray(this._weekdays) ? this._weekdays[m.day()] : 31049 this._weekdays[this._weekdays.isFormat.test(format) ? 'format' : 'standalone'][m.day()]; 31050} 31051 31052var defaultLocaleWeekdaysShort = 'Sun_Mon_Tue_Wed_Thu_Fri_Sat'.split('_'); 31053function localeWeekdaysShort (m) { 31054 return (m) ? this._weekdaysShort[m.day()] : this._weekdaysShort; 31055} 31056 31057var defaultLocaleWeekdaysMin = 'Su_Mo_Tu_We_Th_Fr_Sa'.split('_'); 31058function localeWeekdaysMin (m) { 31059 return (m) ? this._weekdaysMin[m.day()] : this._weekdaysMin; 31060} 31061 31062function handleStrictParse$1(weekdayName, format, strict) { 31063 var i, ii, mom, llc = weekdayName.toLocaleLowerCase(); 31064 if (!this._weekdaysParse) { 31065 this._weekdaysParse = []; 31066 this._shortWeekdaysParse = []; 31067 this._minWeekdaysParse = []; 31068 31069 for (i = 0; i < 7; ++i) { 31070 mom = createUTC([2000, 1]).day(i); 31071 this._minWeekdaysParse[i] = this.weekdaysMin(mom, '').toLocaleLowerCase(); 31072 this._shortWeekdaysParse[i] = this.weekdaysShort(mom, '').toLocaleLowerCase(); 31073 this._weekdaysParse[i] = this.weekdays(mom, '').toLocaleLowerCase(); 31074 } 31075 } 31076 31077 if (strict) { 31078 if (format === 'dddd') { 31079 ii = indexOf.call(this._weekdaysParse, llc); 31080 return ii !== -1 ? ii : null; 31081 } else if (format === 'ddd') { 31082 ii = indexOf.call(this._shortWeekdaysParse, llc); 31083 return ii !== -1 ? ii : null; 31084 } else { 31085 ii = indexOf.call(this._minWeekdaysParse, llc); 31086 return ii !== -1 ? ii : null; 31087 } 31088 } else { 31089 if (format === 'dddd') { 31090 ii = indexOf.call(this._weekdaysParse, llc); 31091 if (ii !== -1) { 31092 return ii; 31093 } 31094 ii = indexOf.call(this._shortWeekdaysParse, llc); 31095 if (ii !== -1) { 31096 return ii; 31097 } 31098 ii = indexOf.call(this._minWeekdaysParse, llc); 31099 return ii !== -1 ? ii : null; 31100 } else if (format === 'ddd') { 31101 ii = indexOf.call(this._shortWeekdaysParse, llc); 31102 if (ii !== -1) { 31103 return ii; 31104 } 31105 ii = indexOf.call(this._weekdaysParse, llc); 31106 if (ii !== -1) { 31107 return ii; 31108 } 31109 ii = indexOf.call(this._minWeekdaysParse, llc); 31110 return ii !== -1 ? ii : null; 31111 } else { 31112 ii = indexOf.call(this._minWeekdaysParse, llc); 31113 if (ii !== -1) { 31114 return ii; 31115 } 31116 ii = indexOf.call(this._weekdaysParse, llc); 31117 if (ii !== -1) { 31118 return ii; 31119 } 31120 ii = indexOf.call(this._shortWeekdaysParse, llc); 31121 return ii !== -1 ? ii : null; 31122 } 31123 } 31124} 31125 31126function localeWeekdaysParse (weekdayName, format, strict) { 31127 var i, mom, regex; 31128 31129 if (this._weekdaysParseExact) { 31130 return handleStrictParse$1.call(this, weekdayName, format, strict); 31131 } 31132 31133 if (!this._weekdaysParse) { 31134 this._weekdaysParse = []; 31135 this._minWeekdaysParse = []; 31136 this._shortWeekdaysParse = []; 31137 this._fullWeekdaysParse = []; 31138 } 31139 31140 for (i = 0; i < 7; i++) { 31141 // make the regex if we don't have it already 31142 31143 mom = createUTC([2000, 1]).day(i); 31144 if (strict && !this._fullWeekdaysParse[i]) { 31145 this._fullWeekdaysParse[i] = new RegExp('^' + this.weekdays(mom, '').replace('.', '\.?') + '$', 'i'); 31146 this._shortWeekdaysParse[i] = new RegExp('^' + this.weekdaysShort(mom, '').replace('.', '\.?') + '$', 'i'); 31147 this._minWeekdaysParse[i] = new RegExp('^' + this.weekdaysMin(mom, '').replace('.', '\.?') + '$', 'i'); 31148 } 31149 if (!this._weekdaysParse[i]) { 31150 regex = '^' + this.weekdays(mom, '') + '|^' + this.weekdaysShort(mom, '') + '|^' + this.weekdaysMin(mom, ''); 31151 this._weekdaysParse[i] = new RegExp(regex.replace('.', ''), 'i'); 31152 } 31153 // test the regex 31154 if (strict && format === 'dddd' && this._fullWeekdaysParse[i].test(weekdayName)) { 31155 return i; 31156 } else if (strict && format === 'ddd' && this._shortWeekdaysParse[i].test(weekdayName)) { 31157 return i; 31158 } else if (strict && format === 'dd' && this._minWeekdaysParse[i].test(weekdayName)) { 31159 return i; 31160 } else if (!strict && this._weekdaysParse[i].test(weekdayName)) { 31161 return i; 31162 } 31163 } 31164} 31165 31166// MOMENTS 31167 31168function getSetDayOfWeek (input) { 31169 if (!this.isValid()) { 31170 return input != null ? this : NaN; 31171 } 31172 var day = this._isUTC ? this._d.getUTCDay() : this._d.getDay(); 31173 if (input != null) { 31174 input = parseWeekday(input, this.localeData()); 31175 return this.add(input - day, 'd'); 31176 } else { 31177 return day; 31178 } 31179} 31180 31181function getSetLocaleDayOfWeek (input) { 31182 if (!this.isValid()) { 31183 return input != null ? this : NaN; 31184 } 31185 var weekday = (this.day() + 7 - this.localeData()._week.dow) % 7; 31186 return input == null ? weekday : this.add(input - weekday, 'd'); 31187} 31188 31189function getSetISODayOfWeek (input) { 31190 if (!this.isValid()) { 31191 return input != null ? this : NaN; 31192 } 31193 31194 // behaves the same as moment#day except 31195 // as a getter, returns 7 instead of 0 (1-7 range instead of 0-6) 31196 // as a setter, sunday should belong to the previous week. 31197 31198 if (input != null) { 31199 var weekday = parseIsoWeekday(input, this.localeData()); 31200 return this.day(this.day() % 7 ? weekday : weekday - 7); 31201 } else { 31202 return this.day() || 7; 31203 } 31204} 31205 31206var defaultWeekdaysRegex = matchWord; 31207function weekdaysRegex (isStrict) { 31208 if (this._weekdaysParseExact) { 31209 if (!hasOwnProp(this, '_weekdaysRegex')) { 31210 computeWeekdaysParse.call(this); 31211 } 31212 if (isStrict) { 31213 return this._weekdaysStrictRegex; 31214 } else { 31215 return this._weekdaysRegex; 31216 } 31217 } else { 31218 if (!hasOwnProp(this, '_weekdaysRegex')) { 31219 this._weekdaysRegex = defaultWeekdaysRegex; 31220 } 31221 return this._weekdaysStrictRegex && isStrict ? 31222 this._weekdaysStrictRegex : this._weekdaysRegex; 31223 } 31224} 31225 31226var defaultWeekdaysShortRegex = matchWord; 31227function weekdaysShortRegex (isStrict) { 31228 if (this._weekdaysParseExact) { 31229 if (!hasOwnProp(this, '_weekdaysRegex')) { 31230 computeWeekdaysParse.call(this); 31231 } 31232 if (isStrict) { 31233 return this._weekdaysShortStrictRegex; 31234 } else { 31235 return this._weekdaysShortRegex; 31236 } 31237 } else { 31238 if (!hasOwnProp(this, '_weekdaysShortRegex')) { 31239 this._weekdaysShortRegex = defaultWeekdaysShortRegex; 31240 } 31241 return this._weekdaysShortStrictRegex && isStrict ? 31242 this._weekdaysShortStrictRegex : this._weekdaysShortRegex; 31243 } 31244} 31245 31246var defaultWeekdaysMinRegex = matchWord; 31247function weekdaysMinRegex (isStrict) { 31248 if (this._weekdaysParseExact) { 31249 if (!hasOwnProp(this, '_weekdaysRegex')) { 31250 computeWeekdaysParse.call(this); 31251 } 31252 if (isStrict) { 31253 return this._weekdaysMinStrictRegex; 31254 } else { 31255 return this._weekdaysMinRegex; 31256 } 31257 } else { 31258 if (!hasOwnProp(this, '_weekdaysMinRegex')) { 31259 this._weekdaysMinRegex = defaultWeekdaysMinRegex; 31260 } 31261 return this._weekdaysMinStrictRegex && isStrict ? 31262 this._weekdaysMinStrictRegex : this._weekdaysMinRegex; 31263 } 31264} 31265 31266 31267function computeWeekdaysParse () { 31268 function cmpLenRev(a, b) { 31269 return b.length - a.length; 31270 } 31271 31272 var minPieces = [], shortPieces = [], longPieces = [], mixedPieces = [], 31273 i, mom, minp, shortp, longp; 31274 for (i = 0; i < 7; i++) { 31275 // make the regex if we don't have it already 31276 mom = createUTC([2000, 1]).day(i); 31277 minp = this.weekdaysMin(mom, ''); 31278 shortp = this.weekdaysShort(mom, ''); 31279 longp = this.weekdays(mom, ''); 31280 minPieces.push(minp); 31281 shortPieces.push(shortp); 31282 longPieces.push(longp); 31283 mixedPieces.push(minp); 31284 mixedPieces.push(shortp); 31285 mixedPieces.push(longp); 31286 } 31287 // Sorting makes sure if one weekday (or abbr) is a prefix of another it 31288 // will match the longer piece. 31289 minPieces.sort(cmpLenRev); 31290 shortPieces.sort(cmpLenRev); 31291 longPieces.sort(cmpLenRev); 31292 mixedPieces.sort(cmpLenRev); 31293 for (i = 0; i < 7; i++) { 31294 shortPieces[i] = regexEscape(shortPieces[i]); 31295 longPieces[i] = regexEscape(longPieces[i]); 31296 mixedPieces[i] = regexEscape(mixedPieces[i]); 31297 } 31298 31299 this._weekdaysRegex = new RegExp('^(' + mixedPieces.join('|') + ')', 'i'); 31300 this._weekdaysShortRegex = this._weekdaysRegex; 31301 this._weekdaysMinRegex = this._weekdaysRegex; 31302 31303 this._weekdaysStrictRegex = new RegExp('^(' + longPieces.join('|') + ')', 'i'); 31304 this._weekdaysShortStrictRegex = new RegExp('^(' + shortPieces.join('|') + ')', 'i'); 31305 this._weekdaysMinStrictRegex = new RegExp('^(' + minPieces.join('|') + ')', 'i'); 31306} 31307 31308// FORMATTING 31309 31310function hFormat() { 31311 return this.hours() % 12 || 12; 31312} 31313 31314function kFormat() { 31315 return this.hours() || 24; 31316} 31317 31318addFormatToken('H', ['HH', 2], 0, 'hour'); 31319addFormatToken('h', ['hh', 2], 0, hFormat); 31320addFormatToken('k', ['kk', 2], 0, kFormat); 31321 31322addFormatToken('hmm', 0, 0, function () { 31323 return '' + hFormat.apply(this) + zeroFill(this.minutes(), 2); 31324}); 31325 31326addFormatToken('hmmss', 0, 0, function () { 31327 return '' + hFormat.apply(this) + zeroFill(this.minutes(), 2) + 31328 zeroFill(this.seconds(), 2); 31329}); 31330 31331addFormatToken('Hmm', 0, 0, function () { 31332 return '' + this.hours() + zeroFill(this.minutes(), 2); 31333}); 31334 31335addFormatToken('Hmmss', 0, 0, function () { 31336 return '' + this.hours() + zeroFill(this.minutes(), 2) + 31337 zeroFill(this.seconds(), 2); 31338}); 31339 31340function meridiem (token, lowercase) { 31341 addFormatToken(token, 0, 0, function () { 31342 return this.localeData().meridiem(this.hours(), this.minutes(), lowercase); 31343 }); 31344} 31345 31346meridiem('a', true); 31347meridiem('A', false); 31348 31349// ALIASES 31350 31351addUnitAlias('hour', 'h'); 31352 31353// PRIORITY 31354addUnitPriority('hour', 13); 31355 31356// PARSING 31357 31358function matchMeridiem (isStrict, locale) { 31359 return locale._meridiemParse; 31360} 31361 31362addRegexToken('a', matchMeridiem); 31363addRegexToken('A', matchMeridiem); 31364addRegexToken('H', match1to2); 31365addRegexToken('h', match1to2); 31366addRegexToken('k', match1to2); 31367addRegexToken('HH', match1to2, match2); 31368addRegexToken('hh', match1to2, match2); 31369addRegexToken('kk', match1to2, match2); 31370 31371addRegexToken('hmm', match3to4); 31372addRegexToken('hmmss', match5to6); 31373addRegexToken('Hmm', match3to4); 31374addRegexToken('Hmmss', match5to6); 31375 31376addParseToken(['H', 'HH'], HOUR); 31377addParseToken(['k', 'kk'], function (input, array, config) { 31378 var kInput = toInt(input); 31379 array[HOUR] = kInput === 24 ? 0 : kInput; 31380}); 31381addParseToken(['a', 'A'], function (input, array, config) { 31382 config._isPm = config._locale.isPM(input); 31383 config._meridiem = input; 31384}); 31385addParseToken(['h', 'hh'], function (input, array, config) { 31386 array[HOUR] = toInt(input); 31387 getParsingFlags(config).bigHour = true; 31388}); 31389addParseToken('hmm', function (input, array, config) { 31390 var pos = input.length - 2; 31391 array[HOUR] = toInt(input.substr(0, pos)); 31392 array[MINUTE] = toInt(input.substr(pos)); 31393 getParsingFlags(config).bigHour = true; 31394}); 31395addParseToken('hmmss', function (input, array, config) { 31396 var pos1 = input.length - 4; 31397 var pos2 = input.length - 2; 31398 array[HOUR] = toInt(input.substr(0, pos1)); 31399 array[MINUTE] = toInt(input.substr(pos1, 2)); 31400 array[SECOND] = toInt(input.substr(pos2)); 31401 getParsingFlags(config).bigHour = true; 31402}); 31403addParseToken('Hmm', function (input, array, config) { 31404 var pos = input.length - 2; 31405 array[HOUR] = toInt(input.substr(0, pos)); 31406 array[MINUTE] = toInt(input.substr(pos)); 31407}); 31408addParseToken('Hmmss', function (input, array, config) { 31409 var pos1 = input.length - 4; 31410 var pos2 = input.length - 2; 31411 array[HOUR] = toInt(input.substr(0, pos1)); 31412 array[MINUTE] = toInt(input.substr(pos1, 2)); 31413 array[SECOND] = toInt(input.substr(pos2)); 31414}); 31415 31416// LOCALES 31417 31418function localeIsPM (input) { 31419 // IE8 Quirks Mode & IE7 Standards Mode do not allow accessing strings like arrays 31420 // Using charAt should be more compatible. 31421 return ((input + '').toLowerCase().charAt(0) === 'p'); 31422} 31423 31424var defaultLocaleMeridiemParse = /[ap]\.?m?\.?/i; 31425function localeMeridiem (hours, minutes, isLower) { 31426 if (hours > 11) { 31427 return isLower ? 'pm' : 'PM'; 31428 } else { 31429 return isLower ? 'am' : 'AM'; 31430 } 31431} 31432 31433 31434// MOMENTS 31435 31436// Setting the hour should keep the time, because the user explicitly 31437// specified which hour he wants. So trying to maintain the same hour (in 31438// a new timezone) makes sense. Adding/subtracting hours does not follow 31439// this rule. 31440var getSetHour = makeGetSet('Hours', true); 31441 31442// months 31443// week 31444// weekdays 31445// meridiem 31446var baseConfig = { 31447 calendar: defaultCalendar, 31448 longDateFormat: defaultLongDateFormat, 31449 invalidDate: defaultInvalidDate, 31450 ordinal: defaultOrdinal, 31451 dayOfMonthOrdinalParse: defaultDayOfMonthOrdinalParse, 31452 relativeTime: defaultRelativeTime, 31453 31454 months: defaultLocaleMonths, 31455 monthsShort: defaultLocaleMonthsShort, 31456 31457 week: defaultLocaleWeek, 31458 31459 weekdays: defaultLocaleWeekdays, 31460 weekdaysMin: defaultLocaleWeekdaysMin, 31461 weekdaysShort: defaultLocaleWeekdaysShort, 31462 31463 meridiemParse: defaultLocaleMeridiemParse 31464}; 31465 31466// internal storage for locale config files 31467var locales = {}; 31468var localeFamilies = {}; 31469var globalLocale; 31470 31471function normalizeLocale(key) { 31472 return key ? key.toLowerCase().replace('_', '-') : key; 31473} 31474 31475// pick the locale from the array 31476// try ['en-au', 'en-gb'] as 'en-au', 'en-gb', 'en', as in move through the list trying each 31477// substring from most specific to least, but move to the next array item if it's a more specific variant than the current root 31478function chooseLocale(names) { 31479 var i = 0, j, next, locale, split; 31480 31481 while (i < names.length) { 31482 split = normalizeLocale(names[i]).split('-'); 31483 j = split.length; 31484 next = normalizeLocale(names[i + 1]); 31485 next = next ? next.split('-') : null; 31486 while (j > 0) { 31487 locale = loadLocale(split.slice(0, j).join('-')); 31488 if (locale) { 31489 return locale; 31490 } 31491 if (next && next.length >= j && compareArrays(split, next, true) >= j - 1) { 31492 //the next array item is better than a shallower substring of this one 31493 break; 31494 } 31495 j--; 31496 } 31497 i++; 31498 } 31499 return null; 31500} 31501 31502function loadLocale(name) { 31503 var oldLocale = null; 31504 // TODO: Find a better way to register and load all the locales in Node 31505 if (!locales[name] && (typeof module !== 'undefined') && 31506 module && module.exports) { 31507 try { 31508 oldLocale = globalLocale._abbr; 31509 var aliasedRequire = require; 31510 !(function webpackMissingModule() { var e = new Error("Cannot find module \"./locale\""); e.code = 'MODULE_NOT_FOUND'; throw e; }()); 31511 getSetGlobalLocale(oldLocale); 31512 } catch (e) {} 31513 } 31514 return locales[name]; 31515} 31516 31517// This function will load locale and then set the global locale. If 31518// no arguments are passed in, it will simply return the current global 31519// locale key. 31520function getSetGlobalLocale (key, values) { 31521 var data; 31522 if (key) { 31523 if (isUndefined(values)) { 31524 data = getLocale(key); 31525 } 31526 else { 31527 data = defineLocale(key, values); 31528 } 31529 31530 if (data) { 31531 // moment.duration._locale = moment._locale = data; 31532 globalLocale = data; 31533 } 31534 } 31535 31536 return globalLocale._abbr; 31537} 31538 31539function defineLocale (name, config) { 31540 if (config !== null) { 31541 var parentConfig = baseConfig; 31542 config.abbr = name; 31543 if (locales[name] != null) { 31544 deprecateSimple('defineLocaleOverride', 31545 'use moment.updateLocale(localeName, config) to change ' + 31546 'an existing locale. moment.defineLocale(localeName, ' + 31547 'config) should only be used for creating a new locale ' + 31548 'See http://momentjs.com/guides/#/warnings/define-locale/ for more info.'); 31549 parentConfig = locales[name]._config; 31550 } else if (config.parentLocale != null) { 31551 if (locales[config.parentLocale] != null) { 31552 parentConfig = locales[config.parentLocale]._config; 31553 } else { 31554 if (!localeFamilies[config.parentLocale]) { 31555 localeFamilies[config.parentLocale] = []; 31556 } 31557 localeFamilies[config.parentLocale].push({ 31558 name: name, 31559 config: config 31560 }); 31561 return null; 31562 } 31563 } 31564 locales[name] = new Locale(mergeConfigs(parentConfig, config)); 31565 31566 if (localeFamilies[name]) { 31567 localeFamilies[name].forEach(function (x) { 31568 defineLocale(x.name, x.config); 31569 }); 31570 } 31571 31572 // backwards compat for now: also set the locale 31573 // make sure we set the locale AFTER all child locales have been 31574 // created, so we won't end up with the child locale set. 31575 getSetGlobalLocale(name); 31576 31577 31578 return locales[name]; 31579 } else { 31580 // useful for testing 31581 delete locales[name]; 31582 return null; 31583 } 31584} 31585 31586function updateLocale(name, config) { 31587 if (config != null) { 31588 var locale, parentConfig = baseConfig; 31589 // MERGE 31590 if (locales[name] != null) { 31591 parentConfig = locales[name]._config; 31592 } 31593 config = mergeConfigs(parentConfig, config); 31594 locale = new Locale(config); 31595 locale.parentLocale = locales[name]; 31596 locales[name] = locale; 31597 31598 // backwards compat for now: also set the locale 31599 getSetGlobalLocale(name); 31600 } else { 31601 // pass null for config to unupdate, useful for tests 31602 if (locales[name] != null) { 31603 if (locales[name].parentLocale != null) { 31604 locales[name] = locales[name].parentLocale; 31605 } else if (locales[name] != null) { 31606 delete locales[name]; 31607 } 31608 } 31609 } 31610 return locales[name]; 31611} 31612 31613// returns locale data 31614function getLocale (key) { 31615 var locale; 31616 31617 if (key && key._locale && key._locale._abbr) { 31618 key = key._locale._abbr; 31619 } 31620 31621 if (!key) { 31622 return globalLocale; 31623 } 31624 31625 if (!isArray(key)) { 31626 //short-circuit everything else 31627 locale = loadLocale(key); 31628 if (locale) { 31629 return locale; 31630 } 31631 key = [key]; 31632 } 31633 31634 return chooseLocale(key); 31635} 31636 31637function listLocales() { 31638 return keys(locales); 31639} 31640 31641function checkOverflow (m) { 31642 var overflow; 31643 var a = m._a; 31644 31645 if (a && getParsingFlags(m).overflow === -2) { 31646 overflow = 31647 a[MONTH] < 0 || a[MONTH] > 11 ? MONTH : 31648 a[DATE] < 1 || a[DATE] > daysInMonth(a[YEAR], a[MONTH]) ? DATE : 31649 a[HOUR] < 0 || a[HOUR] > 24 || (a[HOUR] === 24 && (a[MINUTE] !== 0 || a[SECOND] !== 0 || a[MILLISECOND] !== 0)) ? HOUR : 31650 a[MINUTE] < 0 || a[MINUTE] > 59 ? MINUTE : 31651 a[SECOND] < 0 || a[SECOND] > 59 ? SECOND : 31652 a[MILLISECOND] < 0 || a[MILLISECOND] > 999 ? MILLISECOND : 31653 -1; 31654 31655 if (getParsingFlags(m)._overflowDayOfYear && (overflow < YEAR || overflow > DATE)) { 31656 overflow = DATE; 31657 } 31658 if (getParsingFlags(m)._overflowWeeks && overflow === -1) { 31659 overflow = WEEK; 31660 } 31661 if (getParsingFlags(m)._overflowWeekday && overflow === -1) { 31662 overflow = WEEKDAY; 31663 } 31664 31665 getParsingFlags(m).overflow = overflow; 31666 } 31667 31668 return m; 31669} 31670 31671// Pick the first defined of two or three arguments. 31672function defaults(a, b, c) { 31673 if (a != null) { 31674 return a; 31675 } 31676 if (b != null) { 31677 return b; 31678 } 31679 return c; 31680} 31681 31682function currentDateArray(config) { 31683 // hooks is actually the exported moment object 31684 var nowValue = new Date(hooks.now()); 31685 if (config._useUTC) { 31686 return [nowValue.getUTCFullYear(), nowValue.getUTCMonth(), nowValue.getUTCDate()]; 31687 } 31688 return [nowValue.getFullYear(), nowValue.getMonth(), nowValue.getDate()]; 31689} 31690 31691// convert an array to a date. 31692// the array should mirror the parameters below 31693// note: all values past the year are optional and will default to the lowest possible value. 31694// [year, month, day , hour, minute, second, millisecond] 31695function configFromArray (config) { 31696 var i, date, input = [], currentDate, yearToUse; 31697 31698 if (config._d) { 31699 return; 31700 } 31701 31702 currentDate = currentDateArray(config); 31703 31704 //compute day of the year from weeks and weekdays 31705 if (config._w && config._a[DATE] == null && config._a[MONTH] == null) { 31706 dayOfYearFromWeekInfo(config); 31707 } 31708 31709 //if the day of the year is set, figure out what it is 31710 if (config._dayOfYear != null) { 31711 yearToUse = defaults(config._a[YEAR], currentDate[YEAR]); 31712 31713 if (config._dayOfYear > daysInYear(yearToUse) || config._dayOfYear === 0) { 31714 getParsingFlags(config)._overflowDayOfYear = true; 31715 } 31716 31717 date = createUTCDate(yearToUse, 0, config._dayOfYear); 31718 config._a[MONTH] = date.getUTCMonth(); 31719 config._a[DATE] = date.getUTCDate(); 31720 } 31721 31722 // Default to current date. 31723 // * if no year, month, day of month are given, default to today 31724 // * if day of month is given, default month and year 31725 // * if month is given, default only year 31726 // * if year is given, don't default anything 31727 for (i = 0; i < 3 && config._a[i] == null; ++i) { 31728 config._a[i] = input[i] = currentDate[i]; 31729 } 31730 31731 // Zero out whatever was not defaulted, including time 31732 for (; i < 7; i++) { 31733 config._a[i] = input[i] = (config._a[i] == null) ? (i === 2 ? 1 : 0) : config._a[i]; 31734 } 31735 31736 // Check for 24:00:00.000 31737 if (config._a[HOUR] === 24 && 31738 config._a[MINUTE] === 0 && 31739 config._a[SECOND] === 0 && 31740 config._a[MILLISECOND] === 0) { 31741 config._nextDay = true; 31742 config._a[HOUR] = 0; 31743 } 31744 31745 config._d = (config._useUTC ? createUTCDate : createDate).apply(null, input); 31746 // Apply timezone offset from input. The actual utcOffset can be changed 31747 // with parseZone. 31748 if (config._tzm != null) { 31749 config._d.setUTCMinutes(config._d.getUTCMinutes() - config._tzm); 31750 } 31751 31752 if (config._nextDay) { 31753 config._a[HOUR] = 24; 31754 } 31755 31756 // check for mismatching day of week 31757 if (config._w && typeof config._w.d !== 'undefined' && config._w.d !== config._d.getDay()) { 31758 getParsingFlags(config).weekdayMismatch = true; 31759 } 31760} 31761 31762function dayOfYearFromWeekInfo(config) { 31763 var w, weekYear, week, weekday, dow, doy, temp, weekdayOverflow; 31764 31765 w = config._w; 31766 if (w.GG != null || w.W != null || w.E != null) { 31767 dow = 1; 31768 doy = 4; 31769 31770 // TODO: We need to take the current isoWeekYear, but that depends on 31771 // how we interpret now (local, utc, fixed offset). So create 31772 // a now version of current config (take local/utc/offset flags, and 31773 // create now). 31774 weekYear = defaults(w.GG, config._a[YEAR], weekOfYear(createLocal(), 1, 4).year); 31775 week = defaults(w.W, 1); 31776 weekday = defaults(w.E, 1); 31777 if (weekday < 1 || weekday > 7) { 31778 weekdayOverflow = true; 31779 } 31780 } else { 31781 dow = config._locale._week.dow; 31782 doy = config._locale._week.doy; 31783 31784 var curWeek = weekOfYear(createLocal(), dow, doy); 31785 31786 weekYear = defaults(w.gg, config._a[YEAR], curWeek.year); 31787 31788 // Default to current week. 31789 week = defaults(w.w, curWeek.week); 31790 31791 if (w.d != null) { 31792 // weekday -- low day numbers are considered next week 31793 weekday = w.d; 31794 if (weekday < 0 || weekday > 6) { 31795 weekdayOverflow = true; 31796 } 31797 } else if (w.e != null) { 31798 // local weekday -- counting starts from begining of week 31799 weekday = w.e + dow; 31800 if (w.e < 0 || w.e > 6) { 31801 weekdayOverflow = true; 31802 } 31803 } else { 31804 // default to begining of week 31805 weekday = dow; 31806 } 31807 } 31808 if (week < 1 || week > weeksInYear(weekYear, dow, doy)) { 31809 getParsingFlags(config)._overflowWeeks = true; 31810 } else if (weekdayOverflow != null) { 31811 getParsingFlags(config)._overflowWeekday = true; 31812 } else { 31813 temp = dayOfYearFromWeeks(weekYear, week, weekday, dow, doy); 31814 config._a[YEAR] = temp.year; 31815 config._dayOfYear = temp.dayOfYear; 31816 } 31817} 31818 31819// iso 8601 regex 31820// 0000-00-00 0000-W00 or 0000-W00-0 + T + 00 or 00:00 or 00:00:00 or 00:00:00.000 + +00:00 or +0000 or +00) 31821var extendedIsoRegex = /^\s*((?:[+-]\d{6}|\d{4})-(?:\d\d-\d\d|W\d\d-\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?::\d\d(?::\d\d(?:[.,]\d+)?)?)?)([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?$/; 31822var basicIsoRegex = /^\s*((?:[+-]\d{6}|\d{4})(?:\d\d\d\d|W\d\d\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?:\d\d(?:\d\d(?:[.,]\d+)?)?)?)([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?$/; 31823 31824var tzRegex = /Z|[+-]\d\d(?::?\d\d)?/; 31825 31826var isoDates = [ 31827 ['YYYYYY-MM-DD', /[+-]\d{6}-\d\d-\d\d/], 31828 ['YYYY-MM-DD', /\d{4}-\d\d-\d\d/], 31829 ['GGGG-[W]WW-E', /\d{4}-W\d\d-\d/], 31830 ['GGGG-[W]WW', /\d{4}-W\d\d/, false], 31831 ['YYYY-DDD', /\d{4}-\d{3}/], 31832 ['YYYY-MM', /\d{4}-\d\d/, false], 31833 ['YYYYYYMMDD', /[+-]\d{10}/], 31834 ['YYYYMMDD', /\d{8}/], 31835 // YYYYMM is NOT allowed by the standard 31836 ['GGGG[W]WWE', /\d{4}W\d{3}/], 31837 ['GGGG[W]WW', /\d{4}W\d{2}/, false], 31838 ['YYYYDDD', /\d{7}/] 31839]; 31840 31841// iso time formats and regexes 31842var isoTimes = [ 31843 ['HH:mm:ss.SSSS', /\d\d:\d\d:\d\d\.\d+/], 31844 ['HH:mm:ss,SSSS', /\d\d:\d\d:\d\d,\d+/], 31845 ['HH:mm:ss', /\d\d:\d\d:\d\d/], 31846 ['HH:mm', /\d\d:\d\d/], 31847 ['HHmmss.SSSS', /\d\d\d\d\d\d\.\d+/], 31848 ['HHmmss,SSSS', /\d\d\d\d\d\d,\d+/], 31849 ['HHmmss', /\d\d\d\d\d\d/], 31850 ['HHmm', /\d\d\d\d/], 31851 ['HH', /\d\d/] 31852]; 31853 31854var aspNetJsonRegex = /^\/?Date\((\-?\d+)/i; 31855 31856// date from iso format 31857function configFromISO(config) { 31858 var i, l, 31859 string = config._i, 31860 match = extendedIsoRegex.exec(string) || basicIsoRegex.exec(string), 31861 allowTime, dateFormat, timeFormat, tzFormat; 31862 31863 if (match) { 31864 getParsingFlags(config).iso = true; 31865 31866 for (i = 0, l = isoDates.length; i < l; i++) { 31867 if (isoDates[i][1].exec(match[1])) { 31868 dateFormat = isoDates[i][0]; 31869 allowTime = isoDates[i][2] !== false; 31870 break; 31871 } 31872 } 31873 if (dateFormat == null) { 31874 config._isValid = false; 31875 return; 31876 } 31877 if (match[3]) { 31878 for (i = 0, l = isoTimes.length; i < l; i++) { 31879 if (isoTimes[i][1].exec(match[3])) { 31880 // match[2] should be 'T' or space 31881 timeFormat = (match[2] || ' ') + isoTimes[i][0]; 31882 break; 31883 } 31884 } 31885 if (timeFormat == null) { 31886 config._isValid = false; 31887 return; 31888 } 31889 } 31890 if (!allowTime && timeFormat != null) { 31891 config._isValid = false; 31892 return; 31893 } 31894 if (match[4]) { 31895 if (tzRegex.exec(match[4])) { 31896 tzFormat = 'Z'; 31897 } else { 31898 config._isValid = false; 31899 return; 31900 } 31901 } 31902 config._f = dateFormat + (timeFormat || '') + (tzFormat || ''); 31903 configFromStringAndFormat(config); 31904 } else { 31905 config._isValid = false; 31906 } 31907} 31908 31909// RFC 2822 regex: For details see https://tools.ietf.org/html/rfc2822#section-3.3 31910var rfc2822 = /^(?:(Mon|Tue|Wed|Thu|Fri|Sat|Sun),?\s)?(\d{1,2})\s(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\s(\d{2,4})\s(\d\d):(\d\d)(?::(\d\d))?\s(?:(UT|GMT|[ECMP][SD]T)|([Zz])|([+-]\d{4}))$/; 31911 31912function extractFromRFC2822Strings(yearStr, monthStr, dayStr, hourStr, minuteStr, secondStr) { 31913 var result = [ 31914 untruncateYear(yearStr), 31915 defaultLocaleMonthsShort.indexOf(monthStr), 31916 parseInt(dayStr, 10), 31917 parseInt(hourStr, 10), 31918 parseInt(minuteStr, 10) 31919 ]; 31920 31921 if (secondStr) { 31922 result.push(parseInt(secondStr, 10)); 31923 } 31924 31925 return result; 31926} 31927 31928function untruncateYear(yearStr) { 31929 var year = parseInt(yearStr, 10); 31930 if (year <= 49) { 31931 return 2000 + year; 31932 } else if (year <= 999) { 31933 return 1900 + year; 31934 } 31935 return year; 31936} 31937 31938function preprocessRFC2822(s) { 31939 // Remove comments and folding whitespace and replace multiple-spaces with a single space 31940 return s.replace(/\([^)]*\)|[\n\t]/g, ' ').replace(/(\s\s+)/g, ' ').trim(
vendor: 6,917 bytes, lines 31940-32169
31940); 31941} 31942 31943function checkWeekday(weekdayStr, parsedInput, config) { 31944 if (weekdayStr) { 31945 // TODO: Replace the vanilla JS Date object with an indepentent day-of-week check. 31946 var weekdayProvided = defaultLocaleWeekdaysShort.indexOf(weekdayStr), 31947 weekdayActual = new Date(parsedInput[0], parsedInput[1], parsedInput[2]).getDay(); 31948 if (weekdayProvided !== weekdayActual) { 31949 getParsingFlags(config).weekdayMismatch = true; 31950 config._isValid = false; 31951 return false; 31952 } 31953 } 31954 return true; 31955} 31956 31957var obsOffsets = { 31958 UT: 0, 31959 GMT: 0, 31960 EDT: -4 * 60, 31961 EST: -5 * 60, 31962 CDT: -5 * 60, 31963 CST: -6 * 60, 31964 MDT: -6 * 60, 31965 MST: -7 * 60, 31966 PDT: -7 * 60, 31967 PST: -8 * 60 31968}; 31969 31970function calculateOffset(obsOffset, militaryOffset, numOffset) { 31971 if (obsOffset) { 31972 return obsOffsets[obsOffset]; 31973 } else if (militaryOffset) { 31974 // the only allowed military tz is Z 31975 return 0; 31976 } else { 31977 var hm = parseInt(numOffset, 10); 31978 var m = hm % 100, h = (hm - m) / 100; 31979 return h * 60 + m; 31980 } 31981} 31982 31983// date and time from ref 2822 format 31984function configFromRFC2822(config) { 31985 var match = rfc2822.exec(preprocessRFC2822(config._i)); 31986 if (match) { 31987 var parsedArray = extractFromRFC2822Strings(match[4], match[3], match[2], match[5], match[6], match[7]); 31988 if (!checkWeekday(match[1], parsedArray, config)) { 31989 return; 31990 } 31991 31992 config._a = parsedArray; 31993 config._tzm = calculateOffset(match[8], match[9], match[10]); 31994 31995 config._d = createUTCDate.apply(null, config._a); 31996 config._d.setUTCMinutes(config._d.getUTCMinutes() - config._tzm); 31997 31998 getParsingFlags(config).rfc2822 = true; 31999 } else { 32000 config._isValid = false; 32001 } 32002} 32003 32004// date from iso format or fallback 32005function configFromString(config) { 32006 var matched = aspNetJsonRegex.exec(config._i); 32007 32008 if (matched !== null) { 32009 config._d = new Date(+matched[1]); 32010 return; 32011 } 32012 32013 configFromISO(config); 32014 if (config._isValid === false) { 32015 delete config._isValid; 32016 } else { 32017 return; 32018 } 32019 32020 configFromRFC2822(config); 32021 if (config._isValid === false) { 32022 delete config._isValid; 32023 } else { 32024 return; 32025 } 32026 32027 // Final attempt, use Input Fallback 32028 hooks.createFromInputFallback(config); 32029} 32030 32031hooks.createFromInputFallback = deprecate( 32032 'value provided is not in a recognized RFC2822 or ISO format. moment construction falls back to js Date(), ' + 32033 'which is not reliable across all browsers and versions. Non RFC2822/ISO date formats are ' + 32034 'discouraged and will be removed in an upcoming major release. Please refer to ' + 32035 'http://momentjs.com/guides/#/warnings/js-date/ for more info.', 32036 function (config) { 32037 config._d = new Date(config._i + (config._useUTC ? ' UTC' : '')); 32038 } 32039); 32040 32041// constant that refers to the ISO standard 32042hooks.ISO_8601 = function () {}; 32043 32044// constant that refers to the RFC 2822 form 32045hooks.RFC_2822 = function () {}; 32046 32047// date from string and format string 32048function configFromStringAndFormat(config) { 32049 // TODO: Move this to another part of the creation flow to prevent circular deps 32050 if (config._f === hooks.ISO_8601) { 32051 configFromISO(config); 32052 return; 32053 } 32054 if (config._f === hooks.RFC_2822) { 32055 configFromRFC2822(config); 32056 return; 32057 } 32058 config._a = []; 32059 getParsingFlags(config).empty = true; 32060 32061 // This array is used to make a Date, either with `new Date` or `Date.UTC` 32062 var string = '' + config._i, 32063 i, parsedInput, tokens, token, skipped, 32064 stringLength = string.length, 32065 totalParsedInputLength = 0; 32066 32067 tokens = expandFormat(config._f, config._locale).match(formattingTokens) || []; 32068 32069 for (i = 0; i < tokens.length; i++) { 32070 token = tokens[i]; 32071 parsedInput = (string.match(getParseRegexForToken(token, config)) || [])[0]; 32072 // console.log('token', token, 'parsedInput', parsedInput, 32073 // 'regex', getParseRegexForToken(token, config)); 32074 if (parsedInput) { 32075 skipped = string.substr(0, string.indexOf(parsedInput)); 32076 if (skipped.length > 0) { 32077 getParsingFlags(config).unusedInput.push(skipped); 32078 } 32079 string = string.slice(string.indexOf(parsedInput) + parsedInput.length); 32080 totalParsedInputLength += parsedInput.length; 32081 } 32082 // don't parse if it's not a known token 32083 if (formatTokenFunctions[token]) { 32084 if (parsedInput) { 32085 getParsingFlags(config).empty = false; 32086 } 32087 else { 32088 getParsingFlags(config).unusedTokens.push(token); 32089 } 32090 addTimeToArrayFromToken(token, parsedInput, config); 32091 } 32092 else if (config._strict && !parsedInput) { 32093 getParsingFlags(config).unusedTokens.push(token); 32094 } 32095 } 32096 32097 // add remaining unparsed input length to the string 32098 getParsingFlags(config).charsLeftOver = stringLength - totalParsedInputLength; 32099 if (string.length > 0) { 32100 getParsingFlags(config).unusedInput.push(string); 32101 } 32102 32103 // clear _12h flag if hour is <= 12 32104 if (config._a[HOUR] <= 12 && 32105 getParsingFlags(config).bigHour === true && 32106 config._a[HOUR] > 0) { 32107 getParsingFlags(config).bigHour = undefined; 32108 } 32109 32110 getParsingFlags(config).parsedDateParts = config._a.slice(0); 32111 getParsingFlags(config).meridiem = config._meridiem; 32112 // handle meridiem 32113 config._a[HOUR] = meridiemFixWrap(config._locale, config._a[HOUR], config._meridiem); 32114 32115 configFromArray(config); 32116 checkOverflow(config); 32117} 32118 32119 32120function meridiemFixWrap (locale, hour, meridiem) { 32121 var isPm; 32122 32123 if (meridiem == null) { 32124 // nothing to do 32125 return hour; 32126 } 32127 if (locale.meridiemHour != null) { 32128 return locale.meridiemHour(hour, meridiem); 32129 } else if (locale.isPM != null) { 32130 // Fallback 32131 isPm = locale.isPM(meridiem); 32132 if (isPm && hour < 12) { 32133 hour += 12; 32134 } 32135 if (!isPm && hour === 12) { 32136 hour = 0; 32137 } 32138 return hour; 32139 } else { 32140 // this is not supposed to happen 32141 return hour; 32142 } 32143} 32144 32145// date from string and array of format strings 32146function configFromStringAndArray(config) { 32147 var tempConfig, 32148 bestMoment, 32149 32150 scoreToBeat, 32151 i, 32152 currentScore; 32153 32154 if (config._f.length === 0) { 32155 getParsingFlags(config).invalidFormat = true; 32156 config._d = new Date(NaN); 32157 return; 32158 } 32159 32160 for (i = 0; i < config._f.length; i++) { 32161 currentScore = 0; 32162 tempConfig = copyConfig({}, config); 32163 if (config._useUTC != null) { 32164 tempConfig._useUTC = config._useUTC; 32165 } 32166 tempConfig._f = config._f[i]; 32167 configFromStringAndFormat(tempConfig); 32168 32169 if (!isValid
vendor: 24,430 bytes, lines 32169-32987
32169(tempConfig)) { 32170 continue; 32171 } 32172 32173 // if there is any input that was not parsed add a penalty for that format 32174 currentScore += getParsingFlags(tempConfig).charsLeftOver; 32175 32176 //or tokens 32177 currentScore += getParsingFlags(tempConfig).unusedTokens.length * 10; 32178 32179 getParsingFlags(tempConfig).score = currentScore; 32180 32181 if (scoreToBeat == null || currentScore < scoreToBeat) { 32182 scoreToBeat = currentScore; 32183 bestMoment = tempConfig; 32184 } 32185 } 32186 32187 extend(config, bestMoment || tempConfig); 32188} 32189 32190function configFromObject(config) { 32191 if (config._d) { 32192 return; 32193 } 32194 32195 var i = normalizeObjectUnits(config._i); 32196 config._a = map([i.year, i.month, i.day || i.date, i.hour, i.minute, i.second, i.millisecond], function (obj) { 32197 return obj && parseInt(obj, 10); 32198 }); 32199 32200 configFromArray(config); 32201} 32202 32203function createFromConfig (config) { 32204 var res = new Moment(checkOverflow(prepareConfig(config))); 32205 if (res._nextDay) { 32206 // Adding is smart enough around DST 32207 res.add(1, 'd'); 32208 res._nextDay = undefined; 32209 } 32210 32211 return res; 32212} 32213 32214function prepareConfig (config) { 32215 var input = config._i, 32216 format = config._f; 32217 32218 config._locale = config._locale || getLocale(config._l); 32219 32220 if (input === null || (format === undefined && input === '')) { 32221 return createInvalid({nullInput: true}); 32222 } 32223 32224 if (typeof input === 'string') { 32225 config._i = input = config._locale.preparse(input); 32226 } 32227 32228 if (isMoment(input)) { 32229 return new Moment(checkOverflow(input)); 32230 } else if (isDate(input)) { 32231 config._d = input; 32232 } else if (isArray(format)) { 32233 configFromStringAndArray(config); 32234 } else if (format) { 32235 configFromStringAndFormat(config); 32236 } else { 32237 configFromInput(config); 32238 } 32239 32240 if (!isValid(config)) { 32241 config._d = null; 32242 } 32243 32244 return config; 32245} 32246 32247function configFromInput(config) { 32248 var input = config._i; 32249 if (isUndefined(input)) { 32250 config._d = new Date(hooks.now()); 32251 } else if (isDate(input)) { 32252 config._d = new Date(input.valueOf()); 32253 } else if (typeof input === 'string') { 32254 configFromString(config); 32255 } else if (isArray(input)) { 32256 config._a = map(input.slice(0), function (obj) { 32257 return parseInt(obj, 10); 32258 }); 32259 configFromArray(config); 32260 } else if (isObject(input)) { 32261 configFromObject(config); 32262 } else if (isNumber(input)) { 32263 // from milliseconds 32264 config._d = new Date(input); 32265 } else { 32266 hooks.createFromInputFallback(config); 32267 } 32268} 32269 32270function createLocalOrUTC (input, format, locale, strict, isUTC) { 32271 var c = {}; 32272 32273 if (locale === true || locale === false) { 32274 strict = locale; 32275 locale = undefined; 32276 } 32277 32278 if ((isObject(input) && isObjectEmpty(input)) || 32279 (isArray(input) && input.length === 0)) { 32280 input = undefined; 32281 } 32282 // object construction must be done this way. 32283 // https://github.com/moment/moment/issues/1423 32284 c._isAMomentObject = true; 32285 c._useUTC = c._isUTC = isUTC; 32286 c._l = locale; 32287 c._i = input; 32288 c._f = format; 32289 c._strict = strict; 32290 32291 return createFromConfig(c); 32292} 32293 32294function createLocal (input, format, locale, strict) { 32295 return createLocalOrUTC(input, format, locale, strict, false); 32296} 32297 32298var prototypeMin = deprecate( 32299 'moment().min is deprecated, use moment.max instead. http://momentjs.com/guides/#/warnings/min-max/', 32300 function () { 32301 var other = createLocal.apply(null, arguments); 32302 if (this.isValid() && other.isValid()) { 32303 return other < this ? this : other; 32304 } else { 32305 return createInvalid(); 32306 } 32307 } 32308); 32309 32310var prototypeMax = deprecate( 32311 'moment().max is deprecated, use moment.min instead. http://momentjs.com/guides/#/warnings/min-max/', 32312 function () { 32313 var other = createLocal.apply(null, arguments); 32314 if (this.isValid() && other.isValid()) { 32315 return other > this ? this : other; 32316 } else { 32317 return createInvalid(); 32318 } 32319 } 32320); 32321 32322// Pick a moment m from moments so that m[fn](other) is true for all 32323// other. This relies on the function fn to be transitive. 32324// 32325// moments should either be an array of moment objects or an array, whose 32326// first element is an array of moment objects. 32327function pickBy(fn, moments) { 32328 var res, i; 32329 if (moments.length === 1 && isArray(moments[0])) { 32330 moments = moments[0]; 32331 } 32332 if (!moments.length) { 32333 return createLocal(); 32334 } 32335 res = moments[0]; 32336 for (i = 1; i < moments.length; ++i) { 32337 if (!moments[i].isValid() || moments[i][fn](res)) { 32338 res = moments[i]; 32339 } 32340 } 32341 return res; 32342} 32343 32344// TODO: Use [].sort instead? 32345function min () { 32346 var args = [].slice.call(arguments, 0); 32347 32348 return pickBy('isBefore', args); 32349} 32350 32351function max () { 32352 var args = [].slice.call(arguments, 0); 32353 32354 return pickBy('isAfter', args); 32355} 32356 32357var now = function () { 32358 return Date.now ? Date.now() : +(new Date()); 32359}; 32360 32361var ordering = ['year', 'quarter', 'month', 'week', 'day', 'hour', 'minute', 'second', 'millisecond']; 32362 32363function isDurationValid(m) { 32364 for (var key in m) { 32365 if (!(indexOf.call(ordering, key) !== -1 && (m[key] == null || !isNaN(m[key])))) { 32366 return false; 32367 } 32368 } 32369 32370 var unitHasDecimal = false; 32371 for (var i = 0; i < ordering.length; ++i) { 32372 if (m[ordering[i]]) { 32373 if (unitHasDecimal) { 32374 return false; // only allow non-integers for smallest unit 32375 } 32376 if (parseFloat(m[ordering[i]]) !== toInt(m[ordering[i]])) { 32377 unitHasDecimal = true; 32378 } 32379 } 32380 } 32381 32382 return true; 32383} 32384 32385function isValid$1() { 32386 return this._isValid; 32387} 32388 32389function createInvalid$1() { 32390 return createDuration(NaN); 32391} 32392 32393function Duration (duration) { 32394 var normalizedInput = normalizeObjectUnits(duration), 32395 years = normalizedInput.year || 0, 32396 quarters = normalizedInput.quarter || 0, 32397 months = normalizedInput.month || 0, 32398 weeks = normalizedInput.week || 0, 32399 days = normalizedInput.day || 0, 32400 hours = normalizedInput.hour || 0, 32401 minutes = normalizedInput.minute || 0, 32402 seconds = normalizedInput.second || 0, 32403 milliseconds = normalizedInput.millisecond || 0; 32404 32405 this._isValid = isDurationValid(normalizedInput); 32406 32407 // representation for dateAddRemove 32408 this._milliseconds = +milliseconds + 32409 seconds * 1e3 + // 1000 32410 minutes * 6e4 + // 1000 * 60 32411 hours * 1000 * 60 * 60; //using 1000 * 60 * 60 instead of 36e5 to avoid floating point rounding errors https://github.com/moment/moment/issues/2978 32412 // Because of dateAddRemove treats 24 hours as different from a 32413 // day when working around DST, we need to store them separately 32414 this._days = +days + 32415 weeks * 7; 32416 // It is impossible to translate months into days without knowing 32417 // which months you are are talking about, so we have to store 32418 // it separately. 32419 this._months = +months + 32420 quarters * 3 + 32421 years * 12; 32422 32423 this._data = {}; 32424 32425 this._locale = getLocale(); 32426 32427 this._bubble(); 32428} 32429 32430function isDuration (obj) { 32431 return obj instanceof Duration; 32432} 32433 32434function absRound (number) { 32435 if (number < 0) { 32436 return Math.round(-1 * number) * -1; 32437 } else { 32438 return Math.round(number); 32439 } 32440} 32441 32442// FORMATTING 32443 32444function offset (token, separator) { 32445 addFormatToken(token, 0, 0, function () { 32446 var offset = this.utcOffset(); 32447 var sign = '+'; 32448 if (offset < 0) { 32449 offset = -offset; 32450 sign = '-'; 32451 } 32452 return sign + zeroFill(~~(offset / 60), 2) + separator + zeroFill(~~(offset) % 60, 2); 32453 }); 32454} 32455 32456offset('Z', ':'); 32457offset('ZZ', ''); 32458 32459// PARSING 32460 32461addRegexToken('Z', matchShortOffset); 32462addRegexToken('ZZ', matchShortOffset); 32463addParseToken(['Z', 'ZZ'], function (input, array, config) { 32464 config._useUTC = true; 32465 config._tzm = offsetFromString(matchShortOffset, input); 32466}); 32467 32468// HELPERS 32469 32470// timezone chunker 32471// '+10:00' > ['10', '00'] 32472// '-1530' > ['-15', '30'] 32473var chunkOffset = /([\+\-]|\d\d)/gi; 32474 32475function offsetFromString(matcher, string) { 32476 var matches = (string || '').match(matcher); 32477 32478 if (matches === null) { 32479 return null; 32480 } 32481 32482 var chunk = matches[matches.length - 1] || []; 32483 var parts = (chunk + '').match(chunkOffset) || ['-', 0, 0]; 32484 var minutes = +(parts[1] * 60) + toInt(parts[2]); 32485 32486 return minutes === 0 ? 32487 0 : 32488 parts[0] === '+' ? minutes : -minutes; 32489} 32490 32491// Return a moment from input, that is local/utc/zone equivalent to model. 32492function cloneWithOffset(input, model) { 32493 var res, diff; 32494 if (model._isUTC) { 32495 res = model.clone(); 32496 diff = (isMoment(input) || isDate(input) ? input.valueOf() : createLocal(input).valueOf()) - res.valueOf(); 32497 // Use low-level api, because this fn is low-level api. 32498 res._d.setTime(res._d.valueOf() + diff); 32499 hooks.updateOffset(res, false); 32500 return res; 32501 } else { 32502 return createLocal(input).local(); 32503 } 32504} 32505 32506function getDateOffset (m) { 32507 // On Firefox.24 Date#getTimezoneOffset returns a floating point. 32508 // https://github.com/moment/moment/pull/1871 32509 return -Math.round(m._d.getTimezoneOffset() / 15) * 15; 32510} 32511 32512// HOOKS 32513 32514// This function will be called whenever a moment is mutated. 32515// It is intended to keep the offset in sync with the timezone. 32516hooks.updateOffset = function () {}; 32517 32518// MOMENTS 32519 32520// keepLocalTime = true means only change the timezone, without 32521// affecting the local hour. So 5:31:26 +0300 --[utcOffset(2, true)]--> 32522// 5:31:26 +0200 It is possible that 5:31:26 doesn't exist with offset 32523// +0200, so we adjust the time as needed, to be valid. 32524// 32525// Keeping the time actually adds/subtracts (one hour) 32526// from the actual represented time. That is why we call updateOffset 32527// a second time. In case it wants us to change the offset again 32528// _changeInProgress == true case, then we have to adjust, because 32529// there is no such time in the given timezone. 32530function getSetOffset (input, keepLocalTime, keepMinutes) { 32531 var offset = this._offset || 0, 32532 localAdjust; 32533 if (!this.isValid()) { 32534 return input != null ? this : NaN; 32535 } 32536 if (input != null) { 32537 if (typeof input === 'string') { 32538 input = offsetFromString(matchShortOffset, input); 32539 if (input === null) { 32540 return this; 32541 } 32542 } else if (Math.abs(input) < 16 && !keepMinutes) { 32543 input = input * 60; 32544 } 32545 if (!this._isUTC && keepLocalTime) { 32546 localAdjust = getDateOffset(this); 32547 } 32548 this._offset = input; 32549 this._isUTC = true; 32550 if (localAdjust != null) { 32551 this.add(localAdjust, 'm'); 32552 } 32553 if (offset !== input) { 32554 if (!keepLocalTime || this._changeInProgress) { 32555 addSubtract(this, createDuration(input - offset, 'm'), 1, false); 32556 } else if (!this._changeInProgress) { 32557 this._changeInProgress = true; 32558 hooks.updateOffset(this, true); 32559 this._changeInProgress = null; 32560 } 32561 } 32562 return this; 32563 } else { 32564 return this._isUTC ? offset : getDateOffset(this); 32565 } 32566} 32567 32568function getSetZone (input, keepLocalTime) { 32569 if (input != null) { 32570 if (typeof input !== 'string') { 32571 input = -input; 32572 } 32573 32574 this.utcOffset(input, keepLocalTime); 32575 32576 return this; 32577 } else { 32578 return -this.utcOffset(); 32579 } 32580} 32581 32582function setOffsetToUTC (keepLocalTime) { 32583 return this.utcOffset(0, keepLocalTime); 32584} 32585 32586function setOffsetToLocal (keepLocalTime) { 32587 if (this._isUTC) { 32588 this.utcOffset(0, keepLocalTime); 32589 this._isUTC = false; 32590 32591 if (keepLocalTime) { 32592 this.subtract(getDateOffset(this), 'm'); 32593 } 32594 } 32595 return this; 32596} 32597 32598function setOffsetToParsedOffset () { 32599 if (this._tzm != null) { 32600 this.utcOffset(this._tzm, false, true); 32601 } else if (typeof this._i === 'string') { 32602 var tZone = offsetFromString(matchOffset, this._i); 32603 if (tZone != null) { 32604 this.utcOffset(tZone); 32605 } 32606 else { 32607 this.utcOffset(0, true); 32608 } 32609 } 32610 return this; 32611} 32612 32613function hasAlignedHourOffset (input) { 32614 if (!this.isValid()) { 32615 return false; 32616 } 32617 input = input ? createLocal(input).utcOffset() : 0; 32618 32619 return (this.utcOffset() - input) % 60 === 0; 32620} 32621 32622function isDaylightSavingTime () { 32623 return ( 32624 this.utcOffset() > this.clone().month(0).utcOffset() || 32625 this.utcOffset() > this.clone().month(5).utcOffset() 32626 ); 32627} 32628 32629function isDaylightSavingTimeShifted () { 32630 if (!isUndefined(this._isDSTShifted)) { 32631 return this._isDSTShifted; 32632 } 32633 32634 var c = {}; 32635 32636 copyConfig(c, this); 32637 c = prepareConfig(c); 32638 32639 if (c._a) { 32640 var other = c._isUTC ? createUTC(c._a) : createLocal(c._a); 32641 this._isDSTShifted = this.isValid() && 32642 compareArrays(c._a, other.toArray()) > 0; 32643 } else { 32644 this._isDSTShifted = false; 32645 } 32646 32647 return this._isDSTShifted; 32648} 32649 32650function isLocal () { 32651 return this.isValid() ? !this._isUTC : false; 32652} 32653 32654function isUtcOffset () { 32655 return this.isValid() ? this._isUTC : false; 32656} 32657 32658function isUtc () { 32659 return this.isValid() ? this._isUTC && this._offset === 0 : false; 32660} 32661 32662// ASP.NET json date format regex 32663var aspNetRegex = /^(\-|\+)?(?:(\d*)[. ])?(\d+)\:(\d+)(?:\:(\d+)(\.\d*)?)?$/; 32664 32665// from http://docs.closure-library.googlecode.com/git/closure_goog_date_date.js.source.html 32666// somewhat more in line with 4.4.3.2 2004 spec, but allows decimal anywhere 32667// and further modified to allow for strings containing both week and day 32668var isoRegex = /^(-|\+)?P(?:([-+]?[0-9,.]*)Y)?(?:([-+]?[0-9,.]*)M)?(?:([-+]?[0-9,.]*)W)?(?:([-+]?[0-9,.]*)D)?(?:T(?:([-+]?[0-9,.]*)H)?(?:([-+]?[0-9,.]*)M)?(?:([-+]?[0-9,.]*)S)?)?$/; 32669 32670function createDuration (input, key) { 32671 var duration = input, 32672 // matching against regexp is expensive, do it on demand 32673 match = null, 32674 sign, 32675 ret, 32676 diffRes; 32677 32678 if (isDuration(input)) { 32679 duration = { 32680 ms : input._milliseconds, 32681 d : input._days, 32682 M : input._months 32683 }; 32684 } else if (isNumber(input)) { 32685 duration = {}; 32686 if (key) { 32687 duration[key] = input; 32688 } else { 32689 duration.milliseconds = input; 32690 } 32691 } else if (!!(match = aspNetRegex.exec(input))) { 32692 sign = (match[1] === '-') ? -1 : 1; 32693 duration = { 32694 y : 0, 32695 d : toInt(match[DATE]) * sign, 32696 h : toInt(match[HOUR]) * sign, 32697 m : toInt(match[MINUTE]) * sign, 32698 s : toInt(match[SECOND]) * sign, 32699 ms : toInt(absRound(match[MILLISECOND] * 1000)) * sign // the millisecond decimal point is included in the match 32700 }; 32701 } else if (!!(match = isoRegex.exec(input))) { 32702 sign = (match[1] === '-') ? -1 : (match[1] === '+') ? 1 : 1; 32703 duration = { 32704 y : parseIso(match[2], sign), 32705 M : parseIso(match[3], sign), 32706 w : parseIso(match[4], sign), 32707 d : parseIso(match[5], sign), 32708 h : parseIso(match[6], sign), 32709 m : parseIso(match[7], sign), 32710 s : parseIso(match[8], sign) 32711 }; 32712 } else if (duration == null) {// checks for null or undefined 32713 duration = {}; 32714 } else if (typeof duration === 'object' && ('from' in duration || 'to' in duration)) { 32715 diffRes = momentsDifference(createLocal(duration.from), createLocal(duration.to)); 32716 32717 duration = {}; 32718 duration.ms = diffRes.milliseconds; 32719 duration.M = diffRes.months; 32720 } 32721 32722 ret = new Duration(duration); 32723 32724 if (isDuration(input) && hasOwnProp(input, '_locale')) { 32725 ret._locale = input._locale; 32726 } 32727 32728 return ret; 32729} 32730 32731createDuration.fn = Duration.prototype; 32732createDuration.invalid = createInvalid$1; 32733 32734function parseIso (inp, sign) { 32735 // We'd normally use ~~inp for this, but unfortunately it also 32736 // converts floats to ints. 32737 // inp may be undefined, so careful calling replace on it. 32738 var res = inp && parseFloat(inp.replace(',', '.')); 32739 // apply sign while we're at it 32740 return (isNaN(res) ? 0 : res) * sign; 32741} 32742 32743function positiveMomentsDifference(base, other) { 32744 var res = {milliseconds: 0, months: 0}; 32745 32746 res.months = other.month() - base.month() + 32747 (other.year() - base.year()) * 12; 32748 if (base.clone().add(res.months, 'M').isAfter(other)) { 32749 --res.months; 32750 } 32751 32752 res.milliseconds = +other - +(base.clone().add(res.months, 'M')); 32753 32754 return res; 32755} 32756 32757function momentsDifference(base, other) { 32758 var res; 32759 if (!(base.isValid() && other.isValid())) { 32760 return {milliseconds: 0, months: 0}; 32761 } 32762 32763 other = cloneWithOffset(other, base); 32764 if (base.isBefore(other)) { 32765 res = positiveMomentsDifference(base, other); 32766 } else { 32767 res = positiveMomentsDifference(other, base); 32768 res.milliseconds = -res.milliseconds; 32769 res.months = -res.months; 32770 } 32771 32772 return res; 32773} 32774 32775// TODO: remove 'name' arg after deprecation is removed 32776function createAdder(direction, name) { 32777 return function (val, period) { 32778 var dur, tmp; 32779 //invert the arguments, but complain about it 32780 if (period !== null && !isNaN(+period)) { 32781 deprecateSimple(name, 'moment().' + name + '(period, number) is deprecated. Please use moment().' + name + '(number, period). ' + 32782 'See http://momentjs.com/guides/#/warnings/add-inverted-param/ for more info.'); 32783 tmp = val; val = period; period = tmp; 32784 } 32785 32786 val = typeof val === 'string' ? +val : val; 32787 dur = createDuration(val, period); 32788 addSubtract(this, dur, direction); 32789 return this; 32790 }; 32791} 32792 32793function addSubtract (mom, duration, isAdding, updateOffset) { 32794 var milliseconds = duration._milliseconds, 32795 days = absRound(duration._days), 32796 months = absRound(duration._months); 32797 32798 if (!mom.isValid()) { 32799 // No op 32800 return; 32801 } 32802 32803 updateOffset = updateOffset == null ? true : updateOffset; 32804 32805 if (months) { 32806 setMonth(mom, get(mom, 'Month') + months * isAdding); 32807 } 32808 if (days) { 32809 set$1(mom, 'Date', get(mom, 'Date') + days * isAdding); 32810 } 32811 if (milliseconds) { 32812 mom._d.setTime(mom._d.valueOf() + milliseconds * isAdding); 32813 } 32814 if (updateOffset) { 32815 hooks.updateOffset(mom, days || months); 32816 } 32817} 32818 32819var add = createAdder(1, 'add'); 32820var subtract = createAdder(-1, 'subtract'); 32821 32822function getCalendarFormat(myMoment, now) { 32823 var diff = myMoment.diff(now, 'days', true); 32824 return diff < -6 ? 'sameElse' : 32825 diff < -1 ? 'lastWeek' : 32826 diff < 0 ? 'lastDay' : 32827 diff < 1 ? 'sameDay' : 32828 diff < 2 ? 'nextDay' : 32829 diff < 7 ? 'nextWeek' : 'sameElse'; 32830} 32831 32832function calendar$1 (time, formats) { 32833 // We want to compare the start of today, vs this. 32834 // Getting start-of-today depends on whether we're local/utc/offset or not. 32835 var now = time || createLocal(), 32836 sod = cloneWithOffset(now, this).startOf('day'), 32837 format = hooks.calendarFormat(this, sod) || 'sameElse'; 32838 32839 var output = formats && (isFunction(formats[format]) ? formats[format].call(this, now) : formats[format]); 32840 32841 return this.format(output || this.localeData().calendar(format, this, createLocal(now))); 32842} 32843 32844function clone () { 32845 return new Moment(this); 32846} 32847 32848function isAfter (input, units) { 32849 var localInput = isMoment(input) ? input : createLocal(input); 32850 if (!(this.isValid() && localInput.isValid())) { 32851 return false; 32852 } 32853 units = normalizeUnits(!isUndefined(units) ? units : 'millisecond'); 32854 if (units === 'millisecond') { 32855 return this.valueOf() > localInput.valueOf(); 32856 } else { 32857 return localInput.valueOf() < this.clone().startOf(units).valueOf(); 32858 } 32859} 32860 32861function isBefore (input, units) { 32862 var localInput = isMoment(input) ? input : createLocal(input); 32863 if (!(this.isValid() && localInput.isValid())) { 32864 return false; 32865 } 32866 units = normalizeUnits(!isUndefined(units) ? units : 'millisecond'); 32867 if (units === 'millisecond') { 32868 return this.valueOf() < localInput.valueOf(); 32869 } else { 32870 return this.clone().endOf(units).valueOf() < localInput.valueOf(); 32871 } 32872} 32873 32874function isBetween (from, to, units, inclusivity) { 32875 inclusivity = inclusivity || '()'; 32876 return (inclusivity[0] === '(' ? this.isAfter(from, units) : !this.isBefore(from, units)) && 32877 (inclusivity[1] === ')' ? this.isBefore(to, units) : !this.isAfter(to, units)); 32878} 32879 32880function isSame (input, units) { 32881 var localInput = isMoment(input) ? input : createLocal(input), 32882 inputMs; 32883 if (!(this.isValid() && localInput.isValid())) { 32884 return false; 32885 } 32886 units = normalizeUnits(units || 'millisecond'); 32887 if (units === 'millisecond') { 32888 return this.valueOf() === localInput.valueOf(); 32889 } else { 32890 inputMs = localInput.valueOf(); 32891 return this.clone().startOf(units).valueOf() <= inputMs && inputMs <= this.clone().endOf(units).valueOf(); 32892 } 32893} 32894 32895function isSameOrAfter (input, units) { 32896 return this.isSame(input, units) || this.isAfter(input,units); 32897} 32898 32899function isSameOrBefore (input, units) { 32900 return this.isSame(input, units) || this.isBefore(input,units); 32901} 32902 32903function diff (input, units, asFloat) { 32904 var that, 32905 zoneDelta, 32906 delta, output; 32907 32908 if (!this.isValid()) { 32909 return NaN; 32910 } 32911 32912 that = cloneWithOffset(input, this); 32913 32914 if (!that.isValid()) { 32915 return NaN; 32916 } 32917 32918 zoneDelta = (that.utcOffset() - this.utcOffset()) * 6e4; 32919 32920 units = normalizeUnits(units); 32921 32922 switch (units) { 32923 case 'year': output = monthDiff(this, that) / 12; break; 32924 case 'month': output = monthDiff(this, that); break; 32925 case 'quarter': output = monthDiff(this, that) / 3; break; 32926 case 'second': output = (this - that) / 1e3; break; // 1000 32927 case 'minute': output = (this - that) / 6e4; break; // 1000 * 60 32928 case 'hour': output = (this - that) / 36e5; break; // 1000 * 60 * 60 32929 case 'day': output = (this - that - zoneDelta) / 864e5; break; // 1000 * 60 * 60 * 24, negate dst 32930 case 'week': output = (this - that - zoneDelta) / 6048e5; break; // 1000 * 60 * 60 * 24 * 7, negate dst 32931 default: output = this - that; 32932 } 32933 32934 return asFloat ? output : absFloor(output); 32935} 32936 32937function monthDiff (a, b) { 32938 // difference in months 32939 var wholeMonthDiff = ((b.year() - a.year()) * 12) + (b.month() - a.month()), 32940 // b is in (anchor - 1 month, anchor + 1 month) 32941 anchor = a.clone().add(wholeMonthDiff, 'months'), 32942 anchor2, adjust; 32943 32944 if (b - anchor < 0) { 32945 anchor2 = a.clone().add(wholeMonthDiff - 1, 'months'); 32946 // linear across the month 32947 adjust = (b - anchor) / (anchor - anchor2); 32948 } else { 32949 anchor2 = a.clone().add(wholeMonthDiff + 1, 'months'); 32950 // linear across the month 32951 adjust = (b - anchor) / (anchor2 - anchor); 32952 } 32953 32954 //check for negative zero, return zero if negative zero 32955 return -(wholeMonthDiff + adjust) || 0; 32956} 32957 32958hooks.defaultFormat = 'YYYY-MM-DDTHH:mm:ssZ'; 32959hooks.defaultFormatUtc = 'YYYY-MM-DDTHH:mm:ss[Z]'; 32960 32961function toString () { 32962 return this.clone().locale('en').format('ddd MMM DD YYYY HH:mm:ss [GMT]ZZ'); 32963} 32964 32965function toISOString() { 32966 if (!this.isValid()) { 32967 return null; 32968 } 32969 var m = this.clone().utc(); 32970 if (m.year() < 0 || m.year() > 9999) { 32971 return formatMoment(m, 'YYYYYY-MM-DD[T]HH:mm:ss.SSS[Z]'); 32972 } 32973 if (isFunction(Date.prototype.toISOString)) { 32974 // native implementation is ~50x faster, use it when we can 32975 return this.toDate().toISOString(); 32976 } 32977 return formatMoment(m, 'YYYY-MM-DD[T]HH:mm:ss.SSS[Z]'); 32978} 32979 32980/** 32981 * Return a human readable representation of a moment that can 32982 * also be evaluated to get a new moment which is the same 32983 * 32984 * @link https://nodejs.org/dist/latest/docs/api/util.html#util_custom_inspect_function_on_objects 32985 */ 32986function inspect () { 32987 if (!this.isValid()) {
32988 return 'moment.invalid(/* ' + this._i + ' */)'; 32989 } 32990 var func = 'moment'; 32991 var zone = ''; 32992 if (!this.isLocal()) { 32993 func = this.utcOffset() === 0 ? 'moment.utc' : 'moment.parseZone'; 32994 zone = 'Z'; 32995 } 32996 var prefix = '[' + func + '("]'; 32997 var year = (0 <= this.year() && this.year() <= 9999) ? 'YYYY' : 'YYYYYY'; 32998 var datetime = '-MM-DD[T]HH:mm:ss.SSS'; 32999 var suffix = zone + '[")]'; 33000 33001 return this.format(prefix + year + datetime + suffix); 33002} 33003 33004function format (inputString) { 33005 if (!inputString) { 33006 inputString = this.isUtc() ? hooks.defaultFormatUtc : hooks.defaultFormat; 33007 } 33008 var output = formatMoment(this, inputString); 33009 return this.localeData().postformat(output); 33010} 33011 33012function from (time, withoutSuffix) { 33013 if (this.isValid() && 33014 ((isMoment(time) && time.isValid()) || 33015 createLocal(time).isValid())) { 33016 return createDuration({to: this, from: time}).locale(this.locale()).humanize(!withoutSuffix); 33017 } else { 33018 return this.localeData().invalidDate(); 33019 } 33020} 33021 33022function fromNow (withoutSuffix) { 33023 return this.from(createLocal(), withoutSuffix); 33024} 33025 33026function to (time, withoutSuffix) { 33027 if (this.isValid() && 33028 ((isMoment(time) && time.isValid()) || 33029 createLocal(time).isValid())) { 33030 return createDuration({from: this, to: time}).locale(this.locale()).humanize(!withoutSuffix); 33031 } else { 33032 return this.localeData().invalidDate(); 33033 } 33034} 33035 33036function toNow (withoutSuffix) { 33037 return this.to(createLocal(), withoutSuffix); 33038} 33039 33040// If passed a locale key, it will set the locale for this 33041// instance. Otherwise, it will return the locale configuration 33042// variables for this instance. 33043function locale (key) { 33044 var newLocaleData; 33045 33046 if (key === undefined) { 33047 return this._locale._abbr; 33048 } else { 33049 newLocaleData = getLocale(key); 33050 if (newLocaleData != null) { 33051 this._locale = newLocaleData; 33052 } 33053 return this; 33054 } 33055} 33056 33057var lang = deprecate( 33058 'moment().lang() is deprecated. Instead, use moment().localeData() to get the language configuration. Use moment().locale() to change languages.', 33059 function (key) { 33060 if (key === undefined) { 33061 return this.localeData(); 33062 } else { 33063 return this.locale(key); 33064 } 33065 } 33066); 33067 33068function localeData () { 33069 return this._locale; 33070} 33071 33072function startOf (units) { 33073 units = normalizeUnits(units); 33074 // the following switch intentionally omits break keywords 33075 // to utilize falling through the cases. 33076 switch (units) { 33077 case 'year': 33078 this.month(0); 33079 /* falls through */ 33080 case 'quarter': 33081 case 'month': 33082 this.date(1); 33083 /* falls through */ 33084 case 'week': 33085 case 'isoWeek': 33086 case 'day': 33087 case 'date': 33088 this.hours(0); 33089 /* falls through */ 33090 case 'hour': 33091 this.minutes(0); 33092 /* falls through */ 33093 case 'minute': 33094 this.seconds(0); 33095 /* falls through */ 33096 case 'second': 33097 this.milliseconds(0); 33098 } 33099 33100 // weeks are a special case 33101 if (units === 'week') { 33102 this.weekday(0); 33103 } 33104 if (units === 'isoWeek') { 33105 this.isoWeekday(1); 33106 } 33107 33108 // quarters are also special 33109 if (units === 'quarter') { 33110 this.month(Math.floor(this.month() / 3) * 3); 33111 } 33112 33113 return this; 33114} 33115 33116function endOf (units) { 33117 units = normalizeUnits(units); 33118 if (units === undefined || units === 'millisecond') { 33119 return this; 33120 } 33121 33122 // 'date' is an alias for 'day', so it should be considered as such. 33123 if (units === 'date') { 33124 units = 'day'; 33125 } 33126 33127 return this.startOf(units).add(1, (units === 'isoWeek' ? 'week' : units)).subtract(1, 'ms'); 33128} 33129 33130function valueOf () { 33131 return this._d.valueOf() - ((this._offset || 0) * 60000); 33132} 33133 33134function unix () { 33135 return Math.floor(this.valueOf() / 1000); 33136} 33137 33138function toDate () { 33139 return new Date(this.valueOf()); 33140} 33141 33142function toArray () { 33143 var m = this; 33144 return [m.year(), m.month(), m.date(), m.hour(), m.minute(), m.second(), m.millisecond()]; 33145} 33146 33147function toObject () { 33148 var m = this; 33149 return { 33150 years: m.year(), 33151 months: m.month(), 33152 date: m.date(), 33153 hours: m.hours(), 33154 minutes: m.minutes(), 33155 seconds: m.seconds(), 33156 milliseconds: m.milliseconds() 33157 }; 33158} 33159 33160function toJSON () { 33161 // new Date(NaN).toJSON() === null 33162 return this.isValid() ? this.toISOString() : null; 33163} 33164 33165function isValid$2 () { 33166 return isValid(this); 33167} 33168 33169function parsingFlags () { 33170 return extend({}, getParsingFlags(this)); 33171} 33172
vendor: 26,181 bytes, lines 33173-34134
33173function invalidAt () { 33174 return getParsingFlags(this).overflow; 33175} 33176 33177function creationData() { 33178 return { 33179 input: this._i, 33180 format: this._f, 33181 locale: this._locale, 33182 isUTC: this._isUTC, 33183 strict: this._strict 33184 }; 33185} 33186 33187// FORMATTING 33188 33189addFormatToken(0, ['gg', 2], 0, function () { 33190 return this.weekYear() % 100; 33191}); 33192 33193addFormatToken(0, ['GG', 2], 0, function () { 33194 return this.isoWeekYear() % 100; 33195}); 33196 33197function addWeekYearFormatToken (token, getter) { 33198 addFormatToken(0, [token, token.length], 0, getter); 33199} 33200 33201addWeekYearFormatToken('gggg', 'weekYear'); 33202addWeekYearFormatToken('ggggg', 'weekYear'); 33203addWeekYearFormatToken('GGGG', 'isoWeekYear'); 33204addWeekYearFormatToken('GGGGG', 'isoWeekYear'); 33205 33206// ALIASES 33207 33208addUnitAlias('weekYear', 'gg'); 33209addUnitAlias('isoWeekYear', 'GG'); 33210 33211// PRIORITY 33212 33213addUnitPriority('weekYear', 1); 33214addUnitPriority('isoWeekYear', 1); 33215 33216 33217// PARSING 33218 33219addRegexToken('G', matchSigned); 33220addRegexToken('g', matchSigned); 33221addRegexToken('GG', match1to2, match2); 33222addRegexToken('gg', match1to2, match2); 33223addRegexToken('GGGG', match1to4, match4); 33224addRegexToken('gggg', match1to4, match4); 33225addRegexToken('GGGGG', match1to6, match6); 33226addRegexToken('ggggg', match1to6, match6); 33227 33228addWeekParseToken(['gggg', 'ggggg', 'GGGG', 'GGGGG'], function (input, week, config, token) { 33229 week[token.substr(0, 2)] = toInt(input); 33230}); 33231 33232addWeekParseToken(['gg', 'GG'], function (input, week, config, token) { 33233 week[token] = hooks.parseTwoDigitYear(input); 33234}); 33235 33236// MOMENTS 33237 33238function getSetWeekYear (input) { 33239 return getSetWeekYearHelper.call(this, 33240 input, 33241 this.week(), 33242 this.weekday(), 33243 this.localeData()._week.dow, 33244 this.localeData()._week.doy); 33245} 33246 33247function getSetISOWeekYear (input) { 33248 return getSetWeekYearHelper.call(this, 33249 input, this.isoWeek(), this.isoWeekday(), 1, 4); 33250} 33251 33252function getISOWeeksInYear () { 33253 return weeksInYear(this.year(), 1, 4); 33254} 33255 33256function getWeeksInYear () { 33257 var weekInfo = this.localeData()._week; 33258 return weeksInYear(this.year(), weekInfo.dow, weekInfo.doy); 33259} 33260 33261function getSetWeekYearHelper(input, week, weekday, dow, doy) { 33262 var weeksTarget; 33263 if (input == null) { 33264 return weekOfYear(this, dow, doy).year; 33265 } else { 33266 weeksTarget = weeksInYear(input, dow, doy); 33267 if (week > weeksTarget) { 33268 week = weeksTarget; 33269 } 33270 return setWeekAll.call(this, input, week, weekday, dow, doy); 33271 } 33272} 33273 33274function setWeekAll(weekYear, week, weekday, dow, doy) { 33275 var dayOfYearData = dayOfYearFromWeeks(weekYear, week, weekday, dow, doy), 33276 date = createUTCDate(dayOfYearData.year, 0, dayOfYearData.dayOfYear); 33277 33278 this.year(date.getUTCFullYear()); 33279 this.month(date.getUTCMonth()); 33280 this.date(date.getUTCDate()); 33281 return this; 33282} 33283 33284// FORMATTING 33285 33286addFormatToken('Q', 0, 'Qo', 'quarter'); 33287 33288// ALIASES 33289 33290addUnitAlias('quarter', 'Q'); 33291 33292// PRIORITY 33293 33294addUnitPriority('quarter', 7); 33295 33296// PARSING 33297 33298addRegexToken('Q', match1); 33299addParseToken('Q', function (input, array) { 33300 array[MONTH] = (toInt(input) - 1) * 3; 33301}); 33302 33303// MOMENTS 33304 33305function getSetQuarter (input) { 33306 return input == null ? Math.ceil((this.month() + 1) / 3) : this.month((input - 1) * 3 + this.month() % 3); 33307} 33308 33309// FORMATTING 33310 33311addFormatToken('D', ['DD', 2], 'Do', 'date'); 33312 33313// ALIASES 33314 33315addUnitAlias('date', 'D'); 33316 33317// PRIOROITY 33318addUnitPriority('date', 9); 33319 33320// PARSING 33321 33322addRegexToken('D', match1to2); 33323addRegexToken('DD', match1to2, match2); 33324addRegexToken('Do', function (isStrict, locale) { 33325 // TODO: Remove "ordinalParse" fallback in next major release. 33326 return isStrict ? 33327 (locale._dayOfMonthOrdinalParse || locale._ordinalParse) : 33328 locale._dayOfMonthOrdinalParseLenient; 33329}); 33330 33331addParseToken(['D', 'DD'], DATE); 33332addParseToken('Do', function (input, array) { 33333 array[DATE] = toInt(input.match(match1to2)[0], 10); 33334}); 33335 33336// MOMENTS 33337 33338var getSetDayOfMonth = makeGetSet('Date', true); 33339 33340// FORMATTING 33341 33342addFormatToken('DDD', ['DDDD', 3], 'DDDo', 'dayOfYear'); 33343 33344// ALIASES 33345 33346addUnitAlias('dayOfYear', 'DDD'); 33347 33348// PRIORITY 33349addUnitPriority('dayOfYear', 4); 33350 33351// PARSING 33352 33353addRegexToken('DDD', match1to3); 33354addRegexToken('DDDD', match3); 33355addParseToken(['DDD', 'DDDD'], function (input, array, config) { 33356 config._dayOfYear = toInt(input); 33357}); 33358 33359// HELPERS 33360 33361// MOMENTS 33362 33363function getSetDayOfYear (input) { 33364 var dayOfYear = Math.round((this.clone().startOf('day') - this.clone().startOf('year')) / 864e5) + 1; 33365 return input == null ? dayOfYear : this.add((input - dayOfYear), 'd'); 33366} 33367 33368// FORMATTING 33369 33370addFormatToken('m', ['mm', 2], 0, 'minute'); 33371 33372// ALIASES 33373 33374addUnitAlias('minute', 'm'); 33375 33376// PRIORITY 33377 33378addUnitPriority('minute', 14); 33379 33380// PARSING 33381 33382addRegexToken('m', match1to2); 33383addRegexToken('mm', match1to2, match2); 33384addParseToken(['m', 'mm'], MINUTE); 33385 33386// MOMENTS 33387 33388var getSetMinute = makeGetSet('Minutes', false); 33389 33390// FORMATTING 33391 33392addFormatToken('s', ['ss', 2], 0, 'second'); 33393 33394// ALIASES 33395 33396addUnitAlias('second', 's'); 33397 33398// PRIORITY 33399 33400addUnitPriority('second', 15); 33401 33402// PARSING 33403 33404addRegexToken('s', match1to2); 33405addRegexToken('ss', match1to2, match2); 33406addParseToken(['s', 'ss'], SECOND); 33407 33408// MOMENTS 33409 33410var getSetSecond = makeGetSet('Seconds', false); 33411 33412// FORMATTING 33413 33414addFormatToken('S', 0, 0, function () { 33415 return ~~(this.millisecond() / 100); 33416}); 33417 33418addFormatToken(0, ['SS', 2], 0, function () { 33419 return ~~(this.millisecond() / 10); 33420}); 33421 33422addFormatToken(0, ['SSS', 3], 0, 'millisecond'); 33423addFormatToken(0, ['SSSS', 4], 0, function () { 33424 return this.millisecond() * 10; 33425}); 33426addFormatToken(0, ['SSSSS', 5], 0, function () { 33427 return this.millisecond() * 100; 33428}); 33429addFormatToken(0, ['SSSSSS', 6], 0, function () { 33430 return this.millisecond() * 1000; 33431}); 33432addFormatToken(0, ['SSSSSSS', 7], 0, function () { 33433 return this.millisecond() * 10000; 33434}); 33435addFormatToken(0, ['SSSSSSSS', 8], 0, function () { 33436 return this.millisecond() * 100000; 33437}); 33438addFormatToken(0, ['SSSSSSSSS', 9], 0, function () { 33439 return this.millisecond() * 1000000; 33440}); 33441 33442 33443// ALIASES 33444 33445addUnitAlias('millisecond', 'ms'); 33446 33447// PRIORITY 33448 33449addUnitPriority('millisecond', 16); 33450 33451// PARSING 33452 33453addRegexToken('S', match1to3, match1); 33454addRegexToken('SS', match1to3, match2); 33455addRegexToken('SSS', match1to3, match3); 33456 33457var token; 33458for (token = 'SSSS'; token.length <= 9; token += 'S') { 33459 addRegexToken(token, matchUnsigned); 33460} 33461 33462function parseMs(input, array) { 33463 array[MILLISECOND] = toInt(('0.' + input) * 1000); 33464} 33465 33466for (token = 'S'; token.length <= 9; token += 'S') { 33467 addParseToken(token, parseMs); 33468} 33469// MOMENTS 33470 33471var getSetMillisecond = makeGetSet('Milliseconds', false); 33472 33473// FORMATTING 33474 33475addFormatToken('z', 0, 0, 'zoneAbbr'); 33476addFormatToken('zz', 0, 0, 'zoneName'); 33477 33478// MOMENTS 33479 33480function getZoneAbbr () { 33481 return this._isUTC ? 'UTC' : ''; 33482} 33483 33484function getZoneName () { 33485 return this._isUTC ? 'Coordinated Universal Time' : ''; 33486} 33487 33488var proto = Moment.prototype; 33489 33490proto.add = add; 33491proto.calendar = calendar$1; 33492proto.clone = clone; 33493proto.diff = diff; 33494proto.endOf = endOf; 33495proto.format = format; 33496proto.from = from; 33497proto.fromNow = fromNow; 33498proto.to = to; 33499proto.toNow = toNow; 33500proto.get = stringGet; 33501proto.invalidAt = invalidAt; 33502proto.isAfter = isAfter; 33503proto.isBefore = isBefore; 33504proto.isBetween = isBetween; 33505proto.isSame = isSame; 33506proto.isSameOrAfter = isSameOrAfter; 33507proto.isSameOrBefore = isSameOrBefore; 33508proto.isValid = isValid$2; 33509proto.lang = lang; 33510proto.locale = locale; 33511proto.localeData = localeData; 33512proto.max = prototypeMax; 33513proto.min = prototypeMin; 33514proto.parsingFlags = parsingFlags; 33515proto.set = stringSet; 33516proto.startOf = startOf; 33517proto.subtract = subtract; 33518proto.toArray = toArray; 33519proto.toObject = toObject; 33520proto.toDate = toDate; 33521proto.toISOString = toISOString; 33522proto.inspect = inspect; 33523proto.toJSON = toJSON; 33524proto.toString = toString; 33525proto.unix = unix; 33526proto.valueOf = valueOf; 33527proto.creationData = creationData; 33528 33529// Year 33530proto.year = getSetYear; 33531proto.isLeapYear = getIsLeapYear; 33532 33533// Week Year 33534proto.weekYear = getSetWeekYear; 33535proto.isoWeekYear = getSetISOWeekYear; 33536 33537// Quarter 33538proto.quarter = proto.quarters = getSetQuarter; 33539 33540// Month 33541proto.month = getSetMonth; 33542proto.daysInMonth = getDaysInMonth; 33543 33544// Week 33545proto.week = proto.weeks = getSetWeek; 33546proto.isoWeek = proto.isoWeeks = getSetISOWeek; 33547proto.weeksInYear = getWeeksInYear; 33548proto.isoWeeksInYear = getISOWeeksInYear; 33549 33550// Day 33551proto.date = getSetDayOfMonth; 33552proto.day = proto.days = getSetDayOfWeek; 33553proto.weekday = getSetLocaleDayOfWeek; 33554proto.isoWeekday = getSetISODayOfWeek; 33555proto.dayOfYear = getSetDayOfYear; 33556 33557// Hour 33558proto.hour = proto.hours = getSetHour; 33559 33560// Minute 33561proto.minute = proto.minutes = getSetMinute; 33562 33563// Second 33564proto.second = proto.seconds = getSetSecond; 33565 33566// Millisecond 33567proto.millisecond = proto.milliseconds = getSetMillisecond; 33568 33569// Offset 33570proto.utcOffset = getSetOffset; 33571proto.utc = setOffsetToUTC; 33572proto.local = setOffsetToLocal; 33573proto.parseZone = setOffsetToParsedOffset; 33574proto.hasAlignedHourOffset = hasAlignedHourOffset; 33575proto.isDST = isDaylightSavingTime; 33576proto.isLocal = isLocal; 33577proto.isUtcOffset = isUtcOffset; 33578proto.isUtc = isUtc; 33579proto.isUTC = isUtc; 33580 33581// Timezone 33582proto.zoneAbbr = getZoneAbbr; 33583proto.zoneName = getZoneName; 33584 33585// Deprecations 33586proto.dates = deprecate('dates accessor is deprecated. Use date instead.', getSetDayOfMonth); 33587proto.months = deprecate('months accessor is deprecated. Use month instead', getSetMonth); 33588proto.years = deprecate('years accessor is deprecated. Use year instead', getSetYear); 33589proto.zone = deprecate('moment().zone is deprecated, use moment().utcOffset instead. http://momentjs.com/guides/#/warnings/zone/', getSetZone); 33590proto.isDSTShifted = deprecate('isDSTShifted is deprecated. See http://momentjs.com/guides/#/warnings/dst-shifted/ for more information', isDaylightSavingTimeShifted); 33591 33592function createUnix (input) { 33593 return createLocal(input * 1000); 33594} 33595 33596function createInZone () { 33597 return createLocal.apply(null, arguments).parseZone(); 33598} 33599 33600function preParsePostFormat (string) { 33601 return string; 33602} 33603 33604var proto$1 = Locale.prototype; 33605 33606proto$1.calendar = calendar; 33607proto$1.longDateFormat = longDateFormat; 33608proto$1.invalidDate = invalidDate; 33609proto$1.ordinal = ordinal; 33610proto$1.preparse = preParsePostFormat; 33611proto$1.postformat = preParsePostFormat; 33612proto$1.relativeTime = relativeTime; 33613proto$1.pastFuture = pastFuture; 33614proto$1.set = set; 33615 33616// Month 33617proto$1.months = localeMonths; 33618proto$1.monthsShort = localeMonthsShort; 33619proto$1.monthsParse = localeMonthsParse; 33620proto$1.monthsRegex = monthsRegex; 33621proto$1.monthsShortRegex = monthsShortRegex; 33622 33623// Week 33624proto$1.week = localeWeek; 33625proto$1.firstDayOfYear = localeFirstDayOfYear; 33626proto$1.firstDayOfWeek = localeFirstDayOfWeek; 33627 33628// Day of Week 33629proto$1.weekdays = localeWeekdays; 33630proto$1.weekdaysMin = localeWeekdaysMin; 33631proto$1.weekdaysShort = localeWeekdaysShort; 33632proto$1.weekdaysParse = localeWeekdaysParse; 33633 33634proto$1.weekdaysRegex = weekdaysRegex; 33635proto$1.weekdaysShortRegex = weekdaysShortRegex; 33636proto$1.weekdaysMinRegex = weekdaysMinRegex; 33637 33638// Hours 33639proto$1.isPM = localeIsPM; 33640proto$1.meridiem = localeMeridiem; 33641 33642function get$1 (format, index, field, setter) { 33643 var locale = getLocale(); 33644 var utc = createUTC().set(setter, index); 33645 return locale[field](utc, format); 33646} 33647 33648function listMonthsImpl (format, index, field) { 33649 if (isNumber(format)) { 33650 index = format; 33651 format = undefined; 33652 } 33653 33654 format = format || ''; 33655 33656 if (index != null) { 33657 return get$1(format, index, field, 'month'); 33658 } 33659 33660 var i; 33661 var out = []; 33662 for (i = 0; i < 12; i++) { 33663 out[i] = get$1(format, i, field, 'month'); 33664 } 33665 return out; 33666} 33667 33668// () 33669// (5) 33670// (fmt, 5) 33671// (fmt) 33672// (true) 33673// (true, 5) 33674// (true, fmt, 5) 33675// (true, fmt) 33676function listWeekdaysImpl (localeSorted, format, index, field) { 33677 if (typeof localeSorted === 'boolean') { 33678 if (isNumber(format)) { 33679 index = format; 33680 format = undefined; 33681 } 33682 33683 format = format || ''; 33684 } else { 33685 format = localeSorted; 33686 index = format; 33687 localeSorted = false; 33688 33689 if (isNumber(format)) { 33690 index = format; 33691 format = undefined; 33692 } 33693 33694 format = format || ''; 33695 } 33696 33697 var locale = getLocale(), 33698 shift = localeSorted ? locale._week.dow : 0; 33699 33700 if (index != null) { 33701 return get$1(format, (index + shift) % 7, field, 'day'); 33702 } 33703 33704 var i; 33705 var out = []; 33706 for (i = 0; i < 7; i++) { 33707 out[i] = get$1(format, (i + shift) % 7, field, 'day'); 33708 } 33709 return out; 33710} 33711 33712function listMonths (format, index) { 33713 return listMonthsImpl(format, index, 'months'); 33714} 33715 33716function listMonthsShort (format, index) { 33717 return listMonthsImpl(format, index, 'monthsShort'); 33718} 33719 33720function listWeekdays (localeSorted, format, index) { 33721 return listWeekdaysImpl(localeSorted, format, index, 'weekdays'); 33722} 33723 33724function listWeekdaysShort (localeSorted, format, index) { 33725 return listWeekdaysImpl(localeSorted, format, index, 'weekdaysShort'); 33726} 33727 33728function listWeekdaysMin (localeSorted, format, index) { 33729 return listWeekdaysImpl(localeSorted, format, index, 'weekdaysMin'); 33730} 33731 33732getSetGlobalLocale('en', { 33733 dayOfMonthOrdinalParse: /\d{1,2}(th|st|nd|rd)/, 33734 ordinal : function (number) { 33735 var b = number % 10, 33736 output = (toInt(number % 100 / 10) === 1) ? 'th' : 33737 (b === 1) ? 'st' : 33738 (b === 2) ? 'nd' : 33739 (b === 3) ? 'rd' : 'th'; 33740 return number + output; 33741 } 33742}); 33743 33744// Side effect imports 33745hooks.lang = deprecate('moment.lang is deprecated. Use moment.locale instead.', getSetGlobalLocale); 33746hooks.langData = deprecate('moment.langData is deprecated. Use moment.localeData instead.', getLocale); 33747 33748var mathAbs = Math.abs; 33749 33750function abs () { 33751 var data = this._data; 33752 33753 this._milliseconds = mathAbs(this._milliseconds); 33754 this._days = mathAbs(this._days); 33755 this._months = mathAbs(this._months); 33756 33757 data.milliseconds = mathAbs(data.milliseconds); 33758 data.seconds = mathAbs(data.seconds); 33759 data.minutes = mathAbs(data.minutes); 33760 data.hours = mathAbs(data.hours); 33761 data.months = mathAbs(data.months); 33762 data.years = mathAbs(data.years); 33763 33764 return this; 33765} 33766 33767function addSubtract$1 (duration, input, value, direction) { 33768 var other = createDuration(input, value); 33769 33770 duration._milliseconds += direction * other._milliseconds; 33771 duration._days += direction * other._days; 33772 duration._months += direction * other._months; 33773 33774 return duration._bubble(); 33775} 33776 33777// supports only 2.0-style add(1, 's') or add(duration) 33778function add$1 (input, value) { 33779 return addSubtract$1(this, input, value, 1); 33780} 33781 33782// supports only 2.0-style subtract(1, 's') or subtract(duration) 33783function subtract$1 (input, value) { 33784 return addSubtract$1(this, input, value, -1); 33785} 33786 33787function absCeil (number) { 33788 if (number < 0) { 33789 return Math.floor(number); 33790 } else { 33791 return Math.ceil(number); 33792 } 33793} 33794 33795function bubble () { 33796 var milliseconds = this._milliseconds; 33797 var days = this._days; 33798 var months = this._months; 33799 var data = this._data; 33800 var seconds, minutes, hours, years, monthsFromDays; 33801 33802 // if we have a mix of positive and negative values, bubble down first 33803 // check: https://github.com/moment/moment/issues/2166 33804 if (!((milliseconds >= 0 && days >= 0 && months >= 0) || 33805 (milliseconds <= 0 && days <= 0 && months <= 0))) { 33806 milliseconds += absCeil(monthsToDays(months) + days) * 864e5; 33807 days = 0; 33808 months = 0; 33809 } 33810 33811 // The following code bubbles up values, see the tests for 33812 // examples of what that means. 33813 data.milliseconds = milliseconds % 1000; 33814 33815 seconds = absFloor(milliseconds / 1000); 33816 data.seconds = seconds % 60; 33817 33818 minutes = absFloor(seconds / 60); 33819 data.minutes = minutes % 60; 33820 33821 hours = absFloor(minutes / 60); 33822 data.hours = hours % 24; 33823 33824 days += absFloor(hours / 24); 33825 33826 // convert days to months 33827 monthsFromDays = absFloor(daysToMonths(days)); 33828 months += monthsFromDays; 33829 days -= absCeil(monthsToDays(monthsFromDays)); 33830 33831 // 12 months -> 1 year 33832 years = absFloor(months / 12); 33833 months %= 12; 33834 33835 data.days = days; 33836 data.months = months; 33837 data.years = years; 33838 33839 return this; 33840} 33841 33842function daysToMonths (days) { 33843 // 400 years have 146097 days (taking into account leap year rules) 33844 // 400 years have 12 months === 4800 33845 return days * 4800 / 146097; 33846} 33847 33848function monthsToDays (months) { 33849 // the reverse of daysToMonths 33850 return months * 146097 / 4800; 33851} 33852 33853function as (units) { 33854 if (!this.isValid()) { 33855 return NaN; 33856 } 33857 var days; 33858 var months; 33859 var milliseconds = this._milliseconds; 33860 33861 units = normalizeUnits(units); 33862 33863 if (units === 'month' || units === 'year') { 33864 days = this._days + milliseconds / 864e5; 33865 months = this._months + daysToMonths(days); 33866 return units === 'month' ? months : months / 12; 33867 } else { 33868 // handle milliseconds separately because of floating point math errors (issue #1867) 33869 days = this._days + Math.round(monthsToDays(this._months)); 33870 switch (units) { 33871 case 'week' : return days / 7 + milliseconds / 6048e5; 33872 case 'day' : return days + milliseconds / 864e5; 33873 case 'hour' : return days * 24 + milliseconds / 36e5; 33874 case 'minute' : return days * 1440 + milliseconds / 6e4; 33875 case 'second' : return days * 86400 + milliseconds / 1000; 33876 // Math.floor prevents floating point math errors here 33877 case 'millisecond': return Math.floor(days * 864e5) + milliseconds; 33878 default: throw new Error('Unknown unit ' + units); 33879 } 33880 } 33881} 33882 33883// TODO: Use this.as('ms')? 33884function valueOf$1 () { 33885 if (!this.isValid()) { 33886 return NaN; 33887 } 33888 return ( 33889 this._milliseconds + 33890 this._days * 864e5 + 33891 (this._months % 12) * 2592e6 + 33892 toInt(this._months / 12) * 31536e6 33893 ); 33894} 33895 33896function makeAs (alias) { 33897 return function () { 33898 return this.as(alias); 33899 }; 33900} 33901 33902var asMilliseconds = makeAs('ms'); 33903var asSeconds = makeAs('s'); 33904var asMinutes = makeAs('m'); 33905var asHours = makeAs('h'); 33906var asDays = makeAs('d'); 33907var asWeeks = makeAs('w'); 33908var asMonths = makeAs('M'); 33909var asYears = makeAs('y'); 33910 33911function clone$1 () { 33912 return createDuration(this); 33913} 33914 33915function get$2 (units) { 33916 units = normalizeUnits(units); 33917 return this.isValid() ? this[units + 's']() : NaN; 33918} 33919 33920function makeGetter(name) { 33921 return function () { 33922 return this.isValid() ? this._data[name] : NaN; 33923 }; 33924} 33925 33926var milliseconds = makeGetter('milliseconds'); 33927var seconds = makeGetter('seconds'); 33928var minutes = makeGetter('minutes'); 33929var hours = makeGetter('hours'); 33930var days = makeGetter('days'); 33931var months = makeGetter('months'); 33932var years = makeGetter('years'); 33933 33934function weeks () { 33935 return absFloor(this.days() / 7); 33936} 33937 33938var round = Math.round; 33939var thresholds = { 33940 ss: 44, // a few seconds to seconds 33941 s : 45, // seconds to minute 33942 m : 45, // minutes to hour 33943 h : 22, // hours to day 33944 d : 26, // days to month 33945 M : 11 // months to year 33946}; 33947 33948// helper function for moment.fn.from, moment.fn.fromNow, and moment.duration.fn.humanize 33949function substituteTimeAgo(string, number, withoutSuffix, isFuture, locale) { 33950 return locale.relativeTime(number || 1, !!withoutSuffix, string, isFuture); 33951} 33952 33953function relativeTime$1 (posNegDuration, withoutSuffix, locale) { 33954 var duration = createDuration(posNegDuration).abs(); 33955 var seconds = round(duration.as('s')); 33956 var minutes = round(duration.as('m')); 33957 var hours = round(duration.as('h')); 33958 var days = round(duration.as('d')); 33959 var months = round(duration.as('M')); 33960 var years = round(duration.as('y')); 33961 33962 var a = seconds <= thresholds.ss && ['s', seconds] || 33963 seconds < thresholds.s && ['ss', seconds] || 33964 minutes <= 1 && ['m'] || 33965 minutes < thresholds.m && ['mm', minutes] || 33966 hours <= 1 && ['h'] || 33967 hours < thresholds.h && ['hh', hours] || 33968 days <= 1 && ['d'] || 33969 days < thresholds.d && ['dd', days] || 33970 months <= 1 && ['M'] || 33971 months < thresholds.M && ['MM', months] || 33972 years <= 1 && ['y'] || ['yy', years]; 33973 33974 a[2] = withoutSuffix; 33975 a[3] = +posNegDuration > 0; 33976 a[4] = locale; 33977 return substituteTimeAgo.apply(null, a); 33978} 33979 33980// This function allows you to set the rounding function for relative time strings 33981function getSetRelativeTimeRounding (roundingFunction) { 33982 if (roundingFunction === undefined) { 33983 return round; 33984 } 33985 if (typeof(roundingFunction) === 'function') { 33986 round = roundingFunction; 33987 return true; 33988 } 33989 return false; 33990} 33991 33992// This function allows you to set a threshold for relative time strings 33993function getSetRelativeTimeThreshold (threshold, limit) { 33994 if (thresholds[threshold] === undefined) { 33995 return false; 33996 } 33997 if (limit === undefined) { 33998 return thresholds[threshold]; 33999 } 34000 thresholds[threshold] = limit; 34001 if (threshold === 's') { 34002 thresholds.ss = limit - 1; 34003 } 34004 return true; 34005} 34006 34007function humanize (withSuffix) { 34008 if (!this.isValid()) { 34009 return this.localeData().invalidDate(); 34010 } 34011 34012 var locale = this.localeData(); 34013 var output = relativeTime$1(this, !withSuffix, locale); 34014 34015 if (withSuffix) { 34016 output = locale.pastFuture(+this, output); 34017 } 34018 34019 return locale.postformat(output); 34020} 34021 34022var abs$1 = Math.abs; 34023 34024function sign(x) { 34025 return ((x > 0) - (x < 0)) || +x; 34026} 34027 34028function toISOString$1() { 34029 // for ISO strings we do not use the normal bubbling rules: 34030 // * milliseconds bubble up until they become hours 34031 // * days do not bubble at all 34032 // * months bubble up until they become years 34033 // This is because there is no context-free conversion between hours and days 34034 // (think of clock changes) 34035 // and also not between days and months (28-31 days per month) 34036 if (!this.isValid()) { 34037 return this.localeData().invalidDate(); 34038 } 34039 34040 var seconds = abs$1(this._milliseconds) / 1000; 34041 var days = abs$1(this._days); 34042 var months = abs$1(this._months); 34043 var minutes, hours, years; 34044 34045 // 3600 seconds -> 60 minutes -> 1 hour 34046 minutes = absFloor(seconds / 60); 34047 hours = absFloor(minutes / 60); 34048 seconds %= 60; 34049 minutes %= 60; 34050 34051 // 12 months -> 1 year 34052 years = absFloor(months / 12); 34053 months %= 12; 34054 34055 34056 // inspired by https://github.com/dordille/moment-isoduration/blob/master/moment.isoduration.js 34057 var Y = years; 34058 var M = months; 34059 var D = days; 34060 var h = hours; 34061 var m = minutes; 34062 var s = seconds ? seconds.toFixed(3).replace(/\.?0+$/, '') : ''; 34063 var total = this.asSeconds(); 34064 34065 if (!total) { 34066 // this is the same as C#'s (Noda) and python (isodate)... 34067 // but not other JS (goog.date) 34068 return 'P0D'; 34069 } 34070 34071 var totalSign = total < 0 ? '-' : ''; 34072 var ymSign = sign(this._months) !== sign(total) ? '-' : ''; 34073 var daysSign = sign(this._days) !== sign(total) ? '-' : ''; 34074 var hmsSign = sign(this._milliseconds) !== sign(total) ? '-' : ''; 34075 34076 return totalSign + 'P' + 34077 (Y ? ymSign + Y + 'Y' : '') + 34078 (M ? ymSign + M + 'M' : '') + 34079 (D ? daysSign + D + 'D' : '') + 34080 ((h || m || s) ? 'T' : '') + 34081 (h ? hmsSign + h + 'H' : '') + 34082 (m ? hmsSign + m + 'M' : '') + 34083 (s ? hmsSign + s + 'S' : ''); 34084} 34085 34086var proto$2 = Duration.prototype; 34087 34088proto$2.isValid = isValid$1; 34089proto$2.abs = abs; 34090proto$2.add = add$1; 34091proto$2.subtract = subtract$1; 34092proto$2.as = as; 34093proto$2.asMilliseconds = asMilliseconds; 34094proto$2.asSeconds = asSeconds; 34095proto$2.asMinutes = asMinutes; 34096proto$2.asHours = asHours; 34097proto$2.asDays = asDays; 34098proto$2.asWeeks = asWeeks; 34099proto$2.asMonths = asMonths; 34100proto$2.asYears = asYears; 34101proto$2.valueOf = valueOf$1; 34102proto$2._bubble = bubble; 34103proto$2.clone = clone$1; 34104proto$2.get = get$2; 34105proto$2.milliseconds = milliseconds; 34106proto$2.seconds = seconds; 34107proto$2.minutes = minutes; 34108proto$2.hours = hours; 34109proto$2.days = days; 34110proto$2.weeks = weeks; 34111proto$2.months = months; 34112proto$2.years = years; 34113proto$2.humanize = humanize; 34114proto$2.toISOString = toISOString$1; 34115proto$2.toString = toISOString$1; 34116proto$2.toJSON = toISOString$1; 34117proto$2.locale = locale; 34118proto$2.localeData = localeData; 34119 34120// Deprecations 34121proto$2.toIsoString = deprecate('toIsoString() is deprecated. Please use toISOString() instead (notice the capitals)', toISOString$1); 34122proto$2.lang = lang; 34123 34124// Side effect imports 34125 34126// FORMATTING 34127 34128addFormatToken('X', 0, 0, 'unix'); 34129addFormatToken('x', 0, 0, 'valueOf'); 34130 34131// PARSING 34132 34133addRegexToken('x', matchSigned); 34134addRegexToken('X', matchTimestamp);
34135addParseToken('X', function (input, array, config) { 34136 config._d = new Date(parseFloat(input, 10) * 1000); 34137}); 34138addParseToken('x', function (input, array, config) { 34139 config._d = new Date(toInt(input)); 34140}); 34141 34142// Side effect imports 34143 34144 34145hooks.version = '2.19.1'; 34146 34147setHookCallback(createLocal); 34148 34149hooks.fn = proto; 34150hooks.min = min; 34151hooks.max = max; 34152hooks.now = now; 34153hooks.utc = createUTC; 34154hooks.unix = createUnix; 34155hooks.months = listMonths; 34156hooks.isDate = isDate; 34157hooks.locale = getSetGlobalLocale; 34158hooks.invalid = createInvalid; 34159hooks.duration = createDuration; 34160hooks.isMoment = isMoment; 34161hooks.weekdays = listWeekdays; 34162hooks.parseZone = createInZone; 34163hooks.localeData = getLocale; 34164hooks.isDuration = isDuration; 34165hooks.monthsShort = listMonthsShort; 34166hooks.weekdaysMin = listWeekdaysMin; 34167hooks.defineLocale = defineLocale; 34168hooks.updateLocale = updateLocale; 34169hooks.locales = listLocales; 34170hooks.weekdaysShort = listWeekdaysShort; 34171hooks.normalizeUnits = normalizeUnits; 34172hooks.relativeTimeRounding = getSetRelativeTimeRounding; 34173hooks.relativeTimeThreshold = getSetRelativeTimeThreshold; 34174hooks.calendarFormat = getCalendarFormat; 34175hooks.prototype = proto; 34176 34177return hooks; 34178 34179}))); 34180 34181/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(155)(module))) 34182 34183/***/ }), 34184/* 155 */ 34185/***/ (function(module, exports) { 34186 34187module.exports = function(module) { 34188 if(!module.webpackPolyfill) { 34189 module.deprecate = function() {}; 34190 module.paths = []; 34191 // module.parent = undefined by default 34192 if(!module.children) module.children = []; 34193 Object.defineProperty(module, "loaded", { 34194 enumerable: true, 34195 get: function() { 34196 return module.l; 34197 } 34198 }); 34199 Object.defineProperty(module, "id", { 34200 enumerable: true, 34201 get: function() { 34202 return module.i; 34203 } 34204 }); 34205 module.webpackPolyfill = 1; 34206 } 34207 return module; 34208}; 34209 34210 34211/***/ }), 34212/* 156 */ 34213/***/ (function(module, exports) { 34214 34215function webpackEmptyContext(req) { 34216 throw new Error("Cannot find module '" + req + "'."); 34217} 34218webpackEmptyContext.keys = function() { return []; }; 34219webpackEmptyContext.resolve = webpackEmptyContext; 34220module.exports = webpackEmptyContext; 34221webpackEmptyContext.id = 156; 34222 34223/***/ }), 34224/* 157 */ 34225/***/ (function(module, exports, __webpack_require__) { 34226 34227"use strict"; 34228/* WEBPACK VAR INJECTION */(function(global) { 34229 34230/* eslint-disable require-jsdoc */ 34231 34232var _rng; 34233 34234var globalVar = typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : null; 34235 34236if (globalVar && globalVar.crypto && crypto.getRandomValues) { 34237 // WHATWG crypto-based RNG - http://wiki.whatwg.org/wiki/Crypto 34238 // Moderately fast, high quality 34239 var _rnds8 = new Uint8Array(16); 34240 _rng = function whatwgRNG() { 34241 crypto.getRandomValues(_rnds8); 34242 return _rnds8; 34243 }; 34244} 34245 34246if (!_rng) { 34247 // Math.random()-based (RNG) 34248 // 34249 // If all else fails, use Math.random(). It's fast, but is of unspecified 34250 // quality. 34251 var _rnds = new Array(16); 34252 _rng = function _rng() { 34253 for (var i = 0, r; i < 16; i++) { 34254 if ((i & 0x03) === 0) r = Math.random() * 0x100000000; 34255 _rnds[i] = r >>> ((i & 0x03) << 3) & 0xff;
34256 } 34257 34258 return _rnds; 34259 }; 34260} 34261 34262// uuid.js 34263// 34264// Copyright (c) 2010-2012 Robert Kieffer 34265// MIT License - http://opensource.org/licenses/mit-license.php 34266 34267// Unique ID creation requires a high quality random # generator. We feature 34268// detect to determine the best RNG source, normalizing to a function that 34269// returns 128-bits of randomness, since that's what's usually required 34270 34271//var _rng = require('./rng'); 34272 34273// Maps for number <-> hex string conversion 34274var _byteToHex = []; 34275var _hexToByte = {}; 34276for (var i = 0; i < 256; i++) { 34277 _byteToHex[i] = (i + 0x100).toString(16).substr(1); 34278 _hexToByte[_byteToHex[i]] = i; 34279} 34280 34281// **`parse()` - Parse a UUID into it's component bytes** 34282function parse(s, buf, offset) { 34283 var i = buf && offset || 0, 34284 ii = 0; 34285 34286 buf = buf || []; 34287 s.toLowerCase().replace(/[0-9a-f]{2}/g, function (oct) { 34288 if (ii < 16) { 34289 // Don't overflow! 34290 buf[i + ii++] = _hexToByte[oct]; 34291 } 34292 }); 34293 34294 // Zero out remaining bytes if string was short 34295 while (ii < 16) { 34296 buf[i + ii++] = 0; 34297 } 34298 34299 return buf; 34300} 34301 34302// **`unparse()` - Convert UUID byte array (ala parse()) into a string** 34303function unparse(buf, offset) { 34304 var i = offset || 0, 34305 bth = _byteToHex; 34306 return bth[buf[i++]] + bth[buf[i++]] + bth[buf[i++]] + bth[buf[i++]] + '-' + bth[buf[i++]] + bth[buf[i++]] + '-' + bth[buf[i++]] + bth[buf[i++]] + '-' + bth[buf[i++]] + bth[buf[i++]] + '-' + bth[buf[i++]] + bth[buf[i++]] + bth[buf[i++]] + bth[buf[i++]] + bth[buf[i++]] + bth[buf[i++]]; 34307} 34308 34309// **`v1()` - Generate time-based UUID** 34310// 34311// Inspired by https://github.com/LiosK/UUID.js 34312// and http://docs.python.org/library/uuid.html 34313 34314// random #'s we need to init node and clockseq 34315var _seedBytes = _rng(); 34316 34317// Per 4.5, create and 48-bit node id, (47 random bits + multicast bit = 1) 34318var _nodeId = [_seedBytes[0] | 0x01, _seedBytes[1], _seedBytes[2], _seedBytes[3], _seedBytes[4], _seedBytes[5]]; 34319 34320// Per 4.2.2, randomize (14 bit) clockseq 34321var _clockseq = (_seedBytes[6] << 8 | _seedBytes[7]) & 0x3fff; 34322 34323// Previous uuid creation time 34324var _lastMSecs = 0, 34325 _lastNSecs = 0; 34326 34327// See https://github.com/broofa/node-uuid for API details 34328function v1(options, buf, offset) { 34329 var i = buf && offset || 0; 34330 var b = buf || []; 34331 34332 options = options || {}; 34333 34334 var clockseq = options.clockseq !== undefined ? options.clockseq : _clockseq; 34335 34336 // UUID timestamps are 100 nano-second units since the Gregorian epoch, 34337 // (1582-10-15 00:00). JSNumbers aren't precise enough for this, so 34338 // time is handled internally as 'msecs' (integer milliseconds) and 'nsecs' 34339 // (100-nanoseconds offset from msecs) since unix epoch, 1970-01-01 00:00. 34340 var msecs = options.msecs !== undefined ? options.msecs : new Date().getTime(); 34341 34342 // Per 4.2.1.2, use count of uuid's generated during the current clock 34343 // cycle to simulate higher resolution clock 34344 var nsecs = options.nsecs !== undefined ? options.nsecs : _lastNSecs + 1; 34345 34346 // Time since last uuid creation (in msecs) 34347 var dt = msecs - _lastMSecs + (nsecs - _lastNSecs) / 10000; 34348 34349 // Per 4.2.1.2, Bump clockseq on clock regression 34350 if (dt < 0 && options.clockseq === undefined) { 34351 clockseq = clockseq + 1 & 0x3fff; 34352 } 34353 34354 // Reset nsecs if clock regresses (new clockseq) or we've moved onto a new 34355 // time interval 34356 if ((dt < 0 || msecs > _lastMSecs) && options.nsecs === undefined) { 34357 nsecs = 0; 34358 } 34359 34360 // Per 4.2.1.2 Throw error if too many uuids are requested 34361 if (nsecs >= 10000) { 34362 throw new Error('uuid.v1(): Can\'t create more than 10M uuids/sec'); 34363 } 34364 34365 _lastMSecs = msecs; 34366 _lastNSecs = nsecs; 34367 _clockseq = clockseq; 34368 34369 // Per 4.1.4 - Convert from unix epoch to Gregorian epoch 34370 msecs += 12219292800000; 34371 34372 // `time_low` 34373 var tl = ((msecs & 0xfffffff) * 10000 + nsecs) % 0x100000000; 34374 b[i++] = tl >>> 24 & 0xff; 34375 b[i++] = tl >>> 16 & 0xff; 34376 b[i++] = tl >>> 8 & 0xff; 34377 b[i++] = tl & 0xff; 34378 34379 // `time_mid` 34380 var tmh = msecs / 0x100000000 * 10000 & 0xfffffff; 34381 b[i++] = tmh >>> 8 & 0xff; 34382 b[i++] = tmh & 0xff; 34383 34384 // `time_high_and_version` 34385 b[i++] = tmh >>> 24 & 0xf | 0x10; // include version 34386 b[i++] = tmh >>> 16 & 0xff;
34387 34388 // `clock_seq_hi_and_reserved` (Per 4.2.2 - include variant) 34389 b[i++] = clockseq >>> 8 | 0x80; 34390 34391 // `clock_seq_low` 34392 b[i++] = clockseq & 0xff; 34393 34394 // `node` 34395 var node = options.node || _nodeId; 34396 for (var n = 0; n < 6; n++) { 34397 b[i + n] = node[n]; 34398 } 34399 34400 return buf ? buf : unparse(b); 34401} 34402 34403// **`v4()` - Generate random UUID** 34404 34405// See https://github.com/broofa/node-uuid for API details 34406function v4(options, buf, offset) { 34407 // Deprecated - 'format' argument, as supported in v1.2 34408 var i = buf && offset || 0; 34409 34410 if (typeof options == 'string') { 34411 buf = options == 'binary' ? new Array(16) : null; 34412 options = null; 34413 } 34414 options = options || {}; 34415 34416 var rnds = options.random || (options.rng || _rng)(); 34417 34418 // Per 4.4, set bits for version and `clock_seq_hi_and_reserved` 34419 rnds[6] = rnds[6] & 0x0f | 0x40; 34420 rnds[8] = rnds[8] & 0x3f | 0x80; 34421 34422 // Copy bytes to buffer, if provided 34423 if (buf) { 34424 for (var ii = 0; ii < 16; ii++) { 34425 buf[i + ii] = rnds[ii]; 34426 } 34427 } 34428 34429 return buf || unparse(rnds); 34430} 34431 34432// Export public API 34433var uuid = v4; 34434uuid.v1 = v1; 34435uuid.v4 = v4; 34436uuid.parse = parse; 34437uuid.unparse = unparse; 34438 34439module.exports = uuid; 34440/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(158))) 34441 34442/***/ }), 34443/* 158 */ 34444/***/ (function(module, exports) { 34445 34446var g; 34447 34448// This works in non-strict mode 34449g = (function() { 34450 return this; 34451})(); 34452 34453try { 34454 // This works if eval is allowed (see CSP) 34455 g = g || Function("return this")() || (1,eval)("this"); 34456} catch(e) { 34457 // This works if the window reference is available 34458 if(typeof window === "object") 34459 g = window; 34460} 34461 34462// g can still be undefined, but nothing to do about it... 34463// We return undefined, instead of nothing here, so it's 34464// easier to handle this case. if(!global) { ...} 34465 34466module.exports = g; 34467 34468 34469/***/ }), 34470/* 159 */ 34471/***/ (function(module, exports, __webpack_require__) { 34472 34473"use strict"; 34474 34475 34476// utils 34477exports.util = __webpack_require__(2); 34478exports.DOMutil = __webpack_require__(14); 34479 34480// data 34481exports.DataSet = __webpack_require__(11); 34482exports.DataView = __webpack_require__(12); 34483exports.Queue = __webpack_require__(43); 34484 34485// Graph3d 34486exports.Graph3d = __webpack_require__(161); 34487exports.graph3d = { 34488 Camera: __webpack_require__(95), 34489 Filter: __webpack_require__(96), 34490 Point2d: __webpack_require__(91), 34491 Point3d: __webpack_require__(34), 34492 Slider: __webpack_require__(92), 34493 StepNumber: __webpack_require__(93) 34494}; 34495 34496// bundled external libraries 34497exports.moment = __webpack_require__(9); 34498exports.Hammer = __webpack_require__(10); 34499exports.keycharm = __webpack_require__(35); 34500 34501/***/ }), 34502/* 160 */ 34503/***/ (function(module, exports, __webpack_require__) { 34504 34505var core = __webpack_require__(7); 34506var $JSON = core.JSON || (core.JSON = { stringify: JSON.stringify }); 34507module.exports = function stringify(it) { // eslint-disable-line no-unused-vars 34508 return $JSON.stringify.apply($JSON, arguments); 34509}; 34510 34511 34512/***/ }), 34513/* 161 */ 34514/***/ (function(module, exports, __webpack_require__) { 34515 34516"use strict"; 34517 34518 34519var _assign = __webpack_require__(90); 34520 34521var _assign2 = _interopRequireDefault(_assign); 34522 34523function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 34524 34525var Emitter = __webpack_require__(44); 34526var util = __webpack_require__(2); 34527var Point3d = __webpack_require__(34); 34528var Point2d = __webpack_require__(91); 34529var Slider = __webpack_require__(92); 34530var StepNumber = __webpack_require__(93); 34531var Settings = __webpack_require__(94); 34532var Validator = __webpack_require__(15)['default']; 34533 34534var _require = __webpack_require__(15), 34535 printStyle = _require.printStyle; 34536 34537var _require2 = __webpack_require__(172), 34538 allOptions = _require2.allOptions; 34539 34540var DataGroup = __webpack_require__(173); 34541 34542/// enumerate the available styles 34543Graph3d.STYLE = Settings.STYLE; 34544 34545/** 34546 * Following label is used in the settings to describe values which should be 34547 * determined by the code while running, from the current data and graph style. 34548 * 34549 * Using 'undefined' directly achieves the same thing, but this is more 34550 * descriptive by describing the intent. 34551 */ 34552var autoByDefault = undefined; 34553 34554/** 34555 * Default values for option settings. 34556 * 34557 * These are the values used when a Graph3d instance is initialized without 34558 * custom settings. 34559 * 34560 * If a field is not in this list, a default value of 'autoByDefault' is assumed, 34561 * which is just an alias for 'undefined'. 34562 */ 34563Graph3d.DEFAULTS = { 34564 width: '400px', 34565 height: '400px', 34566 filterLabel: 'time', 34567 legendLabel: 'value', 34568 xLabel: 'x', 34569 yLabel: 'y',
34570 zLabel: 'z', 34571 xValueLabel: function xValueLabel(v) { 34572 return v; 34573 }, 34574 yValueLabel: function yValueLabel(v) { 34575 return v; 34576 }, 34577 zValueLabel: function zValueLabel(v) { 34578 return v; 34579 }, 34580 showXAxis: true, 34581 showYAxis: true, 34582 showZAxis: true, 34583 showGrid: true, 34584 showPerspective: true, 34585 showShadow: false, 34586 keepAspectRatio: true, 34587 verticalRatio: 0.5, // 0.1 to 1.0, where 1.0 results in a 'cube' 34588 34589 dotSizeRatio: 0.02, // size of the dots as a fraction of the graph width 34590 dotSizeMinFraction: 0.5, // size of min-value dot as a fraction of dotSizeRatio 34591 dotSizeMaxFraction: 2.5, // size of max-value dot as a fraction of dotSizeRatio 34592 34593 showAnimationControls: autoByDefault, 34594 animationInterval: 1000, // milliseconds 34595 animationPreload: false, 34596 animationAutoStart: autoByDefault, 34597 34598 axisColor: '#4D4D4D', 34599 gridColor: '#D3D3D3', 34600 xCenter: '55%', 34601 yCenter: '50%', 34602 34603 style: Graph3d.STYLE.DOT, 34604 tooltip: false, 34605 34606 tooltipStyle: { 34607 content: { 34608 padding: '10px', 34609 border: '1px solid #4d4d4d', 34610 color: '#1a1a1a', 34611 background: 'rgba(255,255,255,0.7)', 34612 borderRadius: '2px', 34613 boxShadow: '5px 5px 10px rgba(128,128,128,0.5)' 34614 }, 34615 line: { 34616 height: '40px', 34617 width: '0', 34618 borderLeft: '1px solid #4d4d4d' 34619 }, 34620 dot: { 34621 height: '0', 34622 width: '0', 34623 border: '5px solid #4d4d4d', 34624 borderRadius: '5px' 34625 } 34626 }, 34627 34628 dataColor: { 34629 fill: '#7DC1FF', 34630 stroke: '#3267D2', 34631 strokeWidth: 1 // px 34632 }, 34633 34634 cameraPosition: { 34635 horizontal: 1.0, 34636 vertical: 0.5, 34637 distance: 1.7 34638 }, 34639 34640 /* 34641 The following fields are 'auto by default', see above. 34642 */ 34643 showLegend: autoByDefault, // determined by graph style 34644 backgroundColor: autoByDefault, 34645 34646 xBarWidth: autoByDefault, 34647 yBarWidth: autoByDefault, 34648 valueMin: autoByDefault, 34649 valueMax: autoByDefault, 34650 xMin: autoByDefault, 34651 xMax: autoByDefault, 34652 xStep: autoByDefault, 34653 yMin: autoByDefault, 34654 yMax: autoByDefault, 34655 yStep: autoByDefault, 34656 zMin: autoByDefault, 34657 zMax: autoByDefault, 34658 zStep: autoByDefault 34659}; 34660 34661// ----------------------------------------------------------------------------- 34662// Class Graph3d 34663// ----------------------------------------------------------------------------- 34664 34665 34666/** 34667 * Graph3d displays data in 3d. 34668 * 34669 * Graph3d is developed in javascript as a Google Visualization Chart. 34670 * 34671 * @constructor Graph3d 34672 * @param {Element} container The DOM element in which the Graph3d will 34673 * be created. Normally a div element. 34674 * @param {DataSet | DataView | Array} [data] 34675 * @param {Object} [options] 34676 */ 34677function Graph3d(container, data, options) { 34678 if (!(this instanceof Graph3d)) { 34679 throw new SyntaxError('Constructor must be called with the new operator'); 34680 } 34681 34682 // create variables and set default values 34683 this.containerElement = container; 34684 34685 this.dataGroup = new DataGroup(); 34686 this.dataPoints = null; // The table with point objects 34687 34688 // create a frame and canvas 34689 this.create(); 34690 34691 Settings.setDefaults(Graph3d.DEFAULTS, this); 34692 34693 // the column indexes 34694 this.colX = undefined; 34695 this.colY = undefined; 34696 this.colZ = undefined; 34697 this.colValue = undefined; 34698 34699 // TODO: customize axis range 34700 34701 // apply options (also when undefined) 34702 this.setOptions(options); 34703 34704 // apply data 34705 this.setData(data); 34706} 34707 34708// Extend Graph3d with an Emitter mixin 34709Emitter(Graph3d.prototype); 34710 34711/** 34712 * Calculate the scaling values, dependent on the range in x, y, and z direction 34713 */ 34714Graph3d.prototype._setScale = function () { 34715 this.scale = new Point3d(1 / this.xRange.range(), 1 / this.yRange.range(), 1 / this.zRange.range()); 34716 34717 // keep aspect ration between x and y scale if desired 34718 if (this.keepAspectRatio) { 34719 if (this.scale.x < this.scale.y) { 34720 //noinspection JSSuspiciousNameCombination 34721 this.scale.y = this.scale.x; 34722 } else { 34723 //noinspection JSSuspiciousNameCombination 34724 this.scale.x = this.scale.y; 34725 } 34726 } 34727 34728 // scale the vertical axis 34729 this.scale.z *= this.verticalRatio; 34730 // TODO: can this be automated? verticalRatio? 34731 34732 // determine scale for (optional) value 34733 if (this.valueRange !== undefined) { 34734 this.scale.value = 1 / this.valueRange.range(); 34735 } 34736 34737 // position the camera arm 34738 var xCenter = this.xRange.center() * this.scale.x; 34739 var yCenter = this.yRange.center() * this.scale.y; 34740 var zCenter = this.zRange.center() * this.scale.z;
34741 this.camera.setArmLocation(xCenter, yCenter, zCenter); 34742}; 34743 34744/** 34745 * Convert a 3D location to a 2D location on screen 34746 * Source: ttp://en.wikipedia.org/wiki/3D_projection 34747 * 34748 * @param {Point3d} point3d A 3D point with parameters x, y, z 34749 * @returns {Point2d} point2d A 2D point with parameters x, y 34750 */ 34751Graph3d.prototype._convert3Dto2D = function (point3d) { 34752 var translation = this._convertPointToTranslation(point3d); 34753 return this._convertTranslationToScreen(translation); 34754}; 34755 34756/** 34757 * Convert a 3D location its translation seen from the camera 34758 * Source: http://en.wikipedia.org/wiki/3D_projection 34759 * 34760 * @param {Point3d} point3d A 3D point with parameters x, y, z 34761 * @returns {Point3d} translation A 3D point with parameters x, y, z This is 34762 * the translation of the point, seen from the 34763 * camera. 34764 */ 34765Graph3d.prototype._convertPointToTranslation = function (point3d) { 34766 var cameraLocation = this.camera.getCameraLocation(), 34767 cameraRotation = this.camera.getCameraRotation(), 34768 ax = point3d.x * this.scale.x, 34769 ay = point3d.y * this.scale.y, 34770 az = point3d.z * this.scale.z, 34771 cx = cameraLocation.x, 34772 cy = cameraLocation.y, 34773 cz = cameraLocation.z, 34774 34775 34776 // calculate angles 34777 sinTx = Math.sin(cameraRotation.x), 34778 cosTx = Math.cos(cameraRotation.x), 34779 sinTy = Math.sin(cameraRotation.y), 34780 cosTy = Math.cos(cameraRotation.y), 34781 sinTz = Math.sin(cameraRotation.z),
34782 cosTz = Math.cos(cameraRotation.z), 34783 34784 34785 // calculate translation 34786 dx = cosTy * (sinTz * (ay - cy) + cosTz * (ax - cx)) - sinTy * (az - cz), 34787 dy = sinTx * (cosTy * (az - cz) + sinTy * (sinTz * (ay - cy) + cosTz * (ax - cx))) + cosTx * (cosTz * (ay - cy) - sinTz * (ax - cx)), 34788 dz = cosTx * (cosTy * (az - cz) + sinTy * (sinTz * (ay - cy) + cosTz * (ax - cx))) - sinTx * (cosTz * (ay - cy) - sinTz * (ax - cx)); 34789 34790 return new Point3d(dx, dy, dz); 34791}; 34792 34793/** 34794 * Convert a translation point to a point on the screen 34795 * 34796 * @param {Point3d} translation A 3D point with parameters x, y, z This is 34797 * the translation of the point, seen from the 34798 * camera. 34799 * @returns {Point2d} point2d A 2D point with parameters x, y 34800 */ 34801Graph3d.prototype._convertTranslationToScreen = function (translation) { 34802 var ex = this.eye.x, 34803 ey = this.eye.y, 34804 ez = this.eye.z, 34805 dx = translation.x, 34806 dy = translation.y, 34807 dz = translation.z; 34808 34809 // calculate position on screen from translation 34810 var bx; 34811 var by; 34812 if (this.showPerspective) { 34813 bx = (dx - ex) * (ez / dz); 34814 by = (dy - ey) * (ez / dz); 34815 } else { 34816 bx = dx * -(ez / this.camera.getArmLength()); 34817 by = dy * -(ez / this.camera.getArmLength()); 34818 } 34819 34820 // shift and scale the point to the center of the screen 34821 // use the width of the graph to scale both horizontally and vertically. 34822 return new Point2d(this.currentXCenter + bx * this.frame.canvas.clientWidth, this.currentYCenter - by * this.frame.canvas.clientWidth); 34823}; 34824 34825/** 34826 * Calculate the translations and screen positions of all points 34827 * 34828 * @param {Array.<Point3d>} points 34829 * @private 34830 */ 34831Graph3d.prototype._calcTranslations = function (points) { 34832 for (var i = 0; i < points.length; i++) { 34833 var point = points[i]; 34834 point.trans = this._convertPointToTranslation(point.point); 34835 point.screen = this._convertTranslationToScreen(point.trans); 34836 34837 // calculate the translation of the point at the bottom (needed for sorting) 34838 var transBottom = this._convertPointToTranslation(point.bottom); 34839 point.dist = this.showPerspective ? transBottom.length() : -transBottom.z; 34840 } 34841 34842 // sort the points on depth of their (x,y) position (not on z) 34843 var sortDepth = function sortDepth(a, b) { 34844 return b.dist - a.dist; 34845 }; 34846 points.sort(sortDepth); 34847}; 34848 34849/** 34850 * Transfer min/max values to the Graph3d instance. 34851 */ 34852Graph3d.prototype._initializeRanges = function () { 34853 // TODO: later on, all min/maxes of all datagroups will be combined here 34854 var dg = this.dataGroup; 34855 this.xRange = dg.xRange; 34856 this.yRange = dg.yRange; 34857 this.zRange = dg.zRange; 34858 this.valueRange = dg.valueRange; 34859 34860 // Values currently needed but which need to be sorted out for 34861 // the multiple graph case. 34862 this.xStep = dg.xStep; 34863 this.yStep = dg.yStep; 34864 this.zStep = dg.zStep; 34865 this.xBarWidth = dg.xBarWidth; 34866 this.yBarWidth = dg.yBarWidth; 34867 this.colX = dg.colX; 34868 this.colY = dg.colY; 34869 this.colZ = dg.colZ; 34870 this.colValue = dg.colValue; 34871 34872 // set the scale dependent on the ranges. 34873 this._setScale(); 34874}; 34875 34876/** 34877 * Return all data values as a list of Point3d objects 34878 * 34879 * @param {vis.DataSet} data 34880 * @returns {Array.<Object>} 34881 */ 34882Graph3d.prototype.getDataPoints = function (data) { 34883 var dataPoints = []; 34884 34885 for (var i = 0; i < data.length; i++) { 34886 var point = new Point3d(); 34887 point.x = data[i][this.colX] || 0; 34888 point.y = data[i][this.colY] || 0; 34889 point.z = data[i][this.colZ] || 0; 34890 point.data = data[i]; 34891 34892 if (this.colValue !== undefined) { 34893 point.value = data[i][this.colValue] || 0; 34894 } 34895 34896 var obj = {}; 34897 obj.point = point; 34898 obj.bottom = new Point3d(point.x, point.y, this.zRange.min); 34899 obj.trans = undefined; 34900 obj.screen = undefined; 34901 34902 dataPoints.push(obj); 34903 } 34904 34905 return dataPoints; 34906}; 34907 34908/** 34909 * Filter the data based on the current filter 34910 * 34911 * @param {Array} data 34912 * @returns {Array} dataPoints Array with point objects which can be drawn on 34913 * screen 34914 */ 34915Graph3d.prototype._getDataPoints = function (data) { 34916 // TODO: store the created matrix dataPoints in the filters instead of 34917 // reloading each time. 34918 var x, y, i, obj; 34919 34920 var dataPoints = []; 34921 34922 if (this.style === Graph3d.STYLE.GRID || this.style === Graph3d.STYLE.SURFACE) { 34923 // copy all values from the data table to a matrix 34924 // the provided values are supposed to form a grid of (x,y) positions 34925 34926 // create two lists with all present x and y values 34927 var dataX = this.dataGroup.getDistinctValues(this.colX, data); 34928 var dataY = this.dataGroup.getDistinctValues(this.colY, data); 34929 34930 dataPoints = this.getDataPoints(data); 34931 34932 // create a grid, a 2d matrix, with all values. 34933 var dataMatrix = []; // temporary data matrix 34934 for (i = 0; i < dataPoints.length; i++) { 34935 obj = dataPoints[i]; 34936 34937 // TODO: implement Array().indexOf() for Internet Explorer 34938 var xIndex = dataX.indexOf(obj.point.x); 34939 var yIndex = dataY.indexOf(obj.point.y); 34940 34941 if (dataMatrix[xIndex] === undefined) { 34942 dataMatrix[xIndex] = []; 34943 } 34944 34945 dataMatrix[xIndex][yIndex] = obj; 34946 } 34947 34948 // fill in the pointers to the neighbors. 34949 for (x = 0; x < dataMatrix.length; x++) { 34950 for (y = 0; y < dataMatrix[x].length; y++) { 34951 if (dataMatrix[x][y]) { 34952 dataMatrix[x][y].pointRight = x < dataMatrix.length - 1 ? dataMatrix[x + 1][y] : undefined; 34953 dataMatrix[x][y].pointTop = y < dataMatrix[x].length - 1 ? dataMatrix[x][y + 1] : undefined; 34954 dataMatrix[x][y].pointCross = x < dataMatrix.length - 1 && y < dataMatrix[x].length - 1 ? dataMatrix[x + 1][y + 1] : undefined; 34955 } 34956 } 34957 } 34958 } else { 34959 // 'dot', 'dot-line', etc. 34960 this._checkValueField(data); 34961 dataPoints = this.getDataPoints(data); 34962 34963 if (this.style === Graph3d.STYLE.LINE) { 34964 // Add next member points for line drawing 34965 for (i = 0; i < dataPoints.length; i++) { 34966 if (i > 0) { 34967 dataPoints[i - 1].pointNext = dataPoints[i]; 34968 } 34969 } 34970 } 34971 } 34972 34973 return dataPoints; 34974}; 34975 34976/** 34977 * Create the main frame for the Graph3d. 34978 * 34979 * This function is executed once when a Graph3d object is created. The frame 34980 * contains a canvas, and this canvas contains all objects like the axis and 34981 * nodes. 34982 */ 34983Graph3d.prototype.create = function () { 34984 // remove all elements from the container element. 34985 while (this.containerElement.hasChildNodes()) { 34986 this.containerElement.removeChild(this.containerElement.firstChild); 34987 } 34988 34989 this.frame = document.createElement('div'); 34990 this.frame.style.position = 'relative'; 34991 this.frame.style.overflow = 'hidden'; 34992 34993 // create the graph canvas (HTML canvas element) 34994 this.frame.canvas = document.createElement('canvas'); 34995 this.frame.canvas.style.position = 'relative'; 34996 this.frame.appendChild(this.frame.canvas); 34997 //if (!this.frame.canvas.getContext) { 34998 { 34999 var noCanvas = document.createElement('DIV'); 35000 noCanvas.style.color = 'red'; 35001 noCanvas.style.fontWeight = 'bold'; 35002 noCanvas.style.padding = '10px'; 35003 noCanvas.innerHTML = 'Error: your browser does not support HTML canvas'; 35004 this.frame.canvas.appendChild(noCanvas); 35005 } 35006 35007 this.frame.filter = document.createElement('div'); 35008 this.frame.filter.style.position = 'absolute'; 35009 this.frame.filter.style.bottom = '0px'; 35010 this.frame.filter.style.left = '0px'; 35011 this.frame.filter.style.width = '100%'; 35012 this.frame.appendChild(this.frame.filter); 35013 35014 // add event listeners to handle moving and zooming the contents 35015 var me = this; 35016 var onmousedown = function onmousedown(event) { 35017 me._onMouseDown(event); 35018 }; 35019 var ontouchstart = function ontouchstart(event) { 35020 me._onTouchStart(event); 35021 }; 35022 var onmousewheel = function onmousewheel(event) { 35023 me._onWheel(event); 35024 }; 35025 var ontooltip = function ontooltip(event) { 35026 me._onTooltip(event); 35027 }; 35028 var onclick = function onclick(event) { 35029 me._onClick(event); 35030 }; 35031 // TODO: these events are never cleaned up... can give a 'memory leakage' 35032 35033 util.addEventListener(this.frame.canvas, 'mousedown', onmousedown); 35034 util.addEventListener(this.frame.canvas, 'touchstart', ontouchstart); 35035 util.addEventListener(this.frame.canvas, 'mousewheel', onmousewheel); 35036 util.addEventListener(this.frame.canvas, 'mousemove', ontooltip); 35037 util.addEventListener(this.frame.canvas, 'click', onclick); 35038 35039 // add the new graph to the container element 35040 this.containerElement.appendChild(this.frame); 35041}; 35042 35043/** 35044 * Set a new size for the graph 35045 * 35046 * @param {number} width 35047 * @param {number} height 35048 * @private 35049 */ 35050Graph3d.prototype._setSize = function (width, height) { 35051 this.frame.style.width = width; 35052 this.frame.style.height = height; 35053 35054 this._resizeCanvas(); 35055}; 35056 35057/** 35058 * Resize the canvas to the current size of the frame 35059 */ 35060Graph3d.prototype._resizeCanvas = function () { 35061 this.frame.canvas.style.width = '100%'; 35062 this.frame.canvas.style.height = '100%'; 35063 35064 this.frame.canvas.width = this.frame.canvas.clientWidth; 35065 this.frame.canvas.height = this.frame.canvas.clientHeight; 35066 35067 // adjust with for margin 35068 this.frame.filter.style.width = this.frame.canvas.clientWidth - 2 * 10 + 'px'; 35069}; 35070 35071/** 35072 * Start playing the animation, if requested and filter present. Only applicable 35073 * when animation data is available. 35074 */ 35075Graph3d.prototype.animationStart = function () { 35076 // start animation when option is true 35077 if (!this.animationAutoStart || !this.dataGroup.dataFilter) return; 35078 35079 if (!this.frame.filter || !this.frame.filter.slider) throw new Error('No animation available'); 35080 35081 this.frame.filter.slider.play(); 35082}; 35083 35084/** 35085 * Stop animation 35086 */ 35087Graph3d.prototype.animationStop = function () { 35088 if (!this.frame.filter || !this.frame.filter.slider) return; 35089 35090 this.frame.filter.slider.stop(); 35091}; 35092 35093/** 35094 * Resize the center position based on the current values in this.xCenter 35095 * and this.yCenter (which are strings with a percentage or a value 35096 * in pixels). The center positions are the variables this.currentXCenter 35097 * and this.currentYCenter 35098 */ 35099Graph3d.prototype._resizeCenter = function () { 35100 // calculate the horizontal center position 35101 if (this.xCenter.charAt(this.xCenter.length - 1) === '%') { 35102 this.currentXCenter = parseFloat(this.xCenter) / 100 * this.frame.canvas.clientWidth; 35103 } else { 35104 this.currentXCenter = parseFloat(this.xCenter); // supposed to be in px 35105 } 35106 35107 // calculate the vertical center position 35108 if (this.yCenter.charAt(this.yCenter.length - 1) === '%') { 35109 this.currentYCenter = parseFloat(this.yCenter) / 100 * (this.frame.canvas.clientHeight - this.frame.filter.clientHeight); 35110 } else { 35111 this.currentYCenter = parseFloat(this.yCenter); // supposed to be in px 35112 } 35113}; 35114 35115/** 35116 * Retrieve the current camera rotation 35117 * 35118 * @returns {object} An object with parameters horizontal, vertical, and 35119 * distance 35120 */ 35121Graph3d.prototype.getCameraPosition = function () { 35122 var pos = this.camera.getArmRotation(); 35123 pos.distance = this.camera.getArmLength(); 35124 return pos; 35125}; 35126 35127/** 35128 * Load data into the 3D Graph 35129 * 35130 * @param {vis.DataSet} data 35131 * @private 35132 */ 35133Graph3d.prototype._readData = function (data) { 35134 // read the data 35135 this.dataPoints = this.dataGroup.initializeData(this, data, this.style); 35136 35137 this._initializeRanges(); 35138 this._redrawFilter(); 35139}; 35140 35141/** 35142 * Replace the dataset of the Graph3d 35143 * 35144 * @param {Array | DataSet | DataView} data 35145 */ 35146Graph3d.prototype.setData = function (data) { 35147 if (data === undefined || data === null) return; 35148 35149 this._readData(data); 35150 this.redraw(); 35151 this.animationStart(); 35152}; 35153 35154/** 35155 * Update the options. Options will be merged with current options 35156 * 35157 * @param {Object} options 35158 */ 35159Graph3d.prototype.setOptions = function (options) { 35160 if (options === undefined) return; 35161 35162 var errorFound = Validator.validate(options, allOptions); 35163 if (errorFound === true) { 35164 console.log('%cErrors have been found in the supplied options object.', printStyle); 35165 } 35166 35167 this.animationStop(); 35168 35169 Settings.setOptions(options, this); 35170 this.setPointDrawingMethod(); 35171 this._setSize(this.width, this.height); 35172 35173 this.setData(this.dataGroup.getDataTable()); 35174 this.animationStart(); 35175}; 35176 35177/** 35178 * Determine which point drawing method to use for the current graph style. 35179 */ 35180Graph3d.prototype.setPointDrawingMethod = function () { 35181 var method = undefined; 35182 35183 switch (this.style) { 35184 case Graph3d.STYLE.BAR: 35185 method = Graph3d.prototype._redrawBarGraphPoint; 35186 break; 35187 case Graph3d.STYLE.BARCOLOR: 35188 method = Graph3d.prototype._redrawBarColorGraphPoint; 35189 break; 35190 case Graph3d.STYLE.BARSIZE: 35191 method = Graph3d.prototype._redrawBarSizeGraphPoint; 35192 break; 35193 case Graph3d.STYLE.DOT: 35194 method = Graph3d.prototype._redrawDotGraphPoint; 35195 break; 35196 case Graph3d.STYLE.DOTLINE: 35197 method = Graph3d.prototype._redrawDotLineGraphPoint; 35198 break; 35199 case Graph3d.STYLE.DOTCOLOR: 35200 method = Graph3d.prototype._redrawDotColorGraphPoint; 35201 break; 35202 case Graph3d.STYLE.DOTSIZE: 35203 method = Graph3d.prototype._redrawDotSizeGraphPoint; 35204 break; 35205 case Graph3d.STYLE.SURFACE: 35206 method = Graph3d.prototype._redrawSurfaceGraphPoint; 35207 break; 35208 case Graph3d.STYLE.GRID:
35209 method = Graph3d.prototype._redrawGridGraphPoint; 35210 break; 35211 case Graph3d.STYLE.LINE: 35212 method = Graph3d.prototype._redrawLineGraphPoint; 35213 break; 35214 default: 35215 throw new Error('Can not determine point drawing method ' + 'for graph style \'' + this.style + '\''); 35216 } 35217 35218 this._pointDrawingMethod = method; 35219}; 35220 35221/** 35222 * Redraw the Graph. 35223 */ 35224Graph3d.prototype.redraw = function () { 35225 if (this.dataPoints === undefined) { 35226 throw new Error('Graph data not initialized'); 35227 } 35228 35229 this._resizeCanvas(); 35230 this._resizeCenter(); 35231 this._redrawSlider(); 35232 this._redrawClear(); 35233 this._redrawAxis(); 35234 35235 this._redrawDataGraph(); 35236 35237 this._redrawInfo(); 35238 this._redrawLegend(); 35239}; 35240 35241/** 35242 * Get drawing context without exposing canvas 35243 * 35244 * @returns {CanvasRenderingContext2D} 35245 * @private 35246 */ 35247Graph3d.prototype._getContext = function () { 35248 var canvas = this.frame.canvas; 35249 var ctx = canvas.getContext('2d'); 35250 35251 ctx.lineJoin = 'round'; 35252 ctx.lineCap = 'round'; 35253 35254 return ctx; 35255}; 35256 35257/** 35258 * Clear the canvas before redrawing 35259 */ 35260Graph3d.prototype._redrawClear = function () { 35261 var canvas = this.frame.canvas; 35262 var ctx = canvas.getContext('2d'); 35263 35264 ctx.clearRect(0, 0, canvas.width, canvas.height); 35265}; 35266 35267Graph3d.prototype._dotSize = function () { 35268 return this.frame.clientWidth * this.dotSizeRatio; 35269}; 35270 35271/** 35272 * Get legend width 35273 * 35274 * @returns {*} 35275 * @private 35276 */ 35277Graph3d.prototype._getLegendWidth = function () { 35278 var width; 35279 35280 if (this.style === Graph3d.STYLE.DOTSIZE) { 35281 var dotSize = this._dotSize(); 35282 //width = dotSize / 2 + dotSize * 2; 35283 width = dotSize * this.dotSizeMaxFraction; 35284 } else if (this.style === Graph3d.STYLE.BARSIZE) { 35285 width = this.xBarWidth; 35286 } else { 35287 width = 20; 35288 } 35289 return width; 35290}; 35291 35292/** 35293 * Redraw the legend based on size, dot color, or surface height 35294 */ 35295Graph3d.prototype._redrawLegend = function () { 35296 35297 //Return without drawing anything, if no legend is specified 35298 if (this.showLegend !== true) { 35299 return; 35300 } 35301 35302 // Do not draw legend when graph style does not support 35303 if (this.style === Graph3d.STYLE.LINE || this.style === Graph3d.STYLE.BARSIZE //TODO add legend support for BARSIZE 35304 ) { 35305 return; 35306 } 35307 35308 // Legend types - size and color. Determine if size legend. 35309 var isSizeLegend = this.style === Graph3d.STYLE.BARSIZE || this.style === Graph3d.STYLE.DOTSIZE; 35310 35311 // Legend is either tracking z values or style values. This flag if false means use z values. 35312 var isValueLegend = this.style === Graph3d.STYLE.DOTSIZE || this.style === Graph3d.STYLE.DOTCOLOR || this.style === Graph3d.STYLE.BARCOLOR; 35313 35314 var height = Math.max(this.frame.clientHeight * 0.25, 100); 35315 var top = this.margin; 35316 var width = this._getLegendWidth(); // px - overwritten by size legend 35317 var right = this.frame.clientWidth - this.margin; 35318 var left = right - width; 35319 var bottom = top + height; 35320 35321 var ctx = this._getContext(); 35322 ctx.lineWidth = 1; 35323 ctx.font = '14px arial'; // TODO: put in options 35324 35325 if (isSizeLegend === false) { 35326 // draw the color bar 35327 var ymin = 0; 35328 var ymax = height; // Todo: make height customizable 35329 var y; 35330 35331 for (y = ymin; y < ymax; y++) { 35332 var f = (y - ymin) / (ymax - ymin); 35333 var hue = f * 240; 35334 var color = this._hsv2rgb(hue, 1, 1); 35335 35336 ctx.strokeStyle = color; 35337 ctx.beginPath(); 35338 ctx.moveTo(left, top + y); 35339 ctx.lineTo(right, top + y); 35340 ctx.stroke(); 35341 } 35342 ctx.strokeStyle = this.axisColor; 35343 ctx.strokeRect(left, top, width, height); 35344 } else { 35345 35346 // draw the size legend box 35347 var widthMin; 35348 if (this.style === Graph3d.STYLE.DOTSIZE) { 35349 // Get the proportion to max and min right 35350 widthMin = width * (this.dotSizeMinFraction / this.dotSizeMaxFraction); 35351 } else if (this.style === Graph3d.STYLE.BARSIZE) { 35352 //widthMin = this.xBarWidth * 0.2 this is wrong - barwidth measures in terms of xvalues 35353 } 35354 ctx.strokeStyle = this.axisColor; 35355 ctx.fillStyle = this.dataColor.fill; 35356 ctx.beginPath(); 35357 ctx.moveTo(left, top); 35358 ctx.lineTo(right, top); 35359 ctx.lineTo(left + widthMin, bottom); 35360 ctx.lineTo(left, bottom); 35361 ctx.closePath(); 35362 ctx.fill(); 35363 ctx.stroke(); 35364 } 35365 35366 // print value text along the legend edge 35367 var gridLineLen = 5; // px 35368 35369 var legendMin = isValueLegend ? this.valueRange.min : this.zRange.min; 35370 var legendMax = isValueLegend ? this.valueRange.max : this.zRange.max; 35371 var step = new StepNumber(legendMin, legendMax, (legendMax - legendMin) / 5, true); 35372 step.start(true); 35373 35374 var from; 35375 var to; 35376 while (!step.end()) { 35377 y = bottom - (step.getCurrent() - legendMin) / (legendMax - legendMin) * height; 35378 from = new Point2d(left - gridLineLen, y); 35379 to = new Point2d(left, y); 35380 this._line(ctx, from, to); 35381 35382 ctx.textAlign = 'right'; 35383 ctx.textBaseline = 'middle'; 35384 ctx.fillStyle = this.axisColor; 35385 ctx.fillText(step.getCurrent(), left - 2 * gridLineLen, y); 35386 35387 step.next(); 35388 } 35389 35390 ctx.textAlign = 'right'; 35391 ctx.textBaseline = 'top'; 35392 var label = this.legendLabel; 35393 ctx.fillText(label, right, bottom + this.margin); 35394}; 35395 35396/** 35397 * Redraw the filter 35398 */ 35399Graph3d.prototype._redrawFilter = function () { 35400 var dataFilter = this.dataGroup.dataFilter; 35401 var filter = this.frame.filter; 35402 filter.innerHTML = ''; 35403 35404 if (!dataFilter) { 35405 filter.slider = undefined; 35406 return; 35407 } 35408 35409 var options = { 35410 'visible': this.showAnimationControls 35411 }; 35412 var slider = new Slider(filter, options); 35413 filter.slider = slider; 35414 35415 // TODO: css here is not nice here... 35416 filter.style.padding = '10px'; 35417 //this.frame.filter.style.backgroundColor = '#EFEFEF'; 35418 35419 slider.setValues(dataFilter.values); 35420 slider.setPlayInterval(this.animationInterval); 35421 35422 // create an event handler 35423 var me = this; 35424 var onchange = function onchange() { 35425 var dataFilter = me.dataGroup.dataFilter; 35426 var index = slider.getIndex(); 35427 35428 dataFilter.selectValue(index); 35429 me.dataPoints = dataFilter._getDataPoints(); 35430 35431 me.redraw(); 35432 }; 35433 35434 slider.setOnChangeCallback(onchange); 35435}; 35436 35437/** 35438 * Redraw the slider 35439 */ 35440Graph3d.prototype._redrawSlider = function () { 35441 if (this.frame.filter.slider !== undefined) { 35442 this.frame.filter.slider.redraw(); 35443 } 35444}; 35445 35446/** 35447 * Redraw common information 35448 */ 35449Graph3d.prototype._redrawInfo = function () { 35450 var info = this.dataGroup.getInfo(); 35451 if (info === undefined) return; 35452 35453 var ctx = this._getContext(); 35454 35455 ctx.font = '14px arial'; // TODO: put in options 35456 ctx.lineStyle = 'gray'; 35457 ctx.fillStyle = 'gray'; 35458 ctx.textAlign = 'left'; 35459 ctx.textBaseline = 'top'; 35460 35461 var x = this.margin; 35462 var y = this.margin; 35463 ctx.fillText(info, x, y); 35464}; 35465 35466/** 35467 * Draw a line between 2d points 'from' and 'to'. 35468 * 35469 * If stroke style specified, set that as well. 35470 * 35471 * @param {CanvasRenderingContext2D} ctx 35472 * @param {vis.Point2d} from 35473 * @param {vis.Point2d} to 35474 * @param {string} [strokeStyle] 35475 * @private 35476 */ 35477Graph3d.prototype._line = function (ctx, from, to, strokeStyle) { 35478 if (strokeStyle !== undefined) { 35479 ctx.strokeStyle = strokeStyle; 35480 } 35481 35482 ctx.beginPath(); 35483 ctx.moveTo(from.x, from.y); 35484 ctx.lineTo(to.x, to.y); 35485 ctx.stroke(); 35486}; 35487 35488/** 35489 * 35490 * @param {CanvasRenderingContext2D} ctx 35491 * @param {vis.Point3d} point3d 35492 * @param {string} text 35493 * @param {number} armAngle 35494 * @param {number} [yMargin=0] 35495 */ 35496Graph3d.prototype.drawAxisLabelX = function (ctx, point3d, text, armAngle, yMargin) { 35497 if (yMargin === undefined) { 35498 yMargin = 0; 35499 } 35500 35501 var point2d = this._convert3Dto2D(point3d); 35502 35503 if (Math.cos(armAngle * 2) > 0) { 35504 ctx.textAlign = 'center'; 35505 ctx.textBaseline = 'top'; 35506 point2d.y += yMargin; 35507 } else if (Math.sin(armAngle * 2) < 0) { 35508 ctx.textAlign = 'right'; 35509 ctx.textBaseline = 'middle'; 35510 } else { 35511 ctx.textAlign = 'left'; 35512 ctx.textBaseline = 'middle'; 35513 } 35514 35515 ctx.fillStyle = this.axisColor; 35516 ctx.fillText(text, point2d.x, point2d.y); 35517}; 35518 35519/** 35520 * 35521 * @param {CanvasRenderingContext2D} ctx 35522 * @param {vis.Point3d} point3d 35523 * @param {string} text 35524 * @param {number} armAngle 35525 * @param {number} [yMargin=0] 35526 */ 35527Graph3d.prototype.drawAxisLabelY = function (ctx, point3d, text, armAngle, yMargin) { 35528 if (yMargin === undefined) { 35529 yMargin = 0; 35530 } 35531 35532 var point2d = this._convert3Dto2D(point3d); 35533 35534 if (Math.cos(armAngle * 2) < 0) { 35535 ctx.textAlign = 'center'; 35536 ctx.textBaseline = 'top'; 35537 point2d.y += yMargin; 35538 } else if (Math.sin(armAngle * 2) > 0) { 35539 ctx.textAlign = 'right'; 35540 ctx.textBaseline = 'middle'; 35541 } else { 35542 ctx.textAlign = 'left'; 35543 ctx.textBaseline = 'middle'; 35544 } 35545 35546 ctx.fillStyle = this.axisColor; 35547 ctx.fillText(text, point2d.x, point2d.y); 35548}; 35549 35550/** 35551 * 35552 * @param {CanvasRenderingContext2D} ctx 35553 * @param {vis.Point3d} point3d 35554 * @param {string} text 35555 * @param {number} [offset=0] 35556 */ 35557Graph3d.prototype.drawAxisLabelZ = function (ctx, point3d, text, offset) { 35558 if (offset === undefined) { 35559 offset = 0; 35560 } 35561 35562 var point2d = this._convert3Dto2D(point3d); 35563 ctx.textAlign = 'right'; 35564 ctx.textBaseline = 'middle'; 35565 ctx.fillStyle = this.axisColor; 35566 ctx.fillText(text, point2d.x - offset, point2d.y); 35567}; 35568 35569/** 35570 35571 35572/** 35573 * Draw a line between 2d points 'from' and 'to'. 35574 * 35575 * If stroke style specified, set that as well. 35576 * 35577 * @param {CanvasRenderingContext2D} ctx 35578 * @param {vis.Point2d} from 35579 * @param {vis.Point2d} to 35580 * @param {string} [strokeStyle] 35581 * @private 35582 */ 35583Graph3d.prototype._line3d = function (ctx, from, to, strokeStyle) { 35584 var from2d = this._convert3Dto2D(from); 35585 var to2d = this._convert3Dto2D(to); 35586 35587 this._line(ctx, from2d, to2d, strokeStyle); 35588}; 35589 35590/** 35591 * Redraw the axis 35592 */ 35593Graph3d.prototype._redrawAxis = function () { 35594 var ctx = this._getContext(), 35595 from, 35596 to, 35597 step, 35598 prettyStep, 35599 text, 35600 xText, 35601 yText, 35602 zText, 35603 offset, 35604 xOffset, 35605 yOffset; 35606 35607 // TODO: get the actual rendered style of the containerElement 35608 //ctx.font = this.containerElement.style.font; 35609 ctx.font = 24 / this.camera.getArmLength() + 'px arial'; 35610 35611 // calculate the length for the short grid lines 35612 var gridLenX = 0.025 / this.scale.x; 35613 var gridLenY = 0.025 / this.scale.y; 35614 var textMargin = 5 / this.camera.getArmLength(); // px 35615 var armAngle = this.camera.getArmRotation().horizontal; 35616 var armVector = new Point2d(Math.cos(armAngle), Math.sin(armAngle)); 35617 35618 var xRange = this.xRange; 35619 var yRange = this.yRange; 35620 var zRange = this.zRange; 35621 var point3d; 35622 35623 // draw x-grid lines 35624 ctx.lineWidth = 1; 35625 prettyStep = this.defaultXStep === undefined; 35626 step = new StepNumber(xRange.min, xRange.max, this.xStep, prettyStep); 35627 step.start(true); 35628 35629 while (!step.end()) { 35630 var x = step.getCurrent(); 35631 35632 if (this.showGrid) { 35633 from = new Point3d(x, yRange.min, zRange.min); 35634 to = new Point3d(x, yRange.max, zRange.min); 35635 this._line3d(ctx, from, to, this.gridColor); 35636 } else if (this.showXAxis) { 35637 from = new Point3d(x, yRange.min, zRange.min); 35638 to = new Point3d(x, yRange.min + gridLenX, zRange.min); 35639 this._line3d(ctx, from, to, this.axisColor); 35640 35641 from = new Point3d(x, yRange.max, zRange.min); 35642 to = new Point3d(x, yRange.max - gridLenX, zRange.min); 35643 this._line3d(ctx, from, to, this.axisColor); 35644 } 35645 35646 if (this.showXAxis) { 35647 yText = armVector.x > 0 ? yRange.min : yRange.max; 35648 point3d = new Point3d(x, yText, zRange.min); 35649 var msg = ' ' + this.xValueLabel(x) + ' '; 35650 this.drawAxisLabelX(ctx, point3d, msg, armAngle, textMargin); 35651 } 35652 35653 step.next(); 35654 } 35655 35656 // draw y-grid lines 35657 ctx.lineWidth = 1; 35658 prettyStep = this.defaultYStep === undefined; 35659 step = new StepNumber(yRange.min, yRange.max, this.yStep, prettyStep); 35660 step.start(true); 35661 35662 while (!step.end()) { 35663 var y = step.getCurrent(); 35664 35665 if (this.showGrid) { 35666 from = new Point3d(xRange.min, y, zRange.min); 35667 to = new Point3d(xRange.max, y, zRange.min); 35668 this._line3d(ctx, from, to, this.gridColor); 35669 } else if (this.showYAxis) { 35670 from = new Point3d(xRange.min, y, zRange.min); 35671 to = new Point3d(xRange.min + gridLenY, y, zRange.min); 35672 this._line3d(ctx, from, to, this.axisColor); 35673 35674 from = new Point3d(xRange.max, y, zRange.min); 35675 to = new Point3d(xRange.max - gridLenY, y, zRange.min); 35676 this._line3d(ctx, from, to, this.axisColor); 35677 } 35678 35679 if (this.showYAxis) { 35680 xText = armVector.y > 0 ? xRange.min : xRange.max; 35681 point3d = new Point3d(xText, y, zRange.min); 35682 var _msg = ' ' + this.yValueLabel(y) + ' '; 35683 this.drawAxisLabelY(ctx, point3d, _msg, armAngle, textMargin); 35684 } 35685 35686 step.next(); 35687 } 35688 35689 // draw z-grid lines and axis 35690 if (this.showZAxis) { 35691 ctx.lineWidth = 1; 35692 prettyStep = this.defaultZStep === undefined; 35693 step = new StepNumber(zRange.min, zRange.max, this.zStep, prettyStep); 35694 step.start(true); 35695 35696 xText = armVector.x > 0 ? xRange.min : xRange.max; 35697 yText = armVector.y < 0 ? yRange.min : yRange.max; 35698 35699 while (!step.end()) { 35700 var z = step.getCurrent(); 35701
35702 // TODO: make z-grid lines really 3d? 35703 var from3d = new Point3d(xText, yText, z); 35704 var from2d = this._convert3Dto2D(from3d); 35705 to = new Point2d(from2d.x - textMargin, from2d.y); 35706 this._line(ctx, from2d, to, this.axisColor); 35707 35708 var _msg2 = this.zValueLabel(z) + ' '; 35709 this.drawAxisLabelZ(ctx, from3d, _msg2, 5); 35710 35711 step.next(); 35712 } 35713 35714 ctx.lineWidth = 1; 35715 from = new Point3d(xText, yText, zRange.min); 35716 to = new Point3d(xText, yText, zRange.max); 35717 this._line3d(ctx, from, to, this.axisColor); 35718 } 35719 35720 // draw x-axis 35721 if (this.showXAxis) { 35722 var xMin2d; 35723 var xMax2d; 35724 ctx.lineWidth = 1; 35725 35726 // line at yMin 35727 xMin2d = new Point3d(xRange.min, yRange.min, zRange.min); 35728 xMax2d = new Point3d(xRange.max, yRange.min, zRange.min); 35729 this._line3d(ctx, xMin2d, xMax2d, this.axisColor); 35730 // line at ymax 35731 xMin2d = new Point3d(xRange.min, yRange.max, zRange.min); 35732 xMax2d = new Point3d(xRange.max, yRange.max, zRange.min); 35733 this._line3d(ctx, xMin2d, xMax2d, this.axisColor); 35734 } 35735 35736 // draw y-axis 35737 if (this.showYAxis) { 35738 ctx.lineWidth = 1; 35739 // line at xMin 35740 from = new Point3d(xRange.min, yRange.min, zRange.min); 35741 to = new Point3d(xRange.min, yRange.max, zRange.min); 35742 this._line3d(ctx, from, to, this.axisColor); 35743 // line at xMax 35744 from = new Point3d(xRange.max, yRange.min, zRange.min); 35745 to = new Point3d(xRange.max, yRange.max, zRange.min); 35746 this._line3d(ctx, from, to, this.axisColor); 35747 } 35748 35749 // draw x-label 35750 var xLabel = this.xLabel; 35751 if (xLabel.length > 0 && this.showXAxis) { 35752 yOffset = 0.1 / this.scale.y; 35753 xText = (xRange.max + 3 * xRange.min) / 4; 35754 yText = armVector.x > 0 ? yRange.min - yOffset : yRange.max + yOffset; 35755 text = new Point3d(xText, yText, zRange.min); 35756 this.drawAxisLabelX(ctx, text, xLabel, armAngle); 35757 } 35758 35759 // draw y-label 35760 var yLabel = this.yLabel; 35761 if (yLabel.length > 0 && this.showYAxis) { 35762 xOffset = 0.1 / this.scale.x; 35763 xText = armVector.y > 0 ? xRange.min - xOffset : xRange.max + xOffset; 35764 yText = (yRange.max + 3 * yRange.min) / 4; 35765 text = new Point3d(xText, yText, zRange.min); 35766 35767 this.drawAxisLabelY(ctx, text, yLabel, armAngle); 35768 } 35769 35770 // draw z-label 35771 var zLabel = this.zLabel; 35772 if (zLabel.length > 0 && this.showZAxis) { 35773 offset = 30; // pixels. // TODO: relate to the max width of the values on the z axis? 35774 xText = armVector.x > 0 ? xRange.min : xRange.max; 35775 yText = armVector.y < 0 ? yRange.min : yRange.max; 35776 zText = (zRange.max + 3 * zRange.min) / 4; 35777 text = new Point3d(xText, yText, zText); 35778 35779 this.drawAxisLabelZ(ctx, text, zLabel, offset); 35780 } 35781}; 35782 35783/** 35784 * Calculate the color based on the given value. 35785 * @param {number} H Hue, a value be between 0 and 360 35786 * @param {number} S Saturation, a value between 0 and 1 35787 * @param {number} V Value, a value between 0 and 1 35788 * @returns {string} 35789 * @private 35790 */ 35791Graph3d.prototype._hsv2rgb = function (H, S, V) { 35792 var R, G, B, C, Hi, X; 35793 35794 C = V * S; 35795 Hi = Math.floor(H / 60); // hi = 0,1,2,3,4,5 35796 X = C * (1 - Math.abs(H / 60 % 2 - 1)); 35797 35798 switch (Hi) { 35799 case 0: 35800 R = C;G = X;B = 0;break; 35801 case 1: 35802 R = X;G = C;B = 0;break; 35803 case 2: 35804 R = 0;G = C;B = X;break; 35805 case 3: 35806 R = 0;G = X;B = C;break; 35807 case 4: 35808 R = X;G = 0;B = C;break; 35809 case 5: 35810 R = C;G = 0;B = X;break; 35811 35812 default: 35813 R = 0;G = 0;B = 0;break; 35814 } 35815 35816 return 'RGB(' + parseInt(R * 255) + ',' + parseInt(G * 255) + ',' + parseInt(B * 255) + ')'; 35817}; 35818 35819/** 35820 * 35821 * @param {vis.Point3d} point 35822 * @returns {*} 35823 * @private 35824 */ 35825Graph3d.prototype._getStrokeWidth = function (point) { 35826 if (point !== undefined) { 35827 if (this.showPerspective) { 35828 return 1 / -point.trans.z * this.dataColor.strokeWidth; 35829 } else { 35830 return -(this.eye.z / this.camera.getArmLength()) * this.dataColor.strokeWidth; 35831 } 35832 } 35833 35834 return this.dataColor.strokeWidth; 35835}; 35836 35837// ----------------------------------------------------------------------------- 35838// Drawing primitives for the graphs 35839// ----------------------------------------------------------------------------- 35840 35841 35842/** 35843 * Draw a bar element in the view with the given properties. 35844 * 35845 * @param {CanvasRenderingContext2D} ctx 35846 * @param {Object} point 35847 * @param {number} xWidth 35848 * @param {number} yWidth 35849 * @param {string} color 35850 * @param {string} borderColor 35851 * @private 35852 */ 35853Graph3d.prototype._redrawBar = function (ctx, point, xWidth, yWidth, color, borderColor) { 35854 var surface; 35855 35856 // calculate all corner points 35857 var me = this; 35858 var point3d = point.point; 35859 var zMin = this.zRange.min; 35860 var top = [{ point: new Point3d(point3d.x - xWidth, point3d.y - yWidth, point3d.z) }, { point: new Point3d(point3d.x + xWidth, point3d.y - yWidth, point3d.z) }, { point: new Point3d(point3d.x + xWidth, point3d.y + yWidth, point3d.z) }, { point: new Point3d(point3d.x - xWidth, point3d.y + yWidth, point3d.z) }]; 35861 var bottom = [{ point: new Point3d(point3d.x - xWidth, point3d.y - yWidth, zMin) }, { point: new Point3d(point3d.x + xWidth, point3d.y - yWidth, zMin) }, { point: new Point3d(point3d.x + xWidth, point3d.y + yWidth, zMin) }, { point: new Point3d(point3d.x - xWidth, point3d.y + yWidth, zMin) }]; 35862 35863 // calculate screen location of the points
35864 top.forEach(function (obj) { 35865 obj.screen = me._convert3Dto2D(obj.point); 35866 }); 35867 bottom.forEach(function (obj) { 35868 obj.screen = me._convert3Dto2D(obj.point); 35869 }); 35870 35871 // create five sides, calculate both corner points and center points 35872 var surfaces = [{ corners: top, center: Point3d.avg(bottom[0].point, bottom[2].point) }, { corners: [top[0], top[1], bottom[1], bottom[0]], center: Point3d.avg(bottom[1].point, bottom[0].point) }, { corners: [top[1], top[2], bottom[2], bottom[1]], center: Point3d.avg(bottom[2].point, bottom[1].point) }, { corners: [top[2], top[3], bottom[3], bottom[2]], center: Point3d.avg(bottom[3].point, bottom[2].point) }, { corners: [top[3], top[0], bottom[0], bottom[3]], center: Point3d.avg(bottom[0].point, bottom[3].point) }]; 35873 point.surfaces = surfaces; 35874 35875 // calculate the distance of each of the surface centers to the camera 35876 for (var j = 0; j < surfaces.length; j++) { 35877 surface = surfaces[j]; 35878 var transCenter = this._convertPointToTranslation(surface.center); 35879 surface.dist = this.showPerspective ? transCenter.length() : -transCenter.z; 35880 // TODO: this dept calculation doesn't work 100% of the cases due to perspective, 35881 // but the current solution is fast/simple and works in 99.9% of all cases 35882 // the issue is visible in example 14, with graph.setCameraPosition({horizontal: 2.97, vertical: 0.5, distance: 0.9}) 35883 } 35884 35885 // order the surfaces by their (translated) depth 35886 surfaces.sort(function (a, b) { 35887 var diff = b.dist - a.dist; 35888 if (diff) return diff; 35889 35890 // if equal depth, sort the top surface last 35891 if (a.corners === top) return 1; 35892 if (b.corners === top) return -1; 35893 35894 // both are equal 35895 return 0; 35896 }); 35897 35898 // draw the ordered surfaces 35899 ctx.lineWidth = this._getStrokeWidth(point); 35900 ctx.strokeStyle = borderColor; 35901 ctx.fillStyle = color; 35902 // NOTE: we start at j=2 instead of j=0 as we don't need to draw the two surfaces at the backside 35903 for (var _j = 2; _j < surfaces.length; _j++) { 35904 surface = surfaces[_j]; 35905 this._polygon(ctx, surface.corners); 35906 } 35907}; 35908 35909/** 35910 * Draw a polygon using the passed points and fill it with the passed style and stroke. 35911 * 35912 * @param {CanvasRenderingContext2D} ctx 35913 * @param {Array.<vis.Point3d>} points an array of points. 35914 * @param {string} [fillStyle] the fill style to set 35915 * @param {string} [strokeStyle] the stroke style to set 35916 */ 35917Graph3d.prototype._polygon = function (ctx, points, fillStyle, strokeStyle) { 35918 if (points.length < 2) { 35919 return; 35920 } 35921 35922 if (fillStyle !== undefined) { 35923 ctx.fillStyle = fillStyle; 35924 } 35925 if (strokeStyle !== undefined) { 35926 ctx.strokeStyle = strokeStyle; 35927 } 35928 ctx.beginPath(); 35929 ctx.moveTo(points[0].screen.x, points[0].screen.y); 35930 35931 for (var i = 1; i < points.length; ++i) { 35932 var point = points[i]; 35933 ctx.lineTo(point.screen.x, point.screen.y); 35934 } 35935 35936 ctx.closePath(); 35937 ctx.fill(); 35938 ctx.stroke(); // TODO: only draw stroke when strokeWidth > 0 35939}; 35940 35941/** 35942 * @param {CanvasRenderingContext2D} ctx 35943 * @param {object} point 35944 * @param {string} color 35945 * @param {string} borderColor 35946 * @param {number} [size=this._dotSize()] 35947 * @private 35948 */ 35949Graph3d.prototype._drawCircle = function (ctx, point, color, borderColor, size) { 35950 var radius = this._calcRadius(point, size); 35951 35952 ctx.lineWidth = this._getStrokeWidth(point); 35953 ctx.strokeStyle = borderColor; 35954 ctx.fillStyle = color; 35955 ctx.beginPath(); 35956 ctx.arc(point.screen.x, point.screen.y, radius, 0, Math.PI * 2, true); 35957 ctx.fill(); 35958 ctx.stroke(); 35959}; 35960 35961/** 35962 * Determine the colors for the 'regular' graph styles. 35963 * 35964 * @param {object} point 35965 * @returns {{fill, border}} 35966 * @private 35967 */ 35968Graph3d.prototype._getColorsRegular = function (point) { 35969 // calculate Hue from the current value. At zMin the hue is 240, at zMax the hue is 0 35970 var hue = (1 - (point.point.z - this.zRange.min) * this.scale.z / this.verticalRatio) * 240; 35971 var color = this._hsv2rgb(hue, 1, 1); 35972 var borderColor = this._hsv2rgb(hue, 1, 0.8); 35973 35974 return { 35975 fill: color, 35976 border: borderColor 35977 }; 35978}; 35979 35980/** 35981 * Get the colors for the 'color' graph styles. 35982 * These styles are currently: 'bar-color' and 'dot-color' 35983 * Color may be set as a string representation of HTML color, like #ff00ff, 35984 * or calculated from a number, for example, distance from this point 35985 * The first option is useful when we have some pre-given legend, to which we have to adjust ourselves 35986 * The second option is useful when we are interested in automatically setting the color, from some value, 35987 * using some color scale 35988 * @param {object} point 35989 * @returns {{fill: *, border: *}} 35990 * @private 35991 */ 35992Graph3d.prototype._getColorsColor = function (point) { 35993 // calculate the color based on the value 35994 var color, borderColor; 35995 35996 if (typeof point.point.value === "string") { 35997 color = point.point.value; 35998 borderColor = point.point.value; 35999 } else { 36000 var hue = (1 - (point.point.value - this.valueRange.min) * this.scale.value) * 240; 36001 color = this._hsv2rgb(hue, 1, 1); 36002 borderColor = this._hsv2rgb(hue, 1, 0.8); 36003 } 36004 return { 36005 fill: color, 36006 border: borderColor 36007 }; 36008}; 36009 36010/** 36011 * Get the colors for the 'size' graph styles. 36012 * These styles are currently: 'bar-size' and 'dot-size' 36013 * 36014 * @returns {{fill: *, border: (string|colorOptions.stroke|{string, undefined}|string|colorOptions.stroke|{string}|*)}} 36015 * @private 36016 */ 36017Graph3d.prototype._getColorsSize = function () { 36018 return { 36019 fill: this.dataColor.fill, 36020 border: this.dataColor.stroke 36021 }; 36022}; 36023 36024/** 36025 * Determine the size of a point on-screen, as determined by the 36026 * distance to the camera. 36027 * 36028 * @param {Object} point 36029 * @param {number} [size=this._dotSize()] the size that needs to be translated to screen coordinates. 36030 * optional; if not passed, use the default point size. 36031 * @returns {number} 36032 * @private 36033 */ 36034Graph3d.prototype._calcRadius = function (point, size) { 36035 if (size === undefined) { 36036 size = this._dotSize(); 36037 } 36038 36039 var radius; 36040 if (this.showPerspective) { 36041 radius = size / -point.trans.z; 36042 } else { 36043 radius = size * -(this.eye.z / this.camera.getArmLength()); 36044 } 36045 if (radius < 0) { 36046 radius = 0; 36047 } 36048
36049 return radius; 36050}; 36051 36052// ----------------------------------------------------------------------------- 36053// Methods for drawing points per graph style. 36054// ----------------------------------------------------------------------------- 36055 36056 36057/** 36058 * Draw single datapoint for graph style 'bar'. 36059 * 36060 * @param {CanvasRenderingContext2D} ctx 36061 * @param {Object} point 36062 * @private 36063 */ 36064Graph3d.prototype._redrawBarGraphPoint = function (ctx, point) { 36065 var xWidth = this.xBarWidth / 2; 36066 var yWidth = this.yBarWidth / 2; 36067 var colors = this._getColorsRegular(point); 36068 36069 this._redrawBar(ctx, point, xWidth, yWidth, colors.fill, colors.border); 36070}; 36071 36072/** 36073 * Draw single datapoint for graph style 'bar-color'. 36074 * 36075 * @param {CanvasRenderingContext2D} ctx 36076 * @param {Object} point 36077 * @private 36078 */ 36079Graph3d.prototype._redrawBarColorGraphPoint = function (ctx, point) { 36080 var xWidth = this.xBarWidth / 2; 36081 var yWidth = this.yBarWidth / 2; 36082 var colors = this._getColorsColor(point); 36083 36084 this._redrawBar(ctx, point, xWidth, yWidth, colors.fill, colors.border); 36085}; 36086 36087/** 36088 * Draw single datapoint for graph style 'bar-size'. 36089 * 36090 * @param {CanvasRenderingContext2D} ctx 36091 * @param {Object} point 36092 * @private 36093 */ 36094Graph3d.prototype._redrawBarSizeGraphPoint = function (ctx, point) { 36095 // calculate size for the bar 36096 var fraction = (point.point.value - this.valueRange.min) / this.valueRange.range(); 36097 var xWidth = this.xBarWidth / 2 * (fraction * 0.8 + 0.2); 36098 var yWidth = this.yBarWidth / 2 * (fraction * 0.8 + 0.2); 36099 36100 var colors = this._getColorsSize(); 36101 36102 this._redrawBar(ctx, point, xWidth, yWidth, colors.fill, colors.border); 36103}; 36104 36105/** 36106 * Draw single datapoint for graph style 'dot'. 36107 * 36108 * @param {CanvasRenderingContext2D} ctx 36109 * @param {Object} point 36110 * @private 36111 */ 36112Graph3d.prototype._redrawDotGraphPoint = function (ctx, point) { 36113 var colors = this._getColorsRegular(point); 36114 36115 this._drawCircle(ctx, point, colors.fill, colors.border); 36116}; 36117 36118/** 36119 * Draw single datapoint for graph style 'dot-line'. 36120 * 36121 * @param {CanvasRenderingContext2D} ctx 36122 * @param {Object} point 36123 * @private 36124 */ 36125Graph3d.prototype._redrawDotLineGraphPoint = function (ctx, point) { 36126 // draw a vertical line from the XY-plane to the graph value 36127 var from = this._convert3Dto2D(point.bottom); 36128 ctx.lineWidth = 1; 36129 this._line(ctx, from, point.screen, this.gridColor); 36130 36131 this._redrawDotGraphPoint(ctx, point); 36132}; 36133 36134/** 36135 * Draw single datapoint for graph style 'dot-color'. 36136 * 36137 * @param {CanvasRenderingContext2D} ctx 36138 * @param {Object} point 36139 * @private 36140 */ 36141Graph3d.prototype._redrawDotColorGraphPoint = function (ctx, point) { 36142 var colors = this._getColorsColor(point); 36143 36144 this._drawCircle(ctx, point, colors.fill, colors.border); 36145}; 36146 36147/** 36148 * Draw single datapoint for graph style 'dot-size'. 36149 * 36150 * @param {CanvasRenderingContext2D} ctx 36151 * @param {Object} point 36152 * @private 36153 */ 36154Graph3d.prototype._redrawDotSizeGraphPoint = function (ctx, point) { 36155 var dotSize = this._dotSize(); 36156 var fraction = (point.point.value - this.valueRange.min) / this.valueRange.range(); 36157 36158 var sizeMin = dotSize * this.dotSizeMinFraction; 36159 var sizeRange = dotSize * this.dotSizeMaxFraction - sizeMin; 36160 var size = sizeMin + sizeRange * fraction; 36161 36162 var colors = this._getColorsSize(); 36163 36164 this._drawCircle(ctx, point, colors.fill, colors.border, size); 36165}; 36166 36167/** 36168 * Draw single datapoint for graph style 'surface'. 36169 * 36170 * @param {CanvasRenderingContext2D} ctx 36171 * @param {Object} point 36172 * @private 36173 */ 36174Graph3d.prototype._redrawSurfaceGraphPoint = function (ctx, point) { 36175 var right = point.pointRight; 36176 var top = point.pointTop; 36177 var cross = point.pointCross; 36178 36179 if (point === undefined || right === undefined || top === undefined || cross === undefined) { 36180 return; 36181 } 36182 36183 var topSideVisible = true; 36184 var fillStyle; 36185 var strokeStyle; 36186 36187 if (this.showGrayBottom || this.showShadow) { 36188 // calculate the cross product of the two vectors from center 36189 // to left and right, in order to know whether we are looking at the 36190 // bottom or at the top side. We can also use the cross product 36191 // for calculating light intensity 36192 var aDiff = Point3d.subtract(cross.trans, point.trans); 36193 var bDiff = Point3d.subtract(top.trans, right.trans); 36194 var crossproduct = Point3d.crossProduct(aDiff, bDiff); 36195 var len = crossproduct.length(); 36196 // FIXME: there is a bug with determining the surface side (shadow or colored) 36197 36198 topSideVisible = crossproduct.z > 0; 36199 } 36200 36201 if (topSideVisible) { 36202 36203 // calculate Hue from the current value. At zMin the hue is 240, at zMax the hue is 0 36204 var zAvg = (point.point.z + right.point.z + top.point.z + cross.point.z) / 4; 36205 var h = (1 - (zAvg - this.zRange.min) * this.scale.z / this.verticalRatio) * 240; 36206 var s = 1; // saturation 36207 var v; 36208 36209 if (this.showShadow) { 36210 v = Math.min(1 + crossproduct.x / len / 2, 1); // value. TODO: scale 36211 fillStyle = this._hsv2rgb(h, s, v); 36212 strokeStyle = fillStyle; 36213 } else { 36214 v = 1; 36215 fillStyle = this._hsv2rgb(h, s, v); 36216 strokeStyle = this.axisColor; // TODO: should be customizable 36217 } 36218 } else { 36219 fillStyle = 'gray'; 36220 strokeStyle = this.axisColor; 36221 } 36222 36223 ctx.lineWidth = this._getStrokeWidth(point); 36224 // TODO: only draw stroke when strokeWidth > 0 36225 36226 var points = [point, right, cross, top]; 36227 this._polygon(ctx, points, fillStyle, strokeStyle); 36228}; 36229 36230/**
36231 * Helper method for _redrawGridGraphPoint() 36232 * 36233 * @param {CanvasRenderingContext2D} ctx 36234 * @param {Object} from 36235 * @param {Object} to 36236 * @private 36237 */ 36238Graph3d.prototype._drawGridLine = function (ctx, from, to) { 36239 if (from === undefined || to === undefined) { 36240 return; 36241 } 36242 36243 // calculate Hue from the current value. At zMin the hue is 240, at zMax the hue is 0 36244 var zAvg = (from.point.z + to.point.z) / 2; 36245 var h = (1 - (zAvg - this.zRange.min) * this.scale.z / this.verticalRatio) * 240; 36246 36247 ctx.lineWidth = this._getStrokeWidth(from) * 2; 36248 ctx.strokeStyle = this._hsv2rgb(h, 1, 1); 36249 this._line(ctx, from.screen, to.screen); 36250}; 36251 36252/** 36253 * Draw single datapoint for graph style 'Grid'. 36254 * 36255 * @param {CanvasRenderingContext2D} ctx 36256 * @param {Object} point 36257 * @private 36258 */ 36259Graph3d.prototype._redrawGridGraphPoint = function (ctx, point) { 36260 this._drawGridLine(ctx, point, point.pointRight); 36261 this._drawGridLine(ctx, point, point.pointTop); 36262}; 36263 36264/** 36265 * Draw single datapoint for graph style 'line'. 36266 * 36267 * @param {CanvasRenderingContext2D} ctx 36268 * @param {Object} point 36269 * @private 36270 */ 36271Graph3d.prototype._redrawLineGraphPoint = function (ctx, point) { 36272 if (point.pointNext === undefined) { 36273 return; 36274 } 36275 36276 ctx.lineWidth = this._getStrokeWidth(point); 36277 ctx.strokeStyle = this.dataColor.stroke; 36278 36279 this._line(ctx, point.screen, point.pointNext.screen); 36280}; 36281 36282/** 36283 * Draw all datapoints for currently selected graph style. 36284 * 36285 */ 36286Graph3d.prototype._redrawDataGraph = function () { 36287 var ctx = this._getContext(); 36288 var i; 36289 36290 if (this.dataPoints === undefined || this.dataPoints.length <= 0) return; // TODO: throw exception? 36291 36292 this._calcTranslations(this.dataPoints); 36293 36294 for (i = 0; i < this.dataPoints.length; i++) { 36295 var point = this.dataPoints[i]; 36296 36297 // Using call() ensures that the correct context is used 36298 this._pointDrawingMethod.call(this, ctx, point); 36299 } 36300}; 36301 36302// ----------------------------------------------------------------------------- 36303// End methods for drawing points per graph style. 36304// ----------------------------------------------------------------------------- 36305 36306/** 36307 * Store startX, startY and startOffset for mouse operations 36308 * 36309 * @param {Event} event The event that occurred 36310 */ 36311Graph3d.prototype._storeMousePosition = function (event) { 36312 // get mouse position (different code for IE and all other browsers) 36313 this.startMouseX = getMouseX(event); 36314 this.startMouseY = getMouseY(event); 36315 36316 this._startCameraOffset = this.camera.getOffset(); 36317}; 36318 36319/** 36320 * Start a moving operation inside the provided parent element 36321 * @param {Event} event The event that occurred (required for 36322 * retrieving the mouse position) 36323 */ 36324Graph3d.prototype._onMouseDown = function (event) { 36325 event = event || window.event; 36326 36327 // check if mouse is still down (may be up when focus is lost for example 36328 // in an iframe) 36329 if (this.leftButtonDown) { 36330 this._onMouseUp(event); 36331 } 36332 36333 // only react on left mouse button down 36334 this.leftButtonDown = event.which ? event.which === 1 : event.button === 1; 36335 if (!this.leftButtonDown && !this.touchDown) return; 36336 36337 this._storeMousePosition(event); 36338 36339 this.startStart = new Date(this.start); 36340 this.startEnd = new Date(this.end); 36341 this.startArmRotation = this.camera.getArmRotation(); 36342 36343 this.frame.style.cursor = 'move'; 36344 36345 // add event listeners to handle moving the contents 36346 // we store the function onmousemove and onmouseup in the graph, so we can 36347 // remove the eventlisteners lateron in the function mouseUp() 36348 var me = this; 36349 this.onmousemove = function (event) { 36350 me._onMouseMove(event); 36351 }; 36352 this.onmouseup = function (event) { 36353 me._onMouseUp(event); 36354 }; 36355 util.addEventListener(document, 'mousemove', me.onmousemove); 36356 util.addEventListener(document, 'mouseup', me.onmouseup); 36357 util.preventDefault(event); 36358}; 36359 36360/** 36361 * Perform moving operating. 36362 * This function activated from within the funcion Graph.mouseDown(). 36363 * @param {Event} event Well, eehh, the event 36364 */ 36365Graph3d.prototype._onMouseMove = function (event) { 36366 this.moving = true; 36367 event = event || window.event; 36368 36369 // calculate change in mouse position 36370 var diffX = parseFloat(getMouseX(event)) - this.startMouseX; 36371 var diffY = parseFloat(getMouseY(event)) - this.startMouseY; 36372 36373 // move with ctrl or rotate by other 36374 if (event && event.ctrlKey === true) { 36375 // calculate change in mouse position 36376 var scaleX = this.frame.clientWidth * 0.5; 36377 var scaleY = this.frame.clientHeight * 0.5; 36378 36379 var offXNew = (this._startCameraOffset.x || 0) - diffX / scaleX * this.camera.armLength * 0.8;
36380 var offYNew = (this._startCameraOffset.y || 0) + diffY / scaleY * this.camera.armLength * 0.8; 36381 36382 this.camera.setOffset(offXNew, offYNew); 36383 this._storeMousePosition(event); 36384 } else { 36385 var horizontalNew = this.startArmRotation.horizontal + diffX / 200; 36386 var verticalNew = this.startArmRotation.vertical + diffY / 200; 36387 36388 var snapAngle = 4; // degrees 36389 var snapValue = Math.sin(snapAngle / 360 * 2 * Math.PI); 36390 36391 // snap horizontally to nice angles at 0pi, 0.5pi, 1pi, 1.5pi, etc... 36392 // the -0.001 is to take care that the vertical axis is always drawn at the left front corner 36393 if (Math.abs(Math.sin(horizontalNew)) < snapValue) { 36394 horizontalNew = Math.round(horizontalNew / Math.PI) * Math.PI - 0.001; 36395 } 36396 if (Math.abs(Math.cos(horizontalNew)) < snapValue) { 36397 horizontalNew = (Math.round(horizontalNew / Math.PI - 0.5) + 0.5) * Math.PI - 0.001; 36398 } 36399 36400 // snap vertically to nice angles 36401 if (Math.abs(Math.sin(verticalNew)) < snapValue) { 36402 verticalNew = Math.round(verticalNew / Math.PI) * Math.PI; 36403 } 36404 if (Math.abs(Math.cos(verticalNew)) < snapValue) { 36405 verticalNew = (Math.round(verticalNew / Math.PI - 0.5) + 0.5) * Math.PI; 36406 } 36407 this.camera.setArmRotation(horizontalNew, verticalNew); 36408 } 36409 36410 this.redraw(); 36411 36412 // fire a cameraPositionChange event 36413 var parameters = this.getCameraPosition(); 36414 this.emit('cameraPositionChange', parameters); 36415 36416 util.preventDefault(event); 36417}; 36418 36419/** 36420 * Stop moving operating. 36421 * This function activated from within the funcion Graph.mouseDown(). 36422 * @param {Event} event The event 36423 */ 36424Graph3d.prototype._onMouseUp = function (event) { 36425 this.frame.style.cursor = 'auto'; 36426 this.leftButtonDown = false; 36427 36428 // remove event listeners here 36429 util.removeEventListener(document, 'mousemove', this.onmousemove); 36430 util.removeEventListener(document, 'mouseup', this.onmouseup); 36431 util.preventDefault(event); 36432}; 36433 36434/** 36435 * @param {Event} event The event 36436 */ 36437Graph3d.prototype._onClick = function (event) { 36438 if (!this.onclick_callback) return; 36439 if (!this.moving) { 36440 var boundingRect = this.frame.getBoundingClientRect(); 36441 var mouseX = getMouseX(event) - boundingRect.left; 36442 var mouseY = getMouseY(event) - boundingRect.top; 36443 var dataPoint = this._dataPointFromXY(mouseX, mouseY); 36444 if (dataPoint) this.onclick_callback(dataPoint.point.data); 36445 } else { 36446 // disable onclick callback, if it came immediately after rotate/pan 36447 this.moving = false; 36448 } 36449 util.preventDefault(event); 36450}; 36451 36452/** 36453 * After having moved the mouse, a tooltip should pop up when the mouse is resting on a data point 36454 * @param {Event} event A mouse move event 36455 */ 36456Graph3d.prototype._onTooltip = function (event) { 36457 var delay = 300; // ms 36458 var boundingRect = this.frame.getBoundingClientRect(); 36459 var mouseX = getMouseX(event) - boundingRect.left; 36460 var mouseY = getMouseY(event) - boundingRect.top; 36461 36462 if (!this.showTooltip) { 36463 return; 36464 } 36465 36466 if (this.tooltipTimeout) { 36467 clearTimeout(this.tooltipTimeout); 36468 } 36469 36470 // (delayed) display of a tooltip only if no mouse button is down 36471 if (this.leftButtonDown) { 36472 this._hideTooltip(); 36473 return; 36474 } 36475 36476 if (this.tooltip && this.tooltip.dataPoint) { 36477 // tooltip is currently visible 36478 var dataPoint = this._dataPointFromXY(mouseX, mouseY); 36479 if (dataPoint !== this.tooltip.dataPoint) { 36480 // datapoint changed 36481 if (dataPoint) { 36482 this._showTooltip(dataPoint); 36483 } else { 36484 this._hideTooltip(); 36485 } 36486 } 36487 } else { 36488 // tooltip is currently not visible 36489 var me = this; 36490 this.tooltipTimeout = setTimeout(function () { 36491 me.tooltipTimeout = null; 36492 36493 // show a tooltip if we have a data point 36494 var dataPoint = me._dataPointFromXY(mouseX, mouseY); 36495 if (dataPoint) { 36496 me._showTooltip(dataPoint); 36497 } 36498 }, delay); 36499 } 36500}; 36501 36502/** 36503 * Event handler for touchstart event on mobile devices 36504 * @param {Event} event The event 36505 */ 36506Graph3d.prototype._onTouchStart = function (event) { 36507 this.touchDown = true; 36508 36509 var me = this; 36510 this.ontouchmove = function (event) { 36511 me._onTouchMove(event); 36512 }; 36513 this.ontouchend = function (event) { 36514 me._onTouchEnd(event); 36515 }; 36516 util.addEventListener(document, 'touchmove', me.ontouchmove); 36517 util.addEventListener(document, 'touchend', me.ontouchend); 36518 36519 this._onMouseDown(event); 36520}; 36521 36522/** 36523 * Event handler for touchmove event on mobile devices 36524 * @param {Event} event The event 36525 */ 36526Graph3d.prototype._onTouchMove = function (event) { 36527 this._onMouseMove(event); 36528}; 36529 36530/** 36531 * Event handler for touchend event on mobile devices 36532 * @param {Event} event The event 36533 */ 36534Graph3d.prototype._onTouchEnd = function (event) { 36535 this.touchDown = false;
36536 36537 util.removeEventListener(document, 'touchmove', this.ontouchmove); 36538 util.removeEventListener(document, 'touchend', this.ontouchend); 36539 36540 this._onMouseUp(event); 36541}; 36542 36543/** 36544 * Event handler for mouse wheel event, used to zoom the graph 36545 * Code from http://adomas.org/javascript-mouse-wheel/ 36546 * @param {Event} event The event 36547 */ 36548Graph3d.prototype._onWheel = function (event) { 36549 if (!event) /* For IE. */ 36550 event = window.event; 36551 36552 // retrieve delta 36553 var delta = 0; 36554 if (event.wheelDelta) { 36555 /* IE/Opera. */ 36556 delta = event.wheelDelta / 120; 36557 } else if (event.detail) { 36558 /* Mozilla case. */ 36559 // In Mozilla, sign of delta is different than in IE. 36560 // Also, delta is multiple of 3. 36561 delta = -event.detail / 3; 36562 } 36563 36564 // If delta is nonzero, handle it. 36565 // Basically, delta is now positive if wheel was scrolled up, 36566 // and negative, if wheel was scrolled down. 36567 if (delta) { 36568 var oldLength = this.camera.getArmLength(); 36569 var newLength = oldLength * (1 - delta / 10); 36570 36571 this.camera.setArmLength(newLength); 36572 this.redraw(); 36573 36574 this._hideTooltip(); 36575 } 36576 36577 // fire a cameraPositionChange event 36578 var parameters = this.getCameraPosition(); 36579 this.emit('cameraPositionChange', parameters); 36580 36581 // Prevent default actions caused by mouse wheel. 36582 // That might be ugly, but we handle scrolls somehow 36583 // anyway, so don't bother here.. 36584 util.preventDefault(event); 36585}; 36586 36587/** 36588 * Test whether a point lies inside given 2D triangle 36589 * 36590 * @param {vis.Point2d} point 36591 * @param {vis.Point2d[]} triangle 36592 * @returns {boolean} true if given point lies inside or on the edge of the 36593 * triangle, false otherwise 36594 * @private 36595 */ 36596Graph3d.prototype._insideTriangle = function (point, triangle) { 36597 var a = triangle[0], 36598 b = triangle[1], 36599 c = triangle[2]; 36600 36601 /** 36602 * 36603 * @param {number} x 36604 * @returns {number} 36605 */ 36606 function sign(x) { 36607 return x > 0 ? 1 : x < 0 ? -1 : 0; 36608 } 36609 36610 var as = sign((b.x - a.x) * (point.y - a.y) - (b.y - a.y) * (point.x - a.x)); 36611 var bs = sign((c.x - b.x) * (point.y - b.y) - (c.y - b.y) * (point.x - b.x)); 36612 var cs = sign((a.x - c.x) * (point.y - c.y) - (a.y - c.y) * (point.x - c.x)); 36613 36614 // each of the three signs must be either equal to each other or zero 36615 return (as == 0 || bs == 0 || as == bs) && (bs == 0 || cs == 0 || bs == cs) && (as == 0 || cs == 0 || as == cs); 36616}; 36617 36618/** 36619 * Find a data point close to given screen position (x, y) 36620 * 36621 * @param {number} x 36622 * @param {number} y 36623 * @returns {Object | null} The closest data point or null if not close to any 36624 * data point 36625 * @private 36626 */ 36627Graph3d.prototype._dataPointFromXY = function (x, y) { 36628 var i, 36629 distMax = 100, 36630 // px 36631 dataPoint = null, 36632 closestDataPoint = null, 36633 closestDist = null, 36634 center = new Point2d(x, y); 36635 36636 if (this.style === Graph3d.STYLE.BAR || this.style === Graph3d.STYLE.BARCOLOR || this.style === Graph3d.STYLE.BARSIZE) { 36637 // the data points are ordered from far away to closest 36638 for (i = this.dataPoints.length - 1; i >= 0; i--) { 36639 dataPoint = this.dataPoints[i]; 36640 var surfaces = dataPoint.surfaces; 36641 if (surfaces) { 36642 for (var s = surfaces.length - 1; s >= 0; s--) { 36643 // split each surface in two triangles, and see if the center point is inside one of these 36644 var surface = surfaces[s]; 36645 var corners = surface.corners; 36646 var triangle1 = [corners[0].screen, corners[1].screen, corners[2].screen]; 36647 var triangle2 = [corners[2].screen, corners[3].screen, corners[0].screen]; 36648 if (this._insideTriangle(center, triangle1) || this._insideTriangle(center, triangle2)) { 36649 // return immediately at the first hit 36650 return dataPoint; 36651 } 36652 } 36653 } 36654 } 36655 } else { 36656 // find the closest data point, using distance to the center of the point on 2d screen 36657 for (i = 0; i < this.dataPoints.length; i++) { 36658 dataPoint = this.dataPoints[i]; 36659 var point = dataPoint.screen; 36660 if (point) { 36661 var distX = Math.abs(x - point.x); 36662 var distY = Math.abs(y - point.y); 36663 var dist = Math.sqrt(distX * distX + distY * distY); 36664
36665 if ((closestDist === null || dist < closestDist) && dist < distMax) { 36666 closestDist = dist; 36667 closestDataPoint = dataPoint; 36668 } 36669 } 36670 } 36671 } 36672 36673 return closestDataPoint; 36674}; 36675 36676/** 36677 * Determine if the given style has bars 36678 * 36679 * @param {number} style the style to check 36680 * @returns {boolean} true if bar style, false otherwise 36681 */ 36682Graph3d.prototype.hasBars = function (style) { 36683 return style == Graph3d.STYLE.BAR || style == Graph3d.STYLE.BARCOLOR || style == Graph3d.STYLE.BARSIZE; 36684}; 36685 36686/** 36687 * Display a tooltip for given data point 36688 * @param {Object} dataPoint 36689 * @private 36690 */ 36691Graph3d.prototype._showTooltip = function (dataPoint) { 36692 var content, line, dot; 36693 36694 if (!this.tooltip) { 36695 content = document.createElement('div'); 36696 (0, _assign2['default'])(content.style, {}, this.tooltipStyle.content); 36697 content.style.position = 'absolute'; 36698 36699 line = document.createElement('div'); 36700 (0, _assign2['default'])(line.style, {}, this.tooltipStyle.line); 36701 line.style.position = 'absolute'; 36702 36703 dot = document.createElement('div'); 36704 (0, _assign2['default'])(dot.style, {}, this.tooltipStyle.dot); 36705 dot.style.position = 'absolute'; 36706 36707 this.tooltip = { 36708 dataPoint: null, 36709 dom: { 36710 content: content, 36711 line: line, 36712 dot: dot 36713 } 36714 }; 36715 } else { 36716 content = this.tooltip.dom.content; 36717 line = this.tooltip.dom.line; 36718 dot = this.tooltip.dom.dot; 36719 } 36720 36721 this._hideTooltip(); 36722 36723 this.tooltip.dataPoint = dataPoint; 36724 if (typeof this.showTooltip === 'function') { 36725 content.innerHTML = this.showTooltip(dataPoint.point); 36726 } else { 36727 content.innerHTML = '<table>' + '<tr><td>' + this.xLabel + ':</td><td>' + dataPoint.point.x + '</td></tr>' + '<tr><td>' + this.yLabel + ':</td><td>' + dataPoint.point.y + '</td></tr>' + '<tr><td>' + this.zLabel + ':</td><td>' + dataPoint.point.z + '</td></tr>' + '</table>'; 36728 } 36729 36730 content.style.left = '0'; 36731 content.style.top = '0'; 36732 this.frame.appendChild(content); 36733 this.frame.appendChild(line); 36734 this.frame.appendChild(dot); 36735 36736 // calculate sizes 36737 var contentWidth = content.offsetWidth; 36738 var contentHeight = content.offsetHeight; 36739 var lineHeight = line.offsetHeight; 36740 var dotWidth = dot.offsetWidth; 36741 var dotHeight = dot.offsetHeight; 36742 36743 var left = dataPoint.screen.x - contentWidth / 2; 36744 left = Math.min(Math.max(left, 10), this.frame.clientWidth - 10 - contentWidth); 36745 36746 line.style.left = dataPoint.screen.x + 'px'; 36747 line.style.top = dataPoint.screen.y - lineHeight + 'px'; 36748 content.style.left = left + 'px'; 36749 content.style.top = dataPoint.screen.y - lineHeight - contentHeight + 'px'; 36750 dot.style.left = dataPoint.screen.x - dotWidth / 2 + 'px'; 36751 dot.style.top = dataPoint.screen.y - dotHeight / 2 + 'px'; 36752}; 36753 36754/** 36755 * Hide the tooltip when displayed 36756 * @private 36757 */ 36758Graph3d.prototype._hideTooltip = function () { 36759 if (this.tooltip) { 36760 this.tooltip.dataPoint = null; 36761 36762 for (var prop in this.tooltip.dom) { 36763 if (this.tooltip.dom.hasOwnProperty(prop)) { 36764 var elem = this.tooltip.dom[prop]; 36765 if (elem && elem.parentNode) { 36766 elem.parentNode.removeChild(elem); 36767 } 36768 } 36769 } 36770 } 36771}; 36772 36773/**--------------------------------------------------------------------------**/ 36774 36775/** 36776 * Get the horizontal mouse position from a mouse event 36777 * 36778 * @param {Event} event 36779 * @returns {number} mouse x 36780 */ 36781function getMouseX(event) { 36782 if ('clientX' in event) return event.clientX; 36783 return event.targetTouches[0] && event.targetTouches[0].clientX || 0; 36784} 36785 36786/** 36787 * Get the vertical mouse position from a mouse event 36788 * 36789 * @param {Event} event 36790 * @returns {number} mouse y 36791 */ 36792function getMouseY(event) { 36793 if ('clientY' in event) return event.clientY; 36794 return event.targetTouches[0] && event.targetTouches[0].clientY || 0; 36795} 36796 36797// ----------------------------------------------------------------------------- 36798// Public methods for specific settings 36799// ----------------------------------------------------------------------------- 36800 36801/** 36802 * Set the rotation and distance of the camera 36803 * 36804 * @param {Object} pos An object with the camera position 36805 * @param {number} [pos.horizontal] The horizontal rotation, between 0 and 2*PI. 36806 * Optional, can be left undefined. 36807 * @param {number} [pos.vertical] The vertical rotation, between 0 and 0.5*PI. 36808 * if vertical=0.5*PI, the graph is shown from 36809 * the top. Optional, can be left undefined. 36810 * @param {number} [pos.distance] The (normalized) distance of the camera to the 36811 * center of the graph, a value between 0.71 and 36812 * 5.0. Optional, can be left undefined. 36813 */ 36814Graph3d.prototype.setCameraPosition = function (pos) { 36815 Settings.setCameraPosition(pos, this); 36816 this.redraw(); 36817}; 36818 36819/** 36820 * Set a new size for the graph 36821 * 36822 * @param {string} width Width in pixels or percentage (for example '800px' 36823 * or '50%') 36824 * @param {string} height Height in pixels or percentage (for example '400px'
vendor: 3,074 bytes, lines 36825-36943
36825 * or '30%') 36826 */ 36827Graph3d.prototype.setSize = function (width, height) { 36828 this._setSize(width, height); 36829 this.redraw(); 36830}; 36831 36832// ----------------------------------------------------------------------------- 36833// End public methods for specific settings 36834// ----------------------------------------------------------------------------- 36835 36836 36837module.exports = Graph3d; 36838 36839/***/ }), 36840/* 162 */ 36841/***/ (function(module, exports, __webpack_require__) { 36842 36843__webpack_require__(163); 36844module.exports = __webpack_require__(7).Object.assign; 36845 36846 36847/***/ }), 36848/* 163 */ 36849/***/ (function(module, exports, __webpack_require__) { 36850 36851// 19.1.3.1 Object.assign(target, source) 36852var $export = __webpack_require__(17); 36853 36854$export($export.S + $export.F, 'Object', { assign: __webpack_require__(164) }); 36855 36856 36857/***/ }), 36858/* 164 */ 36859/***/ (function(module, exports, __webpack_require__) { 36860 36861"use strict"; 36862 36863// 19.1.2.1 Object.assign(target, source, ...) 36864var getKeys = __webpack_require__(33); 36865var gOPS = __webpack_require__(63); 36866var pIE = __webpack_require__(42); 36867var toObject = __webpack_require__(41); 36868var IObject = __webpack_require__(78); 36869var $assign = Object.assign; 36870 36871// should work with symbols and should have deterministic property order (V8 bug) 36872module.exports = !$assign || __webpack_require__(28)(function () { 36873 var A = {}; 36874 var B = {}; 36875 // eslint-disable-next-line no-undef 36876 var S = Symbol(); 36877 var K = 'abcdefghijklmnopqrst'; 36878 A[S] = 7; 36879 K.split('').forEach(function (k) { B[k] = k; }); 36880 return $assign({}, A)[S] != 7 || Object.keys($assign({}, B)).join('') != K; 36881}) ? function assign(target, source) { // eslint-disable-line no-unused-vars 36882 var T = toObject(target); 36883 var aLen = arguments.length; 36884 var index = 1; 36885 var getSymbols = gOPS.f; 36886 var isEnum = pIE.f; 36887 while (aLen > index) { 36888 var S = IObject(arguments[index++]); 36889 var keys = getSymbols ? getKeys(S).concat(getSymbols(S)) : getKeys(S); 36890 var length = keys.length; 36891 var j = 0; 36892 var key; 36893 while (length > j) if (isEnum.call(S, key = keys[j++])) T[key] = S[key]; 36894 } return T; 36895} : $assign; 36896 36897 36898/***/ }), 36899/* 165 */ 36900/***/ (function(module, exports, __webpack_require__) { 36901 36902module.exports = { "default": __webpack_require__(166), __esModule: true }; 36903 36904/***/ }), 36905/* 166 */ 36906/***/ (function(module, exports, __webpack_require__) { 36907 36908__webpack_require__(167); 36909module.exports = __webpack_require__(7).Math.sign; 36910 36911 36912/***/ }), 36913/* 167 */ 36914/***/ (function(module, exports, __webpack_require__) { 36915 36916// 20.2.2.28 Math.sign(x) 36917var $export = __webpack_require__(17); 36918 36919$export($export.S, 'Math', { sign: __webpack_require__(168) }); 36920 36921 36922/***/ }), 36923/* 168 */ 36924/***/ (function(module, exports) { 36925 36926// 20.2.2.28 Math.sign(x) 36927module.exports = Math.sign || function sign(x) { 36928 // eslint-disable-next-line no-self-compare 36929 return (x = +x) == 0 || x != x ? x : x < 0 ? -1 : 1; 36930}; 36931 36932 36933/***/ }), 36934/* 169 */ 36935/***/ (function(module, exports, __webpack_require__) { 36936 36937module.exports = { "default": __webpack_require__(170), __esModule: true }; 36938 36939/***/ }), 36940/* 170 */ 36941/***/ (function(module, exports, __webpack_require__) { 36942 36943__webpack_require__(171);
36944var $Object = __webpack_require__(7).Object; 36945module.exports = function defineProperty(it, key, desc) { 36946 return $Object.defineProperty(it, key, desc); 36947}; 36948 36949 36950/***/ }), 36951/* 171 */ 36952/***/ (function(module, exports, __webpack_require__) { 36953 36954var $export = __webpack_require__(17); 36955// 19.1.2.4 / 15.2.3.6 Object.defineProperty(O, P, Attributes) 36956$export($export.S + $export.F * !__webpack_require__(21), 'Object', { defineProperty: __webpack_require__(20).f }); 36957 36958 36959/***/ }), 36960/* 172 */ 36961/***/ (function(module, exports, __webpack_require__) { 36962 36963"use strict"; 36964 36965 36966Object.defineProperty(exports, "__esModule", { 36967 value: true 36968}); 36969/** 36970 * This object contains all possible options. It will check if the types are correct, if required if the option is one 36971 * of the allowed values. 36972 * 36973 * __any__ means that the name of the property does not matter. 36974 * __type__ is a required field for all objects and contains the allowed types of all objects 36975 */ 36976var string = 'string'; 36977var bool = 'boolean'; 36978var number = 'number'; 36979var object = 'object'; // should only be in a __type__ property 36980// Following not used here, but useful for reference 36981//let array = 'array'; 36982//let dom = 'dom'; 36983//let any = 'any'; 36984 36985 36986var colorOptions = { 36987 fill: { string: string }, 36988 stroke: { string: string }, 36989 strokeWidth: { number: number }, 36990 __type__: { string: string, object: object, 'undefined': 'undefined' } 36991}; 36992 36993/** 36994 * Order attempted to be alphabetical. 36995 * - x/y/z-prefixes ignored in sorting 36996 * - __type__ always at end 36997 * - globals at end 36998 */ 36999var allOptions = { 37000 animationAutoStart: { boolean: bool, 'undefined': 'undefined' }, 37001 animationInterval: { number: number }, 37002 animationPreload: { boolean: bool }, 37003 axisColor: { string: string }, 37004 backgroundColor: colorOptions, 37005 xBarWidth: { number: number, 'undefined': 'undefined' }, 37006 yBarWidth: { number: number, 'undefined': 'undefined' }, 37007 cameraPosition: { 37008 distance: { number: number }, 37009 horizontal: { number: number }, 37010 vertical: { number: number }, 37011 __type__: { object: object } 37012 }, 37013 xCenter: { string: string }, 37014 yCenter: { string: string }, 37015 dataColor: colorOptions, 37016 dotSizeMinFraction: { number: number }, 37017 dotSizeMaxFraction: { number: number }, 37018 dotSizeRatio: { number: number }, 37019 filterLabel: { string: string }, 37020 gridColor: { string: string }, 37021 onclick: { 'function': 'function' }, 37022 keepAspectRatio: { boolean: bool }, 37023 xLabel: { string: string }, 37024 yLabel: { string: string }, 37025 zLabel: { string: string }, 37026 legendLabel: { string: string }, 37027 xMin: { number: number, 'undefined': 'undefined' }, 37028 yMin: { number: number, 'undefined': 'undefined' }, 37029 zMin: { number: number, 'undefined': 'undefined' }, 37030 xMax: { number: number, 'undefined': 'undefined' }, 37031 yMax: { number: number, 'undefined': 'undefined' }, 37032 zMax: { number: number, 'undefined': 'undefined' }, 37033 showAnimationControls: { boolean: bool, 'undefined': 'undefined' }, 37034 showGrid: { boolean: bool }, 37035 showLegend: { boolean: bool, 'undefined': 'undefined' }, 37036 showPerspective: { boolean: bool }, 37037 showShadow: { boolean: bool }, 37038 showXAxis: { boolean: bool }, 37039 showYAxis: { boolean: bool }, 37040 showZAxis: { boolean: bool }, 37041 xStep: { number: number, 'undefined': 'undefined' }, 37042 yStep: { number: number, 'undefined': 'undefined' }, 37043 zStep: { number: number, 'undefined': 'undefined' }, 37044 style: { 37045 number: number, // TODO: either Graph3d.DEFAULT has string, or number allowed in documentation 37046 string: ['bar', 'bar-color', 'bar-size', 'dot', 'dot-line', 'dot-color', 'dot-size', 'line', 'grid', 'surface'] 37047 }, 37048 tooltip: { boolean: bool, 'function': 'function' }, 37049 tooltipStyle: { 37050 content: { 37051 color: { string: string }, 37052 background: { string: string }, 37053 border: { string: string }, 37054 borderRadius: { string: string }, 37055 boxShadow: { string: string }, 37056 padding: { string: string }, 37057 __type__: { object: object } 37058 }, 37059 line: { 37060 borderLeft: { string: string }, 37061 height: { string: string }, 37062 width: { string: string }, 37063 __type__: { object: object } 37064 }, 37065 dot: { 37066 border: { string: string }, 37067 borderRadius: { string: string }, 37068 height: { string: string },
37069 width: { string: string }, 37070 __type__: { object: object } 37071 }, 37072 __type__: { object: object } 37073 }, 37074 xValueLabel: { 'function': 'function' }, 37075 yValueLabel: { 'function': 'function' }, 37076 zValueLabel: { 'function': 'function' }, 37077 valueMax: { number: number, 'undefined': 'undefined' }, 37078 valueMin: { number: number, 'undefined': 'undefined' }, 37079 verticalRatio: { number: number }, 37080 37081 //globals : 37082 height: { string: string }, 37083 width: { string: string }, 37084 __type__: { object: object } 37085}; 37086 37087exports.allOptions = allOptions; 37088 37089/***/ }), 37090/* 173 */ 37091/***/ (function(module, exports, __webpack_require__) { 37092 37093"use strict"; 37094 37095 37096var DataSet = __webpack_require__(11); 37097var DataView = __webpack_require__(12); 37098var Range = __webpack_require__(174); 37099var Filter = __webpack_require__(96); 37100var Settings = __webpack_require__(94); 37101var Point3d = __webpack_require__(34); 37102 37103/** 37104 * Creates a container for all data of one specific 3D-graph. 37105 * 37106 * On construction, the container is totally empty; the data 37107 * needs to be initialized with method initializeData(). 37108 * Failure to do so will result in the following exception begin thrown 37109 * on instantiation of Graph3D: 37110 * 37111 * Error: Array, DataSet, or DataView expected 37112 * 37113 * @constructor DataGroup 37114 */ 37115function DataGroup() { 37116 this.dataTable = null; // The original data table 37117} 37118 37119/** 37120 * Initializes the instance from the passed data. 37121 * 37122 * Calculates minimum and maximum values and column index values. 37123 * 37124 * The graph3d instance is used internally to access the settings for 37125 * the given instance. 37126 * TODO: Pass settings only instead. 37127 * 37128 * @param {vis.Graph3d} graph3d Reference to the calling Graph3D instance. 37129 * @param {Array | DataSet | DataView} rawData The data containing the items for 37130 * the Graph. 37131 * @param {number} style Style Number 37132 * @returns {Array.<Object>} 37133 */ 37134DataGroup.prototype.initializeData = function (graph3d, rawData, style) { 37135 if (rawData === undefined) return; 37136 37137 if (Array.isArray(rawData)) { 37138 rawData = new DataSet(rawData); 37139 } 37140 37141 var data; 37142 if (rawData instanceof DataSet || rawData instanceof DataView) { 37143 data = rawData.get(); 37144 } else { 37145 throw new Error('Array, DataSet, or DataView expected'); 37146 } 37147 37148 if (data.length == 0) return; 37149 37150 this.style = style; 37151 37152 // unsubscribe from the dataTable 37153 if (this.dataSet) { 37154 this.dataSet.off('*', this._onChange); 37155 } 37156 37157 this.dataSet = rawData; 37158 this.dataTable = data; 37159 37160 // subscribe to changes in the dataset 37161 var me = this; 37162 this._onChange = function () { 37163 graph3d.setData(me.dataSet); 37164 }; 37165 this.dataSet.on('*', this._onChange); 37166 37167 // determine the location of x,y,z,value,filter columns 37168 this.colX = 'x'; 37169 this.colY = 'y'; 37170 this.colZ = 'z'; 37171 37172 var withBars = graph3d.hasBars(style); 37173 37174 // determine barWidth from data 37175 if (withBars) { 37176 if (graph3d.defaultXBarWidth !== undefined) { 37177 this.xBarWidth = graph3d.defaultXBarWidth; 37178 } else { 37179 this.xBarWidth = this.getSmallestDifference(data, this.colX) || 1; 37180 } 37181 37182 if (graph3d.defaultYBarWidth !== undefined) { 37183 this.yBarWidth = graph3d.defaultYBarWidth; 37184 } else { 37185 this.yBarWidth = this.getSmallestDifference(data, this.colY) || 1; 37186 } 37187 } 37188 37189 // calculate minima and maxima 37190 this._initializeRange(data, this.colX, graph3d, withBars); 37191 this._initializeRange(data, this.colY, graph3d, withBars); 37192 this._initializeRange(data, this.colZ, graph3d, false); 37193 37194 if (data[0].hasOwnProperty('style')) { 37195 this.colValue = 'style'; 37196 var valueRange = this.getColumnRange(data, this.colValue); 37197 this._setRangeDefaults(valueRange, graph3d.defaultValueMin, graph3d.defaultValueMax); 37198 this.valueRange = valueRange; 37199 } 37200 37201 // Initialize data filter if a filter column is provided 37202 var table = this.getDataTable(); 37203 if (table[0].hasOwnProperty('filter')) { 37204 if (this.dataFilter === undefined) { 37205 this.dataFilter = new Filter(this, 'filter', graph3d); 37206 this.dataFilter.setOnLoadCallback(function () { 37207 graph3d.redraw(); 37208 }); 37209 } 37210 } 37211 37212 var dataPoints; 37213 if (this.dataFilter) { 37214 // apply filtering 37215 dataPoints = this.dataFilter._getDataPoints(); 37216 } else { 37217 // no filtering. load all data 37218 dataPoints = this._getDataPoints(this.getDataTable()); 37219 } 37220 return dataPoints; 37221}; 37222 37223/** 37224 * Collect the range settings for the given data column. 37225 * 37226 * This internal method is intended to make the range 37227 * initalization more generic. 37228 * 37229 * TODO: if/when combined settings per axis defined, get rid of this. 37230 * 37231 * @private 37232 * 37233 * @param {'x'|'y'|'z'} column The data column to process 37234 * @param {vis.Graph3d} graph3d Reference to the calling Graph3D instance; 37235 * required for access to settings 37236 * @returns {Object} 37237 */ 37238DataGroup.prototype._collectRangeSettings = function (column, graph3d) { 37239 var index = ['x', 'y', 'z'].indexOf(column); 37240 37241 if (index == -1) { 37242 throw new Error('Column \'' + column + '\' invalid'); 37243 } 37244 37245 var upper = column.toUpperCase(); 37246 37247 return { 37248 barWidth: this[column + 'BarWidth'], 37249 min: graph3d['default' + upper + 'Min'],
37250 max: graph3d['default' + upper + 'Max'], 37251 step: graph3d['default' + upper + 'Step'], 37252 range_label: column + 'Range', // Name of instance field to write to 37253 step_label: column + 'Step' // Name of instance field to write to 37254 }; 37255}; 37256 37257/** 37258 * Initializes the settings per given column. 37259 * 37260 * TODO: if/when combined settings per axis defined, rewrite this. 37261 * 37262 * @private 37263 * 37264 * @param {DataSet | DataView} data The data containing the items for the Graph 37265 * @param {'x'|'y'|'z'} column The data column to process 37266 * @param {vis.Graph3d} graph3d Reference to the calling Graph3D instance; 37267 * required for access to settings 37268 * @param {boolean} withBars True if initializing for bar graph 37269 */ 37270DataGroup.prototype._initializeRange = function (data, column, graph3d, withBars) { 37271 var NUMSTEPS = 5; 37272 var settings = this._collectRangeSettings(column, graph3d); 37273 37274 var range = this.getColumnRange(data, column); 37275 if (withBars && column != 'z') { 37276 // Safeguard for 'z'; it doesn't have a bar width 37277 range.expand(settings.barWidth / 2); 37278 } 37279 37280 this._setRangeDefaults(range, settings.min, settings.max); 37281 this[settings.range_label] = range; 37282 this[settings.step_label] = settings.step !== undefined ? settings.step : range.range() / NUMSTEPS; 37283}; 37284 37285/** 37286 * Creates a list with all the different values in the data for the given column. 37287 * 37288 * If no data passed, use the internal data of this instance. 37289 * 37290 * @param {'x'|'y'|'z'} column The data column to process 37291 * @param {DataSet|DataView|undefined} data The data containing the items for the Graph 37292 * 37293 * @returns {Array} All distinct values in the given column data, sorted ascending. 37294 */ 37295DataGroup.prototype.getDistinctValues = function (column, data) { 37296 if (data === undefined) { 37297 data = this.dataTable; 37298 } 37299 37300 var values = []; 37301 37302 for (var i = 0; i < data.length; i++) { 37303 var value = data[i][column] || 0; 37304 if (values.indexOf(value) === -1) { 37305 values.push(value); 37306 } 37307 } 37308 37309 return values.sort(function (a, b) { 37310 return a - b; 37311 }); 37312}; 37313 37314/** 37315 * Determine the smallest difference between the values for given 37316 * column in the passed data set. 37317 * 37318 * @param {DataSet|DataView|undefined} data The data containing the items for the Graph 37319 * @param {'x'|'y'|'z'} column The data column to process 37320 * 37321 * @returns {number|null} Smallest difference value or 37322 * null, if it can't be determined. 37323 */ 37324DataGroup.prototype.getSmallestDifference = function (data, column) { 37325 var values = this.getDistinctValues(data, column); 37326 37327 // Get all the distinct diffs 37328 // Array values is assumed to be sorted here 37329 var smallest_diff = null; 37330 37331 for (var i = 1; i < values.length; i++) { 37332 var diff = values[i] - values[i - 1]; 37333 37334 if (smallest_diff == null || smallest_diff > diff) { 37335 smallest_diff = diff; 37336 } 37337 } 37338 37339 return smallest_diff; 37340}; 37341 37342/** 37343 * Get the absolute min/max values for the passed data column. 37344 * 37345 * @param {DataSet|DataView|undefined} data The data containing the items for the Graph 37346 * @param {'x'|'y'|'z'} column The data column to process 37347 * 37348 * @returns {Range} A Range instance with min/max members properly set. 37349 */ 37350DataGroup.prototype.getColumnRange = function (data, column) { 37351 var range = new Range(); 37352 37353 // Adjust the range so that it covers all values in the passed data elements. 37354 for (var i = 0; i < data.length; i++) { 37355 var item = data[i][column]; 37356 range.adjust(item); 37357 } 37358 37359 return range; 37360}; 37361 37362/** 37363 * Determines the number of rows in the current data. 37364 * 37365 * @returns {number} 37366 */ 37367DataGroup.prototype.getNumberOfRows = function () { 37368 return this.dataTable.length; 37369}; 37370 37371/** 37372 * Set default values for range 37373 * 37374 * The default values override the range values, if defined. 37375 * 37376 * Because it's possible that only defaultMin or defaultMax is set, it's better 37377 * to pass in a range already set with the min/max set from the data. Otherwise, 37378 * it's quite hard to process the min/max properly. 37379 * 37380 * @param {vis.Range} range 37381 * @param {number} [defaultMin=range.min] 37382 * @param {number} [defaultMax=range.max] 37383 * @private 37384 */ 37385DataGroup.prototype._setRangeDefaults = function (range, defaultMin, defaultMax) { 37386 if (defaultMin !== undefined) { 37387 range.min = defaultMin; 37388 } 37389 37390 if (defaultMax !== undefined) { 37391 range.max = defaultMax; 37392 } 37393 37394 // This is the original way that the default min/max values were adjusted. 37395 // TODO: Perhaps it's better if an error is thrown if the values do not agree. 37396 // But this will change the behaviour. 37397 if (range.max <= range.min) range.max = range.min + 1; 37398}; 37399 37400DataGroup.prototype.getDataTable = function () { 37401 return this.dataTable; 37402}; 37403 37404DataGroup.prototype.getDataSet = function () { 37405 return this.dataSet; 37406}; 37407 37408/** 37409 * Return all data values as a list of Point3d objects 37410 * @param {Array.<Object>} data 37411 * @returns {Array.<Object>} 37412 */ 37413DataGroup.prototype.getDataPoints = function (data) { 37414 var dataPoints = []; 37415 37416 for (var i = 0; i < data.length; i++) { 37417 var point = new Point3d(); 37418 point.x = data[i][this.colX] || 0; 37419 point.y = data[i][this.colY] || 0; 37420 point.z = data[i][this.colZ] || 0; 37421 point.data = data[i]; 37422 37423 if (this.colValue !== undefined) { 37424 point.value = data[i][this.colValue] || 0; 37425 } 37426 37427 var obj = {}; 37428 obj.point = point; 37429 obj.bottom = new Point3d(point.x, point.y, this.zRange.min); 37430 obj.trans = undefined; 37431 obj.screen = undefined; 37432 37433 dataPoints.push(obj); 37434 } 37435 37436 return dataPoints; 37437}; 37438 37439/** 37440 * Copy all values from the data table to a matrix. 37441 * 37442 * The provided values are supposed to form a grid of (x,y) positions. 37443 * @param {Array.<Object>} data 37444 * @returns {Array.<Object>} 37445 * @private 37446 */ 37447DataGroup.prototype.initDataAsMatrix = function (data) { 37448 // TODO: store the created matrix dataPoints in the filters instead of 37449 // reloading each time. 37450 var x, y, i, obj; 37451 37452 // create two lists with all present x and y values 37453 var dataX = this.getDistinctValues(this.colX, data); 37454 var dataY = this.getDistinctValues(this.colY, data); 37455 37456 var dataPoints = this.getDataPoints(data); 37457 37458 // create a grid, a 2d matrix, with all values. 37459 var dataMatrix = []; // temporary data matrix 37460 for (i = 0; i < dataPoints.length; i++) { 37461 obj = dataPoints[i]; 37462 37463 // TODO: implement Array().indexOf() for Internet Explorer 37464 var xIndex = dataX.indexOf(obj.point.x); 37465 var yIndex = dataY.indexOf(obj.point.y); 37466 37467 if (dataMatrix[xIndex] === undefined) { 37468 dataMatrix[xIndex] = []; 37469 } 37470 37471 dataMatrix[xIndex][yIndex] = obj; 37472 } 37473 37474 // fill in the pointers to the neighbors. 37475 for (x = 0; x < dataMatrix.length; x++) { 37476 for (y = 0; y < dataMatrix[x].length; y++) { 37477 if (dataMatrix[x][y]) { 37478 dataMatrix[x][y].pointRight = x < dataMatrix.length - 1 ? dataMatrix[x + 1][y] : undefined; 37479 dataMatrix[x][y].pointTop = y < dataMatrix[x].length - 1 ? dataMatrix[x][y + 1] : undefined; 37480 dataMatrix[x][y].pointCross = x < dataMatrix.length - 1 && y < dataMatrix[x].length - 1 ? dataMatrix[x + 1][y + 1] : undefined; 37481 } 37482 } 37483 } 37484 37485 return dataPoints; 37486}; 37487 37488/** 37489 * Return common information, if present 37490 * 37491 * @returns {string} 37492 */ 37493DataGroup.prototype.getInfo = function () { 37494 var dataFilter = this.dataFilter; 37495 if (!dataFilter) return undefined; 37496 37497 return dataFilter.getLabel() + ': ' + dataFilter.getSelectedValue(); 37498}; 37499 37500/** 37501 * Reload the data 37502 */ 37503DataGroup.prototype.reload = function () { 37504 if (this.dataTable) { 37505 this.setData(this.dataTable); 37506 } 37507}; 37508 37509/** 37510 * Filter the data based on the current filter 37511 * 37512 * @param {Array} data 37513 * @returns {Array} dataPoints Array with point objects which can be drawn on 37514 * screen 37515 */ 37516DataGroup.prototype._getDataPoints = function (data) { 37517 var dataPoints = []; 37518 37519 if (this.style === Settings.STYLE.GRID || this.style === Settings.STYLE.SURFACE) { 37520 dataPoints = this.initDataAsMatrix(data); 37521 } else { 37522 // 'dot', 'dot-line', etc. 37523 this._checkValueField(data); 37524 dataPoints = this.getDataPoints(data); 37525 37526 if (this.style === Settings.STYLE.LINE) { 37527 // Add next member points for line drawing 37528 for (var i = 0; i < dataPoints.length; i++) { 37529 if (i > 0) { 37530 dataPoints[i - 1].pointNext = dataPoints[i]; 37531 } 37532 } 37533 } 37534 } 37535 37536 return dataPoints; 37537}; 37538 37539/** 37540 * Check if the state is consistent for the use of the value field. 37541 * 37542 * Throws if a problem is detected. 37543 * 37544 * @param {Array.<Object>} data 37545 * @private 37546 */ 37547DataGroup.prototype._checkValueField = function (data) { 37548 37549 var hasValueField = this.style === Settings.STYLE.BARCOLOR || this.style === Settings.STYLE.BARSIZE || this.style === Settings.STYLE.DOTCOLOR || this.style === Settings.STYLE.DOTSIZE; 37550 37551 if (!hasValueField) { 37552 return; // No need to check further 37553 } 37554 37555 // Following field must be present for the current graph style 37556 if (this.colValue === undefined) { 37557 throw new Error('Expected data to have ' + ' field \'style\' ' + ' for graph style \'' + this.style + '\''); 37558 } 37559 37560 // The data must also contain this field. 37561 // Note that only first data element is checked. 37562 if (data[0][this.colValue] === undefined) { 37563 throw new Error('Expected data to have ' + ' field \'' + this.colValue + '\' ' + ' for graph style \'' + this.style + '\''); 37564 } 37565}; 37566 37567module.exports = DataGroup; 37568 37569/***/ }), 37570/* 174 */ 37571/***/ (function(module, exports, __webpack_require__) { 37572 37573"use strict"; 37574 37575 37576/** 37577 * @prototype Range 37578 * 37579 * Helper class to make working with related min and max values easier. 37580 * 37581 * The range is inclusive; a given value is considered part of the range if: 37582 * 37583 * this.min <= value <= this.max 37584 */ 37585function Range() { 37586 this.min = undefined; 37587 this.max = undefined; 37588} 37589 37590/** 37591 * Adjust the range so that the passed value fits in it. 37592 * 37593 * If the value is outside of the current extremes, adjust
37594 * the min or max so that the value is within the range. 37595 * 37596 * @param {number} value Numeric value to fit in range 37597 */ 37598Range.prototype.adjust = function (value) { 37599 if (value === undefined) return; 37600 37601 if (this.min === undefined || this.min > value) { 37602 this.min = value; 37603 } 37604 37605 if (this.max === undefined || this.max < value) { 37606 this.max = value; 37607 } 37608}; 37609 37610/** 37611 * Adjust the current range so that the passed range fits in it. 37612 * 37613 * @param {Range} range Range instance to fit in current instance 37614 */ 37615Range.prototype.combine = function (range) { 37616 this.add(range.min); 37617 this.add(range.max); 37618}; 37619 37620/** 37621 * Expand the range by the given value 37622 * 37623 * min will be lowered by given value; 37624 * max will be raised by given value 37625 * 37626 * Shrinking by passing a negative value is allowed. 37627 * 37628 * @param {number} val Amount by which to expand or shrink current range with 37629 */ 37630Range.prototype.expand = function (val) { 37631 if (val === undefined) { 37632 return; 37633 } 37634 37635 var newMin = this.min - val; 37636 var newMax = this.max + val; 37637 37638 // Note that following allows newMin === newMax. 37639 // This should be OK, since method expand() allows this also. 37640 if (newMin > newMax) { 37641 throw new Error('Passed expansion value makes range invalid'); 37642 } 37643 37644 this.min = newMin; 37645 this.max = newMax; 37646}; 37647 37648/** 37649 * Determine the full range width of current instance. 37650 * 37651 * @returns {num} The calculated width of this range 37652 */ 37653Range.prototype.range = function () { 37654 return this.max - this.min; 37655}; 37656 37657/** 37658 * Determine the central point of current instance. 37659 * 37660 * @returns {number} the value in the middle of min and max 37661 */ 37662Range.prototype.center = function () { 37663 return (this.min + this.max) / 2; 37664}; 37665 37666module.exports = Range; 37667 37668/***/ }), 37669/* 175 */ 37670/***/ (function(module, exports, __webpack_require__) { 37671 37672"use strict"; 37673var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__; 37674 37675(function (factory) { 37676 if (true) { 37677 // AMD. Register as an anonymous module. 37678 !(__WEBPACK_AMD_DEFINE_ARRAY__ = [], __WEBPACK_AMD_DEFINE_FACTORY__ = (factory), 37679 __WEBPACK_AMD_DEFINE_RESULT__ = (typeof __WEBPACK_AMD_DEFINE_FACTORY__ === 'function' ? 37680 (__WEBPACK_AMD_DEFINE_FACTORY__.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__)) : __WEBPACK_AMD_DEFINE_FACTORY__), 37681 __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); 37682 } else if (typeof exports === 'object') { 37683 // Node. Does not work with strict CommonJS, but 37684 // only CommonJS-like environments that support module.exports, 37685 // like Node. 37686 module.exports = factory(); 37687 } else { 37688 // Browser globals (root is window) 37689 window.propagating = factory(); 37690 } 37691}(function () { 37692 var _firstTarget = null; // singleton, will contain the target element where the touch event started 37693 37694 /** 37695 * Extend an Hammer.js instance with event propagation. 37696 * 37697 * Features: 37698 * - Events emitted by hammer will propagate in order from child to parent 37699 * elements. 37700 * - Events are extended with a function `event.stopPropagation()` to stop 37701 * propagation to parent elements. 37702 * - An option `preventDefault` to stop all default browser behavior. 37703 * 37704 * Usage: 37705 * var hammer = propagatingHammer(new Hammer(element)); 37706 * var hammer = propagatingHammer(new Hammer(element), {preventDefault: true}); 37707 * 37708 * @param {Hammer.Manager} hammer An hammer instance. 37709 * @param {Object} [options] Available options: 37710 * - `preventDefault: true | false | 'mouse' | 'touch' | 'pen'`. 37711 * Enforce preventing the default browser behavior. 37712 * Cannot be set to `false`. 37713 * @return {Hammer.Manager} Returns the same hammer instance with extended 37714 * functionality 37715 */ 37716 return function propagating(hammer, options) { 37717 var _options = options || { 37718 preventDefault: false 37719 }; 37720 37721 if (hammer.Manager) { 37722 // This looks like the Hammer constructor. 37723 // Overload the constructors with our own. 37724 var Hammer = hammer; 37725 37726 var PropagatingHammer = function(element, options) { 37727 var o = Object.create(_options); 37728 if (options) Hammer.assign(o, options); 37729 return propagating(new Hammer(element, o), o); 37730 }; 37731 Hammer.assign(PropagatingHammer, Hammer); 37732 37733 PropagatingHammer.Manager = function (element, options) { 37734 var o = Object.create(_options); 37735 if (options) Hammer.assign(o, options); 37736 return propagating(new Hammer.Manager(element, o), o); 37737 }; 37738 37739 return PropagatingHammer; 37740 } 37741 37742 // create a wrapper object which will override the functions 37743 // `on`, `off`, `destroy`, and `emit` of the hammer instance 37744 var wrapper = Object.create(hammer); 37745 37746 // attach to DOM element 37747 var element = hammer.element; 37748 37749 if(!element.hammer) element.hammer = []; 37750 element.hammer.push(wrapper); 37751 37752 // register an event to catch the start of a gesture and store the 37753 // target in a singleton 37754 hammer.on('hammer.input', function (event) { 37755 if (_options.preventDefault === true || (_options.preventDefault === event.pointerType)) { 37756 event.preventDefault(); 37757 } 37758 if (event.isFirst) { 37759 _firstTarget = event.target; 37760 } 37761 }); 37762 37763 /** @type {Object.<String, Array.<function>>} */ 37764 wrapper._handlers = {}; 37765 37766 /** 37767 * Register a handler for one or multiple events 37768 * @param {String} events A space separated string with events 37769 * @param {function}
vendor: 4,256 bytes, lines 37769-37913
37769 handler A callback function, called as handler(event) 37770 * @returns {Hammer.Manager} Returns the hammer instance 37771 */ 37772 wrapper.on = function (events, handler) { 37773 // register the handler 37774 split(events).forEach(function (event) { 37775 var _handlers = wrapper._handlers[event]; 37776 if (!_handlers) { 37777 wrapper._handlers[event] = _handlers = []; 37778 37779 // register the static, propagated handler 37780 hammer.on(event, propagatedHandler); 37781 } 37782 _handlers.push(handler); 37783 }); 37784 37785 return wrapper; 37786 }; 37787 37788 /** 37789 * Unregister a handler for one or multiple events 37790 * @param {String} events A space separated string with events 37791 * @param {function} [handler] Optional. The registered handler. If not 37792 * provided, all handlers for given events 37793 * are removed. 37794 * @returns {Hammer.Manager} Returns the hammer instance 37795 */ 37796 wrapper.off = function (events, handler) { 37797 // unregister the handler 37798 split(events).forEach(function (event) { 37799 var _handlers = wrapper._handlers[event]; 37800 if (_handlers) { 37801 _handlers = handler ? _handlers.filter(function (h) { 37802 return h !== handler; 37803 }) : []; 37804 37805 if (_handlers.length > 0) { 37806 wrapper._handlers[event] = _handlers; 37807 } 37808 else { 37809 // remove static, propagated handler 37810 hammer.off(event, propagatedHandler); 37811 delete wrapper._handlers[event]; 37812 } 37813 } 37814 }); 37815 37816 return wrapper; 37817 }; 37818 37819 /** 37820 * Emit to the event listeners 37821 * @param {string} eventType 37822 * @param {Event} event 37823 */ 37824 wrapper.emit = function(eventType, event) { 37825 _firstTarget = event.target; 37826 hammer.emit(eventType, event); 37827 }; 37828 37829 wrapper.destroy = function () { 37830 // Detach from DOM element 37831 var hammers = hammer.element.hammer; 37832 var idx = hammers.indexOf(wrapper); 37833 if(idx !== -1) hammers.splice(idx,1); 37834 if(!hammers.length) delete hammer.element.hammer; 37835 37836 // clear all handlers 37837 wrapper._handlers = {}; 37838 37839 // call original hammer destroy 37840 hammer.destroy(); 37841 }; 37842 37843 // split a string with space separated words 37844 function split(events) { 37845 return events.match(/[^ ]+/g); 37846 } 37847 37848 /** 37849 * A static event handler, applying event propagation. 37850 * @param {Object} event 37851 */ 37852 function propagatedHandler(event) { 37853 // let only a single hammer instance handle this event 37854 if (event.type !== 'hammer.input') { 37855 // it is possible that the same srcEvent is used with multiple hammer events, 37856 // we keep track on which events are handled in an object _handled 37857 if (!event.srcEvent._handled) { 37858 event.srcEvent._handled = {}; 37859 } 37860 37861 if (event.srcEvent._handled[event.type]) { 37862 return; 37863 } 37864 else { 37865 event.srcEvent._handled[event.type] = true; 37866 } 37867 } 37868 37869 // attach a stopPropagation function to the event 37870 var stopped = false; 37871 event.stopPropagation = function () { 37872 stopped = true; 37873 }; 37874 37875 //wrap the srcEvent's stopPropagation to also stop hammer propagation: 37876 var srcStop = event.srcEvent.stopPropagation.bind(event.srcEvent); 37877 if(typeof srcStop == "function") { 37878 event.srcEvent.stopPropagation = function(){ 37879 srcStop(); 37880 event.stopPropagation(); 37881 } 37882 } 37883 37884 // attach firstTarget property to the event 37885 event.firstTarget = _firstTarget; 37886 37887 // propagate over all elements (until stopped) 37888 var elem = _firstTarget; 37889 while (elem && !stopped) { 37890 var elemHammer = elem.hammer; 37891 if(elemHammer){ 37892 var _handlers; 37893 for(var k = 0; k < elemHammer.length; k++){ 37894 _handlers = elemHammer[k]._handlers[event.type]; 37895 if(_handlers) for (var i = 0; i < _handlers.length && !stopped; i++) { 37896 _handlers[i](event); 37897 } 37898 } 37899 } 37900 elem = elem.parentNode; 37901 } 37902 } 37903 37904 return wrapper; 37905 }; 37906})); 37907 37908 37909/***/ }), 37910/* 176 */ 37911/***/ (function(module, exports, __webpack_require__) { 37912 37913var __WEBPACK_AMD_DEFINE_RESULT__;
vendor: 73,525 bytes, lines 37913-40545
37913/*! Hammer.JS - v2.0.7 - 2016-04-22 37914 * http://hammerjs.github.io/ 37915 * 37916 * Copyright (c) 2016 Jorik Tangelder; 37917 * Licensed under the MIT license */ 37918(function(window, document, exportName, undefined) { 37919 'use strict'; 37920 37921var VENDOR_PREFIXES = ['', 'webkit', 'Moz', 'MS', 'ms', 'o']; 37922var TEST_ELEMENT = document.createElement('div'); 37923 37924var TYPE_FUNCTION = 'function'; 37925 37926var round = Math.round; 37927var abs = Math.abs; 37928var now = Date.now; 37929 37930/** 37931 * set a timeout with a given scope 37932 * @param {Function} fn 37933 * @param {Number} timeout 37934 * @param {Object} context 37935 * @returns {number} 37936 */ 37937function setTimeoutContext(fn, timeout, context) { 37938 return setTimeout(bindFn(fn, context), timeout); 37939} 37940 37941/** 37942 * if the argument is an array, we want to execute the fn on each entry 37943 * if it aint an array we don't want to do a thing. 37944 * this is used by all the methods that accept a single and array argument. 37945 * @param {*|Array} arg 37946 * @param {String} fn 37947 * @param {Object} [context] 37948 * @returns {Boolean} 37949 */ 37950function invokeArrayArg(arg, fn, context) { 37951 if (Array.isArray(arg)) { 37952 each(arg, context[fn], context); 37953 return true; 37954 } 37955 return false; 37956} 37957 37958/** 37959 * walk objects and arrays 37960 * @param {Object} obj 37961 * @param {Function} iterator 37962 * @param {Object} context 37963 */ 37964function each(obj, iterator, context) { 37965 var i; 37966 37967 if (!obj) { 37968 return; 37969 } 37970 37971 if (obj.forEach) { 37972 obj.forEach(iterator, context); 37973 } else if (obj.length !== undefined) { 37974 i = 0; 37975 while (i < obj.length) { 37976 iterator.call(context, obj[i], i, obj); 37977 i++; 37978 } 37979 } else { 37980 for (i in obj) { 37981 obj.hasOwnProperty(i) && iterator.call(context, obj[i], i, obj); 37982 } 37983 } 37984} 37985 37986/** 37987 * wrap a method with a deprecation warning and stack trace 37988 * @param {Function} method 37989 * @param {String} name 37990 * @param {String} message 37991 * @returns {Function} A new function wrapping the supplied method. 37992 */ 37993function deprecate(method, name, message) { 37994 var deprecationMessage = 'DEPRECATED METHOD: ' + name + '\n' + message + ' AT \n'; 37995 return function() { 37996 var e = new Error('get-stack-trace'); 37997 var stack = e && e.stack ? e.stack.replace(/^[^\(]+?[\n$]/gm, '') 37998 .replace(/^\s+at\s+/gm, '') 37999 .replace(/^Object.<anonymous>\s*\(/gm, '{anonymous}()@') : 'Unknown Stack Trace'; 38000 38001 var log = window.console && (window.console.warn || window.console.log); 38002 if (log) { 38003 log.call(window.console, deprecationMessage, stack); 38004 } 38005 return method.apply(this, arguments); 38006 }; 38007} 38008 38009/** 38010 * extend object. 38011 * means that properties in dest will be overwritten by the ones in src. 38012 * @param {Object} target 38013 * @param {...Object} objects_to_assign 38014 * @returns {Object} target 38015 */ 38016var assign; 38017if (typeof Object.assign !== 'function') { 38018 assign = function assign(target) { 38019 if (target === undefined || target === null) { 38020 throw new TypeError('Cannot convert undefined or null to object'); 38021 } 38022 38023 var output = Object(target); 38024 for (var index = 1; index < arguments.length; index++) { 38025 var source = arguments[index]; 38026 if (source !== undefined && source !== null) { 38027 for (var nextKey in source) { 38028 if (source.hasOwnProperty(nextKey)) { 38029 output[nextKey] = source[nextKey]; 38030 } 38031 } 38032 } 38033 } 38034 return output; 38035 }; 38036} else { 38037 assign = Object.assign; 38038} 38039 38040/** 38041 * extend object. 38042 * means that properties in dest will be overwritten by the ones in src. 38043 * @param {Object} dest 38044 * @param {Object} src 38045 * @param {Boolean} [merge=false] 38046 * @returns {Object} dest 38047 */ 38048var extend = deprecate(function extend(dest, src, merge) { 38049 var keys = Object.keys(src); 38050 var i = 0; 38051 while (i < keys.length) { 38052 if (!merge || (merge && dest[keys[i]] === undefined)) { 38053 dest[keys[i]] = src[keys[i]]; 38054 } 38055 i++; 38056 } 38057 return dest; 38058}, 'extend', 'Use `assign`.'); 38059 38060/** 38061 * merge the values from src in the dest. 38062 * means that properties that exist in dest will not be overwritten by src 38063 * @param {Object} dest 38064 * @param {Object} src 38065 * @returns {Object} dest 38066 */ 38067var merge = deprecate(function merge(dest, src) { 38068 return extend(dest, src, true); 38069}, 'merge', 'Use `assign`.'); 38070 38071/** 38072 * simple class inheritance 38073 * @param {Function} child 38074 * @param {Function} base 38075 * @param {Object} [properties] 38076 */ 38077function inherit(child, base, properties) { 38078 var baseP = base.prototype, 38079 childP; 38080 38081 childP = child.prototype = Object.create(baseP); 38082 childP.constructor = child; 38083 childP._super = baseP; 38084 38085 if (properties) { 38086 assign(childP, properties); 38087 } 38088} 38089 38090/** 38091 * simple function bind 38092 * @param {Function} fn 38093 * @param {Object} context 38094 * @returns {Function} 38095 */ 38096function bindFn(fn, context) { 38097 return function boundFn() { 38098 return fn.apply(context, arguments); 38099 }; 38100} 38101 38102/** 38103 * let a boolean value also be a function that must return a boolean 38104 * this first item in args will be used as the context 38105 * @param {Boolean|Function} val 38106 * @param {Array} [args] 38107 * @returns {Boolean} 38108 */ 38109function boolOrFn(val, args) { 38110 if (typeof val == TYPE_FUNCTION) { 38111 return val.apply(args ? args[0] || undefined : undefined, args); 38112 } 38113 return val; 38114} 38115 38116/** 38117 * use the val2 when val1 is undefined 38118 * @param {*} val1 38119 * @param {*} val2 38120 * @returns {*} 38121 */ 38122function ifUndefined(val1, val2) { 38123 return (val1 === undefined) ? val2 : val1; 38124} 38125 38126/** 38127 * addEventListener with multiple events at once 38128 * @param {EventTarget} target 38129 * @param {String} types 38130 * @param {Function} handler 38131 */ 38132function addEventListeners(target, types, handler) { 38133 each(splitStr(types), function(type) { 38134 target.addEventListener(type, handler, false); 38135 }); 38136} 38137 38138/** 38139 * removeEventListener with multiple events at once 38140 * @param {EventTarget} target 38141 * @param {String} types 38142 * @param {Function} handler 38143 */ 38144function removeEventListeners(target, types, handler) { 38145 each(splitStr(types), function(type) { 38146 target.removeEventListener(type, handler, false); 38147 }); 38148} 38149 38150/** 38151 * find if a node is in the given parent 38152 * @method hasParent 38153 * @param {HTMLElement} node 38154 * @param {HTMLElement} parent 38155 * @return {Boolean} found 38156 */ 38157function hasParent(node, parent) { 38158 while (node) { 38159 if (node == parent) { 38160 return true; 38161 } 38162 node = node.parentNode; 38163 } 38164 return false; 38165} 38166 38167/** 38168 * small indexOf wrapper 38169 * @param {String} str 38170 * @param {String} find 38171 * @returns {Boolean} found 38172 */ 38173function inStr(str, find) { 38174 return str.indexOf(find) > -1; 38175} 38176 38177/** 38178 * split string on whitespace 38179 * @param {String} str 38180 * @returns {Array} words 38181 */ 38182function splitStr(str) { 38183 return str.trim().split(/\s+/g); 38184} 38185 38186/** 38187 * find if a array contains the object using indexOf or a simple polyFill 38188 * @param {Array} src 38189 * @param {String} find 38190 * @param {String} [findByKey] 38191 * @return {Boolean|Number} false when not found, or the index 38192 */ 38193function inArray(src, find, findByKey) { 38194 if (src.indexOf && !findByKey) { 38195 return src.indexOf(find); 38196 } else { 38197 var i = 0; 38198 while (i < src.length) { 38199 if ((findByKey && src[i][findByKey] == find) || (!findByKey && src[i] === find)) { 38200 return i; 38201 } 38202 i++; 38203 } 38204 return -1; 38205 } 38206} 38207 38208/** 38209 * convert array-like objects to real arrays 38210 * @param {Object} obj 38211 * @returns {Array} 38212 */ 38213function toArray(obj) { 38214 return Array.prototype.slice.call(obj, 0); 38215} 38216 38217/** 38218 * unique array with objects based on a key (like 'id') or just by the array's value 38219 * @param {Array} src [{id:1},{id:2},{id:1}] 38220 * @param {String} [key] 38221 * @param {Boolean} [sort=False] 38222 * @returns {Array} [{id:1},{id:2}] 38223 */ 38224function uniqueArray(src, key, sort) { 38225 var results = []; 38226 var values = []; 38227 var i = 0; 38228 38229 while (i < src.length) { 38230 var val = key ? src[i][key] : src[i]; 38231 if (inArray(values, val) < 0) { 38232 results.push(src[i]); 38233 } 38234 values[i] = val; 38235 i++; 38236 } 38237 38238 if (sort) { 38239 if (!key) { 38240 results = results.sort(); 38241 } else { 38242 results = results.sort(function sortUniqueArray(a, b) { 38243 return a[key] > b[key]; 38244 }); 38245 } 38246 } 38247 38248 return results; 38249} 38250 38251/** 38252 * get the prefixed property 38253 * @param {Object} obj 38254 * @param {String} property 38255 * @returns {String|Undefined} prefixed 38256 */ 38257function prefixed(obj, property) { 38258 var prefix, prop; 38259 var camelProp = property[0].toUpperCase() + property.slice(1); 38260 38261 var i = 0; 38262 while (i < VENDOR_PREFIXES.length) { 38263 prefix = VENDOR_PREFIXES[i]; 38264 prop = (prefix) ? prefix + camelProp : property; 38265 38266 if (prop in obj) { 38267 return prop; 38268 } 38269 i++; 38270 } 38271 return undefined; 38272} 38273 38274/** 38275 * get a unique id 38276 * @returns {number} uniqueId 38277 */ 38278var _uniqueId = 1; 38279function uniqueId() { 38280 return _uniqueId++; 38281} 38282 38283/** 38284 * get the window object of an element 38285 * @param {HTMLElement} element 38286 * @returns {DocumentView|Window} 38287 */ 38288function getWindowForElement(element) { 38289 var doc = element.ownerDocument || element; 38290 return (doc.defaultView || doc.parentWindow || window); 38291} 38292 38293var MOBILE_REGEX = /mobile|tablet|ip(ad|hone|od)|android/i; 38294 38295var SUPPORT_TOUCH = ('ontouchstart' in window); 38296var SUPPORT_POINTER_EVENTS = prefixed(window, 'PointerEvent') !== undefined; 38297var SUPPORT_ONLY_TOUCH = SUPPORT_TOUCH && MOBILE_REGEX.test(navigator.userAgent); 38298 38299var INPUT_TYPE_TOUCH = 'touch'; 38300var INPUT_TYPE_PEN = 'pen'; 38301var INPUT_TYPE_MOUSE = 'mouse'; 38302var INPUT_TYPE_KINECT = 'kinect'; 38303 38304var COMPUTE_INTERVAL = 25; 38305 38306var INPUT_START = 1; 38307var INPUT_MOVE = 2; 38308var INPUT_END = 4; 38309var INPUT_CANCEL = 8; 38310 38311var DIRECTION_NONE = 1; 38312var DIRECTION_LEFT = 2; 38313var DIRECTION_RIGHT = 4; 38314var DIRECTION_UP = 8; 38315var DIRECTION_DOWN = 16; 38316 38317var DIRECTION_HORIZONTAL = DIRECTION_LEFT | DIRECTION_RIGHT; 38318var DIRECTION_VERTICAL = DIRECTION_UP | DIRECTION_DOWN; 38319var DIRECTION_ALL = DIRECTION_HORIZONTAL | DIRECTION_VERTICAL; 38320 38321var PROPS_XY = ['x', 'y']; 38322var PROPS_CLIENT_XY = ['clientX', 'clientY']; 38323 38324/** 38325 * create new input type manager 38326 * @param {Manager} manager 38327 * @param {Function} callback 38328 * @returns {Input} 38329 * @constructor 38330 */ 38331function Input(manager, callback) { 38332 var self = this; 38333 this.manager = manager; 38334 this.callback = callback; 38335 this.element = manager.element; 38336 this.target = manager.options.inputTarget; 38337 38338 // smaller wrapper around the handler, for the scope and the enabled state of the manager, 38339 // so when disabled the input events are completely bypassed. 38340 this.domHandler = function(ev) { 38341 if (boolOrFn(manager.options.enable, [manager])) { 38342 self.handler(ev); 38343 } 38344 }; 38345 38346 this.init(); 38347 38348} 38349 38350Input.prototype = { 38351 /** 38352 * should handle the inputEvent data and trigger the callback 38353 * @virtual 38354 */ 38355 handler: function() { }, 38356 38357 /** 38358 * bind the events 38359 */ 38360 init: function() { 38361 this.evEl && addEventListeners(this.element, this.evEl, this.domHandler); 38362 this.evTarget && addEventListeners(this.target, this.evTarget, this.domHandler); 38363 this.evWin && addEventListeners(getWindowForElement(this.element), this.evWin, this.domHandler); 38364 }, 38365 38366 /** 38367 * unbind the events 38368 */ 38369 destroy: function() { 38370 this.evEl && removeEventListeners(this.element, this.evEl, this.domHandler); 38371 this.evTarget && removeEventListeners(this.target, this.evTarget, this.domHandler); 38372 this.evWin && removeEventListeners(getWindowForElement(this.element), this.evWin, this.domHandler); 38373 } 38374}; 38375 38376/** 38377 * create new input type manager 38378 * called by the Manager constructor 38379 * @param {Hammer} manager 38380 * @returns {Input} 38381 */ 38382function createInputInstance(manager) { 38383 var Type; 38384 var inputClass = manager.options.inputClass; 38385 38386 if (inputClass) { 38387 Type = inputClass; 38388 } else if (SUPPORT_POINTER_EVENTS) { 38389 Type = PointerEventInput; 38390 } else if (SUPPORT_ONLY_TOUCH) { 38391 Type = TouchInput; 38392 } else if (!SUPPORT_TOUCH) { 38393 Type = MouseInput; 38394 } else { 38395 Type = TouchMouseInput; 38396 } 38397 return new (Type)(manager, inputHandler); 38398} 38399 38400/** 38401 * handle input events 38402 * @param {Manager} manager 38403 * @param {String} eventType 38404 * @param {Object} input 38405 */ 38406function inputHandler(manager, eventType, input) { 38407 var pointersLen = input.pointers.length; 38408 var changedPointersLen = input.changedPointers.length; 38409 var isFirst = (eventType & INPUT_START && (pointersLen - changedPointersLen === 0)); 38410 var isFinal = (eventType & (INPUT_END | INPUT_CANCEL) && (pointersLen - changedPointersLen === 0)); 38411 38412 input.isFirst = !!isFirst; 38413 input.isFinal = !!isFinal; 38414 38415 if (isFirst) { 38416 manager.session = {}; 38417 } 38418 38419 // source event is the normalized value of the domEvents 38420 // like 'touchstart, mouseup, pointerdown' 38421 input.eventType = eventType; 38422 38423 // compute scale, rotation etc 38424 computeInputData(manager, input); 38425 38426 // emit secret event 38427 manager.emit('hammer.input', input); 38428 38429 manager.recognize(input); 38430 manager.session.prevInput = input; 38431} 38432 38433/** 38434 * extend the data with some usable properties like scale, rotate, velocity etc 38435 * @param {Object} manager 38436 * @param {Object} input 38437 */ 38438function computeInputData(manager, input) { 38439 var session = manager.session; 38440 var pointers = input.pointers; 38441 var pointersLength = pointers.length; 38442 38443 // store the first input to calculate the distance and direction 38444 if (!session.firstInput) { 38445 session.firstInput = simpleCloneInputData(input); 38446 } 38447 38448 // to compute scale and rotation we need to store the multiple touches 38449 if (pointersLength > 1 && !session.firstMultiple) { 38450 session.firstMultiple = simpleCloneInputData(input); 38451 } else if (pointersLength === 1) { 38452 session.firstMultiple = false; 38453 } 38454 38455 var firstInput = session.firstInput; 38456 var firstMultiple = session.firstMultiple; 38457 var offsetCenter = firstMultiple ? firstMultiple.center : firstInput.center; 38458 38459 var center = input.center = getCenter(pointers); 38460 input.timeStamp = now(); 38461 input.deltaTime = input.timeStamp - firstInput.timeStamp; 38462 38463 input.angle = getAngle(offsetCenter, center); 38464 input.distance = getDistance(offsetCenter, center); 38465 38466 computeDeltaXY(session, input); 38467 input.offsetDirection = getDirection(input.deltaX, input.deltaY); 38468 38469 var overallVelocity = getVelocity(input.deltaTime, input.deltaX, input.deltaY); 38470 input.overallVelocityX = overallVelocity.x; 38471 input.overallVelocityY = overallVelocity.y; 38472 input.overallVelocity = (abs(overallVelocity.x) > abs(overallVelocity.y)) ? overallVelocity.x : overallVelocity.y; 38473 38474 input.scale = firstMultiple ? getScale(firstMultiple.pointers, pointers) : 1; 38475 input.rotation = firstMultiple ? getRotation(firstMultiple.pointers, pointers) : 0; 38476 38477 input.maxPointers = !session.prevInput ? input.pointers.length : ((input.pointers.length > 38478 session.prevInput.maxPointers) ? input.pointers.length : session.prevInput.maxPointers); 38479 38480 computeIntervalInputData(session, input); 38481 38482 // find the correct target 38483 var target = manager.element; 38484 if (hasParent(input.srcEvent.target, target)) { 38485 target = input.srcEvent.target; 38486 } 38487 input.target = target; 38488} 38489 38490function computeDeltaXY(session, input) { 38491 var center = input.center; 38492 var offset = session.offsetDelta || {}; 38493 var prevDelta = session.prevDelta || {}; 38494 var prevInput = session.prevInput || {}; 38495 38496 if (input.eventType === INPUT_START || prevInput.eventType === INPUT_END) { 38497 prevDelta = session.prevDelta = { 38498 x: prevInput.deltaX || 0, 38499 y: prevInput.deltaY || 0 38500 }; 38501 38502 offset = session.offsetDelta = { 38503 x: center.x, 38504 y: center.y 38505 }; 38506 } 38507 38508 input.deltaX = prevDelta.x + (center.x - offset.x); 38509 input.deltaY = prevDelta.y + (center.y - offset.y); 38510} 38511 38512/** 38513 * velocity is calculated every x ms 38514 * @param {Object} session 38515 * @param {Object} input 38516 */ 38517function computeIntervalInputData(session, input) { 38518 var last = session.lastInterval || input, 38519 deltaTime = input.timeStamp - last.timeStamp, 38520 velocity, velocityX, velocityY, direction; 38521 38522 if (input.eventType != INPUT_CANCEL && (deltaTime > COMPUTE_INTERVAL || last.velocity === undefined)) { 38523 var deltaX = input.deltaX - last.deltaX; 38524 var deltaY = input.deltaY - last.deltaY; 38525 38526 var v = getVelocity(deltaTime, deltaX, deltaY); 38527 velocityX = v.x; 38528 velocityY = v.y; 38529 velocity = (abs(v.x) > abs(v.y)) ? v.x : v.y; 38530 direction = getDirection(deltaX, deltaY); 38531 38532 session.lastInterval = input; 38533 } else { 38534 // use latest velocity info if it doesn't overtake a minimum period 38535 velocity = last.velocity; 38536 velocityX = last.velocityX; 38537 velocityY = last.velocityY; 38538 direction = last.direction; 38539 } 38540 38541 input.velocity = velocity; 38542 input.velocityX = velocityX; 38543 input.velocityY = velocityY; 38544 input.direction = direction; 38545} 38546 38547/** 38548 * create a simple clone from the input used for storage of firstInput and firstMultiple 38549 * @param {Object} input 38550 * @returns {Object} clonedInputData 38551 */ 38552function simpleCloneInputData(input) { 38553 // make a simple copy of the pointers because we will get a reference if we don't 38554 // we only need clientXY for the calculations 38555 var pointers = []; 38556 var i = 0; 38557 while (i < input.pointers.length) { 38558 pointers[i] = { 38559 clientX: round(input.pointers[i].clientX), 38560 clientY: round(input.pointers[i].clientY) 38561 }; 38562 i++; 38563 } 38564 38565 return { 38566 timeStamp: now(), 38567 pointers: pointers, 38568 center: getCenter(pointers), 38569 deltaX: input.deltaX, 38570 deltaY: input.deltaY 38571 }; 38572} 38573 38574/** 38575 * get the center of all the pointers 38576 * @param {Array} pointers 38577 * @return {Object} center contains `x` and `y` properties 38578 */ 38579function getCenter(pointers) { 38580 var pointersLength = pointers.length; 38581 38582 // no need to loop when only one touch 38583 if (pointersLength === 1) { 38584 return { 38585 x: round(pointers[0].clientX), 38586 y: round(pointers[0].clientY) 38587 }; 38588 } 38589 38590 var x = 0, y = 0, i = 0; 38591 while (i < pointersLength) { 38592 x += pointers[i].clientX; 38593 y += pointers[i].clientY; 38594 i++; 38595 } 38596 38597 return { 38598 x: round(x / pointersLength), 38599 y: round(y / pointersLength) 38600 }; 38601} 38602 38603/** 38604 * calculate the velocity between two points. unit is in px per ms. 38605 * @param {Number} deltaTime 38606 * @param {Number} x 38607 * @param {Number} y 38608 * @return {Object} velocity `x` and `y` 38609 */ 38610function getVelocity(deltaTime, x, y) { 38611 return { 38612 x: x / deltaTime || 0, 38613 y: y / deltaTime || 0 38614 }; 38615} 38616 38617/** 38618 * get the direction between two points 38619 * @param {Number} x 38620 * @param {Number} y 38621 * @return {Number} direction 38622 */ 38623function getDirection(x, y) { 38624 if (x === y) { 38625 return DIRECTION_NONE; 38626 } 38627 38628 if (abs(x) >= abs(y)) { 38629 return x < 0 ? DIRECTION_LEFT : DIRECTION_RIGHT; 38630 } 38631 return y < 0 ? DIRECTION_UP : DIRECTION_DOWN; 38632} 38633 38634/** 38635 * calculate the absolute distance between two points 38636 * @param {Object} p1 {x, y} 38637 * @param {Object} p2 {x, y} 38638 * @param {Array} [props] containing x and y keys 38639 * @return {Number} distance 38640 */ 38641function getDistance(p1, p2, props) { 38642 if (!props) { 38643 props = PROPS_XY; 38644 } 38645 var x = p2[props[0]] - p1[props[0]], 38646 y = p2[props[1]] - p1[props[1]]; 38647 38648 return Math.sqrt((x * x) + (y * y)); 38649} 38650 38651/** 38652 * calculate the angle between two coordinates 38653 * @param {Object} p1 38654 * @param {Object} p2 38655 * @param {Array} [props] containing x and y keys 38656 * @return {Number} angle 38657 */ 38658function getAngle(p1, p2, props) { 38659 if (!props) { 38660 props = PROPS_XY; 38661 } 38662 var x = p2[props[0]] - p1[props[0]], 38663 y = p2[props[1]] - p1[props[1]]; 38664 return Math.atan2(y, x) * 180 / Math.PI; 38665} 38666 38667/** 38668 * calculate the rotation degrees between two pointersets 38669 * @param {Array} start array of pointers 38670 * @param {Array} end array of pointers 38671 * @return {Number} rotation 38672 */ 38673function getRotation(start, end) { 38674 return getAngle(end[1], end[0], PROPS_CLIENT_XY) + getAngle(start[1], start[0], PROPS_CLIENT_XY); 38675} 38676 38677/** 38678 * calculate the scale factor between two pointersets 38679 * no scale is 1, and goes down to 0 when pinched together, and bigger when pinched out 38680 * @param {Array} start array of pointers 38681 * @param {Array} end array of pointers 38682 * @return {Number} scale 38683 */ 38684function getScale(start, end) { 38685 return getDistance(end[0], end[1], PROPS_CLIENT_XY) / getDistance(start[0], start[1], PROPS_CLIENT_XY); 38686} 38687 38688var MOUSE_INPUT_MAP = { 38689 mousedown: INPUT_START, 38690 mousemove: INPUT_MOVE, 38691 mouseup: INPUT_END 38692}; 38693 38694var MOUSE_ELEMENT_EVENTS = 'mousedown'; 38695var MOUSE_WINDOW_EVENTS = 'mousemove mouseup'; 38696 38697/** 38698 * Mouse events input 38699 * @constructor 38700 * @extends Input 38701 */ 38702function MouseInput() { 38703 this.evEl = MOUSE_ELEMENT_EVENTS; 38704 this.evWin = MOUSE_WINDOW_EVENTS; 38705 38706 this.pressed = false; // mousedown state 38707 38708 Input.apply(this, arguments); 38709} 38710 38711inherit(MouseInput, Input, { 38712 /** 38713 * handle mouse events 38714 * @param {Object} ev 38715 */ 38716 handler: function MEhandler(ev) { 38717 var eventType = MOUSE_INPUT_MAP[ev.type]; 38718 38719 // on start we want to have the left mouse button down 38720 if (eventType & INPUT_START && ev.button === 0) { 38721 this.pressed = true; 38722 } 38723 38724 if (eventType & INPUT_MOVE && ev.which !== 1) { 38725 eventType = INPUT_END; 38726 } 38727 38728 // mouse must be down 38729 if (!this.pressed) { 38730 return; 38731 } 38732 38733 if (eventType & INPUT_END) { 38734 this.pressed = false; 38735 } 38736 38737 this.callback(this.manager, eventType, { 38738 pointers: [ev], 38739 changedPointers: [ev], 38740 pointerType: INPUT_TYPE_MOUSE, 38741 srcEvent: ev 38742 }); 38743 } 38744}); 38745 38746var POINTER_INPUT_MAP = { 38747 pointerdown: INPUT_START, 38748 pointermove: INPUT_MOVE, 38749 pointerup: INPUT_END, 38750 pointercancel: INPUT_CANCEL, 38751 pointerout: INPUT_CANCEL 38752}; 38753 38754// in IE10 the pointer types is defined as an enum 38755var IE10_POINTER_TYPE_ENUM = { 38756 2: INPUT_TYPE_TOUCH, 38757 3: INPUT_TYPE_PEN, 38758 4: INPUT_TYPE_MOUSE, 38759 5: INPUT_TYPE_KINECT // see https://twitter.com/jacobrossi/status/480596438489890816 38760}; 38761 38762var POINTER_ELEMENT_EVENTS = 'pointerdown'; 38763var POINTER_WINDOW_EVENTS = 'pointermove pointerup pointercancel'; 38764 38765// IE10 has prefixed support, and case-sensitive 38766if (window.MSPointerEvent && !window.PointerEvent) { 38767 POINTER_ELEMENT_EVENTS = 'MSPointerDown'; 38768 POINTER_WINDOW_EVENTS = 'MSPointerMove MSPointerUp MSPointerCancel'; 38769} 38770 38771/** 38772 * Pointer events input 38773 * @constructor 38774 * @extends Input 38775 */ 38776function PointerEventInput() { 38777 this.evEl = POINTER_ELEMENT_EVENTS; 38778 this.evWin = POINTER_WINDOW_EVENTS; 38779 38780 Input.apply(this, arguments); 38781 38782 this.store = (this.manager.session.pointerEvents = []); 38783} 38784 38785inherit(PointerEventInput, Input, { 38786 /** 38787 * handle mouse events 38788 * @param {Object} ev 38789 */ 38790 handler: function PEhandler(ev) { 38791 var store = this.store; 38792 var removePointer = false; 38793 38794 var eventTypeNormalized = ev.type.toLowerCase().replace('ms', ''); 38795 var eventType = POINTER_INPUT_MAP[eventTypeNormalized]; 38796 var pointerType = IE10_POINTER_TYPE_ENUM[ev.pointerType] || ev.pointerType; 38797 38798 var isTouch = (pointerType == INPUT_TYPE_TOUCH); 38799 38800 // get index of the event in the store 38801 var storeIndex = inArray(store, ev.pointerId, 'pointerId'); 38802 38803 // start and mouse must be down 38804 if (eventType & INPUT_START && (ev.button === 0 || isTouch)) { 38805 if (storeIndex < 0) { 38806 store.push(ev); 38807 storeIndex = store.length - 1; 38808 } 38809 } else if (eventType & (INPUT_END | INPUT_CANCEL)) { 38810 removePointer = true; 38811 } 38812 38813 // it not found, so the pointer hasn't been down (so it's probably a hover) 38814 if (storeIndex < 0) { 38815 return; 38816 } 38817 38818 // update the event in the store 38819 store[storeIndex] = ev; 38820 38821 this.callback(this.manager, eventType, { 38822 pointers: store, 38823 changedPointers: [ev], 38824 pointerType: pointerType, 38825 srcEvent: ev 38826 }); 38827 38828 if (removePointer) { 38829 // remove from the store 38830 store.splice(storeIndex, 1); 38831 } 38832 } 38833}); 38834 38835var SINGLE_TOUCH_INPUT_MAP = { 38836 touchstart: INPUT_START, 38837 touchmove: INPUT_MOVE, 38838 touchend: INPUT_END, 38839 touchcancel: INPUT_CANCEL 38840}; 38841 38842var SINGLE_TOUCH_TARGET_EVENTS = 'touchstart'; 38843var SINGLE_TOUCH_WINDOW_EVENTS = 'touchstart touchmove touchend touchcancel'; 38844 38845/** 38846 * Touch events input 38847 * @constructor 38848 * @extends Input 38849 */ 38850function SingleTouchInput() { 38851 this.evTarget = SINGLE_TOUCH_TARGET_EVENTS; 38852 this.evWin = SINGLE_TOUCH_WINDOW_EVENTS; 38853 this.started = false; 38854 38855 Input.apply(this, arguments); 38856} 38857 38858inherit(SingleTouchInput, Input, { 38859 handler: function TEhandler(ev) { 38860 var type = SINGLE_TOUCH_INPUT_MAP[ev.type]; 38861 38862 // should we handle the touch events? 38863 if (type === INPUT_START) { 38864 this.started = true; 38865 } 38866 38867 if (!this.started) { 38868 return; 38869 } 38870 38871 var touches = normalizeSingleTouches.call(this, ev, type); 38872 38873 // when done, reset the started state 38874 if (type & (INPUT_END | INPUT_CANCEL) && touches[0].length - touches[1].length === 0) { 38875 this.started = false; 38876 } 38877 38878 this.callback(this.manager, type, { 38879 pointers: touches[0], 38880 changedPointers: touches[1], 38881 pointerType: INPUT_TYPE_TOUCH, 38882 srcEvent: ev 38883 }); 38884 } 38885}); 38886 38887/** 38888 * @this {TouchInput} 38889 * @param {Object} ev 38890 * @param {Number} type flag 38891 * @returns {undefined|Array} [all, changed] 38892 */ 38893function normalizeSingleTouches(ev, type) { 38894 var all = toArray(ev.touches); 38895 var changed = toArray(ev.changedTouches); 38896 38897 if (type & (INPUT_END | INPUT_CANCEL)) { 38898 all = uniqueArray(all.concat(changed), 'identifier', true); 38899 } 38900 38901 return [all, changed]; 38902} 38903 38904var TOUCH_INPUT_MAP = { 38905 touchstart: INPUT_START, 38906 touchmove: INPUT_MOVE, 38907 touchend: INPUT_END, 38908 touchcancel: INPUT_CANCEL 38909}; 38910 38911var TOUCH_TARGET_EVENTS = 'touchstart touchmove touchend touchcancel'; 38912 38913/** 38914 * Multi-user touch events input 38915 * @constructor 38916 * @extends Input 38917 */ 38918function TouchInput() { 38919 this.evTarget = TOUCH_TARGET_EVENTS; 38920 this.targetIds = {}; 38921 38922 Input.apply(this, arguments); 38923} 38924 38925inherit(TouchInput, Input, { 38926 handler: function MTEhandler(ev) { 38927 var type = TOUCH_INPUT_MAP[ev.type]; 38928 var touches = getTouches.call(this, ev, type); 38929 if (!touches) { 38930 return; 38931 } 38932 38933 this.callback(this.manager, type, { 38934 pointers: touches[0], 38935 changedPointers: touches[1], 38936 pointerType: INPUT_TYPE_TOUCH, 38937 srcEvent: ev 38938 }); 38939 } 38940}); 38941 38942/** 38943 * @this {TouchInput} 38944 * @param {Object} ev 38945 * @param {Number} type flag 38946 * @returns {undefined|Array} [all, changed] 38947 */ 38948function getTouches(ev, type) { 38949 var allTouches = toArray(ev.touches); 38950 var targetIds = this.targetIds; 38951 38952 // when there is only one touch, the process can be simplified 38953 if (type & (INPUT_START | INPUT_MOVE) && allTouches.length === 1) { 38954 targetIds[allTouches[0].identifier] = true; 38955 return [allTouches, allTouches]; 38956 } 38957 38958 var i, 38959 targetTouches, 38960 changedTouches = toArray(ev.changedTouches), 38961 changedTargetTouches = [], 38962 target = this.target; 38963 38964 // get target touches from touches 38965 targetTouches = allTouches.filter(function(touch) { 38966 return hasParent(touch.target, target); 38967 }); 38968 38969 // collect touches 38970 if (type === INPUT_START) { 38971 i = 0; 38972 while (i < targetTouches.length) { 38973 targetIds[targetTouches[i].identifier] = true; 38974 i++; 38975 } 38976 } 38977 38978 // filter changed touches to only contain touches that exist in the collected target ids 38979 i = 0; 38980 while (i < changedTouches.length) { 38981 if (targetIds[changedTouches[i].identifier]) { 38982 changedTargetTouches.push(changedTouches[i]); 38983 } 38984 38985 // cleanup removed touches 38986 if (type & (INPUT_END | INPUT_CANCEL)) { 38987 delete targetIds[changedTouches[i].identifier]; 38988 } 38989 i++; 38990 } 38991 38992 if (!changedTargetTouches.length) { 38993 return; 38994 } 38995 38996 return [ 38997 // merge targetTouches with changedTargetTouches so it contains ALL touches, including 'end' and 'cancel' 38998 uniqueArray(targetTouches.concat(changedTargetTouches), 'identifier', true), 38999 changedTargetTouches 39000 ]; 39001} 39002 39003/** 39004 * Combined touch and mouse input 39005 * 39006 * Touch has a higher priority then mouse, and while touching no mouse events are allowed. 39007 * This because touch devices also emit mouse events while doing a touch. 39008 * 39009 * @constructor 39010 * @extends Input 39011 */ 39012 39013var DEDUP_TIMEOUT = 2500; 39014var DEDUP_DISTANCE = 25; 39015 39016function TouchMouseInput() { 39017 Input.apply(this, arguments); 39018 39019 var handler = bindFn(this.handler, this); 39020 this.touch = new TouchInput(this.manager, handler); 39021 this.mouse = new MouseInput(this.manager, handler); 39022 39023 this.primaryTouch = null; 39024 this.lastTouches = []; 39025} 39026 39027inherit(TouchMouseInput, Input, { 39028 /** 39029 * handle mouse and touch events 39030 * @param {Hammer} manager 39031 * @param {String} inputEvent 39032 * @param {Object} inputData 39033 */ 39034 handler: function TMEhandler(manager, inputEvent, inputData) { 39035 var isTouch = (inputData.pointerType == INPUT_TYPE_TOUCH), 39036 isMouse = (inputData.pointerType == INPUT_TYPE_MOUSE); 39037 39038 if (isMouse && inputData.sourceCapabilities && inputData.sourceCapabilities.firesTouchEvents) { 39039 return; 39040 } 39041 39042 // when we're in a touch event, record touches to de-dupe synthetic mouse event 39043 if (isTouch) { 39044 recordTouches.call(this, inputEvent, inputData); 39045 } else if (isMouse && isSyntheticEvent.call(this, inputData)) { 39046 return; 39047 } 39048 39049 this.callback(manager, inputEvent, inputData); 39050 }, 39051 39052 /** 39053 * remove the event listeners 39054 */ 39055 destroy: function destroy() { 39056 this.touch.destroy(); 39057 this.mouse.destroy(); 39058 } 39059}); 39060 39061function recordTouches(eventType, eventData) { 39062 if (eventType & INPUT_START) { 39063 this.primaryTouch = eventData.changedPointers[0].identifier; 39064 setLastTouch.call(this, eventData); 39065 } else if (eventType & (INPUT_END | INPUT_CANCEL)) { 39066 setLastTouch.call(this, eventData); 39067 } 39068} 39069 39070function setLastTouch(eventData) { 39071 var touch = eventData.changedPointers[0]; 39072 39073 if (touch.identifier === this.primaryTouch) { 39074 var lastTouch = {x: touch.clientX, y: touch.clientY}; 39075 this.lastTouches.push(lastTouch); 39076 var lts = this.lastTouches; 39077 var removeLastTouch = function() { 39078 var i = lts.indexOf(lastTouch); 39079 if (i > -1) { 39080 lts.splice(i, 1); 39081 } 39082 }; 39083 setTimeout(removeLastTouch, DEDUP_TIMEOUT); 39084 } 39085} 39086 39087function isSyntheticEvent(eventData) { 39088 var x = eventData.srcEvent.clientX, y = eventData.srcEvent.clientY; 39089 for (var i = 0; i < this.lastTouches.length; i++) { 39090 var t = this.lastTouches[i]; 39091 var dx = Math.abs(x - t.x), dy = Math.abs(y - t.y); 39092 if (dx <= DEDUP_DISTANCE && dy <= DEDUP_DISTANCE) { 39093 return true; 39094 } 39095 } 39096 return false; 39097} 39098 39099var PREFIXED_TOUCH_ACTION = prefixed(TEST_ELEMENT.style, 'touchAction'); 39100var NATIVE_TOUCH_ACTION = PREFIXED_TOUCH_ACTION !== undefined; 39101 39102// magical touchAction value 39103var TOUCH_ACTION_COMPUTE = 'compute'; 39104var TOUCH_ACTION_AUTO = 'auto'; 39105var TOUCH_ACTION_MANIPULATION = 'manipulation'; // not implemented 39106var TOUCH_ACTION_NONE = 'none'; 39107var TOUCH_ACTION_PAN_X = 'pan-x'; 39108var TOUCH_ACTION_PAN_Y = 'pan-y'; 39109var TOUCH_ACTION_MAP = getTouchActionProps(); 39110 39111/** 39112 * Touch Action 39113 * sets the touchAction property or uses the js alternative 39114 * @param {Manager} manager 39115 * @param {String} value 39116 * @constructor 39117 */ 39118function TouchAction(manager, value) { 39119 this.manager = manager; 39120 this.set(value); 39121} 39122 39123TouchAction.prototype = { 39124 /** 39125 * set the touchAction value on the element or enable the polyfill 39126 * @param {String} value 39127 */ 39128 set: function(value) { 39129 // find out the touch-action by the event handlers 39130 if (value == TOUCH_ACTION_COMPUTE) { 39131 value = this.compute(); 39132 } 39133 39134 if (NATIVE_TOUCH_ACTION && this.manager.element.style && TOUCH_ACTION_MAP[value]) { 39135 this.manager.element.style[PREFIXED_TOUCH_ACTION] = value; 39136 } 39137 this.actions = value.toLowerCase().trim(); 39138 }, 39139 39140 /** 39141 * just re-set the touchAction value 39142 */ 39143 update: function() { 39144 this.set(this.manager.options.touchAction); 39145 }, 39146 39147 /** 39148 * compute the value for the touchAction property based on the recognizer's settings 39149 * @returns {String} value 39150 */ 39151 compute: function() { 39152 var actions = []; 39153 each(this.manager.recognizers, function(recognizer) { 39154 if (boolOrFn(recognizer.options.enable, [recognizer])) { 39155 actions = actions.concat(recognizer.getTouchAction()); 39156 } 39157 }); 39158 return cleanTouchActions(actions.join(' ')); 39159 }, 39160 39161 /** 39162 * this method is called on each input cycle and provides the preventing of the browser behavior 39163 * @param {Object} input 39164 */ 39165 preventDefaults: function(input) { 39166 var srcEvent = input.srcEvent; 39167 var direction = input.offsetDirection; 39168 39169 // if the touch action did prevented once this session 39170 if (this.manager.session.prevented) { 39171 srcEvent.preventDefault(); 39172 return; 39173 } 39174 39175 var actions = this.actions; 39176 var hasNone = inStr(actions, TOUCH_ACTION_NONE) && !TOUCH_ACTION_MAP[TOUCH_ACTION_NONE]; 39177 var hasPanY = inStr(actions, TOUCH_ACTION_PAN_Y) && !TOUCH_ACTION_MAP[TOUCH_ACTION_PAN_Y]; 39178 var hasPanX = inStr(actions, TOUCH_ACTION_PAN_X) && !TOUCH_ACTION_MAP[TOUCH_ACTION_PAN_X]; 39179 39180 if (hasNone) { 39181 //do not prevent defaults if this is a tap gesture 39182 39183 var isTapPointer = input.pointers.length === 1; 39184 var isTapMovement = input.distance < 2; 39185 var isTapTouchTime = input.deltaTime < 250; 39186 39187 if (isTapPointer && isTapMovement && isTapTouchTime) { 39188 return; 39189 } 39190 } 39191 39192 if (hasPanX && hasPanY) { 39193 // `pan-x pan-y` means browser handles all scrolling/panning, do not prevent 39194 return; 39195 } 39196 39197 if (hasNone || 39198 (hasPanY && direction & DIRECTION_HORIZONTAL) || 39199 (hasPanX && direction & DIRECTION_VERTICAL)) { 39200 return this.preventSrc(srcEvent); 39201 } 39202 }, 39203 39204 /** 39205 * call preventDefault to prevent the browser's default behavior (scrolling in most cases) 39206 * @param {Object} srcEvent 39207 */ 39208 preventSrc: function(srcEvent) { 39209 this.manager.session.prevented = true; 39210 srcEvent.preventDefault(); 39211 } 39212}; 39213 39214/** 39215 * when the touchActions are collected they are not a valid value, so we need to clean things up. * 39216 * @param {String} actions 39217 * @returns {*} 39218 */ 39219function cleanTouchActions(actions) { 39220 // none 39221 if (inStr(actions, TOUCH_ACTION_NONE)) { 39222 return TOUCH_ACTION_NONE; 39223 } 39224 39225 var hasPanX = inStr(actions, TOUCH_ACTION_PAN_X); 39226 var hasPanY = inStr(actions, TOUCH_ACTION_PAN_Y); 39227 39228 // if both pan-x and pan-y are set (different recognizers 39229 // for different directions, e.g. horizontal pan but vertical swipe?) 39230 // we need none (as otherwise with pan-x pan-y combined none of these 39231 // recognizers will work, since the browser would handle all panning 39232 if (hasPanX && hasPanY) { 39233 return TOUCH_ACTION_NONE; 39234 } 39235 39236 // pan-x OR pan-y 39237 if (hasPanX || hasPanY) { 39238 return hasPanX ? TOUCH_ACTION_PAN_X : TOUCH_ACTION_PAN_Y; 39239 } 39240 39241 // manipulation 39242 if (inStr(actions, TOUCH_ACTION_MANIPULATION)) { 39243 return TOUCH_ACTION_MANIPULATION; 39244 } 39245 39246 return TOUCH_ACTION_AUTO; 39247} 39248 39249function getTouchActionProps() { 39250 if (!NATIVE_TOUCH_ACTION) { 39251 return false; 39252 } 39253 var touchMap = {}; 39254 var cssSupports = window.CSS && window.CSS.supports; 39255 ['auto', 'manipulation', 'pan-y', 'pan-x', 'pan-x pan-y', 'none'].forEach(function(val) { 39256 39257 // If css.supports is not supported but there is native touch-action assume it supports 39258 // all values. This is the case for IE 10 and 11. 39259 touchMap[val] = cssSupports ? window.CSS.supports('touch-action', val) : true; 39260 }); 39261 return touchMap; 39262} 39263 39264/** 39265 * Recognizer flow explained; * 39266 * All recognizers have the initial state of POSSIBLE when a input session starts. 39267 * The definition of a input session is from the first input until the last input, with all it's movement in it. * 39268 * Example session for mouse-input: mousedown -> mousemove -> mouseup 39269 * 39270 * On each recognizing cycle (see Manager.recognize) the .recognize() method is executed 39271 * which determines with state it should be. 39272 * 39273 * If the recognizer has the state FAILED, CANCELLED or RECOGNIZED (equals ENDED), it is reset to 39274 * POSSIBLE to give it another change on the next cycle. 39275 * 39276 * Possible 39277 * | 39278 * +-----+---------------+ 39279 * | | 39280 * +-----+-----+ | 39281 * | | | 39282 * Failed Cancelled | 39283 * +-------+------+ 39284 * | | 39285 * Recognized Began 39286 * | 39287 * Changed 39288 * | 39289 * Ended/Recognized 39290 */ 39291var STATE_POSSIBLE = 1; 39292var STATE_BEGAN = 2; 39293var STATE_CHANGED = 4; 39294var STATE_ENDED = 8; 39295var STATE_RECOGNIZED = STATE_ENDED; 39296var STATE_CANCELLED = 16; 39297var STATE_FAILED = 32; 39298 39299/** 39300 * Recognizer 39301 * Every recognizer needs to extend from this class. 39302 * @constructor 39303 * @param {Object} options 39304 */ 39305function Recognizer(options) { 39306 this.options = assign({}, this.defaults, options || {}); 39307 39308 this.id = uniqueId(); 39309 39310 this.manager = null; 39311 39312 // default is enable true 39313 this.options.enable = ifUndefined(this.options.enable, true); 39314 39315 this.state = STATE_POSSIBLE; 39316 39317 this.simultaneous = {}; 39318 this.requireFail = []; 39319} 39320 39321Recognizer.prototype = { 39322 /** 39323 * @virtual 39324 * @type {Object} 39325 */ 39326 defaults: {}, 39327 39328 /** 39329 * set options 39330 * @param {Object} options 39331 * @return {Recognizer} 39332 */ 39333 set: function(options) { 39334 assign(this.options, options); 39335 39336 // also update the touchAction, in case something changed about the directions/enabled state 39337 this.manager && this.manager.touchAction.update(); 39338 return this; 39339 }, 39340 39341 /** 39342 * recognize simultaneous with an other recognizer. 39343 * @param {Recognizer} otherRecognizer 39344 * @returns {Recognizer} this 39345 */ 39346 recognizeWith: function(otherRecognizer) { 39347 if (invokeArrayArg(otherRecognizer, 'recognizeWith', this)) { 39348 return this; 39349 } 39350 39351 var simultaneous = this.simultaneous; 39352 otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this); 39353 if (!simultaneous[otherRecognizer.id]) { 39354 simultaneous[otherRecognizer.id] = otherRecognizer; 39355 otherRecognizer.recognizeWith(this); 39356 } 39357 return this; 39358 }, 39359 39360 /** 39361 * drop the simultaneous link. it doesnt remove the link on the other recognizer. 39362 * @param {Recognizer} otherRecognizer 39363 * @returns {Recognizer} this 39364 */ 39365 dropRecognizeWith: function(otherRecognizer) { 39366 if (invokeArrayArg(otherRecognizer, 'dropRecognizeWith', this)) { 39367 return this; 39368 } 39369 39370 otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this); 39371 delete this.simultaneous[otherRecognizer.id]; 39372 return this; 39373 }, 39374 39375 /** 39376 * recognizer can only run when an other is failing 39377 * @param {Recognizer} otherRecognizer 39378 * @returns {Recognizer} this 39379 */ 39380 requireFailure: function(otherRecognizer) { 39381 if (invokeArrayArg(otherRecognizer, 'requireFailure', this)) { 39382 return this; 39383 } 39384 39385 var requireFail = this.requireFail; 39386 otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this); 39387 if (inArray(requireFail, otherRecognizer) === -1) { 39388 requireFail.push(otherRecognizer); 39389 otherRecognizer.requireFailure(this); 39390 } 39391 return this; 39392 }, 39393 39394 /** 39395 * drop the requireFailure link. it does not remove the link on the other recognizer. 39396 * @param {Recognizer} otherRecognizer 39397 * @returns {Recognizer} this 39398 */ 39399 dropRequireFailure: function(otherRecognizer) { 39400 if (invokeArrayArg(otherRecognizer, 'dropRequireFailure', this)) { 39401 return this; 39402 } 39403 39404 otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this); 39405 var index = inArray(this.requireFail, otherRecognizer); 39406 if (index > -1) { 39407 this.requireFail.splice(index, 1); 39408 } 39409 return this; 39410 }, 39411 39412 /** 39413 * has require failures boolean 39414 * @returns {boolean} 39415 */ 39416 hasRequireFailures: function() { 39417 return this.requireFail.length > 0; 39418 }, 39419 39420 /** 39421 * if the recognizer can recognize simultaneous with an other recognizer 39422 * @param {Recognizer} otherRecognizer 39423 * @returns {Boolean} 39424 */ 39425 canRecognizeWith: function(otherRecognizer) { 39426 return !!this.simultaneous[otherRecognizer.id]; 39427 }, 39428 39429 /** 39430 * You should use `tryEmit` instead of `emit` directly to check 39431 * that all the needed recognizers has failed before emitting. 39432 * @param {Object} input 39433 */ 39434 emit: function(input) { 39435 var self = this; 39436 var state = this.state; 39437 39438 function emit(event) { 39439 self.manager.emit(event, input); 39440 } 39441 39442 // 'panstart' and 'panmove' 39443 if (state < STATE_ENDED) { 39444 emit(self.options.event + stateStr(state)); 39445 } 39446 39447 emit(self.options.event); // simple 'eventName' events 39448 39449 if (input.additionalEvent) { // additional event(panleft, panright, pinchin, pinchout...) 39450 emit(input.additionalEvent); 39451 } 39452 39453 // panend and pancancel 39454 if (state >= STATE_ENDED) { 39455 emit(self.options.event + stateStr(state)); 39456 } 39457 }, 39458 39459 /** 39460 * Check that all the require failure recognizers has failed, 39461 * if true, it emits a gesture event, 39462 * otherwise, setup the state to FAILED. 39463 * @param {Object} input 39464 */ 39465 tryEmit: function(input) { 39466 if (this.canEmit()) { 39467 return this.emit(input); 39468 } 39469 // it's failing anyway 39470 this.state = STATE_FAILED; 39471 }, 39472 39473 /** 39474 * can we emit? 39475 * @returns {boolean} 39476 */ 39477 canEmit: function() { 39478 var i = 0; 39479 while (i < this.requireFail.length) { 39480 if (!(this.requireFail[i].state & (STATE_FAILED | STATE_POSSIBLE))) { 39481 return false; 39482 } 39483 i++; 39484 } 39485 return true; 39486 }, 39487 39488 /** 39489 * update the recognizer 39490 * @param {Object} inputData 39491 */ 39492 recognize: function(inputData) { 39493 // make a new copy of the inputData 39494 // so we can change the inputData without messing up the other recognizers 39495 var inputDataClone = assign({}, inputData); 39496 39497 // is is enabled and allow recognizing? 39498 if (!boolOrFn(this.options.enable, [this, inputDataClone])) { 39499 this.reset(); 39500 this.state = STATE_FAILED; 39501 return; 39502 } 39503 39504 // reset when we've reached the end 39505 if (this.state & (STATE_RECOGNIZED | STATE_CANCELLED | STATE_FAILED)) { 39506 this.state = STATE_POSSIBLE; 39507 } 39508 39509 this.state = this.process(inputDataClone); 39510 39511 // the recognizer has recognized a gesture 39512 // so trigger an event 39513 if (this.state & (STATE_BEGAN | STATE_CHANGED | STATE_ENDED | STATE_CANCELLED)) { 39514 this.tryEmit(inputDataClone); 39515 } 39516 }, 39517 39518 /** 39519 * return the state of the recognizer 39520 * the actual recognizing happens in this method 39521 * @virtual 39522 * @param {Object} inputData 39523 * @returns {Const} STATE 39524 */ 39525 process: function(inputData) { }, // jshint ignore:line 39526 39527 /** 39528 * return the preferred touch-action 39529 * @virtual 39530 * @returns {Array} 39531 */ 39532 getTouchAction: function() { }, 39533 39534 /** 39535 * called when the gesture isn't allowed to recognize 39536 * like when another is being recognized or it is disabled 39537 * @virtual 39538 */ 39539 reset: function() { } 39540}; 39541 39542/** 39543 * get a usable string, used as event postfix 39544 * @param {Const} state 39545 * @returns {String} state 39546 */ 39547function stateStr(state) { 39548 if (state & STATE_CANCELLED) { 39549 return 'cancel'; 39550 } else if (state & STATE_ENDED) { 39551 return 'end'; 39552 } else if (state & STATE_CHANGED) { 39553 return 'move'; 39554 } else if (state & STATE_BEGAN) { 39555 return 'start'; 39556 } 39557 return ''; 39558} 39559 39560/** 39561 * direction cons to string 39562 * @param {Const} direction 39563 * @returns {String} 39564 */ 39565function directionStr(direction) { 39566 if (direction == DIRECTION_DOWN) { 39567 return 'down'; 39568 } else if (direction == DIRECTION_UP) { 39569 return 'up'; 39570 } else if (direction == DIRECTION_LEFT) { 39571 return 'left'; 39572 } else if (direction == DIRECTION_RIGHT) { 39573 return 'right'; 39574 } 39575 return ''; 39576} 39577 39578/** 39579 * get a recognizer by name if it is bound to a manager 39580 * @param {Recognizer|String} otherRecognizer 39581 * @param {Recognizer} recognizer 39582 * @returns {Recognizer} 39583 */ 39584function getRecognizerByNameIfManager(otherRecognizer, recognizer) { 39585 var manager = recognizer.manager; 39586 if (manager) { 39587 return manager.get(otherRecognizer); 39588 } 39589 return otherRecognizer; 39590} 39591 39592/** 39593 * This recognizer is just used as a base for the simple attribute recognizers. 39594 * @constructor 39595 * @extends Recognizer 39596 */ 39597function AttrRecognizer() { 39598 Recognizer.apply(this, arguments); 39599} 39600 39601inherit(AttrRecognizer, Recognizer, { 39602 /** 39603 * @namespace 39604 * @memberof AttrRecognizer 39605 */ 39606 defaults: { 39607 /** 39608 * @type {Number} 39609 * @default 1 39610 */ 39611 pointers: 1 39612 }, 39613 39614 /** 39615 * Used to check if it the recognizer receives valid input, like input.distance > 10. 39616 * @memberof AttrRecognizer 39617 * @param {Object} input 39618 * @returns {Boolean} recognized 39619 */ 39620 attrTest: function(input) { 39621 var optionPointers = this.options.pointers; 39622 return optionPointers === 0 || input.pointers.length === optionPointers; 39623 }, 39624 39625 /** 39626 * Process the input and return the state for the recognizer 39627 * @memberof AttrRecognizer 39628 * @param {Object} input 39629 * @returns {*} State 39630 */ 39631 process: function(input) { 39632 var state = this.state; 39633 var eventType = input.eventType; 39634 39635 var isRecognized = state & (STATE_BEGAN | STATE_CHANGED); 39636 var isValid = this.attrTest(input); 39637 39638 // on cancel input and we've recognized before, return STATE_CANCELLED 39639 if (isRecognized && (eventType & INPUT_CANCEL || !isValid)) { 39640 return state | STATE_CANCELLED; 39641 } else if (isRecognized || isValid) { 39642 if (eventType & INPUT_END) { 39643 return state | STATE_ENDED; 39644 } else if (!(state & STATE_BEGAN)) { 39645 return STATE_BEGAN; 39646 } 39647 return state | STATE_CHANGED; 39648 } 39649 return STATE_FAILED; 39650 } 39651}); 39652 39653/** 39654 * Pan 39655 * Recognized when the pointer is down and moved in the allowed direction. 39656 * @constructor 39657 * @extends AttrRecognizer 39658 */ 39659function PanRecognizer() { 39660 AttrRecognizer.apply(this, arguments); 39661 39662 this.pX = null; 39663 this.pY = null; 39664} 39665 39666inherit(PanRecognizer, AttrRecognizer, { 39667 /** 39668 * @namespace 39669 * @memberof PanRecognizer 39670 */ 39671 defaults: { 39672 event: 'pan', 39673 threshold: 10, 39674 pointers: 1, 39675 direction: DIRECTION_ALL 39676 }, 39677 39678 getTouchAction: function() { 39679 var direction = this.options.direction; 39680 var actions = []; 39681 if (direction & DIRECTION_HORIZONTAL) { 39682 actions.push(TOUCH_ACTION_PAN_Y); 39683 } 39684 if (direction & DIRECTION_VERTICAL) { 39685 actions.push(TOUCH_ACTION_PAN_X); 39686 } 39687 return actions; 39688 }, 39689 39690 directionTest: function(input) { 39691 var options = this.options; 39692 var hasMoved = true; 39693 var distance = input.distance; 39694 var direction = input.direction; 39695 var x = input.deltaX; 39696 var y = input.deltaY; 39697 39698 // lock to axis? 39699 if (!(direction & options.direction)) { 39700 if (options.direction & DIRECTION_HORIZONTAL) { 39701 direction = (x === 0) ? DIRECTION_NONE : (x < 0) ? DIRECTION_LEFT : DIRECTION_RIGHT; 39702 hasMoved = x != this.pX; 39703 distance = Math.abs(input.deltaX); 39704 } else { 39705 direction = (y === 0) ? DIRECTION_NONE : (y < 0) ? DIRECTION_UP : DIRECTION_DOWN; 39706 hasMoved = y != this.pY; 39707 distance = Math.abs(input.deltaY); 39708 } 39709 } 39710 input.direction = direction; 39711 return hasMoved && distance > options.threshold && direction & options.direction; 39712 }, 39713 39714 attrTest: function(input) { 39715 return AttrRecognizer.prototype.attrTest.call(this, input) && 39716 (this.state & STATE_BEGAN || (!(this.state & STATE_BEGAN) && this.directionTest(input))); 39717 }, 39718 39719 emit: function(input) { 39720 39721 this.pX = input.deltaX; 39722 this.pY = input.deltaY; 39723 39724 var direction = directionStr(input.direction); 39725 39726 if (direction) { 39727 input.additionalEvent = this.options.event + direction; 39728 } 39729 this._super.emit.call(this, input); 39730 } 39731}); 39732 39733/** 39734 * Pinch 39735 * Recognized when two or more pointers are moving toward (zoom-in) or away from each other (zoom-out). 39736 * @constructor 39737 * @extends AttrRecognizer 39738 */ 39739function PinchRecognizer() { 39740 AttrRecognizer.apply(this, arguments); 39741} 39742 39743inherit(PinchRecognizer, AttrRecognizer, { 39744 /** 39745 * @namespace 39746 * @memberof PinchRecognizer 39747 */ 39748 defaults: { 39749 event: 'pinch', 39750 threshold: 0, 39751 pointers: 2 39752 }, 39753 39754 getTouchAction: function() { 39755 return [TOUCH_ACTION_NONE]; 39756 }, 39757 39758 attrTest: function(input) { 39759 return this._super.attrTest.call(this, input) && 39760 (Math.abs(input.scale - 1) > this.options.threshold || this.state & STATE_BEGAN); 39761 }, 39762 39763 emit: function(input) { 39764 if (input.scale !== 1) { 39765 var inOut = input.scale < 1 ? 'in' : 'out'; 39766 input.additionalEvent = this.options.event + inOut; 39767 } 39768 this._super.emit.call(this, input); 39769 } 39770}); 39771 39772/** 39773 * Press 39774 * Recognized when the pointer is down for x ms without any movement. 39775 * @constructor 39776 * @extends Recognizer 39777 */ 39778function PressRecognizer() { 39779 Recognizer.apply(this, arguments); 39780 39781 this._timer = null; 39782 this._input = null; 39783} 39784 39785inherit(PressRecognizer, Recognizer, { 39786 /** 39787 * @namespace 39788 * @memberof PressRecognizer 39789 */ 39790 defaults: { 39791 event: 'press', 39792 pointers: 1, 39793 time: 251, // minimal time of the pointer to be pressed 39794 threshold: 9 // a minimal movement is ok, but keep it low 39795 }, 39796 39797 getTouchAction: function() { 39798 return [TOUCH_ACTION_AUTO]; 39799 }, 39800 39801 process: function(input) { 39802 var options = this.options; 39803 var validPointers = input.pointers.length === options.pointers; 39804 var validMovement = input.distance < options.threshold; 39805 var validTime = input.deltaTime > options.time; 39806 39807 this._input = input; 39808 39809 // we only allow little movement 39810 // and we've reached an end event, so a tap is possible 39811 if (!validMovement || !validPointers || (input.eventType & (INPUT_END | INPUT_CANCEL) && !validTime)) { 39812 this.reset(); 39813 } else if (input.eventType & INPUT_START) { 39814 this.reset(); 39815 this._timer = setTimeoutContext(function() { 39816 this.state = STATE_RECOGNIZED; 39817 this.tryEmit(); 39818 }, options.time, this); 39819 } else if (input.eventType & INPUT_END) { 39820 return STATE_RECOGNIZED; 39821 } 39822 return STATE_FAILED; 39823 }, 39824 39825 reset: function() { 39826 clearTimeout(this._timer); 39827 }, 39828 39829 emit: function(input) { 39830 if (this.state !== STATE_RECOGNIZED) { 39831 return; 39832 } 39833 39834 if (input && (input.eventType & INPUT_END)) { 39835 this.manager.emit(this.options.event + 'up', input); 39836 } else { 39837 this._input.timeStamp = now(); 39838 this.manager.emit(this.options.event, this._input); 39839 } 39840 } 39841}); 39842 39843/** 39844 * Rotate 39845 * Recognized when two or more pointer are moving in a circular motion. 39846 * @constructor 39847 * @extends AttrRecognizer 39848 */ 39849function RotateRecognizer() { 39850 AttrRecognizer.apply(this, arguments); 39851} 39852 39853inherit(RotateRecognizer, AttrRecognizer, { 39854 /** 39855 * @namespace 39856 * @memberof RotateRecognizer 39857 */ 39858 defaults: { 39859 event: 'rotate', 39860 threshold: 0, 39861 pointers: 2 39862 }, 39863 39864 getTouchAction: function() { 39865 return [TOUCH_ACTION_NONE]; 39866 }, 39867 39868 attrTest: function(input) { 39869 return this._super.attrTest.call(this, input) && 39870 (Math.abs(input.rotation) > this.options.threshold || this.state & STATE_BEGAN); 39871 } 39872}); 39873 39874/** 39875 * Swipe 39876 * Recognized when the pointer is moving fast (velocity), with enough distance in the allowed direction. 39877 * @constructor 39878 * @extends AttrRecognizer 39879 */ 39880function SwipeRecognizer() { 39881 AttrRecognizer.apply(this, arguments); 39882} 39883 39884inherit(SwipeRecognizer, AttrRecognizer, { 39885 /** 39886 * @namespace 39887 * @memberof SwipeRecognizer 39888 */ 39889 defaults: { 39890 event: 'swipe', 39891 threshold: 10, 39892 velocity: 0.3, 39893 direction: DIRECTION_HORIZONTAL | DIRECTION_VERTICAL, 39894 pointers: 1 39895 }, 39896 39897 getTouchAction: function() { 39898 return PanRecognizer.prototype.getTouchAction.call(this); 39899 }, 39900 39901 attrTest: function(input) { 39902 var direction = this.options.direction; 39903 var velocity; 39904 39905 if (direction & (DIRECTION_HORIZONTAL | DIRECTION_VERTICAL)) { 39906 velocity = input.overallVelocity; 39907 } else if (direction & DIRECTION_HORIZONTAL) { 39908 velocity = input.overallVelocityX; 39909 } else if (direction & DIRECTION_VERTICAL) { 39910 velocity = input.overallVelocityY; 39911 } 39912 39913 return this._super.attrTest.call(this, input) && 39914 direction & input.offsetDirection && 39915 input.distance > this.options.threshold && 39916 input.maxPointers == this.options.pointers && 39917 abs(velocity) > this.options.velocity && input.eventType & INPUT_END; 39918 }, 39919 39920 emit: function(input) { 39921 var direction = directionStr(input.offsetDirection); 39922 if (direction) { 39923 this.manager.emit(this.options.event + direction, input); 39924 } 39925 39926 this.manager.emit(this.options.event, input); 39927 } 39928}); 39929 39930/** 39931 * A tap is ecognized when the pointer is doing a small tap/click. Multiple taps are recognized if they occur 39932 * between the given interval and position. The delay option can be used to recognize multi-taps without firing 39933 * a single tap. 39934 * 39935 * The eventData from the emitted event contains the property `tapCount`, which contains the amount of 39936 * multi-taps being recognized. 39937 * @constructor 39938 * @extends Recognizer 39939 */ 39940function TapRecognizer() { 39941 Recognizer.apply(this, arguments); 39942 39943 // previous time and center, 39944 // used for tap counting 39945 this.pTime = false; 39946 this.pCenter = false; 39947 39948 this._timer = null; 39949 this._input = null; 39950 this.count = 0; 39951} 39952 39953inherit(TapRecognizer, Recognizer, { 39954 /** 39955 * @namespace 39956 * @memberof PinchRecognizer 39957 */ 39958 defaults: { 39959 event: 'tap', 39960 pointers: 1, 39961 taps: 1, 39962 interval: 300, // max time between the multi-tap taps 39963 time: 250, // max time of the pointer to be down (like finger on the screen) 39964 threshold: 9, // a minimal movement is ok, but keep it low 39965 posThreshold: 10 // a multi-tap can be a bit off the initial position 39966 }, 39967 39968 getTouchAction: function() { 39969 return [TOUCH_ACTION_MANIPULATION]; 39970 }, 39971 39972 process: function(input) { 39973 var options = this.options; 39974 39975 var validPointers = input.pointers.length === options.pointers; 39976 var validMovement = input.distance < options.threshold; 39977 var validTouchTime = input.deltaTime < options.time; 39978 39979 this.reset(); 39980 39981 if ((input.eventType & INPUT_START) && (this.count === 0)) { 39982 return this.failTimeout(); 39983 } 39984 39985 // we only allow little movement 39986 // and we've reached an end event, so a tap is possible 39987 if (validMovement && validTouchTime && validPointers) { 39988 if (input.eventType != INPUT_END) { 39989 return this.failTimeout(); 39990 } 39991 39992 var validInterval = this.pTime ? (input.timeStamp - this.pTime < options.interval) : true; 39993 var validMultiTap = !this.pCenter || getDistance(this.pCenter, input.center) < options.posThreshold; 39994 39995 this.pTime = input.timeStamp; 39996 this.pCenter = input.center; 39997 39998 if (!validMultiTap || !validInterval) { 39999 this.count = 1; 40000 } else { 40001 this.count += 1; 40002 } 40003 40004 this._input = input; 40005 40006 // if tap count matches we have recognized it, 40007 // else it has began recognizing... 40008 var tapCount = this.count % options.taps; 40009 if (tapCount === 0) { 40010 // no failing requirements, immediately trigger the tap event 40011 // or wait as long as the multitap interval to trigger 40012 if (!this.hasRequireFailures()) { 40013 return STATE_RECOGNIZED; 40014 } else { 40015 this._timer = setTimeoutContext(function() { 40016 this.state = STATE_RECOGNIZED; 40017 this.tryEmit(); 40018 }, options.interval, this); 40019 return STATE_BEGAN; 40020 } 40021 } 40022 } 40023 return STATE_FAILED; 40024 }, 40025 40026 failTimeout: function() { 40027 this._timer = setTimeoutContext(function() { 40028 this.state = STATE_FAILED; 40029 }, this.options.interval, this); 40030 return STATE_FAILED; 40031 }, 40032 40033 reset: function() { 40034 clearTimeout(this._timer); 40035 }, 40036 40037 emit: function() { 40038 if (this.state == STATE_RECOGNIZED) { 40039 this._input.tapCount = this.count; 40040 this.manager.emit(this.options.event, this._input); 40041 } 40042 } 40043}); 40044 40045/** 40046 * Simple way to create a manager with a default set of recognizers. 40047 * @param {HTMLElement} element 40048 * @param {Object} [options] 40049 * @constructor 40050 */ 40051function Hammer(element, options) { 40052 options = options || {}; 40053 options.recognizers = ifUndefined(options.recognizers, Hammer.defaults.preset); 40054 return new Manager(element, options); 40055} 40056 40057/** 40058 * @const {string} 40059 */ 40060Hammer.VERSION = '2.0.7'; 40061 40062/** 40063 * default settings 40064 * @namespace 40065 */ 40066Hammer.defaults = { 40067 /** 40068 * set if DOM events are being triggered. 40069 * But this is slower and unused by simple implementations, so disabled by default. 40070 * @type {Boolean} 40071 * @default false 40072 */ 40073 domEvents: false, 40074 40075 /** 40076 * The value for the touchAction property/fallback. 40077 * When set to `compute` it will magically set the correct value based on the added recognizers. 40078 * @type {String} 40079 * @default compute 40080 */ 40081 touchAction: TOUCH_ACTION_COMPUTE, 40082 40083 /** 40084 * @type {Boolean} 40085 * @default true 40086 */ 40087 enable: true, 40088 40089 /** 40090 * EXPERIMENTAL FEATURE -- can be removed/changed 40091 * Change the parent input target element. 40092 * If Null, then it is being set the to main element. 40093 * @type {Null|EventTarget} 40094 * @default null 40095 */ 40096 inputTarget: null, 40097 40098 /** 40099 * force an input class 40100 * @type {Null|Function} 40101 * @default null 40102 */ 40103 inputClass: null, 40104 40105 /** 40106 * Default recognizer setup when calling `Hammer()` 40107 * When creating a new Manager these will be skipped. 40108 * @type {Array} 40109 */ 40110 preset: [ 40111 // RecognizerClass, options, [recognizeWith, ...], [requireFailure, ...] 40112 [RotateRecognizer, {enable: false}], 40113 [PinchRecognizer, {enable: false}, ['rotate']], 40114 [SwipeRecognizer, {direction: DIRECTION_HORIZONTAL}], 40115 [PanRecognizer, {direction: DIRECTION_HORIZONTAL}, ['swipe']], 40116 [TapRecognizer], 40117 [TapRecognizer, {event: 'doubletap', taps: 2}, ['tap']], 40118 [PressRecognizer] 40119 ], 40120 40121 /** 40122 * Some CSS properties can be used to improve the working of Hammer. 40123 * Add them to this method and they will be set when creating a new Manager. 40124 * @namespace 40125 */ 40126 cssProps: { 40127 /** 40128 * Disables text selection to improve the dragging gesture. Mainly for desktop browsers. 40129 * @type {String} 40130 * @default 'none' 40131 */ 40132 userSelect: 'none', 40133 40134 /** 40135 * Disable the Windows Phone grippers when pressing an element. 40136 * @type {String} 40137 * @default 'none' 40138 */ 40139 touchSelect: 'none', 40140 40141 /** 40142 * Disables the default callout shown when you touch and hold a touch target. 40143 * On iOS, when you touch and hold a touch target such as a link, Safari displays 40144 * a callout containing information about the link. This property allows you to disable that callout. 40145 * @type {String} 40146 * @default 'none' 40147 */ 40148 touchCallout: 'none', 40149 40150 /** 40151 * Specifies whether zooming is enabled. Used by IE10> 40152 * @type {String} 40153 * @default 'none' 40154 */ 40155 contentZooming: 'none', 40156 40157 /** 40158 * Specifies that an entire element should be draggable instead of its contents. Mainly for desktop browsers. 40159 * @type {String} 40160 * @default 'none' 40161 */ 40162 userDrag: 'none', 40163 40164 /** 40165 * Overrides the highlight color shown when the user taps a link or a JavaScript 40166 * clickable element in iOS. This property obeys the alpha value, if specified. 40167 * @type {String} 40168 * @default 'rgba(0,0,0,0)' 40169 */ 40170 tapHighlightColor: 'rgba(0,0,0,0)' 40171 } 40172}; 40173 40174var STOP = 1; 40175var FORCED_STOP = 2; 40176 40177/** 40178 * Manager 40179 * @param {HTMLElement} element 40180 * @param {Object} [options] 40181 * @constructor 40182 */ 40183function Manager(element, options) { 40184 this.options = assign({}, Hammer.defaults, options || {}); 40185 40186 this.options.inputTarget = this.options.inputTarget || element; 40187 40188 this.handlers = {}; 40189 this.session = {}; 40190 this.recognizers = []; 40191 this.oldCssProps = {}; 40192 40193 this.element = element; 40194 this.input = createInputInstance(this); 40195 this.touchAction = new TouchAction(this, this.options.touchAction); 40196 40197 toggleCssProps(this, true); 40198 40199 each(this.options.recognizers, function(item) { 40200 var recognizer = this.add(new (item[0])(item[1])); 40201 item[2] && recognizer.recognizeWith(item[2]); 40202 item[3] && recognizer.requireFailure(item[3]); 40203 }, this); 40204} 40205 40206Manager.prototype = { 40207 /** 40208 * set options 40209 * @param {Object} options 40210 * @returns {Manager} 40211 */ 40212 set: function(options) { 40213 assign(this.options, options); 40214 40215 // Options that need a little more setup 40216 if (options.touchAction) { 40217 this.touchAction.update(); 40218 } 40219 if (options.inputTarget) { 40220 // Clean up existing event listeners and reinitialize 40221 this.input.destroy(); 40222 this.input.target = options.inputTarget; 40223 this.input.init(); 40224 } 40225 return this; 40226 }, 40227 40228 /** 40229 * stop recognizing for this session. 40230 * This session will be discarded, when a new [input]start event is fired. 40231 * When forced, the recognizer cycle is stopped immediately. 40232 * @param {Boolean} [force] 40233 */ 40234 stop: function(force) { 40235 this.session.stopped = force ? FORCED_STOP : STOP; 40236 }, 40237 40238 /** 40239 * run the recognizers! 40240 * called by the inputHandler function on every movement of the pointers (touches) 40241 * it walks through all the recognizers and tries to detect the gesture that is being made 40242 * @param {Object} inputData 40243 */ 40244 recognize: function(inputData) { 40245 var session = this.session; 40246 if (session.stopped) { 40247 return; 40248 } 40249 40250 // run the touch-action polyfill 40251 this.touchAction.preventDefaults(inputData); 40252 40253 var recognizer; 40254 var recognizers = this.recognizers; 40255 40256 // this holds the recognizer that is being recognized. 40257 // so the recognizer's state needs to be BEGAN, CHANGED, ENDED or RECOGNIZED 40258 // if no recognizer is detecting a thing, it is set to `null` 40259 var curRecognizer = session.curRecognizer; 40260 40261 // reset when the last recognizer is recognized 40262 // or when we're in a new session 40263 if (!curRecognizer || (curRecognizer && curRecognizer.state & STATE_RECOGNIZED)) { 40264 curRecognizer = session.curRecognizer = null; 40265 } 40266 40267 var i = 0; 40268 while (i < recognizers.length) { 40269 recognizer = recognizers[i]; 40270 40271 // find out if we are allowed try to recognize the input for this one. 40272 // 1. allow if the session is NOT forced stopped (see the .stop() method) 40273 // 2. allow if we still haven't recognized a gesture in this session, or the this recognizer is the one 40274 // that is being recognized. 40275 // 3. allow if the recognizer is allowed to run simultaneous with the current recognized recognizer. 40276 // this can be setup with the `recognizeWith()` method on the recognizer. 40277 if (session.stopped !== FORCED_STOP && ( // 1 40278 !curRecognizer || recognizer == curRecognizer || // 2 40279 recognizer.canRecognizeWith(curRecognizer))) { // 3 40280 recognizer.recognize(inputData); 40281 } else { 40282 recognizer.reset(); 40283 } 40284 40285 // if the recognizer has been recognizing the input as a valid gesture, we want to store this one as the 40286 // current active recognizer. but only if we don't already have an active recognizer 40287 if (!curRecognizer && recognizer.state & (STATE_BEGAN | STATE_CHANGED | STATE_ENDED)) { 40288 curRecognizer = session.curRecognizer = recognizer; 40289 } 40290 i++; 40291 } 40292 }, 40293 40294 /** 40295 * get a recognizer by its event name. 40296 * @param {Recognizer|String} recognizer 40297 * @returns {Recognizer|Null} 40298 */ 40299 get: function(recognizer) { 40300 if (recognizer instanceof Recognizer) { 40301 return recognizer; 40302 } 40303 40304 var recognizers = this.recognizers; 40305 for (var i = 0; i < recognizers.length; i++) { 40306 if (recognizers[i].options.event == recognizer) { 40307 return recognizers[i]; 40308 } 40309 } 40310 return null; 40311 }, 40312 40313 /** 40314 * add a recognizer to the manager 40315 * existing recognizers with the same event name will be removed 40316 * @param {Recognizer} recognizer 40317 * @returns {Recognizer|Manager} 40318 */ 40319 add: function(recognizer) { 40320 if (invokeArrayArg(recognizer, 'add', this)) { 40321 return this; 40322 } 40323 40324 // remove existing 40325 var existing = this.get(recognizer.options.event); 40326 if (existing) { 40327 this.remove(existing); 40328 } 40329 40330 this.recognizers.push(recognizer); 40331 recognizer.manager = this; 40332 40333 this.touchAction.update(); 40334 return recognizer; 40335 }, 40336 40337 /** 40338 * remove a recognizer by name or instance 40339 * @param {Recognizer|String} recognizer 40340 * @returns {Manager} 40341 */ 40342 remove: function(recognizer) { 40343 if (invokeArrayArg(recognizer, 'remove', this)) { 40344 return this; 40345 } 40346 40347 recognizer = this.get(recognizer); 40348 40349 // let's make sure this recognizer exists 40350 if (recognizer) { 40351 var recognizers = this.recognizers; 40352 var index = inArray(recognizers, recognizer); 40353 40354 if (index !== -1) { 40355 recognizers.splice(index, 1); 40356 this.touchAction.update(); 40357 } 40358 } 40359 40360 return this; 40361 }, 40362 40363 /** 40364 * bind event 40365 * @param {String} events 40366 * @param {Function} handler 40367 * @returns {EventEmitter} this 40368 */ 40369 on: function(events, handler) { 40370 if (events === undefined) { 40371 return; 40372 } 40373 if (handler === undefined) { 40374 return; 40375 } 40376 40377 var handlers = this.handlers; 40378 each(splitStr(events), function(event) { 40379 handlers[event] = handlers[event] || []; 40380 handlers[event].push(handler); 40381 }); 40382 return this; 40383 }, 40384 40385 /** 40386 * unbind event, leave emit blank to remove all handlers 40387 * @param {String} events 40388 * @param {Function} [handler] 40389 * @returns {EventEmitter} this 40390 */ 40391 off: function(events, handler) { 40392 if (events === undefined) { 40393 return; 40394 } 40395 40396 var handlers = this.handlers; 40397 each(splitStr(events), function(event) { 40398 if (!handler) { 40399 delete handlers[event]; 40400 } else { 40401 handlers[event] && handlers[event].splice(inArray(handlers[event], handler), 1); 40402 } 40403 }); 40404 return this; 40405 }, 40406 40407 /** 40408 * emit event to the listeners 40409 * @param {String} event 40410 * @param {Object} data 40411 */ 40412 emit: function(event, data) { 40413 // we also want to trigger dom events 40414 if (this.options.domEvents) { 40415 triggerDomEvent(event, data); 40416 } 40417 40418 // no handlers, so skip it all 40419 var handlers = this.handlers[event] && this.handlers[event].slice(); 40420 if (!handlers || !handlers.length) { 40421 return; 40422 } 40423 40424 data.type = event; 40425 data.preventDefault = function() { 40426 data.srcEvent.preventDefault(); 40427 }; 40428 40429 var i = 0; 40430 while (i < handlers.length) { 40431 handlers[i](data); 40432 i++; 40433 } 40434 }, 40435 40436 /** 40437 * destroy the manager and unbinds all events 40438 * it doesn't unbind dom events, that is the user own responsibility 40439 */ 40440 destroy: function() { 40441 this.element && toggleCssProps(this, false); 40442 40443 this.handlers = {}; 40444 this.session = {}; 40445 this.input.destroy(); 40446 this.element = null; 40447 } 40448}; 40449 40450/** 40451 * add/remove the css properties as defined in manager.options.cssProps 40452 * @param {Manager} manager 40453 * @param {Boolean} add 40454 */ 40455function toggleCssProps(manager, add) { 40456 var element = manager.element; 40457 if (!element.style) { 40458 return; 40459 } 40460 var prop; 40461 each(manager.options.cssProps, function(value, name) { 40462 prop = prefixed(element.style, name); 40463 if (add) { 40464 manager.oldCssProps[prop] = element.style[prop]; 40465 element.style[prop] = value; 40466 } else { 40467 element.style[prop] = manager.oldCssProps[prop] || ''; 40468 } 40469 }); 40470 if (!add) { 40471 manager.oldCssProps = {}; 40472 } 40473} 40474 40475/** 40476 * trigger dom event 40477 * @param {String} event 40478 * @param {Object} data 40479 */ 40480function triggerDomEvent(event, data) { 40481 var gestureEvent = document.createEvent('Event'); 40482 gestureEvent.initEvent(event, true, true); 40483 gestureEvent.gesture = data; 40484 data.target.dispatchEvent(gestureEvent); 40485} 40486 40487assign(Hammer, { 40488 INPUT_START: INPUT_START, 40489 INPUT_MOVE: INPUT_MOVE, 40490 INPUT_END: INPUT_END, 40491 INPUT_CANCEL: INPUT_CANCEL, 40492 40493 STATE_POSSIBLE: STATE_POSSIBLE, 40494 STATE_BEGAN: STATE_BEGAN, 40495 STATE_CHANGED: STATE_CHANGED, 40496 STATE_ENDED: STATE_ENDED, 40497 STATE_RECOGNIZED: STATE_RECOGNIZED, 40498 STATE_CANCELLED: STATE_CANCELLED, 40499 STATE_FAILED: STATE_FAILED, 40500 40501 DIRECTION_NONE: DIRECTION_NONE, 40502 DIRECTION_LEFT: DIRECTION_LEFT, 40503 DIRECTION_RIGHT: DIRECTION_RIGHT, 40504 DIRECTION_UP: DIRECTION_UP, 40505 DIRECTION_DOWN: DIRECTION_DOWN, 40506 DIRECTION_HORIZONTAL: DIRECTION_HORIZONTAL, 40507 DIRECTION_VERTICAL: DIRECTION_VERTICAL, 40508 DIRECTION_ALL: DIRECTION_ALL, 40509 40510 Manager: Manager, 40511 Input: Input, 40512 TouchAction: TouchAction, 40513 40514 TouchInput: TouchInput, 40515 MouseInput: MouseInput, 40516 PointerEventInput: PointerEventInput, 40517 TouchMouseInput: TouchMouseInput, 40518 SingleTouchInput: SingleTouchInput, 40519 40520 Recognizer: Recognizer, 40521 AttrRecognizer: AttrRecognizer, 40522 Tap: TapRecognizer, 40523 Pan: PanRecognizer, 40524 Swipe: SwipeRecognizer, 40525 Pinch: PinchRecognizer, 40526 Rotate: RotateRecognizer, 40527 Press: PressRecognizer, 40528 40529 on: addEventListeners, 40530 off: removeEventListeners, 40531 each: each, 40532 merge: merge, 40533 extend: extend, 40534 assign: assign, 40535 inherit: inherit, 40536 bindFn: bindFn, 40537 prefixed: prefixed 40538}); 40539 40540// this prevents errors when Hammer is loaded in the presence of an AMD 40541// style loader but by script tag, not by the loader. 40542var freeGlobal = (typeof window !== 'undefined' ? window : (typeof self !== 'undefined' ? self : {})); // jshint ignore:line 40543freeGlobal.Hammer = Hammer; 40544 40545if (t
40545rue) { 40546 !(__WEBPACK_AMD_DEFINE_RESULT__ = function() { 40547 return Hammer; 40548 }.call(exports, __webpack_require__, exports, module), 40549 __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); 40550} else if (typeof module != 'undefined' && module.exports) { 40551 module.exports = Hammer; 40552} else { 40553 window[exportName] = Hammer; 40554} 40555 40556})(window, document, 'Hammer'); 40557 40558 40559/***/ }), 40560/* 177 */ 40561/***/ (function(module, exports, __webpack_require__) { 40562 40563"use strict"; 40564 40565 40566// utils 40567exports.util = __webpack_require__(2); 40568exports.DOMutil = __webpack_require__(14); 40569 40570// data 40571exports.DataSet = __webpack_require__(11); 40572exports.DataView = __webpack_require__(12); 40573exports.Queue = __webpack_require__(43); 40574 40575// Timeline 40576exports.Timeline = __webpack_require__(178); 40577exports.Graph2d = __webpack_require__(180); 40578exports.timeline = { 40579 Core: __webpack_require__(65), 40580 DateUtil: __webpack_require__(36), 40581 Range: __webpack_require__(64), 40582 stack: __webpack_require__(100), 40583 TimeStep: __webpack_require__(66), 40584 40585 components: { 40586 items: { 40587 Item: __webpack_require__(38), 40588 BackgroundItem: __webpack_require__(103), 40589 BoxItem: __webpack_require__(101), 40590 PointItem: __webpack_require__(102), 40591 RangeItem: __webpack_require__(70) 40592 }, 40593 40594 BackgroundGroup: __webpack_require__(69), 40595 Component: __webpack_require__(16), 40596 CurrentTime: __webpack_require__(67), 40597 CustomTime: __webpack_require__(46), 40598 DataAxis: __webpack_require__(107), 40599 DataScale: __webpack_require__(108), 40600 GraphGroup: __webpack_require__(109), 40601 Group: __webpack_require__(68), 40602 ItemSet: __webpack_require__(99), 40603 Legend: __webpack_require__(112), 40604 LineGraph: __webpack_require__(106), 40605 TimeAxis: __webpack_require__(45) 40606 } 40607}; 40608 40609// bundled external libraries 40610exports.moment = __webpack_require__(9); 40611exports.Hammer = __webpack_require__(10); 40612exports.keycharm = __webpack_require__(35); 40613 40614/***/ }), 40615/* 178 */ 40616/***/ (function(module, exports, __webpack_require__) { 40617 40618"use strict"; 40619 40620 40621var moment = __webpack_require__(9); 40622var util = __webpack_require__(2); 40623var DataSet = __webpack_require__(11); 40624var DataView = __webpack_require__(12); 40625var Range = __webpack_require__(64); 40626var Core = __webpack_require__(65);
40627var TimeAxis = __webpack_require__(45); 40628var CurrentTime = __webpack_require__(67); 40629var CustomTime = __webpack_require__(46); 40630var ItemSet = __webpack_require__(99); 40631 40632var printStyle = __webpack_require__(15).printStyle; 40633var allOptions = __webpack_require__(105).allOptions; 40634var configureOptions = __webpack_require__(105).configureOptions; 40635 40636var Configurator = __webpack_require__(71)['default']; 40637var Validator = __webpack_require__(15)['default']; 40638 40639/** 40640 * Create a timeline visualization 40641 * @param {HTMLElement} container 40642 * @param {vis.DataSet | vis.DataView | Array} [items] 40643 * @param {vis.DataSet | vis.DataView | Array} [groups] 40644 * @param {Object} [options] See Timeline.setOptions for the available options. 40645 * @constructor Timeline 40646 * @extends Core 40647 */ 40648function Timeline(container, items, groups, options) { 40649 40650 if (!(this instanceof Timeline)) { 40651 throw new SyntaxError('Constructor must be called with the new operator'); 40652 } 40653 40654 // if the third element is options, the forth is groups (optionally); 40655 if (!(Array.isArray(groups) || groups instanceof DataSet || groups instanceof DataView) && groups instanceof Object) { 40656 var forthArgument = options; 40657 options = groups; 40658 groups = forthArgument; 40659 } 40660 40661 // TODO: REMOVE THIS in the next MAJOR release 40662 // see https://github.com/almende/vis/issues/2511 40663 if (options && options.throttleRedraw) { 40664 console.warn("Timeline option \"throttleRedraw\" is DEPRICATED and no longer supported. It will be removed in the next MAJOR release."); 40665 } 40666 40667 var me = this; 40668 this.defaultOptions = { 40669 start: null, 40670 end: null, 40671 autoResize: true, 40672 orientation: { 40673 axis: 'bottom', // axis orientation: 'bottom', 'top', or 'both' 40674 item: 'bottom' // not relevant 40675 }, 40676 moment: moment, 40677 width: null, 40678 height: null, 40679 maxHeight: null, 40680 minHeight: null 40681 }; 40682 this.options = util.deepExtend({}, this.defaultOptions); 40683 40684 // Create the DOM, props, and emitter 40685 this._create(container); 40686 if (!options || options && typeof options.rtl == "undefined") { 40687 this.dom.root.style.visibility = 'hidden'; 40688 var directionFromDom, 40689 domNode = this.dom.root; 40690 while (!directionFromDom && domNode) { 40691 directionFromDom = window.getComputedStyle(domNode, null).direction; 40692 domNode = domNode.parentElement; 40693 } 40694 this.options.rtl = directionFromDom && directionFromDom.toLowerCase() == "rtl"; 40695 } else { 40696 this.options.rtl = options.rtl; 40697 } 40698 40699 this.options.rollingMode = options && options.rollingMode; 40700 this.options.onInitialDrawComplete = options && options.onInitialDrawComplete; 40701 40702 // all components listed here will be repainted automatically 40703 this.components = []; 40704 40705 this.body = { 40706 dom: this.dom, 40707 domProps: this.props, 40708 emitter: { 40709 on: this.on.bind(this), 40710 off: this.off.bind(this), 40711 emit: this.emit.bind(this) 40712 }, 40713 hiddenDates: [], 40714 util: { 40715 getScale: function getScale() { 40716 return me.timeAxis.step.scale; 40717 }, 40718 getStep: function getStep() { 40719 return me.timeAxis.step.step; 40720 }, 40721 40722 toScreen: me._toScreen.bind(me), 40723 toGlobalScreen: me._toGlobalScreen.bind(me), // this refers to the root.width 40724 toTime: me._toTime.bind(me), 40725 toGlobalTime: me._toGlobalTime.bind(me) 40726 } 40727 }; 40728 40729 // range 40730 this.range = new Range(this.body, this.options); 40731 this.components.push(this.range); 40732 this.body.range = this.range; 40733 40734 // time axis 40735 this.timeAxis = new TimeAxis(this.body, this.options); 40736 this.timeAxis2 = null; // used in case of orientation option 'both' 40737 this.components.push(this.timeAxis); 40738 40739 // current time bar 40740 this.currentTime = new CurrentTime(this.body, this.options); 40741 this.components.push(this.currentTime); 40742 40743 // item set 40744 this.itemSet = new ItemSet(this.body, this.options); 40745 this.components.push(this.itemSet); 40746 40747 this.itemsData = null; // DataSet 40748 this.groupsData = null; // DataSet 40749 40750 this.dom.root.onclick = function (event) { 40751 me.emit('click', me.getEventProperties(event)); 40752 }; 40753 this.dom.root.ondblclick = function (event) { 40754 me.emit('doubleClick', me.getEventProperties(event)); 40755 }; 40756 this.dom.root.oncontextmenu = function (event) { 40757 me.emit('contextmenu', me.getEventProperties(event)); 40758 }; 40759 this.dom.root.onmouseover = function (event) { 40760 me.emit('mouseOver', me.getEventProperties(event)); 40761 }; 40762 if (window.PointerEvent) { 40763 this.dom.root.onpointerdown = function (event) { 40764 me.emit('mouseDown', me.getEventProperties(event)); 40765 }; 40766 this.dom.root.onpointermove = function (event) { 40767 me.emit('mouseMove', me.getEventProperties(event)); 40768 }; 40769 this.dom.root.onpointerup = function (event) { 40770 me.emit('mouseUp', me.getEventProperties(event)); 40771 }; 40772 } else { 40773 this.dom.root.onmousemove = function (event) { 40774 me.emit('mouseMove', me.getEventProperties(event)); 40775 }; 40776 this.dom.root.onmousedown = function (event) { 40777 me.emit('mouseDown', me.getEventProperties(event)); 40778 }; 40779 this.dom.root.onmouseup = function (event) { 40780 me.emit('mouseUp', me.getEventProperties(event)); 40781 }; 40782 } 40783 40784 //Single time autoscale/fit 40785 this.initialFitDone = false;
40786 this.on('changed', function () { 40787 if (this.itemsData == null || this.options.rollingMode) return; 40788 if (!me.initialFitDone) { 40789 me.initialFitDone = true; 40790 if (me.options.start != undefined || me.options.end != undefined) { 40791 if (me.options.start == undefined || me.options.end == undefined) { 40792 var range = me.getItemRange(); 40793 } 40794 40795 var start = me.options.start != undefined ? me.options.start : range.min; 40796 var end = me.options.end != undefined ? me.options.end : range.max; 40797 me.setWindow(start, end, { animation: false }); 40798 } else { 40799 me.fit({ animation: false }); 40800 } 40801 } 40802 40803 if (!me.initialDrawDone && me.initialRangeChangeDone) { 40804 me.initialDrawDone = true; 40805 me.dom.root.style.visibility = 'visible'; 40806 if (me.options.onInitialDrawComplete) { 40807 setTimeout(function () { 40808 return me.options.onInitialDrawComplete(); 40809 }, 0); 40810 } 40811 } 40812 }); 40813 40814 // apply options 40815 if (options) { 40816 this.setOptions(options); 40817 } 40818 40819 // IMPORTANT: THIS HAPPENS BEFORE SET ITEMS! 40820 if (groups) { 40821 this.setGroups(groups); 40822 } 40823 40824 // create itemset 40825 if (items) { 40826 this.setItems(items); 40827 } 40828 40829 // draw for the first time 40830 this._redraw(); 40831} 40832 40833// Extend the functionality from Core 40834Timeline.prototype = new Core(); 40835 40836/** 40837 * Load a configurator 40838 * @return {Object} 40839 * @private 40840 */ 40841Timeline.prototype._createConfigurator = function () { 40842 return new Configurator(this, this.dom.container, configureOptions); 40843}; 40844 40845/** 40846 * Force a redraw. The size of all items will be recalculated. 40847 * Can be useful to manually redraw when option autoResize=false and the window 40848 * has been resized, or when the items CSS has been changed. 40849 * 40850 * Note: this function will be overridden on construction with a trottled version 40851 */ 40852Timeline.prototype.redraw = function () { 40853 this.itemSet && this.itemSet.markDirty({ refreshItems: true }); 40854 this._redraw(); 40855}; 40856 40857Timeline.prototype.setOptions = function (options) { 40858 // validate options 40859 var errorFound = Validator.validate(options, allOptions); 40860 40861 if (errorFound === true) { 40862 console.log('%cErrors have been found in the supplied options object.', printStyle); 40863 } 40864 Core.prototype.setOptions.call(this, options); 40865 40866 if ('type' in options) { 40867 if (options.type !== this.options.type) { 40868 this.options.type = options.type; 40869 40870 // force recreation of all items 40871 var itemsData = this.itemsData; 40872 if (itemsData) { 40873 var selection = this.getSelection(); 40874 this.setItems(null); // remove all 40875 this.setItems(itemsData); // add all 40876 this.setSelection(selection); // restore selection 40877 } 40878 } 40879 } 40880}; 40881 40882/** 40883 * Set items 40884 * @param {vis.DataSet | Array | null} items 40885 */ 40886Timeline.prototype.setItems = function (items) { 40887 // convert to type DataSet when needed 40888 var newDataSet; 40889 if (!items) { 40890 newDataSet = null; 40891 } else if (items instanceof DataSet || items instanceof DataView) { 40892 newDataSet = items; 40893 } else { 40894 // turn an array into a dataset 40895 newDataSet = new DataSet(items, { 40896 type: { 40897 start: 'Date', 40898 end: 'Date' 40899 } 40900 }); 40901 } 40902 40903 // set items 40904 this.itemsData = newDataSet; 40905 this.itemSet && this.itemSet.setItems(newDataSet); 40906}; 40907 40908/** 40909 * Set groups 40910 * @param {vis.DataSet | Array} groups 40911 */ 40912Timeline.prototype.setGroups = function (groups) { 40913 // convert to type DataSet when needed 40914 var newDataSet; 40915 if (!groups) { 40916 newDataSet = null; 40917 } else { 40918 var filter = function filter(group) { 40919 return group.visible !== false; 40920 }; 40921 if (groups instanceof DataSet || groups instanceof DataView) { 40922 newDataSet = new DataView(groups, { filter: filter }); 40923 } else { 40924 // turn an array into a dataset 40925 newDataSet = new DataSet(groups.filter(filter)); 40926 } 40927 } 40928 40929 this.groupsData = newDataSet; 40930 this.itemSet.setGroups(newDataSet); 40931}; 40932 40933/** 40934 * Set both items and groups in one go 40935 * @param {{items: (Array | vis.DataSet), groups: (Array | vis.DataSet)}} data 40936 */ 40937Timeline.prototype.setData = function (data) { 40938 if (data && data.groups) { 40939 this.setGroups(data.groups); 40940 } 40941 40942 if (data && data.items) { 40943 this.setItems(data.items); 40944 } 40945}; 40946 40947/** 40948 * Set selected items by their id. Replaces the current selection 40949 * Unknown id's are silently ignored. 40950 * @param {string[] | string} [ids] An array with zero or more id's of the items to be 40951 * selected. If ids is an empty array, all items will be 40952 * unselected. 40953 * @param {Object} [options] Available options: 40954 * `focus: boolean` 40955 * If true, focus will be set to the selected item(s) 40956 * `animation: boolean | {duration: number, easingFunction: string}` 40957 * If true (default), the range is animated 40958 * smoothly to the new window. An object can be 40959 * provided to specify duration and easing function. 40960 * Default duration is 500 ms, and default easing 40961 * function is 'easeInOutQuad'. 40962 * Only applicable when option focus is true. 40963 */ 40964Timeline.prototype.setSelection = function (ids, options) { 40965 this.itemSet && this.itemSet.setSelection(ids); 40966 40967 if (options && options.focus) { 40968 this.focus(ids, options); 40969 } 40970}; 40971 40972/** 40973 * Get the selected items by their id 40974 * @return {Array} ids The ids of the selected items 40975 */ 40976Timeline.prototype.getSelection = function () { 40977 return this.itemSet && this.itemSet.getSelection() || []; 40978}; 40979 40980/** 40981 * Adjust the visible window such that the selected item (or multiple items) 40982 * are centered on screen. 40983 * @param {string | String[]} id An item id or array with item ids 40984 * @param {Object} [options] Available options: 40985 * `animation: boolean | {duration: number, easingFunction: string}` 40986 * If true (default), the range is animated 40987 * smoothly to the new window. An object can be 40988 * provided to specify duration and easing function. 40989 * Default duration is 500 ms, and default easing 40990 * function is 'easeInOutQuad'. 40991 */
40992Timeline.prototype.focus = function (id, options) { 40993 if (!this.itemsData || id == undefined) return; 40994 40995 var ids = Array.isArray(id) ? id : [id]; 40996 40997 // get the specified item(s) 40998 var itemsData = this.itemsData.getDataSet().get(ids, { 40999 type: { 41000 start: 'Date', 41001 end: 'Date' 41002 } 41003 }); 41004 41005 // calculate minimum start and maximum end of specified items 41006 var start = null; 41007 var end = null; 41008 itemsData.forEach(function (itemData) { 41009 var s = itemData.start.valueOf(); 41010 var e = 'end' in itemData ? itemData.end.valueOf() : itemData.start.valueOf(); 41011 41012 if (start === null || s < start) { 41013 start = s; 41014 } 41015 41016 if (end === null || e > end) { 41017 end = e; 41018 } 41019 }); 41020 41021 if (start !== null && end !== null) { 41022 var me = this; 41023 // Use the first item for the vertical focus 41024 var item = this.itemSet.items[ids[0]]; 41025 var startPos = this._getScrollTop() * -1; 41026 var initialVerticalScroll = null; 41027 41028 // Setup a handler for each frame of the vertical scroll 41029 var verticalAnimationFrame = function verticalAnimationFrame(ease, willDraw, done) { 41030 var verticalScroll = getItemVerticalScroll(me, item); 41031 41032 if (!initialVerticalScroll) { 41033 initialVerticalScroll = verticalScroll; 41034 } 41035 41036 if (initialVerticalScroll.itemTop == verticalScroll.itemTop && !initialVerticalScroll.shouldScroll) { 41037 return; // We don't need to scroll, so do nothing 41038 } else if (initialVerticalScroll.itemTop != verticalScroll.itemTop && verticalScroll.shouldScroll) { 41039 // The redraw shifted elements, so reset the animation to correct 41040 initialVerticalScroll = verticalScroll; 41041 startPos = me._getScrollTop() * -1; 41042 } 41043 41044 var from = startPos; 41045 var to = initialVerticalScroll.scrollOffset; 41046 var scrollTop = done ? to : from + (to - from) * ease; 41047 41048 me._setScrollTop(-scrollTop); 41049 41050 if (!willDraw) { 41051 me._redraw(); 41052 } 41053 }; 41054 41055 // Enforces the final vertical scroll position 41056 var setFinalVerticalPosition = function setFinalVerticalPosition() { 41057 var finalVerticalScroll = getItemVerticalScroll(me, item); 41058 41059 if (finalVerticalScroll.shouldScroll && finalVerticalScroll.itemTop != initialVerticalScroll.itemTop) { 41060 me._setScrollTop(-finalVerticalScroll.scrollOffset); 41061 me._redraw(); 41062 } 41063 }; 41064 41065 // Perform one last check at the end to make sure the final vertical 41066 // position is correct 41067 var finalVerticalCallback = function finalVerticalCallback() { 41068 // Double check we ended at the proper scroll position 41069 setFinalVerticalPosition(); 41070 41071 // Let the redraw settle and finalize the position. 41072 setTimeout(setFinalVerticalPosition, 100); 41073 }; 41074 41075 // calculate the new middle and interval for the window 41076 var middle = (start + end) / 2; 41077 var interval = Math.max(this.range.end - this.range.start, (end - start) * 1.1); 41078 41079 var animation = options && options.animation !== undefined ? options.animation : true; 41080 41081 if (!animation) { 41082 // We aren't animating so set a default so that the final callback forces the vertical location 41083 initialVerticalScroll = { shouldScroll: false, scrollOffset: -1, itemTop: -1 }; 41084 } 41085 41086 this.range.setRange(middle - interval / 2, middle + interval / 2, { animation: animation }, finalVerticalCallback, verticalAnimationFrame); 41087 } 41088}; 41089 41090/** 41091 * Set Timeline window such that it fits all items 41092 * @param {Object} [options] Available options: 41093 * `animation: boolean | {duration: number, easingFunction: string}` 41094 * If true (default), the range is animated 41095 * smoothly to the new window. An object can be 41096 * provided to specify duration and easing function. 41097 * Default duration is 500 ms, and default easing 41098 * function is 'easeInOutQuad'. 41099 * @param {function} [callback] 41100 */ 41101Timeline.prototype.fit = function (options, callback) { 41102 var animation = options && options.animation !== undefined ? options.animation : true; 41103 var range; 41104 41105 var dataset = this.itemsData && this.itemsData.getDataSet(); 41106 if (dataset.length === 1 && dataset.get()[0].end === undefined) { 41107 // a single item -> don't fit, just show a range around the item from -4 to +3 days 41108 range = this.getDataRange(); 41109 this.moveTo(range.min.valueOf(), { animation: animation }, callback); 41110 } else {
41111 // exactly fit the items (plus a small margin) 41112 range = this.getItemRange(); 41113 this.range.setRange(range.min, range.max, { animation: animation }, callback); 41114 } 41115}; 41116 41117/** 41118 * 41119 * @param {vis.Item} item 41120 * @returns {number} 41121 */ 41122function getStart(item) { 41123 return util.convert(item.data.start, 'Date').valueOf(); 41124} 41125 41126/** 41127 * 41128 * @param {vis.Item} item 41129 * @returns {number} 41130 */ 41131function getEnd(item) { 41132 var end = item.data.end != undefined ? item.data.end : item.data.start; 41133 return util.convert(end, 'Date').valueOf(); 41134} 41135 41136/** 41137 * @param {vis.Timeline} timeline 41138 * @param {vis.Item} item 41139 * @return {{shouldScroll: bool, scrollOffset: number, itemTop: number}} 41140 */ 41141function getItemVerticalScroll(timeline, item) { 41142 var leftHeight = timeline.props.leftContainer.height; 41143 var contentHeight = timeline.props.left.height; 41144 41145 var group = item.parent; 41146 var offset = group.top; 41147 var shouldScroll = true; 41148 var orientation = timeline.timeAxis.options.orientation.axis; 41149 41150 var itemTop = function itemTop() { 41151 if (orientation == "bottom") { 41152 return group.height - item.top - item.height; 41153 } else { 41154 return item.top; 41155 } 41156 }; 41157 41158 var currentScrollHeight = timeline._getScrollTop() * -1; 41159 var targetOffset = offset + itemTop(); 41160 var height = item.height; 41161 41162 if (targetOffset < currentScrollHeight) { 41163 if (offset + leftHeight <= offset + itemTop() + height) { 41164 offset += itemTop() - timeline.itemSet.options.margin.item.vertical; 41165 } 41166 } else if (targetOffset + height > currentScrollHeight + leftHeight) { 41167 offset += itemTop() + height - leftHeight + timeline.itemSet.options.margin.item.vertical; 41168 } else { 41169 shouldScroll = false; 41170 } 41171 41172 offset = Math.min(offset, contentHeight - leftHeight); 41173 41174 return { shouldScroll: shouldScroll, scrollOffset: offset, itemTop: targetOffset }; 41175} 41176 41177/** 41178 * Determine the range of the items, taking into account their actual width 41179 * and a margin of 10 pixels on both sides. 41180 * 41181 * @returns {{min: Date, max: Date}} 41182 */ 41183Timeline.prototype.getItemRange = function () { 41184 // get a rough approximation for the range based on the items start and end dates 41185 var range = this.getDataRange(); 41186 var min = range.min !== null ? range.min.valueOf() : null; 41187 var max = range.max !== null ? range.max.valueOf() : null; 41188 var minItem = null; 41189 var maxItem = null; 41190 41191 if (min != null && max != null) { 41192 var interval = max - min; // ms 41193 if (interval <= 0) { 41194 interval = 10; 41195 } 41196 var factor = interval / this.props.center.width; 41197 41198 var redrawQueue = {}; 41199 var redrawQueueLength = 0; 41200 41201 // collect redraw functions 41202 util.forEach(this.itemSet.items, function (item, key) { 41203 if (item.groupShowing) { 41204 var returnQueue = true; 41205 redrawQueue[key] = item.redraw(returnQueue); 41206 redrawQueueLength = redrawQueue[key].length; 41207 } 41208 }); 41209 41210 var needRedraw = redrawQueueLength > 0; 41211 if (needRedraw) { 41212 // redraw all regular items 41213 for (var i = 0; i < redrawQueueLength; i++) { 41214 util.forEach(redrawQueue, function (fns) { 41215 fns[i](); 41216 }); 41217 } 41218 } 41219 41220 // calculate the date of the left side and right side of the items given 41221 util.forEach(this.itemSet.items, function (item) { 41222 var start = getStart(item); 41223 var end = getEnd(item); 41224 var startSide; 41225 var endSide; 41226 41227 if (this.options.rtl) { 41228 startSide = start - (item.getWidthRight() + 10) * factor; 41229 endSide = end + (item.getWidthLeft() + 10) * factor; 41230 } else { 41231 startSide = start - (item.getWidthLeft() + 10) * factor; 41232 endSide = end + (item.getWidthRight() + 10) * factor; 41233 } 41234 41235 if (startSide < min) { 41236 min = startSide; 41237 minItem = item; 41238 } 41239 if (endSide > max) { 41240 max = endSide; 41241 maxItem = item; 41242 } 41243 }.bind(this)); 41244 41245 if (minItem && maxItem) { 41246 var lhs = minItem.getWidthLeft() + 10; 41247 var rhs = maxItem.getWidthRight() + 10; 41248 var delta = this.props.center.width - lhs - rhs; // px 41249 41250 if (delta > 0) { 41251 if (this.options.rtl) { 41252 min = getStart(minItem) - rhs * interval / delta; // ms 41253 max = getEnd(maxItem) + lhs * interval / delta; // ms 41254 } else { 41255 min = getStart(minItem) - lhs * interval / delta; // ms 41256 max = getEnd(maxItem) + rhs * interval / delta; // ms 41257 } 41258 } 41259 } 41260 } 41261 41262 return { 41263 min: min != null ? new Date(min) : null, 41264 max: max != null ? new Date(max) : null 41265 }; 41266}; 41267 41268/** 41269 * Calculate the data range of the items start and end dates 41270 * @returns {{min: Date, max: Date}} 41271 */
41272Timeline.prototype.getDataRange = function () { 41273 var min = null; 41274 var max = null; 41275 41276 var dataset = this.itemsData && this.itemsData.getDataSet(); 41277 if (dataset) { 41278 dataset.forEach(function (item) { 41279 var start = util.convert(item.start, 'Date').valueOf(); 41280 var end = util.convert(item.end != undefined ? item.end : item.start, 'Date').valueOf(); 41281 if (min === null || start < min) { 41282 min = start; 41283 } 41284 if (max === null || end > max) { 41285 max = end; 41286 } 41287 }); 41288 } 41289 41290 return { 41291 min: min != null ? new Date(min) : null, 41292 max: max != null ? new Date(max) : null 41293 }; 41294}; 41295 41296/** 41297 * Generate Timeline related information from an event 41298 * @param {Event} event 41299 * @return {Object} An object with related information, like on which area 41300 * The event happened, whether clicked on an item, etc. 41301 */ 41302Timeline.prototype.getEventProperties = function (event) { 41303 var clientX = event.center ? event.center.x : event.clientX; 41304 var clientY = event.center ? event.center.y : event.clientY; 41305 var x; 41306 if (this.options.rtl) { 41307 x = util.getAbsoluteRight(this.dom.centerContainer) - clientX; 41308 } else { 41309 x = clientX - util.getAbsoluteLeft(this.dom.centerContainer); 41310 } 41311 var y = clientY - util.getAbsoluteTop(this.dom.centerContainer); 41312 41313 var item = this.itemSet.itemFromTarget(event); 41314 var group = this.itemSet.groupFromTarget(event); 41315 var customTime = CustomTime.customTimeFromTarget(event); 41316 41317 var snap = this.itemSet.options.snap || null; 41318 var scale = this.body.util.getScale(); 41319 var step = this.body.util.getStep(); 41320 var time = this._toTime(x); 41321 var snappedTime = snap ? snap(time, scale, step) : time; 41322 41323 var element = util.getTarget(event); 41324 var what = null; 41325 if (item != null) { 41326 what = 'item'; 41327 } else if (customTime != null) { 41328 what = 'custom-time'; 41329 } else if (util.hasParent(element, this.timeAxis.dom.foreground)) { 41330 what = 'axis'; 41331 } else if (this.timeAxis2 && util.hasParent(element, this.timeAxis2.dom.foreground)) { 41332 what = 'axis'; 41333 } else if (util.hasParent(element, this.itemSet.dom.labelSet)) { 41334 what = 'group-label'; 41335 } else if (util.hasParent(element, this.currentTime.bar)) { 41336 what = 'current-time'; 41337 } else if (util.hasParent(element, this.dom.center)) { 41338 what = 'background'; 41339 } 41340 41341 return { 41342 event: event, 41343 item: item ? item.id : null, 41344 group: group ? group.groupId : null, 41345 what: what, 41346 pageX: event.srcEvent ? event.srcEvent.pageX : event.pageX, 41347 pageY: event.srcEvent ? event.srcEvent.pageY : event.pageY, 41348 x: x, 41349 y: y, 41350 time: time, 41351 snappedTime: snappedTime 41352 }; 41353}; 41354 41355/** 41356 * Toggle Timeline rolling mode 41357 */ 41358 41359Timeline.prototype.toggleRollingMode = function () { 41360 if (this.range.rolling) { 41361 this.range.stopRolling(); 41362 } else { 41363 if (this.options.rollingMode == undefined) { 41364 this.setOptions(this.options); 41365 } 41366 this.range.startRolling(); 41367 } 41368}; 41369 41370module.exports = Timeline; 41371 41372/***/ }), 41373/* 179 */ 41374/***/ (function(module, exports, __webpack_require__) { 41375 41376"use strict"; 41377 41378 41379Object.defineProperty(exports, "__esModule", { 41380 value: true 41381}); 41382 41383var _stringify = __webpack_require__(19); 41384 41385var _stringify2 = _interopRequireDefault(_stringify); 41386 41387var _classCallCheck2 = __webpack_require__(0); 41388 41389var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); 41390 41391var _createClass2 = __webpack_require__(1); 41392 41393var _createClass3 = _interopRequireDefault(_createClass2); 41394 41395function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 41396 41397var Hammer = __webpack_require__(10); 41398var hammerUtil = __webpack_require__(37); 41399var util = __webpack_require__(2); 41400 41401/** 41402 * @param {number} [pixelRatio=1] 41403 */ 41404 41405var ColorPicker = function () { 41406 /** 41407 * @param {number} [pixelRatio=1] 41408 */ 41409 function ColorPicker() { 41410 var pixelRatio = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 1; 41411 (0, _classCallCheck3['default'])(this, ColorPicker); 41412 41413 this.pixelRatio = pixelRatio; 41414 this.generated = false; 41415 this.centerCoordinates = { x: 289 / 2, y: 289 / 2 }; 41416 this.r = 289 * 0.49; 41417 this.color = { r: 255, g: 255, b: 255, a: 1.0 }; 41418 this.hueCircle = undefined; 41419 this.initialColor = { r: 255, g: 255, b: 255, a: 1.0 }; 41420 this.previousColor = undefined; 41421 this.applied = false;
41422 41423 // bound by 41424 this.updateCallback = function () {}; 41425 this.closeCallback = function () {}; 41426 41427 // create all DOM elements 41428 this._create(); 41429 } 41430 41431 /** 41432 * this inserts the colorPicker into a div from the DOM 41433 * @param {Element} container 41434 */ 41435 41436 41437 (0, _createClass3['default'])(ColorPicker, [{ 41438 key: 'insertTo', 41439 value: function insertTo(container) { 41440 if (this.hammer !== undefined) { 41441 this.hammer.destroy(); 41442 this.hammer = undefined; 41443 } 41444 this.container = container; 41445 this.container.appendChild(this.frame); 41446 this._bindHammer(); 41447 41448 this._setSize(); 41449 } 41450 41451 /** 41452 * the callback is executed on apply and save. Bind it to the application 41453 * @param {function} callback 41454 */ 41455 41456 }, { 41457 key: 'setUpdateCallback', 41458 value: function setUpdateCallback(callback) { 41459 if (typeof callback === 'function') { 41460 this.updateCallback = callback; 41461 } else { 41462 throw new Error("Function attempted to set as colorPicker update callback is not a function."); 41463 } 41464 } 41465 41466 /** 41467 * the callback is executed on apply and save. Bind it to the application 41468 * @param {function} callback 41469 */ 41470 41471 }, { 41472 key: 'setCloseCallback', 41473 value: function setCloseCallback(callback) { 41474 if (typeof callback === 'function') { 41475 this.closeCallback = callback; 41476 } else { 41477 throw new Error("Function attempted to set as colorPicker closing callback is not a function."); 41478 } 41479 } 41480 41481 /** 41482 * 41483 * @param {string} color 41484 * @returns {String} 41485 * @private 41486 */ 41487 41488 }, { 41489 key: '_isColorString', 41490 value: function _isColorString(color) { 41491 var htmlColors = { black: '#000000', navy: '#000080', darkblue: '#00008B', mediumblue: '#0000CD', blue: '#0000FF', darkgreen: '#006400', green: '#008000', teal: '#008080', darkcyan: '#008B8B', deepskyblue: '#00BFFF', darkturquoise: '#00CED1', mediumspringgreen: '#00FA9A', lime: '#00FF00', springgreen: '#00FF7F', aqua: '#00FFFF', cyan: '#00FFFF', midnightblue: '#191970', dodgerblue: '#1E90FF', lightseagreen: '#20B2AA', forestgreen: '#228B22', seagreen: '#2E8B57', darkslategray: '#2F4F4F', limegreen: '#32CD32', mediumseagreen: '#3CB371', turquoise: '#40E0D0', royalblue: '#4169E1', steelblue: '#4682B4', darkslateblue: '#483D8B', mediumturquoise: '#48D1CC', indigo: '#4B0082', darkolivegreen: '#556B2F', cadetblue: '#5F9EA0', cornflowerblue: '#6495ED', mediumaquamarine: '#66CDAA', dimgray: '#696969', slateblue: '#6A5ACD', olivedrab: '#6B8E23', slategray: '#708090', lightslategray: '#778899', mediumslateblue: '#7B68EE', lawngreen: '#7CFC00', chartreuse: '#7FFF00', aquamarine: '#7FFFD4', maroon: '#800000', purple: '#800080', olive: '#808000', gray: '#808080', skyblue: '#87CEEB', lightskyblue: '#87CEFA', blueviolet: '#8A2BE2', darkred: '#8B0000', darkmagenta: '#8B008B', saddlebrown: '#8B4513', darkseagreen: '#8FBC8F', lightgreen: '#90EE90', mediumpurple: '#9370D8', darkviolet: '#9400D3', palegreen: '#98FB98', darkorchid: '#9932CC', yellowgreen: '#9ACD32', sienna: '#A0522D', brown: '#A52A2A', darkgray: '#A9A9A9', lightblue: '#ADD8E6', greenyellow: '#ADFF2F', paleturquoise: '#AFEEEE', lightsteelblue: '#B0C4DE', powderblue: '#B0E0E6', firebrick: '#B22222', darkgoldenrod: '#B8860B', mediumorchid: '#BA55D3', rosybrown: '#BC8F8F', darkkhaki: '#BDB76B', silver: '#C0C0C0', mediumvioletred: '#C71585', indianred: '#CD5C5C', peru: '#CD853F', chocolate: '#D2691E', tan: '#D2B48C', lightgrey: '#D3D3D3', palevioletred: '#D87093', thistle: '#D8BFD8', orchid: '#DA70D6', goldenrod: '#DAA520', crimson: '#DC143C', gainsboro: '#DCDCDC', plum: '#DDA0DD', burlywood: '#DEB887', lightcyan: '#E0FFFF', lavender: '#E6E6FA', darksalmon: '#E9967A', violet: '#EE82EE', palegoldenrod: '#EEE8AA', lightcoral: '#F08080', khaki: '#F0E68C', aliceblue: '#F0F8FF', honeydew: '#F0FFF0', azure: '#F0FFFF', sandybrown: '#F4A460', wheat: '#F5DEB3', beige: '#F5F5DC', whitesmoke: '#F5F5F5', mintcream: '#F5FFFA', ghostwhite: '#F8F8FF', salmon: '#FA8072', antiquewhite: '#FAEBD7', linen: '#FAF0E6', lightgoldenrodyellow: '#FAFAD2', oldlace: '#FDF5E6', red: '#FF0000', fuchsia: '#FF00FF', magenta: '#FF00FF', deeppink: '#FF1493', orangered: '#FF4500', tomato: '#FF6347', hotpink: '#FF69B4', coral: '#FF7F50', darkorange: '#FF8C00', lightsalmon: '#FFA07A', orange: '#FFA500', lightpink: '#FFB6C1', pink: '#FFC0CB', gold: '#FFD700', peachpuff: '#FFDAB9', navajowhite: '#FFDEAD', moccasin: '#FFE4B5', bisque: '#FFE4C4', mistyrose: '#FFE4E1', blanchedalmond: '#FFEBCD', papayawhip: '#FFEFD5', lavenderblush: '#FFF0F5', seashell: '#FFF5EE', cornsilk: '#FFF8DC', lemonchiffon: '#FFFACD', floralwhite: '#FFFAF0', snow: '#FFFAFA', yellow: '#FFFF00', lightyellow: '#FFFFE0', ivory: '#FFFFF0', white: '#FFFFFF' }; 41492 if (typeof color === 'string') { 41493 return htmlColors[color]; 41494 } 41495 } 41496 41497 /** 41498 * Set the color of the colorPicker 41499 * Supported formats: 41500 * 'red' --> HTML color string 41501 * '#ffffff' --> hex string 41502 * 'rbg(255,255,255)' --> rgb string
41503 * 'rgba(255,255,255,1.0)' --> rgba string 41504 * {r:255,g:255,b:255} --> rgb object 41505 * {r:255,g:255,b:255,a:1.0} --> rgba object 41506 * @param {string|Object} color 41507 * @param {boolean} [setInitial=true] 41508 */ 41509 41510 }, { 41511 key: 'setColor', 41512 value: function setColor(color) { 41513 var setInitial = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true; 41514 41515 if (color === 'none') { 41516 return; 41517 } 41518 41519 var rgba = void 0; 41520 41521 // if a html color shorthand is used, convert to hex 41522 var htmlColor = this._isColorString(color); 41523 if (htmlColor !== undefined) { 41524 color = htmlColor; 41525 } 41526 41527 // check format 41528 if (util.isString(color) === true) { 41529 if (util.isValidRGB(color) === true) { 41530 var rgbaArray = color.substr(4).substr(0, color.length - 5).split(','); 41531 rgba = { r: rgbaArray[0], g: rgbaArray[1], b: rgbaArray[2], a: 1.0 }; 41532 } else if (util.isValidRGBA(color) === true) { 41533 var _rgbaArray = color.substr(5).substr(0, color.length - 6).split(','); 41534 rgba = { r: _rgbaArray[0], g: _rgbaArray[1], b: _rgbaArray[2], a: _rgbaArray[3] }; 41535 } else if (util.isValidHex(color) === true) { 41536 var rgbObj = util.hexToRGB(color); 41537 rgba = { r: rgbObj.r, g: rgbObj.g, b: rgbObj.b, a: 1.0 }; 41538 } 41539 } else { 41540 if (color instanceof Object) { 41541 if (color.r !== undefined && color.g !== undefined && color.b !== undefined) { 41542 var alpha = color.a !== undefined ? color.a : '1.0'; 41543 rgba = { r: color.r, g: color.g, b: color.b, a: alpha }; 41544 } 41545 } 41546 } 41547 41548 // set color 41549 if (rgba === undefined) { 41550 throw new Error("Unknown color passed to the colorPicker. Supported are strings: rgb, hex, rgba. Object: rgb ({r:r,g:g,b:b,[a:a]}). Supplied: " + (0, _stringify2['default'])(color)); 41551 } else { 41552 this._setColor(rgba, setInitial); 41553 } 41554 } 41555 41556 /** 41557 * this shows the color picker. 41558 * The hue circle is constructed once and stored. 41559 */ 41560 41561 }, { 41562 key: 'show', 41563 value: function show() { 41564 if (this.closeCallback !== undefined) { 41565 this.closeCallback(); 41566 this.closeCallback = undefined; 41567 } 41568 41569 this.applied = false; 41570 this.frame.style.display = 'block'; 41571 this._generateHueCircle(); 41572 } 41573 41574 // ------------------------------------------ PRIVATE ----------------------------- // 41575 41576 /** 41577 * Hide the picker. Is called by the cancel button. 41578 * Optional boolean to store the previous color for easy access later on. 41579 * @param {boolean} [storePrevious=true] 41580 * @private 41581 */ 41582 41583 }, { 41584 key: '_hide', 41585 value: function _hide() { 41586 var _this = this; 41587 41588 var storePrevious = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true; 41589 41590 // store the previous color for next time; 41591 if (storePrevious === true) { 41592 this.previousColor = util.extend({}, this.color); 41593 } 41594 41595 if (this.applied === true) { 41596 this.updateCallback(this.initialColor); 41597 } 41598 41599 this.frame.style.display = 'none'; 41600 41601 // call the closing callback, restoring the onclick method. 41602 // this is in a setTimeout because it will trigger the show again before the click is done. 41603 setTimeout(function () { 41604 if (_this.closeCallback !== undefined) { 41605 _this.closeCallback(); 41606 _this.closeCallback = undefined; 41607 } 41608 }, 0); 41609 } 41610 41611 /** 41612 * bound to the save button. Saves and hides. 41613 * @private 41614 */ 41615 41616 }, { 41617 key: '_save', 41618 value: function _save() { 41619 this.updateCallback(this.color); 41620 this.applied = false; 41621 this._hide(); 41622 } 41623 41624 /** 41625 * Bound to apply button. Saves but does not close. Is undone by the cancel button. 41626 * @private 41627 */ 41628 41629 }, { 41630 key: '_apply', 41631 value: function _apply() { 41632 this.applied = true; 41633 this.updateCallback(this.color); 41634 this._updatePicker(this.color); 41635 } 41636 41637 /** 41638 * load the color from the previous session. 41639 * @private 41640 */ 41641 41642 }, { 41643 key: '_loadLast', 41644 value: function _loadLast() { 41645 if (this.previousColor !== undefined) { 41646 this.setColor(this.previousColor, false); 41647 } else { 41648 alert("There is no last color to load..."); 41649 } 41650 } 41651 41652 /** 41653 * set the color, place the picker 41654 * @param {Object} rgba 41655 * @param {boolean} [setInitial=true] 41656 * @private 41657 */ 41658 41659 }, { 41660 key: '_setColor', 41661 value: function _setColor(rgba) { 41662 var setInitial = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true; 41663 41664 // store the initial color 41665 if (setInitial === true) { 41666 this.initialColor = util.extend({}, rgba); 41667 } 41668 41669 this.color = rgba; 41670 var hsv = util.RGBToHSV(rgba.r, rgba.g, rgba.b); 41671 41672 var angleConvert = 2 * Math.PI; 41673 var radius = this.r * hsv.s; 41674 var x = this.centerCoordinates.x + radius * Math.sin(angleConvert * hsv.h); 41675 var y = this.centerCoordinates.y + radius * Math.cos(angleConvert * hsv.h); 41676 41677 this.colorPickerSelector.style.left = x - 0.5 * this.colorPickerSelector.clientWidth + 'px'; 41678 this.colorPickerSelector.style.top = y - 0.5 * this.colorPickerSelector.clientHeight + 'px'; 41679 41680 this._updatePicker(rgba); 41681 } 41682 41683 /** 41684 * bound to opacity control 41685 * @param {number} value 41686 * @private 41687 */ 41688 41689 }, { 41690 key: '_setOpacity', 41691 value: function _setOpacity(value) { 41692 this.color.a = value / 100; 41693 this._updatePicker(this.color); 41694 } 41695 41696 /** 41697 * bound to brightness control 41698 * @param {number} value 41699 * @private 41700 */ 41701 41702 }, { 41703 key: '_setBrightness', 41704 value: function _setBrightness(value) { 41705 var hsv = util.RGBToHSV(this.color.r, this.color.g, this.color.b); 41706 hsv.v = value / 100; 41707 var rgba = util.HSVToRGB(hsv.h, hsv.s, hsv.v); 41708 rgba['a'] = this.color.a; 41709 this.color = rgba; 41710 this._updatePicker(); 41711 } 41712 41713 /** 41714 * update the color picker. A black circle overlays the hue circle to mimic the brightness decreasing. 41715 * @param {Object} rgba 41716 * @private 41717 */ 41718 41719 }, { 41720 key: '_updatePicker', 41721 value: function _updatePicker() { 41722 var rgba = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : this.color; 41723 41724 var hsv = util.RGBToHSV(rgba.r, rgba.g, rgba.b); 41725 var ctx = this.colorPickerCanvas.getContext('2d'); 41726 if (this.pixelRation === undefined) { 41727 this.pixelRatio = (window.devicePixelRatio || 1) / (ctx.webkitBackingStorePixelRatio || ctx.mozBackingStorePixelRatio || ctx.msBackingStorePixelRatio || ctx.oBackingStorePixelRatio || ctx.backingStorePixelRatio || 1); 41728 } 41729 ctx.setTransform(this.pixelRatio, 0, 0, this.pixelRatio, 0, 0); 41730 41731 // clear the canvas 41732 var w = this.colorPickerCanvas.clientWidth; 41733 var h = this.colorPickerCanvas.clientHeight; 41734 ctx.clearRect(0, 0, w, h); 41735 41736 ctx.putImageData(this.hueCircle, 0, 0); 41737 ctx.fillStyle = 'rgba(0,0,0,' + (1 - hsv.v) + ')'; 41738 ctx.circle(this.centerCoordinates.x, this.centerCoordinates.y, this.r); 41739 ctx.fill(); 41740 41741 this.brightnessRange.value = 100 * hsv.v; 41742 this.opacityRange.value = 100 * rgba.a; 41743 41744 this.initialColorDiv.style.backgroundColor = 'rgba(' + this.initialColor.r + ',' + this.initialColor.g + ',' + this.initialColor.b + ',' + this.initialColor.a + ')'; 41745 this.newColorDiv.style.backgroundColor = 'rgba(' + this.color.r + ',' + this.color.g + ',' + this.color.b + ',' + this.color.a + ')'; 41746 } 41747 41748 /** 41749 * used by create to set the size of the canvas. 41750 * @private 41751 */ 41752 41753 }, { 41754 key: '_setSize', 41755 value: function _setSize() { 41756 this.colorPickerCanvas.style.width = '100%'; 41757 this.colorPickerCanvas.style.height = '100%'; 41758 41759 this.colorPickerCanvas.width = 289 * this.pixelRatio; 41760 this.colorPickerCanvas.height = 289 * this.pixelRatio; 41761 } 41762 41763 /** 41764 * create all dom elements 41765 * TODO: cleanup, lots of similar dom elements 41766 * @private 41767 */ 41768 41769 }, { 41770 key: '_create', 41771 value: function _create() { 41772 this.frame = document.createElement('div'); 41773 this.frame.className = 'vis-color-picker'; 41774 41775 this.colorPickerDiv = document.createElement('div'); 41776 this.colorPickerSelector = document.createElement('div'); 41777 this.colorPickerSelector.className = 'vis-selector'; 41778 this.colorPickerDiv.appendChild(this.colorPickerSelector); 41779 41780 this.colorPickerCanvas = document.createElement('canvas'); 41781 this.colorPickerDiv.appendChild(this.colorPickerCanvas); 41782 41783 if (!this.colorPickerCanvas.getContext) { 41784 var noCanvas = document.createElement('DIV'); 41785 noCanvas.style.color = 'red'; 41786 noCanvas.style.fontWeight = 'bold'; 41787 noCanvas.style.padding = '10px'; 41788 noCanvas.innerHTML = 'Error: your browser does not support HTML canvas'; 41789 this.colorPickerCanvas.appendChild(noCanvas); 41790 } else { 41791 var ctx = this.colorPickerCanvas.getContext("2d"); 41792 this.pixelRatio = (window.devicePixelRatio || 1) / (ctx.webkitBackingStorePixelRatio || ctx.mozBackingStorePixelRatio || ctx.msBackingStorePixelRatio || ctx.oBackingStorePixelRatio || ctx.backingStorePixelRatio || 1); 41793 41794 this.colorPickerCanvas.getContext("2d").setTransform(this.pixelRatio, 0, 0, this.pixelRatio, 0, 0); 41795 } 41796 41797 this.colorPickerDiv.className = 'vis-color'; 41798 41799 this.opacityDiv = document.createElement('div'); 41800 this.opacityDiv.className = 'vis-opacity'; 41801 41802 this.brightnessDiv = document.createElement('div'); 41803 this.brightnessDiv.className = 'vis-brightness'; 41804 41805 this.arrowDiv = document.createElement('div'); 41806 this.arrowDiv.className = 'vis-arrow'; 41807 41808 this.opacityRange = document.createElement('input');
41809 try { 41810 this.opacityRange.type = 'range'; // Not supported on IE9 41811 this.opacityRange.min = '0'; 41812 this.opacityRange.max = '100'; 41813 } 41814 // TODO: Add some error handling and remove this lint exception 41815 catch (err) {} // eslint-disable-line no-empty 41816 this.opacityRange.value = '100'; 41817 this.opacityRange.className = 'vis-range'; 41818 41819 this.brightnessRange = document.createElement('input'); 41820 try { 41821 this.brightnessRange.type = 'range'; // Not supported on IE9 41822 this.brightnessRange.min = '0'; 41823 this.brightnessRange.max = '100'; 41824 } 41825 // TODO: Add some error handling and remove this lint exception 41826 catch (err) {} // eslint-disable-line no-empty 41827 this.brightnessRange.value = '100'; 41828 this.brightnessRange.className = 'vis-range'; 41829 41830 this.opacityDiv.appendChild(this.opacityRange); 41831 this.brightnessDiv.appendChild(this.brightnessRange); 41832 41833 var me = this; 41834 this.opacityRange.onchange = function () { 41835 me._setOpacity(this.value); 41836 }; 41837 this.opacityRange.oninput = function () { 41838 me._setOpacity(this.value); 41839 }; 41840 this.brightnessRange.onchange = function () { 41841 me._setBrightness(this.value); 41842 }; 41843 this.brightnessRange.oninput = function () { 41844 me._setBrightness(this.value); 41845 }; 41846 41847 this.brightnessLabel = document.createElement("div"); 41848 this.brightnessLabel.className = "vis-label vis-brightness"; 41849 this.brightnessLabel.innerHTML = 'brightness:'; 41850 41851 this.opacityLabel = document.createElement("div"); 41852 this.opacityLabel.className = "vis-label vis-opacity"; 41853 this.opacityLabel.innerHTML = 'opacity:'; 41854 41855 this.newColorDiv = document.createElement("div"); 41856 this.newColorDiv.className = "vis-new-color"; 41857 this.newColorDiv.innerHTML = 'new'; 41858 41859 this.initialColorDiv = document.createElement("div"); 41860 this.initialColorDiv.className = "vis-initial-color"; 41861 this.initialColorDiv.innerHTML = 'initial'; 41862 41863 this.cancelButton = document.createElement("div"); 41864 this.cancelButton.className = "vis-button vis-cancel"; 41865 this.cancelButton.innerHTML = 'cancel'; 41866 this.cancelButton.onclick = this._hide.bind(this, false); 41867 41868 this.applyButton = document.createElement("div"); 41869 this.applyButton.className = "vis-button vis-apply"; 41870 this.applyButton.innerHTML = 'apply'; 41871 this.applyButton.onclick = this._apply.bind(this); 41872 41873 this.saveButton = document.createElement("div"); 41874 this.saveButton.className = "vis-button vis-save"; 41875 this.saveButton.innerHTML = 'save'; 41876 this.saveButton.onclick = this._save.bind(this); 41877 41878 this.loadButton = document.createElement("div"); 41879 this.loadButton.className = "vis-button vis-load"; 41880 this.loadButton.innerHTML = 'load last'; 41881 this.loadButton.onclick = this._loadLast.bind(this); 41882 41883 this.frame.appendChild(this.colorPickerDiv); 41884 this.frame.appendChild(this.arrowDiv); 41885 this.frame.appendChild(this.brightnessLabel); 41886 this.frame.appendChild(this.brightnessDiv); 41887 this.frame.appendChild(this.opacityLabel); 41888 this.frame.appendChild(this.opacityDiv); 41889 this.frame.appendChild(this.newColorDiv); 41890 this.frame.appendChild(this.initialColorDiv); 41891 41892 this.frame.appendChild(this.cancelButton); 41893 this.frame.appendChild(this.applyButton); 41894 this.frame.appendChild(this.saveButton); 41895 this.frame.appendChild(this.loadButton); 41896 } 41897 41898 /** 41899 * bind hammer to the color picker 41900 * @private 41901 */ 41902 41903 }, { 41904 key: '_bindHammer', 41905 value: function _bindHammer() { 41906 var _this2 = this; 41907 41908 this.drag = {}; 41909 this.pinch = {}; 41910 this.hammer = new Hammer(this.colorPickerCanvas); 41911 this.hammer.get('pinch').set({ enable: true }); 41912 41913 hammerUtil.onTouch(this.hammer, function (event) { 41914 _this2._moveSelector(event); 41915 }); 41916 this.hammer.on('tap', function (event) { 41917 _this2._moveSelector(event); 41918 }); 41919 this.hammer.on('panstart', function (event) { 41920 _this2._moveSelector(event); 41921 }); 41922 this.hammer.on('panmove', function (event) { 41923 _this2._moveSelector(event); 41924 }); 41925 this.hammer.on('panend', function (event) { 41926 _this2._moveSelector(event); 41927 }); 41928 } 41929 41930 /** 41931 * generate the hue circle. This is relatively heavy (200ms) and is done only once on the first time it is shown.
41932 * @private 41933 */ 41934 41935 }, { 41936 key: '_generateHueCircle', 41937 value: function _generateHueCircle() { 41938 if (this.generated === false) { 41939 var ctx = this.colorPickerCanvas.getContext('2d'); 41940 if (this.pixelRation === undefined) { 41941 this.pixelRatio = (window.devicePixelRatio || 1) / (ctx.webkitBackingStorePixelRatio || ctx.mozBackingStorePixelRatio || ctx.msBackingStorePixelRatio || ctx.oBackingStorePixelRatio || ctx.backingStorePixelRatio || 1); 41942 } 41943 ctx.setTransform(this.pixelRatio, 0, 0, this.pixelRatio, 0, 0); 41944 41945 // clear the canvas 41946 var w = this.colorPickerCanvas.clientWidth; 41947 var h = this.colorPickerCanvas.clientHeight; 41948 ctx.clearRect(0, 0, w, h); 41949 41950 // draw hue circle 41951 var x = void 0, 41952 y = void 0, 41953 hue = void 0, 41954 sat = void 0; 41955 this.centerCoordinates = { x: w * 0.5, y: h * 0.5 }; 41956 this.r = 0.49 * w; 41957 var angleConvert = 2 * Math.PI / 360; 41958 var hfac = 1 / 360; 41959 var sfac = 1 / this.r; 41960 var rgb = void 0; 41961 for (hue = 0; hue < 360; hue++) { 41962 for (sat = 0; sat < this.r; sat++) { 41963 x = this.centerCoordinates.x + sat * Math.sin(angleConvert * hue); 41964 y = this.centerCoordinates.y + sat * Math.cos(angleConvert * hue); 41965 rgb = util.HSVToRGB(hue * hfac, sat * sfac, 1); 41966 ctx.fillStyle = 'rgb(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ')'; 41967 ctx.fillRect(x - 0.5, y - 0.5, 2, 2); 41968 } 41969 } 41970 ctx.strokeStyle = 'rgba(0,0,0,1)'; 41971 ctx.circle(this.centerCoordinates.x, this.centerCoordinates.y, this.r); 41972 ctx.stroke(); 41973 41974 this.hueCircle = ctx.getImageData(0, 0, w, h); 41975 } 41976 this.generated = true; 41977 } 41978 41979 /** 41980 * move the selector. This is called by hammer functions. 41981 * 41982 * @param {Event} event The event 41983 * @private 41984 */ 41985 41986 }, { 41987 key: '_moveSelector', 41988 value: function _moveSelector(event) { 41989 var rect = this.colorPickerDiv.getBoundingClientRect(); 41990 var left = event.center.x - rect.left; 41991 var top = event.center.y - rect.top; 41992 41993 var centerY = 0.5 * this.colorPickerDiv.clientHeight; 41994 var centerX = 0.5 * this.colorPickerDiv.clientWidth; 41995 41996 var x = left - centerX; 41997 var y = top - centerY; 41998 41999 var angle = Math.atan2(x, y); 42000 var radius = 0.98 * Math.min(Math.sqrt(x * x + y * y), centerX); 42001 42002 var newTop = Math.cos(angle) * radius + centerY; 42003 var newLeft = Math.sin(angle) * radius + centerX; 42004 42005 this.colorPickerSelector.style.top = newTop - 0.5 * this.colorPickerSelector.clientHeight + 'px'; 42006 this.colorPickerSelector.style.left = newLeft - 0.5 * this.colorPickerSelector.clientWidth + 'px'; 42007 42008 // set color 42009 var h = angle / (2 * Math.PI); 42010 h = h < 0 ? h + 1 : h; 42011 var s = radius / this.r; 42012 var hsv = util.RGBToHSV(this.color.r, this.color.g, this.color.b); 42013 hsv.h = h; 42014 hsv.s = s; 42015 var rgba = util.HSVToRGB(hsv.h, hsv.s, hsv.v); 42016 rgba['a'] = this.color.a; 42017 this.color = rgba; 42018 42019 // update previews 42020 this.initialColorDiv.style.backgroundColor = 'rgba(' + this.initialColor.r + ',' + this.initialColor.g + ',' + this.initialColor.b + ',' + this.initialColor.a + ')'; 42021 this.newColorDiv.style.backgroundColor = 'rgba(' + this.color.r + ',' + this.color.g + ',' + this.color.b + ',' + this.color.a + ')'; 42022 } 42023 }]); 42024 return ColorPicker; 42025}(); 42026 42027exports['default'] = ColorPicker; 42028 42029/***/ }), 42030/* 180 */ 42031/***/ (function(module, exports, __webpack_require__) { 42032 42033"use strict"; 42034 42035 42036var moment = __webpack_require__(9); 42037var util = __webpack_require__(2); 42038var DataSet = __webpack_require__(11); 42039var DataView = __webpack_require__(12); 42040var Range = __webpack_require__(64); 42041var Core = __webpack_require__(65);
42042var TimeAxis = __webpack_require__(45); 42043var CurrentTime = __webpack_require__(67); 42044var CustomTime = __webpack_require__(46); 42045var LineGraph = __webpack_require__(106); 42046 42047var printStyle = __webpack_require__(15).printStyle; 42048var allOptions = __webpack_require__(113).allOptions; 42049var configureOptions = __webpack_require__(113).configureOptions; 42050 42051var Configurator = __webpack_require__(71)['default']; 42052var Validator = __webpack_require__(15)['default']; 42053 42054/** 42055 * Create a timeline visualization 42056 * @param {HTMLElement} container 42057 * @param {vis.DataSet | Array} [items] 42058 * @param {vis.DataSet | Array | vis.DataView | Object} [groups] 42059 * @param {Object} [options] See Graph2d.setOptions for the available options. 42060 * @constructor Graph2d 42061 * @extends Core 42062 */ 42063function Graph2d(container, items, groups, options) { 42064 // if the third element is options, the forth is groups (optionally); 42065 if (!(Array.isArray(groups) || groups instanceof DataSet || groups instanceof DataView) && groups instanceof Object) { 42066 var forthArgument = options; 42067 options = groups; 42068 groups = forthArgument; 42069 } 42070 42071 // TODO: REMOVE THIS in the next MAJOR release 42072 // see https://github.com/almende/vis/issues/2511 42073 if (options && options.throttleRedraw) { 42074 console.warn("Graph2d option \"throttleRedraw\" is DEPRICATED and no longer supported. It will be removed in the next MAJOR release."); 42075 } 42076 42077 var me = this; 42078 this.defaultOptions = { 42079 start: null, 42080 end: null, 42081 42082 autoResize: true, 42083 42084 orientation: { 42085 axis: 'bottom', // axis orientation: 'bottom', 'top', or 'both' 42086 item: 'bottom' // not relevant for Graph2d 42087 }, 42088 42089 moment: moment, 42090 42091 width: null, 42092 height: null, 42093 maxHeight: null, 42094 minHeight: null 42095 }; 42096 this.options = util.deepExtend({}, this.defaultOptions); 42097 42098 // Create the DOM, props, and emitter 42099 this._create(container); 42100 42101 // all components listed here will be repainted automatically 42102 this.components = []; 42103 42104 this.body = { 42105 dom: this.dom, 42106 domProps: this.props, 42107 emitter: { 42108 on: this.on.bind(this), 42109 off: this.off.bind(this), 42110 emit: this.emit.bind(this) 42111 }, 42112 hiddenDates: [], 42113 util: { 42114 toScreen: me._toScreen.bind(me), 42115 toGlobalScreen: me._toGlobalScreen.bind(me), // this refers to the root.width 42116 toTime: me._toTime.bind(me), 42117 toGlobalTime: me._toGlobalTime.bind(me) 42118 } 42119 }; 42120 42121 // range 42122 this.range = new Range(this.body); 42123 this.components.push(this.range); 42124 this.body.range = this.range; 42125 42126 // time axis 42127 this.timeAxis = new TimeAxis(this.body); 42128 this.components.push(this.timeAxis); 42129 //this.body.util.snap = this.timeAxis.snap.bind(this.timeAxis); 42130 42131 // current time bar 42132 this.currentTime = new CurrentTime(this.body); 42133 this.components.push(this.currentTime); 42134 42135 // item set 42136 this.linegraph = new LineGraph(this.body); 42137 42138 this.components.push(this.linegraph); 42139 42140 this.itemsData = null; // DataSet 42141 this.groupsData = null; // DataSet 42142 42143 42144 this.on('tap', function (event) { 42145 me.emit('click', me.getEventProperties(event)); 42146 }); 42147 this.on('doubletap', function (event) { 42148 me.emit('doubleClick', me.getEventProperties(event)); 42149 }); 42150 this.dom.root.oncontextmenu = function (event) { 42151 me.emit('contextmenu', me.getEventProperties(event)); 42152 }; 42153 42154 // apply options 42155 if (options) { 42156 this.setOptions(options); 42157 } 42158 42159 // IMPORTANT: THIS HAPPENS BEFORE SET ITEMS! 42160 if (groups) { 42161 this.setGroups(groups); 42162 } 42163 42164 // create itemset 42165 if (items) { 42166 this.setItems(items); 42167 } 42168 42169 // draw for the first time 42170 this._redraw(); 42171} 42172 42173// Extend the functionality from Core 42174Graph2d.prototype = new Core(); 42175 42176Graph2d.prototype.setOptions = function (options) { 42177 // validate options 42178 var errorFound = Validator.validate(options, allOptions); 42179 if (errorFound === true) { 42180 console.log('%cErrors have been found in the supplied options object.', printStyle); 42181 } 42182 42183 Core.prototype.setOptions.call(this, options); 42184}; 42185 42186/** 42187 * Set items 42188 * @param {vis.DataSet | Array | null} items 42189 */ 42190Graph2d.prototype.setItems = function (items) { 42191 var initialLoad = this.itemsData == null; 42192 42193 // convert to type DataSet when needed 42194 var newDataSet; 42195 if (!items) { 42196 newDataSet = null; 42197 } else if (items instanceof DataSet || items instanceof DataView) { 42198 newDataSet = items; 42199 } else { 42200 // turn an array into a dataset 42201 newDataSet = new DataSet(items, { 42202 type: { 42203 start: 'Date', 42204 end: 'Date' 42205 } 42206 }); 42207 } 42208 42209 // set items 42210 this.itemsData = newDataSet; 42211 this.linegraph && this.linegraph.setItems(newDataSet); 42212 42213 if (initialLoad) { 42214 if (this.options.start != undefined || this.options.end != undefined) { 42215 var start = this.options.start != undefined ? this.options.start : null; 42216 var end = this.options.end != undefined ? this.options.end : null; 42217 this.setWindow(start, end, { animation: false }); 42218 } else { 42219 this.fit({ animation: false }); 42220 } 42221 } 42222}; 42223 42224/** 42225 * Set groups 42226 * @param {vis.DataSet | Array} groups 42227 */ 42228Graph2d.prototype.setGroups = function (groups) { 42229 // convert to type DataSet when needed 42230 var newDataSet; 42231 if (!groups) { 42232 newDataSet = null; 42233 } else if (groups instanceof DataSet || groups instanceof DataView) { 42234 newDataSet = groups; 42235 } else { 42236 // turn an array into a dataset 42237 newDataSet = new DataSet(groups); 42238 } 42239 42240 this.groupsData = newDataSet; 42241 this.linegraph.setGroups(newDataSet); 42242}; 42243 42244/**
vendor: 4,944 bytes, lines 42245-42395
42245 * Returns an object containing an SVG element with the icon of the group (size determined by iconWidth and iconHeight), the label of the group (content) and the yAxisOrientation of the group (left or right). 42246 * @param {vis.GraphGroup.id} groupId 42247 * @param {number} width 42248 * @param {number} height 42249 * @returns {{icon: SVGElement, label: string, orientation: string}|string} 42250 */ 42251Graph2d.prototype.getLegend = function (groupId, width, height) { 42252 if (width === undefined) { 42253 width = 15; 42254 } 42255 if (height === undefined) { 42256 height = 15; 42257 } 42258 if (this.linegraph.groups[groupId] !== undefined) { 42259 return this.linegraph.groups[groupId].getLegend(width, height); 42260 } else { 42261 return "cannot find group:'" + groupId + "'"; 42262 } 42263}; 42264 42265/** 42266 * This checks if the visible option of the supplied group (by ID) is true or false. 42267 * @param {vis.GraphGroup.id} groupId 42268 * @returns {boolean} 42269 */ 42270Graph2d.prototype.isGroupVisible = function (groupId) { 42271 if (this.linegraph.groups[groupId] !== undefined) { 42272 return this.linegraph.groups[groupId].visible && (this.linegraph.options.groups.visibility[groupId] === undefined || this.linegraph.options.groups.visibility[groupId] == true); 42273 } else { 42274 return false; 42275 } 42276}; 42277 42278/** 42279 * Get the data range of the item set. 42280 * @returns {{min: Date, max: Date}} range A range with a start and end Date. 42281 * When no minimum is found, min==null 42282 * When no maximum is found, max==null 42283 */ 42284Graph2d.prototype.getDataRange = function () { 42285 var min = null; 42286 var max = null; 42287 42288 // calculate min from start filed 42289 for (var groupId in this.linegraph.groups) { 42290 if (this.linegraph.groups.hasOwnProperty(groupId)) { 42291 if (this.linegraph.groups[groupId].visible == true) { 42292 for (var i = 0; i < this.linegraph.groups[groupId].itemsData.length; i++) { 42293 var item = this.linegraph.groups[groupId].itemsData[i]; 42294 var value = util.convert(item.x, 'Date').valueOf(); 42295 min = min == null ? value : min > value ? value : min; 42296 max = max == null ? value : max < value ? value : max; 42297 } 42298 } 42299 } 42300 } 42301 42302 return { 42303 min: min != null ? new Date(min) : null, 42304 max: max != null ? new Date(max) : null 42305 }; 42306}; 42307 42308/** 42309 * Generate Timeline related information from an event 42310 * @param {Event} event 42311 * @return {Object} An object with related information, like on which area 42312 * The event happened, whether clicked on an item, etc. 42313 */ 42314Graph2d.prototype.getEventProperties = function (event) { 42315 var clientX = event.center ? event.center.x : event.clientX; 42316 var clientY = event.center ? event.center.y : event.clientY; 42317 var x = clientX - util.getAbsoluteLeft(this.dom.centerContainer); 42318 var y = clientY - util.getAbsoluteTop(this.dom.centerContainer); 42319 var time = this._toTime(x); 42320 42321 var customTime = CustomTime.customTimeFromTarget(event); 42322 42323 var element = util.getTarget(event); 42324 var what = null; 42325 if (util.hasParent(element, this.timeAxis.dom.foreground)) { 42326 what = 'axis'; 42327 } else if (this.timeAxis2 && util.hasParent(element, this.timeAxis2.dom.foreground)) { 42328 what = 'axis'; 42329 } else if (util.hasParent(element, this.linegraph.yAxisLeft.dom.frame)) { 42330 what = 'data-axis'; 42331 } else if (util.hasParent(element, this.linegraph.yAxisRight.dom.frame)) { 42332 what = 'data-axis'; 42333 } else if (util.hasParent(element, this.linegraph.legendLeft.dom.frame)) { 42334 what = 'legend'; 42335 } else if (util.hasParent(element, this.linegraph.legendRight.dom.frame)) { 42336 what = 'legend'; 42337 } else if (customTime != null) { 42338 what = 'custom-time'; 42339 } else if (util.hasParent(element, this.currentTime.bar)) { 42340 what = 'current-time'; 42341 } else if (util.hasParent(element, this.dom.center)) { 42342 what = 'background'; 42343 } 42344 42345 var value = []; 42346 var yAxisLeft = this.linegraph.yAxisLeft; 42347 var yAxisRight = this.linegraph.yAxisRight; 42348 if (!yAxisLeft.hidden && this.itemsData.length > 0) { 42349 value.push(yAxisLeft.screenToValue(y)); 42350 } 42351 if (!yAxisRight.hidden && this.itemsData.length > 0) { 42352 value.push(yAxisRight.screenToValue(y)); 42353 } 42354 42355 return { 42356 event: event, 42357 what: what, 42358 pageX: event.srcEvent ? event.srcEvent.pageX : event.pageX, 42359 pageY: event.srcEvent ? event.srcEvent.pageY : event.pageY, 42360 x: x, 42361 y: y, 42362 time: time, 42363 value: value 42364 }; 42365}; 42366 42367/** 42368 * Load a configurator 42369 * @return {Object} 42370 * @private 42371 */ 42372Graph2d.prototype._createConfigurator = function () { 42373 return new Configurator(this, this.dom.container, configureOptions); 42374}; 42375 42376module.exports = Graph2d; 42377 42378/***/ }), 42379/* 181 */ 42380/***/ (function(module, exports, __webpack_require__) { 42381 42382"use strict"; 42383 42384 42385// utils 42386exports.util = __webpack_require__(2); 42387exports.DOMutil = __webpack_require__(14); 42388 42389// data 42390exports.DataSet = __webpack_require__(11); 42391exports.DataView = __webpack_require__(12); 42392exports.Queue = __webpack_require__(43); 42393 42394// Network 42395exports.Network = __webpack_require__(182);
42396exports.network = { 42397 Images: __webpack_require__(116), 42398 dotparser: __webpack_require__(114), 42399 gephiParser: __webpack_require__(115), 42400 allOptions: __webpack_require__(122) 42401}; 42402exports.network.convertDot = function (input) { 42403 return exports.network.dotparser.DOTToGraph(input); 42404}; 42405exports.network.convertGephi = function (input, options) { 42406 return exports.network.gephiParser.parseGephi(input, options); 42407}; 42408 42409// bundled external libraries 42410exports.moment = __webpack_require__(9); 42411exports.Hammer = __webpack_require__(10); 42412exports.keycharm = __webpack_require__(35); 42413 42414/***/ }), 42415/* 182 */ 42416/***/ (function(module, exports, __webpack_require__) { 42417 42418"use strict"; 42419 42420 42421// Load custom shapes into CanvasRenderingContext2D 42422__webpack_require__(183); 42423 42424var Emitter = __webpack_require__(44); 42425var util = __webpack_require__(2); 42426var dotparser = __webpack_require__(114); 42427var gephiParser = __webpack_require__(115); 42428var Activator = __webpack_require__(97); 42429var locales = __webpack_require__(184); 42430 42431var Images = __webpack_require__(116)['default']; 42432var Groups = __webpack_require__(186)['default']; 42433var NodesHandler = __webpack_require__(187)['default']; 42434var EdgesHandler = __webpack_require__(214)['default']; 42435var PhysicsEngine = __webpack_require__(220)['default']; 42436var ClusterEngine = __webpack_require__(227)['default']; 42437var CanvasRenderer = __webpack_require__(229)['default']; 42438var Canvas = __webpack_require__(230)['default']; 42439var View = __webpack_require__(231)['default']; 42440var InteractionHandler = __webpack_require__(232)['default']; 42441var SelectionHandler = __webpack_require__(234)['default']; 42442var LayoutEngine = __webpack_require__(235)['default']; 42443var ManipulationSystem = __webpack_require__(237)['default']; 42444var Configurator = __webpack_require__(71)['default']; 42445var Validator = __webpack_require__(15)['default']; 42446 42447var _require = __webpack_require__(15), 42448 printStyle = _require.printStyle; 42449 42450var _require2 = __webpack_require__(122), 42451 allOptions = _require2.allOptions, 42452 configureOptions = _require2.configureOptions; 42453 42454var KamadaKawai = __webpack_require__(238)['default']; 42455 42456/** 42457 * Create a network visualization, displaying nodes and edges. 42458 * 42459 * @param {Element} container The DOM element in which the Network will 42460 * be created. Normally a div element. 42461 * @param {Object} data An object containing parameters 42462 * {Array} nodes 42463 * {Array} edges 42464 * @param {Object} options Options 42465 * @constructor Network 42466 */ 42467function Network(container, data, options) { 42468 var _this = this; 42469 42470 if (!(this instanceof Network)) { 42471 throw new SyntaxError('Constructor must be called with the new operator'); 42472 } 42473 42474 // set constant values 42475 this.options = {}; 42476 this.defaultOptions = { 42477 locale: 'en', 42478 locales: locales, 42479 clickToUse: false 42480 }; 42481 util.extend(this.options, this.defaultOptions); 42482 42483 /** 42484 * Containers for nodes and edges. 42485 * 42486 * 'edges' and 'nodes' contain the full definitions of all the network elements. 42487 * 'nodeIndices' and 'edgeIndices' contain the id's of the active elements. 42488 * 42489 * The distinction is important, because a defined node need not be active, i.e. 42490 * visible on the canvas. This happens in particular when clusters are defined, in 42491 * that case there will be nodes and edges not displayed.
42492 * The bottom line is that all code with actions related to visibility, *must* use 42493 * 'nodeIndices' and 'edgeIndices', not 'nodes' and 'edges' directly. 42494 */ 42495 this.body = { 42496 container: container, 42497 42498 // See comment above for following fields 42499 nodes: {}, 42500 nodeIndices: [], 42501 edges: {}, 42502 edgeIndices: [], 42503 42504 emitter: { 42505 on: this.on.bind(this), 42506 off: this.off.bind(this), 42507 emit: this.emit.bind(this), 42508 once: this.once.bind(this) 42509 }, 42510 eventListeners: { 42511 onTap: function onTap() {}, 42512 onTouch: function onTouch() {}, 42513 onDoubleTap: function onDoubleTap() {}, 42514 onHold: function onHold() {}, 42515 onDragStart: function onDragStart() {}, 42516 onDrag: function onDrag() {}, 42517 onDragEnd: function onDragEnd() {}, 42518 onMouseWheel: function onMouseWheel() {}, 42519 onPinch: function onPinch() {}, 42520 onMouseMove: function onMouseMove() {}, 42521 onRelease: function onRelease() {}, 42522 onContext: function onContext() {} 42523 }, 42524 data: { 42525 nodes: null, // A DataSet or DataView 42526 edges: null // A DataSet or DataView 42527 }, 42528 functions: { 42529 createNode: function createNode() {}, 42530 createEdge: function createEdge() {}, 42531 getPointer: function getPointer() {} 42532 }, 42533 modules: {}, 42534 view: { 42535 scale: 1, 42536 translation: { x: 0, y: 0 } 42537 } 42538 }; 42539 42540 // bind the event listeners 42541 this.bindEventListeners(); 42542 42543 // setting up all modules 42544 this.images = new Images(function () { 42545 return _this.body.emitter.emit("_requestRedraw"); 42546 }); // object with images 42547 this.groups = new Groups(); // object with groups 42548 this.canvas = new Canvas(this.body); // DOM handler 42549 this.selectionHandler = new SelectionHandler(this.body, this.canvas); // Selection handler 42550 this.interactionHandler = new InteractionHandler(this.body, this.canvas, this.selectionHandler); // Interaction handler handles all the hammer bindings (that are bound by canvas), key 42551 this.view = new View(this.body, this.canvas); // camera handler, does animations and zooms 42552 this.renderer = new CanvasRenderer(this.body, this.canvas); // renderer, starts renderloop, has events that modules can hook into 42553 this.physics = new PhysicsEngine(this.body); // physics engine, does all the simulations 42554 this.layoutEngine = new LayoutEngine(this.body); // layout engine for inital layout and hierarchical layout 42555 this.clustering = new ClusterEngine(this.body); // clustering api 42556 this.manipulation = new ManipulationSystem(this.body, this.canvas, this.selectionHandler); // data manipulation system 42557 42558 this.nodesHandler = new NodesHandler(this.body, this.images, this.groups, this.layoutEngine); // Handle adding, deleting and updating of nodes as well as global options 42559 this.edgesHandler = new EdgesHandler(this.body, this.images, this.groups); // Handle adding, deleting and updating of edges as well as global options 42560 42561 this.body.modules["kamadaKawai"] = new KamadaKawai(this.body, 150, 0.05); // Layouting algorithm. 42562 this.body.modules["clustering"] = this.clustering; 42563 42564 // create the DOM elements 42565 this.canvas._create(); 42566 42567 // apply options 42568 this.setOptions(options); 42569 42570 // load data (the disable start variable will be the same as the enabled clustering) 42571 this.setData(data); 42572} 42573 42574// Extend Network with an Emitter mixin 42575Emitter(Network.prototype); 42576 42577/** 42578 * Set options 42579 * @param {Object} options 42580 */ 42581Network.prototype.setOptions = function (options) { 42582 var _this2 = this; 42583 42584 if (options !== undefined) { 42585 var errorFound = Validator.validate(options, allOptions); 42586 if (errorFound === true) { 42587 console.log('%cErrors have been found in the supplied options object.', printStyle); 42588 } 42589 42590 // copy the global fields over 42591 var fields = ['locale', 'locales', 'clickToUse']; 42592 util.selectiveDeepExtend(fields, this.options, options); 42593 42594 // the hierarchical system can adapt the edges and the physics to it's own options because not all combinations work with the hierarichical system. 42595 options = this.layoutEngine.setOptions(options.layout, options); 42596 42597 this.canvas.setOptions(options); // options for canvas are in globals 42598 42599 // pass the options to the modules 42600 this.groups.setOptions(options.groups); 42601 this.nodesHandler.setOptions(options.nodes); 42602 this.edgesHandler.setOptions(options.edges); 42603 this.physics.setOptions(options.physics); 42604 this.manipulation.setOptions(options.manipulation, options, this.options); // manipulation uses the locales in the globals 42605 42606 this.interactionHandler.setOptions(options.interaction); 42607 this.renderer.setOptions(options.interaction); // options for rendering are in interaction 42608 this.selectionHandler.setOptions(options.interaction); // options for selection are in interaction 42609 42610 // reload the settings of the nodes to apply changes in groups that are not referenced by pointer. 42611 if (options.groups !== undefined) { 42612 this.body.emitter.emit("refreshNodes"); 42613 } 42614 // these two do not have options at the moment, here for completeness 42615 //this.view.setOptions(options.view); 42616 //this.clustering.setOptions(options.clustering); 42617
42618 if ('configure' in options) { 42619 if (!this.configurator) { 42620 this.configurator = new Configurator(this, this.body.container, configureOptions, this.canvas.pixelRatio); 42621 } 42622 42623 this.configurator.setOptions(options.configure); 42624 } 42625 42626 // if the configuration system is enabled, copy all options and put them into the config system 42627 if (this.configurator && this.configurator.options.enabled === true) { 42628 var networkOptions = { nodes: {}, edges: {}, layout: {}, interaction: {}, manipulation: {}, physics: {}, global: {} }; 42629 util.deepExtend(networkOptions.nodes, this.nodesHandler.options); 42630 util.deepExtend(networkOptions.edges, this.edgesHandler.options); 42631 util.deepExtend(networkOptions.layout, this.layoutEngine.options); 42632 // load the selectionHandler and render default options in to the interaction group 42633 util.deepExtend(networkOptions.interaction, this.selectionHandler.options); 42634 util.deepExtend(networkOptions.interaction, this.renderer.options); 42635 42636 util.deepExtend(networkOptions.interaction, this.interactionHandler.options); 42637 util.deepExtend(networkOptions.manipulation, this.manipulation.options); 42638 util.deepExtend(networkOptions.physics, this.physics.options); 42639 42640 // load globals into the global object 42641 util.deepExtend(networkOptions.global, this.canvas.options); 42642 util.deepExtend(networkOptions.global, this.options); 42643 42644 this.configurator.setModuleOptions(networkOptions); 42645 } 42646 42647 // handle network global options 42648 if (options.clickToUse !== undefined) { 42649 if (options.clickToUse === true) { 42650 if (this.activator === undefined) { 42651 this.activator = new Activator(this.canvas.frame); 42652 this.activator.on('change', function () { 42653 _this2.body.emitter.emit("activate"); 42654 }); 42655 } 42656 } else { 42657 if (this.activator !== undefined) { 42658 this.activator.destroy(); 42659 delete this.activator; 42660 } 42661 this.body.emitter.emit("activate"); 42662 } 42663 } else { 42664 this.body.emitter.emit("activate"); 42665 } 42666 42667 this.canvas.setSize(); 42668 // start the physics simulation. Can be safely called multiple times. 42669 this.body.emitter.emit("startSimulation"); 42670 } 42671}; 42672 42673/** 42674 * Update the visible nodes and edges list with the most recent node state. 42675 * 42676 * Visible nodes are stored in this.body.nodeIndices. 42677 * Visible edges are stored in this.body.edgeIndices. 42678 * A node or edges is visible if it is not hidden or clustered. 42679 * 42680 * @private 42681 */ 42682Network.prototype._updateVisibleIndices = function () { 42683 var nodes = this.body.nodes; 42684 var edges = this.body.edges; 42685 this.body.nodeIndices = []; 42686 this.body.edgeIndices = []; 42687 42688 for (var nodeId in nodes) { 42689 if (nodes.hasOwnProperty(nodeId)) { 42690 if (!this.clustering._isClusteredNode(nodeId) && nodes[nodeId].options.hidden === false) { 42691 this.body.nodeIndices.push(nodes[nodeId].id); 42692 } 42693 } 42694 } 42695 42696 for (var edgeId in edges) { 42697 if (edges.hasOwnProperty(edgeId)) { 42698 var edge = edges[edgeId]; 42699 42700 // It can happen that this is executed *after* a node edge has been removed, 42701 // but *before* the edge itself has been removed. Taking this into account. 42702 var fromNode = nodes[edge.fromId]; 42703 var toNode = nodes[edge.toId]; 42704 var edgeNodesPresent = fromNode !== undefined && toNode !== undefined; 42705 42706 var isVisible = !this.clustering._isClusteredEdge(edgeId) && edge.options.hidden === false && edgeNodesPresent && fromNode.options.hidden === false // Also hidden if any of its connecting nodes are hidden 42707 && toNode.options.hidden === false; // idem 42708 42709 if (isVisible) { 42710 this.body.edgeIndices.push(edge.id); 42711 } 42712 } 42713 } 42714}; 42715 42716/** 42717 * Bind all events 42718 */ 42719Network.prototype.bindEventListeners = function () { 42720 var _this3 = this; 42721 42722 // This event will trigger a rebuilding of the cache everything. 42723 // Used when nodes or edges have been added or removed. 42724 this.body.emitter.on("_dataChanged", function () { 42725 _this3.edgesHandler._updateState(); 42726 _this3.body.emitter.emit("_dataUpdated"); 42727 }); 42728 42729 // this is called when options of EXISTING nodes or edges have changed. 42730 this.body.emitter.on("_dataUpdated", function () { 42731 // Order important in following block 42732 _this3.clustering._updateState(); 42733 _this3._updateVisibleIndices(); 42734
42735 _this3._updateValueRange(_this3.body.nodes); 42736 _this3._updateValueRange(_this3.body.edges); 42737 // start simulation (can be called safely, even if already running) 42738 _this3.body.emitter.emit("startSimulation"); 42739 _this3.body.emitter.emit("_requestRedraw"); 42740 }); 42741}; 42742 42743/** 42744 * Set nodes and edges, and optionally options as well. 42745 * 42746 * @param {Object} data Object containing parameters: 42747 * {Array | DataSet | DataView} [nodes] Array with nodes 42748 * {Array | DataSet | DataView} [edges] Array with edges 42749 * {String} [dot] String containing data in DOT format 42750 * {String} [gephi] String containing data in gephi JSON format 42751 * {Options} [options] Object with options 42752 */ 42753Network.prototype.setData = function (data) { 42754 // reset the physics engine. 42755 this.body.emitter.emit("resetPhysics"); 42756 this.body.emitter.emit("_resetData"); 42757 42758 // unselect all to ensure no selections from old data are carried over. 42759 this.selectionHandler.unselectAll(); 42760 42761 if (data && data.dot && (data.nodes || data.edges)) { 42762 throw new SyntaxError('Data must contain either parameter "dot" or ' + ' parameter pair "nodes" and "edges", but not both.'); 42763 } 42764 42765 // set options 42766 this.setOptions(data && data.options); 42767 // set all data 42768 if (data && data.dot) { 42769 console.log('The dot property has been deprecated. Please use the static convertDot method to convert DOT into vis.network format and use the normal data format with nodes and edges. This converter is used like this: var data = vis.network.convertDot(dotString);'); 42770 // parse DOT file 42771 var dotData = dotparser.DOTToGraph(data.dot); 42772 this.setData(dotData); 42773 return; 42774 } else if (data && data.gephi) { 42775 // parse DOT file 42776 console.log('The gephi property has been deprecated. Please use the static convertGephi method to convert gephi into vis.network format and use the normal data format with nodes and edges. This converter is used like this: var data = vis.network.convertGephi(gephiJson);'); 42777 var gephiData = gephiParser.parseGephi(data.gephi); 42778 this.setData(gephiData); 42779 return; 42780 } else { 42781 this.nodesHandler.setData(data && data.nodes, true); 42782 this.edgesHandler.setData(data && data.edges, true); 42783 } 42784 42785 // emit change in data 42786 this.body.emitter.emit("_dataChanged"); 42787 42788 // emit data loaded 42789 this.body.emitter.emit("_dataLoaded"); 42790 42791 // find a stable position or start animating to a stable position 42792 this.body.emitter.emit("initPhysics"); 42793}; 42794 42795/** 42796 * Cleans up all bindings of the network, removing it fully from the memory IF the variable is set to null after calling this function. 42797 * var network = new vis.Network(..); 42798 * network.destroy(); 42799 * network = null; 42800 */ 42801Network.prototype.destroy = function () { 42802 this.body.emitter.emit("destroy"); 42803 // clear events 42804 this.body.emitter.off(); 42805 this.off(); 42806 42807 // delete modules 42808 delete this.groups; 42809 delete this.canvas; 42810 delete this.selectionHandler; 42811 delete this.interactionHandler; 42812 delete this.view; 42813 delete this.renderer; 42814 delete this.physics; 42815 delete this.layoutEngine; 42816 delete this.clustering; 42817 delete this.manipulation; 42818 delete this.nodesHandler; 42819 delete this.edgesHandler; 42820 delete this.configurator; 42821 delete this.images; 42822 42823 for (var nodeId in this.body.nodes) { 42824 if (!this.body.nodes.hasOwnProperty(nodeId)) continue; 42825 delete this.body.nodes[nodeId]; 42826 } 42827 42828 for (var edgeId in this.body.edges) { 42829 if (!this.body.edges.hasOwnProperty(edgeId)) continue; 42830 delete this.body.edges[edgeId]; 42831 } 42832 42833 // remove the container and everything inside it recursively 42834 util.recursiveDOMDelete(this.body.container); 42835}; 42836 42837/** 42838 * Update the values of all object in the given array according to the current 42839 * value range of the objects in the array. 42840 * @param {Object} obj An object containing a set of Edges or Nodes 42841 * The objects must have a method getValue() and 42842 * setValueRange(min, max). 42843 * @private 42844 */
42845Network.prototype._updateValueRange = function (obj) { 42846 var id; 42847 42848 // determine the range of the objects 42849 var valueMin = undefined; 42850 var valueMax = undefined; 42851 var valueTotal = 0; 42852 for (id in obj) { 42853 if (obj.hasOwnProperty(id)) { 42854 var value = obj[id].getValue(); 42855 if (value !== undefined) { 42856 valueMin = valueMin === undefined ? value : Math.min(value, valueMin); 42857 valueMax = valueMax === undefined ? value : Math.max(value, valueMax); 42858 valueTotal += value; 42859 } 42860 } 42861 } 42862 42863 // adjust the range of all objects 42864 if (valueMin !== undefined && valueMax !== undefined) { 42865 for (id in obj) { 42866 if (obj.hasOwnProperty(id)) { 42867 obj[id].setValueRange(valueMin, valueMax, valueTotal); 42868 } 42869 } 42870 } 42871}; 42872 42873/** 42874 * Returns true when the Network is active. 42875 * @returns {boolean} 42876 */ 42877Network.prototype.isActive = function () { 42878 return !this.activator || this.activator.active; 42879}; 42880 42881Network.prototype.setSize = function () { 42882 return this.canvas.setSize.apply(this.canvas, arguments); 42883}; 42884Network.prototype.canvasToDOM = function () { 42885 return this.canvas.canvasToDOM.apply(this.canvas, arguments); 42886}; 42887Network.prototype.DOMtoCanvas = function () { 42888 return this.canvas.DOMtoCanvas.apply(this.canvas, arguments); 42889}; 42890Network.prototype.findNode = function () { 42891 return this.clustering.findNode.apply(this.clustering, arguments); 42892}; 42893Network.prototype.isCluster = function () { 42894 return this.clustering.isCluster.apply(this.clustering, arguments); 42895}; 42896Network.prototype.openCluster = function () { 42897 return this.clustering.openCluster.apply(this.clustering, arguments); 42898}; 42899Network.prototype.cluster = function () { 42900 return this.clustering.cluster.apply(this.clustering, arguments); 42901}; 42902Network.prototype.getNodesInCluster = function () { 42903 return this.clustering.getNodesInCluster.apply(this.clustering, arguments); 42904}; 42905Network.prototype.clusterByConnection = function () { 42906 return this.clustering.clusterByConnection.apply(this.clustering, arguments); 42907}; 42908Network.prototype.clusterByHubsize = function () { 42909 return this.clustering.clusterByHubsize.apply(this.clustering, arguments); 42910}; 42911Network.prototype.clusterOutliers = function () { 42912 return this.clustering.clusterOutliers.apply(this.clustering, arguments); 42913}; 42914Network.prototype.getSeed = function () { 42915 return this.layoutEngine.getSeed.apply(this.layoutEngine, arguments); 42916}; 42917Network.prototype.enableEditMode = function () { 42918 return this.manipulation.enableEditMode.apply(this.manipulation, arguments); 42919}; 42920Network.prototype.disableEditMode = function () { 42921 return this.manipulation.disableEditMode.apply(this.manipulation, arguments); 42922}; 42923Network.prototype.addNodeMode = function () { 42924 return this.manipulation.addNodeMode.apply(this.manipulation, arguments); 42925}; 42926Network.prototype.editNode = function () { 42927 return this.manipulation.editNode.apply(this.manipulation, arguments); 42928}; 42929Network.prototype.editNodeMode = function () { 42930 console.log("Deprecated: Please use editNode instead of editNodeMode.");return this.manipulation.editNode.apply(this.manipulation, arguments); 42931}; 42932Network.prototype.addEdgeMode = function () { 42933 return this.manipulation.addEdgeMode.apply(this.manipulation, arguments); 42934}; 42935Network.prototype.editEdgeMode = function () { 42936 return this.manipulation.editEdgeMode.apply(this.manipulation, arguments); 42937}; 42938Network.prototype.deleteSelected = function () { 42939 return this.manipulation.deleteSelected.apply(this.manipulation, arguments); 42940}; 42941Network.prototype.getPositions = function () { 42942 return this.nodesHandler.getPositions.apply(this.nodesHandler, arguments); 42943}; 42944Network.prototype.storePositions = function () { 42945 return this.nodesHandler.storePositions.apply(this.nodesHandler, arguments); 42946}; 42947Network.prototype.moveNode = function () { 42948 return this.nodesHandler.moveNode.apply(this.nodesHandler, arguments); 42949}; 42950Network.prototype.getBoundingBox = function () { 42951 return this.nodesHandler.getBoundingBox.apply(this.nodesHandler, arguments); 42952}; 42953Network.prototype.getConnectedNodes = function (objectId) { 42954 if (this.body.nodes[objectId] !== undefined) { 42955 return this.nodesHandler.getConnectedNodes.apply(this.nodesHandler, arguments); 42956 } else { 42957 return this.edgesHandler.getConnectedNodes.apply(this.edgesHandler, arguments); 42958 } 42959}; 42960Network.prototype.getConnectedEdges = function () { 42961 return this.nodesHandler.getConnectedEdges.apply(this.nodesHandler, arguments); 42962}; 42963Network.prototype.startSimulation = function () { 42964 return this.physics.startSimulation.apply(this.physics, arguments); 42965}; 42966Network.prototype.stopSimulation = function () { 42967 return this.physics.stopSimulation.apply(this.physics, arguments); 42968}; 42969Network.prototype.stabilize = function () { 42970 return this.physics.stabilize.apply(this.physics, arguments); 42971}; 42972Network.prototype.getSelection = function () { 42973 return this.selectionHandler.getSelection.apply(this.selectionHandler, arguments); 42974}; 42975Network.prototype.setSelection = function () { 42976 return this.selectionHandler.setSelection.apply(this.selectionHandler, arguments); 42977}; 42978Network.prototype.getSelectedNodes = function () { 42979 return this.selectionHandler.getSelectedNodes.apply(this.selectionHandler, arguments); 42980}; 42981Network.prototype.getSelectedEdges = function () { 42982 return this.selectionHandler.getSelectedEdges.apply(this.selectionHandler, arguments); 42983}; 42984Network.prototype.getNodeAt = function () { 42985 var node = this.selectionHandler.getNodeAt.apply(this.selectionHandler, arguments); 42986 if (node !== undefined && node.id !== undefined) { 42987 return node.id; 42988 } 42989 return node; 42990}; 42991Network.prototype.getEdgeAt = function () { 42992 var edge = this.selectionHandler.getEdgeAt.apply(this.selectionHandler, arguments); 42993 if (edge !== undefined && edge.id !== undefined) { 42994 return edge.id; 42995 } 42996 return edge; 42997}; 42998Network.prototype.selectNodes = function () { 42999 return this.selectionHandler.selectNodes.apply(this.selectionHandler, arguments); 43000}; 43001Network.prototype.selectEdges = function () { 43002 return this.selectionHandler.selectEdges.apply(this.selectionHandler, arguments); 43003}; 43004Network.prototype.unselectAll = function () { 43005 this.selectionHandler.unselectAll.apply(this.selectionHandler, arguments); 43006 this.redraw(); 43007}; 43008Network.prototype.redraw = function () { 43009 return this.renderer.redraw.apply(this.renderer, arguments); 43010}; 43011Network.prototype.getScale = function () { 43012 return this.view.getScale.apply(this.view, arguments); 43013}; 43014Network.prototype.getViewPosition = function () { 43015 return this.view.getViewPosition.apply(this.view, arguments); 43016}; 43017Network.prototype.fit = function () { 43018 return this.view.fit.apply(this.view, arguments); 43019}; 43020Network.prototype.moveTo = function () { 43021 return this.view.moveTo.apply(this.view, arguments); 43022};
43023Network.prototype.focus = function () { 43024 return this.view.focus.apply(this.view, arguments); 43025}; 43026Network.prototype.releaseNode = function () { 43027 return this.view.releaseNode.apply(this.view, arguments); 43028}; 43029Network.prototype.getOptionsFromConfigurator = function () { 43030 var options = {}; 43031 if (this.configurator) { 43032 options = this.configurator.getOptions.apply(this.configurator); 43033 } 43034 return options; 43035}; 43036 43037module.exports = Network; 43038 43039/***/ }), 43040/* 183 */ 43041/***/ (function(module, exports, __webpack_require__) { 43042 43043"use strict"; 43044 43045 43046/** 43047 * Canvas shapes used by Network 43048 */ 43049if (typeof CanvasRenderingContext2D !== 'undefined') { 43050 43051 /** 43052 * Draw a circle shape 43053 * 43054 * @param {number} x 43055 * @param {number} y 43056 * @param {number} r 43057 */ 43058 CanvasRenderingContext2D.prototype.circle = function (x, y, r) { 43059 this.beginPath(); 43060 this.arc(x, y, r, 0, 2 * Math.PI, false); 43061 this.closePath(); 43062 }; 43063 43064 /** 43065 * Draw a square shape 43066 * @param {number} x horizontal center 43067 * @param {number} y vertical center 43068 * @param {number} r size, width and height of the square 43069 */ 43070 CanvasRenderingContext2D.prototype.square = function (x, y, r) { 43071 this.beginPath(); 43072 this.rect(x - r, y - r, r * 2, r * 2); 43073 this.closePath(); 43074 }; 43075 43076 /** 43077 * Draw a triangle shape 43078 * @param {number} x horizontal center 43079 * @param {number} y vertical center 43080 * @param {number} r radius, half the length of the sides of the triangle 43081 */ 43082 CanvasRenderingContext2D.prototype.triangle = function (x, y, r) { 43083 // http://en.wikipedia.org/wiki/Equilateral_triangle 43084 this.beginPath(); 43085 43086 // the change in radius and the offset is here to center the shape 43087 r *= 1.15; 43088 y += 0.275 * r; 43089 43090 var s = r * 2; 43091 var s2 = s / 2; 43092 var ir = Math.sqrt(3) / 6 * s; // radius of inner circle 43093 var h = Math.sqrt(s * s - s2 * s2); // height 43094 43095 43096 this.moveTo(x, y - (h - ir)); 43097 this.lineTo(x + s2, y + ir); 43098 this.lineTo(x - s2, y + ir); 43099 this.lineTo(x, y - (h - ir)); 43100 this.closePath(); 43101 }; 43102 43103 /** 43104 * Draw a triangle shape in downward orientation 43105 * @param {number} x horizontal center 43106 * @param {number} y vertical center 43107 * @param {number} r radius 43108 */ 43109 CanvasRenderingContext2D.prototype.triangleDown = function (x, y, r) { 43110 // http://en.wikipedia.org/wiki/Equilateral_triangle 43111 this.beginPath(); 43112 43113 // the change in radius and the offset is here to center the shape 43114 r *= 1.15; 43115 y -= 0.275 * r; 43116 43117 var s = r * 2; 43118 var s2 = s / 2; 43119 var ir = Math.sqrt(3) / 6 * s; // radius of inner circle 43120 var h = Math.sqrt(s * s - s2 * s2); // height 43121 43122 this.moveTo(x, y + (h - ir)); 43123 this.lineTo(x + s2, y - ir); 43124 this.lineTo(x - s2, y - ir); 43125 this.lineTo(x, y + (h - ir)); 43126 this.closePath(); 43127 }; 43128 43129 /** 43130 * Draw a star shape, a star with 5 points 43131 * @param {number} x horizontal center 43132 * @param {number} y vertical center 43133 * @param {number} r radius, half the length of the sides of the triangle 43134 */ 43135 CanvasRenderingContext2D.prototype.star = function (x, y, r) { 43136 // http://www.html5canvastutorials.com/labs/html5-canvas-star-spinner/ 43137 this.beginPath(); 43138 43139 // the change in radius and the offset is here to center the shape 43140 r *= 0.82; 43141 y += 0.1 * r; 43142 43143 for (var n = 0; n < 10; n++) { 43144 var radius = n % 2 === 0 ? r * 1.3 : r * 0.5; 43145 this.lineTo(x + radius * Math.sin(n * 2 * Math.PI / 10), y - radius * Math.cos(n * 2 * Math.PI / 10)); 43146 } 43147 43148 this.closePath(); 43149 }; 43150 43151 /** 43152 * Draw a Diamond shape 43153 * @param {number} x horizontal center 43154 * @param {number} y vertical center 43155 * @param {number} r radius, half the length of the sides of the triangle 43156 */ 43157 CanvasRenderingContext2D.prototype.diamond = function (x, y, r) { 43158 // http://www.html5canvastutorials.com/labs/html5-canvas-star-spinner/ 43159 this.beginPath(); 43160 43161 this.lineTo(x, y + r); 43162 this.lineTo(x + r, y); 43163 this.lineTo(x, y - r); 43164 this.lineTo(x - r, y); 43165 43166 this.closePath(); 43167 }; 43168 43169 /** 43170 * http://stackoverflow.com/questions/1255512/how-to-draw-a-rounded-rectangle-on-html-canvas 43171 * 43172 * @param {number} x 43173 * @param {number} y 43174 * @param {number} w 43175 * @param {number} h 43176 * @param {number} r 43177 */ 43178 CanvasRenderingContext2D.prototype.roundRect = function (x, y, w, h, r) { 43179 var r2d = Math.PI / 180; 43180 if (w - 2 * r < 0) { 43181 r = w / 2; 43182 } //ensure that the radius isn't too large for x 43183 if (h - 2 * r < 0) { 43184 r = h / 2; 43185 } //ensure that the radius isn't too large for y 43186 this.beginPath(); 43187 this.moveTo(x + r, y); 43188 this.lineTo(x + w - r, y); 43189 this.arc(x + w - r, y + r, r, r2d * 270, r2d * 360, false); 43190 this.lineTo(x + w, y + h - r); 43191 this.arc(x + w - r, y + h - r, r, 0, r2d * 90, false); 43192 this.lineTo(x + r, y + h); 43193 this.arc(x + r, y + h - r, r, r2d * 90, r2d * 180, false); 43194 this.lineTo(x, y + r); 43195 this.arc(x + r, y + r, r, r2d * 180, r2d * 270, false); 43196 this.closePath(); 43197 }; 43198 43199 /** 43200 * http://stackoverflow.com/questions/2172798/how-to-draw-an-oval-in-html5-canvas 43201 * 43202 * Postfix '_vis' added to discern it from standard method ellipse(). 43203 * 43204 * @param {number} x 43205 * @param {number} y 43206 * @param {number} w 43207 * @param {number} h 43208 */ 43209 CanvasRenderingContext2D.prototype.ellipse_vis = function (x, y, w, h) { 43210 var kappa = .5522848, 43211 ox = w / 2 * kappa, 43212 // control point offset horizontal 43213 oy = h / 2 * kappa, 43214 // control point offset vertical 43215 xe = x + w, 43216 // x-end 43217 ye = y + h, 43218 // y-end 43219 xm = x + w / 2, 43220 // x-middle 43221 ym = y + h / 2; // y-middle 43222 43223 this.beginPath(); 43224 this.moveTo(x, ym); 43225 this.bezierCurveTo(x, ym - oy, xm - ox, y, xm, y); 43226 this.bezierCurveTo(xm + ox, y, xe, ym - oy, xe, ym); 43227 this.bezierCurveTo(xe, ym + oy, xm + ox, ye, xm, ye); 43228 this.bezierCurveTo(xm - ox, ye, x, ym + oy, x, ym); 43229 this.closePath(); 43230 }; 43231 43232 /** 43233 * http://stackoverflow.com/questions/2172798/how-to-draw-an-oval-in-html5-canvas 43234 * 43235 * @param {number} x 43236 * @param {number} y 43237 * @param {number} w 43238 * @param {number} h 43239 */ 43240 CanvasRenderingContext2D.prototype.database = function (x, y, w, h) { 43241 var f = 1 / 3; 43242 var wEllipse = w; 43243 var hEllipse = h * f; 43244 43245 var kappa = .5522848, 43246 ox = wEllipse / 2 * kappa, 43247 // control point offset horizontal 43248 oy = hEllipse / 2 * kappa, 43249 // control point offset vertical 43250 xe = x + wEllipse, 43251 // x-end 43252 ye = y + hEllipse, 43253 // y-end 43254 xm = x + wEllipse / 2, 43255 // x-middle 43256 ym = y + hEllipse / 2, 43257 // y-middle 43258 ymb = y + (h - hEllipse / 2), 43259 // y-midlle, bottom ellipse 43260 yeb = y + h; // y-end, bottom ellipse 43261 43262 this.beginPath(); 43263 this.moveTo(xe, ym); 43264 43265 this.bezierCurveTo(xe, ym + oy, xm + ox, ye, xm, ye); 43266 this.bezierCurveTo(xm - ox, ye, x, ym + oy, x, ym); 43267 43268 this.bezierCurveTo(x, ym - oy, xm - ox, y, xm, y); 43269 this.bezierCurveTo(xm + ox, y, xe, ym - oy, xe, ym); 43270 43271 this.lineTo(xe, ymb); 43272 43273 this.bezierCurveTo(xe, ymb + oy, xm + ox, yeb, xm, yeb); 43274 this.bezierCurveTo(xm - ox, yeb, x, ymb + oy, x, ymb); 43275 43276 this.lineTo(x, ym); 43277 }; 43278 43279 /** 43280 * Sets up the dashedLine functionality for drawing
43281 * Original code came from http://stackoverflow.com/questions/4576724/dotted-stroke-in-canvas 43282 * @author David Jordan 43283 * @date 2012-08-08 43284 * 43285 * @param {number} x 43286 * @param {number} y 43287 * @param {number} x2 43288 * @param {number} y2 43289 * @param {string} pattern 43290 */ 43291 CanvasRenderingContext2D.prototype.dashedLine = function (x, y, x2, y2, pattern) { 43292 this.beginPath(); 43293 this.moveTo(x, y); 43294 43295 var patternLength = pattern.length; 43296 var dx = x2 - x; 43297 var dy = y2 - y; 43298 var slope = dy / dx; 43299 var distRemaining = Math.sqrt(dx * dx + dy * dy); 43300 var patternIndex = 0; 43301 var draw = true; 43302 var xStep = 0; 43303 var dashLength = pattern[0]; 43304 43305 while (distRemaining >= 0.1) { 43306 dashLength = pattern[patternIndex++ % patternLength]; 43307 if (dashLength > distRemaining) { 43308 dashLength = distRemaining; 43309 } 43310 43311 xStep = Math.sqrt(dashLength * dashLength / (1 + slope * slope)); 43312 xStep = dx < 0 ? -xStep : xStep; 43313 x += xStep; 43314 y += slope * xStep; 43315 43316 if (draw === true) { 43317 this.lineTo(x, y); 43318 } else { 43319 this.moveTo(x, y); 43320 } 43321 43322 distRemaining -= dashLength; 43323 draw = !draw; 43324 } 43325 }; 43326 43327 /** 43328 * Draw a Hexagon shape with 6 sides 43329 * @param {Number} x horizontal center 43330 * @param {Number} y vertical center 43331 * @param {Number} r radius 43332 */ 43333 CanvasRenderingContext2D.prototype.hexagon = function (x, y, r) { 43334 this.beginPath(); 43335 var sides = 6; 43336 var a = Math.PI * 2 / sides; 43337 this.moveTo(x + r, y); 43338 for (var i = 1; i < sides; i++) { 43339 this.lineTo(x + r * Math.cos(a * i), y + r * Math.sin(a * i)); 43340 } 43341 this.closePath(); 43342 }; 43343} 43344 43345/***/ }), 43346/* 184 */ 43347/***/ (function(module, exports, __webpack_require__) { 43348 43349"use strict"; 43350 43351 43352// English 43353exports['en'] = { 43354 edit: 'Edit', 43355 del: 'Delete selected', 43356 back: 'Back', 43357 addNode: 'Add Node', 43358 addEdge: 'Add Edge', 43359 editNode: 'Edit Node', 43360 editEdge: 'Edit Edge', 43361 addDescription: 'Click in an empty space to place a new node.', 43362 edgeDescription: 'Click on a node and drag the edge to another node to connect them.', 43363 editEdgeDescription: 'Click on the control points and drag them to a node to connect to it.', 43364 createEdgeError: 'Cannot link edges to a cluster.', 43365 deleteClusterError: 'Clusters cannot be deleted.', 43366 editClusterError: 'Clusters cannot be edited.' 43367}; 43368exports['en_EN'] = exports['en']; 43369exports['en_US'] = exports['en']; 43370 43371// German 43372exports['de'] = { 43373 edit: 'Editieren', 43374 del: 'L\xF6sche Auswahl', 43375 back: 'Zur\xFCck', 43376 addNode: 'Knoten hinzuf\xFCgen', 43377 addEdge: 'Kante hinzuf\xFCgen', 43378 editNode: 'Knoten editieren', 43379 editEdge: 'Kante editieren', 43380 addDescription: 'Klicke auf eine freie Stelle, um einen neuen Knoten zu plazieren.', 43381 edgeDescription: 'Klicke auf einen Knoten und ziehe die Kante zu einem anderen Knoten, um diese zu verbinden.', 43382 editEdgeDescription: 'Klicke auf die Verbindungspunkte und ziehe diese auf einen Knoten, um sie zu verbinden.', 43383 createEdgeError: 'Es ist nicht m\xF6glich, Kanten mit Clustern zu verbinden.', 43384 deleteClusterError: 'Cluster k\xF6nnen nicht gel\xF6scht werden.', 43385 editClusterError: 'Cluster k\xF6nnen nicht editiert werden.' 43386}; 43387exports['de_DE'] = exports['de']; 43388 43389// Spanish 43390exports['es'] = { 43391 edit: 'Editar', 43392 del: 'Eliminar selecci\xF3n', 43393 back: '\xC1tras', 43394 addNode: 'A\xF1adir nodo', 43395 addEdge: 'A\xF1adir arista', 43396 editNode: 'Editar nodo', 43397 editEdge: 'Editar arista', 43398 addDescription: 'Haga clic en un lugar vac\xEDo para colocar un nuevo nodo.', 43399 edgeDescription: 'Haga clic en un nodo y arrastre la arista hacia otro nodo para conectarlos.', 43400 editEdgeDescription: 'Haga clic en un punto de control y arrastrelo a un nodo para conectarlo.', 43401 createEdgeError: 'No se puede conectar una arista a un grupo.', 43402 deleteClusterError: 'No es posible eliminar grupos.', 43403 editClusterError: 'No es posible editar grupos.' 43404}; 43405exports['es_ES'] = exports['es']; 43406 43407//Italiano 43408exports['it'] = { 43409 edit: 'Modifica', 43410 del: 'Cancella la selezione', 43411 back: 'Indietro', 43412 addNode: 'Aggiungi un nodo', 43413 addEdge: 'Aggiungi un vertice', 43414 editNode: 'Modifica il nodo', 43415 editEdge: 'Modifica il vertice', 43416 addDescription: 'Clicca per aggiungere un nuovo nodo', 43417 edgeDescription: 'Clicca su un nodo e trascinalo ad un altro nodo per connetterli.', 43418 editEdgeDescription: 'Clicca sui Punti di controllo e trascinali ad un nodo per connetterli.', 43419 createEdgeError: 'Non si possono collegare vertici ad un cluster', 43420 deleteClusterError: 'I cluster non possono essere cancellati', 43421 editClusterError: 'I clusters non possono essere modificati.' 43422}; 43423exports['it_IT'] = exports['it']; 43424 43425// Dutch 43426exports['nl'] = { 43427 edit: 'Wijzigen', 43428 del: 'Selectie verwijderen', 43429 back: 'Terug', 43430 addNode: 'Node toevoegen', 43431 addEdge: 'Link toevoegen', 43432 editNode: 'Node wijzigen', 43433 editEdge: 'Link wijzigen', 43434 addDescription: 'Klik op een leeg gebied om een nieuwe node te maken.',
43435 edgeDescription: 'Klik op een node en sleep de link naar een andere node om ze te verbinden.', 43436 editEdgeDescription: 'Klik op de verbindingspunten en sleep ze naar een node om daarmee te verbinden.', 43437 createEdgeError: 'Kan geen link maken naar een cluster.', 43438 deleteClusterError: 'Clusters kunnen niet worden verwijderd.', 43439 editClusterError: 'Clusters kunnen niet worden aangepast.' 43440}; 43441exports['nl_NL'] = exports['nl']; 43442exports['nl_BE'] = exports['nl']; 43443 43444// Portuguese Brazil 43445exports['pt-br'] = { 43446 edit: 'Editar', 43447 del: 'Remover selecionado', 43448 back: 'Voltar', 43449 addNode: 'Adicionar nó', 43450 addEdge: 'Adicionar aresta', 43451 editNode: 'Editar nó', 43452 editEdge: 'Editar aresta', 43453 addDescription: 'Clique em um espaço em branco para adicionar um novo nó', 43454 edgeDescription: 'Clique em um nó e arraste a aresta até outro nó para conectá-los', 43455 editEdgeDescription: 'Clique nos pontos de controle e os arraste para um nó para conectá-los', 43456 createEdgeError: 'Não foi possÃvel linkar arestas a um cluster.', 43457 deleteClusterError: 'Clusters não puderam ser removidos.', 43458 editClusterError: 'Clusters não puderam ser editados.' 43459}; 43460exports['pt-BR'] = exports['pt-br']; 43461exports['pt_BR'] = exports['pt-br']; 43462exports['pt_br'] = exports['pt-br']; 43463 43464// Russian 43465exports['ru'] = { 43466 edit: 'РедакÑиÑоваÑÑ', 43467 del: 'УдалиÑÑ Ð²ÑбÑанное', 43468 back: 'Ðазад', 43469 addNode: 'ÐобавиÑÑ Ñзел', 43470 addEdge: 'ÐобавиÑÑ ÑебÑо', 43471 editNode: 'РедакÑиÑоваÑÑ Ñзел', 43472 editEdge: 'РедакÑиÑоваÑÑ ÑебÑо', 43473 addDescription: 'ÐликниÑе в Ñвободное меÑÑо, ÑÑÐ¾Ð±Ñ Ð´Ð¾Ð±Ð°Ð²Ð¸ÑÑ Ð½Ð¾Ð²Ñй Ñзел.', 43474 edgeDescription: 'ÐликниÑе на Ñзел и пÑоÑÑниÑе ÑебÑо к дÑÑÐ³Ð¾Ð¼Ñ ÑзлÑ, ÑÑÐ¾Ð±Ñ ÑоединиÑÑ Ð¸Ñ .', 43475 editEdgeDescription: 'ÐликниÑе на конÑÑолÑнÑе ÑоÑки и пеÑеÑаÑиÑе Ð¸Ñ Ð² Ñзел, ÑÑÐ¾Ð±Ñ Ð¿Ð¾Ð´ÐºÐ»ÑÑиÑÑÑÑ Ðº немÑ.', 43476 createEdgeError: 'Ðевозможно ÑоединиÑÑ ÑебÑа в клаÑÑеÑ.', 43477 deleteClusterError: 'ÐлаÑÑеÑÑ Ð½Ðµ могÑÑ Ð±ÑÑÑ ÑдаленÑ', 43478 editClusterError: 'ÐлаÑÑеÑÑ Ð½ÐµÐ´Ð¾ÑÑÑÐ¿Ð½Ñ Ð´Ð»Ñ ÑедакÑиÑованиÑ.' 43479}; 43480exports['ru_RU'] = exports['ru']; 43481 43482// Chinese 43483exports['cn'] = { 43484 edit: 'ç¼è¾', 43485 del: 'å é¤éå®', 43486 back: 'è¿å', 43487 addNode: 'æ·»å èç¹', 43488 addEdge: 'æ·»å è¿æ¥çº¿', 43489 editNode: 'ç¼è¾èç¹', 43490 editEdge: 'ç¼è¾è¿æ¥çº¿', 43491 addDescription: 'åå»ç©ºç½å¤æ¾ç½®æ°èç¹ã', 43492 edgeDescription: 'åå»æä¸ªèç¹å¹¶å°è¯¥è¿æ¥çº¿æå¨å°å¦ä¸ä¸ªèç¹ä»¥è¿æ¥å®ä»¬ã', 43493 editEdgeDescription: 'å廿§å¶èç¹å¹¶å°å®ä»¬æå°èç¹ä¸è¿æ¥ã', 43494 createEdgeError: 'æ æ³å°è¿æ¥çº¿è¿æ¥å°ç¾¤éã', 43495 deleteClusterError: 'æ æ³å é¤ç¾¤éã', 43496 editClusterError: 'æ æ³ç¼è¾ç¾¤éã' 43497}; 43498exports['zh_CN'] = exports['cn']; 43499 43500/***/ }), 43501/* 185 */ 43502/***/ (function(module, exports, __webpack_require__) { 43503 43504"use strict"; 43505 43506 43507Object.defineProperty(exports, "__esModule", { 43508 value: true 43509}); 43510 43511var _classCallCheck2 = __webpack_require__(0); 43512 43513var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); 43514 43515var _createClass2 = __webpack_require__(1); 43516 43517var _createClass3 = _interopRequireDefault(_createClass2); 43518 43519function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 43520 43521/** 43522 * Associates a canvas to a given image, containing a number of renderings 43523 * of the image at various sizes. 43524 * 43525 * This technique is known as 'mipmapping'. 43526 * 43527 * NOTE: Images can also be of type 'data:svg+xml`. This code also works 43528 * for svg, but the mipmapping may not be necessary. 43529 * 43530 * @param {Image} image 43531 */ 43532var CachedImage = function () { 43533 /** 43534 * @ignore 43535 */ 43536 function CachedImage() { 43537 (0, _classCallCheck3['default'])(this, CachedImage); 43538 // eslint-disable-line no-unused-vars 43539 this.NUM_ITERATIONS = 4; // Number of items in the coordinates array 43540 43541 this.image = new Image(); 43542 this.canvas = document.createElement('canvas'); 43543 } 43544 43545 /** 43546 * Called when the image has been successfully loaded. 43547 */ 43548 43549 43550 (0, _createClass3['default'])(CachedImage, [{ 43551 key: 'init',
43552 value: function init() { 43553 if (this.initialized()) return; 43554 43555 this.src = this.image.src; // For same interface with Image 43556 var w = this.image.width; 43557 var h = this.image.height; 43558 43559 // Ease external access 43560 this.width = w; 43561 this.height = h; 43562 43563 var h2 = Math.floor(h / 2); 43564 var h4 = Math.floor(h / 4); 43565 var h8 = Math.floor(h / 8); 43566 var h16 = Math.floor(h / 16); 43567 43568 var w2 = Math.floor(w / 2); 43569 var w4 = Math.floor(w / 4); 43570 var w8 = Math.floor(w / 8); 43571 var w16 = Math.floor(w / 16); 43572 43573 // Make canvas as small as possible 43574 this.canvas.width = 3 * w4; 43575 this.canvas.height = h2; 43576 43577 // Coordinates and sizes of images contained in the canvas 43578 // Values per row: [top x, left y, width, height] 43579 43580 this.coordinates = [[0, 0, w2, h2], [w2, 0, w4, h4], [w2, h4, w8, h8], [5 * w8, h4, w16, h16]]; 43581 43582 this._fillMipMap(); 43583 } 43584 43585 /** 43586 * @return {Boolean} true if init() has been called, false otherwise. 43587 */ 43588 43589 }, { 43590 key: 'initialized', 43591 value: function initialized() { 43592 return this.coordinates !== undefined; 43593 } 43594 43595 /** 43596 * Redraw main image in various sizes to the context. 43597 * 43598 * The rationale behind this is to reduce artefacts due to interpolation 43599 * at differing zoom levels. 43600 * 43601 * Source: http://stackoverflow.com/q/18761404/1223531 43602 * 43603 * This methods takes the resizing out of the drawing loop, in order to 43604 * reduce performance overhead. 43605 * 43606 * TODO: The code assumes that a 2D context can always be gotten. This is 43607 * not necessarily true! OTOH, if not true then usage of this class 43608 * is senseless. 43609 * 43610 * @private 43611 */ 43612 43613 }, { 43614 key: '_fillMipMap', 43615 value: function _fillMipMap() { 43616 var ctx = this.canvas.getContext('2d'); 43617 43618 // First zoom-level comes from the image 43619 var to = this.coordinates[0]; 43620 ctx.drawImage(this.image, to[0], to[1], to[2], to[3]); 43621 43622 // The rest are copy actions internal to the canvas/context 43623 for (var iterations = 1; iterations < this.NUM_ITERATIONS; iterations++) { 43624 var from = this.coordinates[iterations - 1]; 43625 var _to = this.coordinates[iterations]; 43626 43627 ctx.drawImage(this.canvas, from[0], from[1], from[2], from[3], _to[0], _to[1], _to[2], _to[3]); 43628 } 43629 } 43630 43631 /** 43632 * Draw the image, using the mipmap if necessary. 43633 * 43634 * MipMap is only used if param factor > 2; otherwise, original bitmap 43635 * is resized. This is also used to skip mipmap usage, e.g. by setting factor = 1 43636 * 43637 * Credits to 'Alex de Mulder' for original implementation. 43638 * 43639 * @param {CanvasRenderingContext2D} ctx context on which to draw zoomed image 43640 * @param {Float} factor scale factor at which to draw 43641 * @param {number} left 43642 * @param {number} top 43643 * @param {number} width 43644 * @param {number} height 43645 */ 43646 43647 }, { 43648 key: 'drawImageAtPosition', 43649 value: function drawImageAtPosition(ctx, factor, left, top, width, height) { 43650 43651 if (!this.initialized()) return; //can't draw image yet not intialized 43652 43653 if (factor > 2) { 43654 // Determine which zoomed image to use 43655 factor *= 0.5; 43656 var iterations = 0; 43657 while (factor > 2 && iterations < this.NUM_ITERATIONS) { 43658 factor *= 0.5; 43659 iterations += 1; 43660 } 43661 43662 if (iterations >= this.NUM_ITERATIONS) { 43663 iterations = this.NUM_ITERATIONS - 1; 43664 } 43665 //console.log("iterations: " + iterations); 43666 43667 var from = this.coordinates[iterations]; 43668 ctx.drawImage(this.canvas, from[0], from[1], from[2], from[3], left, top, width, height); 43669 } else { 43670 // Draw image directly 43671 ctx.drawImage(this.image, left, top, width, height); 43672 } 43673 } 43674 }]); 43675 return CachedImage; 43676}(); 43677 43678exports['default'] = CachedImage; 43679 43680/***/ }), 43681/* 186 */ 43682/***/ (function(module, exports, __webpack_require__) { 43683 43684"use strict"; 43685 43686 43687Object.defineProperty(exports, "__esModule", { 43688 value: true 43689}); 43690 43691var _classCallCheck2 = __webpack_require__(0); 43692 43693var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); 43694 43695var _createClass2 = __webpack_require__(1); 43696 43697var _createClass3 = _interopRequireDefault(_createClass2); 43698 43699function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } 43700 43701var util = __webpack_require__(2); 43702 43703/** 43704 * This class can store groups and options specific for groups. 43705 */ 43706 43707var Groups = function () { 43708 /** 43709 * @ignore 43710 */ 43711 function Groups() { 43712 (0, _classCallCheck3["default"])(this, Groups); 43713 43714 this.clear(); 43715 this.defaultIndex = 0; 43716 this.groupsArray = []; 43717 this.groupIndex = 0; 43718 43719 this.defaultGroups = [{ border: "#2B7CE9", background: "#97C2FC", highlight: { border: "#2B7CE9", background: "#D2E5FF" }, hover: { border: "#2B7CE9", background: "#D2E5FF" } }, // 0: blue 43720 { border: "#FFA500", background: "#FFFF00", highlight: { border: "#FFA500", background: "#FFFFA3" }, hover: { border: "#FFA500", background: "#FFFFA3" } }, // 1: yellow 43721 { border: "#FA0A10", background: "#FB7E81", highlight: { border: "#FA0A10", background: "#FFAFB1" }, hover: { border: "#FA0A10", background: "#FFAFB1" } }, // 2: red 43722 { border: "#41A906", background: "#7BE141", highlight: { border: "#41A906", background: "#A1EC76" }, hover: { border: "#41A906", background: "#A1EC76" } }, // 3: green 43723 { border: "#E129F0", background: "#EB7DF4", highlight: { border: "#E129F0", background: "#F0B3F5" }, hover: { border: "#E129F0", background: "#F0B3F5" } }, // 4: magenta 43724 { border: "#7C29F0", background: "#AD85E4", highlight: { border: "#7C29F0", background: "#D3BDF0" }, hover: { border: "#7C29F0", background: "#D3BDF0" } }, // 5: purple 43725 { border: "#C37F00", background: "#FFA807", highlight: { border: "#C37F00", background: "#FFCA66" }, hover: { border: "#C37F00", background: "#FFCA66" } }, // 6: orange 43726 { border: "#4220FB", background: "#6E6EFD", highlight: { border: "#4220FB", background: "#9B9BFD" }, hover: { border: "#4220FB", background: "#9B9BFD" } }, // 7: darkblue 43727 { border: "#FD5A77", background: "#FFC0CB", highlight: { border: "#FD5A77", background: "#FFD1D9" }, hover: { border: "#FD5A77", background: "#FFD1D9" } }, // 8: pink 43728 { border: "#4AD63A", background: "#C2FABC", highlight: { border: "#4AD63A", background: "#E6FFE3" }, hover: { border: "#4AD63A", background: "#E6FFE3" } }, // 9: mint 43729 43730 { border: "#990000", background: "#EE0000", highlight: { border: "#BB0000", background: "#FF3333" }, hover: { border: "#BB0000", background: "#FF3333" } }, // 10:bright red 43731 43732 { border: "#FF6000", background: "#FF6000", highlight: { border: "#FF6000", background: "#FF6000" }, hover: { border: "#FF6000", background: "#FF6000" } }, // 12: real orange 43733 { border: "#97C2FC", background: "#2B7CE9", highlight: { border: "#D2E5FF", background: "#2B7CE9" }, hover: { border: "#D2E5FF", background: "#2B7CE9" } }, // 13: blue 43734 { border: "#399605", background: "#255C03", highlight: { border: "#399605", background: "#255C03" }, hover: { border: "#399605", background: "#255C03" } }, // 14: green 43735 { border: "#B70054", background: "#FF007E", highlight: { border: "#B70054", background: "#FF007E" }, hover: { border: "#B70054", background: "#FF007E" } }, // 15: magenta 43736 { border: "#AD85E4", background: "#7C29F0", highlight: { border: "#D3BDF0", background: "#7C29F0" }, hover: { border: "#D3BDF0", background: "#7C29F0" } }, // 16: purple 43737 { border: "#4557FA", background: "#000EA1", highlight: { border: "#6E6EFD", background: "#000EA1" }, hover: { border: "#6E6EFD", background: "#000EA1" } }, // 17: darkblue 43738 { border: "#FFC0CB", background: "#FD5A77", highlight: { border: "#FFD1D9", background: "#FD5A77" }, hover: { border: "#FFD1D9", background: "#FD5A77" } }, // 18: pink 43739 { border: "#C2FABC", background: "#74D66A", highlight: { border: "#E6FFE3", background: "#74D66A" }, hover: { border: "#E6FFE3", background: "#74D66A" } }, // 19: mint 43740 43741 { border: "#EE0000", background: "#990000", highlight: { border: "#FF3333", background: "#BB0000" }, hover: { border: "#FF3333", background: "#BB0000" } // 20:bright red 43742 }]; 43743 43744 this.options = {}; 43745 this.defaultOptions = { 43746 useDefaultGroups: true 43747 }; 43748 util.extend(this.options, this.defaultOptions); 43749 } 43750 43751 /** 43752 * 43753 * @param {Object} options 43754 */ 43755 43756 43757 (0, _createClass3["default"])(Groups, [{ 43758 key: "setOptions", 43759 value: function setOptions(options) { 43760 var optionFields = ['useDefaultGroups']; 43761 43762 if (options !== undefined) { 43763 for (var groupName in options) { 43764 if (options.hasOwnProperty(groupName)) { 43765 if (optionFields.indexOf(groupName) === -1) { 43766 var group = options[groupName]; 43767 this.add(groupName, group); 43768 } 43769 } 43770 } 43771 } 43772 } 43773 43774 /** 43775 * Clear all groups 43776 */ 43777 43778 }, { 43779 key: "clear", 43780 value: function clear() { 43781 this.groups = {}; 43782 this.groupsArray = []; 43783 } 43784 43785 /** 43786 * Get group options of a groupname. 43787 * If groupname is not found, a new group may be created. 43788 * 43789 * @param {*} groupname Can be a number, string, Date, etc. 43790 * @param {boolean} [shouldCreate=true] If true, create a new group 43791 * @return {Object} The found or created group 43792 */ 43793 43794 }, { 43795 key: "get", 43796 value: function get(groupname) { 43797 var shouldCreate = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true; 43798 43799 var group = this.groups[groupname]; 43800 43801 if (group === undefined && shouldCreate) { 43802 if (this.options.useDefaultGroups === false && this.groupsArray.length > 0) { 43803 // create new group 43804 var index = this.groupIndex % this.groupsArray.length; 43805 this.groupIndex++; 43806 group = {}; 43807 group.color = this.groups[this.groupsArray[index]]; 43808 this.groups[groupname] = group; 43809 } else { 43810 // create new group 43811 var _index = this.defaultIndex % this.defaultGroups.length; 43812 this.defaultIndex++; 43813 group = {}; 43814 group.color = this.defaultGroups[_index]; 43815 this.groups[groupname] = group; 43816 } 43817 } 43818 43819 return group; 43820 } 43821 43822 /** 43823 * Add a custom group style 43824 * @param {string} groupName 43825 * @param {Object}
43825 style An object containing borderColor, 43826 * backgroundColor, etc. 43827 * @return {Object} group The created group object 43828 */ 43829 43830 }, { 43831 key: "add", 43832 value: function add(groupName, style) { 43833 this.groups[groupName] = style; 43834 this.groupsArray.push(groupName); 43835 return style; 43836 } 43837 }]); 43838 return Groups; 43839}(); 43840 43841exports["default"] = Groups; 43842 43843/***/ }), 43844/* 187 */ 43845/***/ (function(module, exports, __webpack_require__) { 43846 43847"use strict"; 43848 43849 43850Object.defineProperty(exports, "__esModule", { 43851 value: true 43852}); 43853 43854var _classCallCheck2 = __webpack_require__(0); 43855 43856var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); 43857 43858var _createClass2 = __webpack_require__(1); 43859 43860var _createClass3 = _interopRequireDefault(_createClass2); 43861 43862function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 43863 43864var util = __webpack_require__(2); 43865var DataSet = __webpack_require__(11); 43866var DataView = __webpack_require__(12); 43867var Node = __webpack_require__(47)['default']; 43868 43869/** 43870 * Handler for Nodes 43871 */ 43872 43873var NodesHandler = function () { 43874 /** 43875 * @param {Object} body 43876 * @param {Images} images 43877 * @param {Array.<Group>} groups 43878 * @param {LayoutEngine} layoutEngine 43879 */ 43880 function NodesHandler(body, images, groups, layoutEngine) { 43881 var _this = this; 43882 43883 (0, _classCallCheck3['default'])(this, NodesHandler); 43884 43885 this.body = body; 43886 this.images = images; 43887 this.groups = groups; 43888 this.layoutEngine = layoutEngine; 43889 43890 // create the node API in the body container 43891 this.body.functions.createNode = this.create.bind(this); 43892 43893 this.nodesListeners = { 43894 add: function add(event, params) { 43895 _this.add(params.items); 43896 }, 43897 update: function update(event, params) { 43898 _this.update(params.items, params.data, params.oldData); 43899 }, 43900 remove: function remove(event, params) { 43901 _this.remove(params.items); 43902 } 43903 }; 43904 43905 this.defaultOptions = { 43906 borderWidth: 1, 43907 borderWidthSelected: 2, 43908 brokenImage: undefined, 43909 color: { 43910 border: '#2B7CE9', 43911 background: '#97C2FC', 43912 highlight: { 43913 border: '#2B7CE9', 43914 background: '#D2E5FF' 43915 }, 43916 hover: { 43917 border: '#2B7CE9', 43918 background: '#D2E5FF' 43919 } 43920 }, 43921 fixed: { 43922 x: false, 43923 y: false 43924 }, 43925 font: { 43926 color: '#343434', 43927 size: 14, // px 43928 face: 'arial', 43929 background: 'none', 43930 strokeWidth: 0, // px 43931 strokeColor: '#ffffff', 43932 align: 'center', 43933 vadjust: 0, 43934 multi: false, 43935 bold: { 43936 mod: 'bold' 43937 }, 43938 boldital: { 43939 mod: 'bold italic' 43940 }, 43941 ital: { 43942 mod: 'italic' 43943 }, 43944 mono: { 43945 mod: '', 43946 size: 15, // px 43947 face: 'monospace', 43948 vadjust: 2 43949 } 43950 }, 43951 group: undefined, 43952 hidden: false, 43953 icon: { 43954 face: 'FontAwesome', //'FontAwesome', 43955 code: undefined, //'\uf007', 43956 size: 50, //50, 43957 color: '#2B7CE9' //'#aa00ff' 43958 }, 43959 image: undefined, // --> URL 43960 label: undefined, 43961 labelHighlightBold: true, 43962 level: undefined, 43963 margin: { 43964 top: 5, 43965 right: 5, 43966 bottom: 5, 43967 left: 5 43968 }, 43969 mass: 1, 43970 physics: true, 43971 scaling: { 43972 min: 10, 43973 max: 30, 43974 label: { 43975 enabled: false, 43976 min: 14, 43977 max: 30, 43978 maxVisible: 30, 43979 drawThreshold: 5 43980 }, 43981 customScalingFunction: function customScalingFunction(min, max, total, value) { 43982 if (max === min) { 43983 return 0.5; 43984 } else { 43985 var scale = 1 / (max - min); 43986 return Math.max(0, (value - min) * scale); 43987 } 43988 } 43989 }, 43990 shadow: { 43991 enabled: false, 43992 color: 'rgba(0,0,0,0.5)', 43993 size: 10, 43994 x: 5, 43995 y: 5 43996 }, 43997 shape: 'ellipse', 43998 shapeProperties: { 43999 borderDashes: false, // only for borders 44000 borderRadius: 6, // only for box shape 44001 interpolation: true, // only for image and circularImage shapes 44002 useImageSize: false, // only for image and circularImage shapes 44003 useBorderWithImage: false // only for image shape 44004 }, 44005 size: 25, 44006 title: undefined, 44007 value: undefined, 44008 x: undefined, 44009 y: undefined 44010 }; 44011 44012 // Protect from idiocy 44013 if (this.defaultOptions.mass <= 0) { 44014 throw 'Internal error: mass in defaultOptions of NodesHandler may not be zero or negative'; 44015 } 44016 44017 this.options = util.bridgeObject(this.defaultOptions); 44018 44019 this.bindEventListeners(); 44020 } 44021 44022 /** 44023 * Binds event listeners 44024 */ 44025 44026 44027 (0, _createClass3['default'])(NodesHandler, [{ 44028 key: 'bindEventListeners', 44029 value: function bindEventListeners() { 44030 var _this2 = this; 44031 44032 // refresh the nodes. Used when reverting from hierarchical layout 44033 this.body.emitter.on('refreshNodes', this.refresh.bind(this)); 44034 this.body.emitter.on('refresh', this.refresh.bind(this)); 44035 this.body.emitter.on('destroy', function () { 44036 util.forEach(_this2.nodesListeners, function (callback, event) { 44037 if (_this2.body.data.nodes) _this2.body.data.nodes.off(event, callback); 44038 }); 44039 delete _this2.body.functions.createNode; 44040 delete _this2.nodesListeners.add; 44041 delete _this2.nodesListeners.update; 44042 delete _this2.nodesListeners.remove; 44043 delete _this2.nodesListeners; 44044 }); 44045 } 44046 44047 /** 44048 * 44049 * @param {Object} options 44050 */ 44051 44052 }, { 44053 key: 'setOptions', 44054 value: function setOptions(options) { 44055 if (options !== undefined) { 44056 Node.parseOptions(this.options, options); 44057 44058 // update the shape in all nodes 44059 if (options.shape !== undefined) { 44060 for (var nodeId in this.body.nodes) { 44061 if (this.body.nodes.hasOwnProperty(nodeId)) { 44062 this.body.nodes[nodeId].updateShape(); 44063 } 44064 } 44065 } 44066 44067 // update the font in all nodes 44068 if (options.font !== undefined) { 44069 for (var _nodeId in this.body.nodes) { 44070 if (this.body.nodes.hasOwnProperty(_nodeId)) {
44071 this.body.nodes[_nodeId].updateLabelModule(); 44072 this.body.nodes[_nodeId].needsRefresh(); 44073 } 44074 } 44075 } 44076 44077 // update the shape size in all nodes 44078 if (options.size !== undefined) { 44079 for (var _nodeId2 in this.body.nodes) { 44080 if (this.body.nodes.hasOwnProperty(_nodeId2)) { 44081 this.body.nodes[_nodeId2].needsRefresh(); 44082 } 44083 } 44084 } 44085 44086 // update the state of the variables if needed 44087 if (options.hidden !== undefined || options.physics !== undefined) { 44088 this.body.emitter.emit('_dataChanged'); 44089 } 44090 } 44091 } 44092 44093 /** 44094 * Set a data set with nodes for the network 44095 * @param {Array | DataSet | DataView} nodes The data containing the nodes. 44096 * @param {boolean} [doNotEmit=false] 44097 * @private 44098 */ 44099 44100 }, { 44101 key: 'setData', 44102 value: function setData(nodes) { 44103 var doNotEmit = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; 44104 44105 var oldNodesData = this.body.data.nodes; 44106 44107 if (nodes instanceof DataSet || nodes instanceof DataView) { 44108 this.body.data.nodes = nodes; 44109 } else if (Array.isArray(nodes)) { 44110 this.body.data.nodes = new DataSet(); 44111 this.body.data.nodes.add(nodes); 44112 } else if (!nodes) { 44113 this.body.data.nodes = new DataSet(); 44114 } else { 44115 throw new TypeError('Array or DataSet expected'); 44116 } 44117 44118 if (oldNodesData) { 44119 // unsubscribe from old dataset 44120 util.forEach(this.nodesListeners, function (callback, event) { 44121 oldNodesData.off(event, callback); 44122 }); 44123 } 44124 44125 // remove drawn nodes 44126 this.body.nodes = {}; 44127 44128 if (this.body.data.nodes) { 44129 // subscribe to new dataset 44130 var me = this; 44131 util.forEach(this.nodesListeners, function (callback, event) { 44132 me.body.data.nodes.on(event, callback); 44133 }); 44134 44135 // draw all new nodes 44136 var ids = this.body.data.nodes.getIds(); 44137 this.add(ids, true); 44138 } 44139 44140 if (doNotEmit === false) { 44141 this.body.emitter.emit("_dataChanged"); 44142 } 44143 } 44144 44145 /** 44146 * Add nodes 44147 * @param {number[] | string[]} ids 44148 * @param {boolean} [doNotEmit=false] 44149 * @private 44150 */ 44151 44152 }, { 44153 key: 'add', 44154 value: function add(ids) { 44155 var doNotEmit = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; 44156 44157 var id = void 0; 44158 var newNodes = []; 44159 for (var i = 0; i < ids.length; i++) { 44160 id = ids[i]; 44161 var properties = this.body.data.nodes.get(id); 44162 var node = this.create(properties); 44163 newNodes.push(node); 44164 this.body.nodes[id] = node; // note: this may replace an existing node 44165 } 44166 44167 this.layoutEngine.positionInitially(newNodes); 44168 44169 if (doNotEmit === false) { 44170 this.body.emitter.emit("_dataChanged"); 44171 } 44172 } 44173 44174 /** 44175 * Update existing nodes, or create them when not yet existing 44176 * @param {number[] | string[]} ids id's of changed nodes 44177 * @param {Array} changedData array with changed data 44178 * @param {Array|undefined} oldData optional; array with previous data 44179 * @private 44180 */ 44181 44182 }, { 44183 key: 'update', 44184 value: function update(ids, changedData, oldData) { 44185 var nodes = this.body.nodes; 44186 var dataChanged = false; 44187 for (var i = 0; i < ids.length; i++) { 44188 var id = ids[i]; 44189 var node = nodes[id]; 44190 var data = changedData[i]; 44191 if (node !== undefined) { 44192 // update node 44193 if (node.setOptions(data)) { 44194 dataChanged = true; 44195 } 44196 } else { 44197 dataChanged = true; 44198 // create node 44199 node = this.create(data); 44200 nodes[id] = node; 44201 } 44202 } 44203 44204 if (!dataChanged && oldData !== undefined) { 44205 // Check for any changes which should trigger a layout recalculation 44206 // For now, this is just 'level' for hierarchical layout 44207 // Assumption: old and new data arranged in same order; at time of writing, this holds. 44208 dataChanged = changedData.some(function (newValue, index) { 44209 var oldValue = oldData[index]; 44210 return oldValue && oldValue.level !== newValue.level; 44211 }); 44212 } 44213 44214 if (dataChanged === true) { 44215 this.body.emitter.emit("_dataChanged"); 44216 } else { 44217 this.body.emitter.emit("_dataUpdated"); 44218 } 44219 } 44220 44221 /** 44222 * Remove existing nodes. If nodes do not exist, the method will just ignore it. 44223 * @param {number[] | string[]} ids 44224 * @private 44225 */ 44226 44227 }, { 44228 key: 'remove', 44229 value: function remove(ids) { 44230 var nodes = this.body.nodes; 44231 44232 for (var i = 0; i < ids.length; i++) { 44233 var id = ids[i]; 44234 delete nodes[id]; 44235 } 44236 44237 this.body.emitter.emit("_dataChanged"); 44238 } 44239 44240 /** 44241 * create a node 44242 * @param {Object} properties 44243 * @param {class} [constructorClass=Node.default] 44244 * @returns {*} 44245 */ 44246 44247 }, { 44248 key: 'create', 44249 value: function create(properties) { 44250 var constructorClass = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : Node; 44251 44252 return new constructorClass(properties, this.body, this.images, this.groups, this.options, this.defaultOptions); 44253 } 44254 44255 /** 44256 * 44257 * @param {boolean} [clearPositions=false] 44258 */ 44259 44260 }, { 44261 key: 'refresh', 44262 value: function refresh() { 44263 var _this3 = this; 44264 44265 var clearPositions = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
44266 44267 util.forEach(this.body.nodes, function (node, nodeId) { 44268 var data = _this3.body.data.nodes.get(nodeId); 44269 if (data !== undefined) { 44270 if (clearPositions === true) { 44271 node.setOptions({ x: null, y: null }); 44272 } 44273 node.setOptions({ fixed: false }); 44274 node.setOptions(data); 44275 } 44276 }); 44277 } 44278 44279 /** 44280 * Returns the positions of the nodes. 44281 * @param {Array.<Node.id>|String} [ids] --> optional, can be array of nodeIds, can be string 44282 * @returns {{}} 44283 */ 44284 44285 }, { 44286 key: 'getPositions', 44287 value: function getPositions(ids) { 44288 var dataArray = {}; 44289 if (ids !== undefined) { 44290 if (Array.isArray(ids) === true) { 44291 for (var i = 0; i < ids.length; i++) { 44292 if (this.body.nodes[ids[i]] !== undefined) { 44293 var node = this.body.nodes[ids[i]]; 44294 dataArray[ids[i]] = { x: Math.round(node.x), y: Math.round(node.y) }; 44295 } 44296 } 44297 } else { 44298 if (this.body.nodes[ids] !== undefined) { 44299 var _node = this.body.nodes[ids]; 44300 dataArray[ids] = { x: Math.round(_node.x), y: Math.round(_node.y) }; 44301 } 44302 } 44303 } else { 44304 for (var _i = 0; _i < this.body.nodeIndices.length; _i++) { 44305 var _node2 = this.body.nodes[this.body.nodeIndices[_i]]; 44306 dataArray[this.body.nodeIndices[_i]] = { x: Math.round(_node2.x), y: Math.round(_node2.y) }; 44307 } 44308 } 44309 return dataArray; 44310 } 44311 44312 /** 44313 * Load the XY positions of the nodes into the dataset. 44314 */ 44315 44316 }, { 44317 key: 'storePositions', 44318 value: function storePositions() { 44319 // todo: add support for clusters and hierarchical. 44320 var dataArray = []; 44321 var dataset = this.body.data.nodes.getDataSet(); 44322 44323 for (var nodeId in dataset._data) { 44324 if (dataset._data.hasOwnProperty(nodeId)) { 44325 var node = this.body.nodes[nodeId]; 44326 if (dataset._data[nodeId].x != Math.round(node.x) || dataset._data[nodeId].y != Math.round(node.y)) { 44327 dataArray.push({ id: node.id, x: Math.round(node.x), y: Math.round(node.y) }); 44328 } 44329 } 44330 } 44331 dataset.update(dataArray); 44332 } 44333 44334 /** 44335 * get the bounding box of a node. 44336 * @param {Node.id} nodeId 44337 * @returns {j|*} 44338 */ 44339 44340 }, { 44341 key: 'getBoundingBox', 44342 value: function getBoundingBox(nodeId) { 44343 if (this.body.nodes[nodeId] !== undefined) { 44344 return this.body.nodes[nodeId].shape.boundingBox; 44345 } 44346 } 44347 44348 /** 44349 * Get the Ids of nodes connected to this node. 44350 * @param {Node.id} nodeId 44351 * @param {'to'|'from'|undefined} direction values 'from' and 'to' select respectively parent and child nodes only. 44352 * Any other value returns both parent and child nodes. 44353 * @returns {Array} 44354 */ 44355 44356 }, { 44357 key: 'getConnectedNodes', 44358 value: function getConnectedNodes(nodeId, direction) { 44359 var nodeList = []; 44360 if (this.body.nodes[nodeId] !== undefined) { 44361 var node = this.body.nodes[nodeId]; 44362 var nodeObj = {}; // used to quickly check if node already exists 44363 for (var i = 0; i < node.edges.length; i++) { 44364 var edge = node.edges[i]; 44365 if (direction !== 'to' && edge.toId == node.id) { 44366 // these are double equals since ids can be numeric or string 44367 if (nodeObj[edge.fromId] === undefined) { 44368 nodeList.push(edge.fromId); 44369 nodeObj[edge.fromId] = true; 44370 } 44371 } else if (direction !== 'from' && edge.fromId == node.id) { 44372 // these are double equals since ids can be numeric or string 44373 if (nodeObj[edge.toId] === undefined) { 44374 nodeList.push(edge.toId); 44375 nodeObj[edge.toId] = true; 44376 } 44377 } 44378 } 44379 } 44380 return nodeList; 44381 } 44382 44383 /** 44384 * Get the ids of the edges connected to this node. 44385 * @param {Node.id} nodeId 44386 * @returns {*} 44387 */ 44388 44389 }, { 44390 key: 'getConnectedEdges', 44391 value: function getConnectedEdges(nodeId) { 44392 var edgeList = []; 44393 if (this.body.nodes[nodeId] !== undefined) { 44394 var node = this.body.nodes[nodeId]; 44395 for (var i = 0; i < node.edges.length; i++) { 44396 edgeList.push(node.edges[i].id); 44397 } 44398 } else { 44399 console.log("NodeId provided for getConnectedEdges does not exist. Provided: ", nodeId); 44400 } 44401 return edgeList; 44402 } 44403 44404 /** 44405 * Move a node. 44406 * 44407 * @param {Node.id} nodeId 44408 * @param {number} x 44409 * @param {number} y 44410 */ 44411 44412 }, { 44413 key: 'moveNode', 44414 value: function moveNode(nodeId, x, y) { 44415 var _this4 = this; 44416 44417 if (this.body.nodes[nodeId] !== undefined) { 44418 this.body.nodes[nodeId].x = Number(x); 44419 this.body.nodes[nodeId].y = Number(y); 44420 setTimeout(function () { 44421 _this4.body.emitter.emit("startSimulation"); 44422 }, 0); 44423 } else { 44424 console.log("Node id supplied to moveNode does not exist. Provided: ", nodeId); 44425 } 44426 } 44427 }]); 44428 return NodesHandler; 44429}(); 44430 44431exports['default'] = NodesHandler; 44432 44433/***/ }), 44434/* 188 */ 44435/***/ (function(module, exports, __webpack_require__) { 44436 44437module.exports = { "default": __webpack_require__(189), __esModule: true }; 44438 44439/***/ }), 44440/* 189 */ 44441/***/ (function(module, exports, __webpack_require__) { 44442 44443__webpack_require__(49); 44444__webpack_require__(60); 44445module.exports = __webpack_require__(190); 44446 44447 44448/***/ }), 44449/* 190 */ 44450/***/ (function(module, exports, __webpack_require__) { 44451 44452var classof = __webpack_require__(86);
44453var ITERATOR = __webpack_require__(13)('iterator'); 44454var Iterators = __webpack_require__(31); 44455module.exports = __webpack_require__(7).isIterable = function (it) { 44456 var O = Object(it); 44457 return O[ITERATOR] !== undefined 44458 || '@@iterator' in O 44459 // eslint-disable-next-line no-prototype-builtins 44460 || Iterators.hasOwnProperty(classof(O)); 44461}; 44462 44463 44464/***/ }), 44465/* 191 */ 44466/***/ (function(module, exports, __webpack_require__) { 44467 44468"use strict"; 44469 44470 44471Object.defineProperty(exports, "__esModule", { 44472 value: true 44473}); 44474 44475var _classCallCheck2 = __webpack_require__(0); 44476 44477var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); 44478 44479var _createClass2 = __webpack_require__(1); 44480 44481var _createClass3 = _interopRequireDefault(_createClass2); 44482 44483function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 44484 44485var LabelAccumulator = __webpack_require__(192)['default']; 44486var ComponentUtil = __webpack_require__(48)['default']; 44487 44488/** 44489 * Helper class for Label which explodes the label text into lines and blocks within lines 44490 * 44491 * @private 44492 */ 44493 44494var LabelSplitter = function () { 44495 44496 /** 44497 * @param {CanvasRenderingContext2D} ctx Canvas rendering context 44498 * @param {Label} parent reference to the Label instance using current instance 44499 * @param {boolean} selected 44500 * @param {boolean} hover 44501 */ 44502 function LabelSplitter(ctx, parent, selected, hover) { 44503 var _this = this; 44504 44505 (0, _classCallCheck3['default'])(this, LabelSplitter); 44506 44507 this.ctx = ctx; 44508 this.parent = parent; 44509 44510 /** 44511 * Callback to determine text width; passed to LabelAccumulator instance 44512 * 44513 * @param {String} text string to determine width of 44514 * @param {String} mod font type to use for this text 44515 * @return {Object} { width, values} width in pixels and font attributes 44516 */ 44517 var textWidth = function textWidth(text, mod) { 44518 if (text === undefined) return 0; 44519 44520 // TODO: This can be done more efficiently with caching 44521 var values = _this.parent.getFormattingValues(ctx, selected, hover, mod); 44522 44523 var width = 0; 44524 if (text !== '') { 44525 // NOTE: The following may actually be *incorrect* for the mod fonts! 44526 // This returns the size with a regular font, bold etc. may 44527 // have different sizes. 44528 var measure = _this.ctx.measureText(text); 44529 width = measure.width; 44530 } 44531 44532 return { width: width, values: values }; 44533 }; 44534 44535 this.lines = new LabelAccumulator(textWidth); 44536 } 44537 44538 /** 44539 * Split passed text of a label into lines and blocks. 44540 * 44541 * # NOTE 44542 * 44543 * The handling of spacing is option dependent: 44544 * 44545 * - if `font.multi : false`, all spaces are retained 44546 * - if `font.multi : true`, every sequence of spaces is compressed to a single space 44547 * 44548 * This might not be the best way to do it, but this is as it has been working till now. 44549 * In order not to break existing functionality, for the time being this behaviour will 44550 * be retained in any code changes. 44551 * 44552 * @param {string} text text to split 44553 * @returns {Array<line>} 44554 */ 44555 44556 44557 (0, _createClass3['default'])(LabelSplitter, [{ 44558 key: 'process', 44559 value: function process(text) { 44560 if (!ComponentUtil.isValidLabel(text)) { 44561 return this.lines.finalize(); 44562 } 44563 44564 var font = this.parent.fontOptions; 44565 44566 // Normalize the end-of-line's to a single representation - order important 44567 text = text.replace(/\r\n/g, '\n'); // Dos EOL's 44568 text = text.replace(/\r/g, '\n'); // Mac EOL's 44569 44570 // Note that at this point, there can be no \r's in the text. 44571 // This is used later on splitStringIntoLines() to split multifont texts. 44572 44573 var nlLines = String(text).split('\n'); 44574 var lineCount = nlLines.length; 44575 44576 if (font.multi) { 44577 // Multi-font case: styling tags active 44578 for (var i = 0; i < lineCount; i++) { 44579 var blocks = this.splitBlocks(nlLines[i], font.multi); 44580 // Post: Sequences of tabs and spaces are reduced to single space 44581 44582 if (blocks === undefined) continue; 44583 44584 if (blocks.length === 0) { 44585 this.lines.newLine(""); 44586 continue; 44587 } 44588 44589 if (font.maxWdt > 0) { 44590 // widthConstraint.maximum defined 44591 //console.log('Running widthConstraint multi, max: ' + this.fontOptions.maxWdt); 44592 for (var j = 0; j < blocks.length; j++) { 44593 var mod = blocks[j].mod; 44594 var _text = blocks[j].text; 44595 this.splitStringIntoLines(_text, mod, true); 44596 } 44597 } else { 44598 // widthConstraint.maximum NOT defined 44599 for (var _j = 0; _j < blocks.length; _j++) { 44600 var _mod = blocks[_j].mod; 44601 var _text2 = blocks[_j].text; 44602 this.lines.append(_text2, _mod); 44603 } 44604 } 44605 44606 this.lines.newLine(); 44607 } 44608 } else { 44609 // Single-font case 44610 if (font.maxWdt > 0) { 44611 // widthConstraint.maximum defined 44612 // console.log('Running widthConstraint normal, max: ' + this.fontOptions.maxWdt); 44613 for (var _i = 0; _i < lineCount; _i++) { 44614 this.splitStringIntoLines(nlLines[_i]); 44615 } 44616 } else { 44617 // widthConstraint.maximum NOT defined 44618 for (var _i2 = 0; _i2 < lineCount; _i2++) { 44619 this.lines.newLine(nlLines[_i2]); 44620 } 44621 } 44622 } 44623 44624 return this.lines.finalize(); 44625 } 44626 44627 /** 44628 * normalize the markup system 44629 * 44630 * @param {boolean|'md'|'markdown'|'html'} markupSystem 44631 * @returns {string} 44632 */ 44633 44634 }, {
44635 key: 'decodeMarkupSystem', 44636 value: function decodeMarkupSystem(markupSystem) { 44637 var system = 'none'; 44638 if (markupSystem === 'markdown' || markupSystem === 'md') { 44639 system = 'markdown'; 44640 } else if (markupSystem === true || markupSystem === 'html') { 44641 system = 'html'; 44642 } 44643 return system; 44644 } 44645 44646 /** 44647 * 44648 * @param {string} text 44649 * @returns {Array} 44650 */ 44651 44652 }, { 44653 key: 'splitHtmlBlocks', 44654 value: function splitHtmlBlocks(text) { 44655 var blocks = []; 44656 44657 // TODO: consolidate following + methods/closures with splitMarkdownBlocks() 44658 // NOTE: sequences of tabs and spaces are reduced to single space; scan usage of `this.spacing` within method 44659 var s = { 44660 bold: false, 44661 ital: false, 44662 mono: false, 44663 spacing: false, 44664 position: 0, 44665 buffer: "", 44666 modStack: [] 44667 }; 44668 44669 s.mod = function () { 44670 return this.modStack.length === 0 ? 'normal' : this.modStack[0]; 44671 }; 44672 44673 s.modName = function () { 44674 if (this.modStack.length === 0) return 'normal';else if (this.modStack[0] === 'mono') return 'mono';else { 44675 if (s.bold && s.ital) { 44676 return 'boldital'; 44677 } else if (s.bold) { 44678 return 'bold'; 44679 } else if (s.ital) { 44680 return 'ital'; 44681 } 44682 } 44683 }; 44684 44685 s.emitBlock = function () { 44686 var override = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false; 44687 // eslint-disable-line no-unused-vars 44688 if (this.spacing) { 44689 this.add(" "); 44690 this.spacing = false; 44691 } 44692 if (this.buffer.length > 0) { 44693 blocks.push({ text: this.buffer, mod: this.modName() }); 44694 this.buffer = ""; 44695 } 44696 }; 44697 44698 s.add = function (text) { 44699 if (text === " ") { 44700 s.spacing = true; 44701 } 44702 if (s.spacing) { 44703 this.buffer += " "; 44704 this.spacing = false; 44705 } 44706 if (text != " ") { 44707 this.buffer += text; 44708 } 44709 }; 44710 44711 while (s.position < text.length) { 44712 var ch = text.charAt(s.position); 44713 if (/[ \t]/.test(ch)) { 44714 if (!s.mono) { 44715 s.spacing = true; 44716 } else { 44717 s.add(ch); 44718 } 44719 } else if (/</.test(ch)) { 44720 if (!s.mono && !s.bold && /<b>/.test(text.substr(s.position, 3))) { 44721 s.emitBlock(); 44722 s.bold = true; 44723 s.modStack.unshift("bold"); 44724 s.position += 2; 44725 } else if (!s.mono && !s.ital && /<i>/.test(text.substr(s.position, 3))) { 44726 s.emitBlock(); 44727 s.ital = true; 44728 s.modStack.unshift("ital"); 44729 s.position += 2; 44730 } else if (!s.mono && /<code>/.test(text.substr(s.position, 6))) { 44731 s.emitBlock(); 44732 s.mono = true; 44733 s.modStack.unshift("mono"); 44734 s.position += 5; 44735 } else if (!s.mono && s.mod() === 'bold' && /<\/b>/.test(text.substr(s.position, 4))) { 44736 s.emitBlock(); 44737 s.bold = false; 44738 s.modStack.shift(); 44739 s.position += 3; 44740 } else if (!s.mono && s.mod() === 'ital' && /<\/i>/.test(text.substr(s.position, 4))) { 44741 s.emitBlock(); 44742 s.ital = false; 44743 s.modStack.shift(); 44744 s.position += 3; 44745 } else if (s.mod() === 'mono' && /<\/code>/.test(text.substr(s.position, 7))) { 44746 s.emitBlock(); 44747 s.mono = false; 44748 s.modStack.shift(); 44749 s.position += 6; 44750 } else { 44751 s.add(ch); 44752 } 44753 } else if (/&/.test(ch)) { 44754 if (/</.test(text.substr(s.position, 4))) { 44755 s.add("<"); 44756 s.position += 3; 44757 } else if (/&/.test(text.substr(s.position, 5))) { 44758 s.add("&"); 44759 s.position += 4; 44760 } else { 44761 s.add("&"); 44762 } 44763 } else { 44764 s.add(ch); 44765 } 44766 s.position++; 44767 } 44768 s.emitBlock(); 44769 return blocks; 44770 } 44771 44772 /** 44773 * 44774 * @param {string} text 44775 * @returns {Array} 44776 */ 44777 44778 }, { 44779 key: 'splitMarkdownBlocks', 44780 value: function splitMarkdownBlocks(text) { 44781 var blocks = []; 44782 44783 // TODO: consolidate following + methods/closures with splitHtmlBlocks() 44784 // NOTE: sequences of tabs and spaces are reduced to single space; scan usage of `this.spacing` within method 44785 var s = {
44786 bold: false, 44787 ital: false, 44788 mono: false, 44789 beginable: true, 44790 spacing: false, 44791 position: 0, 44792 buffer: "", 44793 modStack: [] 44794 }; 44795 44796 s.mod = function () { 44797 return this.modStack.length === 0 ? 'normal' : this.modStack[0]; 44798 }; 44799 44800 s.modName = function () { 44801 if (this.modStack.length === 0) return 'normal';else if (this.modStack[0] === 'mono') return 'mono';else { 44802 if (s.bold && s.ital) { 44803 return 'boldital'; 44804 } else if (s.bold) { 44805 return 'bold'; 44806 } else if (s.ital) { 44807 return 'ital'; 44808 } 44809 } 44810 }; 44811 44812 s.emitBlock = function () { 44813 var override = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false; 44814 // eslint-disable-line no-unused-vars 44815 if (this.spacing) { 44816 this.add(" "); 44817 this.spacing = false; 44818 } 44819 if (this.buffer.length > 0) { 44820 blocks.push({ text: this.buffer, mod: this.modName() }); 44821 this.buffer = ""; 44822 } 44823 }; 44824 44825 s.add = function (text) { 44826 if (text === " ") { 44827 s.spacing = true; 44828 } 44829 if (s.spacing) { 44830 this.buffer += " "; 44831 this.spacing = false; 44832 } 44833 if (text != " ") { 44834 this.buffer += text; 44835 } 44836 }; 44837 44838 while (s.position < text.length) { 44839 var ch = text.charAt(s.position); 44840 if (/[ \t]/.test(ch)) { 44841 if (!s.mono) { 44842 s.spacing = true; 44843 } else { 44844 s.add(ch); 44845 } 44846 s.beginable = true; 44847 } else if (/\\/.test(ch)) { 44848 if (s.position < text.length + 1) { 44849 s.position++; 44850 ch = text.charAt(s.position); 44851 if (/ \t/.test(ch)) { 44852 s.spacing = true; 44853 } else { 44854 s.add(ch); 44855 s.beginable = false; 44856 } 44857 } 44858 } else if (!s.mono && !s.bold && (s.beginable || s.spacing) && /\*/.test(ch)) { 44859 s.emitBlock(); 44860 s.bold = true; 44861 s.modStack.unshift("bold"); 44862 } else if (!s.mono && !s.ital && (s.beginable || s.spacing) && /\_/.test(ch)) { 44863 s.emitBlock(); 44864 s.ital = true; 44865 s.modStack.unshift("ital"); 44866 } else if (!s.mono && (s.beginable || s.spacing) && /`/.test(ch)) { 44867 s.emitBlock(); 44868 s.mono = true; 44869 s.modStack.unshift("mono"); 44870 } else if (!s.mono && s.mod() === "bold" && /\*/.test(ch)) { 44871 if (s.position === text.length - 1 || /[.,_` \t\n]/.test(text.charAt(s.position + 1))) { 44872 s.emitBlock(); 44873 s.bold = false; 44874 s.modStack.shift(); 44875 } else { 44876 s.add(ch); 44877 } 44878 } else if (!s.mono && s.mod() === "ital" && /\_/.test(ch)) { 44879 if (s.position === text.length - 1 || /[.,*` \t\n]/.test(text.charAt(s.position + 1))) { 44880 s.emitBlock(); 44881 s.ital = false; 44882 s.modStack.shift(); 44883 } else { 44884 s.add(ch); 44885 } 44886 } else if (s.mono && s.mod() === "mono" && /`/.test(ch)) { 44887 if (s.position === text.length - 1 || /[.,*_ \t\n]/.test(text.charAt(s.position + 1))) { 44888 s.emitBlock(); 44889 s.mono = false; 44890 s.modStack.shift(); 44891 } else { 44892 s.add(ch); 44893 } 44894 } else { 44895 s.add(ch); 44896 s.beginable = false; 44897 } 44898 s.position++; 44899 } 44900 s.emitBlock(); 44901 return blocks; 44902 } 44903 44904 /** 44905 * Explodes a piece of text into single-font blocks using a given markup 44906 * 44907 * @param {string} text 44908 * @param {boolean|'md'|'markdown'|'html'} markupSystem 44909 * @returns {Array.<{text: string, mod: string}>} 44910 * @private 44911 */ 44912 44913 }, { 44914 key: 'splitBlocks', 44915 value: function splitBlocks(text, markupSystem) { 44916 var system = this.decodeMarkupSystem(markupSystem); 44917 if (system === 'none') { 44918 return [{ 44919 text: text, 44920 mod: 'normal' 44921 }]; 44922 } else if (system === 'markdown') { 44923 return this.splitMarkdownBlocks(text); 44924 } else if (system === 'html') { 44925 return this.splitHtmlBlocks(text); 44926 } 44927 } 44928 44929 /** 44930 * @param {string} text 44931 * @returns {boolean} true if text length over the current max with 44932 * @private 44933 */ 44934 44935 }, { 44936 key: 'overMaxWidth', 44937 value: function overMaxWidth(text) { 44938 var width = this.ctx.measureText(text).width; 44939 return this.lines.curWidth() + width > this.parent.fontOptions.maxWdt; 44940 } 44941 44942 /** 44943 * Determine the longest part of the sentence which still fits in the 44944 * current max width. 44945 * 44946 * @param {Array}
44946 words Array of strings signifying a text lines 44947 * @return {number} index of first item in string making string go over max 44948 * @private 44949 */ 44950 44951 }, { 44952 key: 'getLongestFit', 44953 value: function getLongestFit(words) { 44954 var text = ''; 44955 var w = 0; 44956 44957 while (w < words.length) { 44958 var pre = text === '' ? '' : ' '; 44959 var newText = text + pre + words[w]; 44960 44961 if (this.overMaxWidth(newText)) break; 44962 text = newText; 44963 w++; 44964 } 44965 44966 return w; 44967 } 44968 44969 /** 44970 * Determine the longest part of the string which still fits in the 44971 * current max width. 44972 * 44973 * @param {Array} words Array of strings signifying a text lines 44974 * @return {number} index of first item in string making string go over max 44975 */ 44976 44977 }, { 44978 key: 'getLongestFitWord', 44979 value: function getLongestFitWord(words) { 44980 var w = 0; 44981 44982 while (w < words.length) { 44983 if (this.overMaxWidth(words.slice(0, w))) break; 44984 w++; 44985 } 44986 44987 return w; 44988 } 44989 44990 /** 44991 * Split the passed text into lines, according to width constraint (if any). 44992 * 44993 * The method assumes that the input string is a single line, i.e. without lines break. 44994 * 44995 * This method retains spaces, if still present (case `font.multi: false`). 44996 * A space which falls on an internal line break, will be replaced by a newline. 44997 * There is no special handling of tabs; these go along with the flow. 44998 * 44999 * @param {string} str 45000 * @param {string} [mod='normal'] 45001 * @param {boolean} [appendLast=false] 45002 * @private 45003 */ 45004 45005 }, { 45006 key: 'splitStringIntoLines', 45007 value: function splitStringIntoLines(str) { 45008 var mod = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'normal'; 45009 var appendLast = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false; 45010 45011 // Still-present spaces are relevant, retain them 45012 str = str.replace(/^( +)/g, '$1\r'); 45013 str = str.replace(/([^\r][^ ]*)( +)/g, '$1\r$2\r'); 45014 var words = str.split('\r'); 45015 45016 while (words.length > 0) { 45017 var w = this.getLongestFit(words); 45018 45019 if (w === 0) { 45020 // Special case: the first word is already larger than the max width. 45021 var word = words[0]; 45022 45023 // Break the word to the largest part that fits the line 45024 var x = this.getLongestFitWord(word); 45025 this.lines.newLine(word.slice(0, x), mod); 45026 45027 // Adjust the word, so that the rest will be done next iteration 45028 words[0] = word.slice(x); 45029 } else { 45030 // skip any space that is replaced by a newline 45031 var newW = w; 45032 if (words[w - 1] === ' ') { 45033 w--; 45034 } else if (words[newW] === ' ') { 45035 newW++; 45036 } 45037 45038 var text = words.slice(0, w).join(""); 45039 45040 if (w == words.length && appendLast) { 45041 this.lines.append(text, mod); 45042 } else { 45043 this.lines.newLine(text, mod); 45044 } 45045 45046 // Adjust the word, so that the rest will be done next iteration 45047 words = words.slice(newW); 45048 } 45049 } 45050 } 45051 }]); 45052 return LabelSplitter; 45053}(); 45054 45055exports['default'] = LabelSplitter; 45056 45057/***/ }), 45058/* 192 */ 45059/***/ (function(module, exports, __webpack_require__) { 45060 45061"use strict"; 45062 45063 45064Object.defineProperty(exports, "__esModule", { 45065 value: true 45066}); 45067 45068var _assign = __webpack_require__(90); 45069 45070var _assign2 = _interopRequireDefault(_assign); 45071 45072var _classCallCheck2 = __webpack_require__(0); 45073 45074var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); 45075 45076var _createClass2 = __webpack_require__(1); 45077 45078var _createClass3 = _interopRequireDefault(_createClass2); 45079 45080function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } 45081 45082/** 45083 * Callback to determine text dimensions, using the parent label settings. 45084 * @callback MeasureText 45085 * @param {text} text 45086 * @param {text} mod 45087 * @return {Object} { width, values} width in pixels and font attributes 45088 */ 45089 45090/** 45091 * Helper class for Label which collects results of splitting labels into lines and blocks. 45092 * 45093 * @private 45094 */ 45095var LabelAccumulator = function () { 45096 45097 /** 45098 * @param {MeasureText} measureText 45099 */ 45100 function LabelAccumulator(measureText) { 45101 (0, _classCallCheck3["default"])(this, LabelAccumulator); 45102 45103 this.measureText = measureText; 45104 this.current = 0; 45105 this.width = 0; 45106 this.height = 0; 45107 this.lines = []; 45108 } 45109 45110 /** 45111 * Append given text to the given line. 45112 * 45113 * @param {number} l index of line to add to 45114 * @param {string}
45114 text string to append to line 45115 * @param {'bold'|'ital'|'boldital'|'mono'|'normal'} [mod='normal'] 45116 * @private 45117 */ 45118 45119 45120 (0, _createClass3["default"])(LabelAccumulator, [{ 45121 key: "_add", 45122 value: function _add(l, text) { 45123 var mod = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 'normal'; 45124 45125 45126 if (this.lines[l] === undefined) { 45127 this.lines[l] = { 45128 width: 0, 45129 height: 0, 45130 blocks: [] 45131 }; 45132 } 45133 45134 // We still need to set a block for undefined and empty texts, hence return at this point 45135 // This is necessary because we don't know at this point if we're at the 45136 // start of an empty line or not. 45137 // To compensate, empty blocks are removed in `finalize()`. 45138 // 45139 // Empty strings should still have a height 45140 var tmpText = text; 45141 if (text === undefined || text === "") tmpText = " "; 45142 45143 // Determine width and get the font properties 45144 var result = this.measureText(tmpText, mod); 45145 var block = (0, _assign2["default"])({}, result.values); 45146 block.text = text; 45147 block.width = result.width; 45148 block.mod = mod; 45149 45150 if (text === undefined || text === "") { 45151 block.width = 0; 45152 } 45153 45154 this.lines[l].blocks.push(block); 45155 45156 // Update the line width. We need this for determining if a string goes over max width 45157 this.lines[l].width += block.width; 45158 } 45159 45160 /** 45161 * Returns the width in pixels of the current line. 45162 * 45163 * @returns {number} 45164 */ 45165 45166 }, { 45167 key: "curWidth", 45168 value: function curWidth() { 45169 var line = this.lines[this.current]; 45170 if (line === undefined) return 0; 45171 45172 return line.width; 45173 } 45174 45175 /** 45176 * Add text in block to current line 45177 * 45178 * @param {string} text 45179 * @param {'bold'|'ital'|'boldital'|'mono'|'normal'} [mod='normal'] 45180 */ 45181 45182 }, { 45183 key: "append", 45184 value: function append(text) { 45185 var mod = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'normal'; 45186 45187 this._add(this.current, text, mod); 45188 } 45189 45190 /** 45191 * Add text in block to current line and start a new line 45192 * 45193 * @param {string} text 45194 * @param {'bold'|'ital'|'boldital'|'mono'|'normal'} [mod='normal'] 45195 */ 45196 45197 }, { 45198 key: "newLine", 45199 value: function newLine(text) { 45200 var mod = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'normal'; 45201 45202 this._add(this.current, text, mod); 45203 this.current++; 45204 } 45205 45206 /** 45207 * Determine and set the heights of all the lines currently contained in this instance 45208 * 45209 * Note that width has already been set. 45210 * 45211 * @private 45212 */ 45213 45214 }, { 45215 key: "determineLineHeights", 45216 value: function determineLineHeights() { 45217 for (var k = 0; k < this.lines.length; k++) { 45218 var line = this.lines[k]; 45219 45220 // Looking for max height of blocks in line 45221 var height = 0; 45222 45223 if (line.blocks !== undefined) { 45224 // Can happen if text contains e.g. '\n ' 45225 for (var l = 0; l < line.blocks.length; l++) { 45226 var block = line.blocks[l]; 45227 45228 if (height < block.height) { 45229 height = block.height; 45230 } 45231 } 45232 } 45233 45234 line.height = height; 45235 } 45236 } 45237 45238 /** 45239 * Determine the full size of the label text, as determined by current lines and blocks 45240 * 45241 * @private 45242 */ 45243 45244 }, { 45245 key: "determineLabelSize", 45246 value: function determineLabelSize() { 45247 var width = 0; 45248 var height = 0; 45249 for (var k = 0; k < this.lines.length; k++) { 45250 var line = this.lines[k]; 45251 45252 if (line.width > width) { 45253 width = line.width; 45254 } 45255 height += line.height; 45256 } 45257 45258 this.width = width; 45259 this.height = height; 45260 } 45261 45262 /** 45263 * Remove all empty blocks and empty lines we don't need 45264 * 45265 * This must be done after the width/height determination, 45266 * so that these are set properly for processing here. 45267 * 45268 * @returns {Array<Line>} Lines with empty blocks (and some empty lines) removed 45269 * @private 45270 */ 45271 45272 }, { 45273 key: "removeEmptyBlocks", 45274 value: function removeEmptyBlocks() { 45275 var tmpLines = []; 45276 for (var k = 0; k < this.lines.length; k++) { 45277 var line = this.lines[k]; 45278 45279 // Note: an empty line in between text has width zero but is still relevant to layout. 45280 // So we can't use width for testing empty line here 45281 if (line.blocks.length === 0) continue; 45282 45283 // Discard final empty line always 45284 if (k === this.lines.length - 1) { 45285 if (line.width === 0) continue; 45286 } 45287 45288 var tmpLine = {}; 45289 (0, _assign2["default"])(tmpLine, line); 45290 tmpLine.blocks = []; 45291 45292 var firstEmptyBlock = void 0; 45293 var tmpBlocks = []; 45294 for (var l = 0; l < line.blocks.length; l++) { 45295 var block = line.blocks[l]; 45296 if (block.width !== 0) { 45297 tmpBlocks.push(block); 45298 } else { 45299 if (firstEmptyBlock === undefined) { 45300 firstEmptyBlock = block; 45301 } 45302 } 45303 } 45304 45305 // Ensure that there is *some* text present 45306 if (tmpBlocks.length === 0 && firstEmptyBlock !== undefined) { 45307 tmpBlocks.push(firstEmptyBlock); 45308 } 45309 45310 tmpLine.blocks = tmpBlocks; 45311 45312 tmpLines.push(tmpLine); 45313 } 45314 45315 return tmpLines; 45316 } 45317 45318 /** 45319 * Set the sizes for all lines and the whole thing. 45320 * 45321 * @returns {{width: (number|*), height: (number|*), lines: Array}} 45322 */ 45323 45324 }, { 45325 key: "finalize", 45326 value: function finalize() { 45327 //console.log(JSON.stringify(this.lines, null, 2)); 45328 45329 this.determineLineHeights(); 45330 this.determineLabelSize(); 45331 var tmpLines = this.removeEmptyBlocks(); 45332 45333 // Return a simple hash object for further processing. 45334 return { 45335 width: this.width, 45336 height: this.height, 45337 lines: tmpLines 45338 }; 45339 } 45340 }]); 45341 return LabelAccumulator; 45342}(); 45343 45344exports["default"] = LabelAccumulator; 45345 45346/***/ }), 45347/* 193 */ 45348/***/ (function(module, exports, __webpack_require__) { 45349 45350"use strict"; 45351 45352 45353Object.defineProperty(exports, "__esModule", { 45354 value: true 45355}); 45356 45357var _getPrototypeOf = __webpack_require__(3); 45358 45359var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf); 45360 45361var _classCallCheck2 = __webpack_require__(0); 45362 45363var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); 45364 45365var _createClass2 = __webpack_require__(1); 45366 45367var _createClass3 = _interopRequireDefault(_createClass2); 45368 45369var _possibleConstructorReturn2 = __webpack_require__(4); 45370 45371var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2); 45372 45373var _inherits2 = __webpack_require__(5); 45374 45375var _inherits3 = _interopRequireDefault(_inherits2); 45376 45377var _NodeBase2 = __webpack_require__(23); 45378 45379var _NodeBase3 = _interopRequireDefault(_NodeBase2); 45380 45381function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 45382 45383/** 45384 * A Box Node/Cluster shape. 45385 * 45386 * @extends NodeBase 45387 */ 45388var Box = function (_NodeBase) { 45389 (0, _inherits3['default'])(Box, _NodeBase); 45390 45391 /** 45392 * @param {Object} options 45393 * @param {Object} body 45394 * @param {Label} labelModule 45395 */ 45396 function Box(options, body, labelModule) { 45397 (0, _classCallCheck3['default'])(this, Box); 45398 45399 var _this = (0, _possibleConstructorReturn3['default'])(this, (Box.__proto__ || (0, _getPrototypeOf2['default'])(Box)).call(this, options, body, labelModule)); 45400 45401 _this._setMargins(labelModule); 45402 return _this; 45403 } 45404 45405 /** 45406 * 45407 * @param {CanvasRenderingContext2D} ctx 45408 * @param {boolean} [selected] 45409 * @param {boolean} [hover] 45410 */ 45411 45412 45413 (0, _createClass3['default'])(Box, [{ 45414 key: 'resize', 45415 value: function resize(ctx) { 45416 var selected = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : this.selected; 45417 var hover = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : this.hover; 45418 45419 if (this.needsRefresh(selected, hover)) { 45420 var dimensions = this.getDimensionsFromLabel(ctx, selected, hover); 45421 45422 this.width = dimensions.width + this.margin.right + this.margin.left; 45423 this.height = dimensions.height + this.margin.top + this.margin.bottom; 45424 this.radius = this.width / 2; 45425 } 45426 } 45427 45428 /** 45429 * 45430 * @param {CanvasRenderingContext2D} ctx 45431 * @param {number} x width 45432 * @param {number} y height 45433 * @param {boolean} selected 45434 * @param {boolean} hover 45435 * @param {{toArrow: boolean, toArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), toArrowType: *, middleArrow: boolean, middleArrowScale: (number|allOptions.edges.arrows.middle.scaleFactor|{number}|Array), middleArrowType: (allOptions.edges.arrows.middle.type|{string}|string|*), fromArrow: boolean, fromArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), fromArrowType: *, arrowStrikethrough: (*|boolean|allOptions.edges.arrowStrikethrough|{boolean}), color: undefined, inheritsColor: (string|string|string|allOptions.edges.color.inherit|{string, boolean}|Array|*), opacity: *, hidden: *, length: *, shadow: *, shadowColor: *, shadowSize: *, shadowX: *, shadowY: *, dashes: (*|boolean|Array|allOptions.edges.dashes|{boolean, array}), width: *}} values 45436 */ 45437 45438 }, { 45439 key: 'draw', 45440 value: function draw(ctx, x, y, selected, hover, values) { 45441 this.resize(ctx, selected, hover); 45442 this.left = x - this.width / 2; 45443 this.top = y - this.height / 2; 45444 45445 this.initContextForDraw(ctx, values); 45446 ctx.roundRect(this.left, this.top, this.width, this.height, values.borderRadius); 45447 this.performFill(ctx, values); 45448 45449 this.updateBoundingBox(x, y, ctx, selected, hover); 45450 this.labelModule.draw(ctx, this.left + this.textSize.width / 2 + this.margin.left, this.top + this.textSize.height / 2 + this.margin.top, selected, hover); 45451 } 45452 45453 /** 45454 * 45455 * @param {number} x width 45456 * @param {number} y height 45457 * @param {CanvasRenderingContext2D} ctx 45458 * @param {boolean} selected 45459 * @param {boolean} hover 45460 */ 45461 45462 }, { 45463 key: 'updateBoundingBox', 45464 value: function updateBoundingBox(x, y, ctx, selected, hover) { 45465 this._updateBoundingBox(x, y, ctx, selected, hover); 45466 45467 var borderRadius = this.options.shapeProperties.borderRadius; // only effective for box 45468 this._addBoundingBoxMargin(borderRadius); 45469 } 45470 45471 /** 45472 * 45473 * @param {CanvasRenderingContext2D} ctx 45474 * @param {number} angle 45475 * @returns {number} 45476 */ 45477 45478 }, { 45479 key: 'distanceToBorder', 45480 value: function distanceToBorder(ctx, angle) { 45481 this.resize(ctx); 45482 var borderWidth = this.options.borderWidth; 45483 45484 return Math.min(Math.abs(this.width / 2 / Math.cos(angle)), Math.abs(this.height / 2 / Math.sin(angle))) + borderWidth; 45485 } 45486 }]); 45487 return Box; 45488}(_NodeBase3['default']); 45489 45490exports['default'] = Box; 45491 45492/***/ }), 45493/* 194 */ 45494/***/ (function(module, exports, __webpack_require__) { 45495 45496__webpack_require__(195);
45497module.exports = __webpack_require__(7).Object.getPrototypeOf; 45498 45499 45500/***/ }), 45501/* 195 */ 45502/***/ (function(module, exports, __webpack_require__) { 45503 45504// 19.1.2.9 Object.getPrototypeOf(O) 45505var toObject = __webpack_require__(41); 45506var $getPrototypeOf = __webpack_require__(85); 45507 45508__webpack_require__(87)('getPrototypeOf', function () { 45509 return function getPrototypeOf(it) { 45510 return $getPrototypeOf(toObject(it)); 45511 }; 45512}); 45513 45514 45515/***/ }), 45516/* 196 */ 45517/***/ (function(module, exports, __webpack_require__) { 45518 45519module.exports = { "default": __webpack_require__(197), __esModule: true }; 45520 45521/***/ }), 45522/* 197 */ 45523/***/ (function(module, exports, __webpack_require__) { 45524 45525__webpack_require__(198); 45526module.exports = __webpack_require__(7).Object.setPrototypeOf; 45527 45528 45529/***/ }), 45530/* 198 */ 45531/***/ (function(module, exports, __webpack_require__) { 45532 45533// 19.1.3.19 Object.setPrototypeOf(O, proto) 45534var $export = __webpack_require__(17); 45535$export($export.S, 'Object', { setPrototypeOf: __webpack_require__(199).set }); 45536 45537 45538/***/ }), 45539/* 199 */ 45540/***/ (function(module, exports, __webpack_require__) { 45541 45542// Works with __proto__ only. Old v8 can't work with null proto objects. 45543/* eslint-disable no-proto */ 45544var isObject = __webpack_require__(32); 45545var anObject = __webpack_require__(27); 45546var check = function (O, proto) { 45547 anObject(O); 45548 if (!isObject(proto) && proto !== null) throw TypeError(proto + ": can't set as prototype!"); 45549}; 45550module.exports = { 45551 set: Object.setPrototypeOf || ('__proto__' in {} ? // eslint-disable-line 45552 function (test, buggy, set) { 45553 try { 45554 set = __webpack_require__(80)(Function.call, __webpack_require__(89).f(Object.prototype, '__proto__').set, 2); 45555 set(test, []); 45556 buggy = !(test instanceof Array); 45557 } catch (e) { buggy = true; } 45558 return function setPrototypeOf(O, proto) { 45559 check(O, proto); 45560 if (buggy) O.__proto__ = proto; 45561 else set(O, proto); 45562 return O; 45563 }; 45564 }({}, false) : undefined), 45565 check: check 45566}; 45567 45568 45569/***/ }), 45570/* 200 */ 45571/***/ (function(module, exports, __webpack_require__) { 45572 45573"use strict"; 45574 45575 45576Object.defineProperty(exports, "__esModule", { 45577 value: true 45578}); 45579 45580var _getPrototypeOf = __webpack_require__(3); 45581 45582var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf); 45583 45584var _classCallCheck2 = __webpack_require__(0); 45585 45586var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); 45587 45588var _createClass2 = __webpack_require__(1); 45589 45590var _createClass3 = _interopRequireDefault(_createClass2); 45591 45592var _possibleConstructorReturn2 = __webpack_require__(4); 45593 45594var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2); 45595 45596var _inherits2 = __webpack_require__(5); 45597 45598var _inherits3 = _interopRequireDefault(_inherits2); 45599 45600var _CircleImageBase2 = __webpack_require__(73); 45601 45602var _CircleImageBase3 = _interopRequireDefault(_CircleImageBase2); 45603 45604function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 45605 45606/** 45607 * A Circle Node/Cluster shape. 45608 * 45609 * @extends CircleImageBase 45610 */ 45611var Circle = function (_CircleImageBase) { 45612 (0, _inherits3['default'])(Circle, _CircleImageBase); 45613 45614 /** 45615 * @param {Object} options 45616 * @param {Object} body 45617 * @param {Label} labelModule 45618 */ 45619 function Circle(options, body, labelModule) { 45620 (0, _classCallCheck3['default'])(this, Circle); 45621 45622 var _this = (0, _possibleConstructorReturn3['default'])(this, (Circle.__proto__ || (0, _getPrototypeOf2['default'])(Circle)).call(this, options, body, labelModule)); 45623 45624 _this._setMargins(labelModule); 45625 return _this; 45626 } 45627 45628 /** 45629 * 45630 * @param {CanvasRenderingContext2D} ctx 45631 * @param {boolean} [selected] 45632 * @param {boolean} [hover] 45633 */ 45634 45635 45636 (0, _createClass3['default'])(Circle, [{ 45637 key: 'resize', 45638 value: function resize(ctx) { 45639 var selected = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : this.selected; 45640 var hover = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : this.hover; 45641 45642 if (this.needsRefresh(selected, hover)) { 45643 var dimensions = this.getDimensionsFromLabel(ctx, selected, hover); 45644 45645 var diameter = Math.max(dimensions.width + this.margin.right + this.margin.left, dimensions.height + this.margin.top + this.margin.bottom); 45646 45647 this.options.size = diameter / 2; // NOTE: this size field only set here, not in Ellipse, Database, Box 45648 this.width = diameter; 45649 this.height = diameter; 45650 this.radius = this.width / 2; 45651 } 45652 } 45653 45654 /** 45655 * 45656 * @param {CanvasRenderingContext2D} ctx 45657 * @param {number} x width 45658 * @param {number} y height 45659 * @param {boolean} selected 45660 * @param {boolean} hover 45661 * @param {{toArrow: boolean, toArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), toArrowType: *, middleArrow: boolean, middleArrowScale: (number|allOptions.edges.arrows.middle.scaleFactor|{number}|Array), middleArrowType: (allOptions.edges.arrows.middle.type|{string}|string|*), fromArrow: boolean, fromArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), fromArrowType: *, arrowStrikethrough: (*|boolean|allOptions.edges.arrowStrikethrough|{boolean}), color: undefined, inheritsColor: (string|string|string|allOptions.edges.color.inherit|{string, boolean}|Array|*), opacity: *, hidden: *, length: *, shadow: *, shadowColor: *, shadowSize: *, shadowX: *, shadowY: *, dashes: (*|boolean|Array|allOptions.edges.dashes|{boolean, array}), width: *}} values 45662 */ 45663 45664 }, { 45665 key: 'draw', 45666 value: function draw(ctx, x, y, selected, hover, values) { 45667 this.resize(ctx, selected, hover); 45668 this.left = x - this.width / 2; 45669 this.top = y - this.height / 2; 45670 45671 this._drawRawCircle(ctx, x, y, values); 45672 45673 this.updateBoundingBox(x, y); 45674 this.labelModule.draw(ctx, this.left + this.textSize.width / 2 + this.margin.left, y, selected, hover); 45675 } 45676 45677 /** 45678 * 45679 * @param {number} x width 45680 * @param {number} y height 45681 */ 45682 45683 }, { 45684 key: 'updateBoundingBox', 45685 value: function updateBoundingBox(x, y) { 45686 this.boundingBox.top = y - this.options.size; 45687 this.boundingBox.left = x - this.options.size; 45688 this.boundingBox.right = x + this.options.size; 45689 this.boundingBox.bottom = y + this.options.size; 45690 } 45691 45692 /** 45693 * 45694 * @param {CanvasRenderingContext2D} ctx 45695 * @param {number} angle - Unused 45696 * @returns {number} 45697 */ 45698 45699 }, { 45700 key: 'distanceToBorder', 45701 value: function distanceToBorder(ctx, angle) { 45702 // eslint-disable-line no-unused-vars 45703 this.resize(ctx); 45704 return this.width * 0.5; 45705 } 45706 }]); 45707 return Circle; 45708}
45708(_CircleImageBase3['default']); 45709 45710exports['default'] = Circle; 45711 45712/***/ }), 45713/* 201 */ 45714/***/ (function(module, exports, __webpack_require__) { 45715 45716"use strict"; 45717 45718 45719Object.defineProperty(exports, "__esModule", { 45720 value: true 45721}); 45722 45723var _getPrototypeOf = __webpack_require__(3); 45724 45725var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf); 45726 45727var _classCallCheck2 = __webpack_require__(0); 45728 45729var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); 45730 45731var _createClass2 = __webpack_require__(1); 45732 45733var _createClass3 = _interopRequireDefault(_createClass2); 45734 45735var _possibleConstructorReturn2 = __webpack_require__(4); 45736 45737var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2); 45738 45739var _inherits2 = __webpack_require__(5); 45740 45741var _inherits3 = _interopRequireDefault(_inherits2); 45742 45743var _CircleImageBase2 = __webpack_require__(73); 45744 45745var _CircleImageBase3 = _interopRequireDefault(_CircleImageBase2); 45746 45747function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 45748 45749/** 45750 * A CircularImage Node/Cluster shape. 45751 * 45752 * @extends CircleImageBase 45753 */ 45754var CircularImage = function (_CircleImageBase) { 45755 (0, _inherits3['default'])(CircularImage, _CircleImageBase); 45756 45757 /** 45758 * @param {Object} options 45759 * @param {Object} body 45760 * @param {Label} labelModule 45761 * @param {Image} imageObj 45762 * @param {Image} imageObjAlt 45763 */ 45764 function CircularImage(options, body, labelModule, imageObj, imageObjAlt) { 45765 (0, _classCallCheck3['default'])(this, CircularImage); 45766 45767 var _this = (0, _possibleConstructorReturn3['default'])(this, (CircularImage.__proto__ || (0, _getPrototypeOf2['default'])(CircularImage)).call(this, options, body, labelModule)); 45768 45769 _this.setImages(imageObj, imageObjAlt); 45770 return _this; 45771 } 45772 45773 /** 45774 * 45775 * @param {CanvasRenderingContext2D} ctx 45776 * @param {boolean} [selected] 45777 * @param {boolean} [hover] 45778 */ 45779 45780 45781 (0, _createClass3['default'])(CircularImage, [{ 45782 key: 'resize', 45783 value: function resize(ctx) { 45784 var selected = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : this.selected; 45785 var hover = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : this.hover; 45786 45787 var imageAbsent = this.imageObj.src === undefined || this.imageObj.width === undefined || this.imageObj.height === undefined; 45788 45789 if (imageAbsent) { 45790 var diameter = this.options.size * 2; 45791 this.width = diameter; 45792 this.height = diameter; 45793 this.radius = 0.5 * this.width; 45794 return; 45795 } 45796 45797 // At this point, an image is present, i.e. this.imageObj is valid. 45798 if (this.needsRefresh(selected, hover)) { 45799 this._resizeImage(); 45800 } 45801 } 45802 45803 /** 45804 * 45805 * @param {CanvasRenderingContext2D} ctx 45806 * @param {number} x width 45807 * @param {number} y height 45808 * @param {boolean} selected 45809 * @param {boolean} hover 45810 * @param {{toArrow: boolean, toArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), toArrowType: *, middleArrow: boolean, middleArrowScale: (number|allOptions.edges.arrows.middle.scaleFactor|{number}|Array), middleArrowType: (allOptions.edges.arrows.middle.type|{string}|string|*), fromArrow: boolean, fromArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), fromArrowType: *, arrowStrikethrough: (*|boolean|allOptions.edges.arrowStrikethrough|{boolean}), color: undefined, inheritsColor: (string|string|string|allOptions.edges.color.inherit|{string, boolean}|Array|*), opacity: *, hidden: *, length: *, shadow: *, shadowColor: *, shadowSize: *, shadowX: *, shadowY: *, dashes: (*|boolean|Array|allOptions.edges.dashes|{boolean, array}), width: *}} values 45811 */ 45812 45813 }, { 45814 key: 'draw', 45815 value: function draw(ctx, x, y, selected, hover, values) { 45816 this.switchImages(selected); 45817 this.resize(); 45818 this.left = x - this.width / 2; 45819 this.top = y - this.height / 2; 45820 45821 // draw the background circle. IMPORTANT: the stroke in this method is used by the clip method below. 45822 this._drawRawCircle(ctx, x, y, values); 45823 45824 // now we draw in the circle, we save so we can revert the clip operation after drawing. 45825 ctx.save(); 45826 // clip is used to use the stroke in drawRawCircle as an area that we can draw in. 45827 ctx.clip(); 45828 // draw the image 45829 this._drawImageAtPosition(ctx, values); 45830 // restore so we can again draw on the full canvas
45831 ctx.restore(); 45832 45833 this._drawImageLabel(ctx, x, y, selected, hover); 45834 45835 this.updateBoundingBox(x, y); 45836 } 45837 45838 // TODO: compare with Circle.updateBoundingBox(), consolidate? More stuff is happening here 45839 /** 45840 * 45841 * @param {number} x width 45842 * @param {number} y height 45843 */ 45844 45845 }, { 45846 key: 'updateBoundingBox', 45847 value: function updateBoundingBox(x, y) { 45848 this.boundingBox.top = y - this.options.size; 45849 this.boundingBox.left = x - this.options.size; 45850 this.boundingBox.right = x + this.options.size; 45851 this.boundingBox.bottom = y + this.options.size; 45852 45853 // TODO: compare with Image.updateBoundingBox(), consolidate? 45854 this.boundingBox.left = Math.min(this.boundingBox.left, this.labelModule.size.left); 45855 this.boundingBox.right = Math.max(this.boundingBox.right, this.labelModule.size.left + this.labelModule.size.width); 45856 this.boundingBox.bottom = Math.max(this.boundingBox.bottom, this.boundingBox.bottom + this.labelOffset); 45857 } 45858 45859 /** 45860 * 45861 * @param {CanvasRenderingContext2D} ctx 45862 * @param {number} angle - Unused 45863 * @returns {number} 45864 */ 45865 45866 }, { 45867 key: 'distanceToBorder', 45868 value: function distanceToBorder(ctx, angle) { 45869 // eslint-disable-line no-unused-vars 45870 this.resize(ctx); 45871 return this.width * 0.5; 45872 } 45873 }]); 45874 return CircularImage; 45875}
45875(_CircleImageBase3['default']); 45876 45877exports['default'] = CircularImage; 45878 45879/***/ }), 45880/* 202 */ 45881/***/ (function(module, exports, __webpack_require__) { 45882 45883"use strict"; 45884 45885 45886Object.defineProperty(exports, "__esModule", { 45887 value: true 45888}); 45889 45890var _getPrototypeOf = __webpack_require__(3); 45891 45892var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf); 45893 45894var _classCallCheck2 = __webpack_require__(0); 45895 45896var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); 45897 45898var _createClass2 = __webpack_require__(1); 45899 45900var _createClass3 = _interopRequireDefault(_createClass2); 45901 45902var _possibleConstructorReturn2 = __webpack_require__(4); 45903 45904var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2); 45905 45906var _inherits2 = __webpack_require__(5); 45907 45908var _inherits3 = _interopRequireDefault(_inherits2); 45909 45910var _NodeBase2 = __webpack_require__(23); 45911 45912var _NodeBase3 = _interopRequireDefault(_NodeBase2); 45913 45914function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 45915 45916/** 45917 * A Database Node/Cluster shape. 45918 * 45919 * @extends NodeBase 45920 */ 45921var Database = function (_NodeBase) { 45922 (0, _inherits3['default'])(Database, _NodeBase); 45923 45924 /** 45925 * @param {Object} options 45926 * @param {Object} body 45927 * @param {Label} labelModule 45928 */ 45929 function Database(options, body, labelModule) { 45930 (0, _classCallCheck3['default'])(this, Database); 45931 45932 var _this = (0, _possibleConstructorReturn3['default'])(this, (Database.__proto__ || (0, _getPrototypeOf2['default'])(Database)).call(this, options, body, labelModule)); 45933 45934 _this._setMargins(labelModule); 45935 return _this; 45936 } 45937 45938 /** 45939 * 45940 * @param {CanvasRenderingContext2D} ctx 45941 * @param {boolean} selected 45942 * @param {boolean} hover 45943 */ 45944 45945 45946 (0, _createClass3['default'])(Database, [{ 45947 key: 'resize', 45948 value: function resize(ctx, selected, hover) { 45949 if (this.needsRefresh(selected, hover)) { 45950 var dimensions = this.getDimensionsFromLabel(ctx, selected, hover); 45951 var size = dimensions.width + this.margin.right + this.margin.left; 45952 45953 this.width = size; 45954 this.height = size; 45955 this.radius = this.width / 2; 45956 } 45957 } 45958 45959 /** 45960 * 45961 * @param {CanvasRenderingContext2D} ctx 45962 * @param {number} x width 45963 * @param {number} y height 45964 * @param {boolean} selected 45965 * @param {boolean} hover 45966 * @param {{toArrow: boolean, toArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), toArrowType: *, middleArrow: boolean, middleArrowScale: (number|allOptions.edges.arrows.middle.scaleFactor|{number}|Array), middleArrowType: (allOptions.edges.arrows.middle.type|{string}|string|*), fromArrow: boolean, fromArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), fromArrowType: *, arrowStrikethrough: (*|boolean|allOptions.edges.arrowStrikethrough|{boolean}), color: undefined, inheritsColor: (string|string|string|allOptions.edges.color.inherit|{string, boolean}|Array|*), opacity: *, hidden: *, length: *, shadow: *, shadowColor: *, shadowSize: *, shadowX: *, shadowY: *, dashes: (*|boolean|Array|allOptions.edges.dashes|{boolean, array}), width: *}} values 45967 */ 45968 45969 }, { 45970 key: 'draw', 45971 value: function draw(ctx, x, y, selected, hover, values) { 45972 this.resize(ctx, selected, hover); 45973 this.left = x - this.width / 2; 45974 this.top = y - this.height / 2; 45975 45976 this.initContextForDraw(ctx, values); 45977 ctx.database(x - this.width / 2, y - this.height / 2, this.width, this.height); 45978 this.performFill(ctx, values); 45979 45980 this.updateBoundingBox(x, y, ctx, selected, hover); 45981 this.labelModule.draw(ctx, this.left + this.textSize.width / 2 + this.margin.left, this.top + this.textSize.height / 2 + this.margin.top, selected, hover); 45982 } 45983 /** 45984 * 45985 * @param {CanvasRenderingContext2D} ctx 45986 * @param {number} angle 45987 * @returns {number} 45988 */ 45989 45990 }, { 45991 key: 'distanceToBorder', 45992 value: function distanceToBorder(ctx, angle) { 45993 return this._distanceToBorder(ctx, angle); 45994 } 45995 }]); 45996 return Database; 45997}(_NodeBase3['default']); 45998 45999exports['default'] = Database; 46000 46001/***/ }), 46002/* 203 */ 46003/***/ (function(module, exports, __webpack_require__) { 46004 46005"use strict"; 46006 46007 46008Object.defineProperty(exports, "__esModule", { 46009 value: true 46010}); 46011 46012var _getPrototypeOf = __webpack_require__(3); 46013 46014var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf); 46015 46016var _classCallCheck2 = __webpack_require__(0); 46017 46018var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); 46019 46020var _createClass2 = __webpack_require__(1); 46021 46022var _createClass3 = _interopRequireDefault(_createClass2); 46023 46024var _possibleConstructorReturn2 = __webpack_require__(4); 46025 46026var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2); 46027 46028var _inherits2 = __webpack_require__(5); 46029 46030var _inherits3 = _interopRequireDefault(_inherits2); 46031 46032var _ShapeBase2 = __webpack_require__(24); 46033 46034var _ShapeBase3 = _interopRequireDefault(_ShapeBase2); 46035 46036function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 46037 46038/** 46039 * A Diamond Node/Cluster shape. 46040 * 46041 * @extends ShapeBase 46042 */
46043var Diamond = function (_ShapeBase) { 46044 (0, _inherits3['default'])(Diamond, _ShapeBase); 46045 46046 /** 46047 * @param {Object} options 46048 * @param {Object} body 46049 * @param {Label} labelModule 46050 */ 46051 function Diamond(options, body, labelModule) { 46052 (0, _classCallCheck3['default'])(this, Diamond); 46053 return (0, _possibleConstructorReturn3['default'])(this, (Diamond.__proto__ || (0, _getPrototypeOf2['default'])(Diamond)).call(this, options, body, labelModule)); 46054 } 46055 46056 /** 46057 * 46058 * @param {CanvasRenderingContext2D} ctx 46059 * @param {number} x width 46060 * @param {number} y height 46061 * @param {boolean} selected 46062 * @param {boolean} hover 46063 * @param {{toArrow: boolean, toArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), toArrowType: *, middleArrow: boolean, middleArrowScale: (number|allOptions.edges.arrows.middle.scaleFactor|{number}|Array), middleArrowType: (allOptions.edges.arrows.middle.type|{string}|string|*), fromArrow: boolean, fromArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), fromArrowType: *, arrowStrikethrough: (*|boolean|allOptions.edges.arrowStrikethrough|{boolean}), color: undefined, inheritsColor: (string|string|string|allOptions.edges.color.inherit|{string, boolean}|Array|*), opacity: *, hidden: *, length: *, shadow: *, shadowColor: *, shadowSize: *, shadowX: *, shadowY: *, dashes: (*|boolean|Array|allOptions.edges.dashes|{boolean, array}), width: *}} values 46064 */ 46065 46066 46067 (0, _createClass3['default'])(Diamond, [{ 46068 key: 'draw', 46069 value: function draw(ctx, x, y, selected, hover, values) { 46070 this._drawShape(ctx, 'diamond', 4, x, y, selected, hover, values); 46071 } 46072 46073 /** 46074 * 46075 * @param {CanvasRenderingContext2D} ctx 46076 * @param {number} angle 46077 * @returns {number} 46078 */ 46079 46080 }, { 46081 key: 'distanceToBorder', 46082 value: function distanceToBorder(ctx, angle) { 46083 return this._distanceToBorder(ctx, angle); 46084 } 46085 }]); 46086 return Diamond; 46087}(_ShapeBase3['default']); 46088 46089exports['default'] = Diamond; 46090 46091/***/ }), 46092/* 204 */ 46093/***/ (function(module, exports, __webpack_require__) { 46094 46095"use strict"; 46096 46097 46098Object.defineProperty(exports, "__esModule", { 46099 value: true 46100}); 46101 46102var _getPrototypeOf = __webpack_require__(3); 46103 46104var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf); 46105 46106var _classCallCheck2 = __webpack_require__(0); 46107 46108var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); 46109 46110var _createClass2 = __webpack_require__(1); 46111 46112var _createClass3 = _interopRequireDefault(_createClass2); 46113 46114var _possibleConstructorReturn2 = __webpack_require__(4); 46115 46116var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2); 46117 46118var _inherits2 = __webpack_require__(5); 46119 46120var _inherits3 = _interopRequireDefault(_inherits2); 46121 46122var _ShapeBase2 = __webpack_require__(24); 46123 46124var _ShapeBase3 = _interopRequireDefault(_ShapeBase2); 46125 46126function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 46127 46128/** 46129 * A Dot Node/Cluster shape. 46130 * 46131 * @extends ShapeBase 46132 */ 46133var Dot = function (_ShapeBase) { 46134 (0, _inherits3['default'])(Dot, _ShapeBase); 46135 46136 /** 46137 * @param {Object} options 46138 * @param {Object} body 46139 * @param {Label} labelModule 46140 */ 46141 function Dot(options, body, labelModule) { 46142 (0, _classCallCheck3['default'])(this, Dot); 46143 return (0, _possibleConstructorReturn3['default'])(this, (Dot.__proto__ || (0, _getPrototypeOf2['default'])(Dot)).call(this, options, body, labelModule)); 46144 } 46145 46146 /** 46147 * 46148 * @param {CanvasRenderingContext2D} ctx 46149 * @param {number} x width 46150 * @param {number} y height 46151 * @param {boolean} selected 46152 * @param {boolean} hover 46153 * @param {{toArrow: boolean, toArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), toArrowType: *, middleArrow: boolean, middleArrowScale: (number|allOptions.edges.arrows.middle.scaleFactor|{number}|Array), middleArrowType: (allOptions.edges.arrows.middle.type|{string}|string|*), fromArrow: boolean, fromArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), fromArrowType: *, arrowStrikethrough: (*|boolean|allOptions.edges.arrowStrikethrough|{boolean}), color: undefined, inheritsColor: (string|string|string|allOptions.edges.color.inherit|{string, boolean}|Array|*), opacity: *, hidden: *, length: *, shadow: *, shadowColor: *, shadowSize: *, shadowX: *, shadowY: *, dashes: (*|boolean|Array|allOptions.edges.dashes|{boolean, array}), width: *}} values 46154 */ 46155 46156 46157 (0, _createClass3['default'])(Dot, [{ 46158 key: 'draw', 46159 value: function draw(ctx, x, y, selected, hover, values) {
46160 this._drawShape(ctx, 'circle', 2, x, y, selected, hover, values); 46161 } 46162 46163 /** 46164 * 46165 * @param {CanvasRenderingContext2D} ctx 46166 * @param {number} angle 46167 * @returns {number} 46168 */ 46169 46170 }, { 46171 key: 'distanceToBorder', 46172 value: function distanceToBorder(ctx, angle) { 46173 // eslint-disable-line no-unused-vars 46174 this.resize(ctx); 46175 return this.options.size; 46176 } 46177 }]); 46178 return Dot; 46179}(_ShapeBase3['default']); 46180 46181exports['default'] = Dot; 46182 46183/***/ }), 46184/* 205 */ 46185/***/ (function(module, exports, __webpack_require__) { 46186 46187"use strict"; 46188 46189 46190Object.defineProperty(exports, "__esModule", { 46191 value: true 46192}); 46193 46194var _getPrototypeOf = __webpack_require__(3); 46195 46196var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf); 46197 46198var _classCallCheck2 = __webpack_require__(0); 46199 46200var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); 46201 46202var _createClass2 = __webpack_require__(1); 46203 46204var _createClass3 = _interopRequireDefault(_createClass2); 46205 46206var _possibleConstructorReturn2 = __webpack_require__(4); 46207 46208var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2); 46209 46210var _inherits2 = __webpack_require__(5); 46211 46212var _inherits3 = _interopRequireDefault(_inherits2); 46213 46214var _NodeBase2 = __webpack_require__(23); 46215 46216var _NodeBase3 = _interopRequireDefault(_NodeBase2); 46217 46218function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 46219 46220/** 46221 * Am Ellipse Node/Cluster shape. 46222 * 46223 * @extends NodeBase 46224 */ 46225var Ellipse = function (_NodeBase) { 46226 (0, _inherits3['default'])(Ellipse, _NodeBase); 46227 46228 /** 46229 * @param {Object} options 46230 * @param {Object} body 46231 * @param {Label} labelModule 46232 */ 46233 function Ellipse(options, body, labelModule) { 46234 (0, _classCallCheck3['default'])(this, Ellipse); 46235 return (0, _possibleConstructorReturn3['default'])(this, (Ellipse.__proto__ || (0, _getPrototypeOf2['default'])(Ellipse)).call(this, options, body, labelModule)); 46236 } 46237 46238 /** 46239 * 46240 * @param {CanvasRenderingContext2D} ctx 46241 * @param {boolean} [selected] 46242 * @param {boolean} [hover] 46243 */ 46244 46245 46246 (0, _createClass3['default'])(Ellipse, [{ 46247 key: 'resize', 46248 value: function resize(ctx) { 46249 var selected = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : this.selected; 46250 var hover = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : this.hover; 46251 46252 if (this.needsRefresh(selected, hover)) { 46253 var dimensions = this.getDimensionsFromLabel(ctx, selected, hover); 46254 46255 this.height = dimensions.height * 2; 46256 this.width = dimensions.width + dimensions.height; 46257 this.radius = 0.5 * this.width; 46258 } 46259 } 46260 46261 /** 46262 * 46263 * @param {CanvasRenderingContext2D} ctx 46264 * @param {number} x width 46265 * @param {number} y height 46266 * @param {boolean} selected 46267 * @param {boolean} hover 46268 * @param {{toArrow: boolean, toArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), toArrowType: *, middleArrow: boolean, middleArrowScale: (number|allOptions.edges.arrows.middle.scaleFactor|{number}|Array), middleArrowType: (allOptions.edges.arrows.middle.type|{string}|string|*), fromArrow: boolean, fromArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), fromArrowType: *, arrowStrikethrough: (*|boolean|allOptions.edges.arrowStrikethrough|{boolean}), color: undefined, inheritsColor: (string|string|string|allOptions.edges.color.inherit|{string, boolean}|Array|*), opacity: *, hidden: *, length: *, shadow: *, shadowColor: *, shadowSize: *, shadowX: *, shadowY: *, dashes: (*|boolean|Array|allOptions.edges.dashes|{boolean, array}), width: *}} values 46269 */ 46270 46271 }, { 46272 key: 'draw', 46273 value: function draw(ctx, x, y, selected, hover, values) { 46274 this.resize(ctx, selected, hover); 46275 this.left = x - this.width * 0.5; 46276 this.top = y - this.height * 0.5; 46277 46278 this.initContextForDraw(ctx, values); 46279 ctx.ellipse_vis(this.left, this.top, this.width, this.height); 46280 this.performFill(ctx, values); 46281 46282 this.updateBoundingBox(x, y, ctx, selected, hover); 46283 this.labelModule.draw(ctx, x, y, selected, hover); 46284 } 46285 46286 /** 46287 * 46288 * @param {CanvasRenderingContext2D} ctx 46289 * @param {number} angle 46290 * @returns {number} 46291 */ 46292 46293 }, { 46294 key: 'distanceToBorder', 46295 value: function distanceToBorder(ctx, angle) { 46296 this.resize(ctx); 46297 var a = this.width * 0.5; 46298 var b = this.height * 0.5; 46299 var w = Math.sin(angle) * a; 46300 var h = Math.cos(angle) * b; 46301 return a * b / Math.sqrt(w * w + h * h); 46302 } 46303 }]); 46304 return Ellipse; 46305}(_NodeBase3['default']); 46306 46307exports['default'] = Ellipse; 46308 46309/***/ }), 46310/* 206 */ 46311/***/ (function(module, exports, __webpack_require__) { 46312 46313"use strict"; 46314 46315 46316Object.defineProperty(exports, "__esModule", { 46317 value: true 46318}); 46319 46320var _getPrototypeOf = __webpack_require__(3); 46321 46322var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf); 46323 46324var _classCallCheck2 = __webpack_require__(0); 46325 46326var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); 46327 46328var _createClass2 = __webpack_require__(1); 46329 46330var _createClass3 = _interopRequireDefault(_createClass2); 46331 46332var _possibleConstructorReturn2 = __webpack_require__(4); 46333 46334var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2); 46335 46336var _inherits2 = __webpack_require__(5); 46337 46338var _inherits3 = _interopRequireDefault(_inherits2); 46339 46340var _NodeBase2 = __webpack_require__(23); 46341 46342var _NodeBase3 = _interopRequireDefault(_NodeBase2); 46343 46344function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 46345 46346/** 46347 * An icon replacement for the default Node shape. 46348 * 46349 * @extends NodeBase 46350 */ 46351var Icon = function (_NodeBase) { 46352 (0, _inherits3['default'])(Icon, _NodeBase); 46353 46354 /** 46355 * @param {Object} options 46356 * @param {Object} body 46357 * @param {Label} labelModule 46358 */ 46359 function Icon(options, body, labelModule) { 46360 (0, _classCallCheck3['default'])(this, Icon); 46361 46362 var _this = (0, _possibleConstructorReturn3['default'])(this, (Icon.__proto__ || (0, _getPrototypeOf2['default'])(Icon)).call(this, options, body, labelModule)); 46363 46364 _this._setMargins(labelModule); 46365 return _this; 46366 } 46367 46368 /** 46369 * 46370 * @param {CanvasRenderingContext2D} ctx - Unused. 46371 * @param {boolean} [selected] 46372 * @param {boolean} [hover] 46373 */ 46374 46375 46376 (0, _createClass3['default'])(Icon, [{ 46377 key: 'resize', 46378 value: function resize(ctx, selected, hover) { 46379 if (this.needsRefresh(selected, hover)) { 46380 this.iconSize = { 46381 width: Number(this.options.icon.size), 46382 height: Number(this.options.icon.size) 46383 }; 46384 this.width = this.iconSize.width + this.margin.right + this.margin.left; 46385 this.height = this.iconSize.height + this.margin.top + this.margin.bottom; 46386 this.radius = 0.5 * this.width; 46387 } 46388 } 46389 46390 /** 46391 * 46392 * @param {CanvasRenderingContext2D} ctx 46393 * @param {number} x width 46394 * @param {number} y height 46395 * @param {boolean} selected 46396 * @param {boolean} hover 46397 * @param {{toArrow: boolean, toArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), toArrowType: *, middleArrow: boolean, middleArrowScale: (number|allOptions.edges.arrows.middle.scaleFactor|{number}|Array), middleArrowType: (allOptions.edges.arrows.middle.type|{string}|string|*), fromArrow: boolean, fromArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), fromArrowType: *, arrowStrikethrough: (*|boolean|allOptions.edges.arrowStrikethrough|{boolean}), color: undefined, inheritsColor: (string|string|string|allOptions.edges.color.inherit|{string, boolean}|Array|*), opacity: *, hidden: *, length: *, shadow: *, shadowColor: *, shadowSize: *, shadowX: *, shadowY: *, dashes: (*|boolean|Array|allOptions.edges.dashes|{boolean, array}), width: *}} values 46398 */ 46399 46400 }, { 46401 key: 'draw', 46402 value: function draw(ctx, x, y, selected, hover, values) { 46403 this.resize(ctx, selected, hover); 46404 this.options.icon.size = this.options.icon.size || 50; 46405 46406 this.left = x - this.width / 2; 46407 this.top = y - this.height / 2; 46408 this._icon(ctx, x, y, selected, hover, values); 46409 46410 if (this.options.label !== undefined) { 46411 var iconTextSpacing = 5; 46412 this.labelModule.draw(ctx, this.left + this.iconSize.width / 2 + this.margin.left, y + this.height / 2 + iconTextSpacing, selected); 46413 } 46414 46415 this.updateBoundingBox(x, y); 46416 } 46417 46418 /** 46419 * 46420 * @param {number} x 46421 * @param {number} y 46422 */ 46423 46424 }, { 46425 key: 'updateBoundingBox', 46426 value: function updateBoundingBox(x, y) { 46427 this.boundingBox.top = y - this.options.icon.size * 0.5; 46428 this.boundingBox.left = x - this.options.icon.size * 0.5; 46429 this.boundingBox.right = x + this.options.icon.size * 0.5; 46430 this.boundingBox.bottom = y + this.options.icon.size * 0.5; 46431 46432 if (this.options.label !== undefined && this.labelModule.size.width > 0) { 46433 var iconTextSpacing = 5; 46434 this.boundingBox.left = Math.min(this.boundingBox.left, this.labelModule.size.left); 46435 this.boundingBox.right = Math.max(this.boundingBox.right, this.labelModule.size.left + this.labelModule.size.width); 46436 this.boundingBox.bottom = Math.max(this.boundingBox.bottom, this.boundingBox.bottom + this.labelModule.size.height + iconTextSpacing); 46437 } 46438 } 46439 46440 /** 46441 * 46442 * @param {CanvasRenderingContext2D} ctx 46443 * @param {number} x width 46444 * @param {number} y height 46445 * @param {boolean} selected 46446 * @param {boolean} hover - Unused 46447 * @param {{toArrow: boolean, toArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), toArrowType: *, middleArrow: boolean, middleArrowScale: (number|allOptions.edges.arrows.middle.scaleFactor|{number}|Array), middleArrowType: (allOptions.edges.arrows.middle.type|{string}|string|*), fromArrow: boolean, fromArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), fromArrowType: *, arrowStrikethrough: (*|boolean|allOptions.edges.arrowStrikethrough|{boolean}), color: undefined, inheritsColor: (string|string|string|allOptions.edges.color.inherit|{string, boolean}|Array|*), opacity: *, hidden: *, length: *, shadow: *, shadowColor: *, shadowSize: *, shadowX: *, shadowY: *, dashes: (*|boolean|Array|allOptions.edges.dashes|{boolean, array}), width: *}} values 46448 */ 46449 46450 }, { 46451 key: '_icon', 46452 value: function _icon(ctx, x, y, selected, hover, values) { 46453 var iconSize = Number(this.options.icon.size); 46454 46455 if (this.options.icon.code !== undefined) { 46456 ctx.font = (selected ? "bold " : "") + iconSize + "px " + this.options.icon.face; 46457 46458 // draw icon 46459 ctx.fillStyle = this.options.icon.color || "black"; 46460 ctx.textAlign = "center"; 46461 ctx.textBaseline = "middle"; 46462 46463 // draw shadow if enabled 46464 this.enableShadow(ctx, values); 46465 ctx.fillText(this.options.icon.code, x, y); 46466 46467 // disable shadows for other elements. 46468 this.disableShadow(ctx, values); 46469 } else { 46470 console.error('When using the icon shape, you need to define the code in the icon options object. This can be done per node or globally.'); 46471 } 46472 } 46473 46474 /** 46475 * 46476 * @param {CanvasRenderingContext2D} ctx 46477 * @param {number} angle 46478 * @returns {number} 46479 */ 46480 46481 }, { 46482 key: 'distanceToBorder', 46483 value: function distanceToBorder(ctx, angle) { 46484 return this._distanceToBorder(ctx, angle); 46485 } 46486 }]); 46487 return Icon; 46488}(_NodeBase3['default']); 46489 46490exports['default'] = Icon; 46491 46492/***/ }), 46493/* 207 */ 46494/***/ (function(module, exports, __webpack_require__) { 46495 46496"use strict"; 46497 46498 46499Object.defineProperty(exports, "__esModule", { 46500 value: true 46501}); 46502 46503var _getPrototypeOf = __webpack_require__(3); 46504 46505var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf); 46506 46507var _classCallCheck2 = __webpack_require__(0); 46508 46509var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); 46510 46511var _createClass2 = __webpack_require__(1); 46512 46513var _createClass3 = _interopRequireDefault(_createClass2); 46514 46515var _possibleConstructorReturn2 = __webpack_require__(4); 46516 46517var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2); 46518 46519var _inherits2 = __webpack_require__(5); 46520 46521var _inherits3 = _interopRequireDefault(_inherits2); 46522 46523var _CircleImageBase2 = __webpack_require__(73); 46524 46525var _CircleImageBase3 = _interopRequireDefault(_CircleImageBase2); 46526 46527function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 46528 46529/** 46530 * An image-based replacement for the default Node shape. 46531 * 46532 * @extends CircleImageBase 46533 */ 46534var Image = function (_CircleImageBase) { 46535 (0, _inherits3['default'])(Image, _CircleImageBase); 46536 46537 /** 46538 * @param {Object} options 46539 * @param {Object} body 46540 * @param {Label} labelModule 46541 * @param {Image} imageObj 46542 * @param {Image} imageObjAlt 46543 */ 46544 function Image(options, body, labelModule, imageObj, imageObjAlt) { 46545 (0, _classCallCheck3['default'])(this, Image); 46546 46547 var _this = (0, _possibleConstructorReturn3['default'])(this, (Image.__proto__ || (0, _getPrototypeOf2['default'])(Image)).call(this, options, body, labelModule)); 46548 46549 _this.setImages(imageObj, imageObjAlt); 46550 return _this; 46551 } 46552 46553 /** 46554 * 46555 * @param {CanvasRenderingContext2D} ctx - Unused. 46556 * @param {boolean} [selected] 46557 * @param {boolean} [hover] 46558 */ 46559 46560 46561 (0, _createClass3['default'])(Image, [{ 46562 key: 'resize', 46563 value: function resize(ctx) { 46564 var selected = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : this.selected; 46565 var hover = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : this.hover; 46566 46567 var imageAbsent = this.imageObj.src === undefined || this.imageObj.width === undefined || this.imageObj.height === undefined; 46568 46569 if (imageAbsent) { 46570 var side = this.options.size * 2; 46571 this.width = side; 46572 this.height = side; 46573 return; 46574 } 46575 46576 if (this.needsRefresh(selected, hover)) { 46577 this._resizeImage(); 46578 } 46579 } 46580 46581 /** 46582 * 46583 * @param {CanvasRenderingContext2D} ctx 46584 * @param {number} x width 46585 * @param {number} y height 46586 * @param {boolean} selected 46587 * @param {boolean} hover 46588 * @param {{toArrow: boolean, toArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), toArrowType: *, middleArrow: boolean, middleArrowScale: (number|allOptions.edges.arrows.middle.scaleFactor|{number}|Array), middleArrowType: (allOptions.edges.arrows.middle.type|{string}|string|*), fromArrow: boolean, fromArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), fromArrowType: *, arrowStrikethrough: (*|boolean|allOptions.edges.arrowStrikethrough|{boolean}), color: undefined, inheritsColor: (string|string|string|allOptions.edges.color.inherit|{string, boolean}|Array|*), opacity: *, hidden: *, length: *, shadow: *, shadowColor: *, shadowSize: *, shadowX: *, shadowY: *, dashes: (*|boolean|Array|allOptions.edges.dashes|{boolean, array}), width: *}} values 46589 */ 46590 46591 }, { 46592 key: 'draw', 46593 value: function draw(ctx, x, y, selected, hover, values) { 46594 this.switchImages(selected); 46595 this.resize(); 46596 this.left = x - this.width / 2; 46597 this.top = y - this.height / 2; 46598 46599 if (this.options.shapeProperties.useBorderWithImage === true) { 46600 var neutralborderWidth = this.options.borderWidth; 46601 var selectionLineWidth = this.options.borderWidthSelected || 2 * this.options.borderWidth; 46602 var borderWidth = (selected ? selectionLineWidth : neutralborderWidth) / this.body.view.scale; 46603 ctx.lineWidth = Math.min(this.width, borderWidth); 46604 46605 ctx.beginPath(); 46606 46607 // setup the line properties. 46608 ctx.strokeStyle = selected ? this.options.color.highlight.border : hover ? this.option
46608s.color.hover.border : this.options.color.border; 46609 46610 // set a fillstyle 46611 ctx.fillStyle = selected ? this.options.color.highlight.background : hover ? this.options.color.hover.background : this.options.color.background; 46612 46613 // draw a rectangle to form the border around. This rectangle is filled so the opacity of a picture (in future vis releases?) can be used to tint the image 46614 ctx.rect(this.left - 0.5 * ctx.lineWidth, this.top - 0.5 * ctx.lineWidth, this.width + ctx.lineWidth, this.height + ctx.lineWidth); 46615 ctx.fill(); 46616 46617 this.performStroke(ctx, values); 46618 46619 ctx.closePath(); 46620 } 46621 46622 this._drawImageAtPosition(ctx, values); 46623 46624 this._drawImageLabel(ctx, x, y, selected, hover); 46625 46626 this.updateBoundingBox(x, y); 46627 } 46628 46629 /** 46630 * 46631 * @param {number} x 46632 * @param {number} y 46633 */ 46634 46635 }, { 46636 key: 'updateBoundingBox', 46637 value: function updateBoundingBox(x, y) { 46638 this.resize(); 46639 this._updateBoundingBox(x, y); 46640 46641 if (this.options.label !== undefined && this.labelModule.size.width > 0) { 46642 this.boundingBox.left = Math.min(this.boundingBox.left, this.labelModule.size.left); 46643 this.boundingBox.right = Math.max(this.boundingBox.right, this.labelModule.size.left + this.labelModule.size.width); 46644 this.boundingBox.bottom = Math.max(this.boundingBox.bottom, this.boundingBox.bottom + this.labelOffset); 46645 } 46646 } 46647 46648 /** 46649 * 46650 * @param {CanvasRenderingContext2D} ctx 46651 * @param {number} angle 46652 * @returns {number} 46653 */ 46654 46655 }, { 46656 key: 'distanceToBorder', 46657 value: function distanceToBorder(ctx, angle) { 46658 return this._distanceToBorder(ctx, angle); 46659 } 46660 }]); 46661 return Image; 46662}
46662(_CircleImageBase3['default']); 46663 46664exports['default'] = Image; 46665 46666/***/ }), 46667/* 208 */ 46668/***/ (function(module, exports, __webpack_require__) { 46669 46670"use strict"; 46671 46672 46673Object.defineProperty(exports, "__esModule", { 46674 value: true 46675}); 46676 46677var _getPrototypeOf = __webpack_require__(3); 46678 46679var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf); 46680 46681var _classCallCheck2 = __webpack_require__(0); 46682 46683var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); 46684 46685var _createClass2 = __webpack_require__(1); 46686 46687var _createClass3 = _interopRequireDefault(_createClass2); 46688 46689var _possibleConstructorReturn2 = __webpack_require__(4); 46690 46691var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2); 46692 46693var _inherits2 = __webpack_require__(5); 46694 46695var _inherits3 = _interopRequireDefault(_inherits2); 46696 46697var _ShapeBase2 = __webpack_require__(24); 46698 46699var _ShapeBase3 = _interopRequireDefault(_ShapeBase2); 46700 46701function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 46702 46703/** 46704 * A Square Node/Cluster shape. 46705 * 46706 * @extends ShapeBase 46707 */ 46708var Square = function (_ShapeBase) { 46709 (0, _inherits3['default'])(Square, _ShapeBase); 46710 46711 /** 46712 * @param {Object} options 46713 * @param {Object} body 46714 * @param {Label} labelModule 46715 */ 46716 function Square(options, body, labelModule) { 46717 (0, _classCallCheck3['default'])(this, Square); 46718 return (0, _possibleConstructorReturn3['default'])(this, (Square.__proto__ || (0, _getPrototypeOf2['default'])(Square)).call(this, options, body, labelModule)); 46719 } 46720 46721 /** 46722 * 46723 * @param {CanvasRenderingContext2D} ctx 46724 * @param {number} x width 46725 * @param {number} y height 46726 * @param {boolean} selected 46727 * @param {boolean} hover 46728 * @param {{toArrow: boolean, toArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), toArrowType: *, middleArrow: boolean, middleArrowScale: (number|allOptions.edges.arrows.middle.scaleFactor|{number}|Array), middleArrowType: (allOptions.edges.arrows.middle.type|{string}|string|*), fromArrow: boolean, fromArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), fromArrowType: *, arrowStrikethrough: (*|boolean|allOptions.edges.arrowStrikethrough|{boolean}), color: undefined, inheritsColor: (string|string|string|allOptions.edges.color.inherit|{string, boolean}|Array|*), opacity: *, hidden: *, length: *, shadow: *, shadowColor: *, shadowSize: *, shadowX: *, shadowY: *, dashes: (*|boolean|Array|allOptions.edges.dashes|{boolean, array}), width: *}} values 46729 */ 46730 46731 46732 (0, _createClass3['default'])(Square, [{ 46733 key: 'draw', 46734 value: function draw(ctx, x, y, selected, hover, values) { 46735 this._drawShape(ctx, 'square', 2, x, y, selected, hover, values); 46736 } 46737 46738 /** 46739 * 46740 * @param {CanvasRenderingContext2D} ctx 46741 * @param {number} angle 46742 * @returns {number} 46743 */ 46744 46745 }, { 46746 key: 'distanceToBorder', 46747 value: function distanceToBorder(ctx, angle) { 46748 return this._distanceToBorder(ctx, angle); 46749 } 46750 }]); 46751 return Square; 46752}(_ShapeBase3['default']); 46753 46754exports['default'] = Square; 46755 46756/***/ }), 46757/* 209 */ 46758/***/ (function(module, exports, __webpack_require__) { 46759 46760"use strict"; 46761 46762 46763Object.defineProperty(exports, "__esModule", { 46764 value: true 46765}); 46766 46767var _getPrototypeOf = __webpack_require__(3); 46768 46769var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf); 46770 46771var _classCallCheck2 = __webpack_require__(0); 46772 46773var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); 46774 46775var _createClass2 = __webpack_require__(1); 46776 46777var _createClass3 = _interopRequireDefault(_createClass2); 46778 46779var _possibleConstructorReturn2 = __webpack_require__(4); 46780 46781var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2); 46782 46783var _inherits2 = __webpack_require__(5); 46784 46785var _inherits3 = _interopRequireDefault(_inherits2); 46786 46787var _ShapeBase2 = __webpack_require__(24); 46788 46789var _ShapeBase3 = _interopRequireDefault(_ShapeBase2); 46790 46791function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 46792 46793/** 46794 * A Hexagon Node/Cluster shape. 46795 * 46796 * @extends ShapeBase 46797 */
46798var Hexagon = function (_ShapeBase) { 46799 (0, _inherits3['default'])(Hexagon, _ShapeBase); 46800 46801 /** 46802 * @param {Object} options 46803 * @param {Object} body 46804 * @param {Label} labelModule 46805 */ 46806 function Hexagon(options, body, labelModule) { 46807 (0, _classCallCheck3['default'])(this, Hexagon); 46808 return (0, _possibleConstructorReturn3['default'])(this, (Hexagon.__proto__ || (0, _getPrototypeOf2['default'])(Hexagon)).call(this, options, body, labelModule)); 46809 } 46810 46811 /** 46812 * 46813 * @param {CanvasRenderingContext2D} ctx 46814 * @param {number} x width 46815 * @param {number} y height 46816 * @param {boolean} selected 46817 * @param {boolean} hover 46818 * @param {{toArrow: boolean, toArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), toArrowType: *, middleArrow: boolean, middleArrowScale: (number|allOptions.edges.arrows.middle.scaleFactor|{number}|Array), middleArrowType: (allOptions.edges.arrows.middle.type|{string}|string|*), fromArrow: boolean, fromArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), fromArrowType: *, arrowStrikethrough: (*|boolean|allOptions.edges.arrowStrikethrough|{boolean}), color: undefined, inheritsColor: (string|string|string|allOptions.edges.color.inherit|{string, boolean}|Array|*), opacity: *, hidden: *, length: *, shadow: *, shadowColor: *, shadowSize: *, shadowX: *, shadowY: *, dashes: (*|boolean|Array|allOptions.edges.dashes|{boolean, array}), width: *}} values 46819 */ 46820 46821 46822 (0, _createClass3['default'])(Hexagon, [{ 46823 key: 'draw', 46824 value: function draw(ctx, x, y, selected, hover, values) { 46825 this._drawShape(ctx, 'hexagon', 4, x, y, selected, hover, values); 46826 } 46827 46828 /** 46829 * 46830 * @param {CanvasRenderingContext2D} ctx 46831 * @param {number} angle 46832 * @returns {number} 46833 */ 46834 46835 }, { 46836 key: 'distanceToBorder', 46837 value: function distanceToBorder(ctx, angle) { 46838 return this._distanceToBorder(ctx, angle); 46839 } 46840 }]); 46841 return Hexagon; 46842}(_ShapeBase3['default']); 46843 46844exports['default'] = Hexagon; 46845 46846/***/ }), 46847/* 210 */ 46848/***/ (function(module, exports, __webpack_require__) { 46849 46850"use strict"; 46851 46852 46853Object.defineProperty(exports, "__esModule", { 46854 value: true 46855}); 46856 46857var _getPrototypeOf = __webpack_require__(3); 46858 46859var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf); 46860 46861var _classCallCheck2 = __webpack_require__(0); 46862 46863var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); 46864 46865var _createClass2 = __webpack_require__(1); 46866 46867var _createClass3 = _interopRequireDefault(_createClass2); 46868 46869var _possibleConstructorReturn2 = __webpack_require__(4); 46870 46871var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2); 46872 46873var _inherits2 = __webpack_require__(5); 46874 46875var _inherits3 = _interopRequireDefault(_inherits2); 46876 46877var _ShapeBase2 = __webpack_require__(24); 46878 46879var _ShapeBase3 = _interopRequireDefault(_ShapeBase2); 46880 46881function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 46882 46883/** 46884 * A Star Node/Cluster shape. 46885 * 46886 * @extends ShapeBase 46887 */ 46888var Star = function (_ShapeBase) { 46889 (0, _inherits3['default'])(Star, _ShapeBase); 46890 46891 /** 46892 * @param {Object} options 46893 * @param {Object} body 46894 * @param {Label} labelModule 46895 */ 46896 function Star(options, body, labelModule) { 46897 (0, _classCallCheck3['default'])(this, Star); 46898 return (0, _possibleConstructorReturn3['default'])(this, (Star.__proto__ || (0, _getPrototypeOf2['default'])(Star)).call(this, options, body, labelModule)); 46899 } 46900 46901 /** 46902 * 46903 * @param {CanvasRenderingContext2D} ctx 46904 * @param {number} x width 46905 * @param {number} y height 46906 * @param {boolean} selected 46907 * @param {boolean} hover 46908 * @param {{toArrow: boolean, toArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), toArrowType: *, middleArrow: boolean, middleArrowScale: (number|allOptions.edges.arrows.middle.scaleFactor|{number}|Array), middleArrowType: (allOptions.edges.arrows.middle.type|{string}|string|*), fromArrow: boolean, fromArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), fromArrowType: *, arrowStrikethrough: (*|boolean|allOptions.edges.arrowStrikethrough|{boolean}), color: undefined, inheritsColor: (string|string|string|allOptions.edges.color.inherit|{string, boolean}|Array|*), opacity: *, hidden: *, length: *, shadow: *, shadowColor: *, shadowSize: *, shadowX: *, shadowY: *, dashes: (*|boolean|Array|allOptions.edges.dashes|{boolean, array}), width: *}} values 46909 */ 46910 46911 46912 (0, _createClass3['default'])(Star, [{ 46913 key: 'draw', 46914 value: function draw(ctx, x, y, selected, hover, values) { 46915 this._drawShape(ctx, 'star', 4, x, y, selected, hover, values); 46916 } 46917 46918 /** 46919 * 46920 * @param {CanvasRenderingContext2D} ctx 46921 * @param {number} angle 46922 * @returns {number} 46923 */ 46924 46925 }, { 46926 key: 'distanceToBorder', 46927 value: function distanceToBorder(ctx, angle) { 46928 return this._distanceToBorder(ctx, angle); 46929 } 46930 }]); 46931 return Star; 46932}(_ShapeBase3['default']); 46933 46934exports['default'] = Star; 46935 46936/***/ }), 46937/* 211 */ 46938/***/ (function(module, exports, __webpack_require__) { 46939 46940"use strict"; 46941 46942 46943Object.defineProperty(exports, "__esModule", { 46944 value: true 46945}); 46946 46947var _getPrototypeOf = __webpack_require__(3); 46948 46949var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf); 46950 46951var _classCallCheck2 = __webpack_require__(0); 46952 46953var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); 46954 46955var _createClass2 = __webpack_require__(1); 46956 46957var _createClass3 = _interopRequireDefault(_createClass2); 46958 46959var _possibleConstructorReturn2 = __webpack_require__(4); 46960 46961var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2); 46962 46963var _inherits2 = __webpack_require__(5); 46964 46965var _inherits3 = _interopRequireDefault(_inherits2); 46966 46967var _NodeBase2 = __webpack_require__(23); 46968 46969var _NodeBase3 = _interopRequireDefault(_NodeBase2); 46970 46971function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 46972 46973/** 46974 * A text-based replacement for the default Node shape. 46975 * 46976 * @extends NodeBase 46977 */ 46978var Text = function (_NodeBase) { 46979 (0, _inherits3['default'])(Text, _NodeBase); 46980 46981 /** 46982 * @param {Object} options 46983 * @param {Object} body 46984 * @param {Label} labelModule 46985 */ 46986 function Text(options, body, labelModule) { 46987 (0, _classCallCheck3['default'])(this, Text); 46988 46989 var _this = (0, _possibleConstructorReturn3['default'])(this, (Text.__proto__ || (0, _getPrototypeOf2['default'])(Text)).call(this, options, body, labelModule)); 46990 46991 _this._setMargins(labelModule); 46992 return _this; 46993 } 46994 46995 /** 46996 * 46997 * @param {CanvasRenderingContext2D} ctx 46998 * @param {boolean} selected 46999 * @param {boolean} hover 47000 */ 47001 47002 47003 (0, _createClass3['default'])(Text, [{ 47004 key: 'resize', 47005 value: function resize(ctx, selected, hover) { 47006 if (this.needsRefresh(selected, hover)) { 47007 this.textSize = this.labelModule.getTextSize(ctx, selected, hover); 47008 this.width = this.textSize.width + this.margin.right + this.margin.left; 47009 this.height = this.textSize.height + this.margin.top + this.margin.bottom; 47010 this.radius = 0.5 * this.width; 47011 } 47012 } 47013 47014 /** 47015 * 47016 * @param {CanvasRenderingContext2D} ctx 47017 * @param {number} x width 47018 * @param {number} y height 47019 * @param {boolean} selected 47020 * @param {boolean} hover 47021 * @param {{toArrow: boolean, toArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), toArrowType: *, middleArrow: boolean, middleArrowScale: (number|allOptions.edges.arrows.middle.scaleFactor|{number}|Array), middleArrowType: (allOptions.edges.arrows.middle.type|{string}|string|*), fromArrow: boolean, fromArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), fromArrowType: *, arrowStrikethrough: (*|boolean|allOptions.edges.arrowStrikethrough|{boolean}), color: undefined, inheritsColor: (string|string|string|allOptions.edges.color.inherit|{string, boolean}|Array|*), opacity: *, hidden: *, length: *, shadow: *, shadowColor: *, shadowSize: *, shadowX: *, shadowY: *, dashes: (*|boolean|Array|allOptions.edges.dashes|{boolean, array}), width: *}} values 47022 */ 47023 47024 }, { 47025 key: 'draw', 47026 value: function draw(ctx, x, y, selected, hover, values) { 47027 this.resize(ctx, selected, hover); 47028 this.left = x - this.width / 2; 47029 this.top = y - this.height / 2; 47030 47031 // draw shadow if enabled 47032 this.enableShadow(ctx, values); 47033 this.labelModule.draw(ctx, this.left + this.textSize.width / 2 + this.margin.left, this.top + this.textSize.height / 2 + this.margin.top, selected, hover); 47034 47035 // disable shadows for other elements. 47036 this.disableShadow(ctx, values); 47037 47038 this.updateBoundingBox(x, y, ctx, selected, hover); 47039 } 47040 47041 /** 47042 * 47043 * @param {CanvasRenderingContext2D} ctx 47044 * @param {number} angle 47045 * @returns {number} 47046 */ 47047 47048 }, { 47049 key: 'distanceToBorder', 47050 value: function distanceToBorder(ctx, angle) { 47051 return this._distanceToBorder(ctx, angle); 47052 } 47053 }]); 47054 return Text; 47055}(_NodeBase3['default']); 47056 47057exports['default'] = Text; 47058 47059/***/ }), 47060/* 212 */ 47061/***/ (function(module, exports, __webpack_require__) { 47062 47063"use strict"; 47064 47065 47066Object.defineProperty(exports, "__esModule", { 47067 value: true 47068}); 47069 47070var _getPrototypeOf = __webpack_require__(3); 47071 47072var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf); 47073 47074var _classCallCheck2 = __webpack_require__(0); 47075 47076var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); 47077 47078var _createClass2 = __webpack_require__(1); 47079 47080var _createClass3 = _interopRequireDefault(_createClass2); 47081 47082var _possibleConstructorReturn2 = __webpack_require__(4); 47083 47084var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2); 47085 47086var _inherits2 = __webpack_require__(5); 47087 47088var _inherits3 = _interopRequireDefault(_inherits2); 47089 47090var _ShapeBase2 = __webpack_require__(24); 47091 47092var _ShapeBase3 = _interopRequireDefault(_ShapeBase2); 47093 47094function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 47095 47096/** 47097 * A Triangle Node/Cluster shape. 47098 * 47099 * @extends ShapeBase 47100 */
47101var Triangle = function (_ShapeBase) { 47102 (0, _inherits3['default'])(Triangle, _ShapeBase); 47103 47104 /** 47105 * @param {Object} options 47106 * @param {Object} body 47107 * @param {Label} labelModule 47108 */ 47109 function Triangle(options, body, labelModule) { 47110 (0, _classCallCheck3['default'])(this, Triangle); 47111 return (0, _possibleConstructorReturn3['default'])(this, (Triangle.__proto__ || (0, _getPrototypeOf2['default'])(Triangle)).call(this, options, body, labelModule)); 47112 } 47113 47114 /** 47115 * 47116 * @param {CanvasRenderingContext2D} ctx 47117 * @param {number} x 47118 * @param {number} y 47119 * @param {boolean} selected 47120 * @param {boolean} hover 47121 * @param {{toArrow: boolean, toArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), toArrowType: *, middleArrow: boolean, middleArrowScale: (number|allOptions.edges.arrows.middle.scaleFactor|{number}|Array), middleArrowType: (allOptions.edges.arrows.middle.type|{string}|string|*), fromArrow: boolean, fromArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), fromArrowType: *, arrowStrikethrough: (*|boolean|allOptions.edges.arrowStrikethrough|{boolean}), color: undefined, inheritsColor: (string|string|string|allOptions.edges.color.inherit|{string, boolean}|Array|*), opacity: *, hidden: *, length: *, shadow: *, shadowColor: *, shadowSize: *, shadowX: *, shadowY: *, dashes: (*|boolean|Array|allOptions.edges.dashes|{boolean, array}), width: *}} values 47122 */ 47123 47124 47125 (0, _createClass3['default'])(Triangle, [{ 47126 key: 'draw', 47127 value: function draw(ctx, x, y, selected, hover, values) { 47128 this._drawShape(ctx, 'triangle', 3, x, y, selected, hover, values); 47129 } 47130 47131 /** 47132 * 47133 * @param {CanvasRenderingContext2D} ctx 47134 * @param {number} angle 47135 * @returns {number} 47136 */ 47137 47138 }, { 47139 key: 'distanceToBorder', 47140 value: function distanceToBorder(ctx, angle) { 47141 return this._distanceToBorder(ctx, angle); 47142 } 47143 }]); 47144 return Triangle; 47145}(_ShapeBase3['default']); 47146 47147exports['default'] = Triangle; 47148 47149/***/ }), 47150/* 213 */ 47151/***/ (function(module, exports, __webpack_require__) { 47152 47153"use strict"; 47154 47155 47156Object.defineProperty(exports, "__esModule", { 47157 value: true 47158}); 47159 47160var _getPrototypeOf = __webpack_require__(3); 47161 47162var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf); 47163 47164var _classCallCheck2 = __webpack_require__(0); 47165 47166var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); 47167 47168var _createClass2 = __webpack_require__(1); 47169 47170var _createClass3 = _interopRequireDefault(_createClass2); 47171 47172var _possibleConstructorReturn2 = __webpack_require__(4); 47173 47174var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2); 47175 47176var _inherits2 = __webpack_require__(5); 47177 47178var _inherits3 = _interopRequireDefault(_inherits2); 47179 47180var _ShapeBase2 = __webpack_require__(24); 47181 47182var _ShapeBase3 = _interopRequireDefault(_ShapeBase2); 47183 47184function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 47185 47186/** 47187 * A downward facing Triangle Node/Cluster shape. 47188 * 47189 * @extends ShapeBase 47190 */ 47191var TriangleDown = function (_ShapeBase) { 47192 (0, _inherits3['default'])(TriangleDown, _ShapeBase); 47193 47194 /** 47195 * @param {Object} options 47196 * @param {Object} body 47197 * @param {Label} labelModule 47198 */ 47199 function TriangleDown(options, body, labelModule) { 47200 (0, _classCallCheck3['default'])(this, TriangleDown); 47201 return (0, _possibleConstructorReturn3['default'])(this, (TriangleDown.__proto__ || (0, _getPrototypeOf2['default'])(TriangleDown)).call(this, options, body, labelModule)); 47202 } 47203 47204 /** 47205 * 47206 * @param {CanvasRenderingContext2D} ctx 47207 * @param {number} x 47208 * @param {number} y 47209 * @param {boolean} selected 47210 * @param {boolean} hover 47211 * @param {{toArrow: boolean, toArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), toArrowType: *, middleArrow: boolean, middleArrowScale: (number|allOptions.edges.arrows.middle.scaleFactor|{number}|Array), middleArrowType: (allOptions.edges.arrows.middle.type|{string}|string|*), fromArrow: boolean, fromArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), fromArrowType: *, arrowStrikethrough: (*|boolean|allOptions.edges.arrowStrikethrough|{boolean}), color: undefined, inheritsColor: (string|string|string|allOptions.edges.color.inherit|{string, boolean}|Array|*), opacity: *, hidden: *, length: *, shadow: *, shadowColor: *, shadowSize: *, shadowX: *, shadowY: *, dashes: (*|boolean|Array|allOptions.edges.dashes|{boolean, array}), width: *}} values 47212 */ 47213 47214 47215 (0, _createClass3['default'])(TriangleDown, [{ 47216 key: 'draw', 47217 value: function draw(ctx, x, y, selected, hover, values) { 47218 this._drawShape(ctx, 'triangleDown', 3, x, y, selected, hover, values); 47219 } 47220 47221 /** 47222 * 47223 * @param {CanvasRenderingContext2D} ctx 47224 * @param {number} angle 47225 * @returns {number} 47226 */ 47227 47228 }, { 47229 key: 'distanceToBorder', 47230 value: function distanceToBorder(ctx, angle) { 47231 return this._distanceToBorder(ctx, angle); 47232 } 47233 }]); 47234 return TriangleDown; 47235}(_ShapeBase3['default']); 47236 47237exports['default'] = TriangleDown; 47238 47239/***/ }), 47240/* 214 */ 47241/***/ (function(module, exports, __webpack_require__) { 47242 47243"use strict"; 47244 47245 47246Object.defineProperty(exports, "__esModule", { 47247 value: true 47248}); 47249 47250var _classCallCheck2 = __webpack_require__(0); 47251 47252var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); 47253 47254var _createClass2 = __webpack_require__(1); 47255 47256var _createClass3 = _interopRequireDefault(_createClass2); 47257 47258function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 47259 47260var util = __webpack_require__(2); 47261var DataSet = __webpack_require__(11); 47262var DataView = __webpack_require__(12); 47263var Edge = __webpack_require__(74)['default']; 47264 47265/** 47266 * Handler for Edges 47267 */ 47268 47269var EdgesHandler = function () { 47270 /** 47271 * @param {Object} body 47272 * @param {Array.<Image>} images 47273 * @param {Array.<Group>} groups 47274 */ 47275 function EdgesHandler(body, images, groups) { 47276 var _this = this; 47277 47278 (0, _classCallCheck3['default'])(this, EdgesHandler); 47279 47280 this.body = body; 47281 this.images = images; 47282 this.groups = groups; 47283 47284 // create the edge API in the body container 47285 this.body.functions.createEdge = this.create.bind(this); 47286 47287 this.edgesListeners = { 47288 add: function add(event, params) { 47289 _this.add(params.items); 47290 }, 47291 update: function update(event, params) { 47292 _this.update(params.items); 47293 }, 47294 remove: function remove(event, params) { 47295 _this.remove(params.items); 47296 } 47297 }; 47298 47299 this.options = {}; 47300 this.defaultOptions = { 47301 arrows: { 47302 to: { enabled: false, scaleFactor: 1, type: 'arrow' }, // boolean / {arrowScaleFactor:1} / {enabled: false, arrowScaleFactor:1} 47303 middle: { enabled: false, scaleFactor: 1, type: 'arrow' }, 47304 from: { enabled: false, scaleFactor: 1, type: 'arrow' } 47305 }, 47306 arrowStrikethrough: true, 47307 color: { 47308 color: '#848484', 47309 highlight: '#848484', 47310 hover: '#848484', 47311 inherit: 'from', 47312 opacity: 1.0 47313 }, 47314 dashes: false, 47315 font: { 47316 color: '#343434', 47317 size: 14, // px 47318 face: 'arial', 47319 background: 'none', 47320 strokeWidth: 2, // px 47321 strokeColor: '#ffffff', 47322 align: 'horizontal', 47323 multi: false, 47324 vadjust: 0, 47325 bold: { 47326 mod: 'bold' 47327 }, 47328 boldital: { 47329 mod: 'bold italic' 47330 }, 47331 ital: { 47332 mod: 'italic' 47333 }, 47334 mono: { 47335 mod: '', 47336 size: 15, // px 47337 face: 'courier new', 47338 vadjust: 2 47339 } 47340 }, 47341 hidden: false, 47342 hoverWidth: 1.5, 47343 label: undefined, 47344 labelHighlightBold: true, 47345 length: undefined, 47346 physics: true, 47347 scaling: { 47348 min: 1, 47349 max: 15, 47350 label: { 47351 enabled: true, 47352 min: 14, 47353 max: 30, 47354 maxVisible: 30, 47355 drawThreshold: 5 47356 }, 47357 customScalingFunction: function customScalingFunction(min, max, total, value) { 47358 if (max === min) { 47359 return 0.5; 47360 } else { 47361 var scale = 1 / (max - min); 47362 return Math.max(0, (value - min) * scale); 47363 } 47364 } 47365 }, 47366 selectionWidth: 1.5, 47367 selfReferenceSize: 20, 47368 shadow: { 47369 enabled: false, 47370 color: 'rgba(0,0,0,0.5)', 47371 size: 10, 47372 x: 5, 47373 y: 5 47374 }, 47375 smooth: { 47376 enabled: true, 47377 type: "dynamic", 47378 forceDirection: 'none', 47379 roundness: 0.5 47380 }, 47381 title: undefined, 47382 width: 1, 47383 value: undefined 47384 }; 47385 47386 util.deepExtend(this.options, this.defaultOptions); 47387 47388 this.bindEventListeners(); 47389 } 47390 47391 /** 47392 * Binds event listeners 47393 */ 47394 47395 47396 (0, _createClass3['default'])(EdgesHandler, [{ 47397 key: 'bindEventListeners', 47398 value: function bindEventListeners() { 47399 var _this2 = this; 47400 47401 // this allows external modules to force all dynamic curves to turn static. 47402 this.body.emitter.on("_forceDisableDynamicCurves", function (type) { 47403 var emit = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true; 47404 47405 if (type === 'dynamic') { 47406 type = 'continuous'; 47407 } 47408 var dataChanged = false;
47409 for (var edgeId in _this2.body.edges) { 47410 if (_this2.body.edges.hasOwnProperty(edgeId)) { 47411 var edge = _this2.body.edges[edgeId]; 47412 var edgeData = _this2.body.data.edges._data[edgeId]; 47413 47414 // only forcibly remove the smooth curve if the data has been set of the edge has the smooth curves defined. 47415 // this is because a change in the global would not affect these curves. 47416 if (edgeData !== undefined) { 47417 var smoothOptions = edgeData.smooth; 47418 if (smoothOptions !== undefined) { 47419 if (smoothOptions.enabled === true && smoothOptions.type === 'dynamic') { 47420 if (type === undefined) { 47421 edge.setOptions({ smooth: false }); 47422 } else { 47423 edge.setOptions({ smooth: { type: type } }); 47424 } 47425 dataChanged = true; 47426 } 47427 } 47428 } 47429 } 47430 } 47431 if (emit === true && dataChanged === true) { 47432 _this2.body.emitter.emit("_dataChanged"); 47433 } 47434 }); 47435 47436 // this is called when options of EXISTING nodes or edges have changed. 47437 // 47438 // NOTE: Not true, called when options have NOT changed, for both existing as well as new nodes. 47439 // See update() for logic. 47440 // TODO: Verify and examine the consequences of this. It might still trigger when 47441 // non-option fields have changed, but then reconnecting edges is still useless. 47442 // Alternatively, it might also be called when edges are removed. 47443 // 47444 this.body.emitter.on("_dataUpdated", function () { 47445 _this2.reconnectEdges(); 47446 }); 47447 47448 // refresh the edges. Used when reverting from hierarchical layout 47449 this.body.emitter.on("refreshEdges", this.refresh.bind(this)); 47450 this.body.emitter.on("refresh", this.refresh.bind(this)); 47451 this.body.emitter.on("destroy", function () { 47452 util.forEach(_this2.edgesListeners, function (callback, event) { 47453 if (_this2.body.data.edges) _this2.body.data.edges.off(event, callback); 47454 }); 47455 delete _this2.body.functions.createEdge; 47456 delete _this2.edgesListeners.add; 47457 delete _this2.edgesListeners.update; 47458 delete _this2.edgesListeners.remove; 47459 delete _this2.edgesListeners; 47460 }); 47461 } 47462 47463 /** 47464 * 47465 * @param {Object} options 47466 */ 47467 47468 }, { 47469 key: 'setOptions', 47470 value: function setOptions(options) { 47471 if (options !== undefined) { 47472 // use the parser from the Edge class to fill in all shorthand notations 47473 Edge.parseOptions(this.options, options, true, this.defaultOptions, true); 47474 47475 // update smooth settings in all edges 47476 var dataChanged = false; 47477 if (options.smooth !== undefined) { 47478 for (var edgeId in this.body.edges) { 47479 if (this.body.edges.hasOwnProperty(edgeId)) { 47480 dataChanged = this.body.edges[edgeId].updateEdgeType() || dataChanged; 47481 } 47482 } 47483 } 47484 47485 // update fonts in all edges 47486 if (options.font !== undefined) { 47487 for (var _edgeId in this.body.edges) { 47488 if (this.body.edges.hasOwnProperty(_edgeId)) { 47489 this.body.edges[_edgeId].updateLabelModule(); 47490 } 47491 } 47492 } 47493 47494 // update the state of the variables if needed 47495 if (options.hidden !== undefined || options.physics !== undefined || dataChanged === true) { 47496 this.body.emitter.emit('_dataChanged'); 47497 } 47498 } 47499 } 47500 47501 /** 47502 * Load edges by reading the data table 47503 * @param {Array | DataSet | DataView} edges The data containing the edges. 47504 * @param {boolean} [doNotEmit=false] 47505 * @private 47506 */ 47507 47508 }, { 47509 key: 'setData', 47510 value: function setData(edges) { 47511 var _this3 = this; 47512 47513 var doNotEmit = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; 47514 47515 var oldEdgesData = this.body.data.edges; 47516 47517 if (edges instanceof DataSet || edges instanceof DataView) { 47518 this.body.data.edges = edges; 47519 } else if (Array.isArray(edges)) { 47520 this.body.data.edges = new DataSet(); 47521 this.body.data.edges.add(edges); 47522 } else if (!edges) { 47523 this.body.data.edges = new DataSet(); 47524 } else { 47525 throw new TypeError('Array or DataSet expected'); 47526 } 47527 47528 // TODO: is this null or undefined or false? 47529 if (oldEdgesData) { 47530 // unsubscribe from old dataset 47531 util.forEach(this.edgesListeners, function (callback, event) { 47532 oldEdgesData.off(event, callback); 47533 }); 47534 } 47535 47536 // remove drawn edges 47537 this.body.edges = {}; 47538 47539 // TODO: is this null or undefined or false? 47540 if (this.body.data.edges) { 47541 // subscribe to new dataset 47542 util.forEach(this.edgesListeners, function (callback, event) { 47543 _this3.body.data.edges.on(event, callback); 47544 }); 47545 47546 // draw all new nodes 47547 var ids = this.body.data.edges.getIds(); 47548 this.add(ids, true); 47549 } 47550 47551 this.body.emitter.emit('_adjustEdgesForHierarchicalLayout'); 47552 if (doNotEmit === false) { 47553 this.body.emitter.emit("_dataChanged"); 47554 } 47555 } 47556 47557 /** 47558 * Add edges 47559 * @param {number[] | string[]} ids 47560 * @param {boolean} [doNotEmit=false] 47561 * @private 47562 */ 47563 47564 }, { 47565 key: 'add', 47566 value: function add(ids) { 47567 var doNotEmit = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
47568 47569 var edges = this.body.edges; 47570 var edgesData = this.body.data.edges; 47571 47572 for (var i = 0; i < ids.length; i++) { 47573 var id = ids[i]; 47574 47575 var oldEdge = edges[id]; 47576 if (oldEdge) { 47577 oldEdge.disconnect(); 47578 } 47579 47580 var data = edgesData.get(id, { "showInternalIds": true }); 47581 edges[id] = this.create(data); 47582 } 47583 47584 this.body.emitter.emit('_adjustEdgesForHierarchicalLayout'); 47585 47586 if (doNotEmit === false) { 47587 this.body.emitter.emit("_dataChanged"); 47588 } 47589 } 47590 47591 /** 47592 * Update existing edges, or create them when not yet existing 47593 * @param {number[] | string[]} ids 47594 * @private 47595 */ 47596 47597 }, { 47598 key: 'update', 47599 value: function update(ids) { 47600 var edges = this.body.edges; 47601 var edgesData = this.body.data.edges; 47602 var dataChanged = false; 47603 for (var i = 0; i < ids.length; i++) { 47604 var id = ids[i]; 47605 var data = edgesData.get(id); 47606 var edge = edges[id]; 47607 if (edge !== undefined) { 47608 // update edge 47609 edge.disconnect(); 47610 dataChanged = edge.setOptions(data) || dataChanged; // if a support node is added, data can be changed. 47611 edge.connect(); 47612 } else { 47613 // create edge 47614 this.body.edges[id] = this.create(data); 47615 dataChanged = true; 47616 } 47617 } 47618 47619 if (dataChanged === true) { 47620 this.body.emitter.emit('_adjustEdgesForHierarchicalLayout'); 47621 this.body.emitter.emit("_dataChanged"); 47622 } else { 47623 this.body.emitter.emit("_dataUpdated"); 47624 } 47625 } 47626 47627 /** 47628 * Remove existing edges. Non existing ids will be ignored 47629 * @param {number[] | string[]} ids 47630 * @param {boolean} [emit=true] 47631 * @private 47632 */ 47633 47634 }, { 47635 key: 'remove', 47636 value: function remove(ids) { 47637 var emit = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true; 47638 47639 if (ids.length === 0) return; // early out 47640 47641 var edges = this.body.edges; 47642 util.forEach(ids, function (id) { 47643 var edge = edges[id]; 47644 if (edge !== undefined) { 47645 edge.remove(); 47646 } 47647 }); 47648 47649 if (emit) { 47650 this.body.emitter.emit("_dataChanged"); 47651 } 47652 } 47653 47654 /** 47655 * Refreshes Edge Handler 47656 */ 47657 47658 }, { 47659 key: 'refresh', 47660 value: function refresh() { 47661 var _this4 = this; 47662 47663 util.forEach(this.body.edges, function (edge, edgeId) { 47664 var data = _this4.body.data.edges._data[edgeId]; 47665 if (data !== undefined) { 47666 edge.setOptions(data); 47667 } 47668 }); 47669 } 47670 47671 /** 47672 * 47673 * @param {Object} properties 47674 * @returns {Edge} 47675 */ 47676 47677 }, { 47678 key: 'create', 47679 value: function create(properties) { 47680 return new Edge(properties, this.body, this.options, this.defaultOptions); 47681 } 47682 47683 /** 47684 * Reconnect all edges 47685 * @private 47686 */ 47687 47688 }, { 47689 key: 'reconnectEdges', 47690 value: function reconnectEdges() { 47691 var id; 47692 var nodes = this.body.nodes; 47693 var edges = this.body.edges; 47694 47695 for (id in nodes) { 47696 if (nodes.hasOwnProperty(id)) { 47697 nodes[id].edges = []; 47698 } 47699 } 47700 47701 for (id in edges) { 47702 if (edges.hasOwnProperty(id)) { 47703 var edge = edges[id]; 47704 edge.from = null; 47705 edge.to = null; 47706 edge.connect(); 47707 } 47708 } 47709 } 47710 47711 /** 47712 * 47713 * @param {Edge.id} edgeId 47714 * @returns {Array} 47715 */ 47716 47717 }, { 47718 key: 'getConnectedNodes', 47719 value: function getConnectedNodes(edgeId) { 47720 var nodeList = []; 47721 if (this.body.edges[edgeId] !== undefined) { 47722 var edge = this.body.edges[edgeId]; 47723 if (edge.fromId !== undefined) { 47724 nodeList.push(edge.fromId); 47725 } 47726 if (edge.toId !== undefined) { 47727 nodeList.push(edge.toId); 47728 } 47729 } 47730 return nodeList; 47731 } 47732 47733 /** 47734 * There is no direct relation between the nodes and the edges DataSet, 47735 * so the right place to do call this is in the handler for event `_dataUpdated`. 47736 */ 47737 47738 }, { 47739 key: '_updateState', 47740 value: function _updateState() { 47741 this._addMissingEdges(); 47742 this._removeInvalidEdges(); 47743 } 47744 47745 /** 47746 * Scan for missing nodes and remove corresponding edges, if any. 47747 * @private 47748 */ 47749 47750 }, { 47751 key: '_removeInvalidEdges', 47752 value: function _removeInvalidEdges() { 47753 var _this5 = this; 47754 47755 var edgesToDelete = []; 47756 47757 util.forEach(this.body.edges, function (edge, id) { 47758 var toNode = _this5.body.nodes[edge.toId]; 47759 var fromNode = _this5.body.nodes[edge.fromId]; 47760 47761 // Skip clustering edges here, let the Clustering module handle those 47762 if (toNode !== undefined && toNode.isCluster === true || fromNode !== undefined && fromNode.isCluster === true) { 47763 return; 47764 } 47765 47766 if (toNode === undefined || fromNode === undefined) { 47767 edgesToDelete.push(id); 47768 } 47769 }); 47770 47771 this.remove(edgesToDelete, false); 47772 } 47773 47774 /** 47775 * add all edges from dataset that are not in the cached state 47776 * @private 47777 */ 47778 47779 }, { 47780 key: '_addMissingEdges', 47781 value: function _addMissingEdges() { 47782 var edges = this.body.edges; 47783 var edgesData = this.body.data.edges; 47784 var addIds = []; 47785 47786 edgesData.forEach(function (edgeData, edgeId) { 47787 var edge = edges[edgeId]; 47788 if (edge === undefined) { 47789 addIds.push(edgeId); 47790 } 47791 }); 47792 47793 this.add(addIds, true); 47794 } 47795 }]); 47796 return EdgesHandler; 47797}(); 47798 47799exports['default'] = EdgesHandler; 47800 47801/***/ }), 47802/* 215 */ 47803/***/ (function(module, exports, __webpack_require__) { 47804 47805"use strict"; 47806 47807 47808Object.defineProperty(exports, "__esModule", { 47809 value: true 47810}); 47811 47812var _slicedToArray2 = __webpack_require__(30); 47813 47814var _slicedToArray3 = _interopRequireDefault(_slicedToArray2); 47815 47816var _getPrototypeOf = __webpack_require__(3); 47817 47818var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf); 47819 47820var _classCallCheck2 = __webpack_require__(0); 47821 47822var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); 47823 47824var _createClass2 = __webpack_require__(1); 47825 47826var _createClass3 = _interopRequireDefault(_createClass2); 47827 47828var _possibleConstructorReturn2 = __webpack_require__(4); 47829 47830var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2); 47831 47832var _inherits2 = __webpack_require__(5); 47833 47834var _inherits3 = _interopRequireDefault(_inherits2); 47835 47836var _CubicBezierEdgeBase2 = __webpack_require__(216); 47837 47838var _CubicBezierEdgeBase3 = _interopRequireDefault(_CubicBezierEdgeBase2); 47839 47840function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 47841 47842/** 47843 * A Cubic Bezier Edge. Bezier curves are used to model smooth gradual 47844 * curves in paths between nodes. 47845 * 47846 * @extends CubicBezierEdgeBase 47847 */ 47848var CubicBezierEdge = function (_CubicBezierEdgeBase) { 47849 (0, _inherits3['default'])(CubicBezierEdge, _CubicBezierEdgeBase); 47850 47851 /** 47852 * @param {Object} options 47853 * @param {Object} body 47854 * @param {Label} labelModule 47855 */ 47856 function CubicBezierEdge(options, body, labelModule) { 47857 (0, _classCallCheck3['default'])(this, CubicBezierEdge); 47858 return (0, _possibleConstructorReturn3['default'])(this, (CubicBezierEdge.__proto__ || (0, _getPrototypeOf2['default'])(CubicBezierEdge)).call(this, options, body, labelModule)); 47859 } 47860 47861 /** 47862 * Draw a line between two nodes 47863 * @param {CanvasRenderingContext2D} ctx 47864 * @param {{toArrow: boolean, toArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), toArrowType: *, middleArrow: boolean, middleArrowScale: (number|allOptions.edges.arrows.middle.scaleFactor|{number}|Array), middleArrowType: (allOptions.edges.arrows.middle.type|{string}|string|*), fromArrow: boolean, fromArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), fromArrowType: *, arrowStrikethrough: (*|boolean|allOptions.edges.arrowStrikethrough|{boolean}), color: undefined, inheritsColor: (string|string|string|allOptions.edges.color.inherit|{string, boolean}|Array|*), opacity: *, hidden: *, length: *, shadow: *, shadowColor: *, shadowSize: *, shadowX: *, shadowY: *, dashes: (*|boolean|Array|allOptions.edges.dashes|{boolean, array}), width: *}} values 47865 * @param {Array.<Node>} viaNodes 47866 * @private 47867 */ 47868 47869 47870 (0, _createClass3['default'])(CubicBezierEdge, [{ 47871 key: '_line', 47872 value: function _line(ctx, values, viaNodes) { 47873 // get the coordinates of the support points. 47874 var via1 = viaNodes[0]; 47875 var via2 = viaNodes[1]; 47876 this._bezierCurve(ctx, values, via1, via2); 47877 } 47878 47879 /** 47880 * 47881 * @returns {Array.<{x: number, y: number}>} 47882 * @private 47883 */ 47884 47885 }, { 47886 key: '_getViaCoordinates', 47887 value: function _getViaCoordinates() { 47888 var dx = this.from.x - this.to.x; 47889 var dy = this.from.y - this.to.y; 47890 47891 var x1 = void 0, 47892 y1 = void 0, 47893 x2 = void 0, 47894 y2 = void 0; 47895 var roundness = this.options.smooth.roundness; 47896 47897 // horizontal if x > y or if direction is forced or if direction is horizontal 47898 if ((Math.abs(dx) > Math.abs(dy) || this.options.smooth.forceDirection === true || this.options.smooth.forceDirection === 'horizontal') && this.options.smooth.forceDirection !== 'vertical') { 47899 y1 = this.from.y; 47900 y2 = this.to.y; 47901 x1 = this.from.x - roundness * dx; 47902 x2 = this.to.x + roundness * dx; 47903 } else { 47904 y1 = this.from.y - roundness * dy; 47905 y2 = this.to.y + roundness * dy; 47906 x1 = this.from.x; 47907 x2 = this.to.x; 47908 } 47909 47910 return [{ x: x1, y: y1 }, { x: x2, y: y2 }]; 47911 } 47912 47913 /** 47914 * 47915 * @returns {Array.<{x: number, y: number}>} 47916 */ 47917 47918 }, { 47919 key: 'getViaNode', 47920 value: function getViaNode() { 47921 return this._getViaCoordinates(); 47922 } 47923 47924 /** 47925 * 47926 * @param {Node} nearNode 47927 * @param {CanvasRenderingContext2D} ctx 47928 * @returns {{x: number, y: number, t: number}} 47929 * @private 47930 */ 47931 47932 }, { 47933 key: '_findBorderPosition', 47934 value: function _findBorderPosition(nearNode, ctx) { 47935 return this._findBorderPositionBezier(nearNode, ctx); 47936 } 47937 47938 /** 47939 * 47940 * @param {number} x1 47941 * @param {number} y1 47942 * @param {number} x2 47943 * @param {number} y2 47944 * @param {number} x3 47945 * @param {number} y3 47946 * @param {Node} via1 47947 * @param {Node} via2 47948 * @returns {number} 47949 * @private 47950 */ 47951 47952 }, { 47953 key: '_getDistanceToEdge', 47954 value: function _getDistanceToEdge(x1, y1, x2, y2, x3, y3) { 47955 var _ref = arguments.length > 6 && arguments[6] !== undefined ? arguments[6] : this._getViaCoordinates(), 47956 _ref2 = (0, _slicedToArray3['default'])(_ref, 2), 47957 via1 = _ref2[0], 47958 via2 = _ref2[1]; 47959 47960 // x3,y3 is the point 47961 return this._getDistanceToBezierEdge(x1, y1, x2, y2, x3, y3, via1, via2); 47962 } 47963 47964 /** 47965 * Combined function of pointOnLine and pointOnBezier. This gives the coordinates of a point on the line at a certain percentage of the way 47966 * @param {number} percentage 47967 * @param {{x: number, y: number}} [via1=this._getViaCoordinates()[0]] 47968 * @param {{x: number, y: number}} [via2=this._getViaCoordinates()[1]] 47969 * @returns {{x: number, y: number}} 47970 * @private 47971 */ 47972 47973 }, { 47974 key: 'getPoint', 47975 value: function getPoint(percentage) { 47976 var _ref3 = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : this._getViaCoordinates(), 47977 _ref4 = (0, _slicedToArray3['default'])(_ref3, 2), 47978 via1 = _ref4[0], 47979 via2 = _ref4[1]; 47980 47981 var t = percentage; 47982 var vec = []; 47983 vec[0] = Math.pow(1 - t, 3); 47984 vec[1] = 3 * t * Math.pow(1 - t, 2); 47985 vec[2] = 3 * Math.pow(t, 2) * (1 - t); 47986 vec[3] = Math.pow(t, 3); 47987 var x = vec[0] * this.fromPoint.x + vec[1] * via1.x + vec[2] * via2.x + vec[3] * this.toPoint.x; 47988 var y = vec[0] * this.fromPoint.y + vec[1] * via1.y + vec[2] * via2.y + vec[3] * this.toPoint.y; 47989 47990 return { x: x, y: y }; 47991 } 47992 }]); 47993 return CubicBezierEdge; 47994}(_CubicBezierEdgeBase3['default']); 47995 47996exports['default'] = CubicBezierEdge; 47997 47998/***/ }), 47999/* 216 */ 48000/***/ (function(module, exports, __webpack_require__) { 48001 48002"use strict"; 48003 48004 48005Object.defineProperty(exports, "__esModule", { 48006 value: true 48007}); 48008 48009var _getPrototypeOf = __webpack_require__(3); 48010 48011var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf); 48012 48013var _classCallCheck2 = __webpack_require__(0); 48014 48015var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); 48016 48017var _createClass2 = __webpack_require__(1); 48018 48019var _createClass3 = _interopRequireDefault(_createClass2); 48020 48021var _possibleConstructorReturn2 = __webpack_require__(4); 48022 48023var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2); 48024 48025var _inherits2 = __webpack_require__(5); 48026 48027var _inherits3 = _interopRequireDefault(_inherits2); 48028 48029var _BezierEdgeBase2 = __webpack_require__(75); 48030 48031var _BezierEdgeBase3 = _interopRequireDefault(_BezierEdgeBase2); 48032 48033function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 48034 48035/** 48036 * A Base Class for all Cubic Bezier Edges. Bezier curves are used to model 48037 * smooth gradual curves in paths between nodes. 48038 * 48039 * @extends BezierEdgeBase 48040 */ 48041var CubicBezierEdgeBase = function (_BezierEdgeBase) { 48042 (0, _inherits3['default'])(CubicBezierEdgeBase, _BezierEdgeBase); 48043 48044 /** 48045 * @param {Object} options 48046 * @param {Object} body 48047 * @param {Label} labelModule 48048 */ 48049 function CubicBezierEdgeBase(options, body, labelModule) { 48050 (0, _classCallCheck3['default'])(this, CubicBezierEdgeBase); 48051 return (0, _possibleConstructorReturn3['default'])(this, (CubicBezierEdgeBase.__proto__ || (0, _getPrototypeOf2['default'])(CubicBezierEdgeBase)).call(this, options, body, labelModule)); 48052 } 48053 48054 /** 48055 * Calculate the distance between a point (x3,y3) and a line segment from 48056 * (x1,y1) to (x2,y2). 48057 * http://stackoverflow.com/questions/849211/shortest-distancae-between-a-point-and-a-line-segment 48058 * https://en.wikipedia.org/wiki/B%C3%A9zier_curve 48059 * @param {number} x1 from x 48060 * @param {number} y1 from y 48061 * @param {number} x2 to x 48062 * @param {number} y2 to y 48063 * @param {number} x3 point to check x 48064 * @param {number} y3 point to check y 48065 * @param {Node} via1 48066 * @param {Node} via2 48067 * @returns {number} 48068 * @private 48069 */ 48070 48071 48072 (0, _createClass3['default'])(CubicBezierEdgeBase, [{ 48073 key: '_getDistanceToBezierEdge', 48074 value: function _getDistanceToBezierEdge(x1, y1, x2, y2, x3, y3, via1, via2) { 48075 // x3,y3 is the point 48076 var minDistance = 1e9; 48077 var distance = void 0; 48078 var i = void 0, 48079 t = void 0, 48080 x = void 0, 48081 y = void 0; 48082 var lastX = x1; 48083 var lastY = y1; 48084 var vec = [0, 0, 0, 0]; 48085 for (i = 1; i < 10; i++) { 48086 t = 0.1 * i; 48087 vec[0] = Math.pow(1 - t, 3); 48088 vec[1] = 3 * t * Math.pow(1 - t, 2); 48089 vec[2] = 3 * Math.pow(t, 2) * (1 - t); 48090 vec[3] = Math.pow(t, 3); 48091 x = vec[0] * x1 + vec[1] * via1.x + vec[2] * via2.x + vec[3] * x2; 48092 y = vec[0] * y1 + vec[1] * via1.y + vec[2] * via2.y + vec[3] * y2; 48093 if (i > 0) { 48094 distance = this._getDistanceToLine(lastX, lastY, x, y, x3, y3); 48095 minDistance = distance < minDistance ? distance : minDistance; 48096 } 48097 lastX = x; 48098 lastY = y; 48099 } 48100 48101 return minDistance; 48102 } 48103 }]); 48104 return CubicBezierEdgeBase; 48105}(_BezierEdgeBase3['default']); 48106 48107exports['default'] = CubicBezierEdgeBase; 48108 48109/***/ }), 48110/* 217 */ 48111/***/ (function(module, exports, __webpack_require__) { 48112 48113"use strict"; 48114 48115 48116Object.defineProperty(exports, "__esModule", { 48117 value: true 48118}); 48119 48120var _slicedToArray2 = __webpack_require__(30); 48121 48122var _slicedToArray3 = _interopRequireDefault(_slicedToArray2); 48123 48124var _getPrototypeOf = __webpack_require__(3); 48125 48126var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf); 48127 48128var _classCallCheck2 = __webpack_require__(0); 48129 48130var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); 48131 48132var _createClass2 = __webpack_require__(1); 48133 48134var _createClass3 = _interopRequireDefault(_createClass2); 48135 48136var _possibleConstructorReturn2 = __webpack_require__(4); 48137 48138var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2); 48139 48140var _inherits2 = __webpack_require__(5); 48141 48142var _inherits3 = _interopRequi
48142reDefault(_inherits2); 48143 48144var _BezierEdgeBase2 = __webpack_require__(75); 48145 48146var _BezierEdgeBase3 = _interopRequireDefault(_BezierEdgeBase2); 48147 48148function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } 48149 48150/** 48151 * A Dynamic Bezier Edge. Bezier curves are used to model smooth gradual 48152 * curves in paths between nodes. The Dynamic piece refers to how the curve 48153 * reacts to physics changes. 48154 * 48155 * @extends BezierEdgeBase 48156 */ 48157var BezierEdgeDynamic = function (_BezierEdgeBase) { 48158 (0, _inherits3["default"])(BezierEdgeDynamic, _BezierEdgeBase); 48159 48160 /** 48161 * @param {Object} options 48162 * @param {Object} body 48163 * @param {Label} labelModule 48164 */ 48165 function BezierEdgeDynamic(options, body, labelModule) { 48166 (0, _classCallCheck3["default"])(this, BezierEdgeDynamic); 48167 48168 // --> this calls the setOptions below 48169 var _this = (0, _possibleConstructorReturn3["default"])(this, (BezierEdgeDynamic.__proto__ || (0, _getPrototypeOf2["default"])(BezierEdgeDynamic)).call(this, options, body, labelModule)); 48170 //this.via = undefined; // Here for completeness but not allowed to defined before super() is invoked. 48171 48172 48173 _this._boundFunction = function () { 48174 _this.positionBezierNode(); 48175 }; 48176 _this.body.emitter.on("_repositionBezierNodes", _this._boundFunction); 48177 return _this; 48178 } 48179 48180 /** 48181 * 48182 * @param {Object} options 48183 */ 48184 48185 48186 (0, _createClass3["default"])(BezierEdgeDynamic, [{ 48187 key: "setOptions", 48188 value: function setOptions(options) { 48189 // check if the physics has changed. 48190 var physicsChange = false; 48191 if (this.options.physics !== options.physics) { 48192 physicsChange = true; 48193 } 48194 48195 // set the options and the to and from nodes 48196 this.options = options; 48197 this.id = this.options.id; 48198 this.from = this.body.nodes[this.options.from]; 48199 this.to = this.body.nodes[this.options.to]; 48200 48201 // setup the support node and connect 48202 this.setupSupportNode(); 48203 this.connect(); 48204 48205 // when we change the physics state of the edge, we reposition the support node. 48206 if (physicsChange === true) { 48207 this.via.setOptions({ physics: this.options.physics }); 48208 this.positionBezierNode(); 48209 } 48210 } 48211 48212 /** 48213 * Connects an edge to node(s) 48214 */ 48215 48216 }, { 48217 key: "connect", 48218 value: function connect() { 48219 this.from = this.body.nodes[this.options.from]; 48220 this.to = this.body.nodes[this.options.to]; 48221 if (this.from === undefined || this.to === undefined || this.options.physics === false) { 48222 this.via.setOptions({ physics: false }); 48223 } else { 48224 // fix weird behaviour where a self referencing node has physics enabled 48225 if (this.from.id === this.to.id) { 48226 this.via.setOptions({ physics: false }); 48227 } else { 48228 this.via.setOptions({ physics: true }); 48229 } 48230 } 48231 } 48232 48233 /** 48234 * remove the support nodes 48235 * @returns {boolean} 48236 */ 48237 48238 }, { 48239 key: "cleanup", 48240 value: function cleanup() { 48241 this.body.emitter.off("_repositionBezierNodes", this._boundFunction); 48242 if (this.via !== undefined) { 48243 delete this.body.nodes[this.via.id]; 48244 this.via = undefined; 48245 return true; 48246 } 48247 return false; 48248 } 48249 48250 /** 48251 * Bezier curves require an anchor point to calculate the smooth flow. These points are nodes. These nodes are invisible but 48252 * are used for the force calculation. 48253 * 48254 * The changed data is not called, if needed, it is returned by the main edge constructor. 48255 * @private 48256 */ 48257 48258 }, { 48259 key: "setupSupportNode", 48260 value: function setupSupportNode() { 48261 if (this.via === undefined) { 48262 var nodeId = "edgeId:" + this.id; 48263 var node = this.body.functions.createNode({ 48264 id: nodeId, 48265 shape: 'circle', 48266 physics: true, 48267 hidden: true 48268 }); 48269 this.body.nodes[nodeId] = node; 48270 this.via = node; 48271 this.via.parentEdgeId = this.id; 48272 this.positionBezierNode(); 48273 } 48274 } 48275 48276 /** 48277 * Positions bezier node 48278 */ 48279 48280 }, { 48281 key: "positionBezierNode", 48282 value: function positionBezierNode() { 48283 if (this.via !== undefined && this.from !== undefined && this.to !== undefined) { 48284 this.via.x = 0.5 * (this.from.x + this.to.x); 48285 this.via.y = 0.5 * (this.from.y + this.to.y); 48286 } else if (this.via !== undefined) { 48287 this.via.x = 0; 48288 this.via.y = 0; 48289 } 48290 } 48291 48292 /** 48293 * Draw a line between two nodes 48294 * @param {CanvasRenderingContext2D} ctx 48295 * @param {{toArrow: boolean, toArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), toArrowType: *, middleArrow: boolean, middleArrowScale: (number|allOptions.edges.arrows.middle.scaleFactor|{number}|Array), middleArrowType: (allOptions.edges.arrows.middle.type|{string}|string|*), fromArrow: boolean, fromArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), fromArrowType: *, arrowStrikethrough: (*|boolean|allOptions.edges.arrowStrikethrough|{boolean}), color: undefined, inheritsColor: (string|string|string|allOptions.edges.color.inherit|{string, boolean}|Array|*), opacity: *, hidden: *, length: *, shadow: *, shadowColor: *, shadowSize: *, shadowX: *, shadowY: *, dashes: (*|boolean|Array|allOptions.edges.dashes|{boolean, array}), width: *}} values 48296 * @param {Node} viaNode 48297 * @private 48298 */ 48299 48300 }, { 48301 key: "_line", 48302 value: function _line(ctx, values, viaNode) { 48303 this._bezierCurve(ctx, values, viaNode); 48304 } 48305 48306 /** 48307 * 48308 * @returns {Node|undefined|*|{index, line, column}} 48309 */ 48310 48311 }, { 48312 key: "getViaNode", 48313 value: function getViaNode() { 48314 return this.via; 48315 } 48316 48317 /** 48318 * Combined function of pointOnLine and pointOnBezier. This gives the coordinates of a point on the line at a certain percentage of the way 48319 * 48320 * @param {number} percentage 48321 * @param {Node} viaNode 48322 * @returns {{x: number, y: number}} 48323 * @private 48324 */ 48325 48326 }, { 48327 key: "getPoint", 48328 value: function getPoint(percentage) { 48329 var viaNode = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : this.via; 48330 48331 var t = percentage; 48332 var x = void 0, 48333 y = void 0; 48334 if (this.from === this.to) { 48335 var _getCircleData = this._getCircleData(this.from), 48336 _getCircleData2 = (0, _slicedToArray3["default"])(_getCircleData, 3), 48337 cx = _getCircleData2[0], 48338 cy = _getCircleData2[1], 48339 cr = _getCircleData2[2]; 48340 48341 var a = 2 * Math.PI * (1 - t); 48342 x = cx + cr * Math.sin(a); 48343 y = cy + cr - cr * (1 - Math.cos(a)); 48344 } else { 48345 x = Math.pow(1 - t, 2) * this.fromPoint.x + 2 * t * (1 - t) * viaNode.x + Math.pow(t, 2) * this.toPoint.x; 48346 y = Math.pow(1 - t, 2) * this.fromPoint.y + 2 * t * (1 - t) * viaNode.y + Math.pow(t, 2) * this.toPoint.y; 48347 } 48348 48349 return { x: x, y: y }; 48350 } 48351 48352 /** 48353 * 48354 * @param {Node} nearNode 48355 * @param {CanvasRenderingContext2D} ctx 48356 * @returns {*} 48357 * @private 48358 */ 48359 48360 }, { 48361 key: "_findBorderPosition", 48362 value: function _findBorderPosition(nearNode, ctx) { 48363 return this._findBorderPositionBezier(nearNode, ctx, this.via); 48364 } 48365 48366 /** 48367 * 48368 * @param {number} x1 48369 * @param {number} y1 48370 * @param {number} x2 48371 * @param {number} y2 48372 * @param {number} x3 48373 * @param {number} y3 48374 * @returns {number} 48375 * @private 48376 */ 48377 48378 }, { 48379 key: "_getDistanceToEdge", 48380 value: function _getDistanceToEdge(x1, y1, x2, y2, x3, y3) { 48381 // x3,y3 is the point 48382 return this._getDistanceToBezierEdge(x1, y1, x2, y2, x3, y3, this.via); 48383 } 48384 }]); 48385 return BezierEdgeDynamic; 48386}(_BezierEdgeBase3["default"]); 48387 48388exports["default"] = BezierEdgeDynamic; 48389 48390/***/ }), 48391/* 218 */ 48392/***/ (function(module, exports, __webpack_require__) { 48393 48394"use strict"; 48395 48396 48397Object.defineProperty(exports, "__esModule", { 48398 value: true 48399}); 48400 48401var _getPrototypeOf = __webpack_require__(3); 48402 48403var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf); 48404 48405var _classCallCheck2 = __webpack_require__(0); 48406 48407var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); 48408 48409var _createClass2 = __webpack_require__(1); 48410 48411var _createClass3 = _interopRequireDefault(_createClass2); 48412 48413var _possibleConstructorReturn2 = __webpack_require__(4); 48414 48415var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2); 48416 48417var _inherits2 = __webpack_require__(5); 48418 48419var _inherits3 = _interopRequireDefault(_inherits2); 48420 48421var _BezierEdgeBase2 = __webpack_require__(75); 48422 48423var _BezierEdgeBase3 = _interopRequireDefault(_BezierEdgeBase2); 48424 48425function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 48426 48427/** 48428 * A Static Bezier Edge. Bezier curves are used to model smooth gradual 48429 * curves in paths between nodes. 48430 * 48431 * @extends BezierEdgeBase 48432 */ 48433var BezierEdgeStatic = function (_BezierEdgeBase) {
48434 (0, _inherits3['default'])(BezierEdgeStatic, _BezierEdgeBase); 48435 48436 /** 48437 * @param {Object} options 48438 * @param {Object} body 48439 * @param {Label} labelModule 48440 */ 48441 function BezierEdgeStatic(options, body, labelModule) { 48442 (0, _classCallCheck3['default'])(this, BezierEdgeStatic); 48443 return (0, _possibleConstructorReturn3['default'])(this, (BezierEdgeStatic.__proto__ || (0, _getPrototypeOf2['default'])(BezierEdgeStatic)).call(this, options, body, labelModule)); 48444 } 48445 48446 /** 48447 * Draw a line between two nodes 48448 * @param {CanvasRenderingContext2D} ctx 48449 * @param {{toArrow: boolean, toArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), toArrowType: *, middleArrow: boolean, middleArrowScale: (number|allOptions.edges.arrows.middle.scaleFactor|{number}|Array), middleArrowType: (allOptions.edges.arrows.middle.type|{string}|string|*), fromArrow: boolean, fromArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), fromArrowType: *, arrowStrikethrough: (*|boolean|allOptions.edges.arrowStrikethrough|{boolean}), color: undefined, inheritsColor: (string|string|string|allOptions.edges.color.inherit|{string, boolean}|Array|*), opacity: *, hidden: *, length: *, shadow: *, shadowColor: *, shadowSize: *, shadowX: *, shadowY: *, dashes: (*|boolean|Array|allOptions.edges.dashes|{boolean, array}), width: *}} values 48450 * @param {Node} viaNode 48451 * @private 48452 */ 48453 48454 48455 (0, _createClass3['default'])(BezierEdgeStatic, [{ 48456 key: '_line', 48457 value: function _line(ctx, values, viaNode) { 48458 this._bezierCurve(ctx, values, viaNode); 48459 } 48460 48461 /** 48462 * 48463 * @returns {Array.<{x: number, y: number}>} 48464 */ 48465 48466 }, { 48467 key: 'getViaNode', 48468 value: function getViaNode() { 48469 return this._getViaCoordinates(); 48470 } 48471 48472 /** 48473 * We do not use the to and fromPoints here to make the via nodes the same as edges without arrows. 48474 * @returns {{x: undefined, y: undefined}} 48475 * @private 48476 */ 48477 48478 }, { 48479 key: '_getViaCoordinates', 48480 value: function _getViaCoordinates() { 48481 // Assumption: x/y coordinates in from/to always defined 48482 var xVia = undefined; 48483 var yVia = undefined; 48484 var factor = this.options.smooth.roundness; 48485 var type = this.options.smooth.type; 48486 var dx = Math.abs(this.from.x - this.to.x); 48487 var dy = Math.abs(this.from.y - this.to.y); 48488 if (type === 'discrete' || type === 'diagonalCross') { 48489 var stepX = void 0; 48490 var stepY = void 0; 48491 48492 if (dx <= dy) { 48493 stepX = stepY = factor * dy; 48494 } else { 48495 stepX = stepY = factor * dx; 48496 } 48497 48498 if (this.from.x > this.to.x) stepX = -stepX; 48499 if (this.from.y >= this.to.y) stepY = -stepY; 48500 48501 xVia = this.from.x + stepX; 48502 yVia = this.from.y + stepY; 48503 48504 if (type === "discrete") { 48505 if (dx <= dy) { 48506 xVia = dx < factor * dy ? this.from.x : xVia; 48507 } else { 48508 yVia = dy < factor * dx ? this.from.y : yVia; 48509 } 48510 } 48511 } else if (type === "straightCross") { 48512 var _stepX = (1 - factor) * dx; 48513 var _stepY = (1 - factor) * dy; 48514 48515 if (dx <= dy) { 48516 // up - down 48517 _stepX = 0; 48518 if (this.from.y < this.to.y) _stepY = -_stepY; 48519 } else { 48520 // left - right 48521 if (this.from.x < this.to.x) _stepX = -_stepX; 48522 _stepY = 0; 48523 } 48524 xVia = this.to.x + _stepX; 48525 yVia = this.to.y + _stepY; 48526 } else if (type === 'horizontal') { 48527 var _stepX2 = (1 - factor) * dx; 48528 if (this.from.x < this.to.x) _stepX2 = -_stepX2; 48529 xVia = this.to.x + _stepX2; 48530 yVia = this.from.y; 48531 } else if (type === 'vertical') { 48532 var _stepY2 = (1 - factor) * dy; 48533 if (this.from.y < this.to.y) _stepY2 = -_stepY2; 48534 xVia = this.from.x; 48535 yVia = this.to.y + _stepY2; 48536 } else if (type === 'curvedCW') { 48537 dx = this.to.x - this.from.x; 48538 dy = this.from.y - this.to.y; 48539 var radius = Math.sqrt(dx * dx + dy * dy); 48540 var pi = Math.PI; 48541 48542 var originalAngle = Math.atan2(dy, dx); 48543 var myAngle = (originalAngle + (factor * 0.5 + 0.5) * pi) % (2 * pi); 48544 48545 xVia = this.from.x + (factor * 0.5 + 0.5) * radius * Math.sin(myAngle); 48546 yVia = this.from.y + (factor * 0.5 + 0.5) * radius * Math.cos(myAngle); 48547 } else if (type === 'curvedCCW') { 48548 dx = this.to.x - this.from.x; 48549 dy = this.from.y - this.to.y; 48550 var _radius = Math.sqrt(dx * dx + dy * dy); 48551 var _pi = Math.PI; 48552 48553 var _originalAngle = Math.atan2(dy, dx); 48554 var _myAngle = (_originalAngle + (-factor * 0.5 + 0.5) * _pi) % (2 * _pi); 48555 48556 xVia = this.from.x + (factor * 0.5 + 0.5) * _radius * Math.sin(_myAngle); 48557 yVia = this.from.y + (factor * 0.5 + 0.5) * _radius * Math.cos(_myAngle); 48558 } else { 48559 // continuous 48560 var _stepX3 = void 0; 48561 var _stepY3 = void 0; 48562 48563 if (dx <= dy) { 48564 _stepX3 = _stepY3 = factor * dy; 48565 } else { 48566 _stepX3 = _stepY3 = factor * dx; 48567 } 48568 48569 if (this.from.x > this.to.x) _stepX3 = -_stepX3; 48570 if (this.from.y >= this.to.y) _stepY3 = -_stepY3; 48571 48572 xVia = this.from.x + _stepX3; 48573 yVia = this.from.y + _stepY3; 48574 48575 if (dx <= dy) { 48576 if (this.from.x <= this.to.x) { 48577 xVia = this.to.x < xVia ? this.to.x : xVia; 48578 } else { 48579 xVia = this.to.x > xVia ? this.to.x : xVia; 48580 } 48581 } else { 48582 if (this.from.y >= this.to.y) { 48583 yVia = this.to.y > yVia ? this.to.y : yVia; 48584 } else { 48585 yVia = this.to.y < yVia ? this.to.y : yVia; 48586 } 48587 } 48588 } 48589 return { x: xVia, y: yVia }; 48590 } 48591 48592 /** 48593 * 48594 * @param {Node} nearNode 48595 * @param {CanvasRenderingContext2D} ctx 48596 * @param {Object} options 48597 * @returns {*} 48598 * @private 48599 */ 48600 48601 }, { 48602 key: '_findBorderPosition', 48603 value: function _findBorderPosition(nearNode, ctx) { 48604 var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; 48605 48606 return this._findBorderPositionBezier(nearNode, ctx, options.via); 48607 } 48608 48609 /** 48610 * 48611 * @param {number} x1 48612 * @param {number} y1 48613 * @param {number} x2 48614 * @param {number} y2 48615 * @param {number} x3 48616 * @param {number} y3 48617 * @param {Node} viaNode 48618 * @returns {number} 48619 * @private 48620 */ 48621 48622 }, { 48623 key: '_getDistanceToEdge', 48624 value: function _getDistanceToEdge(x1, y1, x2, y2, x3, y3) { 48625 var viaNode = arguments.length > 6 && arguments[6] !== undefined ? arguments[6] : this._getViaCoordinates(); 48626 // x3,y3 is the point 48627 return this._getDistanceToBezierEdge(x1, y1, x2, y2, x3, y3, viaNode); 48628 } 48629 48630 /** 48631 * Combined function of pointOnLine and pointOnBezier. This gives the coordinates of a point on the line at a certain percentage of the way 48632 * @param {number} percentage 48633 * @param {Node} viaNode 48634 * @returns {{x: number, y: number}} 48635 * @private 48636 */ 48637 48638 }, { 48639 key: 'getPoint', 48640 value: function getPoint(percentage) { 48641 var viaNode = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : this._getViaCoordinates(); 48642 48643 var t = percentage; 48644 var x = Math.pow(1 - t, 2) * this.fromPoint.x + 2 * t * (1 - t) * viaNode.x + Math.pow(t, 2) * this.toPoint.x; 48645 var y = Math.pow(1 - t, 2) * this.fromPoint.y + 2 * t * (1 - t) * viaNode.y + Math.pow(t, 2) * this.toPoint.y; 48646 48647 return { x: x, y: y }; 48648 } 48649 }]); 48650 return BezierEdgeStatic; 48651}(_BezierEdgeBase3['default']); 48652 48653exports['default'] = BezierEdgeStatic; 48654 48655/***/ }), 48656/* 219 */ 48657/***/ (function(module, exports, __webpack_require__) { 48658 48659"use strict"; 48660 48661 48662Object.defineProperty(exports, "__esModule", { 48663 value: true 48664}); 48665 48666var _getPrototypeOf = __webpack_require__(3); 48667 48668var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf); 48669 48670var _classCallCheck2 = __webpack_require__(0); 48671 48672var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); 48673 48674var _createClass2 = __webpack_require__(1); 48675 48676var _createClass3 = _interopRequireDefault(_createClass2); 48677 48678var _possibleConstructorReturn2 = __webpack_require__(4); 48679 48680var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2); 48681 48682var _inherits2 = __webpack_require__(5); 48683 48684var _inherits3 = _interopRequireDefault(_inherits2); 48685 48686var _EdgeBase2 = __webpack_require__(118); 48687 48688var _EdgeBase3 = _interopRequireDefault(_EdgeBase2); 48689 48690function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 48691 48692/** 48693 * A Straight Edge. 48694 *
48695 * @extends EdgeBase 48696 */ 48697var StraightEdge = function (_EdgeBase) { 48698 (0, _inherits3['default'])(StraightEdge, _EdgeBase); 48699 48700 /** 48701 * @param {Object} options 48702 * @param {Object} body 48703 * @param {Label} labelModule 48704 */ 48705 function StraightEdge(options, body, labelModule) { 48706 (0, _classCallCheck3['default'])(this, StraightEdge); 48707 return (0, _possibleConstructorReturn3['default'])(this, (StraightEdge.__proto__ || (0, _getPrototypeOf2['default'])(StraightEdge)).call(this, options, body, labelModule)); 48708 } 48709 48710 /** 48711 * Draw a line between two nodes 48712 * @param {CanvasRenderingContext2D} ctx 48713 * @param {{toArrow: boolean, toArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), toArrowType: *, middleArrow: boolean, middleArrowScale: (number|allOptions.edges.arrows.middle.scaleFactor|{number}|Array), middleArrowType: (allOptions.edges.arrows.middle.type|{string}|string|*), fromArrow: boolean, fromArrowScale: (allOptions.edges.arrows.to.scaleFactor|{number}|allOptions.edges.arrows.middle.scaleFactor|allOptions.edges.arrows.from.scaleFactor|Array|number), fromArrowType: *, arrowStrikethrough: (*|boolean|allOptions.edges.arrowStrikethrough|{boolean}), color: undefined, inheritsColor: (string|string|string|allOptions.edges.color.inherit|{string, boolean}|Array|*), opacity: *, hidden: *, length: *, shadow: *, shadowColor: *, shadowSize: *, shadowX: *, shadowY: *, dashes: (*|boolean|Array|allOptions.edges.dashes|{boolean, array}), width: *}} values 48714 * @private 48715 */ 48716 48717 48718 (0, _createClass3['default'])(StraightEdge, [{ 48719 key: '_line', 48720 value: function _line(ctx, values) { 48721 // draw a straight line 48722 ctx.beginPath(); 48723 ctx.moveTo(this.fromPoint.x, this.fromPoint.y); 48724 ctx.lineTo(this.toPoint.x, this.toPoint.y); 48725 // draw shadow if enabled 48726 this.enableShadow(ctx, values); 48727 ctx.stroke(); 48728 this.disableShadow(ctx, values); 48729 } 48730 48731 /** 48732 * 48733 * @returns {undefined} 48734 */ 48735 48736 }, { 48737 key: 'getViaNode', 48738 value: function getViaNode() { 48739 return undefined; 48740 } 48741 48742 /** 48743 * Combined function of pointOnLine and pointOnBezier. This gives the coordinates of a point on the line at a certain percentage of the way 48744 * 48745 * @param {number} percentage 48746 * @returns {{x: number, y: number}} 48747 * @private 48748 */ 48749 48750 }, { 48751 key: 'getPoint', 48752 value: function getPoint(percentage) { 48753 return { 48754 x: (1 - percentage) * this.fromPoint.x + percentage * this.toPoint.x, 48755 y: (1 - percentage) * this.fromPoint.y + percentage * this.toPoint.y 48756 }; 48757 } 48758 48759 /** 48760 * 48761 * @param {Node} nearNode 48762 * @param {CanvasRenderingContext2D} ctx 48763 * @returns {{x: number, y: number}} 48764 * @private 48765 */ 48766 48767 }, { 48768 key: '_findBorderPosition', 48769 value: function _findBorderPosition(nearNode, ctx) { 48770 var node1 = this.to; 48771 var node2 = this.from; 48772 if (nearNode.id === this.from.id) { 48773 node1 = this.from; 48774 node2 = this.to; 48775 } 48776 48777 var angle = Math.atan2(node1.y - node2.y, node1.x - node2.x); 48778 var dx = node1.x - node2.x; 48779 var dy = node1.y - node2.y; 48780 var edgeSegmentLength = Math.sqrt(dx * dx + dy * dy); 48781 var toBorderDist = nearNode.distanceToBorder(ctx, angle); 48782 var toBorderPoint = (edgeSegmentLength - toBorderDist) / edgeSegmentLength; 48783 48784 var borderPos = {}; 48785 borderPos.x = (1 - toBorderPoint) * node2.x + toBorderPoint * node1.x; 48786 borderPos.y = (1 - toBorderPoint) * node2.y + toBorderPoint * node1.y; 48787 48788 return borderPos; 48789 } 48790 48791 /** 48792 * 48793 * @param {number} x1 48794 * @param {number} y1 48795 * @param {number} x2 48796 * @param {number} y2 48797 * @param {number} x3 48798 * @param {number} y3 48799 * @returns {number} 48800 * @private 48801 */ 48802 48803 }, { 48804 key: '_getDistanceToEdge', 48805 value: function _getDistanceToEdge(x1, y1, x2, y2, x3, y3) { 48806 // x3,y3 is the point 48807 return this._getDistanceToLine(x1, y1, x2, y2, x3, y3); 48808 } 48809 }]); 48810 return StraightEdge; 48811}(_EdgeBase3['default']); 48812 48813exports['default'] = StraightEdge; 48814 48815/***/ }), 48816/* 220 */ 48817/***/ (function(module, exports, __webpack_require__) { 48818 48819"use strict"; 48820 48821 48822Object.defineProperty(exports, "__esModule", { 48823 value: true 48824}); 48825 48826var _keys = __webpack_require__(8); 48827 48828var _keys2 = _interopRequireDefault(_keys); 48829 48830var _classCallCheck2 = __webpack_require__(0); 48831 48832var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); 48833 48834var _createClass2 = __webpack_require__(1); 48835 48836var _createClass3 = _interopRequireDefault(_createClass2); 48837 48838function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 48839 48840var BarnesHutSolver = __webpack_require__(120)['default']; 48841var Repulsion = __webpack_require__(221)['default']; 48842var HierarchicalRepulsion = __webpack_require__(222)['default']; 48843var SpringSolver = __webpack_require__(223)['default']; 48844var HierarchicalSpringSolver = __webpack_require__(224)['default']; 48845var CentralGravitySolver = __webpack_require__(121)['default']; 48846var ForceAtlas2BasedRepulsionSolver = __webpack_require__(225)['default']; 48847var ForceAtlas2BasedCentralGravitySolver = __webpack_require__(226)['default']; 48848var util = __webpack_require__(2); 48849var EndPoints = __webpack_require__(119)['default']; // for debugging with _drawForces() 48850 48851 48852/** 48853 * The physics engine 48854 */ 48855 48856var PhysicsEngine = function () { 48857 /** 48858 * @param {Object} body 48859 */ 48860 function PhysicsEngine(body) { 48861 (0, _classCallCheck3['default'])(this, PhysicsEngine); 48862 48863 this.body = body; 48864 this.physicsBody = { physicsNodeIndices: [], physicsEdgeIndices: [], forces: {}, velocities: {} }; 48865 48866 this.physicsEnabled = true; 48867 this.simulationInterval = 1000 / 60; 48868 this.requiresTimeout = true; 48869 this.previousStates = {}; 48870 this.referenceState = {}; 48871 this.freezeCache = {}; 48872 this.renderTimer = undefined; 48873 48874 // parameters for the adaptive timestep 48875 this.adaptiveTimestep = false;
48876 this.adaptiveTimestepEnabled = false; 48877 this.adaptiveCounter = 0; 48878 this.adaptiveInterval = 3; 48879 48880 this.stabilized = false; 48881 this.startedStabilization = false; 48882 this.stabilizationIterations = 0; 48883 this.ready = false; // will be set to true if the stabilize 48884 48885 // default options 48886 this.options = {}; 48887 this.defaultOptions = { 48888 enabled: true, 48889 barnesHut: { 48890 theta: 0.5, 48891 gravitationalConstant: -2000, 48892 centralGravity: 0.3, 48893 springLength: 95, 48894 springConstant: 0.04, 48895 damping: 0.09, 48896 avoidOverlap: 0 48897 }, 48898 forceAtlas2Based: { 48899 theta: 0.5, 48900 gravitationalConstant: -50, 48901 centralGravity: 0.01, 48902 springConstant: 0.08, 48903 springLength: 100, 48904 damping: 0.4, 48905 avoidOverlap: 0 48906 }, 48907 repulsion: { 48908 centralGravity: 0.2, 48909 springLength: 200, 48910 springConstant: 0.05, 48911 nodeDistance: 100, 48912 damping: 0.09, 48913 avoidOverlap: 0 48914 }, 48915 hierarchicalRepulsion: { 48916 centralGravity: 0.0, 48917 springLength: 100, 48918 springConstant: 0.01, 48919 nodeDistance: 120, 48920 damping: 0.09 48921 }, 48922 maxVelocity: 50, 48923 minVelocity: 0.75, // px/s 48924 solver: 'barnesHut', 48925 stabilization: { 48926 enabled: true, 48927 iterations: 1000, // maximum number of iteration to stabilize 48928 updateInterval: 50, 48929 onlyDynamicEdges: false, 48930 fit: true 48931 }, 48932 timestep: 0.5, 48933 adaptiveTimestep: true 48934 }; 48935 util.extend(this.options, this.defaultOptions); 48936 this.timestep = 0.5; 48937 this.layoutFailed = false; 48938 48939 this.bindEventListeners(); 48940 } 48941 48942 /** 48943 * Binds event listeners 48944 */ 48945 48946 48947 (0, _createClass3['default'])(PhysicsEngine, [{ 48948 key: 'bindEventListeners', 48949 value: function bindEventListeners() { 48950 var _this = this; 48951 48952 this.body.emitter.on('initPhysics', function () { 48953 _this.initPhysics(); 48954 }); 48955 this.body.emitter.on('_layoutFailed', function () { 48956 _this.layoutFailed = true; 48957 }); 48958 this.body.emitter.on('resetPhysics', function () { 48959 _this.stopSimulation();_this.ready = false; 48960 }); 48961 this.body.emitter.on('disablePhysics', function () { 48962 _this.physicsEnabled = false;_this.stopSimulation(); 48963 }); 48964 this.body.emitter.on('restorePhysics', function () { 48965 _this.setOptions(_this.options); 48966 if (_this.ready === true) { 48967 _this.startSimulation(); 48968 } 48969 }); 48970 this.body.emitter.on('startSimulation', function () { 48971 if (_this.ready === true) { 48972 _this.startSimulation(); 48973 } 48974 }); 48975 this.body.emitter.on('stopSimulation', function () { 48976 _this.stopSimulation(); 48977 }); 48978 this.body.emitter.on('destroy', function () { 48979 _this.stopSimulation(false); 48980 _this.body.emitter.off(); 48981 }); 48982 this.body.emitter.on("_dataChanged", function () { 48983 // Nodes and/or edges have been added or removed, update shortcut lists. 48984 _this.updatePhysicsData(); 48985 }); 48986 48987 // debug: show forces 48988 // this.body.emitter.on("afterDrawing", (ctx) => {this._drawForces(ctx);}); 48989 } 48990 48991 /** 48992 * set the physics options 48993 * @param {Object} options 48994 */ 48995 48996 }, { 48997 key: 'setOptions', 48998 value: function setOptions(options) { 48999 if (options !== undefined) { 49000 if (options === false) { 49001 this.options.enabled = false; 49002 this.physicsEnabled = false; 49003 this.stopSimulation(); 49004 } else if (options === true) { 49005 this.options.enabled = true; 49006 this.physicsEnabled = true; 49007 this.startSimulation(); 49008 } else { 49009 this.physicsEnabled = true; 49010 util.selectiveNotDeepExtend(['stabilization'], this.options, options); 49011 util.mergeOptions(this.options, options, 'stabilization'); 49012 49013 if (options.enabled === undefined) { 49014 this.options.enabled = true; 49015 } 49016 49017 if (this.options.enabled === false) { 49018 this.physicsEnabled = false; 49019 this.stopSimulation(); 49020 } 49021 49022 // set the timestep 49023 this.timestep = this.options.timestep; 49024 } 49025 } 49026 this.init(); 49027 } 49028 49029 /** 49030 * configure the engine. 49031 */ 49032 49033 }, { 49034 key: 'init',
49035 value: function init() { 49036 var options; 49037 if (this.options.solver === 'forceAtlas2Based') { 49038 options = this.options.forceAtlas2Based; 49039 this.nodesSolver = new ForceAtlas2BasedRepulsionSolver(this.body, this.physicsBody, options); 49040 this.edgesSolver = new SpringSolver(this.body, this.physicsBody, options); 49041 this.gravitySolver = new ForceAtlas2BasedCentralGravitySolver(this.body, this.physicsBody, options); 49042 } else if (this.options.solver === 'repulsion') { 49043 options = this.options.repulsion; 49044 this.nodesSolver = new Repulsion(this.body, this.physicsBody, options); 49045 this.edgesSolver = new SpringSolver(this.body, this.physicsBody, options); 49046 this.gravitySolver = new CentralGravitySolver(this.body, this.physicsBody, options); 49047 } else if (this.options.solver === 'hierarchicalRepulsion') { 49048 options = this.options.hierarchicalRepulsion; 49049 this.nodesSolver = new HierarchicalRepulsion(this.body, this.physicsBody, options); 49050 this.edgesSolver = new HierarchicalSpringSolver(this.body, this.physicsBody, options); 49051 this.gravitySolver = new CentralGravitySolver(this.body, this.physicsBody, options); 49052 } else { 49053 // barnesHut 49054 options = this.options.barnesHut; 49055 this.nodesSolver = new BarnesHutSolver(this.body, this.physicsBody, options); 49056 this.edgesSolver = new SpringSolver(this.body, this.physicsBody, options); 49057 this.gravitySolver = new CentralGravitySolver(this.body, this.physicsBody, options); 49058 } 49059 49060 this.modelOptions = options; 49061 } 49062 49063 /** 49064 * initialize the engine 49065 */ 49066 49067 }, { 49068 key: 'initPhysics', 49069 value: function initPhysics() { 49070 if (this.physicsEnabled === true && this.options.enabled === true) { 49071 if (this.options.stabilization.enabled === true) { 49072 this.stabilize(); 49073 } else { 49074 this.stabilized = false; 49075 this.ready = true; 49076 this.body.emitter.emit('fit', {}, this.layoutFailed); // if the layout failed, we use the approximation for the zoom 49077 this.startSimulation(); 49078 } 49079 } else { 49080 this.ready = true; 49081 this.body.emitter.emit('fit'); 49082 } 49083 } 49084 49085 /** 49086 * Start the simulation 49087 */ 49088 49089 }, { 49090 key: 'startSimulation', 49091 value: function startSimulation() { 49092 if (this.physicsEnabled === true && this.options.enabled === true) { 49093 this.stabilized = false; 49094 49095 // when visible, adaptivity is disabled. 49096 this.adaptiveTimestep = false; 49097 49098 // this sets the width of all nodes initially which could be required for the avoidOverlap 49099 this.body.emitter.emit("_resizeNodes"); 49100 if (this.viewFunction === undefined) { 49101 this.viewFunction = this.simulationStep.bind(this); 49102 this.body.emitter.on('initRedraw', this.viewFunction); 49103 this.body.emitter.emit('_startRendering'); 49104 } 49105 } else { 49106 this.body.emitter.emit('_redraw'); 49107 } 49108 } 49109 49110 /** 49111 * Stop the simulation, force stabilization. 49112 * @param {boolean} [emit=true] 49113 */ 49114 49115 }, { 49116 key: 'stopSimulation', 49117 value: function stopSimulation() { 49118 var emit = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true; 49119 49120 this.stabilized = true; 49121 if (emit === true) { 49122 this._emitStabilized(); 49123 } 49124 if (this.viewFunction !== undefined) { 49125 this.body.emitter.off('initRedraw', this.viewFunction); 49126 this.viewFunction = undefined; 49127 if (emit === true) { 49128 this.body.emitter.emit('_stopRendering'); 49129 } 49130 } 49131 } 49132 49133 /** 49134 * The viewFunction inserts this step into each render loop. It calls the physics tick and handles the cleanup at stabilized. 49135 * 49136 */ 49137 49138 }, { 49139 key: 'simulationStep', 49140 value: function simulationStep() { 49141 // check if the physics have settled 49142 var startTime = Date.now(); 49143 this.physicsTick(); 49144 var physicsTime = Date.now() - startTime; 49145 49146 // run double speed if it is a little graph 49147 if ((physicsTime < 0.4 * this.simulationInterval || this.runDoubleSpeed === true) && this.stabilized === false) { 49148 this.physicsTick(); 49149 49150 // this makes sure there is no jitter. The decision is taken once to run it at double speed. 49151 this.runDoubleSpeed = true; 49152 } 49153 49154 if (this.stabilized === true) { 49155 this.stopSimulation(); 49156 } 49157 } 49158 49159 /** 49160 * trigger the stabilized event. 49161 * 49162 * @param {number} [amountOfIterations=this.stabilizationIterations] 49163 * @private 49164 */ 49165 49166 }, { 49167 key: '_emitStabilized', 49168 value: function _emitStabilized() { 49169 var _this2 = this; 49170 49171 var amountOfIterations = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : this.stabilizationIterations; 49172 49173 if (this.stabilizationIterations > 1 || this.startedStabilization === true) { 49174 setTimeout(function () { 49175 _this2.body.emitter.emit('stabilized', { iterations: amountOfIterations }); 49176 _this2.startedStabilization = false;
49177 _this2.stabilizationIterations = 0; 49178 }, 0); 49179 } 49180 } 49181 49182 /** 49183 * Calculate the forces for one physics iteration and move the nodes. 49184 * @private 49185 */ 49186 49187 }, { 49188 key: 'physicsStep', 49189 value: function physicsStep() { 49190 this.gravitySolver.solve(); 49191 this.nodesSolver.solve(); 49192 this.edgesSolver.solve(); 49193 this.moveNodes(); 49194 } 49195 49196 /** 49197 * Make dynamic adjustments to the timestep, based on current state. 49198 * 49199 * Helper function for physicsTick(). 49200 * @private 49201 */ 49202 49203 }, { 49204 key: 'adjustTimeStep', 49205 value: function adjustTimeStep() { 49206 var factor = 1.2; // Factor for increasing the timestep on success. 49207 49208 // we compare the two steps. if it is acceptable we double the step. 49209 if (this._evaluateStepQuality() === true) { 49210 this.timestep = factor * this.timestep; 49211 } else { 49212 // if not, we decrease the step to a minimum of the options timestep. 49213 // if the decreased timestep is smaller than the options step, we do not reset the counter 49214 // we assume that the options timestep is stable enough. 49215 if (this.timestep / factor < this.options.timestep) { 49216 this.timestep = this.options.timestep; 49217 } else { 49218 // if the timestep was larger than 2 times the option one we check the adaptivity again to ensure 49219 // that large instabilities do not form. 49220 this.adaptiveCounter = -1; // check again next iteration 49221 this.timestep = Math.max(this.options.timestep, this.timestep / factor); 49222 } 49223 } 49224 } 49225 49226 /** 49227 * A single simulation step (or 'tick') in the physics simulation 49228 * 49229 * @private 49230 */ 49231 49232 }, { 49233 key: 'physicsTick', 49234 value: function physicsTick() { 49235 this._startStabilizing(); // this ensures that there is no start event when the network is already stable. 49236 if (this.stabilized === true) return; 49237 49238 // adaptivity means the timestep adapts to the situation, only applicable for stabilization 49239 if (this.adaptiveTimestep === true && this.adaptiveTimestepEnabled === true) { 49240 // timestep remains stable for "interval" iterations. 49241 var doAdaptive = this.adaptiveCounter % this.adaptiveInterval === 0; 49242 49243 if (doAdaptive) { 49244 // first the big step and revert. 49245 this.timestep = 2 * this.timestep; 49246 this.physicsStep(); 49247 this.revert(); // saves the reference state 49248 49249 // now the normal step. Since this is the last step, it is the more stable one and we will take this. 49250 this.timestep = 0.5 * this.timestep; 49251 49252 // since it's half the step, we do it twice. 49253 this.physicsStep(); 49254 this.physicsStep(); 49255 49256 this.adjustTimeStep(); 49257 } else { 49258 this.physicsStep(); // normal step, keeping timestep constant 49259 } 49260 49261 this.adaptiveCounter += 1; 49262 } else { 49263 // case for the static timestep, we reset it to the one in options and take a normal step. 49264 this.timestep = this.options.timestep; 49265 this.physicsStep(); 49266 } 49267 49268 if (this.stabilized === true) this.revert(); 49269 this.stabilizationIterations++; 49270 } 49271 49272 /** 49273 * Nodes and edges can have the physics toggles on or off. A collection of indices is created here so we can skip the check all the time. 49274 * 49275 * @private 49276 */ 49277 49278 }, { 49279 key: 'updatePhysicsData', 49280 value: function updatePhysicsData() { 49281 this.physicsBody.forces = {}; 49282 this.physicsBody.physicsNodeIndices = []; 49283 this.physicsBody.physicsEdgeIndices = []; 49284 var nodes = this.body.nodes; 49285 var edges = this.body.edges; 49286 49287 // get node indices for physics 49288 for (var nodeId in nodes) { 49289 if (nodes.hasOwnProperty(nodeId)) { 49290 if (nodes[nodeId].options.physics === true) { 49291 this.physicsBody.physicsNodeIndices.push(nodes[nodeId].id); 49292 } 49293 } 49294 } 49295 49296 // get edge indices for physics 49297 for (var edgeId in edges) { 49298 if (edges.hasOwnProperty(edgeId)) { 49299 if (edges[edgeId].options.physics === true) { 49300 this.physicsBody.physicsEdgeIndices.push(edges[edgeId].id); 49301 } 49302 } 49303 } 49304 49305 // get the velocity and the forces vector 49306 for (var i = 0; i < this.physicsBody.physicsNodeIndices.length; i++) { 49307 var _nodeId = this.physicsBody.physicsNodeIndices[i]; 49308 this.physicsBody.forces[_nodeId] = { x: 0, y: 0 }; 49309 49310 // forces can be reset because they are recalculated. Velocities have to persist. 49311 if (this.physicsBody.velocities[_nodeId] === undefined) { 49312 this.physicsBody.velocities[_nodeId] = { x: 0, y: 0 }; 49313 } 49314 } 49315 49316 // clean deleted nodes from the velocity vector 49317 for (var _nodeId2 in this.physicsBody.velocities) { 49318 if (nodes[_nodeId2] === undefined) { 49319 delete this.physicsBody.velocities[_nodeId2]; 49320 } 49321 } 49322 } 49323 49324 /** 49325 * Revert the simulation one step. This is done so after stabilization, every new start of the simulation will also say stabilized. 49326 */ 49327 49328 }, { 49329 key: 'revert', 49330 value: function revert() { 49331 var nodeIds = (0, _keys2['default'])(this.previousStates); 49332 var nodes = this.body.nodes; 49333 var velocities = this.physicsBody.velocities; 49334 this.referenceState = {}; 49335 49336 for (var i = 0; i < nodeIds.length; i++) { 49337 var nodeId = nodeIds[i]; 49338 if (nodes[nodeId] !== undefined) { 49339 if (nodes[nodeId].options.physics === true) { 49340 this.referenceState[nodeId] = {
49341 positions: { x: nodes[nodeId].x, y: nodes[nodeId].y } 49342 }; 49343 velocities[nodeId].x = this.previousStates[nodeId].vx; 49344 velocities[nodeId].y = this.previousStates[nodeId].vy; 49345 nodes[nodeId].x = this.previousStates[nodeId].x; 49346 nodes[nodeId].y = this.previousStates[nodeId].y; 49347 } 49348 } else { 49349 delete this.previousStates[nodeId]; 49350 } 49351 } 49352 } 49353 49354 /** 49355 * This compares the reference state to the current state 49356 * 49357 * @returns {boolean} 49358 * @private 49359 */ 49360 49361 }, { 49362 key: '_evaluateStepQuality', 49363 value: function _evaluateStepQuality() { 49364 var dx = void 0, 49365 dy = void 0, 49366 dpos = void 0; 49367 var nodes = this.body.nodes; 49368 var reference = this.referenceState; 49369 var posThreshold = 0.3; 49370 49371 for (var nodeId in this.referenceState) { 49372 if (this.referenceState.hasOwnProperty(nodeId) && nodes[nodeId] !== undefined) { 49373 dx = nodes[nodeId].x - reference[nodeId].positions.x; 49374 dy = nodes[nodeId].y - reference[nodeId].positions.y; 49375 49376 dpos = Math.sqrt(Math.pow(dx, 2) + Math.pow(dy, 2)); 49377 49378 if (dpos > posThreshold) { 49379 return false; 49380 } 49381 } 49382 } 49383 return true; 49384 } 49385 49386 /** 49387 * move the nodes one timestep and check if they are stabilized 49388 */ 49389 49390 }, { 49391 key: 'moveNodes', 49392 value: function moveNodes() { 49393 var nodeIndices = this.physicsBody.physicsNodeIndices; 49394 var maxNodeVelocity = 0; 49395 var averageNodeVelocity = 0; 49396 49397 // the velocity threshold (energy in the system) for the adaptivity toggle 49398 var velocityAdaptiveThreshold = 5; 49399 49400 for (var i = 0; i < nodeIndices.length; i++) { 49401 var nodeId = nodeIndices[i]; 49402 var nodeVelocity = this._performStep(nodeId); 49403 // stabilized is true if stabilized is true and velocity is smaller than vmin --> all nodes must be stabilized 49404 maxNodeVelocity = Math.max(maxNodeVelocity, nodeVelocity); 49405 averageNodeVelocity += nodeVelocity; 49406 } 49407 49408 // evaluating the stabilized and adaptiveTimestepEnabled conditions 49409 this.adaptiveTimestepEnabled = averageNodeVelocity / nodeIndices.length < velocityAdaptiveThreshold; 49410 this.stabilized = maxNodeVelocity < this.options.minVelocity; 49411 } 49412 49413 /** 49414 * Calculate new velocity for a coordinate direction 49415 * 49416 * @param {number} v velocity for current coordinate 49417 * @param {number} f regular force for current coordinate 49418 * @param {number} m mass of current node 49419 * @returns {number} new velocity for current coordinate 49420 * @private 49421 */ 49422 49423 }, { 49424 key: 'calculateComponentVelocity', 49425 value: function calculateComponentVelocity(v, f, m) { 49426 var df = this.modelOptions.damping * v; // damping force 49427 var a = (f - df) / m; // acceleration 49428 49429 v += a * this.timestep; 49430 49431 // Put a limit on the velocities if it is really high 49432 var maxV = this.options.maxVelocity || 1e9; 49433 if (Math.abs(v) > maxV) { 49434 v = v > 0 ? maxV : -maxV; 49435 } 49436 49437 return v; 49438 } 49439 49440 /** 49441 * Perform the actual step 49442 * 49443 * @param {Node.id} nodeId 49444 * @returns {number} the new velocity of given node 49445 * @private 49446 */ 49447 49448 }, { 49449 key: '_performStep', 49450 value: function _performStep(nodeId) { 49451 var node = this.body.nodes[nodeId]; 49452 var force = this.physicsBody.forces[nodeId]; 49453 var velocity = this.physicsBody.velocities[nodeId]; 49454 49455 // store the state so we can revert 49456 this.previousStates[nodeId] = { x: node.x, y: node.y, vx: velocity.x, vy: velocity.y }; 49457 49458 if (node.options.fixed.x === false) { 49459 velocity.x = this.calculateComponentVelocity(velocity.x, force.x, node.options.mass); 49460 node.x += velocity.x * this.timestep; 49461 } else { 49462 force.x = 0; 49463 velocity.x = 0; 49464 } 49465 49466 if (node.options.fixed.y === false) { 49467 velocity.y = this.calculateComponentVelocity(velocity.y, force.y, node.options.mass); 49468 node.y += velocity.y * this.timestep; 49469 } else { 49470 force.y = 0; 49471 velocity.y = 0; 49472 } 49473 49474 var totalVelocity = Math.sqrt(Math.pow(velocity.x, 2) + Math.pow(velocity.y, 2)); 49475 return totalVelocity; 49476 } 49477 49478 /** 49479 * When initializing and stabilizing, we can freeze nodes with a predefined position. 49480 * This greatly speeds up stabilization because only the supportnodes for the smoothCurves have to settle. 49481 * 49482 * @private 49483 */ 49484 49485 }, { 49486 key: '_freezeNodes', 49487 value: function _freezeNodes() { 49488 var nodes = this.body.nodes; 49489 for (var id in nodes) { 49490 if (nodes.hasOwnProperty(id)) { 49491 if (nodes[id].x && nodes[id].y) { 49492 var fixed = nodes[id].options.fixed; 49493 this.freezeCache[id] = { x: fixed.x, y: fixed.y }; 49494 fixed.x = true; 49495 fixed.y = true; 49496 } 49497 } 49498 } 49499 } 49500 49501 /** 49502 * Unfreezes the nodes that have been frozen by _freezeDefinedNodes. 49503 * 49504 * @private 49505 */ 49506 49507 }, { 49508 key: '_restoreFrozenNodes', 49509 value: function _restoreFrozenNodes() { 49510 var nodes = this.body.nodes; 49511 for (var id in nodes) { 49512 if (nodes.hasOwnProperty(id)) { 49513 if (this.freezeCache[id] !== undefined) { 49514 nodes[id].options.fixed.x = this.freezeCache[id].x; 49515 nodes[id].options.fixed.y = this.freezeCache[id].y; 49516 } 49517 } 49518 } 49519 this.freezeCache = {}; 49520 } 49521 49522 /** 49523 * Find a stable position for all nodes 49524 * 49525 * @param {number} [iterations=this.options.stabilization.iterations] 49526 */ 49527 49528 }, { 49529 key: 'stabilize', 49530 value: function stabilize() { 49531 var _this3 = this; 49532 49533 var iterations = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : this.options.stabilization.iterations; 49534 49535 if (typeof iterations !== 'number') { 49536 iterations = this.options.stabilization.iterations; 49537 console.log('The stabilize method needs a numeric amount of iterations. Switching to default: ', iterations); 49538 } 49539 49540 if (this.physicsBody.physicsNodeIndices.length === 0) { 49541 this.ready = true; 49542 return; 49543 } 49544 49545 // enable adaptive timesteps 49546 this.adaptiveTimestep = true && this.options.adaptiveTimestep; 49547 49548 // this sets the width of all nodes initially which could be required for the avoidOverlap 49549 this.body.emitter.emit("_resizeNodes"); 49550 49551 this.stopSimulation(); // stop the render loop 49552 this.stabilized = false;
49553 49554 // block redraw requests 49555 this.body.emitter.emit('_blockRedraw'); 49556 this.targetIterations = iterations; 49557 49558 // start the stabilization 49559 if (this.options.stabilization.onlyDynamicEdges === true) { 49560 this._freezeNodes(); 49561 } 49562 this.stabilizationIterations = 0; 49563 49564 setTimeout(function () { 49565 return _this3._stabilizationBatch(); 49566 }, 0); 49567 } 49568 49569 /** 49570 * If not already stabilizing, start it and emit a start event. 49571 * 49572 * @returns {boolean} true if stabilization started with this call 49573 * @private 49574 */ 49575 49576 }, { 49577 key: '_startStabilizing', 49578 value: function _startStabilizing() { 49579 if (this.startedStabilization === true) return false; 49580 49581 this.body.emitter.emit('startStabilizing'); 49582 this.startedStabilization = true; 49583 return true; 49584 } 49585 49586 /** 49587 * One batch of stabilization 49588 * @private 49589 */ 49590 49591 }, { 49592 key: '_stabilizationBatch', 49593 value: function _stabilizationBatch() { 49594 var _this4 = this; 49595 49596 var running = function running() { 49597 return _this4.stabilized === false && _this4.stabilizationIterations < _this4.targetIterations; 49598 }; 49599 49600 var sendProgress = function sendProgress() { 49601 _this4.body.emitter.emit('stabilizationProgress', { 49602 iterations: _this4.stabilizationIterations, 49603 total: _this4.targetIterations 49604 }); 49605 }; 49606 49607 if (this._startStabilizing()) { 49608 sendProgress(); // Ensure that there is at least one start event. 49609 } 49610 49611 var count = 0; 49612 while (running() && count < this.options.stabilization.updateInterval) { 49613 this.physicsTick(); 49614 count++; 49615 } 49616 49617 sendProgress(); 49618 49619 if (running()) { 49620 setTimeout(this._stabilizationBatch.bind(this), 0); 49621 } else { 49622 this._finalizeStabilization(); 49623 } 49624 } 49625 49626 /** 49627 * Wrap up the stabilization, fit and emit the events. 49628 * @private 49629 */ 49630 49631 }, { 49632 key: '_finalizeStabilization', 49633 value: function _finalizeStabilization() { 49634 this.body.emitter.emit('_allowRedraw'); 49635 if (this.options.stabilization.fit === true) { 49636 this.body.emitter.emit('fit'); 49637 } 49638 49639 if (this.options.stabilization.onlyDynamicEdges === true) { 49640 this._restoreFrozenNodes(); 49641 } 49642 49643 this.body.emitter.emit('stabilizationIterationsDone'); 49644 this.body.emitter.emit('_requestRedraw'); 49645 49646 if (this.stabilized === true) { 49647 this._emitStabilized(); 49648 } else { 49649 this.startSimulation(); 49650 } 49651 49652 this.ready = true; 49653 } 49654 49655 //--------------------------- DEBUGGING BELOW ---------------------------// 49656 49657 49658 /** 49659 * Debug function that display arrows for the forces currently active in the network. 49660 * 49661 * Use this when debugging only. 49662 * 49663 * @param {CanvasRenderingContext2D} ctx 49664 * @private 49665 */ 49666 49667 }, { 49668 key: '_drawForces', 49669 value: function _drawForces(ctx) { 49670 for (var i = 0; i < this.physicsBody.physicsNodeIndices.length; i++) { 49671 var index = this.physicsBody.physicsNodeIndices[i]; 49672 var node = this.body.nodes[index]; 49673 var force = this.physicsBody.forces[index]; 49674 var factor = 20; 49675 var colorFactor = 0.03; 49676 var forceSize = Math.sqrt(Math.pow(force.x, 2) + Math.pow(force.x, 2)); 49677 49678 var size = Math.min(Math.max(5, forceSize), 15); 49679 var arrowSize = 3 * size; 49680 49681 var color = util.HSVToHex((180 - Math.min(1, Math.max(0, colorFactor * forceSize)) * 180) / 360, 1, 1); 49682 49683 var point = { 49684 x: node.x + factor * force.x, 49685 y: node.y + factor * force.y 49686 }; 49687 49688 ctx.lineWidth = size; 49689 ctx.strokeStyle = color; 49690 ctx.beginPath(); 49691 ctx.moveTo(node.x, node.y); 49692 ctx.lineTo(point.x, point.y); 49693 ctx.stroke(); 49694 49695 var angle = Math.atan2(force.y, force.x); 49696 ctx.fillStyle = color; 49697 EndPoints.draw(ctx, { type: 'arrow', point: point, angle: angle, length: arrowSize }); 49698 ctx.fill(); 49699 } 49700 } 49701 }]); 49702 return PhysicsEngine; 49703}(); 49704 49705exports['default'] = PhysicsEngine; 49706 49707/***/ }), 49708/* 221 */ 49709/***/ (function(module, exports, __webpack_require__) { 49710 49711"use strict"; 49712 49713 49714Object.defineProperty(exports, "__esModule", { 49715 value: true 49716}); 49717 49718var _classCallCheck2 = __webpack_require__(0); 49719 49720var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); 49721 49722var _createClass2 = __webpack_require__(1); 49723 49724var _createClass3 = _interopRequireDefault(_createClass2); 49725 49726function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } 49727 49728/** 49729 * Repulsion Solver 49730 */ 49731var RepulsionSolver = function () { 49732 /** 49733 * @param {Object} body 49734 * @param {{physicsNodeIndices: Array, physicsEdgeIndices: Array, forces: {}, velocities: {}}} physicsBody 49735 * @param {Object} options 49736 */ 49737 function RepulsionSolver(body, physicsBody, options) { 49738 (0, _classCallCheck3["default"])(this, RepulsionSolver); 49739 49740 this.body = body; 49741 this.physicsBody = physicsBody; 49742 this.setOptions(options); 49743 } 49744 49745 /** 49746 * 49747 * @param {Object} options 49748 */ 49749 49750 49751 (0, _createClass3["default"])(RepulsionSolver, [{ 49752 key: "setOptions", 49753 value: function setOptions(options) { 49754 this.options = options; 49755 } 49756 49757 /** 49758 * Calculate the forces the nodes apply on each other based on a repulsion field. 49759 * This field is linearly approximated. 49760 * 49761 * @private 49762 */ 49763 49764 }, { 49765 key: "solve", 49766 value: function solve() { 49767 var dx, dy, distance, fx, fy, repulsingForce, node1, node2; 49768 49769 var nodes = this.body.nodes; 49770 var nodeIndices = this.physicsBody.physicsNodeIndices; 49771 var forces = this.physicsBody.forces; 49772 49773 // repulsing forces between nodes 49774 var nodeDistance = this.options.nodeDistance; 49775 49776 // approximation constants 49777 var a = -2 / 3 / nodeDistance; 49778 var b = 4 / 3; 49779 49780 // we loop from i over all but the last entree in the array 49781 // j loops from i+1 to the last. This way we do not double count any of the indices, nor i === j 49782 for (var i = 0; i < nodeIndices.length - 1; i++) { 49783 node1 = nodes[nodeIndices[i]]; 49784 for (var j = i + 1; j < nodeIndices.length; j++) { 49785 node2 = nodes[nodeIndices[j]]; 49786 49787 dx = node2.x - node1.x; 49788 dy = node2.y - node1.y; 49789 distance = Math.sqrt(dx * dx + dy * dy); 49790 49791 // same condition as BarnesHutSolver, making sure nodes are never 100% overlapping. 49792 if (distance === 0) { 49793 distance = 0.1 * Math.random(); 49794 dx = distance; 49795 } 49796 49797 if (distance < 2 * nodeDistance) { 49798 if (distance < 0.5 * nodeDistance) { 49799 repulsingForce = 1.0; 49800 } else { 49801 repulsingForce = a * distance + b; // linear approx of 1 / (1 + Math.exp((distance / nodeDistance - 1) * steepne
49801ss)) 49802 } 49803 repulsingForce = repulsingForce / distance; 49804 49805 fx = dx * repulsingForce; 49806 fy = dy * repulsingForce; 49807 49808 forces[node1.id].x -= fx; 49809 forces[node1.id].y -= fy; 49810 forces[node2.id].x += fx; 49811 forces[node2.id].y += fy; 49812 } 49813 } 49814 } 49815 } 49816 }]); 49817 return RepulsionSolver; 49818}(); 49819 49820exports["default"] = RepulsionSolver; 49821 49822/***/ }), 49823/* 222 */ 49824/***/ (function(module, exports, __webpack_require__) { 49825 49826"use strict"; 49827 49828 49829Object.defineProperty(exports, "__esModule", { 49830 value: true 49831}); 49832 49833var _classCallCheck2 = __webpack_require__(0); 49834 49835var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); 49836 49837var _createClass2 = __webpack_require__(1); 49838 49839var _createClass3 = _interopRequireDefault(_createClass2); 49840 49841function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } 49842 49843/** 49844 * Hierarchical Repulsion Solver 49845 */ 49846var HierarchicalRepulsionSolver = function () { 49847 /** 49848 * @param {Object} body 49849 * @param {{physicsNodeIndices: Array, physicsEdgeIndices: Array, forces: {}, velocities: {}}} physicsBody 49850 * @param {Object} options 49851 */ 49852 function HierarchicalRepulsionSolver(body, physicsBody, options) { 49853 (0, _classCallCheck3["default"])(this, HierarchicalRepulsionSolver); 49854 49855 this.body = body; 49856 this.physicsBody = physicsBody; 49857 this.setOptions(options); 49858 } 49859 49860 /** 49861 * 49862 * @param {Object} options 49863 */ 49864 49865 49866 (0, _createClass3["default"])(HierarchicalRepulsionSolver, [{ 49867 key: "setOptions", 49868 value: function setOptions(options) { 49869 this.options = options; 49870 } 49871 49872 /** 49873 * Calculate the forces the nodes apply on each other based on a repulsion field. 49874 * This field is linearly approximated. 49875 * 49876 * @private 49877 */ 49878 49879 }, { 49880 key: "solve", 49881 value: function solve() { 49882 var dx, dy, distance, fx, fy, repulsingForce, node1, node2, i, j; 49883 49884 var nodes = this.body.nodes; 49885 var nodeIndices = this.physicsBody.physicsNodeIndices; 49886 var forces = this.physicsBody.forces; 49887 49888 // repulsing forces between nodes 49889 var nodeDistance = this.options.nodeDistance; 49890 49891 // we loop from i over all but the last entree in the array 49892 // j loops from i+1 to the last. This way we do not double count any of the indices, nor i === j 49893 for (i = 0; i < nodeIndices.length - 1; i++) { 49894 node1 = nodes[nodeIndices[i]]; 49895 for (j = i + 1; j < nodeIndices.length; j++) { 49896 node2 = nodes[nodeIndices[j]]; 49897 49898 // nodes only affect nodes on their level 49899 if (node1.level === node2.level) { 49900 dx = node2.x - node1.x; 49901 dy = node2.y - node1.y; 49902 distance = Math.sqrt(dx * dx + dy * dy); 49903 49904 var steepness = 0.05; 49905 if (distance < nodeDistance) { 49906 repulsingForce = -Math.pow(steepness * distance, 2) + Math.pow(steepness * nodeDistance, 2); 49907 } else { 49908 repulsingForce = 0; 49909 } 49910 // normalize force with 49911 if (distance === 0) { 49912 distance = 0.01; 49913 } else { 49914 repulsingForce = repulsingForce / distance; 49915 } 49916 fx = dx * repulsingForce; 49917 fy = dy * repulsingForce; 49918 49919 forces[node1.id].x -= fx; 49920 forces[node1.id].y -= fy; 49921 forces[node2.id].x += fx; 49922 forces[node2.id].y += fy; 49923 } 49924 } 49925 } 49926 } 49927 }]); 49928 return HierarchicalRepulsionSolver; 49929}(); 49930 49931exports["default"] = HierarchicalRepulsionSolver; 49932 49933/***/ }), 49934/* 223 */ 49935/***/ (function(module, exports, __webpack_require__) { 49936 49937"use strict"; 49938 49939 49940Object.defineProperty(exports, "__esModule", { 49941 value: true 49942}); 49943 49944var _classCallCheck2 = __webpack_require__(0); 49945 49946var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); 49947 49948var _createClass2 = __webpack_require__(1); 49949 49950var _createClass3 = _interopRequireDefault(_createClass2); 49951 49952function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } 49953 49954/** 49955 * Spring Solver 49956 */ 49957var SpringSolver = function () { 49958 /** 49959 * @param {Object} body 49960 * @param {{physicsNodeIndices: Array, physicsEdgeIndices: Array, forces: {}, velocities: {}}} physicsBody 49961 * @param {Object} options 49962 */ 49963 function SpringSolver(body, physicsBody, options) { 49964 (0, _classCallCheck3["default"])(this, SpringSolver); 49965 49966 this.body = body; 49967 this.physicsBody = physicsBody; 49968 this.setOptions(options); 49969 } 49970 49971 /** 49972 * 49973 * @param {Object} options 49974 */ 49975 49976 49977 (0, _createClass3["default"])(SpringSolver, [{ 49978 key: "setOptions", 49979 value: function setOptions(options) { 49980 this.options = options; 49981 } 49982 49983 /** 49984 * This function calculates the springforces on the nodes, accounting for the support nodes. 49985 * 49986 * @private 49987 */ 49988 49989 }, { 49990 key: "solve", 49991 value: function solve() { 49992 var edgeLength = void 0, 49993 edge = void 0; 49994 var edgeIndices = this.physicsBody.physicsEdgeIndices; 49995 var edges = this.body.edges; 49996 var node1 = void 0, 49997 node2 = void 0, 49998 node3 = void 0; 49999 50000 // forces caused by the edges, modelled as springs 50001 for (var i = 0; i < edgeIndices.length; i++) { 50002 edge = edges[edgeIndices[i]]; 50003 if (edge.connected === true && edge.toId !== edge.fromId) { 50004 // only calculate forces if nodes are in the same sector 50005 if (this.body.nodes[edge.toId] !== undefined && this.body.nodes[edge.fromId] !== undefined) { 50006 if (edge.edgeType.via !== undefined) { 50007 edgeLength = edge.options.length === undefined ? this.options.springLength : edge.options.length; 50008 node1 = edge.to; 50009 node2 = edge.edgeType.via; 50010 node3 = edge.from; 50011 50012 this._calculateSpringForce(node1, node2, 0.5 * edgeLength); 50013 this._calculateSpringForce(node2, node3, 0.5 * edgeLength); 50014 } else { 50015 // the * 1.5 is here so the edge looks as large as a smooth edge. It does not initially because the smooth edges use 50016 // the support nodes which exert a repulsive force on the to and from nodes, making the edge appear larger. 50017 edgeLength = edge.options.length === undefined ? this.options.springLength * 1.5 : edge.options.length; 50018 this._calculateSpringForce(edge.from, edge.to, edgeLength); 50019 } 50020 } 50021 } 50022 } 50023 } 50024 50025 /** 50026 * This is the code actually performing the calculation for the function above. 50027 * 50028 * @param {Node} node1 50029 * @param {Node} node2 50030 * @param {number} edgeLength 50031 * @private 50032 */ 50033 50034 }, { 50035 key: "_calculateSpringForce", 50036 value: function _calculateSpringForce(node1, node2, edgeLength) { 50037 var dx = node1.x - node2.x; 50038 var dy = node1.y - node2.y; 50039 var distance = Math.max(Math.sqrt(dx * dx + dy * dy), 0.01); 50040 50041 // the 1/distance is so the fx and fy can be calculated without sine or cosine. 50042 var springForce = this.options.springConstant * (edgeLength - distance) / distance; 50043
50044 var fx = dx * springForce; 50045 var fy = dy * springForce; 50046 50047 // handle the case where one node is not part of the physcis 50048 if (this.physicsBody.forces[node1.id] !== undefined) { 50049 this.physicsBody.forces[node1.id].x += fx; 50050 this.physicsBody.forces[node1.id].y += fy; 50051 } 50052 50053 if (this.physicsBody.forces[node2.id] !== undefined) { 50054 this.physicsBody.forces[node2.id].x -= fx; 50055 this.physicsBody.forces[node2.id].y -= fy; 50056 } 50057 } 50058 }]); 50059 return SpringSolver; 50060}(); 50061 50062exports["default"] = SpringSolver; 50063 50064/***/ }), 50065/* 224 */ 50066/***/ (function(module, exports, __webpack_require__) { 50067 50068"use strict"; 50069 50070 50071Object.defineProperty(exports, "__esModule", { 50072 value: true 50073}); 50074 50075var _classCallCheck2 = __webpack_require__(0); 50076 50077var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); 50078 50079var _createClass2 = __webpack_require__(1); 50080 50081var _createClass3 = _interopRequireDefault(_createClass2); 50082 50083function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } 50084 50085/** 50086 * Hierarchical Spring Solver 50087 */ 50088var HierarchicalSpringSolver = function () { 50089 /** 50090 * @param {Object} body 50091 * @param {{physicsNodeIndices: Array, physicsEdgeIndices: Array, forces: {}, velocities: {}}} physicsBody 50092 * @param {Object} options 50093 */ 50094 function HierarchicalSpringSolver(body, physicsBody, options) { 50095 (0, _classCallCheck3["default"])(this, HierarchicalSpringSolver); 50096 50097 this.body = body; 50098 this.physicsBody = physicsBody; 50099 this.setOptions(options); 50100 } 50101 50102 /** 50103 * 50104 * @param {Object} options 50105 */ 50106 50107 50108 (0, _createClass3["default"])(HierarchicalSpringSolver, [{ 50109 key: "setOptions", 50110 value: function setOptions(options) { 50111 this.options = options; 50112 } 50113 50114 /** 50115 * This function calculates the springforces on the nodes, accounting for the support nodes. 50116 * 50117 * @private 50118 */ 50119 50120 }, { 50121 key: "solve", 50122 value: function solve() { 50123 var edgeLength, edge; 50124 var dx, dy, fx, fy, springForce, distance; 50125 var edges = this.body.edges; 50126 var factor = 0.5; 50127 50128 var edgeIndices = this.physicsBody.physicsEdgeIndices; 50129 var nodeIndices = this.physicsBody.physicsNodeIndices; 50130 var forces = this.physicsBody.forces; 50131 50132 // initialize the spring force counters 50133 for (var i = 0; i < nodeIndices.length; i++) { 50134 var nodeId = nodeIndices[i]; 50135 forces[nodeId].springFx = 0; 50136 forces[nodeId].springFy = 0; 50137 } 50138 50139 // forces caused by the edges, modelled as springs 50140 for (var _i = 0; _i < edgeIndices.length; _i++) { 50141 edge = edges[edgeIndices[_i]]; 50142 if (edge.connected === true) { 50143 edgeLength = edge.options.length === undefined ? this.options.springLength : edge.options.length; 50144 50145 dx = edge.from.x - edge.to.x; 50146 dy = edge.from.y - edge.to.y; 50147 distance = Math.sqrt(dx * dx + dy * dy); 50148 distance = distance === 0 ? 0.01 : distance; 50149 50150 // the 1/distance is so the fx and fy can be calculated without sine or cosine. 50151 springForce = this.options.springConstant * (edgeLength - distance) / distance; 50152
50153 fx = dx * springForce; 50154 fy = dy * springForce; 50155 50156 if (edge.to.level != edge.from.level) { 50157 if (forces[edge.toId] !== undefined) { 50158 forces[edge.toId].springFx -= fx; 50159 forces[edge.toId].springFy -= fy; 50160 } 50161 if (forces[edge.fromId] !== undefined) { 50162 forces[edge.fromId].springFx += fx; 50163 forces[edge.fromId].springFy += fy; 50164 } 50165 } else { 50166 if (forces[edge.toId] !== undefined) { 50167 forces[edge.toId].x -= factor * fx; 50168 forces[edge.toId].y -= factor * fy; 50169 } 50170 if (forces[edge.fromId] !== undefined) { 50171 forces[edge.fromId].x += factor * fx; 50172 forces[edge.fromId].y += factor * fy; 50173 } 50174 } 50175 } 50176 } 50177 50178 // normalize spring forces 50179 springForce = 1; 50180 var springFx, springFy; 50181 for (var _i2 = 0; _i2 < nodeIndices.length; _i2++) { 50182 var _nodeId = nodeIndices[_i2]; 50183 springFx = Math.min(springForce, Math.max(-springForce, forces[_nodeId].springFx)); 50184 springFy = Math.min(springForce, Math.max(-springForce, forces[_nodeId].springFy)); 50185 50186 forces[_nodeId].x += springFx; 50187 forces[_nodeId].y += springFy; 50188 } 50189 50190 // retain energy balance 50191 var totalFx = 0; 50192 var totalFy = 0; 50193 for (var _i3 = 0; _i3 < nodeIndices.length; _i3++) { 50194 var _nodeId2 = nodeIndices[_i3]; 50195 totalFx += forces[_nodeId2].x; 50196 totalFy += forces[_nodeId2].y; 50197 } 50198 var correctionFx = totalFx / nodeIndices.length; 50199 var correctionFy = totalFy / nodeIndices.length; 50200 50201 for (var _i4 = 0; _i4 < nodeIndices.length; _i4++) { 50202 var _nodeId3 = nodeIndices[_i4]; 50203 forces[_nodeId3].x -= correctionFx; 50204 forces[_nodeId3].y -= correctionFy; 50205 } 50206 } 50207 }]); 50208 return HierarchicalSpringSolver; 50209}(); 50210 50211exports["default"] = HierarchicalSpringSolver; 50212 50213/***/ }), 50214/* 225 */ 50215/***/ (function(module, exports, __webpack_require__) { 50216 50217"use strict"; 50218 50219 50220Object.defineProperty(exports, "__esModule", { 50221 value: true 50222}); 50223 50224var _getPrototypeOf = __webpack_require__(3); 50225 50226var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf); 50227 50228var _classCallCheck2 = __webpack_require__(0); 50229 50230var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); 50231 50232var _createClass2 = __webpack_require__(1); 50233 50234var _createClass3 = _interopRequireDefault(_createClass2); 50235 50236var _possibleConstructorReturn2 = __webpack_require__(4); 50237 50238var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2); 50239 50240var _inherits2 = __webpack_require__(5); 50241 50242var _inherits3 = _interopRequireDefault(_inherits2); 50243 50244var _BarnesHutSolver2 = __webpack_require__(120); 50245 50246var _BarnesHutSolver3 = _interopRequireDefault(_BarnesHutSolver2); 50247 50248function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } 50249 50250/** 50251 * @extends BarnesHutSolver 50252 */ 50253var ForceAtlas2BasedRepulsionSolver = function (_BarnesHutSolver) { 50254 (0, _inherits3["default"])(ForceAtlas2BasedRepulsionSolver, _BarnesHutSolver); 50255 50256 /** 50257 * @param {Object} body 50258 * @param {{physicsNodeIndices: Array, physicsEdgeIndices: Array, forces: {}, velocities: {}}} physicsBody 50259 * @param {Object} options 50260 */ 50261 function ForceAtlas2BasedRepulsionSolver(body, physicsBody, options) { 50262 (0, _classCallCheck3["default"])(this, ForceAtlas2BasedRepulsionSolver); 50263 return (0, _possibleConstructorReturn3["default"])(this, (ForceAtlas2BasedRepulsionSolver.__proto__ || (0, _getPrototypeOf2["default"])(ForceAtlas2BasedRepulsionSolver)).call(this, body, physicsBody, options)); 50264 } 50265 50266 /** 50267 * Calculate the forces based on the distance. 50268 * 50269 * @param {number} distance 50270 * @param {number} dx 50271 * @param {number} dy 50272 * @param {Node} node 50273 * @param {Object} parentBranch 50274 * @private 50275 */ 50276 50277 50278 (0, _createClass3["default"])(ForceAtlas2BasedRepulsionSolver, [{ 50279 key: "_calculateForces", 50280 value: function _calculateForces(distance, dx, dy, node, parentBranch) { 50281 if (distance === 0) { 50282 distance = 0.1 * Math.random(); 50283 dx = distance; 50284 } 50285
50286 if (this.overlapAvoidanceFactor < 1 && node.shape.radius) { 50287 distance = Math.max(0.1 + this.overlapAvoidanceFactor * node.shape.radius, distance - node.shape.radius); 50288 } 50289 50290 var degree = node.edges.length + 1; 50291 // the dividing by the distance cubed instead of squared allows us to get the fx and fy components without sines and cosines 50292 // it is shorthand for gravityforce with distance squared and fx = dx/distance * gravityForce 50293 var gravityForce = this.options.gravitationalConstant * parentBranch.mass * node.options.mass * degree / Math.pow(distance, 2); 50294 var fx = dx * gravityForce; 50295 var fy = dy * gravityForce; 50296 50297 this.physicsBody.forces[node.id].x += fx; 50298 this.physicsBody.forces[node.id].y += fy; 50299 } 50300 }]); 50301 return ForceAtlas2BasedRepulsionSolver; 50302}(_BarnesHutSolver3["default"]); 50303 50304exports["default"] = ForceAtlas2BasedRepulsionSolver; 50305 50306/***/ }), 50307/* 226 */ 50308/***/ (function(module, exports, __webpack_require__) { 50309 50310"use strict"; 50311 50312 50313Object.defineProperty(exports, "__esModule", { 50314 value: true 50315}); 50316 50317var _getPrototypeOf = __webpack_require__(3); 50318 50319var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf); 50320 50321var _classCallCheck2 = __webpack_require__(0); 50322 50323var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); 50324 50325var _createClass2 = __webpack_require__(1); 50326 50327var _createClass3 = _interopRequireDefault(_createClass2); 50328 50329var _possibleConstructorReturn2 = __webpack_require__(4); 50330 50331var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2); 50332 50333var _inherits2 = __webpack_require__(5); 50334 50335var _inherits3 = _interopRequireDefault(_inherits2); 50336 50337var _CentralGravitySolver2 = __webpack_require__(121); 50338 50339var _CentralGravitySolver3 = _interopRequireDefault(_CentralGravitySolver2); 50340 50341function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } 50342 50343/** 50344 * @extends CentralGravitySolver 50345 */ 50346var ForceAtlas2BasedCentralGravitySolver = function (_CentralGravitySolver) { 50347 (0, _inherits3["default"])(ForceAtlas2BasedCentralGravitySolver, _CentralGravitySolver); 50348 50349 /** 50350 * @param {Object} body 50351 * @param {{physicsNodeIndices: Array, physicsEdgeIndices: Array, forces: {}, velocities: {}}} physicsBody 50352 * @param {Object} options 50353 */ 50354 function ForceAtlas2BasedCentralGravitySolver(body, physicsBody, options) { 50355 (0, _classCallCheck3["default"])(this, ForceAtlas2BasedCentralGravitySolver); 50356 return (0, _possibleConstructorReturn3["default"])(this, (ForceAtlas2BasedCentralGravitySolver.__proto__ || (0, _getPrototypeOf2["default"])(ForceAtlas2BasedCentralGravitySolver)).call(this, body, physicsBody, options)); 50357 } 50358 50359 /** 50360 * Calculate the forces based on the distance. 50361 * 50362 * @param {number} distance 50363 * @param {number} dx 50364 * @param {number} dy 50365 * @param {Object<Node.id, Node>} forces 50366 * @param {Node} node 50367 * @private 50368 */ 50369 50370 50371 (0, _createClass3["default"])(ForceAtlas2BasedCentralGravitySolver, [{ 50372 key: "_calculateForces", 50373 value: function _calculateForces(distance, dx, dy, forces, node) { 50374 if (distance > 0) { 50375 var degree = node.edges.length + 1; 50376 var gravityForce = this.options.centralGravity * degree * node.options.mass; 50377 forces[node.id].x = dx * gravityForce; 50378 forces[node.id].y = dy * gravityForce; 50379 } 50380 } 50381 }]); 50382 return ForceAtlas2BasedCentralGravitySolver; 50383}(_CentralGravitySolver3["default"]); 50384 50385exports["default"] = ForceAtlas2BasedCentralGravitySolver; 50386 50387/***/ }), 50388/* 227 */ 50389/***/ (function(module, exports, __webpack_require__) { 50390 50391"use strict"; 50392 50393 50394Object.defineProperty(exports, "__esModule", { 50395 value: true 50396}); 50397 50398var _keys = __webpack_require__(8); 50399 50400var _keys2 = _interopRequireDefault(_keys); 50401 50402var _typeof2 = __webpack_require__(6); 50403 50404var _typeof3 = _interopRequireDefault(_typeof2); 50405 50406var _classCallCheck2 = __webpack_require__(0); 50407 50408var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); 50409 50410var _createClass2 = __webpack_require__(1); 50411 50412var _createClass3 = _interopRequireDefault(_createClass2); 50413 50414function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 50415 50416/* =========================================================================== 50417 50418# TODO 50419 50420- `edgeReplacedById` not cleaned up yet on cluster edge removal 50421- allowSingleNodeCluster could be a global option as well; currently needs to always 50422 be passed to clustering methods 50423 50424---------------------------------------------- 50425 50426# State Model for Clustering 50427 50428The total state for clustering is non-trivial. It is useful to have a model 50429available as to how it works. The following documents the relevant state items. 50430 50431 50432## Network State 50433
50434The following `network`-members are relevant to clustering: 50435 50436- `body.nodes` - all nodes actively participating in the network 50437- `body.edges` - same for edges 50438- `body.nodeIndices` - id's of nodes that are visible at a given moment 50439- `body.edgeIndices` - same for edges 50440 50441This includes: 50442 50443- helper nodes for dragging in `manipulation` 50444- helper nodes for edge type `dynamic` 50445- cluster nodes and edges 50446- there may be more than this. 50447 50448A node/edge may be missing in the `Indices` member if: 50449 50450- it is a helper node 50451- the node or edge state has option `hidden` set 50452- It is not visible due to clustering 50453 50454 50455## Clustering State 50456 50457For the hashes, the id's of the nodes/edges are used as key. 50458 50459Member `network.clustering` contains the following items: 50460 50461- `clusteredNodes` - hash with values: { clusterId: <id of cluster>, node: <node instance>} 50462- `clusteredEdges` - hash with values: restore information for given edge 50463 50464 50465Due to nesting of clusters, these members can contain cluster nodes and edges as well. 50466 50467The important thing to note here, is that the clustered nodes and edges also 50468appear in the members of the cluster nodes. For data update, it is therefore 50469important to scan these lists as well as the cluster nodes. 50470 50471 50472### Cluster Node 50473 50474A cluster node has the following extra fields: 50475 50476- `isCluster : true` - indication that this is a cluster node 50477- `containedNodes` - hash of nodes contained in this cluster 50478- `containedEdges` - same for edges 50479- `edges` - array of cluster edges for this node 50480 50481 50482**NOTE:** 50483 50484- `containedEdges` can also contain edges which are not clustered; e.g. an edge 50485 connecting two nodes in the same cluster. 50486 50487 50488### Cluster Edge 50489 50490These are the items in the `edges` member of a clustered node. They have the 50491following relevant members: 50492 50493- 'clusteringEdgeReplacingIds` - array of id's of edges replaced by this edge 50494 50495Note that it's possible to nest clusters, so that `clusteringEdgeReplacingIds` 50496can contain edge id's of other clusters. 50497 50498 50499### Clustered Edge 50500 50501This is any edge contained by a cluster edge. It gets the following additional 50502member: 50503 50504- `edgeReplacedById` - id of the cluster edge in which current edge is clustered 50505 50506 50507 =========================================================================== */ 50508var util = __webpack_require__(2); 50509var NetworkUtil = __webpack_require__(76)['default']; 50510var Cluster = __webpack_require__(228)['default']; 50511var Edge = __webpack_require__(74)['default']; // Only needed for check on type! 50512var Node = __webpack_require__(47)['default']; // Only needed for check on type! 50513 50514/** 50515 * The clustering engine 50516 */ 50517 50518var ClusterEngine = function () { 50519 /** 50520 * @param {Object} body 50521 */ 50522 function ClusterEngine(body) { 50523 var _this = this; 50524 50525 (0, _classCallCheck3['default'])(this, ClusterEngine); 50526 50527 this.body = body; 50528 this.clusteredNodes = {}; // key: node id, value: { clusterId: <id of cluster>, node: <node instance>} 50529 this.clusteredEdges = {}; // key: edge id, value: restore information for given edge 50530 50531 this.options = {}; 50532 this.defaultOptions = {}; 50533 util.extend(this.options, this.defaultOptions); 50534 50535 this.body.emitter.on('_resetData', function () { 50536 _this.clusteredNodes = {};_this.clusteredEdges = {}; 50537 }); 50538 } 50539 50540 /** 50541 * 50542 * @param {number} hubsize 50543 * @param {Object} options 50544 */ 50545 50546 50547 (0, _createClass3['default'])(ClusterEngine, [{ 50548 key: 'clusterByHubsize', 50549 value: function clusterByHubsize(hubsize, options) { 50550 if (hubsize === undefined) { 50551 hubsize = this._getHubSize(); 50552 } else if ((typeof hubsize === 'undefined' ? 'undefined' : (0, _typeof3['default'])(hubsize)) === "object") { 50553 options = this._checkOptions(hubsize); 50554 hubsize = this._getHubSize(); 50555 } 50556 50557 var nodesToCluster = []; 50558 for (var i = 0; i < this.body.nodeIndices.length; i++) { 50559 var node = this.body.nodes[this.body.nodeIndices[i]]; 50560 if (node.edges.length >= hubsize) { 50561 nodesToCluster.push(node.id); 50562 } 50563 } 50564 50565 for (var _i = 0; _i < nodesToCluster.length; _i++) { 50566 this.clusterByConnection(nodesToCluster[_i], options, true); 50567 } 50568 50569 this.body.emitter.emit('_dataChanged'); 50570 } 50571 50572 /** 50573 * loop over all nodes, check if they adhere to the condition and cluster if needed. 50574 * @param {Object} options 50575 * @param {boolean} [refreshData=true] 50576 */ 50577 50578 }, { 50579 key: 'cluster', 50580 value: function cluster() { 50581 var _this2 = this; 50582 50583 var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; 50584 var refreshData = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true; 50585 50586 if (options.joinCondition === undefined) { 50587 throw new Error("Cannot call clusterByNodeData without a joinCondition function in the options."); 50588 } 50589 50590 // check if the options object is fine, append if needed 50591 options = this._checkOptions(options); 50592 50593 var childNodesObj = {}; 50594 var childEdgesObj = {}; 50595 50596 // collect the nodes that will be in the cluster 50597 util.forEach(this.body.nodes, function (node, nodeId) { 50598 var clonedOptions = NetworkUtil.cloneOptions(node); 50599 if (options.joinCondition(clonedOptions) === true) { 50600 childNodesObj[nodeId] = node; 50601 50602 // collect the edges that will be in the cluster 50603 util.forEach(node.edges, function (edge) { 50604 if (_this2.clusteredEdges[edge.id] === undefined) { 50605 childEdgesObj[edge.id] = edge; 50606 } 50607 }); 50608 } 50609 }); 50610 50611 this._cluster(childNodesObj, childEdgesObj, options, refreshData); 50612 } 50613 50614 /** 50615 * Cluster all nodes in the network that have only X edges 50616 * @param {number} edgeCount 50617 * @param {Object} options 50618 * @param {boolean} [refreshData=true] 50619 */ 50620 50621 }, { 50622 key: 'clusterByEdgeCount', 50623 value: function clusterByEdgeCount(edgeCount, options) { 50624 var _this3 = this; 50625 50626 var refreshData = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true; 50627 50628 options = this._checkOptions(options);
50629 var clusters = []; 50630 var usedNodes = {}; 50631 var edge = void 0, 50632 edges = void 0, 50633 relevantEdgeCount = void 0; 50634 // collect the nodes that will be in the cluster 50635 50636 var _loop = function _loop(i) { 50637 var childNodesObj = {}; 50638 var childEdgesObj = {}; 50639 var nodeId = _this3.body.nodeIndices[i]; 50640 var node = _this3.body.nodes[nodeId]; 50641 50642 // if this node is already used in another cluster this session, we do not have to re-evaluate it. 50643 if (usedNodes[nodeId] === undefined) { 50644 relevantEdgeCount = 0; 50645 edges = []; 50646 for (var j = 0; j < node.edges.length; j++) { 50647 edge = node.edges[j]; 50648 if (_this3.clusteredEdges[edge.id] === undefined) { 50649 if (edge.toId !== edge.fromId) { 50650 relevantEdgeCount++; 50651 } 50652 edges.push(edge); 50653 } 50654 } 50655 50656 // this node qualifies, we collect its neighbours to start the clustering process. 50657 if (relevantEdgeCount === edgeCount) { 50658 checkJoinCondition = function checkJoinCondition(node) { 50659 if (options.joinCondition === undefined || options.joinCondition === null) { 50660 return true; 50661 } 50662 50663 var clonedOptions = NetworkUtil.cloneOptions(node); 50664 return options.joinCondition(clonedOptions); 50665 }; 50666 50667 var gatheringSuccessful = true; 50668 for (var _j = 0; _j < edges.length; _j++) { 50669 edge = edges[_j]; 50670 var childNodeId = _this3._getConnectedId(edge, nodeId); 50671 // add the nodes to the list by the join condition. 50672 if (checkJoinCondition(node)) { 50673 childEdgesObj[edge.id] = edge; 50674 childNodesObj[nodeId] = node; 50675 childNodesObj[childNodeId] = _this3.body.nodes[childNodeId]; 50676 usedNodes[nodeId] = true; 50677 } else { 50678 // this node does not qualify after all. 50679 gatheringSuccessful = false; 50680 break; 50681 } 50682 } 50683 50684 // add to the cluster queue 50685 if ((0, _keys2['default'])(childNodesObj).length > 0 && (0, _keys2['default'])(childEdgesObj).length > 0 && gatheringSuccessful === true) { 50686 /** 50687 * Search for cluster data that contains any of the node id's 50688 * @returns {Boolean} true if no joinCondition, otherwise return value of joinCondition 50689 */ 50690 findClusterData = function findClusterData() { 50691 for (var n = 0; n < clusters.length; ++n) { 50692 // Search for a cluster containing any of the node id's 50693 for (var m in childNodesObj) { 50694 if (clusters[n].nodes[m] !== undefined) { 50695 return clusters[n]; 50696 } 50697 } 50698 } 50699 50700 return undefined; 50701 }; 50702 50703 // If any of the found nodes is part of a cluster found in this method, 50704 // add the current values to that cluster 50705 50706 50707 foundCluster = findClusterData(); 50708 50709 if (foundCluster !== undefined) { 50710 // Add nodes to found cluster if not present 50711 for (var m in childNodesObj) { 50712 if (foundCluster.nodes[m] === undefined) { 50713 foundCluster.nodes[m] = childNodesObj[m]; 50714 } 50715 } 50716 50717 // Add edges to found cluster, if not present 50718 for (var _m in childEdgesObj) { 50719 if (foundCluster.edges[_m] === undefined) { 50720 foundCluster.edges[_m] = childEdgesObj[_m]; 50721 } 50722 } 50723 } else { 50724 // Create a new cluster group 50725 clusters.push({ nodes: childNodesObj, edges: childEdgesObj }); 50726 } 50727 } 50728 } 50729 } 50730 }; 50731 50732 for (var i = 0; i < this.body.nodeIndices.length; i++) { 50733 var checkJoinCondition; 50734 var findClusterData; 50735 var foundCluster; 50736 50737 _loop(i); 50738 } 50739 50740 for (var i = 0; i < clusters.length; i++) {
50741 this._cluster(clusters[i].nodes, clusters[i].edges, options, false); 50742 } 50743 50744 if (refreshData === true) { 50745 this.body.emitter.emit('_dataChanged'); 50746 } 50747 } 50748 50749 /** 50750 * Cluster all nodes in the network that have only 1 edge 50751 * @param {Object} options 50752 * @param {boolean} [refreshData=true] 50753 */ 50754 50755 }, { 50756 key: 'clusterOutliers', 50757 value: function clusterOutliers(options) { 50758 var refreshData = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true; 50759 50760 this.clusterByEdgeCount(1, options, refreshData); 50761 } 50762 50763 /** 50764 * Cluster all nodes in the network that have only 2 edge 50765 * @param {Object} options 50766 * @param {boolean} [refreshData=true] 50767 */ 50768 50769 }, { 50770 key: 'clusterBridges', 50771 value: function clusterBridges(options) { 50772 var refreshData = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true; 50773 50774 this.clusterByEdgeCount(2, options, refreshData); 50775 } 50776 50777 /** 50778 * suck all connected nodes of a node into the node. 50779 * @param {Node.id} nodeId 50780 * @param {Object} options 50781 * @param {boolean} [refreshData=true] 50782 */ 50783 50784 }, { 50785 key: 'clusterByConnection', 50786 value: function clusterByConnection(nodeId, options) { 50787 var refreshData = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true; 50788 50789 // kill conditions 50790 if (nodeId === undefined) { 50791 throw new Error("No nodeId supplied to clusterByConnection!"); 50792 } 50793 if (this.body.nodes[nodeId] === undefined) { 50794 throw new Error("The nodeId given to clusterByConnection does not exist!"); 50795 } 50796 50797 var node = this.body.nodes[nodeId]; 50798 options = this._checkOptions(options, node); 50799 if (options.clusterNodeProperties.x === undefined) { 50800 options.clusterNodeProperties.x = node.x; 50801 } 50802 if (options.clusterNodeProperties.y === undefined) { 50803 options.clusterNodeProperties.y = node.y; 50804 } 50805 if (options.clusterNodeProperties.fixed === undefined) { 50806 options.clusterNodeProperties.fixed = {}; 50807 options.clusterNodeProperties.fixed.x = node.options.fixed.x; 50808 options.clusterNodeProperties.fixed.y = node.options.fixed.y; 50809 } 50810 50811 var childNodesObj = {}; 50812 var childEdgesObj = {}; 50813 var parentNodeId = node.id; 50814 var parentClonedOptions = NetworkUtil.cloneOptions(node); 50815 childNodesObj[parentNodeId] = node; 50816 50817 // collect the nodes that will be in the cluster 50818 for (var i = 0; i < node.edges.length; i++) { 50819 var edge = node.edges[i]; 50820 if (this.clusteredEdges[edge.id] === undefined) { 50821 var childNodeId = this._getConnectedId(edge, parentNodeId); 50822 50823 // if the child node is not in a cluster 50824 if (this.clusteredNodes[childNodeId] === undefined) { 50825 if (childNodeId !== parentNodeId) { 50826 if (options.joinCondition === undefined) { 50827 childEdgesObj[edge.id] = edge; 50828 childNodesObj[childNodeId] = this.body.nodes[childNodeId]; 50829 } else { 50830 // clone the options and insert some additional parameters that could be interesting. 50831 var childClonedOptions = NetworkUtil.cloneOptions(this.body.nodes[childNodeId]); 50832 if (options.joinCondition(parentClonedOptions, childClonedOptions) === true) { 50833 childEdgesObj[edge.id] = edge; 50834 childNodesObj[childNodeId] = this.body.nodes[childNodeId]; 50835 } 50836 } 50837 } else { 50838 // swallow the edge if it is self-referencing. 50839 childEdgesObj[edge.id] = edge; 50840 } 50841 } 50842 } 50843 } 50844 var childNodeIDs = (0, _keys2['default'])(childNodesObj).map(function (childNode) { 50845 return childNodesObj[childNode].id; 50846 }); 50847 50848 for (childNode in childNodesObj) { 50849 if (!childNodesObj.hasOwnProperty(childNode)) continue; 50850 50851 var childNode = childNodesObj[childNode]; 50852 for (var y = 0; y < childNode.edges.length; y++) { 50853 var childEdge = childNode.edges[y]; 50854 if (childNodeIDs.indexOf(this._getConnectedId(childEdge, childNode.id)) > -1) { 50855 childEdgesObj[childEdge.id] = childEdge; 50856 } 50857 } 50858 } 50859 this._cluster(childNodesObj, childEdgesObj, options, refreshData); 50860 } 50861 50862 /** 50863 * This function creates the edges that will be attached to the cluster 50864 * It looks for edges that are connected to the nodes from the "outside' of the cluster. 50865 * 50866 * @param {{Node.id: vis.Node}} childNodesObj 50867 * @param {{vis.Edge.id: vis.Edge}} childEdgesObj 50868 * @param {Object} clusterNodeProperties 50869 * @param {Object} clusterEdgeProperties 50870 * @private 50871 */ 50872 50873 }, { 50874 key: '_createClusterEdges', 50875 value: function _createClusterEdges(childNodesObj, childEdgesObj, clusterNodeProperties, clusterEdgeProperties) { 50876 var edge = void 0, 50877 childNodeId = void 0, 50878 childNode = void 0, 50879 toId = void 0, 50880 fromId = void 0, 50881 otherNodeId = void 0; 50882 50883 // loop over all child nodes and their edges to find edges going out of the cluster 50884 // these edges will be replaced by clusterEdges. 50885 var childKeys = (0, _keys2['default'])(childNodesObj); 50886 var createEdges = []; 50887 for (var i = 0; i < childKeys.length; i++) { 50888 childNodeId = childKeys[i]; 50889 childNode = childNodesObj[childNodeId]; 50890 50891 // construct new edges from the cluster to others 50892 for (var j = 0; j < childNode.edges.length; j++) { 50893 edge = childNode.edges[j]; 50894 // we only handle edges that are visible to the system, not the disabled ones from the clustering process. 50895 if (this.clusteredEdges[edge.id] === undefined) { 50896 // self-referencing edges will be added to the "hidden" list 50897 if (edge.toId == edge.fromId) { 50898 childEdgesObj[edge.id] = edge; 50899 } else { 50900 // set up the from and to. 50901 if (edge.toId == childNodeId) { 50902 // this is a double equals because ints and strings can be interchanged here. 50903 toId = clusterNodeProperties.id; 50904 fromId = edge.fromId; 50905 otherNodeId = fromId; 50906 } else { 50907 toId = edge.toId; 50908 fromId = clusterNodeProperties.id; 50909 otherNodeId = toId; 50910 } 50911 } 50912 50913 // Only edges from the cluster outwards are being replaced. 50914 if (childNodesObj[otherNodeId] === undefined) { 50915 createEdges.push({ edge: edge, fromId: fromId, toId: toId }); 50916 } 50917 } 50918 } 50919 } 50920 50921 // 50922 // Here we actually create the replacement edges. 50923 // 50924 // We could not do this in the loop above as the creation process 50925 // would add an edge to the edges array we are iterating over. 50926 //
50927 // NOTE: a clustered edge can have multiple base edges! 50928 // 50929 var newEdges = []; 50930 50931 /** 50932 * Find a cluster edge which matches the given created edge. 50933 * @param {vis.Edge} createdEdge 50934 * @returns {vis.Edge} 50935 */ 50936 var getNewEdge = function getNewEdge(createdEdge) { 50937 for (var _j2 = 0; _j2 < newEdges.length; _j2++) { 50938 var newEdge = newEdges[_j2]; 50939 50940 // We replace both to and from edges with a single cluster edge 50941 var matchToDirection = createdEdge.fromId === newEdge.fromId && createdEdge.toId === newEdge.toId; 50942 var matchFromDirection = createdEdge.fromId === newEdge.toId && createdEdge.toId === newEdge.fromId; 50943 50944 if (matchToDirection || matchFromDirection) { 50945 return newEdge; 50946 } 50947 } 50948 50949 return null; 50950 }; 50951 50952 for (var _j3 = 0; _j3 < createEdges.length; _j3++) { 50953 var createdEdge = createEdges[_j3]; 50954 var _edge = createdEdge.edge; 50955 var newEdge = getNewEdge(createdEdge); 50956 50957 if (newEdge === null) { 50958 // Create a clustered edge for this connection 50959 newEdge = this._createClusteredEdge(createdEdge.fromId, createdEdge.toId, _edge, clusterEdgeProperties); 50960 50961 newEdges.push(newEdge); 50962 } else { 50963 newEdge.clusteringEdgeReplacingIds.push(_edge.id); 50964 } 50965 50966 // also reference the new edge in the old edge 50967 this.body.edges[_edge.id].edgeReplacedById = newEdge.id; 50968 50969 // hide the replaced edge 50970 this._backupEdgeOptions(_edge); 50971 _edge.setOptions({ physics: false }); 50972 } 50973 } 50974 50975 /** 50976 * This function checks the options that can be supplied to the different cluster functions 50977 * for certain fields and inserts defaults if needed 50978 * @param {Object} options 50979 * @returns {*} 50980 * @private 50981 */ 50982 50983 }, { 50984 key: '_checkOptions', 50985 value: function _checkOptions() { 50986 var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; 50987 50988 if (options.clusterEdgeProperties === undefined) { 50989 options.clusterEdgeProperties = {}; 50990 } 50991 if (options.clusterNodeProperties === undefined) { 50992 options.clusterNodeProperties = {}; 50993 } 50994 50995 return options; 50996 } 50997 50998 /** 50999 * 51000 * @param {Object} childNodesObj | object with node objects, id as keys, same as childNodes except it also contains a source node 51001 * @param {Object} childEdgesObj | object with edge objects, id as keys 51002 * @param {Array} options | object with {clusterNodeProperties, clusterEdgeProperties, processProperties} 51003 * @param {boolean} refreshData | when true, do not wrap up 51004 * @private 51005 */ 51006 51007 }, { 51008 key: '_cluster', 51009 value: function _cluster(childNodesObj, childEdgesObj, options) { 51010 var refreshData = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true; 51011 51012 // Remove nodes which are already clustered 51013 var tmpNodesToRemove = []; 51014 for (var _nodeId in childNodesObj) { 51015 if (childNodesObj.hasOwnProperty(_nodeId)) { 51016 if (this.clusteredNodes[_nodeId] !== undefined) { 51017 tmpNodesToRemove.push(_nodeId); 51018 } 51019 } 51020 } 51021 51022 for (var n = 0; n < tmpNodesToRemove.length; ++n) { 51023 delete childNodesObj[tmpNodesToRemove[n]]; 51024 } 51025 51026 // kill condition: no nodes don't bother 51027 if ((0, _keys2['default'])(childNodesObj).length == 0) { 51028 return; 51029 } 51030 51031 // allow clusters of 1 if options allow 51032 if ((0, _keys2['default'])(childNodesObj).length == 1 && options.clusterNodeProperties.allowSingleNodeCluster != true) { 51033 return; 51034 } 51035 51036 var clusterNodeProperties = util.deepExtend({}, options.clusterNodeProperties); 51037 51038 // construct the clusterNodeProperties 51039 if (options.processProperties !== undefined) { 51040 // get the childNode options 51041 var childNodesOptions = []; 51042 for (var _nodeId2 in childNodesObj) { 51043 if (childNodesObj.hasOwnProperty(_nodeId2)) { 51044 var clonedOptions = NetworkUtil.cloneOptions(childNodesObj[_nodeId2]); 51045 childNodesOptions.push(clonedOptions); 51046 } 51047 } 51048 51049 // get cluster properties based on childNodes 51050 var childEdgesOptions = []; 51051 for (var edgeId in childEdgesObj) { 51052 if (childEdgesObj.hasOwnProperty(edgeId)) { 51053 // these cluster edges will be removed on creation of the cluster. 51054 if (edgeId.substr(0, 12) !== "clusterEdge:") { 51055 var _clonedOptions = NetworkUtil.cloneOptions(childEdgesObj[edgeId], 'edge'); 51056 childEdgesOptions.push(_clonedOptions); 51057 } 51058 } 51059 } 51060 51061 clusterNodeProperties = options.processProperties(clusterNodeProperties, childNodesOptions, childEdgesOptions); 51062 if (!clusterNodeProperties) { 51063 throw new Error("The processProperties function does not return properties!"); 51064 } 51065 } 51066 51067 // check if we have an unique id; 51068 if (clusterNodeProperties.id === undefined) { 51069 clusterNodeProperties.id = 'cluster:' + util.randomUUID(); 51070 } 51071 var clusterId = clusterNodeProperties.id; 51072 51073 if (clusterNodeProperties.label === undefined) { 51074 clusterNodeProperties.label = 'cluster'; 51075 } 51076 51077 // give the clusterNode a position if it does not have one. 51078 var pos = undefined; 51079 if (clusterNodeProperties.x === undefined) { 51080 pos = this._getClusterPosition(childNodesObj); 51081 clusterNodeProperties.x = pos.x; 51082 } 51083 if (clusterNodeProperties.y === undefined) { 51084 if (pos === undefined) { 51085 pos = this._getClusterPosition(childNodesObj); 51086 } 51087 clusterNodeProperties.y = pos.y; 51088 } 51089 51090 // force the ID to remain the same 51091 clusterNodeProperties.id = clusterId; 51092 51093 // create the cluster Node 51094 // Note that allowSingleNodeCluster, if present, is stored in the options as well 51095 var clusterNode = this.body.functions.createNode(clusterNodeProperties, Cluster); 51096 clusterNode.containedNodes = childNodesObj; 51097 clusterNode.containedEdges = childEdgesObj; 51098 // cache a copy from the cluster edge properties if we have to reconnect others later on 51099 clusterNode.clusterEdgeProperties = options.clusterEdgeProperties; 51100 51101 // finally put the cluster node into global 51102 this.body.nodes[clusterNodeProperties.id] = clusterNode; 51103 51104 this._clusterEdges(childNodesObj, childEdgesObj, clusterNodeProperties, options.clusterEdgeProperties); 51105 51106 // set ID to undefined so no duplicates arise 51107 clusterNodeProperties.id = undefined; 51108 51109 // wrap up 51110 if (refreshData === true) { 51111 this.body.emitter.emit('_dataChanged'); 51112 } 51113 } 51114 51115 /** 51116 * 51117 * @param {Edge} edge 51118 * @private 51119 */ 51120 51121 }, { 51122 key: '_backupEdgeOptions', 51123 value: function _backupEdgeOptions(edge) { 51124 if (this.clusteredEdges[edge.id] === undefined) {
51125 this.clusteredEdges[edge.id] = { physics: edge.options.physics }; 51126 } 51127 } 51128 51129 /** 51130 * 51131 * @param {Edge} edge 51132 * @private 51133 */ 51134 51135 }, { 51136 key: '_restoreEdge', 51137 value: function _restoreEdge(edge) { 51138 var originalOptions = this.clusteredEdges[edge.id]; 51139 if (originalOptions !== undefined) { 51140 edge.setOptions({ physics: originalOptions.physics }); 51141 delete this.clusteredEdges[edge.id]; 51142 } 51143 } 51144 51145 /** 51146 * Check if a node is a cluster. 51147 * @param {Node.id} nodeId 51148 * @returns {*} 51149 */ 51150 51151 }, { 51152 key: 'isCluster', 51153 value: function isCluster(nodeId) { 51154 if (this.body.nodes[nodeId] !== undefined) { 51155 return this.body.nodes[nodeId].isCluster === true; 51156 } else { 51157 console.log("Node does not exist."); 51158 return false; 51159 } 51160 } 51161 51162 /** 51163 * get the position of the cluster node based on what's inside 51164 * @param {object} childNodesObj | object with node objects, id as keys 51165 * @returns {{x: number, y: number}} 51166 * @private 51167 */ 51168 51169 }, { 51170 key: '_getClusterPosition', 51171 value: function _getClusterPosition(childNodesObj) { 51172 var childKeys = (0, _keys2['default'])(childNodesObj); 51173 var minX = childNodesObj[childKeys[0]].x; 51174 var maxX = childNodesObj[childKeys[0]].x; 51175 var minY = childNodesObj[childKeys[0]].y; 51176 var maxY = childNodesObj[childKeys[0]].y; 51177 var node = void 0; 51178 for (var i = 1; i < childKeys.length; i++) { 51179 node = childNodesObj[childKeys[i]]; 51180 minX = node.x < minX ? node.x : minX; 51181 maxX = node.x >
51181 maxX ? node.x : maxX; 51182 minY = node.y < minY ? node.y : minY; 51183 maxY = node.y > maxY ? node.y : maxY; 51184 } 51185 51186 return { x: 0.5 * (minX + maxX), y: 0.5 * (minY + maxY) }; 51187 } 51188 51189 /** 51190 * Open a cluster by calling this function. 51191 * @param {vis.Edge.id} clusterNodeId | the ID of the cluster node 51192 * @param {Object} options 51193 * @param {boolean} refreshData | wrap up afterwards if not true 51194 */ 51195 51196 }, { 51197 key: 'openCluster', 51198 value: function openCluster(clusterNodeId, options) { 51199 var refreshData = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true; 51200 51201 // kill conditions 51202 if (clusterNodeId === undefined) { 51203 throw new Error("No clusterNodeId supplied to openCluster."); 51204 } 51205 51206 var clusterNode = this.body.nodes[clusterNodeId]; 51207 51208 if (clusterNode === undefined) { 51209 throw new Error("The clusterNodeId supplied to openCluster does not exist."); 51210 } 51211 if (clusterNode.isCluster !== true || clusterNode.containedNodes === undefined || clusterNode.containedEdges === undefined) { 51212 throw new Error("The node:" + clusterNodeId + " is not a valid cluster."); 51213 } 51214 51215 // Check if current cluster is clustered itself 51216 var stack = this.findNode(clusterNodeId); 51217 var parentIndex = stack.indexOf(clusterNodeId) - 1; 51218 if (parentIndex >= 0) { 51219 // Current cluster is clustered; transfer contained nodes and edges to parent 51220 var parentClusterNodeId = stack[parentIndex]; 51221 var parentClusterNode = this.body.nodes[parentClusterNodeId]; 51222 51223 // clustering.clusteredNodes and clustering.clusteredEdges remain unchanged 51224 parentClusterNode._openChildCluster(clusterNodeId); 51225 51226 // All components of child cluster node have been transferred. It can die now. 51227 delete this.body.nodes[clusterNodeId]; 51228 if (refreshData === true) { 51229 this.body.emitter.emit('_dataChanged'); 51230 } 51231 51232 return; 51233 } 51234 51235 // main body 51236 var containedNodes = clusterNode.containedNodes; 51237 var containedEdges = clusterNode.containedEdges; 51238 51239 // allow the user to position the nodes after release. 51240 if (options !== undefined && options.releaseFunction !== undefined && typeof options.releaseFunction === 'function') { 51241 var positions = {}; 51242 var clusterPosition = { x: clusterNode.x, y: clusterNode.y }; 51243 for (var _nodeId3 in containedNodes) { 51244 if (containedNodes.hasOwnProperty(_nodeId3)) { 51245 var containedNode = this.body.nodes[_nodeId3]; 51246 positions[_nodeId3] = { x: containedNode.x, y: containedNode.y }; 51247 } 51248 } 51249 var newPositions = options.releaseFunction(clusterPosition, positions); 51250 51251 for (var _nodeId4 in containedNodes) { 51252 if (containedNodes.hasOwnProperty(_nodeId4)) { 51253 var _containedNode = this.body.nodes[_nodeId4]; 51254 if (newPositions[_nodeId4] !== undefined) { 51255 _containedNode.x = newPositions[_nodeId4].x === undefined ? clusterNode.x : newPositions[_nodeId4].x; 51256 _containedNode.y = newPositions[_nodeId4].y === undefined ? clusterNode.y : newPositions[_nodeId4].y; 51257 } 51258 } 51259 } 51260 } else { 51261 // copy the position from the cluster 51262 util.forEach(containedNodes, function (containedNode) { 51263 // inherit position 51264 if (containedNode.options.fixed.x === false) { 51265 containedNode.x = clusterNode.x; 51266 } 51267 if (containedNode.options.fixed.y === false) { 51268 containedNode.y = clusterNode.y; 51269 } 51270 }); 51271 } 51272 51273 // release nodes 51274 for (var _nodeId5 in containedNodes) { 51275 if (containedNodes.hasOwnProperty(_nodeId5)) { 51276 var _containedNode2 = this.body.nodes[_nodeId5]; 51277 51278 // inherit speed 51279 _containedNode2.vx = clusterNode.vx; 51280 _containedNode2.vy = clusterNode.vy; 51281 51282 _containedNode2.setOptions({ physics: true }); 51283 51284 delete this.clusteredNodes[_nodeId5]; 51285 } 51286 } 51287 51288 // copy the clusterNode edges because we cannot iterate over an object that we add or remove from. 51289 var edgesToBeDeleted = []; 51290 for (var i = 0; i < clusterNode.edges.length; i++) { 51291 edgesToBeDeleted.push(clusterNode.edges[i]); 51292 } 51293 51294 // actually handling the deleting. 51295 for (var _i2 = 0; _i2 < edgesToBeDeleted.length; _i2++) { 51296 var edge = edgesToBeDeleted[_i2]; 51297 var otherNodeId = this._getConnectedId(edge, clusterNodeId); 51298 var otherNode = this.clusteredNodes[otherNodeId]; 51299 51300 for (var j = 0; j < edge.clusteringEdgeReplacingIds.length; j++) { 51301 var transferId = edge.clusteringEdgeReplacingIds[j]; 51302 var transferEdge = this.body.edges[transferId]; 51303 if (transferEdge === undefined) continue; 51304 51305 // if the other node is in another cluster, we transfer ownership of this edge to the other cluster
51306 if (otherNode !== undefined) { 51307 // transfer ownership: 51308 var otherCluster = this.body.nodes[otherNode.clusterId]; 51309 otherCluster.containedEdges[transferEdge.id] = transferEdge; 51310 51311 // delete local reference 51312 delete containedEdges[transferEdge.id]; 51313 51314 // get to and from 51315 var fromId = transferEdge.fromId; 51316 var toId = transferEdge.toId; 51317 if (transferEdge.toId == otherNodeId) { 51318 toId = otherNode.clusterId; 51319 } else { 51320 fromId = otherNode.clusterId; 51321 } 51322 51323 // create new cluster edge from the otherCluster 51324 this._createClusteredEdge(fromId, toId, transferEdge, otherCluster.clusterEdgeProperties, { hidden: false, physics: true }); 51325 } else { 51326 this._restoreEdge(transferEdge); 51327 } 51328 } 51329 51330 edge.remove(); 51331 } 51332 51333 // handle the releasing of the edges 51334 for (var edgeId in containedEdges) { 51335 if (containedEdges.hasOwnProperty(edgeId)) { 51336 this._restoreEdge(containedEdges[edgeId]); 51337 } 51338 } 51339 51340 // remove clusterNode 51341 delete this.body.nodes[clusterNodeId]; 51342 51343 if (refreshData === true) { 51344 this.body.emitter.emit('_dataChanged'); 51345 } 51346 } 51347 51348 /** 51349 * 51350 * @param {Cluster.id} clusterId 51351 * @returns {Array.<Node.id>} 51352 */ 51353 51354 }, { 51355 key: 'getNodesInCluster', 51356 value: function getNodesInCluster(clusterId) { 51357 var nodesArray = []; 51358 if (this.isCluster(clusterId) === true) { 51359 var containedNodes = this.body.nodes[clusterId].containedNodes; 51360 for (var _nodeId6 in containedNodes) { 51361 if (containedNodes.hasOwnProperty(_nodeId6)) { 51362 nodesArray.push(this.body.nodes[_nodeId6].id); 51363 } 51364 } 51365 } 51366 51367 return nodesArray; 51368 } 51369 51370 /** 51371 * Get the stack clusterId's that a certain node resides in. cluster A -> cluster B -> cluster C -> node 51372 * 51373 * If a node can't be found in the chain, return an empty array. 51374 * 51375 * @param {string|number} nodeId 51376 * @returns {Array} 51377 */ 51378 51379 }, { 51380 key: 'findNode', 51381 value: function findNode(nodeId) { 51382 var stack = []; 51383 var max = 100; 51384 var counter = 0; 51385 var node = void 0; 51386 51387 while (this.clusteredNodes[nodeId] !== undefined && counter < max) { 51388 node = this.body.nodes[nodeId]; 51389 if (node === undefined) return []; 51390 stack.push(node.id); 51391 51392 nodeId = this.clusteredNodes[nodeId].clusterId; 51393 counter++; 51394 } 51395 51396 node = this.body.nodes[nodeId]; 51397 if (node === undefined) return []; 51398 stack.push(node.id); 51399 51400 stack.reverse(); 51401 return stack; 51402 } 51403 51404 /** 51405 * Using a clustered nodeId, update with the new options 51406 * @param {vis.Edge.id} clusteredNodeId 51407 * @param {object} newOptions 51408 */ 51409 51410 }, { 51411 key: 'updateClusteredNode', 51412 value: function updateClusteredNode(clusteredNodeId, newOptions) { 51413 if (clusteredNodeId === undefined) { 51414 throw new Error("No clusteredNodeId supplied to updateClusteredNode."); 51415 } 51416 if (newOptions === undefined) { 51417 throw new Error("No newOptions supplied to updateClusteredNode."); 51418 } 51419 if (this.body.nodes[clusteredNodeId] === undefined) { 51420 throw new Error("The clusteredNodeId supplied to updateClusteredNode does not exist."); 51421 } 51422 51423 this.body.nodes[clusteredNodeId].setOptions(newOptions); 51424 this.body.emitter.emit('_dataChanged'); 51425 } 51426 51427 /** 51428 * Using a base edgeId, update all related clustered edges with the new options 51429 * @param {vis.Edge.id} startEdgeId 51430 * @param {object} newOptions 51431 */ 51432 51433 }, { 51434 key: 'updateEdge', 51435 value: function updateEdge(startEdgeId, newOptions) { 51436 if (startEdgeId === undefined) { 51437 throw new Error("No startEdgeId supplied to updateEdge."); 51438 } 51439 if (newOptions === undefined) { 51440 throw new Error("No newOptions supplied to updateEdge."); 51441 } 51442 if (this.body.edges[startEdgeId] === undefined) { 51443 throw new Error("The startEdgeId supplied to updateEdge does not exist."); 51444 } 51445 51446 var allEdgeIds = this.getClusteredEdges(startEdgeId); 51447 for (var i = 0; i < allEdgeIds.length; i++) { 51448 var edge = this.body.edges[allEdgeIds[i]]; 51449 edge.setOptions(newOptions); 51450 } 51451 this.body.emitter.emit('_dataChanged'); 51452 } 51453 51454 /** 51455 * Get a stack of clusterEdgeId's (+base edgeid) that a base edge is the same as. cluster edge C -> cluster edge B -> cluster edge A -> base edge(edgeId) 51456 * @param {vis.Edge.id} edgeId 51457 * @returns {Array.<vis.Edge.id>} 51458 */ 51459 51460 }, { 51461 key: 'getClusteredEdges', 51462 value: function getClusteredEdges(edgeId) { 51463 var stack = []; 51464 var max = 100; 51465 var counter = 0; 51466 51467 while (edgeId !== undefined && this.body.edges[edgeId] !== undefined && counter < max) { 51468 stack.push(this.body.edges[edgeId].id); 51469 edgeId = this.body.edges[edgeId].edgeReplacedById; 51470 counter++; 51471 } 51472 stack.reverse(); 51473 return stack; 51474 } 51475 51476 /** 51477 * Get the base edge id of clusterEdgeId. cluster edge (clusteredEdgeId) -> cluster edge B -> cluster edge C -> base edge 51478 * @param {vis.Edge.id} clusteredEdgeId 51479 * @returns {vis.Edge.id} baseEdgeId 51480 * 51481 * TODO: deprecate in 5.0.0. Method getBaseEdges() is the correct one to use. 51482 */ 51483 51484 }, { 51485 key: 'getBaseEdge', 51486 value: function getBaseEdge(clusteredEdgeId) { 51487 // Just kludge this by returning the first base edge id found 51488 return this.getBaseEdges(clusteredEdgeId)[0]; 51489 } 51490 51491 /** 51492 * Get all regular edges for this clustered edge id. 51493 * 51494 * @param {vis.Edge.id} clusteredEdgeId 51495 * @returns {Array.<vis.Edge.id>} all baseEdgeId's under this clustered edge 51496 */ 51497 51498 }, { 51499 key: 'getBaseEdges', 51500 value: function getBaseEdges(clusteredEdgeId) { 51501 var IdsToHandle = [clusteredEdgeId]; 51502 var doneIds = []; 51503 var foundIds = []; 51504 var max = 100; 51505 var counter = 0; 51506 51507 while (IdsToHandle.length > 0 && counter < max) { 51508 var nextId = IdsToHandle.pop(); 51509 if (nextId === undefined) continue; // Paranoia here and onwards 51510 var nextEdge = this.body.edges[nextId]; 51511 if (nextEdge === undefined) continue; 51512 counter++; 51513 51514 var replacingIds = nextEdge.clusteringEdgeReplacingIds; 51515 if (replacingIds === undefined) { 51516 // nextId is a base id 51517 foundIds.push(nextId); 51518 } else { 51519 // Another cluster edge, unravel this one as well 51520 for (var i = 0; i < replacingIds.length; ++i) { 51521 var replacingId = replacingIds[i]; 51522 51523 // Don't add if already handled 51524 // TODO: never triggers; find a test-case which does 51525 if (IdsToHandle.indexOf(replacingIds) !== -1 || doneIds.indexOf(replacingIds) !== -1) { 51526 continue; 51527 } 51528 51529 IdsToHandle.push(replacingId); 51530 } 51531 } 51532 51533 doneIds.push(nextId); 51534 } 51535 51536 return foundIds; 51537 } 51538 51539 /** 51540 * Get the Id the node is connected to 51541 * @param {vis.Edge} edge 51542 * @param {Node.id} nodeId 51543 * @returns {*} 51544 * @private 51545 */ 51546 51547 }, { 51548 key: '_getConnectedId', 51549 value: function _getConnectedId(edge, nodeId) { 51550 if (edge.toId != nodeId) { 51551 return edge.toId; 51552 } else if (edge.fromId != nodeId) { 51553 return edge.fromId; 51554 } else { 51555 return edge.fromId; 51556 } 51557 } 51558 51559 /** 51560 * We determine how many connections denote an important hub. 51561 * We take the mean + 2*std as the important hub size. (Assuming a normal distribution of data, ~2.2%) 51562 * 51563 * @returns {number} 51564 * @private 51565 */ 51566 51567 }, { 51568 key: '_getHubSize', 51569 value: function _getHubSize() { 51570 var average = 0; 51571 var averageSquared = 0; 51572 var hubCounter = 0; 51573 var largestHub = 0; 51574 51575 for (var i = 0; i < this.body.nodeIndices.length; i++) { 51576 var _node = this.body.nodes[this.body.nodeIndices[i]]; 51577 if (_node.edges.length > largestHub) { 51578 largestHub = _node.edges.length; 51579 } 51580 average += _node.edges.length; 51581 averageSquared += Math.pow(_node.edges.length, 2); 51582 hubCounter += 1; 51583 } 51584 average = average / hubCounter; 51585 averageSquared = averageSquared / hubCounter; 51586 51587 var variance = averageSquared - Math.pow(average, 2); 51588 var standardDeviation = Math.sqrt(variance); 51589 51590 var hubThreshold = Math.floor(average + 2 * standardDeviation); 51591 51592 // always have at least one to cluster 51593 if (hubThreshold > largestHub) { 51594 hubThreshold = largestHub; 51595 } 51596 51597 return hubThreshold; 51598 } 51599 51600 /** 51601 * Create an edge for the cluster representation. 51602 * 51603 * @param {Node.id} fromId 51604 * @param {Node.id} toId 51605 * @param {vis.Edge} baseEdge 51606 * @param {Object} clusterEdgeProperties 51607 * @param {Object} extraOptions 51608 * @returns {Edge} newly created clustered edge 51609 * @private 51610 */ 51611 51612 }, { 51613 key: '_createClusteredEdge', 51614 value: function _createClusteredEdge(fromId, toId, baseEdge, clusterEdgeProperties, extraOptions) { 51615 // copy the options of the edge we will replace
51616 var clonedOptions = NetworkUtil.cloneOptions(baseEdge, 'edge'); 51617 // make sure the properties of clusterEdges are superimposed on it 51618 util.deepExtend(clonedOptions, clusterEdgeProperties); 51619 51620 // set up the edge 51621 clonedOptions.from = fromId; 51622 clonedOptions.to = toId; 51623 clonedOptions.id = 'clusterEdge:' + util.randomUUID(); 51624 51625 // apply the edge specific options to it if specified 51626 if (extraOptions !== undefined) { 51627 util.deepExtend(clonedOptions, extraOptions); 51628 } 51629 51630 var newEdge = this.body.functions.createEdge(clonedOptions); 51631 newEdge.clusteringEdgeReplacingIds = [baseEdge.id]; 51632 newEdge.connect(); 51633 51634 // Register the new edge 51635 this.body.edges[newEdge.id] = newEdge; 51636 51637 return newEdge; 51638 } 51639 51640 /** 51641 * Add the passed child nodes and edges to the given cluster node. 51642 * 51643 * @param {Object|Node} childNodes hash of nodes or single node to add in cluster 51644 * @param {Object|Edge} childEdges hash of edges or single edge to take into account when clustering 51645 * @param {Node} clusterNode cluster node to add nodes and edges to 51646 * @param {Object} [clusterEdgeProperties] 51647 * @private 51648 */ 51649 51650 }, { 51651 key: '_clusterEdges', 51652 value: function _clusterEdges(childNodes, childEdges, clusterNode, clusterEdgeProperties) { 51653 if (childEdges instanceof Edge) { 51654 var edge = childEdges; 51655 var obj = {}; 51656 obj[edge.id] = edge; 51657 childEdges = obj; 51658 } 51659 51660 if (childNodes instanceof Node) { 51661 var _node2 = childNodes; 51662 var _obj = {}; 51663 _obj[_node2.id] = _node2; 51664 childNodes = _obj; 51665 } 51666 51667 if (clusterNode === undefined || clusterNode === null) { 51668 throw new Error("_clusterEdges: parameter clusterNode required"); 51669 } 51670 51671 if (clusterEdgeProperties === undefined) { 51672 // Take the required properties from the cluster node 51673 clusterEdgeProperties = clusterNode.clusterEdgeProperties; 51674 } 51675 51676 // create the new edges that will connect to the cluster. 51677 // All self-referencing edges will be added to childEdges here. 51678 this._createClusterEdges(childNodes, childEdges, clusterNode, clusterEdgeProperties); 51679 51680 // disable the childEdges 51681 for (var edgeId in childEdges) { 51682 if (childEdges.hasOwnProperty(edgeId)) { 51683 if (this.body.edges[edgeId] !== undefined) { 51684 var _edge2 = this.body.edges[edgeId]; 51685 // cache the options before changing 51686 this._backupEdgeOptions(_edge2); 51687 // disable physics and hide the edge 51688 _edge2.setOptions({ physics: false }); 51689 } 51690 } 51691 } 51692 51693 // disable the childNodes 51694 for (var _nodeId7 in childNodes) { 51695 if (childNodes.hasOwnProperty(_nodeId7)) { 51696 this.clusteredNodes[_nodeId7] = { clusterId: clusterNode.id, node: this.body.nodes[_nodeId7] }; 51697 this.body.nodes[_nodeId7].setOptions({ physics: false }); 51698 } 51699 } 51700 } 51701 51702 /** 51703 * Determine in which cluster given nodeId resides. 51704 * 51705 * If not in cluster, return undefined. 51706 * 51707 * NOTE: If you know a cleaner way to do this, please enlighten me (wimrijnders). 51708 * 51709 * @param {Node.id} nodeId 51710 * @returns {Node|undefined} Node instance for cluster, if present 51711 * @private 51712 */ 51713 51714 }, { 51715 key: '_getClusterNodeForNode', 51716 value: function _getClusterNodeForNode(nodeId) { 51717 if (nodeId === undefined) return undefined; 51718 var clusteredNode = this.clusteredNodes[nodeId]; 51719 51720 // NOTE: If no cluster info found, it should actually be an error 51721 if (clusteredNode === undefined) return undefined; 51722 var clusterId = clusteredNode.clusterId; 51723 if (clusterId === undefined) return undefined; 51724 51725 return this.body.nodes[clusterId]; 51726 } 51727 51728 /** 51729 * Internal helper function for conditionally removing items in array 51730 * 51731 * Done like this because Array.filter() is not fully supported by all IE's. 51732 * 51733 * @param {Array} arr 51734 * @param {function} callback 51735 * @returns {Array} 51736 * @private 51737 */ 51738 51739 }, { 51740 key: '_filter', 51741 value: function _filter(arr, callback) { 51742 var ret = []; 51743 51744 util.forEach(arr, function (item) { 51745 if (callback(item)) {
51746 ret.push(item); 51747 } 51748 }); 51749 51750 return ret; 51751 } 51752 51753 /** 51754 * Scan all edges for changes in clustering and adjust this if necessary. 51755 * 51756 * Call this (internally) after there has been a change in node or edge data. 51757 * 51758 * Pre: States of this.body.nodes and this.body.edges consistent 51759 * Pre: this.clusteredNodes and this.clusteredEdge consistent with containedNodes and containedEdges 51760 * of cluster nodes. 51761 */ 51762 51763 }, { 51764 key: '_updateState', 51765 value: function _updateState() { 51766 var _this4 = this; 51767 51768 var nodeId = void 0; 51769 var deletedNodeIds = []; 51770 var deletedEdgeIds = []; 51771 51772 /** 51773 * Utility function to iterate over clustering nodes only 51774 * 51775 * @param {Function} callback function to call for each cluster node 51776 */ 51777 var eachClusterNode = function eachClusterNode(callback) { 51778 util.forEach(_this4.body.nodes, function (node) { 51779 if (node.isCluster === true) { 51780 callback(node); 51781 } 51782 }); 51783 }; 51784 51785 // 51786 // Remove deleted regular nodes from clustering 51787 // 51788 51789 // Determine the deleted nodes 51790 for (nodeId in this.clusteredNodes) { 51791 if (!this.clusteredNodes.hasOwnProperty(nodeId)) continue; 51792 var _node3 = this.body.nodes[nodeId]; 51793 51794 if (_node3 === undefined) { 51795 deletedNodeIds.push(nodeId); 51796 } 51797 } 51798 51799 // Remove nodes from cluster nodes 51800 eachClusterNode(function (clusterNode) { 51801 for (var n = 0; n < deletedNodeIds.length; n++) { 51802 delete clusterNode.containedNodes[deletedNodeIds[n]]; 51803 } 51804 }); 51805 51806 // Remove nodes from cluster list 51807 for (var n = 0; n < deletedNodeIds.length; n++) { 51808 delete this.clusteredNodes[deletedNodeIds[n]]; 51809 } 51810 51811 // 51812 // Remove deleted edges from clustering 51813 // 51814 51815 // Add the deleted clustered edges to the list 51816 util.forEach(this.clusteredEdges, function (edgeId) { 51817 var edge = _this4.body.edges[edgeId]; 51818 if (edge === undefined || !edge.endPointsValid()) { 51819 deletedEdgeIds.push(edgeId); 51820 } 51821 }); 51822 51823 // Cluster nodes can also contain edges which are not clustered, 51824 // i.e. nodes 1-2 within cluster with an edge in between. 51825 // So the cluster nodes also need to be scanned for invalid edges 51826 eachClusterNode(function (clusterNode) { 51827 util.forEach(clusterNode.containedEdges, function (edge, edgeId) { 51828 if (!edge.endPointsValid() && deletedEdgeIds.indexOf(edgeId) === -1) { 51829 deletedEdgeIds.push(edgeId); 51830 } 51831 }); 51832 }); 51833 51834 // Also scan for cluster edges which need to be removed in the active list. 51835 // Regular edges have been removed beforehand, so this only picks up the cluster edges. 51836 util.forEach(this.body.edges, function (edge, edgeId) { 51837 // Explicitly scan the contained edges for validity 51838 var isValid = true; 51839 var replacedIds = edge.clusteringEdgeReplacingIds; 51840 if (replacedIds !== undefined) { 51841 var numValid = 0; 51842 51843 util.forEach(replacedIds, function (containedEdgeId) { 51844 var containedEdge = _this4.body.edges[containedEdgeId]; 51845 51846 if (containedEdge !== undefined && containedEdge.endPointsValid()) { 51847 numValid += 1; 51848 } 51849 }); 51850 51851 isValid = numValid > 0; 51852 } 51853 51854 if (!edge.endPointsValid() || !isValid) { 51855 deletedEdgeIds.push(edgeId); 51856 } 51857 }); 51858 51859 // Remove edges from cluster nodes 51860 eachClusterNode(function (clusterNode) { 51861 util.forEach(deletedEdgeIds, function (deletedEdgeId) { 51862 delete clusterNode.containedEdges[deletedEdgeId]; 51863 51864 util.forEach(clusterNode.edges, function (edge, m) { 51865 if (edge.id === deletedEdgeId) { 51866 clusterNode.edges[m] = null; // Don't want to directly delete here, because in the loop 51867 return; 51868 } 51869 51870 edge.clusteringEdgeReplacingIds = _this4._filter(edge.clusteringEdgeReplacingIds, function (id) { 51871 return deletedEdgeIds.indexOf(id) === -1; 51872 }); 51873 }); 51874 51875 // Clean up the nulls 51876 clusterNode.edges = _this4._filter(clusterNode.edges, function (item) { 51877 return item !== null; 51878 }); 51879 }); 51880 }); 51881 51882 // Remove from cluster list 51883 util.forEach(deletedEdgeIds, function (edgeId) { 51884 delete _this4.clusteredEdges[edgeId]; 51885 }); 51886 51887 // Remove cluster edges from active list (this.body.edges). 51888 // deletedEdgeIds still contains id of regular edges, but these should all 51889 // be gone when you reach here. 51890 util.forEach(deletedEdgeIds, function (edgeId) { 51891 delete _this4.body.edges[edgeId]; 51892 }); 51893 51894 // 51895 // Check changed cluster state of edges 51896 // 51897 51898 // Iterating over keys here, because edges may be removed in the loop 51899 var ids = (0, _keys2['default'])(this.body.edges); 51900 util.forEach(ids, function (edgeId) { 51901 var edge = _this4.body.edges[edgeId]; 51902 51903 var shouldBeClustered = _this4._isClusteredNode(edge.fromId) || _this4._isClusteredNode(edge.toId); 51904 if (shouldBeClustered === _this4._isClusteredEdge(edge.id)) { 51905 return;
51905 // all is well 51906 } 51907 51908 if (shouldBeClustered) { 51909 // add edge to clustering 51910 var clusterFrom = _this4._getClusterNodeForNode(edge.fromId); 51911 if (clusterFrom !== undefined) { 51912 _this4._clusterEdges(_this4.body.nodes[edge.fromId], edge, clusterFrom); 51913 } 51914 51915 var clusterTo = _this4._getClusterNodeForNode(edge.toId); 51916 if (clusterTo !== undefined) { 51917 _this4._clusterEdges(_this4.body.nodes[edge.toId], edge, clusterTo); 51918 } 51919 51920 // TODO: check that it works for both edges clustered 51921 // (This might be paranoia) 51922 } else { 51923 // This should not be happening, the state should 51924 // be properly updated at this point. 51925 // 51926 // If it *is* reached during normal operation, then we have to implement 51927 // undo clustering for this edge here. 51928 throw new Error('remove edge from clustering not implemented!'); 51929 } 51930 }); 51931 51932 // Clusters may be nested to any level. Keep on opening until nothing to open 51933 var changed = false; 51934 var continueLoop = true; 51935 51936 var _loop2 = function _loop2() { 51937 var clustersToOpen = []; 51938 51939 // Determine the id's of clusters that need opening 51940 eachClusterNode(function (clusterNode) { 51941 var numNodes = (0, _keys2['default'])(clusterNode.containedNodes).length; 51942 var allowSingle = clusterNode.options.allowSingleNodeCluster === true; 51943 if (allowSingle && numNodes < 1 || !allowSingle && numNodes < 2) { 51944 clustersToOpen.push(clusterNode.id); 51945 } 51946 }); 51947 51948 // Open them 51949 for (var _n = 0; _n < clustersToOpen.length; ++_n) { 51950 _this4.openCluster(clustersToOpen[_n], {}, false /* Don't refresh, we're in an refresh/update already */); 51951 } 51952 51953 continueLoop = clustersToOpen.length > 0; 51954 changed = changed || continueLoop; 51955 }; 51956 51957 while (continueLoop) { 51958 _loop2(); 51959 } 51960 51961 if (changed) { 51962 this._updateState(); // Redo this method (recursion possible! should be safe) 51963 } 51964 } 51965 51966 /** 51967 * Determine if node with given id is part of a cluster. 51968 * 51969 * @param {Node.id} nodeId 51970 * @return {boolean} true if part of a cluster. 51971 */ 51972 51973 }, { 51974 key: '_isClusteredNode', 51975 value: function _isClusteredNode(nodeId) { 51976 return this.clusteredNodes[nodeId] !== undefined; 51977 } 51978 51979 /** 51980 * Determine if edge with given id is not visible due to clustering. 51981 * 51982 * An edge is considered clustered if: 51983 * - it is directly replaced by a clustering edge 51984 * - any of its connecting nodes is in a cluster 51985 * 51986 * @param {vis.Edge.id} edgeId 51987 * @return {boolean} true if part of a cluster. 51988 */ 51989 51990 }, { 51991 key: '_isClusteredEdge', 51992 value: function _isClusteredEdge(edgeId) { 51993 return this.clusteredEdges[edgeId] !== undefined; 51994 } 51995 }]); 51996 return ClusterEngine; 51997}(); 51998 51999exports['default'] = ClusterEngine; 52000 52001/***/ }), 52002/* 228 */ 52003/***/ (function(module, exports, __webpack_require__) { 52004 52005"use strict"; 52006 52007 52008Object.defineProperty(exports, "__esModule", { 52009 value: true 52010}); 52011 52012var _getPrototypeOf = __webpack_require__(3); 52013 52014var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf); 52015 52016var _classCallCheck2 = __webpack_require__(0); 52017 52018var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); 52019 52020var _createClass2 = __webpack_require__(1); 52021 52022var _createClass3 = _interopRequireDefault(_createClass2); 52023 52024var _possibleConstructorReturn2 = __webpack_require__(4); 52025 52026var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2); 52027 52028var _inherits2 = __webpack_require__(5); 52029 52030var _inherits3 = _interopRequireDefault(_inherits2); 52031 52032function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } 52033 52034var util = __webpack_require__(2); 52035var Node = __webpack_require__(47)["default"]; 52036 52037/** 52038 * A Cluster is a special Node that allows a group of Nodes positioned closely together 52039 * to be represented by a single Cluster Node. 52040 * 52041 * @extends Node 52042 */ 52043 52044var Cluster = function (_Node) { 52045 (0, _inherits3["default"])(Cluster, _Node); 52046 52047 /** 52048 * @param {Object} options 52049 * @param {Object} body 52050 * @param {Array.<HTMLImageElement>}imagelist 52051 * @param {Array} grouplist 52052 * @param {Object} globalOptions 52053 * @param {Object} defaultOptions Global default options for nodes 52054 */ 52055 function Cluster(options, body, imagelist, grouplist, globalOptions, defaultOptions) { 52056 (0, _classCallCheck3["default"])(this, Cluster); 52057 52058 var _this = (0, _possibleConstructorReturn3["default"])(this, (Cluster.__proto__ || (0, _getPrototypeOf2["default"])(Cluster)).call(this, options, body, imagelist, grouplist, globalOptions, defaultOptions)); 52059 52060 _this.isCluster = true; 52061 _this.containedNodes = {}; 52062 _this.containedEdges = {}; 52063 return _this; 52064 } 52065 52066 /** 52067 * Transfer child cluster data to current and disconnect the child cluster. 52068 * 52069 * Please consult the header comment in 'Clustering.js' for the fields set here. 52070 * 52071 * @param {string|number} childClusterId id of child cluster to open 52072 */ 52073 52074 52075 (0, _createClass3["default"])(Cluster, [{ 52076 key: "_openChildCluster", 52077 value: function _openChildCluster(childClusterId) { 52078 var _this2 = this; 52079 52080 var childCluster = this.body.nodes[childClusterId]; 52081 if (this.containedNodes[childClusterId] === undefined) {
52082 throw new Error('node with id: ' + childClusterId + ' not in current cluster'); 52083 } 52084 if (!childCluster.isCluster) { 52085 throw new Error('node with id: ' + childClusterId + ' is not a cluster'); 52086 } 52087 52088 // Disconnect child cluster from current cluster 52089 delete this.containedNodes[childClusterId]; 52090 util.forEach(childCluster.edges, function (edge) { 52091 delete _this2.containedEdges[edge.id]; 52092 }); 52093 52094 // Transfer nodes and edges 52095 util.forEach(childCluster.containedNodes, function (node, nodeId) { 52096 _this2.containedNodes[nodeId] = node; 52097 }); 52098 childCluster.containedNodes = {}; 52099 52100 util.forEach(childCluster.containedEdges, function (edge, edgeId) { 52101 _this2.containedEdges[edgeId] = edge; 52102 }); 52103 childCluster.containedEdges = {}; 52104 52105 // Transfer edges within cluster edges which are clustered 52106 util.forEach(childCluster.edges, function (clusterEdge) { 52107 util.forEach(_this2.edges, function (parentClusterEdge) { 52108 // Assumption: a clustered edge can only be present in a single clustering edge 52109 // Not tested here 52110 var index = parentClusterEdge.clusteringEdgeReplacingIds.indexOf(clusterEdge.id); 52111 if (index === -1) return; 52112 52113 util.forEach(clusterEdge.clusteringEdgeReplacingIds, function (srcId) { 52114 parentClusterEdge.clusteringEdgeReplacingIds.push(srcId); 52115 52116 // Maintain correct bookkeeping for transferred edge 52117 _this2.body.edges[srcId].edgeReplacedById = parentClusterEdge.id; 52118 }); 52119 52120 // Remove cluster edge from parent cluster edge 52121 parentClusterEdge.clusteringEdgeReplacingIds.splice(index, 1); 52122 }); 52123 }); 52124 childCluster.edges = []; 52125 } 52126 }]); 52127 return Cluster; 52128}(Node); 52129 52130exports["default"] = Cluster; 52131 52132/***/ }), 52133/* 229 */ 52134/***/ (function(module, exports, __webpack_require__) { 52135 52136"use strict"; 52137 52138 52139Object.defineProperty(exports, "__esModule", { 52140 value: true 52141}); 52142 52143var _classCallCheck2 = __webpack_require__(0); 52144 52145var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); 52146 52147var _createClass2 = __webpack_require__(1); 52148 52149var _createClass3 = _interopRequireDefault(_createClass2); 52150 52151function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } 52152 52153/** 52154 * Initializes window.requestAnimationFrame() to a usable form. 52155 * 52156 * Specifically, set up this method for the case of running on node.js with jsdom enabled. 52157 * 52158 * NOTES: 52159 * 52160 * * On node.js, when calling this directly outside of this class, `window` is not defined. 52161 * This happens even if jsdom is used. 52162 * * For node.js + jsdom, `window` is available at the moment the constructor is called. 52163 * For this reason, the called is placed within the constructor. 52164 * * Even then, `window.requestAnimationFrame()` is not defined, so it still needs to be added. 52165 * * During unit testing, it happens that the window object is reset during execution, causing 52166 * a runtime error due to missing `requestAnimationFrame()`. This needs to be compensated for, 52167 * see `_requestNextFrame()`. 52168 * * Since this is a global object, it may affect other modules besides `Network`. With normal 52169 * usage, this does not cause any problems. During unit testing, errors may occur. These have 52170 * been compensated for, see comment block in _requestNextFrame(). 52171 * 52172 * @private 52173 */ 52174function _initRequestAnimationFrame() { 52175 var func; 52176 52177 if (window !== undefined) { 52178 func = window.requestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame || window.msRequestAnimationFrame; 52179 } 52180 52181 if (func === undefined) { 52182 // window or method not present, setting mock requestAnimationFrame 52183 window.requestAnimationFrame = function (callback) { 52184 //console.log("Called mock requestAnimationFrame"); 52185 callback(); 52186 }; 52187 } else { 52188 window.requestAnimationFrame = func; 52189 } 52190} 52191 52192var util = __webpack_require__(2); 52193 52194/** 52195 * The canvas renderer 52196 */ 52197 52198var CanvasRenderer = function () { 52199 /** 52200 * @param {Object} body 52201 * @param {Canvas} canvas 52202 */ 52203 function CanvasRenderer(body, canvas) { 52204 (0, _classCallCheck3["default"])(this, CanvasRenderer); 52205 52206 _initRequestAnimationFrame(); 52207 this.body = body; 52208 this.canvas = canvas; 52209 52210 this.redrawRequested = false;
52211 this.renderTimer = undefined; 52212 this.requiresTimeout = true; 52213 this.renderingActive = false; 52214 this.renderRequests = 0; 52215 this.allowRedraw = true; 52216 52217 this.dragging = false; 52218 this.options = {}; 52219 this.defaultOptions = { 52220 hideEdgesOnDrag: false, 52221 hideNodesOnDrag: false 52222 }; 52223 util.extend(this.options, this.defaultOptions); 52224 52225 this._determineBrowserMethod(); 52226 this.bindEventListeners(); 52227 } 52228 52229 /** 52230 * Binds event listeners 52231 */ 52232 52233 52234 (0, _createClass3["default"])(CanvasRenderer, [{ 52235 key: "bindEventListeners", 52236 value: function bindEventListeners() { 52237 var _this = this; 52238 52239 this.body.emitter.on("dragStart", function () { 52240 _this.dragging = true; 52241 }); 52242 this.body.emitter.on("dragEnd", function () { 52243 _this.dragging = false; 52244 }); 52245 this.body.emitter.on("_resizeNodes", function () { 52246 _this._resizeNodes(); 52247 }); 52248 this.body.emitter.on("_redraw", function () { 52249 if (_this.renderingActive === false) { 52250 _this._redraw(); 52251 } 52252 }); 52253 this.body.emitter.on("_blockRedraw", function () { 52254 _this.allowRedraw = false; 52255 }); 52256 this.body.emitter.on("_allowRedraw", function () { 52257 _this.allowRedraw = true;_this.redrawRequested = false; 52258 }); 52259 this.body.emitter.on("_requestRedraw", this._requestRedraw.bind(this)); 52260 this.body.emitter.on("_startRendering", function () { 52261 _this.renderRequests += 1; 52262 _this.renderingActive = true; 52263 _this._startRendering(); 52264 }); 52265 this.body.emitter.on("_stopRendering", function () { 52266 _this.renderRequests -= 1; 52267 _this.renderingActive = _this.renderRequests > 0; 52268 _this.renderTimer = undefined; 52269 }); 52270 this.body.emitter.on('destroy', function () { 52271 _this.renderRequests = 0; 52272 _this.allowRedraw = false; 52273 _this.renderingActive = false; 52274 if (_this.requiresTimeout === true) { 52275 clearTimeout(_this.renderTimer); 52276 } else { 52277 window.cancelAnimationFrame(_this.renderTimer); 52278 } 52279 _this.body.emitter.off(); 52280 }); 52281 } 52282 52283 /** 52284 * 52285 * @param {Object} options 52286 */ 52287 52288 }, { 52289 key: "setOptions", 52290 value: function setOptions(options) { 52291 if (options !== undefined) { 52292 var fields = ['hideEdgesOnDrag', 'hideNodesOnDrag']; 52293 util.selectiveDeepExtend(fields, this.options, options); 52294 } 52295 } 52296 52297 /** 52298 * Prepare the drawing of the next frame. 52299 * 52300 * Calls the callback when the next frame can or will be drawn. 52301 * 52302 * @param {function} callback 52303 * @param {number} delay - timeout case only, wait this number of milliseconds 52304 * @returns {function|undefined} 52305 * @private 52306 */ 52307 52308 }, { 52309 key: "_requestNextFrame", 52310 value: function _requestNextFrame(callback, delay) { 52311 // During unit testing, it happens that the mock window object is reset while 52312 // the next frame is still pending. Then, either 'window' is not present, or 52313 // 'requestAnimationFrame()' is not present because it is not defined on the 52314 // mock window object. 52315 // 52316 // As a consequence, unrelated unit tests may appear to fail, even if the problem 52317 // described happens in the current unit test. 52318 // 52319 // This is not something that will happen in normal operation, but we still need 52320 // to take it into account. 52321 // 52322 if (typeof window === 'undefined') return; // Doing `if (window === undefined)` does not work here! 52323 52324 var timer = void 0; 52325 52326 var myWindow = window; // Grab a reference to reduce the possibility that 'window' is reset 52327 // while running this method. 52328 52329 if (this.requiresTimeout === true) { 52330 // wait given number of milliseconds and perform the animation step function 52331 timer = myWindow.setTimeout(callback, delay); 52332 } else { 52333 if (myWindow.requestAnimationFrame) { 52334 timer = myWindow.requestAnimationFrame(callback); 52335 } 52336 } 52337 52338 return timer; 52339 } 52340 52341 /** 52342 * 52343 * @private 52344 */ 52345 52346 }, { 52347 key: "_startRendering", 52348 value: function _startRendering() { 52349 if (this.renderingActive === true) { 52350 if (this.renderTimer === undefined) { 52351 this.renderTimer = this._requestNextFrame(this._renderStep.bind(this), this.simulationInterval); 52352 } 52353 } 52354 } 52355 52356 /** 52357 * 52358 * @private 52359 */ 52360 52361 }, { 52362 key: "_renderStep", 52363 value: function _renderStep() { 52364 if (this.renderingActive === true) { 52365 // reset the renderTimer so a new scheduled animation step can be set 52366 this.renderTimer = undefined; 52367 52368 if (this.requiresTimeout === true) { 52369 // this schedules a new simulation step 52370 this._startRendering(); 52371 } 52372 52373 this._redraw(); 52374 52375 if (this.requiresTimeout === false) { 52376 // this schedules a new simulation step 52377 this._startRendering(); 52378 } 52379 } 52380 } 52381 52382 /** 52383 * Redraw the network with the current data 52384 * chart will be resized too. 52385 */ 52386 52387 }, { 52388 key: "redraw", 52389 value: function redraw() { 52390 this.body.emitter.emit('setSize'); 52391 this._redraw(); 52392 } 52393 52394 /** 52395 * Redraw the network with the current data 52396 * @private 52397 */ 52398 52399 }, { 52400 key: "_requestRedraw", 52401 value: function _requestRedraw() { 52402 var _this2 = this; 52403 52404 if (this.redrawRequested !== true && this.renderingActive === false && this.allowRedraw === true) { 52405 this.redrawRequested = true; 52406 this._requestNextFrame(function () { 52407 _this2._redraw(false); 52408 }, 0); 52409 } 52410 } 52411 52412 /** 52413 * Redraw the network with the current data 52414 * @param {boolean} [hidden=false] | Used to get the first estimate of the node sizes. 52415 * Only the nodes are drawn after which they are quickly drawn over. 52416 * @private 52417 */ 52418 52419 }, { 52420 key: "_redraw", 52421 value: function _redraw() { 52422 var hidden = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
52423 52424 if (this.allowRedraw === true) { 52425 this.body.emitter.emit("initRedraw"); 52426 52427 this.redrawRequested = false; 52428 52429 // when the container div was hidden, this fixes it back up! 52430 if (this.canvas.frame.canvas.width === 0 || this.canvas.frame.canvas.height === 0) { 52431 this.canvas.setSize(); 52432 } 52433 52434 this.canvas.setTransform(); 52435 52436 var ctx = this.canvas.getContext(); 52437 52438 // clear the canvas 52439 var w = this.canvas.frame.canvas.clientWidth; 52440 var h = this.canvas.frame.canvas.clientHeight; 52441 ctx.clearRect(0, 0, w, h); 52442 52443 // if the div is hidden, we stop the redraw here for performance. 52444 if (this.canvas.frame.clientWidth === 0) { 52445 return; 52446 } 52447 52448 // set scaling and translation 52449 ctx.save(); 52450 ctx.translate(this.body.view.translation.x, this.body.view.translation.y); 52451 ctx.scale(this.body.view.scale, this.body.view.scale); 52452 52453 ctx.beginPath(); 52454 this.body.emitter.emit("beforeDrawing", ctx); 52455 ctx.closePath(); 52456 52457 if (hidden === false) { 52458 if (this.dragging === false || this.dragging === true && this.options.hideEdgesOnDrag === false) { 52459 this._drawEdges(ctx); 52460 } 52461 } 52462 52463 if (this.dragging === false || this.dragging === true && this.options.hideNodesOnDrag === false) { 52464 this._drawNodes(ctx, hidden); 52465 } 52466 52467 ctx.beginPath(); 52468 this.body.emitter.emit("afterDrawing", ctx); 52469 ctx.closePath(); 52470 52471 // restore original scaling and translation 52472 ctx.restore(); 52473 if (hidden === true) { 52474 ctx.clearRect(0, 0, w, h); 52475 } 52476 } 52477 } 52478 52479 /** 52480 * Redraw all nodes 52481 * 52482 * @param {CanvasRenderingContext2D} ctx 52483 * @param {boolean} [alwaysShow] 52484 * @private 52485 */ 52486 52487 }, { 52488 key: "_resizeNodes", 52489 value: function _resizeNodes() { 52490 this.canvas.setTransform(); 52491 var ctx = this.canvas.getContext(); 52492 ctx.save(); 52493 ctx.translate(this.body.view.translation.x, this.body.view.translation.y); 52494 ctx.scale(this.body.view.scale, this.body.view.scale); 52495 52496 var nodes = this.body.nodes; 52497 var node = void 0; 52498 52499 // resize all nodes 52500 for (var nodeId in nodes) { 52501 if (nodes.hasOwnProperty(nodeId)) { 52502 node = nodes[nodeId]; 52503 node.resize(ctx); 52504 node.updateBoundingBox(ctx, node.selected); 52505 } 52506 } 52507 52508 // restore original scaling and translation 52509 ctx.restore(); 52510 } 52511 52512 /** 52513 * Redraw all nodes 52514 * 52515 * @param {CanvasRenderingContext2D} ctx 2D context of a HTML canvas 52516 * @param {boolean} [alwaysShow] 52517 * @private 52518 */ 52519 52520 }, { 52521 key: "_drawNodes", 52522 value: function _drawNodes(ctx) { 52523 var alwaysShow = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; 52524 52525 var nodes = this.body.nodes; 52526 var nodeIndices = this.body.nodeIndices; 52527 var node = void 0; 52528 var selected = []; 52529 var margin = 20; 52530 var topLeft = this.canvas.DOMtoCanvas({ x: -margin, y: -margin }); 52531 var bottomRight = this.canvas.DOMtoCanvas({ 52532 x: this.canvas.frame.canvas.clientWidth + margin, 52533 y: this.canvas.frame.canvas.clientHeight + margin 52534 }); 52535 var viewableArea = { top: topLeft.y, left: topLeft.x, bottom: bottomRight.y, right: bottomRight.x }; 52536 52537 // draw unselected nodes; 52538 for (var i = 0; i < nodeIndices.length; i++) { 52539 node = nodes[nodeIndices[i]]; 52540 // set selected nodes aside 52541 if (node.isSelected()) { 52542 selected.push(nodeIndices[i]); 52543 } else { 52544 if (alwaysShow === true) { 52545 node.draw(ctx); 52546 } else if (node.isBoundingBoxOverlappingWith(viewableArea) === true) { 52547 node.draw(ctx); 52548 } else { 52549 node.updateBoundingBox(ctx, node.selected); 52550 } 52551 } 52552 } 52553 52554 // draw the selected nodes on top 52555 for (var _i = 0; _i < selected.length; _i++) { 52556 node = nodes[selected[_i]]; 52557 node.draw(ctx); 52558 } 52559 } 52560 52561 /** 52562 * Redraw all edges 52563 * @param {CanvasRenderingContext2D} ctx 2D context of a HTML canvas 52564 * @private 52565 */ 52566 52567 }, { 52568 key: "_drawEdges", 52569 value: function _drawEdges(ctx) { 52570 var edges = this.body.edges; 52571 var edgeIndices = this.body.edgeIndices; 52572 var edge = void 0; 52573 52574 for (var i = 0; i < edgeIndices.length; i++) { 52575 edge = edges[edgeIndices[i]]; 52576 if (edge.connected === true) { 52577 edge.draw(ctx); 52578 } 52579 } 52580 } 52581 52582 /** 52583 * Determine if the browser requires a setTimeout or a requestAnimationFrame. This was required because 52584 * some implementations (safari and IE9) did not support requestAnimationFrame 52585 * @private 52586 */ 52587 52588 }, { 52589 key: "_determineBrowserMethod", 52590 value: function _determineBrowserMethod() { 52591 if (typeof window !== 'undefined') { 52592 var browserType = navigator.userAgent.toLowerCase(); 52593 this.requiresTimeout = false;
52594 if (browserType.indexOf('msie 9.0') != -1) { 52595 // IE 9 52596 this.requiresTimeout = true; 52597 } else if (browserType.indexOf('safari') != -1) { 52598 // safari 52599 if (browserType.indexOf('chrome') <= -1) { 52600 this.requiresTimeout = true; 52601 } 52602 } 52603 } else { 52604 this.requiresTimeout = true; 52605 } 52606 } 52607 }]); 52608 return CanvasRenderer; 52609}(); 52610 52611exports["default"] = CanvasRenderer; 52612 52613/***/ }), 52614/* 230 */ 52615/***/ (function(module, exports, __webpack_require__) { 52616 52617"use strict"; 52618 52619 52620Object.defineProperty(exports, "__esModule", { 52621 value: true 52622}); 52623 52624var _classCallCheck2 = __webpack_require__(0); 52625 52626var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); 52627 52628var _createClass2 = __webpack_require__(1); 52629 52630var _createClass3 = _interopRequireDefault(_createClass2); 52631 52632function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 52633 52634var Hammer = __webpack_require__(10); 52635var hammerUtil = __webpack_require__(37); 52636 52637var util = __webpack_require__(2); 52638 52639/** 52640 * Create the main frame for the Network. 52641 * This function is executed once when a Network object is created. The frame 52642 * contains a canvas, and this canvas contains all objects like the axis and 52643 * nodes. 52644 */ 52645 52646var Canvas = function () { 52647 /** 52648 * @param {Object} body 52649 */ 52650 function Canvas(body) { 52651 (0, _classCallCheck3['default'])(this, Canvas); 52652 52653 this.body = body; 52654 this.pixelRatio = 1; 52655 this.resizeTimer = undefined; 52656 this.resizeFunction = this._onResize.bind(this); 52657 this.cameraState = {}; 52658 this.initialized = false; 52659 this.canvasViewCenter = {}; 52660 52661 this.options = {}; 52662 this.defaultOptions = { 52663 autoResize: true, 52664 height: '100%', 52665 width: '100%' 52666 }; 52667 util.extend(this.options, this.defaultOptions); 52668 52669 this.bindEventListeners(); 52670 } 52671 52672 /** 52673 * Binds event listeners 52674 */ 52675 52676 52677 (0, _createClass3['default'])(Canvas, [{ 52678 key: 'bindEventListeners', 52679 value: function bindEventListeners() { 52680 var _this = this; 52681 52682 // bind the events 52683 this.body.emitter.once("resize", function (obj) { 52684 if (obj.width !== 0) { 52685 _this.body.view.translation.x = obj.width * 0.5; 52686 } 52687 if (obj.height !== 0) { 52688 _this.body.view.translation.y = obj.height * 0.5; 52689 } 52690 }); 52691 this.body.emitter.on("setSize", this.setSize.bind(this)); 52692 this.body.emitter.on("destroy", function () { 52693 _this.hammerFrame.destroy(); 52694 _this.hammer.destroy(); 52695 _this._cleanUp(); 52696 }); 52697 } 52698 52699 /** 52700 * @param {Object} options 52701 */ 52702 52703 }, { 52704 key: 'setOptions', 52705 value: function setOptions(options) { 52706 var _this2 = this; 52707 52708 if (options !== undefined) { 52709 var fields = ['width', 'height', 'autoResize']; 52710 util.selectiveDeepExtend(fields, this.options, options); 52711 } 52712 52713 if (this.options.autoResize === true) { 52714 // automatically adapt to a changing size of the browser. 52715 this._cleanUp(); 52716 this.resizeTimer = setInterval(function () { 52717 var changed = _this2.setSize(); 52718 if (changed === true) { 52719 _this2.body.emitter.emit("_requestRedraw"); 52720 } 52721 }, 1000); 52722 this.resizeFunction = this._onResize.bind(this); 52723 util.addEventListener(window, 'resize', this.resizeFunction); 52724 } 52725 } 52726 52727 /** 52728 * @private 52729 */ 52730 52731 }, { 52732 key: '_cleanUp', 52733 value: function _cleanUp() { 52734 // automatically adapt to a changing size of the browser. 52735 if (this.resizeTimer !== undefined) { 52736 clearInterval(this.resizeTimer); 52737 } 52738 util.removeEventListener(window, 'resize', this.resizeFunction); 52739 this.resizeFunction = undefined; 52740 } 52741 52742 /** 52743 * @private 52744 */ 52745 52746 }, { 52747 key: '_onResize', 52748 value: function _onResize() { 52749 this.setSize(); 52750 this.body.emitter.emit("_redraw"); 52751 } 52752 52753 /** 52754 * Get and store the cameraState 52755 * 52756 * @param {number} [pixelRatio=this.pixelRatio] 52757 * @private 52758 */ 52759 52760 }, { 52761 key: '_getCameraState', 52762 value: function _getCameraState() { 52763 var pixelRatio = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : this.pixelRatio; 52764 52765 if (this.initialized === true) { 52766 this.cameraState.previousWidth = this.frame.canvas.width / pixelRatio; 52767 this.cameraState.previousHeight = this.frame.canvas.height / pixelRatio; 52768 this.cameraState.scale = this.body.view.scale; 52769 this.cameraState.position = this.DOMtoCanvas({ 52770 x: 0.5 * this.frame.canvas.width / pixelRatio, 52771 y: 0.5 * this.frame.canvas.height / pixelRatio 52772 }); 52773 } 52774 } 52775 52776 /** 52777 * Set the cameraState 52778 * @private 52779 */ 52780 52781 }, { 52782 key: '_setCameraState', 52783 value: function _setCameraState() { 52784 if (this.cameraState.scale !== undefined && this.frame.canvas.clientWidth !== 0 && this.frame.canvas.clientHeight !== 0 && this.pixelRatio !== 0 && this.cameraState.previousWidth > 0) { 52785 52786 var widthRatio = this.frame.canvas.width / this.pixelRatio / this.cameraState.previousWidth; 52787 var heightRatio = this.frame.canvas.height / this.pixelRatio / this.cameraState.previousHeight; 52788 var newScale = this.cameraState.scale; 52789 52790 if (widthRatio != 1 && heightRatio != 1) { 52791 newScale = this.cameraState.scale * 0.5 * (widthRatio + heightRatio); 52792 } else if (widthRatio != 1) { 52793 newScale = this.cameraState.scale * widthRatio; 52794 } else if (heightRatio != 1) { 52795 newScale = this.cameraState.scale * heightRatio; 52796 } 52797 52798 this.body.view.scale = newScale; 52799 // this comes from the view module. 52800 var currentViewCenter = this.DOMtoCanvas({ 52801 x: 0.5 * this.frame.canvas.clientWidth, 52802 y: 0.5 * this.frame.canvas.clientHeight 52803 }); 52804 52805 var distanceFromCenter = { // offset from view, distance view has to change by these x and y to center the node 52806 x: currentViewCenter.x - this.cameraState.position.x, 52807 y: currentViewCenter.y - this.cameraState.position.y 52808 }; 52809 this.body.view.translation.x += distanceFromCenter.x * this.body.view.scale; 52810 this.body.view.translation.y += distanceFromCenter.y * this.body.view.scale; 52811 } 52812 } 52813 52814 /** 52815 * 52816 * @param {number|string} value 52817 * @returns {string} 52818 * @private 52819 */ 52820 52821 }, { 52822 key: '_prepareValue', 52823 value: function _prepareValue(value) { 52824 if (typeof value === 'number') { 52825 return value + 'px'; 52826 } else if (typeof value === 'string') { 52827 if (value.indexOf('%') !== -1 || value.indexOf('px') !== -1) { 52828 return value; 52829 } else if (value.indexOf('%') === -1) { 52830 return value + 'px'; 52831 } 52832 } 52833 throw new Error('Could not use the value supplied for width or height:' + value); 52834 } 52835 52836 /** 52837 * Create the HTML 52838 */ 52839 52840 }, { 52841 key: '_create', 52842 value: function _create() { 52843 // remove all elements from the container element. 52844 while (this.body.container.hasChildNodes()) { 52845 this.body.container.removeChild(this.body.container.firstChild); 52846 } 52847 52848 this.frame = document.createElement('div'); 52849 this.frame.className = 'vis-network'; 52850 this.frame.style.position = 'relative'; 52851 this.frame.style.overflow = 'hidden'; 52852 this.frame.tabIndex = 900; // tab index is required for keycharm to bind keystrokes to the div instead of the window 52853
52854 ////////////////////////////////////////////////////////////////// 52855 52856 this.frame.canvas = document.createElement("canvas"); 52857 this.frame.canvas.style.position = 'relative'; 52858 this.frame.appendChild(this.frame.canvas); 52859 52860 if (!this.frame.canvas.getContext) { 52861 var noCanvas = document.createElement('DIV'); 52862 noCanvas.style.color = 'red'; 52863 noCanvas.style.fontWeight = 'bold'; 52864 noCanvas.style.padding = '10px'; 52865 noCanvas.innerHTML = 'Error: your browser does not support HTML canvas'; 52866 this.frame.canvas.appendChild(noCanvas); 52867 } else { 52868 this._setPixelRatio(); 52869 this.setTransform(); 52870 } 52871 52872 // add the frame to the container element 52873 this.body.container.appendChild(this.frame); 52874 52875 this.body.view.scale = 1; 52876 this.body.view.translation = { x: 0.5 * this.frame.canvas.clientWidth, y: 0.5 * this.frame.canvas.clientHeight }; 52877 52878 this._bindHammer(); 52879 } 52880 52881 /** 52882 * This function binds hammer, it can be repeated over and over due to the uniqueness check. 52883 * @private 52884 */ 52885 52886 }, { 52887 key: '_bindHammer', 52888 value: function _bindHammer() { 52889 var _this3 = this; 52890 52891 if (this.hammer !== undefined) { 52892 this.hammer.destroy(); 52893 } 52894 this.drag = {}; 52895 this.pinch = {}; 52896 52897 // init hammer 52898 this.hammer = new Hammer(this.frame.canvas); 52899 this.hammer.get('pinch').set({ enable: true }); 52900 // enable to get better response, todo: test on mobile. 52901 this.hammer.get('pan').set({ threshold: 5, direction: Hammer.DIRECTION_ALL }); 52902 52903 hammerUtil.onTouch(this.hammer, function (event) { 52904 _this3.body.eventListeners.onTouch(event); 52905 }); 52906 this.hammer.on('tap', function (event) { 52907 _this3.body.eventListeners.onTap(event); 52908 }); 52909 this.hammer.on('doubletap', function (event) { 52910 _this3.body.eventListeners.onDoubleTap(event); 52911 }); 52912 this.hammer.on('press', function (event) { 52913 _this3.body.eventListeners.onHold(event); 52914 }); 52915 this.hammer.on('panstart', function (event) { 52916 _this3.body.eventListeners.onDragStart(event); 52917 }); 52918 this.hammer.on('panmove', function (event) { 52919 _this3.body.eventListeners.onDrag(event); 52920 }); 52921 this.hammer.on('panend', function (event) { 52922 _this3.body.eventListeners.onDragEnd(event); 52923 }); 52924 this.hammer.on('pinch', function (event) { 52925 _this3.body.eventListeners.onPinch(event); 52926 }); 52927 52928 // TODO: neatly cleanup these handlers when re-creating the Canvas, IF these are done with hammer, event.stopPropagation will not work? 52929 this.frame.canvas.addEventListener('mousewheel', function (event) { 52930 _this3.body.eventListeners.onMouseWheel(event); 52931 }); 52932 this.frame.canvas.addEventListener('DOMMouseScroll', function (event) { 52933 _this3.body.eventListeners.onMouseWheel(event); 52934 }); 52935 52936 this.frame.canvas.addEventListener('mousemove', function (event) { 52937 _this3.body.eventListeners.onMouseMove(event); 52938 }); 52939 this.frame.canvas.addEventListener('contextmenu', function (event) { 52940 _this3.body.eventListeners.onContext(event); 52941 }); 52942 52943 this.hammerFrame = new Hammer(this.frame); 52944 hammerUtil.onRelease(this.hammerFrame, function (event) { 52945 _this3.body.eventListeners.onRelease(event); 52946 }); 52947 } 52948 52949 /** 52950 * Set a new size for the network 52951 * @param {string} width Width in pixels or percentage (for example '800px' 52952 * or '50%') 52953 * @param {string} height Height in pixels or percentage (for example '400px' 52954 * or '30%') 52955 * @returns {boolean} 52956 */ 52957 52958 }, { 52959 key: 'setSize', 52960 value: function setSize() { 52961 var width = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : this.options.width; 52962 var height = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : this.options.height; 52963 52964 width = this._prepareValue(width); 52965 height = this._prepareValue(height); 52966 52967 var emitEvent = false; 52968 var oldWidth = this.frame.canvas.width; 52969 var oldHeight = this.frame.canvas.height; 52970 52971 // update the pixel ratio 52972 // 52973 // NOTE: Comment in following is rather inconsistent; this is the ONLY place in the code 52974 // where it is assumed that the pixel ratio could change at runtime. 52975 // The only way I can think of this happening is a rotating screen or tablet; but then 52976 // there should be a mechanism for reloading the data (TODO: check if this is present). 52977 // 52978 // If the assumption is true (i.e. pixel ratio can change at runtime), then *all* usage 52979 // of pixel ratio must be overhauled for this. 52980 // 52981 // For the time being, I will humor the assumption here, and in the rest of the code assume it is 52982 // constant. 52983 var previousRatio = this.pixelRatio; // we cache this because the camera state storage needs the old value 52984 this._setPixelRatio(); 52985 52986 if (width != this.options.width || height != this.options.height || this.frame.style.width != width || this.frame.style.height != height) { 52987 this._getCameraState(previousRatio); 52988 52989 this.frame.style.width = width; 52990 this.frame.style.height = height; 52991 52992 this.frame.canvas.style.width = '100%'; 52993 this.frame.canvas.style.height = '100%'; 52994 52995 this.frame.canvas.width = Math.round(this.frame.canvas.clientWidth * this.pixelRatio); 52996 this.frame.canvas.height = Math.round(this.frame.canvas.clientHeight * this.pixelRatio); 52997 52998 this.options.width = width; 52999 this.options.height = height; 53000 53001 this.canvasViewCenter = { 53002 x: 0.5 * this.frame.clientWidth, 53003 y: 0.5 * this.frame.clientHeight 53004 }; 53005 53006 emitEvent = true; 53007 } else { 53008 // this would adapt the width of the canvas to the width from 100% if and only if 53009 // there is a change. 53010 53011 var newWidth = Math.round(this.frame.canvas.clientWidth * this.pixelRatio); 53012 var newHeight = Math.round(this.frame.canvas.clientHeight * this.pixelRatio); 53013 53014 // store the camera if there is a change in size. 53015 if (this.frame.canvas.width !== newWidth || this.frame.canvas.height !== newHeight) { 53016 this._getCameraState(previousRatio); 53017 } 53018 53019 if (this.frame.canvas.width !== newWidth) { 53020 this.frame.canvas.width = newWidth; 53021 emitEvent = true; 53022 } 53023 if (this.frame.canvas.height !== newHeight) { 53024 this.frame.canvas.height = newHeight; 53025 emitEvent = true; 53026 } 53027 } 53028 53029 if (emitEvent === true) { 53030 this.body.emitter.emit('resize', { 53031 width: Math.round(this.frame.canvas.width / this.pixelRatio), 53032 height: Math.round(this.frame.canvas.height / this.pixelRatio), 53033 oldWidth: Math.round(oldWidth / this.pixelRatio), 53034 oldHeight: Math.round(oldHeight / this.pixelRatio) 53035 }); 53036 53037 // restore the camera on change. 53038 this._setCameraState(); 53039 } 53040 53041 // set initialized so the get and set camera will work from now on. 53042 this.initialized = true; 53043 return emitEvent; 53044 } 53045 53046 /** 53047 * 53048 * @returns {CanvasRenderingContext2D} 53049 */ 53050 53051 }, { 53052 key: 'getContext', 53053 value: function getContext() { 53054 return this.frame.canvas.getContext("2d"); 53055 } 53056 53057 /** 53058 * Determine the pixel ratio for various browsers. 53059 * 53060 * @returns {number} 53061 * @private 53062 */ 53063 53064 }, { 53065 key: '_determinePixelRatio', 53066 value: function _determinePixelRatio() { 53067 var ctx = this.getContext(); 53068 if (ctx === undefined) { 53069 throw new Error("Could not get canvax context"); 53070 } 53071 53072 var numerator = 1; 53073 if (typeof window !== 'undefined') { 53074 // (window !== undefined) doesn't work here! 53075 // Protection during unit tests, where 'window' can be missing 53076 numerator = window.devicePixelRatio || 1; 53077 } 53078 53079 var denominator = ctx.webkitBackingStorePixelRatio || ctx.mozBackingStorePixelRatio || ctx.msBackingStorePixelRatio || ctx.oBackingStorePixelRatio || ctx.backingStorePixelRatio || 1; 53080 53081 return numerator / denominator; 53082 } 53083 53084 /** 53085 * Lazy determination of pixel ratio. 53086 * 53087 * @private 53088 */ 53089 53090 }, { 53091 key: '_setPixelRatio', 53092 value: function _setPixelRatio() { 53093 this.pixelRatio = this._determinePixelRatio(); 53094 } 53095 53096 /** 53097 * Set the transform in the contained context, based on its pixelRatio 53098 */ 53099 53100 }, { 53101 key: 'setTransform', 53102 value: function setTransform() { 53103 var ctx = this.getContext(); 53104 if (ctx === undefined) { 53105 throw new Error("Could not get canvax context"); 53106 } 53107 53108 ctx.setTransform(this.pixelRatio, 0, 0, this.pixelRatio, 0, 0); 53109 } 53110 53111 /** 53112 * Convert the X coordinate in DOM-space (coordinate point in browser relative to the container div) to 53113 * the X coordinate in canvas-space (the simulation sandbox, which the camera looks upon) 53114 * @param {number} x 53115 * @returns {number} 53116 * @private 53117 */ 53118 53119 }, { 53120 key: '_XconvertDOMtoCanvas', 53121 value: function _XconvertDOMtoCanvas(x) { 53122 return (x - this.body.view.translation.x) / this.body.view.scale; 53123 } 53124 53125 /** 53126 * Convert the X coordinate in canvas-space (the simulation sandbox, which the camera looks upon) to 53127 * the X coordinate in DOM-space (coordinate point in browser relative to the container div) 53128 * @param {number} x 53129 * @returns {number} 53130 * @private 53131 */ 53132 53133 }, { 53134 key: '_XconvertCanvasToDOM', 53135 value: function _XconvertCanvasToDOM(x) { 53136 return x * this.body.view.scale + this.body.view.translation.x; 53137 } 53138 53139 /** 53140 * Convert the Y coordinate in DOM-space (coordinate point in browser relative to the container div) to 53141 * the Y coordinate in canvas-space (the simulation sandbox, which the camera looks upon) 53142 * @param {number} y 53143 * @returns {number} 53144 * @private 53145 */ 53146 53147 }, { 53148 key: '_YconvertDOMtoCanvas', 53149 value: function _YconvertDOMtoCanvas(y) { 53150 return (y - this.body.view.translation.y) / this.body.view.scale; 53151 } 53152 53153 /** 53154 * Convert the Y coordinate in canvas-space (the simulation sandbox, which the camera looks upon) to 53155 * the Y coordinate in DOM-space (coordinate point in browser relative to the container div) 53156 * @param {number} y 53157 * @returns {number} 53158 * @private 53159 */ 53160 53161 }, { 53162 key: '_YconvertCanvasToDOM', 53163 value: function _YconvertCanvasToDOM(y) { 53164 return y * this.body.view.scale + this.body.view.translation.y; 53165 } 53166 53167 /** 53168 * @param {point} pos 53169 * @returns {point} 53170 */ 53171 53172 }, { 53173 key: 'canvasToDOM', 53174 value: function canvasToDOM(pos) { 53175 return { x: this._XconvertCanvasToDOM(pos.x), y: this._YconvertCanvasToDOM(pos.y) }; 53176 } 53177 53178 /** 53179 * 53180 * @param {point} pos 53181 * @returns {point} 53182 */ 53183 53184 }, { 53185 key: 'DOMtoCanvas', 53186 value: function DOMtoCanvas(pos) { 53187 return { x: this._XconvertDOMtoCanvas(pos.x), y: this._YconvertDOMtoCanvas(pos.y) }; 53188 } 53189 }]); 53190 return Canvas; 53191}(); 53192 53193exports['default'] = Canvas; 53194 53195/***/ }), 53196/* 231 */ 53197/***/ (function(module, exports, __webpack_require__) { 53198 53199"use strict"; 53200 53201 53202Object.defineProperty(exports, "__esModule", { 53203 value: true 53204}); 53205 53206var _classCallCheck2 = __webpack_require__(0); 53207 53208var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); 53209 53210var _createClass2 = __webpack_require__(1); 53211 53212var _createClass3 = _interopRequireDefault(_createClass2); 53213 53214function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 53215 53216var util = __webpack_require__(2); 53217 53218var NetworkUtil = __webpack_require__(76)['default']; 53219 53220/** 53221 * The view 53222 */ 53223 53224var View = function () { 53225 /** 53226 * @param {Object} body 53227 * @param {Canvas} canvas 53228 */ 53229 function View(body, canvas) { 53230 var _this = this; 53231 53232 (0, _classCallCheck3['default'])(this, View); 53233 53234 this.body = body; 53235 this.canvas = canvas; 53236 53237 this.animationSpeed = 1 / this.renderRefreshRate; 53238 this.animationEasingFunction = "easeInOutQuint"; 53239 this.easingTime = 0; 53240 this.sourceScale = 0; 53241 this.targetScale = 0; 53242 this.sourceTranslation = 0; 53243 this.targetTranslation = 0; 53244 this.lockedOnNodeId = undefined; 53245 this.lockedOnNodeOffset = undefined; 53246 this.touchTime = 0; 53247 53248 this.viewFunction = undefined; 53249 53250 this.body.emitter.on("fit", this.fit.bind(this)); 53251 this.body.emitter.on("animationFinished", function () { 53252 _this.body.emitter.emit("_stopRendering"); 53253 }); 53254 this.body.emitter.on("unlockNode", this.releaseNode.bind(this)); 53255 } 53256 53257 /** 53258 * 53259 * @param {Object} [options={}] 53260 */ 53261 53262 53263 (0, _createClass3['default'])(View, [{ 53264 key: 'setOptions', 53265 value: function setOptions() { 53266 var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; 53267 53268 this.options = options; 53269 } 53270 53271 /** 53272 * This function zooms out to fit all data on screen based on amount of nodes 53273 * @param {Object} [options={{nodes=Array}}] 53274 * @param {boolean} [initialZoom=false] | zoom based on fitted formula or range, true = fitted, default = false;
53275 */ 53276 53277 }, { 53278 key: 'fit', 53279 value: function fit() { 53280 var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : { nodes: [] }; 53281 var initialZoom = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; 53282 53283 var range = void 0; 53284 var zoomLevel = void 0; 53285 if (options.nodes === undefined || options.nodes.length === 0) { 53286 options.nodes = this.body.nodeIndices; 53287 } 53288 53289 if (initialZoom === true) { 53290 // check if more than half of the nodes have a predefined position. If so, we use the range, not the approximation. 53291 var positionDefined = 0; 53292 for (var nodeId in this.body.nodes) { 53293 if (this.body.nodes.hasOwnProperty(nodeId)) { 53294 var node = this.body.nodes[nodeId]; 53295 if (node.predefinedPosition === true) { 53296 positionDefined += 1; 53297 } 53298 } 53299 } 53300 if (positionDefined > 0.5 * this.body.nodeIndices.length) { 53301 this.fit(options, false); 53302 return; 53303 } 53304 53305 range = NetworkUtil.getRange(this.body.nodes, options.nodes); 53306 53307 var numberOfNodes = this.body.nodeIndices.length; 53308 zoomLevel = 12.662 / (numberOfNodes + 7.4147) + 0.0964822; // this is obtained from fitting a dataset from 5 points with scale levels that looked good. 53309 53310 // correct for larger canvasses. 53311 var factor = Math.min(this.canvas.frame.canvas.clientWidth / 600, this.canvas.frame.canvas.clientHeight / 600); 53312 zoomLevel *= factor; 53313 } else { 53314 this.body.emitter.emit("_resizeNodes"); 53315 range = NetworkUtil.getRange(this.body.nodes, options.nodes); 53316 53317 var xDistance = Math.abs(range.maxX - range.minX) * 1.1; 53318 var yDistance = Math.abs(range.maxY - range.minY) * 1.1; 53319 53320 var xZoomLevel = this.canvas.frame.canvas.clientWidth / xDistance; 53321 var yZoomLevel = this.canvas.frame.canvas.clientHeight / yDistance; 53322 53323 zoomLevel = xZoomLevel <= yZoomLevel ? xZoomLevel : yZoomLevel; 53324 } 53325 53326 if (zoomLevel > 1.0) { 53327 zoomLevel = 1.0; 53328 } else if (zoomLevel === 0) { 53329 zoomLevel = 1.0; 53330 } 53331 53332 var center = NetworkUtil.findCenter(range); 53333 var animationOptions = { position: center, scale: zoomLevel, animation: options.animation }; 53334 this.moveTo(animationOptions); 53335 } 53336 53337 // animation 53338 53339 /** 53340 * Center a node in view. 53341 * 53342 * @param {number} nodeId 53343 * @param {number} [options] 53344 */ 53345 53346 }, { 53347 key: 'focus', 53348 value: function focus(nodeId) { 53349 var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; 53350 53351 if (this.body.nodes[nodeId] !== undefined) { 53352 var nodePosition = { x: this.body.nodes[nodeId].x, y: this.body.nodes[nodeId].y }; 53353 options.position = nodePosition; 53354 options.lockedOnNode = nodeId; 53355 53356 this.moveTo(options); 53357 } else { 53358 console.log("Node: " + nodeId + " cannot be found."); 53359 } 53360 } 53361 53362 /** 53363 * 53364 * @param {Object} options | options.offset = {x:number, y:number} // offset from the center in DOM pixels 53365 * | options.scale = number // scale to move to 53366 * | options.position = {x:number, y:number} // position to move to 53367 * | options.animation = {duration:number, easingFunction:String} || Boolean // position to move to 53368 */ 53369 53370 }, { 53371 key: 'moveTo', 53372 value: function moveTo(options) { 53373 if (options === undefined) { 53374 options = {}; 53375 return; 53376 } 53377 if (options.offset === undefined) { 53378 options.offset = { x: 0, y: 0 }; 53379 } 53380 if (options.offset.x === undefined) { 53381 options.offset.x = 0; 53382 } 53383 if (options.offset.y === undefined) { 53384 options.offset.y = 0; 53385 } 53386 if (options.scale === undefined) { 53387 options.scale = this.body.view.scale; 53388 } 53389 if (options.position === undefined) { 53390 options.position = this.getViewPosition(); 53391 } 53392 if (options.animation === undefined) { 53393 options.animation = { duration: 0 }; 53394 } 53395 if (options.animation === false) { 53396 options.animation = { duration: 0 }; 53397 } 53398 if (options.animation === true) { 53399 options.animation = {}; 53400 } 53401 if (options.animation.duration === undefined) { 53402 options.animation.duration = 1000; 53403 } // default duration 53404 if (options.animation.easingFunction === undefined) { 53405 options.animation.easingFunction = "easeInOutQuad"; 53406 } // default easing function 53407 53408 this.animateView(options); 53409 } 53410 53411 /** 53412 * 53413 * @param {Object} options | options.offset = {x:number, y:number} // offset from the center in DOM pixels 53414 * | options.time = number // animation time in milliseconds 53415 * | options.scale = number // scale to animate to 53416 * | options.position = {x:number, y:number} // position to animate to 53417 * | options.easingFunction = String // linear, easeInQuad, easeOutQuad, easeInOutQuad, 53418 * // easeInCubic, easeOutCubic, easeInOutCubic, 53419 * // easeInQuart, easeOutQuart, easeInOutQuart, 53420 * // easeInQuint, easeOutQuint, easeInOutQuint 53421 */ 53422 53423 }, { 53424 key: 'animateView', 53425 value: function animateView(options) { 53426 if (options === undefined) { 53427 return; 53428 } 53429 this.animationEasingFunction = options.animation.easingFunction; 53430 // release if something focussed on the node 53431 this.releaseNode(); 53432 if (options.locked === true) { 53433 this.lockedOnNodeId = options.lockedOnNode; 53434 this.lockedOnNodeOffset = options.offset; 53435 } 53436 53437 // forcefully complete the old animation if it was still running 53438 if (this.easingTime != 0) { 53439 this._transitionRedraw(true); // by setting easingtime to 1, we finish the animation. 53440 } 53441 53442 this.sourceScale = this.body.view.scale; 53443 this.sourceTranslation = this.body.view.translation; 53444 this.targetScale = options.scale; 53445 53446 // set the scale so the viewCenter is based on the correct zoom level. This is overridden in the transitionRedraw 53447 // but at least then we'll have the target transition 53448 this.body.view.scale = this.targetScale; 53449 var viewCenter = this.canvas.DOMtoCanvas({ x: 0.5 * this.canvas.frame.canvas.clientWidth, y: 0.5 * this.canvas.frame.canvas.clientHeight }); 53450 53451 var distanceFromCenter = { // offset from view, distance view has to change by these x and y to center the node 53452 x: viewCenter.x - options.position.x, 53453 y: viewCenter.y - options.position.y 53454 }; 53455 this.targetTranslation = { 53456 x: this.sourceTranslation.x + distanceFromCenter.x * this.targetScale + options.offset.x, 53457 y: this.sourceTranslation.y + distanceFromCenter.y * this.targetScale + options.offset.y 53458 }; 53459 53460 // if the time is set to 0, don't do an animation 53461 if (options.animation.duration === 0) { 53462 if (this.lockedOnNodeId != undefined) { 53463 this.viewFunction = this._lockedRedraw.bind(this); 53464 this.body.emitter.on("initRedraw", this.viewFunction); 53465 } else { 53466 this.body.view.scale = this.targetScale; 53467 this.body.view.translation = this.targetTranslation; 53468 this.body.emitter.emit("_requestRedraw"); 53469 } 53470 } else { 53471 this.animationSpeed = 1 / (60 * options.animation.duration * 0.001) || 1 / 60; // 60 for 60 seconds, 0.001 for milli's 53472 this.animationEasingFunction = options.animation.easingFunction; 53473 53474 this.viewFunction = this._transitionRedraw.bind(this); 53475 this.body.emitter.on("initRedraw", this.viewFunction); 53476 this.body.emitter.emit("_startRendering"); 53477 } 53478 } 53479 53480 /** 53481 * used to animate smoothly by hijacking the redraw function. 53482 * @private 53483 */ 53484 53485 }, { 53486 key: '_lockedRedraw', 53487 value: function _lockedRedraw() { 53488 var nodePosition = { x: this.body.nodes[this.lockedOnNodeId].x, y: this.body.nodes[this.lockedOnNodeId].y }; 53489 var viewCenter = this.canvas.DOMtoCanvas({ x: 0.5 * this.canvas.frame.canvas.clientWidth, y: 0.5 * this.canvas.frame.canvas.clientHeight }); 53490 var distanceFromCenter = { // offset from view, distance view has to change by these x and y to center the node 53491 x: viewCenter.x - nodePosition.x, 53492 y: viewCenter.y - nodePosition.y 53493 }; 53494 var sourceTranslation = this.body.view.translation; 53495 var targetTranslation = { 53496 x: sourceTranslation.x + distanceFromCenter.x * this.body.view.scale + this.lockedOnNodeOffset.x, 53497 y: sourceTranslation.y + distanceFromCenter.y * this.body.view.scale + this.lockedOnNodeOffset.y 53498 }; 53499 53500 this.body.view.translation = targetTranslation; 53501 } 53502 53503 /** 53504 * Resets state of a locked on Node 53505 */ 53506 53507 }, { 53508 key: 'releaseNode', 53509 value: function releaseNode() { 53510 if (this.lockedOnNodeId !== undefined && this.viewFunction !== undefined) { 53511 this.body.emitter.off("initRedraw", this.viewFunction); 53512 this.lockedOnNodeId = undefined; 53513 this.lockedOnNodeOffset = undefined; 53514 } 53515 } 53516 53517 /** 53518 * @param {boolean} [finished=false] 53519 * @private 53520 */ 53521 53522 }, { 53523 key: '_transitionRedraw', 53524 value: function _transitionRedraw() { 53525 var finished = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
53526 53527 this.easingTime += this.animationSpeed; 53528 this.easingTime = finished === true ? 1.0 : this.easingTime; 53529 53530 var progress = util.easingFunctions[this.animationEasingFunction](this.easingTime); 53531 53532 this.body.view.scale = this.sourceScale + (this.targetScale - this.sourceScale) * progress; 53533 this.body.view.translation = { 53534 x: this.sourceTranslation.x + (this.targetTranslation.x - this.sourceTranslation.x) * progress, 53535 y: this.sourceTranslation.y + (this.targetTranslation.y - this.sourceTranslation.y) * progress 53536 }; 53537 53538 // cleanup 53539 if (this.easingTime >= 1.0) { 53540 this.body.emitter.off("initRedraw", this.viewFunction); 53541 this.easingTime = 0; 53542 if (this.lockedOnNodeId != undefined) { 53543 this.viewFunction = this._lockedRedraw.bind(this); 53544 this.body.emitter.on("initRedraw", this.viewFunction); 53545 } 53546 this.body.emitter.emit("animationFinished"); 53547 } 53548 } 53549 53550 /** 53551 * 53552 * @returns {number} 53553 */ 53554 53555 }, { 53556 key: 'getScale', 53557 value: function getScale() { 53558 return this.body.view.scale; 53559 } 53560 53561 /** 53562 * 53563 * @returns {{x: number, y: number}} 53564 */ 53565 53566 }, { 53567 key: 'getViewPosition', 53568 value: function getViewPosition() { 53569 return this.canvas.DOMtoCanvas({ x: 0.5 * this.canvas.frame.canvas.clientWidth, y: 0.5 * this.canvas.frame.canvas.clientHeight }); 53570 } 53571 }]); 53572 return View; 53573}(); 53574 53575exports['default'] = View; 53576 53577/***/ }), 53578/* 232 */ 53579/***/ (function(module, exports, __webpack_require__) { 53580 53581"use strict"; 53582 53583 53584Object.defineProperty(exports, "__esModule", { 53585 value: true 53586}); 53587 53588var _classCallCheck2 = __webpack_require__(0); 53589 53590var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); 53591 53592var _createClass2 = __webpack_require__(1); 53593 53594var _createClass3 = _interopRequireDefault(_createClass2); 53595 53596function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 53597 53598var util = __webpack_require__(2); 53599var NavigationHandler = __webpack_require__(233)['default']; 53600var Popup = __webpack_require__(104)['default']; 53601 53602/** 53603 * Handler for interactions 53604 */ 53605 53606var InteractionHandler = function () { 53607 /** 53608 * @param {Object} body 53609 * @param {Canvas} canvas 53610 * @param {SelectionHandler} selectionHandler 53611 */ 53612 function InteractionHandler(body, canvas, selectionHandler) { 53613 (0, _classCallCheck3['default'])(this, InteractionHandler); 53614 53615 this.body = body; 53616 this.canvas = canvas; 53617 this.selectionHandler = selectionHandler; 53618 this.navigationHandler = new NavigationHandler(body, canvas); 53619 53620 // bind the events from hammer to functions in this object 53621 this.body.eventListeners.onTap = this.onTap.bind(this); 53622 this.body.eventListeners.onTouch = this.onTouch.bind(this); 53623 this.body.eventListeners.onDoubleTap = this.onDoubleTap.bind(this); 53624 this.body.eventListeners.onHold = this.onHold.bind(this); 53625 this.body.eventListeners.onDragStart = this.onDragStart.bind(this); 53626 this.body.eventListeners.onDrag = this.onDrag.bind(this); 53627 this.body.eventListeners.onDragEnd = this.onDragEnd.bind(this); 53628 this.body.eventListeners.onMouseWheel = this.onMouseWheel.bind(this); 53629 this.body.eventListeners.onPinch = this.onPinch.bind(this); 53630 this.body.eventListeners.onMouseMove = this.onMouseMove.bind(this); 53631 this.body.eventListeners.onRelease = this.onRelease.bind(this); 53632 this.body.eventListeners.onContext = this.onContext.bind(this); 53633 53634 this.touchTime = 0; 53635 this.drag = {}; 53636 this.pinch = {}; 53637 this.popup = undefined; 53638 this.popupObj = undefined; 53639 this.popupTimer = undefined; 53640 53641 this.body.functions.getPointer = this.getPointer.bind(this); 53642 53643 this.options = {}; 53644 this.defaultOptions = { 53645 dragNodes: true, 53646 dragView: true, 53647 hover: false, 53648 keyboard: { 53649 enabled: false, 53650 speed: { x: 10, y: 10, zoom: 0.02 }, 53651 bindToWindow: true 53652 }, 53653 navigationButtons: false, 53654 tooltipDelay: 300, 53655 zoomView: true 53656 }; 53657 util.extend(this.options, this.defaultOptions); 53658 53659 this.bindEventListeners(); 53660 } 53661 53662 /** 53663 * Binds event listeners 53664 */ 53665 53666 53667 (0, _createClass3['default'])(InteractionHandler, [{ 53668 key: 'bindEventListeners', 53669 value: function bindEventListeners() { 53670 var _this = this; 53671 53672 this.body.emitter.on('destroy', function () { 53673 clearTimeout(_this.popupTimer); 53674 delete _this.body.functions.getPointer; 53675 }); 53676 } 53677 53678 /** 53679 * 53680 * @param {Object} options 53681 */ 53682 53683 }, { 53684 key: 'setOptions', 53685 value: function setOptions(options) { 53686 if (options !== undefined) { 53687 // extend all but the values in fields 53688 var fields = ['hideEdgesOnDrag', 'hideNodesOnDrag', 'keyboard', 'multiselect', 'selectable', 'selectConnectedEdges']; 53689 util.selectiveNotDeepExtend(fields, this.options, options); 53690 53691 // merge the keyboard options in. 53692 util.mergeOptions(this.options, options, 'keyboard'); 53693 53694 if (options.tooltip) { 53695 util.extend(this.options.tooltip, options.tooltip); 53696 if (options.tooltip.color) { 53697 this.options.tooltip.color = util.parseColor(options.tooltip.color); 53698 } 53699 } 53700 } 53701 53702 this.navigationHandler.setOptions(this.options); 53703 } 53704 53705 /** 53706 * Get the pointer location from a touch location 53707 * @param {{x: number, y: number}} touch 53708 * @return {{x: number, y: number}} pointer 53709 * @private 53710 */ 53711 53712 }, { 53713 key: 'getPointer', 53714 value: function getPointer(touch) { 53715 return { 53716 x: touch.x - util.getAbsoluteLeft(this.canvas.frame.canvas), 53717 y: touch.y - util.getAbsoluteTop(this.canvas.frame.canvas) 53718 }; 53719 } 53720 53721 /** 53722 * On start of a touch gesture, store the pointer 53723 * @param {Event} event The event 53724 * @private 53725 */ 53726 53727 }, { 53728 key: 'onTouch', 53729 value: function onTouch(event) { 53730 if (new Date().valueOf() - this.touchTime > 50) { 53731 this.drag.pointer = this.getPointer(event.center); 53732 this.drag.pinched = false;
53733 this.pinch.scale = this.body.view.scale; 53734 // to avoid double fireing of this event because we have two hammer instances. (on canvas and on frame) 53735 this.touchTime = new Date().valueOf(); 53736 } 53737 } 53738 53739 /** 53740 * handle tap/click event: select/unselect a node 53741 * @param {Event} event 53742 * @private 53743 */ 53744 53745 }, { 53746 key: 'onTap', 53747 value: function onTap(event) { 53748 var pointer = this.getPointer(event.center); 53749 var multiselect = this.selectionHandler.options.multiselect && (event.changedPointers[0].ctrlKey || event.changedPointers[0].metaKey); 53750 53751 this.checkSelectionChanges(pointer, event, multiselect); 53752 this.selectionHandler._generateClickEvent('click', event, pointer); 53753 } 53754 53755 /** 53756 * handle doubletap event 53757 * @param {Event} event 53758 * @private 53759 */ 53760 53761 }, { 53762 key: 'onDoubleTap', 53763 value: function onDoubleTap(event) { 53764 var pointer = this.getPointer(event.center); 53765 this.selectionHandler._generateClickEvent('doubleClick', event, pointer); 53766 } 53767 53768 /** 53769 * handle long tap event: multi select nodes 53770 * @param {Event} event 53771 * @private 53772 */ 53773 53774 }, { 53775 key: 'onHold', 53776 value: function onHold(event) { 53777 var pointer = this.getPointer(event.center); 53778 var multiselect = this.selectionHandler.options.multiselect; 53779 53780 this.checkSelectionChanges(pointer, event, multiselect); 53781 53782 this.selectionHandler._generateClickEvent('click', event, pointer); 53783 this.selectionHandler._generateClickEvent('hold', event, pointer); 53784 } 53785 53786 /** 53787 * handle the release of the screen 53788 * 53789 * @param {Event} event 53790 * @private 53791 */ 53792 53793 }, { 53794 key: 'onRelease', 53795 value: function onRelease(event) { 53796 if (new Date().valueOf() - this.touchTime > 10) { 53797 var pointer = this.getPointer(event.center); 53798 this.selectionHandler._generateClickEvent('release', event, pointer); 53799 // to avoid double fireing of this event because we have two hammer instances. (on canvas and on frame) 53800 this.touchTime = new Date().valueOf(); 53801 } 53802 } 53803 53804 /** 53805 * 53806 * @param {Event} event 53807 */ 53808 53809 }, { 53810 key: 'onContext', 53811 value: function onContext(event) { 53812 var pointer = this.getPointer({ x: event.clientX, y: event.clientY }); 53813 this.selectionHandler._generateClickEvent('oncontext', event, pointer); 53814 } 53815 53816 /** 53817 * Select and deselect nodes depending current selection change. 53818 * 53819 * For changing nodes, select/deselect events are fired. 53820 * 53821 * NOTE: For a given edge, if one connecting node is deselected and with the same 53822 * click the other node is selected, no events for the edge will fire. 53823 * It was selected and it will remain selected. 53824 * 53825 * TODO: This is all SelectionHandler calls; the method should be moved to there. 53826 * 53827 * @param {{x: number, y: number}} pointer 53828 * @param {Event} event 53829 * @param {boolean} [add=false] 53830 */ 53831 53832 }, { 53833 key: 'checkSelectionChanges', 53834 value: function checkSelectionChanges(pointer, event) { 53835 var add = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false; 53836 53837 var previousSelection = this.selectionHandler.getSelection(); 53838 var selected = false; 53839 if (add === true) { 53840 selected = this.selectionHandler.selectAdditionalOnPoint(pointer); 53841 } else { 53842 selected = this.selectionHandler.selectOnPoint(pointer); 53843 } 53844 var currentSelection = this.selectionHandler.getSelection(); 53845 53846 // See NOTE in method comment for the reason to do it like this 53847 var deselectedItems = this._determineDifference(previousSelection, currentSelection); 53848 var selectedItems = this._determineDifference(currentSelection, previousSelection); 53849 53850 if (deselectedItems.edges.length > 0) { 53851 this.selectionHandler._generateClickEvent('deselectEdge', event, pointer, previousSelection); 53852 selected = true; 53853 } 53854 53855 if (deselectedItems.nodes.length > 0) { 53856 this.selectionHandler._generateClickEvent('deselectNode', event, pointer, previousSelection); 53857 selected = true; 53858 } 53859 53860 if (selectedItems.nodes.length > 0) { 53861 this.selectionHandler._generateClickEvent('selectNode', event, pointer); 53862 selected = true; 53863 } 53864 53865 if (selectedItems.edges.length > 0) { 53866 this.selectionHandler._generateClickEvent('selectEdge', event, pointer); 53867 selected = true; 53868 } 53869 53870 // fire the select event if anything has been selected or deselected 53871 if (selected === true) { 53872 // select or unselect 53873 this.selectionHandler._generateClickEvent('select', event, pointer); 53874 } 53875 } 53876 53877 /** 53878 * Remove all node and edge id's from the first set that are present in the second one. 53879 * 53880 * @param {{nodes: Array.<Node>, edges: Array.<vis.Edge>}} firstSet 53881 * @param {{nodes: Array.<Node>, edges: Array.<vis.Edge>}} secondSet 53882 * @returns {{nodes: Array.<Node>, edges: Array.<vis.Edge>}} 53883 * @private 53884 */ 53885 53886 }, { 53887 key: '_determineDifference', 53888 value: function _determineDifference(firstSet, secondSet) { 53889 var arrayDiff = function arrayDiff(firstArr, secondArr) { 53890 var result = []; 53891 53892 for (var i = 0; i < firstArr.length; i++) { 53893 var value = firstArr[i]; 53894 if (secondArr.indexOf(value) === -1) { 53895 result.push(value); 53896 } 53897 } 53898 53899 return result; 53900 }; 53901 53902 return { 53903 nodes: arrayDiff(firstSet.nodes, secondSet.nodes), 53904 edges: arrayDiff(firstSet.edges, secondSet.edges) 53905 }; 53906 } 53907 53908 /** 53909 * This function is called by onDragStart. 53910 * It is separated out because we can then overload it for the datamanipulation system. 53911 * 53912 * @param {Event} event 53913 * @private 53914 */ 53915 53916 }, { 53917 key: 'onDragStart', 53918 value: function onDragStart(event) { 53919 //in case the touch event was triggered on an external div, do the initial touch now. 53920 if (this.drag.pointer === undefined) { 53921 this.onTouch(event); 53922 } 53923 53924 // note: drag.pointer is set in onTouch to get the initial touch location 53925 var node = this.selectionHandler.getNodeAt(this.drag.pointer); 53926 53927 this.drag.dragging = true; 53928 this.drag.selection = []; 53929 this.drag.translation = util.extend({}, this.body.view.translation); // copy the object 53930 this.drag.nodeId = undefined; 53931 53932 if (node !== undefined && this.options.dragNodes === true) { 53933 this.drag.nodeId = node.id; 53934 // select the clicked node if not yet selected 53935 if (node.isSelected() === false) { 53936 this.selectionHandler.unselectAll(); 53937 this.selectionHandler.selectObject(node); 53938 } 53939 53940 // after select to contain the node 53941 this.selectionHandler._generateClickEvent('dragStart', event, this.drag.pointer); 53942 53943 var selection = this.selectionHandler.selectionObj.nodes; 53944 // create an array with the selected nodes and their original location and status 53945 for (var nodeId in selection) { 53946 if (selection.hasOwnProperty(nodeId)) { 53947 var object = selection[nodeId]; 53948 var s = { 53949 id: object.id, 53950 node: object, 53951 53952 // store original x, y, xFixed and yFixed, make the node temporarily Fixed 53953 x: object.x, 53954 y: object.y, 53955 xFixed: object.options.fixed.x, 53956 yFixed: object.options.fixed.y 53957 }; 53958 53959 object.options.fixed.x = true; 53960 object.options.fixed.y = true; 53961 53962 this.drag.selection.push(s); 53963 } 53964 } 53965 } else { 53966 // fallback if no node is selected and thus the view is dragged. 53967 this.selectionHandler._generateClickEvent('dragStart', event, this.drag.pointer, undefined, true); 53968 } 53969 } 53970 53971 /** 53972 * handle drag event 53973 * @param {Event} event 53974 * @private 53975 */ 53976 53977 }, { 53978 key: 'onDrag', 53979 value: function onDrag(event) { 53980 var _this2 = this; 53981 53982 if (this.drag.pinched === true) { 53983 return; 53984 } 53985 53986 // remove the focus on node if it is focussed on by the focusOnNode 53987 this.body.emitter.emit('unlockNode'); 53988 53989 var pointer = this.getPointer(event.center); 53990 53991 var selection = this.drag.selection; 53992 if (selection && selection.length && this.options.dragNodes === true) { 53993 this.selectionHandler._generateClickEvent('dragging', event, pointer); 53994 53995 // calculate delta's and new location 53996 var deltaX = pointer.x - this.drag.pointer.x; 53997 var deltaY = pointer.y - this.drag.pointer.y; 53998 53999 // update position of all selected nodes 54000 selection.forEach(function (selection) { 54001 var node = selection.node; 54002 // only move the node if it was not fixed initially 54003 if (selection.xFixed === false) { 54004 node.x = _this2.canvas._XconvertDOMtoCanvas(_this2.canvas._XconvertCanvasToDOM(selection.x) + deltaX); 54005 } 54006 // only move the node if it was not fixed initially 54007 if (selection.yFixed === false) { 54008 node.y = _this2.canvas._YconvertDOMtoCanvas(_this2.canvas._YconvertCanvasToDOM(selection.y) + deltaY); 54009 } 54010 }); 54011 54012 // start the simulation of the physics 54013 this.body.emitter.emit('startSimulation'); 54014 } else { 54015 // move the network 54016 if (this.options.dragView === true) { 54017 this.selectionHandler._generateClickEvent('dragging', event, pointer, undefined, true); 54018 54019 // if the drag was not started properly because the click started outside the network div, start it now. 54020 if (this.drag.pointer === undefined) { 54021 this.onDragStart(event); 54022 return; 54023 } 54024 var diffX = pointer.x - this.drag.pointer.x; 54025 var diffY = pointer.y - this.drag.pointer.y; 54026 54027 this.body.view.translation = { x: this.drag.translation.x + diffX, y: this.drag.translation.y + diffY }; 54028 this.body.emitter.emit('_requestRedraw'); 54029 } 54030 } 54031 } 54032 54033 /** 54034 * handle drag start event 54035 * @param {Event} event 54036 * @private 54037 */ 54038 54039 }, { 54040 key: 'onDragEnd', 54041 value: function onDragEnd(event) { 54042 this.drag.dragging = false;
54043 var selection = this.drag.selection; 54044 if (selection && selection.length) { 54045 selection.forEach(function (s) { 54046 // restore original xFixed and yFixed 54047 s.node.options.fixed.x = s.xFixed; 54048 s.node.options.fixed.y = s.yFixed; 54049 }); 54050 this.selectionHandler._generateClickEvent('dragEnd', event, this.getPointer(event.center)); 54051 this.body.emitter.emit('startSimulation'); 54052 } else { 54053 this.selectionHandler._generateClickEvent('dragEnd', event, this.getPointer(event.center), undefined, true); 54054 this.body.emitter.emit('_requestRedraw'); 54055 } 54056 } 54057 54058 /** 54059 * Handle pinch event 54060 * @param {Event} event The event 54061 * @private 54062 */ 54063 54064 }, { 54065 key: 'onPinch', 54066 value: function onPinch(event) { 54067 var pointer = this.getPointer(event.center); 54068 54069 this.drag.pinched = true; 54070 if (this.pinch['scale'] === undefined) { 54071 this.pinch.scale = 1; 54072 } 54073 54074 // TODO: enabled moving while pinching? 54075 var scale = this.pinch.scale * event.scale; 54076 this.zoom(scale, pointer); 54077 } 54078 54079 /** 54080 * Zoom the network in or out 54081 * @param {number} scale a number around 1, and between 0.01 and 10 54082 * @param {{x: number, y: number}} pointer Position on screen 54083 * @private 54084 */ 54085 54086 }, { 54087 key: 'zoom', 54088 value: function zoom(scale, pointer) { 54089 if (this.options.zoomView === true) { 54090 var scaleOld = this.body.view.scale; 54091 if (scale < 0.00001) { 54092 scale = 0.00001; 54093 } 54094 if (scale > 10) { 54095 scale = 10; 54096 } 54097 54098 var preScaleDragPointer = undefined; 54099 if (this.drag !== undefined) { 54100 if (this.drag.dragging === true) { 54101 preScaleDragPointer = this.canvas.DOMtoCanvas(this.drag.pointer); 54102 } 54103 } 54104 // + this.canvas.frame.canvas.clientHeight / 2 54105 var translation = this.body.view.translation; 54106 54107 var scaleFrac = scale / scaleOld; 54108 var tx = (1 - scaleFrac) * pointer.x + translation.x * scaleFrac; 54109 var ty = (1 - scaleFrac) * pointer.y + translation.y * scaleFrac; 54110 54111 this.body.view.scale = scale; 54112 this.body.view.translation = { x: tx, y: ty }; 54113 54114 if (preScaleDragPointer != undefined) { 54115 var postScaleDragPointer = this.canvas.canvasToDOM(preScaleDragPointer); 54116 this.drag.pointer.x = postScaleDragPointer.x; 54117 this.drag.pointer.y = postScaleDragPointer.y; 54118 } 54119 54120 this.body.emitter.emit('_requestRedraw'); 54121 54122 if (scaleOld < scale) { 54123 this.body.emitter.emit('zoom', { direction: '+', scale: this.body.view.scale, pointer: pointer }); 54124 } else { 54125 this.body.emitter.emit('zoom', { direction: '-', scale: this.body.view.scale, pointer: pointer }); 54126 } 54127 } 54128 } 54129 54130 /** 54131 * Event handler for mouse wheel event, used to zoom the timeline 54132 * See http://adomas.org/javascript-mouse-wheel/ 54133 * https://github.com/EightMedia/hammer.js/issues/256 54134 * @param {MouseEvent} event 54135 * @private 54136 */ 54137 54138 }, { 54139 key: 'onMouseWheel', 54140 value: function onMouseWheel(event) { 54141 if (this.options.zoomView === true) { 54142 // retrieve delta 54143 var delta = 0; 54144 if (event.wheelDelta) { 54145 /* IE/Opera. */ 54146 delta = event.wheelDelta / 120; 54147 } else if (event.detail) { 54148 /* Mozilla case. */ 54149 // In Mozilla, sign of delta is different than in IE. 54150 // Also, delta is multiple of 3. 54151 delta = -event.detail / 3; 54152 } 54153 54154 // If delta is nonzero, handle it. 54155 // Basically, delta is now positive if wheel was scrolled up, 54156 // and negative, if wheel was scrolled down. 54157 if (delta !== 0) { 54158 54159 // calculate the new scale 54160 var scale = this.body.view.scale; 54161 var zoom = delta / 10; 54162 if (delta < 0) { 54163 zoom = zoom / (1 - zoom); 54164 } 54165 scale *= 1 + zoom; 54166 54167 // calculate the pointer location 54168 var pointer = this.getPointer({ x: event.clientX, y: event.clientY }); 54169 54170 // apply the new scale 54171 this.zoom(scale, pointer); 54172 } 54173 54174 // Prevent default actions caused by mouse wheel. 54175 event.preventDefault(); 54176 } 54177 } 54178 54179 /** 54180 * Mouse move handler for checking whether the title moves over a node with a title. 54181 * @param {Event} event 54182 * @private 54183 */ 54184 54185 }, { 54186 key: 'onMouseMove', 54187 value: function onMouseMove(event) { 54188 var _this3 = this; 54189 54190 var pointer = this.getPointer({ x: event.clientX, y: event.clientY }); 54191 var popupVisible = false;
54192 54193 // check if the previously selected node is still selected 54194 if (this.popup !== undefined) { 54195 if (this.popup.hidden === false) { 54196 this._checkHidePopup(pointer); 54197 } 54198 54199 // if the popup was not hidden above 54200 if (this.popup.hidden === false) { 54201 popupVisible = true; 54202 this.popup.setPosition(pointer.x + 3, pointer.y - 5); 54203 this.popup.show(); 54204 } 54205 } 54206 54207 // if we bind the keyboard to the div, we have to highlight it to use it. This highlights it on mouse over. 54208 if (this.options.keyboard.bindToWindow === false && this.options.keyboard.enabled === true) { 54209 this.canvas.frame.focus(); 54210 } 54211 54212 // start a timeout that will check if the mouse is positioned above an element 54213 if (popupVisible === false) { 54214 if (this.popupTimer !== undefined) { 54215 clearInterval(this.popupTimer); // stop any running calculationTimer 54216 this.popupTimer = undefined; 54217 } 54218 if (!this.drag.dragging) { 54219 this.popupTimer = setTimeout(function () { 54220 return _this3._checkShowPopup(pointer); 54221 }, this.options.tooltipDelay); 54222 } 54223 } 54224 54225 // adding hover highlights 54226 if (this.options.hover === true) { 54227 this.selectionHandler.hoverObject(event, pointer); 54228 } 54229 } 54230 54231 /** 54232 * Check if there is an element on the given position in the network 54233 * (a node or edge). If so, and if this element has a title, 54234 * show a popup window with its title. 54235 * 54236 * @param {{x:number, y:number}} pointer 54237 * @private 54238 */ 54239 54240 }, { 54241 key: '_checkShowPopup', 54242 value: function _checkShowPopup(pointer) { 54243 var x = this.canvas._XconvertDOMtoCanvas(pointer.x); 54244 var y = this.canvas._YconvertDOMtoCanvas(pointer.y); 54245 var pointerObj = { 54246 left: x, 54247 top: y, 54248 right: x, 54249 bottom: y 54250 }; 54251 54252 var previousPopupObjId = this.popupObj === undefined ? undefined : this.popupObj.id; 54253 var nodeUnderCursor = false; 54254 var popupType = 'node'; 54255 54256 // check if a node is under the cursor. 54257 if (this.popupObj === undefined) { 54258 // search the nodes for overlap, select the top one in case of multiple nodes 54259 var nodeIndices = this.body.nodeIndices; 54260 var nodes = this.body.nodes; 54261 var node = void 0; 54262 var overlappingNodes = []; 54263 for (var i = 0; i < nodeIndices.length; i++) { 54264 node = nodes[nodeIndices[i]]; 54265 if (node.isOverlappingWith(pointerObj) === true) { 54266 if (node.getTitle() !== undefined) { 54267 overlappingNodes.push(nodeIndices[i]); 54268 } 54269 } 54270 } 54271 54272 if (overlappingNodes.length > 0) { 54273 // if there are overlapping nodes, select the last one, this is the one which is drawn on top of the others 54274 this.popupObj = nodes[overlappingNodes[overlappingNodes.length - 1]]; 54275 // if you hover over a node, the title of the edge is not supposed to be shown. 54276 nodeUnderCursor = true; 54277 } 54278 } 54279 54280 if (this.popupObj === undefined && nodeUnderCursor === false) { 54281 // search the edges for overlap 54282 var edgeIndices = this.body.edgeIndices; 54283 var edges = this.body.edges; 54284 var edge = void 0; 54285 var overlappingEdges = []; 54286 for (var _i = 0; _i < edgeIndices.length; _i++) { 54287 edge = edges[edgeIndices[_i]]; 54288 if (edge.isOverlappingWith(pointerObj) === true) { 54289 if (edge.connected === true && edge.getTitle() !== undefined) { 54290 overlappingEdges.push(edgeIndices[_i]); 54291 } 54292 } 54293 } 54294 54295 if (overlappingEdges.length > 0) { 54296 this.popupObj = edges[overlappingEdges[overlappingEdges.length - 1]]; 54297 popupType = 'edge'; 54298 } 54299 } 54300 54301 if (this.popupObj !== undefined) { 54302 // show popup message window 54303 if (this.popupObj.id !== previousPopupObjId) { 54304 if (this.popup === undefined) { 54305 this.popup = new Popup(this.canvas.frame); 54306 } 54307 54308 this.popup.popupTargetType = popupType; 54309 this.popup.popupTargetId = this.popupObj.id; 54310
54311 // adjust a small offset such that the mouse cursor is located in the 54312 // bottom left location of the popup, and you can easily move over the 54313 // popup area 54314 this.popup.setPosition(pointer.x + 3, pointer.y - 5); 54315 this.popup.setText(this.popupObj.getTitle()); 54316 this.popup.show(); 54317 this.body.emitter.emit('showPopup', this.popupObj.id); 54318 } 54319 } else { 54320 if (this.popup !== undefined) { 54321 this.popup.hide(); 54322 this.body.emitter.emit('hidePopup'); 54323 } 54324 } 54325 } 54326 54327 /** 54328 * Check if the popup must be hidden, which is the case when the mouse is no 54329 * longer hovering on the object 54330 * @param {{x:number, y:number}} pointer 54331 * @private 54332 */ 54333 54334 }, { 54335 key: '_checkHidePopup', 54336 value: function _checkHidePopup(pointer) { 54337 var pointerObj = this.selectionHandler._pointerToPositionObject(pointer); 54338 54339 var stillOnObj = false; 54340 if (this.popup.popupTargetType === 'node') { 54341 if (this.body.nodes[this.popup.popupTargetId] !== undefined) { 54342 stillOnObj = this.body.nodes[this.popup.popupTargetId].isOverlappingWith(pointerObj); 54343 54344 // if the mouse is still one the node, we have to check if it is not also on one that is drawn on top of it. 54345 // we initially only check stillOnObj because this is much faster. 54346 if (stillOnObj === true) { 54347 var overNode = this.selectionHandler.getNodeAt(pointer); 54348 stillOnObj = overNode === undefined ? false : overNode.id === this.popup.popupTargetId; 54349 } 54350 } 54351 } else { 54352 if (this.selectionHandler.getNodeAt(pointer) === undefined) { 54353 if (this.body.edges[this.popup.popupTargetId] !== undefined) { 54354 stillOnObj = this.body.edges[this.popup.popupTargetId].isOverlappingWith(pointerObj); 54355 } 54356 } 54357 } 54358 54359 if (stillOnObj === false) { 54360 this.popupObj = undefined; 54361 this.popup.hide(); 54362 this.body.emitter.emit('hidePopup'); 54363 } 54364 } 54365 }]); 54366 return InteractionHandler; 54367}(); 54368 54369exports['default'] = InteractionHandler; 54370 54371/***/ }), 54372/* 233 */ 54373/***/ (function(module, exports, __webpack_require__) { 54374 54375"use strict"; 54376 54377 54378Object.defineProperty(exports, "__esModule", { 54379 value: true 54380}); 54381 54382var _classCallCheck2 = __webpack_require__(0); 54383 54384var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); 54385 54386var _createClass2 = __webpack_require__(1); 54387 54388var _createClass3 = _interopRequireDefault(_createClass2); 54389 54390function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 54391 54392var Hammer = __webpack_require__(10); 54393var hammerUtil = __webpack_require__(37); 54394var keycharm = __webpack_require__(35); 54395 54396/** 54397 * Navigation Handler 54398 */ 54399 54400var NavigationHandler = function () { 54401 /** 54402 * @param {Object} body 54403 * @param {Canvas} canvas 54404 */ 54405 function NavigationHandler(body, canvas) { 54406 var _this = this; 54407 54408 (0, _classCallCheck3['default'])(this, NavigationHandler); 54409 54410 this.body = body; 54411 this.canvas = canvas; 54412 54413 this.iconsCreated = false; 54414 this.navigationHammers = []; 54415 this.boundFunctions = {}; 54416 this.touchTime = 0; 54417 this.activated = false; 54418 54419 this.body.emitter.on("activate", function () { 54420 _this.activated = true;_this.configureKeyboardBindings(); 54421 }); 54422 this.body.emitter.on("deactivate", function () { 54423 _this.activated = false;_this.configureKeyboardBindings(); 54424 }); 54425 this.body.emitter.on("destroy", function () { 54426 if (_this.keycharm !== undefined) { 54427 _this.keycharm.destroy(); 54428 } 54429 }); 54430 54431 this.options = {}; 54432 } 54433 54434 /** 54435 * 54436 * @param {Object} options 54437 */ 54438 54439 54440 (0, _createClass3['default'])(NavigationHandler, [{ 54441 key: 'setOptions', 54442 value: function setOptions(options) { 54443 if (options !== undefined) { 54444 this.options = options; 54445 this.create(); 54446 } 54447 } 54448 54449 /** 54450 * Creates or refreshes navigation and sets key bindings 54451 */ 54452 54453 }, { 54454 key: 'create', 54455 value: function create() { 54456 if (this.options.navigationButtons === true) { 54457 if (this.iconsCreated === false) { 54458 this.loadNavigationElements(); 54459 } 54460 } else if (this.iconsCreated === true) { 54461 this.cleanNavigation(); 54462 } 54463 54464 this.configureKeyboardBindings(); 54465 } 54466 54467 /** 54468 * Cleans up previous navigation items 54469 */ 54470 54471 }, { 54472 key: 'cleanNavigation', 54473 value: function cleanNavigation() { 54474 // clean hammer bindings
54475 if (this.navigationHammers.length != 0) { 54476 for (var i = 0; i < this.navigationHammers.length; i++) { 54477 this.navigationHammers[i].destroy(); 54478 } 54479 this.navigationHammers = []; 54480 } 54481 54482 // clean up previous navigation items 54483 if (this.navigationDOM && this.navigationDOM['wrapper'] && this.navigationDOM['wrapper'].parentNode) { 54484 this.navigationDOM['wrapper'].parentNode.removeChild(this.navigationDOM['wrapper']); 54485 } 54486 54487 this.iconsCreated = false; 54488 } 54489 54490 /** 54491 * Creation of the navigation controls nodes. They are drawn over the rest of the nodes and are not affected by scale and translation 54492 * they have a triggerFunction which is called on click. If the position of the navigation controls is dependent 54493 * on this.frame.canvas.clientWidth or this.frame.canvas.clientHeight, we flag horizontalAlignLeft and verticalAlignTop false. 54494 * This means that the location will be corrected by the _relocateNavigation function on a size change of the canvas. 54495 * 54496 * @private 54497 */ 54498 54499 }, { 54500 key: 'loadNavigationElements', 54501 value: function loadNavigationElements() { 54502 var _this2 = this; 54503 54504 this.cleanNavigation(); 54505 54506 this.navigationDOM = {}; 54507 var navigationDivs = ['up', 'down', 'left', 'right', 'zoomIn', 'zoomOut', 'zoomExtends']; 54508 var navigationDivActions = ['_moveUp', '_moveDown', '_moveLeft', '_moveRight', '_zoomIn', '_zoomOut', '_fit']; 54509 54510 this.navigationDOM['wrapper'] = document.createElement('div'); 54511 this.navigationDOM['wrapper'].className = 'vis-navigation'; 54512 this.canvas.frame.appendChild(this.navigationDOM['wrapper']); 54513 54514 for (var i = 0; i < navigationDivs.length; i++) { 54515 this.navigationDOM[navigationDivs[i]] = document.createElement('div'); 54516 this.navigationDOM[navigationDivs[i]].className = 'vis-button vis-' + navigationDivs[i]; 54517 this.navigationDOM['wrapper'].appendChild(this.navigationDOM[navigationDivs[i]]); 54518 54519 var hammer = new Hammer(this.navigationDOM[navigationDivs[i]]); 54520 if (navigationDivActions[i] === "_fit") { 54521 hammerUtil.onTouch(hammer, this._fit.bind(this)); 54522 } else { 54523 hammerUtil.onTouch(hammer, this.bindToRedraw.bind(this, navigationDivActions[i])); 54524 } 54525 54526 this.navigationHammers.push(hammer); 54527 } 54528 54529 // use a hammer for the release so we do not require the one used in the rest of the network 54530 // the one the rest uses can be overloaded by the manipulation system. 54531 var hammerFrame = new Hammer(this.canvas.frame); 54532 hammerUtil.onRelease(hammerFrame, function () { 54533 _this2._stopMovement(); 54534 }); 54535 this.navigationHammers.push(hammerFrame); 54536 54537 this.iconsCreated = true; 54538 } 54539 54540 /** 54541 * 54542 * @param {string} action 54543 */ 54544 54545 }, { 54546 key: 'bindToRedraw', 54547 value: function bindToRedraw(action) { 54548 if (this.boundFunctions[action] === undefined) { 54549 this.boundFunctions[action] = this[action].bind(this); 54550 this.body.emitter.on("initRedraw", this.boundFunctions[action]); 54551 this.body.emitter.emit("_startRendering"); 54552 } 54553 } 54554 54555 /** 54556 * 54557 * @param {string} action 54558 */ 54559 54560 }, { 54561 key: 'unbindFromRedraw', 54562 value: function unbindFromRedraw(action) { 54563 if (this.boundFunctions[action] !== undefined) { 54564 this.body.emitter.off("initRedraw", this.boundFunctions[action]); 54565 this.body.emitter.emit("_stopRendering"); 54566 delete this.boundFunctions[action]; 54567 } 54568 } 54569 54570 /** 54571 * this stops all movement induced by the navigation buttons 54572 * 54573 * @private 54574 */ 54575 54576 }, { 54577 key: '_fit', 54578 value: function _fit() { 54579 if (new Date().valueOf() - this.touchTime > 700) {
54580 // TODO: fix ugly hack to avoid hammer's double fireing of event (because we use release?) 54581 this.body.emitter.emit("fit", { duration: 700 }); 54582 this.touchTime = new Date().valueOf(); 54583 } 54584 } 54585 54586 /** 54587 * this stops all movement induced by the navigation buttons 54588 * 54589 * @private 54590 */ 54591 54592 }, { 54593 key: '_stopMovement', 54594 value: function _stopMovement() { 54595 for (var boundAction in this.boundFunctions) { 54596 if (this.boundFunctions.hasOwnProperty(boundAction)) { 54597 this.body.emitter.off("initRedraw", this.boundFunctions[boundAction]); 54598 this.body.emitter.emit("_stopRendering"); 54599 } 54600 } 54601 this.boundFunctions = {}; 54602 } 54603 /** 54604 * 54605 * @private 54606 */ 54607 54608 }, { 54609 key: '_moveUp', 54610 value: function _moveUp() { 54611 this.body.view.translation.y += this.options.keyboard.speed.y; 54612 } 54613 /** 54614 * 54615 * @private 54616 */ 54617 54618 }, { 54619 key: '_moveDown', 54620 value: function _moveDown() { 54621 this.body.view.translation.y -= this.options.keyboard.speed.y; 54622 } 54623 /** 54624 * 54625 * @private 54626 */ 54627 54628 }, { 54629 key: '_moveLeft', 54630 value: function _moveLeft() { 54631 this.body.view.translation.x += this.options.keyboard.speed.x; 54632 } 54633 /** 54634 * 54635 * @private 54636 */ 54637 54638 }, { 54639 key: '_moveRight', 54640 value: function _moveRight() { 54641 this.body.view.translation.x -= this.options.keyboard.speed.x; 54642 } 54643 /** 54644 * 54645 * @private 54646 */ 54647 54648 }, { 54649 key: '_zoomIn', 54650 value: function _zoomIn() { 54651 var scaleOld = this.body.view.scale; 54652 var scale = this.body.view.scale * (1 + this.options.keyboard.speed.zoom); 54653 var translation = this.body.view.translation; 54654 var scaleFrac = scale / scaleOld; 54655 var tx = (1 - scaleFrac) * this.canvas.canvasViewCenter.x + translation.x * scaleFrac; 54656 var ty = (1 - scaleFrac) * this.canvas.canvasViewCenter.y + translation.y * scaleFrac; 54657 54658 this.body.view.scale = scale; 54659 this.body.view.translation = { x: tx, y: ty }; 54660 this.body.emitter.emit('zoom', { direction: '+', scale: this.body.view.scale, pointer: null }); 54661 } 54662 54663 /** 54664 * 54665 * @private 54666 */ 54667 54668 }, { 54669 key: '_zoomOut', 54670 value: function _zoomOut() { 54671 var scaleOld = this.body.view.scale; 54672 var scale = this.body.view.scale / (1 + this.options.keyboard.speed.zoom); 54673 var translation = this.body.view.translation; 54674 var scaleFrac = scale / scaleOld; 54675 var tx = (1 - scaleFrac) * this.canvas.canvasViewCenter.x + translation.x * scaleFrac; 54676 var ty = (1 - scaleFrac) * this.canvas.canvasViewCenter.y + translation.y * scaleFrac; 54677 54678 this.body.view.scale = scale; 54679 this.body.view.translation = { x: tx, y: ty }; 54680 this.body.emitter.emit('zoom', { direction: '-', scale: this.body.view.scale, pointer: null }); 54681 } 54682 54683 /** 54684 * bind all keys using keycharm. 54685 */ 54686 54687 }, { 54688 key: 'configureKeyboardBindings', 54689 value: function configureKeyboardBindings() { 54690 var _this3 = this; 54691 54692 if (this.keycharm !== undefined) { 54693 this.keycharm.destroy(); 54694 } 54695 54696 if (this.options.keyboard.enabled === true) { 54697 if (this.options.keyboard.bindToWindow === true) { 54698 this.keycharm = keycharm({ container: window, preventDefault: true }); 54699 } else { 54700 this.keycharm = keycharm({ container: this.canvas.frame, preventDefault: true }); 54701 } 54702 54703 this.keycharm.reset(); 54704 54705 if (this.activated === true) { 54706 this.keycharm.bind("up", function () { 54707 _this3.bindToRedraw("_moveUp"); 54708 }, "keydown"); 54709 this.keycharm.bind("down", function () { 54710 _this3.bindToRedraw("_moveDown"); 54711 }, "keydown"); 54712 this.keycharm.bind("left", function () { 54713 _this3.bindToRedraw("_moveLeft"); 54714 }, "keydown"); 54715 this.keycharm.bind("right", function () { 54716 _this3.bindToRedraw("_moveRight"); 54717 }, "keydown"); 54718 this.keycharm.bind("=", function () { 54719 _this3.bindToRedraw("_zoomIn"); 54720 }, "keydown"); 54721 this.keycharm.bind("num+", function () { 54722 _this3.bindToRedraw("_zoomIn"); 54723 }, "keydown"); 54724 this.keycharm.bind("num-", function () { 54725 _this3.bindToRedraw("_zoomOut"); 54726 }, "keydown"); 54727 this.keycharm.bind("-", function () { 54728 _this3.bindToRedraw("_zoomOut"); 54729 }, "keydown"); 54730 this.keycharm.bind("[", function () { 54731 _this3.bindToRedraw("_zoomOut"); 54732 }, "keydown"); 54733 this.keycharm.bind("]", function () { 54734 _this3.bindToRedraw("_zoomIn"); 54735 }, "keydown"); 54736 this.keycharm.bind("pageup", function () { 54737 _this3.bindToRedraw("_zoomIn"); 54738 }, "keydown"); 54739 this.keycharm.bind("pagedown", function () { 54740 _this3.bindToRedraw("_zoomOut"); 54741 }, "keydown"); 54742 54743 this.keycharm.bind("up", function () { 54744 _this3.unbindFromRedraw("_moveUp"); 54745 }, "keyup"); 54746 this.keycharm.bind("down", function () { 54747 _this3.unbindFromRedraw("_moveDown"); 54748 }, "keyup"); 54749 this.keycharm.bind("left", function () { 54750 _this3.unbindFromRedraw("_moveLeft"); 54751 }, "keyup"); 54752 this.keycharm.bind("right", function () { 54753 _this3.unbindFromRedraw("_moveRight"); 54754 }, "keyup"); 54755 this.keycharm.bind("=", function () { 54756 _this3.unbindFromRedraw("_zoomIn"); 54757 }, "keyup"); 54758 this.keycharm.bind("num+", function () { 54759 _this3.unbindFromRedraw("_zoomIn"); 54760 }, "keyup"); 54761 this.keycharm.bind("num-", function () { 54762 _this3.unbindFromRedraw("_zoomOut"); 54763 }, "keyup"); 54764 this.keycharm.bind("-", function () { 54765 _this3.unbindFromRedraw("_zoomOut"); 54766 }, "keyup"); 54767 this.keycharm.bind("[", function () { 54768 _this3.unbindFromRedraw("_zoomOut"); 54769 }, "keyup"); 54770 this.keycharm.bind("]", function () { 54771 _this3.unbindFromRedraw("_zoomIn"); 54772 }, "keyup"); 54773 this.keycharm.bind("pageup", function () { 54774 _this3.unbindFromRedraw("_zoomIn"); 54775 }, "keyup"); 54776 this.keycharm.bind("pagedown", function () { 54777 _this3.unbindFromRedraw("_zoomOut"); 54778 }, "keyup"); 54779 } 54780 } 54781 } 54782 }]); 54783 return NavigationHandler; 54784}(); 54785 54786exports['default'] = NavigationHandler; 54787 54788/***/ }), 54789/* 234 */ 54790/***/ (function(module, exports, __webpack_require__) { 54791 54792"use strict"; 54793 54794 54795Object.defineProperty(exports, "__esModule", { 54796 value: true 54797}); 54798 54799var _classCallCheck2 = __webpack_require__(0); 54800 54801var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); 54802 54803var _createClass2 = __webpack_require__(1); 54804 54805var _createClass3 = _interopRequireDefault(_createClass2); 54806 54807function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 54808 54809var Node = __webpack_require__(47)['default']; 54810var Edge = __webpack_require__(74)['default']; 54811 54812var util = __webpack_require__(2); 54813 54814/** 54815 * The handler for selections 54816 */ 54817 54818var SelectionHandler = function () { 54819 /** 54820 * @param {Object} body 54821 * @param {Canvas} canvas 54822 */ 54823 function SelectionHandler(body, canvas) { 54824 var _this = this; 54825 54826 (0, _classCallCheck3['default'])(this, SelectionHandler); 54827 54828 this.body = body; 54829 this.canvas = canvas; 54830 this.selectionObj = { nodes: [], edges: [] }; 54831 this.hoverObj = { nodes: {}, edges: {} }; 54832 54833 this.options = {}; 54834 this.defaultOptions = { 54835 multiselect: false, 54836 selectable: true, 54837 selectConnectedEdges: true, 54838 hoverConnectedEdges: true 54839 }; 54840 util.extend(this.options, this.defaultOptions); 54841 54842 this.body.emitter.on("_dataChanged", function () { 54843 _this.updateSelection(); 54844 }); 54845 } 54846 54847 /** 54848 * 54849 * @param {Object} [options] 54850 */ 54851 54852 54853 (0, _createClass3['default'])(SelectionHandler, [{ 54854 key: 'setOptions', 54855 value: function setOptions(options) { 54856 if (options !== undefined) { 54857 var fields = ['multiselect', 'hoverConnectedEdges', 'selectable', 'selectConnectedEdges']; 54858 util.selectiveDeepExtend(fields, this.options, options); 54859 } 54860 } 54861 54862 /** 54863 * handles the selection part of the tap; 54864 * 54865 * @param {{x: number, y: number}} pointer 54866 * @returns {boolean} 54867 */ 54868 54869 }, { 54870 key: 'selectOnPoint', 54871 value: function selectOnPoint(pointer) { 54872 var selected = false;
54873 if (this.options.selectable === true) { 54874 var obj = this.getNodeAt(pointer) || this.getEdgeAt(pointer); 54875 54876 // unselect after getting the objects in order to restore width and height. 54877 this.unselectAll(); 54878 54879 if (obj !== undefined) { 54880 selected = this.selectObject(obj); 54881 } 54882 this.body.emitter.emit("_requestRedraw"); 54883 } 54884 return selected; 54885 } 54886 54887 /** 54888 * 54889 * @param {{x: number, y: number}} pointer 54890 * @returns {boolean} 54891 */ 54892 54893 }, { 54894 key: 'selectAdditionalOnPoint', 54895 value: function selectAdditionalOnPoint(pointer) { 54896 var selectionChanged = false; 54897 if (this.options.selectable === true) { 54898 var obj = this.getNodeAt(pointer) || this.getEdgeAt(pointer); 54899 54900 if (obj !== undefined) { 54901 selectionChanged = true; 54902 if (obj.isSelected() === true) { 54903 this.deselectObject(obj); 54904 } else { 54905 this.selectObject(obj); 54906 } 54907 54908 this.body.emitter.emit("_requestRedraw"); 54909 } 54910 } 54911 return selectionChanged; 54912 } 54913 54914 /** 54915 * Create an object containing the standard fields for an event. 54916 * 54917 * @param {Event} event 54918 * @param {{x: number, y: number}} pointer Object with the x and y screen coordinates of the mouse 54919 * @returns {{}} 54920 * @private 54921 */ 54922 54923 }, { 54924 key: '_initBaseEvent', 54925 value: function _initBaseEvent(event, pointer) { 54926 var properties = {}; 54927 54928 properties['pointer'] = { 54929 DOM: { x: pointer.x, y: pointer.y }, 54930 canvas: this.canvas.DOMtoCanvas(pointer) 54931 }; 54932 properties['event'] = event; 54933 54934 return properties; 54935 } 54936 54937 /** 54938 * Generate an event which the user can catch. 54939 * 54940 * This adds some extra data to the event with respect to cursor position and 54941 * selected nodes and edges. 54942 * 54943 * @param {string} eventType Name of event to send 54944 * @param {Event} event 54945 * @param {{x: number, y: number}} pointer Object with the x and y screen coordinates of the mouse 54946 * @param {Object|undefined} oldSelection If present, selection state before event occured 54947 * @param {boolean|undefined} [emptySelection=false] Indicate if selection data should be passed 54948 */ 54949 54950 }, { 54951 key: '_generateClickEvent', 54952 value: function _generateClickEvent(eventType, event, pointer, oldSelection) { 54953 var emptySelection = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : false; 54954 54955 var properties = this._initBaseEvent(event, pointer); 54956 54957 if (emptySelection === true) { 54958 properties.nodes = []; 54959 properties.edges = []; 54960 } else { 54961 var tmp = this.getSelection(); 54962 properties.nodes = tmp.nodes; 54963 properties.edges = tmp.edges; 54964 } 54965 54966 if (oldSelection !== undefined) { 54967 properties['previousSelection'] = oldSelection; 54968 } 54969 54970 if (eventType == 'click') { 54971 // For the time being, restrict this functionality to 54972 // just the click event. 54973 properties.items = this.getClickedItems(pointer); 54974 } 54975 54976 this.body.emitter.emit(eventType, properties); 54977 } 54978 54979 /** 54980 * 54981 * @param {Object} obj 54982 * @param {boolean} [highlightEdges=this.options.selectConnectedEdges] 54983 * @returns {boolean} 54984 */ 54985 54986 }, { 54987 key: 'selectObject', 54988 value: function selectObject(obj) { 54989 var highlightEdges = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : this.options.selectConnectedEdges; 54990 54991 if (obj !== undefined) { 54992 if (obj instanceof Node) { 54993 if (highlightEdges === true) { 54994 this._selectConnectedEdges(obj); 54995 } 54996 } 54997 obj.select(); 54998 this._addToSelection(obj); 54999 return true; 55000 } 55001 return false; 55002 } 55003 55004 /** 55005 * 55006 * @param {Object} obj 55007 */ 55008 55009 }, { 55010 key: 'deselectObject', 55011 value: function deselectObject(obj) { 55012 if (obj.isSelected() === true) { 55013 obj.selected = false; 55014 this._removeFromSelection(obj); 55015 } 55016 } 55017 55018 /** 55019 * retrieve all nodes overlapping with given object 55020 * @param {Object} object An object with parameters left, top, right, bottom 55021 * @return {number[]} An array with id's of the overlapping nodes 55022 * @private 55023 */ 55024 55025 }, { 55026 key: '_getAllNodesOverlappingWith', 55027 value: function _getAllNodesOverlappingWith(object) { 55028 var overlappingNodes = []; 55029 var nodes = this.body.nodes; 55030 for (var i = 0; i < this.body.nodeIndices.length; i++) { 55031 var nodeId = this.body.nodeIndices[i]; 55032 if (nodes[nodeId].isOverlappingWith(object)) { 55033 overlappingNodes.push(nodeId); 55034 } 55035 } 55036 return overlappingNodes; 55037 } 55038 55039 /** 55040 * Return a position object in canvasspace from a single point in screenspace 55041 * 55042 * @param {{x: number, y: number}} pointer 55043 * @returns {{left: number, top: number, right: number, bottom: number}} 55044 * @private 55045 */ 55046 55047 }, { 55048 key: '_pointerToPositionObject', 55049 value: function _pointerToPositionObject(pointer) { 55050 var canvasPos = this.canvas.DOMtoCanvas(pointer); 55051 return { 55052 left: canvasPos.x - 1, 55053 top: canvasPos.y + 1, 55054 right: canvasPos.x + 1, 55055 bottom: canvasPos.y - 1 55056 }; 55057 } 55058 55059 /** 55060 * Get the top node at the passed point (like a click) 55061 * 55062 * @param {{x: number, y: number}} pointer 55063 * @param {boolean} [returnNode=true] 55064 * @return {Node | undefined} node 55065 */ 55066 55067 }, { 55068 key: 'getNodeAt', 55069 value: function getNodeAt(pointer) { 55070 var returnNode = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true; 55071 55072 // we first check if this is an navigation controls element 55073 var positionObject = this._pointerToPositionObject(pointer); 55074 var overlappingNodes = this._getAllNodesOverlappingWith(positionObject); 55075 // if there are overlapping nodes, select the last one, this is the 55076 // one which is drawn on top of the others 55077 if (overlappingNodes.length > 0) { 55078 if (returnNode === true) { 55079 return this.body.nodes[overlappingNodes[overlappingNodes.length - 1]]; 55080 } else { 55081 return overlappingNodes[overlappingNodes.length - 1]; 55082 } 55083 } else { 55084 return undefined; 55085 } 55086 } 55087 55088 /** 55089 * retrieve all edges overlapping with given object, selector is around center 55090 * @param {Object} object An object with parameters left, top, right, bottom 55091 * @param {number[]} overlappingEdges An array with id's of the overlapping nodes 55092 * @private 55093 */ 55094 55095 }, { 55096 key: '_getEdgesOverlappingWith', 55097 value: function _getEdgesOverlappingWith(object, overlappingEdges) { 55098 var edges = this.body.edges; 55099 for (var i = 0; i < this.body.edgeIndices.length; i++) { 55100 var edgeId = this.body.edgeIndices[i]; 55101 if (edges[edgeId].isOverlappingWith(object)) { 55102 overlappingEdges.push(edgeId); 55103 } 55104 } 55105 } 55106 55107 /** 55108 * retrieve all nodes overlapping with given object 55109 * @param {Object} object An object with parameters left, top, right, bottom 55110 * @return {number[]} An array with id's of the overlapping nodes 55111 * @private 55112 */ 55113 55114 }, { 55115 key: '_getAllEdgesOverlappingWith', 55116 value: function _getAllEdgesOverlappingWith(object) { 55117 var overlappingEdges = []; 55118 this._getEdgesOverlappingWith(object, overlappingEdges); 55119 return overlappingEdges; 55120 } 55121 55122 /** 55123 * Get the edges nearest to the passed point (like a click) 55124 * 55125 * @param {{x: number, y: number}} pointer 55126 * @param {boolean} [returnEdge=true] 55127 * @return {Edge | undefined} node 55128 */ 55129 55130 }, { 55131 key: 'getEdgeAt', 55132 value: function getEdgeAt(pointer) { 55133 var returnEdge = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true; 55134 55135 // Iterate over edges, pick closest within 10 55136 var canvasPos = this.canvas.DOMtoCanvas(pointer); 55137 var mindist = 10; 55138 var overlappingEdge = null; 55139 var edges = this.body.edges; 55140 for (var i = 0; i < this.body.edgeIndices.length; i++) { 55141 var edgeId = this.body.edgeIndices[i]; 55142 var edge = edges[edgeId]; 55143 if (edge.connected) { 55144 var xFrom = edge.from.x; 55145 var yFrom = edge.from.y; 55146 var xTo = edge.to.x; 55147 var yTo = edge.to.y; 55148 var dist = edge.edgeType.getDistanceToEdge(xFrom, yFrom, xTo, yTo, canvasPos.x, canvasPos.y); 55149 if (dist < mindist) { 55150 overlappingEdge = edgeId; 55151 mindist = dist; 55152 } 55153 } 55154 } 55155 if (overlappingEdge !== null) { 55156 if (returnEdge === true) { 55157 return this.body.edges[overlappingEdge]; 55158 } else { 55159 return overlappingEdge; 55160 } 55161 } else { 55162 return undefined; 55163 } 55164 } 55165 55166 /** 55167 * Add object to the selection array.
55168 * 55169 * @param {Object} obj 55170 * @private 55171 */ 55172 55173 }, { 55174 key: '_addToSelection', 55175 value: function _addToSelection(obj) { 55176 if (obj instanceof Node) { 55177 this.selectionObj.nodes[obj.id] = obj; 55178 } else { 55179 this.selectionObj.edges[obj.id] = obj; 55180 } 55181 } 55182 55183 /** 55184 * Add object to the selection array. 55185 * 55186 * @param {Object} obj 55187 * @private 55188 */ 55189 55190 }, { 55191 key: '_addToHover', 55192 value: function _addToHover(obj) { 55193 if (obj instanceof Node) { 55194 this.hoverObj.nodes[obj.id] = obj; 55195 } else { 55196 this.hoverObj.edges[obj.id] = obj; 55197 } 55198 } 55199 55200 /** 55201 * Remove a single option from selection. 55202 * 55203 * @param {Object} obj 55204 * @private 55205 */ 55206 55207 }, { 55208 key: '_removeFromSelection', 55209 value: function _removeFromSelection(obj) { 55210 if (obj instanceof Node) { 55211 delete this.selectionObj.nodes[obj.id]; 55212 this._unselectConnectedEdges(obj); 55213 } else { 55214 delete this.selectionObj.edges[obj.id]; 55215 } 55216 } 55217 55218 /** 55219 * Unselect all. The selectionObj is useful for this. 55220 */ 55221 55222 }, { 55223 key: 'unselectAll', 55224 value: function unselectAll() { 55225 for (var nodeId in this.selectionObj.nodes) { 55226 if (this.selectionObj.nodes.hasOwnProperty(nodeId)) { 55227 this.selectionObj.nodes[nodeId].unselect(); 55228 } 55229 } 55230 for (var edgeId in this.selectionObj.edges) { 55231 if (this.selectionObj.edges.hasOwnProperty(edgeId)) { 55232 this.selectionObj.edges[edgeId].unselect(); 55233 } 55234 } 55235 55236 this.selectionObj = { nodes: {}, edges: {} }; 55237 } 55238 55239 /** 55240 * return the number of selected nodes 55241 * 55242 * @returns {number} 55243 * @private 55244 */ 55245 55246 }, { 55247 key: '_getSelectedNodeCount', 55248 value: function _getSelectedNodeCount() { 55249 var count = 0; 55250 for (var nodeId in this.selectionObj.nodes) { 55251 if (this.selectionObj.nodes.hasOwnProperty(nodeId)) { 55252 count += 1; 55253 } 55254 } 55255 return count; 55256 } 55257 55258 /** 55259 * return the selected node 55260 * 55261 * @returns {number} 55262 * @private 55263 */ 55264 55265 }, { 55266 key: '_getSelectedNode', 55267 value: function _getSelectedNode() { 55268 for (var nodeId in this.selectionObj.nodes) { 55269 if (this.selectionObj.nodes.hasOwnProperty(nodeId)) { 55270 return this.selectionObj.nodes[nodeId]; 55271 } 55272 } 55273 return undefined; 55274 } 55275 55276 /** 55277 * return the selected edge 55278 * 55279 * @returns {number} 55280 * @private 55281 */ 55282 55283 }, { 55284 key: '_getSelectedEdge', 55285 value: function _getSelectedEdge() { 55286 for (var edgeId in this.selectionObj.edges) { 55287 if (this.selectionObj.edges.hasOwnProperty(edgeId)) { 55288 return this.selectionObj.edges[edgeId]; 55289 } 55290 } 55291 return undefined; 55292 } 55293 55294 /** 55295 * return the number of selected edges 55296 * 55297 * @returns {number} 55298 * @private 55299 */ 55300 55301 }, { 55302 key: '_getSelectedEdgeCount', 55303 value: function _getSelectedEdgeCount() { 55304 var count = 0; 55305 for (var edgeId in this.selectionObj.edges) { 55306 if (this.selectionObj.edges.hasOwnProperty(edgeId)) { 55307 count += 1; 55308 } 55309 } 55310 return count; 55311 } 55312 55313 /** 55314 * return the number of selected objects. 55315 * 55316 * @returns {number} 55317 * @private 55318 */ 55319 55320 }, { 55321 key: '_getSelectedObjectCount', 55322 value: function _getSelectedObjectCount() { 55323 var count = 0; 55324 for (var nodeId in this.selectionObj.nodes) { 55325 if (this.selectionObj.nodes.hasOwnProperty(nodeId)) { 55326 count += 1; 55327 } 55328 } 55329 for (var edgeId in this.selectionObj.edges) { 55330 if (this.selectionObj.edges.hasOwnProperty(edgeId)) { 55331 count += 1; 55332 } 55333 } 55334 return count; 55335 } 55336 55337 /** 55338 * Check if anything is selected 55339 * 55340 * @returns {boolean} 55341 * @private 55342 */ 55343 55344 }, { 55345 key: '_selectionIsEmpty', 55346 value: function _selectionIsEmpty() { 55347 for (var nodeId in this.selectionObj.nodes) { 55348 if (this.selectionObj.nodes.hasOwnProperty(nodeId)) { 55349 return false; 55350 } 55351 } 55352 for (var edgeId in this.selectionObj.edges) { 55353 if (this.selectionObj.edges.hasOwnProperty(edgeId)) { 55354 return false;
55355 } 55356 } 55357 return true; 55358 } 55359 55360 /** 55361 * check if one of the selected nodes is a cluster. 55362 * 55363 * @returns {boolean} 55364 * @private 55365 */ 55366 55367 }, { 55368 key: '_clusterInSelection', 55369 value: function _clusterInSelection() { 55370 for (var nodeId in this.selectionObj.nodes) { 55371 if (this.selectionObj.nodes.hasOwnProperty(nodeId)) { 55372 if (this.selectionObj.nodes[nodeId].clusterSize > 1) { 55373 return true; 55374 } 55375 } 55376 } 55377 return false; 55378 } 55379 55380 /** 55381 * select the edges connected to the node that is being selected 55382 * 55383 * @param {Node} node 55384 * @private 55385 */ 55386 55387 }, { 55388 key: '_selectConnectedEdges', 55389 value: function _selectConnectedEdges(node) { 55390 for (var i = 0; i < node.edges.length; i++) { 55391 var edge = node.edges[i]; 55392 edge.select(); 55393 this._addToSelection(edge); 55394 } 55395 } 55396 55397 /** 55398 * select the edges connected to the node that is being selected 55399 * 55400 * @param {Node} node 55401 * @private 55402 */ 55403 55404 }, { 55405 key: '_hoverConnectedEdges', 55406 value: function _hoverConnectedEdges(node) { 55407 for (var i = 0; i < node.edges.length; i++) { 55408 var edge = node.edges[i]; 55409 edge.hover = true; 55410 this._addToHover(edge); 55411 } 55412 } 55413 55414 /** 55415 * unselect the edges connected to the node that is being selected 55416 * 55417 * @param {Node} node 55418 * @private 55419 */ 55420 55421 }, { 55422 key: '_unselectConnectedEdges', 55423 value: function _unselectConnectedEdges(node) { 55424 for (var i = 0; i < node.edges.length; i++) { 55425 var edge = node.edges[i]; 55426 edge.unselect(); 55427 this._removeFromSelection(edge); 55428 } 55429 } 55430 55431 /** 55432 * Remove the highlight from a node or edge, in response to mouse movement 55433 * 55434 * @param {Event} event 55435 * @param {{x: number, y: number}} pointer object with the x and y screen coordinates of the mouse 55436 * @param {Node|vis.Edge} object 55437 * @private 55438 */ 55439 55440 }, { 55441 key: 'emitBlurEvent', 55442 value: function emitBlurEvent(event, pointer, object) { 55443 var properties = this._initBaseEvent(event, pointer); 55444 55445 if (object.hover === true) { 55446 object.hover = false; 55447 if (object instanceof Node) { 55448 properties.node = object.id; 55449 this.body.emitter.emit("blurNode", properties); 55450 } else { 55451 properties.edge = object.id; 55452 this.body.emitter.emit("blurEdge", properties); 55453 } 55454 } 55455 } 55456 55457 /** 55458 * Create the highlight for a node or edge, in response to mouse movement 55459 * 55460 * @param {Event} event 55461 * @param {{x: number, y: number}} pointer object with the x and y screen coordinates of the mouse 55462 * @param {Node|vis.Edge} object 55463 * @returns {boolean} hoverChanged 55464 * @private 55465 */ 55466 55467 }, { 55468 key: 'emitHoverEvent', 55469 value: function emitHoverEvent(event, pointer, object) { 55470 var properties = this._initBaseEvent(event, pointer); 55471 var hoverChanged = false; 55472 55473 if (object.hover === false) { 55474 object.hover = true; 55475 this._addToHover(object); 55476 hoverChanged = true; 55477 if (object instanceof Node) { 55478 properties.node = object.id; 55479 this.body.emitter.emit("hoverNode", properties); 55480 } else { 55481 properties.edge = object.id; 55482 this.body.emitter.emit("hoverEdge", properties); 55483 } 55484 } 55485 55486 return hoverChanged; 55487 } 55488 55489 /** 55490 * Perform actions in response to a mouse movement. 55491 * 55492 * @param {Event} event 55493 * @param {{x: number, y: number}} pointer | object with the x and y screen coordinates of the mouse 55494 */ 55495 55496 }, { 55497 key: 'hoverObject', 55498 value: function hoverObject(event, pointer) { 55499 var object = this.getNodeAt(pointer); 55500 if (object === undefined) { 55501 object = this.getEdgeAt(pointer); 55502 } 55503 55504 var hoverChanged = false; 55505 // remove all node hover highlights 55506 for (var nodeId in this.hoverObj.nodes) { 55507 if (this.hoverObj.nodes.hasOwnProperty(nodeId)) { 55508 if (object === undefined || object instanceof Node && object.id != nodeId || object instanceof Edge) { 55509 this.emitBlurEvent(event, pointer, this.hoverObj.nodes[nodeId]); 55510 delete this.hoverObj.nodes[nodeId]; 55511 hoverChanged = true; 55512 } 55513 } 55514 } 55515 55516 // removing all edge hover highlights 55517 for (var edgeId in this.hoverObj.edges) { 55518 if (this.hoverObj.edges.hasOwnProperty(edgeId)) { 55519 // if the hover has been changed here it means that the node has been hovered over or off 55520 // we then do not use the emitBlurEvent method here. 55521 if (hoverChanged === true) { 55522 this.hoverObj.edges[edgeId].hover = false;
55523 delete this.hoverObj.edges[edgeId]; 55524 } 55525 // if the blur remains the same and the object is undefined (mouse off) or another 55526 // edge has been hovered, or another node has been hovered we blur the edge. 55527 else if (object === undefined || object instanceof Edge && object.id != edgeId || object instanceof Node && !object.hover) { 55528 this.emitBlurEvent(event, pointer, this.hoverObj.edges[edgeId]); 55529 delete this.hoverObj.edges[edgeId]; 55530 hoverChanged = true; 55531 } 55532 } 55533 } 55534 55535 if (object !== undefined) { 55536 hoverChanged = hoverChanged || this.emitHoverEvent(event, pointer, object); 55537 if (object instanceof Node && this.options.hoverConnectedEdges === true) { 55538 this._hoverConnectedEdges(object); 55539 } 55540 } 55541 55542 if (hoverChanged === true) { 55543 this.body.emitter.emit('_requestRedraw'); 55544 } 55545 } 55546 55547 /** 55548 * 55549 * retrieve the currently selected objects 55550 * @return {{nodes: Array.<string>, edges: Array.<string>}} selection 55551 */ 55552 55553 }, { 55554 key: 'getSelection', 55555 value: function getSelection() { 55556 var nodeIds = this.getSelectedNodes(); 55557 var edgeIds = this.getSelectedEdges(); 55558 return { nodes: nodeIds, edges: edgeIds }; 55559 } 55560 55561 /** 55562 * 55563 * retrieve the currently selected nodes 55564 * @return {string[]} selection An array with the ids of the 55565 * selected nodes. 55566 */ 55567 55568 }, { 55569 key: 'getSelectedNodes', 55570 value: function getSelectedNodes() { 55571 var idArray = []; 55572 if (this.options.selectable === true) { 55573 for (var nodeId in this.selectionObj.nodes) { 55574 if (this.selectionObj.nodes.hasOwnProperty(nodeId)) { 55575 idArray.push(this.selectionObj.nodes[nodeId].id); 55576 } 55577 } 55578 } 55579 return idArray; 55580 } 55581 55582 /** 55583 * 55584 * retrieve the currently selected edges 55585 * @return {Array} selection An array with the ids of the 55586 * selected nodes. 55587 */ 55588 55589 }, { 55590 key: 'getSelectedEdges', 55591 value: function getSelectedEdges() { 55592 var idArray = []; 55593 if (this.options.selectable === true) { 55594 for (var edgeId in this.selectionObj.edges) { 55595 if (this.selectionObj.edges.hasOwnProperty(edgeId)) { 55596 idArray.push(this.selectionObj.edges[edgeId].id); 55597 } 55598 } 55599 } 55600 return idArray; 55601 } 55602 55603 /** 55604 * Updates the current selection 55605 * @param {{nodes: Array.<string>, edges: Array.<string>}} selection 55606 * @param {Object} options Options 55607 */ 55608 55609 }, { 55610 key: 'setSelection', 55611 value: function setSelection(selection) { 55612 var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; 55613 55614 var i = void 0, 55615 id = void 0; 55616 55617 if (!selection || !selection.nodes && !selection.edges) throw 'Selection must be an object with nodes and/or edges properties'; 55618 // first unselect any selected node, if option is true or undefined 55619 if (options.unselectAll || options.unselectAll === undefined) { 55620 this.unselectAll(); 55621 } 55622 if (selection.nodes) { 55623 for (i = 0; i < selection.nodes.length; i++) { 55624 id = selection.nodes[i]; 55625 55626 var node = this.body.nodes[id]; 55627 if (!node) {
55628 throw new RangeError('Node with id "' + id + '" not found'); 55629 } 55630 // don't select edges with it 55631 this.selectObject(node, options.highlightEdges); 55632 } 55633 } 55634 55635 if (selection.edges) { 55636 for (i = 0; i < selection.edges.length; i++) { 55637 id = selection.edges[i]; 55638 55639 var edge = this.body.edges[id]; 55640 if (!edge) { 55641 throw new RangeError('Edge with id "' + id + '" not found'); 55642 } 55643 this.selectObject(edge); 55644 } 55645 } 55646 this.body.emitter.emit('_requestRedraw'); 55647 } 55648 55649 /** 55650 * select zero or more nodes with the option to highlight edges 55651 * @param {number[] | string[]} selection An array with the ids of the 55652 * selected nodes. 55653 * @param {boolean} [highlightEdges] 55654 */ 55655 55656 }, { 55657 key: 'selectNodes', 55658 value: function selectNodes(selection) { 55659 var highlightEdges = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true; 55660 55661 if (!selection || selection.length === undefined) throw 'Selection must be an array with ids'; 55662 55663 this.setSelection({ nodes: selection }, { highlightEdges: highlightEdges }); 55664 } 55665 55666 /** 55667 * select zero or more edges 55668 * @param {number[] | string[]} selection An array with the ids of the 55669 * selected nodes. 55670 */ 55671 55672 }, { 55673 key: 'selectEdges', 55674 value: function selectEdges(selection) { 55675 if (!selection || selection.length === undefined) throw 'Selection must be an array with ids'; 55676 55677 this.setSelection({ edges: selection }); 55678 } 55679 55680 /** 55681 * Validate the selection: remove ids of nodes which no longer exist 55682 * @private 55683 */ 55684 55685 }, { 55686 key: 'updateSelection', 55687 value: function updateSelection() { 55688 for (var nodeId in this.selectionObj.nodes) { 55689 if (this.selectionObj.nodes.hasOwnProperty(nodeId)) { 55690 if (!this.body.nodes.hasOwnProperty(nodeId)) { 55691 delete this.selectionObj.nodes[nodeId]; 55692 } 55693 } 55694 } 55695 for (var edgeId in this.selectionObj.edges) { 55696 if (this.selectionObj.edges.hasOwnProperty(edgeId)) { 55697 if (!this.body.edges.hasOwnProperty(edgeId)) { 55698 delete this.selectionObj.edges[edgeId]; 55699 } 55700 } 55701 } 55702 } 55703 55704 /** 55705 * Determine all the visual elements clicked which are on the given point. 55706 * 55707 * All elements are returned; this includes nodes, edges and their labels. 55708 * The order returned is from highest to lowest, i.e. element 0 of the return 55709 * value is the topmost item clicked on. 55710 * 55711 * The return value consists of an array of the following possible elements: 55712 * 55713 * - `{nodeId:number}` - node with given id clicked on 55714 * - `{nodeId:number, labelId:0}` - label of node with given id clicked on 55715 * - `{edgeId:number}` - edge with given id clicked on 55716 * - `{edge:number, labelId:0}` - label of edge with given id clicked on 55717 * 55718 * ## NOTES 55719 * 55720 * - Currently, there is only one label associated with a node or an edge, 55721 * but this is expected to change somewhere in the future. 55722 * - Since there is no z-indexing yet, it is not really possible to set the nodes and 55723 * edges in the correct order. For the time being, nodes come first. 55724 * 55725 * @param {point} pointer mouse position in screen coordinates 55726 * @returns {Array.<nodeClickItem|nodeLabelClickItem|edgeClickItem|edgeLabelClickItem>} 55727 * @private 55728 */ 55729 55730 }, { 55731 key: 'getClickedItems', 55732 value: function getClickedItems(pointer) { 55733 var point = this.canvas.DOMtoCanvas(pointer); 55734 var items = []; 55735 55736 // Note reverse order; we want the topmost clicked items to be first in the array 55737 // Also note that selected nodes are disregarded here; these normally display on top 55738 var nodeIndices = this.body.nodeIndices; 55739 var nodes = this.body.nodes; 55740 for (var i = nodeIndices.length - 1; i >= 0; i--) { 55741 var node = nodes[nodeIndices[i]]; 55742 var ret = node.getItemsOnPoint(point); 55743 items.push.apply(items, ret); // Append the return value to the running list. 55744 } 55745 55746 var edgeIndices = this.body.edgeIndices; 55747 var edges = this.body.edges; 55748 for (var _i = edgeIndices.length - 1; _i >= 0; _i--) { 55749 var edge = edges[edgeIndices[_i]]; 55750 var _ret = edge.getItemsOnPoint(point); 55751 items.push.apply(items, _ret); // Append the return value to the running list. 55752 } 55753 55754 return items; 55755 } 55756 }]); 55757 return SelectionHandler; 55758}(); 55759 55760exports['default'] = SelectionHandler; 55761 55762/***/ }), 55763/* 235 */ 55764/***/ (function(module, exports, __webpack_require__) { 55765 55766"use strict"; 55767 55768/** 55769 * There's a mix-up with terms in the code. Following are the formal definitions: 55770 * 55771 * tree - a strict hierarchical network, i.e. every node has at most one parent 55772 * forest - a collection of trees. These distinct trees are thus not connected. 55773 * 55774 * So: 55775 * - in a network that is not a tree, there exist nodes with multiple parents. 55776 * - a network consisting of unconnected sub-networks, of which at least one 55777 * is not a tree, is not a forest. 55778 * 55779 * In the code, the definitions are: 55780 * 55781 * tree - any disconnected sub-network, strict hierarchical or not. 55782 * forest - a bunch of these sub-networks 55783 * 55784 * The difference between tree and not-tree is important in the code, notably within 55785 * to the block-shifting algorithm. The algorithm assumes formal trees and fails 55786 * for not-trees, often in a spectacular manner (search for 'exploding network' in the issues). 55787 * 55788 * In order to distinguish the definitions in the following code, the adjective 'formal' is 55789 * used. If 'formal' is absent, you must assume the non-formal definition. 55790 * 55791 * ---------------------------------------------------------------------------------- 55792 * NOTES 55793 * ===== 55794 * 55795 * A hierarchical layout is a different thing from a hierarchical network. 55796 * The layout is a way to arrange the nodes in the view; this can be done 55797 * on non-hierarchical networks as well. The converse is also possible. 55798 */ 55799 55800Object.defineProperty(exports, "__esModule", { 55801 value: true 55802}); 55803 55804var _slicedToArray2 = __webpack_require__(30); 55805 55806var _slicedToArray3 = _interopRequireDefault(_slicedToArray2); 55807 55808var _typeof2 = __webpack_require__(6); 55809 55810var _typeof3 = _interopRequireDefault(_typeof2); 55811 55812var _keys = __webpack_require__(8); 55813 55814var _keys2 = _interopRequireDefault(_keys); 55815 55816var _classCallCheck2 = __webpack_require__(0); 55817 55818var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); 55819 55820var _createClass2 = __webpack_require__(1); 55821 55822var _createClass3 = _interopRequireDefault(_createClass2); 55823 55824function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 55825 55826var util = __webpack_require__(2); 55827var NetworkUtil = __webpack_require__(76)['default']; 55828 55829var _require = __webpack_require__(236), 55830 HorizontalStrategy = _require.HorizontalStrategy, 55831 VerticalStrategy = _require.VerticalStrategy; 55832 55833/** 55834 * Container for derived data on current network, relating to hierarchy. 55835 * 55836 * @private 55837 */ 55838 55839 55840var HierarchicalStatus = function () { 55841 /** 55842 * @ignore 55843 */ 55844 function HierarchicalStatus() { 55845 (0, _classCallCheck3['default'])(this, HierarchicalStatus); 55846 55847 this.childrenReference = {}; // child id's per node id 55848 this.parentReference = {}; // parent id's per node id 55849 this.trees = {}; // tree id per node id; i.e. to which tree does given node id belong 55850 55851 this.distributionOrdering = {}; // The nodes per level, in the display order 55852 this.levels = {}; // hierarchy level per node id 55853 this.distributionIndex = {}; // The position of the node in the level sorting order, per node id. 55854 55855 this.isTree = false; // True if current network is a formal tree 55856 this.treeIndex = -1; // Highest tree id in current network. 55857 } 55858 55859 /** 55860 * Add the relation between given nodes to the current state. 55861 * 55862 * @param {Node.id} parentNodeId 55863 * @param {Node.id} childNodeId 55864 */ 55865 55866 55867 (0, _createClass3['default'])(HierarchicalStatus, [{ 55868 key: 'addRelation', 55869 value: function addRelation(parentNodeId, childNodeId) { 55870 if (this.childrenReference[parentNodeId] === undefined) { 55871 this.childrenReference[parentNodeId] = []; 55872 } 55873 this.childrenReference[parentNodeId].push(childNodeId); 55874 55875 if (this.parentReference[childNodeId] === undefined) { 55876 this.parentReference[childNodeId] = []; 55877 } 55878 this.parentReference[childNodeId].push(parentNodeId); 55879 } 55880 55881 /** 55882 * Check if the current state is for a formal tree or formal forest. 55883 * 55884 * This is the case if every node has at most one parent. 55885 * 55886 * Pre: parentReference init'ed properly for current network 55887 */ 55888 55889 }, { 55890 key: 'checkIfTree', 55891 value: function checkIfTree() { 55892 for (var i in this.parentReference) { 55893 if (this.parentReference[i].length > 1) { 55894 this.isTree = false;
55895 return; 55896 } 55897 } 55898 55899 this.isTree = true; 55900 } 55901 55902 /** 55903 * Return the number of separate trees in the current network. 55904 * @returns {number} 55905 */ 55906 55907 }, { 55908 key: 'numTrees', 55909 value: function numTrees() { 55910 return this.treeIndex + 1; // This assumes the indexes are assigned consecitively 55911 } 55912 55913 /** 55914 * Assign a tree id to a node 55915 * @param {Node} node 55916 * @param {string|number} treeId 55917 */ 55918 55919 }, { 55920 key: 'setTreeIndex', 55921 value: function setTreeIndex(node, treeId) { 55922 if (treeId === undefined) return; // Don't bother 55923 55924 if (this.trees[node.id] === undefined) { 55925 this.trees[node.id] = treeId; 55926 this.treeIndex = Math.max(treeId, this.treeIndex); 55927 } 55928 } 55929 55930 /** 55931 * Ensure level for given id is defined. 55932 * 55933 * Sets level to zero for given node id if not already present 55934 * 55935 * @param {Node.id} nodeId 55936 */ 55937 55938 }, { 55939 key: 'ensureLevel', 55940 value: function ensureLevel(nodeId) { 55941 if (this.levels[nodeId] === undefined) { 55942 this.levels[nodeId] = 0; 55943 } 55944 } 55945 55946 /** 55947 * get the maximum level of a branch. 55948 * 55949 * TODO: Never entered; find a test case to test this! 55950 * @param {Node.id} nodeId 55951 * @returns {number} 55952 */ 55953 55954 }, { 55955 key: 'getMaxLevel', 55956 value: function getMaxLevel(nodeId) { 55957 var _this = this; 55958 55959 var accumulator = {}; 55960 55961 var _getMaxLevel = function _getMaxLevel(nodeId) { 55962 if (accumulator[nodeId] !== undefined) { 55963 return accumulator[nodeId]; 55964 } 55965 var level = _this.levels[nodeId]; 55966 if (_this.childrenReference[nodeId]) { 55967 var children = _this.childrenReference[nodeId]; 55968 if (children.length > 0) { 55969 for (var i = 0; i < children.length; i++) { 55970 level = Math.max(level, _getMaxLevel(children[i])); 55971 } 55972 } 55973 } 55974 accumulator[nodeId] = level; 55975 return level; 55976 }; 55977 55978 return _getMaxLevel(nodeId); 55979 } 55980 55981 /** 55982 * 55983 * @param {Node} nodeA 55984 * @param {Node} nodeB 55985 */ 55986 55987 }, { 55988 key: 'levelDownstream', 55989 value: function levelDownstream(nodeA, nodeB) { 55990 if (this.levels[nodeB.id] === undefined) { 55991 // set initial level 55992 if (this.levels[nodeA.id] === undefined) { 55993 this.levels[nodeA.id] = 0; 55994 } 55995 // set level 55996 this.levels[nodeB.id] = this.levels[nodeA.id] + 1; 55997 } 55998 } 55999 56000 /** 56001 * Small util method to set the minimum levels of the nodes to zero. 56002 * 56003 * @param {Array.<Node>} nodes 56004 */ 56005 56006 }, { 56007 key: 'setMinLevelToZero', 56008 value: function setMinLevelToZero(nodes) { 56009 var minLevel = 1e9; 56010 // get the minimum level 56011 for (var nodeId in nodes) { 56012 if (nodes.hasOwnProperty(nodeId)) { 56013 if (this.levels[nodeId] !== undefined) { 56014 minLevel = Math.min(this.levels[nodeId], minLevel); 56015 } 56016 } 56017 } 56018 56019 // subtract the minimum from the set so we have a range starting from 0 56020 for (var _nodeId in nodes) { 56021 if (nodes.hasOwnProperty(_nodeId)) { 56022 if (this.levels[_nodeId] !== undefined) { 56023 this.levels[_nodeId] -= minLevel; 56024 } 56025 } 56026 } 56027 } 56028 56029 /** 56030 * Get the min and max xy-coordinates of a given tree 56031 * 56032 * @param {Array.<Node>} nodes 56033 * @param {number} index 56034 * @returns {{min_x: number, max_x: number, min_y: number, max_y: number}} 56035 */ 56036 56037 }, { 56038 key: 'getTreeSize', 56039 value: function getTreeSize(nodes, index) { 56040 var min_x = 1e9; 56041 var max_x = -1e9; 56042 var min_y = 1e9; 56043 var max_y = -1e9; 56044 56045 for (var nodeId in this.trees) { 56046 if (this.trees.hasOwnProperty(nodeId)) { 56047 if (this.trees[nodeId] === index) { 56048 var node = nodes[nodeId]; 56049 min_x = Math.min(node.x, min_x); 56050 max_x = Math.max(node.x, max_x); 56051 min_y = Math.min(node.y, min_y); 56052 max_y = Math.max(node.y, max_y); 56053 } 56054 } 56055 } 56056 56057 return { 56058 min_x: min_x, 56059 max_x: max_x, 56060 min_y: min_y, 56061 max_y: max_y 56062 }; 56063 } 56064 56065 /** 56066 * Check if two nodes have the same parent(s) 56067 * 56068 * @param {Node} node1 56069 * @param {Node} node2 56070 * @return {boolean} true if the two nodes have a same ancestor node, false otherwise 56071 */ 56072 56073 }, { 56074 key: 'hasSameParent', 56075 value: function hasSameParent(node1, node2) { 56076 var parents1 = this.parentReference[node1.id]; 56077 var parents2 = this.parentReference[node2.id]; 56078 if (parents1 === undefined || parents2 === undefined) { 56079 return false;
56080 } 56081 56082 for (var i = 0; i < parents1.length; i++) { 56083 for (var j = 0; j < parents2.length; j++) { 56084 if (parents1[i] == parents2[j]) { 56085 return true; 56086 } 56087 } 56088 } 56089 return false; 56090 } 56091 56092 /** 56093 * Check if two nodes are in the same tree. 56094 * 56095 * @param {Node} node1 56096 * @param {Node} node2 56097 * @return {Boolean} true if this is so, false otherwise 56098 */ 56099 56100 }, { 56101 key: 'inSameSubNetwork', 56102 value: function inSameSubNetwork(node1, node2) { 56103 return this.trees[node1.id] === this.trees[node2.id]; 56104 } 56105 56106 /** 56107 * Get a list of the distinct levels in the current network 56108 * 56109 * @returns {Array} 56110 */ 56111 56112 }, { 56113 key: 'getLevels', 56114 value: function getLevels() { 56115 return (0, _keys2['default'])(this.distributionOrdering); 56116 } 56117 56118 /** 56119 * Add a node to the ordering per level 56120 * 56121 * @param {Node} node 56122 * @param {number} level 56123 */ 56124 56125 }, { 56126 key: 'addToOrdering', 56127 value: function addToOrdering(node, level) { 56128 if (this.distributionOrdering[level] === undefined) { 56129 this.distributionOrdering[level] = []; 56130 } 56131 56132 var isPresent = false; 56133 var curLevel = this.distributionOrdering[level]; 56134 for (var n in curLevel) { 56135 //if (curLevel[n].id === node.id) { 56136 if (curLevel[n] === node) { 56137 isPresent = true; 56138 break; 56139 } 56140 } 56141 56142 if (!isPresent) { 56143 this.distributionOrdering[level].push(node); 56144 this.distributionIndex[node.id] = this.distributionOrdering[level].length - 1; 56145 } 56146 } 56147 }]); 56148 return HierarchicalStatus; 56149}(); 56150 56151/** 56152 * The Layout Engine 56153 */ 56154 56155 56156var LayoutEngine = function () { 56157 /** 56158 * @param {Object} body 56159 */ 56160 function LayoutEngine(body) { 56161 (0, _classCallCheck3['default'])(this, LayoutEngine); 56162 56163 this.body = body; 56164 56165 this.initialRandomSeed = Math.round(Math.random() * 1000000); 56166 this.randomSeed = this.initialRandomSeed; 56167 this.setPhysics = false; 56168 this.options = {}; 56169 this.optionsBackup = { physics: {} }; 56170 56171 this.defaultOptions = { 56172 randomSeed: undefined, 56173 improvedLayout: true, 56174 hierarchical: { 56175 enabled: false, 56176 levelSeparation: 150, 56177 nodeSpacing: 100, 56178 treeSpacing: 200, 56179 blockShifting: true, 56180 edgeMinimization: true, 56181 parentCentralization: true, 56182 direction: 'UD', // UD, DU, LR, RL 56183 sortMethod: 'hubsize' // hubsize, directed 56184 } 56185 }; 56186 util.extend(this.options, this.defaultOptions); 56187 this.bindEventListeners(); 56188 } 56189 56190 /** 56191 * Binds event listeners 56192 */ 56193 56194 56195 (0, _createClass3['default'])(LayoutEngine, [{ 56196 key: 'bindEventListeners', 56197 value: function bindEventListeners() { 56198 var _this2 = this; 56199 56200 this.body.emitter.on('_dataChanged', function () { 56201 _this2.setupHierarchicalLayout(); 56202 }); 56203 this.body.emitter.on('_dataLoaded', function () { 56204 _this2.layoutNetwork(); 56205 }); 56206 this.body.emitter.on('_resetHierarchicalLayout', function () { 56207 _this2.setupHierarchicalLayout(); 56208 }); 56209 this.body.emitter.on('_adjustEdgesForHierarchicalLayout', function () { 56210 if (_this2.options.hierarchical.enabled !== true) { 56211 return; 56212 } 56213 // get the type of static smooth curve in case it is required 56214 var type = _this2.direction.curveType(); 56215 56216 // force all edges into static smooth curves. 56217 _this2.body.emitter.emit('_forceDisableDynamicCurves', type, false); 56218 }); 56219 } 56220 56221 /** 56222 * 56223 * @param {Object} options 56224 * @param {Object} allOptions 56225 * @returns {Object} 56226 */ 56227 56228 }, { 56229 key: 'setOptions', 56230 value: function setOptions(options, allOptions) { 56231 if (options !== undefined) { 56232 var hierarchical = this.options.hierarchical; 56233 var prevHierarchicalState = hierarchical.enabled; 56234 util.selectiveDeepExtend(["randomSeed", "improvedLayout"], this.options, options); 56235 util.mergeOptions(this.options, options, 'hierarchical'); 56236 if (options.randomSeed !== undefined) { 56237 this.initialRandomSeed = options.randomSeed; 56238 } 56239 56240 if (hierarchical.enabled === true) { 56241 if (prevHierarchicalState === true) { 56242 // refresh the overridden options for nodes and edges. 56243 this.body.emitter.emit('refresh', true); 56244 } 56245 56246 // make sure the level separation is the right way up 56247 if (hierarchical.direction === 'RL' || hierarchical.direction === 'DU') { 56248 if (hierarchical.levelSeparation > 0) { 56249 hierarchical.levelSeparation *= -1; 56250 } 56251 } else { 56252 if (hierarchical.levelSeparation < 0) { 56253 hierarchical.levelSeparation *= -1; 56254 } 56255 } 56256 56257 this.setDirectionStrategy(); 56258 56259 this.body.emitter.emit('_resetHierarchicalLayout'); 56260 // because the hierarchical system needs it's own physics and smooth curve settings, 56261 // we adapt the other options if needed. 56262 return this.adaptAllOptionsForHierarchicalLayout(allOptions); 56263 } else { 56264 if (prevHierarchicalState === true) { 56265 // refresh the overridden options for nodes and edges. 56266 this.body.emitter.emit('refresh'); 56267 return util.deepExtend(allOptions, this.optionsBackup); 56268 } 56269 } 56270 } 56271 return allOptions; 56272 } 56273 56274 /** 56275 * 56276 * @param {Object} allOptions 56277 * @returns {Object} 56278 */ 56279 56280 }, { 56281 key: 'adaptAllOptionsForHierarchicalLayout', 56282 value: function adaptAllOptionsForHierarchicalLayout(allOptions) { 56283 if (this.options.hierarchical.enabled === true) { 56284 var backupPhysics = this.optionsBackup.physics; 56285 56286 // set the physics 56287 if (allOptions.physics === undefined || allOptions.physics === true) { 56288 allOptions.physics = { 56289 enabled: backupPhysics.enabled === undefined ? true : backupPhysics.enabled, 56290 solver: 'hierarchicalRepulsion' 56291 }; 56292 backupPhysics.enabled = backupPhysics.enabled === undefined ? true : backupPhysics.enabled; 56293 backupPhysics.solver = backupPhysics.solver || 'barnesHut'; 56294 } else if ((0, _typeof3['default'])(allOptions.physics) === 'object') { 56295 backupPhysics.enabled = allOptions.physics.enabled === undefined ? true : allOptions.physics.enabled; 56296 backupPhysics.solver = allOptions.physics.solver || 'barnesHut'; 56297 allOptions.physics.solver = 'hierarchicalRepulsion'; 56298 } else if (allOptions.physics !== false) { 56299 backupPhysics.solver = 'barnesHut'; 56300 allOptions.physics = { solver: 'hierarchicalRepulsion' }; 56301 } 56302 56303 // get the type of static smooth curve in case it is required 56304 var type = this.direction.curveType(); 56305 56306 // disable smooth curves if nothing is defined. If smooth curves have been turned on, 56307 // turn them into static smooth curves. 56308 if (allOptions.edges === undefined) { 56309 this.optionsBackup.edges = { smooth: { enabled: true, type: 'dynamic' } }; 56310 allOptions.edges = { smooth: false }; 56311 } else if (allOptions.edges.smooth === undefined) { 56312 this.optionsBackup.edges = { smooth: { enabled: true, type: 'dynamic' } }; 56313 allOptions.edges.smooth = false;
56314 } else { 56315 if (typeof allOptions.edges.smooth === 'boolean') { 56316 this.optionsBackup.edges = { smooth: allOptions.edges.smooth }; 56317 allOptions.edges.smooth = { enabled: allOptions.edges.smooth, type: type }; 56318 } else { 56319 var smooth = allOptions.edges.smooth; 56320 56321 // allow custom types except for dynamic 56322 if (smooth.type !== undefined && smooth.type !== 'dynamic') { 56323 type = smooth.type; 56324 } 56325 56326 // TODO: this is options merging; see if the standard routines can be used here. 56327 this.optionsBackup.edges = { 56328 smooth: smooth.enabled === undefined ? true : smooth.enabled, 56329 type: smooth.type === undefined ? 'dynamic' : smooth.type, 56330 roundness: smooth.roundness === undefined ? 0.5 : smooth.roundness, 56331 forceDirection: smooth.forceDirection === undefined ? false : smooth.forceDirection 56332 }; 56333 56334 // NOTE: Copying an object to self; this is basically setting defaults for undefined variables 56335 allOptions.edges.smooth = { 56336 enabled: smooth.enabled === undefined ? true : smooth.enabled, 56337 type: type, 56338 roundness: smooth.roundness === undefined ? 0.5 : smooth.roundness, 56339 forceDirection: smooth.forceDirection === undefined ? false : smooth.forceDirection 56340 }; 56341 } 56342 } 56343 56344 // Force all edges into static smooth curves. 56345 // Only applies to edges that do not use the global options for smooth. 56346 this.body.emitter.emit('_forceDisableDynamicCurves', type); 56347 } 56348 56349 return allOptions; 56350 } 56351 56352 /** 56353 * 56354 * @returns {number} 56355 */ 56356 56357 }, { 56358 key: 'seededRandom', 56359 value: function seededRandom() { 56360 var x = Math.sin(this.randomSeed++) * 10000; 56361 return x - Math.floor(x); 56362 } 56363 56364 /** 56365 * 56366 * @param {Array.<Node>} nodesArray 56367 */ 56368 56369 }, { 56370 key: 'positionInitially', 56371 value: function positionInitially(nodesArray) { 56372 if (this.options.hierarchical.enabled !== true) { 56373 this.randomSeed = this.initialRandomSeed; 56374 var radius = nodesArray.length + 50; 56375 for (var i = 0; i < nodesArray.length; i++) { 56376 var node = nodesArray[i]; 56377 var angle = 2 * Math.PI * this.seededRandom(); 56378 if (node.x === undefined) { 56379 node.x = radius * Math.cos(angle); 56380 } 56381 if (node.y === undefined) { 56382 node.y = radius * Math.sin(angle); 56383 } 56384 } 56385 } 56386 } 56387 56388 /** 56389 * Use Kamada Kawai to position nodes. This is quite a heavy algorithm so if there are a lot of nodes we 56390 * cluster them first to reduce the amount. 56391 */ 56392 56393 }, { 56394 key: 'layoutNetwork', 56395 value: function layoutNetwork() { 56396 if (this.options.hierarchical.enabled !== true && this.options.improvedLayout === true) { 56397 var indices = this.body.nodeIndices; 56398 56399 // first check if we should Kamada Kawai to layout. The threshold is if less than half of the visible 56400 // nodes have predefined positions we use this. 56401 var positionDefined = 0; 56402 for (var i = 0; i < indices.length; i++) { 56403 var node = this.body.nodes[indices[i]]; 56404 if (node.predefinedPosition === true) { 56405 positionDefined += 1; 56406 } 56407 } 56408 56409 // if less than half of the nodes have a predefined position we continue 56410 if (positionDefined < 0.5 * indices.length) { 56411 var MAX_LEVELS = 10; 56412 var level = 0; 56413 var clusterThreshold = 150; // TODO add this to options 56414 56415 // 56416 // Define the options for the hidden cluster nodes 56417 // These options don't propagate outside the clustering phase. 56418 // 56419 // Some options are explicitly disabled, because they may be set in group or default node options. 56420 // The clusters are never displayed, so most explicit settings here serve as performance optimizations. 56421 // 56422 // The explicit setting of 'shape' is to avoid `shape: 'image'`; images are not passed to the hidden 56423 // cluster nodes, leading to an exception on creation. 56424 // 56425 // All settings here are performance related, except when noted otherwise. 56426 // 56427 var clusterOptions = { 56428 clusterNodeProperties: { 56429 shape: 'ellipse', // Bugfix: avoid type 'image', no images supplied 56430 label: '', // avoid label handling 56431 group: '', // avoid group handling 56432 font: { multi: false } // avoid font propagation 56433 }, 56434 clusterEdgeProperties: { 56435 label: '', // avoid label handling 56436 font: { multi: false }, // avoid font propagation 56437 smooth: { 56438 enabled: false // avoid drawing penalty for complex edges 56439 } 56440 } 56441 }; 56442 56443 // if there are a lot of nodes, we cluster before we run the algorithm. 56444 // NOTE: this part fails to find clusters for large scale-free networks, which should 56445 // be easily clusterable. 56446 // TODO: examine why this is so 56447 if (indices.length > clusterThreshold) { 56448 var startLength = indices.length; 56449 while (indices.length > clusterThreshold && level <= MAX_LEVELS) { 56450 //console.time("clustering")
56451 level += 1; 56452 var before = indices.length; 56453 // if there are many nodes we do a hubsize cluster 56454 if (level % 3 === 0) { 56455 this.body.modules.clustering.clusterBridges(clusterOptions); 56456 } else { 56457 this.body.modules.clustering.clusterOutliers(clusterOptions); 56458 } 56459 var after = indices.length; 56460 if (before == after && level % 3 !== 0) { 56461 this._declusterAll(); 56462 this.body.emitter.emit("_layoutFailed"); 56463 console.info("This network could not be positioned by this version of the improved layout algorithm." + " Please disable improvedLayout for better performance."); 56464 return; 56465 } 56466 //console.timeEnd("clustering") 56467 //console.log(before,level,after); 56468 } 56469 // increase the size of the edges 56470 this.body.modules.kamadaKawai.setOptions({ springLength: Math.max(150, 2 * startLength) }); 56471 } 56472 if (level > MAX_LEVELS) { 56473 console.info("The clustering didn't succeed within the amount of interations allowed," + " progressing with partial result."); 56474 } 56475 56476 // position the system for these nodes and edges 56477 this.body.modules.kamadaKawai.solve(indices, this.body.edgeIndices, true); 56478 56479 // shift to center point 56480 this._shiftToCenter(); 56481 56482 // perturb the nodes a little bit to force the physics to kick in 56483 var offset = 70; 56484 for (var _i = 0; _i < indices.length; _i++) { 56485 // Only perturb the nodes that aren't fixed 56486 var _node = this.body.nodes[indices[_i]]; 56487 if (_node.predefinedPosition === false) { 56488 _node.x += (0.5 - this.seededRandom()) * offset; 56489 _node.y += (0.5 - this.seededRandom()) * offset; 56490 } 56491 } 56492 56493 // uncluster all clusters 56494 this._declusterAll(); 56495 56496 // reposition all bezier nodes. 56497 this.body.emitter.emit("_repositionBezierNodes"); 56498 } 56499 } 56500 } 56501 56502 /** 56503 * Move all the nodes towards to the center so gravitational pull wil not move the nodes away from view 56504 * @private 56505 */ 56506 56507 }, { 56508 key: '_shiftToCenter', 56509 value: function _shiftToCenter() { 56510 var range = NetworkUtil.getRangeCore(this.body.nodes, this.body.nodeIndices); 56511 var center = NetworkUtil.findCenter(range); 56512 for (var i = 0; i < this.body.nodeIndices.length; i++) { 56513 var node = this.body.nodes[this.body.nodeIndices[i]]; 56514 node.x -= center.x; 56515 node.y -= center.y; 56516 } 56517 } 56518 56519 /** 56520 * Expands all clusters 56521 * @private 56522 */ 56523 56524 }, { 56525 key: '_declusterAll', 56526 value: function _declusterAll() { 56527 var clustersPresent = true; 56528 while (clustersPresent === true) { 56529 clustersPresent = false; 56530 for (var i = 0; i < this.body.nodeIndices.length; i++) { 56531 if (this.body.nodes[this.body.nodeIndices[i]].isCluster === true) { 56532 clustersPresent = true; 56533 this.body.modules.clustering.openCluster(this.body.nodeIndices[i], {}, false); 56534 } 56535 } 56536 if (clustersPresent === true) { 56537 this.body.emitter.emit('_dataChanged'); 56538 } 56539 } 56540 } 56541 56542 /** 56543 * 56544 * @returns {number|*} 56545 */ 56546 56547 }, { 56548 key: 'getSeed', 56549 value: function getSeed() { 56550 return this.initialRandomSeed; 56551 } 56552 56553 /** 56554 * This is the main function to layout the nodes in a hierarchical way. 56555 * It checks if the node details are supplied correctly 56556 * 56557 * @private 56558 */ 56559 56560 }, { 56561 key: 'setupHierarchicalLayout', 56562 value: function setupHierarchicalLayout() { 56563 if (this.options.hierarchical.enabled === true && this.body.nodeIndices.length > 0) { 56564 // get the size of the largest hubs and check if the user has defined a level for a node. 56565 var node = void 0, 56566 nodeId = void 0; 56567 var definedLevel = false; 56568 var undefinedLevel = false; 56569 this.lastNodeOnLevel = {}; 56570 this.hierarchical = new HierarchicalStatus(); 56571 56572 for (nodeId in this.body.nodes) { 56573 if (this.body.nodes.hasOwnProperty(nodeId)) { 56574 node = this.body.nodes[nodeId]; 56575 if (node.options.level !== undefined) { 56576 definedLevel = true; 56577 this.hierarchical.levels[nodeId] = node.options.level; 56578 } else { 56579 undefinedLevel = true; 56580 } 56581 } 56582 } 56583 56584 // if the user defined some levels but not all, alert and run without hierarchical layout 56585 if (undefinedLevel === true && definedLevel === true) { 56586 throw new Error('To use the hierarchical layout, nodes require either no predefined levels' + ' or levels have to be defined for all nodes.'); 56587 } else { 56588 // define levels if undefined by the users. Based on hubsize. 56589 if (undefinedLevel === true) { 56590 var sortMethod = this.options.hierarchical.sortMethod; 56591 if (sortMethod === 'hubsize') { 56592 this._determineLevelsByHubsize(); 56593 } else if (sortMethod === 'directed') { 56594 this._determineLevelsDirected(); 56595 } else if (sortMethod === 'custom') { 56596 this._determineLevelsCustomCallback(); 56597 } 56598 } 56599 56600 // fallback for cases where there are nodes but no edges 56601 for (var _nodeId2 in this.body.nodes) { 56602 if (this.body.nodes.hasOwnProperty(_nodeId2)) { 56603 this.hierarchical.ensureLevel(_nodeId2); 56604 } 56605 } 56606 // check the distribution of the nodes per level. 56607 var distribution = this._getDistribution(); 56608 56609 // get the parent children relations. 56610 this._generateMap(); 56611 56612 // place the nodes on the canvas. 56613 this._placeNodesByHierarchy(distribution); 56614 56615 // condense the whitespace. 56616 this._condenseHierarchy(); 56617 56618 // shift to center so gravity does not have to do much 56619 this._shiftToCenter(); 56620 } 56621 } 56622 } 56623 56624 /** 56625 * @private 56626 */ 56627 56628 }, { 56629 key: '_condenseHierarchy', 56630 value: function _condenseHierarchy() { 56631 var _this3 = this; 56632 56633 // Global var in this scope to define when the movement has stopped. 56634 var stillShifting = false;
56635 var branches = {}; 56636 // first we have some methods to help shifting trees around. 56637 // the main method to shift the trees 56638 var shiftTrees = function shiftTrees() { 56639 var treeSizes = getTreeSizes(); 56640 var shiftBy = 0; 56641 for (var i = 0; i < treeSizes.length - 1; i++) { 56642 var diff = treeSizes[i].max - treeSizes[i + 1].min; 56643 shiftBy += diff + _this3.options.hierarchical.treeSpacing; 56644 shiftTree(i + 1, shiftBy); 56645 } 56646 }; 56647 56648 // shift a single tree by an offset 56649 var shiftTree = function shiftTree(index, offset) { 56650 var trees = _this3.hierarchical.trees; 56651 56652 for (var nodeId in trees) { 56653 if (trees.hasOwnProperty(nodeId)) { 56654 if (trees[nodeId] === index) { 56655 _this3.direction.shift(nodeId, offset); 56656 } 56657 } 56658 } 56659 }; 56660 56661 // get the width of all trees 56662 var getTreeSizes = function getTreeSizes() { 56663 var treeWidths = []; 56664 for (var i = 0; i < _this3.hierarchical.numTrees(); i++) { 56665 treeWidths.push(_this3.direction.getTreeSize(i)); 56666 } 56667 return treeWidths; 56668 }; 56669 56670 // get a map of all nodes in this branch 56671 var getBranchNodes = function getBranchNodes(source, map) { 56672 if (map[source.id]) { 56673 return; 56674 } 56675 map[source.id] = true; 56676 if (_this3.hierarchical.childrenReference[source.id]) { 56677 var children = _this3.hierarchical.childrenReference[source.id]; 56678 if (children.length > 0) { 56679 for (var i = 0; i < children.length; i++) { 56680 getBranchNodes(_this3.body.nodes[children[i]], map); 56681 } 56682 } 56683 } 56684 }; 56685 56686 // get a min max width as well as the maximum movement space it has on either sides 56687 // we use min max terminology because width and height can interchange depending on the direction of the layout 56688 var getBranchBoundary = function getBranchBoundary(branchMap) { 56689 var maxLevel = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 1e9; 56690 56691 var minSpace = 1e9; 56692 var maxSpace = 1e9; 56693 var min = 1e9; 56694 var max = -1e9; 56695 for (var branchNode in branchMap) { 56696 if (branchMap.hasOwnProperty(branchNode)) { 56697 var node = _this3.body.nodes[branchNode]; 56698 var level = _this3.hierarchical.levels[node.id]; 56699 var position = _this3.direction.getPosition(node); 56700 56701 // get the space around the node. 56702 56703 var _getSpaceAroundNode2 = _this3._getSpaceAroundNode(node, branchMap), 56704 _getSpaceAroundNode3 = (0, _slicedToArray3['default'])(_getSpaceAroundNode2, 2), 56705 minSpaceNode = _getSpaceAroundNode3[0], 56706 maxSpaceNode = _getSpaceAroundNode3[1]; 56707 56708 minSpace = Math.min(minSpaceNode, minSpace); 56709 maxSpace = Math.min(maxSpaceNode, maxSpace); 56710 56711 // the width is only relevant for the levels two nodes have in common. This is why we filter on this. 56712 if (level <= maxLevel) { 56713 min = Math.min(position, min); 56714 max = Math.max(position, max); 56715 } 56716 } 56717 } 56718 56719 return [min, max, minSpace, maxSpace]; 56720 }; 56721 56722 // check what the maximum level is these nodes have in common. 56723 var getCollisionLevel = function getCollisionLevel(node1, node2) { 56724 var maxLevel1 = _this3.hierarchical.getMaxLevel(node1.id); 56725 var maxLevel2 = _this3.hierarchical.getMaxLevel(node2.id); 56726 return Math.min(maxLevel1, maxLevel2); 56727 }; 56728 56729 /** 56730 * Condense elements. These can be nodes or branches depending on the callback. 56731 * 56732 * @param {function} callback 56733 * @param {Array.<number>} levels 56734 * @param {*} centerParents 56735 */ 56736 var shiftElementsCloser = function shiftElementsCloser(callback, levels, centerParents) { 56737 var hier = _this3.hierarchical; 56738 56739 for (var i = 0; i < levels.length; i++) { 56740 var level = levels[i]; 56741 var levelNodes = hier.distributionOrdering[level]; 56742 if (levelNodes.length > 1) { 56743 for (var j = 0; j < levelNodes.length - 1; j++) { 56744 var node1 = levelNodes[j]; 56745 var node2 = levelNodes[j + 1]; 56746 56747 // NOTE: logic maintained as it was; if nodes have same ancestor, 56748 // then of course they are in the same sub-network. 56749 if (hier.hasSameParent(node1, node2) && hier.inSameSubNetwork(node1, node2)) { 56750 callback(node1, node2, centerParents); 56751 } 56752 } 56753 } 56754 } 56755 }; 56756 56757 // callback for shifting branches 56758 var branchShiftCallback = function branchShiftCallback(node1, node2) { 56759 var centerParent = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
56760 56761 //window.CALLBACKS.push(() => { 56762 var pos1 = _this3.direction.getPosition(node1); 56763 var pos2 = _this3.direction.getPosition(node2); 56764 var diffAbs = Math.abs(pos2 - pos1); 56765 var nodeSpacing = _this3.options.hierarchical.nodeSpacing; 56766 //console.log("NOW CHECKING:", node1.id, node2.id, diffAbs); 56767 if (diffAbs > nodeSpacing) { 56768 var branchNodes1 = {}; 56769 var branchNodes2 = {}; 56770 56771 getBranchNodes(node1, branchNodes1); 56772 getBranchNodes(node2, branchNodes2); 56773 56774 // check the largest distance between the branches 56775 var maxLevel = getCollisionLevel(node1, node2); 56776 var branchNodeBoundary1 = getBranchBoundary(branchNodes1, maxLevel); 56777 var branchNodeBoundary2 = getBranchBoundary(branchNodes2, maxLevel); 56778 var max1 = branchNodeBoundary1[1]; 56779 var min2 = branchNodeBoundary2[0]; 56780 var minSpace2 = branchNodeBoundary2[2]; 56781 56782 //console.log(node1.id, getBranchBoundary(branchNodes1, maxLevel), node2.id, 56783 // getBranchBoundary(branchNodes2, maxLevel), maxLevel); 56784 var diffBranch = Math.abs(max1 - min2); 56785 if (diffBranch > nodeSpacing) { 56786 var offset = max1 - min2 + nodeSpacing; 56787 if (offset < -minSpace2 + nodeSpacing) { 56788 offset = -minSpace2 + nodeSpacing; 56789 //console.log("RESETTING OFFSET", max1 - min2 + this.options.hierarchical.nodeSpacing, -minSpace2, offset); 56790 } 56791 if (offset < 0) { 56792 //console.log("SHIFTING", node2.id, offset); 56793 _this3._shiftBlock(node2.id, offset); 56794 stillShifting = true; 56795 56796 if (centerParent === true) _this3._centerParent(node2); 56797 } 56798 } 56799 } 56800 //this.body.emitter.emit("_redraw");}) 56801 }; 56802 56803 var minimizeEdgeLength = function minimizeEdgeLength(iterations, node) { 56804 //window.CALLBACKS.push(() => { 56805 // console.log("ts",node.id); 56806 var nodeId = node.id; 56807 var allEdges = node.edges; 56808 var nodeLevel = _this3.hierarchical.levels[node.id]; 56809 56810 // gather constants 56811 var C2 = _this3.options.hierarchical.levelSeparation * _this3.options.hierarchical.levelSeparation; 56812 var referenceNodes = {}; 56813 var aboveEdges = []; 56814 for (var i = 0; i < allEdges.length; i++) { 56815 var edge = allEdges[i]; 56816 if (edge.toId != edge.fromId) { 56817 var otherNode = edge.toId == nodeId ? edge.from : edge.to; 56818 referenceNodes[allEdges[i].id] = otherNode; 56819 if (_this3.hierarchical.levels[otherNode.id] < nodeLevel) { 56820 aboveEdges.push(edge); 56821 } 56822 } 56823 } 56824 56825 // differentiated sum of lengths based on only moving one node over one axis 56826 var getFx = function getFx(point, edges) { 56827 var sum = 0; 56828 for (var _i2 = 0; _i2 < edges.length; _i2++) { 56829 if (referenceNodes[edges[_i2].id] !== undefined) { 56830 var a = _this3.direction.getPosition(referenceNodes[edges[_i2].id]) - point; 56831 sum += a / Math.sqrt(a * a + C2); 56832 } 56833 } 56834 return sum; 56835 }; 56836 56837 // doubly differentiated sum of lengths based on only moving one node over one axis 56838 var getDFx = function getDFx(point, edges) { 56839 var sum = 0; 56840 for (var _i3 = 0; _i3 < edges.length; _i3++) { 56841 if (referenceNodes[edges[_i3].id] !== undefined) { 56842 var a = _this3.direction.getPosition(referenceNodes[edges[_i3].id]) - point; 56843 sum -= C2 * Math.pow(a * a + C2, -1.5); 56844 } 56845 } 56846 return sum; 56847 }; 56848 56849 var getGuess = function getGuess(iterations, edges) { 56850 var guess = _this3.direction.getPosition(node); 56851 // Newton's method for optimization 56852 var guessMap = {}; 56853 for (var _i4 = 0; _i4 < iterations; _i4++) { 56854 var fx = getFx(guess, edges); 56855 var dfx = getDFx(guess, edges); 56856 56857 // we limit the movement to avoid instability. 56858 var limit = 40; 56859 var ratio = Math.max(-limit, Math.min(limit, Math.round(fx / dfx))); 56860 guess = guess - ratio; 56861 // reduce duplicates 56862 if (guessMap[guess] !== undefined) { 56863 break; 56864 } 56865 guessMap[guess] = _i4; 56866 } 56867 return guess; 56868 }; 56869 56870 var moveBranch = function moveBranch(guess) { 56871 // position node if there is space 56872 var nodePosition = _this3.direction.getPosition(node); 56873 56874 // check movable area of the branch 56875 if (branches[node.id] === undefined) { 56876 var branchNodes = {}; 56877 getBranchNodes(node, branchNodes); 56878 branches[node.id] = branchNodes; 56879 } 56880 var branchBoundary = getBranchBoundary(branches[node.id]); 56881 var minSpaceBranch = branchBoundary[2]; 56882 var maxSpaceBranch = branchBoundary[3]; 56883 56884 var diff = guess - nodePosition; 56885 56886 // check if we are allowed to move the node: 56887 var branchOffset = 0; 56888 if (diff > 0) { 56889 branchOffset = Math.min(diff, maxSpaceBranch - _this3.options.hierarchical.nodeSpacing); 56890 } else if (diff < 0) { 56891 branchOffset = -Math.min(-diff, minSpaceBranch - _this3.options.hierarchical.nodeSpacing); 56892 } 56893 56894 if (branchOffset != 0) { 56895 //console.log("moving branch:",branchOffset, maxSpaceBranch, minS
56895paceBranch) 56896 _this3._shiftBlock(node.id, branchOffset); 56897 //this.body.emitter.emit("_redraw"); 56898 stillShifting = true; 56899 } 56900 }; 56901 56902 var moveNode = function moveNode(guess) { 56903 var nodePosition = _this3.direction.getPosition(node); 56904 56905 // position node if there is space 56906 56907 var _getSpaceAroundNode4 = _this3._getSpaceAroundNode(node), 56908 _getSpaceAroundNode5 = (0, _slicedToArray3['default'])(_getSpaceAroundNode4, 2), 56909 minSpace = _getSpaceAroundNode5[0], 56910 maxSpace = _getSpaceAroundNode5[1]; 56911 56912 var diff = guess - nodePosition; 56913 // check if we are allowed to move the node: 56914 var newPosition = nodePosition; 56915 if (diff > 0) { 56916 newPosition = Math.min(nodePosition + (maxSpace - _this3.options.hierarchical.nodeSpacing), guess); 56917 } else if (diff < 0) { 56918 newPosition = Math.max(nodePosition - (minSpace - _this3.options.hierarchical.nodeSpacing), guess); 56919 } 56920 56921 if (newPosition !== nodePosition) { 56922 //console.log("moving Node:",diff, minSpace, maxSpace); 56923 _this3.direction.setPosition(node, newPosition); 56924 //this.body.emitter.emit("_redraw"); 56925 stillShifting = true; 56926 } 56927 }; 56928 56929 var guess = getGuess(iterations, aboveEdges); 56930 moveBranch(guess); 56931 guess = getGuess(iterations, allEdges); 56932 moveNode(guess); 56933 //}) 56934 }; 56935 56936 // method to remove whitespace between branches. Because we do bottom up, we can center the parents. 56937 var minimizeEdgeLengthBottomUp = function minimizeEdgeLengthBottomUp(iterations) { 56938 var levels = _this3.hierarchical.getLevels(); 56939 levels = levels.reverse(); 56940 for (var i = 0; i < iterations; i++) { 56941 stillShifting = false; 56942 for (var j = 0; j < levels.length; j++) { 56943 var level = levels[j]; 56944 var levelNodes = _this3.hierarchical.distributionOrdering[level]; 56945 for (var k = 0; k < levelNodes.length; k++) { 56946 minimizeEdgeLength(1000, levelNodes[k]); 56947 } 56948 } 56949 if (stillShifting !== true) { 56950 //console.log("FINISHED minimizeEdgeLengthBottomUp IN " + i); 56951 break; 56952 } 56953 } 56954 }; 56955 56956 // method to remove whitespace between branches. Because we do bottom up, we can center the parents. 56957 var shiftBranchesCloserBottomUp = function shiftBranchesCloserBottomUp(iterations) { 56958 var levels = _this3.hierarchical.getLevels(); 56959 levels = levels.reverse(); 56960 for (var i = 0; i < iterations; i++) { 56961 stillShifting = false; 56962 shiftElementsCloser(branchShiftCallback, levels, true); 56963 if (stillShifting !== true) { 56964 //console.log("FINISHED shiftBranchesCloserBottomUp IN " + (i+1)); 56965 break; 56966 } 56967 } 56968 }; 56969 56970 // center all parents 56971 var centerAllParents = function centerAllParents() { 56972 for (var nodeId in _this3.body.nodes) { 56973 if (_this3.body.nodes.hasOwnProperty(nodeId)) _this3._centerParent(_this3.body.nodes[nodeId]); 56974 } 56975 }; 56976 56977 // center all parents 56978 var centerAllParentsBottomUp = function centerAllParentsBottomUp() { 56979 var levels = _this3.hierarchical.getLevels(); 56980 levels = levels.reverse(); 56981 for (var i = 0; i < levels.length; i++) { 56982 var level = levels[i]; 56983 var levelNodes = _this3.hierarchical.distributionOrdering[level]; 56984 for (var j = 0; j < levelNodes.length; j++) { 56985 _this3._centerParent(levelNodes[j]); 56986 } 56987 } 56988 }; 56989 56990 // the actual work is done here. 56991 if (this.options.hierarchical.blockShifting === true) { 56992 shiftBranchesCloserBottomUp(5); 56993 centerAllParents(); 56994 } 56995 56996 // minimize edge length 56997 if (this.options.hierarchical.edgeMinimization === true) { 56998 minimizeEdgeLengthBottomUp(20); 56999 } 57000 57001 if (this.options.hierarchical.parentCentralization === true) { 57002 centerAllParentsBottomUp(); 57003 } 57004 57005 shiftTrees(); 57006 } 57007 57008 /** 57009 * This gives the space around the node. IF a map is supplied, it will only check against nodes NOT in the map. 57010 * This is used to only get the distances to nodes outside of a branch. 57011 * @param {Node} node 57012 * @param {{Node.id: vis.Node}} map 57013 * @returns {number[]} 57014 * @private 57015 */ 57016 57017 }, { 57018 key: '_getSpaceAroundNode', 57019 value: function _getSpaceAroundNode(node, map) { 57020 var useMap = true; 57021 if (map === undefined) { 57022 useMap = false;
57023 } 57024 var level = this.hierarchical.levels[node.id]; 57025 if (level !== undefined) { 57026 var index = this.hierarchical.distributionIndex[node.id]; 57027 var position = this.direction.getPosition(node); 57028 var ordering = this.hierarchical.distributionOrdering[level]; 57029 var minSpace = 1e9; 57030 var maxSpace = 1e9; 57031 if (index !== 0) { 57032 var prevNode = ordering[index - 1]; 57033 if (useMap === true && map[prevNode.id] === undefined || useMap === false) { 57034 var prevPos = this.direction.getPosition(prevNode); 57035 minSpace = position - prevPos; 57036 } 57037 } 57038 57039 if (index != ordering.length - 1) { 57040 var nextNode = ordering[index + 1]; 57041 if (useMap === true && map[nextNode.id] === undefined || useMap === false) { 57042 var nextPos = this.direction.getPosition(nextNode); 57043 maxSpace = Math.min(maxSpace, nextPos - position); 57044 } 57045 } 57046 57047 return [minSpace, maxSpace]; 57048 } else { 57049 return [0, 0]; 57050 } 57051 } 57052 57053 /** 57054 * We use this method to center a parent node and check if it does not cross other nodes when it does. 57055 * @param {Node} node 57056 * @private 57057 */ 57058 57059 }, { 57060 key: '_centerParent', 57061 value: function _centerParent(node) { 57062 if (this.hierarchical.parentReference[node.id]) { 57063 var parents = this.hierarchical.parentReference[node.id]; 57064 for (var i = 0; i < parents.length; i++) { 57065 var parentId = parents[i]; 57066 var parentNode = this.body.nodes[parentId]; 57067 var children = this.hierarchical.childrenReference[parentId]; 57068 57069 if (children !== undefined) { 57070 // get the range of the children 57071 var newPosition = this._getCenterPosition(children); 57072 57073 var position = this.direction.getPosition(parentNode); 57074 57075 var _getSpaceAroundNode6 = this._getSpaceAroundNode(parentNode), 57076 _getSpaceAroundNode7 = (0, _slicedToArray3['default'])(_getSpaceAroundNode6, 2), 57077 minSpace = _getSpaceAroundNode7[0], 57078 maxSpace = _getSpaceAroundNode7[1]; 57079 57080 var diff = position - newPosition; 57081 if (diff < 0 && Math.abs(diff) < maxSpace - this.options.hierarchical.nodeSpacing || diff > 0 && Math.abs(diff) < minSpace - this.options.hierarchical.nodeSpacing) { 57082 this.direction.setPosition(parentNode, newPosition); 57083 } 57084 } 57085 } 57086 } 57087 } 57088 57089 /** 57090 * This function places the nodes on the canvas based on the hierarchial distribution. 57091 * 57092 * @param {Object} distribution | obtained by the function this._getDistribution() 57093 * @private 57094 */ 57095 57096 }, { 57097 key: '_placeNodesByHierarchy', 57098 value: function _placeNodesByHierarchy(distribution) { 57099 this.positionedNodes = {}; 57100 // start placing all the level 0 nodes first. Then recursively position their branches. 57101 for (var level in distribution) { 57102 if (distribution.hasOwnProperty(level)) { 57103 // sort nodes in level by position: 57104 var nodeArray = (0, _keys2['default'])(distribution[level]); 57105 nodeArray = this._indexArrayToNodes(nodeArray); 57106 this.direction.sort(nodeArray); 57107 var handledNodeCount = 0; 57108 57109 for (var i = 0; i < nodeArray.length; i++) { 57110 var node = nodeArray[i]; 57111 if (this.positionedNodes[node.id] === undefined) { 57112 var spacing = this.options.hierarchical.nodeSpacing; 57113 var pos = spacing * handledNodeCount; 57114 // We get the X or Y values we need and store them in pos and previousPos. 57115 // The get and set make sure we get X or Y 57116 if (handledNodeCount > 0) { 57117 pos = this.direction.getPosition(nodeArray[i - 1]) + spacing; 57118 } 57119 this.direction.setPosition(node, pos, level); 57120 this._validatePositionAndContinue(node, level, pos); 57121 57122 handledNodeCount++; 57123 } 57124 } 57125 } 57126 } 57127 } 57128 57129 /** 57130 * This is a recursively called function to enumerate the branches from the largest hubs and place the nodes 57131 * on a X position that ensures there will be no overlap. 57132 * 57133 * @param {Node.id} parentId 57134 * @param {number} parentLevel 57135 * @private 57136 */ 57137 57138 }, { 57139 key: '_placeBranchNodes', 57140 value: function _placeBranchNodes(parentId, parentLevel) { 57141 var childRef = this.hierarchical.childrenReference[parentId]; 57142 57143 // if this is not a parent, cancel the placing. This can happen with multiple parents to one child. 57144 if (childRef === undefined) { 57145 return; 57146 } 57147 57148 // get a list of childNodes 57149 var childNodes = []; 57150 for (var i = 0; i < childRef.length; i++) { 57151 childNodes.push(this.body.nodes[childRef[i]]); 57152 } 57153 57154 // use the positions to order the nodes. 57155 this.direction.sort(childNodes); 57156 57157 // position the childNodes 57158 for (var _i5 = 0; _i5 < childNodes.length; _i5++) { 57159 var childNode = childNodes[_i5]; 57160 var childNodeLevel = this.hierarchical.levels[childNode.id]; 57161 // check if the child node is below the parent node and if it has already been positioned. 57162 if (childNodeLevel > parentLevel && this.positionedNodes[childNode.id] === undefined) { 57163 // get the amount of space required for this node. If parent the width is based on the amount of children. 57164 var spacing = this.options.hierarchical.nodeSpacing; 57165 var pos = void 0; 57166 57167 // we get the X or Y values we need and store them in pos and previousPos.
57168 // The get and set make sure we get X or Y 57169 if (_i5 === 0) { 57170 pos = this.direction.getPosition(this.body.nodes[parentId]); 57171 } else { 57172 pos = this.direction.getPosition(childNodes[_i5 - 1]) + spacing; 57173 } 57174 this.direction.setPosition(childNode, pos, childNodeLevel); 57175 this._validatePositionAndContinue(childNode, childNodeLevel, pos); 57176 } else { 57177 return; 57178 } 57179 } 57180 57181 // center the parent nodes. 57182 var center = this._getCenterPosition(childNodes); 57183 this.direction.setPosition(this.body.nodes[parentId], center, parentLevel); 57184 } 57185 57186 /** 57187 * This method checks for overlap and if required shifts the branch. It also keeps records of positioned nodes. 57188 * Finally it will call _placeBranchNodes to place the branch nodes. 57189 * @param {Node} node 57190 * @param {number} level 57191 * @param {number} pos 57192 * @private 57193 */ 57194 57195 }, { 57196 key: '_validatePositionAndContinue', 57197 value: function _validatePositionAndContinue(node, level, pos) { 57198 // This method only works for formal trees and formal forests 57199 // Early exit if this is not the case 57200 if (!this.hierarchical.isTree) return; 57201 57202 // if overlap has been detected, we shift the branch 57203 if (this.lastNodeOnLevel[level] !== undefined) { 57204 var previousPos = this.direction.getPosition(this.body.nodes[this.lastNodeOnLevel[level]]); 57205 if (pos - previousPos < this.options.hierarchical.nodeSpacing) { 57206 var diff = previousPos + this.options.hierarchical.nodeSpacing - pos; 57207 var sharedParent = this._findCommonParent(this.lastNodeOnLevel[level], node.id); 57208 this._shiftBlock(sharedParent.withChild, diff); 57209 } 57210 } 57211 57212 this.lastNodeOnLevel[level] = node.id; // store change in position. 57213 this.positionedNodes[node.id] = true; 57214 this._placeBranchNodes(node.id, level); 57215 } 57216 57217 /** 57218 * Receives an array with node indices and returns an array with the actual node references. 57219 * Used for sorting based on node properties. 57220 * @param {Array.<Node.id>} idArray 57221 * @returns {Array.<Node>} 57222 */ 57223 57224 }, { 57225 key: '_indexArrayToNodes', 57226 value: function _indexArrayToNodes(idArray) { 57227 var array = []; 57228 for (var i = 0; i < idArray.length; i++) { 57229 array.push(this.body.nodes[idArray[i]]); 57230 } 57231 return array; 57232 } 57233 57234 /** 57235 * This function get the distribution of levels based on hubsize 57236 * 57237 * @returns {Object} 57238 * @private 57239 */ 57240 57241 }, { 57242 key: '_getDistribution', 57243 value: function _getDistribution() { 57244 var distribution = {}; 57245 var nodeId = void 0, 57246 node = void 0; 57247 57248 // we fix Y because the hierarchy is vertical, 57249 // we fix X so we do not give a node an x position for a second time. 57250 // the fix of X is removed after the x value has been set. 57251 for (nodeId in this.body.nodes) { 57252 if (this.body.nodes.hasOwnProperty(nodeId)) { 57253 node = this.body.nodes[nodeId]; 57254 var level = this.hierarchical.levels[nodeId] === undefined ? 0 : this.hierarchical.levels[nodeId]; 57255 this.direction.fix(node, level); 57256 if (distribution[level] === undefined) { 57257 distribution[level] = {}; 57258 } 57259 distribution[level][nodeId] = node; 57260 } 57261 } 57262 return distribution; 57263 } 57264 57265 /** 57266 * Return the active (i.e. visible) edges for this node 57267 * 57268 * @param {Node} node 57269 * @returns {Array.<vis.Edge>} Array of edge instances 57270 * @private 57271 */ 57272 57273 }, { 57274 key: '_getActiveEdges', 57275 value: function _getActiveEdges(node) { 57276 var _this4 = this; 57277 57278 var result = []; 57279 57280 util.forEach(node.edges, function (edge) { 57281 if (_this4.body.edgeIndices.indexOf(edge.id) !== -1) { 57282 result.push(edge); 57283 } 57284 }); 57285 57286 return result; 57287 } 57288 57289 /** 57290 * Get the hubsizes for all active nodes. 57291 * 57292 * @returns {number} 57293 * @private 57294 */ 57295 57296 }, { 57297 key: '_getHubSizes', 57298 value: function _getHubSizes() { 57299 var _this5 = this; 57300 57301 var hubSizes = {}; 57302 var nodeIds = this.body.nodeIndices; 57303 57304 util.forEach(nodeIds, function (nodeId) { 57305 var node = _this5.body.nodes[nodeId]; 57306 var hubSize = _this5._getActiveEdges(node).length;
57307 hubSizes[hubSize] = true; 57308 }); 57309 57310 // Make an array of the size sorted descending 57311 var result = []; 57312 util.forEach(hubSizes, function (size) { 57313 result.push(Number(size)); 57314 }); 57315 57316 result.sort(function (a, b) { 57317 return b - a; 57318 }); 57319 57320 return result; 57321 } 57322 57323 /** 57324 * this function allocates nodes in levels based on the recursive branching from the largest hubs. 57325 * 57326 * @private 57327 */ 57328 57329 }, { 57330 key: '_determineLevelsByHubsize', 57331 value: function _determineLevelsByHubsize() { 57332 var _this6 = this; 57333 57334 var levelDownstream = function levelDownstream(nodeA, nodeB) { 57335 _this6.hierarchical.levelDownstream(nodeA, nodeB); 57336 }; 57337 57338 var hubSizes = this._getHubSizes(); 57339 57340 var _loop = function _loop(i) { 57341 var hubSize = hubSizes[i]; 57342 if (hubSize === 0) return 'break'; 57343 57344 util.forEach(_this6.body.nodeIndices, function (nodeId) { 57345 var node = _this6.body.nodes[nodeId]; 57346 57347 if (hubSize === _this6._getActiveEdges(node).length) { 57348 _this6._crawlNetwork(levelDownstream, nodeId); 57349 } 57350 }); 57351 }; 57352 57353 for (var i = 0; i < hubSizes.length; ++i) { 57354 var _ret = _loop(i); 57355 57356 if (_ret === 'break') break; 57357 } 57358 } 57359 57360 /** 57361 * TODO: release feature 57362 * TODO: Determine if this feature is needed at all 57363 * 57364 * @private 57365 */ 57366 57367 }, { 57368 key: '_determineLevelsCustomCallback', 57369 value: function _determineLevelsCustomCallback() { 57370 var _this7 = this; 57371 57372 var minLevel = 100000; 57373 57374 // TODO: this should come from options. 57375 var customCallback = function customCallback(nodeA, nodeB, edge) {// eslint-disable-line no-unused-vars 57376 57377 }; 57378 57379 // TODO: perhaps move to HierarchicalStatus. 57380 // But I currently don't see the point, this method is not used. 57381 var levelByDirection = function levelByDirection(nodeA, nodeB, edge) { 57382 var levelA = _this7.hierarchical.levels[nodeA.id]; 57383 // set initial level 57384 if (levelA === undefined) { 57385 levelA = _this7.hierarchical.levels[nodeA.id] = minLevel; 57386 } 57387 57388 var diff = customCallback(NetworkUtil.cloneOptions(nodeA, 'node'), NetworkUtil.cloneOptions(nodeB, 'node'), NetworkUtil.cloneOptions(edge, 'edge')); 57389 57390 _this7.hierarchical.levels[nodeB.id] = levelA + diff; 57391 }; 57392 57393 this._crawlNetwork(levelByDirection); 57394 this.hierarchical.setMinLevelToZero(this.body.nodes); 57395 } 57396 57397 /** 57398 * Allocate nodes in levels based on the direction of the edges. 57399 * 57400 * @private 57401 */ 57402 57403 }, { 57404 key: '_determineLevelsDirected', 57405 value: function _determineLevelsDirected() { 57406 var _this8 = this; 57407 57408 var minLevel = 10000; 57409 57410 /** 57411 * Check if there is an edge going the opposite direction for given edge 57412 * 57413 * @param {Edge} edge edge to check 57414 * @returns {boolean} true if there's another edge going into the opposite direction 57415 */ 57416 var isBidirectional = function isBidirectional(edge) { 57417 util.forEach(_this8.body.edges, function (otherEdge) { 57418 if (otherEdge.toId === edge.fromId && otherEdge.fromId === edge.toId) { 57419 return true; 57420 } 57421 }); 57422 57423 return false; 57424 }; 57425 57426 var levelByDirection = function levelByDirection(nodeA, nodeB, edge) { 57427 var levelA = _this8.hierarchical.levels[nodeA.id]; 57428 var levelB = _this8.hierarchical.levels[nodeB.id]; 57429 57430 if (isBidirectional(edge) && levelA !== undefined && levelB !== undefined) { 57431 // Don't redo the level determination if already done in this case. 57432 return; 57433 } 57434 57435 // set initial level 57436 if (levelA === undefined) { 57437 levelA = _this8.hierarchical.levels[nodeA.id] = minLevel; 57438 } 57439 if (edge.toId == nodeB.id) { 57440 _this8.hierarchical.levels[nodeB.id] = levelA + 1; 57441 } else { 57442 _this8.hierarchical.levels[nodeB.id] = levelA - 1; 57443 } 57444 }; 57445 57446 this._crawlNetwork(levelByDirection); 57447 this.hierarchical.setMinLevelToZero(this.body.nodes); 57448 } 57449 57450 /** 57451 * Update the bookkeeping of parent and child. 57452 * @private 57453 */ 57454 57455 }, { 57456 key: '_generateMap', 57457 value: function _generateMap() { 57458 var _this9 = this; 57459 57460 var fillInRelations = function fillInRelations(parentNode, childNode) { 57461 if (_this9.hierarchical.levels[childNode.id] > _this9.hierarchical.levels[parentNode.id]) { 57462 _this9.hierarchical.addRelation(parentNode.id, childNode.id); 57463 } 57464 }; 57465 57466 this._crawlNetwork(fillInRelations); 57467 this.hierarchical.checkIfTree(); 57468 } 57469 57470 /** 57471 * Crawl over the entire network and use a callback on each node couple that is connected to each other. 57472 * @param {function} [callback=function(){}] | will receive nodeA, nodeB and the connecting edge. A and B are distinct. 57473 * @param {Node.id} startingNodeId 57474 * @private 57475 */ 57476 57477 }, { 57478 key: '_crawlNetwork', 57479 value: function _crawlNetwork() { 57480 var _this10 = this; 57481 57482 var callback = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : function () {}; 57483 var startingNodeId = arguments[1]; 57484 57485 var progress = {}; 57486 57487 var crawler = function crawler(node, tree) { 57488 if (progress[node.id] === undefined) { 57489 _this10.hierarchical.setTreeIndex(node, tree); 57490 57491 progress[node.id] = true; 57492 var childNode = void 0; 57493 var edges = _this10._getActiveEdges(node); 57494 for (var i = 0; i < edges.length; i++) { 57495 var edge = edges[i]; 57496 if (edge.connected === true) { 57497 if (edge.toId == node.id) { 57498 // Not '===' because id's can be string and numeric 57499 childNode = edge.from; 57500 } else { 57501 childNode = edge.to; 57502 } 57503 57504 if (node.id != childNode.id) { 57505 // Not '!==' because id's can be string and numeric 57506 callback(node, childNode, edge); 57507 crawler(childNode, tree); 57508 } 57509 } 57510 } 57511 } 57512 }; 57513 57514 if (startingNodeId === undefined) { 57515 // Crawl over all nodes 57516 var treeIndex = 0; // Serves to pass a unique id for the current distinct tree 57517 57518 for (var i = 0; i < this.body.nodeIndices.length; i++) { 57519 var nodeId = this.body.nodeIndices[i]; 57520 57521 if (progress[nodeId] === undefined) { 57522 var node = this.body.nodes[nodeId]; 57523 crawler(node, treeIndex); 57524 treeIndex += 1; 57525 } 57526 } 57527 } else { 57528 // Crawl from the given starting node 57529 var _node2 = this.body.nodes[startingNodeId]; 57530 if (_node2 === undefined) { 57531 console.error("Node not found:", startingNodeId); 57532 return; 57533 } 57534 crawler(_node2); 57535 } 57536 } 57537 57538 /** 57539 * Shift a branch a certain distance 57540 * @param {Node.id} parentId 57541 * @param {number} diff 57542 * @private 57543 */ 57544 57545 }, { 57546 key: '_shiftBlock', 57547 value: function _shiftBlock(parentId, diff) { 57548 var _this11 = this; 57549 57550 var progress = {}; 57551 var shifter = function shifter(parentId) { 57552 if (progress[parentId]) { 57553 return; 57554 } 57555 progress[parentId] = true; 57556 _this11.direction.shift(parentId, diff); 57557 57558 var childRef = _this11.hierarchical.childrenReference[parentId];
57559 if (childRef !== undefined) { 57560 for (var i = 0; i < childRef.length; i++) { 57561 shifter(childRef[i]); 57562 } 57563 } 57564 }; 57565 shifter(parentId); 57566 } 57567 57568 /** 57569 * Find a common parent between branches. 57570 * @param {Node.id} childA 57571 * @param {Node.id} childB 57572 * @returns {{foundParent, withChild}} 57573 * @private 57574 */ 57575 57576 }, { 57577 key: '_findCommonParent', 57578 value: function _findCommonParent(childA, childB) { 57579 var _this12 = this; 57580 57581 var parents = {}; 57582 var iterateParents = function iterateParents(parents, child) { 57583 var parentRef = _this12.hierarchical.parentReference[child]; 57584 if (parentRef !== undefined) { 57585 for (var i = 0; i < parentRef.length; i++) { 57586 var parent = parentRef[i]; 57587 parents[parent] = true; 57588 iterateParents(parents, parent); 57589 } 57590 } 57591 }; 57592 var findParent = function findParent(parents, child) { 57593 var parentRef = _this12.hierarchical.parentReference[child]; 57594 if (parentRef !== undefined) { 57595 for (var i = 0; i < parentRef.length; i++) { 57596 var parent = parentRef[i]; 57597 if (parents[parent] !== undefined) { 57598 return { foundParent: parent, withChild: child }; 57599 } 57600 var branch = findParent(parents, parent); 57601 if (branch.foundParent !== null) { 57602 return branch; 57603 } 57604 } 57605 } 57606 return { foundParent: null, withChild: child }; 57607 }; 57608 57609 iterateParents(parents, childA); 57610 return findParent(parents, childB); 57611 } 57612 57613 /** 57614 * Set the strategy pattern for handling the coordinates given the current direction. 57615 * 57616 * The individual instances contain all the operations and data specific to a layout direction. 57617 * 57618 * @param {Node} node 57619 * @param {{x: number, y: number}} position 57620 * @param {number} level 57621 * @param {boolean} [doNotUpdate=false] 57622 * @private 57623 */ 57624 57625 }, { 57626 key: 'setDirectionStrategy', 57627 value: function setDirectionStrategy() { 57628 var isVertical = this.options.hierarchical.direction === 'UD' || this.options.hierarchical.direction === 'DU'; 57629 57630 if (isVertical) { 57631 this.direction = new VerticalStrategy(this); 57632 } else { 57633 this.direction = new HorizontalStrategy(this); 57634 } 57635 } 57636 57637 /** 57638 * Determine the center position of a branch from the passed list of child nodes 57639 * 57640 * This takes into account the positions of all the child nodes. 57641 * @param {Array.<Node|vis.Node.id>} childNodes Array of either child nodes or node id's 57642 * @return {number} 57643 * @private 57644 */ 57645 57646 }, { 57647 key: '_getCenterPosition', 57648 value: function _getCenterPosition(childNodes) { 57649 var minPos = 1e9; 57650 var maxPos = -1e9; 57651 57652 for (var i = 0; i < childNodes.length; i++) { 57653 var childNode = void 0; 57654 if (childNodes[i].id !== undefined) { 57655 childNode = childNodes[i]; 57656 } else { 57657 var childNodeId = childNodes[i]; 57658 childNode = this.body.nodes[childNodeId]; 57659 } 57660 57661 var position = this.direction.getPosition(childNode); 57662 minPos = Math.min(minPos, position); 57663 maxPos = Math.max(maxPos, position); 57664 } 57665 57666 return 0.5 * (minPos + maxPos); 57667 } 57668 }]); 57669 return LayoutEngine; 57670}(); 57671 57672exports['default'] = LayoutEngine; 57673 57674/***/ }), 57675/* 236 */ 57676/***/ (function(module, exports, __webpack_require__) { 57677 57678"use strict"; 57679 57680 57681Object.defineProperty(exports, "__esModule", { 57682 value: true 57683}); 57684exports.VerticalStrategy = exports.HorizontalStrategy = undefined; 57685 57686var _getPrototypeOf = __webpack_require__(3); 57687 57688var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf); 57689 57690var _possibleConstructorReturn2 = __webpack_require__(4); 57691 57692var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2); 57693 57694var _inherits2 = __webpack_require__(5); 57695 57696var _inherits3 = _interopRequireDefault(_inherits2); 57697 57698var _classCallCheck2 = __webpack_require__(0); 57699 57700var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); 57701 57702var _createClass2 = __webpack_require__(1); 57703 57704var _createClass3 = _interopRequireDefault(_createClass2); 57705 57706function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 57707 57708/** 57709 * Helper classes for LayoutEngine. 57710 * 57711 * Strategy pattern for usage of direction methods for hierarchical layouts. 57712 */ 57713 57714/** 57715 * Interface definition for direction strategy classes. 57716 * 57717 * This class describes the interface for the Strategy 57718 * pattern classes used to differentiate horizontal and vertical 57719 * direction of hierarchical results. 57720 * 57721 * For a given direction, one coordinate will be 'fixed', meaning that it is 57722 * determined by level. 57723 * The other coordinate is 'unfixed', meaning that the nodes on a given level 57724 * can still move along that coordinate. So: 57725 * 57726 * - `vertical` layout: `x` unfixed, `y` fixed per level 57727 * - `horizontal` layout: `x` fixed per level, `y` unfixed 57728 * 57729 * The local methods are stubs and should be regarded as abstract. 57730 * Derived classes **must** implement all the methods themselves. 57731 * 57732 * @private 57733 */ 57734var DirectionInterface = function () { 57735 function DirectionInterface() { 57736 (0, _classCallCheck3['default'])(this, DirectionInterface); 57737 } 57738 57739 (0, _createClass3['default'])(DirectionInterface, [{ 57740 key: 'abstract', 57741 57742 /** @ignore **/ 57743 value: function abstract() { 57744 throw new Error("Can't instantiate abstract class!"); 57745 } 57746 57747 /** 57748 * This is a dummy call which is used to suppress the jsdoc errors of type: 57749 * 57750 * "'param' is assigned a value but never used" 57751 * 57752 * @ignore 57753 **/ 57754 57755 }, { 57756 key: 'fake_use', 57757 value: function fake_use() {}
57758 // Do nothing special 57759 57760 57761 /** 57762 * Type to use to translate dynamic curves to, in the case of hierarchical layout. 57763 * Dynamic curves do not work for these. 57764 * 57765 * The value should be perpendicular to the actual direction of the layout. 57766 * 57767 * @return {string} Direction, either 'vertical' or 'horizontal' 57768 */ 57769 57770 }, { 57771 key: 'curveType', 57772 value: function curveType() { 57773 return this.abstract(); 57774 } 57775 57776 /** 57777 * Return the value of the coordinate that is not fixed for this direction. 57778 * 57779 * @param {Node} node The node to read 57780 * @return {number} Value of the unfixed coordinate 57781 */ 57782 57783 }, { 57784 key: 'getPosition', 57785 value: function getPosition(node) { 57786 this.fake_use(node);return this.abstract(); 57787 } 57788 57789 /** 57790 * Set the value of the coordinate that is not fixed for this direction. 57791 * 57792 * @param {Node} node The node to adjust 57793 * @param {number} position 57794 * @param {number} [level] if specified, the hierarchy level that this node should be fixed to 57795 */ 57796 57797 }, { 57798 key: 'setPosition', 57799 value: function setPosition(node, position) { 57800 var level = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : undefined; 57801 this.fake_use(node, position, level);this.abstract(); 57802 } 57803 57804 /** 57805 * Get the width of a tree. 57806 * 57807 * A `tree` here is a subset of nodes within the network which are not connected to other nodes, 57808 * only among themselves. In essence, it is a sub-network. 57809 * 57810 * @param {number} index The index number of a tree 57811 * @return {number} the width of a tree in the view coordinates 57812 */ 57813 57814 }, { 57815 key: 'getTreeSize', 57816 value: function getTreeSize(index) { 57817 this.fake_use(index);return this.abstract(); 57818 } 57819 57820 /** 57821 * Sort array of nodes on the unfixed coordinates. 57822 * 57823 * @param {Array.<Node>} nodeArray array of nodes to sort 57824 */ 57825 57826 }, { 57827 key: 'sort', 57828 value: function sort(nodeArray) { 57829 this.fake_use(nodeArray);this.abstract(); 57830 } 57831 57832 /** 57833 * Assign the fixed coordinate of the node to the given level 57834 * 57835 * @param {Node} node The node to adjust 57836 * @param {number} level The level to fix to 57837 */ 57838 57839 }, { 57840 key: 'fix', 57841 value: function fix(node, level) { 57842 this.fake_use(node, level);this.abstract(); 57843 } 57844 57845 /** 57846 * Add an offset to the unfixed coordinate of the given node. 57847 * 57848 * @param {NodeId} nodeId Id of the node to adjust 57849 * @param {number} diff Offset to add to the unfixed coordinate 57850 */ 57851 57852 }, { 57853 key: 'shift', 57854 value: function shift(nodeId, diff) { 57855 this.fake_use(nodeId, diff);this.abstract(); 57856 } 57857 }]); 57858 return DirectionInterface; 57859}(); 57860 57861/** 57862 * Vertical Strategy 57863 * 57864 * Coordinate `y` is fixed on levels, coordinate `x` is unfixed. 57865 * 57866 * @extends DirectionInterface 57867 * @private 57868 */ 57869 57870 57871var VerticalStrategy = function (_DirectionInterface) { 57872 (0, _inherits3['default'])(VerticalStrategy, _DirectionInterface); 57873 57874 /** 57875 * Constructor 57876 * 57877 * @param {Object} layout reference to the parent LayoutEngine instance. 57878 */ 57879 function VerticalStrategy(layout) { 57880 (0, _classCallCheck3['default'])(this, VerticalStrategy); 57881 57882 var _this = (0, _possibleConstructorReturn3['default'])(this, (VerticalStrategy.__proto__ || (0, _getPrototypeOf2['default'])(VerticalStrategy)).call(this)); 57883 57884 _this.layout = layout; 57885 return _this; 57886 } 57887 57888 /** @inheritdoc */ 57889 57890 57891 (0, _createClass3['default'])(VerticalStrategy, [{ 57892 key: 'curveType', 57893 value: function curveType() { 57894 return 'horizontal'; 57895 } 57896 57897 /** @inheritdoc */ 57898 57899 }, { 57900 key: 'getPosition', 57901 value: function getPosition(node) { 57902 return node.x; 57903 } 57904 57905 /** @inheritdoc */ 57906 57907 }, { 57908 key: 'setPosition', 57909 value: function setPosition(node, position) { 57910 var level = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : undefined; 57911 57912 if (level !== undefined) { 57913 this.layout.hierarchical.addToOrdering(node, level); 57914 } 57915 node.x = position; 57916 } 57917 57918 /** @inheritdoc */ 57919 57920 }, { 57921 key: 'getTreeSize', 57922 value: function getTreeSize(index) { 57923 var res = this.layout.hierarchical.getTreeSize(this.layout.body.nodes, index); 57924 return { min: res.min_x, max: res.max_x }; 57925 } 57926 57927 /** @inheritdoc */ 57928 57929 }, { 57930 key: 'sort', 57931 value: function sort(nodeArray) { 57932 nodeArray.sort(function (a, b) { 57933 // Test on 'undefined' takes care of divergent behaviour in chrome 57934 if (a.x === undefined || b.x === undefined) return 0; // THIS HAPPENS 57935 return a.x - b.x; 57936 }); 57937 } 57938 57939 /** @inheritdoc */ 57940 57941 }, { 57942 key: 'fix', 57943 value: function fix(node, level) { 57944 node.y = this.layout.options.hierarchical.levelSeparation * level; 57945 node.options.fixed.y = true; 57946 } 57947 57948 /** @inheritdoc */ 57949 57950 }, { 57951 key: 'shift', 57952 value: function shift(nodeId, diff) { 57953 this.layout.body.nodes[nodeId].x += diff; 57954 } 57955 }]); 57956 return VerticalStrategy; 57957}(DirectionInterface); 57958 57959/** 57960 * Horizontal Strategy 57961 * 57962 * Coordinate `x` is fixed on levels, coordinate `y` is unfixed. 57963 * 57964 * @extends DirectionInterface 57965 * @private 57966 */ 57967 57968 57969var HorizontalStrategy = function (_DirectionInterface2) { 57970 (0, _inherits3['default'])(HorizontalStrategy, _DirectionInterface2); 57971 57972 /** 57973 * Constructor 57974 * 57975 * @param {Object} layout reference to the parent LayoutEngine instance. 57976 */ 57977 function HorizontalStrategy(layout) { 57978 (0, _classCallCheck3['default'])(this, HorizontalStrategy); 57979 57980 var _this2 = (0, _possibleConstructorReturn3['default'])(this, (HorizontalStrategy.__proto__ || (0, _getPrototypeOf2['default'])(HorizontalStrategy)).c
57980all(this)); 57981 57982 _this2.layout = layout; 57983 return _this2; 57984 } 57985 57986 /** @inheritdoc */ 57987 57988 57989 (0, _createClass3['default'])(HorizontalStrategy, [{ 57990 key: 'curveType', 57991 value: function curveType() { 57992 return 'vertical'; 57993 } 57994 57995 /** @inheritdoc */ 57996 57997 }, { 57998 key: 'getPosition', 57999 value: function getPosition(node) { 58000 return node.y; 58001 } 58002 58003 /** @inheritdoc */ 58004 58005 }, { 58006 key: 'setPosition', 58007 value: function setPosition(node, position) { 58008 var level = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : undefined; 58009 58010 if (level !== undefined) { 58011 this.layout.hierarchical.addToOrdering(node, level); 58012 } 58013 node.y = position; 58014 } 58015 58016 /** @inheritdoc */ 58017 58018 }, { 58019 key: 'getTreeSize', 58020 value: function getTreeSize(index) { 58021 var res = this.layout.hierarchical.getTreeSize(this.layout.body.nodes, index); 58022 return { min: res.min_y, max: res.max_y }; 58023 } 58024 58025 /** @inheritdoc */ 58026 58027 }, { 58028 key: 'sort', 58029 value: function sort(nodeArray) { 58030 nodeArray.sort(function (a, b) { 58031 // Test on 'undefined' takes care of divergent behaviour in chrome 58032 if (a.y === undefined || b.y === undefined) return 0; // THIS HAPPENS 58033 return a.y - b.y; 58034 }); 58035 } 58036 58037 /** @inheritdoc */ 58038 58039 }, { 58040 key: 'fix', 58041 value: function fix(node, level) { 58042 node.x = this.layout.options.hierarchical.levelSeparation * level; 58043 node.options.fixed.x = true; 58044 } 58045 58046 /** @inheritdoc */ 58047 58048 }, { 58049 key: 'shift', 58050 value: function shift(nodeId, diff) { 58051 this.layout.body.nodes[nodeId].y += diff; 58052 } 58053 }]); 58054 return HorizontalStrategy; 58055}(DirectionInterface); 58056 58057exports.HorizontalStrategy = HorizontalStrategy; 58058exports.VerticalStrategy = VerticalStrategy; 58059 58060/***/ }), 58061/* 237 */ 58062/***/ (function(module, exports, __webpack_require__) { 58063 58064"use strict"; 58065 58066 58067Object.defineProperty(exports, "__esModule", { 58068 value: true 58069}); 58070 58071var _keys = __webpack_require__(8); 58072 58073var _keys2 = _interopRequireDefault(_keys); 58074 58075var _stringify = __webpack_require__(19); 58076 58077var _stringify2 = _interopRequireDefault(_stringify); 58078 58079var _typeof2 = __webpack_require__(6); 58080 58081var _typeof3 = _interopRequireDefault(_typeof2); 58082 58083var _classCallCheck2 = __webpack_require__(0); 58084 58085var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); 58086 58087var _createClass2 = __webpack_require__(1); 58088 58089var _createClass3 = _interopRequireDefault(_createClass2); 58090 58091function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 58092 58093var util = __webpack_require__(2); 58094var Hammer = __webpack_require__(10); 58095var hammerUtil = __webpack_require__(37); 58096 58097/** 58098 * Clears the toolbar div element of children 58099 * 58100 * @private 58101 */ 58102 58103var ManipulationSystem = function () { 58104 /** 58105 * @param {Object} body 58106 * @param {Canvas} canvas 58107 * @param {SelectionHandler} selectionHandler 58108 */ 58109 function ManipulationSystem(body, canvas, selectionHandler) { 58110 var _this = this; 58111 58112 (0, _classCallCheck3['default'])(this, ManipulationSystem); 58113 58114 this.body = body; 58115 this.canvas = canvas; 58116 this.selectionHandler = selectionHandler; 58117 58118 this.editMode = false; 58119 this.manipulationDiv = undefined; 58120 this.editModeDiv = undefined; 58121 this.closeDiv = undefined; 58122 58123 this.manipulationHammers = []; 58124 this.temporaryUIFunctions = {}; 58125 this.temporaryEventFunctions = []; 58126 58127 this.touchTime = 0; 58128 this.temporaryIds = { nodes: [], edges: [] }; 58129 this.guiEnabled = false; 58130 this.inMode = false; 58131 this.selectedControlNode = undefined; 58132 58133 this.options = {}; 58134 this.defaultOptions = { 58135 enabled: false, 58136 initiallyActive: false, 58137 addNode: true, 58138 addEdge: true, 58139 editNode: undefined, 58140 editEdge: true, 58141 deleteNode: true, 58142 deleteEdge: true, 58143 controlNodeStyle: { 58144 shape: 'dot', 58145 size: 6, 58146 color: { background: '#ff0000', border: '#3c3c3c', highlight: { background: '#07f968', border: '#3c3c3c' } }, 58147 borderWidth: 2, 58148 borderWidthSelected: 2 58149 } 58150 }; 58151 util.extend(this.options, this.defaultOptions); 58152 58153 this.body.emitter.on('destroy', function () { 58154 _this._clean(); 58155 }); 58156 this.body.emitter.on('_dataChanged', this._restore.bind(this)); 58157 this.body.emitter.on('_resetData', this._restore.bind(this)); 58158 } 58159 58160 /** 58161 * If something changes in the data during editing, switch back to the initial datamanipulation state and close all edit modes. 58162 * @private 58163 */ 58164 58165 58166 (0, _createClass3['default'])(ManipulationSystem, [{ 58167 key: '_restore',
58168 value: function _restore() { 58169 if (this.inMode !== false) { 58170 if (this.options.initiallyActive === true) { 58171 this.enableEditMode(); 58172 } else { 58173 this.disableEditMode(); 58174 } 58175 } 58176 } 58177 58178 /** 58179 * Set the Options 58180 * 58181 * @param {Object} options 58182 * @param {Object} allOptions 58183 * @param {Object} globalOptions 58184 */ 58185 58186 }, { 58187 key: 'setOptions', 58188 value: function setOptions(options, allOptions, globalOptions) { 58189 if (allOptions !== undefined) { 58190 if (allOptions.locale !== undefined) { 58191 this.options.locale = allOptions.locale; 58192 } else { 58193 this.options.locale = globalOptions.locale; 58194 } 58195 if (allOptions.locales !== undefined) { 58196 this.options.locales = allOptions.locales; 58197 } else { 58198 this.options.locales = globalOptions.locales; 58199 } 58200 } 58201 58202 if (options !== undefined) { 58203 if (typeof options === 'boolean') { 58204 this.options.enabled = options; 58205 } else { 58206 this.options.enabled = true; 58207 util.deepExtend(this.options, options); 58208 } 58209 if (this.options.initiallyActive === true) { 58210 this.editMode = true; 58211 } 58212 this._setup(); 58213 } 58214 } 58215 58216 /** 58217 * Enable or disable edit-mode. Draws the DOM required and cleans up after itself. 58218 * 58219 * @private 58220 */ 58221 58222 }, { 58223 key: 'toggleEditMode', 58224 value: function toggleEditMode() { 58225 if (this.editMode === true) { 58226 this.disableEditMode(); 58227 } else { 58228 this.enableEditMode(); 58229 } 58230 } 58231 58232 /** 58233 * Enables Edit Mode 58234 */ 58235 58236 }, { 58237 key: 'enableEditMode', 58238 value: function enableEditMode() { 58239 this.editMode = true; 58240 58241 this._clean(); 58242 if (this.guiEnabled === true) { 58243 this.manipulationDiv.style.display = 'block'; 58244 this.closeDiv.style.display = 'block'; 58245 this.editModeDiv.style.display = 'none'; 58246 this.showManipulatorToolbar(); 58247 } 58248 } 58249 58250 /** 58251 * Disables Edit Mode 58252 */ 58253 58254 }, { 58255 key: 'disableEditMode', 58256 value: function disableEditMode() { 58257 this.editMode = false; 58258 58259 this._clean(); 58260 if (this.guiEnabled === true) { 58261 this.manipulationDiv.style.display = 'none'; 58262 this.closeDiv.style.display = 'none'; 58263 this.editModeDiv.style.display = 'block'; 58264 this._createEditButton(); 58265 } 58266 } 58267 58268 /** 58269 * Creates the main toolbar. Removes functions bound to the select event. Binds all the buttons of the toolbar. 58270 * 58271 * @private 58272 */ 58273 58274 }, { 58275 key: 'showManipulatorToolbar', 58276 value: function showManipulatorToolbar() { 58277 // restore the state of any bound functions or events, remove control nodes, restore physics 58278 this._clean(); 58279 58280 // reset global variables 58281 this.manipulationDOM = {}; 58282 58283 // if the gui is enabled, draw all elements. 58284 if (this.guiEnabled === true) { 58285 // a _restore will hide these menus 58286 this.editMode = true; 58287 this.manipulationDiv.style.display = 'block'; 58288 this.closeDiv.style.display = 'block'; 58289 58290 var selectedNodeCount = this.selectionHandler._getSelectedNodeCount(); 58291 var selectedEdgeCount = this.selectionHandler._getSelectedEdgeCount(); 58292 var selectedTotalCount = selectedNodeCount + selectedEdgeCount; 58293 var locale = this.options.locales[this.options.locale]; 58294 var needSeperator = false; 58295 58296 if (this.options.addNode !== false) { 58297 this._createAddNodeButton(locale); 58298 needSeperator = true; 58299 } 58300 if (this.options.addEdge !== false) { 58301 if (needSeperator === true) { 58302 this._createSeperator(1); 58303 } else { 58304 needSeperator = true; 58305 } 58306 this._createAddEdgeButton(locale); 58307 } 58308 58309 if (selectedNodeCount === 1 && typeof this.options.editNode === 'function') { 58310 if (needSeperator === true) { 58311 this._createSeperator(2); 58312 } else { 58313 needSeperator = true; 58314 } 58315 this._createEditNodeButton(locale); 58316 } else if (selectedEdgeCount === 1 && selectedNodeCount === 0 && this.options.editEdge !== false) { 58317 if (needSeperator === true) { 58318 this._createSeperator(3); 58319 } else { 58320 needSeperator = true; 58321 } 58322 this._createEditEdgeButton(locale); 58323 } 58324 58325 // remove buttons 58326 if (selectedTotalCount !== 0) { 58327 if (selectedNodeCount > 0 && this.options.deleteNode !== false) { 58328 if (needSeperator === true) { 58329 this._createSeperator(4); 58330 } 58331 this._createDeleteButton(locale); 58332 } else if (selectedNodeCount === 0 && this.options.deleteEdge !== false) { 58333 if (needSeperator === true) { 58334 this._createSeperator(4); 58335 } 58336 this._createDeleteButton(locale); 58337 } 58338 } 58339 58340 // bind the close button 58341 this._bindHammerToDiv(this.closeDiv, this.toggleEditMode.bind(this)); 58342 58343 // refresh this bar based on what has been selected 58344 this._temporaryBindEvent('select', this.showManipulatorToolbar.bind(this)); 58345 } 58346 58347 // redraw to show any possible changes 58348 this.body.emitter.emit('_redraw'); 58349 } 58350 58351 /** 58352 * Create the toolbar for adding Nodes 58353 */ 58354 58355 }, { 58356 key: 'addNodeMode', 58357 value: function addNodeMode() { 58358 // when using the gui, enable edit mode if it wasnt already. 58359 if (this.editMode !== true) { 58360 this.enableEditMode(); 58361 } 58362
58363 // restore the state of any bound functions or events, remove control nodes, restore physics 58364 this._clean(); 58365 58366 this.inMode = 'addNode'; 58367 if (this.guiEnabled === true) { 58368 var locale = this.options.locales[this.options.locale]; 58369 this.manipulationDOM = {}; 58370 this._createBackButton(locale); 58371 this._createSeperator(); 58372 this._createDescription(locale['addDescription'] || this.options.locales['en']['addDescription']); 58373 58374 // bind the close button 58375 this._bindHammerToDiv(this.closeDiv, this.toggleEditMode.bind(this)); 58376 } 58377 58378 this._temporaryBindEvent('click', this._performAddNode.bind(this)); 58379 } 58380 58381 /** 58382 * call the bound function to handle the editing of the node. The node has to be selected. 58383 */ 58384 58385 }, { 58386 key: 'editNode', 58387 value: function editNode() { 58388 var _this2 = this; 58389 58390 // when using the gui, enable edit mode if it wasnt already. 58391 if (this.editMode !== true) { 58392 this.enableEditMode(); 58393 } 58394 58395 // restore the state of any bound functions or events, remove control nodes, restore physics 58396 this._clean(); 58397 var node = this.selectionHandler._getSelectedNode(); 58398 if (node !== undefined) { 58399 this.inMode = 'editNode'; 58400 if (typeof this.options.editNode === 'function') { 58401 if (node.isCluster !== true) { 58402 var data = util.deepExtend({}, node.options, false); 58403 data.x = node.x; 58404 data.y = node.y; 58405 58406 if (this.options.editNode.length === 2) { 58407 this.options.editNode(data, function (finalizedData) { 58408 if (finalizedData !== null && finalizedData !== undefined && _this2.inMode === 'editNode') { 58409 // if for whatever reason the mode has changes (due to dataset change) disregard the callback) { 58410 _this2.body.data.nodes.getDataSet().update(finalizedData); 58411 } 58412 _this2.showManipulatorToolbar(); 58413 }); 58414 } else { 58415 throw new Error('The function for edit does not support two arguments (data, callback)'); 58416 } 58417 } else { 58418 alert(this.options.locales[this.options.locale]['editClusterError'] || this.options.locales['en']['editClusterError']); 58419 } 58420 } else { 58421 throw new Error('No function has been configured to handle the editing of nodes.'); 58422 } 58423 } else { 58424 this.showManipulatorToolbar(); 58425 } 58426 } 58427 58428 /** 58429 * create the toolbar to connect nodes 58430 */ 58431 58432 }, { 58433 key: 'addEdgeMode', 58434 value: function addEdgeMode() { 58435 // when using the gui, enable edit mode if it wasnt already. 58436 if (this.editMode !== true) { 58437 this.enableEditMode(); 58438 } 58439 58440 // restore the state of any bound functions or events, remove control nodes, restore physics 58441 this._clean(); 58442 58443 this.inMode = 'addEdge'; 58444 if (this.guiEnabled === true) { 58445 var locale = this.options.locales[this.options.locale]; 58446 this.manipulationDOM = {}; 58447 this._createBackButton(locale); 58448 this._createSeperator(); 58449 this._createDescription(locale['edgeDescription'] || this.options.locales['en']['edgeDescription']); 58450 58451 // bind the close button 58452 this._bindHammerToDiv(this.closeDiv, this.toggleEditMode.bind(this)); 58453 } 58454 58455 // temporarily overload functions 58456 this._temporaryBindUI('onTouch', this._handleConnect.bind(this)); 58457 this._temporaryBindUI('onDragEnd', this._finishConnect.bind(this)); 58458 this._temporaryBindUI('onDrag', this._dragControlNode.bind(this)); 58459 this._temporaryBindUI('onRelease', this._finishConnect.bind(this)); 58460 this._temporaryBindUI('onDragStart', this._dragStartEdge.bind(this)); 58461 this._temporaryBindUI('onHold', function () {}); 58462 } 58463 58464 /** 58465 * create the toolbar to edit edges 58466 */ 58467 58468 }, { 58469 key: 'editEdgeMode', 58470 value: function editEdgeMode() { 58471 // when using the gui, enable edit mode if it wasn't already. 58472 if (this.editMode !== true) { 58473 this.enableEditMode(); 58474 } 58475
58476 // restore the state of any bound functions or events, remove control nodes, restore physics 58477 this._clean(); 58478 58479 this.inMode = 'editEdge'; 58480 if ((0, _typeof3['default'])(this.options.editEdge) === 'object' && typeof this.options.editEdge.editWithoutDrag === "function") { 58481 this.edgeBeingEditedId = this.selectionHandler.getSelectedEdges()[0]; 58482 if (this.edgeBeingEditedId !== undefined) { 58483 var edge = this.body.edges[this.edgeBeingEditedId]; 58484 this._performEditEdge(edge.from, edge.to); 58485 return; 58486 } 58487 } 58488 if (this.guiEnabled === true) { 58489 var locale = this.options.locales[this.options.locale]; 58490 this.manipulationDOM = {}; 58491 this._createBackButton(locale); 58492 this._createSeperator(); 58493 this._createDescription(locale['editEdgeDescription'] || this.options.locales['en']['editEdgeDescription']); 58494 58495 // bind the close button 58496 this._bindHammerToDiv(this.closeDiv, this.toggleEditMode.bind(this)); 58497 } 58498 58499 this.edgeBeingEditedId = this.selectionHandler.getSelectedEdges()[0]; 58500 if (this.edgeBeingEditedId !== undefined) { 58501 var _edge = this.body.edges[this.edgeBeingEditedId]; 58502 58503 // create control nodes 58504 var controlNodeFrom = this._getNewTargetNode(_edge.from.x, _edge.from.y); 58505 var controlNodeTo = this._getNewTargetNode(_edge.to.x, _edge.to.y); 58506 58507 this.temporaryIds.nodes.push(controlNodeFrom.id); 58508 this.temporaryIds.nodes.push(controlNodeTo.id); 58509 58510 this.body.nodes[controlNodeFrom.id] = controlNodeFrom; 58511 this.body.nodeIndices.push(controlNodeFrom.id); 58512 this.body.nodes[controlNodeTo.id] = controlNodeTo; 58513 this.body.nodeIndices.push(controlNodeTo.id); 58514 58515 // temporarily overload UI functions, cleaned up automatically because of _temporaryBindUI 58516 this._temporaryBindUI('onTouch', this._controlNodeTouch.bind(this)); // used to get the position 58517 this._temporaryBindUI('onTap', function () {}); // disabled 58518 this._temporaryBindUI('onHold', function () {}); // disabled 58519 this._temporaryBindUI('onDragStart', this._controlNodeDragStart.bind(this)); // used to select control node 58520 this._temporaryBindUI('onDrag', this._controlNodeDrag.bind(this)); // used to drag control node 58521 this._temporaryBindUI('onDragEnd', this._controlNodeDragEnd.bind(this)); // used to connect or revert control nodes 58522 this._temporaryBindUI('onMouseMove', function () {}); // disabled 58523 58524 // create function to position control nodes correctly on movement 58525 // automatically cleaned up because we use the temporary bind 58526 this._temporaryBindEvent('beforeDrawing', function (ctx) { 58527 var positions = _edge.edgeType.findBorderPositions(ctx); 58528 if (controlNodeFrom.selected === false) { 58529 controlNodeFrom.x = positions.from.x; 58530 controlNodeFrom.y = positions.from.y; 58531 } 58532 if (controlNodeTo.selected === false) { 58533 controlNodeTo.x = positions.to.x; 58534 controlNodeTo.y = positions.to.y; 58535 } 58536 }); 58537 58538 this.body.emitter.emit('_redraw'); 58539 } else { 58540 this.showManipulatorToolbar(); 58541 } 58542 } 58543 58544 /** 58545 * delete everything in the selection 58546 */ 58547 58548 }, { 58549 key: 'deleteSelected', 58550 value: function deleteSelected() { 58551 var _this3 = this; 58552 58553 // when using the gui, enable edit mode if it wasnt already. 58554 if (this.editMode !== true) { 58555 this.enableEditMode(); 58556 } 58557 58558 // restore the state of any bound functions or events, remove control nodes, restore physics 58559 this._clean(); 58560 58561 this.inMode = 'delete'; 58562 var selectedNodes = this.selectionHandler.getSelectedNodes(); 58563 var selectedEdges = this.selectionHandler.getSelectedEdges(); 58564 var deleteFunction = undefined; 58565 if (selectedNodes.length > 0) { 58566 for (var i = 0; i < selectedNodes.length; i++) { 58567 if (this.body.nodes[selectedNodes[i]].isCluster === true) { 58568 alert(this.options.locales[this.options.locale]['deleteClusterError'] || this.options.locales['en']['deleteClusterError']); 58569 return; 58570 } 58571 } 58572 58573 if (typeof this.options.deleteNode === 'function') { 58574 deleteFunction = this.options.deleteNode; 58575 } 58576 } else if (selectedEdges.length > 0) { 58577 if (typeof this.options.deleteEdge === 'function') { 58578 deleteFunction = this.options.deleteEdge; 58579 } 58580 } 58581 58582 if (typeof deleteFunction === 'function') { 58583 var data = { nodes: selectedNodes, edges: selectedEdges }; 58584 if (deleteFunction.length === 2) { 58585 deleteFunction(data, function (finalizedData) { 58586 if (finalizedData !== null && finalizedData !== undefined && _this3.inMode === 'delete') { 58587 // if for whatever reason the mode has changes (due to dataset change) disregard the callback) { 58588 _this3.body.data.edges.getDataSet().remove(finalizedData.edges); 58589 _this3.body.data.nodes.getDataSet().remove(finalizedData.nodes); 58590 _this3.body.emitter.emit('startSimulation'); 58591 _this3.showManipulatorToolbar(); 58592 } else { 58593 _this3.body.emitter.emit('startSimulation'); 58594 _this3.showManipulatorToolbar(); 58595 } 58596 }); 58597 } else { 58598 throw new Error('The function for delete does not support two arguments (data, callback)'); 58599 } 58600 } else { 58601 this.body.data.edges.getDataSet().remove(selectedEdges); 58602 this.body.data.nodes.getDataSet().remove(selectedNodes); 58603 this.body.emitter.emit('startSimulation'); 58604 this.showManipulatorToolbar(); 58605 } 58606 } 58607
58608 //********************************************** PRIVATE ***************************************// 58609 58610 /** 58611 * draw or remove the DOM 58612 * @private 58613 */ 58614 58615 }, { 58616 key: '_setup', 58617 value: function _setup() { 58618 if (this.options.enabled === true) { 58619 // Enable the GUI 58620 this.guiEnabled = true; 58621 58622 this._createWrappers(); 58623 if (this.editMode === false) { 58624 this._createEditButton(); 58625 } else { 58626 this.showManipulatorToolbar(); 58627 } 58628 } else { 58629 this._removeManipulationDOM(); 58630 58631 // disable the gui 58632 this.guiEnabled = false; 58633 } 58634 } 58635 58636 /** 58637 * create the div overlays that contain the DOM 58638 * @private 58639 */ 58640 58641 }, { 58642 key: '_createWrappers', 58643 value: function _createWrappers() { 58644 // load the manipulator HTML elements. All styling done in css. 58645 if (this.manipulationDiv === undefined) { 58646 this.manipulationDiv = document.createElement('div'); 58647 this.manipulationDiv.className = 'vis-manipulation'; 58648 if (this.editMode === true) { 58649 this.manipulationDiv.style.display = 'block'; 58650 } else { 58651 this.manipulationDiv.style.display = 'none'; 58652 } 58653 this.canvas.frame.appendChild(this.manipulationDiv); 58654 } 58655 58656 // container for the edit button. 58657 if (this.editModeDiv === undefined) { 58658 this.editModeDiv = document.createElement('div'); 58659 this.editModeDiv.className = 'vis-edit-mode'; 58660 if (this.editMode === true) { 58661 this.editModeDiv.style.display = 'none'; 58662 } else { 58663 this.editModeDiv.style.display = 'block'; 58664 } 58665 this.canvas.frame.appendChild(this.editModeDiv); 58666 } 58667 58668 // container for the close div button 58669 if (this.closeDiv === undefined) { 58670 this.closeDiv = document.createElement('div'); 58671 this.closeDiv.className = 'vis-close'; 58672 this.closeDiv.style.display = this.manipulationDiv.style.display; 58673 this.canvas.frame.appendChild(this.closeDiv); 58674 } 58675 } 58676 58677 /** 58678 * generate a new target node. Used for creating new edges and editing edges 58679 * 58680 * @param {number} x 58681 * @param {number} y 58682 * @returns {Node} 58683 * @private 58684 */ 58685 58686 }, { 58687 key: '_getNewTargetNode', 58688 value: function _getNewTargetNode(x, y) { 58689 var controlNodeStyle = util.deepExtend({}, this.options.controlNodeStyle); 58690 58691 controlNodeStyle.id = 'targetNode' + util.randomUUID(); 58692 controlNodeStyle.hidden = false; 58693 controlNodeStyle.physics = false; 58694 controlNodeStyle.x = x; 58695 controlNodeStyle.y = y; 58696 58697 // we have to define the bounding box in order for the nodes to be drawn immediately 58698 var node = this.body.functions.createNode(controlNodeStyle); 58699 node.shape.boundingBox = { left: x, right: x, top: y, bottom: y }; 58700 58701 return node; 58702 } 58703 58704 /** 58705 * Create the edit button 58706 */ 58707 58708 }, { 58709 key: '_createEditButton', 58710 value: function _createEditButton() { 58711 // restore everything to it's original state (if applicable) 58712 this._clean(); 58713 58714 // reset the manipulationDOM 58715 this.manipulationDOM = {}; 58716 58717 // empty the editModeDiv 58718 util.recursiveDOMDelete(this.editModeDiv); 58719 58720 // create the contents for the editMode button 58721 var locale = this.options.locales[this.options.locale]; 58722 var button = this._createButton('editMode', 'vis-button vis-edit vis-edit-mode', locale['edit'] || this.options.locales['en']['edit']); 58723 this.editModeDiv.appendChild(button); 58724 58725 // bind a hammer listener to the button, calling the function toggleEditMode. 58726 this._bindHammerToDiv(button, this.toggleEditMode.bind(this)); 58727 } 58728 58729 /** 58730 * this function cleans up after everything this module does. Temporary elements, functions and events are removed, physics restored, hammers removed. 58731 * @private 58732 */ 58733 58734 }, { 58735 key: '_clean', 58736 value: function _clean() { 58737 // not in mode 58738 this.inMode = false; 58739 58740 // _clean the divs 58741 if (this.guiEnabled === true) { 58742 util.recursiveDOMDelete(this.editModeDiv); 58743 util.recursiveDOMDelete(this.manipulationDiv); 58744 58745 // removes all the bindings and overloads 58746 this._cleanManipulatorHammers(); 58747 } 58748 58749 // remove temporary nodes and edges 58750 this._cleanupTemporaryNodesAndEdges(); 58751 58752 // restore overloaded UI functions 58753 this._unbindTemporaryUIs(); 58754 58755 // remove the temporaryEventFunctions 58756 this._unbindTemporaryEvents(); 58757 58758 // restore the physics if required 58759 this.body.emitter.emit('restorePhysics'); 58760 } 58761 58762 /** 58763 * Each dom element has it's own hammer. They are stored in this.manipulationHammer
58763s. This cleans them up. 58764 * @private 58765 */ 58766 58767 }, { 58768 key: '_cleanManipulatorHammers', 58769 value: function _cleanManipulatorHammers() { 58770 // _clean hammer bindings 58771 if (this.manipulationHammers.length != 0) { 58772 for (var i = 0; i < this.manipulationHammers.length; i++) { 58773 this.manipulationHammers[i].destroy(); 58774 } 58775 this.manipulationHammers = []; 58776 } 58777 } 58778 58779 /** 58780 * Remove all DOM elements created by this module. 58781 * @private 58782 */ 58783 58784 }, { 58785 key: '_removeManipulationDOM', 58786 value: function _removeManipulationDOM() { 58787 // removes all the bindings and overloads 58788 this._clean(); 58789 58790 // empty the manipulation divs 58791 util.recursiveDOMDelete(this.manipulationDiv); 58792 util.recursiveDOMDelete(this.editModeDiv); 58793 util.recursiveDOMDelete(this.closeDiv); 58794 58795 // remove the manipulation divs 58796 if (this.manipulationDiv) { 58797 this.canvas.frame.removeChild(this.manipulationDiv); 58798 } 58799 if (this.editModeDiv) { 58800 this.canvas.frame.removeChild(this.editModeDiv); 58801 } 58802 if (this.closeDiv) { 58803 this.canvas.frame.removeChild(this.closeDiv); 58804 } 58805 58806 // set the references to undefined 58807 this.manipulationDiv = undefined; 58808 this.editModeDiv = undefined; 58809 this.closeDiv = undefined; 58810 } 58811 58812 /** 58813 * create a seperator line. the index is to differentiate in the manipulation dom 58814 * @param {number} [index=1] 58815 * @private 58816 */ 58817 58818 }, { 58819 key: '_createSeperator', 58820 value: function _createSeperator() { 58821 var index = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 1; 58822 58823 this.manipulationDOM['seperatorLineDiv' + index] = document.createElement('div'); 58824 this.manipulationDOM['seperatorLineDiv' + index].className = 'vis-separator-line'; 58825 this.manipulationDiv.appendChild(this.manipulationDOM['seperatorLineDiv' + index]); 58826 } 58827 58828 // ---------------------- DOM functions for buttons --------------------------// 58829 58830 /** 58831 * 58832 * @param {Locale} locale 58833 * @private 58834 */ 58835 58836 }, { 58837 key: '_createAddNodeButton', 58838 value: function _createAddNodeButton(locale) { 58839 var button = this._createButton('addNode', 'vis-button vis-add', locale['addNode'] || this.options.locales['en']['addNode']); 58840 this.manipulationDiv.appendChild(button); 58841 this._bindHammerToDiv(button, this.addNodeMode.bind(this)); 58842 } 58843 58844 /** 58845 * 58846 * @param {Locale} locale 58847 * @private 58848 */ 58849 58850 }, { 58851 key: '_createAddEdgeButton', 58852 value: function _createAddEdgeButton(locale) { 58853 var button = this._createButton('addEdge', 'vis-button vis-connect', locale['addEdge'] || this.options.locales['en']['addEdge']); 58854 this.manipulationDiv.appendChild(button); 58855 this._bindHammerToDiv(button, this.addEdgeMode.bind(this)); 58856 } 58857 58858 /** 58859 * 58860 * @param {Locale} locale 58861 * @private 58862 */ 58863 58864 }, { 58865 key: '_createEditNodeButton', 58866 value: function _createEditNodeButton(locale) { 58867 var button = this._createButton('editNode', 'vis-button vis-edit', locale['editNode'] || this.options.locales['en']['editNode']); 58868 this.manipulationDiv.appendChild(button); 58869 this._bindHammerToDiv(button, this.editNode.bind(this)); 58870 } 58871 58872 /** 58873 * 58874 * @param {Locale} locale 58875 * @private 58876 */ 58877 58878 }, { 58879 key: '_createEditEdgeButton', 58880 value: function _createEditEdgeButton(locale) { 58881 var button = this._createButton('editEdge', 'vis-button vis-edit', locale['editEdge'] || this.options.locales['en']['editEdge']); 58882 this.manipulationDiv.appendChild(button); 58883 this._bindHammerToDiv(button, this.editEdgeMode.bind(this)); 58884 } 58885 58886 /** 58887 * 58888 * @param {Locale} locale 58889 * @private 58890 */ 58891 58892 }, { 58893 key: '_createDeleteButton', 58894 value: function _createDeleteButton(locale) { 58895 var deleteBtnClass; 58896 if (this.options.rtl) { 58897 deleteBtnClass = 'vis-button vis-delete-rtl'; 58898 } else { 58899 deleteBtnClass = 'vis-button vis-delete'; 58900 } 58901 var button = this._createButton('delete', deleteBtnClass, locale['del'] || this.options.locales['en']['del']); 58902 this.manipulationDiv.appendChild(button); 58903 this._bindHammerToDiv(button, this.deleteSelected.bind(this)); 58904 } 58905 58906 /** 58907 * 58908 * @param {Locale} locale 58909 * @private 58910 */ 58911 58912 }, { 58913 key: '_createBackButton', 58914 value: function _createBackButton(locale) { 58915 var button = this._createButton('back', 'vis-button vis-back', locale['back'] || this.options.locales['en']['back']); 58916 this.manipulationDiv.appendChild(button); 58917 this._bindHammerToDiv(button, this.showManipulatorToolbar.bind(this)); 58918 } 58919 58920 /** 58921 * 58922 * @param {number|string} id 58923 * @param {string} className 58924 * @param {label} label 58925 * @param {string} labelClassName 58926 * @returns {HTMLElement} 58927 * @private 58928 */ 58929 58930 }, { 58931 key: '_createButton', 58932 value: function _createButton(id, className, label) { 58933 var labelClassName = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 'vis-label'; 58934 58935 58936 this.manipulationDOM[id + 'Div'] = document.createElement('div'); 58937 this.manipulationDOM[id + 'Div'].className = className; 58938 this.manipulationDOM[id + 'Label'] = document.createElement('div'); 58939 this.manipulationDOM[id + 'Label'].className = labelClassName; 58940 this.manipulationDOM[id + 'Label'].innerHTML = label; 58941 this.manipulationDOM[id + 'Div'].appendChild(this.manipulationDOM[id + 'Label']); 58942 return this.manipulationDOM[id + 'Div']; 58943 } 58944 58945 /** 58946 * 58947 * @param {Label} label 58948 * @private 58949 */ 58950 58951 }, { 58952 key: '_createDescription', 58953 value: function _createDescription(label) { 58954 this.manipulationDiv.appendChild(this._createButton('description', 'vis-button vis-none', label)); 58955 } 58956 58957 // -------------------------- End of DOM functions for buttons ------------------------------// 58958 58959 /** 58960 * this binds an event until cleanup by the clean functions. 58961 * @param {Event} event The event 58962 * @param {function} newFunction 58963 * @private 58964 */ 58965 58966 }, { 58967 key: '_temporaryBindEvent', 58968 value: function _temporaryBindEvent(event, newFunction) { 58969 this.temporaryEventFunctions.push({ event: event, boundFunction: newFunction }); 58970 this.body.emitter.on(event, newFunction); 58971 } 58972 58973 /** 58974 * this overrides an UI function until cleanup by the clean function 58975 * @param {string} UIfunctionName 58976 * @param {function} newFunction 58977 * @private 58978 */ 58979 58980 }, { 58981 key: '_temporaryBindUI', 58982 value: function _temporaryBindUI(UIfunctionName, newFunction) { 58983 if (this.body.eventListeners[UIfunctionName] !== undefined) { 58984 this.temporaryUIFunctions[UIfunctionName] = this.body.eventListeners[UIfunctionName]; 58985 this.body.eventListeners[UIfunctionName] = newFunction; 58986 } else { 58987 throw new Error('This UI function does not exist. Typo? You tried: ' + UIfunctionName + ' possible are: ' + (0, _stringify2['default'])((0, _keys2['default'])(this.body.eventListeners))); 58988 } 58989 } 58990 58991 /** 58992 * Restore the overridden UI functions to their original state. 58993 * 58994 * @private 58995 */ 58996 58997 }, { 58998 key: '_unbindTemporaryUIs', 58999 value: function _unbindTemporaryUIs() { 59000 for (var functionName in this.temporaryUIFunctions) { 59001 if (this.temporaryUIFunctions.hasOwnProperty(functionName)) { 59002 this.body.eventListeners[functionName] = this.temporaryUIFunctions[functionName]; 59003 delete this.temporaryUIFunctions[functionName]; 59004 } 59005 } 59006 this.temporaryUIFunctions = {}; 59007 } 59008 59009 /** 59010 * Unbind the events created by _temporaryBindEvent 59011 * @private 59012 */ 59013 59014 }, { 59015 key: '_unbindTemporaryEvents', 59016 value: function _unbindTemporaryEvents() { 59017 for (var i = 0; i < this.temporaryEventFunctions.length; i++) { 59018 var eventName = this.temporaryEventFunctions[i].event; 59019 var boundFunction = this.temporaryEventFunctions[i].boundFunction; 59020 this.body.emitter.off(eventName, boundFunction); 59021 } 59022 this.temporaryEventFunctions = []; 59023 } 59024 59025 /** 59026 * Bind an hammer instance to a DOM element. 59027 * 59028 * @param {Element} domElement 59029 * @param {function} boundFunction 59030 */ 59031 59032 }, { 59033 key: '_bindHammerToDiv', 59034 value: function _bindHammerToDiv(domElement, boundFunction) { 59035 var hammer = new Hammer(domElement, {}); 59036 hammerUtil.onTouch(hammer, boundFunction);
59037 this.manipulationHammers.push(hammer); 59038 } 59039 59040 /** 59041 * Neatly clean up temporary edges and nodes 59042 * @private 59043 */ 59044 59045 }, { 59046 key: '_cleanupTemporaryNodesAndEdges', 59047 value: function _cleanupTemporaryNodesAndEdges() { 59048 // _clean temporary edges 59049 for (var i = 0; i < this.temporaryIds.edges.length; i++) { 59050 this.body.edges[this.temporaryIds.edges[i]].disconnect(); 59051 delete this.body.edges[this.temporaryIds.edges[i]]; 59052 var indexTempEdge = this.body.edgeIndices.indexOf(this.temporaryIds.edges[i]); 59053 if (indexTempEdge !== -1) { 59054 this.body.edgeIndices.splice(indexTempEdge, 1); 59055 } 59056 } 59057 59058 // _clean temporary nodes 59059 for (var _i = 0; _i < this.temporaryIds.nodes.length; _i++) { 59060 delete this.body.nodes[this.temporaryIds.nodes[_i]]; 59061 var indexTempNode = this.body.nodeIndices.indexOf(this.temporaryIds.nodes[_i]); 59062 if (indexTempNode !== -1) { 59063 this.body.nodeIndices.splice(indexTempNode, 1); 59064 } 59065 } 59066 59067 this.temporaryIds = { nodes: [], edges: [] }; 59068 } 59069 59070 // ------------------------------------------ EDIT EDGE FUNCTIONS -----------------------------------------// 59071 59072 /** 59073 * the touch is used to get the position of the initial click 59074 * @param {Event} event The event 59075 * @private 59076 */ 59077 59078 }, { 59079 key: '_controlNodeTouch', 59080 value: function _controlNodeTouch(event) { 59081 this.selectionHandler.unselectAll(); 59082 this.lastTouch = this.body.functions.getPointer(event.center); 59083 this.lastTouch.translation = util.extend({}, this.body.view.translation); // copy the object 59084 } 59085 59086 /** 59087 * the drag start is used to mark one of the control nodes as selected. 59088 * @param {Event} event The event 59089 * @private 59090 */ 59091 59092 }, { 59093 key: '_controlNodeDragStart', 59094 value: function _controlNodeDragStart(event) { 59095 // eslint-disable-line no-unused-vars 59096 var pointer = this.lastTouch; 59097 var pointerObj = this.selectionHandler._pointerToPositionObject(pointer); 59098 var from = this.body.nodes[this.temporaryIds.nodes[0]]; 59099 var to = this.body.nodes[this.temporaryIds.nodes[1]]; 59100 var edge = this.body.edges[this.edgeBeingEditedId]; 59101 this.selectedControlNode = undefined; 59102 59103 var fromSelect = from.isOverlappingWith(pointerObj); 59104 var toSelect = to.isOverlappingWith(pointerObj); 59105 59106 if (fromSelect === true) { 59107 this.selectedControlNode = from; 59108 edge.edgeType.from = from; 59109 } else if (toSelect === true) { 59110 this.selectedControlNode = to; 59111 edge.edgeType.to = to; 59112 } 59113 59114 // we use the selection to find the node that is being dragged. We explicitly select it here. 59115 if (this.selectedControlNode !== undefined) { 59116 this.selectionHandler.selectObject(this.selectedControlNode); 59117 } 59118 59119 this.body.emitter.emit('_redraw'); 59120 } 59121 59122 /** 59123 * dragging the control nodes or the canvas 59124 * @param {Event} event The event 59125 * @private 59126 */ 59127 59128 }, { 59129 key: '_controlNodeDrag', 59130 value: function _controlNodeDrag(event) { 59131 this.body.emitter.emit('disablePhysics'); 59132 var pointer = this.body.functions.getPointer(event.center); 59133 var pos = this.canvas.DOMtoCanvas(pointer); 59134 if (this.selectedControlNode !== undefined) { 59135 this.selectedControlNode.x = pos.x; 59136 this.selectedControlNode.y = pos.y; 59137 } else { 59138 // if the drag was not started properly because the click started outside the network div, start it now. 59139 var diffX = pointer.x - this.lastTouch.x; 59140 var diffY = pointer.y - this.lastTouch.y; 59141 this.body.view.translation = { x: this.lastTouch.translation.x + diffX, y: this.lastTouch.translation.y + diffY }; 59142 } 59143 this.body.emitter.emit('_redraw'); 59144 } 59145 59146 /** 59147 * connecting or restoring the control nodes. 59148 * @param {Event} event The event 59149 * @private 59150 */ 59151 59152 }, { 59153 key: '_controlNodeDragEnd', 59154 value: function _controlNodeDragEnd(event) { 59155 var pointer = this.body.functions.getPointer(event.center); 59156 var pointerObj = this.selectionHandler._pointerToPositionObject(pointer); 59157 var edge = this.body.edges[this.edgeBeingEditedId]; 59158 // if the node that was dragged is not a control node, return 59159 if (this.selectedControlNode === undefined) { 59160 return; 59161 } 59162 59163 // we use the selection to find the node that is being dragged. We explicitly DEselect the control node here. 59164 this.selectionHandler.unselectAll(); 59165 var overlappingNodeIds = this.selectionHandler._getAllNodesOverlappingWith(pointerObj); 59166 var node = undefined; 59167 for (var i = overlappingNodeIds.length - 1; i >= 0; i--) { 59168 if (overlappingNodeIds[i] !== this.selectedControlNode.id) { 59169 node = this.body.nodes[overlappingNodeIds[i]]; 59170 break; 59171 } 59172 } 59173 // perform the connection 59174 if (node !== undefined && this.selectedControlNode !== undefined) { 59175 if (node.isCluster === true) { 59176 alert(this.options.locales[this.options.locale]['createEdgeError'] || this.options.locales['en']['createEdgeError']); 59177 } else { 59178 var from = this.body.nodes[this.temporaryIds.nodes[0]]; 59179 if (this.selectedControlNode.id === from.id) {
59180 this._performEditEdge(node.id, edge.to.id); 59181 } else { 59182 this._performEditEdge(edge.from.id, node.id); 59183 } 59184 } 59185 } else { 59186 edge.updateEdgeType(); 59187 this.body.emitter.emit('restorePhysics'); 59188 } 59189 59190 this.body.emitter.emit('_redraw'); 59191 } 59192 59193 // ------------------------------------ END OF EDIT EDGE FUNCTIONS -----------------------------------------// 59194 59195 59196 // ------------------------------------------- ADD EDGE FUNCTIONS -----------------------------------------// 59197 /** 59198 * the function bound to the selection event. It checks if you want to connect a cluster and changes the description 59199 * to walk the user through the process. 59200 * 59201 * @param {Event} event 59202 * @private 59203 */ 59204 59205 }, { 59206 key: '_handleConnect', 59207 value: function _handleConnect(event) { 59208 // check to avoid double fireing of this function. 59209 if (new Date().valueOf() - this.touchTime > 100) { 59210 this.lastTouch = this.body.functions.getPointer(event.center); 59211 this.lastTouch.translation = util.extend({}, this.body.view.translation); // copy the object 59212 59213 var pointer = this.lastTouch; 59214 var node = this.selectionHandler.getNodeAt(pointer); 59215 59216 if (node !== undefined) { 59217 if (node.isCluster === true) { 59218 alert(this.options.locales[this.options.locale]['createEdgeError'] || this.options.locales['en']['createEdgeError']); 59219 } else { 59220 // create a node the temporary line can look at 59221 var targetNode = this._getNewTargetNode(node.x, node.y); 59222 this.body.nodes[targetNode.id] = targetNode; 59223 this.body.nodeIndices.push(targetNode.id); 59224 59225 // create a temporary edge 59226 var connectionEdge = this.body.functions.createEdge({ 59227 id: 'connectionEdge' + util.randomUUID(), 59228 from: node.id, 59229 to: targetNode.id, 59230 physics: false, 59231 smooth: { 59232 enabled: true, 59233 type: 'continuous', 59234 roundness: 0.5 59235 } 59236 }); 59237 this.body.edges[connectionEdge.id] = connectionEdge; 59238 this.body.edgeIndices.push(connectionEdge.id); 59239 59240 this.temporaryIds.nodes.push(targetNode.id); 59241 this.temporaryIds.edges.push(connectionEdge.id); 59242 } 59243 } 59244 this.touchTime = new Date().valueOf(); 59245 } 59246 } 59247 59248 /** 59249 * 59250 * @param {Event} event 59251 * @private 59252 */ 59253 59254 }, { 59255 key: '_dragControlNode', 59256 value: function _dragControlNode(event) { 59257 var pointer = this.body.functions.getPointer(event.center); 59258 if (this.temporaryIds.nodes[0] !== undefined) { 59259 var targetNode = this.body.nodes[this.temporaryIds.nodes[0]]; // there is only one temp node in the add edge mode. 59260 targetNode.x = this.canvas._XconvertDOMtoCanvas(pointer.x); 59261 targetNode.y = this.canvas._YconvertDOMtoCanvas(pointer.y); 59262 this.body.emitter.emit('_redraw'); 59263 } else { 59264 var diffX = pointer.x - this.lastTouch.x; 59265 var diffY = pointer.y - this.lastTouch.y; 59266 this.body.view.translation = { x: this.lastTouch.translation.x + diffX, y: this.lastTouch.translation.y + diffY }; 59267 } 59268 } 59269 59270 /** 59271 * Connect the new edge to the target if one exists, otherwise remove temp line 59272 * @param {Event} event The event 59273 * @private 59274 */ 59275 59276 }, { 59277 key: '_finishConnect', 59278 value: function _finishConnect(event) { 59279 var pointer = this.body.functions.getPointer(event.center); 59280 var pointerObj = this.selectionHandler._pointerToPositionObject(pointer); 59281 59282 // remember the edge id 59283 var connectFromId = undefined; 59284 if (this.temporaryIds.edges[0] !== undefined) { 59285 connectFromId = this.body.edges[this.temporaryIds.edges[0]].fromId; 59286 } 59287 59288 // get the overlapping node but NOT the temporary node; 59289 var overlappingNodeIds = this.selectionHandler._getAllNodesOverlappingWith(pointerObj); 59290 var node = undefined; 59291 for (var i = overlappingNodeIds.length - 1; i >= 0; i--) { 59292 // if the node id is NOT a temporary node, accept the node. 59293 if (this.temporaryIds.nodes.indexOf(overlappingNodeIds[i]) === -1) { 59294 node = this.body.nodes[overlappingNodeIds[i]]; 59295 break; 59296 } 59297 } 59298 59299 // clean temporary nodes and edges. 59300 this._cleanupTemporaryNodesAndEdges(); 59301 59302 // perform the connection 59303 if (node !== undefined) { 59304 if (node.isCluster === true) { 59305 alert(this.options.locales[this.options.locale]['createEdgeError'] || this.options.locales['en']['createEdgeError']); 59306 } else { 59307 if (this.body.nodes[connectFromId] !== undefined && this.body.nodes[node.id] !== undefined) { 59308 this._performAddEdge(connectFromId, node.id); 59309 } 59310 } 59311 } 59312 59313 // No need to do _generateclickevent('dragEnd') here, the regular dragEnd event fires. 59314 this.body.emitter.emit('_redraw'); 59315 } 59316 59317 /** 59318 * 59319 * @param {Event} event 59320 * @private 59321 */ 59322 59323 }, { 59324 key: '_dragStartEdge', 59325 value: function _dragStartEdge(event) { 59326 var pointer = this.lastTouch; 59327 this.selectionHandler._generateClickEvent('dragStart', event, pointer, undefined, true); 59328 } 59329 59330 // --------------------------------------- END OF ADD EDGE FUNCTIONS -------------------------------------// 59331 59332 59333 // ------------------------------ Performing all the actual data manipulation ------------------------// 59334 59335 /**
59336 * Adds a node on the specified location 59337 * 59338 * @param {Object} clickData 59339 * @private 59340 */ 59341 59342 }, { 59343 key: '_performAddNode', 59344 value: function _performAddNode(clickData) { 59345 var _this4 = this; 59346 59347 var defaultData = { 59348 id: util.randomUUID(), 59349 x: clickData.pointer.canvas.x, 59350 y: clickData.pointer.canvas.y, 59351 label: 'new' 59352 }; 59353 59354 if (typeof this.options.addNode === 'function') { 59355 if (this.options.addNode.length === 2) { 59356 this.options.addNode(defaultData, function (finalizedData) { 59357 if (finalizedData !== null && finalizedData !== undefined && _this4.inMode === 'addNode') { 59358 // if for whatever reason the mode has changes (due to dataset change) disregard the callback 59359 _this4.body.data.nodes.getDataSet().add(finalizedData); 59360 _this4.showManipulatorToolbar(); 59361 } 59362 }); 59363 } else { 59364 this.showManipulatorToolbar(); 59365 throw new Error('The function for add does not support two arguments (data,callback)'); 59366 } 59367 } else { 59368 this.body.data.nodes.getDataSet().add(defaultData); 59369 this.showManipulatorToolbar(); 59370 } 59371 } 59372 59373 /** 59374 * connect two nodes with a new edge. 59375 * 59376 * @param {Node.id} sourceNodeId 59377 * @param {Node.id} targetNodeId 59378 * @private 59379 */ 59380 59381 }, { 59382 key: '_performAddEdge', 59383 value: function _performAddEdge(sourceNodeId, targetNodeId) { 59384 var _this5 = this; 59385 59386 var defaultData = { from: sourceNodeId, to: targetNodeId }; 59387 if (typeof this.options.addEdge === 'function') { 59388 if (this.options.addEdge.length === 2) { 59389 this.options.addEdge(defaultData, function (finalizedData) { 59390 if (finalizedData !== null && finalizedData !== undefined && _this5.inMode === 'addEdge') { 59391 // if for whatever reason the mode has changes (due to dataset change) disregard the callback 59392 _this5.body.data.edges.getDataSet().add(finalizedData); 59393 _this5.selectionHandler.unselectAll(); 59394 _this5.showManipulatorToolbar(); 59395 } 59396 }); 59397 } else { 59398 throw new Error('The function for connect does not support two arguments (data,callback)'); 59399 } 59400 } else { 59401 this.body.data.edges.getDataSet().add(defaultData); 59402 this.selectionHandler.unselectAll(); 59403 this.showManipulatorToolbar(); 59404 } 59405 } 59406 59407 /** 59408 * connect two nodes with a new edge. 59409 * 59410 * @param {Node.id} sourceNodeId 59411 * @param {Node.id} targetNodeId 59412 * @private 59413 */ 59414 59415 }, { 59416 key: '_performEditEdge', 59417 value: function _performEditEdge(sourceNodeId, targetNodeId) { 59418 var _this6 = this; 59419 59420 var defaultData = { id: this.edgeBeingEditedId, from: sourceNodeId, to: targetNodeId, label: this.body.data.edges._data[this.edgeBeingEditedId].label }; 59421 var eeFunct = this.options.editEdge; 59422 if ((typeof eeFunct === 'undefined' ? 'undefined' : (0, _typeof3['default'])(eeFunct)) === 'object') { 59423 eeFunct = eeFunct.editWithoutDrag; 59424 } 59425 if (typeof eeFunct === 'function') { 59426 if (eeFunct.length === 2) { 59427 eeFunct(defaultData, function (finalizedData) { 59428 if (finalizedData === null || finalizedData === undefined || _this6.inMode !== 'editEdge') { 59429 // if for whatever reason the mode has changes (due to dataset change) disregard the callback) { 59430 _this6.body.edges[defaultData.id].updateEdgeType(); 59431 _this6.body.emitter.emit('_redraw'); 59432 _this6.showManipulatorToolbar(); 59433 } else { 59434 _this6.body.data.edges.getDataSet().update(finalizedData); 59435 _this6.selectionHandler.unselectAll(); 59436 _this6.showManipulatorToolbar(); 59437 } 59438 }); 59439 } else { 59440 throw new Error('The function for edit does not support two arguments (data, callback)'); 59441 } 59442 } else { 59443 this.body.data.edges.getDataSet().update(defaultData); 59444 this.selectionHandler.unselectAll(); 59445 this.showManipulatorToolbar(); 59446 } 59447 } 59448 }]); 59449 return ManipulationSystem; 59450}(); 59451 59452exports['default'] = ManipulationSystem; 59453 59454/***/ }), 59455/* 238 */ 59456/***/ (function(module, exports, __webpack_require__) { 59457 59458"use strict"; 59459 59460 59461Object.defineProperty(exports, "__esModule", { 59462 value: true 59463}); 59464 59465var _slicedToArray2 = __webpack_require__(30); 59466 59467var _slicedToArray3 = _interopRequireDefault(_slicedToArray2); 59468 59469var _classCallCheck2 = __webpack_require__(0); 59470 59471var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); 59472 59473var _createClass2 = __webpack_require__(1); 59474 59475var _createClass3 = _interopRequireDefault(_createClass2); 59476 59477var _FloydWarshall = __webpack_require__(239); 59478 59479var _FloydWarshall2 = _interopRequireDefault(_FloydWarshall); 59480 59481function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } 59482 59483/** 59484 * KamadaKawai positions the nodes initially based on 59485 * 59486 * "AN ALGORITHM FOR DRAWING GENERAL UNDIRECTED GRAPHS" 59487 * -- Tomihisa KAMADA and Satoru KAWAI in 1989 59488 * 59489 * Possible optimizations in the distance calculation can be implemented. 59490 */ 59491var KamadaKawai = function () { 59492 /** 59493 * @param {Object} body 59494 * @param {number} edgeLength 59495 * @param {number} edgeStrength 59496 */ 59497 function KamadaKawai(body, edgeLength, edgeStrength) { 59498 (0, _classCallCheck3["default"])(this, KamadaKawai); 59499 59500 this.body = body; 59501 this.springLength = edgeLength; 59502 this.springConstant = edgeStrength; 59503 this.distanceSolver = new _FloydWarshall2["default"](); 59504 } 59505 59506 /** 59507 * Not sure if needed but can be used to update the spring length and spring constant 59508 * @param {Object} options 59509 */ 59510 59511 59512 (0, _createClass3["default"])(KamadaKawai, [{ 59513 key: "setOptions", 59514 value: function setOptions(options) { 59515 if (options) { 59516 if (options.springLength) { 59517 this.springLength = options.springLength; 59518 } 59519 if (options.springConstant) { 59520 this.springConstant = options.springConstant; 59521 } 59522 } 59523 } 59524 59525 /** 59526 * Position the system 59527 * @param {Array.<Node>} nodesArray 59528 * @param {Array.<vis.Edge>} edgesArray 59529 * @param {boolean}
59529 [ignoreClusters=false] 59530 */ 59531 59532 }, { 59533 key: "solve", 59534 value: function solve(nodesArray, edgesArray) { 59535 var ignoreClusters = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false; 59536 59537 // get distance matrix 59538 var D_matrix = this.distanceSolver.getDistances(this.body, nodesArray, edgesArray); // distance matrix 59539 59540 // get the L Matrix 59541 this._createL_matrix(D_matrix); 59542 59543 // get the K Matrix 59544 this._createK_matrix(D_matrix); 59545 59546 // initial E Matrix 59547 this._createE_matrix(); 59548 59549 // calculate positions 59550 var threshold = 0.01; 59551 var innerThreshold = 1; 59552 var iterations = 0; 59553 var maxIterations = Math.max(1000, Math.min(10 * this.body.nodeIndices.length, 6000)); 59554 var maxInnerIterations = 5; 59555 59556 var maxEnergy = 1e9; 59557 var highE_nodeId = 0, 59558 dE_dx = 0, 59559 dE_dy = 0, 59560 delta_m = 0, 59561 subIterations = 0; 59562 59563 while (maxEnergy > threshold && iterations < maxIterations) { 59564 iterations += 1; 59565 59566 var _getHighestEnergyNode2 = this._getHighestEnergyNode(ignoreClusters); 59567 59568 var _getHighestEnergyNode3 = (0, _slicedToArray3["default"])(_getHighestEnergyNode2, 4); 59569 59570 highE_nodeId = _getHighestEnergyNode3[0]; 59571 maxEnergy = _getHighestEnergyNode3[1]; 59572 dE_dx = _getHighestEnergyNode3[2]; 59573 dE_dy = _getHighestEnergyNode3[3]; 59574 59575 delta_m = maxEnergy; 59576 subIterations = 0; 59577 while (delta_m > innerThreshold && subIterations < maxInnerIterations) { 59578 subIterations += 1; 59579 this._moveNode(highE_nodeId, dE_dx, dE_dy); 59580 59581 var _getEnergy2 = this._getEnergy(highE_nodeId); 59582 59583 var _getEnergy3 = (0, _slicedToArray3["default"])(_getEnergy2, 3); 59584 59585 delta_m = _getEnergy3[0]; 59586 dE_dx = _getEnergy3[1]; 59587 dE_dy = _getEnergy3[2]; 59588 } 59589 } 59590 } 59591 59592 /** 59593 * get the node with the highest energy 59594 * @param {boolean} ignoreClusters 59595 * @returns {number[]} 59596 * @private 59597 */ 59598 59599 }, { 59600 key: "_getHighestEnergyNode", 59601 value: function _getHighestEnergyNode(ignoreClusters) { 59602 var nodesArray = this.body.nodeIndices; 59603 var nodes = this.body.nodes; 59604 var maxEnergy = 0; 59605 var maxEnergyNodeId = nodesArray[0]; 59606 var dE_dx_max = 0, 59607 dE_dy_max = 0; 59608 59609 for (var nodeIdx = 0; nodeIdx < nodesArray.length; nodeIdx++) { 59610 var m = nodesArray[nodeIdx]; 59611 // by not evaluating nodes with predefined positions we should only move nodes that have no positions. 59612 if (nodes[m].predefinedPosition === false || nodes[m].isCluster === true && ignoreClusters === true || nodes[m].options.fixed.x === true || nodes[m].options.fixed.y === true) { 59613 var _getEnergy4 = this._getEnergy(m), 59614 _getEnergy5 = (0, _slicedToArray3["default"])(_getEnergy4, 3), 59615 delta_m = _getEnergy5[0], 59616 dE_dx = _getEnergy5[1], 59617 dE_dy = _getEnergy5[2]; 59618 59619 if (maxEnergy < delta_m) { 59620 maxEnergy = delta_m; 59621 maxEnergyNodeId = m; 59622 dE_dx_max = dE_dx; 59623 dE_dy_max = dE_dy; 59624 } 59625 } 59626 } 59627 59628 return [maxEnergyNodeId, maxEnergy, dE_dx_max, dE_dy_max]; 59629 } 59630 59631 /** 59632 * calculate the energy of a single node 59633 * @param {Node.id} m 59634 * @returns {number[]} 59635 * @private 59636 */ 59637 59638 }, { 59639 key: "_getEnergy", 59640 value: function _getEnergy(m) { 59641 var _E_sums$m = (0, _slicedToArray3["default"])(this.E_sums[m], 2), 59642 dE_dx = _E_sums$m[0], 59643 dE_dy = _E_sums$m[1]; 59644 59645 var delta_m = Math.sqrt(Math.pow(dE_dx, 2) + Math.pow(dE_dy, 2)); 59646 return [delta_m, dE_dx, dE_dy]; 59647 } 59648 59649 /** 59650 * move the node based on it's energy 59651 * the dx and dy are calculated from the linear system proposed by Kamada and Kawai 59652 * @param {number} m 59653 * @param {number} dE_dx 59654 * @param {number} dE_dy 59655 * @private 59656 */ 59657 59658 }, { 59659 key: "_moveNode", 59660 value: function _moveNode(m, dE_dx, dE_dy) { 59661 var nodesArray = this.body.nodeIndices; 59662 var nodes = this.body.nodes; 59663 var d2E_dx2 = 0; 59664 var d2E_dxdy = 0; 59665 var d2E_dy2 = 0; 59666
59667 var x_m = nodes[m].x; 59668 var y_m = nodes[m].y; 59669 var km = this.K_matrix[m]; 59670 var lm = this.L_matrix[m]; 59671 59672 for (var iIdx = 0; iIdx < nodesArray.length; iIdx++) { 59673 var i = nodesArray[iIdx]; 59674 if (i !== m) { 59675 var x_i = nodes[i].x; 59676 var y_i = nodes[i].y; 59677 var kmat = km[i]; 59678 var lmat = lm[i]; 59679 var denominator = 1.0 / Math.pow(Math.pow(x_m - x_i, 2) + Math.pow(y_m - y_i, 2), 1.5); 59680 d2E_dx2 += kmat * (1 - lmat * Math.pow(y_m - y_i, 2) * denominator); 59681 d2E_dxdy += kmat * (lmat * (x_m - x_i) * (y_m - y_i) * denominator); 59682 d2E_dy2 += kmat * (1 - lmat * Math.pow(x_m - x_i, 2) * denominator); 59683 } 59684 } 59685 // make the variable names easier to make the solving of the linear system easier to read 59686 var A = d2E_dx2, 59687 B = d2E_dxdy, 59688 C = dE_dx, 59689 D = d2E_dy2, 59690 E = dE_dy; 59691 59692 // solve the linear system for dx and dy 59693 var dy = (C / A + E / B) / (B / A - D / B); 59694 var dx = -(B * dy + C) / A; 59695 59696 // move the node 59697 nodes[m].x += dx; 59698 nodes[m].y += dy; 59699 59700 // Recalculate E_matrix (should be incremental) 59701 this._updateE_matrix(m); 59702 } 59703 59704 /** 59705 * Create the L matrix: edge length times shortest path 59706 * @param {Object} D_matrix 59707 * @private 59708 */ 59709 59710 }, { 59711 key: "_createL_matrix", 59712 value: function _createL_matrix(D_matrix) { 59713 var nodesArray = this.body.nodeIndices; 59714 var edgeLength = this.springLength; 59715 59716 this.L_matrix = []; 59717 for (var i = 0; i < nodesArray.length; i++) { 59718 this.L_matrix[nodesArray[i]] = {}; 59719 for (var j = 0; j < nodesArray.length; j++) { 59720 this.L_matrix[nodesArray[i]][nodesArray[j]] = edgeLength * D_matrix[nodesArray[i]][nodesArray[j]]; 59721 } 59722 } 59723 } 59724 59725 /** 59726 * Create the K matrix: spring constants times shortest path 59727 * @param {Object} D_matrix 59728 * @private 59729 */ 59730 59731 }, { 59732 key: "_createK_matrix", 59733 value: function _createK_matrix(D_matrix) { 59734 var nodesArray = this.body.nodeIndices; 59735 var edgeStrength = this.springConstant; 59736 59737 this.K_matrix = []; 59738 for (var i = 0; i < nodesArray.length; i++) { 59739 this.K_matrix[nodesArray[i]] = {}; 59740 for (var j = 0; j < nodesArray.length; j++) { 59741 this.K_matrix[nodesArray[i]][nodesArray[j]] = edgeStrength * Math.pow(D_matrix[nodesArray[i]][nodesArray[j]], -2); 59742 } 59743 } 59744 } 59745 59746 /** 59747 * Create matrix with all energies between nodes 59748 * @private 59749 */ 59750 59751 }, { 59752 key: "_createE_matrix", 59753 value: function _createE_matrix() { 59754 var nodesArray = this.body.nodeIndices; 59755 var nodes = this.body.nodes; 59756 this.E_matrix = {}; 59757 this.E_sums = {}; 59758 for (var mIdx = 0; mIdx < nodesArray.length; mIdx++) { 59759 this.E_matrix[nodesArray[mIdx]] = []; 59760 } 59761 for (var _mIdx = 0; _mIdx < nodesArray.length; _mIdx++) { 59762 var m = nodesArray[_mIdx]; 59763 var x_m = nodes[m].x; 59764 var y_m = nodes[m].y; 59765 var dE_dx = 0; 59766 var dE_dy = 0; 59767 for (var iIdx = _mIdx; iIdx < nodesArray.length; iIdx++) { 59768 var i = nodesArray[iIdx]; 59769 if (i !== m) { 59770 var x_i = nodes[i].x; 59771 var y_i = nodes[i].y; 59772 var denominator = 1.0 / Math.sqrt(Math.pow(x_m - x_i, 2) + Math.pow(y_m - y_i, 2)); 59773 this.E_matrix[m][iIdx] = [this.K_matrix[m][i] * (x_m - x_i - this.L_matrix[m][i] * (x_m - x_i) * denominator), this.K_matrix[m][i] * (y_m - y_i - this.L_matrix[m][i] * (y_m - y_i) * denominator)]; 59774 this.E_matrix[i][_mIdx] = this.E_matrix[m][iIdx]; 59775 dE_dx += this.E_matrix[m][iIdx][0]; 59776 dE_dy += this.E_matrix[m][iIdx][1]; 59777 } 59778 } 59779 //Store sum 59780 this.E_sums[m] = [dE_dx, dE_dy]; 59781 } 59782 } 59783 59784 /** 59785 * Update method, just doing single column (rows are auto-updated) (update all sums) 59786 * 59787 * @param {number} m 59788 * @private 59789 */ 59790 59791 }, { 59792 key: "_updateE_matrix", 59793 value: function _updateE_matrix(m) { 59794 var nodesArray = this.body.nodeIndices; 59795 var nodes = this.body.nodes; 59796 var colm = this.E_matrix[m]; 59797 var kcolm = this.K_matrix[m]; 59798 var lcolm = this.L_matrix[m];
59799 var x_m = nodes[m].x; 59800 var y_m = nodes[m].y; 59801 var dE_dx = 0; 59802 var dE_dy = 0; 59803 for (var iIdx = 0; iIdx < nodesArray.length; iIdx++) { 59804 var i = nodesArray[iIdx]; 59805 if (i !== m) { 59806 //Keep old energy value for sum modification below 59807 var cell = colm[iIdx]; 59808 var oldDx = cell[0]; 59809 var oldDy = cell[1]; 59810 59811 //Calc new energy: 59812 var x_i = nodes[i].x; 59813 var y_i = nodes[i].y; 59814 var denominator = 1.0 / Math.sqrt(Math.pow(x_m - x_i, 2) + Math.pow(y_m - y_i, 2)); 59815 var dx = kcolm[i] * (x_m - x_i - lcolm[i] * (x_m - x_i) * denominator); 59816 var dy = kcolm[i] * (y_m - y_i - lcolm[i] * (y_m - y_i) * denominator); 59817 colm[iIdx] = [dx, dy]; 59818 dE_dx += dx; 59819 dE_dy += dy; 59820 59821 //add new energy to sum of each column 59822 var sum = this.E_sums[i]; 59823 sum[0] += dx - oldDx; 59824 sum[1] += dy - oldDy; 59825 } 59826 } 59827 //Store sum at -1 index 59828 this.E_sums[m] = [dE_dx, dE_dy]; 59829 } 59830 }]); 59831 return KamadaKawai; 59832}(); // distance finding algorithm 59833 59834 59835exports["default"] = KamadaKawai; 59836 59837/***/ }), 59838/* 239 */ 59839/***/ (function(module, exports, __webpack_require__) { 59840 59841"use strict"; 59842 59843 59844Object.defineProperty(exports, "__esModule", { 59845 value: true 59846}); 59847 59848var _classCallCheck2 = __webpack_require__(0); 59849 59850var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); 59851 59852var _createClass2 = __webpack_require__(1); 59853 59854var _createClass3 = _interopRequireDefault(_createClass2); 59855 59856function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } 59857 59858/** 59859 * The FloydâWarshall algorithm is an algorithm for finding shortest paths in 59860 * a weighted graph with positive or negative edge weights (but with no negative 59861 * cycles). - https://en.wikipedia.org/wiki/FloydâWarshall_algorithm 59862 */ 59863var FloydWarshall = function () { 59864 /** 59865 * @ignore 59866 */ 59867 function FloydWarshall() { 59868 (0, _classCallCheck3["default"])(this, FloydWarshall); 59869 } 59870 59871 /** 59872 * 59873 * @param {Object} body 59874 * @param {Array.<Node>} nodesArray 59875 * @param {Array.<Edge>} edgesArray 59876 * @returns {{}} 59877 */ 59878 59879 59880 (0, _createClass3["default"])(FloydWarshall, [{ 59881 key: "getDistances", 59882 value: function getDistances(body, nodesArray, edgesArray) { 59883 var D_matrix = {}; 59884 var edges = body.edges; 59885 59886 // prepare matrix with large numbers 59887 for (var i = 0; i < nodesArray.length; i++) { 59888 var node = nodesArray[i]; 59889 var cell = {}; 59890 D_matrix[node] = cell; 59891 for (var j = 0; j < nodesArray.length; j++) { 59892 cell[nodesArray[j]] = i == j ? 0 : 1e9; 59893 } 59894 } 59895 59896 // put the weights for the edges in. This assumes unidirectionality. 59897 for (var _i = 0; _i < edgesArray.length; _i++) { 59898 var edge = edges[edgesArray[_i]]; 59899 // edge has to be connected if it counts to the distances. If it is connected to inner clusters it will crash so we also check if it is in the D_matrix 59900 if (edge.connected === true && D_matrix[edge.fromId] !== undefined && D_matrix[edge.toId] !== undefined) { 59901 D_matrix[edge.fromId][edge.toId] = 1; 59902 D_matrix[edge.toId][edge.fromId] = 1; 59903 } 59904 } 59905 59906 var nodeCount = nodesArray.length; 59907 59908 // Adapted FloydWarshall based on unidirectionality to greatly reduce complexity. 59909 for (var k = 0; k < nodeCount; k++) { 59910 var knode = nodesArray[k]; 59911 var kcolm = D_matrix[knode]; 59912 for (var _i2 = 0; _i2 < nodeCount - 1; _i2++) { 59913 var inode = nodesArray[_i2]; 59914 var icolm = D_matrix[inode]; 59915 for (var _j = _i2 + 1; _j < nodeCount; _j++) { 59916 var jnode = nodesArray[_j]; 59917 var jcolm = D_matrix[jnode]; 59918 59919 var val = Math.min(icolm[jnode], icolm[knode] + kcolm[jnode]); 59920 icolm[jnode] = val; 59921 jcolm[inode] = val; 59922 } 59923 } 59924 } 59925 59926 return D_matrix; 59927 } 59928 }]); 59929 return FloydWarshall; 59930}(); 59931 59932exports["default"] = FloydWarshall; 59933 59934/***/ }) 59935/******/ ]); 59936});
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.