1/*! 2 * bpmn-js - bpmn-modeler v8.8.2 3 * 4 * Copyright (c) 2014-present, camunda Services GmbH 5 * 6 * Released under the bpmn.io license 7 * http://bpmn.io/license 8 * 9 * Source Code: https://github.com/bpmn-io/bpmn-js 10 * 11 * Date: 2021-10-20 12 */ 13(function (global, factory) { 14 typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : 15 typeof define === 'function' && define.amd ? define(factory) : 16 (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.BpmnJS = factory()); 17}(this, (function () { 'use strict'; 18 19 var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {}; 20 21 var inherits_browser = {exports: {}}; 22 23 if (typeof Object.create === 'function') { 24 // implementation from standard node.js 'util' module 25 inherits_browser.exports = function inherits(ctor, superCtor) { 26 if (superCtor) { 27 ctor.super_ = superCtor; 28 ctor.prototype = Object.create(superCtor.prototype, { 29 constructor: { 30 value: ctor, 31 enumerable: false, 32 writable: true, 33 configurable: true 34 } 35 }); 36 } 37 }; 38 } else { 39 // old school shim for old browsers 40 inherits_browser.exports = function inherits(ctor, superCtor) { 41 if (superCtor) { 42 ctor.super_ = superCtor; 43 var TempCtor = function () {}; 44 TempCtor.prototype = superCtor.prototype; 45 ctor.prototype = new TempCtor(); 46 ctor.prototype.constructor = ctor; 47 } 48 }; 49 } 50 51 var inherits$1 = inherits_browser.exports; 52 53 function createCommonjsModule(fn, module) { 54 return module = { exports: {} }, fn(module, module.exports), module.exports; 55 } 56 57 var hat_1 = createCommonjsModule(function (module) { 58 var hat = module.exports = function (bits, base) { 59 if (!base) base = 16; 60 if (bits === undefined) bits = 128; 61 if (bits <= 0) return '0'; 62 63 var digits = Math.log(Math.pow(2, bits)) / Math.log(base); 64 for (var i = 2; digits === Infinity; i *= 2) { 65 digits = Math.log(Math.pow(2, bits / i)) / Math.log(base) * i; 66 } 67 68 var rem = digits - Math.floor(digits); 69 70 var res = ''; 71 72 for (var i = 0; i < Math.floor(digits); i++) { 73 var x = Math.floor(Math.random() * base).toString(base); 74 res = x + res; 75 } 76 77 if (rem) { 78 var b = Math.pow(base, rem); 79 var x = Math.floor(Math.random() * b).toString(base); 80 res = x + res; 81 } 82 83 var parsed = parseInt(res, base); 84 if (parsed !== Infinity && parsed >= Math.pow(2, bits)) { 85 return hat(bits, base) 86 } 87 else return res; 88 }; 89 90 hat.rack = function (bits, base, expandBy) { 91 var fn = function (data) { 92 var iters = 0; 93 do { 94 if (iters ++ > 10) { 95 if (expandBy) bits += expandBy; 96 else throw new Error('too many ID collisions, use more bits') 97 } 98 99 var id = hat(bits, base); 100 } while (Object.hasOwnProperty.call(hats, id)); 101 102 hats[id] = data; 103 return id; 104 }; 105 var hats = fn.hats = {}; 106 107 fn.get = function (id) { 108 return fn.hats[id]; 109 }; 110 111 fn.set = function (id, value) { 112 fn.hats[id] = value; 113 return fn; 114 }; 115 116 fn.bits = bits || 128; 117 fn.base = base || 16; 118 return fn; 119 }; 120 }); 121 122 /** 123 * Create a new id generator / cache instance. 124 * 125 * You may optionally provide a seed that is used internally. 126 * 127 * @param {Seed} seed 128 */ 129 130 function Ids(seed) { 131 if (!(this instanceof Ids)) { 132 return new Ids(seed); 133 } 134 135 seed = seed || [128, 36, 1]; 136 this._seed = seed.length ? hat_1.rack(seed[0], seed[1], seed[2]) : seed; 137 } 138 /** 139 * Generate a next id. 140 * 141 * @param {Object} [element] element to bind the id to 142 * 143 * @return {String} id 144 */ 145 146 Ids.prototype.next = function (element) { 147 return this._seed(element || true); 148 }; 149 /** 150 * Generate a next id with a given prefix. 151 * 152 * @param {Object} [element] element to bind the id to 153 * 154 * @return {String} id 155 */ 156 157 158 Ids.prototype.nextPrefixed = function (prefix, element) { 159 var id; 160 161 do { 162 id = prefix + this.next(true); 163 } while (this.assigned(id)); // claim {prefix}{random} 164 165 166 this.claim(id, element); // return 167 168 return id; 169 }; 170 /** 171 * Manually claim an existing id. 172 * 173 * @param {String} id 174 * @param {String} [element] element the id is claimed by 175 */ 176 177 178 Ids.prototype.claim = function (id, element) { 179 this._seed.set(id, element || true); 180 }; 181 /** 182 * Returns true if the given id has already been assigned. 183 * 184 * @param {String} id 185 * @return {Boolean} 186 */ 187 188 189 Ids.prototype.assigned = function (id) { 190 return this._seed.get(id) || false; 191 }; 192 /** 193 * Unclaim an id. 194 * 195 * @param {String} id the id to unclaim 196 */ 197 198 199 Ids.prototype.unclaim = function (id) { 200 delete this._seed.hats[id]; 201 }; 202 /** 203 * Clear all claimed ids. 204 */ 205 206 207 Ids.prototype.clear = function () { 208 var hats = this._seed.hats, 209 id; 210 211 for (id in hats) { 212 this.unclaim(id); 213 } 214 }; 215 216 /** 217 * Flatten array, one level deep. 218 * 219 * @param {Array<?>} arr 220 * 221 * @return {Array<?>} 222 */ 223 function flatten(arr) { 224 return Array.prototype.concat.apply([], arr); 225 } 226 227 var nativeToString = Object.prototype.toString; 228 var nativeHasOwnProperty = Object.prototype.hasOwnProperty; 229 function isUndefined$1(obj) { 230 return obj === undefined; 231 } 232 function isDefined(obj) { 233 return obj !== undefined; 234 } 235 function isNil(obj) { 236 return obj == null; 237 } 238 function isArray$2(obj) { 239 return nativeToString.call(obj) === '[object Array]'; 240 } 241 function isObject(obj) { 242 return nativeToString.call(obj) === '[object Object]'; 243 } 244 function isNumber(obj) { 245 return nativeToString.call(obj) === '[object Number]'; 246 } 247 function isFunction(obj) { 248 var tag = nativeToString.call(obj); 249 return tag === '[object Function]' || tag === '[object AsyncFunction]' || tag === '[object GeneratorFunction]' || tag === '[object AsyncGeneratorFunction]' || tag === '[object Proxy]'; 250 } 251 function isString(obj) { 252 return nativeToString.call(obj) === '[object String]'; 253 } 254 /** 255 * Ensure collection is an array. 256 * 257 * @param {Object} obj 258 */ 259 260 function ensureArray(obj) { 261 if (isArray$2(obj)) { 262 return; 263 } 264 265 throw new Error('must supply array'); 266 } 267 /** 268 * Return true, if target owns a property with the given key. 269 * 270 * @param {Object} target 271 * @param {String} key 272 * 273 * @return {Boolean} 274 */ 275 276 function has(target, key) { 277 return nativeHasOwnProperty.call(target, key); 278 } 279 280 /** 281 * Find element in collection. 282 * 283 * @param {Array|Object} collection 284 * @param {Function|Object} matcher 285 * 286 * @return {Object} 287 */ 288 289 function find(collection, matcher) { 290 matcher = toMatcher(matcher); 291 var match; 292 forEach(collection, function (val, key) { 293 if (matcher(val, key)) { 294 match = val; 295 return false; 296 } 297 }); 298 return match; 299 } 300 /** 301 * Find element index in collection. 302 * 303 * @param {Array|Object} collection 304 * @param {Function} matcher 305 * 306 * @return {Object} 307 */ 308 309 function findIndex(collection, matcher) { 310 matcher = toMatcher(matcher); 311 var idx = isArray$2(collection) ? -1 : undefined; 312 forEach(collection, function (val, key) { 313 if (matcher(val, key)) { 314 idx = key; 315 return false; 316 } 317 }); 318 return idx; 319 } 320 /** 321 * Find element in collection. 322 * 323 * @param {Array|Object} collection 324 * @param {Function} matcher 325 * 326 * @return {Array} result 327 */ 328 329 function filter(collection, matcher) { 330 var result = []; 331 forEach(collection, function (val, key) { 332 if (matcher(val, key)) { 333 result.push(val); 334 } 335 }); 336 return result; 337 } 338 /** 339 * Iterate over collection; returning something 340 * (non-undefined) will stop iteration. 341 * 342 * @param {Array|Object} collection 343 * @param {Function} iterator 344 * 345 * @return {Object} return result that stopped the iteration 346 */ 347 348 function forEach(collection, iterator) { 349 var val, result; 350 351 if (isUndefined$1(collection)) { 352 return; 353 } 354 355 var convertKey = isArray$2(collection) ? toNum : identity; 356 357 for (var key in collection) { 358 if (has(collection, key)) { 359 val = collection[key]; 360 result = iterator(val, convertKey(key)); 361 362 if (result === false) { 363 return val; 364 } 365 } 366 } 367 } 368 /** 369 * Return collection without element. 370 * 371 * @param {Array} arr 372 * @param {Function} matcher 373 * 374 * @return {Array} 375 */ 376 377 function without(arr, matcher) { 378 if (isUndefined$1(arr)) { 379 return []; 380 } 381 382 ensureArray(arr); 383 matcher = toMatcher(matcher); 384 return arr.filter(function (el, idx) { 385 return !matcher(el, idx); 386 }); 387 } 388 /** 389 * Reduce collection, returning a single result. 390 * 391 * @param {Object|Array} collection 392 * @param {Function} iterator 393 * @param {Any} result 394 * 395 * @return {Any} result returned from last iterator 396 */ 397 398 function reduce(collection, iterator, result) { 399 forEach(collection, function (value, idx) { 400 result = iterator(result, value, idx); 401 }); 402 return result; 403 } 404 /** 405 * Return true if every element in the collection 406 * matches the criteria. 407 * 408 * @param {Object|Array} collection 409 * @param {Function} matcher 410 * 411 * @return {Boolean} 412 */ 413 414 function every(collection, matcher) { 415 return !!reduce(collection, function (matches, val, key) { 416 return matches && matcher(val, key); 417 }, true); 418 } 419 /** 420 * Return true if some elements in the collection 421 * match the criteria. 422 * 423 * @param {Object|Array} collection 424 * @param {Function} matcher 425 * 426 * @return {Boolean} 427 */ 428 429 function some(collection, matcher) { 430 return !!find(collection, matcher); 431 } 432 /** 433 * Transform a collection into another collection 434 * by piping each member through the given fn. 435 * 436 * @param {Object|Array} collection 437 * @param {Function} fn 438 * 439 * @return {Array} transformed collection 440 */ 441 442 function map$1(collection, fn) { 443 var result = []; 444 forEach(collection, function (val, key) { 445 result.push(fn(val, key)); 446 }); 447 return result; 448 } 449 /** 450 * Get the collections keys. 451 * 452 * @param {Object|Array} collection 453 * 454 * @return {Array} 455 */ 456 457 function keys(collection) { 458 return collection && Object.keys(collection) || []; 459 } 460 /** 461 * Shorthand for `keys(o).length`. 462 * 463 * @param {Object|Array} collection 464 * 465 * @return {Number} 466 */ 467 468 function size(collection) { 469 return keys(collection).length; 470 } 471 /** 472 * Get the values in the collection. 473 * 474 * @param {Object|Array} collection 475 * 476 * @return {Array} 477 */ 478 479 function values(collection) { 480 return map$1(collection, function (val) { 481 return val; 482 }); 483 } 484 /** 485 * Group collection members by attribute. 486 * 487 * @param {Object|Array} collection 488 * @param {Function} extractor 489 * 490 * @return {Object} map with { attrValue => [ a, b, c ] } 491 */ 492 493 function groupBy(collection, extractor) { 494 var grouped = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; 495 extractor = toExtractor(extractor); 496 forEach(collection, function (val) { 497 var discriminator = extractor(val) || '_'; 498 var group = grouped[discriminator]; 499 500 if (!group) { 501 group = grouped[discriminator] = []; 502 } 503 504 group.push(val); 505 }); 506 return grouped; 507 } 508 function uniqueBy(extractor) { 509 extractor = toExtractor(extractor); 510 var grouped = {}; 511 512 for (var _len = arguments.length, collections = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { 513 collections[_key - 1] = arguments[_key]; 514 } 515 516 forEach(collections, function (c) { 517 return groupBy(c, extractor, grouped); 518 }); 519 var result = map$1(grouped, function (val, key) { 520 return val[0]; 521 }); 522 return result; 523 } 524 var unionBy = uniqueBy; 525 /** 526 * Sort collection by criteria. 527 * 528 * @param {Object|Array} collection 529 * @param {String|Function} extractor 530 * 531 * @return {Array} 532 */ 533 534 function sortBy(collection, extractor) { 535 extractor = toExtractor(extractor); 536 var sorted = []; 537 forEach(collection, function (value, key) { 538 var disc = extractor(value, key); 539 var entry = { 540 d: disc, 541 v: value 542 }; 543 544 for (var idx = 0; idx < sorted.length; idx++) { 545 var d = sorted[idx].d; 546 547 if (disc < d) { 548 sorted.splice(idx, 0, entry); 549 return; 550 } 551 } // not inserted, append (!) 552 553 554 sorted.push(entry); 555 }); 556 return map$1(sorted, function (e) { 557 return e.v; 558 }); 559 } 560 /** 561 * Create an object pattern matcher. 562 * 563 * @example 564 * 565 * const matcher = matchPattern({ id: 1 }); 566 * 567 * var element = find(elements, matcher); 568 * 569 * @param {Object} pattern 570 * 571 * @return {Function} matcherFn 572 */ 573 574 function matchPattern(pattern) { 575 return function (el) { 576 return every(pattern, function (val, key) { 577 return el[key] === val; 578 }); 579 }; 580 } 581 582 function toExtractor(extractor) { 583 return isFunction(extractor) ? extractor : function (e) { 584 return e[extractor]; 585 }; 586 } 587 588 function toMatcher(matcher) { 589 return isFunction(matcher) ? matcher : function (e) { 590 return e === matcher; 591 }; 592 } 593 594 function identity(arg) { 595 return arg; 596 } 597 598 function toNum(arg) { 599 return Number(arg); 600 } 601 602 /** 603 * Debounce fn, calling it only once if 604 * the given time elapsed between calls. 605 * 606 * @param {Function} fn 607 * @param {Number} timeout 608 * 609 * @return {Function} debounced function 610 */ 611 function debounce(fn, timeout) { 612 var timer; 613 var lastArgs; 614 var lastThis; 615 var lastNow; 616 617 function fire() { 618 var now = Date.now(); 619 var scheduledDiff = lastNow + timeout - now; 620 621 if (scheduledDiff > 0) { 622 return schedule(scheduledDiff); 623 } 624 625 fn.apply(lastThis, lastArgs); 626 timer = lastNow = lastArgs = lastThis = undefined; 627 } 628 629 function schedule(timeout) { 630 timer = setTimeout(fire, timeout); 631 } 632 633 return function () { 634 lastNow = Date.now(); 635 636 for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { 637 args[_key] = arguments[_key]; 638 } 639 640 lastArgs = args; 641 lastThis = this; // ensure an execution is scheduled 642 643 if (!timer) { 644 schedule(timeout); 645 } 646 }; 647 } 648 /** 649 * Bind function against target <this>. 650 * 651 * @param {Function} fn 652 * @param {Object} target 653 * 654 * @return {Function} bound function 655 */ 656 657 function bind$2(fn, target) { 658 return fn.bind(target); 659 } 660 661 function _extends() { 662 _extends = Object.assign || function (target) { 663 for (var i = 1; i < arguments.length; i++) { 664 var source = arguments[i]; 665 666 for (var key in source) { 667 if (Object.prototype.hasOwnProperty.call(source, key)) { 668 target[key] = source[key]; 669 } 670 } 671 } 672 673 return target; 674 }; 675 676 return _extends.apply(this, arguments); 677 } 678 679 /** 680 * Convenience wrapper for `Object.assign`. 681 * 682 * @param {Object} target 683 * @param {...Object} others 684 * 685 * @return {Object} the target 686 */ 687 688 function assign(target) { 689 for (var _len = arguments.length, others = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { 690 others[_key - 1] = arguments[_key]; 691 } 692 693 return _extends.apply(void 0, [target].concat(others)); 694 } 695 /** 696 * Pick given properties from the target object. 697 * 698 * @param {Object} target 699 * @param {Array} properties 700 * 701 * @return {Object} target 702 */ 703 704 function pick(target, properties) { 705 var result = {}; 706 var obj = Object(target); 707 forEach(properties, function (prop) { 708 if (prop in obj) { 709 result[prop] = target[prop]; 710 } 711 }); 712 return result; 713 } 714 /** 715 * Pick all target properties, excluding the given ones. 716 * 717 * @param {Object} target 718 * @param {Array} properties 719 * 720 * @return {Object} target 721 */ 722 723 function omit(target, properties) { 724 var result = {}; 725 var obj = Object(target); 726 forEach(obj, function (prop, key) { 727 if (properties.indexOf(key) === -1) { 728 result[key] = prop; 729 } 730 }); 731 return result; 732 } 733 734 /** 735 * Set attribute `name` to `val`, or get attr `name`. 736 * 737 * @param {Element} el 738 * @param {String} name 739 * @param {String} [val] 740 * @api public 741 */ 742 function attr$1(el, name, val) { 743 // get 744 if (arguments.length == 2) { 745 return el.getAttribute(name); 746 } 747 748 // remove 749 if (val === null) { 750 return el.removeAttribute(name); 751 } 752 753 // set 754 el.setAttribute(name, val); 755 756 return el; 757 } 758 759 var indexOf$1 = [].indexOf; 760 761 var indexof = function(arr, obj){ 762 if (indexOf$1) return arr.indexOf(obj); 763 for (var i = 0; i < arr.length; ++i) { 764 if (arr[i] === obj) return i; 765 } 766 return -1; 767 }; 768 769 /** 770 * Taken from https://github.com/component/classes 771 * 772 * Without the component bits. 773 */ 774 775 /** 776 * Whitespace regexp. 777 */ 778 779 var re$1 = /\s+/; 780 781 /** 782 * toString reference. 783 */ 784 785 var toString$1 = Object.prototype.toString; 786 787 /** 788 * Wrap `el` in a `ClassList`. 789 * 790 * @param {Element} el 791 * @return {ClassList} 792 * @api public 793 */ 794 795 function classes$1(el) { 796 return new ClassList$1(el); 797 } 798 799 /** 800 * Initialize a new ClassList for `el`. 801 * 802 * @param {Element} el 803 * @api private 804 */ 805 806 function ClassList$1(el) { 807 if (!el || !el.nodeType) { 808 throw new Error('A DOM element reference is required'); 809 } 810 this.el = el; 811 this.list = el.classList; 812 } 813 814 /** 815 * Add class `name` if not already present. 816 * 817 * @param {String} name 818 * @return {ClassList} 819 * @api public 820 */ 821 822 ClassList$1.prototype.add = function (name) { 823 // classList 824 if (this.list) { 825 this.list.add(name); 826 return this; 827 } 828 829 // fallback 830 var arr = this.array(); 831 var i = indexof(arr, name); 832 if (!~i) arr.push(name); 833 this.el.className = arr.join(' '); 834 return this; 835 }; 836 837 /** 838 * Remove class `name` when present, or 839 * pass a regular expression to remove 840 * any which match. 841 * 842 * @param {String|RegExp} name 843 * @return {ClassList} 844 * @api public 845 */ 846 847 ClassList$1.prototype.remove = function (name) { 848 if ('[object RegExp]' == toString$1.call(name)) { 849 return this.removeMatching(name); 850 } 851 852 // classList 853 if (this.list) { 854 this.list.remove(name); 855 return this; 856 } 857 858 // fallback 859 var arr = this.array(); 860 var i = indexof(arr, name); 861 if (~i) arr.splice(i, 1); 862 this.el.className = arr.join(' '); 863 return this; 864 }; 865 866 /** 867 * Remove all classes matching `re`. 868 * 869 * @param {RegExp} re 870 * @return {ClassList} 871 * @api private 872 */ 873 874 ClassList$1.prototype.removeMatching = function (re) { 875 var arr = this.array(); 876 for (var i = 0; i < arr.length; i++) { 877 if (re.test(arr[i])) { 878 this.remove(arr[i]); 879 } 880 } 881 return this; 882 }; 883 884 /** 885 * Toggle class `name`, can force state via `force`. 886 * 887 * For browsers that support classList, but do not support `force` yet, 888 * the mistake will be detected and corrected. 889 * 890 * @param {String} name 891 * @param {Boolean} force 892 * @return {ClassList} 893 * @api public 894 */ 895 896 ClassList$1.prototype.toggle = function (name, force) { 897 // classList 898 if (this.list) { 899 if ('undefined' !== typeof force) { 900 if (force !== this.list.toggle(name, force)) { 901 this.list.toggle(name); // toggle again to correct 902 } 903 } else { 904 this.list.toggle(name); 905 } 906 return this; 907 } 908 909 // fallback 910 if ('undefined' !== typeof force) { 911 if (!force) { 912 this.remove(name); 913 } else { 914 this.add(name); 915 } 916 } else { 917 if (this.has(name)) { 918 this.remove(name); 919 } else { 920 this.add(name); 921 } 922 } 923 924 return this; 925 }; 926 927 /** 928 * Return an array of classes. 929 * 930 * @return {Array} 931 * @api public 932 */ 933 934 ClassList$1.prototype.array = function () { 935 var className = this.el.getAttribute('class') || ''; 936 var str = className.replace(/^\s+|\s+$/g, ''); 937 var arr = str.split(re$1); 938 if ('' === arr[0]) arr.shift(); 939 return arr; 940 }; 941 942 /** 943 * Check if class `name` is present. 944 * 945 * @param {String} name 946 * @return {ClassList} 947 * @api public 948 */ 949 950 ClassList$1.prototype.has = ClassList$1.prototype.contains = function (name) { 951 return this.list ? this.list.contains(name) : !!~indexof(this.array(), name); 952 }; 953 954 /** 955 * Remove all children from the given element. 956 */ 957 function clear$1(el) { 958 959 var c; 960 961 while (el.childNodes.length) { 962 c = el.childNodes[0]; 963 el.removeChild(c); 964 } 965 966 return el; 967 } 968 969 var proto = typeof Element !== 'undefined' ? Element.prototype : {}; 970 var vendor = proto.matches 971 || proto.matchesSelector 972 || proto.webkitMatchesSelector 973 || proto.mozMatchesSelector 974 || proto.msMatchesSelector 975 || proto.oMatchesSelector; 976 977 var matchesSelector = match; 978 979 /** 980 * Match `el` to `selector`. 981 * 982 * @param {Element} el 983 * @param {String} selector 984 * @return {Boolean} 985 * @api public 986 */ 987 988 function match(el, selector) { 989 if (!el || el.nodeType !== 1) return false; 990 if (vendor) return vendor.call(el, selector); 991 var nodes = el.parentNode.querySelectorAll(selector); 992 for (var i = 0; i < nodes.length; i++) { 993 if (nodes[i] == el) return true; 994 } 995 return false; 996 } 997 998 /** 999 * Closest 1000 * 1001 * @param {Element} el 1002 * @param {String} selector 1003 * @param {Boolean} checkYourSelf (optional) 1004 */ 1005 function closest (element, selector, checkYourSelf) { 1006 var currentElem = checkYourSelf ? element : element.parentNode; 1007 1008 while (currentElem && currentElem.nodeType !== document.DOCUMENT_NODE && currentElem.nodeType !== document.DOCUMENT_FRAGMENT_NODE) { 1009 1010 if (matchesSelector(currentElem, selector)) { 1011 return currentElem; 1012 } 1013 1014 currentElem = currentElem.parentNode; 1015 } 1016 1017 return matchesSelector(currentElem, selector) ? currentElem : null; 1018 } 1019 1020 var bind = window.addEventListener ? 'addEventListener' : 'attachEvent', 1021 unbind = window.removeEventListener ? 'removeEventListener' : 'detachEvent', 1022 prefix$6 = bind !== 'addEventListener' ? 'on' : ''; 1023 1024 /** 1025 * Bind `el` event `type` to `fn`. 1026 * 1027 * @param {Element} el 1028 * @param {String} type 1029 * @param {Function} fn 1030 * @param {Boolean} capture 1031 * @return {Function} 1032 * @api public 1033 */ 1034 1035 var bind_1 = function(el, type, fn, capture){ 1036 el[bind](prefix$6 + type, fn, capture || false); 1037 return fn; 1038 }; 1039 1040 /** 1041 * Unbind `el` event `type`'s callback `fn`. 1042 * 1043 * @param {Element} el 1044 * @param {String} type 1045 * @param {Function} fn 1046 * @param {Boolean} capture 1047 * @return {Function} 1048 * @api public 1049 */ 1050 1051 var unbind_1 = function(el, type, fn, capture){ 1052 el[unbind](prefix$6 + type, fn, capture || false); 1053 return fn; 1054 }; 1055 1056 var componentEvent = { 1057 bind: bind_1, 1058 unbind: unbind_1 1059 }; 1060 1061 /** 1062 * Module dependencies. 1063 */ 1064 1065 /** 1066 * Delegate event `type` to `selector` 1067 * and invoke `fn(e)`. A callback function 1068 * is returned which may be passed to `.unbind()`. 1069 * 1070 * @param {Element} el 1071 * @param {String} selector 1072 * @param {String} type 1073 * @param {Function} fn 1074 * @param {Boolean} capture 1075 * @return {Function} 1076 * @api public 1077 */ 1078 1079 // Some events don't bubble, so we want to bind to the capture phase instead 1080 // when delegating. 1081 var forceCaptureEvents = ['focus', 'blur']; 1082 1083 function bind$1(el, selector, type, fn, capture) { 1084 if (forceCaptureEvents.indexOf(type) !== -1) { 1085 capture = true; 1086 } 1087 1088 return componentEvent.bind(el, type, function (e) { 1089 var target = e.target || e.srcElement; 1090 e.delegateTarget = closest(target, selector, true); 1091 if (e.delegateTarget) { 1092 fn.call(el, e); 1093 } 1094 }, capture); 1095 } 1096 1097 /** 1098 * Unbind event `type`'s callback `fn`. 1099 * 1100 * @param {Element} el 1101 * @param {String} type 1102 * @param {Function} fn 1103 * @param {Boolean} capture 1104 * @api public 1105 */ 1106 function unbind$1(el, type, fn, capture) { 1107 if (forceCaptureEvents.indexOf(type) !== -1) { 1108 capture = true; 1109 } 1110 1111 return componentEvent.unbind(el, type, fn, capture); 1112 } 1113 1114 var delegate = { 1115 bind: bind$1, 1116 unbind: unbind$1 1117 }; 1118 1119 /** 1120 * Expose `parse`. 1121 */ 1122 1123 var domify = parse$1; 1124 1125 /** 1126 * Tests for browser support. 1127 */ 1128 1129 var innerHTMLBug = false; 1130 var bugTestDiv; 1131 if (typeof document !== 'undefined') { 1132 bugTestDiv = document.createElement('div'); 1133 // Setup 1134 bugTestDiv.innerHTML = ' <link/><table></table><a href="/a">a</a><input type="checkbox"/>'; 1135 // Make sure that link elements get serialized correctly by innerHTML 1136 // This requires a wrapper element in IE 1137 innerHTMLBug = !bugTestDiv.getElementsByTagName('link').length; 1138 bugTestDiv = undefined; 1139 } 1140 1141 /** 1142 * Wrap map from jquery. 1143 */ 1144 1145 var map = { 1146 legend: [1, '<fieldset>', '</fieldset>'], 1147 tr: [2, '<table><tbody>', '</tbody></table>'], 1148 col: [2, '<table><tbody></tbody><colgroup>', '</colgroup></table>'], 1149 // for script/link/style tags to work in IE6-8, you have to wrap 1150 // in a div with a non-whitespace character in front, ha! 1151 _default: innerHTMLBug ? [1, 'X<div>', '</div>'] : [0, '', ''] 1152 }; 1153 1154 map.td = 1155 map.th = [3, '<table><tbody><tr>', '</tr></tbody></table>']; 1156 1157 map.option = 1158 map.optgroup = [1, '<select multiple="multiple">', '</select>']; 1159 1160 map.thead = 1161 map.tbody = 1162 map.colgroup = 1163 map.caption = 1164 map.tfoot = [1, '<table>', '</table>']; 1165 1166 map.polyline = 1167 map.ellipse = 1168 map.polygon = 1169 map.circle = 1170 map.text = 1171 map.line = 1172 map.path = 1173 map.rect = 1174 map.g = [1, '<svg xmlns="http://www.w3.org/2000/svg" version="1.1">','</svg>']; 1175 1176 /** 1177 * Parse `html` and return a DOM Node instance, which could be a TextNode, 1178 * HTML DOM Node of some kind (<div> for example), or a DocumentFragment 1179 * instance, depending on the contents of the `html` string. 1180 * 1181 * @param {String} html - HTML string to "domify" 1182 * @param {Document} doc - The `document` instance to create the Node for 1183 * @return {DOMNode} the TextNode, DOM Node, or DocumentFragment instance 1184 * @api private 1185 */ 1186 1187 function parse$1(html, doc) { 1188 if ('string' != typeof html) throw new TypeError('String expected'); 1189 1190 // default to the global `document` object 1191 if (!doc) doc = document; 1192 1193 // tag name 1194 var m = /<([\w:]+)/.exec(html); 1195 if (!m) return doc.createTextNode(html); 1196 1197 html = html.replace(/^\s+|\s+$/g, ''); // Remove leading/trailing whitespace 1198 1199 var tag = m[1]; 1200 1201 // body support 1202 if (tag == 'body') { 1203 var el = doc.createElement('html'); 1204 el.innerHTML = html; 1205 return el.removeChild(el.lastChild); 1206 } 1207 1208 // wrap map 1209 var wrap = map[tag] || map._default; 1210 var depth = wrap[0]; 1211 var prefix = wrap[1]; 1212 var suffix = wrap[2]; 1213 var el = doc.createElement('div'); 1214 el.innerHTML = prefix + html + suffix; 1215 while (depth--) el = el.lastChild; 1216 1217 // one element 1218 if (el.firstChild == el.lastChild) { 1219 return el.removeChild(el.firstChild); 1220 } 1221 1222 // several elements 1223 var fragment = doc.createDocumentFragment(); 1224 while (el.firstChild) { 1225 fragment.appendChild(el.removeChild(el.firstChild)); 1226 } 1227 1228 return fragment; 1229 } 1230 1231 function query(selector, el) { 1232 el = el || document; 1233 1234 return el.querySelector(selector); 1235 } 1236 1237 function all(selector, el) { 1238 el = el || document; 1239 1240 return el.querySelectorAll(selector); 1241 } 1242 1243 function remove$2(el) { 1244 el.parentNode && el.parentNode.removeChild(el); 1245 } 1246 1247 function ensureImported(element, target) { 1248 1249 if (element.ownerDocument !== target.ownerDocument) { 1250 try { 1251 // may fail on webkit 1252 return target.ownerDocument.importNode(element, true); 1253 } catch (e) { 1254 // ignore 1255 } 1256 } 1257 1258 return element; 1259 } 1260 1261 /** 1262 * appendTo utility 1263 */ 1264 1265 /** 1266 * Append a node to a target element and return the appended node. 1267 * 1268 * @param {SVGElement} element 1269 * @param {SVGElement} target 1270 * 1271 * @return {SVGElement} the appended node 1272 */ 1273 function appendTo(element, target) { 1274 return target.appendChild(ensureImported(element, target)); 1275 } 1276 1277 /** 1278 * append utility 1279 */ 1280 1281 /** 1282 * Append a node to an element 1283 * 1284 * @param {SVGElement} element 1285 * @param {SVGElement} node 1286 * 1287 * @return {SVGElement} the element 1288 */ 1289 function append(target, node) { 1290 appendTo(node, target); 1291 return target; 1292 } 1293 1294 /** 1295 * attribute accessor utility 1296 */ 1297 1298 var LENGTH_ATTR = 2; 1299 1300 var CSS_PROPERTIES = { 1301 'alignment-baseline': 1, 1302 'baseline-shift': 1, 1303 'clip': 1, 1304 'clip-path': 1, 1305 'clip-rule': 1, 1306 'color': 1, 1307 'color-interpolation': 1, 1308 'color-interpolation-filters': 1, 1309 'color-profile': 1, 1310 'color-rendering': 1, 1311 'cursor': 1, 1312 'direction': 1, 1313 'display': 1, 1314 'dominant-baseline': 1, 1315 'enable-background': 1, 1316 'fill': 1, 1317 'fill-opacity': 1, 1318 'fill-rule': 1, 1319 'filter': 1, 1320 'flood-color': 1, 1321 'flood-opacity': 1, 1322 'font': 1, 1323 'font-family': 1, 1324 'font-size': LENGTH_ATTR, 1325 'font-size-adjust': 1, 1326 'font-stretch': 1, 1327 'font-style': 1, 1328 'font-variant': 1, 1329 'font-weight': 1, 1330 'glyph-orientation-horizontal': 1, 1331 'glyph-orientation-vertical': 1, 1332 'image-rendering': 1, 1333 'kerning': 1, 1334 'letter-spacing': 1, 1335 'lighting-color': 1, 1336 'marker': 1, 1337 'marker-end': 1, 1338 'marker-mid': 1, 1339 'marker-start': 1, 1340 'mask': 1, 1341 'opacity': 1, 1342 'overflow': 1, 1343 'pointer-events': 1, 1344 'shape-rendering': 1, 1345 'stop-color': 1, 1346 'stop-opacity': 1, 1347 'stroke': 1, 1348 'stroke-dasharray': 1, 1349 'stroke-dashoffset': 1, 1350 'stroke-linecap': 1, 1351 'stroke-linejoin': 1, 1352 'stroke-miterlimit': 1, 1353 'stroke-opacity': 1, 1354 'stroke-width': LENGTH_ATTR, 1355 'text-anchor': 1, 1356 'text-decoration': 1, 1357 'text-rendering': 1, 1358 'unicode-bidi': 1, 1359 'visibility': 1, 1360 'word-spacing': 1, 1361 'writing-mode': 1 1362 }; 1363 1364 1365 function getAttribute(node, name) { 1366 if (CSS_PROPERTIES[name]) { 1367 return node.style[name]; 1368 } else { 1369 return node.getAttributeNS(null, name); 1370 } 1371 } 1372 1373 function setAttribute(node, name, value) { 1374 var hyphenated = name.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase(); 1375 1376 var type = CSS_PROPERTIES[hyphenated]; 1377 1378 if (type) { 1379 // append pixel unit, unless present 1380 if (type === LENGTH_ATTR && typeof value === 'number') { 1381 value = String(value) + 'px'; 1382 } 1383 1384 node.style[hyphenated] = value; 1385 } else { 1386 node.setAttributeNS(null, name, value); 1387 } 1388 } 1389 1390 function setAttributes(node, attrs) { 1391 1392 var names = Object.keys(attrs), i, name; 1393 1394 for (i = 0, name; (name = names[i]); i++) { 1395 setAttribute(node, name, attrs[name]); 1396 } 1397 } 1398 1399 /** 1400 * Gets or sets raw attributes on a node. 1401 * 1402 * @param {SVGElement} node 1403 * @param {Object} [attrs] 1404 * @param {String} [name] 1405 * @param {String} [value] 1406 * 1407 * @return {String} 1408 */ 1409 function attr(node, name, value) { 1410 if (typeof name === 'string') { 1411 if (value !== undefined) { 1412 setAttribute(node, name, value); 1413 } else { 1414 return getAttribute(node, name); 1415 } 1416 } else { 1417 setAttributes(node, name); 1418 } 1419 1420 return node; 1421 } 1422 1423 /** 1424 * Clear utility 1425 */ 1426 function index(arr, obj) { 1427 if (arr.indexOf) { 1428 return arr.indexOf(obj); 1429 } 1430 1431 1432 for (var i = 0; i < arr.length; ++i) { 1433 if (arr[i] === obj) { 1434 return i; 1435 } 1436 } 1437 1438 return -1; 1439 } 1440 1441 var re = /\s+/; 1442 1443 var toString = Object.prototype.toString; 1444 1445 function defined(o) { 1446 return typeof o !== 'undefined'; 1447 } 1448 1449 /** 1450 * Wrap `el` in a `ClassList`. 1451 * 1452 * @param {Element} el 1453 * @return {ClassList} 1454 * @api public 1455 */ 1456 1457 function classes(el) { 1458 return new ClassList(el); 1459 } 1460 1461 function ClassList(el) { 1462 if (!el || !el.nodeType) { 1463 throw new Error('A DOM element reference is required'); 1464 } 1465 this.el = el; 1466 this.list = el.classList; 1467 } 1468 1469 /** 1470 * Add class `name` if not already present. 1471 * 1472 * @param {String} name 1473 * @return {ClassList} 1474 * @api public 1475 */ 1476 1477 ClassList.prototype.add = function(name) { 1478 1479 // classList 1480 if (this.list) { 1481 this.list.add(name); 1482 return this; 1483 } 1484 1485 // fallback 1486 var arr = this.array(); 1487 var i = index(arr, name); 1488 if (!~i) { 1489 arr.push(name); 1490 } 1491 1492 if (defined(this.el.className.baseVal)) { 1493 this.el.className.baseVal = arr.join(' '); 1494 } else { 1495 this.el.className = arr.join(' '); 1496 } 1497 1498 return this; 1499 }; 1500 1501 /** 1502 * Remove class `name` when present, or 1503 * pass a regular expression to remove 1504 * any which match. 1505 * 1506 * @param {String|RegExp} name 1507 * @return {ClassList} 1508 * @api public 1509 */ 1510 1511 ClassList.prototype.remove = function(name) { 1512 if ('[object RegExp]' === toString.call(name)) { 1513 return this.removeMatching(name); 1514 } 1515 1516 // classList 1517 if (this.list) { 1518 this.list.remove(name); 1519 return this; 1520 } 1521 1522 // fallback 1523 var arr = this.array(); 1524 var i = index(arr, name); 1525 if (~i) { 1526 arr.splice(i, 1); 1527 } 1528 this.el.className.baseVal = arr.join(' '); 1529 return this; 1530 }; 1531 1532 /** 1533 * Remove all classes matching `re`. 1534 * 1535 * @param {RegExp} re 1536 * @return {ClassList} 1537 * @api private 1538 */ 1539 1540 ClassList.prototype.removeMatching = function(re) { 1541 var arr = this.array(); 1542 for (var i = 0; i < arr.length; i++) { 1543 if (re.test(arr[i])) { 1544 this.remove(arr[i]); 1545 } 1546 } 1547 return this; 1548 }; 1549 1550 /** 1551 * Toggle class `name`, can force state via `force`. 1552 * 1553 * For browsers that support classList, but do not support `force` yet, 1554 * the mistake will be detected and corrected. 1555 * 1556 * @param {String} name 1557 * @param {Boolean} force 1558 * @return {ClassList} 1559 * @api public 1560 */ 1561 1562 ClassList.prototype.toggle = function(name, force) { 1563 // classList 1564 if (this.list) { 1565 if (defined(force)) { 1566 if (force !== this.list.toggle(name, force)) { 1567 this.list.toggle(name); // toggle again to correct 1568 } 1569 } else { 1570 this.list.toggle(name); 1571 } 1572 return this; 1573 } 1574 1575 // fallback 1576 if (defined(force)) { 1577 if (!force) { 1578 this.remove(name); 1579 } else { 1580 this.add(name); 1581 } 1582 } else { 1583 if (this.has(name)) { 1584 this.remove(name); 1585 } else { 1586 this.add(name); 1587 } 1588 } 1589 1590 return this; 1591 }; 1592 1593 /** 1594 * Return an array of classes. 1595 * 1596 * @return {Array} 1597 * @api public 1598 */ 1599 1600 ClassList.prototype.array = function() { 1601 var className = this.el.getAttribute('class') || ''; 1602 var str = className.replace(/^\s+|\s+$/g, ''); 1603 var arr = str.split(re); 1604 if ('' === arr[0]) { 1605 arr.shift(); 1606 } 1607 return arr; 1608 }; 1609 1610 /** 1611 * Check if class `name` is present. 1612 * 1613 * @param {String} name 1614 * @return {ClassList} 1615 * @api public 1616 */ 1617 1618 ClassList.prototype.has = 1619 ClassList.prototype.contains = function(name) { 1620 return ( 1621 this.list ? 1622 this.list.contains(name) : 1623 !! ~index(this.array(), name) 1624 ); 1625 }; 1626 1627 function remove$1(element) { 1628 var parent = element.parentNode; 1629 1630 if (parent) { 1631 parent.removeChild(element); 1632 } 1633 1634 return element; 1635 } 1636 1637 /** 1638 * Clear utility 1639 */ 1640 1641 /** 1642 * Removes all children from the given element 1643 * 1644 * @param {DOMElement} element 1645 * @return {DOMElement} the element (for chaining) 1646 */ 1647 function clear(element) { 1648 var child; 1649 1650 while ((child = element.firstChild)) { 1651 remove$1(child); 1652 } 1653 1654 return element; 1655 } 1656 1657 function clone$1(element) { 1658 return element.cloneNode(true); 1659 } 1660 1661 var ns = { 1662 svg: 'http://www.w3.org/2000/svg' 1663 }; 1664 1665 /** 1666 * DOM parsing utility 1667 */ 1668 1669 var SVG_START = '<svg xmlns="' + ns.svg + '"'; 1670 1671 function parse(svg) { 1672 1673 var unwrap = false; 1674 1675 // ensure we import a valid svg document 1676 if (svg.substring(0, 4) === '<svg') { 1677 if (svg.indexOf(ns.svg) === -1) { 1678 svg = SVG_START + svg.substring(4); 1679 } 1680 } else { 1681 // namespace svg 1682 svg = SVG_START + '>' + svg + '</svg>'; 1683 unwrap = true; 1684 } 1685 1686 var parsed = parseDocument(svg); 1687 1688 if (!unwrap) { 1689 return parsed; 1690 } 1691 1692 var fragment = document.createDocumentFragment(); 1693 1694 var parent = parsed.firstChild; 1695 1696 while (parent.firstChild) { 1697 fragment.appendChild(parent.firstChild); 1698 } 1699 1700 return fragment; 1701 } 1702 1703 function parseDocument(svg) { 1704 1705 var parser; 1706 1707 // parse 1708 parser = new DOMParser(); 1709 parser.async = false; 1710 1711 return parser.parseFromString(svg, 'text/xml'); 1712 } 1713 1714 /** 1715 * Create utility for SVG elements 1716 */ 1717 1718 1719 /** 1720 * Create a specific type from name or SVG markup. 1721 * 1722 * @param {String} name the name or markup of the element 1723 * @param {Object} [attrs] attributes to set on the element 1724 * 1725 * @returns {SVGElement} 1726 */ 1727 function create$1(name, attrs) { 1728 var element; 1729 1730 if (name.charAt(0) === '<') { 1731 element = parse(name).firstChild; 1732 element = document.importNode(element, true); 1733 } else { 1734 element = document.createElementNS(ns.svg, name); 1735 } 1736 1737 if (attrs) { 1738 attr(element, attrs); 1739 } 1740 1741 return element; 1742 } 1743 1744 /** 1745 * Geometry helpers 1746 */ 1747 1748 // fake node used to instantiate svg geometry elements 1749 var node = create$1('svg'); 1750 1751 function extend$1(object, props) { 1752 var i, k, keys = Object.keys(props); 1753 1754 for (i = 0; (k = keys[i]); i++) { 1755 object[k] = props[k]; 1756 } 1757 1758 return object; 1759 } 1760 1761 /** 1762 * Create matrix via args. 1763 * 1764 * @example 1765 * 1766 * createMatrix({ a: 1, b: 1 }); 1767 * createMatrix(); 1768 * createMatrix(1, 2, 0, 0, 30, 20); 1769 * 1770 * @return {SVGMatrix} 1771 */ 1772 function createMatrix(a, b, c, d, e, f) { 1773 var matrix = node.createSVGMatrix(); 1774 1775 switch (arguments.length) { 1776 case 0: 1777 return matrix; 1778 case 1: 1779 return extend$1(matrix, a); 1780 case 6: 1781 return extend$1(matrix, { 1782 a: a, 1783 b: b, 1784 c: c, 1785 d: d, 1786 e: e, 1787 f: f 1788 }); 1789 } 1790 } 1791 1792 function createTransform(matrix) { 1793 if (matrix) { 1794 return node.createSVGTransformFromMatrix(matrix); 1795 } else { 1796 return node.createSVGTransform(); 1797 } 1798 } 1799 1800 /** 1801 * Serialization util 1802 */ 1803 1804 var TEXT_ENTITIES = /([&<>]{1})/g; 1805 var ATTR_ENTITIES = /([\n\r"]{1})/g; 1806 1807 var ENTITY_REPLACEMENT = { 1808 '&': '&', 1809 '<': '<', 1810 '>': '>', 1811 '"': '\'' 1812 }; 1813 1814 function escape$1(str, pattern) { 1815 1816 function replaceFn(match, entity) { 1817 return ENTITY_REPLACEMENT[entity] || entity; 1818 } 1819 1820 return str.replace(pattern, replaceFn); 1821 } 1822 1823 function serialize(node, output) { 1824 1825 var i, len, attrMap, attrNode, childNodes; 1826 1827 switch (node.nodeType) { 1828 // TEXT 1829 case 3: 1830 // replace special XML characters 1831 output.push(escape$1(node.textContent, TEXT_ENTITIES)); 1832 break; 1833 1834 // ELEMENT 1835 case 1: 1836 output.push('<', node.tagName); 1837 1838 if (node.hasAttributes()) { 1839 attrMap = node.attributes; 1840 for (i = 0, len = attrMap.length; i < len; ++i) { 1841 attrNode = attrMap.item(i); 1842 output.push(' ', attrNode.name, '="', escape$1(attrNode.value, ATTR_ENTITIES), '"'); 1843 } 1844 } 1845 1846 if (node.hasChildNodes()) { 1847 output.push('>'); 1848 childNodes = node.childNodes; 1849 for (i = 0, len = childNodes.length; i < len; ++i) { 1850 serialize(childNodes.item(i), output); 1851 } 1852 output.push('</', node.tagName, '>'); 1853 } else { 1854 output.push('/>'); 1855 } 1856 break; 1857 1858 // COMMENT 1859 case 8: 1860 output.push('<!--', escape$1(node.nodeValue, TEXT_ENTITIES), '-->'); 1861 break; 1862 1863 // CDATA 1864 case 4: 1865 output.push('<![CDATA[', node.nodeValue, ']]>'); 1866 break; 1867 1868 default: 1869 throw new Error('unable to handle node ' + node.nodeType); 1870 } 1871 1872 return output; 1873 } 1874 1875 /** 1876 * innerHTML like functionality for SVG elements. 1877 * based on innerSVG (https://code.google.com/p/innersvg) 1878 */ 1879 1880 1881 function set$1(element, svg) { 1882 1883 var parsed = parse(svg); 1884 1885 // clear element contents 1886 clear(element); 1887 1888 if (!svg) { 1889 return; 1890 } 1891 1892 if (!isFragment(parsed)) { 1893 // extract <svg> from parsed document 1894 parsed = parsed.documentElement; 1895 } 1896 1897 var nodes = slice$1(parsed.childNodes); 1898 1899 // import + append each node 1900 for (var i = 0; i < nodes.length; i++) { 1901 appendTo(nodes[i], element); 1902 } 1903 1904 } 1905 1906 function get$1(element) { 1907 var child = element.firstChild, 1908 output = []; 1909 1910 while (child) { 1911 serialize(child, output); 1912 child = child.nextSibling; 1913 } 1914 1915 return output.join(''); 1916 } 1917 1918 function isFragment(node) { 1919 return node.nodeName === '#document-fragment'; 1920 } 1921 1922 function innerSVG(element, svg) { 1923 1924 if (svg !== undefined) { 1925 1926 try { 1927 set$1(element, svg); 1928 } catch (e) { 1929 throw new Error('error parsing SVG: ' + e.message); 1930 } 1931 1932 return element; 1933 } else { 1934 return get$1(element); 1935 } 1936 } 1937 1938 1939 function slice$1(arr) { 1940 return Array.prototype.slice.call(arr); 1941 } 1942 1943 /** 1944 * transform accessor utility 1945 */ 1946 1947 function wrapMatrix(transformList, transform) { 1948 if (transform instanceof SVGMatrix) { 1949 return transformList.createSVGTransformFromMatrix(transform); 1950 } 1951 1952 return transform; 1953 } 1954 1955 1956 function setTransforms(transformList, transforms) { 1957 var i, t; 1958 1959 transformList.clear(); 1960 1961 for (i = 0; (t = transforms[i]); i++) { 1962 transformList.appendItem(wrapMatrix(transformList, t)); 1963 } 1964 } 1965 1966 /** 1967 * Get or set the transforms on the given node. 1968 * 1969 * @param {SVGElement} node 1970 * @param {SVGTransform|SVGMatrix|Array<SVGTransform|SVGMatrix>} [transforms] 1971 * 1972 * @return {SVGTransform} the consolidated transform 1973 */ 1974 function transform$1(node, transforms) { 1975 var transformList = node.transform.baseVal; 1976 1977 if (transforms) { 1978 1979 if (!Array.isArray(transforms)) { 1980 transforms = [ transforms ]; 1981 } 1982 1983 setTransforms(transformList, transforms); 1984 } 1985 1986 return transformList.consolidate(); 1987 } 1988 1989 var CLASS_PATTERN = /^class /; 1990 1991 function isClass(fn) { 1992 return CLASS_PATTERN.test(fn.toString()); 1993 } 1994 1995 function isArray$1(obj) { 1996 return Object.prototype.toString.call(obj) === '[object Array]'; 1997 } 1998 1999 function hasOwnProp(obj, prop) { 2000 return Object.prototype.hasOwnProperty.call(obj, prop); 2001 } 2002 2003 function annotate() { 2004 var args = Array.prototype.slice.call(arguments); 2005 2006 if (args.length === 1 && isArray$1(args[0])) { 2007 args = args[0]; 2008 } 2009 2010 var fn = args.pop(); 2011 2012 fn.$inject = args; 2013 2014 return fn; 2015 } 2016 2017 2018 // Current limitations: 2019 // - can't put into "function arg" comments 2020 // function /* (no parenthesis like this) */ (){} 2021 // function abc( /* xx (no parenthesis like this) */ a, b) {} 2022 // 2023 // Just put the comment before function or inside: 2024 // /* (((this is fine))) */ function(a, b) {} 2025 // function abc(a) { /* (((this is fine))) */} 2026 // 2027 // - can't reliably auto-annotate constructor; we'll match the 2028 // first constructor(...) pattern found which may be the one 2029 // of a nested class, too. 2030 2031 var CONSTRUCTOR_ARGS = /constructor\s*[^(]*\(\s*([^)]*)\)/m; 2032 var FN_ARGS = /^(?:async )?(?:function\s*)?[^(]*\(\s*([^)]*)\)/m; 2033 var FN_ARG = /\/\*([^*]*)\*\//m; 2034 2035 function parseAnnotations(fn) { 2036 2037 if (typeof fn !== 'function') { 2038 throw new Error('Cannot annotate "' + fn + '". Expected a function!'); 2039 } 2040 2041 var match = fn.toString().match(isClass(fn) ? CONSTRUCTOR_ARGS : FN_ARGS); 2042 2043 // may parse class without constructor 2044 if (!match) { 2045 return []; 2046 } 2047 2048 return match[1] && match[1].split(',').map(function(arg) { 2049 match = arg.match(FN_ARG); 2050 return match ? match[1].trim() : arg.trim(); 2051 }) || []; 2052 } 2053 2054 function Module() { 2055 var providers = []; 2056 2057 this.factory = function(name, factory) { 2058 providers.push([name, 'factory', factory]); 2059 return this; 2060 }; 2061 2062 this.value = function(name, value) { 2063 providers.push([name, 'value', value]); 2064 return this; 2065 }; 2066 2067 this.type = function(name, type) { 2068 providers.push([name, 'type', type]); 2069 return this; 2070 }; 2071 2072 this.forEach = function(iterator) { 2073 providers.forEach(iterator); 2074 }; 2075 2076 } 2077 2078 function Injector(modules, parent) { 2079 parent = parent || { 2080 get: function(name, strict) { 2081 currentlyResolving.push(name); 2082 2083 if (strict === false) { 2084 return null; 2085 } else { 2086 throw error('No provider for "' + name + '"!'); 2087 } 2088 } 2089 }; 2090 2091 var currentlyResolving = []; 2092 var providers = this._providers = Object.create(parent._providers || null); 2093 var instances = this._instances = Object.create(null); 2094 2095 var self = instances.injector = this; 2096 2097 var error = function(msg) { 2098 var stack = currentlyResolving.join(' -> '); 2099 currentlyResolving.length = 0; 2100 return new Error(stack ? msg + ' (Resolving: ' + stack + ')' : msg); 2101 }; 2102 2103 /** 2104 * Return a named service. 2105 * 2106 * @param {String} name 2107 * @param {Boolean} [strict=true] if false, resolve missing services to null 2108 * 2109 * @return {Object} 2110 */ 2111 var get = function(name, strict) { 2112 if (!providers[name] && name.indexOf('.') !== -1) { 2113 var parts = name.split('.'); 2114 var pivot = get(parts.shift()); 2115 2116 while (parts.length) { 2117 pivot = pivot[parts.shift()]; 2118 } 2119 2120 return pivot; 2121 } 2122 2123 if (hasOwnProp(instances, name)) { 2124 return instances[name]; 2125 } 2126 2127 if (hasOwnProp(providers, name)) { 2128 if (currentlyResolving.indexOf(name) !== -1) { 2129 currentlyResolving.push(name); 2130 throw error('Cannot resolve circular dependency!'); 2131 } 2132 2133 currentlyResolving.push(name); 2134 instances[name] = providers[name][0](providers[name][1]); 2135 currentlyResolving.pop(); 2136 2137 return instances[name]; 2138 } 2139 2140 return parent.get(name, strict); 2141 }; 2142 2143 var fnDef = function(fn, locals) { 2144 2145 if (typeof locals === 'undefined') { 2146 locals = {}; 2147 } 2148 2149 if (typeof fn !== 'function') { 2150 if (isArray$1(fn)) { 2151 fn = annotate(fn.slice()); 2152 } else { 2153 throw new Error('Cannot invoke "' + fn + '". Expected a function!'); 2154 } 2155 } 2156 2157 var inject = fn.$inject || parseAnnotations(fn); 2158 var dependencies = inject.map(function(dep) { 2159 if (hasOwnProp(locals, dep)) { 2160 return locals[dep]; 2161 } else { 2162 return get(dep); 2163 } 2164 }); 2165 2166 return { 2167 fn: fn, 2168 dependencies: dependencies 2169 }; 2170 }; 2171 2172 var instantiate = function(Type) { 2173 var def = fnDef(Type); 2174 2175 var fn = def.fn, 2176 dependencies = def.dependencies; 2177 2178 // instantiate var args constructor 2179 var Constructor = Function.prototype.bind.apply(fn, [ null ].concat(dependencies)); 2180 2181 return new Constructor(); 2182 }; 2183 2184 var invoke = function(func, context, locals) { 2185 var def = fnDef(func, locals); 2186 2187 var fn = def.fn, 2188 dependencies = def.dependencies; 2189 2190 return fn.apply(context, dependencies); 2191 }; 2192 2193 2194 var createPrivateInjectorFactory = function(privateChildInjector) { 2195 return annotate(function(key) { 2196 return privateChildInjector.get(key); 2197 }); 2198 }; 2199 2200 var createChild = function(modules, forceNewInstances) { 2201 if (forceNewInstances && forceNewInstances.length) { 2202 var fromParentModule = Object.create(null); 2203 var matchedScopes = Object.create(null); 2204 2205 var privateInjectorsCache = []; 2206 var privateChildInjectors = []; 2207 var privateChildFactories = []; 2208 2209 var provider; 2210 var cacheIdx; 2211 var privateChildInjector; 2212 var privateChildInjectorFactory; 2213 for (var name in providers) { 2214 provider = providers[name]; 2215 2216 if (forceNewInstances.indexOf(name) !== -1) { 2217 if (provider[2] === 'private') { 2218 cacheIdx = privateInjectorsCache.indexOf(provider[3]); 2219 if (cacheIdx === -1) { 2220 privateChildInjector = provider[3].createChild([], forceNewInstances); 2221 privateChildInjectorFactory = createPrivateInjectorFactory(privateChildInjector); 2222 privateInjectorsCache.push(provider[3]); 2223 privateChildInjectors.push(privateChildInjector); 2224 privateChildFactories.push(privateChildInjectorFactory); 2225 fromParentModule[name] = [privateChildInjectorFactory, name, 'private', privateChildInjector]; 2226 } else { 2227 fromParentModule[name] = [privateChildFactories[cacheIdx], name, 'private', privateChildInjectors[cacheIdx]]; 2228 } 2229 } else { 2230 fromParentModule[name] = [provider[2], provider[1]]; 2231 } 2232 matchedScopes[name] = true; 2233 } 2234 2235 if ((provider[2] === 'factory' || provider[2] === 'type') && provider[1].$scope) { 2236 /* jshint -W083 */ 2237 forceNewInstances.forEach(function(scope) { 2238 if (provider[1].$scope.indexOf(scope) !== -1) { 2239 fromParentModule[name] = [provider[2], provider[1]]; 2240 matchedScopes[scope] = true; 2241 } 2242 }); 2243 } 2244 } 2245 2246 forceNewInstances.forEach(function(scope) { 2247 if (!matchedScopes[scope]) { 2248 throw new Error('No provider for "' + scope + '". Cannot use provider from the parent!'); 2249 } 2250 }); 2251 2252 modules.unshift(fromParentModule); 2253 } 2254 2255 return new Injector(modules, self); 2256 }; 2257 2258 var factoryMap = { 2259 factory: invoke, 2260 type: instantiate, 2261 value: function(value) { 2262 return value; 2263 } 2264 }; 2265 2266 modules.forEach(function(module) { 2267 2268 function arrayUnwrap(type, value) { 2269 if (type !== 'value' && isArray$1(value)) { 2270 value = annotate(value.slice()); 2271 } 2272 2273 return value; 2274 } 2275 2276 // TODO(vojta): handle wrong inputs (modules) 2277 if (module instanceof Module) { 2278 module.forEach(function(provider) { 2279 var name = provider[0]; 2280 var type = provider[1]; 2281 var value = provider[2]; 2282 2283 providers[name] = [factoryMap[type], arrayUnwrap(type, value), type]; 2284 }); 2285 } else if (typeof module === 'object') { 2286 if (module.__exports__) { 2287 var clonedModule = Object.keys(module).reduce(function(m, key) { 2288 if (key.substring(0, 2) !== '__') { 2289 m[key] = module[key]; 2290 } 2291 return m; 2292 }, Object.create(null)); 2293 2294 var privateInjector = new Injector((module.__modules__ || []).concat([clonedModule]), self); 2295 var getFromPrivateInjector = annotate(function(key) { 2296 return privateInjector.get(key); 2297 }); 2298 module.__exports__.forEach(function(key) { 2299 providers[key] = [getFromPrivateInjector, key, 'private', privateInjector]; 2300 }); 2301 } else { 2302 Object.keys(module).forEach(function(name) { 2303 if (module[name][2] === 'private') { 2304 providers[name] = module[name]; 2305 return; 2306 } 2307 2308 var type = module[name][0]; 2309 var value = module[name][1]; 2310 2311 providers[name] = [factoryMap[type], arrayUnwrap(type, value), type]; 2312 }); 2313 } 2314 } 2315 }); 2316 2317 // public API 2318 this.get = get; 2319 this.invoke = invoke; 2320 this.instantiate = instantiate; 2321 this.createChild = createChild; 2322 } 2323 2324 var DEFAULT_RENDER_PRIORITY$1 = 1000; 2325 2326 /** 2327 * The base implementation of shape and connection renderers. 2328 * 2329 * @param {EventBus} eventBus 2330 * @param {number} [renderPriority=1000] 2331 */ 2332 function BaseRenderer(eventBus, renderPriority) { 2333 var self = this; 2334 2335 renderPriority = renderPriority || DEFAULT_RENDER_PRIORITY$1; 2336 2337 eventBus.on([ 'render.shape', 'render.connection' ], renderPriority, function(evt, context) { 2338 var type = evt.type, 2339 element = context.element, 2340 visuals = context.gfx; 2341 2342 if (self.canRender(element)) { 2343 if (type === 'render.shape') { 2344 return self.drawShape(visuals, element); 2345 } else { 2346 return self.drawConnection(visuals, element); 2347 } 2348 } 2349 }); 2350 2351 eventBus.on([ 'render.getShapePath', 'render.getConnectionPath'], renderPriority, function(evt, element) { 2352 if (self.canRender(element)) { 2353 if (evt.type === 'render.getShapePath') { 2354 return self.getShapePath(element); 2355 } else { 2356 return self.getConnectionPath(element); 2357 } 2358 } 2359 }); 2360 } 2361 2362 /** 2363 * Should check whether *this* renderer can render 2364 * the element/connection. 2365 * 2366 * @param {element} element 2367 * 2368 * @returns {boolean} 2369 */ 2370 BaseRenderer.prototype.canRender = function() {}; 2371 2372 /** 2373 * Provides the shape's snap svg element to be drawn on the `canvas`. 2374 * 2375 * @param {djs.Graphics} visuals 2376 * @param {Shape} shape 2377 * 2378 * @returns {Snap.svg} [returns a Snap.svg paper element ] 2379 */ 2380 BaseRenderer.prototype.drawShape = function() {}; 2381 2382 /** 2383 * Provides the shape's snap svg element to be drawn on the `canvas`. 2384 * 2385 * @param {djs.Graphics} visuals 2386 * @param {Connection} connection 2387 * 2388 * @returns {Snap.svg} [returns a Snap.svg paper element ] 2389 */ 2390 BaseRenderer.prototype.drawConnection = function() {}; 2391 2392 /** 2393 * Gets the SVG path of a shape that represents it's visual bounds. 2394 * 2395 * @param {Shape} shape 2396 * 2397 * @return {string} svg path 2398 */ 2399 BaseRenderer.prototype.getShapePath = function() {}; 2400 2401 /** 2402 * Gets the SVG path of a connection that represents it's visual bounds. 2403 * 2404 * @param {Connection} connection 2405 * 2406 * @return {string} svg path 2407 */ 2408 BaseRenderer.prototype.getConnectionPath = function() {}; 2409 2410 function componentsToPath(elements) { 2411 return elements.join(',').replace(/,?([A-z]),?/g, '$1'); 2412 } 2413 2414 function toSVGPoints(points) { 2415 var result = ''; 2416 2417 for (var i = 0, p; (p = points[i]); i++) { 2418 result += p.x + ',' + p.y + ' '; 2419 } 2420 2421 return result; 2422 } 2423 2424 function createLine(points, attrs) { 2425 2426 var line = create$1('polyline'); 2427 attr(line, { points: toSVGPoints(points) }); 2428 2429 if (attrs) { 2430 attr(line, attrs); 2431 } 2432 2433 return line; 2434 } 2435 2436 function updateLine(gfx, points) { 2437 attr(gfx, { points: toSVGPoints(points) }); 2438 2439 return gfx; 2440 } 2441 2442 /** 2443 * Get parent elements. 2444 * 2445 * @param {Array<djs.model.base>} elements 2446 * 2447 * @returns {Array<djs.model.Base>} 2448 */ 2449 function getParents$1(elements) { 2450 2451 // find elements that are not children of any other elements 2452 return filter(elements, function(element) { 2453 return !find(elements, function(e) { 2454 return e !== element && getParent$1(element, e); 2455 }); 2456 }); 2457 } 2458 2459 2460 function getParent$1(element, parent) { 2461 if (!parent) { 2462 return; 2463 } 2464 2465 if (element === parent) { 2466 return parent; 2467 } 2468 2469 if (!element.parent) { 2470 return; 2471 } 2472 2473 return getParent$1(element.parent, parent); 2474 } 2475 2476 2477 /** 2478 * Adds an element to a collection and returns true if the 2479 * element was added. 2480 * 2481 * @param {Array<Object>} elements 2482 * @param {Object} e 2483 * @param {boolean} unique 2484 */ 2485 function add$1(elements, e, unique) { 2486 var canAdd = !unique || elements.indexOf(e) === -1; 2487 2488 if (canAdd) { 2489 elements.push(e); 2490 } 2491 2492 return canAdd; 2493 } 2494 2495 2496 /** 2497 * Iterate over each element in a collection, calling the iterator function `fn` 2498 * with (element, index, recursionDepth). 2499 * 2500 * Recurse into all elements that are returned by `fn`. 2501 * 2502 * @param {Object|Array<Object>} elements 2503 * @param {Function} fn iterator function called with (element, index, recursionDepth) 2504 * @param {number} [depth] maximum recursion depth 2505 */ 2506 function eachElement(elements, fn, depth) { 2507 2508 depth = depth || 0; 2509 2510 if (!isArray$2(elements)) { 2511 elements = [ elements ]; 2512 } 2513 2514 forEach(elements, function(s, i) { 2515 var filter = fn(s, i, depth); 2516 2517 if (isArray$2(filter) && filter.length) { 2518 eachElement(filter, fn, depth + 1); 2519 } 2520 }); 2521 } 2522 2523 2524 /** 2525 * Collects self + child elements up to a given depth from a list of elements. 2526 * 2527 * @param {djs.model.Base|Array<djs.model.Base>} elements the elements to select the children from 2528 * @param {boolean} unique whether to return a unique result set (no duplicates) 2529 * @param {number} maxDepth the depth to search through or -1 for infinite 2530 * 2531 * @return {Array<djs.model.Base>} found elements 2532 */ 2533 function selfAndChildren(elements, unique, maxDepth) { 2534 var result = [], 2535 processedChildren = []; 2536 2537 eachElement(elements, function(element, i, depth) { 2538 add$1(result, element, unique); 2539 2540 var children = element.children; 2541 2542 // max traversal depth not reached yet 2543 if (maxDepth === -1 || depth < maxDepth) { 2544 2545 // children exist && children not yet processed 2546 if (children && add$1(processedChildren, children, unique)) { 2547 return children; 2548 } 2549 } 2550 }); 2551 2552 return result; 2553 } 2554 2555 2556 /** 2557 * Return self + ALL children for a number of elements 2558 * 2559 * @param {Array<djs.model.Base>} elements to query 2560 * @param {boolean} allowDuplicates to allow duplicates in the result set 2561 * 2562 * @return {Array<djs.model.Base>} the collected elements 2563 */ 2564 function selfAndAllChildren(elements, allowDuplicates) { 2565 return selfAndChildren(elements, !allowDuplicates, -1); 2566 } 2567 2568 2569 /** 2570 * Gets the the closure for all selected elements, 2571 * their enclosed children and connections. 2572 * 2573 * @param {Array<djs.model.Base>} elements 2574 * @param {boolean} [isTopLevel=true] 2575 * @param {Object} [existingClosure] 2576 * 2577 * @return {Object} newClosure 2578 */ 2579 function getClosure(elements, isTopLevel, closure) { 2580 2581 if (isUndefined$1(isTopLevel)) { 2582 isTopLevel = true; 2583 } 2584 2585 if (isObject(isTopLevel)) { 2586 closure = isTopLevel; 2587 isTopLevel = true; 2588 } 2589 2590 2591 closure = closure || {}; 2592 2593 var allShapes = copyObject(closure.allShapes), 2594 allConnections = copyObject(closure.allConnections), 2595 enclosedElements = copyObject(closure.enclosedElements), 2596 enclosedConnections = copyObject(closure.enclosedConnections); 2597 2598 var topLevel = copyObject( 2599 closure.topLevel, 2600 isTopLevel && groupBy(elements, function(e) { return e.id; }) 2601 ); 2602 2603 2604 function handleConnection(c) { 2605 if (topLevel[c.source.id] && topLevel[c.target.id]) { 2606 topLevel[c.id] = [ c ]; 2607 } 2608 2609 // not enclosed as a child, but maybe logically 2610 // (connecting two moved elements?) 2611 if (allShapes[c.source.id] && allShapes[c.target.id]) { 2612 enclosedConnections[c.id] = enclosedElements[c.id] = c; 2613 } 2614 2615 allConnections[c.id] = c; 2616 } 2617 2618 function handleElement(element) { 2619 2620 enclosedElements[element.id] = element; 2621 2622 if (element.waypoints) { 2623 2624 // remember connection 2625 enclosedConnections[element.id] = allConnections[element.id] = element; 2626 } else { 2627 2628 // remember shape 2629 allShapes[element.id] = element; 2630 2631 // remember all connections 2632 forEach(element.incoming, handleConnection); 2633 2634 forEach(element.outgoing, handleConnection); 2635 2636 // recurse into children 2637 return element.children; 2638 } 2639 } 2640 2641 eachElement(elements, handleElement); 2642 2643 return { 2644 allShapes: allShapes, 2645 allConnections: allConnections, 2646 topLevel: topLevel, 2647 enclosedConnections: enclosedConnections, 2648 enclosedElements: enclosedElements 2649 }; 2650 } 2651 2652 /** 2653 * Returns the surrounding bbox for all elements in 2654 * the array or the element primitive. 2655 * 2656 * @param {Array<djs.model.Shape>|djs.model.Shape} elements 2657 * @param {boolean} stopRecursion 2658 */ 2659 function getBBox(elements, stopRecursion) { 2660 2661 stopRecursion = !!stopRecursion; 2662 if (!isArray$2(elements)) { 2663 elements = [elements]; 2664 } 2665 2666 var minX, 2667 minY, 2668 maxX, 2669 maxY; 2670 2671 forEach(elements, function(element) { 2672 2673 // If element is a connection the bbox must be computed first 2674 var bbox = element; 2675 if (element.waypoints && !stopRecursion) { 2676 bbox = getBBox(element.waypoints, true); 2677 } 2678 2679 var x = bbox.x, 2680 y = bbox.y, 2681 height = bbox.height || 0, 2682 width = bbox.width || 0; 2683 2684 if (x < minX || minX === undefined) { 2685 minX = x; 2686 } 2687 if (y < minY || minY === undefined) { 2688 minY = y; 2689 } 2690 2691 if ((x + width) > maxX || maxX === undefined) { 2692 maxX = x + width; 2693 } 2694 if ((y + height) > maxY || maxY === undefined) { 2695 maxY = y + height; 2696 } 2697 }); 2698 2699 return { 2700 x: minX, 2701 y: minY, 2702 height: maxY - minY, 2703 width: maxX - minX 2704 }; 2705 } 2706 2707 2708 /** 2709 * Returns all elements that are enclosed from the bounding box. 2710 * 2711 * * If bbox.(width|height) is not specified the method returns 2712 * all elements with element.x/y > bbox.x/y 2713 * * If only bbox.x or bbox.y is specified, method return all elements with 2714 * e.x > bbox.x or e.y > bbox.y 2715 * 2716 * @param {Array<djs.model.Shape>} elements List of Elements to search through 2717 * @param {djs.model.Shape} bbox the enclosing bbox. 2718 * 2719 * @return {Array<djs.model.Shape>} enclosed elements 2720 */ 2721 function getEnclosedElements(elements, bbox) { 2722 2723 var filteredElements = {}; 2724 2725 forEach(elements, function(element) { 2726 2727 var e = element; 2728 2729 if (e.waypoints) { 2730 e = getBBox(e); 2731 } 2732 2733 if (!isNumber(bbox.y) && (e.x > bbox.x)) { 2734 filteredElements[element.id] = element; 2735 } 2736 if (!isNumber(bbox.x) && (e.y > bbox.y)) { 2737 filteredElements[element.id] = element; 2738 } 2739 if (e.x > bbox.x && e.y > bbox.y) { 2740 if (isNumber(bbox.width) && isNumber(bbox.height) && 2741 e.width + e.x < bbox.width + bbox.x && 2742 e.height + e.y < bbox.height + bbox.y) { 2743 2744 filteredElements[element.id] = element; 2745 } else if (!isNumber(bbox.width) || !isNumber(bbox.height)) { 2746 filteredElements[element.id] = element; 2747 } 2748 } 2749 }); 2750 2751 return filteredElements; 2752 } 2753 2754 2755 function getType(element) { 2756 2757 if ('waypoints' in element) { 2758 return 'connection'; 2759 } 2760 2761 if ('x' in element) { 2762 return 'shape'; 2763 } 2764 2765 return 'root'; 2766 } 2767 2768 function isFrameElement$1(element) { 2769 2770 return !!(element && element.isFrame); 2771 } 2772 2773 // helpers /////////////////////////////// 2774 2775 function copyObject(src1, src2) { 2776 return assign({}, src1 || {}, src2 || {}); 2777 } 2778 2779 // apply default renderer with lowest possible priority 2780 // so that it only kicks in if noone else could render 2781 var DEFAULT_RENDER_PRIORITY = 1; 2782 2783 /** 2784 * The default renderer used for shapes and connections. 2785 * 2786 * @param {EventBus} eventBus 2787 * @param {Styles} styles 2788 */ 2789 function DefaultRenderer(eventBus, styles) { 2790 2791 // 2792 BaseRenderer.call(this, eventBus, DEFAULT_RENDER_PRIORITY); 2793 2794 this.CONNECTION_STYLE = styles.style([ 'no-fill' ], { strokeWidth: 5, stroke: 'fuchsia' }); 2795 this.SHAPE_STYLE = styles.style({ fill: 'white', stroke: 'fuchsia', strokeWidth: 2 }); 2796 this.FRAME_STYLE = styles.style([ 'no-fill' ], { stroke: 'fuchsia', strokeDasharray: 4, strokeWidth: 2 }); 2797 } 2798 2799 inherits$1(DefaultRenderer, BaseRenderer); 2800 2801 2802 DefaultRenderer.prototype.canRender = function() { 2803 return true; 2804 }; 2805 2806 DefaultRenderer.prototype.drawShape = function drawShape(visuals, element) { 2807 var rect = create$1('rect'); 2808 2809 attr(rect, { 2810 x: 0, 2811 y: 0, 2812 width: element.width || 0, 2813 height: element.height || 0 2814 }); 2815 2816 if (isFrameElement$1(element)) { 2817 attr(rect, this.FRAME_STYLE); 2818 } else { 2819 attr(rect, this.SHAPE_STYLE); 2820 } 2821 2822 append(visuals, rect); 2823 2824 return rect; 2825 }; 2826 2827 DefaultRenderer.prototype.drawConnection = function drawConnection(visuals, connection) { 2828 2829 var line = createLine(connection.waypoints, this.CONNECTION_STYLE); 2830 append(visuals, line); 2831 2832 return line; 2833 }; 2834 2835 DefaultRenderer.prototype.getShapePath = function getShapePath(shape) { 2836 2837 var x = shape.x, 2838 y = shape.y, 2839 width = shape.width, 2840 height = shape.height; 2841 2842 var shapePath = [ 2843 ['M', x, y], 2844 ['l', width, 0], 2845 ['l', 0, height], 2846 ['l', -width, 0], 2847 ['z'] 2848 ]; 2849 2850 return componentsToPath(shapePath); 2851 }; 2852 2853 DefaultRenderer.prototype.getConnectionPath = function getConnectionPath(connection) { 2854 var waypoints = connection.waypoints; 2855 2856 var idx, point, connectionPath = []; 2857 2858 for (idx = 0; (point = waypoints[idx]); idx++) { 2859 2860 // take invisible docking into account 2861 // when creating the path 2862 point = point.original || point; 2863 2864 connectionPath.push([ idx === 0 ? 'M' : 'L', point.x, point.y ]); 2865 } 2866 2867 return componentsToPath(connectionPath); 2868 }; 2869 2870 2871 DefaultRenderer.$inject = [ 'eventBus', 'styles' ]; 2872 2873 /** 2874 * A component that manages shape styles 2875 */ 2876 function Styles() { 2877 2878 var defaultTraits = { 2879 2880 'no-fill': { 2881 fill: 'none' 2882 }, 2883 'no-border': { 2884 strokeOpacity: 0.0 2885 }, 2886 'no-events': { 2887 pointerEvents: 'none' 2888 } 2889 }; 2890 2891 var self = this; 2892 2893 /** 2894 * Builds a style definition from a className, a list of traits and an object of additional attributes. 2895 * 2896 * @param {string} className 2897 * @param {Array<string>} traits 2898 * @param {Object} additionalAttrs 2899 * 2900 * @return {Object} the style defintion 2901 */ 2902 this.cls = function(className, traits, additionalAttrs) { 2903 var attrs = this.style(traits, additionalAttrs); 2904 2905 return assign(attrs, { 'class': className }); 2906 }; 2907 2908 /** 2909 * Builds a style definition from a list of traits and an object of additional attributes. 2910 * 2911 * @param {Array<string>} traits 2912 * @param {Object} additionalAttrs 2913 * 2914 * @return {Object} the style defintion 2915 */ 2916 this.style = function(traits, additionalAttrs) { 2917 2918 if (!isArray$2(traits) && !additionalAttrs) { 2919 additionalAttrs = traits; 2920 traits = []; 2921 } 2922 2923 var attrs = reduce(traits, function(attrs, t) { 2924 return assign(attrs, defaultTraits[t] || {}); 2925 }, {}); 2926 2927 return additionalAttrs ? assign(attrs, additionalAttrs) : attrs; 2928 }; 2929 2930 this.computeStyle = function(custom, traits, defaultStyles) { 2931 if (!isArray$2(traits)) { 2932 defaultStyles = traits; 2933 traits = []; 2934 } 2935 2936 return self.style(traits || [], assign({}, defaultStyles, custom || {})); 2937 }; 2938 } 2939 2940 var DrawModule$1 = { 2941 __init__: [ 'defaultRenderer' ], 2942 defaultRenderer: [ 'type', DefaultRenderer ], 2943 styles: [ 'type', Styles ] 2944 }; 2945 2946 /** 2947 * Failsafe remove an element from a collection 2948 * 2949 * @param {Array<Object>} [collection] 2950 * @param {Object} [element] 2951 * 2952 * @return {number} the previous index of the element 2953 */ 2954 function remove(collection, element) { 2955 2956 if (!collection || !element) { 2957 return -1; 2958 } 2959 2960 var idx = collection.indexOf(element); 2961 2962 if (idx !== -1) { 2963 collection.splice(idx, 1); 2964 } 2965 2966 return idx; 2967 } 2968 2969 /** 2970 * Fail save add an element to the given connection, ensuring 2971 * it does not yet exist. 2972 * 2973 * @param {Array<Object>} collection 2974 * @param {Object} element 2975 * @param {number} idx 2976 */ 2977 function add(collection, element, idx) { 2978 2979 if (!collection || !element) { 2980 return; 2981 } 2982 2983 if (typeof idx !== 'number') { 2984 idx = -1; 2985 } 2986 2987 var currentIdx = collection.indexOf(element); 2988 2989 if (currentIdx !== -1) { 2990 2991 if (currentIdx === idx) { 2992 2993 // nothing to do, position has not changed 2994 return; 2995 } else { 2996 2997 if (idx !== -1) { 2998 2999 // remove from current position 3000 collection.splice(currentIdx, 1); 3001 } else { 3002 3003 // already exists in collection 3004 return; 3005 } 3006 } 3007 } 3008 3009 if (idx !== -1) { 3010 3011 // insert at specified position 3012 collection.splice(idx, 0, element); 3013 } else { 3014 3015 // push to end 3016 collection.push(element); 3017 } 3018 } 3019 3020 3021 /** 3022 * Fail save get the index of an element in a collection. 3023 * 3024 * @param {Array<Object>} collection 3025 * @param {Object} element 3026 * 3027 * @return {number} the index or -1 if collection or element do 3028 * not exist or the element is not contained. 3029 */ 3030 function indexOf(collection, element) { 3031 3032 if (!collection || !element) { 3033 return -1; 3034 } 3035 3036 return collection.indexOf(element); 3037 } 3038 3039 /** 3040 * Computes the distance between two points 3041 * 3042 * @param {Point} p 3043 * @param {Point} q 3044 * 3045 * @return {number} distance 3046 */ 3047 function pointDistance(a, b) { 3048 if (!a || !b) { 3049 return -1; 3050 } 3051 3052 return Math.sqrt( 3053 Math.pow(a.x - b.x, 2) + 3054 Math.pow(a.y - b.y, 2) 3055 ); 3056 } 3057 3058 3059 /** 3060 * Returns true if the point r is on the line between p and q 3061 * 3062 * @param {Point} p 3063 * @param {Point} q 3064 * @param {Point} r 3065 * @param {number} [accuracy=5] accuracy for points on line check (lower is better) 3066 * 3067 * @return {boolean} 3068 */ 3069 function pointsOnLine(p, q, r, accuracy) { 3070 3071 if (typeof accuracy === 'undefined') { 3072 accuracy = 5; 3073 } 3074 3075 if (!p || !q || !r) { 3076 return false; 3077 } 3078 3079 var val = (q.x - p.x) * (r.y - p.y) - (q.y - p.y) * (r.x - p.x), 3080 dist = pointDistance(p, q); 3081 3082 // @see http://stackoverflow.com/a/907491/412190 3083 return Math.abs(val / dist) <= accuracy; 3084 } 3085 3086 3087 var ALIGNED_THRESHOLD = 2; 3088 3089 /** 3090 * Check whether two points are horizontally or vertically aligned. 3091 * 3092 * @param {Array<Point>|Point} 3093 * @param {Point} 3094 * 3095 * @return {string|boolean} 3096 */ 3097 function pointsAligned(a, b) { 3098 var points; 3099 3100 if (isArray$2(a)) { 3101 points = a; 3102 } else { 3103 points = [ a, b ]; 3104 } 3105 3106 if (pointsAlignedHorizontally(points)) { 3107 return 'h'; 3108 } 3109 3110 if (pointsAlignedVertically(points)) { 3111 return 'v'; 3112 } 3113 3114 return false; 3115 } 3116 3117 function pointsAlignedHorizontally(a, b) { 3118 var points; 3119 3120 if (isArray$2(a)) { 3121 points = a; 3122 } else { 3123 points = [ a, b ]; 3124 } 3125 3126 var firstPoint = points.slice().shift(); 3127 3128 return every(points, function(point) { 3129 return Math.abs(firstPoint.y - point.y) <= ALIGNED_THRESHOLD; 3130 }); 3131 } 3132 3133 function pointsAlignedVertically(a, b) { 3134 var points; 3135 3136 if (isArray$2(a)) { 3137 points = a; 3138 } else { 3139 points = [ a, b ]; 3140 } 3141 3142 var firstPoint = points.slice().shift(); 3143 3144 return every(points, function(point) { 3145 return Math.abs(firstPoint.x - point.x) <= ALIGNED_THRESHOLD; 3146 }); 3147 } 3148 3149 3150 3151 /** 3152 * Returns true if the point p is inside the rectangle rect 3153 * 3154 * @param {Point} p 3155 * @param {Rect} rect 3156 * @param {number} tolerance 3157 * 3158 * @return {boolean} 3159 */ 3160 function pointInRect(p, rect, tolerance) { 3161 tolerance = tolerance || 0; 3162 3163 return p.x > rect.x - tolerance && 3164 p.y > rect.y - tolerance && 3165 p.x < rect.x + rect.width + tolerance && 3166 p.y < rect.y + rect.height + tolerance; 3167 } 3168 3169 /** 3170 * Returns a point in the middle of points p and q 3171 * 3172 * @param {Point} p 3173 * @param {Point} q 3174 * 3175 * @return {Point} middle point 3176 */ 3177 function getMidPoint(p, q) { 3178 return { 3179 x: Math.round(p.x + ((q.x - p.x) / 2.0)), 3180 y: Math.round(p.y + ((q.y - p.y) / 2.0)) 3181 }; 3182 } 3183 3184 /** 3185 * This file contains source code adapted from Snap.svg (licensed Apache-2.0). 3186 * 3187 * @see https://github.com/adobe-webplatform/Snap.svg/blob/master/src/path.js 3188 */ 3189 3190 /* eslint no-fallthrough: "off" */ 3191 3192 var p2s = /,?([a-z]),?/gi, 3193 toFloat = parseFloat, 3194 math = Math, 3195 PI = math.PI, 3196 mmin = math.min, 3197 mmax = math.max, 3198 pow = math.pow, 3199 abs$7 = math.abs, 3200 pathCommand = /([a-z])[\s,]*((-?\d*\.?\d*(?:e[-+]?\d+)?[\s]*,?[\s]*)+)/ig, 3201 pathValues = /(-?\d*\.?\d*(?:e[-+]?\d+)?)[\s]*,?[\s]*/ig; 3202 3203 var isArray = Array.isArray || function(o) { return o instanceof Array; }; 3204 3205 function hasProperty(obj, property) { 3206 return Object.prototype.hasOwnProperty.call(obj, property); 3207 } 3208 3209 function clone(obj) { 3210 3211 if (typeof obj == 'function' || Object(obj) !== obj) { 3212 return obj; 3213 } 3214 3215 var res = new obj.constructor; 3216 3217 for (var key in obj) { 3218 if (hasProperty(obj, key)) { 3219 res[key] = clone(obj[key]); 3220 } 3221 } 3222 3223 return res; 3224 } 3225 3226 function repush(array, item) { 3227 for (var i = 0, ii = array.length; i < ii; i++) if (array[i] === item) { 3228 return array.push(array.splice(i, 1)[0]); 3229 } 3230 } 3231 3232 function cacher(f) { 3233 3234 function newf() { 3235 3236 var arg = Array.prototype.slice.call(arguments, 0), 3237 args = arg.join('\u2400'), 3238 cache = newf.cache = newf.cache || {}, 3239 count = newf.count = newf.count || []; 3240 3241 if (hasProperty(cache, args)) { 3242 repush(count, args); 3243 return cache[args]; 3244 } 3245 3246 count.length >= 1e3 && delete cache[count.shift()]; 3247 count.push(args); 3248 cache[args] = f.apply(0, arg); 3249 3250 return cache[args]; 3251 } 3252 return newf; 3253 } 3254 3255 function parsePathString(pathString) { 3256 3257 if (!pathString) { 3258 return null; 3259 } 3260 3261 var pth = paths(pathString); 3262 3263 if (pth.arr) { 3264 return clone(pth.arr); 3265 } 3266 3267 var paramCounts = { a: 7, c: 6, h: 1, l: 2, m: 2, q: 4, s: 4, t: 2, v: 1, z: 0 }, 3268 data = []; 3269 3270 if (isArray(pathString) && isArray(pathString[0])) { // rough assumption 3271 data = clone(pathString); 3272 } 3273 3274 if (!data.length) { 3275 3276 String(pathString).replace(pathCommand, function(a, b, c) { 3277 var params = [], 3278 name = b.toLowerCase(); 3279 3280 c.replace(pathValues, function(a, b) { 3281 b && params.push(+b); 3282 }); 3283 3284 if (name == 'm' && params.length > 2) { 3285 data.push([b].concat(params.splice(0, 2))); 3286 name = 'l'; 3287 b = b == 'm' ? 'l' : 'L'; 3288 } 3289 3290 while (params.length >= paramCounts[name]) { 3291 data.push([b].concat(params.splice(0, paramCounts[name]))); 3292 if (!paramCounts[name]) { 3293 break; 3294 } 3295 } 3296 }); 3297 } 3298 3299 data.toString = paths.toString; 3300 pth.arr = clone(data); 3301 3302 return data; 3303 } 3304 3305 function paths(ps) { 3306 var p = paths.ps = paths.ps || {}; 3307 3308 if (p[ps]) { 3309 p[ps].sleep = 100; 3310 } else { 3311 p[ps] = { 3312 sleep: 100 3313 }; 3314 } 3315 3316 setTimeout(function() { 3317 for (var key in p) { 3318 if (hasProperty(p, key) && key != ps) { 3319 p[key].sleep--; 3320 !p[key].sleep && delete p[key]; 3321 } 3322 } 3323 }); 3324 3325 return p[ps]; 3326 } 3327 3328 function rectBBox(x, y, width, height) { 3329 3330 if (arguments.length === 1) { 3331 y = x.y; 3332 width = x.width; 3333 height = x.height; 3334 x = x.x; 3335 } 3336 3337 return { 3338 x: x, 3339 y: y, 3340 width: width, 3341 height: height, 3342 x2: x + width, 3343 y2: y + height 3344 }; 3345 } 3346 3347 function pathToString() { 3348 return this.join(',').replace(p2s, '$1'); 3349 } 3350 3351 function pathClone(pathArray) { 3352 var res = clone(pathArray); 3353 res.toString = pathToString; 3354 return res; 3355 } 3356 3357 function findDotsAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t) { 3358 var t1 = 1 - t, 3359 t13 = pow(t1, 3), 3360 t12 = pow(t1, 2), 3361 t2 = t * t, 3362 t3 = t2 * t, 3363 x = t13 * p1x + t12 * 3 * t * c1x + t1 * 3 * t * t * c2x + t3 * p2x, 3364 y = t13 * p1y + t12 * 3 * t * c1y + t1 * 3 * t * t * c2y + t3 * p2y; 3365 3366 return { 3367 x: fixError(x), 3368 y: fixError(y) 3369 }; 3370 } 3371 3372 function bezierBBox(points) { 3373 3374 var bbox = curveBBox.apply(null, points); 3375 3376 return rectBBox( 3377 bbox.x0, 3378 bbox.y0, 3379 bbox.x1 - bbox.x0, 3380 bbox.y1 - bbox.y0 3381 ); 3382 } 3383 3384 function isPointInsideBBox$2(bbox, x, y) { 3385 return x >= bbox.x && 3386 x <= bbox.x + bbox.width && 3387 y >= bbox.y && 3388 y <= bbox.y + bbox.height; 3389 } 3390 3391 function isBBoxIntersect(bbox1, bbox2) { 3392 bbox1 = rectBBox(bbox1); 3393 bbox2 = rectBBox(bbox2); 3394 return isPointInsideBBox$2(bbox2, bbox1.x, bbox1.y) 3395 || isPointInsideBBox$2(bbox2, bbox1.x2, bbox1.y) 3396 || isPointInsideBBox$2(bbox2, bbox1.x, bbox1.y2) 3397 || isPointInsideBBox$2(bbox2, bbox1.x2, bbox1.y2) 3398 || isPointInsideBBox$2(bbox1, bbox2.x, bbox2.y) 3399 || isPointInsideBBox$2(bbox1, bbox2.x2, bbox2.y) 3400 || isPointInsideBBox$2(bbox1, bbox2.x, bbox2.y2) 3401 || isPointInsideBBox$2(bbox1, bbox2.x2, bbox2.y2) 3402 || (bbox1.x < bbox2.x2 && bbox1.x > bbox2.x 3403 || bbox2.x < bbox1.x2 && bbox2.x > bbox1.x) 3404 && (bbox1.y < bbox2.y2 && bbox1.y > bbox2.y 3405 || bbox2.y < bbox1.y2 && bbox2.y > bbox1.y); 3406 } 3407 3408 function base3(t, p1, p2, p3, p4) { 3409 var t1 = -3 * p1 + 9 * p2 - 9 * p3 + 3 * p4, 3410 t2 = t * t1 + 6 * p1 - 12 * p2 + 6 * p3; 3411 return t * t2 - 3 * p1 + 3 * p2; 3412 } 3413 3414 function bezlen(x1, y1, x2, y2, x3, y3, x4, y4, z) { 3415 3416 if (z == null) { 3417 z = 1; 3418 } 3419 3420 z = z > 1 ? 1 : z < 0 ? 0 : z; 3421 3422 var z2 = z / 2, 3423 n = 12, 3424 Tvalues = [-.1252,.1252,-.3678,.3678,-.5873,.5873,-.7699,.7699,-.9041,.9041,-.9816,.9816], 3425 Cvalues = [0.2491,0.2491,0.2335,0.2335,0.2032,0.2032,0.1601,0.1601,0.1069,0.1069,0.0472,0.0472], 3426 sum = 0; 3427 3428 for (var i = 0; i < n; i++) { 3429 var ct = z2 * Tvalues[i] + z2, 3430 xbase = base3(ct, x1, x2, x3, x4), 3431 ybase = base3(ct, y1, y2, y3, y4), 3432 comb = xbase * xbase + ybase * ybase; 3433 3434 sum += Cvalues[i] * math.sqrt(comb); 3435 } 3436 3437 return z2 * sum; 3438 } 3439 3440 3441 function intersectLines(x1, y1, x2, y2, x3, y3, x4, y4) { 3442 3443 if ( 3444 mmax(x1, x2) < mmin(x3, x4) || 3445 mmin(x1, x2) > mmax(x3, x4) || 3446 mmax(y1, y2) < mmin(y3, y4) || 3447 mmin(y1, y2) > mmax(y3, y4) 3448 ) { 3449 return; 3450 } 3451 3452 var nx = (x1 * y2 - y1 * x2) * (x3 - x4) - (x1 - x2) * (x3 * y4 - y3 * x4), 3453 ny = (x1 * y2 - y1 * x2) * (y3 - y4) - (y1 - y2) * (x3 * y4 - y3 * x4), 3454 denominator = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4); 3455 3456 if (!denominator) { 3457 return; 3458 } 3459 3460 var px = fixError(nx / denominator), 3461 py = fixError(ny / denominator), 3462 px2 = +px.toFixed(2), 3463 py2 = +py.toFixed(2); 3464 3465 if ( 3466 px2 < +mmin(x1, x2).toFixed(2) || 3467 px2 > +mmax(x1, x2).toFixed(2) || 3468 px2 < +mmin(x3, x4).toFixed(2) || 3469 px2 > +mmax(x3, x4).toFixed(2) || 3470 py2 < +mmin(y1, y2).toFixed(2) || 3471 py2 > +mmax(y1, y2).toFixed(2) || 3472 py2 < +mmin(y3, y4).toFixed(2) || 3473 py2 > +mmax(y3, y4).toFixed(2) 3474 ) { 3475 return; 3476 } 3477 3478 return { x: px, y: py }; 3479 } 3480 3481 function fixError(number) { 3482 return Math.round(number * 100000000000) / 100000000000; 3483 } 3484 3485 function findBezierIntersections(bez1, bez2, justCount) { 3486 var bbox1 = bezierBBox(bez1), 3487 bbox2 = bezierBBox(bez2); 3488 3489 if (!isBBoxIntersect(bbox1, bbox2)) { 3490 return justCount ? 0 : []; 3491 } 3492 3493 // As an optimization, lines will have only 1 segment 3494 3495 var l1 = bezlen.apply(0, bez1), 3496 l2 = bezlen.apply(0, bez2), 3497 n1 = isLine(bez1) ? 1 : ~~(l1 / 5) || 1, 3498 n2 = isLine(bez2) ? 1 : ~~(l2 / 5) || 1, 3499 dots1 = [], 3500 dots2 = [], 3501 xy = {}, 3502 res = justCount ? 0 : []; 3503 3504 for (var i = 0; i < n1 + 1; i++) { 3505 var p = findDotsAtSegment.apply(0, bez1.concat(i / n1)); 3506 dots1.push({ x: p.x, y: p.y, t: i / n1 }); 3507 } 3508 3509 for (i = 0; i < n2 + 1; i++) { 3510 p = findDotsAtSegment.apply(0, bez2.concat(i / n2)); 3511 dots2.push({ x: p.x, y: p.y, t: i / n2 }); 3512 } 3513 3514 for (i = 0; i < n1; i++) { 3515 3516 for (var j = 0; j < n2; j++) { 3517 var di = dots1[i], 3518 di1 = dots1[i + 1], 3519 dj = dots2[j], 3520 dj1 = dots2[j + 1], 3521 ci = abs$7(di1.x - di.x) < .01 ? 'y' : 'x', 3522 cj = abs$7(dj1.x - dj.x) < .01 ? 'y' : 'x', 3523 is = intersectLines(di.x, di.y, di1.x, di1.y, dj.x, dj.y, dj1.x, dj1.y), 3524 key; 3525 3526 if (is) { 3527 key = is.x.toFixed(9) + '#' + is.y.toFixed(9); 3528 3529 if (xy[key]) { 3530 continue; 3531 } 3532 3533 xy[key] = true; 3534 3535 var t1 = di.t + abs$7((is[ci] - di[ci]) / (di1[ci] - di[ci])) * (di1.t - di.t), 3536 t2 = dj.t + abs$7((is[cj] - dj[cj]) / (dj1[cj] - dj[cj])) * (dj1.t - dj.t); 3537 3538 if (t1 >= 0 && t1 <= 1 && t2 >= 0 && t2 <= 1) { 3539 3540 if (justCount) { 3541 res++; 3542 } else { 3543 res.push({ 3544 x: is.x, 3545 y: is.y, 3546 t1: t1, 3547 t2: t2 3548 }); 3549 } 3550 } 3551 } 3552 } 3553 } 3554 3555 return res; 3556 } 3557 3558 3559 /** 3560 * Find or counts the intersections between two SVG paths. 3561 * 3562 * Returns a number in counting mode and a list of intersections otherwise. 3563 * 3564 * A single intersection entry contains the intersection coordinates (x, y) 3565 * as well as additional information regarding the intersecting segments 3566 * on each path (segment1, segment2) and the relative location of the 3567 * intersection on these segments (t1, t2). 3568 * 3569 * The path may be an SVG path string or a list of path components 3570 * such as `[ [ 'M', 0, 10 ], [ 'L', 20, 0 ] ]`. 3571 * 3572 * @example 3573 * 3574 * var intersections = findPathIntersections( 3575 * 'M0,0L100,100', 3576 * [ [ 'M', 0, 100 ], [ 'L', 100, 0 ] ] 3577 * ); 3578 * 3579 * // intersections = [ 3580 * // { x: 50, y: 50, segment1: 1, segment2: 1, t1: 0.5, t2: 0.5 } 3581 * // ] 3582 * 3583 * @param {String|Array<PathDef>} path1 3584 * @param {String|Array<PathDef>} path2 3585 * @param {Boolean} [justCount=false] 3586 * 3587 * @return {Array<Intersection>|Number} 3588 */ 3589 function findPathIntersections(path1, path2, justCount) { 3590 path1 = pathToCurve(path1); 3591 path2 = pathToCurve(path2); 3592 3593 var x1, y1, x2, y2, x1m, y1m, x2m, y2m, bez1, bez2, 3594 res = justCount ? 0 : []; 3595 3596 for (var i = 0, ii = path1.length; i < ii; i++) { 3597 var pi = path1[i]; 3598 3599 if (pi[0] == 'M') { 3600 x1 = x1m = pi[1]; 3601 y1 = y1m = pi[2]; 3602 } else { 3603 3604 if (pi[0] == 'C') { 3605 bez1 = [x1, y1].concat(pi.slice(1)); 3606 x1 = bez1[6]; 3607 y1 = bez1[7]; 3608 } else { 3609 bez1 = [x1, y1, x1, y1, x1m, y1m, x1m, y1m]; 3610 x1 = x1m; 3611 y1 = y1m; 3612 } 3613 3614 for (var j = 0, jj = path2.length; j < jj; j++) { 3615 var pj = path2[j]; 3616 3617 if (pj[0] == 'M') { 3618 x2 = x2m = pj[1]; 3619 y2 = y2m = pj[2]; 3620 } else { 3621 3622 if (pj[0] == 'C') { 3623 bez2 = [x2, y2].concat(pj.slice(1)); 3624 x2 = bez2[6]; 3625 y2 = bez2[7]; 3626 } else { 3627 bez2 = [x2, y2, x2, y2, x2m, y2m, x2m, y2m]; 3628 x2 = x2m; 3629 y2 = y2m; 3630 } 3631 3632 var intr = findBezierIntersections(bez1, bez2, justCount); 3633 3634 if (justCount) { 3635 res += intr; 3636 } else { 3637 3638 for (var k = 0, kk = intr.length; k < kk; k++) { 3639 intr[k].segment1 = i; 3640 intr[k].segment2 = j; 3641 intr[k].bez1 = bez1; 3642 intr[k].bez2 = bez2; 3643 } 3644 3645 res = res.concat(intr); 3646 } 3647 } 3648 } 3649 } 3650 } 3651 3652 return res; 3653 } 3654 3655 3656 function pathToAbsolute(pathArray) { 3657 var pth = paths(pathArray); 3658 3659 if (pth.abs) { 3660 return pathClone(pth.abs); 3661 } 3662 3663 if (!isArray(pathArray) || !isArray(pathArray && pathArray[0])) { // rough assumption 3664 pathArray = parsePathString(pathArray); 3665 } 3666 3667 if (!pathArray || !pathArray.length) { 3668 return [['M', 0, 0]]; 3669 } 3670 3671 var res = [], 3672 x = 0, 3673 y = 0, 3674 mx = 0, 3675 my = 0, 3676 start = 0, 3677 pa0; 3678 3679 if (pathArray[0][0] == 'M') { 3680 x = +pathArray[0][1]; 3681 y = +pathArray[0][2]; 3682 mx = x; 3683 my = y; 3684 start++; 3685 res[0] = ['M', x, y]; 3686 } 3687 3688 for (var r, pa, i = start, ii = pathArray.length; i < ii; i++) { 3689 res.push(r = []); 3690 pa = pathArray[i]; 3691 pa0 = pa[0]; 3692 3693 if (pa0 != pa0.toUpperCase()) { 3694 r[0] = pa0.toUpperCase(); 3695 3696 switch (r[0]) { 3697 case 'A': 3698 r[1] = pa[1]; 3699 r[2] = pa[2]; 3700 r[3] = pa[3]; 3701 r[4] = pa[4]; 3702 r[5] = pa[5]; 3703 r[6] = +pa[6] + x; 3704 r[7] = +pa[7] + y; 3705 break; 3706 case 'V': 3707 r[1] = +pa[1] + y; 3708 break; 3709 case 'H': 3710 r[1] = +pa[1] + x; 3711 break; 3712 case 'M': 3713 mx = +pa[1] + x; 3714 my = +pa[2] + y; 3715 default: 3716 for (var j = 1, jj = pa.length; j < jj; j++) { 3717 r[j] = +pa[j] + ((j % 2) ? x : y); 3718 } 3719 } 3720 } else { 3721 for (var k = 0, kk = pa.length; k < kk; k++) { 3722 r[k] = pa[k]; 3723 } 3724 } 3725 pa0 = pa0.toUpperCase(); 3726 3727 switch (r[0]) { 3728 case 'Z': 3729 x = +mx; 3730 y = +my; 3731 break; 3732 case 'H': 3733 x = r[1]; 3734 break; 3735 case 'V': 3736 y = r[1]; 3737 break; 3738 case 'M': 3739 mx = r[r.length - 2]; 3740 my = r[r.length - 1]; 3741 default: 3742 x = r[r.length - 2]; 3743 y = r[r.length - 1]; 3744 } 3745 } 3746 3747 res.toString = pathToString; 3748 pth.abs = pathClone(res); 3749 3750 return res; 3751 } 3752 3753 function isLine(bez) { 3754 return ( 3755 bez[0] === bez[2] && 3756 bez[1] === bez[3] && 3757 bez[4] === bez[6] && 3758 bez[5] === bez[7] 3759 ); 3760 } 3761 3762 function lineToCurve(x1, y1, x2, y2) { 3763 return [ 3764 x1, y1, x2, 3765 y2, x2, y2 3766 ]; 3767 } 3768 3769 function qubicToCurve(x1, y1, ax, ay, x2, y2) { 3770 var _13 = 1 / 3, 3771 _23 = 2 / 3; 3772 3773 return [ 3774 _13 * x1 + _23 * ax, 3775 _13 * y1 + _23 * ay, 3776 _13 * x2 + _23 * ax, 3777 _13 * y2 + _23 * ay, 3778 x2, 3779 y2 3780 ]; 3781 } 3782 3783 function arcToCurve(x1, y1, rx, ry, angle, large_arc_flag, sweep_flag, x2, y2, recursive) { 3784 3785 // for more information of where this math came from visit: 3786 // http://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes 3787 var _120 = PI * 120 / 180, 3788 rad = PI / 180 * (+angle || 0), 3789 res = [], 3790 xy, 3791 rotate = cacher(function(x, y, rad) { 3792 var X = x * math.cos(rad) - y * math.sin(rad), 3793 Y = x * math.sin(rad) + y * math.cos(rad); 3794 3795 return { x: X, y: Y }; 3796 }); 3797 3798 if (!recursive) { 3799 xy = rotate(x1, y1, -rad); 3800 x1 = xy.x; 3801 y1 = xy.y; 3802 xy = rotate(x2, y2, -rad); 3803 x2 = xy.x; 3804 y2 = xy.y; 3805 3806 var x = (x1 - x2) / 2, 3807 y = (y1 - y2) / 2; 3808 3809 var h = (x * x) / (rx * rx) + (y * y) / (ry * ry); 3810 3811 if (h > 1) { 3812 h = math.sqrt(h); 3813 rx = h * rx; 3814 ry = h * ry; 3815 } 3816 3817 var rx2 = rx * rx, 3818 ry2 = ry * ry, 3819 k = (large_arc_flag == sweep_flag ? -1 : 1) * 3820 math.sqrt(abs$7((rx2 * ry2 - rx2 * y * y - ry2 * x * x) / (rx2 * y * y + ry2 * x * x))), 3821 cx = k * rx * y / ry + (x1 + x2) / 2, 3822 cy = k * -ry * x / rx + (y1 + y2) / 2, 3823 f1 = math.asin(((y1 - cy) / ry).toFixed(9)), 3824 f2 = math.asin(((y2 - cy) / ry).toFixed(9)); 3825 3826 f1 = x1 < cx ? PI - f1 : f1; 3827 f2 = x2 < cx ? PI - f2 : f2; 3828 f1 < 0 && (f1 = PI * 2 + f1); 3829 f2 < 0 && (f2 = PI * 2 + f2); 3830 3831 if (sweep_flag && f1 > f2) { 3832 f1 = f1 - PI * 2; 3833 } 3834 if (!sweep_flag && f2 > f1) { 3835 f2 = f2 - PI * 2; 3836 } 3837 } else { 3838 f1 = recursive[0]; 3839 f2 = recursive[1]; 3840 cx = recursive[2]; 3841 cy = recursive[3]; 3842 } 3843 3844 var df = f2 - f1; 3845 3846 if (abs$7(df) > _120) { 3847 var f2old = f2, 3848 x2old = x2, 3849 y2old = y2; 3850 3851 f2 = f1 + _120 * (sweep_flag && f2 > f1 ? 1 : -1); 3852 x2 = cx + rx * math.cos(f2); 3853 y2 = cy + ry * math.sin(f2); 3854 res = arcToCurve(x2, y2, rx, ry, angle, 0, sweep_flag, x2old, y2old, [f2, f2old, cx, cy]); 3855 } 3856 3857 df = f2 - f1; 3858 3859 var c1 = math.cos(f1), 3860 s1 = math.sin(f1), 3861 c2 = math.cos(f2), 3862 s2 = math.sin(f2), 3863 t = math.tan(df / 4), 3864 hx = 4 / 3 * rx * t, 3865 hy = 4 / 3 * ry * t, 3866 m1 = [x1, y1], 3867 m2 = [x1 + hx * s1, y1 - hy * c1], 3868 m3 = [x2 + hx * s2, y2 - hy * c2], 3869 m4 = [x2, y2]; 3870 3871 m2[0] = 2 * m1[0] - m2[0]; 3872 m2[1] = 2 * m1[1] - m2[1]; 3873 3874 if (recursive) { 3875 return [m2, m3, m4].concat(res); 3876 } else { 3877 res = [m2, m3, m4].concat(res).join().split(','); 3878 var newres = []; 3879 3880 for (var i = 0, ii = res.length; i < ii; i++) { 3881 newres[i] = i % 2 ? rotate(res[i - 1], res[i], rad).y : rotate(res[i], res[i + 1], rad).x; 3882 } 3883 3884 return newres; 3885 } 3886 } 3887 3888 // Returns bounding box of cubic bezier curve. 3889 // Source: http://blog.hackers-cafe.net/2009/06/how-to-calculate-bezier-curves-bounding.html 3890 // Original version: NISHIO Hirokazu 3891 // Modifications: https://github.com/timo22345 3892 function curveBBox(x0, y0, x1, y1, x2, y2, x3, y3) { 3893 var tvalues = [], 3894 bounds = [[], []], 3895 a, b, c, t, t1, t2, b2ac, sqrtb2ac; 3896 3897 for (var i = 0; i < 2; ++i) { 3898 3899 if (i == 0) { 3900 b = 6 * x0 - 12 * x1 + 6 * x2; 3901 a = -3 * x0 + 9 * x1 - 9 * x2 + 3 * x3; 3902 c = 3 * x1 - 3 * x0; 3903 } else { 3904 b = 6 * y0 - 12 * y1 + 6 * y2; 3905 a = -3 * y0 + 9 * y1 - 9 * y2 + 3 * y3; 3906 c = 3 * y1 - 3 * y0; 3907 } 3908 3909 if (abs$7(a) < 1e-12) { 3910 3911 if (abs$7(b) < 1e-12) { 3912 continue; 3913 } 3914 3915 t = -c / b; 3916 3917 if (0 < t && t < 1) { 3918 tvalues.push(t); 3919 } 3920 3921 continue; 3922 } 3923 3924 b2ac = b * b - 4 * c * a; 3925 sqrtb2ac = math.sqrt(b2ac); 3926 3927 if (b2ac < 0) { 3928 continue; 3929 } 3930 3931 t1 = (-b + sqrtb2ac) / (2 * a); 3932 3933 if (0 < t1 && t1 < 1) { 3934 tvalues.push(t1); 3935 } 3936 3937 t2 = (-b - sqrtb2ac) / (2 * a); 3938 3939 if (0 < t2 && t2 < 1) { 3940 tvalues.push(t2); 3941 } 3942 } 3943 3944 var j = tvalues.length, 3945 jlen = j, 3946 mt; 3947 3948 while (j--) { 3949 t = tvalues[j]; 3950 mt = 1 - t; 3951 bounds[0][j] = (mt * mt * mt * x0) + (3 * mt * mt * t * x1) + (3 * mt * t * t * x2) + (t * t * t * x3); 3952 bounds[1][j] = (mt * mt * mt * y0) + (3 * mt * mt * t * y1) + (3 * mt * t * t * y2) + (t * t * t * y3); 3953 } 3954 3955 bounds[0][jlen] = x0; 3956 bounds[1][jlen] = y0; 3957 bounds[0][jlen + 1] = x3; 3958 bounds[1][jlen + 1] = y3; 3959 bounds[0].length = bounds[1].length = jlen + 2; 3960 3961 return { 3962 x0: mmin.apply(0, bounds[0]), 3963 y0: mmin.apply(0, bounds[1]), 3964 x1: mmax.apply(0, bounds[0]), 3965 y1: mmax.apply(0, bounds[1]) 3966 }; 3967 } 3968 3969 function pathToCurve(path) { 3970 3971 var pth = paths(path); 3972 3973 // return cached curve, if existing 3974 if (pth.curve) { 3975 return pathClone(pth.curve); 3976 } 3977 3978 var curvedPath = pathToAbsolute(path), 3979 attrs = { x: 0, y: 0, bx: 0, by: 0, X: 0, Y: 0, qx: null, qy: null }, 3980 processPath = function(path, d, pathCommand) { 3981 var nx, ny; 3982 3983 if (!path) { 3984 return ['C', d.x, d.y, d.x, d.y, d.x, d.y]; 3985 } 3986 3987 !(path[0] in { T: 1, Q: 1 }) && (d.qx = d.qy = null); 3988 3989 switch (path[0]) { 3990 case 'M': 3991 d.X = path[1]; 3992 d.Y = path[2]; 3993 break; 3994 case 'A': 3995 path = ['C'].concat(arcToCurve.apply(0, [d.x, d.y].concat(path.slice(1)))); 3996 break; 3997 case 'S': 3998 if (pathCommand == 'C' || pathCommand == 'S') { 3999 4000 // In 'S' case we have to take into account, if the previous command is C/S. 4001 nx = d.x * 2 - d.bx; 4002 4003 // And reflect the previous 4004 ny = d.y * 2 - d.by; 4005 4006 // command's control point relative to the current point. 4007 } 4008 else { 4009 4010 // or some else or nothing 4011 nx = d.x; 4012 ny = d.y; 4013 } 4014 path = ['C', nx, ny].concat(path.slice(1)); 4015 break; 4016 case 'T': 4017 if (pathCommand == 'Q' || pathCommand == 'T') { 4018 4019 // In 'T' case we have to take into account, if the previous command is Q/T. 4020 d.qx = d.x * 2 - d.qx; 4021 4022 // And make a reflection similar 4023 d.qy = d.y * 2 - d.qy; 4024 4025 // to case 'S'. 4026 } 4027 else { 4028 4029 // or something else or nothing 4030 d.qx = d.x; 4031 d.qy = d.y; 4032 } 4033 path = ['C'].concat(qubicToCurve(d.x, d.y, d.qx, d.qy, path[1], path[2])); 4034 break; 4035 case 'Q': 4036 d.qx = path[1]; 4037 d.qy = path[2]; 4038 path = ['C'].concat(qubicToCurve(d.x, d.y, path[1], path[2], path[3], path[4])); 4039 break; 4040 case 'L': 4041 path = ['C'].concat(lineToCurve(d.x, d.y, path[1], path[2])); 4042 break; 4043 case 'H': 4044 path = ['C'].concat(lineToCurve(d.x, d.y, path[1], d.y)); 4045 break; 4046 case 'V': 4047 path = ['C'].concat(lineToCurve(d.x, d.y, d.x, path[1])); 4048 break; 4049 case 'Z': 4050 path = ['C'].concat(lineToCurve(d.x, d.y, d.X, d.Y)); 4051 break; 4052 } 4053 4054 return path; 4055 }, 4056 4057 fixArc = function(pp, i) { 4058 4059 if (pp[i].length > 7) { 4060 pp[i].shift(); 4061 var pi = pp[i]; 4062 4063 while (pi.length) { 4064 pathCommands[i] = 'A'; // if created multiple C:s, their original seg is saved 4065 pp.splice(i++, 0, ['C'].concat(pi.splice(0, 6))); 4066 } 4067 4068 pp.splice(i, 1); 4069 ii = curvedPath.length; 4070 } 4071 }, 4072 4073 pathCommands = [], // path commands of original path p 4074 pfirst = '', // temporary holder for original path command 4075 pathCommand = ''; // holder for previous path command of original path 4076 4077 for (var i = 0, ii = curvedPath.length; i < ii; i++) { 4078 curvedPath[i] && (pfirst = curvedPath[i][0]); // save current path command 4079 4080 if (pfirst != 'C') // C is not saved yet, because it may be result of conversion 4081 { 4082 pathCommands[i] = pfirst; // Save current path command 4083 i && (pathCommand = pathCommands[i - 1]); // Get previous path command pathCommand 4084 } 4085 curvedPath[i] = processPath(curvedPath[i], attrs, pathCommand); // Previous path command is inputted to processPath 4086 4087 if (pathCommands[i] != 'A' && pfirst == 'C') pathCommands[i] = 'C'; // A is the only command 4088 // which may produce multiple C:s 4089 // so we have to make sure that C is also C in original path 4090 4091 fixArc(curvedPath, i); // fixArc adds also the right amount of A:s to pathCommands