UNPKG

872 kBJavaScriptView Raw
1/**
2 * Copyright (c) 2016-2022, The Cytoscape Consortium.
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a copy of
5 * this software and associated documentation files (the “Software”), to deal in
6 * the Software without restriction, including without limitation the rights to
7 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
8 * of the Software, and to permit persons to whom the Software is furnished to do
9 * so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in all
12 * copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20 * SOFTWARE.
21 */
22
23import debounce from 'lodash/debounce';
24import Heap from 'heap';
25import get from 'lodash/get';
26import set from 'lodash/set';
27import toPath from 'lodash/toPath';
28
29function _typeof(obj) {
30 "@babel/helpers - typeof";
31
32 return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (obj) {
33 return typeof obj;
34 } : function (obj) {
35 return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
36 }, _typeof(obj);
37}
38
39function _classCallCheck(instance, Constructor) {
40 if (!(instance instanceof Constructor)) {
41 throw new TypeError("Cannot call a class as a function");
42 }
43}
44
45function _defineProperties(target, props) {
46 for (var i = 0; i < props.length; i++) {
47 var descriptor = props[i];
48 descriptor.enumerable = descriptor.enumerable || false;
49 descriptor.configurable = true;
50 if ("value" in descriptor) descriptor.writable = true;
51 Object.defineProperty(target, descriptor.key, descriptor);
52 }
53}
54
55function _createClass(Constructor, protoProps, staticProps) {
56 if (protoProps) _defineProperties(Constructor.prototype, protoProps);
57 if (staticProps) _defineProperties(Constructor, staticProps);
58 Object.defineProperty(Constructor, "prototype", {
59 writable: false
60 });
61 return Constructor;
62}
63
64function _defineProperty(obj, key, value) {
65 if (key in obj) {
66 Object.defineProperty(obj, key, {
67 value: value,
68 enumerable: true,
69 configurable: true,
70 writable: true
71 });
72 } else {
73 obj[key] = value;
74 }
75
76 return obj;
77}
78
79function _slicedToArray(arr, i) {
80 return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest();
81}
82
83function _arrayWithHoles(arr) {
84 if (Array.isArray(arr)) return arr;
85}
86
87function _iterableToArrayLimit(arr, i) {
88 var _i = arr == null ? null : typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"];
89
90 if (_i == null) return;
91 var _arr = [];
92 var _n = true;
93 var _d = false;
94
95 var _s, _e;
96
97 try {
98 for (_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true) {
99 _arr.push(_s.value);
100
101 if (i && _arr.length === i) break;
102 }
103 } catch (err) {
104 _d = true;
105 _e = err;
106 } finally {
107 try {
108 if (!_n && _i["return"] != null) _i["return"]();
109 } finally {
110 if (_d) throw _e;
111 }
112 }
113
114 return _arr;
115}
116
117function _unsupportedIterableToArray(o, minLen) {
118 if (!o) return;
119 if (typeof o === "string") return _arrayLikeToArray(o, minLen);
120 var n = Object.prototype.toString.call(o).slice(8, -1);
121 if (n === "Object" && o.constructor) n = o.constructor.name;
122 if (n === "Map" || n === "Set") return Array.from(o);
123 if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen);
124}
125
126function _arrayLikeToArray(arr, len) {
127 if (len == null || len > arr.length) len = arr.length;
128
129 for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i];
130
131 return arr2;
132}
133
134function _nonIterableRest() {
135 throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
136}
137
138var window$1 = typeof window === 'undefined' ? null : window; // eslint-disable-line no-undef
139
140var navigator = window$1 ? window$1.navigator : null;
141window$1 ? window$1.document : null;
142
143var typeofstr = _typeof('');
144
145var typeofobj = _typeof({});
146
147var typeoffn = _typeof(function () {});
148
149var typeofhtmlele = typeof HTMLElement === "undefined" ? "undefined" : _typeof(HTMLElement);
150
151var instanceStr = function instanceStr(obj) {
152 return obj && obj.instanceString && fn$6(obj.instanceString) ? obj.instanceString() : null;
153};
154
155var string = function string(obj) {
156 return obj != null && _typeof(obj) == typeofstr;
157};
158var fn$6 = function fn(obj) {
159 return obj != null && _typeof(obj) === typeoffn;
160};
161var array = function array(obj) {
162 return !elementOrCollection(obj) && (Array.isArray ? Array.isArray(obj) : obj != null && obj instanceof Array);
163};
164var plainObject = function plainObject(obj) {
165 return obj != null && _typeof(obj) === typeofobj && !array(obj) && obj.constructor === Object;
166};
167var object = function object(obj) {
168 return obj != null && _typeof(obj) === typeofobj;
169};
170var number$1 = function number(obj) {
171 return obj != null && _typeof(obj) === _typeof(1) && !isNaN(obj);
172};
173var integer = function integer(obj) {
174 return number$1(obj) && Math.floor(obj) === obj;
175};
176var htmlElement = function htmlElement(obj) {
177 if ('undefined' === typeofhtmlele) {
178 return undefined;
179 } else {
180 return null != obj && obj instanceof HTMLElement;
181 }
182};
183var elementOrCollection = function elementOrCollection(obj) {
184 return element(obj) || collection(obj);
185};
186var element = function element(obj) {
187 return instanceStr(obj) === 'collection' && obj._private.single;
188};
189var collection = function collection(obj) {
190 return instanceStr(obj) === 'collection' && !obj._private.single;
191};
192var core = function core(obj) {
193 return instanceStr(obj) === 'core';
194};
195var stylesheet = function stylesheet(obj) {
196 return instanceStr(obj) === 'stylesheet';
197};
198var event = function event(obj) {
199 return instanceStr(obj) === 'event';
200};
201var emptyString = function emptyString(obj) {
202 if (obj === undefined || obj === null) {
203 // null is empty
204 return true;
205 } else if (obj === '' || obj.match(/^\s+$/)) {
206 return true; // empty string is empty
207 }
208
209 return false; // otherwise, we don't know what we've got
210};
211var domElement = function domElement(obj) {
212 if (typeof HTMLElement === 'undefined') {
213 return false; // we're not in a browser so it doesn't matter
214 } else {
215 return obj instanceof HTMLElement;
216 }
217};
218var boundingBox = function boundingBox(obj) {
219 return plainObject(obj) && number$1(obj.x1) && number$1(obj.x2) && number$1(obj.y1) && number$1(obj.y2);
220};
221var promise = function promise(obj) {
222 return object(obj) && fn$6(obj.then);
223};
224var ms = function ms() {
225 return navigator && navigator.userAgent.match(/msie|trident|edge/i);
226}; // probably a better way to detect this...
227
228var memoize = function memoize(fn, keyFn) {
229 if (!keyFn) {
230 keyFn = function keyFn() {
231 if (arguments.length === 1) {
232 return arguments[0];
233 } else if (arguments.length === 0) {
234 return 'undefined';
235 }
236
237 var args = [];
238
239 for (var i = 0; i < arguments.length; i++) {
240 args.push(arguments[i]);
241 }
242
243 return args.join('$');
244 };
245 }
246
247 var memoizedFn = function memoizedFn() {
248 var self = this;
249 var args = arguments;
250 var ret;
251 var k = keyFn.apply(self, args);
252 var cache = memoizedFn.cache;
253
254 if (!(ret = cache[k])) {
255 ret = cache[k] = fn.apply(self, args);
256 }
257
258 return ret;
259 };
260
261 memoizedFn.cache = {};
262 return memoizedFn;
263};
264
265var camel2dash = memoize(function (str) {
266 return str.replace(/([A-Z])/g, function (v) {
267 return '-' + v.toLowerCase();
268 });
269});
270var dash2camel = memoize(function (str) {
271 return str.replace(/(-\w)/g, function (v) {
272 return v[1].toUpperCase();
273 });
274});
275var prependCamel = memoize(function (prefix, str) {
276 return prefix + str[0].toUpperCase() + str.substring(1);
277}, function (prefix, str) {
278 return prefix + '$' + str;
279});
280var capitalize = function capitalize(str) {
281 if (emptyString(str)) {
282 return str;
283 }
284
285 return str.charAt(0).toUpperCase() + str.substring(1);
286};
287
288var number = '(?:[-+]?(?:(?:\\d+|\\d*\\.\\d+)(?:[Ee][+-]?\\d+)?))';
289var rgba = 'rgb[a]?\\((' + number + '[%]?)\\s*,\\s*(' + number + '[%]?)\\s*,\\s*(' + number + '[%]?)(?:\\s*,\\s*(' + number + '))?\\)';
290var rgbaNoBackRefs = 'rgb[a]?\\((?:' + number + '[%]?)\\s*,\\s*(?:' + number + '[%]?)\\s*,\\s*(?:' + number + '[%]?)(?:\\s*,\\s*(?:' + number + '))?\\)';
291var hsla = 'hsl[a]?\\((' + number + ')\\s*,\\s*(' + number + '[%])\\s*,\\s*(' + number + '[%])(?:\\s*,\\s*(' + number + '))?\\)';
292var hslaNoBackRefs = 'hsl[a]?\\((?:' + number + ')\\s*,\\s*(?:' + number + '[%])\\s*,\\s*(?:' + number + '[%])(?:\\s*,\\s*(?:' + number + '))?\\)';
293var hex3 = '\\#[0-9a-fA-F]{3}';
294var hex6 = '\\#[0-9a-fA-F]{6}';
295
296var ascending = function ascending(a, b) {
297 if (a < b) {
298 return -1;
299 } else if (a > b) {
300 return 1;
301 } else {
302 return 0;
303 }
304};
305var descending = function descending(a, b) {
306 return -1 * ascending(a, b);
307};
308
309var extend = Object.assign != null ? Object.assign.bind(Object) : function (tgt) {
310 var args = arguments;
311
312 for (var i = 1; i < args.length; i++) {
313 var obj = args[i];
314
315 if (obj == null) {
316 continue;
317 }
318
319 var keys = Object.keys(obj);
320
321 for (var j = 0; j < keys.length; j++) {
322 var k = keys[j];
323 tgt[k] = obj[k];
324 }
325 }
326
327 return tgt;
328};
329
330var hex2tuple = function hex2tuple(hex) {
331 if (!(hex.length === 4 || hex.length === 7) || hex[0] !== '#') {
332 return;
333 }
334
335 var shortHex = hex.length === 4;
336 var r, g, b;
337 var base = 16;
338
339 if (shortHex) {
340 r = parseInt(hex[1] + hex[1], base);
341 g = parseInt(hex[2] + hex[2], base);
342 b = parseInt(hex[3] + hex[3], base);
343 } else {
344 r = parseInt(hex[1] + hex[2], base);
345 g = parseInt(hex[3] + hex[4], base);
346 b = parseInt(hex[5] + hex[6], base);
347 }
348
349 return [r, g, b];
350}; // get [r, g, b, a] from hsl(0, 0, 0) or hsla(0, 0, 0, 0)
351
352var hsl2tuple = function hsl2tuple(hsl) {
353 var ret;
354 var h, s, l, a, r, g, b;
355
356 function hue2rgb(p, q, t) {
357 if (t < 0) t += 1;
358 if (t > 1) t -= 1;
359 if (t < 1 / 6) return p + (q - p) * 6 * t;
360 if (t < 1 / 2) return q;
361 if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
362 return p;
363 }
364
365 var m = new RegExp('^' + hsla + '$').exec(hsl);
366
367 if (m) {
368 // get hue
369 h = parseInt(m[1]);
370
371 if (h < 0) {
372 h = (360 - -1 * h % 360) % 360;
373 } else if (h > 360) {
374 h = h % 360;
375 }
376
377 h /= 360; // normalise on [0, 1]
378
379 s = parseFloat(m[2]);
380
381 if (s < 0 || s > 100) {
382 return;
383 } // saturation is [0, 100]
384
385
386 s = s / 100; // normalise on [0, 1]
387
388 l = parseFloat(m[3]);
389
390 if (l < 0 || l > 100) {
391 return;
392 } // lightness is [0, 100]
393
394
395 l = l / 100; // normalise on [0, 1]
396
397 a = m[4];
398
399 if (a !== undefined) {
400 a = parseFloat(a);
401
402 if (a < 0 || a > 1) {
403 return;
404 } // alpha is [0, 1]
405
406 } // now, convert to rgb
407 // code from http://mjijackson.com/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript
408
409
410 if (s === 0) {
411 r = g = b = Math.round(l * 255); // achromatic
412 } else {
413 var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
414 var p = 2 * l - q;
415 r = Math.round(255 * hue2rgb(p, q, h + 1 / 3));
416 g = Math.round(255 * hue2rgb(p, q, h));
417 b = Math.round(255 * hue2rgb(p, q, h - 1 / 3));
418 }
419
420 ret = [r, g, b, a];
421 }
422
423 return ret;
424}; // get [r, g, b, a] from rgb(0, 0, 0) or rgba(0, 0, 0, 0)
425
426var rgb2tuple = function rgb2tuple(rgb) {
427 var ret;
428 var m = new RegExp('^' + rgba + '$').exec(rgb);
429
430 if (m) {
431 ret = [];
432 var isPct = [];
433
434 for (var i = 1; i <= 3; i++) {
435 var channel = m[i];
436
437 if (channel[channel.length - 1] === '%') {
438 isPct[i] = true;
439 }
440
441 channel = parseFloat(channel);
442
443 if (isPct[i]) {
444 channel = channel / 100 * 255; // normalise to [0, 255]
445 }
446
447 if (channel < 0 || channel > 255) {
448 return;
449 } // invalid channel value
450
451
452 ret.push(Math.floor(channel));
453 }
454
455 var atLeastOneIsPct = isPct[1] || isPct[2] || isPct[3];
456 var allArePct = isPct[1] && isPct[2] && isPct[3];
457
458 if (atLeastOneIsPct && !allArePct) {
459 return;
460 } // must all be percent values if one is
461
462
463 var alpha = m[4];
464
465 if (alpha !== undefined) {
466 alpha = parseFloat(alpha);
467
468 if (alpha < 0 || alpha > 1) {
469 return;
470 } // invalid alpha value
471
472
473 ret.push(alpha);
474 }
475 }
476
477 return ret;
478};
479var colorname2tuple = function colorname2tuple(color) {
480 return colors[color.toLowerCase()];
481};
482var color2tuple = function color2tuple(color) {
483 return (array(color) ? color : null) || colorname2tuple(color) || hex2tuple(color) || rgb2tuple(color) || hsl2tuple(color);
484};
485var colors = {
486 // special colour names
487 transparent: [0, 0, 0, 0],
488 // NB alpha === 0
489 // regular colours
490 aliceblue: [240, 248, 255],
491 antiquewhite: [250, 235, 215],
492 aqua: [0, 255, 255],
493 aquamarine: [127, 255, 212],
494 azure: [240, 255, 255],
495 beige: [245, 245, 220],
496 bisque: [255, 228, 196],
497 black: [0, 0, 0],
498 blanchedalmond: [255, 235, 205],
499 blue: [0, 0, 255],
500 blueviolet: [138, 43, 226],
501 brown: [165, 42, 42],
502 burlywood: [222, 184, 135],
503 cadetblue: [95, 158, 160],
504 chartreuse: [127, 255, 0],
505 chocolate: [210, 105, 30],
506 coral: [255, 127, 80],
507 cornflowerblue: [100, 149, 237],
508 cornsilk: [255, 248, 220],
509 crimson: [220, 20, 60],
510 cyan: [0, 255, 255],
511 darkblue: [0, 0, 139],
512 darkcyan: [0, 139, 139],
513 darkgoldenrod: [184, 134, 11],
514 darkgray: [169, 169, 169],
515 darkgreen: [0, 100, 0],
516 darkgrey: [169, 169, 169],
517 darkkhaki: [189, 183, 107],
518 darkmagenta: [139, 0, 139],
519 darkolivegreen: [85, 107, 47],
520 darkorange: [255, 140, 0],
521 darkorchid: [153, 50, 204],
522 darkred: [139, 0, 0],
523 darksalmon: [233, 150, 122],
524 darkseagreen: [143, 188, 143],
525 darkslateblue: [72, 61, 139],
526 darkslategray: [47, 79, 79],
527 darkslategrey: [47, 79, 79],
528 darkturquoise: [0, 206, 209],
529 darkviolet: [148, 0, 211],
530 deeppink: [255, 20, 147],
531 deepskyblue: [0, 191, 255],
532 dimgray: [105, 105, 105],
533 dimgrey: [105, 105, 105],
534 dodgerblue: [30, 144, 255],
535 firebrick: [178, 34, 34],
536 floralwhite: [255, 250, 240],
537 forestgreen: [34, 139, 34],
538 fuchsia: [255, 0, 255],
539 gainsboro: [220, 220, 220],
540 ghostwhite: [248, 248, 255],
541 gold: [255, 215, 0],
542 goldenrod: [218, 165, 32],
543 gray: [128, 128, 128],
544 grey: [128, 128, 128],
545 green: [0, 128, 0],
546 greenyellow: [173, 255, 47],
547 honeydew: [240, 255, 240],
548 hotpink: [255, 105, 180],
549 indianred: [205, 92, 92],
550 indigo: [75, 0, 130],
551 ivory: [255, 255, 240],
552 khaki: [240, 230, 140],
553 lavender: [230, 230, 250],
554 lavenderblush: [255, 240, 245],
555 lawngreen: [124, 252, 0],
556 lemonchiffon: [255, 250, 205],
557 lightblue: [173, 216, 230],
558 lightcoral: [240, 128, 128],
559 lightcyan: [224, 255, 255],
560 lightgoldenrodyellow: [250, 250, 210],
561 lightgray: [211, 211, 211],
562 lightgreen: [144, 238, 144],
563 lightgrey: [211, 211, 211],
564 lightpink: [255, 182, 193],
565 lightsalmon: [255, 160, 122],
566 lightseagreen: [32, 178, 170],
567 lightskyblue: [135, 206, 250],
568 lightslategray: [119, 136, 153],
569 lightslategrey: [119, 136, 153],
570 lightsteelblue: [176, 196, 222],
571 lightyellow: [255, 255, 224],
572 lime: [0, 255, 0],
573 limegreen: [50, 205, 50],
574 linen: [250, 240, 230],
575 magenta: [255, 0, 255],
576 maroon: [128, 0, 0],
577 mediumaquamarine: [102, 205, 170],
578 mediumblue: [0, 0, 205],
579 mediumorchid: [186, 85, 211],
580 mediumpurple: [147, 112, 219],
581 mediumseagreen: [60, 179, 113],
582 mediumslateblue: [123, 104, 238],
583 mediumspringgreen: [0, 250, 154],
584 mediumturquoise: [72, 209, 204],
585 mediumvioletred: [199, 21, 133],
586 midnightblue: [25, 25, 112],
587 mintcream: [245, 255, 250],
588 mistyrose: [255, 228, 225],
589 moccasin: [255, 228, 181],
590 navajowhite: [255, 222, 173],
591 navy: [0, 0, 128],
592 oldlace: [253, 245, 230],
593 olive: [128, 128, 0],
594 olivedrab: [107, 142, 35],
595 orange: [255, 165, 0],
596 orangered: [255, 69, 0],
597 orchid: [218, 112, 214],
598 palegoldenrod: [238, 232, 170],
599 palegreen: [152, 251, 152],
600 paleturquoise: [175, 238, 238],
601 palevioletred: [219, 112, 147],
602 papayawhip: [255, 239, 213],
603 peachpuff: [255, 218, 185],
604 peru: [205, 133, 63],
605 pink: [255, 192, 203],
606 plum: [221, 160, 221],
607 powderblue: [176, 224, 230],
608 purple: [128, 0, 128],
609 red: [255, 0, 0],
610 rosybrown: [188, 143, 143],
611 royalblue: [65, 105, 225],
612 saddlebrown: [139, 69, 19],
613 salmon: [250, 128, 114],
614 sandybrown: [244, 164, 96],
615 seagreen: [46, 139, 87],
616 seashell: [255, 245, 238],
617 sienna: [160, 82, 45],
618 silver: [192, 192, 192],
619 skyblue: [135, 206, 235],
620 slateblue: [106, 90, 205],
621 slategray: [112, 128, 144],
622 slategrey: [112, 128, 144],
623 snow: [255, 250, 250],
624 springgreen: [0, 255, 127],
625 steelblue: [70, 130, 180],
626 tan: [210, 180, 140],
627 teal: [0, 128, 128],
628 thistle: [216, 191, 216],
629 tomato: [255, 99, 71],
630 turquoise: [64, 224, 208],
631 violet: [238, 130, 238],
632 wheat: [245, 222, 179],
633 white: [255, 255, 255],
634 whitesmoke: [245, 245, 245],
635 yellow: [255, 255, 0],
636 yellowgreen: [154, 205, 50]
637};
638
639var setMap = function setMap(options) {
640 var obj = options.map;
641 var keys = options.keys;
642 var l = keys.length;
643
644 for (var i = 0; i < l; i++) {
645 var key = keys[i];
646
647 if (plainObject(key)) {
648 throw Error('Tried to set map with object key');
649 }
650
651 if (i < keys.length - 1) {
652 // extend the map if necessary
653 if (obj[key] == null) {
654 obj[key] = {};
655 }
656
657 obj = obj[key];
658 } else {
659 // set the value
660 obj[key] = options.value;
661 }
662 }
663}; // gets the value in a map even if it's not built in places
664
665var getMap = function getMap(options) {
666 var obj = options.map;
667 var keys = options.keys;
668 var l = keys.length;
669
670 for (var i = 0; i < l; i++) {
671 var key = keys[i];
672
673 if (plainObject(key)) {
674 throw Error('Tried to get map with object key');
675 }
676
677 obj = obj[key];
678
679 if (obj == null) {
680 return obj;
681 }
682 }
683
684 return obj;
685}; // deletes the entry in the map
686
687var performance = window$1 ? window$1.performance : null;
688var pnow = performance && performance.now ? function () {
689 return performance.now();
690} : function () {
691 return Date.now();
692};
693
694var raf = function () {
695 if (window$1) {
696 if (window$1.requestAnimationFrame) {
697 return function (fn) {
698 window$1.requestAnimationFrame(fn);
699 };
700 } else if (window$1.mozRequestAnimationFrame) {
701 return function (fn) {
702 window$1.mozRequestAnimationFrame(fn);
703 };
704 } else if (window$1.webkitRequestAnimationFrame) {
705 return function (fn) {
706 window$1.webkitRequestAnimationFrame(fn);
707 };
708 } else if (window$1.msRequestAnimationFrame) {
709 return function (fn) {
710 window$1.msRequestAnimationFrame(fn);
711 };
712 }
713 }
714
715 return function (fn) {
716 if (fn) {
717 setTimeout(function () {
718 fn(pnow());
719 }, 1000 / 60);
720 }
721 };
722}();
723
724var requestAnimationFrame = function requestAnimationFrame(fn) {
725 return raf(fn);
726};
727var performanceNow = pnow;
728
729var DEFAULT_HASH_SEED = 9261;
730var K = 65599; // 37 also works pretty well
731
732var DEFAULT_HASH_SEED_ALT = 5381;
733var hashIterableInts = function hashIterableInts(iterator) {
734 var seed = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : DEFAULT_HASH_SEED;
735 // sdbm/string-hash
736 var hash = seed;
737 var entry;
738
739 for (;;) {
740 entry = iterator.next();
741
742 if (entry.done) {
743 break;
744 }
745
746 hash = hash * K + entry.value | 0;
747 }
748
749 return hash;
750};
751var hashInt = function hashInt(num) {
752 var seed = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : DEFAULT_HASH_SEED;
753 // sdbm/string-hash
754 return seed * K + num | 0;
755};
756var hashIntAlt = function hashIntAlt(num) {
757 var seed = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : DEFAULT_HASH_SEED_ALT;
758 // djb2/string-hash
759 return (seed << 5) + seed + num | 0;
760};
761var combineHashes = function combineHashes(hash1, hash2) {
762 return hash1 * 0x200000 + hash2;
763};
764var combineHashesArray = function combineHashesArray(hashes) {
765 return hashes[0] * 0x200000 + hashes[1];
766};
767var hashArrays = function hashArrays(hashes1, hashes2) {
768 return [hashInt(hashes1[0], hashes2[0]), hashIntAlt(hashes1[1], hashes2[1])];
769};
770var hashIntsArray = function hashIntsArray(ints, seed) {
771 var entry = {
772 value: 0,
773 done: false
774 };
775 var i = 0;
776 var length = ints.length;
777 var iterator = {
778 next: function next() {
779 if (i < length) {
780 entry.value = ints[i++];
781 } else {
782 entry.done = true;
783 }
784
785 return entry;
786 }
787 };
788 return hashIterableInts(iterator, seed);
789};
790var hashString = function hashString(str, seed) {
791 var entry = {
792 value: 0,
793 done: false
794 };
795 var i = 0;
796 var length = str.length;
797 var iterator = {
798 next: function next() {
799 if (i < length) {
800 entry.value = str.charCodeAt(i++);
801 } else {
802 entry.done = true;
803 }
804
805 return entry;
806 }
807 };
808 return hashIterableInts(iterator, seed);
809};
810var hashStrings = function hashStrings() {
811 return hashStringsArray(arguments);
812};
813var hashStringsArray = function hashStringsArray(strs) {
814 var hash;
815
816 for (var i = 0; i < strs.length; i++) {
817 var str = strs[i];
818
819 if (i === 0) {
820 hash = hashString(str);
821 } else {
822 hash = hashString(str, hash);
823 }
824 }
825
826 return hash;
827};
828
829/*global console */
830var warningsEnabled = true;
831var warnSupported = console.warn != null; // eslint-disable-line no-console
832
833var traceSupported = console.trace != null; // eslint-disable-line no-console
834
835var MAX_INT$1 = Number.MAX_SAFE_INTEGER || 9007199254740991;
836var trueify = function trueify() {
837 return true;
838};
839var falsify = function falsify() {
840 return false;
841};
842var zeroify = function zeroify() {
843 return 0;
844};
845var noop$1 = function noop() {};
846var error = function error(msg) {
847 throw new Error(msg);
848};
849var warnings = function warnings(enabled) {
850 if (enabled !== undefined) {
851 warningsEnabled = !!enabled;
852 } else {
853 return warningsEnabled;
854 }
855};
856var warn = function warn(msg) {
857 /* eslint-disable no-console */
858 if (!warnings()) {
859 return;
860 }
861
862 if (warnSupported) {
863 console.warn(msg);
864 } else {
865 console.log(msg);
866
867 if (traceSupported) {
868 console.trace();
869 }
870 }
871};
872/* eslint-enable */
873
874var clone = function clone(obj) {
875 return extend({}, obj);
876}; // gets a shallow copy of the argument
877
878var copy = function copy(obj) {
879 if (obj == null) {
880 return obj;
881 }
882
883 if (array(obj)) {
884 return obj.slice();
885 } else if (plainObject(obj)) {
886 return clone(obj);
887 } else {
888 return obj;
889 }
890};
891var copyArray = function copyArray(arr) {
892 return arr.slice();
893};
894var uuid = function uuid(a, b
895/* placeholders */
896) {
897 for ( // loop :)
898 b = a = ''; // b - result , a - numeric letiable
899 a++ < 36; //
900 b += a * 51 & 52 // if "a" is not 9 or 14 or 19 or 24
901 ? // return a random number or 4
902 (a ^ 15 // if "a" is not 15
903 ? // generate a random number from 0 to 15
904 8 ^ Math.random() * (a ^ 20 ? 16 : 4) // unless "a" is 20, in which case a random number from 8 to 11
905 : 4 // otherwise 4
906 ).toString(16) : '-' // in other cases (if "a" is 9,14,19,24) insert "-"
907 ) {
908 }
909
910 return b;
911};
912var _staticEmptyObject = {};
913var staticEmptyObject = function staticEmptyObject() {
914 return _staticEmptyObject;
915};
916var defaults$g = function defaults(_defaults) {
917 var keys = Object.keys(_defaults);
918 return function (opts) {
919 var filledOpts = {};
920
921 for (var i = 0; i < keys.length; i++) {
922 var key = keys[i];
923 var optVal = opts == null ? undefined : opts[key];
924 filledOpts[key] = optVal === undefined ? _defaults[key] : optVal;
925 }
926
927 return filledOpts;
928 };
929};
930var removeFromArray = function removeFromArray(arr, ele, oneCopy) {
931 for (var i = arr.length - 1; i >= 0; i--) {
932 if (arr[i] === ele) {
933 arr.splice(i, 1);
934
935 if (oneCopy) {
936 break;
937 }
938 }
939 }
940};
941var clearArray = function clearArray(arr) {
942 arr.splice(0, arr.length);
943};
944var push = function push(arr, otherArr) {
945 for (var i = 0; i < otherArr.length; i++) {
946 var el = otherArr[i];
947 arr.push(el);
948 }
949};
950var getPrefixedProperty = function getPrefixedProperty(obj, propName, prefix) {
951 if (prefix) {
952 propName = prependCamel(prefix, propName); // e.g. (labelWidth, source) => sourceLabelWidth
953 }
954
955 return obj[propName];
956};
957var setPrefixedProperty = function setPrefixedProperty(obj, propName, prefix, value) {
958 if (prefix) {
959 propName = prependCamel(prefix, propName); // e.g. (labelWidth, source) => sourceLabelWidth
960 }
961
962 obj[propName] = value;
963};
964
965/* global Map */
966var ObjectMap = /*#__PURE__*/function () {
967 function ObjectMap() {
968 _classCallCheck(this, ObjectMap);
969
970 this._obj = {};
971 }
972
973 _createClass(ObjectMap, [{
974 key: "set",
975 value: function set(key, val) {
976 this._obj[key] = val;
977 return this;
978 }
979 }, {
980 key: "delete",
981 value: function _delete(key) {
982 this._obj[key] = undefined;
983 return this;
984 }
985 }, {
986 key: "clear",
987 value: function clear() {
988 this._obj = {};
989 }
990 }, {
991 key: "has",
992 value: function has(key) {
993 return this._obj[key] !== undefined;
994 }
995 }, {
996 key: "get",
997 value: function get(key) {
998 return this._obj[key];
999 }
1000 }]);
1001
1002 return ObjectMap;
1003}();
1004
1005var Map$1 = typeof Map !== 'undefined' ? Map : ObjectMap;
1006
1007/* global Set */
1008var undef = "undefined" ;
1009
1010var ObjectSet = /*#__PURE__*/function () {
1011 function ObjectSet(arrayOrObjectSet) {
1012 _classCallCheck(this, ObjectSet);
1013
1014 this._obj = Object.create(null);
1015 this.size = 0;
1016
1017 if (arrayOrObjectSet != null) {
1018 var arr;
1019
1020 if (arrayOrObjectSet.instanceString != null && arrayOrObjectSet.instanceString() === this.instanceString()) {
1021 arr = arrayOrObjectSet.toArray();
1022 } else {
1023 arr = arrayOrObjectSet;
1024 }
1025
1026 for (var i = 0; i < arr.length; i++) {
1027 this.add(arr[i]);
1028 }
1029 }
1030 }
1031
1032 _createClass(ObjectSet, [{
1033 key: "instanceString",
1034 value: function instanceString() {
1035 return 'set';
1036 }
1037 }, {
1038 key: "add",
1039 value: function add(val) {
1040 var o = this._obj;
1041
1042 if (o[val] !== 1) {
1043 o[val] = 1;
1044 this.size++;
1045 }
1046 }
1047 }, {
1048 key: "delete",
1049 value: function _delete(val) {
1050 var o = this._obj;
1051
1052 if (o[val] === 1) {
1053 o[val] = 0;
1054 this.size--;
1055 }
1056 }
1057 }, {
1058 key: "clear",
1059 value: function clear() {
1060 this._obj = Object.create(null);
1061 }
1062 }, {
1063 key: "has",
1064 value: function has(val) {
1065 return this._obj[val] === 1;
1066 }
1067 }, {
1068 key: "toArray",
1069 value: function toArray() {
1070 var _this = this;
1071
1072 return Object.keys(this._obj).filter(function (key) {
1073 return _this.has(key);
1074 });
1075 }
1076 }, {
1077 key: "forEach",
1078 value: function forEach(callback, thisArg) {
1079 return this.toArray().forEach(callback, thisArg);
1080 }
1081 }]);
1082
1083 return ObjectSet;
1084}();
1085
1086var Set$1 = (typeof Set === "undefined" ? "undefined" : _typeof(Set)) !== undef ? Set : ObjectSet;
1087
1088var Element = function Element(cy, params) {
1089 var restore = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
1090
1091 if (cy === undefined || params === undefined || !core(cy)) {
1092 error('An element must have a core reference and parameters set');
1093 return;
1094 }
1095
1096 var group = params.group; // try to automatically infer the group if unspecified
1097
1098 if (group == null) {
1099 if (params.data && params.data.source != null && params.data.target != null) {
1100 group = 'edges';
1101 } else {
1102 group = 'nodes';
1103 }
1104 } // validate group
1105
1106
1107 if (group !== 'nodes' && group !== 'edges') {
1108 error('An element must be of type `nodes` or `edges`; you specified `' + group + '`');
1109 return;
1110 } // make the element array-like, just like a collection
1111
1112
1113 this.length = 1;
1114 this[0] = this; // NOTE: when something is added here, add also to ele.json()
1115
1116 var _p = this._private = {
1117 cy: cy,
1118 single: true,
1119 // indicates this is an element
1120 data: params.data || {},
1121 // data object
1122 position: params.position || {
1123 x: 0,
1124 y: 0
1125 },
1126 // (x, y) position pair
1127 autoWidth: undefined,
1128 // width and height of nodes calculated by the renderer when set to special 'auto' value
1129 autoHeight: undefined,
1130 autoPadding: undefined,
1131 compoundBoundsClean: false,
1132 // whether the compound dimensions need to be recalculated the next time dimensions are read
1133 listeners: [],
1134 // array of bound listeners
1135 group: group,
1136 // string; 'nodes' or 'edges'
1137 style: {},
1138 // properties as set by the style
1139 rstyle: {},
1140 // properties for style sent from the renderer to the core
1141 styleCxts: [],
1142 // applied style contexts from the styler
1143 styleKeys: {},
1144 // per-group keys of style property values
1145 removed: true,
1146 // whether it's inside the vis; true if removed (set true here since we call restore)
1147 selected: params.selected ? true : false,
1148 // whether it's selected
1149 selectable: params.selectable === undefined ? true : params.selectable ? true : false,
1150 // whether it's selectable
1151 locked: params.locked ? true : false,
1152 // whether the element is locked (cannot be moved)
1153 grabbed: false,
1154 // whether the element is grabbed by the mouse; renderer sets this privately
1155 grabbable: params.grabbable === undefined ? true : params.grabbable ? true : false,
1156 // whether the element can be grabbed
1157 pannable: params.pannable === undefined ? group === 'edges' ? true : false : params.pannable ? true : false,
1158 // whether the element has passthrough panning enabled
1159 active: false,
1160 // whether the element is active from user interaction
1161 classes: new Set$1(),
1162 // map ( className => true )
1163 animation: {
1164 // object for currently-running animations
1165 current: [],
1166 queue: []
1167 },
1168 rscratch: {},
1169 // object in which the renderer can store information
1170 scratch: params.scratch || {},
1171 // scratch objects
1172 edges: [],
1173 // array of connected edges
1174 children: [],
1175 // array of children
1176 parent: params.parent && params.parent.isNode() ? params.parent : null,
1177 // parent ref
1178 traversalCache: {},
1179 // cache of output of traversal functions
1180 backgrounding: false,
1181 // whether background images are loading
1182 bbCache: null,
1183 // cache of the current bounding box
1184 bbCacheShift: {
1185 x: 0,
1186 y: 0
1187 },
1188 // shift applied to cached bb to be applied on next get
1189 bodyBounds: null,
1190 // bounds cache of element body, w/o overlay
1191 overlayBounds: null,
1192 // bounds cache of element body, including overlay
1193 labelBounds: {
1194 // bounds cache of labels
1195 all: null,
1196 source: null,
1197 target: null,
1198 main: null
1199 },
1200 arrowBounds: {
1201 // bounds cache of edge arrows
1202 source: null,
1203 target: null,
1204 'mid-source': null,
1205 'mid-target': null
1206 }
1207 };
1208
1209 if (_p.position.x == null) {
1210 _p.position.x = 0;
1211 }
1212
1213 if (_p.position.y == null) {
1214 _p.position.y = 0;
1215 } // renderedPosition overrides if specified
1216
1217
1218 if (params.renderedPosition) {
1219 var rpos = params.renderedPosition;
1220 var pan = cy.pan();
1221 var zoom = cy.zoom();
1222 _p.position = {
1223 x: (rpos.x - pan.x) / zoom,
1224 y: (rpos.y - pan.y) / zoom
1225 };
1226 }
1227
1228 var classes = [];
1229
1230 if (array(params.classes)) {
1231 classes = params.classes;
1232 } else if (string(params.classes)) {
1233 classes = params.classes.split(/\s+/);
1234 }
1235
1236 for (var i = 0, l = classes.length; i < l; i++) {
1237 var cls = classes[i];
1238
1239 if (!cls || cls === '') {
1240 continue;
1241 }
1242
1243 _p.classes.add(cls);
1244 }
1245
1246 this.createEmitter();
1247 var bypass = params.style || params.css;
1248
1249 if (bypass) {
1250 warn('Setting a `style` bypass at element creation should be done only when absolutely necessary. Try to use the stylesheet instead.');
1251 this.style(bypass);
1252 }
1253
1254 if (restore === undefined || restore) {
1255 this.restore();
1256 }
1257};
1258
1259var defineSearch = function defineSearch(params) {
1260 params = {
1261 bfs: params.bfs || !params.dfs,
1262 dfs: params.dfs || !params.bfs
1263 }; // from pseudocode on wikipedia
1264
1265 return function searchFn(roots, fn, directed) {
1266 var options;
1267
1268 if (plainObject(roots) && !elementOrCollection(roots)) {
1269 options = roots;
1270 roots = options.roots || options.root;
1271 fn = options.visit;
1272 directed = options.directed;
1273 }
1274
1275 directed = arguments.length === 2 && !fn$6(fn) ? fn : directed;
1276 fn = fn$6(fn) ? fn : function () {};
1277 var cy = this._private.cy;
1278 var v = roots = string(roots) ? this.filter(roots) : roots;
1279 var Q = [];
1280 var connectedNodes = [];
1281 var connectedBy = {};
1282 var id2depth = {};
1283 var V = {};
1284 var j = 0;
1285 var found;
1286
1287 var _this$byGroup = this.byGroup(),
1288 nodes = _this$byGroup.nodes,
1289 edges = _this$byGroup.edges; // enqueue v
1290
1291
1292 for (var i = 0; i < v.length; i++) {
1293 var vi = v[i];
1294 var viId = vi.id();
1295
1296 if (vi.isNode()) {
1297 Q.unshift(vi);
1298
1299 if (params.bfs) {
1300 V[viId] = true;
1301 connectedNodes.push(vi);
1302 }
1303
1304 id2depth[viId] = 0;
1305 }
1306 }
1307
1308 var _loop = function _loop() {
1309 var v = params.bfs ? Q.shift() : Q.pop();
1310 var vId = v.id();
1311
1312 if (params.dfs) {
1313 if (V[vId]) {
1314 return "continue";
1315 }
1316
1317 V[vId] = true;
1318 connectedNodes.push(v);
1319 }
1320
1321 var depth = id2depth[vId];
1322 var prevEdge = connectedBy[vId];
1323 var src = prevEdge != null ? prevEdge.source() : null;
1324 var tgt = prevEdge != null ? prevEdge.target() : null;
1325 var prevNode = prevEdge == null ? undefined : v.same(src) ? tgt[0] : src[0];
1326 var ret = void 0;
1327 ret = fn(v, prevEdge, prevNode, j++, depth);
1328
1329 if (ret === true) {
1330 found = v;
1331 return "break";
1332 }
1333
1334 if (ret === false) {
1335 return "break";
1336 }
1337
1338 var vwEdges = v.connectedEdges().filter(function (e) {
1339 return (!directed || e.source().same(v)) && edges.has(e);
1340 });
1341
1342 for (var _i2 = 0; _i2 < vwEdges.length; _i2++) {
1343 var e = vwEdges[_i2];
1344 var w = e.connectedNodes().filter(function (n) {
1345 return !n.same(v) && nodes.has(n);
1346 });
1347 var wId = w.id();
1348
1349 if (w.length !== 0 && !V[wId]) {
1350 w = w[0];
1351 Q.push(w);
1352
1353 if (params.bfs) {
1354 V[wId] = true;
1355 connectedNodes.push(w);
1356 }
1357
1358 connectedBy[wId] = e;
1359 id2depth[wId] = id2depth[vId] + 1;
1360 }
1361 }
1362 };
1363
1364 while (Q.length !== 0) {
1365 var _ret = _loop();
1366
1367 if (_ret === "continue") continue;
1368 if (_ret === "break") break;
1369 }
1370
1371 var connectedEles = cy.collection();
1372
1373 for (var _i = 0; _i < connectedNodes.length; _i++) {
1374 var node = connectedNodes[_i];
1375 var edge = connectedBy[node.id()];
1376
1377 if (edge != null) {
1378 connectedEles.push(edge);
1379 }
1380
1381 connectedEles.push(node);
1382 }
1383
1384 return {
1385 path: cy.collection(connectedEles),
1386 found: cy.collection(found)
1387 };
1388 };
1389}; // search, spanning trees, etc
1390
1391
1392var elesfn$v = {
1393 breadthFirstSearch: defineSearch({
1394 bfs: true
1395 }),
1396 depthFirstSearch: defineSearch({
1397 dfs: true
1398 })
1399}; // nice, short mathematical alias
1400
1401elesfn$v.bfs = elesfn$v.breadthFirstSearch;
1402elesfn$v.dfs = elesfn$v.depthFirstSearch;
1403
1404var dijkstraDefaults = defaults$g({
1405 root: null,
1406 weight: function weight(edge) {
1407 return 1;
1408 },
1409 directed: false
1410});
1411var elesfn$u = {
1412 dijkstra: function dijkstra(options) {
1413 if (!plainObject(options)) {
1414 var args = arguments;
1415 options = {
1416 root: args[0],
1417 weight: args[1],
1418 directed: args[2]
1419 };
1420 }
1421
1422 var _dijkstraDefaults = dijkstraDefaults(options),
1423 root = _dijkstraDefaults.root,
1424 weight = _dijkstraDefaults.weight,
1425 directed = _dijkstraDefaults.directed;
1426
1427 var eles = this;
1428 var weightFn = weight;
1429 var source = string(root) ? this.filter(root)[0] : root[0];
1430 var dist = {};
1431 var prev = {};
1432 var knownDist = {};
1433
1434 var _this$byGroup = this.byGroup(),
1435 nodes = _this$byGroup.nodes,
1436 edges = _this$byGroup.edges;
1437
1438 edges.unmergeBy(function (ele) {
1439 return ele.isLoop();
1440 });
1441
1442 var getDist = function getDist(node) {
1443 return dist[node.id()];
1444 };
1445
1446 var setDist = function setDist(node, d) {
1447 dist[node.id()] = d;
1448 Q.updateItem(node);
1449 };
1450
1451 var Q = new Heap(function (a, b) {
1452 return getDist(a) - getDist(b);
1453 });
1454
1455 for (var i = 0; i < nodes.length; i++) {
1456 var node = nodes[i];
1457 dist[node.id()] = node.same(source) ? 0 : Infinity;
1458 Q.push(node);
1459 }
1460
1461 var distBetween = function distBetween(u, v) {
1462 var uvs = (directed ? u.edgesTo(v) : u.edgesWith(v)).intersect(edges);
1463 var smallestDistance = Infinity;
1464 var smallestEdge;
1465
1466 for (var _i = 0; _i < uvs.length; _i++) {
1467 var edge = uvs[_i];
1468
1469 var _weight = weightFn(edge);
1470
1471 if (_weight < smallestDistance || !smallestEdge) {
1472 smallestDistance = _weight;
1473 smallestEdge = edge;
1474 }
1475 }
1476
1477 return {
1478 edge: smallestEdge,
1479 dist: smallestDistance
1480 };
1481 };
1482
1483 while (Q.size() > 0) {
1484 var u = Q.pop();
1485 var smalletsDist = getDist(u);
1486 var uid = u.id();
1487 knownDist[uid] = smalletsDist;
1488
1489 if (smalletsDist === Infinity) {
1490 continue;
1491 }
1492
1493 var neighbors = u.neighborhood().intersect(nodes);
1494
1495 for (var _i2 = 0; _i2 < neighbors.length; _i2++) {
1496 var v = neighbors[_i2];
1497 var vid = v.id();
1498 var vDist = distBetween(u, v);
1499 var alt = smalletsDist + vDist.dist;
1500
1501 if (alt < getDist(v)) {
1502 setDist(v, alt);
1503 prev[vid] = {
1504 node: u,
1505 edge: vDist.edge
1506 };
1507 }
1508 } // for
1509
1510 } // while
1511
1512
1513 return {
1514 distanceTo: function distanceTo(node) {
1515 var target = string(node) ? nodes.filter(node)[0] : node[0];
1516 return knownDist[target.id()];
1517 },
1518 pathTo: function pathTo(node) {
1519 var target = string(node) ? nodes.filter(node)[0] : node[0];
1520 var S = [];
1521 var u = target;
1522 var uid = u.id();
1523
1524 if (target.length > 0) {
1525 S.unshift(target);
1526
1527 while (prev[uid]) {
1528 var p = prev[uid];
1529 S.unshift(p.edge);
1530 S.unshift(p.node);
1531 u = p.node;
1532 uid = u.id();
1533 }
1534 }
1535
1536 return eles.spawn(S);
1537 }
1538 };
1539 }
1540};
1541
1542var elesfn$t = {
1543 // kruskal's algorithm (finds min spanning tree, assuming undirected graph)
1544 // implemented from pseudocode from wikipedia
1545 kruskal: function kruskal(weightFn) {
1546 weightFn = weightFn || function (edge) {
1547 return 1;
1548 };
1549
1550 var _this$byGroup = this.byGroup(),
1551 nodes = _this$byGroup.nodes,
1552 edges = _this$byGroup.edges;
1553
1554 var numNodes = nodes.length;
1555 var forest = new Array(numNodes);
1556 var A = nodes; // assumes byGroup() creates new collections that can be safely mutated
1557
1558 var findSetIndex = function findSetIndex(ele) {
1559 for (var i = 0; i < forest.length; i++) {
1560 var eles = forest[i];
1561
1562 if (eles.has(ele)) {
1563 return i;
1564 }
1565 }
1566 }; // start with one forest per node
1567
1568
1569 for (var i = 0; i < numNodes; i++) {
1570 forest[i] = this.spawn(nodes[i]);
1571 }
1572
1573 var S = edges.sort(function (a, b) {
1574 return weightFn(a) - weightFn(b);
1575 });
1576
1577 for (var _i = 0; _i < S.length; _i++) {
1578 var edge = S[_i];
1579 var u = edge.source()[0];
1580 var v = edge.target()[0];
1581 var setUIndex = findSetIndex(u);
1582 var setVIndex = findSetIndex(v);
1583 var setU = forest[setUIndex];
1584 var setV = forest[setVIndex];
1585
1586 if (setUIndex !== setVIndex) {
1587 A.merge(edge); // combine forests for u and v
1588
1589 setU.merge(setV);
1590 forest.splice(setVIndex, 1);
1591 }
1592 }
1593
1594 return A;
1595 }
1596};
1597
1598var aStarDefaults = defaults$g({
1599 root: null,
1600 goal: null,
1601 weight: function weight(edge) {
1602 return 1;
1603 },
1604 heuristic: function heuristic(edge) {
1605 return 0;
1606 },
1607 directed: false
1608});
1609var elesfn$s = {
1610 // Implemented from pseudocode from wikipedia
1611 aStar: function aStar(options) {
1612 var cy = this.cy();
1613
1614 var _aStarDefaults = aStarDefaults(options),
1615 root = _aStarDefaults.root,
1616 goal = _aStarDefaults.goal,
1617 heuristic = _aStarDefaults.heuristic,
1618 directed = _aStarDefaults.directed,
1619 weight = _aStarDefaults.weight;
1620
1621 root = cy.collection(root)[0];
1622 goal = cy.collection(goal)[0];
1623 var sid = root.id();
1624 var tid = goal.id();
1625 var gScore = {};
1626 var fScore = {};
1627 var closedSetIds = {};
1628 var openSet = new Heap(function (a, b) {
1629 return fScore[a.id()] - fScore[b.id()];
1630 });
1631 var openSetIds = new Set$1();
1632 var cameFrom = {};
1633 var cameFromEdge = {};
1634
1635 var addToOpenSet = function addToOpenSet(ele, id) {
1636 openSet.push(ele);
1637 openSetIds.add(id);
1638 };
1639
1640 var cMin, cMinId;
1641
1642 var popFromOpenSet = function popFromOpenSet() {
1643 cMin = openSet.pop();
1644 cMinId = cMin.id();
1645 openSetIds["delete"](cMinId);
1646 };
1647
1648 var isInOpenSet = function isInOpenSet(id) {
1649 return openSetIds.has(id);
1650 };
1651
1652 addToOpenSet(root, sid);
1653 gScore[sid] = 0;
1654 fScore[sid] = heuristic(root); // Counter
1655
1656 var steps = 0; // Main loop
1657
1658 while (openSet.size() > 0) {
1659 popFromOpenSet();
1660 steps++; // If we've found our goal, then we are done
1661
1662 if (cMinId === tid) {
1663 var path = [];
1664 var pathNode = goal;
1665 var pathNodeId = tid;
1666 var pathEdge = cameFromEdge[pathNodeId];
1667
1668 for (;;) {
1669 path.unshift(pathNode);
1670
1671 if (pathEdge != null) {
1672 path.unshift(pathEdge);
1673 }
1674
1675 pathNode = cameFrom[pathNodeId];
1676
1677 if (pathNode == null) {
1678 break;
1679 }
1680
1681 pathNodeId = pathNode.id();
1682 pathEdge = cameFromEdge[pathNodeId];
1683 }
1684
1685 return {
1686 found: true,
1687 distance: gScore[cMinId],
1688 path: this.spawn(path),
1689 steps: steps
1690 };
1691 } // Add cMin to processed nodes
1692
1693
1694 closedSetIds[cMinId] = true; // Update scores for neighbors of cMin
1695 // Take into account if graph is directed or not
1696
1697 var vwEdges = cMin._private.edges;
1698
1699 for (var i = 0; i < vwEdges.length; i++) {
1700 var e = vwEdges[i]; // edge must be in set of calling eles
1701
1702 if (!this.hasElementWithId(e.id())) {
1703 continue;
1704 } // cMin must be the source of edge if directed
1705
1706
1707 if (directed && e.data('source') !== cMinId) {
1708 continue;
1709 }
1710
1711 var wSrc = e.source();
1712 var wTgt = e.target();
1713 var w = wSrc.id() !== cMinId ? wSrc : wTgt;
1714 var wid = w.id(); // node must be in set of calling eles
1715
1716 if (!this.hasElementWithId(wid)) {
1717 continue;
1718 } // if node is in closedSet, ignore it
1719
1720
1721 if (closedSetIds[wid]) {
1722 continue;
1723 } // New tentative score for node w
1724
1725
1726 var tempScore = gScore[cMinId] + weight(e); // Update gScore for node w if:
1727 // w not present in openSet
1728 // OR
1729 // tentative gScore is less than previous value
1730 // w not in openSet
1731
1732 if (!isInOpenSet(wid)) {
1733 gScore[wid] = tempScore;
1734 fScore[wid] = tempScore + heuristic(w);
1735 addToOpenSet(w, wid);
1736 cameFrom[wid] = cMin;
1737 cameFromEdge[wid] = e;
1738 continue;
1739 } // w already in openSet, but with greater gScore
1740
1741
1742 if (tempScore < gScore[wid]) {
1743 gScore[wid] = tempScore;
1744 fScore[wid] = tempScore + heuristic(w);
1745 cameFrom[wid] = cMin;
1746 cameFromEdge[wid] = e;
1747 }
1748 } // End of neighbors update
1749
1750 } // End of main loop
1751 // If we've reached here, then we've not reached our goal
1752
1753
1754 return {
1755 found: false,
1756 distance: undefined,
1757 path: undefined,
1758 steps: steps
1759 };
1760 }
1761}; // elesfn
1762
1763var floydWarshallDefaults = defaults$g({
1764 weight: function weight(edge) {
1765 return 1;
1766 },
1767 directed: false
1768});
1769var elesfn$r = {
1770 // Implemented from pseudocode from wikipedia
1771 floydWarshall: function floydWarshall(options) {
1772 var cy = this.cy();
1773
1774 var _floydWarshallDefault = floydWarshallDefaults(options),
1775 weight = _floydWarshallDefault.weight,
1776 directed = _floydWarshallDefault.directed;
1777
1778 var weightFn = weight;
1779
1780 var _this$byGroup = this.byGroup(),
1781 nodes = _this$byGroup.nodes,
1782 edges = _this$byGroup.edges;
1783
1784 var N = nodes.length;
1785 var Nsq = N * N;
1786
1787 var indexOf = function indexOf(node) {
1788 return nodes.indexOf(node);
1789 };
1790
1791 var atIndex = function atIndex(i) {
1792 return nodes[i];
1793 }; // Initialize distance matrix
1794
1795
1796 var dist = new Array(Nsq);
1797
1798 for (var n = 0; n < Nsq; n++) {
1799 var j = n % N;
1800 var i = (n - j) / N;
1801
1802 if (i === j) {
1803 dist[n] = 0;
1804 } else {
1805 dist[n] = Infinity;
1806 }
1807 } // Initialize matrix used for path reconstruction
1808 // Initialize distance matrix
1809
1810
1811 var next = new Array(Nsq);
1812 var edgeNext = new Array(Nsq); // Process edges
1813
1814 for (var _i = 0; _i < edges.length; _i++) {
1815 var edge = edges[_i];
1816 var src = edge.source()[0];
1817 var tgt = edge.target()[0];
1818
1819 if (src === tgt) {
1820 continue;
1821 } // exclude loops
1822
1823
1824 var s = indexOf(src);
1825 var t = indexOf(tgt);
1826 var st = s * N + t; // source to target index
1827
1828 var _weight = weightFn(edge); // Check if already process another edge between same 2 nodes
1829
1830
1831 if (dist[st] > _weight) {
1832 dist[st] = _weight;
1833 next[st] = t;
1834 edgeNext[st] = edge;
1835 } // If undirected graph, process 'reversed' edge
1836
1837
1838 if (!directed) {
1839 var ts = t * N + s; // target to source index
1840
1841 if (!directed && dist[ts] > _weight) {
1842 dist[ts] = _weight;
1843 next[ts] = s;
1844 edgeNext[ts] = edge;
1845 }
1846 }
1847 } // Main loop
1848
1849
1850 for (var k = 0; k < N; k++) {
1851 for (var _i2 = 0; _i2 < N; _i2++) {
1852 var ik = _i2 * N + k;
1853
1854 for (var _j = 0; _j < N; _j++) {
1855 var ij = _i2 * N + _j;
1856 var kj = k * N + _j;
1857
1858 if (dist[ik] + dist[kj] < dist[ij]) {
1859 dist[ij] = dist[ik] + dist[kj];
1860 next[ij] = next[ik];
1861 }
1862 }
1863 }
1864 }
1865
1866 var getArgEle = function getArgEle(ele) {
1867 return (string(ele) ? cy.filter(ele) : ele)[0];
1868 };
1869
1870 var indexOfArgEle = function indexOfArgEle(ele) {
1871 return indexOf(getArgEle(ele));
1872 };
1873
1874 var res = {
1875 distance: function distance(from, to) {
1876 var i = indexOfArgEle(from);
1877 var j = indexOfArgEle(to);
1878 return dist[i * N + j];
1879 },
1880 path: function path(from, to) {
1881 var i = indexOfArgEle(from);
1882 var j = indexOfArgEle(to);
1883 var fromNode = atIndex(i);
1884
1885 if (i === j) {
1886 return fromNode.collection();
1887 }
1888
1889 if (next[i * N + j] == null) {
1890 return cy.collection();
1891 }
1892
1893 var path = cy.collection();
1894 var prev = i;
1895 var edge;
1896 path.merge(fromNode);
1897
1898 while (i !== j) {
1899 prev = i;
1900 i = next[i * N + j];
1901 edge = edgeNext[prev * N + i];
1902 path.merge(edge);
1903 path.merge(atIndex(i));
1904 }
1905
1906 return path;
1907 }
1908 };
1909 return res;
1910 } // floydWarshall
1911
1912}; // elesfn
1913
1914var bellmanFordDefaults = defaults$g({
1915 weight: function weight(edge) {
1916 return 1;
1917 },
1918 directed: false,
1919 root: null
1920});
1921var elesfn$q = {
1922 // Implemented from pseudocode from wikipedia
1923 bellmanFord: function bellmanFord(options) {
1924 var _this = this;
1925
1926 var _bellmanFordDefaults = bellmanFordDefaults(options),
1927 weight = _bellmanFordDefaults.weight,
1928 directed = _bellmanFordDefaults.directed,
1929 root = _bellmanFordDefaults.root;
1930
1931 var weightFn = weight;
1932 var eles = this;
1933 var cy = this.cy();
1934
1935 var _this$byGroup = this.byGroup(),
1936 edges = _this$byGroup.edges,
1937 nodes = _this$byGroup.nodes;
1938
1939 var numNodes = nodes.length;
1940 var infoMap = new Map$1();
1941 var hasNegativeWeightCycle = false;
1942 var negativeWeightCycles = [];
1943 root = cy.collection(root)[0]; // in case selector passed
1944
1945 edges.unmergeBy(function (edge) {
1946 return edge.isLoop();
1947 });
1948 var numEdges = edges.length;
1949
1950 var getInfo = function getInfo(node) {
1951 var obj = infoMap.get(node.id());
1952
1953 if (!obj) {
1954 obj = {};
1955 infoMap.set(node.id(), obj);
1956 }
1957
1958 return obj;
1959 };
1960
1961 var getNodeFromTo = function getNodeFromTo(to) {
1962 return (string(to) ? cy.$(to) : to)[0];
1963 };
1964
1965 var distanceTo = function distanceTo(to) {
1966 return getInfo(getNodeFromTo(to)).dist;
1967 };
1968
1969 var pathTo = function pathTo(to) {
1970 var thisStart = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : root;
1971 var end = getNodeFromTo(to);
1972 var path = [];
1973 var node = end;
1974
1975 for (;;) {
1976 if (node == null) {
1977 return _this.spawn();
1978 }
1979
1980 var _getInfo = getInfo(node),
1981 edge = _getInfo.edge,
1982 pred = _getInfo.pred;
1983
1984 path.unshift(node[0]);
1985
1986 if (node.same(thisStart) && path.length > 0) {
1987 break;
1988 }
1989
1990 if (edge != null) {
1991 path.unshift(edge);
1992 }
1993
1994 node = pred;
1995 }
1996
1997 return eles.spawn(path);
1998 }; // Initializations { dist, pred, edge }
1999
2000
2001 for (var i = 0; i < numNodes; i++) {
2002 var node = nodes[i];
2003 var info = getInfo(node);
2004
2005 if (node.same(root)) {
2006 info.dist = 0;
2007 } else {
2008 info.dist = Infinity;
2009 }
2010
2011 info.pred = null;
2012 info.edge = null;
2013 } // Edges relaxation
2014
2015
2016 var replacedEdge = false;
2017
2018 var checkForEdgeReplacement = function checkForEdgeReplacement(node1, node2, edge, info1, info2, weight) {
2019 var dist = info1.dist + weight;
2020
2021 if (dist < info2.dist && !edge.same(info1.edge)) {
2022 info2.dist = dist;
2023 info2.pred = node1;
2024 info2.edge = edge;
2025 replacedEdge = true;
2026 }
2027 };
2028
2029 for (var _i = 1; _i < numNodes; _i++) {
2030 replacedEdge = false;
2031
2032 for (var e = 0; e < numEdges; e++) {
2033 var edge = edges[e];
2034 var src = edge.source();
2035 var tgt = edge.target();
2036
2037 var _weight = weightFn(edge);
2038
2039 var srcInfo = getInfo(src);
2040 var tgtInfo = getInfo(tgt);
2041 checkForEdgeReplacement(src, tgt, edge, srcInfo, tgtInfo, _weight); // If undirected graph, we need to take into account the 'reverse' edge
2042
2043 if (!directed) {
2044 checkForEdgeReplacement(tgt, src, edge, tgtInfo, srcInfo, _weight);
2045 }
2046 }
2047
2048 if (!replacedEdge) {
2049 break;
2050 }
2051 }
2052
2053 if (replacedEdge) {
2054 // Check for negative weight cycles
2055 var negativeWeightCycleIds = [];
2056
2057 for (var _e = 0; _e < numEdges; _e++) {
2058 var _edge = edges[_e];
2059
2060 var _src = _edge.source();
2061
2062 var _tgt = _edge.target();
2063
2064 var _weight2 = weightFn(_edge);
2065
2066 var srcDist = getInfo(_src).dist;
2067 var tgtDist = getInfo(_tgt).dist;
2068
2069 if (srcDist + _weight2 < tgtDist || !directed && tgtDist + _weight2 < srcDist) {
2070 if (!hasNegativeWeightCycle) {
2071 warn('Graph contains a negative weight cycle for Bellman-Ford');
2072 hasNegativeWeightCycle = true;
2073 }
2074
2075 if (options.findNegativeWeightCycles !== false) {
2076 var negativeNodes = [];
2077
2078 if (srcDist + _weight2 < tgtDist) {
2079 negativeNodes.push(_src);
2080 }
2081
2082 if (!directed && tgtDist + _weight2 < srcDist) {
2083 negativeNodes.push(_tgt);
2084 }
2085
2086 var numNegativeNodes = negativeNodes.length;
2087
2088 for (var n = 0; n < numNegativeNodes; n++) {
2089 var start = negativeNodes[n];
2090 var cycle = [start];
2091 cycle.push(getInfo(start).edge);
2092 var _node = getInfo(start).pred;
2093
2094 while (cycle.indexOf(_node) === -1) {
2095 cycle.push(_node);
2096 cycle.push(getInfo(_node).edge);
2097 _node = getInfo(_node).pred;
2098 }
2099
2100 cycle = cycle.slice(cycle.indexOf(_node));
2101 var smallestId = cycle[0].id();
2102 var smallestIndex = 0;
2103
2104 for (var c = 2; c < cycle.length; c += 2) {
2105 if (cycle[c].id() < smallestId) {
2106 smallestId = cycle[c].id();
2107 smallestIndex = c;
2108 }
2109 }
2110
2111 cycle = cycle.slice(smallestIndex).concat(cycle.slice(0, smallestIndex));
2112 cycle.push(cycle[0]);
2113 var cycleId = cycle.map(function (el) {
2114 return el.id();
2115 }).join(",");
2116
2117 if (negativeWeightCycleIds.indexOf(cycleId) === -1) {
2118 negativeWeightCycles.push(eles.spawn(cycle));
2119 negativeWeightCycleIds.push(cycleId);
2120 }
2121 }
2122 } else {
2123 break;
2124 }
2125 }
2126 }
2127 }
2128
2129 return {
2130 distanceTo: distanceTo,
2131 pathTo: pathTo,
2132 hasNegativeWeightCycle: hasNegativeWeightCycle,
2133 negativeWeightCycles: negativeWeightCycles
2134 };
2135 } // bellmanFord
2136
2137}; // elesfn
2138
2139var sqrt2 = Math.sqrt(2); // Function which colapses 2 (meta) nodes into one
2140// Updates the remaining edge lists
2141// Receives as a paramater the edge which causes the collapse
2142
2143var collapse = function collapse(edgeIndex, nodeMap, remainingEdges) {
2144 if (remainingEdges.length === 0) {
2145 error("Karger-Stein must be run on a connected (sub)graph");
2146 }
2147
2148 var edgeInfo = remainingEdges[edgeIndex];
2149 var sourceIn = edgeInfo[1];
2150 var targetIn = edgeInfo[2];
2151 var partition1 = nodeMap[sourceIn];
2152 var partition2 = nodeMap[targetIn];
2153 var newEdges = remainingEdges; // re-use array
2154 // Delete all edges between partition1 and partition2
2155
2156 for (var i = newEdges.length - 1; i >= 0; i--) {
2157 var edge = newEdges[i];
2158 var src = edge[1];
2159 var tgt = edge[2];
2160
2161 if (nodeMap[src] === partition1 && nodeMap[tgt] === partition2 || nodeMap[src] === partition2 && nodeMap[tgt] === partition1) {
2162 newEdges.splice(i, 1);
2163 }
2164 } // All edges pointing to partition2 should now point to partition1
2165
2166
2167 for (var _i = 0; _i < newEdges.length; _i++) {
2168 var _edge = newEdges[_i];
2169
2170 if (_edge[1] === partition2) {
2171 // Check source
2172 newEdges[_i] = _edge.slice(); // copy
2173
2174 newEdges[_i][1] = partition1;
2175 } else if (_edge[2] === partition2) {
2176 // Check target
2177 newEdges[_i] = _edge.slice(); // copy
2178
2179 newEdges[_i][2] = partition1;
2180 }
2181 } // Move all nodes from partition2 to partition1
2182
2183
2184 for (var _i2 = 0; _i2 < nodeMap.length; _i2++) {
2185 if (nodeMap[_i2] === partition2) {
2186 nodeMap[_i2] = partition1;
2187 }
2188 }
2189
2190 return newEdges;
2191}; // Contracts a graph until we reach a certain number of meta nodes
2192
2193
2194var contractUntil = function contractUntil(metaNodeMap, remainingEdges, size, sizeLimit) {
2195 while (size > sizeLimit) {
2196 // Choose an edge randomly
2197 var edgeIndex = Math.floor(Math.random() * remainingEdges.length); // Collapse graph based on edge
2198
2199 remainingEdges = collapse(edgeIndex, metaNodeMap, remainingEdges);
2200 size--;
2201 }
2202
2203 return remainingEdges;
2204};
2205
2206var elesfn$p = {
2207 // Computes the minimum cut of an undirected graph
2208 // Returns the correct answer with high probability
2209 kargerStein: function kargerStein() {
2210 var _this = this;
2211
2212 var _this$byGroup = this.byGroup(),
2213 nodes = _this$byGroup.nodes,
2214 edges = _this$byGroup.edges;
2215
2216 edges.unmergeBy(function (edge) {
2217 return edge.isLoop();
2218 });
2219 var numNodes = nodes.length;
2220 var numEdges = edges.length;
2221 var numIter = Math.ceil(Math.pow(Math.log(numNodes) / Math.LN2, 2));
2222 var stopSize = Math.floor(numNodes / sqrt2);
2223
2224 if (numNodes < 2) {
2225 error('At least 2 nodes are required for Karger-Stein algorithm');
2226 return undefined;
2227 } // Now store edge destination as indexes
2228 // Format for each edge (edge index, source node index, target node index)
2229
2230
2231 var edgeIndexes = [];
2232
2233 for (var i = 0; i < numEdges; i++) {
2234 var e = edges[i];
2235 edgeIndexes.push([i, nodes.indexOf(e.source()), nodes.indexOf(e.target())]);
2236 } // We will store the best cut found here
2237
2238
2239 var minCutSize = Infinity;
2240 var minCutEdgeIndexes = [];
2241 var minCutNodeMap = new Array(numNodes); // Initial meta node partition
2242
2243 var metaNodeMap = new Array(numNodes);
2244 var metaNodeMap2 = new Array(numNodes);
2245
2246 var copyNodesMap = function copyNodesMap(from, to) {
2247 for (var _i3 = 0; _i3 < numNodes; _i3++) {
2248 to[_i3] = from[_i3];
2249 }
2250 }; // Main loop
2251
2252
2253 for (var iter = 0; iter <= numIter; iter++) {
2254 // Reset meta node partition
2255 for (var _i4 = 0; _i4 < numNodes; _i4++) {
2256 metaNodeMap[_i4] = _i4;
2257 } // Contract until stop point (stopSize nodes)
2258
2259
2260 var edgesState = contractUntil(metaNodeMap, edgeIndexes.slice(), numNodes, stopSize);
2261 var edgesState2 = edgesState.slice(); // copy
2262 // Create a copy of the colapsed nodes state
2263
2264 copyNodesMap(metaNodeMap, metaNodeMap2); // Run 2 iterations starting in the stop state
2265
2266 var res1 = contractUntil(metaNodeMap, edgesState, stopSize, 2);
2267 var res2 = contractUntil(metaNodeMap2, edgesState2, stopSize, 2); // Is any of the 2 results the best cut so far?
2268
2269 if (res1.length <= res2.length && res1.length < minCutSize) {
2270 minCutSize = res1.length;
2271 minCutEdgeIndexes = res1;
2272 copyNodesMap(metaNodeMap, minCutNodeMap);
2273 } else if (res2.length <= res1.length && res2.length < minCutSize) {
2274 minCutSize = res2.length;
2275 minCutEdgeIndexes = res2;
2276 copyNodesMap(metaNodeMap2, minCutNodeMap);
2277 }
2278 } // end of main loop
2279 // Construct result
2280
2281
2282 var cut = this.spawn(minCutEdgeIndexes.map(function (e) {
2283 return edges[e[0]];
2284 }));
2285 var partition1 = this.spawn();
2286 var partition2 = this.spawn(); // traverse metaNodeMap for best cut
2287
2288 var witnessNodePartition = minCutNodeMap[0];
2289
2290 for (var _i5 = 0; _i5 < minCutNodeMap.length; _i5++) {
2291 var partitionId = minCutNodeMap[_i5];
2292 var node = nodes[_i5];
2293
2294 if (partitionId === witnessNodePartition) {
2295 partition1.merge(node);
2296 } else {
2297 partition2.merge(node);
2298 }
2299 } // construct components corresponding to each disjoint subset of nodes
2300
2301
2302 var constructComponent = function constructComponent(subset) {
2303 var component = _this.spawn();
2304
2305 subset.forEach(function (node) {
2306 component.merge(node);
2307 node.connectedEdges().forEach(function (edge) {
2308 // ensure edge is within calling collection and edge is not in cut
2309 if (_this.contains(edge) && !cut.contains(edge)) {
2310 component.merge(edge);
2311 }
2312 });
2313 });
2314 return component;
2315 };
2316
2317 var components = [constructComponent(partition1), constructComponent(partition2)];
2318 var ret = {
2319 cut: cut,
2320 components: components,
2321 // n.b. partitions are included to be compatible with the old api spec
2322 // (could be removed in a future major version)
2323 partition1: partition1,
2324 partition2: partition2
2325 };
2326 return ret;
2327 }
2328}; // elesfn
2329
2330var copyPosition = function copyPosition(p) {
2331 return {
2332 x: p.x,
2333 y: p.y
2334 };
2335};
2336var modelToRenderedPosition = function modelToRenderedPosition(p, zoom, pan) {
2337 return {
2338 x: p.x * zoom + pan.x,
2339 y: p.y * zoom + pan.y
2340 };
2341};
2342var renderedToModelPosition = function renderedToModelPosition(p, zoom, pan) {
2343 return {
2344 x: (p.x - pan.x) / zoom,
2345 y: (p.y - pan.y) / zoom
2346 };
2347};
2348var array2point = function array2point(arr) {
2349 return {
2350 x: arr[0],
2351 y: arr[1]
2352 };
2353};
2354var min = function min(arr) {
2355 var begin = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
2356 var end = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : arr.length;
2357 var min = Infinity;
2358
2359 for (var i = begin; i < end; i++) {
2360 var val = arr[i];
2361
2362 if (isFinite(val)) {
2363 min = Math.min(val, min);
2364 }
2365 }
2366
2367 return min;
2368};
2369var max = function max(arr) {
2370 var begin = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
2371 var end = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : arr.length;
2372 var max = -Infinity;
2373
2374 for (var i = begin; i < end; i++) {
2375 var val = arr[i];
2376
2377 if (isFinite(val)) {
2378 max = Math.max(val, max);
2379 }
2380 }
2381
2382 return max;
2383};
2384var mean = function mean(arr) {
2385 var begin = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
2386 var end = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : arr.length;
2387 var total = 0;
2388 var n = 0;
2389
2390 for (var i = begin; i < end; i++) {
2391 var val = arr[i];
2392
2393 if (isFinite(val)) {
2394 total += val;
2395 n++;
2396 }
2397 }
2398
2399 return total / n;
2400};
2401var median = function median(arr) {
2402 var begin = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
2403 var end = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : arr.length;
2404 var copy = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true;
2405 var sort = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : true;
2406 var includeHoles = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : true;
2407
2408 if (copy) {
2409 arr = arr.slice(begin, end);
2410 } else {
2411 if (end < arr.length) {
2412 arr.splice(end, arr.length - end);
2413 }
2414
2415 if (begin > 0) {
2416 arr.splice(0, begin);
2417 }
2418 } // all non finite (e.g. Infinity, NaN) elements must be -Infinity so they go to the start
2419
2420
2421 var off = 0; // offset from non-finite values
2422
2423 for (var i = arr.length - 1; i >= 0; i--) {
2424 var v = arr[i];
2425
2426 if (includeHoles) {
2427 if (!isFinite(v)) {
2428 arr[i] = -Infinity;
2429 off++;
2430 }
2431 } else {
2432 // just remove it if we don't want to consider holes
2433 arr.splice(i, 1);
2434 }
2435 }
2436
2437 if (sort) {
2438 arr.sort(function (a, b) {
2439 return a - b;
2440 }); // requires copy = true if you don't want to change the orig
2441 }
2442
2443 var len = arr.length;
2444 var mid = Math.floor(len / 2);
2445
2446 if (len % 2 !== 0) {
2447 return arr[mid + 1 + off];
2448 } else {
2449 return (arr[mid - 1 + off] + arr[mid + off]) / 2;
2450 }
2451};
2452var deg2rad = function deg2rad(deg) {
2453 return Math.PI * deg / 180;
2454};
2455var getAngleFromDisp = function getAngleFromDisp(dispX, dispY) {
2456 return Math.atan2(dispY, dispX) - Math.PI / 2;
2457};
2458var log2 = Math.log2 || function (n) {
2459 return Math.log(n) / Math.log(2);
2460};
2461var signum = function signum(x) {
2462 if (x > 0) {
2463 return 1;
2464 } else if (x < 0) {
2465 return -1;
2466 } else {
2467 return 0;
2468 }
2469};
2470var dist = function dist(p1, p2) {
2471 return Math.sqrt(sqdist(p1, p2));
2472};
2473var sqdist = function sqdist(p1, p2) {
2474 var dx = p2.x - p1.x;
2475 var dy = p2.y - p1.y;
2476 return dx * dx + dy * dy;
2477};
2478var inPlaceSumNormalize = function inPlaceSumNormalize(v) {
2479 var length = v.length; // First, get sum of all elements
2480
2481 var total = 0;
2482
2483 for (var i = 0; i < length; i++) {
2484 total += v[i];
2485 } // Now, divide each by the sum of all elements
2486
2487
2488 for (var _i = 0; _i < length; _i++) {
2489 v[_i] = v[_i] / total;
2490 }
2491
2492 return v;
2493};
2494
2495var qbezierAt = function qbezierAt(p0, p1, p2, t) {
2496 return (1 - t) * (1 - t) * p0 + 2 * (1 - t) * t * p1 + t * t * p2;
2497};
2498var qbezierPtAt = function qbezierPtAt(p0, p1, p2, t) {
2499 return {
2500 x: qbezierAt(p0.x, p1.x, p2.x, t),
2501 y: qbezierAt(p0.y, p1.y, p2.y, t)
2502 };
2503};
2504var lineAt = function lineAt(p0, p1, t, d) {
2505 var vec = {
2506 x: p1.x - p0.x,
2507 y: p1.y - p0.y
2508 };
2509 var vecDist = dist(p0, p1);
2510 var normVec = {
2511 x: vec.x / vecDist,
2512 y: vec.y / vecDist
2513 };
2514 t = t == null ? 0 : t;
2515 d = d != null ? d : t * vecDist;
2516 return {
2517 x: p0.x + normVec.x * d,
2518 y: p0.y + normVec.y * d
2519 };
2520};
2521var bound = function bound(min, val, max) {
2522 return Math.max(min, Math.min(max, val));
2523}; // makes a full bb (x1, y1, x2, y2, w, h) from implicit params
2524
2525var makeBoundingBox = function makeBoundingBox(bb) {
2526 if (bb == null) {
2527 return {
2528 x1: Infinity,
2529 y1: Infinity,
2530 x2: -Infinity,
2531 y2: -Infinity,
2532 w: 0,
2533 h: 0
2534 };
2535 } else if (bb.x1 != null && bb.y1 != null) {
2536 if (bb.x2 != null && bb.y2 != null && bb.x2 >= bb.x1 && bb.y2 >= bb.y1) {
2537 return {
2538 x1: bb.x1,
2539 y1: bb.y1,
2540 x2: bb.x2,
2541 y2: bb.y2,
2542 w: bb.x2 - bb.x1,
2543 h: bb.y2 - bb.y1
2544 };
2545 } else if (bb.w != null && bb.h != null && bb.w >= 0 && bb.h >= 0) {
2546 return {
2547 x1: bb.x1,
2548 y1: bb.y1,
2549 x2: bb.x1 + bb.w,
2550 y2: bb.y1 + bb.h,
2551 w: bb.w,
2552 h: bb.h
2553 };
2554 }
2555 }
2556};
2557var copyBoundingBox = function copyBoundingBox(bb) {
2558 return {
2559 x1: bb.x1,
2560 x2: bb.x2,
2561 w: bb.w,
2562 y1: bb.y1,
2563 y2: bb.y2,
2564 h: bb.h
2565 };
2566};
2567var clearBoundingBox = function clearBoundingBox(bb) {
2568 bb.x1 = Infinity;
2569 bb.y1 = Infinity;
2570 bb.x2 = -Infinity;
2571 bb.y2 = -Infinity;
2572 bb.w = 0;
2573 bb.h = 0;
2574};
2575var updateBoundingBox = function updateBoundingBox(bb1, bb2) {
2576 // update bb1 with bb2 bounds
2577 bb1.x1 = Math.min(bb1.x1, bb2.x1);
2578 bb1.x2 = Math.max(bb1.x2, bb2.x2);
2579 bb1.w = bb1.x2 - bb1.x1;
2580 bb1.y1 = Math.min(bb1.y1, bb2.y1);
2581 bb1.y2 = Math.max(bb1.y2, bb2.y2);
2582 bb1.h = bb1.y2 - bb1.y1;
2583};
2584var expandBoundingBoxByPoint = function expandBoundingBoxByPoint(bb, x, y) {
2585 bb.x1 = Math.min(bb.x1, x);
2586 bb.x2 = Math.max(bb.x2, x);
2587 bb.w = bb.x2 - bb.x1;
2588 bb.y1 = Math.min(bb.y1, y);
2589 bb.y2 = Math.max(bb.y2, y);
2590 bb.h = bb.y2 - bb.y1;
2591};
2592var expandBoundingBox = function expandBoundingBox(bb) {
2593 var padding = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
2594 bb.x1 -= padding;
2595 bb.x2 += padding;
2596 bb.y1 -= padding;
2597 bb.y2 += padding;
2598 bb.w = bb.x2 - bb.x1;
2599 bb.h = bb.y2 - bb.y1;
2600 return bb;
2601};
2602var expandBoundingBoxSides = function expandBoundingBoxSides(bb) {
2603 var padding = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [0];
2604 var top, right, bottom, left;
2605
2606 if (padding.length === 1) {
2607 top = right = bottom = left = padding[0];
2608 } else if (padding.length === 2) {
2609 top = bottom = padding[0];
2610 left = right = padding[1];
2611 } else if (padding.length === 4) {
2612 var _padding = _slicedToArray(padding, 4);
2613
2614 top = _padding[0];
2615 right = _padding[1];
2616 bottom = _padding[2];
2617 left = _padding[3];
2618 }
2619
2620 bb.x1 -= left;
2621 bb.x2 += right;
2622 bb.y1 -= top;
2623 bb.y2 += bottom;
2624 bb.w = bb.x2 - bb.x1;
2625 bb.h = bb.y2 - bb.y1;
2626 return bb;
2627};
2628
2629var assignBoundingBox = function assignBoundingBox(bb1, bb2) {
2630 bb1.x1 = bb2.x1;
2631 bb1.y1 = bb2.y1;
2632 bb1.x2 = bb2.x2;
2633 bb1.y2 = bb2.y2;
2634 bb1.w = bb1.x2 - bb1.x1;
2635 bb1.h = bb1.y2 - bb1.y1;
2636};
2637var boundingBoxesIntersect = function boundingBoxesIntersect(bb1, bb2) {
2638 // case: one bb to right of other
2639 if (bb1.x1 > bb2.x2) {
2640 return false;
2641 }
2642
2643 if (bb2.x1 > bb1.x2) {
2644 return false;
2645 } // case: one bb to left of other
2646
2647
2648 if (bb1.x2 < bb2.x1) {
2649 return false;
2650 }
2651
2652 if (bb2.x2 < bb1.x1) {
2653 return false;
2654 } // case: one bb above other
2655
2656
2657 if (bb1.y2 < bb2.y1) {
2658 return false;
2659 }
2660
2661 if (bb2.y2 < bb1.y1) {
2662 return false;
2663 } // case: one bb below other
2664
2665
2666 if (bb1.y1 > bb2.y2) {
2667 return false;
2668 }
2669
2670 if (bb2.y1 > bb1.y2) {
2671 return false;
2672 } // otherwise, must have some overlap
2673
2674
2675 return true;
2676};
2677var inBoundingBox = function inBoundingBox(bb, x, y) {
2678 return bb.x1 <= x && x <= bb.x2 && bb.y1 <= y && y <= bb.y2;
2679};
2680var pointInBoundingBox = function pointInBoundingBox(bb, pt) {
2681 return inBoundingBox(bb, pt.x, pt.y);
2682};
2683var boundingBoxInBoundingBox = function boundingBoxInBoundingBox(bb1, bb2) {
2684 return inBoundingBox(bb1, bb2.x1, bb2.y1) && inBoundingBox(bb1, bb2.x2, bb2.y2);
2685};
2686var roundRectangleIntersectLine = function roundRectangleIntersectLine(x, y, nodeX, nodeY, width, height, padding) {
2687 var cornerRadius = getRoundRectangleRadius(width, height);
2688 var halfWidth = width / 2;
2689 var halfHeight = height / 2; // Check intersections with straight line segments
2690
2691 var straightLineIntersections; // Top segment, left to right
2692
2693 {
2694 var topStartX = nodeX - halfWidth + cornerRadius - padding;
2695 var topStartY = nodeY - halfHeight - padding;
2696 var topEndX = nodeX + halfWidth - cornerRadius + padding;
2697 var topEndY = topStartY;
2698 straightLineIntersections = finiteLinesIntersect(x, y, nodeX, nodeY, topStartX, topStartY, topEndX, topEndY, false);
2699
2700 if (straightLineIntersections.length > 0) {
2701 return straightLineIntersections;
2702 }
2703 } // Right segment, top to bottom
2704
2705 {
2706 var rightStartX = nodeX + halfWidth + padding;
2707 var rightStartY = nodeY - halfHeight + cornerRadius - padding;
2708 var rightEndX = rightStartX;
2709 var rightEndY = nodeY + halfHeight - cornerRadius + padding;
2710 straightLineIntersections = finiteLinesIntersect(x, y, nodeX, nodeY, rightStartX, rightStartY, rightEndX, rightEndY, false);
2711
2712 if (straightLineIntersections.length > 0) {
2713 return straightLineIntersections;
2714 }
2715 } // Bottom segment, left to right
2716
2717 {
2718 var bottomStartX = nodeX - halfWidth + cornerRadius - padding;
2719 var bottomStartY = nodeY + halfHeight + padding;
2720 var bottomEndX = nodeX + halfWidth - cornerRadius + padding;
2721 var bottomEndY = bottomStartY;
2722 straightLineIntersections = finiteLinesIntersect(x, y, nodeX, nodeY, bottomStartX, bottomStartY, bottomEndX, bottomEndY, false);
2723
2724 if (straightLineIntersections.length > 0) {
2725 return straightLineIntersections;
2726 }
2727 } // Left segment, top to bottom
2728
2729 {
2730 var leftStartX = nodeX - halfWidth - padding;
2731 var leftStartY = nodeY - halfHeight + cornerRadius - padding;
2732 var leftEndX = leftStartX;
2733 var leftEndY = nodeY + halfHeight - cornerRadius + padding;
2734 straightLineIntersections = finiteLinesIntersect(x, y, nodeX, nodeY, leftStartX, leftStartY, leftEndX, leftEndY, false);
2735
2736 if (straightLineIntersections.length > 0) {
2737 return straightLineIntersections;
2738 }
2739 } // Check intersections with arc segments
2740
2741 var arcIntersections; // Top Left
2742
2743 {
2744 var topLeftCenterX = nodeX - halfWidth + cornerRadius;
2745 var topLeftCenterY = nodeY - halfHeight + cornerRadius;
2746 arcIntersections = intersectLineCircle(x, y, nodeX, nodeY, topLeftCenterX, topLeftCenterY, cornerRadius + padding); // Ensure the intersection is on the desired quarter of the circle
2747
2748 if (arcIntersections.length > 0 && arcIntersections[0] <= topLeftCenterX && arcIntersections[1] <= topLeftCenterY) {
2749 return [arcIntersections[0], arcIntersections[1]];
2750 }
2751 } // Top Right
2752
2753 {
2754 var topRightCenterX = nodeX + halfWidth - cornerRadius;
2755 var topRightCenterY = nodeY - halfHeight + cornerRadius;
2756 arcIntersections = intersectLineCircle(x, y, nodeX, nodeY, topRightCenterX, topRightCenterY, cornerRadius + padding); // Ensure the intersection is on the desired quarter of the circle
2757
2758 if (arcIntersections.length > 0 && arcIntersections[0] >= topRightCenterX && arcIntersections[1] <= topRightCenterY) {
2759 return [arcIntersections[0], arcIntersections[1]];
2760 }
2761 } // Bottom Right
2762
2763 {
2764 var bottomRightCenterX = nodeX + halfWidth - cornerRadius;
2765 var bottomRightCenterY = nodeY + halfHeight - cornerRadius;
2766 arcIntersections = intersectLineCircle(x, y, nodeX, nodeY, bottomRightCenterX, bottomRightCenterY, cornerRadius + padding); // Ensure the intersection is on the desired quarter of the circle
2767
2768 if (arcIntersections.length > 0 && arcIntersections[0] >= bottomRightCenterX && arcIntersections[1] >= bottomRightCenterY) {
2769 return [arcIntersections[0], arcIntersections[1]];
2770 }
2771 } // Bottom Left
2772
2773 {
2774 var bottomLeftCenterX = nodeX - halfWidth + cornerRadius;
2775 var bottomLeftCenterY = nodeY + halfHeight - cornerRadius;
2776 arcIntersections = intersectLineCircle(x, y, nodeX, nodeY, bottomLeftCenterX, bottomLeftCenterY, cornerRadius + padding); // Ensure the intersection is on the desired quarter of the circle
2777
2778 if (arcIntersections.length > 0 && arcIntersections[0] <= bottomLeftCenterX && arcIntersections[1] >= bottomLeftCenterY) {
2779 return [arcIntersections[0], arcIntersections[1]];
2780 }
2781 }
2782 return []; // if nothing
2783};
2784var inLineVicinity = function inLineVicinity(x, y, lx1, ly1, lx2, ly2, tolerance) {
2785 var t = tolerance;
2786 var x1 = Math.min(lx1, lx2);
2787 var x2 = Math.max(lx1, lx2);
2788 var y1 = Math.min(ly1, ly2);
2789 var y2 = Math.max(ly1, ly2);
2790 return x1 - t <= x && x <= x2 + t && y1 - t <= y && y <= y2 + t;
2791};
2792var inBezierVicinity = function inBezierVicinity(x, y, x1, y1, x2, y2, x3, y3, tolerance) {
2793 var bb = {
2794 x1: Math.min(x1, x3, x2) - tolerance,
2795 x2: Math.max(x1, x3, x2) + tolerance,
2796 y1: Math.min(y1, y3, y2) - tolerance,
2797 y2: Math.max(y1, y3, y2) + tolerance
2798 }; // if outside the rough bounding box for the bezier, then it can't be a hit
2799
2800 if (x < bb.x1 || x > bb.x2 || y < bb.y1 || y > bb.y2) {
2801 // console.log('bezier out of rough bb')
2802 return false;
2803 } else {
2804 // console.log('do more expensive check');
2805 return true;
2806 }
2807};
2808var solveQuadratic = function solveQuadratic(a, b, c, val) {
2809 c -= val;
2810 var r = b * b - 4 * a * c;
2811
2812 if (r < 0) {
2813 return [];
2814 }
2815
2816 var sqrtR = Math.sqrt(r);
2817 var denom = 2 * a;
2818 var root1 = (-b + sqrtR) / denom;
2819 var root2 = (-b - sqrtR) / denom;
2820 return [root1, root2];
2821};
2822var solveCubic = function solveCubic(a, b, c, d, result) {
2823 // Solves a cubic function, returns root in form [r1, i1, r2, i2, r3, i3], where
2824 // r is the real component, i is the imaginary component
2825 // An implementation of the Cardano method from the year 1545
2826 // http://en.wikipedia.org/wiki/Cubic_function#The_nature_of_the_roots
2827 var epsilon = 0.00001; // avoid division by zero while keeping the overall expression close in value
2828
2829 if (a === 0) {
2830 a = epsilon;
2831 }
2832
2833 b /= a;
2834 c /= a;
2835 d /= a;
2836 var discriminant, q, r, dum1, s, t, term1, r13;
2837 q = (3.0 * c - b * b) / 9.0;
2838 r = -(27.0 * d) + b * (9.0 * c - 2.0 * (b * b));
2839 r /= 54.0;
2840 discriminant = q * q * q + r * r;
2841 result[1] = 0;
2842 term1 = b / 3.0;
2843
2844 if (discriminant > 0) {
2845 s = r + Math.sqrt(discriminant);
2846 s = s < 0 ? -Math.pow(-s, 1.0 / 3.0) : Math.pow(s, 1.0 / 3.0);
2847 t = r - Math.sqrt(discriminant);
2848 t = t < 0 ? -Math.pow(-t, 1.0 / 3.0) : Math.pow(t, 1.0 / 3.0);
2849 result[0] = -term1 + s + t;
2850 term1 += (s + t) / 2.0;
2851 result[4] = result[2] = -term1;
2852 term1 = Math.sqrt(3.0) * (-t + s) / 2;
2853 result[3] = term1;
2854 result[5] = -term1;
2855 return;
2856 }
2857
2858 result[5] = result[3] = 0;
2859
2860 if (discriminant === 0) {
2861 r13 = r < 0 ? -Math.pow(-r, 1.0 / 3.0) : Math.pow(r, 1.0 / 3.0);
2862 result[0] = -term1 + 2.0 * r13;
2863 result[4] = result[2] = -(r13 + term1);
2864 return;
2865 }
2866
2867 q = -q;
2868 dum1 = q * q * q;
2869 dum1 = Math.acos(r / Math.sqrt(dum1));
2870 r13 = 2.0 * Math.sqrt(q);
2871 result[0] = -term1 + r13 * Math.cos(dum1 / 3.0);
2872 result[2] = -term1 + r13 * Math.cos((dum1 + 2.0 * Math.PI) / 3.0);
2873 result[4] = -term1 + r13 * Math.cos((dum1 + 4.0 * Math.PI) / 3.0);
2874 return;
2875};
2876var sqdistToQuadraticBezier = function sqdistToQuadraticBezier(x, y, x1, y1, x2, y2, x3, y3) {
2877 // Find minimum distance by using the minimum of the distance
2878 // function between the given point and the curve
2879 // This gives the coefficients of the resulting cubic equation
2880 // whose roots tell us where a possible minimum is
2881 // (Coefficients are divided by 4)
2882 var a = 1.0 * x1 * x1 - 4 * x1 * x2 + 2 * x1 * x3 + 4 * x2 * x2 - 4 * x2 * x3 + x3 * x3 + y1 * y1 - 4 * y1 * y2 + 2 * y1 * y3 + 4 * y2 * y2 - 4 * y2 * y3 + y3 * y3;
2883 var b = 1.0 * 9 * x1 * x2 - 3 * x1 * x1 - 3 * x1 * x3 - 6 * x2 * x2 + 3 * x2 * x3 + 9 * y1 * y2 - 3 * y1 * y1 - 3 * y1 * y3 - 6 * y2 * y2 + 3 * y2 * y3;
2884 var c = 1.0 * 3 * x1 * x1 - 6 * x1 * x2 + x1 * x3 - x1 * x + 2 * x2 * x2 + 2 * x2 * x - x3 * x + 3 * y1 * y1 - 6 * y1 * y2 + y1 * y3 - y1 * y + 2 * y2 * y2 + 2 * y2 * y - y3 * y;
2885 var d = 1.0 * x1 * x2 - x1 * x1 + x1 * x - x2 * x + y1 * y2 - y1 * y1 + y1 * y - y2 * y; // debug("coefficients: " + a / a + ", " + b / a + ", " + c / a + ", " + d / a);
2886
2887 var roots = []; // Use the cubic solving algorithm
2888
2889 solveCubic(a, b, c, d, roots);
2890 var zeroThreshold = 0.0000001;
2891 var params = [];
2892
2893 for (var index = 0; index < 6; index += 2) {
2894 if (Math.abs(roots[index + 1]) < zeroThreshold && roots[index] >= 0 && roots[index] <= 1.0) {
2895 params.push(roots[index]);
2896 }
2897 }
2898
2899 params.push(1.0);
2900 params.push(0.0);
2901 var minDistanceSquared = -1;
2902 var curX, curY, distSquared;
2903
2904 for (var i = 0; i < params.length; i++) {
2905 curX = Math.pow(1.0 - params[i], 2.0) * x1 + 2.0 * (1 - params[i]) * params[i] * x2 + params[i] * params[i] * x3;
2906 curY = Math.pow(1 - params[i], 2.0) * y1 + 2 * (1.0 - params[i]) * params[i] * y2 + params[i] * params[i] * y3;
2907 distSquared = Math.pow(curX - x, 2) + Math.pow(curY - y, 2); // debug('distance for param ' + params[i] + ": " + Math.sqrt(distSquared));
2908
2909 if (minDistanceSquared >= 0) {
2910 if (distSquared < minDistanceSquared) {
2911 minDistanceSquared = distSquared;
2912 }
2913 } else {
2914 minDistanceSquared = distSquared;
2915 }
2916 }
2917
2918 return minDistanceSquared;
2919};
2920var sqdistToFiniteLine = function sqdistToFiniteLine(x, y, x1, y1, x2, y2) {
2921 var offset = [x - x1, y - y1];
2922 var line = [x2 - x1, y2 - y1];
2923 var lineSq = line[0] * line[0] + line[1] * line[1];
2924 var hypSq = offset[0] * offset[0] + offset[1] * offset[1];
2925 var dotProduct = offset[0] * line[0] + offset[1] * line[1];
2926 var adjSq = dotProduct * dotProduct / lineSq;
2927
2928 if (dotProduct < 0) {
2929 return hypSq;
2930 }
2931
2932 if (adjSq > lineSq) {
2933 return (x - x2) * (x - x2) + (y - y2) * (y - y2);
2934 }
2935
2936 return hypSq - adjSq;
2937};
2938var pointInsidePolygonPoints = function pointInsidePolygonPoints(x, y, points) {
2939 var x1, y1, x2, y2;
2940 var y3; // Intersect with vertical line through (x, y)
2941
2942 var up = 0; // let down = 0;
2943
2944 for (var i = 0; i < points.length / 2; i++) {
2945 x1 = points[i * 2];
2946 y1 = points[i * 2 + 1];
2947
2948 if (i + 1 < points.length / 2) {
2949 x2 = points[(i + 1) * 2];
2950 y2 = points[(i + 1) * 2 + 1];
2951 } else {
2952 x2 = points[(i + 1 - points.length / 2) * 2];
2953 y2 = points[(i + 1 - points.length / 2) * 2 + 1];
2954 }
2955
2956 if (x1 == x && x2 == x) ; else if (x1 >= x && x >= x2 || x1 <= x && x <= x2) {
2957 y3 = (x - x1) / (x2 - x1) * (y2 - y1) + y1;
2958
2959 if (y3 > y) {
2960 up++;
2961 } // if( y3 < y ){
2962 // down++;
2963 // }
2964
2965 } else {
2966 continue;
2967 }
2968 }
2969
2970 if (up % 2 === 0) {
2971 return false;
2972 } else {
2973 return true;
2974 }
2975};
2976var pointInsidePolygon = function pointInsidePolygon(x, y, basePoints, centerX, centerY, width, height, direction, padding) {
2977 var transformedPoints = new Array(basePoints.length); // Gives negative angle
2978
2979 var angle;
2980
2981 if (direction[0] != null) {
2982 angle = Math.atan(direction[1] / direction[0]);
2983
2984 if (direction[0] < 0) {
2985 angle = angle + Math.PI / 2;
2986 } else {
2987 angle = -angle - Math.PI / 2;
2988 }
2989 } else {
2990 angle = direction;
2991 }
2992
2993 var cos = Math.cos(-angle);
2994 var sin = Math.sin(-angle); // console.log("base: " + basePoints);
2995
2996 for (var i = 0; i < transformedPoints.length / 2; i++) {
2997 transformedPoints[i * 2] = width / 2 * (basePoints[i * 2] * cos - basePoints[i * 2 + 1] * sin);
2998 transformedPoints[i * 2 + 1] = height / 2 * (basePoints[i * 2 + 1] * cos + basePoints[i * 2] * sin);
2999 transformedPoints[i * 2] += centerX;
3000 transformedPoints[i * 2 + 1] += centerY;
3001 }
3002
3003 var points;
3004
3005 if (padding > 0) {
3006 var expandedLineSet = expandPolygon(transformedPoints, -padding);
3007 points = joinLines(expandedLineSet);
3008 } else {
3009 points = transformedPoints;
3010 }
3011
3012 return pointInsidePolygonPoints(x, y, points);
3013};
3014var pointInsideRoundPolygon = function pointInsideRoundPolygon(x, y, basePoints, centerX, centerY, width, height) {
3015 var cutPolygonPoints = new Array(basePoints.length);
3016 var halfW = width / 2;
3017 var halfH = height / 2;
3018 var cornerRadius = getRoundPolygonRadius(width, height);
3019 var squaredCornerRadius = cornerRadius * cornerRadius;
3020
3021 for (var i = 0; i < basePoints.length / 4; i++) {
3022 var sourceUv = void 0,
3023 destUv = void 0;
3024
3025 if (i === 0) {
3026 sourceUv = basePoints.length - 2;
3027 } else {
3028 sourceUv = i * 4 - 2;
3029 }
3030
3031 destUv = i * 4 + 2;
3032 var px = centerX + halfW * basePoints[i * 4];
3033 var py = centerY + halfH * basePoints[i * 4 + 1];
3034 var cosTheta = -basePoints[sourceUv] * basePoints[destUv] - basePoints[sourceUv + 1] * basePoints[destUv + 1];
3035 var offset = cornerRadius / Math.tan(Math.acos(cosTheta) / 2);
3036 var cp0x = px - offset * basePoints[sourceUv];
3037 var cp0y = py - offset * basePoints[sourceUv + 1];
3038 var cp1x = px + offset * basePoints[destUv];
3039 var cp1y = py + offset * basePoints[destUv + 1];
3040 cutPolygonPoints[i * 4] = cp0x;
3041 cutPolygonPoints[i * 4 + 1] = cp0y;
3042 cutPolygonPoints[i * 4 + 2] = cp1x;
3043 cutPolygonPoints[i * 4 + 3] = cp1y;
3044 var orthx = basePoints[sourceUv + 1];
3045 var orthy = -basePoints[sourceUv];
3046 var cosAlpha = orthx * basePoints[destUv] + orthy * basePoints[destUv + 1];
3047
3048 if (cosAlpha < 0) {
3049 orthx *= -1;
3050 orthy *= -1;
3051 }
3052
3053 var cx = cp0x + orthx * cornerRadius;
3054 var cy = cp0y + orthy * cornerRadius;
3055 var squaredDistance = Math.pow(cx - x, 2) + Math.pow(cy - y, 2);
3056
3057 if (squaredDistance <= squaredCornerRadius) {
3058 return true;
3059 }
3060 }
3061
3062 return pointInsidePolygonPoints(x, y, cutPolygonPoints);
3063};
3064var joinLines = function joinLines(lineSet) {
3065 var vertices = new Array(lineSet.length / 2);
3066 var currentLineStartX, currentLineStartY, currentLineEndX, currentLineEndY;
3067 var nextLineStartX, nextLineStartY, nextLineEndX, nextLineEndY;
3068
3069 for (var i = 0; i < lineSet.length / 4; i++) {
3070 currentLineStartX = lineSet[i * 4];
3071 currentLineStartY = lineSet[i * 4 + 1];
3072 currentLineEndX = lineSet[i * 4 + 2];
3073 currentLineEndY = lineSet[i * 4 + 3];
3074
3075 if (i < lineSet.length / 4 - 1) {
3076 nextLineStartX = lineSet[(i + 1) * 4];
3077 nextLineStartY = lineSet[(i + 1) * 4 + 1];
3078 nextLineEndX = lineSet[(i + 1) * 4 + 2];
3079 nextLineEndY = lineSet[(i + 1) * 4 + 3];
3080 } else {
3081 nextLineStartX = lineSet[0];
3082 nextLineStartY = lineSet[1];
3083 nextLineEndX = lineSet[2];
3084 nextLineEndY = lineSet[3];
3085 }
3086
3087 var intersection = finiteLinesIntersect(currentLineStartX, currentLineStartY, currentLineEndX, currentLineEndY, nextLineStartX, nextLineStartY, nextLineEndX, nextLineEndY, true);
3088 vertices[i * 2] = intersection[0];
3089 vertices[i * 2 + 1] = intersection[1];
3090 }
3091
3092 return vertices;
3093};
3094var expandPolygon = function expandPolygon(points, pad) {
3095 var expandedLineSet = new Array(points.length * 2);
3096 var currentPointX, currentPointY, nextPointX, nextPointY;
3097
3098 for (var i = 0; i < points.length / 2; i++) {
3099 currentPointX = points[i * 2];
3100 currentPointY = points[i * 2 + 1];
3101
3102 if (i < points.length / 2 - 1) {
3103 nextPointX = points[(i + 1) * 2];
3104 nextPointY = points[(i + 1) * 2 + 1];
3105 } else {
3106 nextPointX = points[0];
3107 nextPointY = points[1];
3108 } // Current line: [currentPointX, currentPointY] to [nextPointX, nextPointY]
3109 // Assume CCW polygon winding
3110
3111
3112 var offsetX = nextPointY - currentPointY;
3113 var offsetY = -(nextPointX - currentPointX); // Normalize
3114
3115 var offsetLength = Math.sqrt(offsetX * offsetX + offsetY * offsetY);
3116 var normalizedOffsetX = offsetX / offsetLength;
3117 var normalizedOffsetY = offsetY / offsetLength;
3118 expandedLineSet[i * 4] = currentPointX + normalizedOffsetX * pad;
3119 expandedLineSet[i * 4 + 1] = currentPointY + normalizedOffsetY * pad;
3120 expandedLineSet[i * 4 + 2] = nextPointX + normalizedOffsetX * pad;
3121 expandedLineSet[i * 4 + 3] = nextPointY + normalizedOffsetY * pad;
3122 }
3123
3124 return expandedLineSet;
3125};
3126var intersectLineEllipse = function intersectLineEllipse(x, y, centerX, centerY, ellipseWradius, ellipseHradius) {
3127 var dispX = centerX - x;
3128 var dispY = centerY - y;
3129 dispX /= ellipseWradius;
3130 dispY /= ellipseHradius;
3131 var len = Math.sqrt(dispX * dispX + dispY * dispY);
3132 var newLength = len - 1;
3133
3134 if (newLength < 0) {
3135 return [];
3136 }
3137
3138 var lenProportion = newLength / len;
3139 return [(centerX - x) * lenProportion + x, (centerY - y) * lenProportion + y];
3140};
3141var checkInEllipse = function checkInEllipse(x, y, width, height, centerX, centerY, padding) {
3142 x -= centerX;
3143 y -= centerY;
3144 x /= width / 2 + padding;
3145 y /= height / 2 + padding;
3146 return x * x + y * y <= 1;
3147}; // Returns intersections of increasing distance from line's start point
3148
3149var intersectLineCircle = function intersectLineCircle(x1, y1, x2, y2, centerX, centerY, radius) {
3150 // Calculate d, direction vector of line
3151 var d = [x2 - x1, y2 - y1]; // Direction vector of line
3152
3153 var f = [x1 - centerX, y1 - centerY];
3154 var a = d[0] * d[0] + d[1] * d[1];
3155 var b = 2 * (f[0] * d[0] + f[1] * d[1]);
3156 var c = f[0] * f[0] + f[1] * f[1] - radius * radius;
3157 var discriminant = b * b - 4 * a * c;
3158
3159 if (discriminant < 0) {
3160 return [];
3161 }
3162
3163 var t1 = (-b + Math.sqrt(discriminant)) / (2 * a);
3164 var t2 = (-b - Math.sqrt(discriminant)) / (2 * a);
3165 var tMin = Math.min(t1, t2);
3166 var tMax = Math.max(t1, t2);
3167 var inRangeParams = [];
3168
3169 if (tMin >= 0 && tMin <= 1) {
3170 inRangeParams.push(tMin);
3171 }
3172
3173 if (tMax >= 0 && tMax <= 1) {
3174 inRangeParams.push(tMax);
3175 }
3176
3177 if (inRangeParams.length === 0) {
3178 return [];
3179 }
3180
3181 var nearIntersectionX = inRangeParams[0] * d[0] + x1;
3182 var nearIntersectionY = inRangeParams[0] * d[1] + y1;
3183
3184 if (inRangeParams.length > 1) {
3185 if (inRangeParams[0] == inRangeParams[1]) {
3186 return [nearIntersectionX, nearIntersectionY];
3187 } else {
3188 var farIntersectionX = inRangeParams[1] * d[0] + x1;
3189 var farIntersectionY = inRangeParams[1] * d[1] + y1;
3190 return [nearIntersectionX, nearIntersectionY, farIntersectionX, farIntersectionY];
3191 }
3192 } else {
3193 return [nearIntersectionX, nearIntersectionY];
3194 }
3195};
3196var midOfThree = function midOfThree(a, b, c) {
3197 if (b <= a && a <= c || c <= a && a <= b) {
3198 return a;
3199 } else if (a <= b && b <= c || c <= b && b <= a) {
3200 return b;
3201 } else {
3202 return c;
3203 }
3204}; // (x1,y1)=>(x2,y2) intersect with (x3,y3)=>(x4,y4)
3205
3206var finiteLinesIntersect = function finiteLinesIntersect(x1, y1, x2, y2, x3, y3, x4, y4, infiniteLines) {
3207 var dx13 = x1 - x3;
3208 var dx21 = x2 - x1;
3209 var dx43 = x4 - x3;
3210 var dy13 = y1 - y3;
3211 var dy21 = y2 - y1;
3212 var dy43 = y4 - y3;
3213 var ua_t = dx43 * dy13 - dy43 * dx13;
3214 var ub_t = dx21 * dy13 - dy21 * dx13;
3215 var u_b = dy43 * dx21 - dx43 * dy21;
3216
3217 if (u_b !== 0) {
3218 var ua = ua_t / u_b;
3219 var ub = ub_t / u_b;
3220 var flptThreshold = 0.001;
3221
3222 var _min = 0 - flptThreshold;
3223
3224 var _max = 1 + flptThreshold;
3225
3226 if (_min <= ua && ua <= _max && _min <= ub && ub <= _max) {
3227 return [x1 + ua * dx21, y1 + ua * dy21];
3228 } else {
3229 if (!infiniteLines) {
3230 return [];
3231 } else {
3232 return [x1 + ua * dx21, y1 + ua * dy21];
3233 }
3234 }
3235 } else {
3236 if (ua_t === 0 || ub_t === 0) {
3237 // Parallel, coincident lines. Check if overlap
3238 // Check endpoint of second line
3239 if (midOfThree(x1, x2, x4) === x4) {
3240 return [x4, y4];
3241 } // Check start point of second line
3242
3243
3244 if (midOfThree(x1, x2, x3) === x3) {
3245 return [x3, y3];
3246 } // Endpoint of first line
3247
3248
3249 if (midOfThree(x3, x4, x2) === x2) {
3250 return [x2, y2];
3251 }
3252
3253 return [];
3254 } else {
3255 // Parallel, non-coincident
3256 return [];
3257 }
3258 }
3259}; // math.polygonIntersectLine( x, y, basePoints, centerX, centerY, width, height, padding )
3260// intersect a node polygon (pts transformed)
3261//
3262// math.polygonIntersectLine( x, y, basePoints, centerX, centerY )
3263// intersect the points (no transform)
3264
3265var polygonIntersectLine = function polygonIntersectLine(x, y, basePoints, centerX, centerY, width, height, padding) {
3266 var intersections = [];
3267 var intersection;
3268 var transformedPoints = new Array(basePoints.length);
3269 var doTransform = true;
3270
3271 if (width == null) {
3272 doTransform = false;
3273 }
3274
3275 var points;
3276
3277 if (doTransform) {
3278 for (var i = 0; i < transformedPoints.length / 2; i++) {
3279 transformedPoints[i * 2] = basePoints[i * 2] * width + centerX;
3280 transformedPoints[i * 2 + 1] = basePoints[i * 2 + 1] * height + centerY;
3281 }
3282
3283 if (padding > 0) {
3284 var expandedLineSet = expandPolygon(transformedPoints, -padding);
3285 points = joinLines(expandedLineSet);
3286 } else {
3287 points = transformedPoints;
3288 }
3289 } else {
3290 points = basePoints;
3291 }
3292
3293 var currentX, currentY, nextX, nextY;
3294
3295 for (var _i2 = 0; _i2 < points.length / 2; _i2++) {
3296 currentX = points[_i2 * 2];
3297 currentY = points[_i2 * 2 + 1];
3298
3299 if (_i2 < points.length / 2 - 1) {
3300 nextX = points[(_i2 + 1) * 2];
3301 nextY = points[(_i2 + 1) * 2 + 1];
3302 } else {
3303 nextX = points[0];
3304 nextY = points[1];
3305 }
3306
3307 intersection = finiteLinesIntersect(x, y, centerX, centerY, currentX, currentY, nextX, nextY);
3308
3309 if (intersection.length !== 0) {
3310 intersections.push(intersection[0], intersection[1]);
3311 }
3312 }
3313
3314 return intersections;
3315};
3316var roundPolygonIntersectLine = function roundPolygonIntersectLine(x, y, basePoints, centerX, centerY, width, height, padding) {
3317 var intersections = [];
3318 var intersection;
3319 var lines = new Array(basePoints.length);
3320 var halfW = width / 2;
3321 var halfH = height / 2;
3322 var cornerRadius = getRoundPolygonRadius(width, height);
3323
3324 for (var i = 0; i < basePoints.length / 4; i++) {
3325 var sourceUv = void 0,
3326 destUv = void 0;
3327
3328 if (i === 0) {
3329 sourceUv = basePoints.length - 2;
3330 } else {
3331 sourceUv = i * 4 - 2;
3332 }
3333
3334 destUv = i * 4 + 2;
3335 var px = centerX + halfW * basePoints[i * 4];
3336 var py = centerY + halfH * basePoints[i * 4 + 1];
3337 var cosTheta = -basePoints[sourceUv] * basePoints[destUv] - basePoints[sourceUv + 1] * basePoints[destUv + 1];
3338 var offset = cornerRadius / Math.tan(Math.acos(cosTheta) / 2);
3339 var cp0x = px - offset * basePoints[sourceUv];
3340 var cp0y = py - offset * basePoints[sourceUv + 1];
3341 var cp1x = px + offset * basePoints[destUv];
3342 var cp1y = py + offset * basePoints[destUv + 1];
3343
3344 if (i === 0) {
3345 lines[basePoints.length - 2] = cp0x;
3346 lines[basePoints.length - 1] = cp0y;
3347 } else {
3348 lines[i * 4 - 2] = cp0x;
3349 lines[i * 4 - 1] = cp0y;
3350 }
3351
3352 lines[i * 4] = cp1x;
3353 lines[i * 4 + 1] = cp1y;
3354 var orthx = basePoints[sourceUv + 1];
3355 var orthy = -basePoints[sourceUv];
3356 var cosAlpha = orthx * basePoints[destUv] + orthy * basePoints[destUv + 1];
3357
3358 if (cosAlpha < 0) {
3359 orthx *= -1;
3360 orthy *= -1;
3361 }
3362
3363 var cx = cp0x + orthx * cornerRadius;
3364 var cy = cp0y + orthy * cornerRadius;
3365 intersection = intersectLineCircle(x, y, centerX, centerY, cx, cy, cornerRadius);
3366
3367 if (intersection.length !== 0) {
3368 intersections.push(intersection[0], intersection[1]);
3369 }
3370 }
3371
3372 for (var _i3 = 0; _i3 < lines.length / 4; _i3++) {
3373 intersection = finiteLinesIntersect(x, y, centerX, centerY, lines[_i3 * 4], lines[_i3 * 4 + 1], lines[_i3 * 4 + 2], lines[_i3 * 4 + 3], false);
3374
3375 if (intersection.length !== 0) {
3376 intersections.push(intersection[0], intersection[1]);
3377 }
3378 }
3379
3380 if (intersections.length > 2) {
3381 var lowestIntersection = [intersections[0], intersections[1]];
3382 var lowestSquaredDistance = Math.pow(lowestIntersection[0] - x, 2) + Math.pow(lowestIntersection[1] - y, 2);
3383
3384 for (var _i4 = 1; _i4 < intersections.length / 2; _i4++) {
3385 var squaredDistance = Math.pow(intersections[_i4 * 2] - x, 2) + Math.pow(intersections[_i4 * 2 + 1] - y, 2);
3386
3387 if (squaredDistance <= lowestSquaredDistance) {
3388 lowestIntersection[0] = intersections[_i4 * 2];
3389 lowestIntersection[1] = intersections[_i4 * 2 + 1];
3390 lowestSquaredDistance = squaredDistance;
3391 }
3392 }
3393
3394 return lowestIntersection;
3395 }
3396
3397 return intersections;
3398};
3399var shortenIntersection = function shortenIntersection(intersection, offset, amount) {
3400 var disp = [intersection[0] - offset[0], intersection[1] - offset[1]];
3401 var length = Math.sqrt(disp[0] * disp[0] + disp[1] * disp[1]);
3402 var lenRatio = (length - amount) / length;
3403
3404 if (lenRatio < 0) {
3405 lenRatio = 0.00001;
3406 }
3407
3408 return [offset[0] + lenRatio * disp[0], offset[1] + lenRatio * disp[1]];
3409};
3410var generateUnitNgonPointsFitToSquare = function generateUnitNgonPointsFitToSquare(sides, rotationRadians) {
3411 var points = generateUnitNgonPoints(sides, rotationRadians);
3412 points = fitPolygonToSquare(points);
3413 return points;
3414};
3415var fitPolygonToSquare = function fitPolygonToSquare(points) {
3416 var x, y;
3417 var sides = points.length / 2;
3418 var minX = Infinity,
3419 minY = Infinity,
3420 maxX = -Infinity,
3421 maxY = -Infinity;
3422
3423 for (var i = 0; i < sides; i++) {
3424 x = points[2 * i];
3425 y = points[2 * i + 1];
3426 minX = Math.min(minX, x);
3427 maxX = Math.max(maxX, x);
3428 minY = Math.min(minY, y);
3429 maxY = Math.max(maxY, y);
3430 } // stretch factors
3431
3432
3433 var sx = 2 / (maxX - minX);
3434 var sy = 2 / (maxY - minY);
3435
3436 for (var _i5 = 0; _i5 < sides; _i5++) {
3437 x = points[2 * _i5] = points[2 * _i5] * sx;
3438 y = points[2 * _i5 + 1] = points[2 * _i5 + 1] * sy;
3439 minX = Math.min(minX, x);
3440 maxX = Math.max(maxX, x);
3441 minY = Math.min(minY, y);
3442 maxY = Math.max(maxY, y);
3443 }
3444
3445 if (minY < -1) {
3446 for (var _i6 = 0; _i6 < sides; _i6++) {
3447 y = points[2 * _i6 + 1] = points[2 * _i6 + 1] + (-1 - minY);
3448 }
3449 }
3450
3451 return points;
3452};
3453var generateUnitNgonPoints = function generateUnitNgonPoints(sides, rotationRadians) {
3454 var increment = 1.0 / sides * 2 * Math.PI;
3455 var startAngle = sides % 2 === 0 ? Math.PI / 2.0 + increment / 2.0 : Math.PI / 2.0;
3456 startAngle += rotationRadians;
3457 var points = new Array(sides * 2);
3458 var currentAngle;
3459
3460 for (var i = 0; i < sides; i++) {
3461 currentAngle = i * increment + startAngle;
3462 points[2 * i] = Math.cos(currentAngle); // x
3463
3464 points[2 * i + 1] = Math.sin(-currentAngle); // y
3465 }
3466
3467 return points;
3468}; // Set the default radius, unless half of width or height is smaller than default
3469
3470var getRoundRectangleRadius = function getRoundRectangleRadius(width, height) {
3471 return Math.min(width / 4, height / 4, 8);
3472}; // Set the default radius
3473
3474var getRoundPolygonRadius = function getRoundPolygonRadius(width, height) {
3475 return Math.min(width / 10, height / 10, 8);
3476};
3477var getCutRectangleCornerLength = function getCutRectangleCornerLength() {
3478 return 8;
3479};
3480var bezierPtsToQuadCoeff = function bezierPtsToQuadCoeff(p0, p1, p2) {
3481 return [p0 - 2 * p1 + p2, 2 * (p1 - p0), p0];
3482}; // get curve width, height, and control point position offsets as a percentage of node height / width
3483
3484var getBarrelCurveConstants = function getBarrelCurveConstants(width, height) {
3485 return {
3486 heightOffset: Math.min(15, 0.05 * height),
3487 widthOffset: Math.min(100, 0.25 * width),
3488 ctrlPtOffsetPct: 0.05
3489 };
3490};
3491
3492var pageRankDefaults = defaults$g({
3493 dampingFactor: 0.8,
3494 precision: 0.000001,
3495 iterations: 200,
3496 weight: function weight(edge) {
3497 return 1;
3498 }
3499});
3500var elesfn$o = {
3501 pageRank: function pageRank(options) {
3502 var _pageRankDefaults = pageRankDefaults(options),
3503 dampingFactor = _pageRankDefaults.dampingFactor,
3504 precision = _pageRankDefaults.precision,
3505 iterations = _pageRankDefaults.iterations,
3506 weight = _pageRankDefaults.weight;
3507
3508 var cy = this._private.cy;
3509
3510 var _this$byGroup = this.byGroup(),
3511 nodes = _this$byGroup.nodes,
3512 edges = _this$byGroup.edges;
3513
3514 var numNodes = nodes.length;
3515 var numNodesSqd = numNodes * numNodes;
3516 var numEdges = edges.length; // Construct transposed adjacency matrix
3517 // First lets have a zeroed matrix of the right size
3518 // We'll also keep track of the sum of each column
3519
3520 var matrix = new Array(numNodesSqd);
3521 var columnSum = new Array(numNodes);
3522 var additionalProb = (1 - dampingFactor) / numNodes; // Create null matrix
3523
3524 for (var i = 0; i < numNodes; i++) {
3525 for (var j = 0; j < numNodes; j++) {
3526 var n = i * numNodes + j;
3527 matrix[n] = 0;
3528 }
3529
3530 columnSum[i] = 0;
3531 } // Now, process edges
3532
3533
3534 for (var _i = 0; _i < numEdges; _i++) {
3535 var edge = edges[_i];
3536 var srcId = edge.data('source');
3537 var tgtId = edge.data('target'); // Don't include loops in the matrix
3538
3539 if (srcId === tgtId) {
3540 continue;
3541 }
3542
3543 var s = nodes.indexOfId(srcId);
3544 var t = nodes.indexOfId(tgtId);
3545 var w = weight(edge);
3546
3547 var _n = t * numNodes + s; // Update matrix
3548
3549
3550 matrix[_n] += w; // Update column sum
3551
3552 columnSum[s] += w;
3553 } // Add additional probability based on damping factor
3554 // Also, take into account columns that have sum = 0
3555
3556
3557 var p = 1.0 / numNodes + additionalProb; // Shorthand
3558 // Traverse matrix, column by column
3559
3560 for (var _j = 0; _j < numNodes; _j++) {
3561 if (columnSum[_j] === 0) {
3562 // No 'links' out from node jth, assume equal probability for each possible node
3563 for (var _i2 = 0; _i2 < numNodes; _i2++) {
3564 var _n2 = _i2 * numNodes + _j;
3565
3566 matrix[_n2] = p;
3567 }
3568 } else {
3569 // Node jth has outgoing link, compute normalized probabilities
3570 for (var _i3 = 0; _i3 < numNodes; _i3++) {
3571 var _n3 = _i3 * numNodes + _j;
3572
3573 matrix[_n3] = matrix[_n3] / columnSum[_j] + additionalProb;
3574 }
3575 }
3576 } // Compute dominant eigenvector using power method
3577
3578
3579 var eigenvector = new Array(numNodes);
3580 var temp = new Array(numNodes);
3581 var previous; // Start with a vector of all 1's
3582 // Also, initialize a null vector which will be used as shorthand
3583
3584 for (var _i4 = 0; _i4 < numNodes; _i4++) {
3585 eigenvector[_i4] = 1;
3586 }
3587
3588 for (var iter = 0; iter < iterations; iter++) {
3589 // Temp array with all 0's
3590 for (var _i5 = 0; _i5 < numNodes; _i5++) {
3591 temp[_i5] = 0;
3592 } // Multiply matrix with previous result
3593
3594
3595 for (var _i6 = 0; _i6 < numNodes; _i6++) {
3596 for (var _j2 = 0; _j2 < numNodes; _j2++) {
3597 var _n4 = _i6 * numNodes + _j2;
3598
3599 temp[_i6] += matrix[_n4] * eigenvector[_j2];
3600 }
3601 }
3602
3603 inPlaceSumNormalize(temp);
3604 previous = eigenvector;
3605 eigenvector = temp;
3606 temp = previous;
3607 var diff = 0; // Compute difference (squared module) of both vectors
3608
3609 for (var _i7 = 0; _i7 < numNodes; _i7++) {
3610 var delta = previous[_i7] - eigenvector[_i7];
3611 diff += delta * delta;
3612 } // If difference is less than the desired threshold, stop iterating
3613
3614
3615 if (diff < precision) {
3616 break;
3617 }
3618 } // Construct result
3619
3620
3621 var res = {
3622 rank: function rank(node) {
3623 node = cy.collection(node)[0];
3624 return eigenvector[nodes.indexOf(node)];
3625 }
3626 };
3627 return res;
3628 } // pageRank
3629
3630}; // elesfn
3631
3632var defaults$f = defaults$g({
3633 root: null,
3634 weight: function weight(edge) {
3635 return 1;
3636 },
3637 directed: false,
3638 alpha: 0
3639});
3640var elesfn$n = {
3641 degreeCentralityNormalized: function degreeCentralityNormalized(options) {
3642 options = defaults$f(options);
3643 var cy = this.cy();
3644 var nodes = this.nodes();
3645 var numNodes = nodes.length;
3646
3647 if (!options.directed) {
3648 var degrees = {};
3649 var maxDegree = 0;
3650
3651 for (var i = 0; i < numNodes; i++) {
3652 var node = nodes[i]; // add current node to the current options object and call degreeCentrality
3653
3654 options.root = node;
3655 var currDegree = this.degreeCentrality(options);
3656
3657 if (maxDegree < currDegree.degree) {
3658 maxDegree = currDegree.degree;
3659 }
3660
3661 degrees[node.id()] = currDegree.degree;
3662 }
3663
3664 return {
3665 degree: function degree(node) {
3666 if (maxDegree === 0) {
3667 return 0;
3668 }
3669
3670 if (string(node)) {
3671 // from is a selector string
3672 node = cy.filter(node);
3673 }
3674
3675 return degrees[node.id()] / maxDegree;
3676 }
3677 };
3678 } else {
3679 var indegrees = {};
3680 var outdegrees = {};
3681 var maxIndegree = 0;
3682 var maxOutdegree = 0;
3683
3684 for (var _i = 0; _i < numNodes; _i++) {
3685 var _node = nodes[_i];
3686
3687 var id = _node.id(); // add current node to the current options object and call degreeCentrality
3688
3689
3690 options.root = _node;
3691
3692 var _currDegree = this.degreeCentrality(options);
3693
3694 if (maxIndegree < _currDegree.indegree) maxIndegree = _currDegree.indegree;
3695 if (maxOutdegree < _currDegree.outdegree) maxOutdegree = _currDegree.outdegree;
3696 indegrees[id] = _currDegree.indegree;
3697 outdegrees[id] = _currDegree.outdegree;
3698 }
3699
3700 return {
3701 indegree: function indegree(node) {
3702 if (maxIndegree == 0) {
3703 return 0;
3704 }
3705
3706 if (string(node)) {
3707 // from is a selector string
3708 node = cy.filter(node);
3709 }
3710
3711 return indegrees[node.id()] / maxIndegree;
3712 },
3713 outdegree: function outdegree(node) {
3714 if (maxOutdegree === 0) {
3715 return 0;
3716 }
3717
3718 if (string(node)) {
3719 // from is a selector string
3720 node = cy.filter(node);
3721 }
3722
3723 return outdegrees[node.id()] / maxOutdegree;
3724 }
3725 };
3726 }
3727 },
3728 // degreeCentralityNormalized
3729 // Implemented from the algorithm in Opsahl's paper
3730 // "Node centrality in weighted networks: Generalizing degree and shortest paths"
3731 // check the heading 2 "Degree"
3732 degreeCentrality: function degreeCentrality(options) {
3733 options = defaults$f(options);
3734 var cy = this.cy();
3735 var callingEles = this;
3736 var _options = options,
3737 root = _options.root,
3738 weight = _options.weight,
3739 directed = _options.directed,
3740 alpha = _options.alpha;
3741 root = cy.collection(root)[0];
3742
3743 if (!directed) {
3744 var connEdges = root.connectedEdges().intersection(callingEles);
3745 var k = connEdges.length;
3746 var s = 0; // Now, sum edge weights
3747
3748 for (var i = 0; i < connEdges.length; i++) {
3749 s += weight(connEdges[i]);
3750 }
3751
3752 return {
3753 degree: Math.pow(k, 1 - alpha) * Math.pow(s, alpha)
3754 };
3755 } else {
3756 var edges = root.connectedEdges();
3757 var incoming = edges.filter(function (edge) {
3758 return edge.target().same(root) && callingEles.has(edge);
3759 });
3760 var outgoing = edges.filter(function (edge) {
3761 return edge.source().same(root) && callingEles.has(edge);
3762 });
3763 var k_in = incoming.length;
3764 var k_out = outgoing.length;
3765 var s_in = 0;
3766 var s_out = 0; // Now, sum incoming edge weights
3767
3768 for (var _i2 = 0; _i2 < incoming.length; _i2++) {
3769 s_in += weight(incoming[_i2]);
3770 } // Now, sum outgoing edge weights
3771
3772
3773 for (var _i3 = 0; _i3 < outgoing.length; _i3++) {
3774 s_out += weight(outgoing[_i3]);
3775 }
3776
3777 return {
3778 indegree: Math.pow(k_in, 1 - alpha) * Math.pow(s_in, alpha),
3779 outdegree: Math.pow(k_out, 1 - alpha) * Math.pow(s_out, alpha)
3780 };
3781 }
3782 } // degreeCentrality
3783
3784}; // elesfn
3785// nice, short mathematical alias
3786
3787elesfn$n.dc = elesfn$n.degreeCentrality;
3788elesfn$n.dcn = elesfn$n.degreeCentralityNormalised = elesfn$n.degreeCentralityNormalized;
3789
3790var defaults$e = defaults$g({
3791 harmonic: true,
3792 weight: function weight() {
3793 return 1;
3794 },
3795 directed: false,
3796 root: null
3797});
3798var elesfn$m = {
3799 closenessCentralityNormalized: function closenessCentralityNormalized(options) {
3800 var _defaults = defaults$e(options),
3801 harmonic = _defaults.harmonic,
3802 weight = _defaults.weight,
3803 directed = _defaults.directed;
3804
3805 var cy = this.cy();
3806 var closenesses = {};
3807 var maxCloseness = 0;
3808 var nodes = this.nodes();
3809 var fw = this.floydWarshall({
3810 weight: weight,
3811 directed: directed
3812 }); // Compute closeness for every node and find the maximum closeness
3813
3814 for (var i = 0; i < nodes.length; i++) {
3815 var currCloseness = 0;
3816 var node_i = nodes[i];
3817
3818 for (var j = 0; j < nodes.length; j++) {
3819 if (i !== j) {
3820 var d = fw.distance(node_i, nodes[j]);
3821
3822 if (harmonic) {
3823 currCloseness += 1 / d;
3824 } else {
3825 currCloseness += d;
3826 }
3827 }
3828 }
3829
3830 if (!harmonic) {
3831 currCloseness = 1 / currCloseness;
3832 }
3833
3834 if (maxCloseness < currCloseness) {
3835 maxCloseness = currCloseness;
3836 }
3837
3838 closenesses[node_i.id()] = currCloseness;
3839 }
3840
3841 return {
3842 closeness: function closeness(node) {
3843 if (maxCloseness == 0) {
3844 return 0;
3845 }
3846
3847 if (string(node)) {
3848 // from is a selector string
3849 node = cy.filter(node)[0].id();
3850 } else {
3851 // from is a node
3852 node = node.id();
3853 }
3854
3855 return closenesses[node] / maxCloseness;
3856 }
3857 };
3858 },
3859 // Implemented from pseudocode from wikipedia
3860 closenessCentrality: function closenessCentrality(options) {
3861 var _defaults2 = defaults$e(options),
3862 root = _defaults2.root,
3863 weight = _defaults2.weight,
3864 directed = _defaults2.directed,
3865 harmonic = _defaults2.harmonic;
3866
3867 root = this.filter(root)[0]; // we need distance from this node to every other node
3868
3869 var dijkstra = this.dijkstra({
3870 root: root,
3871 weight: weight,
3872 directed: directed
3873 });
3874 var totalDistance = 0;
3875 var nodes = this.nodes();
3876
3877 for (var i = 0; i < nodes.length; i++) {
3878 var n = nodes[i];
3879
3880 if (!n.same(root)) {
3881 var d = dijkstra.distanceTo(n);
3882
3883 if (harmonic) {
3884 totalDistance += 1 / d;
3885 } else {
3886 totalDistance += d;
3887 }
3888 }
3889 }
3890
3891 return harmonic ? totalDistance : 1 / totalDistance;
3892 } // closenessCentrality
3893
3894}; // elesfn
3895// nice, short mathematical alias
3896
3897elesfn$m.cc = elesfn$m.closenessCentrality;
3898elesfn$m.ccn = elesfn$m.closenessCentralityNormalised = elesfn$m.closenessCentralityNormalized;
3899
3900var defaults$d = defaults$g({
3901 weight: null,
3902 directed: false
3903});
3904var elesfn$l = {
3905 // Implemented from the algorithm in the paper "On Variants of Shortest-Path Betweenness Centrality and their Generic Computation" by Ulrik Brandes
3906 betweennessCentrality: function betweennessCentrality(options) {
3907 var _defaults = defaults$d(options),
3908 directed = _defaults.directed,
3909 weight = _defaults.weight;
3910
3911 var weighted = weight != null;
3912 var cy = this.cy(); // starting
3913
3914 var V = this.nodes();
3915 var A = {};
3916 var _C = {};
3917 var max = 0;
3918 var C = {
3919 set: function set(key, val) {
3920 _C[key] = val;
3921
3922 if (val > max) {
3923 max = val;
3924 }
3925 },
3926 get: function get(key) {
3927 return _C[key];
3928 }
3929 }; // A contains the neighborhoods of every node
3930
3931 for (var i = 0; i < V.length; i++) {
3932 var v = V[i];
3933 var vid = v.id();
3934
3935 if (directed) {
3936 A[vid] = v.outgoers().nodes(); // get outgoers of every node
3937 } else {
3938 A[vid] = v.openNeighborhood().nodes(); // get neighbors of every node
3939 }
3940
3941 C.set(vid, 0);
3942 }
3943
3944 var _loop = function _loop(s) {
3945 var sid = V[s].id();
3946 var S = []; // stack
3947
3948 var P = {};
3949 var g = {};
3950 var d = {};
3951 var Q = new Heap(function (a, b) {
3952 return d[a] - d[b];
3953 }); // queue
3954 // init dictionaries
3955
3956 for (var _i = 0; _i < V.length; _i++) {
3957 var _vid = V[_i].id();
3958
3959 P[_vid] = [];
3960 g[_vid] = 0;
3961 d[_vid] = Infinity;
3962 }
3963
3964 g[sid] = 1; // sigma
3965
3966 d[sid] = 0; // distance to s
3967
3968 Q.push(sid);
3969
3970 while (!Q.empty()) {
3971 var _v = Q.pop();
3972
3973 S.push(_v);
3974
3975 if (weighted) {
3976 for (var j = 0; j < A[_v].length; j++) {
3977 var w = A[_v][j];
3978 var vEle = cy.getElementById(_v);
3979 var edge = void 0;
3980
3981 if (vEle.edgesTo(w).length > 0) {
3982 edge = vEle.edgesTo(w)[0];
3983 } else {
3984 edge = w.edgesTo(vEle)[0];
3985 }
3986
3987 var edgeWeight = weight(edge);
3988 w = w.id();
3989
3990 if (d[w] > d[_v] + edgeWeight) {
3991 d[w] = d[_v] + edgeWeight;
3992
3993 if (Q.nodes.indexOf(w) < 0) {
3994 //if w is not in Q
3995 Q.push(w);
3996 } else {
3997 // update position if w is in Q
3998 Q.updateItem(w);
3999 }
4000
4001 g[w] = 0;
4002 P[w] = [];
4003 }
4004
4005 if (d[w] == d[_v] + edgeWeight) {
4006 g[w] = g[w] + g[_v];
4007 P[w].push(_v);
4008 }
4009 }
4010 } else {
4011 for (var _j = 0; _j < A[_v].length; _j++) {
4012 var _w = A[_v][_j].id();
4013
4014 if (d[_w] == Infinity) {
4015 Q.push(_w);
4016 d[_w] = d[_v] + 1;
4017 }
4018
4019 if (d[_w] == d[_v] + 1) {
4020 g[_w] = g[_w] + g[_v];
4021
4022 P[_w].push(_v);
4023 }
4024 }
4025 }
4026 }
4027
4028 var e = {};
4029
4030 for (var _i2 = 0; _i2 < V.length; _i2++) {
4031 e[V[_i2].id()] = 0;
4032 }
4033
4034 while (S.length > 0) {
4035 var _w2 = S.pop();
4036
4037 for (var _j2 = 0; _j2 < P[_w2].length; _j2++) {
4038 var _v2 = P[_w2][_j2];
4039 e[_v2] = e[_v2] + g[_v2] / g[_w2] * (1 + e[_w2]);
4040 }
4041
4042 if (_w2 != V[s].id()) {
4043 C.set(_w2, C.get(_w2) + e[_w2]);
4044 }
4045 }
4046 };
4047
4048 for (var s = 0; s < V.length; s++) {
4049 _loop(s);
4050 }
4051
4052 var ret = {
4053 betweenness: function betweenness(node) {
4054 var id = cy.collection(node).id();
4055 return C.get(id);
4056 },
4057 betweennessNormalized: function betweennessNormalized(node) {
4058 if (max == 0) {
4059 return 0;
4060 }
4061
4062 var id = cy.collection(node).id();
4063 return C.get(id) / max;
4064 }
4065 }; // alias
4066
4067 ret.betweennessNormalised = ret.betweennessNormalized;
4068 return ret;
4069 } // betweennessCentrality
4070
4071}; // elesfn
4072// nice, short mathematical alias
4073
4074elesfn$l.bc = elesfn$l.betweennessCentrality;
4075
4076// Implemented by Zoe Xi @zoexi for GSOC 2016
4077/* eslint-disable no-unused-vars */
4078
4079var defaults$c = defaults$g({
4080 expandFactor: 2,
4081 // affects time of computation and cluster granularity to some extent: M * M
4082 inflateFactor: 2,
4083 // affects cluster granularity (the greater the value, the more clusters): M(i,j) / E(j)
4084 multFactor: 1,
4085 // optional self loops for each node. Use a neutral value to improve cluster computations.
4086 maxIterations: 20,
4087 // maximum number of iterations of the MCL algorithm in a single run
4088 attributes: [// attributes/features used to group nodes, ie. similarity values between nodes
4089 function (edge) {
4090 return 1;
4091 }]
4092});
4093/* eslint-enable */
4094
4095var setOptions$3 = function setOptions(options) {
4096 return defaults$c(options);
4097};
4098/* eslint-enable */
4099
4100
4101var getSimilarity$1 = function getSimilarity(edge, attributes) {
4102 var total = 0;
4103
4104 for (var i = 0; i < attributes.length; i++) {
4105 total += attributes[i](edge);
4106 }
4107
4108 return total;
4109};
4110
4111var addLoops = function addLoops(M, n, val) {
4112 for (var i = 0; i < n; i++) {
4113 M[i * n + i] = val;
4114 }
4115};
4116
4117var normalize = function normalize(M, n) {
4118 var sum;
4119
4120 for (var col = 0; col < n; col++) {
4121 sum = 0;
4122
4123 for (var row = 0; row < n; row++) {
4124 sum += M[row * n + col];
4125 }
4126
4127 for (var _row = 0; _row < n; _row++) {
4128 M[_row * n + col] = M[_row * n + col] / sum;
4129 }
4130 }
4131}; // TODO: blocked matrix multiplication?
4132
4133
4134var mmult = function mmult(A, B, n) {
4135 var C = new Array(n * n);
4136
4137 for (var i = 0; i < n; i++) {
4138 for (var j = 0; j < n; j++) {
4139 C[i * n + j] = 0;
4140 }
4141
4142 for (var k = 0; k < n; k++) {
4143 for (var _j = 0; _j < n; _j++) {
4144 C[i * n + _j] += A[i * n + k] * B[k * n + _j];
4145 }
4146 }
4147 }
4148
4149 return C;
4150};
4151
4152var expand = function expand(M, n, expandFactor
4153/** power **/
4154) {
4155 var _M = M.slice(0);
4156
4157 for (var p = 1; p < expandFactor; p++) {
4158 M = mmult(M, _M, n);
4159 }
4160
4161 return M;
4162};
4163
4164var inflate = function inflate(M, n, inflateFactor
4165/** r **/
4166) {
4167 var _M = new Array(n * n); // M(i,j) ^ inflatePower
4168
4169
4170 for (var i = 0; i < n * n; i++) {
4171 _M[i] = Math.pow(M[i], inflateFactor);
4172 }
4173
4174 normalize(_M, n);
4175 return _M;
4176};
4177
4178var hasConverged = function hasConverged(M, _M, n2, roundFactor) {
4179 // Check that both matrices have the same elements (i,j)
4180 for (var i = 0; i < n2; i++) {
4181 var v1 = Math.round(M[i] * Math.pow(10, roundFactor)) / Math.pow(10, roundFactor); // truncate to 'roundFactor' decimal places
4182
4183 var v2 = Math.round(_M[i] * Math.pow(10, roundFactor)) / Math.pow(10, roundFactor);
4184
4185 if (v1 !== v2) {
4186 return false;
4187 }
4188 }
4189
4190 return true;
4191};
4192
4193var assign$2 = function assign(M, n, nodes, cy) {
4194 var clusters = [];
4195
4196 for (var i = 0; i < n; i++) {
4197 var cluster = [];
4198
4199 for (var j = 0; j < n; j++) {
4200 // Row-wise attractors and elements that they attract belong in same cluster
4201 if (Math.round(M[i * n + j] * 1000) / 1000 > 0) {
4202 cluster.push(nodes[j]);
4203 }
4204 }
4205
4206 if (cluster.length !== 0) {
4207 clusters.push(cy.collection(cluster));
4208 }
4209 }
4210
4211 return clusters;
4212};
4213
4214var isDuplicate = function isDuplicate(c1, c2) {
4215 for (var i = 0; i < c1.length; i++) {
4216 if (!c2[i] || c1[i].id() !== c2[i].id()) {
4217 return false;
4218 }
4219 }
4220
4221 return true;
4222};
4223
4224var removeDuplicates = function removeDuplicates(clusters) {
4225 for (var i = 0; i < clusters.length; i++) {
4226 for (var j = 0; j < clusters.length; j++) {
4227 if (i != j && isDuplicate(clusters[i], clusters[j])) {
4228 clusters.splice(j, 1);
4229 }
4230 }
4231 }
4232
4233 return clusters;
4234};
4235
4236var markovClustering = function markovClustering(options) {
4237 var nodes = this.nodes();
4238 var edges = this.edges();
4239 var cy = this.cy(); // Set parameters of algorithm:
4240
4241 var opts = setOptions$3(options); // Map each node to its position in node array
4242
4243 var id2position = {};
4244
4245 for (var i = 0; i < nodes.length; i++) {
4246 id2position[nodes[i].id()] = i;
4247 } // Generate stochastic matrix M from input graph G (should be symmetric/undirected)
4248
4249
4250 var n = nodes.length,
4251 n2 = n * n;
4252
4253 var M = new Array(n2),
4254 _M;
4255
4256 for (var _i = 0; _i < n2; _i++) {
4257 M[_i] = 0;
4258 }
4259
4260 for (var e = 0; e < edges.length; e++) {
4261 var edge = edges[e];
4262 var _i2 = id2position[edge.source().id()];
4263 var j = id2position[edge.target().id()];
4264 var sim = getSimilarity$1(edge, opts.attributes);
4265 M[_i2 * n + j] += sim; // G should be symmetric and undirected
4266
4267 M[j * n + _i2] += sim;
4268 } // Begin Markov cluster algorithm
4269 // Step 1: Add self loops to each node, ie. add multFactor to matrix diagonal
4270
4271
4272 addLoops(M, n, opts.multFactor); // Step 2: M = normalize( M );
4273
4274 normalize(M, n);
4275 var isStillMoving = true;
4276 var iterations = 0;
4277
4278 while (isStillMoving && iterations < opts.maxIterations) {
4279 isStillMoving = false; // Step 3:
4280
4281 _M = expand(M, n, opts.expandFactor); // Step 4:
4282
4283 M = inflate(_M, n, opts.inflateFactor); // Step 5: check to see if ~steady state has been reached
4284
4285 if (!hasConverged(M, _M, n2, 4)) {
4286 isStillMoving = true;
4287 }
4288
4289 iterations++;
4290 } // Build clusters from matrix
4291
4292
4293 var clusters = assign$2(M, n, nodes, cy); // Remove duplicate clusters due to symmetry of graph and M matrix
4294
4295 clusters = removeDuplicates(clusters);
4296 return clusters;
4297};
4298
4299var markovClustering$1 = {
4300 markovClustering: markovClustering,
4301 mcl: markovClustering
4302};
4303
4304// Common distance metrics for clustering algorithms
4305
4306var identity = function identity(x) {
4307 return x;
4308};
4309
4310var absDiff = function absDiff(p, q) {
4311 return Math.abs(q - p);
4312};
4313
4314var addAbsDiff = function addAbsDiff(total, p, q) {
4315 return total + absDiff(p, q);
4316};
4317
4318var addSquaredDiff = function addSquaredDiff(total, p, q) {
4319 return total + Math.pow(q - p, 2);
4320};
4321
4322var sqrt = function sqrt(x) {
4323 return Math.sqrt(x);
4324};
4325
4326var maxAbsDiff = function maxAbsDiff(currentMax, p, q) {
4327 return Math.max(currentMax, absDiff(p, q));
4328};
4329
4330var getDistance = function getDistance(length, getP, getQ, init, visit) {
4331 var post = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : identity;
4332 var ret = init;
4333 var p, q;
4334
4335 for (var dim = 0; dim < length; dim++) {
4336 p = getP(dim);
4337 q = getQ(dim);
4338 ret = visit(ret, p, q);
4339 }
4340
4341 return post(ret);
4342};
4343
4344var distances = {
4345 euclidean: function euclidean(length, getP, getQ) {
4346 if (length >= 2) {
4347 return getDistance(length, getP, getQ, 0, addSquaredDiff, sqrt);
4348 } else {
4349 // for single attr case, more efficient to avoid sqrt
4350 return getDistance(length, getP, getQ, 0, addAbsDiff);
4351 }
4352 },
4353 squaredEuclidean: function squaredEuclidean(length, getP, getQ) {
4354 return getDistance(length, getP, getQ, 0, addSquaredDiff);
4355 },
4356 manhattan: function manhattan(length, getP, getQ) {
4357 return getDistance(length, getP, getQ, 0, addAbsDiff);
4358 },
4359 max: function max(length, getP, getQ) {
4360 return getDistance(length, getP, getQ, -Infinity, maxAbsDiff);
4361 }
4362}; // in case the user accidentally doesn't use camel case
4363
4364distances['squared-euclidean'] = distances['squaredEuclidean'];
4365distances['squaredeuclidean'] = distances['squaredEuclidean'];
4366function clusteringDistance (method, length, getP, getQ, nodeP, nodeQ) {
4367 var impl;
4368
4369 if (fn$6(method)) {
4370 impl = method;
4371 } else {
4372 impl = distances[method] || distances.euclidean;
4373 }
4374
4375 if (length === 0 && fn$6(method)) {
4376 return impl(nodeP, nodeQ);
4377 } else {
4378 return impl(length, getP, getQ, nodeP, nodeQ);
4379 }
4380}
4381
4382var defaults$b = defaults$g({
4383 k: 2,
4384 m: 2,
4385 sensitivityThreshold: 0.0001,
4386 distance: 'euclidean',
4387 maxIterations: 10,
4388 attributes: [],
4389 testMode: false,
4390 testCentroids: null
4391});
4392
4393var setOptions$2 = function setOptions(options) {
4394 return defaults$b(options);
4395};
4396/* eslint-enable */
4397
4398
4399var getDist = function getDist(type, node, centroid, attributes, mode) {
4400 var noNodeP = mode !== 'kMedoids';
4401 var getP = noNodeP ? function (i) {
4402 return centroid[i];
4403 } : function (i) {
4404 return attributes[i](centroid);
4405 };
4406
4407 var getQ = function getQ(i) {
4408 return attributes[i](node);
4409 };
4410
4411 var nodeP = centroid;
4412 var nodeQ = node;
4413 return clusteringDistance(type, attributes.length, getP, getQ, nodeP, nodeQ);
4414};
4415
4416var randomCentroids = function randomCentroids(nodes, k, attributes) {
4417 var ndim = attributes.length;
4418 var min = new Array(ndim);
4419 var max = new Array(ndim);
4420 var centroids = new Array(k);
4421 var centroid = null; // Find min, max values for each attribute dimension
4422
4423 for (var i = 0; i < ndim; i++) {
4424 min[i] = nodes.min(attributes[i]).value;
4425 max[i] = nodes.max(attributes[i]).value;
4426 } // Build k centroids, each represented as an n-dim feature vector
4427
4428
4429 for (var c = 0; c < k; c++) {
4430 centroid = [];
4431
4432 for (var _i = 0; _i < ndim; _i++) {
4433 centroid[_i] = Math.random() * (max[_i] - min[_i]) + min[_i]; // random initial value
4434 }
4435
4436 centroids[c] = centroid;
4437 }
4438
4439 return centroids;
4440};
4441
4442var classify = function classify(node, centroids, distance, attributes, type) {
4443 var min = Infinity;
4444 var index = 0;
4445
4446 for (var i = 0; i < centroids.length; i++) {
4447 var dist = getDist(distance, node, centroids[i], attributes, type);
4448
4449 if (dist < min) {
4450 min = dist;
4451 index = i;
4452 }
4453 }
4454
4455 return index;
4456};
4457
4458var buildCluster = function buildCluster(centroid, nodes, assignment) {
4459 var cluster = [];
4460 var node = null;
4461
4462 for (var n = 0; n < nodes.length; n++) {
4463 node = nodes[n];
4464
4465 if (assignment[node.id()] === centroid) {
4466 //console.log("Node " + node.id() + " is associated with medoid #: " + m);
4467 cluster.push(node);
4468 }
4469 }
4470
4471 return cluster;
4472};
4473
4474var haveValuesConverged = function haveValuesConverged(v1, v2, sensitivityThreshold) {
4475 return Math.abs(v2 - v1) <= sensitivityThreshold;
4476};
4477
4478var haveMatricesConverged = function haveMatricesConverged(v1, v2, sensitivityThreshold) {
4479 for (var i = 0; i < v1.length; i++) {
4480 for (var j = 0; j < v1[i].length; j++) {
4481 var diff = Math.abs(v1[i][j] - v2[i][j]);
4482
4483 if (diff > sensitivityThreshold) {
4484 return false;
4485 }
4486 }
4487 }
4488
4489 return true;
4490};
4491
4492var seenBefore = function seenBefore(node, medoids, n) {
4493 for (var i = 0; i < n; i++) {
4494 if (node === medoids[i]) return true;
4495 }
4496
4497 return false;
4498};
4499
4500var randomMedoids = function randomMedoids(nodes, k) {
4501 var medoids = new Array(k); // For small data sets, the probability of medoid conflict is greater,
4502 // so we need to check to see if we've already seen or chose this node before.
4503
4504 if (nodes.length < 50) {
4505 // Randomly select k medoids from the n nodes
4506 for (var i = 0; i < k; i++) {
4507 var node = nodes[Math.floor(Math.random() * nodes.length)]; // If we've already chosen this node to be a medoid, don't choose it again (for small data sets).
4508 // Instead choose a different random node.
4509
4510 while (seenBefore(node, medoids, i)) {
4511 node = nodes[Math.floor(Math.random() * nodes.length)];
4512 }
4513
4514 medoids[i] = node;
4515 }
4516 } else {
4517 // Relatively large data set, so pretty safe to not check and just select random nodes
4518 for (var _i2 = 0; _i2 < k; _i2++) {
4519 medoids[_i2] = nodes[Math.floor(Math.random() * nodes.length)];
4520 }
4521 }
4522
4523 return medoids;
4524};
4525
4526var findCost = function findCost(potentialNewMedoid, cluster, attributes) {
4527 var cost = 0;
4528
4529 for (var n = 0; n < cluster.length; n++) {
4530 cost += getDist('manhattan', cluster[n], potentialNewMedoid, attributes, 'kMedoids');
4531 }
4532
4533 return cost;
4534};
4535
4536var kMeans = function kMeans(options) {
4537 var cy = this.cy();
4538 var nodes = this.nodes();
4539 var node = null; // Set parameters of algorithm: # of clusters, distance metric, etc.
4540
4541 var opts = setOptions$2(options); // Begin k-means algorithm
4542
4543 var clusters = new Array(opts.k);
4544 var assignment = {};
4545 var centroids; // Step 1: Initialize centroid positions
4546
4547 if (opts.testMode) {
4548 if (typeof opts.testCentroids === 'number') {
4549 // TODO: implement a seeded random number generator.
4550 opts.testCentroids;
4551 centroids = randomCentroids(nodes, opts.k, opts.attributes);
4552 } else if (_typeof(opts.testCentroids) === 'object') {
4553 centroids = opts.testCentroids;
4554 } else {
4555 centroids = randomCentroids(nodes, opts.k, opts.attributes);
4556 }
4557 } else {
4558 centroids = randomCentroids(nodes, opts.k, opts.attributes);
4559 }
4560
4561 var isStillMoving = true;
4562 var iterations = 0;
4563
4564 while (isStillMoving && iterations < opts.maxIterations) {
4565 // Step 2: Assign nodes to the nearest centroid
4566 for (var n = 0; n < nodes.length; n++) {
4567 node = nodes[n]; // Determine which cluster this node belongs to: node id => cluster #
4568
4569 assignment[node.id()] = classify(node, centroids, opts.distance, opts.attributes, 'kMeans');
4570 } // Step 3: For each of the k clusters, update its centroid
4571
4572
4573 isStillMoving = false;
4574
4575 for (var c = 0; c < opts.k; c++) {
4576 // Get all nodes that belong to this cluster
4577 var cluster = buildCluster(c, nodes, assignment);
4578
4579 if (cluster.length === 0) {
4580 // If cluster is empty, break out early & move to next cluster
4581 continue;
4582 } // Update centroids by calculating avg of all nodes within the cluster.
4583
4584
4585 var ndim = opts.attributes.length;
4586 var centroid = centroids[c]; // [ dim_1, dim_2, dim_3, ... , dim_n ]
4587
4588 var newCentroid = new Array(ndim);
4589 var sum = new Array(ndim);
4590
4591 for (var d = 0; d < ndim; d++) {
4592 sum[d] = 0.0;
4593
4594 for (var i = 0; i < cluster.length; i++) {
4595 node = cluster[i];
4596 sum[d] += opts.attributes[d](node);
4597 }
4598
4599 newCentroid[d] = sum[d] / cluster.length; // Check to see if algorithm has converged, i.e. when centroids no longer change
4600
4601 if (!haveValuesConverged(newCentroid[d], centroid[d], opts.sensitivityThreshold)) {
4602 isStillMoving = true;
4603 }
4604 }
4605
4606 centroids[c] = newCentroid;
4607 clusters[c] = cy.collection(cluster);
4608 }
4609
4610 iterations++;
4611 }
4612
4613 return clusters;
4614};
4615
4616var kMedoids = function kMedoids(options) {
4617 var cy = this.cy();
4618 var nodes = this.nodes();
4619 var node = null;
4620 var opts = setOptions$2(options); // Begin k-medoids algorithm
4621
4622 var clusters = new Array(opts.k);
4623 var medoids;
4624 var assignment = {};
4625 var curCost;
4626 var minCosts = new Array(opts.k); // minimum cost configuration for each cluster
4627 // Step 1: Initialize k medoids
4628
4629 if (opts.testMode) {
4630 if (typeof opts.testCentroids === 'number') ; else if (_typeof(opts.testCentroids) === 'object') {
4631 medoids = opts.testCentroids;
4632 } else {
4633 medoids = randomMedoids(nodes, opts.k);
4634 }
4635 } else {
4636 medoids = randomMedoids(nodes, opts.k);
4637 }
4638
4639 var isStillMoving = true;
4640 var iterations = 0;
4641
4642 while (isStillMoving && iterations < opts.maxIterations) {
4643 // Step 2: Assign nodes to the nearest medoid
4644 for (var n = 0; n < nodes.length; n++) {
4645 node = nodes[n]; // Determine which cluster this node belongs to: node id => cluster #
4646
4647 assignment[node.id()] = classify(node, medoids, opts.distance, opts.attributes, 'kMedoids');
4648 }
4649
4650 isStillMoving = false; // Step 3: For each medoid m, and for each node assciated with mediod m,
4651 // select the node with the lowest configuration cost as new medoid.
4652
4653 for (var m = 0; m < medoids.length; m++) {
4654 // Get all nodes that belong to this medoid
4655 var cluster = buildCluster(m, nodes, assignment);
4656
4657 if (cluster.length === 0) {
4658 // If cluster is empty, break out early & move to next cluster
4659 continue;
4660 }
4661
4662 minCosts[m] = findCost(medoids[m], cluster, opts.attributes); // original cost
4663 // Select different medoid if its configuration has the lowest cost
4664
4665 for (var _n = 0; _n < cluster.length; _n++) {
4666 curCost = findCost(cluster[_n], cluster, opts.attributes);
4667
4668 if (curCost < minCosts[m]) {
4669 minCosts[m] = curCost;
4670 medoids[m] = cluster[_n];
4671 isStillMoving = true;
4672 }
4673 }
4674
4675 clusters[m] = cy.collection(cluster);
4676 }
4677
4678 iterations++;
4679 }
4680
4681 return clusters;
4682};
4683
4684var updateCentroids = function updateCentroids(centroids, nodes, U, weight, opts) {
4685 var numerator, denominator;
4686
4687 for (var n = 0; n < nodes.length; n++) {
4688 for (var c = 0; c < centroids.length; c++) {
4689 weight[n][c] = Math.pow(U[n][c], opts.m);
4690 }
4691 }
4692
4693 for (var _c = 0; _c < centroids.length; _c++) {
4694 for (var dim = 0; dim < opts.attributes.length; dim++) {
4695 numerator = 0;
4696 denominator = 0;
4697
4698 for (var _n2 = 0; _n2 < nodes.length; _n2++) {
4699 numerator += weight[_n2][_c] * opts.attributes[dim](nodes[_n2]);
4700 denominator += weight[_n2][_c];
4701 }
4702
4703 centroids[_c][dim] = numerator / denominator;
4704 }
4705 }
4706};
4707
4708var updateMembership = function updateMembership(U, _U, centroids, nodes, opts) {
4709 // Save previous step
4710 for (var i = 0; i < U.length; i++) {
4711 _U[i] = U[i].slice();
4712 }
4713
4714 var sum, numerator, denominator;
4715 var pow = 2 / (opts.m - 1);
4716
4717 for (var c = 0; c < centroids.length; c++) {
4718 for (var n = 0; n < nodes.length; n++) {
4719 sum = 0;
4720
4721 for (var k = 0; k < centroids.length; k++) {
4722 // against all other centroids
4723 numerator = getDist(opts.distance, nodes[n], centroids[c], opts.attributes, 'cmeans');
4724 denominator = getDist(opts.distance, nodes[n], centroids[k], opts.attributes, 'cmeans');
4725 sum += Math.pow(numerator / denominator, pow);
4726 }
4727
4728 U[n][c] = 1 / sum;
4729 }
4730 }
4731};
4732
4733var assign$1 = function assign(nodes, U, opts, cy) {
4734 var clusters = new Array(opts.k);
4735
4736 for (var c = 0; c < clusters.length; c++) {
4737 clusters[c] = [];
4738 }
4739
4740 var max;
4741 var index;
4742
4743 for (var n = 0; n < U.length; n++) {
4744 // for each node (U is N x C matrix)
4745 max = -Infinity;
4746 index = -1; // Determine which cluster the node is most likely to belong in
4747
4748 for (var _c2 = 0; _c2 < U[0].length; _c2++) {
4749 if (U[n][_c2] > max) {
4750 max = U[n][_c2];
4751 index = _c2;
4752 }
4753 }
4754
4755 clusters[index].push(nodes[n]);
4756 } // Turn every array into a collection of nodes
4757
4758
4759 for (var _c3 = 0; _c3 < clusters.length; _c3++) {
4760 clusters[_c3] = cy.collection(clusters[_c3]);
4761 }
4762
4763 return clusters;
4764};
4765
4766var fuzzyCMeans = function fuzzyCMeans(options) {
4767 var cy = this.cy();
4768 var nodes = this.nodes();
4769 var opts = setOptions$2(options); // Begin fuzzy c-means algorithm
4770
4771 var clusters;
4772 var centroids;
4773 var U;
4774
4775 var _U;
4776
4777 var weight; // Step 1: Initialize letiables.
4778
4779 _U = new Array(nodes.length);
4780
4781 for (var i = 0; i < nodes.length; i++) {
4782 // N x C matrix
4783 _U[i] = new Array(opts.k);
4784 }
4785
4786 U = new Array(nodes.length);
4787
4788 for (var _i3 = 0; _i3 < nodes.length; _i3++) {
4789 // N x C matrix
4790 U[_i3] = new Array(opts.k);
4791 }
4792
4793 for (var _i4 = 0; _i4 < nodes.length; _i4++) {
4794 var total = 0;
4795
4796 for (var j = 0; j < opts.k; j++) {
4797 U[_i4][j] = Math.random();
4798 total += U[_i4][j];
4799 }
4800
4801 for (var _j = 0; _j < opts.k; _j++) {
4802 U[_i4][_j] = U[_i4][_j] / total;
4803 }
4804 }
4805
4806 centroids = new Array(opts.k);
4807
4808 for (var _i5 = 0; _i5 < opts.k; _i5++) {
4809 centroids[_i5] = new Array(opts.attributes.length);
4810 }
4811
4812 weight = new Array(nodes.length);
4813
4814 for (var _i6 = 0; _i6 < nodes.length; _i6++) {
4815 // N x C matrix
4816 weight[_i6] = new Array(opts.k);
4817 } // end init FCM
4818
4819
4820 var isStillMoving = true;
4821 var iterations = 0;
4822
4823 while (isStillMoving && iterations < opts.maxIterations) {
4824 isStillMoving = false; // Step 2: Calculate the centroids for each step.
4825
4826 updateCentroids(centroids, nodes, U, weight, opts); // Step 3: Update the partition matrix U.
4827
4828 updateMembership(U, _U, centroids, nodes, opts); // Step 4: Check for convergence.
4829
4830 if (!haveMatricesConverged(U, _U, opts.sensitivityThreshold)) {
4831 isStillMoving = true;
4832 }
4833
4834 iterations++;
4835 } // Assign nodes to clusters with highest probability.
4836
4837
4838 clusters = assign$1(nodes, U, opts, cy);
4839 return {
4840 clusters: clusters,
4841 degreeOfMembership: U
4842 };
4843};
4844
4845var kClustering = {
4846 kMeans: kMeans,
4847 kMedoids: kMedoids,
4848 fuzzyCMeans: fuzzyCMeans,
4849 fcm: fuzzyCMeans
4850};
4851
4852// Implemented by Zoe Xi @zoexi for GSOC 2016
4853var defaults$a = defaults$g({
4854 distance: 'euclidean',
4855 // distance metric to compare nodes
4856 linkage: 'min',
4857 // linkage criterion : how to determine the distance between clusters of nodes
4858 mode: 'threshold',
4859 // mode:'threshold' => clusters must be threshold distance apart
4860 threshold: Infinity,
4861 // the distance threshold
4862 // mode:'dendrogram' => the nodes are organised as leaves in a tree (siblings are close), merging makes clusters
4863 addDendrogram: false,
4864 // whether to add the dendrogram to the graph for viz
4865 dendrogramDepth: 0,
4866 // depth at which dendrogram branches are merged into the returned clusters
4867 attributes: [] // array of attr functions
4868
4869});
4870var linkageAliases = {
4871 'single': 'min',
4872 'complete': 'max'
4873};
4874
4875var setOptions$1 = function setOptions(options) {
4876 var opts = defaults$a(options);
4877 var preferredAlias = linkageAliases[opts.linkage];
4878
4879 if (preferredAlias != null) {
4880 opts.linkage = preferredAlias;
4881 }
4882
4883 return opts;
4884};
4885
4886var mergeClosest = function mergeClosest(clusters, index, dists, mins, opts) {
4887 // Find two closest clusters from cached mins
4888 var minKey = 0;
4889 var min = Infinity;
4890 var dist;
4891 var attrs = opts.attributes;
4892
4893 var getDist = function getDist(n1, n2) {
4894 return clusteringDistance(opts.distance, attrs.length, function (i) {
4895 return attrs[i](n1);
4896 }, function (i) {
4897 return attrs[i](n2);
4898 }, n1, n2);
4899 };
4900
4901 for (var i = 0; i < clusters.length; i++) {
4902 var key = clusters[i].key;
4903 var _dist = dists[key][mins[key]];
4904
4905 if (_dist < min) {
4906 minKey = key;
4907 min = _dist;
4908 }
4909 }
4910
4911 if (opts.mode === 'threshold' && min >= opts.threshold || opts.mode === 'dendrogram' && clusters.length === 1) {
4912 return false;
4913 }
4914
4915 var c1 = index[minKey];
4916 var c2 = index[mins[minKey]];
4917 var merged; // Merge two closest clusters
4918
4919 if (opts.mode === 'dendrogram') {
4920 merged = {
4921 left: c1,
4922 right: c2,
4923 key: c1.key
4924 };
4925 } else {
4926 merged = {
4927 value: c1.value.concat(c2.value),
4928 key: c1.key
4929 };
4930 }
4931
4932 clusters[c1.index] = merged;
4933 clusters.splice(c2.index, 1);
4934 index[c1.key] = merged; // Update distances with new merged cluster
4935
4936 for (var _i = 0; _i < clusters.length; _i++) {
4937 var cur = clusters[_i];
4938
4939 if (c1.key === cur.key) {
4940 dist = Infinity;
4941 } else if (opts.linkage === 'min') {
4942 dist = dists[c1.key][cur.key];
4943
4944 if (dists[c1.key][cur.key] > dists[c2.key][cur.key]) {
4945 dist = dists[c2.key][cur.key];
4946 }
4947 } else if (opts.linkage === 'max') {
4948 dist = dists[c1.key][cur.key];
4949
4950 if (dists[c1.key][cur.key] < dists[c2.key][cur.key]) {
4951 dist = dists[c2.key][cur.key];
4952 }
4953 } else if (opts.linkage === 'mean') {
4954 dist = (dists[c1.key][cur.key] * c1.size + dists[c2.key][cur.key] * c2.size) / (c1.size + c2.size);
4955 } else {
4956 if (opts.mode === 'dendrogram') dist = getDist(cur.value, c1.value);else dist = getDist(cur.value[0], c1.value[0]);
4957 }
4958
4959 dists[c1.key][cur.key] = dists[cur.key][c1.key] = dist; // distance matrix is symmetric
4960 } // Update cached mins
4961
4962
4963 for (var _i2 = 0; _i2 < clusters.length; _i2++) {
4964 var key1 = clusters[_i2].key;
4965
4966 if (mins[key1] === c1.key || mins[key1] === c2.key) {
4967 var _min = key1;
4968
4969 for (var j = 0; j < clusters.length; j++) {
4970 var key2 = clusters[j].key;
4971
4972 if (dists[key1][key2] < dists[key1][_min]) {
4973 _min = key2;
4974 }
4975 }
4976
4977 mins[key1] = _min;
4978 }
4979
4980 clusters[_i2].index = _i2;
4981 } // Clean up meta data used for clustering
4982
4983
4984 c1.key = c2.key = c1.index = c2.index = null;
4985 return true;
4986};
4987
4988var getAllChildren = function getAllChildren(root, arr, cy) {
4989 if (!root) return;
4990
4991 if (root.value) {
4992 arr.push(root.value);
4993 } else {
4994 if (root.left) getAllChildren(root.left, arr);
4995 if (root.right) getAllChildren(root.right, arr);
4996 }
4997};
4998
4999var buildDendrogram = function buildDendrogram(root, cy) {
5000 if (!root) return '';
5001
5002 if (root.left && root.right) {
5003 var leftStr = buildDendrogram(root.left, cy);
5004 var rightStr = buildDendrogram(root.right, cy);
5005 var node = cy.add({
5006 group: 'nodes',
5007 data: {
5008 id: leftStr + ',' + rightStr
5009 }
5010 });
5011 cy.add({
5012 group: 'edges',
5013 data: {
5014 source: leftStr,
5015 target: node.id()
5016 }
5017 });
5018 cy.add({
5019 group: 'edges',
5020 data: {
5021 source: rightStr,
5022 target: node.id()
5023 }
5024 });
5025 return node.id();
5026 } else if (root.value) {
5027 return root.value.id();
5028 }
5029};
5030
5031var buildClustersFromTree = function buildClustersFromTree(root, k, cy) {
5032 if (!root) return [];
5033 var left = [],
5034 right = [],
5035 leaves = [];
5036
5037 if (k === 0) {
5038 // don't cut tree, simply return all nodes as 1 single cluster
5039 if (root.left) getAllChildren(root.left, left);
5040 if (root.right) getAllChildren(root.right, right);
5041 leaves = left.concat(right);
5042 return [cy.collection(leaves)];
5043 } else if (k === 1) {
5044 // cut at root
5045 if (root.value) {
5046 // leaf node
5047 return [cy.collection(root.value)];
5048 } else {
5049 if (root.left) getAllChildren(root.left, left);
5050 if (root.right) getAllChildren(root.right, right);
5051 return [cy.collection(left), cy.collection(right)];
5052 }
5053 } else {
5054 if (root.value) {
5055 return [cy.collection(root.value)];
5056 } else {
5057 if (root.left) left = buildClustersFromTree(root.left, k - 1, cy);
5058 if (root.right) right = buildClustersFromTree(root.right, k - 1, cy);
5059 return left.concat(right);
5060 }
5061 }
5062};
5063/* eslint-enable */
5064
5065
5066var hierarchicalClustering = function hierarchicalClustering(options) {
5067 var cy = this.cy();
5068 var nodes = this.nodes(); // Set parameters of algorithm: linkage type, distance metric, etc.
5069
5070 var opts = setOptions$1(options);
5071 var attrs = opts.attributes;
5072
5073 var getDist = function getDist(n1, n2) {
5074 return clusteringDistance(opts.distance, attrs.length, function (i) {
5075 return attrs[i](n1);
5076 }, function (i) {
5077 return attrs[i](n2);
5078 }, n1, n2);
5079 }; // Begin hierarchical algorithm
5080
5081
5082 var clusters = [];
5083 var dists = []; // distances between each pair of clusters
5084
5085 var mins = []; // closest cluster for each cluster
5086
5087 var index = []; // hash of all clusters by key
5088 // In agglomerative (bottom-up) clustering, each node starts as its own cluster
5089
5090 for (var n = 0; n < nodes.length; n++) {
5091 var cluster = {
5092 value: opts.mode === 'dendrogram' ? nodes[n] : [nodes[n]],
5093 key: n,
5094 index: n
5095 };
5096 clusters[n] = cluster;
5097 index[n] = cluster;
5098 dists[n] = [];
5099 mins[n] = 0;
5100 } // Calculate the distance between each pair of clusters
5101
5102
5103 for (var i = 0; i < clusters.length; i++) {
5104 for (var j = 0; j <= i; j++) {
5105 var dist = void 0;
5106
5107 if (opts.mode === 'dendrogram') {
5108 // modes store cluster values differently
5109 dist = i === j ? Infinity : getDist(clusters[i].value, clusters[j].value);
5110 } else {
5111 dist = i === j ? Infinity : getDist(clusters[i].value[0], clusters[j].value[0]);
5112 }
5113
5114 dists[i][j] = dist;
5115 dists[j][i] = dist;
5116
5117 if (dist < dists[i][mins[i]]) {
5118 mins[i] = j; // Cache mins: closest cluster to cluster i is cluster j
5119 }
5120 }
5121 } // Find the closest pair of clusters and merge them into a single cluster.
5122 // Update distances between new cluster and each of the old clusters, and loop until threshold reached.
5123
5124
5125 var merged = mergeClosest(clusters, index, dists, mins, opts);
5126
5127 while (merged) {
5128 merged = mergeClosest(clusters, index, dists, mins, opts);
5129 }
5130
5131 var retClusters; // Dendrogram mode builds the hierarchy and adds intermediary nodes + edges
5132 // in addition to returning the clusters.
5133
5134 if (opts.mode === 'dendrogram') {
5135 retClusters = buildClustersFromTree(clusters[0], opts.dendrogramDepth, cy);
5136 if (opts.addDendrogram) buildDendrogram(clusters[0], cy);
5137 } else {
5138 // Regular mode simply returns the clusters
5139 retClusters = new Array(clusters.length);
5140 clusters.forEach(function (cluster, i) {
5141 // Clean up meta data used for clustering
5142 cluster.key = cluster.index = null;
5143 retClusters[i] = cy.collection(cluster.value);
5144 });
5145 }
5146
5147 return retClusters;
5148};
5149
5150var hierarchicalClustering$1 = {
5151 hierarchicalClustering: hierarchicalClustering,
5152 hca: hierarchicalClustering
5153};
5154
5155// Implemented by Zoe Xi @zoexi for GSOC 2016
5156var defaults$9 = defaults$g({
5157 distance: 'euclidean',
5158 // distance metric to compare attributes between two nodes
5159 preference: 'median',
5160 // suitability of a data point to serve as an exemplar
5161 damping: 0.8,
5162 // damping factor between [0.5, 1)
5163 maxIterations: 1000,
5164 // max number of iterations to run
5165 minIterations: 100,
5166 // min number of iterations to run in order for clustering to stop
5167 attributes: [// functions to quantify the similarity between any two points
5168 // e.g. node => node.data('weight')
5169 ]
5170});
5171
5172var setOptions = function setOptions(options) {
5173 var dmp = options.damping;
5174 var pref = options.preference;
5175
5176 if (!(0.5 <= dmp && dmp < 1)) {
5177 error("Damping must range on [0.5, 1). Got: ".concat(dmp));
5178 }
5179
5180 var validPrefs = ['median', 'mean', 'min', 'max'];
5181
5182 if (!(validPrefs.some(function (v) {
5183 return v === pref;
5184 }) || number$1(pref))) {
5185 error("Preference must be one of [".concat(validPrefs.map(function (p) {
5186 return "'".concat(p, "'");
5187 }).join(', '), "] or a number. Got: ").concat(pref));
5188 }
5189
5190 return defaults$9(options);
5191};
5192/* eslint-enable */
5193
5194
5195var getSimilarity = function getSimilarity(type, n1, n2, attributes) {
5196 var attr = function attr(n, i) {
5197 return attributes[i](n);
5198 }; // nb negative because similarity should have an inverse relationship to distance
5199
5200
5201 return -clusteringDistance(type, attributes.length, function (i) {
5202 return attr(n1, i);
5203 }, function (i) {
5204 return attr(n2, i);
5205 }, n1, n2);
5206};
5207
5208var getPreference = function getPreference(S, preference) {
5209 // larger preference = greater # of clusters
5210 var p = null;
5211
5212 if (preference === 'median') {
5213 p = median(S);
5214 } else if (preference === 'mean') {
5215 p = mean(S);
5216 } else if (preference === 'min') {
5217 p = min(S);
5218 } else if (preference === 'max') {
5219 p = max(S);
5220 } else {
5221 // Custom preference number, as set by user
5222 p = preference;
5223 }
5224
5225 return p;
5226};
5227
5228var findExemplars = function findExemplars(n, R, A) {
5229 var indices = [];
5230
5231 for (var i = 0; i < n; i++) {
5232 if (R[i * n + i] + A[i * n + i] > 0) {
5233 indices.push(i);
5234 }
5235 }
5236
5237 return indices;
5238};
5239
5240var assignClusters = function assignClusters(n, S, exemplars) {
5241 var clusters = [];
5242
5243 for (var i = 0; i < n; i++) {
5244 var index = -1;
5245 var max = -Infinity;
5246
5247 for (var ei = 0; ei < exemplars.length; ei++) {
5248 var e = exemplars[ei];
5249
5250 if (S[i * n + e] > max) {
5251 index = e;
5252 max = S[i * n + e];
5253 }
5254 }
5255
5256 if (index > 0) {
5257 clusters.push(index);
5258 }
5259 }
5260
5261 for (var _ei = 0; _ei < exemplars.length; _ei++) {
5262 clusters[exemplars[_ei]] = exemplars[_ei];
5263 }
5264
5265 return clusters;
5266};
5267
5268var assign = function assign(n, S, exemplars) {
5269 var clusters = assignClusters(n, S, exemplars);
5270
5271 for (var ei = 0; ei < exemplars.length; ei++) {
5272 var ii = [];
5273
5274 for (var c = 0; c < clusters.length; c++) {
5275 if (clusters[c] === exemplars[ei]) {
5276 ii.push(c);
5277 }
5278 }
5279
5280 var maxI = -1;
5281 var maxSum = -Infinity;
5282
5283 for (var i = 0; i < ii.length; i++) {
5284 var sum = 0;
5285
5286 for (var j = 0; j < ii.length; j++) {
5287 sum += S[ii[j] * n + ii[i]];
5288 }
5289
5290 if (sum > maxSum) {
5291 maxI = i;
5292 maxSum = sum;
5293 }
5294 }
5295
5296 exemplars[ei] = ii[maxI];
5297 }
5298
5299 clusters = assignClusters(n, S, exemplars);
5300 return clusters;
5301};
5302
5303var affinityPropagation = function affinityPropagation(options) {
5304 var cy = this.cy();
5305 var nodes = this.nodes();
5306 var opts = setOptions(options); // Map each node to its position in node array
5307
5308 var id2position = {};
5309
5310 for (var i = 0; i < nodes.length; i++) {
5311 id2position[nodes[i].id()] = i;
5312 } // Begin affinity propagation algorithm
5313
5314
5315 var n; // number of data points
5316
5317 var n2; // size of matrices
5318
5319 var S; // similarity matrix (1D array)
5320
5321 var p; // preference/suitability of a data point to serve as an exemplar
5322
5323 var R; // responsibility matrix (1D array)
5324
5325 var A; // availability matrix (1D array)
5326
5327 n = nodes.length;
5328 n2 = n * n; // Initialize and build S similarity matrix
5329
5330 S = new Array(n2);
5331
5332 for (var _i = 0; _i < n2; _i++) {
5333 S[_i] = -Infinity; // for cases where two data points shouldn't be linked together
5334 }
5335
5336 for (var _i2 = 0; _i2 < n; _i2++) {
5337 for (var j = 0; j < n; j++) {
5338 if (_i2 !== j) {
5339 S[_i2 * n + j] = getSimilarity(opts.distance, nodes[_i2], nodes[j], opts.attributes);
5340 }
5341 }
5342 } // Place preferences on the diagonal of S
5343
5344
5345 p = getPreference(S, opts.preference);
5346
5347 for (var _i3 = 0; _i3 < n; _i3++) {
5348 S[_i3 * n + _i3] = p;
5349 } // Initialize R responsibility matrix
5350
5351
5352 R = new Array(n2);
5353
5354 for (var _i4 = 0; _i4 < n2; _i4++) {
5355 R[_i4] = 0.0;
5356 } // Initialize A availability matrix
5357
5358
5359 A = new Array(n2);
5360
5361 for (var _i5 = 0; _i5 < n2; _i5++) {
5362 A[_i5] = 0.0;
5363 }
5364
5365 var old = new Array(n);
5366 var Rp = new Array(n);
5367 var se = new Array(n);
5368
5369 for (var _i6 = 0; _i6 < n; _i6++) {
5370 old[_i6] = 0.0;
5371 Rp[_i6] = 0.0;
5372 se[_i6] = 0;
5373 }
5374
5375 var e = new Array(n * opts.minIterations);
5376
5377 for (var _i7 = 0; _i7 < e.length; _i7++) {
5378 e[_i7] = 0;
5379 }
5380
5381 var iter;
5382
5383 for (iter = 0; iter < opts.maxIterations; iter++) {
5384 // main algorithmic loop
5385 // Update R responsibility matrix
5386 for (var _i8 = 0; _i8 < n; _i8++) {
5387 var max = -Infinity,
5388 max2 = -Infinity,
5389 maxI = -1,
5390 AS = 0.0;
5391
5392 for (var _j = 0; _j < n; _j++) {
5393 old[_j] = R[_i8 * n + _j];
5394 AS = A[_i8 * n + _j] + S[_i8 * n + _j];
5395
5396 if (AS >= max) {
5397 max2 = max;
5398 max = AS;
5399 maxI = _j;
5400 } else if (AS > max2) {
5401 max2 = AS;
5402 }
5403 }
5404
5405 for (var _j2 = 0; _j2 < n; _j2++) {
5406 R[_i8 * n + _j2] = (1 - opts.damping) * (S[_i8 * n + _j2] - max) + opts.damping * old[_j2];
5407 }
5408
5409 R[_i8 * n + maxI] = (1 - opts.damping) * (S[_i8 * n + maxI] - max2) + opts.damping * old[maxI];
5410 } // Update A availability matrix
5411
5412
5413 for (var _i9 = 0; _i9 < n; _i9++) {
5414 var sum = 0;
5415
5416 for (var _j3 = 0; _j3 < n; _j3++) {
5417 old[_j3] = A[_j3 * n + _i9];
5418 Rp[_j3] = Math.max(0, R[_j3 * n + _i9]);
5419 sum += Rp[_j3];
5420 }
5421
5422 sum -= Rp[_i9];
5423 Rp[_i9] = R[_i9 * n + _i9];
5424 sum += Rp[_i9];
5425
5426 for (var _j4 = 0; _j4 < n; _j4++) {
5427 A[_j4 * n + _i9] = (1 - opts.damping) * Math.min(0, sum - Rp[_j4]) + opts.damping * old[_j4];
5428 }
5429
5430 A[_i9 * n + _i9] = (1 - opts.damping) * (sum - Rp[_i9]) + opts.damping * old[_i9];
5431 } // Check for convergence
5432
5433
5434 var K = 0;
5435
5436 for (var _i10 = 0; _i10 < n; _i10++) {
5437 var E = A[_i10 * n + _i10] + R[_i10 * n + _i10] > 0 ? 1 : 0;
5438 e[iter % opts.minIterations * n + _i10] = E;
5439 K += E;
5440 }
5441
5442 if (K > 0 && (iter >= opts.minIterations - 1 || iter == opts.maxIterations - 1)) {
5443 var _sum = 0;
5444
5445 for (var _i11 = 0; _i11 < n; _i11++) {
5446 se[_i11] = 0;
5447
5448 for (var _j5 = 0; _j5 < opts.minIterations; _j5++) {
5449 se[_i11] += e[_j5 * n + _i11];
5450 }
5451
5452 if (se[_i11] === 0 || se[_i11] === opts.minIterations) {
5453 _sum++;
5454 }
5455 }
5456
5457 if (_sum === n) {
5458 // then we have convergence
5459 break;
5460 }
5461 }
5462 } // Identify exemplars (cluster centers)
5463
5464
5465 var exemplarsIndices = findExemplars(n, R, A); // Assign nodes to clusters
5466
5467 var clusterIndices = assign(n, S, exemplarsIndices);
5468 var clusters = {};
5469
5470 for (var c = 0; c < exemplarsIndices.length; c++) {
5471 clusters[exemplarsIndices[c]] = [];
5472 }
5473
5474 for (var _i12 = 0; _i12 < nodes.length; _i12++) {
5475 var pos = id2position[nodes[_i12].id()];
5476
5477 var clusterIndex = clusterIndices[pos];
5478
5479 if (clusterIndex != null) {
5480 // the node may have not been assigned a cluster if no valid attributes were specified
5481 clusters[clusterIndex].push(nodes[_i12]);
5482 }
5483 }
5484
5485 var retClusters = new Array(exemplarsIndices.length);
5486
5487 for (var _c = 0; _c < exemplarsIndices.length; _c++) {
5488 retClusters[_c] = cy.collection(clusters[exemplarsIndices[_c]]);
5489 }
5490
5491 return retClusters;
5492};
5493
5494var affinityPropagation$1 = {
5495 affinityPropagation: affinityPropagation,
5496 ap: affinityPropagation
5497};
5498
5499var hierholzerDefaults = defaults$g({
5500 root: undefined,
5501 directed: false
5502});
5503var elesfn$k = {
5504 hierholzer: function hierholzer(options) {
5505 if (!plainObject(options)) {
5506 var args = arguments;
5507 options = {
5508 root: args[0],
5509 directed: args[1]
5510 };
5511 }
5512
5513 var _hierholzerDefaults = hierholzerDefaults(options),
5514 root = _hierholzerDefaults.root,
5515 directed = _hierholzerDefaults.directed;
5516
5517 var eles = this;
5518 var dflag = false;
5519 var oddIn;
5520 var oddOut;
5521 var startVertex;
5522 if (root) startVertex = string(root) ? this.filter(root)[0].id() : root[0].id();
5523 var nodes = {};
5524 var edges = {};
5525
5526 if (directed) {
5527 eles.forEach(function (ele) {
5528 var id = ele.id();
5529
5530 if (ele.isNode()) {
5531 var ind = ele.indegree(true);
5532 var outd = ele.outdegree(true);
5533 var d1 = ind - outd;
5534 var d2 = outd - ind;
5535
5536 if (d1 == 1) {
5537 if (oddIn) dflag = true;else oddIn = id;
5538 } else if (d2 == 1) {
5539 if (oddOut) dflag = true;else oddOut = id;
5540 } else if (d2 > 1 || d1 > 1) {
5541 dflag = true;
5542 }
5543
5544 nodes[id] = [];
5545 ele.outgoers().forEach(function (e) {
5546 if (e.isEdge()) nodes[id].push(e.id());
5547 });
5548 } else {
5549 edges[id] = [undefined, ele.target().id()];
5550 }
5551 });
5552 } else {
5553 eles.forEach(function (ele) {
5554 var id = ele.id();
5555
5556 if (ele.isNode()) {
5557 var d = ele.degree(true);
5558
5559 if (d % 2) {
5560 if (!oddIn) oddIn = id;else if (!oddOut) oddOut = id;else dflag = true;
5561 }
5562
5563 nodes[id] = [];
5564 ele.connectedEdges().forEach(function (e) {
5565 return nodes[id].push(e.id());
5566 });
5567 } else {
5568 edges[id] = [ele.source().id(), ele.target().id()];
5569 }
5570 });
5571 }
5572
5573 var result = {
5574 found: false,
5575 trail: undefined
5576 };
5577 if (dflag) return result;else if (oddOut && oddIn) {
5578 if (directed) {
5579 if (startVertex && oddOut != startVertex) {
5580 return result;
5581 }
5582
5583 startVertex = oddOut;
5584 } else {
5585 if (startVertex && oddOut != startVertex && oddIn != startVertex) {
5586 return result;
5587 } else if (!startVertex) {
5588 startVertex = oddOut;
5589 }
5590 }
5591 } else {
5592 if (!startVertex) startVertex = eles[0].id();
5593 }
5594
5595 var walk = function walk(v) {
5596 var currentNode = v;
5597 var subtour = [v];
5598 var adj, adjTail, adjHead;
5599
5600 while (nodes[currentNode].length) {
5601 adj = nodes[currentNode].shift();
5602 adjTail = edges[adj][0];
5603 adjHead = edges[adj][1];
5604
5605 if (currentNode != adjHead) {
5606 nodes[adjHead] = nodes[adjHead].filter(function (e) {
5607 return e != adj;
5608 });
5609 currentNode = adjHead;
5610 } else if (!directed && currentNode != adjTail) {
5611 nodes[adjTail] = nodes[adjTail].filter(function (e) {
5612 return e != adj;
5613 });
5614 currentNode = adjTail;
5615 }
5616
5617 subtour.unshift(adj);
5618 subtour.unshift(currentNode);
5619 }
5620
5621 return subtour;
5622 };
5623
5624 var trail = [];
5625 var subtour = [];
5626 subtour = walk(startVertex);
5627
5628 while (subtour.length != 1) {
5629 if (nodes[subtour[0]].length == 0) {
5630 trail.unshift(eles.getElementById(subtour.shift()));
5631 trail.unshift(eles.getElementById(subtour.shift()));
5632 } else {
5633 subtour = walk(subtour.shift()).concat(subtour);
5634 }
5635 }
5636
5637 trail.unshift(eles.getElementById(subtour.shift())); // final node
5638
5639 for (var d in nodes) {
5640 if (nodes[d].length) {
5641 return result;
5642 }
5643 }
5644
5645 result.found = true;
5646 result.trail = this.spawn(trail, true);
5647 return result;
5648 }
5649};
5650
5651var hopcroftTarjanBiconnected = function hopcroftTarjanBiconnected() {
5652 var eles = this;
5653 var nodes = {};
5654 var id = 0;
5655 var edgeCount = 0;
5656 var components = [];
5657 var stack = [];
5658 var visitedEdges = {};
5659
5660 var buildComponent = function buildComponent(x, y) {
5661 var i = stack.length - 1;
5662 var cutset = [];
5663 var component = eles.spawn();
5664
5665 while (stack[i].x != x || stack[i].y != y) {
5666 cutset.push(stack.pop().edge);
5667 i--;
5668 }
5669
5670 cutset.push(stack.pop().edge);
5671 cutset.forEach(function (edge) {
5672 var connectedNodes = edge.connectedNodes().intersection(eles);
5673 component.merge(edge);
5674 connectedNodes.forEach(function (node) {
5675 var nodeId = node.id();
5676 var connectedEdges = node.connectedEdges().intersection(eles);
5677 component.merge(node);
5678
5679 if (!nodes[nodeId].cutVertex) {
5680 component.merge(connectedEdges);
5681 } else {
5682 component.merge(connectedEdges.filter(function (edge) {
5683 return edge.isLoop();
5684 }));
5685 }
5686 });
5687 });
5688 components.push(component);
5689 };
5690
5691 var biconnectedSearch = function biconnectedSearch(root, currentNode, parent) {
5692 if (root === parent) edgeCount += 1;
5693 nodes[currentNode] = {
5694 id: id,
5695 low: id++,
5696 cutVertex: false
5697 };
5698 var edges = eles.getElementById(currentNode).connectedEdges().intersection(eles);
5699
5700 if (edges.size() === 0) {
5701 components.push(eles.spawn(eles.getElementById(currentNode)));
5702 } else {
5703 var sourceId, targetId, otherNodeId, edgeId;
5704 edges.forEach(function (edge) {
5705 sourceId = edge.source().id();
5706 targetId = edge.target().id();
5707 otherNodeId = sourceId === currentNode ? targetId : sourceId;
5708
5709 if (otherNodeId !== parent) {
5710 edgeId = edge.id();
5711
5712 if (!visitedEdges[edgeId]) {
5713 visitedEdges[edgeId] = true;
5714 stack.push({
5715 x: currentNode,
5716 y: otherNodeId,
5717 edge: edge
5718 });
5719 }
5720
5721 if (!(otherNodeId in nodes)) {
5722 biconnectedSearch(root, otherNodeId, currentNode);
5723 nodes[currentNode].low = Math.min(nodes[currentNode].low, nodes[otherNodeId].low);
5724
5725 if (nodes[currentNode].id <= nodes[otherNodeId].low) {
5726 nodes[currentNode].cutVertex = true;
5727 buildComponent(currentNode, otherNodeId);
5728 }
5729 } else {
5730 nodes[currentNode].low = Math.min(nodes[currentNode].low, nodes[otherNodeId].id);
5731 }
5732 }
5733 });
5734 }
5735 };
5736
5737 eles.forEach(function (ele) {
5738 if (ele.isNode()) {
5739 var nodeId = ele.id();
5740
5741 if (!(nodeId in nodes)) {
5742 edgeCount = 0;
5743 biconnectedSearch(nodeId, nodeId);
5744 nodes[nodeId].cutVertex = edgeCount > 1;
5745 }
5746 }
5747 });
5748 var cutVertices = Object.keys(nodes).filter(function (id) {
5749 return nodes[id].cutVertex;
5750 }).map(function (id) {
5751 return eles.getElementById(id);
5752 });
5753 return {
5754 cut: eles.spawn(cutVertices),
5755 components: components
5756 };
5757};
5758
5759var hopcroftTarjanBiconnected$1 = {
5760 hopcroftTarjanBiconnected: hopcroftTarjanBiconnected,
5761 htbc: hopcroftTarjanBiconnected,
5762 htb: hopcroftTarjanBiconnected,
5763 hopcroftTarjanBiconnectedComponents: hopcroftTarjanBiconnected
5764};
5765
5766var tarjanStronglyConnected = function tarjanStronglyConnected() {
5767 var eles = this;
5768 var nodes = {};
5769 var index = 0;
5770 var components = [];
5771 var stack = [];
5772 var cut = eles.spawn(eles);
5773
5774 var stronglyConnectedSearch = function stronglyConnectedSearch(sourceNodeId) {
5775 stack.push(sourceNodeId);
5776 nodes[sourceNodeId] = {
5777 index: index,
5778 low: index++,
5779 explored: false
5780 };
5781 var connectedEdges = eles.getElementById(sourceNodeId).connectedEdges().intersection(eles);
5782 connectedEdges.forEach(function (edge) {
5783 var targetNodeId = edge.target().id();
5784
5785 if (targetNodeId !== sourceNodeId) {
5786 if (!(targetNodeId in nodes)) {
5787 stronglyConnectedSearch(targetNodeId);
5788 }
5789
5790 if (!nodes[targetNodeId].explored) {
5791 nodes[sourceNodeId].low = Math.min(nodes[sourceNodeId].low, nodes[targetNodeId].low);
5792 }
5793 }
5794 });
5795
5796 if (nodes[sourceNodeId].index === nodes[sourceNodeId].low) {
5797 var componentNodes = eles.spawn();
5798
5799 for (;;) {
5800 var nodeId = stack.pop();
5801 componentNodes.merge(eles.getElementById(nodeId));
5802 nodes[nodeId].low = nodes[sourceNodeId].index;
5803 nodes[nodeId].explored = true;
5804
5805 if (nodeId === sourceNodeId) {
5806 break;
5807 }
5808 }
5809
5810 var componentEdges = componentNodes.edgesWith(componentNodes);
5811 var component = componentNodes.merge(componentEdges);
5812 components.push(component);
5813 cut = cut.difference(component);
5814 }
5815 };
5816
5817 eles.forEach(function (ele) {
5818 if (ele.isNode()) {
5819 var nodeId = ele.id();
5820
5821 if (!(nodeId in nodes)) {
5822 stronglyConnectedSearch(nodeId);
5823 }
5824 }
5825 });
5826 return {
5827 cut: cut,
5828 components: components
5829 };
5830};
5831
5832var tarjanStronglyConnected$1 = {
5833 tarjanStronglyConnected: tarjanStronglyConnected,
5834 tsc: tarjanStronglyConnected,
5835 tscc: tarjanStronglyConnected,
5836 tarjanStronglyConnectedComponents: tarjanStronglyConnected
5837};
5838
5839var elesfn$j = {};
5840[elesfn$v, elesfn$u, elesfn$t, elesfn$s, elesfn$r, elesfn$q, elesfn$p, elesfn$o, elesfn$n, elesfn$m, elesfn$l, markovClustering$1, kClustering, hierarchicalClustering$1, affinityPropagation$1, elesfn$k, hopcroftTarjanBiconnected$1, tarjanStronglyConnected$1].forEach(function (props) {
5841 extend(elesfn$j, props);
5842});
5843
5844/*!
5845Embeddable Minimum Strictly-Compliant Promises/A+ 1.1.1 Thenable
5846Copyright (c) 2013-2014 Ralf S. Engelschall (http://engelschall.com)
5847Licensed under The MIT License (http://opensource.org/licenses/MIT)
5848*/
5849
5850/* promise states [Promises/A+ 2.1] */
5851var STATE_PENDING = 0;
5852/* [Promises/A+ 2.1.1] */
5853
5854var STATE_FULFILLED = 1;
5855/* [Promises/A+ 2.1.2] */
5856
5857var STATE_REJECTED = 2;
5858/* [Promises/A+ 2.1.3] */
5859
5860/* promise object constructor */
5861
5862var api = function api(executor) {
5863 /* optionally support non-constructor/plain-function call */
5864 if (!(this instanceof api)) return new api(executor);
5865 /* initialize object */
5866
5867 this.id = 'Thenable/1.0.7';
5868 this.state = STATE_PENDING;
5869 /* initial state */
5870
5871 this.fulfillValue = undefined;
5872 /* initial value */
5873
5874 /* [Promises/A+ 1.3, 2.1.2.2] */
5875
5876 this.rejectReason = undefined;
5877 /* initial reason */
5878
5879 /* [Promises/A+ 1.5, 2.1.3.2] */
5880
5881 this.onFulfilled = [];
5882 /* initial handlers */
5883
5884 this.onRejected = [];
5885 /* initial handlers */
5886
5887 /* provide optional information-hiding proxy */
5888
5889 this.proxy = {
5890 then: this.then.bind(this)
5891 };
5892 /* support optional executor function */
5893
5894 if (typeof executor === 'function') executor.call(this, this.fulfill.bind(this), this.reject.bind(this));
5895};
5896/* promise API methods */
5897
5898
5899api.prototype = {
5900 /* promise resolving methods */
5901 fulfill: function fulfill(value) {
5902 return deliver(this, STATE_FULFILLED, 'fulfillValue', value);
5903 },
5904 reject: function reject(value) {
5905 return deliver(this, STATE_REJECTED, 'rejectReason', value);
5906 },
5907
5908 /* "The then Method" [Promises/A+ 1.1, 1.2, 2.2] */
5909 then: function then(onFulfilled, onRejected) {
5910 var curr = this;
5911 var next = new api();
5912 /* [Promises/A+ 2.2.7] */
5913
5914 curr.onFulfilled.push(resolver(onFulfilled, next, 'fulfill'));
5915 /* [Promises/A+ 2.2.2/2.2.6] */
5916
5917 curr.onRejected.push(resolver(onRejected, next, 'reject'));
5918 /* [Promises/A+ 2.2.3/2.2.6] */
5919
5920 execute(curr);
5921 return next.proxy;
5922 /* [Promises/A+ 2.2.7, 3.3] */
5923 }
5924};
5925/* deliver an action */
5926
5927var deliver = function deliver(curr, state, name, value) {
5928 if (curr.state === STATE_PENDING) {
5929 curr.state = state;
5930 /* [Promises/A+ 2.1.2.1, 2.1.3.1] */
5931
5932 curr[name] = value;
5933 /* [Promises/A+ 2.1.2.2, 2.1.3.2] */
5934
5935 execute(curr);
5936 }
5937
5938 return curr;
5939};
5940/* execute all handlers */
5941
5942
5943var execute = function execute(curr) {
5944 if (curr.state === STATE_FULFILLED) execute_handlers(curr, 'onFulfilled', curr.fulfillValue);else if (curr.state === STATE_REJECTED) execute_handlers(curr, 'onRejected', curr.rejectReason);
5945};
5946/* execute particular set of handlers */
5947
5948
5949var execute_handlers = function execute_handlers(curr, name, value) {
5950 /* global setImmediate: true */
5951
5952 /* global setTimeout: true */
5953
5954 /* short-circuit processing */
5955 if (curr[name].length === 0) return;
5956 /* iterate over all handlers, exactly once */
5957
5958 var handlers = curr[name];
5959 curr[name] = [];
5960 /* [Promises/A+ 2.2.2.3, 2.2.3.3] */
5961
5962 var func = function func() {
5963 for (var i = 0; i < handlers.length; i++) {
5964 handlers[i](value);
5965 }
5966 /* [Promises/A+ 2.2.5] */
5967
5968 };
5969 /* execute procedure asynchronously */
5970
5971 /* [Promises/A+ 2.2.4, 3.1] */
5972
5973
5974 if (typeof setImmediate === 'function') setImmediate(func);else setTimeout(func, 0);
5975};
5976/* generate a resolver function */
5977
5978
5979var resolver = function resolver(cb, next, method) {
5980 return function (value) {
5981 if (typeof cb !== 'function')
5982 /* [Promises/A+ 2.2.1, 2.2.7.3, 2.2.7.4] */
5983 next[method].call(next, value);
5984 /* [Promises/A+ 2.2.7.3, 2.2.7.4] */
5985 else {
5986 var result;
5987
5988 try {
5989 result = cb(value);
5990 }
5991 /* [Promises/A+ 2.2.2.1, 2.2.3.1, 2.2.5, 3.2] */
5992 catch (e) {
5993 next.reject(e);
5994 /* [Promises/A+ 2.2.7.2] */
5995
5996 return;
5997 }
5998
5999 resolve(next, result);
6000 /* [Promises/A+ 2.2.7.1] */
6001 }
6002 };
6003};
6004/* "Promise Resolution Procedure" */
6005
6006/* [Promises/A+ 2.3] */
6007
6008
6009var resolve = function resolve(promise, x) {
6010 /* sanity check arguments */
6011
6012 /* [Promises/A+ 2.3.1] */
6013 if (promise === x || promise.proxy === x) {
6014 promise.reject(new TypeError('cannot resolve promise with itself'));
6015 return;
6016 }
6017 /* surgically check for a "then" method
6018 (mainly to just call the "getter" of "then" only once) */
6019
6020
6021 var then;
6022
6023 if (_typeof(x) === 'object' && x !== null || typeof x === 'function') {
6024 try {
6025 then = x.then;
6026 }
6027 /* [Promises/A+ 2.3.3.1, 3.5] */
6028 catch (e) {
6029 promise.reject(e);
6030 /* [Promises/A+ 2.3.3.2] */
6031
6032 return;
6033 }
6034 }
6035 /* handle own Thenables [Promises/A+ 2.3.2]
6036 and similar "thenables" [Promises/A+ 2.3.3] */
6037
6038
6039 if (typeof then === 'function') {
6040 var resolved = false;
6041
6042 try {
6043 /* call retrieved "then" method */
6044
6045 /* [Promises/A+ 2.3.3.3] */
6046 then.call(x,
6047 /* resolvePromise */
6048
6049 /* [Promises/A+ 2.3.3.3.1] */
6050 function (y) {
6051 if (resolved) return;
6052 resolved = true;
6053 /* [Promises/A+ 2.3.3.3.3] */
6054
6055 if (y === x)
6056 /* [Promises/A+ 3.6] */
6057 promise.reject(new TypeError('circular thenable chain'));else resolve(promise, y);
6058 },
6059 /* rejectPromise */
6060
6061 /* [Promises/A+ 2.3.3.3.2] */
6062 function (r) {
6063 if (resolved) return;
6064 resolved = true;
6065 /* [Promises/A+ 2.3.3.3.3] */
6066
6067 promise.reject(r);
6068 });
6069 } catch (e) {
6070 if (!resolved)
6071 /* [Promises/A+ 2.3.3.3.3] */
6072 promise.reject(e);
6073 /* [Promises/A+ 2.3.3.3.4] */
6074 }
6075
6076 return;
6077 }
6078 /* handle other values */
6079
6080
6081 promise.fulfill(x);
6082 /* [Promises/A+ 2.3.4, 2.3.3.4] */
6083}; // so we always have Promise.all()
6084
6085
6086api.all = function (ps) {
6087 return new api(function (resolveAll, rejectAll) {
6088 var vals = new Array(ps.length);
6089 var doneCount = 0;
6090
6091 var fulfill = function fulfill(i, val) {
6092 vals[i] = val;
6093 doneCount++;
6094
6095 if (doneCount === ps.length) {
6096 resolveAll(vals);
6097 }
6098 };
6099
6100 for (var i = 0; i < ps.length; i++) {
6101 (function (i) {
6102 var p = ps[i];
6103 var isPromise = p != null && p.then != null;
6104
6105 if (isPromise) {
6106 p.then(function (val) {
6107 fulfill(i, val);
6108 }, function (err) {
6109 rejectAll(err);
6110 });
6111 } else {
6112 var val = p;
6113 fulfill(i, val);
6114 }
6115 })(i);
6116 }
6117 });
6118};
6119
6120api.resolve = function (val) {
6121 return new api(function (resolve, reject) {
6122 resolve(val);
6123 });
6124};
6125
6126api.reject = function (val) {
6127 return new api(function (resolve, reject) {
6128 reject(val);
6129 });
6130};
6131
6132var Promise$1 = typeof Promise !== 'undefined' ? Promise : api; // eslint-disable-line no-undef
6133
6134var Animation = function Animation(target, opts, opts2) {
6135 var isCore = core(target);
6136 var isEle = !isCore;
6137
6138 var _p = this._private = extend({
6139 duration: 1000
6140 }, opts, opts2);
6141
6142 _p.target = target;
6143 _p.style = _p.style || _p.css;
6144 _p.started = false;
6145 _p.playing = false;
6146 _p.hooked = false;
6147 _p.applying = false;
6148 _p.progress = 0;
6149 _p.completes = [];
6150 _p.frames = [];
6151
6152 if (_p.complete && fn$6(_p.complete)) {
6153 _p.completes.push(_p.complete);
6154 }
6155
6156 if (isEle) {
6157 var pos = target.position();
6158 _p.startPosition = _p.startPosition || {
6159 x: pos.x,
6160 y: pos.y
6161 };
6162 _p.startStyle = _p.startStyle || target.cy().style().getAnimationStartStyle(target, _p.style);
6163 }
6164
6165 if (isCore) {
6166 var pan = target.pan();
6167 _p.startPan = {
6168 x: pan.x,
6169 y: pan.y
6170 };
6171 _p.startZoom = target.zoom();
6172 } // for future timeline/animations impl
6173
6174
6175 this.length = 1;
6176 this[0] = this;
6177};
6178
6179var anifn = Animation.prototype;
6180extend(anifn, {
6181 instanceString: function instanceString() {
6182 return 'animation';
6183 },
6184 hook: function hook() {
6185 var _p = this._private;
6186
6187 if (!_p.hooked) {
6188 // add to target's animation queue
6189 var q;
6190 var tAni = _p.target._private.animation;
6191
6192 if (_p.queue) {
6193 q = tAni.queue;
6194 } else {
6195 q = tAni.current;
6196 }
6197
6198 q.push(this); // add to the animation loop pool
6199
6200 if (elementOrCollection(_p.target)) {
6201 _p.target.cy().addToAnimationPool(_p.target);
6202 }
6203
6204 _p.hooked = true;
6205 }
6206
6207 return this;
6208 },
6209 play: function play() {
6210 var _p = this._private; // autorewind
6211
6212 if (_p.progress === 1) {
6213 _p.progress = 0;
6214 }
6215
6216 _p.playing = true;
6217 _p.started = false; // needs to be started by animation loop
6218
6219 _p.stopped = false;
6220 this.hook(); // the animation loop will start the animation...
6221
6222 return this;
6223 },
6224 playing: function playing() {
6225 return this._private.playing;
6226 },
6227 apply: function apply() {
6228 var _p = this._private;
6229 _p.applying = true;
6230 _p.started = false; // needs to be started by animation loop
6231
6232 _p.stopped = false;
6233 this.hook(); // the animation loop will apply the animation at this progress
6234
6235 return this;
6236 },
6237 applying: function applying() {
6238 return this._private.applying;
6239 },
6240 pause: function pause() {
6241 var _p = this._private;
6242 _p.playing = false;
6243 _p.started = false;
6244 return this;
6245 },
6246 stop: function stop() {
6247 var _p = this._private;
6248 _p.playing = false;
6249 _p.started = false;
6250 _p.stopped = true; // to be removed from animation queues
6251
6252 return this;
6253 },
6254 rewind: function rewind() {
6255 return this.progress(0);
6256 },
6257 fastforward: function fastforward() {
6258 return this.progress(1);
6259 },
6260 time: function time(t) {
6261 var _p = this._private;
6262
6263 if (t === undefined) {
6264 return _p.progress * _p.duration;
6265 } else {
6266 return this.progress(t / _p.duration);
6267 }
6268 },
6269 progress: function progress(p) {
6270 var _p = this._private;
6271 var wasPlaying = _p.playing;
6272
6273 if (p === undefined) {
6274 return _p.progress;
6275 } else {
6276 if (wasPlaying) {
6277 this.pause();
6278 }
6279
6280 _p.progress = p;
6281 _p.started = false;
6282
6283 if (wasPlaying) {
6284 this.play();
6285 }
6286 }
6287
6288 return this;
6289 },
6290 completed: function completed() {
6291 return this._private.progress === 1;
6292 },
6293 reverse: function reverse() {
6294 var _p = this._private;
6295 var wasPlaying = _p.playing;
6296
6297 if (wasPlaying) {
6298 this.pause();
6299 }
6300
6301 _p.progress = 1 - _p.progress;
6302 _p.started = false;
6303
6304 var swap = function swap(a, b) {
6305 var _pa = _p[a];
6306
6307 if (_pa == null) {
6308 return;
6309 }
6310
6311 _p[a] = _p[b];
6312 _p[b] = _pa;
6313 };
6314
6315 swap('zoom', 'startZoom');
6316 swap('pan', 'startPan');
6317 swap('position', 'startPosition'); // swap styles
6318
6319 if (_p.style) {
6320 for (var i = 0; i < _p.style.length; i++) {
6321 var prop = _p.style[i];
6322 var name = prop.name;
6323 var startStyleProp = _p.startStyle[name];
6324 _p.startStyle[name] = prop;
6325 _p.style[i] = startStyleProp;
6326 }
6327 }
6328
6329 if (wasPlaying) {
6330 this.play();
6331 }
6332
6333 return this;
6334 },
6335 promise: function promise(type) {
6336 var _p = this._private;
6337 var arr;
6338
6339 switch (type) {
6340 case 'frame':
6341 arr = _p.frames;
6342 break;
6343
6344 default:
6345 case 'complete':
6346 case 'completed':
6347 arr = _p.completes;
6348 }
6349
6350 return new Promise$1(function (resolve, reject) {
6351 arr.push(function () {
6352 resolve();
6353 });
6354 });
6355 }
6356});
6357anifn.complete = anifn.completed;
6358anifn.run = anifn.play;
6359anifn.running = anifn.playing;
6360
6361var define$3 = {
6362 animated: function animated() {
6363 return function animatedImpl() {
6364 var self = this;
6365 var selfIsArrayLike = self.length !== undefined;
6366 var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
6367
6368 var cy = this._private.cy || this;
6369
6370 if (!cy.styleEnabled()) {
6371 return false;
6372 }
6373
6374 var ele = all[0];
6375
6376 if (ele) {
6377 return ele._private.animation.current.length > 0;
6378 }
6379 };
6380 },
6381 // animated
6382 clearQueue: function clearQueue() {
6383 return function clearQueueImpl() {
6384 var self = this;
6385 var selfIsArrayLike = self.length !== undefined;
6386 var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
6387
6388 var cy = this._private.cy || this;
6389
6390 if (!cy.styleEnabled()) {
6391 return this;
6392 }
6393
6394 for (var i = 0; i < all.length; i++) {
6395 var ele = all[i];
6396 ele._private.animation.queue = [];
6397 }
6398
6399 return this;
6400 };
6401 },
6402 // clearQueue
6403 delay: function delay() {
6404 return function delayImpl(time, complete) {
6405 var cy = this._private.cy || this;
6406
6407 if (!cy.styleEnabled()) {
6408 return this;
6409 }
6410
6411 return this.animate({
6412 delay: time,
6413 duration: time,
6414 complete: complete
6415 });
6416 };
6417 },
6418 // delay
6419 delayAnimation: function delayAnimation() {
6420 return function delayAnimationImpl(time, complete) {
6421 var cy = this._private.cy || this;
6422
6423 if (!cy.styleEnabled()) {
6424 return this;
6425 }
6426
6427 return this.animation({
6428 delay: time,
6429 duration: time,
6430 complete: complete
6431 });
6432 };
6433 },
6434 // delay
6435 animation: function animation() {
6436 return function animationImpl(properties, params) {
6437 var self = this;
6438 var selfIsArrayLike = self.length !== undefined;
6439 var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
6440
6441 var cy = this._private.cy || this;
6442 var isCore = !selfIsArrayLike;
6443 var isEles = !isCore;
6444
6445 if (!cy.styleEnabled()) {
6446 return this;
6447 }
6448
6449 var style = cy.style();
6450 properties = extend({}, properties, params);
6451 var propertiesEmpty = Object.keys(properties).length === 0;
6452
6453 if (propertiesEmpty) {
6454 return new Animation(all[0], properties); // nothing to animate
6455 }
6456
6457 if (properties.duration === undefined) {
6458 properties.duration = 400;
6459 }
6460
6461 switch (properties.duration) {
6462 case 'slow':
6463 properties.duration = 600;
6464 break;
6465
6466 case 'fast':
6467 properties.duration = 200;
6468 break;
6469 }
6470
6471 if (isEles) {
6472 properties.style = style.getPropsList(properties.style || properties.css);
6473 properties.css = undefined;
6474 }
6475
6476 if (isEles && properties.renderedPosition != null) {
6477 var rpos = properties.renderedPosition;
6478 var pan = cy.pan();
6479 var zoom = cy.zoom();
6480 properties.position = renderedToModelPosition(rpos, zoom, pan);
6481 } // override pan w/ panBy if set
6482
6483
6484 if (isCore && properties.panBy != null) {
6485 var panBy = properties.panBy;
6486 var cyPan = cy.pan();
6487 properties.pan = {
6488 x: cyPan.x + panBy.x,
6489 y: cyPan.y + panBy.y
6490 };
6491 } // override pan w/ center if set
6492
6493
6494 var center = properties.center || properties.centre;
6495
6496 if (isCore && center != null) {
6497 var centerPan = cy.getCenterPan(center.eles, properties.zoom);
6498
6499 if (centerPan != null) {
6500 properties.pan = centerPan;
6501 }
6502 } // override pan & zoom w/ fit if set
6503
6504
6505 if (isCore && properties.fit != null) {
6506 var fit = properties.fit;
6507 var fitVp = cy.getFitViewport(fit.eles || fit.boundingBox, fit.padding);
6508
6509 if (fitVp != null) {
6510 properties.pan = fitVp.pan;
6511 properties.zoom = fitVp.zoom;
6512 }
6513 } // override zoom (& potentially pan) w/ zoom obj if set
6514
6515
6516 if (isCore && plainObject(properties.zoom)) {
6517 var vp = cy.getZoomedViewport(properties.zoom);
6518
6519 if (vp != null) {
6520 if (vp.zoomed) {
6521 properties.zoom = vp.zoom;
6522 }
6523
6524 if (vp.panned) {
6525 properties.pan = vp.pan;
6526 }
6527 } else {
6528 properties.zoom = null; // an inavalid zoom (e.g. no delta) gets automatically destroyed
6529 }
6530 }
6531
6532 return new Animation(all[0], properties);
6533 };
6534 },
6535 // animate
6536 animate: function animate() {
6537 return function animateImpl(properties, params) {
6538 var self = this;
6539 var selfIsArrayLike = self.length !== undefined;
6540 var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
6541
6542 var cy = this._private.cy || this;
6543
6544 if (!cy.styleEnabled()) {
6545 return this;
6546 }
6547
6548 if (params) {
6549 properties = extend({}, properties, params);
6550 } // manually hook and run the animation
6551
6552
6553 for (var i = 0; i < all.length; i++) {
6554 var ele = all[i];
6555 var queue = ele.animated() && (properties.queue === undefined || properties.queue);
6556 var ani = ele.animation(properties, queue ? {
6557 queue: true
6558 } : undefined);
6559 ani.play();
6560 }
6561
6562 return this; // chaining
6563 };
6564 },
6565 // animate
6566 stop: function stop() {
6567 return function stopImpl(clearQueue, jumpToEnd) {
6568 var self = this;
6569 var selfIsArrayLike = self.length !== undefined;
6570 var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
6571
6572 var cy = this._private.cy || this;
6573
6574 if (!cy.styleEnabled()) {
6575 return this;
6576 }
6577
6578 for (var i = 0; i < all.length; i++) {
6579 var ele = all[i];
6580 var _p = ele._private;
6581 var anis = _p.animation.current;
6582
6583 for (var j = 0; j < anis.length; j++) {
6584 var ani = anis[j];
6585 var ani_p = ani._private;
6586
6587 if (jumpToEnd) {
6588 // next iteration of the animation loop, the animation
6589 // will go straight to the end and be removed
6590 ani_p.duration = 0;
6591 }
6592 } // clear the queue of future animations
6593
6594
6595 if (clearQueue) {
6596 _p.animation.queue = [];
6597 }
6598
6599 if (!jumpToEnd) {
6600 _p.animation.current = [];
6601 }
6602 } // we have to notify (the animation loop doesn't do it for us on `stop`)
6603
6604
6605 cy.notify('draw');
6606 return this;
6607 };
6608 } // stop
6609
6610}; // define
6611
6612var define$2 = {
6613 // access data field
6614 data: function data(params) {
6615 var defaults = {
6616 field: 'data',
6617 bindingEvent: 'data',
6618 allowBinding: false,
6619 allowSetting: false,
6620 allowGetting: false,
6621 settingEvent: 'data',
6622 settingTriggersEvent: false,
6623 triggerFnName: 'trigger',
6624 immutableKeys: {},
6625 // key => true if immutable
6626 updateStyle: false,
6627 beforeGet: function beforeGet(self) {},
6628 beforeSet: function beforeSet(self, obj) {},
6629 onSet: function onSet(self) {},
6630 canSet: function canSet(self) {
6631 return true;
6632 }
6633 };
6634 params = extend({}, defaults, params);
6635 return function dataImpl(name, value) {
6636 var p = params;
6637 var self = this;
6638 var selfIsArrayLike = self.length !== undefined;
6639 var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
6640
6641 var single = selfIsArrayLike ? self[0] : self; // .data('foo', ...)
6642
6643 if (string(name)) {
6644 // set or get property
6645 var isPathLike = name.indexOf('.') !== -1; // there might be a normal field with a dot
6646
6647 var path = isPathLike && toPath(name); // .data('foo')
6648
6649 if (p.allowGetting && value === undefined) {
6650 // get
6651 var ret;
6652
6653 if (single) {
6654 p.beforeGet(single); // check if it's path and a field with the same name doesn't exist
6655
6656 if (path && single._private[p.field][name] === undefined) {
6657 ret = get(single._private[p.field], path);
6658 } else {
6659 ret = single._private[p.field][name];
6660 }
6661 }
6662
6663 return ret; // .data('foo', 'bar')
6664 } else if (p.allowSetting && value !== undefined) {
6665 // set
6666 var valid = !p.immutableKeys[name];
6667
6668 if (valid) {
6669 var change = _defineProperty({}, name, value);
6670
6671 p.beforeSet(self, change);
6672
6673 for (var i = 0, l = all.length; i < l; i++) {
6674 var ele = all[i];
6675
6676 if (p.canSet(ele)) {
6677 if (path && single._private[p.field][name] === undefined) {
6678 set(ele._private[p.field], path, value);
6679 } else {
6680 ele._private[p.field][name] = value;
6681 }
6682 }
6683 } // update mappers if asked
6684
6685
6686 if (p.updateStyle) {
6687 self.updateStyle();
6688 } // call onSet callback
6689
6690
6691 p.onSet(self);
6692
6693 if (p.settingTriggersEvent) {
6694 self[p.triggerFnName](p.settingEvent);
6695 }
6696 }
6697 } // .data({ 'foo': 'bar' })
6698
6699 } else if (p.allowSetting && plainObject(name)) {
6700 // extend
6701 var obj = name;
6702 var k, v;
6703 var keys = Object.keys(obj);
6704 p.beforeSet(self, obj);
6705
6706 for (var _i = 0; _i < keys.length; _i++) {
6707 k = keys[_i];
6708 v = obj[k];
6709
6710 var _valid = !p.immutableKeys[k];
6711
6712 if (_valid) {
6713 for (var j = 0; j < all.length; j++) {
6714 var _ele = all[j];
6715
6716 if (p.canSet(_ele)) {
6717 _ele._private[p.field][k] = v;
6718 }
6719 }
6720 }
6721 } // update mappers if asked
6722
6723
6724 if (p.updateStyle) {
6725 self.updateStyle();
6726 } // call onSet callback
6727
6728
6729 p.onSet(self);
6730
6731 if (p.settingTriggersEvent) {
6732 self[p.triggerFnName](p.settingEvent);
6733 } // .data(function(){ ... })
6734
6735 } else if (p.allowBinding && fn$6(name)) {
6736 // bind to event
6737 var fn = name;
6738 self.on(p.bindingEvent, fn); // .data()
6739 } else if (p.allowGetting && name === undefined) {
6740 // get whole object
6741 var _ret;
6742
6743 if (single) {
6744 p.beforeGet(single);
6745 _ret = single._private[p.field];
6746 }
6747
6748 return _ret;
6749 }
6750
6751 return self; // maintain chainability
6752 }; // function
6753 },
6754 // data
6755 // remove data field
6756 removeData: function removeData(params) {
6757 var defaults = {
6758 field: 'data',
6759 event: 'data',
6760 triggerFnName: 'trigger',
6761 triggerEvent: false,
6762 immutableKeys: {} // key => true if immutable
6763
6764 };
6765 params = extend({}, defaults, params);
6766 return function removeDataImpl(names) {
6767 var p = params;
6768 var self = this;
6769 var selfIsArrayLike = self.length !== undefined;
6770 var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
6771 // .removeData('foo bar')
6772
6773 if (string(names)) {
6774 // then get the list of keys, and delete them
6775 var keys = names.split(/\s+/);
6776 var l = keys.length;
6777
6778 for (var i = 0; i < l; i++) {
6779 // delete each non-empty key
6780 var key = keys[i];
6781
6782 if (emptyString(key)) {
6783 continue;
6784 }
6785
6786 var valid = !p.immutableKeys[key]; // not valid if immutable
6787
6788 if (valid) {
6789 for (var i_a = 0, l_a = all.length; i_a < l_a; i_a++) {
6790 all[i_a]._private[p.field][key] = undefined;
6791 }
6792 }
6793 }
6794
6795 if (p.triggerEvent) {
6796 self[p.triggerFnName](p.event);
6797 } // .removeData()
6798
6799 } else if (names === undefined) {
6800 // then delete all keys
6801 for (var _i_a = 0, _l_a = all.length; _i_a < _l_a; _i_a++) {
6802 var _privateFields = all[_i_a]._private[p.field];
6803
6804 var _keys = Object.keys(_privateFields);
6805
6806 for (var _i2 = 0; _i2 < _keys.length; _i2++) {
6807 var _key = _keys[_i2];
6808 var validKeyToDelete = !p.immutableKeys[_key];
6809
6810 if (validKeyToDelete) {
6811 _privateFields[_key] = undefined;
6812 }
6813 }
6814 }
6815
6816 if (p.triggerEvent) {
6817 self[p.triggerFnName](p.event);
6818 }
6819 }
6820
6821 return self; // maintain chaining
6822 }; // function
6823 } // removeData
6824
6825}; // define
6826
6827var define$1 = {
6828 eventAliasesOn: function eventAliasesOn(proto) {
6829 var p = proto;
6830 p.addListener = p.listen = p.bind = p.on;
6831 p.unlisten = p.unbind = p.off = p.removeListener;
6832 p.trigger = p.emit; // this is just a wrapper alias of .on()
6833
6834 p.pon = p.promiseOn = function (events, selector) {
6835 var self = this;
6836 var args = Array.prototype.slice.call(arguments, 0);
6837 return new Promise$1(function (resolve, reject) {
6838 var callback = function callback(e) {
6839 self.off.apply(self, offArgs);
6840 resolve(e);
6841 };
6842
6843 var onArgs = args.concat([callback]);
6844 var offArgs = onArgs.concat([]);
6845 self.on.apply(self, onArgs);
6846 });
6847 };
6848 }
6849}; // define
6850
6851// use this module to cherry pick functions into your prototype
6852var define = {};
6853[define$3, define$2, define$1].forEach(function (m) {
6854 extend(define, m);
6855});
6856
6857var elesfn$i = {
6858 animate: define.animate(),
6859 animation: define.animation(),
6860 animated: define.animated(),
6861 clearQueue: define.clearQueue(),
6862 delay: define.delay(),
6863 delayAnimation: define.delayAnimation(),
6864 stop: define.stop()
6865};
6866
6867var elesfn$h = {
6868 classes: function classes(_classes) {
6869 var self = this;
6870
6871 if (_classes === undefined) {
6872 var ret = [];
6873
6874 self[0]._private.classes.forEach(function (cls) {
6875 return ret.push(cls);
6876 });
6877
6878 return ret;
6879 } else if (!array(_classes)) {
6880 // extract classes from string
6881 _classes = (_classes || '').match(/\S+/g) || [];
6882 }
6883
6884 var changed = [];
6885 var classesSet = new Set$1(_classes); // check and update each ele
6886
6887 for (var j = 0; j < self.length; j++) {
6888 var ele = self[j];
6889 var _p = ele._private;
6890 var eleClasses = _p.classes;
6891 var changedEle = false; // check if ele has all of the passed classes
6892
6893 for (var i = 0; i < _classes.length; i++) {
6894 var cls = _classes[i];
6895 var eleHasClass = eleClasses.has(cls);
6896
6897 if (!eleHasClass) {
6898 changedEle = true;
6899 break;
6900 }
6901 } // check if ele has classes outside of those passed
6902
6903
6904 if (!changedEle) {
6905 changedEle = eleClasses.size !== _classes.length;
6906 }
6907
6908 if (changedEle) {
6909 _p.classes = classesSet;
6910 changed.push(ele);
6911 }
6912 } // trigger update style on those eles that had class changes
6913
6914
6915 if (changed.length > 0) {
6916 this.spawn(changed).updateStyle().emit('class');
6917 }
6918
6919 return self;
6920 },
6921 addClass: function addClass(classes) {
6922 return this.toggleClass(classes, true);
6923 },
6924 hasClass: function hasClass(className) {
6925 var ele = this[0];
6926 return ele != null && ele._private.classes.has(className);
6927 },
6928 toggleClass: function toggleClass(classes, toggle) {
6929 if (!array(classes)) {
6930 // extract classes from string
6931 classes = classes.match(/\S+/g) || [];
6932 }
6933
6934 var self = this;
6935 var toggleUndefd = toggle === undefined;
6936 var changed = []; // eles who had classes changed
6937
6938 for (var i = 0, il = self.length; i < il; i++) {
6939 var ele = self[i];
6940 var eleClasses = ele._private.classes;
6941 var changedEle = false;
6942
6943 for (var j = 0; j < classes.length; j++) {
6944 var cls = classes[j];
6945 var hasClass = eleClasses.has(cls);
6946 var changedNow = false;
6947
6948 if (toggle || toggleUndefd && !hasClass) {
6949 eleClasses.add(cls);
6950 changedNow = true;
6951 } else if (!toggle || toggleUndefd && hasClass) {
6952 eleClasses["delete"](cls);
6953 changedNow = true;
6954 }
6955
6956 if (!changedEle && changedNow) {
6957 changed.push(ele);
6958 changedEle = true;
6959 }
6960 } // for j classes
6961
6962 } // for i eles
6963 // trigger update style on those eles that had class changes
6964
6965
6966 if (changed.length > 0) {
6967 this.spawn(changed).updateStyle().emit('class');
6968 }
6969
6970 return self;
6971 },
6972 removeClass: function removeClass(classes) {
6973 return this.toggleClass(classes, false);
6974 },
6975 flashClass: function flashClass(classes, duration) {
6976 var self = this;
6977
6978 if (duration == null) {
6979 duration = 250;
6980 } else if (duration === 0) {
6981 return self; // nothing to do really
6982 }
6983
6984 self.addClass(classes);
6985 setTimeout(function () {
6986 self.removeClass(classes);
6987 }, duration);
6988 return self;
6989 }
6990};
6991elesfn$h.className = elesfn$h.classNames = elesfn$h.classes;
6992
6993var tokens = {
6994 metaChar: '[\\!\\"\\#\\$\\%\\&\\\'\\(\\)\\*\\+\\,\\.\\/\\:\\;\\<\\=\\>\\?\\@\\[\\]\\^\\`\\{\\|\\}\\~]',
6995 // chars we need to escape in let names, etc
6996 comparatorOp: '=|\\!=|>|>=|<|<=|\\$=|\\^=|\\*=',
6997 // binary comparison op (used in data selectors)
6998 boolOp: '\\?|\\!|\\^',
6999 // boolean (unary) operators (used in data selectors)
7000 string: '"(?:\\\\"|[^"])*"' + '|' + "'(?:\\\\'|[^'])*'",
7001 // string literals (used in data selectors) -- doublequotes | singlequotes
7002 number: number,
7003 // number literal (used in data selectors) --- e.g. 0.1234, 1234, 12e123
7004 meta: 'degree|indegree|outdegree',
7005 // allowed metadata fields (i.e. allowed functions to use from Collection)
7006 separator: '\\s*,\\s*',
7007 // queries are separated by commas, e.g. edge[foo = 'bar'], node.someClass
7008 descendant: '\\s+',
7009 child: '\\s+>\\s+',
7010 subject: '\\$',
7011 group: 'node|edge|\\*',
7012 directedEdge: '\\s+->\\s+',
7013 undirectedEdge: '\\s+<->\\s+'
7014};
7015tokens.variable = '(?:[\\w-.]|(?:\\\\' + tokens.metaChar + '))+'; // a variable name can have letters, numbers, dashes, and periods
7016
7017tokens.className = '(?:[\\w-]|(?:\\\\' + tokens.metaChar + '))+'; // a class name has the same rules as a variable except it can't have a '.' in the name
7018
7019tokens.value = tokens.string + '|' + tokens.number; // a value literal, either a string or number
7020
7021tokens.id = tokens.variable; // an element id (follows variable conventions)
7022
7023(function () {
7024 var ops, op, i; // add @ variants to comparatorOp
7025
7026 ops = tokens.comparatorOp.split('|');
7027
7028 for (i = 0; i < ops.length; i++) {
7029 op = ops[i];
7030 tokens.comparatorOp += '|@' + op;
7031 } // add ! variants to comparatorOp
7032
7033
7034 ops = tokens.comparatorOp.split('|');
7035
7036 for (i = 0; i < ops.length; i++) {
7037 op = ops[i];
7038
7039 if (op.indexOf('!') >= 0) {
7040 continue;
7041 } // skip ops that explicitly contain !
7042
7043
7044 if (op === '=') {
7045 continue;
7046 } // skip = b/c != is explicitly defined
7047
7048
7049 tokens.comparatorOp += '|\\!' + op;
7050 }
7051})();
7052
7053/**
7054 * Make a new query object
7055 *
7056 * @prop type {Type} The type enum (int) of the query
7057 * @prop checks List of checks to make against an ele to test for a match
7058 */
7059var newQuery = function newQuery() {
7060 return {
7061 checks: []
7062 };
7063};
7064
7065/**
7066 * A check type enum-like object. Uses integer values for fast match() lookup.
7067 * The ordering does not matter as long as the ints are unique.
7068 */
7069var Type = {
7070 /** E.g. node */
7071 GROUP: 0,
7072
7073 /** A collection of elements */
7074 COLLECTION: 1,
7075
7076 /** A filter(ele) function */
7077 FILTER: 2,
7078
7079 /** E.g. [foo > 1] */
7080 DATA_COMPARE: 3,
7081
7082 /** E.g. [foo] */
7083 DATA_EXIST: 4,
7084
7085 /** E.g. [?foo] */
7086 DATA_BOOL: 5,
7087
7088 /** E.g. [[degree > 2]] */
7089 META_COMPARE: 6,
7090
7091 /** E.g. :selected */
7092 STATE: 7,
7093
7094 /** E.g. #foo */
7095 ID: 8,
7096
7097 /** E.g. .foo */
7098 CLASS: 9,
7099
7100 /** E.g. #foo <-> #bar */
7101 UNDIRECTED_EDGE: 10,
7102
7103 /** E.g. #foo -> #bar */
7104 DIRECTED_EDGE: 11,
7105
7106 /** E.g. $#foo -> #bar */
7107 NODE_SOURCE: 12,
7108
7109 /** E.g. #foo -> $#bar */
7110 NODE_TARGET: 13,
7111
7112 /** E.g. $#foo <-> #bar */
7113 NODE_NEIGHBOR: 14,
7114
7115 /** E.g. #foo > #bar */
7116 CHILD: 15,
7117
7118 /** E.g. #foo #bar */
7119 DESCENDANT: 16,
7120
7121 /** E.g. $#foo > #bar */
7122 PARENT: 17,
7123
7124 /** E.g. $#foo #bar */
7125 ANCESTOR: 18,
7126
7127 /** E.g. #foo > $bar > #baz */
7128 COMPOUND_SPLIT: 19,
7129
7130 /** Always matches, useful placeholder for subject in `COMPOUND_SPLIT` */
7131 TRUE: 20
7132};
7133
7134var stateSelectors = [{
7135 selector: ':selected',
7136 matches: function matches(ele) {
7137 return ele.selected();
7138 }
7139}, {
7140 selector: ':unselected',
7141 matches: function matches(ele) {
7142 return !ele.selected();
7143 }
7144}, {
7145 selector: ':selectable',
7146 matches: function matches(ele) {
7147 return ele.selectable();
7148 }
7149}, {
7150 selector: ':unselectable',
7151 matches: function matches(ele) {
7152 return !ele.selectable();
7153 }
7154}, {
7155 selector: ':locked',
7156 matches: function matches(ele) {
7157 return ele.locked();
7158 }
7159}, {
7160 selector: ':unlocked',
7161 matches: function matches(ele) {
7162 return !ele.locked();
7163 }
7164}, {
7165 selector: ':visible',
7166 matches: function matches(ele) {
7167 return ele.visible();
7168 }
7169}, {
7170 selector: ':hidden',
7171 matches: function matches(ele) {
7172 return !ele.visible();
7173 }
7174}, {
7175 selector: ':transparent',
7176 matches: function matches(ele) {
7177 return ele.transparent();
7178 }
7179}, {
7180 selector: ':grabbed',
7181 matches: function matches(ele) {
7182 return ele.grabbed();
7183 }
7184}, {
7185 selector: ':free',
7186 matches: function matches(ele) {
7187 return !ele.grabbed();
7188 }
7189}, {
7190 selector: ':removed',
7191 matches: function matches(ele) {
7192 return ele.removed();
7193 }
7194}, {
7195 selector: ':inside',
7196 matches: function matches(ele) {
7197 return !ele.removed();
7198 }
7199}, {
7200 selector: ':grabbable',
7201 matches: function matches(ele) {
7202 return ele.grabbable();
7203 }
7204}, {
7205 selector: ':ungrabbable',
7206 matches: function matches(ele) {
7207 return !ele.grabbable();
7208 }
7209}, {
7210 selector: ':animated',
7211 matches: function matches(ele) {
7212 return ele.animated();
7213 }
7214}, {
7215 selector: ':unanimated',
7216 matches: function matches(ele) {
7217 return !ele.animated();
7218 }
7219}, {
7220 selector: ':parent',
7221 matches: function matches(ele) {
7222 return ele.isParent();
7223 }
7224}, {
7225 selector: ':childless',
7226 matches: function matches(ele) {
7227 return ele.isChildless();
7228 }
7229}, {
7230 selector: ':child',
7231 matches: function matches(ele) {
7232 return ele.isChild();
7233 }
7234}, {
7235 selector: ':orphan',
7236 matches: function matches(ele) {
7237 return ele.isOrphan();
7238 }
7239}, {
7240 selector: ':nonorphan',
7241 matches: function matches(ele) {
7242 return ele.isChild();
7243 }
7244}, {
7245 selector: ':compound',
7246 matches: function matches(ele) {
7247 if (ele.isNode()) {
7248 return ele.isParent();
7249 } else {
7250 return ele.source().isParent() || ele.target().isParent();
7251 }
7252 }
7253}, {
7254 selector: ':loop',
7255 matches: function matches(ele) {
7256 return ele.isLoop();
7257 }
7258}, {
7259 selector: ':simple',
7260 matches: function matches(ele) {
7261 return ele.isSimple();
7262 }
7263}, {
7264 selector: ':active',
7265 matches: function matches(ele) {
7266 return ele.active();
7267 }
7268}, {
7269 selector: ':inactive',
7270 matches: function matches(ele) {
7271 return !ele.active();
7272 }
7273}, {
7274 selector: ':backgrounding',
7275 matches: function matches(ele) {
7276 return ele.backgrounding();
7277 }
7278}, {
7279 selector: ':nonbackgrounding',
7280 matches: function matches(ele) {
7281 return !ele.backgrounding();
7282 }
7283}].sort(function (a, b) {
7284 // n.b. selectors that are starting substrings of others must have the longer ones first
7285 return descending(a.selector, b.selector);
7286});
7287
7288var lookup = function () {
7289 var selToFn = {};
7290 var s;
7291
7292 for (var i = 0; i < stateSelectors.length; i++) {
7293 s = stateSelectors[i];
7294 selToFn[s.selector] = s.matches;
7295 }
7296
7297 return selToFn;
7298}();
7299
7300var stateSelectorMatches = function stateSelectorMatches(sel, ele) {
7301 return lookup[sel](ele);
7302};
7303var stateSelectorRegex = '(' + stateSelectors.map(function (s) {
7304 return s.selector;
7305}).join('|') + ')';
7306
7307// so that values get compared properly in Selector.filter()
7308
7309var cleanMetaChars = function cleanMetaChars(str) {
7310 return str.replace(new RegExp('\\\\(' + tokens.metaChar + ')', 'g'), function (match, $1) {
7311 return $1;
7312 });
7313};
7314
7315var replaceLastQuery = function replaceLastQuery(selector, examiningQuery, replacementQuery) {
7316 selector[selector.length - 1] = replacementQuery;
7317}; // NOTE: add new expression syntax here to have it recognised by the parser;
7318// - a query contains all adjacent (i.e. no separator in between) expressions;
7319// - the current query is stored in selector[i]
7320// - you need to check the query objects in match() for it actually filter properly, but that's pretty straight forward
7321
7322
7323var exprs = [{
7324 name: 'group',
7325 // just used for identifying when debugging
7326 query: true,
7327 regex: '(' + tokens.group + ')',
7328 populate: function populate(selector, query, _ref) {
7329 var _ref2 = _slicedToArray(_ref, 1),
7330 group = _ref2[0];
7331
7332 query.checks.push({
7333 type: Type.GROUP,
7334 value: group === '*' ? group : group + 's'
7335 });
7336 }
7337}, {
7338 name: 'state',
7339 query: true,
7340 regex: stateSelectorRegex,
7341 populate: function populate(selector, query, _ref3) {
7342 var _ref4 = _slicedToArray(_ref3, 1),
7343 state = _ref4[0];
7344
7345 query.checks.push({
7346 type: Type.STATE,
7347 value: state
7348 });
7349 }
7350}, {
7351 name: 'id',
7352 query: true,
7353 regex: '\\#(' + tokens.id + ')',
7354 populate: function populate(selector, query, _ref5) {
7355 var _ref6 = _slicedToArray(_ref5, 1),
7356 id = _ref6[0];
7357
7358 query.checks.push({
7359 type: Type.ID,
7360 value: cleanMetaChars(id)
7361 });
7362 }
7363}, {
7364 name: 'className',
7365 query: true,
7366 regex: '\\.(' + tokens.className + ')',
7367 populate: function populate(selector, query, _ref7) {
7368 var _ref8 = _slicedToArray(_ref7, 1),
7369 className = _ref8[0];
7370
7371 query.checks.push({
7372 type: Type.CLASS,
7373 value: cleanMetaChars(className)
7374 });
7375 }
7376}, {
7377 name: 'dataExists',
7378 query: true,
7379 regex: '\\[\\s*(' + tokens.variable + ')\\s*\\]',
7380 populate: function populate(selector, query, _ref9) {
7381 var _ref10 = _slicedToArray(_ref9, 1),
7382 variable = _ref10[0];
7383
7384 query.checks.push({
7385 type: Type.DATA_EXIST,
7386 field: cleanMetaChars(variable)
7387 });
7388 }
7389}, {
7390 name: 'dataCompare',
7391 query: true,
7392 regex: '\\[\\s*(' + tokens.variable + ')\\s*(' + tokens.comparatorOp + ')\\s*(' + tokens.value + ')\\s*\\]',
7393 populate: function populate(selector, query, _ref11) {
7394 var _ref12 = _slicedToArray(_ref11, 3),
7395 variable = _ref12[0],
7396 comparatorOp = _ref12[1],
7397 value = _ref12[2];
7398
7399 var valueIsString = new RegExp('^' + tokens.string + '$').exec(value) != null;
7400
7401 if (valueIsString) {
7402 value = value.substring(1, value.length - 1);
7403 } else {
7404 value = parseFloat(value);
7405 }
7406
7407 query.checks.push({
7408 type: Type.DATA_COMPARE,
7409 field: cleanMetaChars(variable),
7410 operator: comparatorOp,
7411 value: value
7412 });
7413 }
7414}, {
7415 name: 'dataBool',
7416 query: true,
7417 regex: '\\[\\s*(' + tokens.boolOp + ')\\s*(' + tokens.variable + ')\\s*\\]',
7418 populate: function populate(selector, query, _ref13) {
7419 var _ref14 = _slicedToArray(_ref13, 2),
7420 boolOp = _ref14[0],
7421 variable = _ref14[1];
7422
7423 query.checks.push({
7424 type: Type.DATA_BOOL,
7425 field: cleanMetaChars(variable),
7426 operator: boolOp
7427 });
7428 }
7429}, {
7430 name: 'metaCompare',
7431 query: true,
7432 regex: '\\[\\[\\s*(' + tokens.meta + ')\\s*(' + tokens.comparatorOp + ')\\s*(' + tokens.number + ')\\s*\\]\\]',
7433 populate: function populate(selector, query, _ref15) {
7434 var _ref16 = _slicedToArray(_ref15, 3),
7435 meta = _ref16[0],
7436 comparatorOp = _ref16[1],
7437 number = _ref16[2];
7438
7439 query.checks.push({
7440 type: Type.META_COMPARE,
7441 field: cleanMetaChars(meta),
7442 operator: comparatorOp,
7443 value: parseFloat(number)
7444 });
7445 }
7446}, {
7447 name: 'nextQuery',
7448 separator: true,
7449 regex: tokens.separator,
7450 populate: function populate(selector, query) {
7451 var currentSubject = selector.currentSubject;
7452 var edgeCount = selector.edgeCount;
7453 var compoundCount = selector.compoundCount;
7454 var lastQ = selector[selector.length - 1];
7455
7456 if (currentSubject != null) {
7457 lastQ.subject = currentSubject;
7458 selector.currentSubject = null;
7459 }
7460
7461 lastQ.edgeCount = edgeCount;
7462 lastQ.compoundCount = compoundCount;
7463 selector.edgeCount = 0;
7464 selector.compoundCount = 0; // go on to next query
7465
7466 var nextQuery = selector[selector.length++] = newQuery();
7467 return nextQuery; // this is the new query to be filled by the following exprs
7468 }
7469}, {
7470 name: 'directedEdge',
7471 separator: true,
7472 regex: tokens.directedEdge,
7473 populate: function populate(selector, query) {
7474 if (selector.currentSubject == null) {
7475 // undirected edge
7476 var edgeQuery = newQuery();
7477 var source = query;
7478 var target = newQuery();
7479 edgeQuery.checks.push({
7480 type: Type.DIRECTED_EDGE,
7481 source: source,
7482 target: target
7483 }); // the query in the selector should be the edge rather than the source
7484
7485 replaceLastQuery(selector, query, edgeQuery);
7486 selector.edgeCount++; // we're now populating the target query with expressions that follow
7487
7488 return target;
7489 } else {
7490 // source/target
7491 var srcTgtQ = newQuery();
7492 var _source = query;
7493
7494 var _target = newQuery();
7495
7496 srcTgtQ.checks.push({
7497 type: Type.NODE_SOURCE,
7498 source: _source,
7499 target: _target
7500 }); // the query in the selector should be the neighbourhood rather than the node
7501
7502 replaceLastQuery(selector, query, srcTgtQ);
7503 selector.edgeCount++;
7504 return _target; // now populating the target with the following expressions
7505 }
7506 }
7507}, {
7508 name: 'undirectedEdge',
7509 separator: true,
7510 regex: tokens.undirectedEdge,
7511 populate: function populate(selector, query) {
7512 if (selector.currentSubject == null) {
7513 // undirected edge
7514 var edgeQuery = newQuery();
7515 var source = query;
7516 var target = newQuery();
7517 edgeQuery.checks.push({
7518 type: Type.UNDIRECTED_EDGE,
7519 nodes: [source, target]
7520 }); // the query in the selector should be the edge rather than the source
7521
7522 replaceLastQuery(selector, query, edgeQuery);
7523 selector.edgeCount++; // we're now populating the target query with expressions that follow
7524
7525 return target;
7526 } else {
7527 // neighbourhood
7528 var nhoodQ = newQuery();
7529 var node = query;
7530 var neighbor = newQuery();
7531 nhoodQ.checks.push({
7532 type: Type.NODE_NEIGHBOR,
7533 node: node,
7534 neighbor: neighbor
7535 }); // the query in the selector should be the neighbourhood rather than the node
7536
7537 replaceLastQuery(selector, query, nhoodQ);
7538 return neighbor; // now populating the neighbor with following expressions
7539 }
7540 }
7541}, {
7542 name: 'child',
7543 separator: true,
7544 regex: tokens.child,
7545 populate: function populate(selector, query) {
7546 if (selector.currentSubject == null) {
7547 // default: child query
7548 var parentChildQuery = newQuery();
7549 var child = newQuery();
7550 var parent = selector[selector.length - 1];
7551 parentChildQuery.checks.push({
7552 type: Type.CHILD,
7553 parent: parent,
7554 child: child
7555 }); // the query in the selector should be the '>' itself
7556
7557 replaceLastQuery(selector, query, parentChildQuery);
7558 selector.compoundCount++; // we're now populating the child query with expressions that follow
7559
7560 return child;
7561 } else if (selector.currentSubject === query) {
7562 // compound split query
7563 var compound = newQuery();
7564 var left = selector[selector.length - 1];
7565 var right = newQuery();
7566 var subject = newQuery();
7567
7568 var _child = newQuery();
7569
7570 var _parent = newQuery(); // set up the root compound q
7571
7572
7573 compound.checks.push({
7574 type: Type.COMPOUND_SPLIT,
7575 left: left,
7576 right: right,
7577 subject: subject
7578 }); // populate the subject and replace the q at the old spot (within left) with TRUE
7579
7580 subject.checks = query.checks; // take the checks from the left
7581
7582 query.checks = [{
7583 type: Type.TRUE
7584 }]; // checks under left refs the subject implicitly
7585 // set up the right q
7586
7587 _parent.checks.push({
7588 type: Type.TRUE
7589 }); // parent implicitly refs the subject
7590
7591
7592 right.checks.push({
7593 type: Type.PARENT,
7594 // type is swapped on right side queries
7595 parent: _parent,
7596 child: _child // empty for now
7597
7598 });
7599 replaceLastQuery(selector, left, compound); // update the ref since we moved things around for `query`
7600
7601 selector.currentSubject = subject;
7602 selector.compoundCount++;
7603 return _child; // now populating the right side's child
7604 } else {
7605 // parent query
7606 // info for parent query
7607 var _parent2 = newQuery();
7608
7609 var _child2 = newQuery();
7610
7611 var pcQChecks = [{
7612 type: Type.PARENT,
7613 parent: _parent2,
7614 child: _child2
7615 }]; // the parent-child query takes the place of the query previously being populated
7616
7617 _parent2.checks = query.checks; // the previous query contains the checks for the parent
7618
7619 query.checks = pcQChecks; // pc query takes over
7620
7621 selector.compoundCount++;
7622 return _child2; // we're now populating the child
7623 }
7624 }
7625}, {
7626 name: 'descendant',
7627 separator: true,
7628 regex: tokens.descendant,
7629 populate: function populate(selector, query) {
7630 if (selector.currentSubject == null) {
7631 // default: descendant query
7632 var ancChQuery = newQuery();
7633 var descendant = newQuery();
7634 var ancestor = selector[selector.length - 1];
7635 ancChQuery.checks.push({
7636 type: Type.DESCENDANT,
7637 ancestor: ancestor,
7638 descendant: descendant
7639 }); // the query in the selector should be the '>' itself
7640
7641 replaceLastQuery(selector, query, ancChQuery);
7642 selector.compoundCount++; // we're now populating the descendant query with expressions that follow
7643
7644 return descendant;
7645 } else if (selector.currentSubject === query) {
7646 // compound split query
7647 var compound = newQuery();
7648 var left = selector[selector.length - 1];
7649 var right = newQuery();
7650 var subject = newQuery();
7651
7652 var _descendant = newQuery();
7653
7654 var _ancestor = newQuery(); // set up the root compound q
7655
7656
7657 compound.checks.push({
7658 type: Type.COMPOUND_SPLIT,
7659 left: left,
7660 right: right,
7661 subject: subject
7662 }); // populate the subject and replace the q at the old spot (within left) with TRUE
7663
7664 subject.checks = query.checks; // take the checks from the left
7665
7666 query.checks = [{
7667 type: Type.TRUE
7668 }]; // checks under left refs the subject implicitly
7669 // set up the right q
7670
7671 _ancestor.checks.push({
7672 type: Type.TRUE
7673 }); // ancestor implicitly refs the subject
7674
7675
7676 right.checks.push({
7677 type: Type.ANCESTOR,
7678 // type is swapped on right side queries
7679 ancestor: _ancestor,
7680 descendant: _descendant // empty for now
7681
7682 });
7683 replaceLastQuery(selector, left, compound); // update the ref since we moved things around for `query`
7684
7685 selector.currentSubject = subject;
7686 selector.compoundCount++;
7687 return _descendant; // now populating the right side's descendant
7688 } else {
7689 // ancestor query
7690 // info for parent query
7691 var _ancestor2 = newQuery();
7692
7693 var _descendant2 = newQuery();
7694
7695 var adQChecks = [{
7696 type: Type.ANCESTOR,
7697 ancestor: _ancestor2,
7698 descendant: _descendant2
7699 }]; // the parent-child query takes the place of the query previously being populated
7700
7701 _ancestor2.checks = query.checks; // the previous query contains the checks for the parent
7702
7703 query.checks = adQChecks; // pc query takes over
7704
7705 selector.compoundCount++;
7706 return _descendant2; // we're now populating the child
7707 }
7708 }
7709}, {
7710 name: 'subject',
7711 modifier: true,
7712 regex: tokens.subject,
7713 populate: function populate(selector, query) {
7714 if (selector.currentSubject != null && selector.currentSubject !== query) {
7715 warn('Redefinition of subject in selector `' + selector.toString() + '`');
7716 return false;
7717 }
7718
7719 selector.currentSubject = query;
7720 var topQ = selector[selector.length - 1];
7721 var topChk = topQ.checks[0];
7722 var topType = topChk == null ? null : topChk.type;
7723
7724 if (topType === Type.DIRECTED_EDGE) {
7725 // directed edge with subject on the target
7726 // change to target node check
7727 topChk.type = Type.NODE_TARGET;
7728 } else if (topType === Type.UNDIRECTED_EDGE) {
7729 // undirected edge with subject on the second node
7730 // change to neighbor check
7731 topChk.type = Type.NODE_NEIGHBOR;
7732 topChk.node = topChk.nodes[1]; // second node is subject
7733
7734 topChk.neighbor = topChk.nodes[0]; // clean up unused fields for new type
7735
7736 topChk.nodes = null;
7737 }
7738 }
7739}];
7740exprs.forEach(function (e) {
7741 return e.regexObj = new RegExp('^' + e.regex);
7742});
7743
7744/**
7745 * Of all the expressions, find the first match in the remaining text.
7746 * @param {string} remaining The remaining text to parse
7747 * @returns The matched expression and the newly remaining text `{ expr, match, name, remaining }`
7748 */
7749
7750var consumeExpr = function consumeExpr(remaining) {
7751 var expr;
7752 var match;
7753 var name;
7754
7755 for (var j = 0; j < exprs.length; j++) {
7756 var e = exprs[j];
7757 var n = e.name;
7758 var m = remaining.match(e.regexObj);
7759
7760 if (m != null) {
7761 match = m;
7762 expr = e;
7763 name = n;
7764 var consumed = m[0];
7765 remaining = remaining.substring(consumed.length);
7766 break; // we've consumed one expr, so we can return now
7767 }
7768 }
7769
7770 return {
7771 expr: expr,
7772 match: match,
7773 name: name,
7774 remaining: remaining
7775 };
7776};
7777/**
7778 * Consume all the leading whitespace
7779 * @param {string} remaining The text to consume
7780 * @returns The text with the leading whitespace removed
7781 */
7782
7783
7784var consumeWhitespace = function consumeWhitespace(remaining) {
7785 var match = remaining.match(/^\s+/);
7786
7787 if (match) {
7788 var consumed = match[0];
7789 remaining = remaining.substring(consumed.length);
7790 }
7791
7792 return remaining;
7793};
7794/**
7795 * Parse the string and store the parsed representation in the Selector.
7796 * @param {string} selector The selector string
7797 * @returns `true` if the selector was successfully parsed, `false` otherwise
7798 */
7799
7800
7801var parse = function parse(selector) {
7802 var self = this;
7803 var remaining = self.inputText = selector;
7804 var currentQuery = self[0] = newQuery();
7805 self.length = 1;
7806 remaining = consumeWhitespace(remaining); // get rid of leading whitespace
7807
7808 for (;;) {
7809 var exprInfo = consumeExpr(remaining);
7810
7811 if (exprInfo.expr == null) {
7812 warn('The selector `' + selector + '`is invalid');
7813 return false;
7814 } else {
7815 var args = exprInfo.match.slice(1); // let the token populate the selector object in currentQuery
7816
7817 var ret = exprInfo.expr.populate(self, currentQuery, args);
7818
7819 if (ret === false) {
7820 return false; // exit if population failed
7821 } else if (ret != null) {
7822 currentQuery = ret; // change the current query to be filled if the expr specifies
7823 }
7824 }
7825
7826 remaining = exprInfo.remaining; // we're done when there's nothing left to parse
7827
7828 if (remaining.match(/^\s*$/)) {
7829 break;
7830 }
7831 }
7832
7833 var lastQ = self[self.length - 1];
7834
7835 if (self.currentSubject != null) {
7836 lastQ.subject = self.currentSubject;
7837 }
7838
7839 lastQ.edgeCount = self.edgeCount;
7840 lastQ.compoundCount = self.compoundCount;
7841
7842 for (var i = 0; i < self.length; i++) {
7843 var q = self[i]; // in future, this could potentially be allowed if there were operator precedence and detection of invalid combinations
7844
7845 if (q.compoundCount > 0 && q.edgeCount > 0) {
7846 warn('The selector `' + selector + '` is invalid because it uses both a compound selector and an edge selector');
7847 return false;
7848 }
7849
7850 if (q.edgeCount > 1) {
7851 warn('The selector `' + selector + '` is invalid because it uses multiple edge selectors');
7852 return false;
7853 } else if (q.edgeCount === 1) {
7854 warn('The selector `' + selector + '` is deprecated. Edge selectors do not take effect on changes to source and target nodes after an edge is added, for performance reasons. Use a class or data selector on edges instead, updating the class or data of an edge when your app detects a change in source or target nodes.');
7855 }
7856 }
7857
7858 return true; // success
7859};
7860/**
7861 * Get the selector represented as a string. This value uses default formatting,
7862 * so things like spacing may differ from the input text passed to the constructor.
7863 * @returns {string} The selector string
7864 */
7865
7866
7867var toString = function toString() {
7868 if (this.toStringCache != null) {
7869 return this.toStringCache;
7870 }
7871
7872 var clean = function clean(obj) {
7873 if (obj == null) {
7874 return '';
7875 } else {
7876 return obj;
7877 }
7878 };
7879
7880 var cleanVal = function cleanVal(val) {
7881 if (string(val)) {
7882 return '"' + val + '"';
7883 } else {
7884 return clean(val);
7885 }
7886 };
7887
7888 var space = function space(val) {
7889 return ' ' + val + ' ';
7890 };
7891
7892 var checkToString = function checkToString(check, subject) {
7893 var type = check.type,
7894 value = check.value;
7895
7896 switch (type) {
7897 case Type.GROUP:
7898 {
7899 var group = clean(value);
7900 return group.substring(0, group.length - 1);
7901 }
7902
7903 case Type.DATA_COMPARE:
7904 {
7905 var field = check.field,
7906 operator = check.operator;
7907 return '[' + field + space(clean(operator)) + cleanVal(value) + ']';
7908 }
7909
7910 case Type.DATA_BOOL:
7911 {
7912 var _operator = check.operator,
7913 _field = check.field;
7914 return '[' + clean(_operator) + _field + ']';
7915 }
7916
7917 case Type.DATA_EXIST:
7918 {
7919 var _field2 = check.field;
7920 return '[' + _field2 + ']';
7921 }
7922
7923 case Type.META_COMPARE:
7924 {
7925 var _operator2 = check.operator,
7926 _field3 = check.field;
7927 return '[[' + _field3 + space(clean(_operator2)) + cleanVal(value) + ']]';
7928 }
7929
7930 case Type.STATE:
7931 {
7932 return value;
7933 }
7934
7935 case Type.ID:
7936 {
7937 return '#' + value;
7938 }
7939
7940 case Type.CLASS:
7941 {
7942 return '.' + value;
7943 }
7944
7945 case Type.PARENT:
7946 case Type.CHILD:
7947 {
7948 return queryToString(check.parent, subject) + space('>') + queryToString(check.child, subject);
7949 }
7950
7951 case Type.ANCESTOR:
7952 case Type.DESCENDANT:
7953 {
7954 return queryToString(check.ancestor, subject) + ' ' + queryToString(check.descendant, subject);
7955 }
7956
7957 case Type.COMPOUND_SPLIT:
7958 {
7959 var lhs = queryToString(check.left, subject);
7960 var sub = queryToString(check.subject, subject);
7961 var rhs = queryToString(check.right, subject);
7962 return lhs + (lhs.length > 0 ? ' ' : '') + sub + rhs;
7963 }
7964
7965 case Type.TRUE:
7966 {
7967 return '';
7968 }
7969 }
7970 };
7971
7972 var queryToString = function queryToString(query, subject) {
7973 return query.checks.reduce(function (str, chk, i) {
7974 return str + (subject === query && i === 0 ? '$' : '') + checkToString(chk, subject);
7975 }, '');
7976 };
7977
7978 var str = '';
7979
7980 for (var i = 0; i < this.length; i++) {
7981 var query = this[i];
7982 str += queryToString(query, query.subject);
7983
7984 if (this.length > 1 && i < this.length - 1) {
7985 str += ', ';
7986 }
7987 }
7988
7989 this.toStringCache = str;
7990 return str;
7991};
7992var parse$1 = {
7993 parse: parse,
7994 toString: toString
7995};
7996
7997var valCmp = function valCmp(fieldVal, operator, value) {
7998 var matches;
7999 var isFieldStr = string(fieldVal);
8000 var isFieldNum = number$1(fieldVal);
8001 var isValStr = string(value);
8002 var fieldStr, valStr;
8003 var caseInsensitive = false;
8004 var notExpr = false;
8005 var isIneqCmp = false;
8006
8007 if (operator.indexOf('!') >= 0) {
8008 operator = operator.replace('!', '');
8009 notExpr = true;
8010 }
8011
8012 if (operator.indexOf('@') >= 0) {
8013 operator = operator.replace('@', '');
8014 caseInsensitive = true;
8015 }
8016
8017 if (isFieldStr || isValStr || caseInsensitive) {
8018 fieldStr = !isFieldStr && !isFieldNum ? '' : '' + fieldVal;
8019 valStr = '' + value;
8020 } // if we're doing a case insensitive comparison, then we're using a STRING comparison
8021 // even if we're comparing numbers
8022
8023
8024 if (caseInsensitive) {
8025 fieldVal = fieldStr = fieldStr.toLowerCase();
8026 value = valStr = valStr.toLowerCase();
8027 }
8028
8029 switch (operator) {
8030 case '*=':
8031 matches = fieldStr.indexOf(valStr) >= 0;
8032 break;
8033
8034 case '$=':
8035 matches = fieldStr.indexOf(valStr, fieldStr.length - valStr.length) >= 0;
8036 break;
8037
8038 case '^=':
8039 matches = fieldStr.indexOf(valStr) === 0;
8040 break;
8041
8042 case '=':
8043 matches = fieldVal === value;
8044 break;
8045
8046 case '>':
8047 isIneqCmp = true;
8048 matches = fieldVal > value;
8049 break;
8050
8051 case '>=':
8052 isIneqCmp = true;
8053 matches = fieldVal >= value;
8054 break;
8055
8056 case '<':
8057 isIneqCmp = true;
8058 matches = fieldVal < value;
8059 break;
8060
8061 case '<=':
8062 isIneqCmp = true;
8063 matches = fieldVal <= value;
8064 break;
8065
8066 default:
8067 matches = false;
8068 break;
8069 } // apply the not op, but null vals for inequalities should always stay non-matching
8070
8071
8072 if (notExpr && (fieldVal != null || !isIneqCmp)) {
8073 matches = !matches;
8074 }
8075
8076 return matches;
8077};
8078var boolCmp = function boolCmp(fieldVal, operator) {
8079 switch (operator) {
8080 case '?':
8081 return fieldVal ? true : false;
8082
8083 case '!':
8084 return fieldVal ? false : true;
8085
8086 case '^':
8087 return fieldVal === undefined;
8088 }
8089};
8090var existCmp = function existCmp(fieldVal) {
8091 return fieldVal !== undefined;
8092};
8093var data$1 = function data(ele, field) {
8094 return ele.data(field);
8095};
8096var meta = function meta(ele, field) {
8097 return ele[field]();
8098};
8099
8100/** A lookup of `match(check, ele)` functions by `Type` int */
8101
8102var match = [];
8103/**
8104 * Returns whether the query matches for the element
8105 * @param query The `{ type, value, ... }` query object
8106 * @param ele The element to compare against
8107*/
8108
8109var matches$1 = function matches(query, ele) {
8110 return query.checks.every(function (chk) {
8111 return match[chk.type](chk, ele);
8112 });
8113};
8114
8115match[Type.GROUP] = function (check, ele) {
8116 var group = check.value;
8117 return group === '*' || group === ele.group();
8118};
8119
8120match[Type.STATE] = function (check, ele) {
8121 var stateSelector = check.value;
8122 return stateSelectorMatches(stateSelector, ele);
8123};
8124
8125match[Type.ID] = function (check, ele) {
8126 var id = check.value;
8127 return ele.id() === id;
8128};
8129
8130match[Type.CLASS] = function (check, ele) {
8131 var cls = check.value;
8132 return ele.hasClass(cls);
8133};
8134
8135match[Type.META_COMPARE] = function (check, ele) {
8136 var field = check.field,
8137 operator = check.operator,
8138 value = check.value;
8139 return valCmp(meta(ele, field), operator, value);
8140};
8141
8142match[Type.DATA_COMPARE] = function (check, ele) {
8143 var field = check.field,
8144 operator = check.operator,
8145 value = check.value;
8146 return valCmp(data$1(ele, field), operator, value);
8147};
8148
8149match[Type.DATA_BOOL] = function (check, ele) {
8150 var field = check.field,
8151 operator = check.operator;
8152 return boolCmp(data$1(ele, field), operator);
8153};
8154
8155match[Type.DATA_EXIST] = function (check, ele) {
8156 var field = check.field;
8157 check.operator;
8158 return existCmp(data$1(ele, field));
8159};
8160
8161match[Type.UNDIRECTED_EDGE] = function (check, ele) {
8162 var qA = check.nodes[0];
8163 var qB = check.nodes[1];
8164 var src = ele.source();
8165 var tgt = ele.target();
8166 return matches$1(qA, src) && matches$1(qB, tgt) || matches$1(qB, src) && matches$1(qA, tgt);
8167};
8168
8169match[Type.NODE_NEIGHBOR] = function (check, ele) {
8170 return matches$1(check.node, ele) && ele.neighborhood().some(function (n) {
8171 return n.isNode() && matches$1(check.neighbor, n);
8172 });
8173};
8174
8175match[Type.DIRECTED_EDGE] = function (check, ele) {
8176 return matches$1(check.source, ele.source()) && matches$1(check.target, ele.target());
8177};
8178
8179match[Type.NODE_SOURCE] = function (check, ele) {
8180 return matches$1(check.source, ele) && ele.outgoers().some(function (n) {
8181 return n.isNode() && matches$1(check.target, n);
8182 });
8183};
8184
8185match[Type.NODE_TARGET] = function (check, ele) {
8186 return matches$1(check.target, ele) && ele.incomers().some(function (n) {
8187 return n.isNode() && matches$1(check.source, n);
8188 });
8189};
8190
8191match[Type.CHILD] = function (check, ele) {
8192 return matches$1(check.child, ele) && matches$1(check.parent, ele.parent());
8193};
8194
8195match[Type.PARENT] = function (check, ele) {
8196 return matches$1(check.parent, ele) && ele.children().some(function (c) {
8197 return matches$1(check.child, c);
8198 });
8199};
8200
8201match[Type.DESCENDANT] = function (check, ele) {
8202 return matches$1(check.descendant, ele) && ele.ancestors().some(function (a) {
8203 return matches$1(check.ancestor, a);
8204 });
8205};
8206
8207match[Type.ANCESTOR] = function (check, ele) {
8208 return matches$1(check.ancestor, ele) && ele.descendants().some(function (d) {
8209 return matches$1(check.descendant, d);
8210 });
8211};
8212
8213match[Type.COMPOUND_SPLIT] = function (check, ele) {
8214 return matches$1(check.subject, ele) && matches$1(check.left, ele) && matches$1(check.right, ele);
8215};
8216
8217match[Type.TRUE] = function () {
8218 return true;
8219};
8220
8221match[Type.COLLECTION] = function (check, ele) {
8222 var collection = check.value;
8223 return collection.has(ele);
8224};
8225
8226match[Type.FILTER] = function (check, ele) {
8227 var filter = check.value;
8228 return filter(ele);
8229};
8230
8231var filter = function filter(collection) {
8232 var self = this; // for 1 id #foo queries, just get the element
8233
8234 if (self.length === 1 && self[0].checks.length === 1 && self[0].checks[0].type === Type.ID) {
8235 return collection.getElementById(self[0].checks[0].value).collection();
8236 }
8237
8238 var selectorFunction = function selectorFunction(element) {
8239 for (var j = 0; j < self.length; j++) {
8240 var query = self[j];
8241
8242 if (matches$1(query, element)) {
8243 return true;
8244 }
8245 }
8246
8247 return false;
8248 };
8249
8250 if (self.text() == null) {
8251 selectorFunction = function selectorFunction() {
8252 return true;
8253 };
8254 }
8255
8256 return collection.filter(selectorFunction);
8257}; // filter
8258// does selector match a single element?
8259
8260
8261var matches = function matches(ele) {
8262 var self = this;
8263
8264 for (var j = 0; j < self.length; j++) {
8265 var query = self[j];
8266
8267 if (matches$1(query, ele)) {
8268 return true;
8269 }
8270 }
8271
8272 return false;
8273}; // matches
8274
8275
8276var matching = {
8277 matches: matches,
8278 filter: filter
8279};
8280
8281var Selector = function Selector(selector) {
8282 this.inputText = selector;
8283 this.currentSubject = null;
8284 this.compoundCount = 0;
8285 this.edgeCount = 0;
8286 this.length = 0;
8287
8288 if (selector == null || string(selector) && selector.match(/^\s*$/)) ; else if (elementOrCollection(selector)) {
8289 this.addQuery({
8290 checks: [{
8291 type: Type.COLLECTION,
8292 value: selector.collection()
8293 }]
8294 });
8295 } else if (fn$6(selector)) {
8296 this.addQuery({
8297 checks: [{
8298 type: Type.FILTER,
8299 value: selector
8300 }]
8301 });
8302 } else if (string(selector)) {
8303 if (!this.parse(selector)) {
8304 this.invalid = true;
8305 }
8306 } else {
8307 error('A selector must be created from a string; found ');
8308 }
8309};
8310
8311var selfn = Selector.prototype;
8312[parse$1, matching].forEach(function (p) {
8313 return extend(selfn, p);
8314});
8315
8316selfn.text = function () {
8317 return this.inputText;
8318};
8319
8320selfn.size = function () {
8321 return this.length;
8322};
8323
8324selfn.eq = function (i) {
8325 return this[i];
8326};
8327
8328selfn.sameText = function (otherSel) {
8329 return !this.invalid && !otherSel.invalid && this.text() === otherSel.text();
8330};
8331
8332selfn.addQuery = function (q) {
8333 this[this.length++] = q;
8334};
8335
8336selfn.selector = selfn.toString;
8337
8338var elesfn$g = {
8339 allAre: function allAre(selector) {
8340 var selObj = new Selector(selector);
8341 return this.every(function (ele) {
8342 return selObj.matches(ele);
8343 });
8344 },
8345 is: function is(selector) {
8346 var selObj = new Selector(selector);
8347 return this.some(function (ele) {
8348 return selObj.matches(ele);
8349 });
8350 },
8351 some: function some(fn, thisArg) {
8352 for (var i = 0; i < this.length; i++) {
8353 var ret = !thisArg ? fn(this[i], i, this) : fn.apply(thisArg, [this[i], i, this]);
8354
8355 if (ret) {
8356 return true;
8357 }
8358 }
8359
8360 return false;
8361 },
8362 every: function every(fn, thisArg) {
8363 for (var i = 0; i < this.length; i++) {
8364 var ret = !thisArg ? fn(this[i], i, this) : fn.apply(thisArg, [this[i], i, this]);
8365
8366 if (!ret) {
8367 return false;
8368 }
8369 }
8370
8371 return true;
8372 },
8373 same: function same(collection) {
8374 // cheap collection ref check
8375 if (this === collection) {
8376 return true;
8377 }
8378
8379 collection = this.cy().collection(collection);
8380 var thisLength = this.length;
8381 var collectionLength = collection.length; // cheap length check
8382
8383 if (thisLength !== collectionLength) {
8384 return false;
8385 } // cheap element ref check
8386
8387
8388 if (thisLength === 1) {
8389 return this[0] === collection[0];
8390 }
8391
8392 return this.every(function (ele) {
8393 return collection.hasElementWithId(ele.id());
8394 });
8395 },
8396 anySame: function anySame(collection) {
8397 collection = this.cy().collection(collection);
8398 return this.some(function (ele) {
8399 return collection.hasElementWithId(ele.id());
8400 });
8401 },
8402 allAreNeighbors: function allAreNeighbors(collection) {
8403 collection = this.cy().collection(collection);
8404 var nhood = this.neighborhood();
8405 return collection.every(function (ele) {
8406 return nhood.hasElementWithId(ele.id());
8407 });
8408 },
8409 contains: function contains(collection) {
8410 collection = this.cy().collection(collection);
8411 var self = this;
8412 return collection.every(function (ele) {
8413 return self.hasElementWithId(ele.id());
8414 });
8415 }
8416};
8417elesfn$g.allAreNeighbours = elesfn$g.allAreNeighbors;
8418elesfn$g.has = elesfn$g.contains;
8419elesfn$g.equal = elesfn$g.equals = elesfn$g.same;
8420
8421var cache = function cache(fn, name) {
8422 return function traversalCache(arg1, arg2, arg3, arg4) {
8423 var selectorOrEles = arg1;
8424 var eles = this;
8425 var key;
8426
8427 if (selectorOrEles == null) {
8428 key = '';
8429 } else if (elementOrCollection(selectorOrEles) && selectorOrEles.length === 1) {
8430 key = selectorOrEles.id();
8431 }
8432
8433 if (eles.length === 1 && key) {
8434 var _p = eles[0]._private;
8435 var tch = _p.traversalCache = _p.traversalCache || {};
8436 var ch = tch[name] = tch[name] || [];
8437 var hash = hashString(key);
8438 var cacheHit = ch[hash];
8439
8440 if (cacheHit) {
8441 return cacheHit;
8442 } else {
8443 return ch[hash] = fn.call(eles, arg1, arg2, arg3, arg4);
8444 }
8445 } else {
8446 return fn.call(eles, arg1, arg2, arg3, arg4);
8447 }
8448 };
8449};
8450
8451var elesfn$f = {
8452 parent: function parent(selector) {
8453 var parents = []; // optimisation for single ele call
8454
8455 if (this.length === 1) {
8456 var parent = this[0]._private.parent;
8457
8458 if (parent) {
8459 return parent;
8460 }
8461 }
8462
8463 for (var i = 0; i < this.length; i++) {
8464 var ele = this[i];
8465 var _parent = ele._private.parent;
8466
8467 if (_parent) {
8468 parents.push(_parent);
8469 }
8470 }
8471
8472 return this.spawn(parents, true).filter(selector);
8473 },
8474 parents: function parents(selector) {
8475 var parents = [];
8476 var eles = this.parent();
8477
8478 while (eles.nonempty()) {
8479 for (var i = 0; i < eles.length; i++) {
8480 var ele = eles[i];
8481 parents.push(ele);
8482 }
8483
8484 eles = eles.parent();
8485 }
8486
8487 return this.spawn(parents, true).filter(selector);
8488 },
8489 commonAncestors: function commonAncestors(selector) {
8490 var ancestors;
8491
8492 for (var i = 0; i < this.length; i++) {
8493 var ele = this[i];
8494 var parents = ele.parents();
8495 ancestors = ancestors || parents;
8496 ancestors = ancestors.intersect(parents); // current list must be common with current ele parents set
8497 }
8498
8499 return ancestors.filter(selector);
8500 },
8501 orphans: function orphans(selector) {
8502 return this.stdFilter(function (ele) {
8503 return ele.isOrphan();
8504 }).filter(selector);
8505 },
8506 nonorphans: function nonorphans(selector) {
8507 return this.stdFilter(function (ele) {
8508 return ele.isChild();
8509 }).filter(selector);
8510 },
8511 children: cache(function (selector) {
8512 var children = [];
8513
8514 for (var i = 0; i < this.length; i++) {
8515 var ele = this[i];
8516 var eleChildren = ele._private.children;
8517
8518 for (var j = 0; j < eleChildren.length; j++) {
8519 children.push(eleChildren[j]);
8520 }
8521 }
8522
8523 return this.spawn(children, true).filter(selector);
8524 }, 'children'),
8525 siblings: function siblings(selector) {
8526 return this.parent().children().not(this).filter(selector);
8527 },
8528 isParent: function isParent() {
8529 var ele = this[0];
8530
8531 if (ele) {
8532 return ele.isNode() && ele._private.children.length !== 0;
8533 }
8534 },
8535 isChildless: function isChildless() {
8536 var ele = this[0];
8537
8538 if (ele) {
8539 return ele.isNode() && ele._private.children.length === 0;
8540 }
8541 },
8542 isChild: function isChild() {
8543 var ele = this[0];
8544
8545 if (ele) {
8546 return ele.isNode() && ele._private.parent != null;
8547 }
8548 },
8549 isOrphan: function isOrphan() {
8550 var ele = this[0];
8551
8552 if (ele) {
8553 return ele.isNode() && ele._private.parent == null;
8554 }
8555 },
8556 descendants: function descendants(selector) {
8557 var elements = [];
8558
8559 function add(eles) {
8560 for (var i = 0; i < eles.length; i++) {
8561 var ele = eles[i];
8562 elements.push(ele);
8563
8564 if (ele.children().nonempty()) {
8565 add(ele.children());
8566 }
8567 }
8568 }
8569
8570 add(this.children());
8571 return this.spawn(elements, true).filter(selector);
8572 }
8573};
8574
8575function forEachCompound(eles, fn, includeSelf, recursiveStep) {
8576 var q = [];
8577 var did = new Set$1();
8578 var cy = eles.cy();
8579 var hasCompounds = cy.hasCompoundNodes();
8580
8581 for (var i = 0; i < eles.length; i++) {
8582 var ele = eles[i];
8583
8584 if (includeSelf) {
8585 q.push(ele);
8586 } else if (hasCompounds) {
8587 recursiveStep(q, did, ele);
8588 }
8589 }
8590
8591 while (q.length > 0) {
8592 var _ele = q.shift();
8593
8594 fn(_ele);
8595 did.add(_ele.id());
8596
8597 if (hasCompounds) {
8598 recursiveStep(q, did, _ele);
8599 }
8600 }
8601
8602 return eles;
8603}
8604
8605function addChildren(q, did, ele) {
8606 if (ele.isParent()) {
8607 var children = ele._private.children;
8608
8609 for (var i = 0; i < children.length; i++) {
8610 var child = children[i];
8611
8612 if (!did.has(child.id())) {
8613 q.push(child);
8614 }
8615 }
8616 }
8617} // very efficient version of eles.add( eles.descendants() ).forEach()
8618// for internal use
8619
8620
8621elesfn$f.forEachDown = function (fn) {
8622 var includeSelf = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
8623 return forEachCompound(this, fn, includeSelf, addChildren);
8624};
8625
8626function addParent(q, did, ele) {
8627 if (ele.isChild()) {
8628 var parent = ele._private.parent;
8629
8630 if (!did.has(parent.id())) {
8631 q.push(parent);
8632 }
8633 }
8634}
8635
8636elesfn$f.forEachUp = function (fn) {
8637 var includeSelf = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
8638 return forEachCompound(this, fn, includeSelf, addParent);
8639};
8640
8641function addParentAndChildren(q, did, ele) {
8642 addParent(q, did, ele);
8643 addChildren(q, did, ele);
8644}
8645
8646elesfn$f.forEachUpAndDown = function (fn) {
8647 var includeSelf = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
8648 return forEachCompound(this, fn, includeSelf, addParentAndChildren);
8649}; // aliases
8650
8651
8652elesfn$f.ancestors = elesfn$f.parents;
8653
8654var fn$5, elesfn$e;
8655fn$5 = elesfn$e = {
8656 data: define.data({
8657 field: 'data',
8658 bindingEvent: 'data',
8659 allowBinding: true,
8660 allowSetting: true,
8661 settingEvent: 'data',
8662 settingTriggersEvent: true,
8663 triggerFnName: 'trigger',
8664 allowGetting: true,
8665 immutableKeys: {
8666 'id': true,
8667 'source': true,
8668 'target': true,
8669 'parent': true
8670 },
8671 updateStyle: true
8672 }),
8673 removeData: define.removeData({
8674 field: 'data',
8675 event: 'data',
8676 triggerFnName: 'trigger',
8677 triggerEvent: true,
8678 immutableKeys: {
8679 'id': true,
8680 'source': true,
8681 'target': true,
8682 'parent': true
8683 },
8684 updateStyle: true
8685 }),
8686 scratch: define.data({
8687 field: 'scratch',
8688 bindingEvent: 'scratch',
8689 allowBinding: true,
8690 allowSetting: true,
8691 settingEvent: 'scratch',
8692 settingTriggersEvent: true,
8693 triggerFnName: 'trigger',
8694 allowGetting: true,
8695 updateStyle: true
8696 }),
8697 removeScratch: define.removeData({
8698 field: 'scratch',
8699 event: 'scratch',
8700 triggerFnName: 'trigger',
8701 triggerEvent: true,
8702 updateStyle: true
8703 }),
8704 rscratch: define.data({
8705 field: 'rscratch',
8706 allowBinding: false,
8707 allowSetting: true,
8708 settingTriggersEvent: false,
8709 allowGetting: true
8710 }),
8711 removeRscratch: define.removeData({
8712 field: 'rscratch',
8713 triggerEvent: false
8714 }),
8715 id: function id() {
8716 var ele = this[0];
8717
8718 if (ele) {
8719 return ele._private.data.id;
8720 }
8721 }
8722}; // aliases
8723
8724fn$5.attr = fn$5.data;
8725fn$5.removeAttr = fn$5.removeData;
8726var data = elesfn$e;
8727
8728var elesfn$d = {};
8729
8730function defineDegreeFunction(callback) {
8731 return function (includeLoops) {
8732 var self = this;
8733
8734 if (includeLoops === undefined) {
8735 includeLoops = true;
8736 }
8737
8738 if (self.length === 0) {
8739 return;
8740 }
8741
8742 if (self.isNode() && !self.removed()) {
8743 var degree = 0;
8744 var node = self[0];
8745 var connectedEdges = node._private.edges;
8746
8747 for (var i = 0; i < connectedEdges.length; i++) {
8748 var edge = connectedEdges[i];
8749
8750 if (!includeLoops && edge.isLoop()) {
8751 continue;
8752 }
8753
8754 degree += callback(node, edge);
8755 }
8756
8757 return degree;
8758 } else {
8759 return;
8760 }
8761 };
8762}
8763
8764extend(elesfn$d, {
8765 degree: defineDegreeFunction(function (node, edge) {
8766 if (edge.source().same(edge.target())) {
8767 return 2;
8768 } else {
8769 return 1;
8770 }
8771 }),
8772 indegree: defineDegreeFunction(function (node, edge) {
8773 if (edge.target().same(node)) {
8774 return 1;
8775 } else {
8776 return 0;
8777 }
8778 }),
8779 outdegree: defineDegreeFunction(function (node, edge) {
8780 if (edge.source().same(node)) {
8781 return 1;
8782 } else {
8783 return 0;
8784 }
8785 })
8786});
8787
8788function defineDegreeBoundsFunction(degreeFn, callback) {
8789 return function (includeLoops) {
8790 var ret;
8791 var nodes = this.nodes();
8792
8793 for (var i = 0; i < nodes.length; i++) {
8794 var ele = nodes[i];
8795 var degree = ele[degreeFn](includeLoops);
8796
8797 if (degree !== undefined && (ret === undefined || callback(degree, ret))) {
8798 ret = degree;
8799 }
8800 }
8801
8802 return ret;
8803 };
8804}
8805
8806extend(elesfn$d, {
8807 minDegree: defineDegreeBoundsFunction('degree', function (degree, min) {
8808 return degree < min;
8809 }),
8810 maxDegree: defineDegreeBoundsFunction('degree', function (degree, max) {
8811 return degree > max;
8812 }),
8813 minIndegree: defineDegreeBoundsFunction('indegree', function (degree, min) {
8814 return degree < min;
8815 }),
8816 maxIndegree: defineDegreeBoundsFunction('indegree', function (degree, max) {
8817 return degree > max;
8818 }),
8819 minOutdegree: defineDegreeBoundsFunction('outdegree', function (degree, min) {
8820 return degree < min;
8821 }),
8822 maxOutdegree: defineDegreeBoundsFunction('outdegree', function (degree, max) {
8823 return degree > max;
8824 })
8825});
8826extend(elesfn$d, {
8827 totalDegree: function totalDegree(includeLoops) {
8828 var total = 0;
8829 var nodes = this.nodes();
8830
8831 for (var i = 0; i < nodes.length; i++) {
8832 total += nodes[i].degree(includeLoops);
8833 }
8834
8835 return total;
8836 }
8837});
8838
8839var fn$4, elesfn$c;
8840
8841var beforePositionSet = function beforePositionSet(eles, newPos, silent) {
8842 for (var i = 0; i < eles.length; i++) {
8843 var ele = eles[i];
8844
8845 if (!ele.locked()) {
8846 var oldPos = ele._private.position;
8847 var delta = {
8848 x: newPos.x != null ? newPos.x - oldPos.x : 0,
8849 y: newPos.y != null ? newPos.y - oldPos.y : 0
8850 };
8851
8852 if (ele.isParent() && !(delta.x === 0 && delta.y === 0)) {
8853 ele.children().shift(delta, silent);
8854 }
8855
8856 ele.dirtyBoundingBoxCache();
8857 }
8858 }
8859};
8860
8861var positionDef = {
8862 field: 'position',
8863 bindingEvent: 'position',
8864 allowBinding: true,
8865 allowSetting: true,
8866 settingEvent: 'position',
8867 settingTriggersEvent: true,
8868 triggerFnName: 'emitAndNotify',
8869 allowGetting: true,
8870 validKeys: ['x', 'y'],
8871 beforeGet: function beforeGet(ele) {
8872 ele.updateCompoundBounds();
8873 },
8874 beforeSet: function beforeSet(eles, newPos) {
8875 beforePositionSet(eles, newPos, false);
8876 },
8877 onSet: function onSet(eles) {
8878 eles.dirtyCompoundBoundsCache();
8879 },
8880 canSet: function canSet(ele) {
8881 return !ele.locked();
8882 }
8883};
8884fn$4 = elesfn$c = {
8885 position: define.data(positionDef),
8886 // position but no notification to renderer
8887 silentPosition: define.data(extend({}, positionDef, {
8888 allowBinding: false,
8889 allowSetting: true,
8890 settingTriggersEvent: false,
8891 allowGetting: false,
8892 beforeSet: function beforeSet(eles, newPos) {
8893 beforePositionSet(eles, newPos, true);
8894 },
8895 onSet: function onSet(eles) {
8896 eles.dirtyCompoundBoundsCache();
8897 }
8898 })),
8899 positions: function positions(pos, silent) {
8900 if (plainObject(pos)) {
8901 if (silent) {
8902 this.silentPosition(pos);
8903 } else {
8904 this.position(pos);
8905 }
8906 } else if (fn$6(pos)) {
8907 var _fn = pos;
8908 var cy = this.cy();
8909 cy.startBatch();
8910
8911 for (var i = 0; i < this.length; i++) {
8912 var ele = this[i];
8913
8914 var _pos = void 0;
8915
8916 if (_pos = _fn(ele, i)) {
8917 if (silent) {
8918 ele.silentPosition(_pos);
8919 } else {
8920 ele.position(_pos);
8921 }
8922 }
8923 }
8924
8925 cy.endBatch();
8926 }
8927
8928 return this; // chaining
8929 },
8930 silentPositions: function silentPositions(pos) {
8931 return this.positions(pos, true);
8932 },
8933 shift: function shift(dim, val, silent) {
8934 var delta;
8935
8936 if (plainObject(dim)) {
8937 delta = {
8938 x: number$1(dim.x) ? dim.x : 0,
8939 y: number$1(dim.y) ? dim.y : 0
8940 };
8941 silent = val;
8942 } else if (string(dim) && number$1(val)) {
8943 delta = {
8944 x: 0,
8945 y: 0
8946 };
8947 delta[dim] = val;
8948 }
8949
8950 if (delta != null) {
8951 var cy = this.cy();
8952 cy.startBatch();
8953
8954 for (var i = 0; i < this.length; i++) {
8955 var ele = this[i]; // exclude any node that is a descendant of the calling collection
8956
8957 if (cy.hasCompoundNodes() && ele.isChild() && ele.ancestors().anySame(this)) {
8958 continue;
8959 }
8960
8961 var pos = ele.position();
8962 var newPos = {
8963 x: pos.x + delta.x,
8964 y: pos.y + delta.y
8965 };
8966
8967 if (silent) {
8968 ele.silentPosition(newPos);
8969 } else {
8970 ele.position(newPos);
8971 }
8972 }
8973
8974 cy.endBatch();
8975 }
8976
8977 return this;
8978 },
8979 silentShift: function silentShift(dim, val) {
8980 if (plainObject(dim)) {
8981 this.shift(dim, true);
8982 } else if (string(dim) && number$1(val)) {
8983 this.shift(dim, val, true);
8984 }
8985
8986 return this;
8987 },
8988 // get/set the rendered (i.e. on screen) positon of the element
8989 renderedPosition: function renderedPosition(dim, val) {
8990 var ele = this[0];
8991 var cy = this.cy();
8992 var zoom = cy.zoom();
8993 var pan = cy.pan();
8994 var rpos = plainObject(dim) ? dim : undefined;
8995 var setting = rpos !== undefined || val !== undefined && string(dim);
8996
8997 if (ele && ele.isNode()) {
8998 // must have an element and must be a node to return position
8999 if (setting) {
9000 for (var i = 0; i < this.length; i++) {
9001 var _ele = this[i];
9002
9003 if (val !== undefined) {
9004 // set one dimension
9005 _ele.position(dim, (val - pan[dim]) / zoom);
9006 } else if (rpos !== undefined) {
9007 // set whole position
9008 _ele.position(renderedToModelPosition(rpos, zoom, pan));
9009 }
9010 }
9011 } else {
9012 // getting
9013 var pos = ele.position();
9014 rpos = modelToRenderedPosition(pos, zoom, pan);
9015
9016 if (dim === undefined) {
9017 // then return the whole rendered position
9018 return rpos;
9019 } else {
9020 // then return the specified dimension
9021 return rpos[dim];
9022 }
9023 }
9024 } else if (!setting) {
9025 return undefined; // for empty collection case
9026 }
9027
9028 return this; // chaining
9029 },
9030 // get/set the position relative to the parent
9031 relativePosition: function relativePosition(dim, val) {
9032 var ele = this[0];
9033 var cy = this.cy();
9034 var ppos = plainObject(dim) ? dim : undefined;
9035 var setting = ppos !== undefined || val !== undefined && string(dim);
9036 var hasCompoundNodes = cy.hasCompoundNodes();
9037
9038 if (ele && ele.isNode()) {
9039 // must have an element and must be a node to return position
9040 if (setting) {
9041 for (var i = 0; i < this.length; i++) {
9042 var _ele2 = this[i];
9043 var parent = hasCompoundNodes ? _ele2.parent() : null;
9044 var hasParent = parent && parent.length > 0;
9045 var relativeToParent = hasParent;
9046
9047 if (hasParent) {
9048 parent = parent[0];
9049 }
9050
9051 var origin = relativeToParent ? parent.position() : {
9052 x: 0,
9053 y: 0
9054 };
9055
9056 if (val !== undefined) {
9057 // set one dimension
9058 _ele2.position(dim, val + origin[dim]);
9059 } else if (ppos !== undefined) {
9060 // set whole position
9061 _ele2.position({
9062 x: ppos.x + origin.x,
9063 y: ppos.y + origin.y
9064 });
9065 }
9066 }
9067 } else {
9068 // getting
9069 var pos = ele.position();
9070
9071 var _parent = hasCompoundNodes ? ele.parent() : null;
9072
9073 var _hasParent = _parent && _parent.length > 0;
9074
9075 var _relativeToParent = _hasParent;
9076
9077 if (_hasParent) {
9078 _parent = _parent[0];
9079 }
9080
9081 var _origin = _relativeToParent ? _parent.position() : {
9082 x: 0,
9083 y: 0
9084 };
9085
9086 ppos = {
9087 x: pos.x - _origin.x,
9088 y: pos.y - _origin.y
9089 };
9090
9091 if (dim === undefined) {
9092 // then return the whole rendered position
9093 return ppos;
9094 } else {
9095 // then return the specified dimension
9096 return ppos[dim];
9097 }
9098 }
9099 } else if (!setting) {
9100 return undefined; // for empty collection case
9101 }
9102
9103 return this; // chaining
9104 }
9105}; // aliases
9106
9107fn$4.modelPosition = fn$4.point = fn$4.position;
9108fn$4.modelPositions = fn$4.points = fn$4.positions;
9109fn$4.renderedPoint = fn$4.renderedPosition;
9110fn$4.relativePoint = fn$4.relativePosition;
9111var position = elesfn$c;
9112
9113var fn$3, elesfn$b;
9114fn$3 = elesfn$b = {};
9115
9116elesfn$b.renderedBoundingBox = function (options) {
9117 var bb = this.boundingBox(options);
9118 var cy = this.cy();
9119 var zoom = cy.zoom();
9120 var pan = cy.pan();
9121 var x1 = bb.x1 * zoom + pan.x;
9122 var x2 = bb.x2 * zoom + pan.x;
9123 var y1 = bb.y1 * zoom + pan.y;
9124 var y2 = bb.y2 * zoom + pan.y;
9125 return {
9126 x1: x1,
9127 x2: x2,
9128 y1: y1,
9129 y2: y2,
9130 w: x2 - x1,
9131 h: y2 - y1
9132 };
9133};
9134
9135elesfn$b.dirtyCompoundBoundsCache = function () {
9136 var silent = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
9137 var cy = this.cy();
9138
9139 if (!cy.styleEnabled() || !cy.hasCompoundNodes()) {
9140 return this;
9141 }
9142
9143 this.forEachUp(function (ele) {
9144 if (ele.isParent()) {
9145 var _p = ele._private;
9146 _p.compoundBoundsClean = false;
9147 _p.bbCache = null;
9148
9149 if (!silent) {
9150 ele.emitAndNotify('bounds');
9151 }
9152 }
9153 });
9154 return this;
9155};
9156
9157elesfn$b.updateCompoundBounds = function () {
9158 var force = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
9159 var cy = this.cy(); // not possible to do on non-compound graphs or with the style disabled
9160
9161 if (!cy.styleEnabled() || !cy.hasCompoundNodes()) {
9162 return this;
9163 } // save cycles when batching -- but bounds will be stale (or not exist yet)
9164
9165
9166 if (!force && cy.batching()) {
9167 return this;
9168 }
9169
9170 function update(parent) {
9171 if (!parent.isParent()) {
9172 return;
9173 }
9174
9175 var _p = parent._private;
9176 var children = parent.children();
9177 var includeLabels = parent.pstyle('compound-sizing-wrt-labels').value === 'include';
9178 var min = {
9179 width: {
9180 val: parent.pstyle('min-width').pfValue,
9181 left: parent.pstyle('min-width-bias-left'),
9182 right: parent.pstyle('min-width-bias-right')
9183 },
9184 height: {
9185 val: parent.pstyle('min-height').pfValue,
9186 top: parent.pstyle('min-height-bias-top'),
9187 bottom: parent.pstyle('min-height-bias-bottom')
9188 }
9189 };
9190 var bb = children.boundingBox({
9191 includeLabels: includeLabels,
9192 includeOverlays: false,
9193 // updating the compound bounds happens outside of the regular
9194 // cache cycle (i.e. before fired events)
9195 useCache: false
9196 });
9197 var pos = _p.position; // if children take up zero area then keep position and fall back on stylesheet w/h
9198
9199 if (bb.w === 0 || bb.h === 0) {
9200 bb = {
9201 w: parent.pstyle('width').pfValue,
9202 h: parent.pstyle('height').pfValue
9203 };
9204 bb.x1 = pos.x - bb.w / 2;
9205 bb.x2 = pos.x + bb.w / 2;
9206 bb.y1 = pos.y - bb.h / 2;
9207 bb.y2 = pos.y + bb.h / 2;
9208 }
9209
9210 function computeBiasValues(propDiff, propBias, propBiasComplement) {
9211 var biasDiff = 0;
9212 var biasComplementDiff = 0;
9213 var biasTotal = propBias + propBiasComplement;
9214
9215 if (propDiff > 0 && biasTotal > 0) {
9216 biasDiff = propBias / biasTotal * propDiff;
9217 biasComplementDiff = propBiasComplement / biasTotal * propDiff;
9218 }
9219
9220 return {
9221 biasDiff: biasDiff,
9222 biasComplementDiff: biasComplementDiff
9223 };
9224 }
9225
9226 function computePaddingValues(width, height, paddingObject, relativeTo) {
9227 // Assuming percentage is number from 0 to 1
9228 if (paddingObject.units === '%') {
9229 switch (relativeTo) {
9230 case 'width':
9231 return width > 0 ? paddingObject.pfValue * width : 0;
9232
9233 case 'height':
9234 return height > 0 ? paddingObject.pfValue * height : 0;
9235
9236 case 'average':
9237 return width > 0 && height > 0 ? paddingObject.pfValue * (width + height) / 2 : 0;
9238
9239 case 'min':
9240 return width > 0 && height > 0 ? width > height ? paddingObject.pfValue * height : paddingObject.pfValue * width : 0;
9241
9242 case 'max':
9243 return width > 0 && height > 0 ? width > height ? paddingObject.pfValue * width : paddingObject.pfValue * height : 0;
9244
9245 default:
9246 return 0;
9247 }
9248 } else if (paddingObject.units === 'px') {
9249 return paddingObject.pfValue;
9250 } else {
9251 return 0;
9252 }
9253 }
9254
9255 var leftVal = min.width.left.value;
9256
9257 if (min.width.left.units === 'px' && min.width.val > 0) {
9258 leftVal = leftVal * 100 / min.width.val;
9259 }
9260
9261 var rightVal = min.width.right.value;
9262
9263 if (min.width.right.units === 'px' && min.width.val > 0) {
9264 rightVal = rightVal * 100 / min.width.val;
9265 }
9266
9267 var topVal = min.height.top.value;
9268
9269 if (min.height.top.units === 'px' && min.height.val > 0) {
9270 topVal = topVal * 100 / min.height.val;
9271 }
9272
9273 var bottomVal = min.height.bottom.value;
9274
9275 if (min.height.bottom.units === 'px' && min.height.val > 0) {
9276 bottomVal = bottomVal * 100 / min.height.val;
9277 }
9278
9279 var widthBiasDiffs = computeBiasValues(min.width.val - bb.w, leftVal, rightVal);
9280 var diffLeft = widthBiasDiffs.biasDiff;
9281 var diffRight = widthBiasDiffs.biasComplementDiff;
9282 var heightBiasDiffs = computeBiasValues(min.height.val - bb.h, topVal, bottomVal);
9283 var diffTop = heightBiasDiffs.biasDiff;
9284 var diffBottom = heightBiasDiffs.biasComplementDiff;
9285 _p.autoPadding = computePaddingValues(bb.w, bb.h, parent.pstyle('padding'), parent.pstyle('padding-relative-to').value);
9286 _p.autoWidth = Math.max(bb.w, min.width.val);
9287 pos.x = (-diffLeft + bb.x1 + bb.x2 + diffRight) / 2;
9288 _p.autoHeight = Math.max(bb.h, min.height.val);
9289 pos.y = (-diffTop + bb.y1 + bb.y2 + diffBottom) / 2;
9290 }
9291
9292 for (var i = 0; i < this.length; i++) {
9293 var ele = this[i];
9294 var _p = ele._private;
9295
9296 if (!_p.compoundBoundsClean || force) {
9297 update(ele);
9298
9299 if (!cy.batching()) {
9300 _p.compoundBoundsClean = true;
9301 }
9302 }
9303 }
9304
9305 return this;
9306};
9307
9308var noninf = function noninf(x) {
9309 if (x === Infinity || x === -Infinity) {
9310 return 0;
9311 }
9312
9313 return x;
9314};
9315
9316var updateBounds = function updateBounds(b, x1, y1, x2, y2) {
9317 // don't update with zero area boxes
9318 if (x2 - x1 === 0 || y2 - y1 === 0) {
9319 return;
9320 } // don't update with null dim
9321
9322
9323 if (x1 == null || y1 == null || x2 == null || y2 == null) {
9324 return;
9325 }
9326
9327 b.x1 = x1 < b.x1 ? x1 : b.x1;
9328 b.x2 = x2 > b.x2 ? x2 : b.x2;
9329 b.y1 = y1 < b.y1 ? y1 : b.y1;
9330 b.y2 = y2 > b.y2 ? y2 : b.y2;
9331 b.w = b.x2 - b.x1;
9332 b.h = b.y2 - b.y1;
9333};
9334
9335var updateBoundsFromBox = function updateBoundsFromBox(b, b2) {
9336 if (b2 == null) {
9337 return b;
9338 }
9339
9340 return updateBounds(b, b2.x1, b2.y1, b2.x2, b2.y2);
9341};
9342
9343var prefixedProperty = function prefixedProperty(obj, field, prefix) {
9344 return getPrefixedProperty(obj, field, prefix);
9345};
9346
9347var updateBoundsFromArrow = function updateBoundsFromArrow(bounds, ele, prefix) {
9348 if (ele.cy().headless()) {
9349 return;
9350 }
9351
9352 var _p = ele._private;
9353 var rstyle = _p.rstyle;
9354 var halfArW = rstyle.arrowWidth / 2;
9355 var arrowType = ele.pstyle(prefix + '-arrow-shape').value;
9356 var x;
9357 var y;
9358
9359 if (arrowType !== 'none') {
9360 if (prefix === 'source') {
9361 x = rstyle.srcX;
9362 y = rstyle.srcY;
9363 } else if (prefix === 'target') {
9364 x = rstyle.tgtX;
9365 y = rstyle.tgtY;
9366 } else {
9367 x = rstyle.midX;
9368 y = rstyle.midY;
9369 } // always store the individual arrow bounds
9370
9371
9372 var bbs = _p.arrowBounds = _p.arrowBounds || {};
9373 var bb = bbs[prefix] = bbs[prefix] || {};
9374 bb.x1 = x - halfArW;
9375 bb.y1 = y - halfArW;
9376 bb.x2 = x + halfArW;
9377 bb.y2 = y + halfArW;
9378 bb.w = bb.x2 - bb.x1;
9379 bb.h = bb.y2 - bb.y1;
9380 expandBoundingBox(bb, 1);
9381 updateBounds(bounds, bb.x1, bb.y1, bb.x2, bb.y2);
9382 }
9383};
9384
9385var updateBoundsFromLabel = function updateBoundsFromLabel(bounds, ele, prefix) {
9386 if (ele.cy().headless()) {
9387 return;
9388 }
9389
9390 var prefixDash;
9391
9392 if (prefix) {
9393 prefixDash = prefix + '-';
9394 } else {
9395 prefixDash = '';
9396 }
9397
9398 var _p = ele._private;
9399 var rstyle = _p.rstyle;
9400 var label = ele.pstyle(prefixDash + 'label').strValue;
9401
9402 if (label) {
9403 var halign = ele.pstyle('text-halign');
9404 var valign = ele.pstyle('text-valign');
9405 var labelWidth = prefixedProperty(rstyle, 'labelWidth', prefix);
9406 var labelHeight = prefixedProperty(rstyle, 'labelHeight', prefix);
9407 var labelX = prefixedProperty(rstyle, 'labelX', prefix);
9408 var labelY = prefixedProperty(rstyle, 'labelY', prefix);
9409 var marginX = ele.pstyle(prefixDash + 'text-margin-x').pfValue;
9410 var marginY = ele.pstyle(prefixDash + 'text-margin-y').pfValue;
9411 var isEdge = ele.isEdge();
9412 var rotation = ele.pstyle(prefixDash + 'text-rotation');
9413 var outlineWidth = ele.pstyle('text-outline-width').pfValue;
9414 var borderWidth = ele.pstyle('text-border-width').pfValue;
9415 var halfBorderWidth = borderWidth / 2;
9416 var padding = ele.pstyle('text-background-padding').pfValue;
9417 var marginOfError = 2; // expand to work around browser dimension inaccuracies
9418
9419 var lh = labelHeight;
9420 var lw = labelWidth;
9421 var lw_2 = lw / 2;
9422 var lh_2 = lh / 2;
9423 var lx1, lx2, ly1, ly2;
9424
9425 if (isEdge) {
9426 lx1 = labelX - lw_2;
9427 lx2 = labelX + lw_2;
9428 ly1 = labelY - lh_2;
9429 ly2 = labelY + lh_2;
9430 } else {
9431 switch (halign.value) {
9432 case 'left':
9433 lx1 = labelX - lw;
9434 lx2 = labelX;
9435 break;
9436
9437 case 'center':
9438 lx1 = labelX - lw_2;
9439 lx2 = labelX + lw_2;
9440 break;
9441
9442 case 'right':
9443 lx1 = labelX;
9444 lx2 = labelX + lw;
9445 break;
9446 }
9447
9448 switch (valign.value) {
9449 case 'top':
9450 ly1 = labelY - lh;
9451 ly2 = labelY;
9452 break;
9453
9454 case 'center':
9455 ly1 = labelY - lh_2;
9456 ly2 = labelY + lh_2;
9457 break;
9458
9459 case 'bottom':
9460 ly1 = labelY;
9461 ly2 = labelY + lh;
9462 break;
9463 }
9464 } // shift by margin and expand by outline and border
9465
9466
9467 lx1 += marginX - Math.max(outlineWidth, halfBorderWidth) - padding - marginOfError;
9468 lx2 += marginX + Math.max(outlineWidth, halfBorderWidth) + padding + marginOfError;
9469 ly1 += marginY - Math.max(outlineWidth, halfBorderWidth) - padding - marginOfError;
9470 ly2 += marginY + Math.max(outlineWidth, halfBorderWidth) + padding + marginOfError; // always store the unrotated label bounds separately
9471
9472 var bbPrefix = prefix || 'main';
9473 var bbs = _p.labelBounds;
9474 var bb = bbs[bbPrefix] = bbs[bbPrefix] || {};
9475 bb.x1 = lx1;
9476 bb.y1 = ly1;
9477 bb.x2 = lx2;
9478 bb.y2 = ly2;
9479 bb.w = lx2 - lx1;
9480 bb.h = ly2 - ly1;
9481 var isAutorotate = isEdge && rotation.strValue === 'autorotate';
9482 var isPfValue = rotation.pfValue != null && rotation.pfValue !== 0;
9483
9484 if (isAutorotate || isPfValue) {
9485 var theta = isAutorotate ? prefixedProperty(_p.rstyle, 'labelAngle', prefix) : rotation.pfValue;
9486 var cos = Math.cos(theta);
9487 var sin = Math.sin(theta); // rotation point (default value for center-center)
9488
9489 var xo = (lx1 + lx2) / 2;
9490 var yo = (ly1 + ly2) / 2;
9491
9492 if (!isEdge) {
9493 switch (halign.value) {
9494 case 'left':
9495 xo = lx2;
9496 break;
9497
9498 case 'right':
9499 xo = lx1;
9500 break;
9501 }
9502
9503 switch (valign.value) {
9504 case 'top':
9505 yo = ly2;
9506 break;
9507
9508 case 'bottom':
9509 yo = ly1;
9510 break;
9511 }
9512 }
9513
9514 var rotate = function rotate(x, y) {
9515 x = x - xo;
9516 y = y - yo;
9517 return {
9518 x: x * cos - y * sin + xo,
9519 y: x * sin + y * cos + yo
9520 };
9521 };
9522
9523 var px1y1 = rotate(lx1, ly1);
9524 var px1y2 = rotate(lx1, ly2);
9525 var px2y1 = rotate(lx2, ly1);
9526 var px2y2 = rotate(lx2, ly2);
9527 lx1 = Math.min(px1y1.x, px1y2.x, px2y1.x, px2y2.x);
9528 lx2 = Math.max(px1y1.x, px1y2.x, px2y1.x, px2y2.x);
9529 ly1 = Math.min(px1y1.y, px1y2.y, px2y1.y, px2y2.y);
9530 ly2 = Math.max(px1y1.y, px1y2.y, px2y1.y, px2y2.y);
9531 }
9532
9533 var bbPrefixRot = bbPrefix + 'Rot';
9534 var bbRot = bbs[bbPrefixRot] = bbs[bbPrefixRot] || {};
9535 bbRot.x1 = lx1;
9536 bbRot.y1 = ly1;
9537 bbRot.x2 = lx2;
9538 bbRot.y2 = ly2;
9539 bbRot.w = lx2 - lx1;
9540 bbRot.h = ly2 - ly1;
9541 updateBounds(bounds, lx1, ly1, lx2, ly2);
9542 updateBounds(_p.labelBounds.all, lx1, ly1, lx2, ly2);
9543 }
9544
9545 return bounds;
9546}; // get the bounding box of the elements (in raw model position)
9547
9548
9549var boundingBoxImpl = function boundingBoxImpl(ele, options) {
9550 var cy = ele._private.cy;
9551 var styleEnabled = cy.styleEnabled();
9552 var headless = cy.headless();
9553 var bounds = makeBoundingBox();
9554 var _p = ele._private;
9555 var isNode = ele.isNode();
9556 var isEdge = ele.isEdge();
9557 var ex1, ex2, ey1, ey2; // extrema of body / lines
9558
9559 var x, y; // node pos
9560
9561 var rstyle = _p.rstyle;
9562 var manualExpansion = isNode && styleEnabled ? ele.pstyle('bounds-expansion').pfValue : [0]; // must use `display` prop only, as reading `compound.width()` causes recursion
9563 // (other factors like width values will be considered later in this function anyway)
9564
9565 var isDisplayed = function isDisplayed(ele) {
9566 return ele.pstyle('display').value !== 'none';
9567 };
9568
9569 var displayed = !styleEnabled || isDisplayed(ele) // must take into account connected nodes b/c of implicit edge hiding on display:none node
9570 && (!isEdge || isDisplayed(ele.source()) && isDisplayed(ele.target()));
9571
9572 if (displayed) {
9573 // displayed suffices, since we will find zero area eles anyway
9574 var overlayOpacity = 0;
9575 var overlayPadding = 0;
9576
9577 if (styleEnabled && options.includeOverlays) {
9578 overlayOpacity = ele.pstyle('overlay-opacity').value;
9579
9580 if (overlayOpacity !== 0) {
9581 overlayPadding = ele.pstyle('overlay-padding').value;
9582 }
9583 }
9584
9585 var underlayOpacity = 0;
9586 var underlayPadding = 0;
9587
9588 if (styleEnabled && options.includeUnderlays) {
9589 underlayOpacity = ele.pstyle('underlay-opacity').value;
9590
9591 if (underlayOpacity !== 0) {
9592 underlayPadding = ele.pstyle('underlay-padding').value;
9593 }
9594 }
9595
9596 var padding = Math.max(overlayPadding, underlayPadding);
9597 var w = 0;
9598 var wHalf = 0;
9599
9600 if (styleEnabled) {
9601 w = ele.pstyle('width').pfValue;
9602 wHalf = w / 2;
9603 }
9604
9605 if (isNode && options.includeNodes) {
9606 var pos = ele.position();
9607 x = pos.x;
9608 y = pos.y;
9609
9610 var _w = ele.outerWidth();
9611
9612 var halfW = _w / 2;
9613 var h = ele.outerHeight();
9614 var halfH = h / 2; // handle node dimensions
9615 /////////////////////////
9616
9617 ex1 = x - halfW;
9618 ex2 = x + halfW;
9619 ey1 = y - halfH;
9620 ey2 = y + halfH;
9621 updateBounds(bounds, ex1, ey1, ex2, ey2);
9622 } else if (isEdge && options.includeEdges) {
9623 if (styleEnabled && !headless) {
9624 var curveStyle = ele.pstyle('curve-style').strValue; // handle edge dimensions (rough box estimate)
9625 //////////////////////////////////////////////
9626
9627 ex1 = Math.min(rstyle.srcX, rstyle.midX, rstyle.tgtX);
9628 ex2 = Math.max(rstyle.srcX, rstyle.midX, rstyle.tgtX);
9629 ey1 = Math.min(rstyle.srcY, rstyle.midY, rstyle.tgtY);
9630 ey2 = Math.max(rstyle.srcY, rstyle.midY, rstyle.tgtY); // take into account edge width
9631
9632 ex1 -= wHalf;
9633 ex2 += wHalf;
9634 ey1 -= wHalf;
9635 ey2 += wHalf;
9636 updateBounds(bounds, ex1, ey1, ex2, ey2); // precise edges
9637 ////////////////
9638
9639 if (curveStyle === 'haystack') {
9640 var hpts = rstyle.haystackPts;
9641
9642 if (hpts && hpts.length === 2) {
9643 ex1 = hpts[0].x;
9644 ey1 = hpts[0].y;
9645 ex2 = hpts[1].x;
9646 ey2 = hpts[1].y;
9647
9648 if (ex1 > ex2) {
9649 var temp = ex1;
9650 ex1 = ex2;
9651 ex2 = temp;
9652 }
9653
9654 if (ey1 > ey2) {
9655 var _temp = ey1;
9656 ey1 = ey2;
9657 ey2 = _temp;
9658 }
9659
9660 updateBounds(bounds, ex1 - wHalf, ey1 - wHalf, ex2 + wHalf, ey2 + wHalf);
9661 }
9662 } else if (curveStyle === 'bezier' || curveStyle === 'unbundled-bezier' || curveStyle === 'segments' || curveStyle === 'taxi') {
9663 var pts;
9664
9665 switch (curveStyle) {
9666 case 'bezier':
9667 case 'unbundled-bezier':
9668 pts = rstyle.bezierPts;
9669 break;
9670
9671 case 'segments':
9672 case 'taxi':
9673 pts = rstyle.linePts;
9674 break;
9675 }
9676
9677 if (pts != null) {
9678 for (var j = 0; j < pts.length; j++) {
9679 var pt = pts[j];
9680 ex1 = pt.x - wHalf;
9681 ex2 = pt.x + wHalf;
9682 ey1 = pt.y - wHalf;
9683 ey2 = pt.y + wHalf;
9684 updateBounds(bounds, ex1, ey1, ex2, ey2);
9685 }
9686 }
9687 } // bezier-like or segment-like edge
9688
9689 } else {
9690 // headless or style disabled
9691 // fallback on source and target positions
9692 //////////////////////////////////////////
9693 var n1 = ele.source();
9694 var n1pos = n1.position();
9695 var n2 = ele.target();
9696 var n2pos = n2.position();
9697 ex1 = n1pos.x;
9698 ex2 = n2pos.x;
9699 ey1 = n1pos.y;
9700 ey2 = n2pos.y;
9701
9702 if (ex1 > ex2) {
9703 var _temp2 = ex1;
9704 ex1 = ex2;
9705 ex2 = _temp2;
9706 }
9707
9708 if (ey1 > ey2) {
9709 var _temp3 = ey1;
9710 ey1 = ey2;
9711 ey2 = _temp3;
9712 } // take into account edge width
9713
9714
9715 ex1 -= wHalf;
9716 ex2 += wHalf;
9717 ey1 -= wHalf;
9718 ey2 += wHalf;
9719 updateBounds(bounds, ex1, ey1, ex2, ey2);
9720 } // headless or style disabled
9721
9722 } // edges
9723 // handle edge arrow size
9724 /////////////////////////
9725
9726
9727 if (styleEnabled && options.includeEdges && isEdge) {
9728 updateBoundsFromArrow(bounds, ele, 'mid-source');
9729 updateBoundsFromArrow(bounds, ele, 'mid-target');
9730 updateBoundsFromArrow(bounds, ele, 'source');
9731 updateBoundsFromArrow(bounds, ele, 'target');
9732 } // ghost
9733 ////////
9734
9735
9736 if (styleEnabled) {
9737 var ghost = ele.pstyle('ghost').value === 'yes';
9738
9739 if (ghost) {
9740 var gx = ele.pstyle('ghost-offset-x').pfValue;
9741 var gy = ele.pstyle('ghost-offset-y').pfValue;
9742 updateBounds(bounds, bounds.x1 + gx, bounds.y1 + gy, bounds.x2 + gx, bounds.y2 + gy);
9743 }
9744 } // always store the body bounds separately from the labels
9745
9746
9747 var bbBody = _p.bodyBounds = _p.bodyBounds || {};
9748 assignBoundingBox(bbBody, bounds);
9749 expandBoundingBoxSides(bbBody, manualExpansion);
9750 expandBoundingBox(bbBody, 1); // expand to work around browser dimension inaccuracies
9751 // overlay
9752 //////////
9753
9754 if (styleEnabled) {
9755 ex1 = bounds.x1;
9756 ex2 = bounds.x2;
9757 ey1 = bounds.y1;
9758 ey2 = bounds.y2;
9759 updateBounds(bounds, ex1 - padding, ey1 - padding, ex2 + padding, ey2 + padding);
9760 } // always store the body bounds separately from the labels
9761
9762
9763 var bbOverlay = _p.overlayBounds = _p.overlayBounds || {};
9764 assignBoundingBox(bbOverlay, bounds);
9765 expandBoundingBoxSides(bbOverlay, manualExpansion);
9766 expandBoundingBox(bbOverlay, 1); // expand to work around browser dimension inaccuracies
9767 // handle label dimensions
9768 //////////////////////////
9769
9770 var bbLabels = _p.labelBounds = _p.labelBounds || {};
9771
9772 if (bbLabels.all != null) {
9773 clearBoundingBox(bbLabels.all);
9774 } else {
9775 bbLabels.all = makeBoundingBox();
9776 }
9777
9778 if (styleEnabled && options.includeLabels) {
9779 if (options.includeMainLabels) {
9780 updateBoundsFromLabel(bounds, ele, null);
9781 }
9782
9783 if (isEdge) {
9784 if (options.includeSourceLabels) {
9785 updateBoundsFromLabel(bounds, ele, 'source');
9786 }
9787
9788 if (options.includeTargetLabels) {
9789 updateBoundsFromLabel(bounds, ele, 'target');
9790 }
9791 }
9792 } // style enabled for labels
9793
9794 } // if displayed
9795
9796
9797 bounds.x1 = noninf(bounds.x1);
9798 bounds.y1 = noninf(bounds.y1);
9799 bounds.x2 = noninf(bounds.x2);
9800 bounds.y2 = noninf(bounds.y2);
9801 bounds.w = noninf(bounds.x2 - bounds.x1);
9802 bounds.h = noninf(bounds.y2 - bounds.y1);
9803
9804 if (bounds.w > 0 && bounds.h > 0 && displayed) {
9805 expandBoundingBoxSides(bounds, manualExpansion); // expand bounds by 1 because antialiasing can increase the visual/effective size by 1 on all sides
9806
9807 expandBoundingBox(bounds, 1);
9808 }
9809
9810 return bounds;
9811};
9812
9813var getKey = function getKey(opts) {
9814 var i = 0;
9815
9816 var tf = function tf(val) {
9817 return (val ? 1 : 0) << i++;
9818 };
9819
9820 var key = 0;
9821 key += tf(opts.incudeNodes);
9822 key += tf(opts.includeEdges);
9823 key += tf(opts.includeLabels);
9824 key += tf(opts.includeMainLabels);
9825 key += tf(opts.includeSourceLabels);
9826 key += tf(opts.includeTargetLabels);
9827 key += tf(opts.includeOverlays);
9828 return key;
9829};
9830
9831var getBoundingBoxPosKey = function getBoundingBoxPosKey(ele) {
9832 if (ele.isEdge()) {
9833 var p1 = ele.source().position();
9834 var p2 = ele.target().position();
9835
9836 var r = function r(x) {
9837 return Math.round(x);
9838 };
9839
9840 return hashIntsArray([r(p1.x), r(p1.y), r(p2.x), r(p2.y)]);
9841 } else {
9842 return 0;
9843 }
9844};
9845
9846var cachedBoundingBoxImpl = function cachedBoundingBoxImpl(ele, opts) {
9847 var _p = ele._private;
9848 var bb;
9849 var isEdge = ele.isEdge();
9850 var key = opts == null ? defBbOptsKey : getKey(opts);
9851 var usingDefOpts = key === defBbOptsKey;
9852 var currPosKey = getBoundingBoxPosKey(ele);
9853 var isPosKeySame = _p.bbCachePosKey === currPosKey;
9854 var useCache = opts.useCache && isPosKeySame;
9855
9856 var isDirty = function isDirty(ele) {
9857 return ele._private.bbCache == null || ele._private.styleDirty;
9858 };
9859
9860 var needRecalc = !useCache || isDirty(ele) || isEdge && isDirty(ele.source()) || isDirty(ele.target());
9861
9862 if (needRecalc) {
9863 if (!isPosKeySame) {
9864 ele.recalculateRenderedStyle(useCache);
9865 }
9866
9867 bb = boundingBoxImpl(ele, defBbOpts);
9868 _p.bbCache = bb;
9869 _p.bbCachePosKey = currPosKey;
9870 } else {
9871 bb = _p.bbCache;
9872 } // not using def opts => need to build up bb from combination of sub bbs
9873
9874
9875 if (!usingDefOpts) {
9876 var isNode = ele.isNode();
9877 bb = makeBoundingBox();
9878
9879 if (opts.includeNodes && isNode || opts.includeEdges && !isNode) {
9880 if (opts.includeOverlays) {
9881 updateBoundsFromBox(bb, _p.overlayBounds);
9882 } else {
9883 updateBoundsFromBox(bb, _p.bodyBounds);
9884 }
9885 }
9886
9887 if (opts.includeLabels) {
9888 if (opts.includeMainLabels && (!isEdge || opts.includeSourceLabels && opts.includeTargetLabels)) {
9889 updateBoundsFromBox(bb, _p.labelBounds.all);
9890 } else {
9891 if (opts.includeMainLabels) {
9892 updateBoundsFromBox(bb, _p.labelBounds.mainRot);
9893 }
9894
9895 if (opts.includeSourceLabels) {
9896 updateBoundsFromBox(bb, _p.labelBounds.sourceRot);
9897 }
9898
9899 if (opts.includeTargetLabels) {
9900 updateBoundsFromBox(bb, _p.labelBounds.targetRot);
9901 }
9902 }
9903 }
9904
9905 bb.w = bb.x2 - bb.x1;
9906 bb.h = bb.y2 - bb.y1;
9907 }
9908
9909 return bb;
9910};
9911
9912var defBbOpts = {
9913 includeNodes: true,
9914 includeEdges: true,
9915 includeLabels: true,
9916 includeMainLabels: true,
9917 includeSourceLabels: true,
9918 includeTargetLabels: true,
9919 includeOverlays: true,
9920 includeUnderlays: true,
9921 useCache: true
9922};
9923var defBbOptsKey = getKey(defBbOpts);
9924var filledBbOpts = defaults$g(defBbOpts);
9925
9926elesfn$b.boundingBox = function (options) {
9927 var bounds; // the main usecase is ele.boundingBox() for a single element with no/def options
9928 // specified s.t. the cache is used, so check for this case to make it faster by
9929 // avoiding the overhead of the rest of the function
9930
9931 if (this.length === 1 && this[0]._private.bbCache != null && !this[0]._private.styleDirty && (options === undefined || options.useCache === undefined || options.useCache === true)) {
9932 if (options === undefined) {
9933 options = defBbOpts;
9934 } else {
9935 options = filledBbOpts(options);
9936 }
9937
9938 bounds = cachedBoundingBoxImpl(this[0], options);
9939 } else {
9940 bounds = makeBoundingBox();
9941 options = options || defBbOpts;
9942 var opts = filledBbOpts(options);
9943 var eles = this;
9944 var cy = eles.cy();
9945 var styleEnabled = cy.styleEnabled();
9946
9947 if (styleEnabled) {
9948 for (var i = 0; i < eles.length; i++) {
9949 var ele = eles[i];
9950 var _p = ele._private;
9951 var currPosKey = getBoundingBoxPosKey(ele);
9952 var isPosKeySame = _p.bbCachePosKey === currPosKey;
9953 var useCache = opts.useCache && isPosKeySame && !_p.styleDirty;
9954 ele.recalculateRenderedStyle(useCache);
9955 }
9956 }
9957
9958 this.updateCompoundBounds(!options.useCache);
9959
9960 for (var _i = 0; _i < eles.length; _i++) {
9961 var _ele = eles[_i];
9962 updateBoundsFromBox(bounds, cachedBoundingBoxImpl(_ele, opts));
9963 }
9964 }
9965
9966 bounds.x1 = noninf(bounds.x1);
9967 bounds.y1 = noninf(bounds.y1);
9968 bounds.x2 = noninf(bounds.x2);
9969 bounds.y2 = noninf(bounds.y2);
9970 bounds.w = noninf(bounds.x2 - bounds.x1);
9971 bounds.h = noninf(bounds.y2 - bounds.y1);
9972 return bounds;
9973};
9974
9975elesfn$b.dirtyBoundingBoxCache = function () {
9976 for (var i = 0; i < this.length; i++) {
9977 var _p = this[i]._private;
9978 _p.bbCache = null;
9979 _p.bbCachePosKey = null;
9980 _p.bodyBounds = null;
9981 _p.overlayBounds = null;
9982 _p.labelBounds.all = null;
9983 _p.labelBounds.source = null;
9984 _p.labelBounds.target = null;
9985 _p.labelBounds.main = null;
9986 _p.labelBounds.sourceRot = null;
9987 _p.labelBounds.targetRot = null;
9988 _p.labelBounds.mainRot = null;
9989 _p.arrowBounds.source = null;
9990 _p.arrowBounds.target = null;
9991 _p.arrowBounds['mid-source'] = null;
9992 _p.arrowBounds['mid-target'] = null;
9993 }
9994
9995 this.emitAndNotify('bounds');
9996 return this;
9997}; // private helper to get bounding box for custom node positions
9998// - good for perf in certain cases but currently requires dirtying the rendered style
9999// - would be better to not modify the nodes but the nodes are read directly everywhere in the renderer...
10000// - try to use for only things like discrete layouts where the node position would change anyway
10001
10002
10003elesfn$b.boundingBoxAt = function (fn) {
10004 var nodes = this.nodes();
10005 var cy = this.cy();
10006 var hasCompoundNodes = cy.hasCompoundNodes();
10007 var parents = cy.collection();
10008
10009 if (hasCompoundNodes) {
10010 parents = nodes.filter(function (node) {
10011 return node.isParent();
10012 });
10013 nodes = nodes.not(parents);
10014 }
10015
10016 if (plainObject(fn)) {
10017 var obj = fn;
10018
10019 fn = function fn() {
10020 return obj;
10021 };
10022 }
10023
10024 var storeOldPos = function storeOldPos(node, i) {
10025 return node._private.bbAtOldPos = fn(node, i);
10026 };
10027
10028 var getOldPos = function getOldPos(node) {
10029 return node._private.bbAtOldPos;
10030 };
10031
10032 cy.startBatch();
10033 nodes.forEach(storeOldPos).silentPositions(fn);
10034
10035 if (hasCompoundNodes) {
10036 parents.dirtyCompoundBoundsCache();
10037 parents.dirtyBoundingBoxCache();
10038 parents.updateCompoundBounds(true); // force update b/c we're inside a batch cycle
10039 }
10040
10041 var bb = copyBoundingBox(this.boundingBox({
10042 useCache: false
10043 }));
10044 nodes.silentPositions(getOldPos);
10045
10046 if (hasCompoundNodes) {
10047 parents.dirtyCompoundBoundsCache();
10048 parents.dirtyBoundingBoxCache();
10049 parents.updateCompoundBounds(true); // force update b/c we're inside a batch cycle
10050 }
10051
10052 cy.endBatch();
10053 return bb;
10054};
10055
10056fn$3.boundingbox = fn$3.bb = fn$3.boundingBox;
10057fn$3.renderedBoundingbox = fn$3.renderedBoundingBox;
10058var bounds = elesfn$b;
10059
10060var fn$2, elesfn$a;
10061fn$2 = elesfn$a = {};
10062
10063var defineDimFns = function defineDimFns(opts) {
10064 opts.uppercaseName = capitalize(opts.name);
10065 opts.autoName = 'auto' + opts.uppercaseName;
10066 opts.labelName = 'label' + opts.uppercaseName;
10067 opts.outerName = 'outer' + opts.uppercaseName;
10068 opts.uppercaseOuterName = capitalize(opts.outerName);
10069
10070 fn$2[opts.name] = function dimImpl() {
10071 var ele = this[0];
10072 var _p = ele._private;
10073 var cy = _p.cy;
10074 var styleEnabled = cy._private.styleEnabled;
10075
10076 if (ele) {
10077 if (styleEnabled) {
10078 if (ele.isParent()) {
10079 ele.updateCompoundBounds();
10080 return _p[opts.autoName] || 0;
10081 }
10082
10083 var d = ele.pstyle(opts.name);
10084
10085 switch (d.strValue) {
10086 case 'label':
10087 ele.recalculateRenderedStyle();
10088 return _p.rstyle[opts.labelName] || 0;
10089
10090 default:
10091 return d.pfValue;
10092 }
10093 } else {
10094 return 1;
10095 }
10096 }
10097 };
10098
10099 fn$2['outer' + opts.uppercaseName] = function outerDimImpl() {
10100 var ele = this[0];
10101 var _p = ele._private;
10102 var cy = _p.cy;
10103 var styleEnabled = cy._private.styleEnabled;
10104
10105 if (ele) {
10106 if (styleEnabled) {
10107 var dim = ele[opts.name]();
10108 var border = ele.pstyle('border-width').pfValue; // n.b. 1/2 each side
10109
10110 var padding = 2 * ele.padding();
10111 return dim + border + padding;
10112 } else {
10113 return 1;
10114 }
10115 }
10116 };
10117
10118 fn$2['rendered' + opts.uppercaseName] = function renderedDimImpl() {
10119 var ele = this[0];
10120
10121 if (ele) {
10122 var d = ele[opts.name]();
10123 return d * this.cy().zoom();
10124 }
10125 };
10126
10127 fn$2['rendered' + opts.uppercaseOuterName] = function renderedOuterDimImpl() {
10128 var ele = this[0];
10129
10130 if (ele) {
10131 var od = ele[opts.outerName]();
10132 return od * this.cy().zoom();
10133 }
10134 };
10135};
10136
10137defineDimFns({
10138 name: 'width'
10139});
10140defineDimFns({
10141 name: 'height'
10142});
10143
10144elesfn$a.padding = function () {
10145 var ele = this[0];
10146 var _p = ele._private;
10147
10148 if (ele.isParent()) {
10149 ele.updateCompoundBounds();
10150
10151 if (_p.autoPadding !== undefined) {
10152 return _p.autoPadding;
10153 } else {
10154 return ele.pstyle('padding').pfValue;
10155 }
10156 } else {
10157 return ele.pstyle('padding').pfValue;
10158 }
10159};
10160
10161elesfn$a.paddedHeight = function () {
10162 var ele = this[0];
10163 return ele.height() + 2 * ele.padding();
10164};
10165
10166elesfn$a.paddedWidth = function () {
10167 var ele = this[0];
10168 return ele.width() + 2 * ele.padding();
10169};
10170
10171var widthHeight = elesfn$a;
10172
10173var ifEdge = function ifEdge(ele, getValue) {
10174 if (ele.isEdge()) {
10175 return getValue(ele);
10176 }
10177};
10178
10179var ifEdgeRenderedPosition = function ifEdgeRenderedPosition(ele, getPoint) {
10180 if (ele.isEdge()) {
10181 var cy = ele.cy();
10182 return modelToRenderedPosition(getPoint(ele), cy.zoom(), cy.pan());
10183 }
10184};
10185
10186var ifEdgeRenderedPositions = function ifEdgeRenderedPositions(ele, getPoints) {
10187 if (ele.isEdge()) {
10188 var cy = ele.cy();
10189 var pan = cy.pan();
10190 var zoom = cy.zoom();
10191 return getPoints(ele).map(function (p) {
10192 return modelToRenderedPosition(p, zoom, pan);
10193 });
10194 }
10195};
10196
10197var controlPoints = function controlPoints(ele) {
10198 return ele.renderer().getControlPoints(ele);
10199};
10200
10201var segmentPoints = function segmentPoints(ele) {
10202 return ele.renderer().getSegmentPoints(ele);
10203};
10204
10205var sourceEndpoint = function sourceEndpoint(ele) {
10206 return ele.renderer().getSourceEndpoint(ele);
10207};
10208
10209var targetEndpoint = function targetEndpoint(ele) {
10210 return ele.renderer().getTargetEndpoint(ele);
10211};
10212
10213var midpoint = function midpoint(ele) {
10214 return ele.renderer().getEdgeMidpoint(ele);
10215};
10216
10217var pts = {
10218 controlPoints: {
10219 get: controlPoints,
10220 mult: true
10221 },
10222 segmentPoints: {
10223 get: segmentPoints,
10224 mult: true
10225 },
10226 sourceEndpoint: {
10227 get: sourceEndpoint
10228 },
10229 targetEndpoint: {
10230 get: targetEndpoint
10231 },
10232 midpoint: {
10233 get: midpoint
10234 }
10235};
10236
10237var renderedName = function renderedName(name) {
10238 return 'rendered' + name[0].toUpperCase() + name.substr(1);
10239};
10240
10241var edgePoints = Object.keys(pts).reduce(function (obj, name) {
10242 var spec = pts[name];
10243 var rName = renderedName(name);
10244
10245 obj[name] = function () {
10246 return ifEdge(this, spec.get);
10247 };
10248
10249 if (spec.mult) {
10250 obj[rName] = function () {
10251 return ifEdgeRenderedPositions(this, spec.get);
10252 };
10253 } else {
10254 obj[rName] = function () {
10255 return ifEdgeRenderedPosition(this, spec.get);
10256 };
10257 }
10258
10259 return obj;
10260}, {});
10261
10262var dimensions = extend({}, position, bounds, widthHeight, edgePoints);
10263
10264/*!
10265Event object based on jQuery events, MIT license
10266
10267https://jquery.org/license/
10268https://tldrlegal.com/license/mit-license
10269https://github.com/jquery/jquery/blob/master/src/event.js
10270*/
10271var Event = function Event(src, props) {
10272 this.recycle(src, props);
10273};
10274
10275function returnFalse() {
10276 return false;
10277}
10278
10279function returnTrue() {
10280 return true;
10281} // http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html
10282
10283
10284Event.prototype = {
10285 instanceString: function instanceString() {
10286 return 'event';
10287 },
10288 recycle: function recycle(src, props) {
10289 this.isImmediatePropagationStopped = this.isPropagationStopped = this.isDefaultPrevented = returnFalse;
10290
10291 if (src != null && src.preventDefault) {
10292 // Browser Event object
10293 this.type = src.type; // Events bubbling up the document may have been marked as prevented
10294 // by a handler lower down the tree; reflect the correct value.
10295
10296 this.isDefaultPrevented = src.defaultPrevented ? returnTrue : returnFalse;
10297 } else if (src != null && src.type) {
10298 // Plain object containing all event details
10299 props = src;
10300 } else {
10301 // Event string
10302 this.type = src;
10303 } // Put explicitly provided properties onto the event object
10304
10305
10306 if (props != null) {
10307 // more efficient to manually copy fields we use
10308 this.originalEvent = props.originalEvent;
10309 this.type = props.type != null ? props.type : this.type;
10310 this.cy = props.cy;
10311 this.target = props.target;
10312 this.position = props.position;
10313 this.renderedPosition = props.renderedPosition;
10314 this.namespace = props.namespace;
10315 this.layout = props.layout;
10316 }
10317
10318 if (this.cy != null && this.position != null && this.renderedPosition == null) {
10319 // create a rendered position based on the passed position
10320 var pos = this.position;
10321 var zoom = this.cy.zoom();
10322 var pan = this.cy.pan();
10323 this.renderedPosition = {
10324 x: pos.x * zoom + pan.x,
10325 y: pos.y * zoom + pan.y
10326 };
10327 } // Create a timestamp if incoming event doesn't have one
10328
10329
10330 this.timeStamp = src && src.timeStamp || Date.now();
10331 },
10332 preventDefault: function preventDefault() {
10333 this.isDefaultPrevented = returnTrue;
10334 var e = this.originalEvent;
10335
10336 if (!e) {
10337 return;
10338 } // if preventDefault exists run it on the original event
10339
10340
10341 if (e.preventDefault) {
10342 e.preventDefault();
10343 }
10344 },
10345 stopPropagation: function stopPropagation() {
10346 this.isPropagationStopped = returnTrue;
10347 var e = this.originalEvent;
10348
10349 if (!e) {
10350 return;
10351 } // if stopPropagation exists run it on the original event
10352
10353
10354 if (e.stopPropagation) {
10355 e.stopPropagation();
10356 }
10357 },
10358 stopImmediatePropagation: function stopImmediatePropagation() {
10359 this.isImmediatePropagationStopped = returnTrue;
10360 this.stopPropagation();
10361 },
10362 isDefaultPrevented: returnFalse,
10363 isPropagationStopped: returnFalse,
10364 isImmediatePropagationStopped: returnFalse
10365};
10366
10367var eventRegex = /^([^.]+)(\.(?:[^.]+))?$/; // regex for matching event strings (e.g. "click.namespace")
10368
10369var universalNamespace = '.*'; // matches as if no namespace specified and prevents users from unbinding accidentally
10370
10371var defaults$8 = {
10372 qualifierCompare: function qualifierCompare(q1, q2) {
10373 return q1 === q2;
10374 },
10375 eventMatches: function
10376 /*context, listener, eventObj*/
10377 eventMatches() {
10378 return true;
10379 },
10380 addEventFields: function
10381 /*context, evt*/
10382 addEventFields() {},
10383 callbackContext: function callbackContext(context
10384 /*, listener, eventObj*/
10385 ) {
10386 return context;
10387 },
10388 beforeEmit: function
10389 /* context, listener, eventObj */
10390 beforeEmit() {},
10391 afterEmit: function
10392 /* context, listener, eventObj */
10393 afterEmit() {},
10394 bubble: function
10395 /*context*/
10396 bubble() {
10397 return false;
10398 },
10399 parent: function
10400 /*context*/
10401 parent() {
10402 return null;
10403 },
10404 context: null
10405};
10406var defaultsKeys = Object.keys(defaults$8);
10407var emptyOpts = {};
10408
10409function Emitter() {
10410 var opts = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : emptyOpts;
10411 var context = arguments.length > 1 ? arguments[1] : undefined;
10412
10413 // micro-optimisation vs Object.assign() -- reduces Element instantiation time
10414 for (var i = 0; i < defaultsKeys.length; i++) {
10415 var key = defaultsKeys[i];
10416 this[key] = opts[key] || defaults$8[key];
10417 }
10418
10419 this.context = context || this.context;
10420 this.listeners = [];
10421 this.emitting = 0;
10422}
10423
10424var p = Emitter.prototype;
10425
10426var forEachEvent = function forEachEvent(self, handler, events, qualifier, callback, conf, confOverrides) {
10427 if (fn$6(qualifier)) {
10428 callback = qualifier;
10429 qualifier = null;
10430 }
10431
10432 if (confOverrides) {
10433 if (conf == null) {
10434 conf = confOverrides;
10435 } else {
10436 conf = extend({}, conf, confOverrides);
10437 }
10438 }
10439
10440 var eventList = array(events) ? events : events.split(/\s+/);
10441
10442 for (var i = 0; i < eventList.length; i++) {
10443 var evt = eventList[i];
10444
10445 if (emptyString(evt)) {
10446 continue;
10447 }
10448
10449 var match = evt.match(eventRegex); // type[.namespace]
10450
10451 if (match) {
10452 var type = match[1];
10453 var namespace = match[2] ? match[2] : null;
10454 var ret = handler(self, evt, type, namespace, qualifier, callback, conf);
10455
10456 if (ret === false) {
10457 break;
10458 } // allow exiting early
10459
10460 }
10461 }
10462};
10463
10464var makeEventObj = function makeEventObj(self, obj) {
10465 self.addEventFields(self.context, obj);
10466 return new Event(obj.type, obj);
10467};
10468
10469var forEachEventObj = function forEachEventObj(self, handler, events) {
10470 if (event(events)) {
10471 handler(self, events);
10472 return;
10473 } else if (plainObject(events)) {
10474 handler(self, makeEventObj(self, events));
10475 return;
10476 }
10477
10478 var eventList = array(events) ? events : events.split(/\s+/);
10479
10480 for (var i = 0; i < eventList.length; i++) {
10481 var evt = eventList[i];
10482
10483 if (emptyString(evt)) {
10484 continue;
10485 }
10486
10487 var match = evt.match(eventRegex); // type[.namespace]
10488
10489 if (match) {
10490 var type = match[1];
10491 var namespace = match[2] ? match[2] : null;
10492 var eventObj = makeEventObj(self, {
10493 type: type,
10494 namespace: namespace,
10495 target: self.context
10496 });
10497 handler(self, eventObj);
10498 }
10499 }
10500};
10501
10502p.on = p.addListener = function (events, qualifier, callback, conf, confOverrides) {
10503 forEachEvent(this, function (self, event, type, namespace, qualifier, callback, conf) {
10504 if (fn$6(callback)) {
10505 self.listeners.push({
10506 event: event,
10507 // full event string
10508 callback: callback,
10509 // callback to run
10510 type: type,
10511 // the event type (e.g. 'click')
10512 namespace: namespace,
10513 // the event namespace (e.g. ".foo")
10514 qualifier: qualifier,
10515 // a restriction on whether to match this emitter
10516 conf: conf // additional configuration
10517
10518 });
10519 }
10520 }, events, qualifier, callback, conf, confOverrides);
10521 return this;
10522};
10523
10524p.one = function (events, qualifier, callback, conf) {
10525 return this.on(events, qualifier, callback, conf, {
10526 one: true
10527 });
10528};
10529
10530p.removeListener = p.off = function (events, qualifier, callback, conf) {
10531 var _this = this;
10532
10533 if (this.emitting !== 0) {
10534 this.listeners = copyArray(this.listeners);
10535 }
10536
10537 var listeners = this.listeners;
10538
10539 var _loop = function _loop(i) {
10540 var listener = listeners[i];
10541 forEachEvent(_this, function (self, event, type, namespace, qualifier, callback
10542 /*, conf*/
10543 ) {
10544 if ((listener.type === type || events === '*') && (!namespace && listener.namespace !== '.*' || listener.namespace === namespace) && (!qualifier || self.qualifierCompare(listener.qualifier, qualifier)) && (!callback || listener.callback === callback)) {
10545 listeners.splice(i, 1);
10546 return false;
10547 }
10548 }, events, qualifier, callback, conf);
10549 };
10550
10551 for (var i = listeners.length - 1; i >= 0; i--) {
10552 _loop(i);
10553 }
10554
10555 return this;
10556};
10557
10558p.removeAllListeners = function () {
10559 return this.removeListener('*');
10560};
10561
10562p.emit = p.trigger = function (events, extraParams, manualCallback) {
10563 var listeners = this.listeners;
10564 var numListenersBeforeEmit = listeners.length;
10565 this.emitting++;
10566
10567 if (!array(extraParams)) {
10568 extraParams = [extraParams];
10569 }
10570
10571 forEachEventObj(this, function (self, eventObj) {
10572 if (manualCallback != null) {
10573 listeners = [{
10574 event: eventObj.event,
10575 type: eventObj.type,
10576 namespace: eventObj.namespace,
10577 callback: manualCallback
10578 }];
10579 numListenersBeforeEmit = listeners.length;
10580 }
10581
10582 var _loop2 = function _loop2(i) {
10583 var listener = listeners[i];
10584
10585 if (listener.type === eventObj.type && (!listener.namespace || listener.namespace === eventObj.namespace || listener.namespace === universalNamespace) && self.eventMatches(self.context, listener, eventObj)) {
10586 var args = [eventObj];
10587
10588 if (extraParams != null) {
10589 push(args, extraParams);
10590 }
10591
10592 self.beforeEmit(self.context, listener, eventObj);
10593
10594 if (listener.conf && listener.conf.one) {
10595 self.listeners = self.listeners.filter(function (l) {
10596 return l !== listener;
10597 });
10598 }
10599
10600 var context = self.callbackContext(self.context, listener, eventObj);
10601 var ret = listener.callback.apply(context, args);
10602 self.afterEmit(self.context, listener, eventObj);
10603
10604 if (ret === false) {
10605 eventObj.stopPropagation();
10606 eventObj.preventDefault();
10607 }
10608 } // if listener matches
10609
10610 };
10611
10612 for (var i = 0; i < numListenersBeforeEmit; i++) {
10613 _loop2(i);
10614 } // for listener
10615
10616
10617 if (self.bubble(self.context) && !eventObj.isPropagationStopped()) {
10618 self.parent(self.context).emit(eventObj, extraParams);
10619 }
10620 }, events);
10621 this.emitting--;
10622 return this;
10623};
10624
10625var emitterOptions$1 = {
10626 qualifierCompare: function qualifierCompare(selector1, selector2) {
10627 if (selector1 == null || selector2 == null) {
10628 return selector1 == null && selector2 == null;
10629 } else {
10630 return selector1.sameText(selector2);
10631 }
10632 },
10633 eventMatches: function eventMatches(ele, listener, eventObj) {
10634 var selector = listener.qualifier;
10635
10636 if (selector != null) {
10637 return ele !== eventObj.target && element(eventObj.target) && selector.matches(eventObj.target);
10638 }
10639
10640 return true;
10641 },
10642 addEventFields: function addEventFields(ele, evt) {
10643 evt.cy = ele.cy();
10644 evt.target = ele;
10645 },
10646 callbackContext: function callbackContext(ele, listener, eventObj) {
10647 return listener.qualifier != null ? eventObj.target : ele;
10648 },
10649 beforeEmit: function beforeEmit(context, listener
10650 /*, eventObj*/
10651 ) {
10652 if (listener.conf && listener.conf.once) {
10653 listener.conf.onceCollection.removeListener(listener.event, listener.qualifier, listener.callback);
10654 }
10655 },
10656 bubble: function bubble() {
10657 return true;
10658 },
10659 parent: function parent(ele) {
10660 return ele.isChild() ? ele.parent() : ele.cy();
10661 }
10662};
10663
10664var argSelector$1 = function argSelector(arg) {
10665 if (string(arg)) {
10666 return new Selector(arg);
10667 } else {
10668 return arg;
10669 }
10670};
10671
10672var elesfn$9 = {
10673 createEmitter: function createEmitter() {
10674 for (var i = 0; i < this.length; i++) {
10675 var ele = this[i];
10676 var _p = ele._private;
10677
10678 if (!_p.emitter) {
10679 _p.emitter = new Emitter(emitterOptions$1, ele);
10680 }
10681 }
10682
10683 return this;
10684 },
10685 emitter: function emitter() {
10686 return this._private.emitter;
10687 },
10688 on: function on(events, selector, callback) {
10689 var argSel = argSelector$1(selector);
10690
10691 for (var i = 0; i < this.length; i++) {
10692 var ele = this[i];
10693 ele.emitter().on(events, argSel, callback);
10694 }
10695
10696 return this;
10697 },
10698 removeListener: function removeListener(events, selector, callback) {
10699 var argSel = argSelector$1(selector);
10700
10701 for (var i = 0; i < this.length; i++) {
10702 var ele = this[i];
10703 ele.emitter().removeListener(events, argSel, callback);
10704 }
10705
10706 return this;
10707 },
10708 removeAllListeners: function removeAllListeners() {
10709 for (var i = 0; i < this.length; i++) {
10710 var ele = this[i];
10711 ele.emitter().removeAllListeners();
10712 }
10713
10714 return this;
10715 },
10716 one: function one(events, selector, callback) {
10717 var argSel = argSelector$1(selector);
10718
10719 for (var i = 0; i < this.length; i++) {
10720 var ele = this[i];
10721 ele.emitter().one(events, argSel, callback);
10722 }
10723
10724 return this;
10725 },
10726 once: function once(events, selector, callback) {
10727 var argSel = argSelector$1(selector);
10728
10729 for (var i = 0; i < this.length; i++) {
10730 var ele = this[i];
10731 ele.emitter().on(events, argSel, callback, {
10732 once: true,
10733 onceCollection: this
10734 });
10735 }
10736 },
10737 emit: function emit(events, extraParams) {
10738 for (var i = 0; i < this.length; i++) {
10739 var ele = this[i];
10740 ele.emitter().emit(events, extraParams);
10741 }
10742
10743 return this;
10744 },
10745 emitAndNotify: function emitAndNotify(event, extraParams) {
10746 // for internal use only
10747 if (this.length === 0) {
10748 return;
10749 } // empty collections don't need to notify anything
10750 // notify renderer
10751
10752
10753 this.cy().notify(event, this);
10754 this.emit(event, extraParams);
10755 return this;
10756 }
10757};
10758define.eventAliasesOn(elesfn$9);
10759
10760var elesfn$8 = {
10761 nodes: function nodes(selector) {
10762 return this.filter(function (ele) {
10763 return ele.isNode();
10764 }).filter(selector);
10765 },
10766 edges: function edges(selector) {
10767 return this.filter(function (ele) {
10768 return ele.isEdge();
10769 }).filter(selector);
10770 },
10771 // internal helper to get nodes and edges as separate collections with single iteration over elements
10772 byGroup: function byGroup() {
10773 var nodes = this.spawn();
10774 var edges = this.spawn();
10775
10776 for (var i = 0; i < this.length; i++) {
10777 var ele = this[i];
10778
10779 if (ele.isNode()) {
10780 nodes.push(ele);
10781 } else {
10782 edges.push(ele);
10783 }
10784 }
10785
10786 return {
10787 nodes: nodes,
10788 edges: edges
10789 };
10790 },
10791 filter: function filter(_filter, thisArg) {
10792 if (_filter === undefined) {
10793 // check this first b/c it's the most common/performant case
10794 return this;
10795 } else if (string(_filter) || elementOrCollection(_filter)) {
10796 return new Selector(_filter).filter(this);
10797 } else if (fn$6(_filter)) {
10798 var filterEles = this.spawn();
10799 var eles = this;
10800
10801 for (var i = 0; i < eles.length; i++) {
10802 var ele = eles[i];
10803 var include = thisArg ? _filter.apply(thisArg, [ele, i, eles]) : _filter(ele, i, eles);
10804
10805 if (include) {
10806 filterEles.push(ele);
10807 }
10808 }
10809
10810 return filterEles;
10811 }
10812
10813 return this.spawn(); // if not handled by above, give 'em an empty collection
10814 },
10815 not: function not(toRemove) {
10816 if (!toRemove) {
10817 return this;
10818 } else {
10819 if (string(toRemove)) {
10820 toRemove = this.filter(toRemove);
10821 }
10822
10823 var elements = this.spawn();
10824
10825 for (var i = 0; i < this.length; i++) {
10826 var element = this[i];
10827 var remove = toRemove.has(element);
10828
10829 if (!remove) {
10830 elements.push(element);
10831 }
10832 }
10833
10834 return elements;
10835 }
10836 },
10837 absoluteComplement: function absoluteComplement() {
10838 var cy = this.cy();
10839 return cy.mutableElements().not(this);
10840 },
10841 intersect: function intersect(other) {
10842 // if a selector is specified, then filter by it instead
10843 if (string(other)) {
10844 var selector = other;
10845 return this.filter(selector);
10846 }
10847
10848 var elements = this.spawn();
10849 var col1 = this;
10850 var col2 = other;
10851 var col1Smaller = this.length < other.length;
10852 var colS = col1Smaller ? col1 : col2;
10853 var colL = col1Smaller ? col2 : col1;
10854
10855 for (var i = 0; i < colS.length; i++) {
10856 var ele = colS[i];
10857
10858 if (colL.has(ele)) {
10859 elements.push(ele);
10860 }
10861 }
10862
10863 return elements;
10864 },
10865 xor: function xor(other) {
10866 var cy = this._private.cy;
10867
10868 if (string(other)) {
10869 other = cy.$(other);
10870 }
10871
10872 var elements = this.spawn();
10873 var col1 = this;
10874 var col2 = other;
10875
10876 var add = function add(col, other) {
10877 for (var i = 0; i < col.length; i++) {
10878 var ele = col[i];
10879 var id = ele._private.data.id;
10880 var inOther = other.hasElementWithId(id);
10881
10882 if (!inOther) {
10883 elements.push(ele);
10884 }
10885 }
10886 };
10887
10888 add(col1, col2);
10889 add(col2, col1);
10890 return elements;
10891 },
10892 diff: function diff(other) {
10893 var cy = this._private.cy;
10894
10895 if (string(other)) {
10896 other = cy.$(other);
10897 }
10898
10899 var left = this.spawn();
10900 var right = this.spawn();
10901 var both = this.spawn();
10902 var col1 = this;
10903 var col2 = other;
10904
10905 var add = function add(col, other, retEles) {
10906 for (var i = 0; i < col.length; i++) {
10907 var ele = col[i];
10908 var id = ele._private.data.id;
10909 var inOther = other.hasElementWithId(id);
10910
10911 if (inOther) {
10912 both.merge(ele);
10913 } else {
10914 retEles.push(ele);
10915 }
10916 }
10917 };
10918
10919 add(col1, col2, left);
10920 add(col2, col1, right);
10921 return {
10922 left: left,
10923 right: right,
10924 both: both
10925 };
10926 },
10927 add: function add(toAdd) {
10928 var cy = this._private.cy;
10929
10930 if (!toAdd) {
10931 return this;
10932 }
10933
10934 if (string(toAdd)) {
10935 var selector = toAdd;
10936 toAdd = cy.mutableElements().filter(selector);
10937 }
10938
10939 var elements = this.spawnSelf();
10940
10941 for (var i = 0; i < toAdd.length; i++) {
10942 var ele = toAdd[i];
10943 var add = !this.has(ele);
10944
10945 if (add) {
10946 elements.push(ele);
10947 }
10948 }
10949
10950 return elements;
10951 },
10952 // in place merge on calling collection
10953 merge: function merge(toAdd) {
10954 var _p = this._private;
10955 var cy = _p.cy;
10956
10957 if (!toAdd) {
10958 return this;
10959 }
10960
10961 if (toAdd && string(toAdd)) {
10962 var selector = toAdd;
10963 toAdd = cy.mutableElements().filter(selector);
10964 }
10965
10966 var map = _p.map;
10967
10968 for (var i = 0; i < toAdd.length; i++) {
10969 var toAddEle = toAdd[i];
10970 var id = toAddEle._private.data.id;
10971 var add = !map.has(id);
10972
10973 if (add) {
10974 var index = this.length++;
10975 this[index] = toAddEle;
10976 map.set(id, {
10977 ele: toAddEle,
10978 index: index
10979 });
10980 }
10981 }
10982
10983 return this; // chaining
10984 },
10985 unmergeAt: function unmergeAt(i) {
10986 var ele = this[i];
10987 var id = ele.id();
10988 var _p = this._private;
10989 var map = _p.map; // remove ele
10990
10991 this[i] = undefined;
10992 map["delete"](id);
10993 var unmergedLastEle = i === this.length - 1; // replace empty spot with last ele in collection
10994
10995 if (this.length > 1 && !unmergedLastEle) {
10996 var lastEleI = this.length - 1;
10997 var lastEle = this[lastEleI];
10998 var lastEleId = lastEle._private.data.id;
10999 this[lastEleI] = undefined;
11000 this[i] = lastEle;
11001 map.set(lastEleId, {
11002 ele: lastEle,
11003 index: i
11004 });
11005 } // the collection is now 1 ele smaller
11006
11007
11008 this.length--;
11009 return this;
11010 },
11011 // remove single ele in place in calling collection
11012 unmergeOne: function unmergeOne(ele) {
11013 ele = ele[0];
11014 var _p = this._private;
11015 var id = ele._private.data.id;
11016 var map = _p.map;
11017 var entry = map.get(id);
11018
11019 if (!entry) {
11020 return this; // no need to remove
11021 }
11022
11023 var i = entry.index;
11024 this.unmergeAt(i);
11025 return this;
11026 },
11027 // remove eles in place on calling collection
11028 unmerge: function unmerge(toRemove) {
11029 var cy = this._private.cy;
11030
11031 if (!toRemove) {
11032 return this;
11033 }
11034
11035 if (toRemove && string(toRemove)) {
11036 var selector = toRemove;
11037 toRemove = cy.mutableElements().filter(selector);
11038 }
11039
11040 for (var i = 0; i < toRemove.length; i++) {
11041 this.unmergeOne(toRemove[i]);
11042 }
11043
11044 return this; // chaining
11045 },
11046 unmergeBy: function unmergeBy(toRmFn) {
11047 for (var i = this.length - 1; i >= 0; i--) {
11048 var ele = this[i];
11049
11050 if (toRmFn(ele)) {
11051 this.unmergeAt(i);
11052 }
11053 }
11054
11055 return this;
11056 },
11057 map: function map(mapFn, thisArg) {
11058 var arr = [];
11059 var eles = this;
11060
11061 for (var i = 0; i < eles.length; i++) {
11062 var ele = eles[i];
11063 var ret = thisArg ? mapFn.apply(thisArg, [ele, i, eles]) : mapFn(ele, i, eles);
11064 arr.push(ret);
11065 }
11066
11067 return arr;
11068 },
11069 reduce: function reduce(fn, initialValue) {
11070 var val = initialValue;
11071 var eles = this;
11072
11073 for (var i = 0; i < eles.length; i++) {
11074 val = fn(val, eles[i], i, eles);
11075 }
11076
11077 return val;
11078 },
11079 max: function max(valFn, thisArg) {
11080 var max = -Infinity;
11081 var maxEle;
11082 var eles = this;
11083
11084 for (var i = 0; i < eles.length; i++) {
11085 var ele = eles[i];
11086 var val = thisArg ? valFn.apply(thisArg, [ele, i, eles]) : valFn(ele, i, eles);
11087
11088 if (val > max) {
11089 max = val;
11090 maxEle = ele;
11091 }
11092 }
11093
11094 return {
11095 value: max,
11096 ele: maxEle
11097 };
11098 },
11099 min: function min(valFn, thisArg) {
11100 var min = Infinity;
11101 var minEle;
11102 var eles = this;
11103
11104 for (var i = 0; i < eles.length; i++) {
11105 var ele = eles[i];
11106 var val = thisArg ? valFn.apply(thisArg, [ele, i, eles]) : valFn(ele, i, eles);
11107
11108 if (val < min) {
11109 min = val;
11110 minEle = ele;
11111 }
11112 }
11113
11114 return {
11115 value: min,
11116 ele: minEle
11117 };
11118 }
11119}; // aliases
11120
11121var fn$1 = elesfn$8;
11122fn$1['u'] = fn$1['|'] = fn$1['+'] = fn$1.union = fn$1.or = fn$1.add;
11123fn$1['\\'] = fn$1['!'] = fn$1['-'] = fn$1.difference = fn$1.relativeComplement = fn$1.subtract = fn$1.not;
11124fn$1['n'] = fn$1['&'] = fn$1['.'] = fn$1.and = fn$1.intersection = fn$1.intersect;
11125fn$1['^'] = fn$1['(+)'] = fn$1['(-)'] = fn$1.symmetricDifference = fn$1.symdiff = fn$1.xor;
11126fn$1.fnFilter = fn$1.filterFn = fn$1.stdFilter = fn$1.filter;
11127fn$1.complement = fn$1.abscomp = fn$1.absoluteComplement;
11128
11129var elesfn$7 = {
11130 isNode: function isNode() {
11131 return this.group() === 'nodes';
11132 },
11133 isEdge: function isEdge() {
11134 return this.group() === 'edges';
11135 },
11136 isLoop: function isLoop() {
11137 return this.isEdge() && this.source()[0] === this.target()[0];
11138 },
11139 isSimple: function isSimple() {
11140 return this.isEdge() && this.source()[0] !== this.target()[0];
11141 },
11142 group: function group() {
11143 var ele = this[0];
11144
11145 if (ele) {
11146 return ele._private.group;
11147 }
11148 }
11149};
11150
11151/**
11152 * Elements are drawn in a specific order based on compound depth (low to high), the element type (nodes above edges),
11153 * and z-index (low to high). These styles affect how this applies:
11154 *
11155 * z-compound-depth: May be `bottom | orphan | auto | top`. The first drawn is `bottom`, then `orphan` which is the
11156 * same depth as the root of the compound graph, followed by the default value `auto` which draws in order from
11157 * root to leaves of the compound graph. The last drawn is `top`.
11158 * z-index-compare: May be `auto | manual`. The default value is `auto` which always draws edges under nodes.
11159 * `manual` ignores this convention and draws based on the `z-index` value setting.
11160 * z-index: An integer value that affects the relative draw order of elements. In general, an element with a higher
11161 * `z-index` will be drawn on top of an element with a lower `z-index`.
11162 */
11163
11164var zIndexSort = function zIndexSort(a, b) {
11165 var cy = a.cy();
11166 var hasCompoundNodes = cy.hasCompoundNodes();
11167
11168 function getDepth(ele) {
11169 var style = ele.pstyle('z-compound-depth');
11170
11171 if (style.value === 'auto') {
11172 return hasCompoundNodes ? ele.zDepth() : 0;
11173 } else if (style.value === 'bottom') {
11174 return -1;
11175 } else if (style.value === 'top') {
11176 return MAX_INT$1;
11177 } // 'orphan'
11178
11179
11180 return 0;
11181 }
11182
11183 var depthDiff = getDepth(a) - getDepth(b);
11184
11185 if (depthDiff !== 0) {
11186 return depthDiff;
11187 }
11188
11189 function getEleDepth(ele) {
11190 var style = ele.pstyle('z-index-compare');
11191
11192 if (style.value === 'auto') {
11193 return ele.isNode() ? 1 : 0;
11194 } // 'manual'
11195
11196
11197 return 0;
11198 }
11199
11200 var eleDiff = getEleDepth(a) - getEleDepth(b);
11201
11202 if (eleDiff !== 0) {
11203 return eleDiff;
11204 }
11205
11206 var zDiff = a.pstyle('z-index').value - b.pstyle('z-index').value;
11207
11208 if (zDiff !== 0) {
11209 return zDiff;
11210 } // compare indices in the core (order added to graph w/ last on top)
11211
11212
11213 return a.poolIndex() - b.poolIndex();
11214};
11215
11216var elesfn$6 = {
11217 forEach: function forEach(fn, thisArg) {
11218 if (fn$6(fn)) {
11219 var N = this.length;
11220
11221 for (var i = 0; i < N; i++) {
11222 var ele = this[i];
11223 var ret = thisArg ? fn.apply(thisArg, [ele, i, this]) : fn(ele, i, this);
11224
11225 if (ret === false) {
11226 break;
11227 } // exit each early on return false
11228
11229 }
11230 }
11231
11232 return this;
11233 },
11234 toArray: function toArray() {
11235 var array = [];
11236
11237 for (var i = 0; i < this.length; i++) {
11238 array.push(this[i]);
11239 }
11240
11241 return array;
11242 },
11243 slice: function slice(start, end) {
11244 var array = [];
11245 var thisSize = this.length;
11246
11247 if (end == null) {
11248 end = thisSize;
11249 }
11250
11251 if (start == null) {
11252 start = 0;
11253 }
11254
11255 if (start < 0) {
11256 start = thisSize + start;
11257 }
11258
11259 if (end < 0) {
11260 end = thisSize + end;
11261 }
11262
11263 for (var i = start; i >= 0 && i < end && i < thisSize; i++) {
11264 array.push(this[i]);
11265 }
11266
11267 return this.spawn(array);
11268 },
11269 size: function size() {
11270 return this.length;
11271 },
11272 eq: function eq(i) {
11273 return this[i] || this.spawn();
11274 },
11275 first: function first() {
11276 return this[0] || this.spawn();
11277 },
11278 last: function last() {
11279 return this[this.length - 1] || this.spawn();
11280 },
11281 empty: function empty() {
11282 return this.length === 0;
11283 },
11284 nonempty: function nonempty() {
11285 return !this.empty();
11286 },
11287 sort: function sort(sortFn) {
11288 if (!fn$6(sortFn)) {
11289 return this;
11290 }
11291
11292 var sorted = this.toArray().sort(sortFn);
11293 return this.spawn(sorted);
11294 },
11295 sortByZIndex: function sortByZIndex() {
11296 return this.sort(zIndexSort);
11297 },
11298 zDepth: function zDepth() {
11299 var ele = this[0];
11300
11301 if (!ele) {
11302 return undefined;
11303 } // let cy = ele.cy();
11304
11305
11306 var _p = ele._private;
11307 var group = _p.group;
11308
11309 if (group === 'nodes') {
11310 var depth = _p.data.parent ? ele.parents().size() : 0;
11311
11312 if (!ele.isParent()) {
11313 return MAX_INT$1 - 1; // childless nodes always on top
11314 }
11315
11316 return depth;
11317 } else {
11318 var src = _p.source;
11319 var tgt = _p.target;
11320 var srcDepth = src.zDepth();
11321 var tgtDepth = tgt.zDepth();
11322 return Math.max(srcDepth, tgtDepth, 0); // depth of deepest parent
11323 }
11324 }
11325};
11326elesfn$6.each = elesfn$6.forEach;
11327
11328var defineSymbolIterator = function defineSymbolIterator() {
11329 var typeofUndef = "undefined" ;
11330 var isIteratorSupported = (typeof Symbol === "undefined" ? "undefined" : _typeof(Symbol)) != typeofUndef && _typeof(Symbol.iterator) != typeofUndef; // eslint-disable-line no-undef
11331
11332 if (isIteratorSupported) {
11333 elesfn$6[Symbol.iterator] = function () {
11334 var _this = this;
11335
11336 // eslint-disable-line no-undef
11337 var entry = {
11338 value: undefined,
11339 done: false
11340 };
11341 var i = 0;
11342 var length = this.length;
11343 return _defineProperty({
11344 next: function next() {
11345 if (i < length) {
11346 entry.value = _this[i++];
11347 } else {
11348 entry.value = undefined;
11349 entry.done = true;
11350 }
11351
11352 return entry;
11353 }
11354 }, Symbol.iterator, function () {
11355 // eslint-disable-line no-undef
11356 return this;
11357 });
11358 };
11359 }
11360};
11361
11362defineSymbolIterator();
11363
11364var getLayoutDimensionOptions = defaults$g({
11365 nodeDimensionsIncludeLabels: false
11366});
11367var elesfn$5 = {
11368 // Calculates and returns node dimensions { x, y } based on options given
11369 layoutDimensions: function layoutDimensions(options) {
11370 options = getLayoutDimensionOptions(options);
11371 var dims;
11372
11373 if (!this.takesUpSpace()) {
11374 dims = {
11375 w: 0,
11376 h: 0
11377 };
11378 } else if (options.nodeDimensionsIncludeLabels) {
11379 var bbDim = this.boundingBox();
11380 dims = {
11381 w: bbDim.w,
11382 h: bbDim.h
11383 };
11384 } else {
11385 dims = {
11386 w: this.outerWidth(),
11387 h: this.outerHeight()
11388 };
11389 } // sanitise the dimensions for external layouts (avoid division by zero)
11390
11391
11392 if (dims.w === 0 || dims.h === 0) {
11393 dims.w = dims.h = 1;
11394 }
11395
11396 return dims;
11397 },
11398 // using standard layout options, apply position function (w/ or w/o animation)
11399 layoutPositions: function layoutPositions(layout, options, fn) {
11400 var nodes = this.nodes().filter(function (n) {
11401 return !n.isParent();
11402 });
11403 var cy = this.cy();
11404 var layoutEles = options.eles; // nodes & edges
11405
11406 var getMemoizeKey = function getMemoizeKey(node) {
11407 return node.id();
11408 };
11409
11410 var fnMem = memoize(fn, getMemoizeKey); // memoized version of position function
11411
11412 layout.emit({
11413 type: 'layoutstart',
11414 layout: layout
11415 });
11416 layout.animations = [];
11417
11418 var calculateSpacing = function calculateSpacing(spacing, nodesBb, pos) {
11419 var center = {
11420 x: nodesBb.x1 + nodesBb.w / 2,
11421 y: nodesBb.y1 + nodesBb.h / 2
11422 };
11423 var spacingVector = {
11424 // scale from center of bounding box (not necessarily 0,0)
11425 x: (pos.x - center.x) * spacing,
11426 y: (pos.y - center.y) * spacing
11427 };
11428 return {
11429 x: center.x + spacingVector.x,
11430 y: center.y + spacingVector.y
11431 };
11432 };
11433
11434 var useSpacingFactor = options.spacingFactor && options.spacingFactor !== 1;
11435
11436 var spacingBb = function spacingBb() {
11437 if (!useSpacingFactor) {
11438 return null;
11439 }
11440
11441 var bb = makeBoundingBox();
11442
11443 for (var i = 0; i < nodes.length; i++) {
11444 var node = nodes[i];
11445 var pos = fnMem(node, i);
11446 expandBoundingBoxByPoint(bb, pos.x, pos.y);
11447 }
11448
11449 return bb;
11450 };
11451
11452 var bb = spacingBb();
11453 var getFinalPos = memoize(function (node, i) {
11454 var newPos = fnMem(node, i);
11455
11456 if (useSpacingFactor) {
11457 var spacing = Math.abs(options.spacingFactor);
11458 newPos = calculateSpacing(spacing, bb, newPos);
11459 }
11460
11461 if (options.transform != null) {
11462 newPos = options.transform(node, newPos);
11463 }
11464
11465 return newPos;
11466 }, getMemoizeKey);
11467
11468 if (options.animate) {
11469 for (var i = 0; i < nodes.length; i++) {
11470 var node = nodes[i];
11471 var newPos = getFinalPos(node, i);
11472 var animateNode = options.animateFilter == null || options.animateFilter(node, i);
11473
11474 if (animateNode) {
11475 var ani = node.animation({
11476 position: newPos,
11477 duration: options.animationDuration,
11478 easing: options.animationEasing
11479 });
11480 layout.animations.push(ani);
11481 } else {
11482 node.position(newPos);
11483 }
11484 }
11485
11486 if (options.fit) {
11487 var fitAni = cy.animation({
11488 fit: {
11489 boundingBox: layoutEles.boundingBoxAt(getFinalPos),
11490 padding: options.padding
11491 },
11492 duration: options.animationDuration,
11493 easing: options.animationEasing
11494 });
11495 layout.animations.push(fitAni);
11496 } else if (options.zoom !== undefined && options.pan !== undefined) {
11497 var zoomPanAni = cy.animation({
11498 zoom: options.zoom,
11499 pan: options.pan,
11500 duration: options.animationDuration,
11501 easing: options.animationEasing
11502 });
11503 layout.animations.push(zoomPanAni);
11504 }
11505
11506 layout.animations.forEach(function (ani) {
11507 return ani.play();
11508 });
11509 layout.one('layoutready', options.ready);
11510 layout.emit({
11511 type: 'layoutready',
11512 layout: layout
11513 });
11514 Promise$1.all(layout.animations.map(function (ani) {
11515 return ani.promise();
11516 })).then(function () {
11517 layout.one('layoutstop', options.stop);
11518 layout.emit({
11519 type: 'layoutstop',
11520 layout: layout
11521 });
11522 });
11523 } else {
11524 nodes.positions(getFinalPos);
11525
11526 if (options.fit) {
11527 cy.fit(options.eles, options.padding);
11528 }
11529
11530 if (options.zoom != null) {
11531 cy.zoom(options.zoom);
11532 }
11533
11534 if (options.pan) {
11535 cy.pan(options.pan);
11536 }
11537
11538 layout.one('layoutready', options.ready);
11539 layout.emit({
11540 type: 'layoutready',
11541 layout: layout
11542 });
11543 layout.one('layoutstop', options.stop);
11544 layout.emit({
11545 type: 'layoutstop',
11546 layout: layout
11547 });
11548 }
11549
11550 return this; // chaining
11551 },
11552 layout: function layout(options) {
11553 var cy = this.cy();
11554 return cy.makeLayout(extend({}, options, {
11555 eles: this
11556 }));
11557 }
11558}; // aliases:
11559
11560elesfn$5.createLayout = elesfn$5.makeLayout = elesfn$5.layout;
11561
11562function styleCache(key, fn, ele) {
11563 var _p = ele._private;
11564 var cache = _p.styleCache = _p.styleCache || [];
11565 var val;
11566
11567 if ((val = cache[key]) != null) {
11568 return val;
11569 } else {
11570 val = cache[key] = fn(ele);
11571 return val;
11572 }
11573}
11574
11575function cacheStyleFunction(key, fn) {
11576 key = hashString(key);
11577 return function cachedStyleFunction(ele) {
11578 return styleCache(key, fn, ele);
11579 };
11580}
11581
11582function cachePrototypeStyleFunction(key, fn) {
11583 key = hashString(key);
11584
11585 var selfFn = function selfFn(ele) {
11586 return fn.call(ele);
11587 };
11588
11589 return function cachedPrototypeStyleFunction() {
11590 var ele = this[0];
11591
11592 if (ele) {
11593 return styleCache(key, selfFn, ele);
11594 }
11595 };
11596}
11597
11598var elesfn$4 = {
11599 recalculateRenderedStyle: function recalculateRenderedStyle(useCache) {
11600 var cy = this.cy();
11601 var renderer = cy.renderer();
11602 var styleEnabled = cy.styleEnabled();
11603
11604 if (renderer && styleEnabled) {
11605 renderer.recalculateRenderedStyle(this, useCache);
11606 }
11607
11608 return this;
11609 },
11610 dirtyStyleCache: function dirtyStyleCache() {
11611 var cy = this.cy();
11612
11613 var dirty = function dirty(ele) {
11614 return ele._private.styleCache = null;
11615 };
11616
11617 if (cy.hasCompoundNodes()) {
11618 var eles;
11619 eles = this.spawnSelf().merge(this.descendants()).merge(this.parents());
11620 eles.merge(eles.connectedEdges());
11621 eles.forEach(dirty);
11622 } else {
11623 this.forEach(function (ele) {
11624 dirty(ele);
11625 ele.connectedEdges().forEach(dirty);
11626 });
11627 }
11628
11629 return this;
11630 },
11631 // fully updates (recalculates) the style for the elements
11632 updateStyle: function updateStyle(notifyRenderer) {
11633 var cy = this._private.cy;
11634
11635 if (!cy.styleEnabled()) {
11636 return this;
11637 }
11638
11639 if (cy.batching()) {
11640 var bEles = cy._private.batchStyleEles;
11641 bEles.merge(this);
11642 return this; // chaining and exit early when batching
11643 }
11644
11645 var hasCompounds = cy.hasCompoundNodes();
11646 var updatedEles = this;
11647 notifyRenderer = notifyRenderer || notifyRenderer === undefined ? true : false;
11648
11649 if (hasCompounds) {
11650 // then add everything up and down for compound selector checks
11651 updatedEles = this.spawnSelf().merge(this.descendants()).merge(this.parents());
11652 } // let changedEles = style.apply( updatedEles );
11653
11654
11655 var changedEles = updatedEles;
11656
11657 if (notifyRenderer) {
11658 changedEles.emitAndNotify('style'); // let renderer know we changed style
11659 } else {
11660 changedEles.emit('style'); // just fire the event
11661 }
11662
11663 updatedEles.forEach(function (ele) {
11664 return ele._private.styleDirty = true;
11665 });
11666 return this; // chaining
11667 },
11668 // private: clears dirty flag and recalculates style
11669 cleanStyle: function cleanStyle() {
11670 var cy = this.cy();
11671
11672 if (!cy.styleEnabled()) {
11673 return;
11674 }
11675
11676 for (var i = 0; i < this.length; i++) {
11677 var ele = this[i];
11678
11679 if (ele._private.styleDirty) {
11680 // n.b. this flag should be set before apply() to avoid potential infinite recursion
11681 ele._private.styleDirty = false;
11682 cy.style().apply(ele);
11683 }
11684 }
11685 },
11686 // get the internal parsed style object for the specified property
11687 parsedStyle: function parsedStyle(property) {
11688 var includeNonDefault = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
11689 var ele = this[0];
11690 var cy = ele.cy();
11691
11692 if (!cy.styleEnabled()) {
11693 return;
11694 }
11695
11696 if (ele) {
11697 this.cleanStyle();
11698 var overriddenStyle = ele._private.style[property];
11699
11700 if (overriddenStyle != null) {
11701 return overriddenStyle;
11702 } else if (includeNonDefault) {
11703 return cy.style().getDefaultProperty(property);
11704 } else {
11705 return null;
11706 }
11707 }
11708 },
11709 numericStyle: function numericStyle(property) {
11710 var ele = this[0];
11711
11712 if (!ele.cy().styleEnabled()) {
11713 return;
11714 }
11715
11716 if (ele) {
11717 var pstyle = ele.pstyle(property);
11718 return pstyle.pfValue !== undefined ? pstyle.pfValue : pstyle.value;
11719 }
11720 },
11721 numericStyleUnits: function numericStyleUnits(property) {
11722 var ele = this[0];
11723
11724 if (!ele.cy().styleEnabled()) {
11725 return;
11726 }
11727
11728 if (ele) {
11729 return ele.pstyle(property).units;
11730 }
11731 },
11732 // get the specified css property as a rendered value (i.e. on-screen value)
11733 // or get the whole rendered style if no property specified (NB doesn't allow setting)
11734 renderedStyle: function renderedStyle(property) {
11735 var cy = this.cy();
11736
11737 if (!cy.styleEnabled()) {
11738 return this;
11739 }
11740
11741 var ele = this[0];
11742
11743 if (ele) {
11744 return cy.style().getRenderedStyle(ele, property);
11745 }
11746 },
11747 // read the calculated css style of the element or override the style (via a bypass)
11748 style: function style(name, value) {
11749 var cy = this.cy();
11750
11751 if (!cy.styleEnabled()) {
11752 return this;
11753 }
11754
11755 var updateTransitions = false;
11756 var style = cy.style();
11757
11758 if (plainObject(name)) {
11759 // then extend the bypass
11760 var props = name;
11761 style.applyBypass(this, props, updateTransitions);
11762 this.emitAndNotify('style'); // let the renderer know we've updated style
11763 } else if (string(name)) {
11764 if (value === undefined) {
11765 // then get the property from the style
11766 var ele = this[0];
11767
11768 if (ele) {
11769 return style.getStylePropertyValue(ele, name);
11770 } else {
11771 // empty collection => can't get any value
11772 return;
11773 }
11774 } else {
11775 // then set the bypass with the property value
11776 style.applyBypass(this, name, value, updateTransitions);
11777 this.emitAndNotify('style'); // let the renderer know we've updated style
11778 }
11779 } else if (name === undefined) {
11780 var _ele = this[0];
11781
11782 if (_ele) {
11783 return style.getRawStyle(_ele);
11784 } else {
11785 // empty collection => can't get any value
11786 return;
11787 }
11788 }
11789
11790 return this; // chaining
11791 },
11792 removeStyle: function removeStyle(names) {
11793 var cy = this.cy();
11794
11795 if (!cy.styleEnabled()) {
11796 return this;
11797 }
11798
11799 var updateTransitions = false;
11800 var style = cy.style();
11801 var eles = this;
11802
11803 if (names === undefined) {
11804 for (var i = 0; i < eles.length; i++) {
11805 var ele = eles[i];
11806 style.removeAllBypasses(ele, updateTransitions);
11807 }
11808 } else {
11809 names = names.split(/\s+/);
11810
11811 for (var _i = 0; _i < eles.length; _i++) {
11812 var _ele2 = eles[_i];
11813 style.removeBypasses(_ele2, names, updateTransitions);
11814 }
11815 }
11816
11817 this.emitAndNotify('style'); // let the renderer know we've updated style
11818
11819 return this; // chaining
11820 },
11821 show: function show() {
11822 this.css('display', 'element');
11823 return this; // chaining
11824 },
11825 hide: function hide() {
11826 this.css('display', 'none');
11827 return this; // chaining
11828 },
11829 effectiveOpacity: function effectiveOpacity() {
11830 var cy = this.cy();
11831
11832 if (!cy.styleEnabled()) {
11833 return 1;
11834 }
11835
11836 var hasCompoundNodes = cy.hasCompoundNodes();
11837 var ele = this[0];
11838
11839 if (ele) {
11840 var _p = ele._private;
11841 var parentOpacity = ele.pstyle('opacity').value;
11842
11843 if (!hasCompoundNodes) {
11844 return parentOpacity;
11845 }
11846
11847 var parents = !_p.data.parent ? null : ele.parents();
11848
11849 if (parents) {
11850 for (var i = 0; i < parents.length; i++) {
11851 var parent = parents[i];
11852 var opacity = parent.pstyle('opacity').value;
11853 parentOpacity = opacity * parentOpacity;
11854 }
11855 }
11856
11857 return parentOpacity;
11858 }
11859 },
11860 transparent: function transparent() {
11861 var cy = this.cy();
11862
11863 if (!cy.styleEnabled()) {
11864 return false;
11865 }
11866
11867 var ele = this[0];
11868 var hasCompoundNodes = ele.cy().hasCompoundNodes();
11869
11870 if (ele) {
11871 if (!hasCompoundNodes) {
11872 return ele.pstyle('opacity').value === 0;
11873 } else {
11874 return ele.effectiveOpacity() === 0;
11875 }
11876 }
11877 },
11878 backgrounding: function backgrounding() {
11879 var cy = this.cy();
11880
11881 if (!cy.styleEnabled()) {
11882 return false;
11883 }
11884
11885 var ele = this[0];
11886 return ele._private.backgrounding ? true : false;
11887 }
11888};
11889
11890function checkCompound(ele, parentOk) {
11891 var _p = ele._private;
11892 var parents = _p.data.parent ? ele.parents() : null;
11893
11894 if (parents) {
11895 for (var i = 0; i < parents.length; i++) {
11896 var parent = parents[i];
11897
11898 if (!parentOk(parent)) {
11899 return false;
11900 }
11901 }
11902 }
11903
11904 return true;
11905}
11906
11907function defineDerivedStateFunction(specs) {
11908 var ok = specs.ok;
11909 var edgeOkViaNode = specs.edgeOkViaNode || specs.ok;
11910 var parentOk = specs.parentOk || specs.ok;
11911 return function () {
11912 var cy = this.cy();
11913
11914 if (!cy.styleEnabled()) {
11915 return true;
11916 }
11917
11918 var ele = this[0];
11919 var hasCompoundNodes = cy.hasCompoundNodes();
11920
11921 if (ele) {
11922 var _p = ele._private;
11923
11924 if (!ok(ele)) {
11925 return false;
11926 }
11927
11928 if (ele.isNode()) {
11929 return !hasCompoundNodes || checkCompound(ele, parentOk);
11930 } else {
11931 var src = _p.source;
11932 var tgt = _p.target;
11933 return edgeOkViaNode(src) && (!hasCompoundNodes || checkCompound(src, edgeOkViaNode)) && (src === tgt || edgeOkViaNode(tgt) && (!hasCompoundNodes || checkCompound(tgt, edgeOkViaNode)));
11934 }
11935 }
11936 };
11937}
11938
11939var eleTakesUpSpace = cacheStyleFunction('eleTakesUpSpace', function (ele) {
11940 return ele.pstyle('display').value === 'element' && ele.width() !== 0 && (ele.isNode() ? ele.height() !== 0 : true);
11941});
11942elesfn$4.takesUpSpace = cachePrototypeStyleFunction('takesUpSpace', defineDerivedStateFunction({
11943 ok: eleTakesUpSpace
11944}));
11945var eleInteractive = cacheStyleFunction('eleInteractive', function (ele) {
11946 return ele.pstyle('events').value === 'yes' && ele.pstyle('visibility').value === 'visible' && eleTakesUpSpace(ele);
11947});
11948var parentInteractive = cacheStyleFunction('parentInteractive', function (parent) {
11949 return parent.pstyle('visibility').value === 'visible' && eleTakesUpSpace(parent);
11950});
11951elesfn$4.interactive = cachePrototypeStyleFunction('interactive', defineDerivedStateFunction({
11952 ok: eleInteractive,
11953 parentOk: parentInteractive,
11954 edgeOkViaNode: eleTakesUpSpace
11955}));
11956
11957elesfn$4.noninteractive = function () {
11958 var ele = this[0];
11959
11960 if (ele) {
11961 return !ele.interactive();
11962 }
11963};
11964
11965var eleVisible = cacheStyleFunction('eleVisible', function (ele) {
11966 return ele.pstyle('visibility').value === 'visible' && ele.pstyle('opacity').pfValue !== 0 && eleTakesUpSpace(ele);
11967});
11968var edgeVisibleViaNode = eleTakesUpSpace;
11969elesfn$4.visible = cachePrototypeStyleFunction('visible', defineDerivedStateFunction({
11970 ok: eleVisible,
11971 edgeOkViaNode: edgeVisibleViaNode
11972}));
11973
11974elesfn$4.hidden = function () {
11975 var ele = this[0];
11976
11977 if (ele) {
11978 return !ele.visible();
11979 }
11980};
11981
11982elesfn$4.isBundledBezier = cachePrototypeStyleFunction('isBundledBezier', function () {
11983 if (!this.cy().styleEnabled()) {
11984 return false;
11985 }
11986
11987 return !this.removed() && this.pstyle('curve-style').value === 'bezier' && this.takesUpSpace();
11988});
11989elesfn$4.bypass = elesfn$4.css = elesfn$4.style;
11990elesfn$4.renderedCss = elesfn$4.renderedStyle;
11991elesfn$4.removeBypass = elesfn$4.removeCss = elesfn$4.removeStyle;
11992elesfn$4.pstyle = elesfn$4.parsedStyle;
11993
11994var elesfn$3 = {};
11995
11996function defineSwitchFunction(params) {
11997 return function () {
11998 var args = arguments;
11999 var changedEles = []; // e.g. cy.nodes().select( data, handler )
12000
12001 if (args.length === 2) {
12002 var data = args[0];
12003 var handler = args[1];
12004 this.on(params.event, data, handler);
12005 } // e.g. cy.nodes().select( handler )
12006 else if (args.length === 1 && fn$6(args[0])) {
12007 var _handler = args[0];
12008 this.on(params.event, _handler);
12009 } // e.g. cy.nodes().select()
12010 // e.g. (private) cy.nodes().select(['tapselect'])
12011 else if (args.length === 0 || args.length === 1 && array(args[0])) {
12012 var addlEvents = args.length === 1 ? args[0] : null;
12013
12014 for (var i = 0; i < this.length; i++) {
12015 var ele = this[i];
12016 var able = !params.ableField || ele._private[params.ableField];
12017 var changed = ele._private[params.field] != params.value;
12018
12019 if (params.overrideAble) {
12020 var overrideAble = params.overrideAble(ele);
12021
12022 if (overrideAble !== undefined) {
12023 able = overrideAble;
12024
12025 if (!overrideAble) {
12026 return this;
12027 } // to save cycles assume not able for all on override
12028
12029 }
12030 }
12031
12032 if (able) {
12033 ele._private[params.field] = params.value;
12034
12035 if (changed) {
12036 changedEles.push(ele);
12037 }
12038 }
12039 }
12040
12041 var changedColl = this.spawn(changedEles);
12042 changedColl.updateStyle(); // change of state => possible change of style
12043
12044 changedColl.emit(params.event);
12045
12046 if (addlEvents) {
12047 changedColl.emit(addlEvents);
12048 }
12049 }
12050
12051 return this;
12052 };
12053}
12054
12055function defineSwitchSet(params) {
12056 elesfn$3[params.field] = function () {
12057 var ele = this[0];
12058
12059 if (ele) {
12060 if (params.overrideField) {
12061 var val = params.overrideField(ele);
12062
12063 if (val !== undefined) {
12064 return val;
12065 }
12066 }
12067
12068 return ele._private[params.field];
12069 }
12070 };
12071
12072 elesfn$3[params.on] = defineSwitchFunction({
12073 event: params.on,
12074 field: params.field,
12075 ableField: params.ableField,
12076 overrideAble: params.overrideAble,
12077 value: true
12078 });
12079 elesfn$3[params.off] = defineSwitchFunction({
12080 event: params.off,
12081 field: params.field,
12082 ableField: params.ableField,
12083 overrideAble: params.overrideAble,
12084 value: false
12085 });
12086}
12087
12088defineSwitchSet({
12089 field: 'locked',
12090 overrideField: function overrideField(ele) {
12091 return ele.cy().autolock() ? true : undefined;
12092 },
12093 on: 'lock',
12094 off: 'unlock'
12095});
12096defineSwitchSet({
12097 field: 'grabbable',
12098 overrideField: function overrideField(ele) {
12099 return ele.cy().autoungrabify() || ele.pannable() ? false : undefined;
12100 },
12101 on: 'grabify',
12102 off: 'ungrabify'
12103});
12104defineSwitchSet({
12105 field: 'selected',
12106 ableField: 'selectable',
12107 overrideAble: function overrideAble(ele) {
12108 return ele.cy().autounselectify() ? false : undefined;
12109 },
12110 on: 'select',
12111 off: 'unselect'
12112});
12113defineSwitchSet({
12114 field: 'selectable',
12115 overrideField: function overrideField(ele) {
12116 return ele.cy().autounselectify() ? false : undefined;
12117 },
12118 on: 'selectify',
12119 off: 'unselectify'
12120});
12121elesfn$3.deselect = elesfn$3.unselect;
12122
12123elesfn$3.grabbed = function () {
12124 var ele = this[0];
12125
12126 if (ele) {
12127 return ele._private.grabbed;
12128 }
12129};
12130
12131defineSwitchSet({
12132 field: 'active',
12133 on: 'activate',
12134 off: 'unactivate'
12135});
12136defineSwitchSet({
12137 field: 'pannable',
12138 on: 'panify',
12139 off: 'unpanify'
12140});
12141
12142elesfn$3.inactive = function () {
12143 var ele = this[0];
12144
12145 if (ele) {
12146 return !ele._private.active;
12147 }
12148};
12149
12150var elesfn$2 = {}; // DAG functions
12151////////////////
12152
12153var defineDagExtremity = function defineDagExtremity(params) {
12154 return function dagExtremityImpl(selector) {
12155 var eles = this;
12156 var ret = [];
12157
12158 for (var i = 0; i < eles.length; i++) {
12159 var ele = eles[i];
12160
12161 if (!ele.isNode()) {
12162 continue;
12163 }
12164
12165 var disqualified = false;
12166 var edges = ele.connectedEdges();
12167
12168 for (var j = 0; j < edges.length; j++) {
12169 var edge = edges[j];
12170 var src = edge.source();
12171 var tgt = edge.target();
12172
12173 if (params.noIncomingEdges && tgt === ele && src !== ele || params.noOutgoingEdges && src === ele && tgt !== ele) {
12174 disqualified = true;
12175 break;
12176 }
12177 }
12178
12179 if (!disqualified) {
12180 ret.push(ele);
12181 }
12182 }
12183
12184 return this.spawn(ret, true).filter(selector);
12185 };
12186};
12187
12188var defineDagOneHop = function defineDagOneHop(params) {
12189 return function (selector) {
12190 var eles = this;
12191 var oEles = [];
12192
12193 for (var i = 0; i < eles.length; i++) {
12194 var ele = eles[i];
12195
12196 if (!ele.isNode()) {
12197 continue;
12198 }
12199
12200 var edges = ele.connectedEdges();
12201
12202 for (var j = 0; j < edges.length; j++) {
12203 var edge = edges[j];
12204 var src = edge.source();
12205 var tgt = edge.target();
12206
12207 if (params.outgoing && src === ele) {
12208 oEles.push(edge);
12209 oEles.push(tgt);
12210 } else if (params.incoming && tgt === ele) {
12211 oEles.push(edge);
12212 oEles.push(src);
12213 }
12214 }
12215 }
12216
12217 return this.spawn(oEles, true).filter(selector);
12218 };
12219};
12220
12221var defineDagAllHops = function defineDagAllHops(params) {
12222 return function (selector) {
12223 var eles = this;
12224 var sEles = [];
12225 var sElesIds = {};
12226
12227 for (;;) {
12228 var next = params.outgoing ? eles.outgoers() : eles.incomers();
12229
12230 if (next.length === 0) {
12231 break;
12232 } // done if none left
12233
12234
12235 var newNext = false;
12236
12237 for (var i = 0; i < next.length; i++) {
12238 var n = next[i];
12239 var nid = n.id();
12240
12241 if (!sElesIds[nid]) {
12242 sElesIds[nid] = true;
12243 sEles.push(n);
12244 newNext = true;
12245 }
12246 }
12247
12248 if (!newNext) {
12249 break;
12250 } // done if touched all outgoers already
12251
12252
12253 eles = next;
12254 }
12255
12256 return this.spawn(sEles, true).filter(selector);
12257 };
12258};
12259
12260elesfn$2.clearTraversalCache = function () {
12261 for (var i = 0; i < this.length; i++) {
12262 this[i]._private.traversalCache = null;
12263 }
12264};
12265
12266extend(elesfn$2, {
12267 // get the root nodes in the DAG
12268 roots: defineDagExtremity({
12269 noIncomingEdges: true
12270 }),
12271 // get the leaf nodes in the DAG
12272 leaves: defineDagExtremity({
12273 noOutgoingEdges: true
12274 }),
12275 // normally called children in graph theory
12276 // these nodes =edges=> outgoing nodes
12277 outgoers: cache(defineDagOneHop({
12278 outgoing: true
12279 }), 'outgoers'),
12280 // aka DAG descendants
12281 successors: defineDagAllHops({
12282 outgoing: true
12283 }),
12284 // normally called parents in graph theory
12285 // these nodes <=edges= incoming nodes
12286 incomers: cache(defineDagOneHop({
12287 incoming: true
12288 }), 'incomers'),
12289 // aka DAG ancestors
12290 predecessors: defineDagAllHops({
12291 incoming: true
12292 })
12293}); // Neighbourhood functions
12294//////////////////////////
12295
12296extend(elesfn$2, {
12297 neighborhood: cache(function (selector) {
12298 var elements = [];
12299 var nodes = this.nodes();
12300
12301 for (var i = 0; i < nodes.length; i++) {
12302 // for all nodes
12303 var node = nodes[i];
12304 var connectedEdges = node.connectedEdges(); // for each connected edge, add the edge and the other node
12305
12306 for (var j = 0; j < connectedEdges.length; j++) {
12307 var edge = connectedEdges[j];
12308 var src = edge.source();
12309 var tgt = edge.target();
12310 var otherNode = node === src ? tgt : src; // need check in case of loop
12311
12312 if (otherNode.length > 0) {
12313 elements.push(otherNode[0]); // add node 1 hop away
12314 } // add connected edge
12315
12316
12317 elements.push(edge[0]);
12318 }
12319 }
12320
12321 return this.spawn(elements, true).filter(selector);
12322 }, 'neighborhood'),
12323 closedNeighborhood: function closedNeighborhood(selector) {
12324 return this.neighborhood().add(this).filter(selector);
12325 },
12326 openNeighborhood: function openNeighborhood(selector) {
12327 return this.neighborhood(selector);
12328 }
12329}); // aliases
12330
12331elesfn$2.neighbourhood = elesfn$2.neighborhood;
12332elesfn$2.closedNeighbourhood = elesfn$2.closedNeighborhood;
12333elesfn$2.openNeighbourhood = elesfn$2.openNeighborhood; // Edge functions
12334/////////////////
12335
12336extend(elesfn$2, {
12337 source: cache(function sourceImpl(selector) {
12338 var ele = this[0];
12339 var src;
12340
12341 if (ele) {
12342 src = ele._private.source || ele.cy().collection();
12343 }
12344
12345 return src && selector ? src.filter(selector) : src;
12346 }, 'source'),
12347 target: cache(function targetImpl(selector) {
12348 var ele = this[0];
12349 var tgt;
12350
12351 if (ele) {
12352 tgt = ele._private.target || ele.cy().collection();
12353 }
12354
12355 return tgt && selector ? tgt.filter(selector) : tgt;
12356 }, 'target'),
12357 sources: defineSourceFunction({
12358 attr: 'source'
12359 }),
12360 targets: defineSourceFunction({
12361 attr: 'target'
12362 })
12363});
12364
12365function defineSourceFunction(params) {
12366 return function sourceImpl(selector) {
12367 var sources = [];
12368
12369 for (var i = 0; i < this.length; i++) {
12370 var ele = this[i];
12371 var src = ele._private[params.attr];
12372
12373 if (src) {
12374 sources.push(src);
12375 }
12376 }
12377
12378 return this.spawn(sources, true).filter(selector);
12379 };
12380}
12381
12382extend(elesfn$2, {
12383 edgesWith: cache(defineEdgesWithFunction(), 'edgesWith'),
12384 edgesTo: cache(defineEdgesWithFunction({
12385 thisIsSrc: true
12386 }), 'edgesTo')
12387});
12388
12389function defineEdgesWithFunction(params) {
12390 return function edgesWithImpl(otherNodes) {
12391 var elements = [];
12392 var cy = this._private.cy;
12393 var p = params || {}; // get elements if a selector is specified
12394
12395 if (string(otherNodes)) {
12396 otherNodes = cy.$(otherNodes);
12397 }
12398
12399 for (var h = 0; h < otherNodes.length; h++) {
12400 var edges = otherNodes[h]._private.edges;
12401
12402 for (var i = 0; i < edges.length; i++) {
12403 var edge = edges[i];
12404 var edgeData = edge._private.data;
12405 var thisToOther = this.hasElementWithId(edgeData.source) && otherNodes.hasElementWithId(edgeData.target);
12406 var otherToThis = otherNodes.hasElementWithId(edgeData.source) && this.hasElementWithId(edgeData.target);
12407 var edgeConnectsThisAndOther = thisToOther || otherToThis;
12408
12409 if (!edgeConnectsThisAndOther) {
12410 continue;
12411 }
12412
12413 if (p.thisIsSrc || p.thisIsTgt) {
12414 if (p.thisIsSrc && !thisToOther) {
12415 continue;
12416 }
12417
12418 if (p.thisIsTgt && !otherToThis) {
12419 continue;
12420 }
12421 }
12422
12423 elements.push(edge);
12424 }
12425 }
12426
12427 return this.spawn(elements, true);
12428 };
12429}
12430
12431extend(elesfn$2, {
12432 connectedEdges: cache(function (selector) {
12433 var retEles = [];
12434 var eles = this;
12435
12436 for (var i = 0; i < eles.length; i++) {
12437 var node = eles[i];
12438
12439 if (!node.isNode()) {
12440 continue;
12441 }
12442
12443 var edges = node._private.edges;
12444
12445 for (var j = 0; j < edges.length; j++) {
12446 var edge = edges[j];
12447 retEles.push(edge);
12448 }
12449 }
12450
12451 return this.spawn(retEles, true).filter(selector);
12452 }, 'connectedEdges'),
12453 connectedNodes: cache(function (selector) {
12454 var retEles = [];
12455 var eles = this;
12456
12457 for (var i = 0; i < eles.length; i++) {
12458 var edge = eles[i];
12459
12460 if (!edge.isEdge()) {
12461 continue;
12462 }
12463
12464 retEles.push(edge.source()[0]);
12465 retEles.push(edge.target()[0]);
12466 }
12467
12468 return this.spawn(retEles, true).filter(selector);
12469 }, 'connectedNodes'),
12470 parallelEdges: cache(defineParallelEdgesFunction(), 'parallelEdges'),
12471 codirectedEdges: cache(defineParallelEdgesFunction({
12472 codirected: true
12473 }), 'codirectedEdges')
12474});
12475
12476function defineParallelEdgesFunction(params) {
12477 var defaults = {
12478 codirected: false
12479 };
12480 params = extend({}, defaults, params);
12481 return function parallelEdgesImpl(selector) {
12482 // micro-optimised for renderer
12483 var elements = [];
12484 var edges = this.edges();
12485 var p = params; // look at all the edges in the collection
12486
12487 for (var i = 0; i < edges.length; i++) {
12488 var edge1 = edges[i];
12489 var edge1_p = edge1._private;
12490 var src1 = edge1_p.source;
12491 var srcid1 = src1._private.data.id;
12492 var tgtid1 = edge1_p.data.target;
12493 var srcEdges1 = src1._private.edges; // look at edges connected to the src node of this edge
12494
12495 for (var j = 0; j < srcEdges1.length; j++) {
12496 var edge2 = srcEdges1[j];
12497 var edge2data = edge2._private.data;
12498 var tgtid2 = edge2data.target;
12499 var srcid2 = edge2data.source;
12500 var codirected = tgtid2 === tgtid1 && srcid2 === srcid1;
12501 var oppdirected = srcid1 === tgtid2 && tgtid1 === srcid2;
12502
12503 if (p.codirected && codirected || !p.codirected && (codirected || oppdirected)) {
12504 elements.push(edge2);
12505 }
12506 }
12507 }
12508
12509 return this.spawn(elements, true).filter(selector);
12510 };
12511} // Misc functions
12512/////////////////
12513
12514
12515extend(elesfn$2, {
12516 components: function components(root) {
12517 var self = this;
12518 var cy = self.cy();
12519 var visited = cy.collection();
12520 var unvisited = root == null ? self.nodes() : root.nodes();
12521 var components = [];
12522
12523 if (root != null && unvisited.empty()) {
12524 // root may contain only edges
12525 unvisited = root.sources(); // doesn't matter which node to use (undirected), so just use the source sides
12526 }
12527
12528 var visitInComponent = function visitInComponent(node, component) {
12529 visited.merge(node);
12530 unvisited.unmerge(node);
12531 component.merge(node);
12532 };
12533
12534 if (unvisited.empty()) {
12535 return self.spawn();
12536 }
12537
12538 var _loop = function _loop() {
12539 // each iteration yields a component
12540 var cmpt = cy.collection();
12541 components.push(cmpt);
12542 var root = unvisited[0];
12543 visitInComponent(root, cmpt);
12544 self.bfs({
12545 directed: false,
12546 roots: root,
12547 visit: function visit(v) {
12548 return visitInComponent(v, cmpt);
12549 }
12550 });
12551 cmpt.forEach(function (node) {
12552 node.connectedEdges().forEach(function (e) {
12553 // connectedEdges() usually cached
12554 if (self.has(e) && cmpt.has(e.source()) && cmpt.has(e.target())) {
12555 // has() is cheap
12556 cmpt.merge(e); // forEach() only considers nodes -- sets N at call time
12557 }
12558 });
12559 });
12560 };
12561
12562 do {
12563 _loop();
12564 } while (unvisited.length > 0);
12565
12566 return components;
12567 },
12568 component: function component() {
12569 var ele = this[0];
12570 return ele.cy().mutableElements().components(ele)[0];
12571 }
12572});
12573elesfn$2.componentsOf = elesfn$2.components;
12574
12575var Collection = function Collection(cy, elements) {
12576 var unique = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
12577 var removed = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false;
12578
12579 if (cy === undefined) {
12580 error('A collection must have a reference to the core');
12581 return;
12582 }
12583
12584 var map = new Map$1();
12585 var createdElements = false;
12586
12587 if (!elements) {
12588 elements = [];
12589 } else if (elements.length > 0 && plainObject(elements[0]) && !element(elements[0])) {
12590 createdElements = true; // make elements from json and restore all at once later
12591
12592 var eles = [];
12593 var elesIds = new Set$1();
12594
12595 for (var i = 0, l = elements.length; i < l; i++) {
12596 var json = elements[i];
12597
12598 if (json.data == null) {
12599 json.data = {};
12600 }
12601
12602 var _data = json.data; // make sure newly created elements have valid ids
12603
12604 if (_data.id == null) {
12605 _data.id = uuid();
12606 } else if (cy.hasElementWithId(_data.id) || elesIds.has(_data.id)) {
12607 continue; // can't create element if prior id already exists
12608 }
12609
12610 var ele = new Element(cy, json, false);
12611 eles.push(ele);
12612 elesIds.add(_data.id);
12613 }
12614
12615 elements = eles;
12616 }
12617
12618 this.length = 0;
12619
12620 for (var _i = 0, _l = elements.length; _i < _l; _i++) {
12621 var element$1 = elements[_i][0]; // [0] in case elements is an array of collections, rather than array of elements
12622
12623 if (element$1 == null) {
12624 continue;
12625 }
12626
12627 var id = element$1._private.data.id;
12628
12629 if (!unique || !map.has(id)) {
12630 if (unique) {
12631 map.set(id, {
12632 index: this.length,
12633 ele: element$1
12634 });
12635 }
12636
12637 this[this.length] = element$1;
12638 this.length++;
12639 }
12640 }
12641
12642 this._private = {
12643 eles: this,
12644 cy: cy,
12645
12646 get map() {
12647 if (this.lazyMap == null) {
12648 this.rebuildMap();
12649 }
12650
12651 return this.lazyMap;
12652 },
12653
12654 set map(m) {
12655 this.lazyMap = m;
12656 },
12657
12658 rebuildMap: function rebuildMap() {
12659 var m = this.lazyMap = new Map$1();
12660 var eles = this.eles;
12661
12662 for (var _i2 = 0; _i2 < eles.length; _i2++) {
12663 var _ele = eles[_i2];
12664 m.set(_ele.id(), {
12665 index: _i2,
12666 ele: _ele
12667 });
12668 }
12669 }
12670 };
12671
12672 if (unique) {
12673 this._private.map = map;
12674 } // restore the elements if we created them from json
12675
12676
12677 if (createdElements && !removed) {
12678 this.restore();
12679 }
12680}; // Functions
12681////////////////////////////////////////////////////////////////////////////////////////////////////
12682// keep the prototypes in sync (an element has the same functions as a collection)
12683// and use elefn and elesfn as shorthands to the prototypes
12684
12685
12686var elesfn$1 = Element.prototype = Collection.prototype = Object.create(Array.prototype);
12687
12688elesfn$1.instanceString = function () {
12689 return 'collection';
12690};
12691
12692elesfn$1.spawn = function (eles, unique) {
12693 return new Collection(this.cy(), eles, unique);
12694};
12695
12696elesfn$1.spawnSelf = function () {
12697 return this.spawn(this);
12698};
12699
12700elesfn$1.cy = function () {
12701 return this._private.cy;
12702};
12703
12704elesfn$1.renderer = function () {
12705 return this._private.cy.renderer();
12706};
12707
12708elesfn$1.element = function () {
12709 return this[0];
12710};
12711
12712elesfn$1.collection = function () {
12713 if (collection(this)) {
12714 return this;
12715 } else {
12716 // an element
12717 return new Collection(this._private.cy, [this]);
12718 }
12719};
12720
12721elesfn$1.unique = function () {
12722 return new Collection(this._private.cy, this, true);
12723};
12724
12725elesfn$1.hasElementWithId = function (id) {
12726 id = '' + id; // id must be string
12727
12728 return this._private.map.has(id);
12729};
12730
12731elesfn$1.getElementById = function (id) {
12732 id = '' + id; // id must be string
12733
12734 var cy = this._private.cy;
12735
12736 var entry = this._private.map.get(id);
12737
12738 return entry ? entry.ele : new Collection(cy); // get ele or empty collection
12739};
12740
12741elesfn$1.$id = elesfn$1.getElementById;
12742
12743elesfn$1.poolIndex = function () {
12744 var cy = this._private.cy;
12745 var eles = cy._private.elements;
12746 var id = this[0]._private.data.id;
12747 return eles._private.map.get(id).index;
12748};
12749
12750elesfn$1.indexOf = function (ele) {
12751 var id = ele[0]._private.data.id;
12752 return this._private.map.get(id).index;
12753};
12754
12755elesfn$1.indexOfId = function (id) {
12756 id = '' + id; // id must be string
12757
12758 return this._private.map.get(id).index;
12759};
12760
12761elesfn$1.json = function (obj) {
12762 var ele = this.element();
12763 var cy = this.cy();
12764
12765 if (ele == null && obj) {
12766 return this;
12767 } // can't set to no eles
12768
12769
12770 if (ele == null) {
12771 return undefined;
12772 } // can't get from no eles
12773
12774
12775 var p = ele._private;
12776
12777 if (plainObject(obj)) {
12778 // set
12779 cy.startBatch();
12780
12781 if (obj.data) {
12782 ele.data(obj.data);
12783 var _data2 = p.data;
12784
12785 if (ele.isEdge()) {
12786 // source and target are immutable via data()
12787 var move = false;
12788 var spec = {};
12789 var src = obj.data.source;
12790 var tgt = obj.data.target;
12791
12792 if (src != null && src != _data2.source) {
12793 spec.source = '' + src; // id must be string
12794
12795 move = true;
12796 }
12797
12798 if (tgt != null && tgt != _data2.target) {
12799 spec.target = '' + tgt; // id must be string
12800
12801 move = true;
12802 }
12803
12804 if (move) {
12805 ele = ele.move(spec);
12806 }
12807 } else {
12808 // parent is immutable via data()
12809 var newParentValSpecd = ('parent' in obj.data);
12810 var parent = obj.data.parent;
12811
12812 if (newParentValSpecd && (parent != null || _data2.parent != null) && parent != _data2.parent) {
12813 if (parent === undefined) {
12814 // can't set undefined imperatively, so use null
12815 parent = null;
12816 }
12817
12818 if (parent != null) {
12819 parent = '' + parent; // id must be string
12820 }
12821
12822 ele = ele.move({
12823 parent: parent
12824 });
12825 }
12826 }
12827 }
12828
12829 if (obj.position) {
12830 ele.position(obj.position);
12831 } // ignore group -- immutable
12832
12833
12834 var checkSwitch = function checkSwitch(k, trueFnName, falseFnName) {
12835 var obj_k = obj[k];
12836
12837 if (obj_k != null && obj_k !== p[k]) {
12838 if (obj_k) {
12839 ele[trueFnName]();
12840 } else {
12841 ele[falseFnName]();
12842 }
12843 }
12844 };
12845
12846 checkSwitch('removed', 'remove', 'restore');
12847 checkSwitch('selected', 'select', 'unselect');
12848 checkSwitch('selectable', 'selectify', 'unselectify');
12849 checkSwitch('locked', 'lock', 'unlock');
12850 checkSwitch('grabbable', 'grabify', 'ungrabify');
12851 checkSwitch('pannable', 'panify', 'unpanify');
12852
12853 if (obj.classes != null) {
12854 ele.classes(obj.classes);
12855 }
12856
12857 cy.endBatch();
12858 return this;
12859 } else if (obj === undefined) {
12860 // get
12861 var json = {
12862 data: copy(p.data),
12863 position: copy(p.position),
12864 group: p.group,
12865 removed: p.removed,
12866 selected: p.selected,
12867 selectable: p.selectable,
12868 locked: p.locked,
12869 grabbable: p.grabbable,
12870 pannable: p.pannable,
12871 classes: null
12872 };
12873 json.classes = '';
12874 var i = 0;
12875 p.classes.forEach(function (cls) {
12876 return json.classes += i++ === 0 ? cls : ' ' + cls;
12877 });
12878 return json;
12879 }
12880};
12881
12882elesfn$1.jsons = function () {
12883 var jsons = [];
12884
12885 for (var i = 0; i < this.length; i++) {
12886 var ele = this[i];
12887 var json = ele.json();
12888 jsons.push(json);
12889 }
12890
12891 return jsons;
12892};
12893
12894elesfn$1.clone = function () {
12895 var cy = this.cy();
12896 var elesArr = [];
12897
12898 for (var i = 0; i < this.length; i++) {
12899 var ele = this[i];
12900 var json = ele.json();
12901 var clone = new Element(cy, json, false); // NB no restore
12902
12903 elesArr.push(clone);
12904 }
12905
12906 return new Collection(cy, elesArr);
12907};
12908
12909elesfn$1.copy = elesfn$1.clone;
12910
12911elesfn$1.restore = function () {
12912 var notifyRenderer = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
12913 var addToPool = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
12914 var self = this;
12915 var cy = self.cy();
12916 var cy_p = cy._private; // create arrays of nodes and edges, since we need to
12917 // restore the nodes first
12918
12919 var nodes = [];
12920 var edges = [];
12921 var elements;
12922
12923 for (var _i3 = 0, l = self.length; _i3 < l; _i3++) {
12924 var ele = self[_i3];
12925
12926 if (addToPool && !ele.removed()) {
12927 // don't need to handle this ele
12928 continue;
12929 } // keep nodes first in the array and edges after
12930
12931
12932 if (ele.isNode()) {
12933 // put to front of array if node
12934 nodes.push(ele);
12935 } else {
12936 // put to end of array if edge
12937 edges.push(ele);
12938 }
12939 }
12940
12941 elements = nodes.concat(edges);
12942 var i;
12943
12944 var removeFromElements = function removeFromElements() {
12945 elements.splice(i, 1);
12946 i--;
12947 }; // now, restore each element
12948
12949
12950 for (i = 0; i < elements.length; i++) {
12951 var _ele2 = elements[i];
12952 var _private = _ele2._private;
12953 var _data3 = _private.data; // the traversal cache should start fresh when ele is added
12954
12955 _ele2.clearTraversalCache(); // set id and validate
12956
12957
12958 if (!addToPool && !_private.removed) ; else if (_data3.id === undefined) {
12959 _data3.id = uuid();
12960 } else if (number$1(_data3.id)) {
12961 _data3.id = '' + _data3.id; // now it's a string
12962 } else if (emptyString(_data3.id) || !string(_data3.id)) {
12963 error('Can not create element with invalid string ID `' + _data3.id + '`'); // can't create element if it has empty string as id or non-string id
12964
12965 removeFromElements();
12966 continue;
12967 } else if (cy.hasElementWithId(_data3.id)) {
12968 error('Can not create second element with ID `' + _data3.id + '`'); // can't create element if one already has that id
12969
12970 removeFromElements();
12971 continue;
12972 }
12973
12974 var id = _data3.id; // id is finalised, now let's keep a ref
12975
12976 if (_ele2.isNode()) {
12977 // extra checks for nodes
12978 var pos = _private.position; // make sure the nodes have a defined position
12979
12980 if (pos.x == null) {
12981 pos.x = 0;
12982 }
12983
12984 if (pos.y == null) {
12985 pos.y = 0;
12986 }
12987 }
12988
12989 if (_ele2.isEdge()) {
12990 // extra checks for edges
12991 var edge = _ele2;
12992 var fields = ['source', 'target'];
12993 var fieldsLength = fields.length;
12994 var badSourceOrTarget = false;
12995
12996 for (var j = 0; j < fieldsLength; j++) {
12997 var field = fields[j];
12998 var val = _data3[field];
12999
13000 if (number$1(val)) {
13001 val = _data3[field] = '' + _data3[field]; // now string
13002 }
13003
13004 if (val == null || val === '') {
13005 // can't create if source or target is not defined properly
13006 error('Can not create edge `' + id + '` with unspecified ' + field);
13007 badSourceOrTarget = true;
13008 } else if (!cy.hasElementWithId(val)) {
13009 // can't create edge if one of its nodes doesn't exist
13010 error('Can not create edge `' + id + '` with nonexistant ' + field + ' `' + val + '`');
13011 badSourceOrTarget = true;
13012 }
13013 }
13014
13015 if (badSourceOrTarget) {
13016 removeFromElements();
13017 continue;
13018 } // can't create this
13019
13020
13021 var src = cy.getElementById(_data3.source);
13022 var tgt = cy.getElementById(_data3.target); // only one edge in node if loop
13023
13024 if (src.same(tgt)) {
13025 src._private.edges.push(edge);
13026 } else {
13027 src._private.edges.push(edge);
13028
13029 tgt._private.edges.push(edge);
13030 }
13031
13032 edge._private.source = src;
13033 edge._private.target = tgt;
13034 } // if is edge
13035 // create mock ids / indexes maps for element so it can be used like collections
13036
13037
13038 _private.map = new Map$1();
13039
13040 _private.map.set(id, {
13041 ele: _ele2,
13042 index: 0
13043 });
13044
13045 _private.removed = false;
13046
13047 if (addToPool) {
13048 cy.addToPool(_ele2);
13049 }
13050 } // for each element
13051 // do compound node sanity checks
13052
13053
13054 for (var _i4 = 0; _i4 < nodes.length; _i4++) {
13055 // each node
13056 var node = nodes[_i4];
13057 var _data4 = node._private.data;
13058
13059 if (number$1(_data4.parent)) {
13060 // then automake string
13061 _data4.parent = '' + _data4.parent;
13062 }
13063
13064 var parentId = _data4.parent;
13065 var specifiedParent = parentId != null;
13066
13067 if (specifiedParent || node._private.parent) {
13068 var parent = node._private.parent ? cy.collection().merge(node._private.parent) : cy.getElementById(parentId);
13069
13070 if (parent.empty()) {
13071 // non-existant parent; just remove it
13072 _data4.parent = undefined;
13073 } else if (parent[0].removed()) {
13074 warn('Node added with missing parent, reference to parent removed');
13075 _data4.parent = undefined;
13076 node._private.parent = null;
13077 } else {
13078 var selfAsParent = false;
13079 var ancestor = parent;
13080
13081 while (!ancestor.empty()) {
13082 if (node.same(ancestor)) {
13083 // mark self as parent and remove from data
13084 selfAsParent = true;
13085 _data4.parent = undefined; // remove parent reference
13086 // exit or we loop forever
13087
13088 break;
13089 }
13090
13091 ancestor = ancestor.parent();
13092 }
13093
13094 if (!selfAsParent) {
13095 // connect with children
13096 parent[0]._private.children.push(node);
13097
13098 node._private.parent = parent[0]; // let the core know we have a compound graph
13099
13100 cy_p.hasCompoundNodes = true;
13101 }
13102 } // else
13103
13104 } // if specified parent
13105
13106 } // for each node
13107
13108
13109 if (elements.length > 0) {
13110 var restored = elements.length === self.length ? self : new Collection(cy, elements);
13111
13112 for (var _i5 = 0; _i5 < restored.length; _i5++) {
13113 var _ele3 = restored[_i5];
13114
13115 if (_ele3.isNode()) {
13116 continue;
13117 } // adding an edge invalidates the traversal caches for the parallel edges
13118
13119
13120 _ele3.parallelEdges().clearTraversalCache(); // adding an edge invalidates the traversal cache for the connected nodes
13121
13122
13123 _ele3.source().clearTraversalCache();
13124
13125 _ele3.target().clearTraversalCache();
13126 }
13127
13128 var toUpdateStyle;
13129
13130 if (cy_p.hasCompoundNodes) {
13131 toUpdateStyle = cy.collection().merge(restored).merge(restored.connectedNodes()).merge(restored.parent());
13132 } else {
13133 toUpdateStyle = restored;
13134 }
13135
13136 toUpdateStyle.dirtyCompoundBoundsCache().dirtyBoundingBoxCache().updateStyle(notifyRenderer);
13137
13138 if (notifyRenderer) {
13139 restored.emitAndNotify('add');
13140 } else if (addToPool) {
13141 restored.emit('add');
13142 }
13143 }
13144
13145 return self; // chainability
13146};
13147
13148elesfn$1.removed = function () {
13149 var ele = this[0];
13150 return ele && ele._private.removed;
13151};
13152
13153elesfn$1.inside = function () {
13154 var ele = this[0];
13155 return ele && !ele._private.removed;
13156};
13157
13158elesfn$1.remove = function () {
13159 var notifyRenderer = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
13160 var removeFromPool = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
13161 var self = this;
13162 var elesToRemove = [];
13163 var elesToRemoveIds = {};
13164 var cy = self._private.cy; // add connected edges
13165
13166 function addConnectedEdges(node) {
13167 var edges = node._private.edges;
13168
13169 for (var i = 0; i < edges.length; i++) {
13170 add(edges[i]);
13171 }
13172 } // add descendant nodes
13173
13174
13175 function addChildren(node) {
13176 var children = node._private.children;
13177
13178 for (var i = 0; i < children.length; i++) {
13179 add(children[i]);
13180 }
13181 }
13182
13183 function add(ele) {
13184 var alreadyAdded = elesToRemoveIds[ele.id()];
13185
13186 if (removeFromPool && ele.removed() || alreadyAdded) {
13187 return;
13188 } else {
13189 elesToRemoveIds[ele.id()] = true;
13190 }
13191
13192 if (ele.isNode()) {
13193 elesToRemove.push(ele); // nodes are removed last
13194
13195 addConnectedEdges(ele);
13196 addChildren(ele);
13197 } else {
13198 elesToRemove.unshift(ele); // edges are removed first
13199 }
13200 } // make the list of elements to remove
13201 // (may be removing more than specified due to connected edges etc)
13202
13203
13204 for (var i = 0, l = self.length; i < l; i++) {
13205 var ele = self[i];
13206 add(ele);
13207 }
13208
13209 function removeEdgeRef(node, edge) {
13210 var connectedEdges = node._private.edges;
13211 removeFromArray(connectedEdges, edge); // removing an edges invalidates the traversal cache for its nodes
13212
13213 node.clearTraversalCache();
13214 }
13215
13216 function removeParallelRef(pllEdge) {
13217 // removing an edge invalidates the traversal caches for the parallel edges
13218 pllEdge.clearTraversalCache();
13219 }
13220
13221 var alteredParents = [];
13222 alteredParents.ids = {};
13223
13224 function removeChildRef(parent, ele) {
13225 ele = ele[0];
13226 parent = parent[0];
13227 var children = parent._private.children;
13228 var pid = parent.id();
13229 removeFromArray(children, ele); // remove parent => child ref
13230
13231 ele._private.parent = null; // remove child => parent ref
13232
13233 if (!alteredParents.ids[pid]) {
13234 alteredParents.ids[pid] = true;
13235 alteredParents.push(parent);
13236 }
13237 }
13238
13239 self.dirtyCompoundBoundsCache();
13240
13241 if (removeFromPool) {
13242 cy.removeFromPool(elesToRemove); // remove from core pool
13243 }
13244
13245 for (var _i6 = 0; _i6 < elesToRemove.length; _i6++) {
13246 var _ele4 = elesToRemove[_i6];
13247
13248 if (_ele4.isEdge()) {
13249 // remove references to this edge in its connected nodes
13250 var src = _ele4.source()[0];
13251
13252 var tgt = _ele4.target()[0];
13253
13254 removeEdgeRef(src, _ele4);
13255 removeEdgeRef(tgt, _ele4);
13256
13257 var pllEdges = _ele4.parallelEdges();
13258
13259 for (var j = 0; j < pllEdges.length; j++) {
13260 var pllEdge = pllEdges[j];
13261 removeParallelRef(pllEdge);
13262
13263 if (pllEdge.isBundledBezier()) {
13264 pllEdge.dirtyBoundingBoxCache();
13265 }
13266 }
13267 } else {
13268 // remove reference to parent
13269 var parent = _ele4.parent();
13270
13271 if (parent.length !== 0) {
13272 removeChildRef(parent, _ele4);
13273 }
13274 }
13275
13276 if (removeFromPool) {
13277 // mark as removed
13278 _ele4._private.removed = true;
13279 }
13280 } // check to see if we have a compound graph or not
13281
13282
13283 var elesStillInside = cy._private.elements;
13284 cy._private.hasCompoundNodes = false;
13285
13286 for (var _i7 = 0; _i7 < elesStillInside.length; _i7++) {
13287 var _ele5 = elesStillInside[_i7];
13288
13289 if (_ele5.isParent()) {
13290 cy._private.hasCompoundNodes = true;
13291 break;
13292 }
13293 }
13294
13295 var removedElements = new Collection(this.cy(), elesToRemove);
13296
13297 if (removedElements.size() > 0) {
13298 // must manually notify since trigger won't do this automatically once removed
13299 if (notifyRenderer) {
13300 removedElements.emitAndNotify('remove');
13301 } else if (removeFromPool) {
13302 removedElements.emit('remove');
13303 }
13304 } // the parents who were modified by the removal need their style updated
13305
13306
13307 for (var _i8 = 0; _i8 < alteredParents.length; _i8++) {
13308 var _ele6 = alteredParents[_i8];
13309
13310 if (!removeFromPool || !_ele6.removed()) {
13311 _ele6.updateStyle();
13312 }
13313 }
13314
13315 return removedElements;
13316};
13317
13318elesfn$1.move = function (struct) {
13319 var cy = this._private.cy;
13320 var eles = this; // just clean up refs, caches, etc. in the same way as when removing and then restoring
13321 // (our calls to remove/restore do not remove from the graph or make events)
13322
13323 var notifyRenderer = false;
13324 var modifyPool = false;
13325
13326 var toString = function toString(id) {
13327 return id == null ? id : '' + id;
13328 }; // id must be string
13329
13330
13331 if (struct.source !== undefined || struct.target !== undefined) {
13332 var srcId = toString(struct.source);
13333 var tgtId = toString(struct.target);
13334 var srcExists = srcId != null && cy.hasElementWithId(srcId);
13335 var tgtExists = tgtId != null && cy.hasElementWithId(tgtId);
13336
13337 if (srcExists || tgtExists) {
13338 cy.batch(function () {
13339 // avoid duplicate style updates
13340 eles.remove(notifyRenderer, modifyPool); // clean up refs etc.
13341
13342 eles.emitAndNotify('moveout');
13343
13344 for (var i = 0; i < eles.length; i++) {
13345 var ele = eles[i];
13346 var _data5 = ele._private.data;
13347
13348 if (ele.isEdge()) {
13349 if (srcExists) {
13350 _data5.source = srcId;
13351 }
13352
13353 if (tgtExists) {
13354 _data5.target = tgtId;
13355 }
13356 }
13357 }
13358
13359 eles.restore(notifyRenderer, modifyPool); // make new refs, style, etc.
13360 });
13361 eles.emitAndNotify('move');
13362 }
13363 } else if (struct.parent !== undefined) {
13364 // move node to new parent
13365 var parentId = toString(struct.parent);
13366 var parentExists = parentId === null || cy.hasElementWithId(parentId);
13367
13368 if (parentExists) {
13369 var pidToAssign = parentId === null ? undefined : parentId;
13370 cy.batch(function () {
13371 // avoid duplicate style updates
13372 var updated = eles.remove(notifyRenderer, modifyPool); // clean up refs etc.
13373
13374 updated.emitAndNotify('moveout');
13375
13376 for (var i = 0; i < eles.length; i++) {
13377 var ele = eles[i];
13378 var _data6 = ele._private.data;
13379
13380 if (ele.isNode()) {
13381 _data6.parent = pidToAssign;
13382 }
13383 }
13384
13385 updated.restore(notifyRenderer, modifyPool); // make new refs, style, etc.
13386 });
13387 eles.emitAndNotify('move');
13388 }
13389 }
13390
13391 return this;
13392};
13393
13394[elesfn$j, elesfn$i, elesfn$h, elesfn$g, elesfn$f, data, elesfn$d, dimensions, elesfn$9, elesfn$8, elesfn$7, elesfn$6, elesfn$5, elesfn$4, elesfn$3, elesfn$2].forEach(function (props) {
13395 extend(elesfn$1, props);
13396});
13397
13398var corefn$9 = {
13399 add: function add(opts) {
13400 var elements;
13401 var cy = this; // add the elements
13402
13403 if (elementOrCollection(opts)) {
13404 var eles = opts;
13405
13406 if (eles._private.cy === cy) {
13407 // same instance => just restore
13408 elements = eles.restore();
13409 } else {
13410 // otherwise, copy from json
13411 var jsons = [];
13412
13413 for (var i = 0; i < eles.length; i++) {
13414 var ele = eles[i];
13415 jsons.push(ele.json());
13416 }
13417
13418 elements = new Collection(cy, jsons);
13419 }
13420 } // specify an array of options
13421 else if (array(opts)) {
13422 var _jsons = opts;
13423 elements = new Collection(cy, _jsons);
13424 } // specify via opts.nodes and opts.edges
13425 else if (plainObject(opts) && (array(opts.nodes) || array(opts.edges))) {
13426 var elesByGroup = opts;
13427 var _jsons2 = [];
13428 var grs = ['nodes', 'edges'];
13429
13430 for (var _i = 0, il = grs.length; _i < il; _i++) {
13431 var group = grs[_i];
13432 var elesArray = elesByGroup[group];
13433
13434 if (array(elesArray)) {
13435 for (var j = 0, jl = elesArray.length; j < jl; j++) {
13436 var json = extend({
13437 group: group
13438 }, elesArray[j]);
13439
13440 _jsons2.push(json);
13441 }
13442 }
13443 }
13444
13445 elements = new Collection(cy, _jsons2);
13446 } // specify options for one element
13447 else {
13448 var _json = opts;
13449 elements = new Element(cy, _json).collection();
13450 }
13451
13452 return elements;
13453 },
13454 remove: function remove(collection) {
13455 if (elementOrCollection(collection)) ; else if (string(collection)) {
13456 var selector = collection;
13457 collection = this.$(selector);
13458 }
13459
13460 return collection.remove();
13461 }
13462};
13463
13464/* global Float32Array */
13465
13466/*! Bezier curve function generator. Copyright Gaetan Renaudeau. MIT License: http://en.wikipedia.org/wiki/MIT_License */
13467function generateCubicBezier(mX1, mY1, mX2, mY2) {
13468 var NEWTON_ITERATIONS = 4,
13469 NEWTON_MIN_SLOPE = 0.001,
13470 SUBDIVISION_PRECISION = 0.0000001,
13471 SUBDIVISION_MAX_ITERATIONS = 10,
13472 kSplineTableSize = 11,
13473 kSampleStepSize = 1.0 / (kSplineTableSize - 1.0),
13474 float32ArraySupported = typeof Float32Array !== 'undefined';
13475 /* Must contain four arguments. */
13476
13477 if (arguments.length !== 4) {
13478 return false;
13479 }
13480 /* Arguments must be numbers. */
13481
13482
13483 for (var i = 0; i < 4; ++i) {
13484 if (typeof arguments[i] !== "number" || isNaN(arguments[i]) || !isFinite(arguments[i])) {
13485 return false;
13486 }
13487 }
13488 /* X values must be in the [0, 1] range. */
13489
13490
13491 mX1 = Math.min(mX1, 1);
13492 mX2 = Math.min(mX2, 1);
13493 mX1 = Math.max(mX1, 0);
13494 mX2 = Math.max(mX2, 0);
13495 var mSampleValues = float32ArraySupported ? new Float32Array(kSplineTableSize) : new Array(kSplineTableSize);
13496
13497 function A(aA1, aA2) {
13498 return 1.0 - 3.0 * aA2 + 3.0 * aA1;
13499 }
13500
13501 function B(aA1, aA2) {
13502 return 3.0 * aA2 - 6.0 * aA1;
13503 }
13504
13505 function C(aA1) {
13506 return 3.0 * aA1;
13507 }
13508
13509 function calcBezier(aT, aA1, aA2) {
13510 return ((A(aA1, aA2) * aT + B(aA1, aA2)) * aT + C(aA1)) * aT;
13511 }
13512
13513 function getSlope(aT, aA1, aA2) {
13514 return 3.0 * A(aA1, aA2) * aT * aT + 2.0 * B(aA1, aA2) * aT + C(aA1);
13515 }
13516
13517 function newtonRaphsonIterate(aX, aGuessT) {
13518 for (var _i = 0; _i < NEWTON_ITERATIONS; ++_i) {
13519 var currentSlope = getSlope(aGuessT, mX1, mX2);
13520
13521 if (currentSlope === 0.0) {
13522 return aGuessT;
13523 }
13524
13525 var currentX = calcBezier(aGuessT, mX1, mX2) - aX;
13526 aGuessT -= currentX / currentSlope;
13527 }
13528
13529 return aGuessT;
13530 }
13531
13532 function calcSampleValues() {
13533 for (var _i2 = 0; _i2 < kSplineTableSize; ++_i2) {
13534 mSampleValues[_i2] = calcBezier(_i2 * kSampleStepSize, mX1, mX2);
13535 }
13536 }
13537
13538 function binarySubdivide(aX, aA, aB) {
13539 var currentX,
13540 currentT,
13541 i = 0;
13542
13543 do {
13544 currentT = aA + (aB - aA) / 2.0;
13545 currentX = calcBezier(currentT, mX1, mX2) - aX;
13546
13547 if (currentX > 0.0) {
13548 aB = currentT;
13549 } else {
13550 aA = currentT;
13551 }
13552 } while (Math.abs(currentX) > SUBDIVISION_PRECISION && ++i < SUBDIVISION_MAX_ITERATIONS);
13553
13554 return currentT;
13555 }
13556
13557 function getTForX(aX) {
13558 var intervalStart = 0.0,
13559 currentSample = 1,
13560 lastSample = kSplineTableSize - 1;
13561
13562 for (; currentSample !== lastSample && mSampleValues[currentSample] <= aX; ++currentSample) {
13563 intervalStart += kSampleStepSize;
13564 }
13565
13566 --currentSample;
13567 var dist = (aX - mSampleValues[currentSample]) / (mSampleValues[currentSample + 1] - mSampleValues[currentSample]),
13568 guessForT = intervalStart + dist * kSampleStepSize,
13569 initialSlope = getSlope(guessForT, mX1, mX2);
13570
13571 if (initialSlope >= NEWTON_MIN_SLOPE) {
13572 return newtonRaphsonIterate(aX, guessForT);
13573 } else if (initialSlope === 0.0) {
13574 return guessForT;
13575 } else {
13576 return binarySubdivide(aX, intervalStart, intervalStart + kSampleStepSize);
13577 }
13578 }
13579
13580 var _precomputed = false;
13581
13582 function precompute() {
13583 _precomputed = true;
13584
13585 if (mX1 !== mY1 || mX2 !== mY2) {
13586 calcSampleValues();
13587 }
13588 }
13589
13590 var f = function f(aX) {
13591 if (!_precomputed) {
13592 precompute();
13593 }
13594
13595 if (mX1 === mY1 && mX2 === mY2) {
13596 return aX;
13597 }
13598
13599 if (aX === 0) {
13600 return 0;
13601 }
13602
13603 if (aX === 1) {
13604 return 1;
13605 }
13606
13607 return calcBezier(getTForX(aX), mY1, mY2);
13608 };
13609
13610 f.getControlPoints = function () {
13611 return [{
13612 x: mX1,
13613 y: mY1
13614 }, {
13615 x: mX2,
13616 y: mY2
13617 }];
13618 };
13619
13620 var str = "generateBezier(" + [mX1, mY1, mX2, mY2] + ")";
13621
13622 f.toString = function () {
13623 return str;
13624 };
13625
13626 return f;
13627}
13628
13629/*! Runge-Kutta spring physics function generator. Adapted from Framer.js, copyright Koen Bok. MIT License: http://en.wikipedia.org/wiki/MIT_License */
13630
13631/* Given a tension, friction, and duration, a simulation at 60FPS will first run without a defined duration in order to calculate the full path. A second pass
13632 then adjusts the time delta -- using the relation between actual time and duration -- to calculate the path for the duration-constrained animation. */
13633var generateSpringRK4 = function () {
13634 function springAccelerationForState(state) {
13635 return -state.tension * state.x - state.friction * state.v;
13636 }
13637
13638 function springEvaluateStateWithDerivative(initialState, dt, derivative) {
13639 var state = {
13640 x: initialState.x + derivative.dx * dt,
13641 v: initialState.v + derivative.dv * dt,
13642 tension: initialState.tension,
13643 friction: initialState.friction
13644 };
13645 return {
13646 dx: state.v,
13647 dv: springAccelerationForState(state)
13648 };
13649 }
13650
13651 function springIntegrateState(state, dt) {
13652 var a = {
13653 dx: state.v,
13654 dv: springAccelerationForState(state)
13655 },
13656 b = springEvaluateStateWithDerivative(state, dt * 0.5, a),
13657 c = springEvaluateStateWithDerivative(state, dt * 0.5, b),
13658 d = springEvaluateStateWithDerivative(state, dt, c),
13659 dxdt = 1.0 / 6.0 * (a.dx + 2.0 * (b.dx + c.dx) + d.dx),
13660 dvdt = 1.0 / 6.0 * (a.dv + 2.0 * (b.dv + c.dv) + d.dv);
13661 state.x = state.x + dxdt * dt;
13662 state.v = state.v + dvdt * dt;
13663 return state;
13664 }
13665
13666 return function springRK4Factory(tension, friction, duration) {
13667 var initState = {
13668 x: -1,
13669 v: 0,
13670 tension: null,
13671 friction: null
13672 },
13673 path = [0],
13674 time_lapsed = 0,
13675 tolerance = 1 / 10000,
13676 DT = 16 / 1000,
13677 have_duration,
13678 dt,
13679 last_state;
13680 tension = parseFloat(tension) || 500;
13681 friction = parseFloat(friction) || 20;
13682 duration = duration || null;
13683 initState.tension = tension;
13684 initState.friction = friction;
13685 have_duration = duration !== null;
13686 /* Calculate the actual time it takes for this animation to complete with the provided conditions. */
13687
13688 if (have_duration) {
13689 /* Run the simulation without a duration. */
13690 time_lapsed = springRK4Factory(tension, friction);
13691 /* Compute the adjusted time delta. */
13692
13693 dt = time_lapsed / duration * DT;
13694 } else {
13695 dt = DT;
13696 }
13697
13698 for (;;) {
13699 /* Next/step function .*/
13700 last_state = springIntegrateState(last_state || initState, dt);
13701 /* Store the position. */
13702
13703 path.push(1 + last_state.x);
13704 time_lapsed += 16;
13705 /* If the change threshold is reached, break. */
13706
13707 if (!(Math.abs(last_state.x) > tolerance && Math.abs(last_state.v) > tolerance)) {
13708 break;
13709 }
13710 }
13711 /* If duration is not defined, return the actual time required for completing this animation. Otherwise, return a closure that holds the
13712 computed path and returns a snapshot of the position according to a given percentComplete. */
13713
13714
13715 return !have_duration ? time_lapsed : function (percentComplete) {
13716 return path[percentComplete * (path.length - 1) | 0];
13717 };
13718 };
13719}();
13720
13721var cubicBezier = function cubicBezier(t1, p1, t2, p2) {
13722 var bezier = generateCubicBezier(t1, p1, t2, p2);
13723 return function (start, end, percent) {
13724 return start + (end - start) * bezier(percent);
13725 };
13726};
13727
13728var easings = {
13729 'linear': function linear(start, end, percent) {
13730 return start + (end - start) * percent;
13731 },
13732 // default easings
13733 'ease': cubicBezier(0.25, 0.1, 0.25, 1),
13734 'ease-in': cubicBezier(0.42, 0, 1, 1),
13735 'ease-out': cubicBezier(0, 0, 0.58, 1),
13736 'ease-in-out': cubicBezier(0.42, 0, 0.58, 1),
13737 // sine
13738 'ease-in-sine': cubicBezier(0.47, 0, 0.745, 0.715),
13739 'ease-out-sine': cubicBezier(0.39, 0.575, 0.565, 1),
13740 'ease-in-out-sine': cubicBezier(0.445, 0.05, 0.55, 0.95),
13741 // quad
13742 'ease-in-quad': cubicBezier(0.55, 0.085, 0.68, 0.53),
13743 'ease-out-quad': cubicBezier(0.25, 0.46, 0.45, 0.94),
13744 'ease-in-out-quad': cubicBezier(0.455, 0.03, 0.515, 0.955),
13745 // cubic
13746 'ease-in-cubic': cubicBezier(0.55, 0.055, 0.675, 0.19),
13747 'ease-out-cubic': cubicBezier(0.215, 0.61, 0.355, 1),
13748 'ease-in-out-cubic': cubicBezier(0.645, 0.045, 0.355, 1),
13749 // quart
13750 'ease-in-quart': cubicBezier(0.895, 0.03, 0.685, 0.22),
13751 'ease-out-quart': cubicBezier(0.165, 0.84, 0.44, 1),
13752 'ease-in-out-quart': cubicBezier(0.77, 0, 0.175, 1),
13753 // quint
13754 'ease-in-quint': cubicBezier(0.755, 0.05, 0.855, 0.06),
13755 'ease-out-quint': cubicBezier(0.23, 1, 0.32, 1),
13756 'ease-in-out-quint': cubicBezier(0.86, 0, 0.07, 1),
13757 // expo
13758 'ease-in-expo': cubicBezier(0.95, 0.05, 0.795, 0.035),
13759 'ease-out-expo': cubicBezier(0.19, 1, 0.22, 1),
13760 'ease-in-out-expo': cubicBezier(1, 0, 0, 1),
13761 // circ
13762 'ease-in-circ': cubicBezier(0.6, 0.04, 0.98, 0.335),
13763 'ease-out-circ': cubicBezier(0.075, 0.82, 0.165, 1),
13764 'ease-in-out-circ': cubicBezier(0.785, 0.135, 0.15, 0.86),
13765 // user param easings...
13766 'spring': function spring(tension, friction, duration) {
13767 if (duration === 0) {
13768 // can't get a spring w/ duration 0
13769 return easings.linear; // duration 0 => jump to end so impl doesn't matter
13770 }
13771
13772 var spring = generateSpringRK4(tension, friction, duration);
13773 return function (start, end, percent) {
13774 return start + (end - start) * spring(percent);
13775 };
13776 },
13777 'cubic-bezier': cubicBezier
13778};
13779
13780function getEasedValue(type, start, end, percent, easingFn) {
13781 if (percent === 1) {
13782 return end;
13783 }
13784
13785 if (start === end) {
13786 return end;
13787 }
13788
13789 var val = easingFn(start, end, percent);
13790
13791 if (type == null) {
13792 return val;
13793 }
13794
13795 if (type.roundValue || type.color) {
13796 val = Math.round(val);
13797 }
13798
13799 if (type.min !== undefined) {
13800 val = Math.max(val, type.min);
13801 }
13802
13803 if (type.max !== undefined) {
13804 val = Math.min(val, type.max);
13805 }
13806
13807 return val;
13808}
13809
13810function getValue(prop, spec) {
13811 if (prop.pfValue != null || prop.value != null) {
13812 if (prop.pfValue != null && (spec == null || spec.type.units !== '%')) {
13813 return prop.pfValue;
13814 } else {
13815 return prop.value;
13816 }
13817 } else {
13818 return prop;
13819 }
13820}
13821
13822function ease(startProp, endProp, percent, easingFn, propSpec) {
13823 var type = propSpec != null ? propSpec.type : null;
13824
13825 if (percent < 0) {
13826 percent = 0;
13827 } else if (percent > 1) {
13828 percent = 1;
13829 }
13830
13831 var start = getValue(startProp, propSpec);
13832 var end = getValue(endProp, propSpec);
13833
13834 if (number$1(start) && number$1(end)) {
13835 return getEasedValue(type, start, end, percent, easingFn);
13836 } else if (array(start) && array(end)) {
13837 var easedArr = [];
13838
13839 for (var i = 0; i < end.length; i++) {
13840 var si = start[i];
13841 var ei = end[i];
13842
13843 if (si != null && ei != null) {
13844 var val = getEasedValue(type, si, ei, percent, easingFn);
13845 easedArr.push(val);
13846 } else {
13847 easedArr.push(ei);
13848 }
13849 }
13850
13851 return easedArr;
13852 }
13853
13854 return undefined;
13855}
13856
13857function step$1(self, ani, now, isCore) {
13858 var isEles = !isCore;
13859 var _p = self._private;
13860 var ani_p = ani._private;
13861 var pEasing = ani_p.easing;
13862 var startTime = ani_p.startTime;
13863 var cy = isCore ? self : self.cy();
13864 var style = cy.style();
13865
13866 if (!ani_p.easingImpl) {
13867 if (pEasing == null) {
13868 // use default
13869 ani_p.easingImpl = easings['linear'];
13870 } else {
13871 // then define w/ name
13872 var easingVals;
13873
13874 if (string(pEasing)) {
13875 var easingProp = style.parse('transition-timing-function', pEasing);
13876 easingVals = easingProp.value;
13877 } else {
13878 // then assume preparsed array
13879 easingVals = pEasing;
13880 }
13881
13882 var name, args;
13883
13884 if (string(easingVals)) {
13885 name = easingVals;
13886 args = [];
13887 } else {
13888 name = easingVals[1];
13889 args = easingVals.slice(2).map(function (n) {
13890 return +n;
13891 });
13892 }
13893
13894 if (args.length > 0) {
13895 // create with args
13896 if (name === 'spring') {
13897 args.push(ani_p.duration); // need duration to generate spring
13898 }
13899
13900 ani_p.easingImpl = easings[name].apply(null, args);
13901 } else {
13902 // static impl by name
13903 ani_p.easingImpl = easings[name];
13904 }
13905 }
13906 }
13907
13908 var easing = ani_p.easingImpl;
13909 var percent;
13910
13911 if (ani_p.duration === 0) {
13912 percent = 1;
13913 } else {
13914 percent = (now - startTime) / ani_p.duration;
13915 }
13916
13917 if (ani_p.applying) {
13918 percent = ani_p.progress;
13919 }
13920
13921 if (percent < 0) {
13922 percent = 0;
13923 } else if (percent > 1) {
13924 percent = 1;
13925 }
13926
13927 if (ani_p.delay == null) {
13928 // then update
13929 var startPos = ani_p.startPosition;
13930 var endPos = ani_p.position;
13931
13932 if (endPos && isEles && !self.locked()) {
13933 var newPos = {};
13934
13935 if (valid(startPos.x, endPos.x)) {
13936 newPos.x = ease(startPos.x, endPos.x, percent, easing);
13937 }
13938
13939 if (valid(startPos.y, endPos.y)) {
13940 newPos.y = ease(startPos.y, endPos.y, percent, easing);
13941 }
13942
13943 self.position(newPos);
13944 }
13945
13946 var startPan = ani_p.startPan;
13947 var endPan = ani_p.pan;
13948 var pan = _p.pan;
13949 var animatingPan = endPan != null && isCore;
13950
13951 if (animatingPan) {
13952 if (valid(startPan.x, endPan.x)) {
13953 pan.x = ease(startPan.x, endPan.x, percent, easing);
13954 }
13955
13956 if (valid(startPan.y, endPan.y)) {
13957 pan.y = ease(startPan.y, endPan.y, percent, easing);
13958 }
13959
13960 self.emit('pan');
13961 }
13962
13963 var startZoom = ani_p.startZoom;
13964 var endZoom = ani_p.zoom;
13965 var animatingZoom = endZoom != null && isCore;
13966
13967 if (animatingZoom) {
13968 if (valid(startZoom, endZoom)) {
13969 _p.zoom = bound(_p.minZoom, ease(startZoom, endZoom, percent, easing), _p.maxZoom);
13970 }
13971
13972 self.emit('zoom');
13973 }
13974
13975 if (animatingPan || animatingZoom) {
13976 self.emit('viewport');
13977 }
13978
13979 var props = ani_p.style;
13980
13981 if (props && props.length > 0 && isEles) {
13982 for (var i = 0; i < props.length; i++) {
13983 var prop = props[i];
13984 var _name = prop.name;
13985 var end = prop;
13986 var start = ani_p.startStyle[_name];
13987 var propSpec = style.properties[start.name];
13988 var easedVal = ease(start, end, percent, easing, propSpec);
13989 style.overrideBypass(self, _name, easedVal);
13990 } // for props
13991
13992
13993 self.emit('style');
13994 } // if
13995
13996 }
13997
13998 ani_p.progress = percent;
13999 return percent;
14000}
14001
14002function valid(start, end) {
14003 if (start == null || end == null) {
14004 return false;
14005 }
14006
14007 if (number$1(start) && number$1(end)) {
14008 return true;
14009 } else if (start && end) {
14010 return true;
14011 }
14012
14013 return false;
14014}
14015
14016function startAnimation(self, ani, now, isCore) {
14017 var ani_p = ani._private;
14018 ani_p.started = true;
14019 ani_p.startTime = now - ani_p.progress * ani_p.duration;
14020}
14021
14022function stepAll(now, cy) {
14023 var eles = cy._private.aniEles;
14024 var doneEles = [];
14025
14026 function stepOne(ele, isCore) {
14027 var _p = ele._private;
14028 var current = _p.animation.current;
14029 var queue = _p.animation.queue;
14030 var ranAnis = false; // if nothing currently animating, get something from the queue
14031
14032 if (current.length === 0) {
14033 var next = queue.shift();
14034
14035 if (next) {
14036 current.push(next);
14037 }
14038 }
14039
14040 var callbacks = function callbacks(_callbacks) {
14041 for (var j = _callbacks.length - 1; j >= 0; j--) {
14042 var cb = _callbacks[j];
14043 cb();
14044 }
14045
14046 _callbacks.splice(0, _callbacks.length);
14047 }; // step and remove if done
14048
14049
14050 for (var i = current.length - 1; i >= 0; i--) {
14051 var ani = current[i];
14052 var ani_p = ani._private;
14053
14054 if (ani_p.stopped) {
14055 current.splice(i, 1);
14056 ani_p.hooked = false;
14057 ani_p.playing = false;
14058 ani_p.started = false;
14059 callbacks(ani_p.frames);
14060 continue;
14061 }
14062
14063 if (!ani_p.playing && !ani_p.applying) {
14064 continue;
14065 } // an apply() while playing shouldn't do anything
14066
14067
14068 if (ani_p.playing && ani_p.applying) {
14069 ani_p.applying = false;
14070 }
14071
14072 if (!ani_p.started) {
14073 startAnimation(ele, ani, now);
14074 }
14075
14076 step$1(ele, ani, now, isCore);
14077
14078 if (ani_p.applying) {
14079 ani_p.applying = false;
14080 }
14081
14082 callbacks(ani_p.frames);
14083
14084 if (ani_p.step != null) {
14085 ani_p.step(now);
14086 }
14087
14088 if (ani.completed()) {
14089 current.splice(i, 1);
14090 ani_p.hooked = false;
14091 ani_p.playing = false;
14092 ani_p.started = false;
14093 callbacks(ani_p.completes);
14094 }
14095
14096 ranAnis = true;
14097 }
14098
14099 if (!isCore && current.length === 0 && queue.length === 0) {
14100 doneEles.push(ele);
14101 }
14102
14103 return ranAnis;
14104 } // stepElement
14105 // handle all eles
14106
14107
14108 var ranEleAni = false;
14109
14110 for (var e = 0; e < eles.length; e++) {
14111 var ele = eles[e];
14112 var handledThisEle = stepOne(ele);
14113 ranEleAni = ranEleAni || handledThisEle;
14114 } // each element
14115
14116
14117 var ranCoreAni = stepOne(cy, true); // notify renderer
14118
14119 if (ranEleAni || ranCoreAni) {
14120 if (eles.length > 0) {
14121 cy.notify('draw', eles);
14122 } else {
14123 cy.notify('draw');
14124 }
14125 } // remove elements from list of currently animating if its queues are empty
14126
14127
14128 eles.unmerge(doneEles);
14129 cy.emit('step');
14130} // stepAll
14131
14132var corefn$8 = {
14133 // pull in animation functions
14134 animate: define.animate(),
14135 animation: define.animation(),
14136 animated: define.animated(),
14137 clearQueue: define.clearQueue(),
14138 delay: define.delay(),
14139 delayAnimation: define.delayAnimation(),
14140 stop: define.stop(),
14141 addToAnimationPool: function addToAnimationPool(eles) {
14142 var cy = this;
14143
14144 if (!cy.styleEnabled()) {
14145 return;
14146 } // save cycles when no style used
14147
14148
14149 cy._private.aniEles.merge(eles);
14150 },
14151 stopAnimationLoop: function stopAnimationLoop() {
14152 this._private.animationsRunning = false;
14153 },
14154 startAnimationLoop: function startAnimationLoop() {
14155 var cy = this;
14156 cy._private.animationsRunning = true;
14157
14158 if (!cy.styleEnabled()) {
14159 return;
14160 } // save cycles when no style used
14161 // NB the animation loop will exec in headless environments if style enabled
14162 // and explicit cy.destroy() is necessary to stop the loop
14163
14164
14165 function headlessStep() {
14166 if (!cy._private.animationsRunning) {
14167 return;
14168 }
14169
14170 requestAnimationFrame(function animationStep(now) {
14171 stepAll(now, cy);
14172 headlessStep();
14173 });
14174 }
14175
14176 var renderer = cy.renderer();
14177
14178 if (renderer && renderer.beforeRender) {
14179 // let the renderer schedule animations
14180 renderer.beforeRender(function rendererAnimationStep(willDraw, now) {
14181 stepAll(now, cy);
14182 }, renderer.beforeRenderPriorities.animations);
14183 } else {
14184 // manage the animation loop ourselves
14185 headlessStep(); // first call
14186 }
14187 }
14188};
14189
14190var emitterOptions = {
14191 qualifierCompare: function qualifierCompare(selector1, selector2) {
14192 if (selector1 == null || selector2 == null) {
14193 return selector1 == null && selector2 == null;
14194 } else {
14195 return selector1.sameText(selector2);
14196 }
14197 },
14198 eventMatches: function eventMatches(cy, listener, eventObj) {
14199 var selector = listener.qualifier;
14200
14201 if (selector != null) {
14202 return cy !== eventObj.target && element(eventObj.target) && selector.matches(eventObj.target);
14203 }
14204
14205 return true;
14206 },
14207 addEventFields: function addEventFields(cy, evt) {
14208 evt.cy = cy;
14209 evt.target = cy;
14210 },
14211 callbackContext: function callbackContext(cy, listener, eventObj) {
14212 return listener.qualifier != null ? eventObj.target : cy;
14213 }
14214};
14215
14216var argSelector = function argSelector(arg) {
14217 if (string(arg)) {
14218 return new Selector(arg);
14219 } else {
14220 return arg;
14221 }
14222};
14223
14224var elesfn = {
14225 createEmitter: function createEmitter() {
14226 var _p = this._private;
14227
14228 if (!_p.emitter) {
14229 _p.emitter = new Emitter(emitterOptions, this);
14230 }
14231
14232 return this;
14233 },
14234 emitter: function emitter() {
14235 return this._private.emitter;
14236 },
14237 on: function on(events, selector, callback) {
14238 this.emitter().on(events, argSelector(selector), callback);
14239 return this;
14240 },
14241 removeListener: function removeListener(events, selector, callback) {
14242 this.emitter().removeListener(events, argSelector(selector), callback);
14243 return this;
14244 },
14245 removeAllListeners: function removeAllListeners() {
14246 this.emitter().removeAllListeners();
14247 return this;
14248 },
14249 one: function one(events, selector, callback) {
14250 this.emitter().one(events, argSelector(selector), callback);
14251 return this;
14252 },
14253 once: function once(events, selector, callback) {
14254 this.emitter().one(events, argSelector(selector), callback);
14255 return this;
14256 },
14257 emit: function emit(events, extraParams) {
14258 this.emitter().emit(events, extraParams);
14259 return this;
14260 },
14261 emitAndNotify: function emitAndNotify(event, eles) {
14262 this.emit(event);
14263 this.notify(event, eles);
14264 return this;
14265 }
14266};
14267define.eventAliasesOn(elesfn);
14268
14269var corefn$7 = {
14270 png: function png(options) {
14271 var renderer = this._private.renderer;
14272 options = options || {};
14273 return renderer.png(options);
14274 },
14275 jpg: function jpg(options) {
14276 var renderer = this._private.renderer;
14277 options = options || {};
14278 options.bg = options.bg || '#fff';
14279 return renderer.jpg(options);
14280 }
14281};
14282corefn$7.jpeg = corefn$7.jpg;
14283
14284var corefn$6 = {
14285 layout: function layout(options) {
14286 var cy = this;
14287
14288 if (options == null) {
14289 error('Layout options must be specified to make a layout');
14290 return;
14291 }
14292
14293 if (options.name == null) {
14294 error('A `name` must be specified to make a layout');
14295 return;
14296 }
14297
14298 var name = options.name;
14299 var Layout = cy.extension('layout', name);
14300
14301 if (Layout == null) {
14302 error('No such layout `' + name + '` found. Did you forget to import it and `cytoscape.use()` it?');
14303 return;
14304 }
14305
14306 var eles;
14307
14308 if (string(options.eles)) {
14309 eles = cy.$(options.eles);
14310 } else {
14311 eles = options.eles != null ? options.eles : cy.$();
14312 }
14313
14314 var layout = new Layout(extend({}, options, {
14315 cy: cy,
14316 eles: eles
14317 }));
14318 return layout;
14319 }
14320};
14321corefn$6.createLayout = corefn$6.makeLayout = corefn$6.layout;
14322
14323var corefn$5 = {
14324 notify: function notify(eventName, eventEles) {
14325 var _p = this._private;
14326
14327 if (this.batching()) {
14328 _p.batchNotifications = _p.batchNotifications || {};
14329 var eles = _p.batchNotifications[eventName] = _p.batchNotifications[eventName] || this.collection();
14330
14331 if (eventEles != null) {
14332 eles.merge(eventEles);
14333 }
14334
14335 return; // notifications are disabled during batching
14336 }
14337
14338 if (!_p.notificationsEnabled) {
14339 return;
14340 } // exit on disabled
14341
14342
14343 var renderer = this.renderer(); // exit if destroy() called on core or renderer in between frames #1499 #1528
14344
14345 if (this.destroyed() || !renderer) {
14346 return;
14347 }
14348
14349 renderer.notify(eventName, eventEles);
14350 },
14351 notifications: function notifications(bool) {
14352 var p = this._private;
14353
14354 if (bool === undefined) {
14355 return p.notificationsEnabled;
14356 } else {
14357 p.notificationsEnabled = bool ? true : false;
14358 }
14359
14360 return this;
14361 },
14362 noNotifications: function noNotifications(callback) {
14363 this.notifications(false);
14364 callback();
14365 this.notifications(true);
14366 },
14367 batching: function batching() {
14368 return this._private.batchCount > 0;
14369 },
14370 startBatch: function startBatch() {
14371 var _p = this._private;
14372
14373 if (_p.batchCount == null) {
14374 _p.batchCount = 0;
14375 }
14376
14377 if (_p.batchCount === 0) {
14378 _p.batchStyleEles = this.collection();
14379 _p.batchNotifications = {};
14380 }
14381
14382 _p.batchCount++;
14383 return this;
14384 },
14385 endBatch: function endBatch() {
14386 var _p = this._private;
14387
14388 if (_p.batchCount === 0) {
14389 return this;
14390 }
14391
14392 _p.batchCount--;
14393
14394 if (_p.batchCount === 0) {
14395 // update style for dirty eles
14396 _p.batchStyleEles.updateStyle();
14397
14398 var renderer = this.renderer(); // notify the renderer of queued eles and event types
14399
14400 Object.keys(_p.batchNotifications).forEach(function (eventName) {
14401 var eles = _p.batchNotifications[eventName];
14402
14403 if (eles.empty()) {
14404 renderer.notify(eventName);
14405 } else {
14406 renderer.notify(eventName, eles);
14407 }
14408 });
14409 }
14410
14411 return this;
14412 },
14413 batch: function batch(callback) {
14414 this.startBatch();
14415 callback();
14416 this.endBatch();
14417 return this;
14418 },
14419 // for backwards compatibility
14420 batchData: function batchData(map) {
14421 var cy = this;
14422 return this.batch(function () {
14423 var ids = Object.keys(map);
14424
14425 for (var i = 0; i < ids.length; i++) {
14426 var id = ids[i];
14427 var data = map[id];
14428 var ele = cy.getElementById(id);
14429 ele.data(data);
14430 }
14431 });
14432 }
14433};
14434
14435var rendererDefaults = defaults$g({
14436 hideEdgesOnViewport: false,
14437 textureOnViewport: false,
14438 motionBlur: false,
14439 motionBlurOpacity: 0.05,
14440 pixelRatio: undefined,
14441 desktopTapThreshold: 4,
14442 touchTapThreshold: 8,
14443 wheelSensitivity: 1,
14444 debug: false,
14445 showFps: false
14446});
14447var corefn$4 = {
14448 renderTo: function renderTo(context, zoom, pan, pxRatio) {
14449 var r = this._private.renderer;
14450 r.renderTo(context, zoom, pan, pxRatio);
14451 return this;
14452 },
14453 renderer: function renderer() {
14454 return this._private.renderer;
14455 },
14456 forceRender: function forceRender() {
14457 this.notify('draw');
14458 return this;
14459 },
14460 resize: function resize() {
14461 this.invalidateSize();
14462 this.emitAndNotify('resize');
14463 return this;
14464 },
14465 initRenderer: function initRenderer(options) {
14466 var cy = this;
14467 var RendererProto = cy.extension('renderer', options.name);
14468
14469 if (RendererProto == null) {
14470 error("Can not initialise: No such renderer `".concat(options.name, "` found. Did you forget to import it and `cytoscape.use()` it?"));
14471 return;
14472 }
14473
14474 if (options.wheelSensitivity !== undefined) {
14475 warn("You have set a custom wheel sensitivity. This will make your app zoom unnaturally when using mainstream mice. You should change this value from the default only if you can guarantee that all your users will use the same hardware and OS configuration as your current machine.");
14476 }
14477
14478 var rOpts = rendererDefaults(options);
14479 rOpts.cy = cy;
14480 cy._private.renderer = new RendererProto(rOpts);
14481 this.notify('init');
14482 },
14483 destroyRenderer: function destroyRenderer() {
14484 var cy = this;
14485 cy.notify('destroy'); // destroy the renderer
14486
14487 var domEle = cy.container();
14488
14489 if (domEle) {
14490 domEle._cyreg = null;
14491
14492 while (domEle.childNodes.length > 0) {
14493 domEle.removeChild(domEle.childNodes[0]);
14494 }
14495 }
14496
14497 cy._private.renderer = null; // to be extra safe, remove the ref
14498
14499 cy.mutableElements().forEach(function (ele) {
14500 var _p = ele._private;
14501 _p.rscratch = {};
14502 _p.rstyle = {};
14503 _p.animation.current = [];
14504 _p.animation.queue = [];
14505 });
14506 },
14507 onRender: function onRender(fn) {
14508 return this.on('render', fn);
14509 },
14510 offRender: function offRender(fn) {
14511 return this.off('render', fn);
14512 }
14513};
14514corefn$4.invalidateDimensions = corefn$4.resize;
14515
14516var corefn$3 = {
14517 // get a collection
14518 // - empty collection on no args
14519 // - collection of elements in the graph on selector arg
14520 // - guarantee a returned collection when elements or collection specified
14521 collection: function collection(eles, opts) {
14522 if (string(eles)) {
14523 return this.$(eles);
14524 } else if (elementOrCollection(eles)) {
14525 return eles.collection();
14526 } else if (array(eles)) {
14527 if (!opts) {
14528 opts = {};
14529 }
14530
14531 return new Collection(this, eles, opts.unique, opts.removed);
14532 }
14533
14534 return new Collection(this);
14535 },
14536 nodes: function nodes(selector) {
14537 var nodes = this.$(function (ele) {
14538 return ele.isNode();
14539 });
14540
14541 if (selector) {
14542 return nodes.filter(selector);
14543 }
14544
14545 return nodes;
14546 },
14547 edges: function edges(selector) {
14548 var edges = this.$(function (ele) {
14549 return ele.isEdge();
14550 });
14551
14552 if (selector) {
14553 return edges.filter(selector);
14554 }
14555
14556 return edges;
14557 },
14558 // search the graph like jQuery
14559 $: function $(selector) {
14560 var eles = this._private.elements;
14561
14562 if (selector) {
14563 return eles.filter(selector);
14564 } else {
14565 return eles.spawnSelf();
14566 }
14567 },
14568 mutableElements: function mutableElements() {
14569 return this._private.elements;
14570 }
14571}; // aliases
14572
14573corefn$3.elements = corefn$3.filter = corefn$3.$;
14574
14575var styfn$8 = {}; // keys for style blocks, e.g. ttfftt
14576
14577var TRUE = 't';
14578var FALSE = 'f'; // (potentially expensive calculation)
14579// apply the style to the element based on
14580// - its bypass
14581// - what selectors match it
14582
14583styfn$8.apply = function (eles) {
14584 var self = this;
14585 var _p = self._private;
14586 var cy = _p.cy;
14587 var updatedEles = cy.collection();
14588
14589 for (var ie = 0; ie < eles.length; ie++) {
14590 var ele = eles[ie];
14591 var cxtMeta = self.getContextMeta(ele);
14592
14593 if (cxtMeta.empty) {
14594 continue;
14595 }
14596
14597 var cxtStyle = self.getContextStyle(cxtMeta);
14598 var app = self.applyContextStyle(cxtMeta, cxtStyle, ele);
14599
14600 if (ele._private.appliedInitStyle) {
14601 self.updateTransitions(ele, app.diffProps);
14602 } else {
14603 ele._private.appliedInitStyle = true;
14604 }
14605
14606 var hintsDiff = self.updateStyleHints(ele);
14607
14608 if (hintsDiff) {
14609 updatedEles.push(ele);
14610 }
14611 } // for elements
14612
14613
14614 return updatedEles;
14615};
14616
14617styfn$8.getPropertiesDiff = function (oldCxtKey, newCxtKey) {
14618 var self = this;
14619 var cache = self._private.propDiffs = self._private.propDiffs || {};
14620 var dualCxtKey = oldCxtKey + '-' + newCxtKey;
14621 var cachedVal = cache[dualCxtKey];
14622
14623 if (cachedVal) {
14624 return cachedVal;
14625 }
14626
14627 var diffProps = [];
14628 var addedProp = {};
14629
14630 for (var i = 0; i < self.length; i++) {
14631 var cxt = self[i];
14632 var oldHasCxt = oldCxtKey[i] === TRUE;
14633 var newHasCxt = newCxtKey[i] === TRUE;
14634 var cxtHasDiffed = oldHasCxt !== newHasCxt;
14635 var cxtHasMappedProps = cxt.mappedProperties.length > 0;
14636
14637 if (cxtHasDiffed || newHasCxt && cxtHasMappedProps) {
14638 var props = void 0;
14639
14640 if (cxtHasDiffed && cxtHasMappedProps) {
14641 props = cxt.properties; // suffices b/c mappedProperties is a subset of properties
14642 } else if (cxtHasDiffed) {
14643 props = cxt.properties; // need to check them all
14644 } else if (cxtHasMappedProps) {
14645 props = cxt.mappedProperties; // only need to check mapped
14646 }
14647
14648 for (var j = 0; j < props.length; j++) {
14649 var prop = props[j];
14650 var name = prop.name; // if a later context overrides this property, then the fact that this context has switched/diffed doesn't matter
14651 // (semi expensive check since it makes this function O(n^2) on context length, but worth it since overall result
14652 // is cached)
14653
14654 var laterCxtOverrides = false;
14655
14656 for (var k = i + 1; k < self.length; k++) {
14657 var laterCxt = self[k];
14658 var hasLaterCxt = newCxtKey[k] === TRUE;
14659
14660 if (!hasLaterCxt) {
14661 continue;
14662 } // can't override unless the context is active
14663
14664
14665 laterCxtOverrides = laterCxt.properties[prop.name] != null;
14666
14667 if (laterCxtOverrides) {
14668 break;
14669 } // exit early as long as one later context overrides
14670
14671 }
14672
14673 if (!addedProp[name] && !laterCxtOverrides) {
14674 addedProp[name] = true;
14675 diffProps.push(name);
14676 }
14677 } // for props
14678
14679 } // if
14680
14681 } // for contexts
14682
14683
14684 cache[dualCxtKey] = diffProps;
14685 return diffProps;
14686};
14687
14688styfn$8.getContextMeta = function (ele) {
14689 var self = this;
14690 var cxtKey = '';
14691 var diffProps;
14692 var prevKey = ele._private.styleCxtKey || ''; // get the cxt key
14693
14694 for (var i = 0; i < self.length; i++) {
14695 var context = self[i];
14696 var contextSelectorMatches = context.selector && context.selector.matches(ele); // NB: context.selector may be null for 'core'
14697
14698 if (contextSelectorMatches) {
14699 cxtKey += TRUE;
14700 } else {
14701 cxtKey += FALSE;
14702 }
14703 } // for context
14704
14705
14706 diffProps = self.getPropertiesDiff(prevKey, cxtKey);
14707 ele._private.styleCxtKey = cxtKey;
14708 return {
14709 key: cxtKey,
14710 diffPropNames: diffProps,
14711 empty: diffProps.length === 0
14712 };
14713}; // gets a computed ele style object based on matched contexts
14714
14715
14716styfn$8.getContextStyle = function (cxtMeta) {
14717 var cxtKey = cxtMeta.key;
14718 var self = this;
14719 var cxtStyles = this._private.contextStyles = this._private.contextStyles || {}; // if already computed style, returned cached copy
14720
14721 if (cxtStyles[cxtKey]) {
14722 return cxtStyles[cxtKey];
14723 }
14724
14725 var style = {
14726 _private: {
14727 key: cxtKey
14728 }
14729 };
14730
14731 for (var i = 0; i < self.length; i++) {
14732 var cxt = self[i];
14733 var hasCxt = cxtKey[i] === TRUE;
14734
14735 if (!hasCxt) {
14736 continue;
14737 }
14738
14739 for (var j = 0; j < cxt.properties.length; j++) {
14740 var prop = cxt.properties[j];
14741 style[prop.name] = prop;
14742 }
14743 }
14744
14745 cxtStyles[cxtKey] = style;
14746 return style;
14747};
14748
14749styfn$8.applyContextStyle = function (cxtMeta, cxtStyle, ele) {
14750 var self = this;
14751 var diffProps = cxtMeta.diffPropNames;
14752 var retDiffProps = {};
14753 var types = self.types;
14754
14755 for (var i = 0; i < diffProps.length; i++) {
14756 var diffPropName = diffProps[i];
14757 var cxtProp = cxtStyle[diffPropName];
14758 var eleProp = ele.pstyle(diffPropName);
14759
14760 if (!cxtProp) {
14761 // no context prop means delete
14762 if (!eleProp) {
14763 continue; // no existing prop means nothing needs to be removed
14764 // nb affects initial application on mapped values like control-point-distances
14765 } else if (eleProp.bypass) {
14766 cxtProp = {
14767 name: diffPropName,
14768 deleteBypassed: true
14769 };
14770 } else {
14771 cxtProp = {
14772 name: diffPropName,
14773 "delete": true
14774 };
14775 }
14776 } // save cycles when the context prop doesn't need to be applied
14777
14778
14779 if (eleProp === cxtProp) {
14780 continue;
14781 } // save cycles when a mapped context prop doesn't need to be applied
14782
14783
14784 if (cxtProp.mapped === types.fn // context prop is function mapper
14785 && eleProp != null // some props can be null even by default (e.g. a prop that overrides another one)
14786 && eleProp.mapping != null // ele prop is a concrete value from from a mapper
14787 && eleProp.mapping.value === cxtProp.value // the current prop on the ele is a flat prop value for the function mapper
14788 ) {
14789 // NB don't write to cxtProp, as it's shared among eles (stored in stylesheet)
14790 var mapping = eleProp.mapping; // can write to mapping, as it's a per-ele copy
14791
14792 var fnValue = mapping.fnValue = cxtProp.value(ele); // temporarily cache the value in case of a miss
14793
14794 if (fnValue === mapping.prevFnValue) {
14795 continue;
14796 }
14797 }
14798
14799 var retDiffProp = retDiffProps[diffPropName] = {
14800 prev: eleProp
14801 };
14802 self.applyParsedProperty(ele, cxtProp);
14803 retDiffProp.next = ele.pstyle(diffPropName);
14804
14805 if (retDiffProp.next && retDiffProp.next.bypass) {
14806 retDiffProp.next = retDiffProp.next.bypassed;
14807 }
14808 }
14809
14810 return {
14811 diffProps: retDiffProps
14812 };
14813};
14814
14815styfn$8.updateStyleHints = function (ele) {
14816 var _p = ele._private;
14817 var self = this;
14818 var propNames = self.propertyGroupNames;
14819 var propGrKeys = self.propertyGroupKeys;
14820
14821 var propHash = function propHash(ele, propNames, seedKey) {
14822 return self.getPropertiesHash(ele, propNames, seedKey);
14823 };
14824
14825 var oldStyleKey = _p.styleKey;
14826
14827 if (ele.removed()) {
14828 return false;
14829 }
14830
14831 var isNode = _p.group === 'nodes'; // get the style key hashes per prop group
14832 // but lazily -- only use non-default prop values to reduce the number of hashes
14833 //
14834
14835 var overriddenStyles = ele._private.style;
14836 propNames = Object.keys(overriddenStyles);
14837
14838 for (var i = 0; i < propGrKeys.length; i++) {
14839 var grKey = propGrKeys[i];
14840 _p.styleKeys[grKey] = [DEFAULT_HASH_SEED, DEFAULT_HASH_SEED_ALT];
14841 }
14842
14843 var updateGrKey1 = function updateGrKey1(val, grKey) {
14844 return _p.styleKeys[grKey][0] = hashInt(val, _p.styleKeys[grKey][0]);
14845 };
14846
14847 var updateGrKey2 = function updateGrKey2(val, grKey) {
14848 return _p.styleKeys[grKey][1] = hashIntAlt(val, _p.styleKeys[grKey][1]);
14849 };
14850
14851 var updateGrKey = function updateGrKey(val, grKey) {
14852 updateGrKey1(val, grKey);
14853 updateGrKey2(val, grKey);
14854 };
14855
14856 var updateGrKeyWStr = function updateGrKeyWStr(strVal, grKey) {
14857 for (var j = 0; j < strVal.length; j++) {
14858 var ch = strVal.charCodeAt(j);
14859 updateGrKey1(ch, grKey);
14860 updateGrKey2(ch, grKey);
14861 }
14862 }; // - hashing works on 32 bit ints b/c we use bitwise ops
14863 // - small numbers get cut off (e.g. 0.123 is seen as 0 by the hashing function)
14864 // - raise up small numbers so more significant digits are seen by hashing
14865 // - make small numbers larger than a normal value to avoid collisions
14866 // - works in practice and it's relatively cheap
14867
14868
14869 var N = 2000000000;
14870
14871 var cleanNum = function cleanNum(val) {
14872 return -128 < val && val < 128 && Math.floor(val) !== val ? N - (val * 1024 | 0) : val;
14873 };
14874
14875 for (var _i = 0; _i < propNames.length; _i++) {
14876 var name = propNames[_i];
14877 var parsedProp = overriddenStyles[name];
14878
14879 if (parsedProp == null) {
14880 continue;
14881 }
14882
14883 var propInfo = this.properties[name];
14884 var type = propInfo.type;
14885 var _grKey = propInfo.groupKey;
14886 var normalizedNumberVal = void 0;
14887
14888 if (propInfo.hashOverride != null) {
14889 normalizedNumberVal = propInfo.hashOverride(ele, parsedProp);
14890 } else if (parsedProp.pfValue != null) {
14891 normalizedNumberVal = parsedProp.pfValue;
14892 } // might not be a number if it allows enums
14893
14894
14895 var numberVal = propInfo.enums == null ? parsedProp.value : null;
14896 var haveNormNum = normalizedNumberVal != null;
14897 var haveUnitedNum = numberVal != null;
14898 var haveNum = haveNormNum || haveUnitedNum;
14899 var units = parsedProp.units; // numbers are cheaper to hash than strings
14900 // 1 hash op vs n hash ops (for length n string)
14901
14902 if (type.number && haveNum && !type.multiple) {
14903 var v = haveNormNum ? normalizedNumberVal : numberVal;
14904 updateGrKey(cleanNum(v), _grKey);
14905
14906 if (!haveNormNum && units != null) {
14907 updateGrKeyWStr(units, _grKey);
14908 }
14909 } else {
14910 updateGrKeyWStr(parsedProp.strValue, _grKey);
14911 }
14912 } // overall style key
14913 //
14914
14915
14916 var hash = [DEFAULT_HASH_SEED, DEFAULT_HASH_SEED_ALT];
14917
14918 for (var _i2 = 0; _i2 < propGrKeys.length; _i2++) {
14919 var _grKey2 = propGrKeys[_i2];
14920 var grHash = _p.styleKeys[_grKey2];
14921 hash[0] = hashInt(grHash[0], hash[0]);
14922 hash[1] = hashIntAlt(grHash[1], hash[1]);
14923 }
14924
14925 _p.styleKey = combineHashes(hash[0], hash[1]); // label dims
14926 //
14927
14928 var sk = _p.styleKeys;
14929 _p.labelDimsKey = combineHashesArray(sk.labelDimensions);
14930 var labelKeys = propHash(ele, ['label'], sk.labelDimensions);
14931 _p.labelKey = combineHashesArray(labelKeys);
14932 _p.labelStyleKey = combineHashesArray(hashArrays(sk.commonLabel, labelKeys));
14933
14934 if (!isNode) {
14935 var sourceLabelKeys = propHash(ele, ['source-label'], sk.labelDimensions);
14936 _p.sourceLabelKey = combineHashesArray(sourceLabelKeys);
14937 _p.sourceLabelStyleKey = combineHashesArray(hashArrays(sk.commonLabel, sourceLabelKeys));
14938 var targetLabelKeys = propHash(ele, ['target-label'], sk.labelDimensions);
14939 _p.targetLabelKey = combineHashesArray(targetLabelKeys);
14940 _p.targetLabelStyleKey = combineHashesArray(hashArrays(sk.commonLabel, targetLabelKeys));
14941 } // node
14942 //
14943
14944
14945 if (isNode) {
14946 var _p$styleKeys = _p.styleKeys,
14947 nodeBody = _p$styleKeys.nodeBody,
14948 nodeBorder = _p$styleKeys.nodeBorder,
14949 backgroundImage = _p$styleKeys.backgroundImage,
14950 compound = _p$styleKeys.compound,
14951 pie = _p$styleKeys.pie;
14952 var nodeKeys = [nodeBody, nodeBorder, backgroundImage, compound, pie].filter(function (k) {
14953 return k != null;
14954 }).reduce(hashArrays, [DEFAULT_HASH_SEED, DEFAULT_HASH_SEED_ALT]);
14955 _p.nodeKey = combineHashesArray(nodeKeys);
14956 _p.hasPie = pie != null && pie[0] !== DEFAULT_HASH_SEED && pie[1] !== DEFAULT_HASH_SEED_ALT;
14957 }
14958
14959 return oldStyleKey !== _p.styleKey;
14960};
14961
14962styfn$8.clearStyleHints = function (ele) {
14963 var _p = ele._private;
14964 _p.styleCxtKey = '';
14965 _p.styleKeys = {};
14966 _p.styleKey = null;
14967 _p.labelKey = null;
14968 _p.labelStyleKey = null;
14969 _p.sourceLabelKey = null;
14970 _p.sourceLabelStyleKey = null;
14971 _p.targetLabelKey = null;
14972 _p.targetLabelStyleKey = null;
14973 _p.nodeKey = null;
14974 _p.hasPie = null;
14975}; // apply a property to the style (for internal use)
14976// returns whether application was successful
14977//
14978// now, this function flattens the property, and here's how:
14979//
14980// for parsedProp:{ bypass: true, deleteBypass: true }
14981// no property is generated, instead the bypass property in the
14982// element's style is replaced by what's pointed to by the `bypassed`
14983// field in the bypass property (i.e. restoring the property the
14984// bypass was overriding)
14985//
14986// for parsedProp:{ mapped: truthy }
14987// the generated flattenedProp:{ mapping: prop }
14988//
14989// for parsedProp:{ bypass: true }
14990// the generated flattenedProp:{ bypassed: parsedProp }
14991
14992
14993styfn$8.applyParsedProperty = function (ele, parsedProp) {
14994 var self = this;
14995 var prop = parsedProp;
14996 var style = ele._private.style;
14997 var flatProp;
14998 var types = self.types;
14999 var type = self.properties[prop.name].type;
15000 var propIsBypass = prop.bypass;
15001 var origProp = style[prop.name];
15002 var origPropIsBypass = origProp && origProp.bypass;
15003 var _p = ele._private;
15004 var flatPropMapping = 'mapping';
15005
15006 var getVal = function getVal(p) {
15007 if (p == null) {
15008 return null;
15009 } else if (p.pfValue != null) {
15010 return p.pfValue;
15011 } else {
15012 return p.value;
15013 }
15014 };
15015
15016 var checkTriggers = function checkTriggers() {
15017 var fromVal = getVal(origProp);
15018 var toVal = getVal(prop);
15019 self.checkTriggers(ele, prop.name, fromVal, toVal);
15020 };
15021
15022 if (prop && prop.name.substr(0, 3) === 'pie') {
15023 warn('The pie style properties are deprecated. Create charts using background images instead.');
15024 } // edge sanity checks to prevent the client from making serious mistakes
15025
15026
15027 if (parsedProp.name === 'curve-style' && ele.isEdge() && ( // loops must be bundled beziers
15028 parsedProp.value !== 'bezier' && ele.isLoop() || // edges connected to compound nodes can not be haystacks
15029 parsedProp.value === 'haystack' && (ele.source().isParent() || ele.target().isParent()))) {
15030 prop = parsedProp = this.parse(parsedProp.name, 'bezier', propIsBypass);
15031 }
15032
15033 if (prop["delete"]) {
15034 // delete the property and use the default value on falsey value
15035 style[prop.name] = undefined;
15036 checkTriggers();
15037 return true;
15038 }
15039
15040 if (prop.deleteBypassed) {
15041 // delete the property that the
15042 if (!origProp) {
15043 checkTriggers();
15044 return true; // can't delete if no prop
15045 } else if (origProp.bypass) {
15046 // delete bypassed
15047 origProp.bypassed = undefined;
15048 checkTriggers();
15049 return true;
15050 } else {
15051 return false; // we're unsuccessful deleting the bypassed
15052 }
15053 } // check if we need to delete the current bypass
15054
15055
15056 if (prop.deleteBypass) {
15057 // then this property is just here to indicate we need to delete
15058 if (!origProp) {
15059 checkTriggers();
15060 return true; // property is already not defined
15061 } else if (origProp.bypass) {
15062 // then replace the bypass property with the original
15063 // because the bypassed property was already applied (and therefore parsed), we can just replace it (no reapplying necessary)
15064 style[prop.name] = origProp.bypassed;
15065 checkTriggers();
15066 return true;
15067 } else {
15068 return false; // we're unsuccessful deleting the bypass
15069 }
15070 }
15071
15072 var printMappingErr = function printMappingErr() {
15073 warn('Do not assign mappings to elements without corresponding data (i.e. ele `' + ele.id() + '` has no mapping for property `' + prop.name + '` with data field `' + prop.field + '`); try a `[' + prop.field + ']` selector to limit scope to elements with `' + prop.field + '` defined');
15074 }; // put the property in the style objects
15075
15076
15077 switch (prop.mapped) {
15078 // flatten the property if mapped
15079 case types.mapData:
15080 {
15081 // flatten the field (e.g. data.foo.bar)
15082 var fields = prop.field.split('.');
15083 var fieldVal = _p.data;
15084
15085 for (var i = 0; i < fields.length && fieldVal; i++) {
15086 var field = fields[i];
15087 fieldVal = fieldVal[field];
15088 }
15089
15090 if (fieldVal == null) {
15091 printMappingErr();
15092 return false;
15093 }
15094
15095 var percent;
15096
15097 if (!number$1(fieldVal)) {
15098 // then don't apply and fall back on the existing style
15099 warn('Do not use continuous mappers without specifying numeric data (i.e. `' + prop.field + ': ' + fieldVal + '` for `' + ele.id() + '` is non-numeric)');
15100 return false;
15101 } else {
15102 var fieldWidth = prop.fieldMax - prop.fieldMin;
15103
15104 if (fieldWidth === 0) {
15105 // safety check -- not strictly necessary as no props of zero range should be passed here
15106 percent = 0;
15107 } else {
15108 percent = (fieldVal - prop.fieldMin) / fieldWidth;
15109 }
15110 } // make sure to bound percent value
15111
15112
15113 if (percent < 0) {
15114 percent = 0;
15115 } else if (percent > 1) {
15116 percent = 1;
15117 }
15118
15119 if (type.color) {
15120 var r1 = prop.valueMin[0];
15121 var r2 = prop.valueMax[0];
15122 var g1 = prop.valueMin[1];
15123 var g2 = prop.valueMax[1];
15124 var b1 = prop.valueMin[2];
15125 var b2 = prop.valueMax[2];
15126 var a1 = prop.valueMin[3] == null ? 1 : prop.valueMin[3];
15127 var a2 = prop.valueMax[3] == null ? 1 : prop.valueMax[3];
15128 var clr = [Math.round(r1 + (r2 - r1) * percent), Math.round(g1 + (g2 - g1) * percent), Math.round(b1 + (b2 - b1) * percent), Math.round(a1 + (a2 - a1) * percent)];
15129 flatProp = {
15130 // colours are simple, so just create the flat property instead of expensive string parsing
15131 bypass: prop.bypass,
15132 // we're a bypass if the mapping property is a bypass
15133 name: prop.name,
15134 value: clr,
15135 strValue: 'rgb(' + clr[0] + ', ' + clr[1] + ', ' + clr[2] + ')'
15136 };
15137 } else if (type.number) {
15138 var calcValue = prop.valueMin + (prop.valueMax - prop.valueMin) * percent;
15139 flatProp = this.parse(prop.name, calcValue, prop.bypass, flatPropMapping);
15140 } else {
15141 return false; // can only map to colours and numbers
15142 }
15143
15144 if (!flatProp) {
15145 // if we can't flatten the property, then don't apply the property and fall back on the existing style
15146 printMappingErr();
15147 return false;
15148 }
15149
15150 flatProp.mapping = prop; // keep a reference to the mapping
15151
15152 prop = flatProp; // the flattened (mapped) property is the one we want
15153
15154 break;
15155 }
15156 // direct mapping
15157
15158 case types.data:
15159 {
15160 // flatten the field (e.g. data.foo.bar)
15161 var _fields = prop.field.split('.');
15162
15163 var _fieldVal = _p.data;
15164
15165 for (var _i3 = 0; _i3 < _fields.length && _fieldVal; _i3++) {
15166 var _field = _fields[_i3];
15167 _fieldVal = _fieldVal[_field];
15168 }
15169
15170 if (_fieldVal != null) {
15171 flatProp = this.parse(prop.name, _fieldVal, prop.bypass, flatPropMapping);
15172 }
15173
15174 if (!flatProp) {
15175 // if we can't flatten the property, then don't apply and fall back on the existing style
15176 printMappingErr();
15177 return false;
15178 }
15179
15180 flatProp.mapping = prop; // keep a reference to the mapping
15181
15182 prop = flatProp; // the flattened (mapped) property is the one we want
15183
15184 break;
15185 }
15186
15187 case types.fn:
15188 {
15189 var fn = prop.value;
15190 var fnRetVal = prop.fnValue != null ? prop.fnValue : fn(ele); // check for cached value before calling function
15191
15192 prop.prevFnValue = fnRetVal;
15193
15194 if (fnRetVal == null) {
15195 warn('Custom function mappers may not return null (i.e. `' + prop.name + '` for ele `' + ele.id() + '` is null)');
15196 return false;
15197 }
15198
15199 flatProp = this.parse(prop.name, fnRetVal, prop.bypass, flatPropMapping);
15200
15201 if (!flatProp) {
15202 warn('Custom function mappers may not return invalid values for the property type (i.e. `' + prop.name + '` for ele `' + ele.id() + '` is invalid)');
15203 return false;
15204 }
15205
15206 flatProp.mapping = copy(prop); // keep a reference to the mapping
15207
15208 prop = flatProp; // the flattened (mapped) property is the one we want
15209
15210 break;
15211 }
15212
15213 case undefined:
15214 break;
15215 // just set the property
15216
15217 default:
15218 return false;
15219 // not a valid mapping
15220 } // if the property is a bypass property, then link the resultant property to the original one
15221
15222
15223 if (propIsBypass) {
15224 if (origPropIsBypass) {
15225 // then this bypass overrides the existing one
15226 prop.bypassed = origProp.bypassed; // steal bypassed prop from old bypass
15227 } else {
15228 // then link the orig prop to the new bypass
15229 prop.bypassed = origProp;
15230 }
15231
15232 style[prop.name] = prop; // and set
15233 } else {
15234 // prop is not bypass
15235 if (origPropIsBypass) {
15236 // then keep the orig prop (since it's a bypass) and link to the new prop
15237 origProp.bypassed = prop;
15238 } else {
15239 // then just replace the old prop with the new one
15240 style[prop.name] = prop;
15241 }
15242 }
15243
15244 checkTriggers();
15245 return true;
15246};
15247
15248styfn$8.cleanElements = function (eles, keepBypasses) {
15249 for (var i = 0; i < eles.length; i++) {
15250 var ele = eles[i];
15251 this.clearStyleHints(ele);
15252 ele.dirtyCompoundBoundsCache();
15253 ele.dirtyBoundingBoxCache();
15254
15255 if (!keepBypasses) {
15256 ele._private.style = {};
15257 } else {
15258 var style = ele._private.style;
15259 var propNames = Object.keys(style);
15260
15261 for (var j = 0; j < propNames.length; j++) {
15262 var propName = propNames[j];
15263 var eleProp = style[propName];
15264
15265 if (eleProp != null) {
15266 if (eleProp.bypass) {
15267 eleProp.bypassed = null;
15268 } else {
15269 style[propName] = null;
15270 }
15271 }
15272 }
15273 }
15274 }
15275}; // updates the visual style for all elements (useful for manual style modification after init)
15276
15277
15278styfn$8.update = function () {
15279 var cy = this._private.cy;
15280 var eles = cy.mutableElements();
15281 eles.updateStyle();
15282}; // diffProps : { name => { prev, next } }
15283
15284
15285styfn$8.updateTransitions = function (ele, diffProps) {
15286 var self = this;
15287 var _p = ele._private;
15288 var props = ele.pstyle('transition-property').value;
15289 var duration = ele.pstyle('transition-duration').pfValue;
15290 var delay = ele.pstyle('transition-delay').pfValue;
15291
15292 if (props.length > 0 && duration > 0) {
15293 var style = {}; // build up the style to animate towards
15294
15295 var anyPrev = false;
15296
15297 for (var i = 0; i < props.length; i++) {
15298 var prop = props[i];
15299 var styProp = ele.pstyle(prop);
15300 var diffProp = diffProps[prop];
15301
15302 if (!diffProp) {
15303 continue;
15304 }
15305
15306 var prevProp = diffProp.prev;
15307 var fromProp = prevProp;
15308 var toProp = diffProp.next != null ? diffProp.next : styProp;
15309 var diff = false;
15310 var initVal = void 0;
15311 var initDt = 0.000001; // delta time % value for initVal (allows animating out of init zero opacity)
15312
15313 if (!fromProp) {
15314 continue;
15315 } // consider px values
15316
15317
15318 if (number$1(fromProp.pfValue) && number$1(toProp.pfValue)) {
15319 diff = toProp.pfValue - fromProp.pfValue; // nonzero is truthy
15320
15321 initVal = fromProp.pfValue + initDt * diff; // consider numerical values
15322 } else if (number$1(fromProp.value) && number$1(toProp.value)) {
15323 diff = toProp.value - fromProp.value; // nonzero is truthy
15324
15325 initVal = fromProp.value + initDt * diff; // consider colour values
15326 } else if (array(fromProp.value) && array(toProp.value)) {
15327 diff = fromProp.value[0] !== toProp.value[0] || fromProp.value[1] !== toProp.value[1] || fromProp.value[2] !== toProp.value[2];
15328 initVal = fromProp.strValue;
15329 } // the previous value is good for an animation only if it's different
15330
15331
15332 if (diff) {
15333 style[prop] = toProp.strValue; // to val
15334
15335 this.applyBypass(ele, prop, initVal); // from val
15336
15337 anyPrev = true;
15338 }
15339 } // end if props allow ani
15340 // can't transition if there's nothing previous to transition from
15341
15342
15343 if (!anyPrev) {
15344 return;
15345 }
15346
15347 _p.transitioning = true;
15348 new Promise$1(function (resolve) {
15349 if (delay > 0) {
15350 ele.delayAnimation(delay).play().promise().then(resolve);
15351 } else {
15352 resolve();
15353 }
15354 }).then(function () {
15355 return ele.animation({
15356 style: style,
15357 duration: duration,
15358 easing: ele.pstyle('transition-timing-function').value,
15359 queue: false
15360 }).play().promise();
15361 }).then(function () {
15362 // if( !isBypass ){
15363 self.removeBypasses(ele, props);
15364 ele.emitAndNotify('style'); // }
15365
15366 _p.transitioning = false;
15367 });
15368 } else if (_p.transitioning) {
15369 this.removeBypasses(ele, props);
15370 ele.emitAndNotify('style');
15371 _p.transitioning = false;
15372 }
15373};
15374
15375styfn$8.checkTrigger = function (ele, name, fromValue, toValue, getTrigger, onTrigger) {
15376 var prop = this.properties[name];
15377 var triggerCheck = getTrigger(prop);
15378
15379 if (triggerCheck != null && triggerCheck(fromValue, toValue)) {
15380 onTrigger(prop);
15381 }
15382};
15383
15384styfn$8.checkZOrderTrigger = function (ele, name, fromValue, toValue) {
15385 var _this = this;
15386
15387 this.checkTrigger(ele, name, fromValue, toValue, function (prop) {
15388 return prop.triggersZOrder;
15389 }, function () {
15390 _this._private.cy.notify('zorder', ele);
15391 });
15392};
15393
15394styfn$8.checkBoundsTrigger = function (ele, name, fromValue, toValue) {
15395 this.checkTrigger(ele, name, fromValue, toValue, function (prop) {
15396 return prop.triggersBounds;
15397 }, function (prop) {
15398 ele.dirtyCompoundBoundsCache();
15399 ele.dirtyBoundingBoxCache(); // if the prop change makes the bb of pll bezier edges invalid,
15400 // then dirty the pll edge bb cache as well
15401
15402 if ( // only for beziers -- so performance of other edges isn't affected
15403 prop.triggersBoundsOfParallelBeziers && (name === 'curve-style' && (fromValue === 'bezier' || toValue === 'bezier') || name === 'display' && (fromValue === 'none' || toValue === 'none'))) {
15404 ele.parallelEdges().forEach(function (pllEdge) {
15405 if (pllEdge.isBundledBezier()) {
15406 pllEdge.dirtyBoundingBoxCache();
15407 }
15408 });
15409 }
15410 });
15411};
15412
15413styfn$8.checkTriggers = function (ele, name, fromValue, toValue) {
15414 ele.dirtyStyleCache();
15415 this.checkZOrderTrigger(ele, name, fromValue, toValue);
15416 this.checkBoundsTrigger(ele, name, fromValue, toValue);
15417};
15418
15419var styfn$7 = {}; // bypasses are applied to an existing style on an element, and just tacked on temporarily
15420// returns true iff application was successful for at least 1 specified property
15421
15422styfn$7.applyBypass = function (eles, name, value, updateTransitions) {
15423 var self = this;
15424 var props = [];
15425 var isBypass = true; // put all the properties (can specify one or many) in an array after parsing them
15426
15427 if (name === '*' || name === '**') {
15428 // apply to all property names
15429 if (value !== undefined) {
15430 for (var i = 0; i < self.properties.length; i++) {
15431 var prop = self.properties[i];
15432 var _name = prop.name;
15433 var parsedProp = this.parse(_name, value, true);
15434
15435 if (parsedProp) {
15436 props.push(parsedProp);
15437 }
15438 }
15439 }
15440 } else if (string(name)) {
15441 // then parse the single property
15442 var _parsedProp = this.parse(name, value, true);
15443
15444 if (_parsedProp) {
15445 props.push(_parsedProp);
15446 }
15447 } else if (plainObject(name)) {
15448 // then parse each property
15449 var specifiedProps = name;
15450 updateTransitions = value;
15451 var names = Object.keys(specifiedProps);
15452
15453 for (var _i = 0; _i < names.length; _i++) {
15454 var _name2 = names[_i];
15455 var _value = specifiedProps[_name2];
15456
15457 if (_value === undefined) {
15458 // try camel case name too
15459 _value = specifiedProps[dash2camel(_name2)];
15460 }
15461
15462 if (_value !== undefined) {
15463 var _parsedProp2 = this.parse(_name2, _value, true);
15464
15465 if (_parsedProp2) {
15466 props.push(_parsedProp2);
15467 }
15468 }
15469 }
15470 } else {
15471 // can't do anything without well defined properties
15472 return false;
15473 } // we've failed if there are no valid properties
15474
15475
15476 if (props.length === 0) {
15477 return false;
15478 } // now, apply the bypass properties on the elements
15479
15480
15481 var ret = false; // return true if at least one succesful bypass applied
15482
15483 for (var _i2 = 0; _i2 < eles.length; _i2++) {
15484 // for each ele
15485 var ele = eles[_i2];
15486 var diffProps = {};
15487 var diffProp = void 0;
15488
15489 for (var j = 0; j < props.length; j++) {
15490 // for each prop
15491 var _prop = props[j];
15492
15493 if (updateTransitions) {
15494 var prevProp = ele.pstyle(_prop.name);
15495 diffProp = diffProps[_prop.name] = {
15496 prev: prevProp
15497 };
15498 }
15499
15500 ret = this.applyParsedProperty(ele, copy(_prop)) || ret;
15501
15502 if (updateTransitions) {
15503 diffProp.next = ele.pstyle(_prop.name);
15504 }
15505 } // for props
15506
15507
15508 if (ret) {
15509 this.updateStyleHints(ele);
15510 }
15511
15512 if (updateTransitions) {
15513 this.updateTransitions(ele, diffProps, isBypass);
15514 }
15515 } // for eles
15516
15517
15518 return ret;
15519}; // only useful in specific cases like animation
15520
15521
15522styfn$7.overrideBypass = function (eles, name, value) {
15523 name = camel2dash(name);
15524
15525 for (var i = 0; i < eles.length; i++) {
15526 var ele = eles[i];
15527 var prop = ele._private.style[name];
15528 var type = this.properties[name].type;
15529 var isColor = type.color;
15530 var isMulti = type.mutiple;
15531 var oldValue = !prop ? null : prop.pfValue != null ? prop.pfValue : prop.value;
15532
15533 if (!prop || !prop.bypass) {
15534 // need a bypass if one doesn't exist
15535 this.applyBypass(ele, name, value);
15536 } else {
15537 prop.value = value;
15538
15539 if (prop.pfValue != null) {
15540 prop.pfValue = value;
15541 }
15542
15543 if (isColor) {
15544 prop.strValue = 'rgb(' + value.join(',') + ')';
15545 } else if (isMulti) {
15546 prop.strValue = value.join(' ');
15547 } else {
15548 prop.strValue = '' + value;
15549 }
15550
15551 this.updateStyleHints(ele);
15552 }
15553
15554 this.checkTriggers(ele, name, oldValue, value);
15555 }
15556};
15557
15558styfn$7.removeAllBypasses = function (eles, updateTransitions) {
15559 return this.removeBypasses(eles, this.propertyNames, updateTransitions);
15560};
15561
15562styfn$7.removeBypasses = function (eles, props, updateTransitions) {
15563 var isBypass = true;
15564
15565 for (var j = 0; j < eles.length; j++) {
15566 var ele = eles[j];
15567 var diffProps = {};
15568
15569 for (var i = 0; i < props.length; i++) {
15570 var name = props[i];
15571 var prop = this.properties[name];
15572 var prevProp = ele.pstyle(prop.name);
15573
15574 if (!prevProp || !prevProp.bypass) {
15575 // if a bypass doesn't exist for the prop, nothing needs to be removed
15576 continue;
15577 }
15578
15579 var value = ''; // empty => remove bypass
15580
15581 var parsedProp = this.parse(name, value, true);
15582 var diffProp = diffProps[prop.name] = {
15583 prev: prevProp
15584 };
15585 this.applyParsedProperty(ele, parsedProp);
15586 diffProp.next = ele.pstyle(prop.name);
15587 } // for props
15588
15589
15590 this.updateStyleHints(ele);
15591
15592 if (updateTransitions) {
15593 this.updateTransitions(ele, diffProps, isBypass);
15594 }
15595 } // for eles
15596
15597};
15598
15599var styfn$6 = {}; // gets what an em size corresponds to in pixels relative to a dom element
15600
15601styfn$6.getEmSizeInPixels = function () {
15602 var px = this.containerCss('font-size');
15603
15604 if (px != null) {
15605 return parseFloat(px);
15606 } else {
15607 return 1; // for headless
15608 }
15609}; // gets css property from the core container
15610
15611
15612styfn$6.containerCss = function (propName) {
15613 var cy = this._private.cy;
15614 var domElement = cy.container();
15615
15616 if (window$1 && domElement && window$1.getComputedStyle) {
15617 return window$1.getComputedStyle(domElement).getPropertyValue(propName);
15618 }
15619};
15620
15621var styfn$5 = {}; // gets the rendered style for an element
15622
15623styfn$5.getRenderedStyle = function (ele, prop) {
15624 if (prop) {
15625 return this.getStylePropertyValue(ele, prop, true);
15626 } else {
15627 return this.getRawStyle(ele, true);
15628 }
15629}; // gets the raw style for an element
15630
15631
15632styfn$5.getRawStyle = function (ele, isRenderedVal) {
15633 var self = this;
15634 ele = ele[0]; // insure it's an element
15635
15636 if (ele) {
15637 var rstyle = {};
15638
15639 for (var i = 0; i < self.properties.length; i++) {
15640 var prop = self.properties[i];
15641 var val = self.getStylePropertyValue(ele, prop.name, isRenderedVal);
15642
15643 if (val != null) {
15644 rstyle[prop.name] = val;
15645 rstyle[dash2camel(prop.name)] = val;
15646 }
15647 }
15648
15649 return rstyle;
15650 }
15651};
15652
15653styfn$5.getIndexedStyle = function (ele, property, subproperty, index) {
15654 var pstyle = ele.pstyle(property)[subproperty][index];
15655 return pstyle != null ? pstyle : ele.cy().style().getDefaultProperty(property)[subproperty][0];
15656};
15657
15658styfn$5.getStylePropertyValue = function (ele, propName, isRenderedVal) {
15659 var self = this;
15660 ele = ele[0]; // insure it's an element
15661
15662 if (ele) {
15663 var prop = self.properties[propName];
15664
15665 if (prop.alias) {
15666 prop = prop.pointsTo;
15667 }
15668
15669 var type = prop.type;
15670 var styleProp = ele.pstyle(prop.name);
15671
15672 if (styleProp) {
15673 var value = styleProp.value,
15674 units = styleProp.units,
15675 strValue = styleProp.strValue;
15676
15677 if (isRenderedVal && type.number && value != null && number$1(value)) {
15678 var zoom = ele.cy().zoom();
15679
15680 var getRenderedValue = function getRenderedValue(val) {
15681 return val * zoom;
15682 };
15683
15684 var getValueStringWithUnits = function getValueStringWithUnits(val, units) {
15685 return getRenderedValue(val) + units;
15686 };
15687
15688 var isArrayValue = array(value);
15689 var haveUnits = isArrayValue ? units.every(function (u) {
15690 return u != null;
15691 }) : units != null;
15692
15693 if (haveUnits) {
15694 if (isArrayValue) {
15695 return value.map(function (v, i) {
15696 return getValueStringWithUnits(v, units[i]);
15697 }).join(' ');
15698 } else {
15699 return getValueStringWithUnits(value, units);
15700 }
15701 } else {
15702 if (isArrayValue) {
15703 return value.map(function (v) {
15704 return string(v) ? v : '' + getRenderedValue(v);
15705 }).join(' ');
15706 } else {
15707 return '' + getRenderedValue(value);
15708 }
15709 }
15710 } else if (strValue != null) {
15711 return strValue;
15712 }
15713 }
15714
15715 return null;
15716 }
15717};
15718
15719styfn$5.getAnimationStartStyle = function (ele, aniProps) {
15720 var rstyle = {};
15721
15722 for (var i = 0; i < aniProps.length; i++) {
15723 var aniProp = aniProps[i];
15724 var name = aniProp.name;
15725 var styleProp = ele.pstyle(name);
15726
15727 if (styleProp !== undefined) {
15728 // then make a prop of it
15729 if (plainObject(styleProp)) {
15730 styleProp = this.parse(name, styleProp.strValue);
15731 } else {
15732 styleProp = this.parse(name, styleProp);
15733 }
15734 }
15735
15736 if (styleProp) {
15737 rstyle[name] = styleProp;
15738 }
15739 }
15740
15741 return rstyle;
15742};
15743
15744styfn$5.getPropsList = function (propsObj) {
15745 var self = this;
15746 var rstyle = [];
15747 var style = propsObj;
15748 var props = self.properties;
15749
15750 if (style) {
15751 var names = Object.keys(style);
15752
15753 for (var i = 0; i < names.length; i++) {
15754 var name = names[i];
15755 var val = style[name];
15756 var prop = props[name] || props[camel2dash(name)];
15757 var styleProp = this.parse(prop.name, val);
15758
15759 if (styleProp) {
15760 rstyle.push(styleProp);
15761 }
15762 }
15763 }
15764
15765 return rstyle;
15766};
15767
15768styfn$5.getNonDefaultPropertiesHash = function (ele, propNames, seed) {
15769 var hash = seed.slice();
15770 var name, val, strVal, chVal;
15771 var i, j;
15772
15773 for (i = 0; i < propNames.length; i++) {
15774 name = propNames[i];
15775 val = ele.pstyle(name, false);
15776
15777 if (val == null) {
15778 continue;
15779 } else if (val.pfValue != null) {
15780 hash[0] = hashInt(chVal, hash[0]);
15781 hash[1] = hashIntAlt(chVal, hash[1]);
15782 } else {
15783 strVal = val.strValue;
15784
15785 for (j = 0; j < strVal.length; j++) {
15786 chVal = strVal.charCodeAt(j);
15787 hash[0] = hashInt(chVal, hash[0]);
15788 hash[1] = hashIntAlt(chVal, hash[1]);
15789 }
15790 }
15791 }
15792
15793 return hash;
15794};
15795
15796styfn$5.getPropertiesHash = styfn$5.getNonDefaultPropertiesHash;
15797
15798var styfn$4 = {};
15799
15800styfn$4.appendFromJson = function (json) {
15801 var style = this;
15802
15803 for (var i = 0; i < json.length; i++) {
15804 var context = json[i];
15805 var selector = context.selector;
15806 var props = context.style || context.css;
15807 var names = Object.keys(props);
15808 style.selector(selector); // apply selector
15809
15810 for (var j = 0; j < names.length; j++) {
15811 var name = names[j];
15812 var value = props[name];
15813 style.css(name, value); // apply property
15814 }
15815 }
15816
15817 return style;
15818}; // accessible cy.style() function
15819
15820
15821styfn$4.fromJson = function (json) {
15822 var style = this;
15823 style.resetToDefault();
15824 style.appendFromJson(json);
15825 return style;
15826}; // get json from cy.style() api
15827
15828
15829styfn$4.json = function () {
15830 var json = [];
15831
15832 for (var i = this.defaultLength; i < this.length; i++) {
15833 var cxt = this[i];
15834 var selector = cxt.selector;
15835 var props = cxt.properties;
15836 var css = {};
15837
15838 for (var j = 0; j < props.length; j++) {
15839 var prop = props[j];
15840 css[prop.name] = prop.strValue;
15841 }
15842
15843 json.push({
15844 selector: !selector ? 'core' : selector.toString(),
15845 style: css
15846 });
15847 }
15848
15849 return json;
15850};
15851
15852var styfn$3 = {};
15853
15854styfn$3.appendFromString = function (string) {
15855 var self = this;
15856 var style = this;
15857 var remaining = '' + string;
15858 var selAndBlockStr;
15859 var blockRem;
15860 var propAndValStr; // remove comments from the style string
15861
15862 remaining = remaining.replace(/[/][*](\s|.)+?[*][/]/g, '');
15863
15864 function removeSelAndBlockFromRemaining() {
15865 // remove the parsed selector and block from the remaining text to parse
15866 if (remaining.length > selAndBlockStr.length) {
15867 remaining = remaining.substr(selAndBlockStr.length);
15868 } else {
15869 remaining = '';
15870 }
15871 }
15872
15873 function removePropAndValFromRem() {
15874 // remove the parsed property and value from the remaining block text to parse
15875 if (blockRem.length > propAndValStr.length) {
15876 blockRem = blockRem.substr(propAndValStr.length);
15877 } else {
15878 blockRem = '';
15879 }
15880 }
15881
15882 for (;;) {
15883 var nothingLeftToParse = remaining.match(/^\s*$/);
15884
15885 if (nothingLeftToParse) {
15886 break;
15887 }
15888
15889 var selAndBlock = remaining.match(/^\s*((?:.|\s)+?)\s*\{((?:.|\s)+?)\}/);
15890
15891 if (!selAndBlock) {
15892 warn('Halting stylesheet parsing: String stylesheet contains more to parse but no selector and block found in: ' + remaining);
15893 break;
15894 }
15895
15896 selAndBlockStr = selAndBlock[0]; // parse the selector
15897
15898 var selectorStr = selAndBlock[1];
15899
15900 if (selectorStr !== 'core') {
15901 var selector = new Selector(selectorStr);
15902
15903 if (selector.invalid) {
15904 warn('Skipping parsing of block: Invalid selector found in string stylesheet: ' + selectorStr); // skip this selector and block
15905
15906 removeSelAndBlockFromRemaining();
15907 continue;
15908 }
15909 } // parse the block of properties and values
15910
15911
15912 var blockStr = selAndBlock[2];
15913 var invalidBlock = false;
15914 blockRem = blockStr;
15915 var props = [];
15916
15917 for (;;) {
15918 var _nothingLeftToParse = blockRem.match(/^\s*$/);
15919
15920 if (_nothingLeftToParse) {
15921 break;
15922 }
15923
15924 var propAndVal = blockRem.match(/^\s*(.+?)\s*:\s*(.+?)(?:\s*;|\s*$)/);
15925
15926 if (!propAndVal) {
15927 warn('Skipping parsing of block: Invalid formatting of style property and value definitions found in:' + blockStr);
15928 invalidBlock = true;
15929 break;
15930 }
15931
15932 propAndValStr = propAndVal[0];
15933 var propStr = propAndVal[1];
15934 var valStr = propAndVal[2];
15935 var prop = self.properties[propStr];
15936
15937 if (!prop) {
15938 warn('Skipping property: Invalid property name in: ' + propAndValStr); // skip this property in the block
15939
15940 removePropAndValFromRem();
15941 continue;
15942 }
15943
15944 var parsedProp = style.parse(propStr, valStr);
15945
15946 if (!parsedProp) {
15947 warn('Skipping property: Invalid property definition in: ' + propAndValStr); // skip this property in the block
15948
15949 removePropAndValFromRem();
15950 continue;
15951 }
15952
15953 props.push({
15954 name: propStr,
15955 val: valStr
15956 });
15957 removePropAndValFromRem();
15958 }
15959
15960 if (invalidBlock) {
15961 removeSelAndBlockFromRemaining();
15962 break;
15963 } // put the parsed block in the style
15964
15965
15966 style.selector(selectorStr);
15967
15968 for (var i = 0; i < props.length; i++) {
15969 var _prop = props[i];
15970 style.css(_prop.name, _prop.val);
15971 }
15972
15973 removeSelAndBlockFromRemaining();
15974 }
15975
15976 return style;
15977};
15978
15979styfn$3.fromString = function (string) {
15980 var style = this;
15981 style.resetToDefault();
15982 style.appendFromString(string);
15983 return style;
15984};
15985
15986var styfn$2 = {};
15987
15988(function () {
15989 var number$1 = number;
15990 var rgba = rgbaNoBackRefs;
15991 var hsla = hslaNoBackRefs;
15992 var hex3$1 = hex3;
15993 var hex6$1 = hex6;
15994
15995 var data = function data(prefix) {
15996 return '^' + prefix + '\\s*\\(\\s*([\\w\\.]+)\\s*\\)$';
15997 };
15998
15999 var mapData = function mapData(prefix) {
16000 var mapArg = number$1 + '|\\w+|' + rgba + '|' + hsla + '|' + hex3$1 + '|' + hex6$1;
16001 return '^' + prefix + '\\s*\\(([\\w\\.]+)\\s*\\,\\s*(' + number$1 + ')\\s*\\,\\s*(' + number$1 + ')\\s*,\\s*(' + mapArg + ')\\s*\\,\\s*(' + mapArg + ')\\)$';
16002 };
16003
16004 var urlRegexes = ['^url\\s*\\(\\s*[\'"]?(.+?)[\'"]?\\s*\\)$', '^(none)$', '^(.+)$']; // each visual style property has a type and needs to be validated according to it
16005
16006 styfn$2.types = {
16007 time: {
16008 number: true,
16009 min: 0,
16010 units: 's|ms',
16011 implicitUnits: 'ms'
16012 },
16013 percent: {
16014 number: true,
16015 min: 0,
16016 max: 100,
16017 units: '%',
16018 implicitUnits: '%'
16019 },
16020 percentages: {
16021 number: true,
16022 min: 0,
16023 max: 100,
16024 units: '%',
16025 implicitUnits: '%',
16026 multiple: true
16027 },
16028 zeroOneNumber: {
16029 number: true,
16030 min: 0,
16031 max: 1,
16032 unitless: true
16033 },
16034 zeroOneNumbers: {
16035 number: true,
16036 min: 0,
16037 max: 1,
16038 unitless: true,
16039 multiple: true
16040 },
16041 nOneOneNumber: {
16042 number: true,
16043 min: -1,
16044 max: 1,
16045 unitless: true
16046 },
16047 nonNegativeInt: {
16048 number: true,
16049 min: 0,
16050 integer: true,
16051 unitless: true
16052 },
16053 position: {
16054 enums: ['parent', 'origin']
16055 },
16056 nodeSize: {
16057 number: true,
16058 min: 0,
16059 enums: ['label']
16060 },
16061 number: {
16062 number: true,
16063 unitless: true
16064 },
16065 numbers: {
16066 number: true,
16067 unitless: true,
16068 multiple: true
16069 },
16070 positiveNumber: {
16071 number: true,
16072 unitless: true,
16073 min: 0,
16074 strictMin: true
16075 },
16076 size: {
16077 number: true,
16078 min: 0
16079 },
16080 bidirectionalSize: {
16081 number: true
16082 },
16083 // allows negative
16084 bidirectionalSizeMaybePercent: {
16085 number: true,
16086 allowPercent: true
16087 },
16088 // allows negative
16089 bidirectionalSizes: {
16090 number: true,
16091 multiple: true
16092 },
16093 // allows negative
16094 sizeMaybePercent: {
16095 number: true,
16096 min: 0,
16097 allowPercent: true
16098 },
16099 axisDirection: {
16100 enums: ['horizontal', 'leftward', 'rightward', 'vertical', 'upward', 'downward', 'auto']
16101 },
16102 paddingRelativeTo: {
16103 enums: ['width', 'height', 'average', 'min', 'max']
16104 },
16105 bgWH: {
16106 number: true,
16107 min: 0,
16108 allowPercent: true,
16109 enums: ['auto'],
16110 multiple: true
16111 },
16112 bgPos: {
16113 number: true,
16114 allowPercent: true,
16115 multiple: true
16116 },
16117 bgRelativeTo: {
16118 enums: ['inner', 'include-padding'],
16119 multiple: true
16120 },
16121 bgRepeat: {
16122 enums: ['repeat', 'repeat-x', 'repeat-y', 'no-repeat'],
16123 multiple: true
16124 },
16125 bgFit: {
16126 enums: ['none', 'contain', 'cover'],
16127 multiple: true
16128 },
16129 bgCrossOrigin: {
16130 enums: ['anonymous', 'use-credentials'],
16131 multiple: true
16132 },
16133 bgClip: {
16134 enums: ['none', 'node'],
16135 multiple: true
16136 },
16137 bgContainment: {
16138 enums: ['inside', 'over'],
16139 multiple: true
16140 },
16141 color: {
16142 color: true
16143 },
16144 colors: {
16145 color: true,
16146 multiple: true
16147 },
16148 fill: {
16149 enums: ['solid', 'linear-gradient', 'radial-gradient']
16150 },
16151 bool: {
16152 enums: ['yes', 'no']
16153 },
16154 bools: {
16155 enums: ['yes', 'no'],
16156 multiple: true
16157 },
16158 lineStyle: {
16159 enums: ['solid', 'dotted', 'dashed']
16160 },
16161 lineCap: {
16162 enums: ['butt', 'round', 'square']
16163 },
16164 borderStyle: {
16165 enums: ['solid', 'dotted', 'dashed', 'double']
16166 },
16167 curveStyle: {
16168 enums: ['bezier', 'unbundled-bezier', 'haystack', 'segments', 'straight', 'straight-triangle', 'taxi']
16169 },
16170 fontFamily: {
16171 regex: '^([\\w- \\"]+(?:\\s*,\\s*[\\w- \\"]+)*)$'
16172 },
16173 fontStyle: {
16174 enums: ['italic', 'normal', 'oblique']
16175 },
16176 fontWeight: {
16177 enums: ['normal', 'bold', 'bolder', 'lighter', '100', '200', '300', '400', '500', '600', '800', '900', 100, 200, 300, 400, 500, 600, 700, 800, 900]
16178 },
16179 textDecoration: {
16180 enums: ['none', 'underline', 'overline', 'line-through']
16181 },
16182 textTransform: {
16183 enums: ['none', 'uppercase', 'lowercase']
16184 },
16185 textWrap: {
16186 enums: ['none', 'wrap', 'ellipsis']
16187 },
16188 textOverflowWrap: {
16189 enums: ['whitespace', 'anywhere']
16190 },
16191 textBackgroundShape: {
16192 enums: ['rectangle', 'roundrectangle', 'round-rectangle']
16193 },
16194 nodeShape: {
16195 enums: ['rectangle', 'roundrectangle', 'round-rectangle', 'cutrectangle', 'cut-rectangle', 'bottomroundrectangle', 'bottom-round-rectangle', 'barrel', 'ellipse', 'triangle', 'round-triangle', 'square', 'pentagon', 'round-pentagon', 'hexagon', 'round-hexagon', 'concavehexagon', 'concave-hexagon', 'heptagon', 'round-heptagon', 'octagon', 'round-octagon', 'tag', 'round-tag', 'star', 'diamond', 'round-diamond', 'vee', 'rhomboid', 'polygon']
16196 },
16197 overlayShape: {
16198 enums: ['roundrectangle', 'round-rectangle', 'ellipse']
16199 },
16200 compoundIncludeLabels: {
16201 enums: ['include', 'exclude']
16202 },
16203 arrowShape: {
16204 enums: ['tee', 'triangle', 'triangle-tee', 'circle-triangle', 'triangle-cross', 'triangle-backcurve', 'vee', 'square', 'circle', 'diamond', 'chevron', 'none']
16205 },
16206 arrowFill: {
16207 enums: ['filled', 'hollow']
16208 },
16209 display: {
16210 enums: ['element', 'none']
16211 },
16212 visibility: {
16213 enums: ['hidden', 'visible']
16214 },
16215 zCompoundDepth: {
16216 enums: ['bottom', 'orphan', 'auto', 'top']
16217 },
16218 zIndexCompare: {
16219 enums: ['auto', 'manual']
16220 },
16221 valign: {
16222 enums: ['top', 'center', 'bottom']
16223 },
16224 halign: {
16225 enums: ['left', 'center', 'right']
16226 },
16227 justification: {
16228 enums: ['left', 'center', 'right', 'auto']
16229 },
16230 text: {
16231 string: true
16232 },
16233 data: {
16234 mapping: true,
16235 regex: data('data')
16236 },
16237 layoutData: {
16238 mapping: true,
16239 regex: data('layoutData')
16240 },
16241 scratch: {
16242 mapping: true,
16243 regex: data('scratch')
16244 },
16245 mapData: {
16246 mapping: true,
16247 regex: mapData('mapData')
16248 },
16249 mapLayoutData: {
16250 mapping: true,
16251 regex: mapData('mapLayoutData')
16252 },
16253 mapScratch: {
16254 mapping: true,
16255 regex: mapData('mapScratch')
16256 },
16257 fn: {
16258 mapping: true,
16259 fn: true
16260 },
16261 url: {
16262 regexes: urlRegexes,
16263 singleRegexMatchValue: true
16264 },
16265 urls: {
16266 regexes: urlRegexes,
16267 singleRegexMatchValue: true,
16268 multiple: true
16269 },
16270 propList: {
16271 propList: true
16272 },
16273 angle: {
16274 number: true,
16275 units: 'deg|rad',
16276 implicitUnits: 'rad'
16277 },
16278 textRotation: {
16279 number: true,
16280 units: 'deg|rad',
16281 implicitUnits: 'rad',
16282 enums: ['none', 'autorotate']
16283 },
16284 polygonPointList: {
16285 number: true,
16286 multiple: true,
16287 evenMultiple: true,
16288 min: -1,
16289 max: 1,
16290 unitless: true
16291 },
16292 edgeDistances: {
16293 enums: ['intersection', 'node-position']
16294 },
16295 edgeEndpoint: {
16296 number: true,
16297 multiple: true,
16298 units: '%|px|em|deg|rad',
16299 implicitUnits: 'px',
16300 enums: ['inside-to-node', 'outside-to-node', 'outside-to-node-or-label', 'outside-to-line', 'outside-to-line-or-label'],
16301 singleEnum: true,
16302 validate: function validate(valArr, unitsArr) {
16303 switch (valArr.length) {
16304 case 2:
16305 // can be % or px only
16306 return unitsArr[0] !== 'deg' && unitsArr[0] !== 'rad' && unitsArr[1] !== 'deg' && unitsArr[1] !== 'rad';
16307
16308 case 1:
16309 // can be enum, deg, or rad only
16310 return string(valArr[0]) || unitsArr[0] === 'deg' || unitsArr[0] === 'rad';
16311
16312 default:
16313 return false;
16314 }
16315 }
16316 },
16317 easing: {
16318 regexes: ['^(spring)\\s*\\(\\s*(' + number$1 + ')\\s*,\\s*(' + number$1 + ')\\s*\\)$', '^(cubic-bezier)\\s*\\(\\s*(' + number$1 + ')\\s*,\\s*(' + number$1 + ')\\s*,\\s*(' + number$1 + ')\\s*,\\s*(' + number$1 + ')\\s*\\)$'],
16319 enums: ['linear', 'ease', 'ease-in', 'ease-out', 'ease-in-out', 'ease-in-sine', 'ease-out-sine', 'ease-in-out-sine', 'ease-in-quad', 'ease-out-quad', 'ease-in-out-quad', 'ease-in-cubic', 'ease-out-cubic', 'ease-in-out-cubic', 'ease-in-quart', 'ease-out-quart', 'ease-in-out-quart', 'ease-in-quint', 'ease-out-quint', 'ease-in-out-quint', 'ease-in-expo', 'ease-out-expo', 'ease-in-out-expo', 'ease-in-circ', 'ease-out-circ', 'ease-in-out-circ']
16320 },
16321 gradientDirection: {
16322 enums: ['to-bottom', 'to-top', 'to-left', 'to-right', 'to-bottom-right', 'to-bottom-left', 'to-top-right', 'to-top-left', 'to-right-bottom', 'to-left-bottom', 'to-right-top', 'to-left-top' // different order
16323 ]
16324 },
16325 boundsExpansion: {
16326 number: true,
16327 multiple: true,
16328 min: 0,
16329 validate: function validate(valArr) {
16330 var length = valArr.length;
16331 return length === 1 || length === 2 || length === 4;
16332 }
16333 }
16334 };
16335 var diff = {
16336 zeroNonZero: function zeroNonZero(val1, val2) {
16337 if ((val1 == null || val2 == null) && val1 !== val2) {
16338 return true; // null cases could represent any value
16339 }
16340
16341 if (val1 == 0 && val2 != 0) {
16342 return true;
16343 } else if (val1 != 0 && val2 == 0) {
16344 return true;
16345 } else {
16346 return false;
16347 }
16348 },
16349 any: function any(val1, val2) {
16350 return val1 != val2;
16351 },
16352 emptyNonEmpty: function emptyNonEmpty(str1, str2) {
16353 var empty1 = emptyString(str1);
16354 var empty2 = emptyString(str2);
16355 return empty1 && !empty2 || !empty1 && empty2;
16356 }
16357 }; // define visual style properties
16358 //
16359 // - n.b. adding a new group of props may require updates to updateStyleHints()
16360 // - adding new props to an existing group gets handled automatically
16361
16362 var t = styfn$2.types;
16363 var mainLabel = [{
16364 name: 'label',
16365 type: t.text,
16366 triggersBounds: diff.any,
16367 triggersZOrder: diff.emptyNonEmpty
16368 }, {
16369 name: 'text-rotation',
16370 type: t.textRotation,
16371 triggersBounds: diff.any
16372 }, {
16373 name: 'text-margin-x',
16374 type: t.bidirectionalSize,
16375 triggersBounds: diff.any
16376 }, {
16377 name: 'text-margin-y',
16378 type: t.bidirectionalSize,
16379 triggersBounds: diff.any
16380 }];
16381 var sourceLabel = [{
16382 name: 'source-label',
16383 type: t.text,
16384 triggersBounds: diff.any
16385 }, {
16386 name: 'source-text-rotation',
16387 type: t.textRotation,
16388 triggersBounds: diff.any
16389 }, {
16390 name: 'source-text-margin-x',
16391 type: t.bidirectionalSize,
16392 triggersBounds: diff.any
16393 }, {
16394 name: 'source-text-margin-y',
16395 type: t.bidirectionalSize,
16396 triggersBounds: diff.any
16397 }, {
16398 name: 'source-text-offset',
16399 type: t.size,
16400 triggersBounds: diff.any
16401 }];
16402 var targetLabel = [{
16403 name: 'target-label',
16404 type: t.text,
16405 triggersBounds: diff.any
16406 }, {
16407 name: 'target-text-rotation',
16408 type: t.textRotation,
16409 triggersBounds: diff.any
16410 }, {
16411 name: 'target-text-margin-x',
16412 type: t.bidirectionalSize,
16413 triggersBounds: diff.any
16414 }, {
16415 name: 'target-text-margin-y',
16416 type: t.bidirectionalSize,
16417 triggersBounds: diff.any
16418 }, {
16419 name: 'target-text-offset',
16420 type: t.size,
16421 triggersBounds: diff.any
16422 }];
16423 var labelDimensions = [{
16424 name: 'font-family',
16425 type: t.fontFamily,
16426 triggersBounds: diff.any
16427 }, {
16428 name: 'font-style',
16429 type: t.fontStyle,
16430 triggersBounds: diff.any
16431 }, {
16432 name: 'font-weight',
16433 type: t.fontWeight,
16434 triggersBounds: diff.any
16435 }, {
16436 name: 'font-size',
16437 type: t.size,
16438 triggersBounds: diff.any
16439 }, {
16440 name: 'text-transform',
16441 type: t.textTransform,
16442 triggersBounds: diff.any
16443 }, {
16444 name: 'text-wrap',
16445 type: t.textWrap,
16446 triggersBounds: diff.any
16447 }, {
16448 name: 'text-overflow-wrap',
16449 type: t.textOverflowWrap,
16450 triggersBounds: diff.any
16451 }, {
16452 name: 'text-max-width',
16453 type: t.size,
16454 triggersBounds: diff.any
16455 }, {
16456 name: 'text-outline-width',
16457 type: t.size,
16458 triggersBounds: diff.any
16459 }, {
16460 name: 'line-height',
16461 type: t.positiveNumber,
16462 triggersBounds: diff.any
16463 }];
16464 var commonLabel = [{
16465 name: 'text-valign',
16466 type: t.valign,
16467 triggersBounds: diff.any
16468 }, {
16469 name: 'text-halign',
16470 type: t.halign,
16471 triggersBounds: diff.any
16472 }, {
16473 name: 'color',
16474 type: t.color
16475 }, {
16476 name: 'text-outline-color',
16477 type: t.color
16478 }, {
16479 name: 'text-outline-opacity',
16480 type: t.zeroOneNumber
16481 }, {
16482 name: 'text-background-color',
16483 type: t.color
16484 }, {
16485 name: 'text-background-opacity',
16486 type: t.zeroOneNumber
16487 }, {
16488 name: 'text-background-padding',
16489 type: t.size,
16490 triggersBounds: diff.any
16491 }, {
16492 name: 'text-border-opacity',
16493 type: t.zeroOneNumber
16494 }, {
16495 name: 'text-border-color',
16496 type: t.color
16497 }, {
16498 name: 'text-border-width',
16499 type: t.size,
16500 triggersBounds: diff.any
16501 }, {
16502 name: 'text-border-style',
16503 type: t.borderStyle,
16504 triggersBounds: diff.any
16505 }, {
16506 name: 'text-background-shape',
16507 type: t.textBackgroundShape,
16508 triggersBounds: diff.any
16509 }, {
16510 name: 'text-justification',
16511 type: t.justification
16512 }];
16513 var behavior = [{
16514 name: 'events',
16515 type: t.bool
16516 }, {
16517 name: 'text-events',
16518 type: t.bool
16519 }];
16520 var visibility = [{
16521 name: 'display',
16522 type: t.display,
16523 triggersZOrder: diff.any,
16524 triggersBounds: diff.any,
16525 triggersBoundsOfParallelBeziers: true
16526 }, {
16527 name: 'visibility',
16528 type: t.visibility,
16529 triggersZOrder: diff.any
16530 }, {
16531 name: 'opacity',
16532 type: t.zeroOneNumber,
16533 triggersZOrder: diff.zeroNonZero
16534 }, {
16535 name: 'text-opacity',
16536 type: t.zeroOneNumber
16537 }, {
16538 name: 'min-zoomed-font-size',
16539 type: t.size
16540 }, {
16541 name: 'z-compound-depth',
16542 type: t.zCompoundDepth,
16543 triggersZOrder: diff.any
16544 }, {
16545 name: 'z-index-compare',
16546 type: t.zIndexCompare,
16547 triggersZOrder: diff.any
16548 }, {
16549 name: 'z-index',
16550 type: t.nonNegativeInt,
16551 triggersZOrder: diff.any
16552 }];
16553 var overlay = [{
16554 name: 'overlay-padding',
16555 type: t.size,
16556 triggersBounds: diff.any
16557 }, {
16558 name: 'overlay-color',
16559 type: t.color
16560 }, {
16561 name: 'overlay-opacity',
16562 type: t.zeroOneNumber,
16563 triggersBounds: diff.zeroNonZero
16564 }, {
16565 name: 'overlay-shape',
16566 type: t.overlayShape,
16567 triggersBounds: diff.any
16568 }];
16569 var underlay = [{
16570 name: 'underlay-padding',
16571 type: t.size,
16572 triggersBounds: diff.any
16573 }, {
16574 name: 'underlay-color',
16575 type: t.color
16576 }, {
16577 name: 'underlay-opacity',
16578 type: t.zeroOneNumber,
16579 triggersBounds: diff.zeroNonZero
16580 }, {
16581 name: 'underlay-shape',
16582 type: t.overlayShape,
16583 triggersBounds: diff.any
16584 }];
16585 var transition = [{
16586 name: 'transition-property',
16587 type: t.propList
16588 }, {
16589 name: 'transition-duration',
16590 type: t.time
16591 }, {
16592 name: 'transition-delay',
16593 type: t.time
16594 }, {
16595 name: 'transition-timing-function',
16596 type: t.easing
16597 }];
16598
16599 var nodeSizeHashOverride = function nodeSizeHashOverride(ele, parsedProp) {
16600 if (parsedProp.value === 'label') {
16601 return -ele.poolIndex(); // no hash key hits is using label size (hitrate for perf probably low anyway)
16602 } else {
16603 return parsedProp.pfValue;
16604 }
16605 };
16606
16607 var nodeBody = [{
16608 name: 'height',
16609 type: t.nodeSize,
16610 triggersBounds: diff.any,
16611 hashOverride: nodeSizeHashOverride
16612 }, {
16613 name: 'width',
16614 type: t.nodeSize,
16615 triggersBounds: diff.any,
16616 hashOverride: nodeSizeHashOverride
16617 }, {
16618 name: 'shape',
16619 type: t.nodeShape,
16620 triggersBounds: diff.any
16621 }, {
16622 name: 'shape-polygon-points',
16623 type: t.polygonPointList,
16624 triggersBounds: diff.any
16625 }, {
16626 name: 'background-color',
16627 type: t.color
16628 }, {
16629 name: 'background-fill',
16630 type: t.fill
16631 }, {
16632 name: 'background-opacity',
16633 type: t.zeroOneNumber
16634 }, {
16635 name: 'background-blacken',
16636 type: t.nOneOneNumber
16637 }, {
16638 name: 'background-gradient-stop-colors',
16639 type: t.colors
16640 }, {
16641 name: 'background-gradient-stop-positions',
16642 type: t.percentages
16643 }, {
16644 name: 'background-gradient-direction',
16645 type: t.gradientDirection
16646 }, {
16647 name: 'padding',
16648 type: t.sizeMaybePercent,
16649 triggersBounds: diff.any
16650 }, {
16651 name: 'padding-relative-to',
16652 type: t.paddingRelativeTo,
16653 triggersBounds: diff.any
16654 }, {
16655 name: 'bounds-expansion',
16656 type: t.boundsExpansion,
16657 triggersBounds: diff.any
16658 }];
16659 var nodeBorder = [{
16660 name: 'border-color',
16661 type: t.color
16662 }, {
16663 name: 'border-opacity',
16664 type: t.zeroOneNumber
16665 }, {
16666 name: 'border-width',
16667 type: t.size,
16668 triggersBounds: diff.any
16669 }, {
16670 name: 'border-style',
16671 type: t.borderStyle
16672 }];
16673 var backgroundImage = [{
16674 name: 'background-image',
16675 type: t.urls
16676 }, {
16677 name: 'background-image-crossorigin',
16678 type: t.bgCrossOrigin
16679 }, {
16680 name: 'background-image-opacity',
16681 type: t.zeroOneNumbers
16682 }, {
16683 name: 'background-image-containment',
16684 type: t.bgContainment
16685 }, {
16686 name: 'background-image-smoothing',
16687 type: t.bools
16688 }, {
16689 name: 'background-position-x',
16690 type: t.bgPos
16691 }, {
16692 name: 'background-position-y',
16693 type: t.bgPos
16694 }, {
16695 name: 'background-width-relative-to',
16696 type: t.bgRelativeTo
16697 }, {
16698 name: 'background-height-relative-to',
16699 type: t.bgRelativeTo
16700 }, {
16701 name: 'background-repeat',
16702 type: t.bgRepeat
16703 }, {
16704 name: 'background-fit',
16705 type: t.bgFit
16706 }, {
16707 name: 'background-clip',
16708 type: t.bgClip
16709 }, {
16710 name: 'background-width',
16711 type: t.bgWH
16712 }, {
16713 name: 'background-height',
16714 type: t.bgWH
16715 }, {
16716 name: 'background-offset-x',
16717 type: t.bgPos
16718 }, {
16719 name: 'background-offset-y',
16720 type: t.bgPos
16721 }];
16722 var compound = [{
16723 name: 'position',
16724 type: t.position,
16725 triggersBounds: diff.any
16726 }, {
16727 name: 'compound-sizing-wrt-labels',
16728 type: t.compoundIncludeLabels,
16729 triggersBounds: diff.any
16730 }, {
16731 name: 'min-width',
16732 type: t.size,
16733 triggersBounds: diff.any
16734 }, {
16735 name: 'min-width-bias-left',
16736 type: t.sizeMaybePercent,
16737 triggersBounds: diff.any
16738 }, {
16739 name: 'min-width-bias-right',
16740 type: t.sizeMaybePercent,
16741 triggersBounds: diff.any
16742 }, {
16743 name: 'min-height',
16744 type: t.size,
16745 triggersBounds: diff.any
16746 }, {
16747 name: 'min-height-bias-top',
16748 type: t.sizeMaybePercent,
16749 triggersBounds: diff.any
16750 }, {
16751 name: 'min-height-bias-bottom',
16752 type: t.sizeMaybePercent,
16753 triggersBounds: diff.any
16754 }];
16755 var edgeLine = [{
16756 name: 'line-style',
16757 type: t.lineStyle
16758 }, {
16759 name: 'line-color',
16760 type: t.color
16761 }, {
16762 name: 'line-fill',
16763 type: t.fill
16764 }, {
16765 name: 'line-cap',
16766 type: t.lineCap
16767 }, {
16768 name: 'line-opacity',
16769 type: t.zeroOneNumber
16770 }, {
16771 name: 'line-dash-pattern',
16772 type: t.numbers
16773 }, {
16774 name: 'line-dash-offset',
16775 type: t.number
16776 }, {
16777 name: 'line-gradient-stop-colors',
16778 type: t.colors
16779 }, {
16780 name: 'line-gradient-stop-positions',
16781 type: t.percentages
16782 }, {
16783 name: 'curve-style',
16784 type: t.curveStyle,
16785 triggersBounds: diff.any,
16786 triggersBoundsOfParallelBeziers: true
16787 }, {
16788 name: 'haystack-radius',
16789 type: t.zeroOneNumber,
16790 triggersBounds: diff.any
16791 }, {
16792 name: 'source-endpoint',
16793 type: t.edgeEndpoint,
16794 triggersBounds: diff.any
16795 }, {
16796 name: 'target-endpoint',
16797 type: t.edgeEndpoint,
16798 triggersBounds: diff.any
16799 }, {
16800 name: 'control-point-step-size',
16801 type: t.size,
16802 triggersBounds: diff.any
16803 }, {
16804 name: 'control-point-distances',
16805 type: t.bidirectionalSizes,
16806 triggersBounds: diff.any
16807 }, {
16808 name: 'control-point-weights',
16809 type: t.numbers,
16810 triggersBounds: diff.any
16811 }, {
16812 name: 'segment-distances',
16813 type: t.bidirectionalSizes,
16814 triggersBounds: diff.any
16815 }, {
16816 name: 'segment-weights',
16817 type: t.numbers,
16818 triggersBounds: diff.any
16819 }, {
16820 name: 'taxi-turn',
16821 type: t.bidirectionalSizeMaybePercent,
16822 triggersBounds: diff.any
16823 }, {
16824 name: 'taxi-turn-min-distance',
16825 type: t.size,
16826 triggersBounds: diff.any
16827 }, {
16828 name: 'taxi-direction',
16829 type: t.axisDirection,
16830 triggersBounds: diff.any
16831 }, {
16832 name: 'edge-distances',
16833 type: t.edgeDistances,
16834 triggersBounds: diff.any
16835 }, {
16836 name: 'arrow-scale',
16837 type: t.positiveNumber,
16838 triggersBounds: diff.any
16839 }, {
16840 name: 'loop-direction',
16841 type: t.angle,
16842 triggersBounds: diff.any
16843 }, {
16844 name: 'loop-sweep',
16845 type: t.angle,
16846 triggersBounds: diff.any
16847 }, {
16848 name: 'source-distance-from-node',
16849 type: t.size,
16850 triggersBounds: diff.any
16851 }, {
16852 name: 'target-distance-from-node',
16853 type: t.size,
16854 triggersBounds: diff.any
16855 }];
16856 var ghost = [{
16857 name: 'ghost',
16858 type: t.bool,
16859 triggersBounds: diff.any
16860 }, {
16861 name: 'ghost-offset-x',
16862 type: t.bidirectionalSize,
16863 triggersBounds: diff.any
16864 }, {
16865 name: 'ghost-offset-y',
16866 type: t.bidirectionalSize,
16867 triggersBounds: diff.any
16868 }, {
16869 name: 'ghost-opacity',
16870 type: t.zeroOneNumber
16871 }];
16872 var core = [{
16873 name: 'selection-box-color',
16874 type: t.color
16875 }, {
16876 name: 'selection-box-opacity',
16877 type: t.zeroOneNumber
16878 }, {
16879 name: 'selection-box-border-color',
16880 type: t.color
16881 }, {
16882 name: 'selection-box-border-width',
16883 type: t.size
16884 }, {
16885 name: 'active-bg-color',
16886 type: t.color
16887 }, {
16888 name: 'active-bg-opacity',
16889 type: t.zeroOneNumber
16890 }, {
16891 name: 'active-bg-size',
16892 type: t.size
16893 }, {
16894 name: 'outside-texture-bg-color',
16895 type: t.color
16896 }, {
16897 name: 'outside-texture-bg-opacity',
16898 type: t.zeroOneNumber
16899 }]; // pie backgrounds for nodes
16900
16901 var pie = [];
16902 styfn$2.pieBackgroundN = 16; // because the pie properties are numbered, give access to a constant N (for renderer use)
16903
16904 pie.push({
16905 name: 'pie-size',
16906 type: t.sizeMaybePercent
16907 });
16908
16909 for (var i = 1; i <= styfn$2.pieBackgroundN; i++) {
16910 pie.push({
16911 name: 'pie-' + i + '-background-color',
16912 type: t.color
16913 });
16914 pie.push({
16915 name: 'pie-' + i + '-background-size',
16916 type: t.percent
16917 });
16918 pie.push({
16919 name: 'pie-' + i + '-background-opacity',
16920 type: t.zeroOneNumber
16921 });
16922 } // edge arrows
16923
16924
16925 var edgeArrow = [];
16926 var arrowPrefixes = styfn$2.arrowPrefixes = ['source', 'mid-source', 'target', 'mid-target'];
16927 [{
16928 name: 'arrow-shape',
16929 type: t.arrowShape,
16930 triggersBounds: diff.any
16931 }, {
16932 name: 'arrow-color',
16933 type: t.color
16934 }, {
16935 name: 'arrow-fill',
16936 type: t.arrowFill
16937 }].forEach(function (prop) {
16938 arrowPrefixes.forEach(function (prefix) {
16939 var name = prefix + '-' + prop.name;
16940 var type = prop.type,
16941 triggersBounds = prop.triggersBounds;
16942 edgeArrow.push({
16943 name: name,
16944 type: type,
16945 triggersBounds: triggersBounds
16946 });
16947 });
16948 }, {});
16949 var props = styfn$2.properties = [].concat(behavior, transition, visibility, overlay, underlay, ghost, commonLabel, labelDimensions, mainLabel, sourceLabel, targetLabel, nodeBody, nodeBorder, backgroundImage, pie, compound, edgeLine, edgeArrow, core);
16950 var propGroups = styfn$2.propertyGroups = {
16951 // common to all eles
16952 behavior: behavior,
16953 transition: transition,
16954 visibility: visibility,
16955 overlay: overlay,
16956 underlay: underlay,
16957 ghost: ghost,
16958 // labels
16959 commonLabel: commonLabel,
16960 labelDimensions: labelDimensions,
16961 mainLabel: mainLabel,
16962 sourceLabel: sourceLabel,
16963 targetLabel: targetLabel,
16964 // node props
16965 nodeBody: nodeBody,
16966 nodeBorder: nodeBorder,
16967 backgroundImage: backgroundImage,
16968 pie: pie,
16969 compound: compound,
16970 // edge props
16971 edgeLine: edgeLine,
16972 edgeArrow: edgeArrow,
16973 core: core
16974 };
16975 var propGroupNames = styfn$2.propertyGroupNames = {};
16976 var propGroupKeys = styfn$2.propertyGroupKeys = Object.keys(propGroups);
16977 propGroupKeys.forEach(function (key) {
16978 propGroupNames[key] = propGroups[key].map(function (prop) {
16979 return prop.name;
16980 });
16981 propGroups[key].forEach(function (prop) {
16982 return prop.groupKey = key;
16983 });
16984 }); // define aliases
16985
16986 var aliases = styfn$2.aliases = [{
16987 name: 'content',
16988 pointsTo: 'label'
16989 }, {
16990 name: 'control-point-distance',
16991 pointsTo: 'control-point-distances'
16992 }, {
16993 name: 'control-point-weight',
16994 pointsTo: 'control-point-weights'
16995 }, {
16996 name: 'edge-text-rotation',
16997 pointsTo: 'text-rotation'
16998 }, {
16999 name: 'padding-left',
17000 pointsTo: 'padding'
17001 }, {
17002 name: 'padding-right',
17003 pointsTo: 'padding'
17004 }, {
17005 name: 'padding-top',
17006 pointsTo: 'padding'
17007 }, {
17008 name: 'padding-bottom',
17009 pointsTo: 'padding'
17010 }]; // list of property names
17011
17012 styfn$2.propertyNames = props.map(function (p) {
17013 return p.name;
17014 }); // allow access of properties by name ( e.g. style.properties.height )
17015
17016 for (var _i = 0; _i < props.length; _i++) {
17017 var prop = props[_i];
17018 props[prop.name] = prop; // allow lookup by name
17019 } // map aliases
17020
17021
17022 for (var _i2 = 0; _i2 < aliases.length; _i2++) {
17023 var alias = aliases[_i2];
17024 var pointsToProp = props[alias.pointsTo];
17025 var aliasProp = {
17026 name: alias.name,
17027 alias: true,
17028 pointsTo: pointsToProp
17029 }; // add alias prop for parsing
17030
17031 props.push(aliasProp);
17032 props[alias.name] = aliasProp; // allow lookup by name
17033 }
17034})();
17035
17036styfn$2.getDefaultProperty = function (name) {
17037 return this.getDefaultProperties()[name];
17038};
17039
17040styfn$2.getDefaultProperties = function () {
17041 var _p = this._private;
17042
17043 if (_p.defaultProperties != null) {
17044 return _p.defaultProperties;
17045 }
17046
17047 var rawProps = extend({
17048 // core props
17049 'selection-box-color': '#ddd',
17050 'selection-box-opacity': 0.65,
17051 'selection-box-border-color': '#aaa',
17052 'selection-box-border-width': 1,
17053 'active-bg-color': 'black',
17054 'active-bg-opacity': 0.15,
17055 'active-bg-size': 30,
17056 'outside-texture-bg-color': '#000',
17057 'outside-texture-bg-opacity': 0.125,
17058 // common node/edge props
17059 'events': 'yes',
17060 'text-events': 'no',
17061 'text-valign': 'top',
17062 'text-halign': 'center',
17063 'text-justification': 'auto',
17064 'line-height': 1,
17065 'color': '#000',
17066 'text-outline-color': '#000',
17067 'text-outline-width': 0,
17068 'text-outline-opacity': 1,
17069 'text-opacity': 1,
17070 'text-decoration': 'none',
17071 'text-transform': 'none',
17072 'text-wrap': 'none',
17073 'text-overflow-wrap': 'whitespace',
17074 'text-max-width': 9999,
17075 'text-background-color': '#000',
17076 'text-background-opacity': 0,
17077 'text-background-shape': 'rectangle',
17078 'text-background-padding': 0,
17079 'text-border-opacity': 0,
17080 'text-border-width': 0,
17081 'text-border-style': 'solid',
17082 'text-border-color': '#000',
17083 'font-family': 'Helvetica Neue, Helvetica, sans-serif',
17084 'font-style': 'normal',
17085 'font-weight': 'normal',
17086 'font-size': 16,
17087 'min-zoomed-font-size': 0,
17088 'text-rotation': 'none',
17089 'source-text-rotation': 'none',
17090 'target-text-rotation': 'none',
17091 'visibility': 'visible',
17092 'display': 'element',
17093 'opacity': 1,
17094 'z-compound-depth': 'auto',
17095 'z-index-compare': 'auto',
17096 'z-index': 0,
17097 'label': '',
17098 'text-margin-x': 0,
17099 'text-margin-y': 0,
17100 'source-label': '',
17101 'source-text-offset': 0,
17102 'source-text-margin-x': 0,
17103 'source-text-margin-y': 0,
17104 'target-label': '',
17105 'target-text-offset': 0,
17106 'target-text-margin-x': 0,
17107 'target-text-margin-y': 0,
17108 'overlay-opacity': 0,
17109 'overlay-color': '#000',
17110 'overlay-padding': 10,
17111 'overlay-shape': 'round-rectangle',
17112 'underlay-opacity': 0,
17113 'underlay-color': '#000',
17114 'underlay-padding': 10,
17115 'underlay-shape': 'round-rectangle',
17116 'transition-property': 'none',
17117 'transition-duration': 0,
17118 'transition-delay': 0,
17119 'transition-timing-function': 'linear',
17120 // node props
17121 'background-blacken': 0,
17122 'background-color': '#999',
17123 'background-fill': 'solid',
17124 'background-opacity': 1,
17125 'background-image': 'none',
17126 'background-image-crossorigin': 'anonymous',
17127 'background-image-opacity': 1,
17128 'background-image-containment': 'inside',
17129 'background-image-smoothing': 'yes',
17130 'background-position-x': '50%',
17131 'background-position-y': '50%',
17132 'background-offset-x': 0,
17133 'background-offset-y': 0,
17134 'background-width-relative-to': 'include-padding',
17135 'background-height-relative-to': 'include-padding',
17136 'background-repeat': 'no-repeat',
17137 'background-fit': 'none',
17138 'background-clip': 'node',
17139 'background-width': 'auto',
17140 'background-height': 'auto',
17141 'border-color': '#000',
17142 'border-opacity': 1,
17143 'border-width': 0,
17144 'border-style': 'solid',
17145 'height': 30,
17146 'width': 30,
17147 'shape': 'ellipse',
17148 'shape-polygon-points': '-1, -1, 1, -1, 1, 1, -1, 1',
17149 'bounds-expansion': 0,
17150 // node gradient
17151 'background-gradient-direction': 'to-bottom',
17152 'background-gradient-stop-colors': '#999',
17153 'background-gradient-stop-positions': '0%',
17154 // ghost props
17155 'ghost': 'no',
17156 'ghost-offset-y': 0,
17157 'ghost-offset-x': 0,
17158 'ghost-opacity': 0,
17159 // compound props
17160 'padding': 0,
17161 'padding-relative-to': 'width',
17162 'position': 'origin',
17163 'compound-sizing-wrt-labels': 'include',
17164 'min-width': 0,
17165 'min-width-bias-left': 0,
17166 'min-width-bias-right': 0,
17167 'min-height': 0,
17168 'min-height-bias-top': 0,
17169 'min-height-bias-bottom': 0
17170 }, {
17171 // node pie bg
17172 'pie-size': '100%'
17173 }, [{
17174 name: 'pie-{{i}}-background-color',
17175 value: 'black'
17176 }, {
17177 name: 'pie-{{i}}-background-size',
17178 value: '0%'
17179 }, {
17180 name: 'pie-{{i}}-background-opacity',
17181 value: 1
17182 }].reduce(function (css, prop) {
17183 for (var i = 1; i <= styfn$2.pieBackgroundN; i++) {
17184 var name = prop.name.replace('{{i}}', i);
17185 var val = prop.value;
17186 css[name] = val;
17187 }
17188
17189 return css;
17190 }, {}), {
17191 // edge props
17192 'line-style': 'solid',
17193 'line-color': '#999',
17194 'line-fill': 'solid',
17195 'line-cap': 'butt',
17196 'line-opacity': 1,
17197 'line-gradient-stop-colors': '#999',
17198 'line-gradient-stop-positions': '0%',
17199 'control-point-step-size': 40,
17200 'control-point-weights': 0.5,
17201 'segment-weights': 0.5,
17202 'segment-distances': 20,
17203 'taxi-turn': '50%',
17204 'taxi-turn-min-distance': 10,
17205 'taxi-direction': 'auto',
17206 'edge-distances': 'intersection',
17207 'curve-style': 'haystack',
17208 'haystack-radius': 0,
17209 'arrow-scale': 1,
17210 'loop-direction': '-45deg',
17211 'loop-sweep': '-90deg',
17212 'source-distance-from-node': 0,
17213 'target-distance-from-node': 0,
17214 'source-endpoint': 'outside-to-node',
17215 'target-endpoint': 'outside-to-node',
17216 'line-dash-pattern': [6, 3],
17217 'line-dash-offset': 0
17218 }, [{
17219 name: 'arrow-shape',
17220 value: 'none'
17221 }, {
17222 name: 'arrow-color',
17223 value: '#999'
17224 }, {
17225 name: 'arrow-fill',
17226 value: 'filled'
17227 }].reduce(function (css, prop) {
17228 styfn$2.arrowPrefixes.forEach(function (prefix) {
17229 var name = prefix + '-' + prop.name;
17230 var val = prop.value;
17231 css[name] = val;
17232 });
17233 return css;
17234 }, {}));
17235 var parsedProps = {};
17236
17237 for (var i = 0; i < this.properties.length; i++) {
17238 var prop = this.properties[i];
17239
17240 if (prop.pointsTo) {
17241 continue;
17242 }
17243
17244 var name = prop.name;
17245 var val = rawProps[name];
17246 var parsedProp = this.parse(name, val);
17247 parsedProps[name] = parsedProp;
17248 }
17249
17250 _p.defaultProperties = parsedProps;
17251 return _p.defaultProperties;
17252};
17253
17254styfn$2.addDefaultStylesheet = function () {
17255 this.selector(':parent').css({
17256 'shape': 'rectangle',
17257 'padding': 10,
17258 'background-color': '#eee',
17259 'border-color': '#ccc',
17260 'border-width': 1
17261 }).selector('edge').css({
17262 'width': 3
17263 }).selector(':loop').css({
17264 'curve-style': 'bezier'
17265 }).selector('edge:compound').css({
17266 'curve-style': 'bezier',
17267 'source-endpoint': 'outside-to-line',
17268 'target-endpoint': 'outside-to-line'
17269 }).selector(':selected').css({
17270 'background-color': '#0169D9',
17271 'line-color': '#0169D9',
17272 'source-arrow-color': '#0169D9',
17273 'target-arrow-color': '#0169D9',
17274 'mid-source-arrow-color': '#0169D9',
17275 'mid-target-arrow-color': '#0169D9'
17276 }).selector(':parent:selected').css({
17277 'background-color': '#CCE1F9',
17278 'border-color': '#aec8e5'
17279 }).selector(':active').css({
17280 'overlay-color': 'black',
17281 'overlay-padding': 10,
17282 'overlay-opacity': 0.25
17283 });
17284 this.defaultLength = this.length;
17285};
17286
17287var styfn$1 = {}; // a caching layer for property parsing
17288
17289styfn$1.parse = function (name, value, propIsBypass, propIsFlat) {
17290 var self = this; // function values can't be cached in all cases, and there isn't much benefit of caching them anyway
17291
17292 if (fn$6(value)) {
17293 return self.parseImplWarn(name, value, propIsBypass, propIsFlat);
17294 }
17295
17296 var flatKey = propIsFlat === 'mapping' || propIsFlat === true || propIsFlat === false || propIsFlat == null ? 'dontcare' : propIsFlat;
17297 var bypassKey = propIsBypass ? 't' : 'f';
17298 var valueKey = '' + value;
17299 var argHash = hashStrings(name, valueKey, bypassKey, flatKey);
17300 var propCache = self.propCache = self.propCache || [];
17301 var ret;
17302
17303 if (!(ret = propCache[argHash])) {
17304 ret = propCache[argHash] = self.parseImplWarn(name, value, propIsBypass, propIsFlat);
17305 } // - bypasses can't be shared b/c the value can be changed by animations or otherwise overridden
17306 // - mappings can't be shared b/c mappings are per-element
17307
17308
17309 if (propIsBypass || propIsFlat === 'mapping') {
17310 // need a copy since props are mutated later in their lifecycles
17311 ret = copy(ret);
17312
17313 if (ret) {
17314 ret.value = copy(ret.value); // because it could be an array, e.g. colour
17315 }
17316 }
17317
17318 return ret;
17319};
17320
17321styfn$1.parseImplWarn = function (name, value, propIsBypass, propIsFlat) {
17322 var prop = this.parseImpl(name, value, propIsBypass, propIsFlat);
17323
17324 if (!prop && value != null) {
17325 warn("The style property `".concat(name, ": ").concat(value, "` is invalid"));
17326 }
17327
17328 if (prop && (prop.name === 'width' || prop.name === 'height') && value === 'label') {
17329 warn('The style value of `label` is deprecated for `' + prop.name + '`');
17330 }
17331
17332 return prop;
17333}; // parse a property; return null on invalid; return parsed property otherwise
17334// fields :
17335// - name : the name of the property
17336// - value : the parsed, native-typed value of the property
17337// - strValue : a string value that represents the property value in valid css
17338// - bypass : true iff the property is a bypass property
17339
17340
17341styfn$1.parseImpl = function (name, value, propIsBypass, propIsFlat) {
17342 var self = this;
17343 name = camel2dash(name); // make sure the property name is in dash form (e.g. 'property-name' not 'propertyName')
17344
17345 var property = self.properties[name];
17346 var passedValue = value;
17347 var types = self.types;
17348
17349 if (!property) {
17350 return null;
17351 } // return null on property of unknown name
17352
17353
17354 if (value === undefined) {
17355 return null;
17356 } // can't assign undefined
17357 // the property may be an alias
17358
17359
17360 if (property.alias) {
17361 property = property.pointsTo;
17362 name = property.name;
17363 }
17364
17365 var valueIsString = string(value);
17366
17367 if (valueIsString) {
17368 // trim the value to make parsing easier
17369 value = value.trim();
17370 }
17371
17372 var type = property.type;
17373
17374 if (!type) {
17375 return null;
17376 } // no type, no luck
17377 // check if bypass is null or empty string (i.e. indication to delete bypass property)
17378
17379
17380 if (propIsBypass && (value === '' || value === null)) {
17381 return {
17382 name: name,
17383 value: value,
17384 bypass: true,
17385 deleteBypass: true
17386 };
17387 } // check if value is a function used as a mapper
17388
17389
17390 if (fn$6(value)) {
17391 return {
17392 name: name,
17393 value: value,
17394 strValue: 'fn',
17395 mapped: types.fn,
17396 bypass: propIsBypass
17397 };
17398 } // check if value is mapped
17399
17400
17401 var data, mapData;
17402
17403 if (!valueIsString || propIsFlat || value.length < 7 || value[1] !== 'a') ; else if (value.length >= 7 && value[0] === 'd' && (data = new RegExp(types.data.regex).exec(value))) {
17404 if (propIsBypass) {
17405 return false;
17406 } // mappers not allowed in bypass
17407
17408
17409 var mapped = types.data;
17410 return {
17411 name: name,
17412 value: data,
17413 strValue: '' + value,
17414 mapped: mapped,
17415 field: data[1],
17416 bypass: propIsBypass
17417 };
17418 } else if (value.length >= 10 && value[0] === 'm' && (mapData = new RegExp(types.mapData.regex).exec(value))) {
17419 if (propIsBypass) {
17420 return false;
17421 } // mappers not allowed in bypass
17422
17423
17424 if (type.multiple) {
17425 return false;
17426 } // impossible to map to num
17427
17428
17429 var _mapped = types.mapData; // we can map only if the type is a colour or a number
17430
17431 if (!(type.color || type.number)) {
17432 return false;
17433 }
17434
17435 var valueMin = this.parse(name, mapData[4]); // parse to validate
17436
17437 if (!valueMin || valueMin.mapped) {
17438 return false;
17439 } // can't be invalid or mapped
17440
17441
17442 var valueMax = this.parse(name, mapData[5]); // parse to validate
17443
17444 if (!valueMax || valueMax.mapped) {
17445 return false;
17446 } // can't be invalid or mapped
17447 // check if valueMin and valueMax are the same
17448
17449
17450 if (valueMin.pfValue === valueMax.pfValue || valueMin.strValue === valueMax.strValue) {
17451 warn('`' + name + ': ' + value + '` is not a valid mapper because the output range is zero; converting to `' + name + ': ' + valueMin.strValue + '`');
17452 return this.parse(name, valueMin.strValue); // can't make much of a mapper without a range
17453 } else if (type.color) {
17454 var c1 = valueMin.value;
17455 var c2 = valueMax.value;
17456 var same = c1[0] === c2[0] // red
17457 && c1[1] === c2[1] // green
17458 && c1[2] === c2[2] // blue
17459 && ( // optional alpha
17460 c1[3] === c2[3] // same alpha outright
17461 || (c1[3] == null || c1[3] === 1 // full opacity for colour 1?
17462 ) && (c2[3] == null || c2[3] === 1) // full opacity for colour 2?
17463 );
17464
17465 if (same) {
17466 return false;
17467 } // can't make a mapper without a range
17468
17469 }
17470
17471 return {
17472 name: name,
17473 value: mapData,
17474 strValue: '' + value,
17475 mapped: _mapped,
17476 field: mapData[1],
17477 fieldMin: parseFloat(mapData[2]),
17478 // min & max are numeric
17479 fieldMax: parseFloat(mapData[3]),
17480 valueMin: valueMin.value,
17481 valueMax: valueMax.value,
17482 bypass: propIsBypass
17483 };
17484 }
17485
17486 if (type.multiple && propIsFlat !== 'multiple') {
17487 var vals;
17488
17489 if (valueIsString) {
17490 vals = value.split(/\s+/);
17491 } else if (array(value)) {
17492 vals = value;
17493 } else {
17494 vals = [value];
17495 }
17496
17497 if (type.evenMultiple && vals.length % 2 !== 0) {
17498 return null;
17499 }
17500
17501 var valArr = [];
17502 var unitsArr = [];
17503 var pfValArr = [];
17504 var strVal = '';
17505 var hasEnum = false;
17506
17507 for (var i = 0; i < vals.length; i++) {
17508 var p = self.parse(name, vals[i], propIsBypass, 'multiple');
17509 hasEnum = hasEnum || string(p.value);
17510 valArr.push(p.value);
17511 pfValArr.push(p.pfValue != null ? p.pfValue : p.value);
17512 unitsArr.push(p.units);
17513 strVal += (i > 0 ? ' ' : '') + p.strValue;
17514 }
17515
17516 if (type.validate && !type.validate(valArr, unitsArr)) {
17517 return null;
17518 }
17519
17520 if (type.singleEnum && hasEnum) {
17521 if (valArr.length === 1 && string(valArr[0])) {
17522 return {
17523 name: name,
17524 value: valArr[0],
17525 strValue: valArr[0],
17526 bypass: propIsBypass
17527 };
17528 } else {
17529 return null;
17530 }
17531 }
17532
17533 return {
17534 name: name,
17535 value: valArr,
17536 pfValue: pfValArr,
17537 strValue: strVal,
17538 bypass: propIsBypass,
17539 units: unitsArr
17540 };
17541 } // several types also allow enums
17542
17543
17544 var checkEnums = function checkEnums() {
17545 for (var _i = 0; _i < type.enums.length; _i++) {
17546 var en = type.enums[_i];
17547
17548 if (en === value) {
17549 return {
17550 name: name,
17551 value: value,
17552 strValue: '' + value,
17553 bypass: propIsBypass
17554 };
17555 }
17556 }
17557
17558 return null;
17559 }; // check the type and return the appropriate object
17560
17561
17562 if (type.number) {
17563 var units;
17564 var implicitUnits = 'px'; // not set => px
17565
17566 if (type.units) {
17567 // use specified units if set
17568 units = type.units;
17569 }
17570
17571 if (type.implicitUnits) {
17572 implicitUnits = type.implicitUnits;
17573 }
17574
17575 if (!type.unitless) {
17576 if (valueIsString) {
17577 var unitsRegex = 'px|em' + (type.allowPercent ? '|\\%' : '');
17578
17579 if (units) {
17580 unitsRegex = units;
17581 } // only allow explicit units if so set
17582
17583
17584 var match = value.match('^(' + number + ')(' + unitsRegex + ')?' + '$');
17585
17586 if (match) {
17587 value = match[1];
17588 units = match[2] || implicitUnits;
17589 }
17590 } else if (!units || type.implicitUnits) {
17591 units = implicitUnits; // implicitly px if unspecified
17592 }
17593 }
17594
17595 value = parseFloat(value); // if not a number and enums not allowed, then the value is invalid
17596
17597 if (isNaN(value) && type.enums === undefined) {
17598 return null;
17599 } // check if this number type also accepts special keywords in place of numbers
17600 // (i.e. `left`, `auto`, etc)
17601
17602
17603 if (isNaN(value) && type.enums !== undefined) {
17604 value = passedValue;
17605 return checkEnums();
17606 } // check if value must be an integer
17607
17608
17609 if (type.integer && !integer(value)) {
17610 return null;
17611 } // check value is within range
17612
17613
17614 if (type.min !== undefined && (value < type.min || type.strictMin && value === type.min) || type.max !== undefined && (value > type.max || type.strictMax && value === type.max)) {
17615 return null;
17616 }
17617
17618 var ret = {
17619 name: name,
17620 value: value,
17621 strValue: '' + value + (units ? units : ''),
17622 units: units,
17623 bypass: propIsBypass
17624 }; // normalise value in pixels
17625
17626 if (type.unitless || units !== 'px' && units !== 'em') {
17627 ret.pfValue = value;
17628 } else {
17629 ret.pfValue = units === 'px' || !units ? value : this.getEmSizeInPixels() * value;
17630 } // normalise value in ms
17631
17632
17633 if (units === 'ms' || units === 's') {
17634 ret.pfValue = units === 'ms' ? value : 1000 * value;
17635 } // normalise value in rad
17636
17637
17638 if (units === 'deg' || units === 'rad') {
17639 ret.pfValue = units === 'rad' ? value : deg2rad(value);
17640 } // normalize value in %
17641
17642
17643 if (units === '%') {
17644 ret.pfValue = value / 100;
17645 }
17646
17647 return ret;
17648 } else if (type.propList) {
17649 var props = [];
17650 var propsStr = '' + value;
17651
17652 if (propsStr === 'none') ; else {
17653 // go over each prop
17654 var propsSplit = propsStr.split(/\s*,\s*|\s+/);
17655
17656 for (var _i2 = 0; _i2 < propsSplit.length; _i2++) {
17657 var propName = propsSplit[_i2].trim();
17658
17659 if (self.properties[propName]) {
17660 props.push(propName);
17661 } else {
17662 warn('`' + propName + '` is not a valid property name');
17663 }
17664 }
17665
17666 if (props.length === 0) {
17667 return null;
17668 }
17669 }
17670
17671 return {
17672 name: name,
17673 value: props,
17674 strValue: props.length === 0 ? 'none' : props.join(' '),
17675 bypass: propIsBypass
17676 };
17677 } else if (type.color) {
17678 var tuple = color2tuple(value);
17679
17680 if (!tuple) {
17681 return null;
17682 }
17683
17684 return {
17685 name: name,
17686 value: tuple,
17687 pfValue: tuple,
17688 strValue: 'rgb(' + tuple[0] + ',' + tuple[1] + ',' + tuple[2] + ')',
17689 // n.b. no spaces b/c of multiple support
17690 bypass: propIsBypass
17691 };
17692 } else if (type.regex || type.regexes) {
17693 // first check enums
17694 if (type.enums) {
17695 var enumProp = checkEnums();
17696
17697 if (enumProp) {
17698 return enumProp;
17699 }
17700 }
17701
17702 var regexes = type.regexes ? type.regexes : [type.regex];
17703
17704 for (var _i3 = 0; _i3 < regexes.length; _i3++) {
17705 var regex = new RegExp(regexes[_i3]); // make a regex from the type string
17706
17707 var m = regex.exec(value);
17708
17709 if (m) {
17710 // regex matches
17711 return {
17712 name: name,
17713 value: type.singleRegexMatchValue ? m[1] : m,
17714 strValue: '' + value,
17715 bypass: propIsBypass
17716 };
17717 }
17718 }
17719
17720 return null; // didn't match any
17721 } else if (type.string) {
17722 // just return
17723 return {
17724 name: name,
17725 value: '' + value,
17726 strValue: '' + value,
17727 bypass: propIsBypass
17728 };
17729 } else if (type.enums) {
17730 // check enums last because it's a combo type in others
17731 return checkEnums();
17732 } else {
17733 return null; // not a type we can handle
17734 }
17735};
17736
17737var Style = function Style(cy) {
17738 if (!(this instanceof Style)) {
17739 return new Style(cy);
17740 }
17741
17742 if (!core(cy)) {
17743 error('A style must have a core reference');
17744 return;
17745 }
17746
17747 this._private = {
17748 cy: cy,
17749 coreStyle: {}
17750 };
17751 this.length = 0;
17752 this.resetToDefault();
17753};
17754
17755var styfn = Style.prototype;
17756
17757styfn.instanceString = function () {
17758 return 'style';
17759}; // remove all contexts
17760
17761
17762styfn.clear = function () {
17763 var _p = this._private;
17764 var cy = _p.cy;
17765 var eles = cy.elements();
17766
17767 for (var i = 0; i < this.length; i++) {
17768 this[i] = undefined;
17769 }
17770
17771 this.length = 0;
17772 _p.contextStyles = {};
17773 _p.propDiffs = {};
17774 this.cleanElements(eles, true);
17775 eles.forEach(function (ele) {
17776 var ele_p = ele[0]._private;
17777 ele_p.styleDirty = true;
17778 ele_p.appliedInitStyle = false;
17779 });
17780 return this; // chaining
17781};
17782
17783styfn.resetToDefault = function () {
17784 this.clear();
17785 this.addDefaultStylesheet();
17786 return this;
17787}; // builds a style object for the 'core' selector
17788
17789
17790styfn.core = function (propName) {
17791 return this._private.coreStyle[propName] || this.getDefaultProperty(propName);
17792}; // create a new context from the specified selector string and switch to that context
17793
17794
17795styfn.selector = function (selectorStr) {
17796 // 'core' is a special case and does not need a selector
17797 var selector = selectorStr === 'core' ? null : new Selector(selectorStr);
17798 var i = this.length++; // new context means new index
17799
17800 this[i] = {
17801 selector: selector,
17802 properties: [],
17803 mappedProperties: [],
17804 index: i
17805 };
17806 return this; // chaining
17807}; // add one or many css rules to the current context
17808
17809
17810styfn.css = function () {
17811 var self = this;
17812 var args = arguments;
17813
17814 if (args.length === 1) {
17815 var map = args[0];
17816
17817 for (var i = 0; i < self.properties.length; i++) {
17818 var prop = self.properties[i];
17819 var mapVal = map[prop.name];
17820
17821 if (mapVal === undefined) {
17822 mapVal = map[dash2camel(prop.name)];
17823 }
17824
17825 if (mapVal !== undefined) {
17826 this.cssRule(prop.name, mapVal);
17827 }
17828 }
17829 } else if (args.length === 2) {
17830 this.cssRule(args[0], args[1]);
17831 } // do nothing if args are invalid
17832
17833
17834 return this; // chaining
17835};
17836
17837styfn.style = styfn.css; // add a single css rule to the current context
17838
17839styfn.cssRule = function (name, value) {
17840 // name-value pair
17841 var property = this.parse(name, value); // add property to current context if valid
17842
17843 if (property) {
17844 var i = this.length - 1;
17845 this[i].properties.push(property);
17846 this[i].properties[property.name] = property; // allow access by name as well
17847
17848 if (property.name.match(/pie-(\d+)-background-size/) && property.value) {
17849 this._private.hasPie = true;
17850 }
17851
17852 if (property.mapped) {
17853 this[i].mappedProperties.push(property);
17854 } // add to core style if necessary
17855
17856
17857 var currentSelectorIsCore = !this[i].selector;
17858
17859 if (currentSelectorIsCore) {
17860 this._private.coreStyle[property.name] = property;
17861 }
17862 }
17863
17864 return this; // chaining
17865};
17866
17867styfn.append = function (style) {
17868 if (stylesheet(style)) {
17869 style.appendToStyle(this);
17870 } else if (array(style)) {
17871 this.appendFromJson(style);
17872 } else if (string(style)) {
17873 this.appendFromString(style);
17874 } // you probably wouldn't want to append a Style, since you'd duplicate the default parts
17875
17876
17877 return this;
17878}; // static function
17879
17880
17881Style.fromJson = function (cy, json) {
17882 var style = new Style(cy);
17883 style.fromJson(json);
17884 return style;
17885};
17886
17887Style.fromString = function (cy, string) {
17888 return new Style(cy).fromString(string);
17889};
17890
17891[styfn$8, styfn$7, styfn$6, styfn$5, styfn$4, styfn$3, styfn$2, styfn$1].forEach(function (props) {
17892 extend(styfn, props);
17893});
17894Style.types = styfn.types;
17895Style.properties = styfn.properties;
17896Style.propertyGroups = styfn.propertyGroups;
17897Style.propertyGroupNames = styfn.propertyGroupNames;
17898Style.propertyGroupKeys = styfn.propertyGroupKeys;
17899
17900var corefn$2 = {
17901 style: function style(newStyle) {
17902 if (newStyle) {
17903 var s = this.setStyle(newStyle);
17904 s.update();
17905 }
17906
17907 return this._private.style;
17908 },
17909 setStyle: function setStyle(style) {
17910 var _p = this._private;
17911
17912 if (stylesheet(style)) {
17913 _p.style = style.generateStyle(this);
17914 } else if (array(style)) {
17915 _p.style = Style.fromJson(this, style);
17916 } else if (string(style)) {
17917 _p.style = Style.fromString(this, style);
17918 } else {
17919 _p.style = Style(this);
17920 }
17921
17922 return _p.style;
17923 },
17924 // e.g. cy.data() changed => recalc ele mappers
17925 updateStyle: function updateStyle() {
17926 this.mutableElements().updateStyle(); // just send to all eles
17927 }
17928};
17929
17930var defaultSelectionType = 'single';
17931var corefn$1 = {
17932 autolock: function autolock(bool) {
17933 if (bool !== undefined) {
17934 this._private.autolock = bool ? true : false;
17935 } else {
17936 return this._private.autolock;
17937 }
17938
17939 return this; // chaining
17940 },
17941 autoungrabify: function autoungrabify(bool) {
17942 if (bool !== undefined) {
17943 this._private.autoungrabify = bool ? true : false;
17944 } else {
17945 return this._private.autoungrabify;
17946 }
17947
17948 return this; // chaining
17949 },
17950 autounselectify: function autounselectify(bool) {
17951 if (bool !== undefined) {
17952 this._private.autounselectify = bool ? true : false;
17953 } else {
17954 return this._private.autounselectify;
17955 }
17956
17957 return this; // chaining
17958 },
17959 selectionType: function selectionType(selType) {
17960 var _p = this._private;
17961
17962 if (_p.selectionType == null) {
17963 _p.selectionType = defaultSelectionType;
17964 }
17965
17966 if (selType !== undefined) {
17967 if (selType === 'additive' || selType === 'single') {
17968 _p.selectionType = selType;
17969 }
17970 } else {
17971 return _p.selectionType;
17972 }
17973
17974 return this;
17975 },
17976 panningEnabled: function panningEnabled(bool) {
17977 if (bool !== undefined) {
17978 this._private.panningEnabled = bool ? true : false;
17979 } else {
17980 return this._private.panningEnabled;
17981 }
17982
17983 return this; // chaining
17984 },
17985 userPanningEnabled: function userPanningEnabled(bool) {
17986 if (bool !== undefined) {
17987 this._private.userPanningEnabled = bool ? true : false;
17988 } else {
17989 return this._private.userPanningEnabled;
17990 }
17991
17992 return this; // chaining
17993 },
17994 zoomingEnabled: function zoomingEnabled(bool) {
17995 if (bool !== undefined) {
17996 this._private.zoomingEnabled = bool ? true : false;
17997 } else {
17998 return this._private.zoomingEnabled;
17999 }
18000
18001 return this; // chaining
18002 },
18003 userZoomingEnabled: function userZoomingEnabled(bool) {
18004 if (bool !== undefined) {
18005 this._private.userZoomingEnabled = bool ? true : false;
18006 } else {
18007 return this._private.userZoomingEnabled;
18008 }
18009
18010 return this; // chaining
18011 },
18012 boxSelectionEnabled: function boxSelectionEnabled(bool) {
18013 if (bool !== undefined) {
18014 this._private.boxSelectionEnabled = bool ? true : false;
18015 } else {
18016 return this._private.boxSelectionEnabled;
18017 }
18018
18019 return this; // chaining
18020 },
18021 pan: function pan() {
18022 var args = arguments;
18023 var pan = this._private.pan;
18024 var dim, val, dims, x, y;
18025
18026 switch (args.length) {
18027 case 0:
18028 // .pan()
18029 return pan;
18030
18031 case 1:
18032 if (string(args[0])) {
18033 // .pan('x')
18034 dim = args[0];
18035 return pan[dim];
18036 } else if (plainObject(args[0])) {
18037 // .pan({ x: 0, y: 100 })
18038 if (!this._private.panningEnabled) {
18039 return this;
18040 }
18041
18042 dims = args[0];
18043 x = dims.x;
18044 y = dims.y;
18045
18046 if (number$1(x)) {
18047 pan.x = x;
18048 }
18049
18050 if (number$1(y)) {
18051 pan.y = y;
18052 }
18053
18054 this.emit('pan viewport');
18055 }
18056
18057 break;
18058
18059 case 2:
18060 // .pan('x', 100)
18061 if (!this._private.panningEnabled) {
18062 return this;
18063 }
18064
18065 dim = args[0];
18066 val = args[1];
18067
18068 if ((dim === 'x' || dim === 'y') && number$1(val)) {
18069 pan[dim] = val;
18070 }
18071
18072 this.emit('pan viewport');
18073 break;
18074 // invalid
18075 }
18076
18077 this.notify('viewport');
18078 return this; // chaining
18079 },
18080 panBy: function panBy(arg0, arg1) {
18081 var args = arguments;
18082 var pan = this._private.pan;
18083 var dim, val, dims, x, y;
18084
18085 if (!this._private.panningEnabled) {
18086 return this;
18087 }
18088
18089 switch (args.length) {
18090 case 1:
18091 if (plainObject(arg0)) {
18092 // .panBy({ x: 0, y: 100 })
18093 dims = args[0];
18094 x = dims.x;
18095 y = dims.y;
18096
18097 if (number$1(x)) {
18098 pan.x += x;
18099 }
18100
18101 if (number$1(y)) {
18102 pan.y += y;
18103 }
18104
18105 this.emit('pan viewport');
18106 }
18107
18108 break;
18109
18110 case 2:
18111 // .panBy('x', 100)
18112 dim = arg0;
18113 val = arg1;
18114
18115 if ((dim === 'x' || dim === 'y') && number$1(val)) {
18116 pan[dim] += val;
18117 }
18118
18119 this.emit('pan viewport');
18120 break;
18121 // invalid
18122 }
18123
18124 this.notify('viewport');
18125 return this; // chaining
18126 },
18127 fit: function fit(elements, padding) {
18128 var viewportState = this.getFitViewport(elements, padding);
18129
18130 if (viewportState) {
18131 var _p = this._private;
18132 _p.zoom = viewportState.zoom;
18133 _p.pan = viewportState.pan;
18134 this.emit('pan zoom viewport');
18135 this.notify('viewport');
18136 }
18137
18138 return this; // chaining
18139 },
18140 getFitViewport: function getFitViewport(elements, padding) {
18141 if (number$1(elements) && padding === undefined) {
18142 // elements is optional
18143 padding = elements;
18144 elements = undefined;
18145 }
18146
18147 if (!this._private.panningEnabled || !this._private.zoomingEnabled) {
18148 return;
18149 }
18150
18151 var bb;
18152
18153 if (string(elements)) {
18154 var sel = elements;
18155 elements = this.$(sel);
18156 } else if (boundingBox(elements)) {
18157 // assume bb
18158 var bbe = elements;
18159 bb = {
18160 x1: bbe.x1,
18161 y1: bbe.y1,
18162 x2: bbe.x2,
18163 y2: bbe.y2
18164 };
18165 bb.w = bb.x2 - bb.x1;
18166 bb.h = bb.y2 - bb.y1;
18167 } else if (!elementOrCollection(elements)) {
18168 elements = this.mutableElements();
18169 }
18170
18171 if (elementOrCollection(elements) && elements.empty()) {
18172 return;
18173 } // can't fit to nothing
18174
18175
18176 bb = bb || elements.boundingBox();
18177 var w = this.width();
18178 var h = this.height();
18179 var zoom;
18180 padding = number$1(padding) ? padding : 0;
18181
18182 if (!isNaN(w) && !isNaN(h) && w > 0 && h > 0 && !isNaN(bb.w) && !isNaN(bb.h) && bb.w > 0 && bb.h > 0) {
18183 zoom = Math.min((w - 2 * padding) / bb.w, (h - 2 * padding) / bb.h); // crop zoom
18184
18185 zoom = zoom > this._private.maxZoom ? this._private.maxZoom : zoom;
18186 zoom = zoom < this._private.minZoom ? this._private.minZoom : zoom;
18187 var pan = {
18188 // now pan to middle
18189 x: (w - zoom * (bb.x1 + bb.x2)) / 2,
18190 y: (h - zoom * (bb.y1 + bb.y2)) / 2
18191 };
18192 return {
18193 zoom: zoom,
18194 pan: pan
18195 };
18196 }
18197
18198 return;
18199 },
18200 zoomRange: function zoomRange(min, max) {
18201 var _p = this._private;
18202
18203 if (max == null) {
18204 var opts = min;
18205 min = opts.min;
18206 max = opts.max;
18207 }
18208
18209 if (number$1(min) && number$1(max) && min <= max) {
18210 _p.minZoom = min;
18211 _p.maxZoom = max;
18212 } else if (number$1(min) && max === undefined && min <= _p.maxZoom) {
18213 _p.minZoom = min;
18214 } else if (number$1(max) && min === undefined && max >= _p.minZoom) {
18215 _p.maxZoom = max;
18216 }
18217
18218 return this;
18219 },
18220 minZoom: function minZoom(zoom) {
18221 if (zoom === undefined) {
18222 return this._private.minZoom;
18223 } else {
18224 return this.zoomRange({
18225 min: zoom
18226 });
18227 }
18228 },
18229 maxZoom: function maxZoom(zoom) {
18230 if (zoom === undefined) {
18231 return this._private.maxZoom;
18232 } else {
18233 return this.zoomRange({
18234 max: zoom
18235 });
18236 }
18237 },
18238 getZoomedViewport: function getZoomedViewport(params) {
18239 var _p = this._private;
18240 var currentPan = _p.pan;
18241 var currentZoom = _p.zoom;
18242 var pos; // in rendered px
18243
18244 var zoom;
18245 var bail = false;
18246
18247 if (!_p.zoomingEnabled) {
18248 // zooming disabled
18249 bail = true;
18250 }
18251
18252 if (number$1(params)) {
18253 // then set the zoom
18254 zoom = params;
18255 } else if (plainObject(params)) {
18256 // then zoom about a point
18257 zoom = params.level;
18258
18259 if (params.position != null) {
18260 pos = modelToRenderedPosition(params.position, currentZoom, currentPan);
18261 } else if (params.renderedPosition != null) {
18262 pos = params.renderedPosition;
18263 }
18264
18265 if (pos != null && !_p.panningEnabled) {
18266 // panning disabled
18267 bail = true;
18268 }
18269 } // crop zoom
18270
18271
18272 zoom = zoom > _p.maxZoom ? _p.maxZoom : zoom;
18273 zoom = zoom < _p.minZoom ? _p.minZoom : zoom; // can't zoom with invalid params
18274
18275 if (bail || !number$1(zoom) || zoom === currentZoom || pos != null && (!number$1(pos.x) || !number$1(pos.y))) {
18276 return null;
18277 }
18278
18279 if (pos != null) {
18280 // set zoom about position
18281 var pan1 = currentPan;
18282 var zoom1 = currentZoom;
18283 var zoom2 = zoom;
18284 var pan2 = {
18285 x: -zoom2 / zoom1 * (pos.x - pan1.x) + pos.x,
18286 y: -zoom2 / zoom1 * (pos.y - pan1.y) + pos.y
18287 };
18288 return {
18289 zoomed: true,
18290 panned: true,
18291 zoom: zoom2,
18292 pan: pan2
18293 };
18294 } else {
18295 // just set the zoom
18296 return {
18297 zoomed: true,
18298 panned: false,
18299 zoom: zoom,
18300 pan: currentPan
18301 };
18302 }
18303 },
18304 zoom: function zoom(params) {
18305 if (params === undefined) {
18306 // get
18307 return this._private.zoom;
18308 } else {
18309 // set
18310 var vp = this.getZoomedViewport(params);
18311 var _p = this._private;
18312
18313 if (vp == null || !vp.zoomed) {
18314 return this;
18315 }
18316
18317 _p.zoom = vp.zoom;
18318
18319 if (vp.panned) {
18320 _p.pan.x = vp.pan.x;
18321 _p.pan.y = vp.pan.y;
18322 }
18323
18324 this.emit('zoom' + (vp.panned ? ' pan' : '') + ' viewport');
18325 this.notify('viewport');
18326 return this; // chaining
18327 }
18328 },
18329 viewport: function viewport(opts) {
18330 var _p = this._private;
18331 var zoomDefd = true;
18332 var panDefd = true;
18333 var events = []; // to trigger
18334
18335 var zoomFailed = false;
18336 var panFailed = false;
18337
18338 if (!opts) {
18339 return this;
18340 }
18341
18342 if (!number$1(opts.zoom)) {
18343 zoomDefd = false;
18344 }
18345
18346 if (!plainObject(opts.pan)) {
18347 panDefd = false;
18348 }
18349
18350 if (!zoomDefd && !panDefd) {
18351 return this;
18352 }
18353
18354 if (zoomDefd) {
18355 var z = opts.zoom;
18356
18357 if (z < _p.minZoom || z > _p.maxZoom || !_p.zoomingEnabled) {
18358 zoomFailed = true;
18359 } else {
18360 _p.zoom = z;
18361 events.push('zoom');
18362 }
18363 }
18364
18365 if (panDefd && (!zoomFailed || !opts.cancelOnFailedZoom) && _p.panningEnabled) {
18366 var p = opts.pan;
18367
18368 if (number$1(p.x)) {
18369 _p.pan.x = p.x;
18370 panFailed = false;
18371 }
18372
18373 if (number$1(p.y)) {
18374 _p.pan.y = p.y;
18375 panFailed = false;
18376 }
18377
18378 if (!panFailed) {
18379 events.push('pan');
18380 }
18381 }
18382
18383 if (events.length > 0) {
18384 events.push('viewport');
18385 this.emit(events.join(' '));
18386 this.notify('viewport');
18387 }
18388
18389 return this; // chaining
18390 },
18391 center: function center(elements) {
18392 var pan = this.getCenterPan(elements);
18393
18394 if (pan) {
18395 this._private.pan = pan;
18396 this.emit('pan viewport');
18397 this.notify('viewport');
18398 }
18399
18400 return this; // chaining
18401 },
18402 getCenterPan: function getCenterPan(elements, zoom) {
18403 if (!this._private.panningEnabled) {
18404 return;
18405 }
18406
18407 if (string(elements)) {
18408 var selector = elements;
18409 elements = this.mutableElements().filter(selector);
18410 } else if (!elementOrCollection(elements)) {
18411 elements = this.mutableElements();
18412 }
18413
18414 if (elements.length === 0) {
18415 return;
18416 } // can't centre pan to nothing
18417
18418
18419 var bb = elements.boundingBox();
18420 var w = this.width();
18421 var h = this.height();
18422 zoom = zoom === undefined ? this._private.zoom : zoom;
18423 var pan = {
18424 // middle
18425 x: (w - zoom * (bb.x1 + bb.x2)) / 2,
18426 y: (h - zoom * (bb.y1 + bb.y2)) / 2
18427 };
18428 return pan;
18429 },
18430 reset: function reset() {
18431 if (!this._private.panningEnabled || !this._private.zoomingEnabled) {
18432 return this;
18433 }
18434
18435 this.viewport({
18436 pan: {
18437 x: 0,
18438 y: 0
18439 },
18440 zoom: 1
18441 });
18442 return this; // chaining
18443 },
18444 invalidateSize: function invalidateSize() {
18445 this._private.sizeCache = null;
18446 },
18447 size: function size() {
18448 var _p = this._private;
18449 var container = _p.container;
18450 return _p.sizeCache = _p.sizeCache || (container ? function () {
18451 var style = window$1.getComputedStyle(container);
18452
18453 var val = function val(name) {
18454 return parseFloat(style.getPropertyValue(name));
18455 };
18456
18457 return {
18458 width: container.clientWidth - val('padding-left') - val('padding-right'),
18459 height: container.clientHeight - val('padding-top') - val('padding-bottom')
18460 };
18461 }() : {
18462 // fallback if no container (not 0 b/c can be used for dividing etc)
18463 width: 1,
18464 height: 1
18465 });
18466 },
18467 width: function width() {
18468 return this.size().width;
18469 },
18470 height: function height() {
18471 return this.size().height;
18472 },
18473 extent: function extent() {
18474 var pan = this._private.pan;
18475 var zoom = this._private.zoom;
18476 var rb = this.renderedExtent();
18477 var b = {
18478 x1: (rb.x1 - pan.x) / zoom,
18479 x2: (rb.x2 - pan.x) / zoom,
18480 y1: (rb.y1 - pan.y) / zoom,
18481 y2: (rb.y2 - pan.y) / zoom
18482 };
18483 b.w = b.x2 - b.x1;
18484 b.h = b.y2 - b.y1;
18485 return b;
18486 },
18487 renderedExtent: function renderedExtent() {
18488 var width = this.width();
18489 var height = this.height();
18490 return {
18491 x1: 0,
18492 y1: 0,
18493 x2: width,
18494 y2: height,
18495 w: width,
18496 h: height
18497 };
18498 },
18499 multiClickDebounceTime: function multiClickDebounceTime(_int) {
18500 if (_int) this._private.multiClickDebounceTime = _int;else return this._private.multiClickDebounceTime;
18501 return this; // chaining
18502 }
18503}; // aliases
18504
18505corefn$1.centre = corefn$1.center; // backwards compatibility
18506
18507corefn$1.autolockNodes = corefn$1.autolock;
18508corefn$1.autoungrabifyNodes = corefn$1.autoungrabify;
18509
18510var fn = {
18511 data: define.data({
18512 field: 'data',
18513 bindingEvent: 'data',
18514 allowBinding: true,
18515 allowSetting: true,
18516 settingEvent: 'data',
18517 settingTriggersEvent: true,
18518 triggerFnName: 'trigger',
18519 allowGetting: true,
18520 updateStyle: true
18521 }),
18522 removeData: define.removeData({
18523 field: 'data',
18524 event: 'data',
18525 triggerFnName: 'trigger',
18526 triggerEvent: true,
18527 updateStyle: true
18528 }),
18529 scratch: define.data({
18530 field: 'scratch',
18531 bindingEvent: 'scratch',
18532 allowBinding: true,
18533 allowSetting: true,
18534 settingEvent: 'scratch',
18535 settingTriggersEvent: true,
18536 triggerFnName: 'trigger',
18537 allowGetting: true,
18538 updateStyle: true
18539 }),
18540 removeScratch: define.removeData({
18541 field: 'scratch',
18542 event: 'scratch',
18543 triggerFnName: 'trigger',
18544 triggerEvent: true,
18545 updateStyle: true
18546 })
18547}; // aliases
18548
18549fn.attr = fn.data;
18550fn.removeAttr = fn.removeData;
18551
18552var Core = function Core(opts) {
18553 var cy = this;
18554 opts = extend({}, opts);
18555 var container = opts.container; // allow for passing a wrapped jquery object
18556 // e.g. cytoscape({ container: $('#cy') })
18557
18558 if (container && !htmlElement(container) && htmlElement(container[0])) {
18559 container = container[0];
18560 }
18561
18562 var reg = container ? container._cyreg : null; // e.g. already registered some info (e.g. readies) via jquery
18563
18564 reg = reg || {};
18565
18566 if (reg && reg.cy) {
18567 reg.cy.destroy();
18568 reg = {}; // old instance => replace reg completely
18569 }
18570
18571 var readies = reg.readies = reg.readies || [];
18572
18573 if (container) {
18574 container._cyreg = reg;
18575 } // make sure container assoc'd reg points to this cy
18576
18577
18578 reg.cy = cy;
18579 var head = window$1 !== undefined && container !== undefined && !opts.headless;
18580 var options = opts;
18581 options.layout = extend({
18582 name: head ? 'grid' : 'null'
18583 }, options.layout);
18584 options.renderer = extend({
18585 name: head ? 'canvas' : 'null'
18586 }, options.renderer);
18587
18588 var defVal = function defVal(def, val, altVal) {
18589 if (val !== undefined) {
18590 return val;
18591 } else if (altVal !== undefined) {
18592 return altVal;
18593 } else {
18594 return def;
18595 }
18596 };
18597
18598 var _p = this._private = {
18599 container: container,
18600 // html dom ele container
18601 ready: false,
18602 // whether ready has been triggered
18603 options: options,
18604 // cached options
18605 elements: new Collection(this),
18606 // elements in the graph
18607 listeners: [],
18608 // list of listeners
18609 aniEles: new Collection(this),
18610 // elements being animated
18611 data: options.data || {},
18612 // data for the core
18613 scratch: {},
18614 // scratch object for core
18615 layout: null,
18616 renderer: null,
18617 destroyed: false,
18618 // whether destroy was called
18619 notificationsEnabled: true,
18620 // whether notifications are sent to the renderer
18621 minZoom: 1e-50,
18622 maxZoom: 1e50,
18623 zoomingEnabled: defVal(true, options.zoomingEnabled),
18624 userZoomingEnabled: defVal(true, options.userZoomingEnabled),
18625 panningEnabled: defVal(true, options.panningEnabled),
18626 userPanningEnabled: defVal(true, options.userPanningEnabled),
18627 boxSelectionEnabled: defVal(true, options.boxSelectionEnabled),
18628 autolock: defVal(false, options.autolock, options.autolockNodes),
18629 autoungrabify: defVal(false, options.autoungrabify, options.autoungrabifyNodes),
18630 autounselectify: defVal(false, options.autounselectify),
18631 styleEnabled: options.styleEnabled === undefined ? head : options.styleEnabled,
18632 zoom: number$1(options.zoom) ? options.zoom : 1,
18633 pan: {
18634 x: plainObject(options.pan) && number$1(options.pan.x) ? options.pan.x : 0,
18635 y: plainObject(options.pan) && number$1(options.pan.y) ? options.pan.y : 0
18636 },
18637 animation: {
18638 // object for currently-running animations
18639 current: [],
18640 queue: []
18641 },
18642 hasCompoundNodes: false,
18643 multiClickDebounceTime: defVal(250, options.multiClickDebounceTime)
18644 };
18645
18646 this.createEmitter(); // set selection type
18647
18648 this.selectionType(options.selectionType); // init zoom bounds
18649
18650 this.zoomRange({
18651 min: options.minZoom,
18652 max: options.maxZoom
18653 });
18654
18655 var loadExtData = function loadExtData(extData, next) {
18656 var anyIsPromise = extData.some(promise);
18657
18658 if (anyIsPromise) {
18659 return Promise$1.all(extData).then(next); // load all data asynchronously, then exec rest of init
18660 } else {
18661 next(extData); // exec synchronously for convenience
18662 }
18663 }; // start with the default stylesheet so we have something before loading an external stylesheet
18664
18665
18666 if (_p.styleEnabled) {
18667 cy.setStyle([]);
18668 } // create the renderer
18669
18670
18671 var rendererOptions = extend({}, options, options.renderer); // allow rendering hints in top level options
18672
18673 cy.initRenderer(rendererOptions);
18674
18675 var setElesAndLayout = function setElesAndLayout(elements, onload, ondone) {
18676 cy.notifications(false); // remove old elements
18677
18678 var oldEles = cy.mutableElements();
18679
18680 if (oldEles.length > 0) {
18681 oldEles.remove();
18682 }
18683
18684 if (elements != null) {
18685 if (plainObject(elements) || array(elements)) {
18686 cy.add(elements);
18687 }
18688 }
18689
18690 cy.one('layoutready', function (e) {
18691 cy.notifications(true);
18692 cy.emit(e); // we missed this event by turning notifications off, so pass it on
18693
18694 cy.one('load', onload);
18695 cy.emitAndNotify('load');
18696 }).one('layoutstop', function () {
18697 cy.one('done', ondone);
18698 cy.emit('done');
18699 });
18700 var layoutOpts = extend({}, cy._private.options.layout);
18701 layoutOpts.eles = cy.elements();
18702 cy.layout(layoutOpts).run();
18703 };
18704
18705 loadExtData([options.style, options.elements], function (thens) {
18706 var initStyle = thens[0];
18707 var initEles = thens[1]; // init style
18708
18709 if (_p.styleEnabled) {
18710 cy.style().append(initStyle);
18711 } // initial load
18712
18713
18714 setElesAndLayout(initEles, function () {
18715 // onready
18716 cy.startAnimationLoop();
18717 _p.ready = true; // if a ready callback is specified as an option, the bind it
18718
18719 if (fn$6(options.ready)) {
18720 cy.on('ready', options.ready);
18721 } // bind all the ready handlers registered before creating this instance
18722
18723
18724 for (var i = 0; i < readies.length; i++) {
18725 var fn = readies[i];
18726 cy.on('ready', fn);
18727 }
18728
18729 if (reg) {
18730 reg.readies = [];
18731 } // clear b/c we've bound them all and don't want to keep it around in case a new core uses the same div etc
18732
18733
18734 cy.emit('ready');
18735 }, options.done);
18736 });
18737};
18738
18739var corefn = Core.prototype; // short alias
18740
18741extend(corefn, {
18742 instanceString: function instanceString() {
18743 return 'core';
18744 },
18745 isReady: function isReady() {
18746 return this._private.ready;
18747 },
18748 destroyed: function destroyed() {
18749 return this._private.destroyed;
18750 },
18751 ready: function ready(fn) {
18752 if (this.isReady()) {
18753 this.emitter().emit('ready', [], fn); // just calls fn as though triggered via ready event
18754 } else {
18755 this.on('ready', fn);
18756 }
18757
18758 return this;
18759 },
18760 destroy: function destroy() {
18761 var cy = this;
18762 if (cy.destroyed()) return;
18763 cy.stopAnimationLoop();
18764 cy.destroyRenderer();
18765 this.emit('destroy');
18766 cy._private.destroyed = true;
18767 return cy;
18768 },
18769 hasElementWithId: function hasElementWithId(id) {
18770 return this._private.elements.hasElementWithId(id);
18771 },
18772 getElementById: function getElementById(id) {
18773 return this._private.elements.getElementById(id);
18774 },
18775 hasCompoundNodes: function hasCompoundNodes() {
18776 return this._private.hasCompoundNodes;
18777 },
18778 headless: function headless() {
18779 return this._private.renderer.isHeadless();
18780 },
18781 styleEnabled: function styleEnabled() {
18782 return this._private.styleEnabled;
18783 },
18784 addToPool: function addToPool(eles) {
18785 this._private.elements.merge(eles);
18786
18787 return this; // chaining
18788 },
18789 removeFromPool: function removeFromPool(eles) {
18790 this._private.elements.unmerge(eles);
18791
18792 return this;
18793 },
18794 container: function container() {
18795 return this._private.container || null;
18796 },
18797 mount: function mount(container) {
18798 if (container == null) {
18799 return;
18800 }
18801
18802 var cy = this;
18803 var _p = cy._private;
18804 var options = _p.options;
18805
18806 if (!htmlElement(container) && htmlElement(container[0])) {
18807 container = container[0];
18808 }
18809
18810 cy.stopAnimationLoop();
18811 cy.destroyRenderer();
18812 _p.container = container;
18813 _p.styleEnabled = true;
18814 cy.invalidateSize();
18815 cy.initRenderer(extend({}, options, options.renderer, {
18816 // allow custom renderer name to be re-used, otherwise use canvas
18817 name: options.renderer.name === 'null' ? 'canvas' : options.renderer.name
18818 }));
18819 cy.startAnimationLoop();
18820 cy.style(options.style);
18821 cy.emit('mount');
18822 return cy;
18823 },
18824 unmount: function unmount() {
18825 var cy = this;
18826 cy.stopAnimationLoop();
18827 cy.destroyRenderer();
18828 cy.initRenderer({
18829 name: 'null'
18830 });
18831 cy.emit('unmount');
18832 return cy;
18833 },
18834 options: function options() {
18835 return copy(this._private.options);
18836 },
18837 json: function json(obj) {
18838 var cy = this;
18839 var _p = cy._private;
18840 var eles = cy.mutableElements();
18841
18842 var getFreshRef = function getFreshRef(ele) {
18843 return cy.getElementById(ele.id());
18844 };
18845
18846 if (plainObject(obj)) {
18847 // set
18848 cy.startBatch();
18849
18850 if (obj.elements) {
18851 var idInJson = {};
18852
18853 var updateEles = function updateEles(jsons, gr) {
18854 var toAdd = [];
18855 var toMod = [];
18856
18857 for (var i = 0; i < jsons.length; i++) {
18858 var json = jsons[i];
18859
18860 if (!json.data.id) {
18861 warn('cy.json() cannot handle elements without an ID attribute');
18862 continue;
18863 }
18864
18865 var id = '' + json.data.id; // id must be string
18866
18867 var ele = cy.getElementById(id);
18868 idInJson[id] = true;
18869
18870 if (ele.length !== 0) {
18871 // existing element should be updated
18872 toMod.push({
18873 ele: ele,
18874 json: json
18875 });
18876 } else {
18877 // otherwise should be added
18878 if (gr) {
18879 json.group = gr;
18880 toAdd.push(json);
18881 } else {
18882 toAdd.push(json);
18883 }
18884 }
18885 }
18886
18887 cy.add(toAdd);
18888
18889 for (var _i = 0; _i < toMod.length; _i++) {
18890 var _toMod$_i = toMod[_i],
18891 _ele = _toMod$_i.ele,
18892 _json = _toMod$_i.json;
18893
18894 _ele.json(_json);
18895 }
18896 };
18897
18898 if (array(obj.elements)) {
18899 // elements: []
18900 updateEles(obj.elements);
18901 } else {
18902 // elements: { nodes: [], edges: [] }
18903 var grs = ['nodes', 'edges'];
18904
18905 for (var i = 0; i < grs.length; i++) {
18906 var gr = grs[i];
18907 var elements = obj.elements[gr];
18908
18909 if (array(elements)) {
18910 updateEles(elements, gr);
18911 }
18912 }
18913 }
18914
18915 var parentsToRemove = cy.collection();
18916 eles.filter(function (ele) {
18917 return !idInJson[ele.id()];
18918 }).forEach(function (ele) {
18919 if (ele.isParent()) {
18920 parentsToRemove.merge(ele);
18921 } else {
18922 ele.remove();
18923 }
18924 }); // so that children are not removed w/parent
18925
18926 parentsToRemove.forEach(function (ele) {
18927 return ele.children().move({
18928 parent: null
18929 });
18930 }); // intermediate parents may be moved by prior line, so make sure we remove by fresh refs
18931
18932 parentsToRemove.forEach(function (ele) {
18933 return getFreshRef(ele).remove();
18934 });
18935 }
18936
18937 if (obj.style) {
18938 cy.style(obj.style);
18939 }
18940
18941 if (obj.zoom != null && obj.zoom !== _p.zoom) {
18942 cy.zoom(obj.zoom);
18943 }
18944
18945 if (obj.pan) {
18946 if (obj.pan.x !== _p.pan.x || obj.pan.y !== _p.pan.y) {
18947 cy.pan(obj.pan);
18948 }
18949 }
18950
18951 if (obj.data) {
18952 cy.data(obj.data);
18953 }
18954
18955 var fields = ['minZoom', 'maxZoom', 'zoomingEnabled', 'userZoomingEnabled', 'panningEnabled', 'userPanningEnabled', 'boxSelectionEnabled', 'autolock', 'autoungrabify', 'autounselectify', 'multiClickDebounceTime'];
18956
18957 for (var _i2 = 0; _i2 < fields.length; _i2++) {
18958 var f = fields[_i2];
18959
18960 if (obj[f] != null) {
18961 cy[f](obj[f]);
18962 }
18963 }
18964
18965 cy.endBatch();
18966 return this; // chaining
18967 } else {
18968 // get
18969 var flat = !!obj;
18970 var json = {};
18971
18972 if (flat) {
18973 json.elements = this.elements().map(function (ele) {
18974 return ele.json();
18975 });
18976 } else {
18977 json.elements = {};
18978 eles.forEach(function (ele) {
18979 var group = ele.group();
18980
18981 if (!json.elements[group]) {
18982 json.elements[group] = [];
18983 }
18984
18985 json.elements[group].push(ele.json());
18986 });
18987 }
18988
18989 if (this._private.styleEnabled) {
18990 json.style = cy.style().json();
18991 }
18992
18993 json.data = copy(cy.data());
18994 var options = _p.options;
18995 json.zoomingEnabled = _p.zoomingEnabled;
18996 json.userZoomingEnabled = _p.userZoomingEnabled;
18997 json.zoom = _p.zoom;
18998 json.minZoom = _p.minZoom;
18999 json.maxZoom = _p.maxZoom;
19000 json.panningEnabled = _p.panningEnabled;
19001 json.userPanningEnabled = _p.userPanningEnabled;
19002 json.pan = copy(_p.pan);
19003 json.boxSelectionEnabled = _p.boxSelectionEnabled;
19004 json.renderer = copy(options.renderer);
19005 json.hideEdgesOnViewport = options.hideEdgesOnViewport;
19006 json.textureOnViewport = options.textureOnViewport;
19007 json.wheelSensitivity = options.wheelSensitivity;
19008 json.motionBlur = options.motionBlur;
19009 json.multiClickDebounceTime = options.multiClickDebounceTime;
19010 return json;
19011 }
19012 }
19013});
19014corefn.$id = corefn.getElementById;
19015[corefn$9, corefn$8, elesfn, corefn$7, corefn$6, corefn$5, corefn$4, corefn$3, corefn$2, corefn$1, fn].forEach(function (props) {
19016 extend(corefn, props);
19017});
19018
19019/* eslint-disable no-unused-vars */
19020
19021var defaults$7 = {
19022 fit: true,
19023 // whether to fit the viewport to the graph
19024 directed: false,
19025 // whether the tree is directed downwards (or edges can point in any direction if false)
19026 padding: 30,
19027 // padding on fit
19028 circle: false,
19029 // put depths in concentric circles if true, put depths top down if false
19030 grid: false,
19031 // whether to create an even grid into which the DAG is placed (circle:false only)
19032 spacingFactor: 1.75,
19033 // positive spacing factor, larger => more space between nodes (N.B. n/a if causes overlap)
19034 boundingBox: undefined,
19035 // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
19036 avoidOverlap: true,
19037 // prevents node overlap, may overflow boundingBox if not enough space
19038 nodeDimensionsIncludeLabels: false,
19039 // Excludes the label when calculating node bounding boxes for the layout algorithm
19040 roots: undefined,
19041 // the roots of the trees
19042 maximal: false,
19043 // whether to shift nodes down their natural BFS depths in order to avoid upwards edges (DAGS only)
19044 depthSort: undefined,
19045 // a sorting function to order nodes at equal depth. e.g. function(a, b){ return a.data('weight') - b.data('weight') }
19046 animate: false,
19047 // whether to transition the node positions
19048 animationDuration: 500,
19049 // duration of animation in ms if enabled
19050 animationEasing: undefined,
19051 // easing of animation if enabled,
19052 animateFilter: function animateFilter(node, i) {
19053 return true;
19054 },
19055 // a function that determines whether the node should be animated. All nodes animated by default on animate enabled. Non-animated nodes are positioned immediately when the layout starts
19056 ready: undefined,
19057 // callback on layoutready
19058 stop: undefined,
19059 // callback on layoutstop
19060 transform: function transform(node, position) {
19061 return position;
19062 } // transform a given node position. Useful for changing flow direction in discrete layouts
19063
19064};
19065/* eslint-enable */
19066
19067var getInfo = function getInfo(ele) {
19068 return ele.scratch('breadthfirst');
19069};
19070
19071var setInfo = function setInfo(ele, obj) {
19072 return ele.scratch('breadthfirst', obj);
19073};
19074
19075function BreadthFirstLayout(options) {
19076 this.options = extend({}, defaults$7, options);
19077}
19078
19079BreadthFirstLayout.prototype.run = function () {
19080 var params = this.options;
19081 var options = params;
19082 var cy = params.cy;
19083 var eles = options.eles;
19084 var nodes = eles.nodes().filter(function (n) {
19085 return !n.isParent();
19086 });
19087 var graph = eles;
19088 var directed = options.directed;
19089 var maximal = options.maximal || options.maximalAdjustments > 0; // maximalAdjustments for compat. w/ old code
19090
19091 var bb = makeBoundingBox(options.boundingBox ? options.boundingBox : {
19092 x1: 0,
19093 y1: 0,
19094 w: cy.width(),
19095 h: cy.height()
19096 });
19097 var roots;
19098
19099 if (elementOrCollection(options.roots)) {
19100 roots = options.roots;
19101 } else if (array(options.roots)) {
19102 var rootsArray = [];
19103
19104 for (var i = 0; i < options.roots.length; i++) {
19105 var id = options.roots[i];
19106 var ele = cy.getElementById(id);
19107 rootsArray.push(ele);
19108 }
19109
19110 roots = cy.collection(rootsArray);
19111 } else if (string(options.roots)) {
19112 roots = cy.$(options.roots);
19113 } else {
19114 if (directed) {
19115 roots = nodes.roots();
19116 } else {
19117 var components = eles.components();
19118 roots = cy.collection();
19119
19120 var _loop = function _loop(_i) {
19121 var comp = components[_i];
19122 var maxDegree = comp.maxDegree(false);
19123 var compRoots = comp.filter(function (ele) {
19124 return ele.degree(false) === maxDegree;
19125 });
19126 roots = roots.add(compRoots);
19127 };
19128
19129 for (var _i = 0; _i < components.length; _i++) {
19130 _loop(_i);
19131 }
19132 }
19133 }
19134
19135 var depths = [];
19136 var foundByBfs = {};
19137
19138 var addToDepth = function addToDepth(ele, d) {
19139 if (depths[d] == null) {
19140 depths[d] = [];
19141 }
19142
19143 var i = depths[d].length;
19144 depths[d].push(ele);
19145 setInfo(ele, {
19146 index: i,
19147 depth: d
19148 });
19149 };
19150
19151 var changeDepth = function changeDepth(ele, newDepth) {
19152 var _getInfo = getInfo(ele),
19153 depth = _getInfo.depth,
19154 index = _getInfo.index;
19155
19156 depths[depth][index] = null;
19157 addToDepth(ele, newDepth);
19158 }; // find the depths of the nodes
19159
19160
19161 graph.bfs({
19162 roots: roots,
19163 directed: options.directed,
19164 visit: function visit(node, edge, pNode, i, depth) {
19165 var ele = node[0];
19166 var id = ele.id();
19167 addToDepth(ele, depth);
19168 foundByBfs[id] = true;
19169 }
19170 }); // check for nodes not found by bfs
19171
19172 var orphanNodes = [];
19173
19174 for (var _i2 = 0; _i2 < nodes.length; _i2++) {
19175 var _ele = nodes[_i2];
19176
19177 if (foundByBfs[_ele.id()]) {
19178 continue;
19179 } else {
19180 orphanNodes.push(_ele);
19181 }
19182 } // assign the nodes a depth and index
19183
19184
19185 var assignDepthsAt = function assignDepthsAt(i) {
19186 var eles = depths[i];
19187
19188 for (var j = 0; j < eles.length; j++) {
19189 var _ele2 = eles[j];
19190
19191 if (_ele2 == null) {
19192 eles.splice(j, 1);
19193 j--;
19194 continue;
19195 }
19196
19197 setInfo(_ele2, {
19198 depth: i,
19199 index: j
19200 });
19201 }
19202 };
19203
19204 var assignDepths = function assignDepths() {
19205 for (var _i3 = 0; _i3 < depths.length; _i3++) {
19206 assignDepthsAt(_i3);
19207 }
19208 };
19209
19210 var adjustMaximally = function adjustMaximally(ele, shifted) {
19211 var eInfo = getInfo(ele);
19212 var incomers = ele.incomers().filter(function (el) {
19213 return el.isNode() && eles.has(el);
19214 });
19215 var maxDepth = -1;
19216 var id = ele.id();
19217
19218 for (var k = 0; k < incomers.length; k++) {
19219 var incmr = incomers[k];
19220 var iInfo = getInfo(incmr);
19221 maxDepth = Math.max(maxDepth, iInfo.depth);
19222 }
19223
19224 if (eInfo.depth <= maxDepth) {
19225 if (shifted[id]) {
19226 return null;
19227 }
19228
19229 changeDepth(ele, maxDepth + 1);
19230 shifted[id] = true;
19231 return true;
19232 }
19233
19234 return false;
19235 }; // for the directed case, try to make the edges all go down (i.e. depth i => depth i + 1)
19236
19237
19238 if (directed && maximal) {
19239 var Q = [];
19240 var shifted = {};
19241
19242 var enqueue = function enqueue(n) {
19243 return Q.push(n);
19244 };
19245
19246 var dequeue = function dequeue() {
19247 return Q.shift();
19248 };
19249
19250 nodes.forEach(function (n) {
19251 return Q.push(n);
19252 });
19253
19254 while (Q.length > 0) {
19255 var _ele3 = dequeue();
19256
19257 var didShift = adjustMaximally(_ele3, shifted);
19258
19259 if (didShift) {
19260 _ele3.outgoers().filter(function (el) {
19261 return el.isNode() && eles.has(el);
19262 }).forEach(enqueue);
19263 } else if (didShift === null) {
19264 warn('Detected double maximal shift for node `' + _ele3.id() + '`. Bailing maximal adjustment due to cycle. Use `options.maximal: true` only on DAGs.');
19265 break; // exit on failure
19266 }
19267 }
19268 }
19269
19270 assignDepths(); // clear holes
19271 // find min distance we need to leave between nodes
19272
19273 var minDistance = 0;
19274
19275 if (options.avoidOverlap) {
19276 for (var _i4 = 0; _i4 < nodes.length; _i4++) {
19277 var n = nodes[_i4];
19278 var nbb = n.layoutDimensions(options);
19279 var w = nbb.w;
19280 var h = nbb.h;
19281 minDistance = Math.max(minDistance, w, h);
19282 }
19283 } // get the weighted percent for an element based on its connectivity to other levels
19284
19285
19286 var cachedWeightedPercent = {};
19287
19288 var getWeightedPercent = function getWeightedPercent(ele) {
19289 if (cachedWeightedPercent[ele.id()]) {
19290 return cachedWeightedPercent[ele.id()];
19291 }
19292
19293 var eleDepth = getInfo(ele).depth;
19294 var neighbors = ele.neighborhood();
19295 var percent = 0;
19296 var samples = 0;
19297
19298 for (var _i5 = 0; _i5 < neighbors.length; _i5++) {
19299 var neighbor = neighbors[_i5];
19300
19301 if (neighbor.isEdge() || neighbor.isParent() || !nodes.has(neighbor)) {
19302 continue;
19303 }
19304
19305 var bf = getInfo(neighbor);
19306
19307 if (bf == null) {
19308 continue;
19309 }
19310
19311 var index = bf.index;
19312 var depth = bf.depth; // unassigned neighbours shouldn't affect the ordering
19313
19314 if (index == null || depth == null) {
19315 continue;
19316 }
19317
19318 var nDepth = depths[depth].length;
19319
19320 if (depth < eleDepth) {
19321 // only get influenced by elements above
19322 percent += index / nDepth;
19323 samples++;
19324 }
19325 }
19326
19327 samples = Math.max(1, samples);
19328 percent = percent / samples;
19329
19330 if (samples === 0) {
19331 // put lone nodes at the start
19332 percent = 0;
19333 }
19334
19335 cachedWeightedPercent[ele.id()] = percent;
19336 return percent;
19337 }; // rearrange the indices in each depth level based on connectivity
19338
19339
19340 var sortFn = function sortFn(a, b) {
19341 var apct = getWeightedPercent(a);
19342 var bpct = getWeightedPercent(b);
19343 var diff = apct - bpct;
19344
19345 if (diff === 0) {
19346 return ascending(a.id(), b.id()); // make sure sort doesn't have don't-care comparisons
19347 } else {
19348 return diff;
19349 }
19350 };
19351
19352 if (options.depthSort !== undefined) {
19353 sortFn = options.depthSort;
19354 } // sort each level to make connected nodes closer
19355
19356
19357 for (var _i6 = 0; _i6 < depths.length; _i6++) {
19358 depths[_i6].sort(sortFn);
19359
19360 assignDepthsAt(_i6);
19361 } // assign orphan nodes to a new top-level depth
19362
19363
19364 var orphanDepth = [];
19365
19366 for (var _i7 = 0; _i7 < orphanNodes.length; _i7++) {
19367 orphanDepth.push(orphanNodes[_i7]);
19368 }
19369
19370 depths.unshift(orphanDepth);
19371 assignDepths();
19372 var biggestDepthSize = 0;
19373
19374 for (var _i8 = 0; _i8 < depths.length; _i8++) {
19375 biggestDepthSize = Math.max(depths[_i8].length, biggestDepthSize);
19376 }
19377
19378 var center = {
19379 x: bb.x1 + bb.w / 2,
19380 y: bb.x1 + bb.h / 2
19381 };
19382 var maxDepthSize = depths.reduce(function (max, eles) {
19383 return Math.max(max, eles.length);
19384 }, 0);
19385
19386 var getPosition = function getPosition(ele) {
19387 var _getInfo2 = getInfo(ele),
19388 depth = _getInfo2.depth,
19389 index = _getInfo2.index;
19390
19391 var depthSize = depths[depth].length;
19392 var distanceX = Math.max(bb.w / ((options.grid ? maxDepthSize : depthSize) + 1), minDistance);
19393 var distanceY = Math.max(bb.h / (depths.length + 1), minDistance);
19394 var radiusStepSize = Math.min(bb.w / 2 / depths.length, bb.h / 2 / depths.length);
19395 radiusStepSize = Math.max(radiusStepSize, minDistance);
19396
19397 if (!options.circle) {
19398 var epos = {
19399 x: center.x + (index + 1 - (depthSize + 1) / 2) * distanceX,
19400 y: (depth + 1) * distanceY
19401 };
19402 return epos;
19403 } else {
19404 var radius = radiusStepSize * depth + radiusStepSize - (depths.length > 0 && depths[0].length <= 3 ? radiusStepSize / 2 : 0);
19405 var theta = 2 * Math.PI / depths[depth].length * index;
19406
19407 if (depth === 0 && depths[0].length === 1) {
19408 radius = 1;
19409 }
19410
19411 return {
19412 x: center.x + radius * Math.cos(theta),
19413 y: center.y + radius * Math.sin(theta)
19414 };
19415 }
19416 };
19417
19418 eles.nodes().layoutPositions(this, options, getPosition);
19419 return this; // chaining
19420};
19421
19422var defaults$6 = {
19423 fit: true,
19424 // whether to fit the viewport to the graph
19425 padding: 30,
19426 // the padding on fit
19427 boundingBox: undefined,
19428 // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
19429 avoidOverlap: true,
19430 // prevents node overlap, may overflow boundingBox and radius if not enough space
19431 nodeDimensionsIncludeLabels: false,
19432 // Excludes the label when calculating node bounding boxes for the layout algorithm
19433 spacingFactor: undefined,
19434 // Applies a multiplicative factor (>0) to expand or compress the overall area that the nodes take up
19435 radius: undefined,
19436 // the radius of the circle
19437 startAngle: 3 / 2 * Math.PI,
19438 // where nodes start in radians
19439 sweep: undefined,
19440 // how many radians should be between the first and last node (defaults to full circle)
19441 clockwise: true,
19442 // whether the layout should go clockwise (true) or counterclockwise/anticlockwise (false)
19443 sort: undefined,
19444 // a sorting function to order the nodes; e.g. function(a, b){ return a.data('weight') - b.data('weight') }
19445 animate: false,
19446 // whether to transition the node positions
19447 animationDuration: 500,
19448 // duration of animation in ms if enabled
19449 animationEasing: undefined,
19450 // easing of animation if enabled
19451 animateFilter: function animateFilter(node, i) {
19452 return true;
19453 },
19454 // a function that determines whether the node should be animated. All nodes animated by default on animate enabled. Non-animated nodes are positioned immediately when the layout starts
19455 ready: undefined,
19456 // callback on layoutready
19457 stop: undefined,
19458 // callback on layoutstop
19459 transform: function transform(node, position) {
19460 return position;
19461 } // transform a given node position. Useful for changing flow direction in discrete layouts
19462
19463};
19464
19465function CircleLayout(options) {
19466 this.options = extend({}, defaults$6, options);
19467}
19468
19469CircleLayout.prototype.run = function () {
19470 var params = this.options;
19471 var options = params;
19472 var cy = params.cy;
19473 var eles = options.eles;
19474 var clockwise = options.counterclockwise !== undefined ? !options.counterclockwise : options.clockwise;
19475 var nodes = eles.nodes().not(':parent');
19476
19477 if (options.sort) {
19478 nodes = nodes.sort(options.sort);
19479 }
19480
19481 var bb = makeBoundingBox(options.boundingBox ? options.boundingBox : {
19482 x1: 0,
19483 y1: 0,
19484 w: cy.width(),
19485 h: cy.height()
19486 });
19487 var center = {
19488 x: bb.x1 + bb.w / 2,
19489 y: bb.y1 + bb.h / 2
19490 };
19491 var sweep = options.sweep === undefined ? 2 * Math.PI - 2 * Math.PI / nodes.length : options.sweep;
19492 var dTheta = sweep / Math.max(1, nodes.length - 1);
19493 var r;
19494 var minDistance = 0;
19495
19496 for (var i = 0; i < nodes.length; i++) {
19497 var n = nodes[i];
19498 var nbb = n.layoutDimensions(options);
19499 var w = nbb.w;
19500 var h = nbb.h;
19501 minDistance = Math.max(minDistance, w, h);
19502 }
19503
19504 if (number$1(options.radius)) {
19505 r = options.radius;
19506 } else if (nodes.length <= 1) {
19507 r = 0;
19508 } else {
19509 r = Math.min(bb.h, bb.w) / 2 - minDistance;
19510 } // calculate the radius
19511
19512
19513 if (nodes.length > 1 && options.avoidOverlap) {
19514 // but only if more than one node (can't overlap)
19515 minDistance *= 1.75; // just to have some nice spacing
19516
19517 var dcos = Math.cos(dTheta) - Math.cos(0);
19518 var dsin = Math.sin(dTheta) - Math.sin(0);
19519 var rMin = Math.sqrt(minDistance * minDistance / (dcos * dcos + dsin * dsin)); // s.t. no nodes overlapping
19520
19521 r = Math.max(rMin, r);
19522 }
19523
19524 var getPos = function getPos(ele, i) {
19525 var theta = options.startAngle + i * dTheta * (clockwise ? 1 : -1);
19526 var rx = r * Math.cos(theta);
19527 var ry = r * Math.sin(theta);
19528 var pos = {
19529 x: center.x + rx,
19530 y: center.y + ry
19531 };
19532 return pos;
19533 };
19534
19535 eles.nodes().layoutPositions(this, options, getPos);
19536 return this; // chaining
19537};
19538
19539var defaults$5 = {
19540 fit: true,
19541 // whether to fit the viewport to the graph
19542 padding: 30,
19543 // the padding on fit
19544 startAngle: 3 / 2 * Math.PI,
19545 // where nodes start in radians
19546 sweep: undefined,
19547 // how many radians should be between the first and last node (defaults to full circle)
19548 clockwise: true,
19549 // whether the layout should go clockwise (true) or counterclockwise/anticlockwise (false)
19550 equidistant: false,
19551 // whether levels have an equal radial distance betwen them, may cause bounding box overflow
19552 minNodeSpacing: 10,
19553 // min spacing between outside of nodes (used for radius adjustment)
19554 boundingBox: undefined,
19555 // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
19556 avoidOverlap: true,
19557 // prevents node overlap, may overflow boundingBox if not enough space
19558 nodeDimensionsIncludeLabels: false,
19559 // Excludes the label when calculating node bounding boxes for the layout algorithm
19560 height: undefined,
19561 // height of layout area (overrides container height)
19562 width: undefined,
19563 // width of layout area (overrides container width)
19564 spacingFactor: undefined,
19565 // Applies a multiplicative factor (>0) to expand or compress the overall area that the nodes take up
19566 concentric: function concentric(node) {
19567 // returns numeric value for each node, placing higher nodes in levels towards the centre
19568 return node.degree();
19569 },
19570 levelWidth: function levelWidth(nodes) {
19571 // the variation of concentric values in each level
19572 return nodes.maxDegree() / 4;
19573 },
19574 animate: false,
19575 // whether to transition the node positions
19576 animationDuration: 500,
19577 // duration of animation in ms if enabled
19578 animationEasing: undefined,
19579 // easing of animation if enabled
19580 animateFilter: function animateFilter(node, i) {
19581 return true;
19582 },
19583 // a function that determines whether the node should be animated. All nodes animated by default on animate enabled. Non-animated nodes are positioned immediately when the layout starts
19584 ready: undefined,
19585 // callback on layoutready
19586 stop: undefined,
19587 // callback on layoutstop
19588 transform: function transform(node, position) {
19589 return position;
19590 } // transform a given node position. Useful for changing flow direction in discrete layouts
19591
19592};
19593
19594function ConcentricLayout(options) {
19595 this.options = extend({}, defaults$5, options);
19596}
19597
19598ConcentricLayout.prototype.run = function () {
19599 var params = this.options;
19600 var options = params;
19601 var clockwise = options.counterclockwise !== undefined ? !options.counterclockwise : options.clockwise;
19602 var cy = params.cy;
19603 var eles = options.eles;
19604 var nodes = eles.nodes().not(':parent');
19605 var bb = makeBoundingBox(options.boundingBox ? options.boundingBox : {
19606 x1: 0,
19607 y1: 0,
19608 w: cy.width(),
19609 h: cy.height()
19610 });
19611 var center = {
19612 x: bb.x1 + bb.w / 2,
19613 y: bb.y1 + bb.h / 2
19614 };
19615 var nodeValues = []; // { node, value }
19616
19617 var maxNodeSize = 0;
19618
19619 for (var i = 0; i < nodes.length; i++) {
19620 var node = nodes[i];
19621 var value = void 0; // calculate the node value
19622
19623 value = options.concentric(node);
19624 nodeValues.push({
19625 value: value,
19626 node: node
19627 }); // for style mapping
19628
19629 node._private.scratch.concentric = value;
19630 } // in case we used the `concentric` in style
19631
19632
19633 nodes.updateStyle(); // calculate max size now based on potentially updated mappers
19634
19635 for (var _i = 0; _i < nodes.length; _i++) {
19636 var _node = nodes[_i];
19637
19638 var nbb = _node.layoutDimensions(options);
19639
19640 maxNodeSize = Math.max(maxNodeSize, nbb.w, nbb.h);
19641 } // sort node values in descreasing order
19642
19643
19644 nodeValues.sort(function (a, b) {
19645 return b.value - a.value;
19646 });
19647 var levelWidth = options.levelWidth(nodes); // put the values into levels
19648
19649 var levels = [[]];
19650 var currentLevel = levels[0];
19651
19652 for (var _i2 = 0; _i2 < nodeValues.length; _i2++) {
19653 var val = nodeValues[_i2];
19654
19655 if (currentLevel.length > 0) {
19656 var diff = Math.abs(currentLevel[0].value - val.value);
19657
19658 if (diff >= levelWidth) {
19659 currentLevel = [];
19660 levels.push(currentLevel);
19661 }
19662 }
19663
19664 currentLevel.push(val);
19665 } // create positions from levels
19666
19667
19668 var minDist = maxNodeSize + options.minNodeSpacing; // min dist between nodes
19669
19670 if (!options.avoidOverlap) {
19671 // then strictly constrain to bb
19672 var firstLvlHasMulti = levels.length > 0 && levels[0].length > 1;
19673 var maxR = Math.min(bb.w, bb.h) / 2 - minDist;
19674 var rStep = maxR / (levels.length + firstLvlHasMulti ? 1 : 0);
19675 minDist = Math.min(minDist, rStep);
19676 } // find the metrics for each level
19677
19678
19679 var r = 0;
19680
19681 for (var _i3 = 0; _i3 < levels.length; _i3++) {
19682 var level = levels[_i3];
19683 var sweep = options.sweep === undefined ? 2 * Math.PI - 2 * Math.PI / level.length : options.sweep;
19684 var dTheta = level.dTheta = sweep / Math.max(1, level.length - 1); // calculate the radius
19685
19686 if (level.length > 1 && options.avoidOverlap) {
19687 // but only if more than one node (can't overlap)
19688 var dcos = Math.cos(dTheta) - Math.cos(0);
19689 var dsin = Math.sin(dTheta) - Math.sin(0);
19690 var rMin = Math.sqrt(minDist * minDist / (dcos * dcos + dsin * dsin)); // s.t. no nodes overlapping
19691
19692 r = Math.max(rMin, r);
19693 }
19694
19695 level.r = r;
19696 r += minDist;
19697 }
19698
19699 if (options.equidistant) {
19700 var rDeltaMax = 0;
19701 var _r = 0;
19702
19703 for (var _i4 = 0; _i4 < levels.length; _i4++) {
19704 var _level = levels[_i4];
19705 var rDelta = _level.r - _r;
19706 rDeltaMax = Math.max(rDeltaMax, rDelta);
19707 }
19708
19709 _r = 0;
19710
19711 for (var _i5 = 0; _i5 < levels.length; _i5++) {
19712 var _level2 = levels[_i5];
19713
19714 if (_i5 === 0) {
19715 _r = _level2.r;
19716 }
19717
19718 _level2.r = _r;
19719 _r += rDeltaMax;
19720 }
19721 } // calculate the node positions
19722
19723
19724 var pos = {}; // id => position
19725
19726 for (var _i6 = 0; _i6 < levels.length; _i6++) {
19727 var _level3 = levels[_i6];
19728 var _dTheta = _level3.dTheta;
19729 var _r2 = _level3.r;
19730
19731 for (var j = 0; j < _level3.length; j++) {
19732 var _val = _level3[j];
19733 var theta = options.startAngle + (clockwise ? 1 : -1) * _dTheta * j;
19734 var p = {
19735 x: center.x + _r2 * Math.cos(theta),
19736 y: center.y + _r2 * Math.sin(theta)
19737 };
19738 pos[_val.node.id()] = p;
19739 }
19740 } // position the nodes
19741
19742
19743 eles.nodes().layoutPositions(this, options, function (ele) {
19744 var id = ele.id();
19745 return pos[id];
19746 });
19747 return this; // chaining
19748};
19749
19750/*
19751The CoSE layout was written by Gerardo Huck.
19752https://www.linkedin.com/in/gerardohuck/
19753
19754Based on the following article:
19755http://dl.acm.org/citation.cfm?id=1498047
19756
19757Modifications tracked on Github.
19758*/
19759var DEBUG;
19760/**
19761 * @brief : default layout options
19762 */
19763
19764var defaults$4 = {
19765 // Called on `layoutready`
19766 ready: function ready() {},
19767 // Called on `layoutstop`
19768 stop: function stop() {},
19769 // Whether to animate while running the layout
19770 // true : Animate continuously as the layout is running
19771 // false : Just show the end result
19772 // 'end' : Animate with the end result, from the initial positions to the end positions
19773 animate: true,
19774 // Easing of the animation for animate:'end'
19775 animationEasing: undefined,
19776 // The duration of the animation for animate:'end'
19777 animationDuration: undefined,
19778 // A function that determines whether the node should be animated
19779 // All nodes animated by default on animate enabled
19780 // Non-animated nodes are positioned immediately when the layout starts
19781 animateFilter: function animateFilter(node, i) {
19782 return true;
19783 },
19784 // The layout animates only after this many milliseconds for animate:true
19785 // (prevents flashing on fast runs)
19786 animationThreshold: 250,
19787 // Number of iterations between consecutive screen positions update
19788 refresh: 20,
19789 // Whether to fit the network view after when done
19790 fit: true,
19791 // Padding on fit
19792 padding: 30,
19793 // Constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
19794 boundingBox: undefined,
19795 // Excludes the label when calculating node bounding boxes for the layout algorithm
19796 nodeDimensionsIncludeLabels: false,
19797 // Randomize the initial positions of the nodes (true) or use existing positions (false)
19798 randomize: false,
19799 // Extra spacing between components in non-compound graphs
19800 componentSpacing: 40,
19801 // Node repulsion (non overlapping) multiplier
19802 nodeRepulsion: function nodeRepulsion(node) {
19803 return 2048;
19804 },
19805 // Node repulsion (overlapping) multiplier
19806 nodeOverlap: 4,
19807 // Ideal edge (non nested) length
19808 idealEdgeLength: function idealEdgeLength(edge) {
19809 return 32;
19810 },
19811 // Divisor to compute edge forces
19812 edgeElasticity: function edgeElasticity(edge) {
19813 return 32;
19814 },
19815 // Nesting factor (multiplier) to compute ideal edge length for nested edges
19816 nestingFactor: 1.2,
19817 // Gravity force (constant)
19818 gravity: 1,
19819 // Maximum number of iterations to perform
19820 numIter: 1000,
19821 // Initial temperature (maximum node displacement)
19822 initialTemp: 1000,
19823 // Cooling factor (how the temperature is reduced between consecutive iterations
19824 coolingFactor: 0.99,
19825 // Lower temperature threshold (below this point the layout will end)
19826 minTemp: 1.0
19827};
19828/**
19829 * @brief : constructor
19830 * @arg options : object containing layout options
19831 */
19832
19833function CoseLayout(options) {
19834 this.options = extend({}, defaults$4, options);
19835 this.options.layout = this;
19836}
19837/**
19838 * @brief : runs the layout
19839 */
19840
19841
19842CoseLayout.prototype.run = function () {
19843 var options = this.options;
19844 var cy = options.cy;
19845 var layout = this;
19846 layout.stopped = false;
19847
19848 if (options.animate === true || options.animate === false) {
19849 layout.emit({
19850 type: 'layoutstart',
19851 layout: layout
19852 });
19853 } // Set DEBUG - Global variable
19854
19855
19856 if (true === options.debug) {
19857 DEBUG = true;
19858 } else {
19859 DEBUG = false;
19860 } // Initialize layout info
19861
19862
19863 var layoutInfo = createLayoutInfo(cy, layout, options); // Show LayoutInfo contents if debugging
19864
19865 if (DEBUG) {
19866 printLayoutInfo(layoutInfo);
19867 } // If required, randomize node positions
19868
19869
19870 if (options.randomize) {
19871 randomizePositions(layoutInfo);
19872 }
19873
19874 var startTime = performanceNow();
19875
19876 var refresh = function refresh() {
19877 refreshPositions(layoutInfo, cy, options); // Fit the graph if necessary
19878
19879 if (true === options.fit) {
19880 cy.fit(options.padding);
19881 }
19882 };
19883
19884 var mainLoop = function mainLoop(i) {
19885 if (layout.stopped || i >= options.numIter) {
19886 // logDebug("Layout manually stopped. Stopping computation in step " + i);
19887 return false;
19888 } // Do one step in the phisical simulation
19889
19890
19891 step(layoutInfo, options); // Update temperature
19892
19893 layoutInfo.temperature = layoutInfo.temperature * options.coolingFactor; // logDebug("New temperature: " + layoutInfo.temperature);
19894
19895 if (layoutInfo.temperature < options.minTemp) {
19896 // logDebug("Temperature drop below minimum threshold. Stopping computation in step " + i);
19897 return false;
19898 }
19899
19900 return true;
19901 };
19902
19903 var done = function done() {
19904 if (options.animate === true || options.animate === false) {
19905 refresh(); // Layout has finished
19906
19907 layout.one('layoutstop', options.stop);
19908 layout.emit({
19909 type: 'layoutstop',
19910 layout: layout
19911 });
19912 } else {
19913 var nodes = options.eles.nodes();
19914 var getScaledPos = getScaleInBoundsFn(layoutInfo, options, nodes);
19915 nodes.layoutPositions(layout, options, getScaledPos);
19916 }
19917 };
19918
19919 var i = 0;
19920 var loopRet = true;
19921
19922 if (options.animate === true) {
19923 var frame = function frame() {
19924 var f = 0;
19925
19926 while (loopRet && f < options.refresh) {
19927 loopRet = mainLoop(i);
19928 i++;
19929 f++;
19930 }
19931
19932 if (!loopRet) {
19933 // it's done
19934 separateComponents(layoutInfo, options);
19935 done();
19936 } else {
19937 var now = performanceNow();
19938
19939 if (now - startTime >= options.animationThreshold) {
19940 refresh();
19941 }
19942
19943 requestAnimationFrame(frame);
19944 }
19945 };
19946
19947 frame();
19948 } else {
19949 while (loopRet) {
19950 loopRet = mainLoop(i);
19951 i++;
19952 }
19953
19954 separateComponents(layoutInfo, options);
19955 done();
19956 }
19957
19958 return this; // chaining
19959};
19960/**
19961 * @brief : called on continuous layouts to stop them before they finish
19962 */
19963
19964
19965CoseLayout.prototype.stop = function () {
19966 this.stopped = true;
19967
19968 if (this.thread) {
19969 this.thread.stop();
19970 }
19971
19972 this.emit('layoutstop');
19973 return this; // chaining
19974};
19975
19976CoseLayout.prototype.destroy = function () {
19977 if (this.thread) {
19978 this.thread.stop();
19979 }
19980
19981 return this; // chaining
19982};
19983/**
19984 * @brief : Creates an object which is contains all the data
19985 * used in the layout process
19986 * @arg cy : cytoscape.js object
19987 * @return : layoutInfo object initialized
19988 */
19989
19990
19991var createLayoutInfo = function createLayoutInfo(cy, layout, options) {
19992 // Shortcut
19993 var edges = options.eles.edges();
19994 var nodes = options.eles.nodes();
19995 var layoutInfo = {
19996 isCompound: cy.hasCompoundNodes(),
19997 layoutNodes: [],
19998 idToIndex: {},
19999 nodeSize: nodes.size(),
20000 graphSet: [],
20001 indexToGraph: [],
20002 layoutEdges: [],
20003 edgeSize: edges.size(),
20004 temperature: options.initialTemp,
20005 clientWidth: cy.width(),
20006 clientHeight: cy.width(),
20007 boundingBox: makeBoundingBox(options.boundingBox ? options.boundingBox : {
20008 x1: 0,
20009 y1: 0,
20010 w: cy.width(),
20011 h: cy.height()
20012 })
20013 };
20014 var components = options.eles.components();
20015 var id2cmptId = {};
20016
20017 for (var i = 0; i < components.length; i++) {
20018 var component = components[i];
20019
20020 for (var j = 0; j < component.length; j++) {
20021 var node = component[j];
20022 id2cmptId[node.id()] = i;
20023 }
20024 } // Iterate over all nodes, creating layout nodes
20025
20026
20027 for (var i = 0; i < layoutInfo.nodeSize; i++) {
20028 var n = nodes[i];
20029 var nbb = n.layoutDimensions(options);
20030 var tempNode = {};
20031 tempNode.isLocked = n.locked();
20032 tempNode.id = n.data('id');
20033 tempNode.parentId = n.data('parent');
20034 tempNode.cmptId = id2cmptId[n.id()];
20035 tempNode.children = [];
20036 tempNode.positionX = n.position('x');
20037 tempNode.positionY = n.position('y');
20038 tempNode.offsetX = 0;
20039 tempNode.offsetY = 0;
20040 tempNode.height = nbb.w;
20041 tempNode.width = nbb.h;
20042 tempNode.maxX = tempNode.positionX + tempNode.width / 2;
20043 tempNode.minX = tempNode.positionX - tempNode.width / 2;
20044 tempNode.maxY = tempNode.positionY + tempNode.height / 2;
20045 tempNode.minY = tempNode.positionY - tempNode.height / 2;
20046 tempNode.padLeft = parseFloat(n.style('padding'));
20047 tempNode.padRight = parseFloat(n.style('padding'));
20048 tempNode.padTop = parseFloat(n.style('padding'));
20049 tempNode.padBottom = parseFloat(n.style('padding')); // forces
20050
20051 tempNode.nodeRepulsion = fn$6(options.nodeRepulsion) ? options.nodeRepulsion(n) : options.nodeRepulsion; // Add new node
20052
20053 layoutInfo.layoutNodes.push(tempNode); // Add entry to id-index map
20054
20055 layoutInfo.idToIndex[tempNode.id] = i;
20056 } // Inline implementation of a queue, used for traversing the graph in BFS order
20057
20058
20059 var queue = [];
20060 var start = 0; // Points to the start the queue
20061
20062 var end = -1; // Points to the end of the queue
20063
20064 var tempGraph = []; // Second pass to add child information and
20065 // initialize queue for hierarchical traversal
20066
20067 for (var i = 0; i < layoutInfo.nodeSize; i++) {
20068 var n = layoutInfo.layoutNodes[i];
20069 var p_id = n.parentId; // Check if node n has a parent node
20070
20071 if (null != p_id) {
20072 // Add node Id to parent's list of children
20073 layoutInfo.layoutNodes[layoutInfo.idToIndex[p_id]].children.push(n.id);
20074 } else {
20075 // If a node doesn't have a parent, then it's in the root graph
20076 queue[++end] = n.id;
20077 tempGraph.push(n.id);
20078 }
20079 } // Add root graph to graphSet
20080
20081
20082 layoutInfo.graphSet.push(tempGraph); // Traverse the graph, level by level,
20083
20084 while (start <= end) {
20085 // Get the node to visit and remove it from queue
20086 var node_id = queue[start++];
20087 var node_ix = layoutInfo.idToIndex[node_id];
20088 var node = layoutInfo.layoutNodes[node_ix];
20089 var children = node.children;
20090
20091 if (children.length > 0) {
20092 // Add children nodes as a new graph to graph set
20093 layoutInfo.graphSet.push(children); // Add children to que queue to be visited
20094
20095 for (var i = 0; i < children.length; i++) {
20096 queue[++end] = children[i];
20097 }
20098 }
20099 } // Create indexToGraph map
20100
20101
20102 for (var i = 0; i < layoutInfo.graphSet.length; i++) {
20103 var graph = layoutInfo.graphSet[i];
20104
20105 for (var j = 0; j < graph.length; j++) {
20106 var index = layoutInfo.idToIndex[graph[j]];
20107 layoutInfo.indexToGraph[index] = i;
20108 }
20109 } // Iterate over all edges, creating Layout Edges
20110
20111
20112 for (var i = 0; i < layoutInfo.edgeSize; i++) {
20113 var e = edges[i];
20114 var tempEdge = {};
20115 tempEdge.id = e.data('id');
20116 tempEdge.sourceId = e.data('source');
20117 tempEdge.targetId = e.data('target'); // Compute ideal length
20118
20119 var idealLength = fn$6(options.idealEdgeLength) ? options.idealEdgeLength(e) : options.idealEdgeLength;
20120 var elasticity = fn$6(options.edgeElasticity) ? options.edgeElasticity(e) : options.edgeElasticity; // Check if it's an inter graph edge
20121
20122 var sourceIx = layoutInfo.idToIndex[tempEdge.sourceId];
20123 var targetIx = layoutInfo.idToIndex[tempEdge.targetId];
20124 var sourceGraph = layoutInfo.indexToGraph[sourceIx];
20125 var targetGraph = layoutInfo.indexToGraph[targetIx];
20126
20127 if (sourceGraph != targetGraph) {
20128 // Find lowest common graph ancestor
20129 var lca = findLCA(tempEdge.sourceId, tempEdge.targetId, layoutInfo); // Compute sum of node depths, relative to lca graph
20130
20131 var lcaGraph = layoutInfo.graphSet[lca];
20132 var depth = 0; // Source depth
20133
20134 var tempNode = layoutInfo.layoutNodes[sourceIx];
20135
20136 while (-1 === lcaGraph.indexOf(tempNode.id)) {
20137 tempNode = layoutInfo.layoutNodes[layoutInfo.idToIndex[tempNode.parentId]];
20138 depth++;
20139 } // Target depth
20140
20141
20142 tempNode = layoutInfo.layoutNodes[targetIx];
20143
20144 while (-1 === lcaGraph.indexOf(tempNode.id)) {
20145 tempNode = layoutInfo.layoutNodes[layoutInfo.idToIndex[tempNode.parentId]];
20146 depth++;
20147 } // logDebug('LCA of nodes ' + tempEdge.sourceId + ' and ' + tempEdge.targetId +
20148 // ". Index: " + lca + " Contents: " + lcaGraph.toString() +
20149 // ". Depth: " + depth);
20150 // Update idealLength
20151
20152
20153 idealLength *= depth * options.nestingFactor;
20154 }
20155
20156 tempEdge.idealLength = idealLength;
20157 tempEdge.elasticity = elasticity;
20158 layoutInfo.layoutEdges.push(tempEdge);
20159 } // Finally, return layoutInfo object
20160
20161
20162 return layoutInfo;
20163};
20164/**
20165 * @brief : This function finds the index of the lowest common
20166 * graph ancestor between 2 nodes in the subtree
20167 * (from the graph hierarchy induced tree) whose
20168 * root is graphIx
20169 *
20170 * @arg node1: node1's ID
20171 * @arg node2: node2's ID
20172 * @arg layoutInfo: layoutInfo object
20173 *
20174 */
20175
20176
20177var findLCA = function findLCA(node1, node2, layoutInfo) {
20178 // Find their common ancester, starting from the root graph
20179 var res = findLCA_aux(node1, node2, 0, layoutInfo);
20180
20181 if (2 > res.count) {
20182 // If aux function couldn't find the common ancester,
20183 // then it is the root graph
20184 return 0;
20185 } else {
20186 return res.graph;
20187 }
20188};
20189/**
20190 * @brief : Auxiliary function used for LCA computation
20191 *
20192 * @arg node1 : node1's ID
20193 * @arg node2 : node2's ID
20194 * @arg graphIx : subgraph index
20195 * @arg layoutInfo : layoutInfo object
20196 *
20197 * @return : object of the form {count: X, graph: Y}, where:
20198 * X is the number of ancesters (max: 2) found in
20199 * graphIx (and it's subgraphs),
20200 * Y is the graph index of the lowest graph containing
20201 * all X nodes
20202 */
20203
20204
20205var findLCA_aux = function findLCA_aux(node1, node2, graphIx, layoutInfo) {
20206 var graph = layoutInfo.graphSet[graphIx]; // If both nodes belongs to graphIx
20207
20208 if (-1 < graph.indexOf(node1) && -1 < graph.indexOf(node2)) {
20209 return {
20210 count: 2,
20211 graph: graphIx
20212 };
20213 } // Make recursive calls for all subgraphs
20214
20215
20216 var c = 0;
20217
20218 for (var i = 0; i < graph.length; i++) {
20219 var nodeId = graph[i];
20220 var nodeIx = layoutInfo.idToIndex[nodeId];
20221 var children = layoutInfo.layoutNodes[nodeIx].children; // If the node has no child, skip it
20222
20223 if (0 === children.length) {
20224 continue;
20225 }
20226
20227 var childGraphIx = layoutInfo.indexToGraph[layoutInfo.idToIndex[children[0]]];
20228 var result = findLCA_aux(node1, node2, childGraphIx, layoutInfo);
20229
20230 if (0 === result.count) {
20231 // Neither node1 nor node2 are present in this subgraph
20232 continue;
20233 } else if (1 === result.count) {
20234 // One of (node1, node2) is present in this subgraph
20235 c++;
20236
20237 if (2 === c) {
20238 // We've already found both nodes, no need to keep searching
20239 break;
20240 }
20241 } else {
20242 // Both nodes are present in this subgraph
20243 return result;
20244 }
20245 }
20246
20247 return {
20248 count: c,
20249 graph: graphIx
20250 };
20251};
20252/**
20253 * @brief: printsLayoutInfo into js console
20254 * Only used for debbuging
20255 */
20256
20257
20258var printLayoutInfo;
20259/**
20260 * @brief : Randomizes the position of all nodes
20261 */
20262
20263
20264var randomizePositions = function randomizePositions(layoutInfo, cy) {
20265 var width = layoutInfo.clientWidth;
20266 var height = layoutInfo.clientHeight;
20267
20268 for (var i = 0; i < layoutInfo.nodeSize; i++) {
20269 var n = layoutInfo.layoutNodes[i]; // No need to randomize compound nodes or locked nodes
20270
20271 if (0 === n.children.length && !n.isLocked) {
20272 n.positionX = Math.random() * width;
20273 n.positionY = Math.random() * height;
20274 }
20275 }
20276};
20277
20278var getScaleInBoundsFn = function getScaleInBoundsFn(layoutInfo, options, nodes) {
20279 var bb = layoutInfo.boundingBox;
20280 var coseBB = {
20281 x1: Infinity,
20282 x2: -Infinity,
20283 y1: Infinity,
20284 y2: -Infinity
20285 };
20286
20287 if (options.boundingBox) {
20288 nodes.forEach(function (node) {
20289 var lnode = layoutInfo.layoutNodes[layoutInfo.idToIndex[node.data('id')]];
20290 coseBB.x1 = Math.min(coseBB.x1, lnode.positionX);
20291 coseBB.x2 = Math.max(coseBB.x2, lnode.positionX);
20292 coseBB.y1 = Math.min(coseBB.y1, lnode.positionY);
20293 coseBB.y2 = Math.max(coseBB.y2, lnode.positionY);
20294 });
20295 coseBB.w = coseBB.x2 - coseBB.x1;
20296 coseBB.h = coseBB.y2 - coseBB.y1;
20297 }
20298
20299 return function (ele, i) {
20300 var lnode = layoutInfo.layoutNodes[layoutInfo.idToIndex[ele.data('id')]];
20301
20302 if (options.boundingBox) {
20303 // then add extra bounding box constraint
20304 var pctX = (lnode.positionX - coseBB.x1) / coseBB.w;
20305 var pctY = (lnode.positionY - coseBB.y1) / coseBB.h;
20306 return {
20307 x: bb.x1 + pctX * bb.w,
20308 y: bb.y1 + pctY * bb.h
20309 };
20310 } else {
20311 return {
20312 x: lnode.positionX,
20313 y: lnode.positionY
20314 };
20315 }
20316 };
20317};
20318/**
20319 * @brief : Updates the positions of nodes in the network
20320 * @arg layoutInfo : LayoutInfo object
20321 * @arg cy : Cytoscape object
20322 * @arg options : Layout options
20323 */
20324
20325
20326var refreshPositions = function refreshPositions(layoutInfo, cy, options) {
20327 // var s = 'Refreshing positions';
20328 // logDebug(s);
20329 var layout = options.layout;
20330 var nodes = options.eles.nodes();
20331 var getScaledPos = getScaleInBoundsFn(layoutInfo, options, nodes);
20332 nodes.positions(getScaledPos); // Trigger layoutReady only on first call
20333
20334 if (true !== layoutInfo.ready) {
20335 // s = 'Triggering layoutready';
20336 // logDebug(s);
20337 layoutInfo.ready = true;
20338 layout.one('layoutready', options.ready);
20339 layout.emit({
20340 type: 'layoutready',
20341 layout: this
20342 });
20343 }
20344};
20345/**
20346 * @brief : Logs a debug message in JS console, if DEBUG is ON
20347 */
20348// var logDebug = function(text) {
20349// if (DEBUG) {
20350// console.debug(text);
20351// }
20352// };
20353
20354/**
20355 * @brief : Performs one iteration of the physical simulation
20356 * @arg layoutInfo : LayoutInfo object already initialized
20357 * @arg cy : Cytoscape object
20358 * @arg options : Layout options
20359 */
20360
20361
20362var step = function step(layoutInfo, options, _step) {
20363 // var s = "\n\n###############################";
20364 // s += "\nSTEP: " + step;
20365 // s += "\n###############################\n";
20366 // logDebug(s);
20367 // Calculate node repulsions
20368 calculateNodeForces(layoutInfo, options); // Calculate edge forces
20369
20370 calculateEdgeForces(layoutInfo); // Calculate gravity forces
20371
20372 calculateGravityForces(layoutInfo, options); // Propagate forces from parent to child
20373
20374 propagateForces(layoutInfo); // Update positions based on calculated forces
20375
20376 updatePositions(layoutInfo);
20377};
20378/**
20379 * @brief : Computes the node repulsion forces
20380 */
20381
20382
20383var calculateNodeForces = function calculateNodeForces(layoutInfo, options) {
20384 // Go through each of the graphs in graphSet
20385 // Nodes only repel each other if they belong to the same graph
20386 // var s = 'calculateNodeForces';
20387 // logDebug(s);
20388 for (var i = 0; i < layoutInfo.graphSet.length; i++) {
20389 var graph = layoutInfo.graphSet[i];
20390 var numNodes = graph.length; // s = "Set: " + graph.toString();
20391 // logDebug(s);
20392 // Now get all the pairs of nodes
20393 // Only get each pair once, (A, B) = (B, A)
20394
20395 for (var j = 0; j < numNodes; j++) {
20396 var node1 = layoutInfo.layoutNodes[layoutInfo.idToIndex[graph[j]]];
20397
20398 for (var k = j + 1; k < numNodes; k++) {
20399 var node2 = layoutInfo.layoutNodes[layoutInfo.idToIndex[graph[k]]];
20400 nodeRepulsion(node1, node2, layoutInfo, options);
20401 }
20402 }
20403 }
20404};
20405
20406var randomDistance = function randomDistance(max) {
20407 return -max + 2 * max * Math.random();
20408};
20409/**
20410 * @brief : Compute the node repulsion forces between a pair of nodes
20411 */
20412
20413
20414var nodeRepulsion = function nodeRepulsion(node1, node2, layoutInfo, options) {
20415 // var s = "Node repulsion. Node1: " + node1.id + " Node2: " + node2.id;
20416 var cmptId1 = node1.cmptId;
20417 var cmptId2 = node2.cmptId;
20418
20419 if (cmptId1 !== cmptId2 && !layoutInfo.isCompound) {
20420 return;
20421 } // Get direction of line connecting both node centers
20422
20423
20424 var directionX = node2.positionX - node1.positionX;
20425 var directionY = node2.positionY - node1.positionY;
20426 var maxRandDist = 1; // s += "\ndirectionX: " + directionX + ", directionY: " + directionY;
20427 // If both centers are the same, apply a random force
20428
20429 if (0 === directionX && 0 === directionY) {
20430 directionX = randomDistance(maxRandDist);
20431 directionY = randomDistance(maxRandDist);
20432 }
20433
20434 var overlap = nodesOverlap(node1, node2, directionX, directionY);
20435
20436 if (overlap > 0) {
20437 // s += "\nNodes DO overlap.";
20438 // s += "\nOverlap: " + overlap;
20439 // If nodes overlap, repulsion force is proportional
20440 // to the overlap
20441 var force = options.nodeOverlap * overlap; // Compute the module and components of the force vector
20442
20443 var distance = Math.sqrt(directionX * directionX + directionY * directionY); // s += "\nDistance: " + distance;
20444
20445 var forceX = force * directionX / distance;
20446 var forceY = force * directionY / distance;
20447 } else {
20448 // s += "\nNodes do NOT overlap.";
20449 // If there's no overlap, force is inversely proportional
20450 // to squared distance
20451 // Get clipping points for both nodes
20452 var point1 = findClippingPoint(node1, directionX, directionY);
20453 var point2 = findClippingPoint(node2, -1 * directionX, -1 * directionY); // Use clipping points to compute distance
20454
20455 var distanceX = point2.x - point1.x;
20456 var distanceY = point2.y - point1.y;
20457 var distanceSqr = distanceX * distanceX + distanceY * distanceY;
20458 var distance = Math.sqrt(distanceSqr); // s += "\nDistance: " + distance;
20459 // Compute the module and components of the force vector
20460
20461 var force = (node1.nodeRepulsion + node2.nodeRepulsion) / distanceSqr;
20462 var forceX = force * distanceX / distance;
20463 var forceY = force * distanceY / distance;
20464 } // Apply force
20465
20466
20467 if (!node1.isLocked) {
20468 node1.offsetX -= forceX;
20469 node1.offsetY -= forceY;
20470 }
20471
20472 if (!node2.isLocked) {
20473 node2.offsetX += forceX;
20474 node2.offsetY += forceY;
20475 } // s += "\nForceX: " + forceX + " ForceY: " + forceY;
20476 // logDebug(s);
20477
20478
20479 return;
20480};
20481/**
20482 * @brief : Determines whether two nodes overlap or not
20483 * @return : Amount of overlapping (0 => no overlap)
20484 */
20485
20486
20487var nodesOverlap = function nodesOverlap(node1, node2, dX, dY) {
20488 if (dX > 0) {
20489 var overlapX = node1.maxX - node2.minX;
20490 } else {
20491 var overlapX = node2.maxX - node1.minX;
20492 }
20493
20494 if (dY > 0) {
20495 var overlapY = node1.maxY - node2.minY;
20496 } else {
20497 var overlapY = node2.maxY - node1.minY;
20498 }
20499
20500 if (overlapX >= 0 && overlapY >= 0) {
20501 return Math.sqrt(overlapX * overlapX + overlapY * overlapY);
20502 } else {
20503 return 0;
20504 }
20505};
20506/**
20507 * @brief : Finds the point in which an edge (direction dX, dY) intersects
20508 * the rectangular bounding box of it's source/target node
20509 */
20510
20511
20512var findClippingPoint = function findClippingPoint(node, dX, dY) {
20513 // Shorcuts
20514 var X = node.positionX;
20515 var Y = node.positionY;
20516 var H = node.height || 1;
20517 var W = node.width || 1;
20518 var dirSlope = dY / dX;
20519 var nodeSlope = H / W; // var s = 'Computing clipping point of node ' + node.id +
20520 // " . Height: " + H + ", Width: " + W +
20521 // "\nDirection " + dX + ", " + dY;
20522 //
20523 // Compute intersection
20524
20525 var res = {}; // Case: Vertical direction (up)
20526
20527 if (0 === dX && 0 < dY) {
20528 res.x = X; // s += "\nUp direction";
20529
20530 res.y = Y + H / 2;
20531 return res;
20532 } // Case: Vertical direction (down)
20533
20534
20535 if (0 === dX && 0 > dY) {
20536 res.x = X;
20537 res.y = Y + H / 2; // s += "\nDown direction";
20538
20539 return res;
20540 } // Case: Intersects the right border
20541
20542
20543 if (0 < dX && -1 * nodeSlope <= dirSlope && dirSlope <= nodeSlope) {
20544 res.x = X + W / 2;
20545 res.y = Y + W * dY / 2 / dX; // s += "\nRightborder";
20546
20547 return res;
20548 } // Case: Intersects the left border
20549
20550
20551 if (0 > dX && -1 * nodeSlope <= dirSlope && dirSlope <= nodeSlope) {
20552 res.x = X - W / 2;
20553 res.y = Y - W * dY / 2 / dX; // s += "\nLeftborder";
20554
20555 return res;
20556 } // Case: Intersects the top border
20557
20558
20559 if (0 < dY && (dirSlope <= -1 * nodeSlope || dirSlope >= nodeSlope)) {
20560 res.x = X + H * dX / 2 / dY;
20561 res.y = Y + H / 2; // s += "\nTop border";
20562
20563 return res;
20564 } // Case: Intersects the bottom border
20565
20566
20567 if (0 > dY && (dirSlope <= -1 * nodeSlope || dirSlope >= nodeSlope)) {
20568 res.x = X - H * dX / 2 / dY;
20569 res.y = Y - H / 2; // s += "\nBottom border";
20570
20571 return res;
20572 } // s += "\nClipping point found at " + res.x + ", " + res.y;
20573 // logDebug(s);
20574
20575
20576 return res;
20577};
20578/**
20579 * @brief : Calculates all edge forces
20580 */
20581
20582
20583var calculateEdgeForces = function calculateEdgeForces(layoutInfo, options) {
20584 // Iterate over all edges
20585 for (var i = 0; i < layoutInfo.edgeSize; i++) {
20586 // Get edge, source & target nodes
20587 var edge = layoutInfo.layoutEdges[i];
20588 var sourceIx = layoutInfo.idToIndex[edge.sourceId];
20589 var source = layoutInfo.layoutNodes[sourceIx];
20590 var targetIx = layoutInfo.idToIndex[edge.targetId];
20591 var target = layoutInfo.layoutNodes[targetIx]; // Get direction of line connecting both node centers
20592
20593 var directionX = target.positionX - source.positionX;
20594 var directionY = target.positionY - source.positionY; // If both centers are the same, do nothing.
20595 // A random force has already been applied as node repulsion
20596
20597 if (0 === directionX && 0 === directionY) {
20598 continue;
20599 } // Get clipping points for both nodes
20600
20601
20602 var point1 = findClippingPoint(source, directionX, directionY);
20603 var point2 = findClippingPoint(target, -1 * directionX, -1 * directionY);
20604 var lx = point2.x - point1.x;
20605 var ly = point2.y - point1.y;
20606 var l = Math.sqrt(lx * lx + ly * ly);
20607 var force = Math.pow(edge.idealLength - l, 2) / edge.elasticity;
20608
20609 if (0 !== l) {
20610 var forceX = force * lx / l;
20611 var forceY = force * ly / l;
20612 } else {
20613 var forceX = 0;
20614 var forceY = 0;
20615 } // Add this force to target and source nodes
20616
20617
20618 if (!source.isLocked) {
20619 source.offsetX += forceX;
20620 source.offsetY += forceY;
20621 }
20622
20623 if (!target.isLocked) {
20624 target.offsetX -= forceX;
20625 target.offsetY -= forceY;
20626 } // var s = 'Edge force between nodes ' + source.id + ' and ' + target.id;
20627 // s += "\nDistance: " + l + " Force: (" + forceX + ", " + forceY + ")";
20628 // logDebug(s);
20629
20630 }
20631};
20632/**
20633 * @brief : Computes gravity forces for all nodes
20634 */
20635
20636
20637var calculateGravityForces = function calculateGravityForces(layoutInfo, options) {
20638 if (options.gravity === 0) {
20639 return;
20640 }
20641
20642 var distThreshold = 1; // var s = 'calculateGravityForces';
20643 // logDebug(s);
20644
20645 for (var i = 0; i < layoutInfo.graphSet.length; i++) {
20646 var graph = layoutInfo.graphSet[i];
20647 var numNodes = graph.length; // s = "Set: " + graph.toString();
20648 // logDebug(s);
20649 // Compute graph center
20650
20651 if (0 === i) {
20652 var centerX = layoutInfo.clientHeight / 2;
20653 var centerY = layoutInfo.clientWidth / 2;
20654 } else {
20655 // Get Parent node for this graph, and use its position as center
20656 var temp = layoutInfo.layoutNodes[layoutInfo.idToIndex[graph[0]]];
20657 var parent = layoutInfo.layoutNodes[layoutInfo.idToIndex[temp.parentId]];
20658 var centerX = parent.positionX;
20659 var centerY = parent.positionY;
20660 } // s = "Center found at: " + centerX + ", " + centerY;
20661 // logDebug(s);
20662 // Apply force to all nodes in graph
20663
20664
20665 for (var j = 0; j < numNodes; j++) {
20666 var node = layoutInfo.layoutNodes[layoutInfo.idToIndex[graph[j]]]; // s = "Node: " + node.id;
20667
20668 if (node.isLocked) {
20669 continue;
20670 }
20671
20672 var dx = centerX - node.positionX;
20673 var dy = centerY - node.positionY;
20674 var d = Math.sqrt(dx * dx + dy * dy);
20675
20676 if (d > distThreshold) {
20677 var fx = options.gravity * dx / d;
20678 var fy = options.gravity * dy / d;
20679 node.offsetX += fx;
20680 node.offsetY += fy; // s += ": Applied force: " + fx + ", " + fy;
20681 } // logDebug(s);
20682
20683 }
20684 }
20685};
20686/**
20687 * @brief : This function propagates the existing offsets from
20688 * parent nodes to its descendents.
20689 * @arg layoutInfo : layoutInfo Object
20690 * @arg cy : cytoscape Object
20691 * @arg options : Layout options
20692 */
20693
20694
20695var propagateForces = function propagateForces(layoutInfo, options) {
20696 // Inline implementation of a queue, used for traversing the graph in BFS order
20697 var queue = [];
20698 var start = 0; // Points to the start the queue
20699
20700 var end = -1; // Points to the end of the queue
20701 // logDebug('propagateForces');
20702 // Start by visiting the nodes in the root graph
20703
20704 queue.push.apply(queue, layoutInfo.graphSet[0]);
20705 end += layoutInfo.graphSet[0].length; // Traverse the graph, level by level,
20706
20707 while (start <= end) {
20708 // Get the node to visit and remove it from queue
20709 var nodeId = queue[start++];
20710 var nodeIndex = layoutInfo.idToIndex[nodeId];
20711 var node = layoutInfo.layoutNodes[nodeIndex];
20712 var children = node.children; // We only need to process the node if it's compound
20713
20714 if (0 < children.length && !node.isLocked) {
20715 var offX = node.offsetX;
20716 var offY = node.offsetY; // var s = "Propagating offset from parent node : " + node.id +
20717 // ". OffsetX: " + offX + ". OffsetY: " + offY;
20718 // s += "\n Children: " + children.toString();
20719 // logDebug(s);
20720
20721 for (var i = 0; i < children.length; i++) {
20722 var childNode = layoutInfo.layoutNodes[layoutInfo.idToIndex[children[i]]]; // Propagate offset
20723
20724 childNode.offsetX += offX;
20725 childNode.offsetY += offY; // Add children to queue to be visited
20726
20727 queue[++end] = children[i];
20728 } // Reset parent offsets
20729
20730
20731 node.offsetX = 0;
20732 node.offsetY = 0;
20733 }
20734 }
20735};
20736/**
20737 * @brief : Updates the layout model positions, based on
20738 * the accumulated forces
20739 */
20740
20741
20742var updatePositions = function updatePositions(layoutInfo, options) {
20743 // var s = 'Updating positions';
20744 // logDebug(s);
20745 // Reset boundaries for compound nodes
20746 for (var i = 0; i < layoutInfo.nodeSize; i++) {
20747 var n = layoutInfo.layoutNodes[i];
20748
20749 if (0 < n.children.length) {
20750 // logDebug("Resetting boundaries of compound node: " + n.id);
20751 n.maxX = undefined;
20752 n.minX = undefined;
20753 n.maxY = undefined;
20754 n.minY = undefined;
20755 }
20756 }
20757
20758 for (var i = 0; i < layoutInfo.nodeSize; i++) {
20759 var n = layoutInfo.layoutNodes[i];
20760
20761 if (0 < n.children.length || n.isLocked) {
20762 // No need to set compound or locked node position
20763 // logDebug("Skipping position update of node: " + n.id);
20764 continue;
20765 } // s = "Node: " + n.id + " Previous position: (" +
20766 // n.positionX + ", " + n.positionY + ").";
20767 // Limit displacement in order to improve stability
20768
20769
20770 var tempForce = limitForce(n.offsetX, n.offsetY, layoutInfo.temperature);
20771 n.positionX += tempForce.x;
20772 n.positionY += tempForce.y;
20773 n.offsetX = 0;
20774 n.offsetY = 0;
20775 n.minX = n.positionX - n.width;
20776 n.maxX = n.positionX + n.width;
20777 n.minY = n.positionY - n.height;
20778 n.maxY = n.positionY + n.height; // s += " New Position: (" + n.positionX + ", " + n.positionY + ").";
20779 // logDebug(s);
20780 // Update ancestry boudaries
20781
20782 updateAncestryBoundaries(n, layoutInfo);
20783 } // Update size, position of compund nodes
20784
20785
20786 for (var i = 0; i < layoutInfo.nodeSize; i++) {
20787 var n = layoutInfo.layoutNodes[i];
20788
20789 if (0 < n.children.length && !n.isLocked) {
20790 n.positionX = (n.maxX + n.minX) / 2;
20791 n.positionY = (n.maxY + n.minY) / 2;
20792 n.width = n.maxX - n.minX;
20793 n.height = n.maxY - n.minY; // s = "Updating position, size of compound node " + n.id;
20794 // s += "\nPositionX: " + n.positionX + ", PositionY: " + n.positionY;
20795 // s += "\nWidth: " + n.width + ", Height: " + n.height;
20796 // logDebug(s);
20797 }
20798 }
20799};
20800/**
20801 * @brief : Limits a force (forceX, forceY) to be not
20802 * greater (in modulo) than max.
20803 8 Preserves force direction.
20804 */
20805
20806
20807var limitForce = function limitForce(forceX, forceY, max) {
20808 // var s = "Limiting force: (" + forceX + ", " + forceY + "). Max: " + max;
20809 var force = Math.sqrt(forceX * forceX + forceY * forceY);
20810
20811 if (force > max) {
20812 var res = {
20813 x: max * forceX / force,
20814 y: max * forceY / force
20815 };
20816 } else {
20817 var res = {
20818 x: forceX,
20819 y: forceY
20820 };
20821 } // s += ".\nResult: (" + res.x + ", " + res.y + ")";
20822 // logDebug(s);
20823
20824
20825 return res;
20826};
20827/**
20828 * @brief : Function used for keeping track of compound node
20829 * sizes, since they should bound all their subnodes.
20830 */
20831
20832
20833var updateAncestryBoundaries = function updateAncestryBoundaries(node, layoutInfo) {
20834 // var s = "Propagating new position/size of node " + node.id;
20835 var parentId = node.parentId;
20836
20837 if (null == parentId) {
20838 // If there's no parent, we are done
20839 // s += ". No parent node.";
20840 // logDebug(s);
20841 return;
20842 } // Get Parent Node
20843
20844
20845 var p = layoutInfo.layoutNodes[layoutInfo.idToIndex[parentId]];
20846 var flag = false; // MaxX
20847
20848 if (null == p.maxX || node.maxX + p.padRight > p.maxX) {
20849 p.maxX = node.maxX + p.padRight;
20850 flag = true; // s += "\nNew maxX for parent node " + p.id + ": " + p.maxX;
20851 } // MinX
20852
20853
20854 if (null == p.minX || node.minX - p.padLeft < p.minX) {
20855 p.minX = node.minX - p.padLeft;
20856 flag = true; // s += "\nNew minX for parent node " + p.id + ": " + p.minX;
20857 } // MaxY
20858
20859
20860 if (null == p.maxY || node.maxY + p.padBottom > p.maxY) {
20861 p.maxY = node.maxY + p.padBottom;
20862 flag = true; // s += "\nNew maxY for parent node " + p.id + ": " + p.maxY;
20863 } // MinY
20864
20865
20866 if (null == p.minY || node.minY - p.padTop < p.minY) {
20867 p.minY = node.minY - p.padTop;
20868 flag = true; // s += "\nNew minY for parent node " + p.id + ": " + p.minY;
20869 } // If updated boundaries, propagate changes upward
20870
20871
20872 if (flag) {
20873 // logDebug(s);
20874 return updateAncestryBoundaries(p, layoutInfo);
20875 } // s += ". No changes in boundaries/position of parent node " + p.id;
20876 // logDebug(s);
20877
20878
20879 return;
20880};
20881
20882var separateComponents = function separateComponents(layoutInfo, options) {
20883 var nodes = layoutInfo.layoutNodes;
20884 var components = [];
20885
20886 for (var i = 0; i < nodes.length; i++) {
20887 var node = nodes[i];
20888 var cid = node.cmptId;
20889 var component = components[cid] = components[cid] || [];
20890 component.push(node);
20891 }
20892
20893 var totalA = 0;
20894
20895 for (var i = 0; i < components.length; i++) {
20896 var c = components[i];
20897
20898 if (!c) {
20899 continue;
20900 }
20901
20902 c.x1 = Infinity;
20903 c.x2 = -Infinity;
20904 c.y1 = Infinity;
20905 c.y2 = -Infinity;
20906
20907 for (var j = 0; j < c.length; j++) {
20908 var n = c[j];
20909 c.x1 = Math.min(c.x1, n.positionX - n.width / 2);
20910 c.x2 = Math.max(c.x2, n.positionX + n.width / 2);
20911 c.y1 = Math.min(c.y1, n.positionY - n.height / 2);
20912 c.y2 = Math.max(c.y2, n.positionY + n.height / 2);
20913 }
20914
20915 c.w = c.x2 - c.x1;
20916 c.h = c.y2 - c.y1;
20917 totalA += c.w * c.h;
20918 }
20919
20920 components.sort(function (c1, c2) {
20921 return c2.w * c2.h - c1.w * c1.h;
20922 });
20923 var x = 0;
20924 var y = 0;
20925 var usedW = 0;
20926 var rowH = 0;
20927 var maxRowW = Math.sqrt(totalA) * layoutInfo.clientWidth / layoutInfo.clientHeight;
20928
20929 for (var i = 0; i < components.length; i++) {
20930 var c = components[i];
20931
20932 if (!c) {
20933 continue;
20934 }
20935
20936 for (var j = 0; j < c.length; j++) {
20937 var n = c[j];
20938
20939 if (!n.isLocked) {
20940 n.positionX += x - c.x1;
20941 n.positionY += y - c.y1;
20942 }
20943 }
20944
20945 x += c.w + options.componentSpacing;
20946 usedW += c.w + options.componentSpacing;
20947 rowH = Math.max(rowH, c.h);
20948
20949 if (usedW > maxRowW) {
20950 y += rowH + options.componentSpacing;
20951 x = 0;
20952 usedW = 0;
20953 rowH = 0;
20954 }
20955 }
20956};
20957
20958var defaults$3 = {
20959 fit: true,
20960 // whether to fit the viewport to the graph
20961 padding: 30,
20962 // padding used on fit
20963 boundingBox: undefined,
20964 // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
20965 avoidOverlap: true,
20966 // prevents node overlap, may overflow boundingBox if not enough space
20967 avoidOverlapPadding: 10,
20968 // extra spacing around nodes when avoidOverlap: true
20969 nodeDimensionsIncludeLabels: false,
20970 // Excludes the label when calculating node bounding boxes for the layout algorithm
20971 spacingFactor: undefined,
20972 // Applies a multiplicative factor (>0) to expand or compress the overall area that the nodes take up
20973 condense: false,
20974 // uses all available space on false, uses minimal space on true
20975 rows: undefined,
20976 // force num of rows in the grid
20977 cols: undefined,
20978 // force num of columns in the grid
20979 position: function position(node) {},
20980 // returns { row, col } for element
20981 sort: undefined,
20982 // a sorting function to order the nodes; e.g. function(a, b){ return a.data('weight') - b.data('weight') }
20983 animate: false,
20984 // whether to transition the node positions
20985 animationDuration: 500,
20986 // duration of animation in ms if enabled
20987 animationEasing: undefined,
20988 // easing of animation if enabled
20989 animateFilter: function animateFilter(node, i) {
20990 return true;
20991 },
20992 // a function that determines whether the node should be animated. All nodes animated by default on animate enabled. Non-animated nodes are positioned immediately when the layout starts
20993 ready: undefined,
20994 // callback on layoutready
20995 stop: undefined,
20996 // callback on layoutstop
20997 transform: function transform(node, position) {
20998 return position;
20999 } // transform a given node position. Useful for changing flow direction in discrete layouts
21000
21001};
21002
21003function GridLayout(options) {
21004 this.options = extend({}, defaults$3, options);
21005}
21006
21007GridLayout.prototype.run = function () {
21008 var params = this.options;
21009 var options = params;
21010 var cy = params.cy;
21011 var eles = options.eles;
21012 var nodes = eles.nodes().not(':parent');
21013
21014 if (options.sort) {
21015 nodes = nodes.sort(options.sort);
21016 }
21017
21018 var bb = makeBoundingBox(options.boundingBox ? options.boundingBox : {
21019 x1: 0,
21020 y1: 0,
21021 w: cy.width(),
21022 h: cy.height()
21023 });
21024
21025 if (bb.h === 0 || bb.w === 0) {
21026 eles.nodes().layoutPositions(this, options, function (ele) {
21027 return {
21028 x: bb.x1,
21029 y: bb.y1
21030 };
21031 });
21032 } else {
21033 // width/height * splits^2 = cells where splits is number of times to split width
21034 var cells = nodes.size();
21035 var splits = Math.sqrt(cells * bb.h / bb.w);
21036 var rows = Math.round(splits);
21037 var cols = Math.round(bb.w / bb.h * splits);
21038
21039 var small = function small(val) {
21040 if (val == null) {
21041 return Math.min(rows, cols);
21042 } else {
21043 var min = Math.min(rows, cols);
21044
21045 if (min == rows) {
21046 rows = val;
21047 } else {
21048 cols = val;
21049 }
21050 }
21051 };
21052
21053 var large = function large(val) {
21054 if (val == null) {
21055 return Math.max(rows, cols);
21056 } else {
21057 var max = Math.max(rows, cols);
21058
21059 if (max == rows) {
21060 rows = val;
21061 } else {
21062 cols = val;
21063 }
21064 }
21065 };
21066
21067 var oRows = options.rows;
21068 var oCols = options.cols != null ? options.cols : options.columns; // if rows or columns were set in options, use those values
21069
21070 if (oRows != null && oCols != null) {
21071 rows = oRows;
21072 cols = oCols;
21073 } else if (oRows != null && oCols == null) {
21074 rows = oRows;
21075 cols = Math.ceil(cells / rows);
21076 } else if (oRows == null && oCols != null) {
21077 cols = oCols;
21078 rows = Math.ceil(cells / cols);
21079 } // otherwise use the automatic values and adjust accordingly
21080 // if rounding was up, see if we can reduce rows or columns
21081 else if (cols * rows > cells) {
21082 var sm = small();
21083 var lg = large(); // reducing the small side takes away the most cells, so try it first
21084
21085 if ((sm - 1) * lg >= cells) {
21086 small(sm - 1);
21087 } else if ((lg - 1) * sm >= cells) {
21088 large(lg - 1);
21089 }
21090 } else {
21091 // if rounding was too low, add rows or columns
21092 while (cols * rows < cells) {
21093 var _sm = small();
21094
21095 var _lg = large(); // try to add to larger side first (adds less in multiplication)
21096
21097
21098 if ((_lg + 1) * _sm >= cells) {
21099 large(_lg + 1);
21100 } else {
21101 small(_sm + 1);
21102 }
21103 }
21104 }
21105
21106 var cellWidth = bb.w / cols;
21107 var cellHeight = bb.h / rows;
21108
21109 if (options.condense) {
21110 cellWidth = 0;
21111 cellHeight = 0;
21112 }
21113
21114 if (options.avoidOverlap) {
21115 for (var i = 0; i < nodes.length; i++) {
21116 var node = nodes[i];
21117 var pos = node._private.position;
21118
21119 if (pos.x == null || pos.y == null) {
21120 // for bb
21121 pos.x = 0;
21122 pos.y = 0;
21123 }
21124
21125 var nbb = node.layoutDimensions(options);
21126 var p = options.avoidOverlapPadding;
21127 var w = nbb.w + p;
21128 var h = nbb.h + p;
21129 cellWidth = Math.max(cellWidth, w);
21130 cellHeight = Math.max(cellHeight, h);
21131 }
21132 }
21133
21134 var cellUsed = {}; // e.g. 'c-0-2' => true
21135
21136 var used = function used(row, col) {
21137 return cellUsed['c-' + row + '-' + col] ? true : false;
21138 };
21139
21140 var use = function use(row, col) {
21141 cellUsed['c-' + row + '-' + col] = true;
21142 }; // to keep track of current cell position
21143
21144
21145 var row = 0;
21146 var col = 0;
21147
21148 var moveToNextCell = function moveToNextCell() {
21149 col++;
21150
21151 if (col >= cols) {
21152 col = 0;
21153 row++;
21154 }
21155 }; // get a cache of all the manual positions
21156
21157
21158 var id2manPos = {};
21159
21160 for (var _i = 0; _i < nodes.length; _i++) {
21161 var _node = nodes[_i];
21162 var rcPos = options.position(_node);
21163
21164 if (rcPos && (rcPos.row !== undefined || rcPos.col !== undefined)) {
21165 // must have at least row or col def'd
21166 var _pos = {
21167 row: rcPos.row,
21168 col: rcPos.col
21169 };
21170
21171 if (_pos.col === undefined) {
21172 // find unused col
21173 _pos.col = 0;
21174
21175 while (used(_pos.row, _pos.col)) {
21176 _pos.col++;
21177 }
21178 } else if (_pos.row === undefined) {
21179 // find unused row
21180 _pos.row = 0;
21181
21182 while (used(_pos.row, _pos.col)) {
21183 _pos.row++;
21184 }
21185 }
21186
21187 id2manPos[_node.id()] = _pos;
21188 use(_pos.row, _pos.col);
21189 }
21190 }
21191
21192 var getPos = function getPos(element, i) {
21193 var x, y;
21194
21195 if (element.locked() || element.isParent()) {
21196 return false;
21197 } // see if we have a manual position set
21198
21199
21200 var rcPos = id2manPos[element.id()];
21201
21202 if (rcPos) {
21203 x = rcPos.col * cellWidth + cellWidth / 2 + bb.x1;
21204 y = rcPos.row * cellHeight + cellHeight / 2 + bb.y1;
21205 } else {
21206 // otherwise set automatically
21207 while (used(row, col)) {
21208 moveToNextCell();
21209 }
21210
21211 x = col * cellWidth + cellWidth / 2 + bb.x1;
21212 y = row * cellHeight + cellHeight / 2 + bb.y1;
21213 use(row, col);
21214 moveToNextCell();
21215 }
21216
21217 return {
21218 x: x,
21219 y: y
21220 };
21221 };
21222
21223 nodes.layoutPositions(this, options, getPos);
21224 }
21225
21226 return this; // chaining
21227};
21228
21229var defaults$2 = {
21230 ready: function ready() {},
21231 // on layoutready
21232 stop: function stop() {} // on layoutstop
21233
21234}; // constructor
21235// options : object containing layout options
21236
21237function NullLayout(options) {
21238 this.options = extend({}, defaults$2, options);
21239} // runs the layout
21240
21241
21242NullLayout.prototype.run = function () {
21243 var options = this.options;
21244 var eles = options.eles; // elements to consider in the layout
21245
21246 var layout = this; // cy is automatically populated for us in the constructor
21247 // (disable eslint for next line as this serves as example layout code to external developers)
21248 // eslint-disable-next-line no-unused-vars
21249
21250 options.cy;
21251 layout.emit('layoutstart'); // puts all nodes at (0, 0)
21252 // n.b. most layouts would use layoutPositions(), instead of positions() and manual events
21253
21254 eles.nodes().positions(function () {
21255 return {
21256 x: 0,
21257 y: 0
21258 };
21259 }); // trigger layoutready when each node has had its position set at least once
21260
21261 layout.one('layoutready', options.ready);
21262 layout.emit('layoutready'); // trigger layoutstop when the layout stops (e.g. finishes)
21263
21264 layout.one('layoutstop', options.stop);
21265 layout.emit('layoutstop');
21266 return this; // chaining
21267}; // called on continuous layouts to stop them before they finish
21268
21269
21270NullLayout.prototype.stop = function () {
21271 return this; // chaining
21272};
21273
21274var defaults$1 = {
21275 positions: undefined,
21276 // map of (node id) => (position obj); or function(node){ return somPos; }
21277 zoom: undefined,
21278 // the zoom level to set (prob want fit = false if set)
21279 pan: undefined,
21280 // the pan level to set (prob want fit = false if set)
21281 fit: true,
21282 // whether to fit to viewport
21283 padding: 30,
21284 // padding on fit
21285 animate: false,
21286 // whether to transition the node positions
21287 animationDuration: 500,
21288 // duration of animation in ms if enabled
21289 animationEasing: undefined,
21290 // easing of animation if enabled
21291 animateFilter: function animateFilter(node, i) {
21292 return true;
21293 },
21294 // a function that determines whether the node should be animated. All nodes animated by default on animate enabled. Non-animated nodes are positioned immediately when the layout starts
21295 ready: undefined,
21296 // callback on layoutready
21297 stop: undefined,
21298 // callback on layoutstop
21299 transform: function transform(node, position) {
21300 return position;
21301 } // transform a given node position. Useful for changing flow direction in discrete layouts
21302
21303};
21304
21305function PresetLayout(options) {
21306 this.options = extend({}, defaults$1, options);
21307}
21308
21309PresetLayout.prototype.run = function () {
21310 var options = this.options;
21311 var eles = options.eles;
21312 var nodes = eles.nodes();
21313 var posIsFn = fn$6(options.positions);
21314
21315 function getPosition(node) {
21316 if (options.positions == null) {
21317 return copyPosition(node.position());
21318 }
21319
21320 if (posIsFn) {
21321 return options.positions(node);
21322 }
21323
21324 var pos = options.positions[node._private.data.id];
21325
21326 if (pos == null) {
21327 return null;
21328 }
21329
21330 return pos;
21331 }
21332
21333 nodes.layoutPositions(this, options, function (node, i) {
21334 var position = getPosition(node);
21335
21336 if (node.locked() || position == null) {
21337 return false;
21338 }
21339
21340 return position;
21341 });
21342 return this; // chaining
21343};
21344
21345var defaults = {
21346 fit: true,
21347 // whether to fit to viewport
21348 padding: 30,
21349 // fit padding
21350 boundingBox: undefined,
21351 // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
21352 animate: false,
21353 // whether to transition the node positions
21354 animationDuration: 500,
21355 // duration of animation in ms if enabled
21356 animationEasing: undefined,
21357 // easing of animation if enabled
21358 animateFilter: function animateFilter(node, i) {
21359 return true;
21360 },
21361 // a function that determines whether the node should be animated. All nodes animated by default on animate enabled. Non-animated nodes are positioned immediately when the layout starts
21362 ready: undefined,
21363 // callback on layoutready
21364 stop: undefined,
21365 // callback on layoutstop
21366 transform: function transform(node, position) {
21367 return position;
21368 } // transform a given node position. Useful for changing flow direction in discrete layouts
21369
21370};
21371
21372function RandomLayout(options) {
21373 this.options = extend({}, defaults, options);
21374}
21375
21376RandomLayout.prototype.run = function () {
21377 var options = this.options;
21378 var cy = options.cy;
21379 var eles = options.eles;
21380 var bb = makeBoundingBox(options.boundingBox ? options.boundingBox : {
21381 x1: 0,
21382 y1: 0,
21383 w: cy.width(),
21384 h: cy.height()
21385 });
21386
21387 var getPos = function getPos(node, i) {
21388 return {
21389 x: bb.x1 + Math.round(Math.random() * bb.w),
21390 y: bb.y1 + Math.round(Math.random() * bb.h)
21391 };
21392 };
21393
21394 eles.nodes().layoutPositions(this, options, getPos);
21395 return this; // chaining
21396};
21397
21398var layout = [{
21399 name: 'breadthfirst',
21400 impl: BreadthFirstLayout
21401}, {
21402 name: 'circle',
21403 impl: CircleLayout
21404}, {
21405 name: 'concentric',
21406 impl: ConcentricLayout
21407}, {
21408 name: 'cose',
21409 impl: CoseLayout
21410}, {
21411 name: 'grid',
21412 impl: GridLayout
21413}, {
21414 name: 'null',
21415 impl: NullLayout
21416}, {
21417 name: 'preset',
21418 impl: PresetLayout
21419}, {
21420 name: 'random',
21421 impl: RandomLayout
21422}];
21423
21424function NullRenderer(options) {
21425 this.options = options;
21426 this.notifications = 0; // for testing
21427}
21428
21429var noop = function noop() {};
21430
21431var throwImgErr = function throwImgErr() {
21432 throw new Error('A headless instance can not render images');
21433};
21434
21435NullRenderer.prototype = {
21436 recalculateRenderedStyle: noop,
21437 notify: function notify() {
21438 this.notifications++;
21439 },
21440 init: noop,
21441 isHeadless: function isHeadless() {
21442 return true;
21443 },
21444 png: throwImgErr,
21445 jpg: throwImgErr
21446};
21447
21448var BRp$f = {};
21449BRp$f.arrowShapeWidth = 0.3;
21450
21451BRp$f.registerArrowShapes = function () {
21452 var arrowShapes = this.arrowShapes = {};
21453 var renderer = this; // Contract for arrow shapes:
21454 // 0, 0 is arrow tip
21455 // (0, 1) is direction towards node
21456 // (1, 0) is right
21457 //
21458 // functional api:
21459 // collide: check x, y in shape
21460 // roughCollide: called before collide, no false negatives
21461 // draw: draw
21462 // spacing: dist(arrowTip, nodeBoundary)
21463 // gap: dist(edgeTip, nodeBoundary), edgeTip may != arrowTip
21464
21465 var bbCollide = function bbCollide(x, y, size, angle, translation, edgeWidth, padding) {
21466 var x1 = translation.x - size / 2 - padding;
21467 var x2 = translation.x + size / 2 + padding;
21468 var y1 = translation.y - size / 2 - padding;
21469 var y2 = translation.y + size / 2 + padding;
21470 var inside = x1 <= x && x <= x2 && y1 <= y && y <= y2;
21471 return inside;
21472 };
21473
21474 var transform = function transform(x, y, size, angle, translation) {
21475 var xRotated = x * Math.cos(angle) - y * Math.sin(angle);
21476 var yRotated = x * Math.sin(angle) + y * Math.cos(angle);
21477 var xScaled = xRotated * size;
21478 var yScaled = yRotated * size;
21479 var xTranslated = xScaled + translation.x;
21480 var yTranslated = yScaled + translation.y;
21481 return {
21482 x: xTranslated,
21483 y: yTranslated
21484 };
21485 };
21486
21487 var transformPoints = function transformPoints(pts, size, angle, translation) {
21488 var retPts = [];
21489
21490 for (var i = 0; i < pts.length; i += 2) {
21491 var x = pts[i];
21492 var y = pts[i + 1];
21493 retPts.push(transform(x, y, size, angle, translation));
21494 }
21495
21496 return retPts;
21497 };
21498
21499 var pointsToArr = function pointsToArr(pts) {
21500 var ret = [];
21501
21502 for (var i = 0; i < pts.length; i++) {
21503 var p = pts[i];
21504 ret.push(p.x, p.y);
21505 }
21506
21507 return ret;
21508 };
21509
21510 var standardGap = function standardGap(edge) {
21511 return edge.pstyle('width').pfValue * edge.pstyle('arrow-scale').pfValue * 2;
21512 };
21513
21514 var defineArrowShape = function defineArrowShape(name, defn) {
21515 if (string(defn)) {
21516 defn = arrowShapes[defn];
21517 }
21518
21519 arrowShapes[name] = extend({
21520 name: name,
21521 points: [-0.15, -0.3, 0.15, -0.3, 0.15, 0.3, -0.15, 0.3],
21522 collide: function collide(x, y, size, angle, translation, padding) {
21523 var points = pointsToArr(transformPoints(this.points, size + 2 * padding, angle, translation));
21524 var inside = pointInsidePolygonPoints(x, y, points);
21525 return inside;
21526 },
21527 roughCollide: bbCollide,
21528 draw: function draw(context, size, angle, translation) {
21529 var points = transformPoints(this.points, size, angle, translation);
21530 renderer.arrowShapeImpl('polygon')(context, points);
21531 },
21532 spacing: function spacing(edge) {
21533 return 0;
21534 },
21535 gap: standardGap
21536 }, defn);
21537 };
21538
21539 defineArrowShape('none', {
21540 collide: falsify,
21541 roughCollide: falsify,
21542 draw: noop$1,
21543 spacing: zeroify,
21544 gap: zeroify
21545 });
21546 defineArrowShape('triangle', {
21547 points: [-0.15, -0.3, 0, 0, 0.15, -0.3]
21548 });
21549 defineArrowShape('arrow', 'triangle');
21550 defineArrowShape('triangle-backcurve', {
21551 points: arrowShapes['triangle'].points,
21552 controlPoint: [0, -0.15],
21553 roughCollide: bbCollide,
21554 draw: function draw(context, size, angle, translation, edgeWidth) {
21555 var ptsTrans = transformPoints(this.points, size, angle, translation);
21556 var ctrlPt = this.controlPoint;
21557 var ctrlPtTrans = transform(ctrlPt[0], ctrlPt[1], size, angle, translation);
21558 renderer.arrowShapeImpl(this.name)(context, ptsTrans, ctrlPtTrans);
21559 },
21560 gap: function gap(edge) {
21561 return standardGap(edge) * 0.8;
21562 }
21563 });
21564 defineArrowShape('triangle-tee', {
21565 points: [0, 0, 0.15, -0.3, -0.15, -0.3, 0, 0],
21566 pointsTee: [-0.15, -0.4, -0.15, -0.5, 0.15, -0.5, 0.15, -0.4],
21567 collide: function collide(x, y, size, angle, translation, edgeWidth, padding) {
21568 var triPts = pointsToArr(transformPoints(this.points, size + 2 * padding, angle, translation));
21569 var teePts = pointsToArr(transformPoints(this.pointsTee, size + 2 * padding, angle, translation));
21570 var inside = pointInsidePolygonPoints(x, y, triPts) || pointInsidePolygonPoints(x, y, teePts);
21571 return inside;
21572 },
21573 draw: function draw(context, size, angle, translation, edgeWidth) {
21574 var triPts = transformPoints(this.points, size, angle, translation);
21575 var teePts = transformPoints(this.pointsTee, size, angle, translation);
21576 renderer.arrowShapeImpl(this.name)(context, triPts, teePts);
21577 }
21578 });
21579 defineArrowShape('circle-triangle', {
21580 radius: 0.15,
21581 pointsTr: [0, -0.15, 0.15, -0.45, -0.15, -0.45, 0, -0.15],
21582 collide: function collide(x, y, size, angle, translation, edgeWidth, padding) {
21583 var t = translation;
21584 var circleInside = Math.pow(t.x - x, 2) + Math.pow(t.y - y, 2) <= Math.pow((size + 2 * padding) * this.radius, 2);
21585 var triPts = pointsToArr(transformPoints(this.points, size + 2 * padding, angle, translation));
21586 return pointInsidePolygonPoints(x, y, triPts) || circleInside;
21587 },
21588 draw: function draw(context, size, angle, translation, edgeWidth) {
21589 var triPts = transformPoints(this.pointsTr, size, angle, translation);
21590 renderer.arrowShapeImpl(this.name)(context, triPts, translation.x, translation.y, this.radius * size);
21591 },
21592 spacing: function spacing(edge) {
21593 return renderer.getArrowWidth(edge.pstyle('width').pfValue, edge.pstyle('arrow-scale').value) * this.radius;
21594 }
21595 });
21596 defineArrowShape('triangle-cross', {
21597 points: [0, 0, 0.15, -0.3, -0.15, -0.3, 0, 0],
21598 baseCrossLinePts: [-0.15, -0.4, // first half of the rectangle
21599 -0.15, -0.4, 0.15, -0.4, // second half of the rectangle
21600 0.15, -0.4],
21601 crossLinePts: function crossLinePts(size, edgeWidth) {
21602 // shift points so that the distance between the cross points matches edge width
21603 var p = this.baseCrossLinePts.slice();
21604 var shiftFactor = edgeWidth / size;
21605 var y0 = 3;
21606 var y1 = 5;
21607 p[y0] = p[y0] - shiftFactor;
21608 p[y1] = p[y1] - shiftFactor;
21609 return p;
21610 },
21611 collide: function collide(x, y, size, angle, translation, edgeWidth, padding) {
21612 var triPts = pointsToArr(transformPoints(this.points, size + 2 * padding, angle, translation));
21613 var teePts = pointsToArr(transformPoints(this.crossLinePts(size, edgeWidth), size + 2 * padding, angle, translation));
21614 var inside = pointInsidePolygonPoints(x, y, triPts) || pointInsidePolygonPoints(x, y, teePts);
21615 return inside;
21616 },
21617 draw: function draw(context, size, angle, translation, edgeWidth) {
21618 var triPts = transformPoints(this.points, size, angle, translation);
21619 var crossLinePts = transformPoints(this.crossLinePts(size, edgeWidth), size, angle, translation);
21620 renderer.arrowShapeImpl(this.name)(context, triPts, crossLinePts);
21621 }
21622 });
21623 defineArrowShape('vee', {
21624 points: [-0.15, -0.3, 0, 0, 0.15, -0.3, 0, -0.15],
21625 gap: function gap(edge) {
21626 return standardGap(edge) * 0.525;
21627 }
21628 });
21629 defineArrowShape('circle', {
21630 radius: 0.15,
21631 collide: function collide(x, y, size, angle, translation, edgeWidth, padding) {
21632 var t = translation;
21633 var inside = Math.pow(t.x - x, 2) + Math.pow(t.y - y, 2) <= Math.pow((size + 2 * padding) * this.radius, 2);
21634 return inside;
21635 },
21636 draw: function draw(context, size, angle, translation, edgeWidth) {
21637 renderer.arrowShapeImpl(this.name)(context, translation.x, translation.y, this.radius * size);
21638 },
21639 spacing: function spacing(edge) {
21640 return renderer.getArrowWidth(edge.pstyle('width').pfValue, edge.pstyle('arrow-scale').value) * this.radius;
21641 }
21642 });
21643 defineArrowShape('tee', {
21644 points: [-0.15, 0, -0.15, -0.1, 0.15, -0.1, 0.15, 0],
21645 spacing: function spacing(edge) {
21646 return 1;
21647 },
21648 gap: function gap(edge) {
21649 return 1;
21650 }
21651 });
21652 defineArrowShape('square', {
21653 points: [-0.15, 0.00, 0.15, 0.00, 0.15, -0.3, -0.15, -0.3]
21654 });
21655 defineArrowShape('diamond', {
21656 points: [-0.15, -0.15, 0, -0.3, 0.15, -0.15, 0, 0],
21657 gap: function gap(edge) {
21658 return edge.pstyle('width').pfValue * edge.pstyle('arrow-scale').value;
21659 }
21660 });
21661 defineArrowShape('chevron', {
21662 points: [0, 0, -0.15, -0.15, -0.1, -0.2, 0, -0.1, 0.1, -0.2, 0.15, -0.15],
21663 gap: function gap(edge) {
21664 return 0.95 * edge.pstyle('width').pfValue * edge.pstyle('arrow-scale').value;
21665 }
21666 });
21667};
21668
21669var BRp$e = {}; // Project mouse
21670
21671BRp$e.projectIntoViewport = function (clientX, clientY) {
21672 var cy = this.cy;
21673 var offsets = this.findContainerClientCoords();
21674 var offsetLeft = offsets[0];
21675 var offsetTop = offsets[1];
21676 var scale = offsets[4];
21677 var pan = cy.pan();
21678 var zoom = cy.zoom();
21679 var x = ((clientX - offsetLeft) / scale - pan.x) / zoom;
21680 var y = ((clientY - offsetTop) / scale - pan.y) / zoom;
21681 return [x, y];
21682};
21683
21684BRp$e.findContainerClientCoords = function () {
21685 if (this.containerBB) {
21686 return this.containerBB;
21687 }
21688
21689 var container = this.container;
21690 var rect = container.getBoundingClientRect();
21691 var style = window$1.getComputedStyle(container);
21692
21693 var styleValue = function styleValue(name) {
21694 return parseFloat(style.getPropertyValue(name));
21695 };
21696
21697 var padding = {
21698 left: styleValue('padding-left'),
21699 right: styleValue('padding-right'),
21700 top: styleValue('padding-top'),
21701 bottom: styleValue('padding-bottom')
21702 };
21703 var border = {
21704 left: styleValue('border-left-width'),
21705 right: styleValue('border-right-width'),
21706 top: styleValue('border-top-width'),
21707 bottom: styleValue('border-bottom-width')
21708 };
21709 var clientWidth = container.clientWidth;
21710 var clientHeight = container.clientHeight;
21711 var paddingHor = padding.left + padding.right;
21712 var paddingVer = padding.top + padding.bottom;
21713 var borderHor = border.left + border.right;
21714 var scale = rect.width / (clientWidth + borderHor);
21715 var unscaledW = clientWidth - paddingHor;
21716 var unscaledH = clientHeight - paddingVer;
21717 var left = rect.left + padding.left + border.left;
21718 var top = rect.top + padding.top + border.top;
21719 return this.containerBB = [left, top, unscaledW, unscaledH, scale];
21720};
21721
21722BRp$e.invalidateContainerClientCoordsCache = function () {
21723 this.containerBB = null;
21724};
21725
21726BRp$e.findNearestElement = function (x, y, interactiveElementsOnly, isTouch) {
21727 return this.findNearestElements(x, y, interactiveElementsOnly, isTouch)[0];
21728};
21729
21730BRp$e.findNearestElements = function (x, y, interactiveElementsOnly, isTouch) {
21731 var self = this;
21732 var r = this;
21733 var eles = r.getCachedZSortedEles();
21734 var near = []; // 1 node max, 1 edge max
21735
21736 var zoom = r.cy.zoom();
21737 var hasCompounds = r.cy.hasCompoundNodes();
21738 var edgeThreshold = (isTouch ? 24 : 8) / zoom;
21739 var nodeThreshold = (isTouch ? 8 : 2) / zoom;
21740 var labelThreshold = (isTouch ? 8 : 2) / zoom;
21741 var minSqDist = Infinity;
21742 var nearEdge;
21743 var nearNode;
21744
21745 if (interactiveElementsOnly) {
21746 eles = eles.interactive;
21747 }
21748
21749 function addEle(ele, sqDist) {
21750 if (ele.isNode()) {
21751 if (nearNode) {
21752 return; // can't replace node
21753 } else {
21754 nearNode = ele;
21755 near.push(ele);
21756 }
21757 }
21758
21759 if (ele.isEdge() && (sqDist == null || sqDist < minSqDist)) {
21760 if (nearEdge) {
21761 // then replace existing edge
21762 // can replace only if same z-index
21763 if (nearEdge.pstyle('z-compound-depth').value === ele.pstyle('z-compound-depth').value && nearEdge.pstyle('z-compound-depth').value === ele.pstyle('z-compound-depth').value) {
21764 for (var i = 0; i < near.length; i++) {
21765 if (near[i].isEdge()) {
21766 near[i] = ele;
21767 nearEdge = ele;
21768 minSqDist = sqDist != null ? sqDist : minSqDist;
21769 break;
21770 }
21771 }
21772 }
21773 } else {
21774 near.push(ele);
21775 nearEdge = ele;
21776 minSqDist = sqDist != null ? sqDist : minSqDist;
21777 }
21778 }
21779 }
21780
21781 function checkNode(node) {
21782 var width = node.outerWidth() + 2 * nodeThreshold;
21783 var height = node.outerHeight() + 2 * nodeThreshold;
21784 var hw = width / 2;
21785 var hh = height / 2;
21786 var pos = node.position();
21787
21788 if (pos.x - hw <= x && x <= pos.x + hw // bb check x
21789 && pos.y - hh <= y && y <= pos.y + hh // bb check y
21790 ) {
21791 var shape = r.nodeShapes[self.getNodeShape(node)];
21792
21793 if (shape.checkPoint(x, y, 0, width, height, pos.x, pos.y)) {
21794 addEle(node, 0);
21795 return true;
21796 }
21797 }
21798 }
21799
21800 function checkEdge(edge) {
21801 var _p = edge._private;
21802 var rs = _p.rscratch;
21803 var styleWidth = edge.pstyle('width').pfValue;
21804 var scale = edge.pstyle('arrow-scale').value;
21805 var width = styleWidth / 2 + edgeThreshold; // more like a distance radius from centre
21806
21807 var widthSq = width * width;
21808 var width2 = width * 2;
21809 var src = _p.source;
21810 var tgt = _p.target;
21811 var sqDist;
21812
21813 if (rs.edgeType === 'segments' || rs.edgeType === 'straight' || rs.edgeType === 'haystack') {
21814 var pts = rs.allpts;
21815
21816 for (var i = 0; i + 3 < pts.length; i += 2) {
21817 if (inLineVicinity(x, y, pts[i], pts[i + 1], pts[i + 2], pts[i + 3], width2) && widthSq > (sqDist = sqdistToFiniteLine(x, y, pts[i], pts[i + 1], pts[i + 2], pts[i + 3]))) {
21818 addEle(edge, sqDist);
21819 return true;
21820 }
21821 }
21822 } else if (rs.edgeType === 'bezier' || rs.edgeType === 'multibezier' || rs.edgeType === 'self' || rs.edgeType === 'compound') {
21823 var pts = rs.allpts;
21824
21825 for (var i = 0; i + 5 < rs.allpts.length; i += 4) {
21826 if (inBezierVicinity(x, y, pts[i], pts[i + 1], pts[i + 2], pts[i + 3], pts[i + 4], pts[i + 5], width2) && widthSq > (sqDist = sqdistToQuadraticBezier(x, y, pts[i], pts[i + 1], pts[i + 2], pts[i + 3], pts[i + 4], pts[i + 5]))) {
21827 addEle(edge, sqDist);
21828 return true;
21829 }
21830 }
21831 } // if we're close to the edge but didn't hit it, maybe we hit its arrows
21832
21833
21834 var src = src || _p.source;
21835 var tgt = tgt || _p.target;
21836 var arSize = self.getArrowWidth(styleWidth, scale);
21837 var arrows = [{
21838 name: 'source',
21839 x: rs.arrowStartX,
21840 y: rs.arrowStartY,
21841 angle: rs.srcArrowAngle
21842 }, {
21843 name: 'target',
21844 x: rs.arrowEndX,
21845 y: rs.arrowEndY,
21846 angle: rs.tgtArrowAngle
21847 }, {
21848 name: 'mid-source',
21849 x: rs.midX,
21850 y: rs.midY,
21851 angle: rs.midsrcArrowAngle
21852 }, {
21853 name: 'mid-target',
21854 x: rs.midX,
21855 y: rs.midY,
21856 angle: rs.midtgtArrowAngle
21857 }];
21858
21859 for (var i = 0; i < arrows.length; i++) {
21860 var ar = arrows[i];
21861 var shape = r.arrowShapes[edge.pstyle(ar.name + '-arrow-shape').value];
21862 var edgeWidth = edge.pstyle('width').pfValue;
21863
21864 if (shape.roughCollide(x, y, arSize, ar.angle, {
21865 x: ar.x,
21866 y: ar.y
21867 }, edgeWidth, edgeThreshold) && shape.collide(x, y, arSize, ar.angle, {
21868 x: ar.x,
21869 y: ar.y
21870 }, edgeWidth, edgeThreshold)) {
21871 addEle(edge);
21872 return true;
21873 }
21874 } // for compound graphs, hitting edge may actually want a connected node instead (b/c edge may have greater z-index precedence)
21875
21876
21877 if (hasCompounds && near.length > 0) {
21878 checkNode(src);
21879 checkNode(tgt);
21880 }
21881 }
21882
21883 function preprop(obj, name, pre) {
21884 return getPrefixedProperty(obj, name, pre);
21885 }
21886
21887 function checkLabel(ele, prefix) {
21888 var _p = ele._private;
21889 var th = labelThreshold;
21890 var prefixDash;
21891
21892 if (prefix) {
21893 prefixDash = prefix + '-';
21894 } else {
21895 prefixDash = '';
21896 }
21897
21898 ele.boundingBox();
21899 var bb = _p.labelBounds[prefix || 'main'];
21900 var text = ele.pstyle(prefixDash + 'label').value;
21901 var eventsEnabled = ele.pstyle('text-events').strValue === 'yes';
21902
21903 if (!eventsEnabled || !text) {
21904 return;
21905 }
21906
21907 var lx = preprop(_p.rscratch, 'labelX', prefix);
21908 var ly = preprop(_p.rscratch, 'labelY', prefix);
21909 var theta = preprop(_p.rscratch, 'labelAngle', prefix);
21910 var ox = ele.pstyle(prefixDash + 'text-margin-x').pfValue;
21911 var oy = ele.pstyle(prefixDash + 'text-margin-y').pfValue;
21912 var lx1 = bb.x1 - th - ox; // (-ox, -oy) as bb already includes margin
21913
21914 var lx2 = bb.x2 + th - ox; // and rotation is about (lx, ly)
21915
21916 var ly1 = bb.y1 - th - oy;
21917 var ly2 = bb.y2 + th - oy;
21918
21919 if (theta) {
21920 var cos = Math.cos(theta);
21921 var sin = Math.sin(theta);
21922
21923 var rotate = function rotate(x, y) {
21924 x = x - lx;
21925 y = y - ly;
21926 return {
21927 x: x * cos - y * sin + lx,
21928 y: x * sin + y * cos + ly
21929 };
21930 };
21931
21932 var px1y1 = rotate(lx1, ly1);
21933 var px1y2 = rotate(lx1, ly2);
21934 var px2y1 = rotate(lx2, ly1);
21935 var px2y2 = rotate(lx2, ly2);
21936 var points = [// with the margin added after the rotation is applied
21937 px1y1.x + ox, px1y1.y + oy, px2y1.x + ox, px2y1.y + oy, px2y2.x + ox, px2y2.y + oy, px1y2.x + ox, px1y2.y + oy];
21938
21939 if (pointInsidePolygonPoints(x, y, points)) {
21940 addEle(ele);
21941 return true;
21942 }
21943 } else {
21944 // do a cheaper bb check
21945 if (inBoundingBox(bb, x, y)) {
21946 addEle(ele);
21947 return true;
21948 }
21949 }
21950 }
21951
21952 for (var i = eles.length - 1; i >= 0; i--) {
21953 // reverse order for precedence
21954 var ele = eles[i];
21955
21956 if (ele.isNode()) {
21957 checkNode(ele) || checkLabel(ele);
21958 } else {
21959 // then edge
21960 checkEdge(ele) || checkLabel(ele) || checkLabel(ele, 'source') || checkLabel(ele, 'target');
21961 }
21962 }
21963
21964 return near;
21965}; // 'Give me everything from this box'
21966
21967
21968BRp$e.getAllInBox = function (x1, y1, x2, y2) {
21969 var eles = this.getCachedZSortedEles().interactive;
21970 var box = [];
21971 var x1c = Math.min(x1, x2);
21972 var x2c = Math.max(x1, x2);
21973 var y1c = Math.min(y1, y2);
21974 var y2c = Math.max(y1, y2);
21975 x1 = x1c;
21976 x2 = x2c;
21977 y1 = y1c;
21978 y2 = y2c;
21979 var boxBb = makeBoundingBox({
21980 x1: x1,
21981 y1: y1,
21982 x2: x2,
21983 y2: y2
21984 });
21985
21986 for (var e = 0; e < eles.length; e++) {
21987 var ele = eles[e];
21988
21989 if (ele.isNode()) {
21990 var node = ele;
21991 var nodeBb = node.boundingBox({
21992 includeNodes: true,
21993 includeEdges: false,
21994 includeLabels: false
21995 });
21996
21997 if (boundingBoxesIntersect(boxBb, nodeBb) && !boundingBoxInBoundingBox(nodeBb, boxBb)) {
21998 box.push(node);
21999 }
22000 } else {
22001 var edge = ele;
22002 var _p = edge._private;
22003 var rs = _p.rscratch;
22004
22005 if (rs.startX != null && rs.startY != null && !inBoundingBox(boxBb, rs.startX, rs.startY)) {
22006 continue;
22007 }
22008
22009 if (rs.endX != null && rs.endY != null && !inBoundingBox(boxBb, rs.endX, rs.endY)) {
22010 continue;
22011 }
22012
22013 if (rs.edgeType === 'bezier' || rs.edgeType === 'multibezier' || rs.edgeType === 'self' || rs.edgeType === 'compound' || rs.edgeType === 'segments' || rs.edgeType === 'haystack') {
22014 var pts = _p.rstyle.bezierPts || _p.rstyle.linePts || _p.rstyle.haystackPts;
22015 var allInside = true;
22016
22017 for (var i = 0; i < pts.length; i++) {
22018 if (!pointInBoundingBox(boxBb, pts[i])) {
22019 allInside = false;
22020 break;
22021 }
22022 }
22023
22024 if (allInside) {
22025 box.push(edge);
22026 }
22027 } else if (rs.edgeType === 'haystack' || rs.edgeType === 'straight') {
22028 box.push(edge);
22029 }
22030 }
22031 }
22032
22033 return box;
22034};
22035
22036var BRp$d = {};
22037
22038BRp$d.calculateArrowAngles = function (edge) {
22039 var rs = edge._private.rscratch;
22040 var isHaystack = rs.edgeType === 'haystack';
22041 var isBezier = rs.edgeType === 'bezier';
22042 var isMultibezier = rs.edgeType === 'multibezier';
22043 var isSegments = rs.edgeType === 'segments';
22044 var isCompound = rs.edgeType === 'compound';
22045 var isSelf = rs.edgeType === 'self'; // Displacement gives direction for arrowhead orientation
22046
22047 var dispX, dispY;
22048 var startX, startY, endX, endY, midX, midY;
22049
22050 if (isHaystack) {
22051 startX = rs.haystackPts[0];
22052 startY = rs.haystackPts[1];
22053 endX = rs.haystackPts[2];
22054 endY = rs.haystackPts[3];
22055 } else {
22056 startX = rs.arrowStartX;
22057 startY = rs.arrowStartY;
22058 endX = rs.arrowEndX;
22059 endY = rs.arrowEndY;
22060 }
22061
22062 midX = rs.midX;
22063 midY = rs.midY; // source
22064 //
22065
22066 if (isSegments) {
22067 dispX = startX - rs.segpts[0];
22068 dispY = startY - rs.segpts[1];
22069 } else if (isMultibezier || isCompound || isSelf || isBezier) {
22070 var pts = rs.allpts;
22071 var bX = qbezierAt(pts[0], pts[2], pts[4], 0.1);
22072 var bY = qbezierAt(pts[1], pts[3], pts[5], 0.1);
22073 dispX = startX - bX;
22074 dispY = startY - bY;
22075 } else {
22076 dispX = startX - midX;
22077 dispY = startY - midY;
22078 }
22079
22080 rs.srcArrowAngle = getAngleFromDisp(dispX, dispY); // mid target
22081 //
22082
22083 var midX = rs.midX;
22084 var midY = rs.midY;
22085
22086 if (isHaystack) {
22087 midX = (startX + endX) / 2;
22088 midY = (startY + endY) / 2;
22089 }
22090
22091 dispX = endX - startX;
22092 dispY = endY - startY;
22093
22094 if (isSegments) {
22095 var pts = rs.allpts;
22096
22097 if (pts.length / 2 % 2 === 0) {
22098 var i2 = pts.length / 2;
22099 var i1 = i2 - 2;
22100 dispX = pts[i2] - pts[i1];
22101 dispY = pts[i2 + 1] - pts[i1 + 1];
22102 } else {
22103 var i2 = pts.length / 2 - 1;
22104 var i1 = i2 - 2;
22105 var i3 = i2 + 2;
22106 dispX = pts[i2] - pts[i1];
22107 dispY = pts[i2 + 1] - pts[i1 + 1];
22108 }
22109 } else if (isMultibezier || isCompound || isSelf) {
22110 var pts = rs.allpts;
22111 var cpts = rs.ctrlpts;
22112 var bp0x, bp0y;
22113 var bp1x, bp1y;
22114
22115 if (cpts.length / 2 % 2 === 0) {
22116 var p0 = pts.length / 2 - 1; // startpt
22117
22118 var ic = p0 + 2;
22119 var p1 = ic + 2;
22120 bp0x = qbezierAt(pts[p0], pts[ic], pts[p1], 0.0);
22121 bp0y = qbezierAt(pts[p0 + 1], pts[ic + 1], pts[p1 + 1], 0.0);
22122 bp1x = qbezierAt(pts[p0], pts[ic], pts[p1], 0.0001);
22123 bp1y = qbezierAt(pts[p0 + 1], pts[ic + 1], pts[p1 + 1], 0.0001);
22124 } else {
22125 var ic = pts.length / 2 - 1; // ctrpt
22126
22127 var p0 = ic - 2; // startpt
22128
22129 var p1 = ic + 2; // endpt
22130
22131 bp0x = qbezierAt(pts[p0], pts[ic], pts[p1], 0.4999);
22132 bp0y = qbezierAt(pts[p0 + 1], pts[ic + 1], pts[p1 + 1], 0.4999);
22133 bp1x = qbezierAt(pts[p0], pts[ic], pts[p1], 0.5);
22134 bp1y = qbezierAt(pts[p0 + 1], pts[ic + 1], pts[p1 + 1], 0.5);
22135 }
22136
22137 dispX = bp1x - bp0x;
22138 dispY = bp1y - bp0y;
22139 }
22140
22141 rs.midtgtArrowAngle = getAngleFromDisp(dispX, dispY);
22142 rs.midDispX = dispX;
22143 rs.midDispY = dispY; // mid source
22144 //
22145
22146 dispX *= -1;
22147 dispY *= -1;
22148
22149 if (isSegments) {
22150 var pts = rs.allpts;
22151
22152 if (pts.length / 2 % 2 === 0) ; else {
22153 var i2 = pts.length / 2 - 1;
22154 var i3 = i2 + 2;
22155 dispX = -(pts[i3] - pts[i2]);
22156 dispY = -(pts[i3 + 1] - pts[i2 + 1]);
22157 }
22158 }
22159
22160 rs.midsrcArrowAngle = getAngleFromDisp(dispX, dispY); // target
22161 //
22162
22163 if (isSegments) {
22164 dispX = endX - rs.segpts[rs.segpts.length - 2];
22165 dispY = endY - rs.segpts[rs.segpts.length - 1];
22166 } else if (isMultibezier || isCompound || isSelf || isBezier) {
22167 var pts = rs.allpts;
22168 var l = pts.length;
22169 var bX = qbezierAt(pts[l - 6], pts[l - 4], pts[l - 2], 0.9);
22170 var bY = qbezierAt(pts[l - 5], pts[l - 3], pts[l - 1], 0.9);
22171 dispX = endX - bX;
22172 dispY = endY - bY;
22173 } else {
22174 dispX = endX - midX;
22175 dispY = endY - midY;
22176 }
22177
22178 rs.tgtArrowAngle = getAngleFromDisp(dispX, dispY);
22179};
22180
22181BRp$d.getArrowWidth = BRp$d.getArrowHeight = function (edgeWidth, scale) {
22182 var cache = this.arrowWidthCache = this.arrowWidthCache || {};
22183 var cachedVal = cache[edgeWidth + ', ' + scale];
22184
22185 if (cachedVal) {
22186 return cachedVal;
22187 }
22188
22189 cachedVal = Math.max(Math.pow(edgeWidth * 13.37, 0.9), 29) * scale;
22190 cache[edgeWidth + ', ' + scale] = cachedVal;
22191 return cachedVal;
22192};
22193
22194var BRp$c = {};
22195
22196BRp$c.findHaystackPoints = function (edges) {
22197 for (var i = 0; i < edges.length; i++) {
22198 var edge = edges[i];
22199 var _p = edge._private;
22200 var rs = _p.rscratch;
22201
22202 if (!rs.haystack) {
22203 var angle = Math.random() * 2 * Math.PI;
22204 rs.source = {
22205 x: Math.cos(angle),
22206 y: Math.sin(angle)
22207 };
22208 angle = Math.random() * 2 * Math.PI;
22209 rs.target = {
22210 x: Math.cos(angle),
22211 y: Math.sin(angle)
22212 };
22213 }
22214
22215 var src = _p.source;
22216 var tgt = _p.target;
22217 var srcPos = src.position();
22218 var tgtPos = tgt.position();
22219 var srcW = src.width();
22220 var tgtW = tgt.width();
22221 var srcH = src.height();
22222 var tgtH = tgt.height();
22223 var radius = edge.pstyle('haystack-radius').value;
22224 var halfRadius = radius / 2; // b/c have to half width/height
22225
22226 rs.haystackPts = rs.allpts = [rs.source.x * srcW * halfRadius + srcPos.x, rs.source.y * srcH * halfRadius + srcPos.y, rs.target.x * tgtW * halfRadius + tgtPos.x, rs.target.y * tgtH * halfRadius + tgtPos.y];
22227 rs.midX = (rs.allpts[0] + rs.allpts[2]) / 2;
22228 rs.midY = (rs.allpts[1] + rs.allpts[3]) / 2; // always override as haystack in case set to different type previously
22229
22230 rs.edgeType = 'haystack';
22231 rs.haystack = true;
22232 this.storeEdgeProjections(edge);
22233 this.calculateArrowAngles(edge);
22234 this.recalculateEdgeLabelProjections(edge);
22235 this.calculateLabelAngles(edge);
22236 }
22237};
22238
22239BRp$c.findSegmentsPoints = function (edge, pairInfo) {
22240 // Segments (multiple straight lines)
22241 var rs = edge._private.rscratch;
22242 var posPts = pairInfo.posPts,
22243 intersectionPts = pairInfo.intersectionPts,
22244 vectorNormInverse = pairInfo.vectorNormInverse;
22245 var edgeDistances = edge.pstyle('edge-distances').value;
22246 var segmentWs = edge.pstyle('segment-weights');
22247 var segmentDs = edge.pstyle('segment-distances');
22248 var segmentsN = Math.min(segmentWs.pfValue.length, segmentDs.pfValue.length);
22249 rs.edgeType = 'segments';
22250 rs.segpts = [];
22251
22252 for (var s = 0; s < segmentsN; s++) {
22253 var w = segmentWs.pfValue[s];
22254 var d = segmentDs.pfValue[s];
22255 var w1 = 1 - w;
22256 var w2 = w;
22257 var midptPts = edgeDistances === 'node-position' ? posPts : intersectionPts;
22258 var adjustedMidpt = {
22259 x: midptPts.x1 * w1 + midptPts.x2 * w2,
22260 y: midptPts.y1 * w1 + midptPts.y2 * w2
22261 };
22262 rs.segpts.push(adjustedMidpt.x + vectorNormInverse.x * d, adjustedMidpt.y + vectorNormInverse.y * d);
22263 }
22264};
22265
22266BRp$c.findLoopPoints = function (edge, pairInfo, i, edgeIsUnbundled) {
22267 // Self-edge
22268 var rs = edge._private.rscratch;
22269 var dirCounts = pairInfo.dirCounts,
22270 srcPos = pairInfo.srcPos;
22271 var ctrlptDists = edge.pstyle('control-point-distances');
22272 var ctrlptDist = ctrlptDists ? ctrlptDists.pfValue[0] : undefined;
22273 var loopDir = edge.pstyle('loop-direction').pfValue;
22274 var loopSwp = edge.pstyle('loop-sweep').pfValue;
22275 var stepSize = edge.pstyle('control-point-step-size').pfValue;
22276 rs.edgeType = 'self';
22277 var j = i;
22278 var loopDist = stepSize;
22279
22280 if (edgeIsUnbundled) {
22281 j = 0;
22282 loopDist = ctrlptDist;
22283 }
22284
22285 var loopAngle = loopDir - Math.PI / 2;
22286 var outAngle = loopAngle - loopSwp / 2;
22287 var inAngle = loopAngle + loopSwp / 2; // increase by step size for overlapping loops, keyed on direction and sweep values
22288
22289 var dc = String(loopDir + '_' + loopSwp);
22290 j = dirCounts[dc] === undefined ? dirCounts[dc] = 0 : ++dirCounts[dc];
22291 rs.ctrlpts = [srcPos.x + Math.cos(outAngle) * 1.4 * loopDist * (j / 3 + 1), srcPos.y + Math.sin(outAngle) * 1.4 * loopDist * (j / 3 + 1), srcPos.x + Math.cos(inAngle) * 1.4 * loopDist * (j / 3 + 1), srcPos.y + Math.sin(inAngle) * 1.4 * loopDist * (j / 3 + 1)];
22292};
22293
22294BRp$c.findCompoundLoopPoints = function (edge, pairInfo, i, edgeIsUnbundled) {
22295 // Compound edge
22296 var rs = edge._private.rscratch;
22297 rs.edgeType = 'compound';
22298 var srcPos = pairInfo.srcPos,
22299 tgtPos = pairInfo.tgtPos,
22300 srcW = pairInfo.srcW,
22301 srcH = pairInfo.srcH,
22302 tgtW = pairInfo.tgtW,
22303 tgtH = pairInfo.tgtH;
22304 var stepSize = edge.pstyle('control-point-step-size').pfValue;
22305 var ctrlptDists = edge.pstyle('control-point-distances');
22306 var ctrlptDist = ctrlptDists ? ctrlptDists.pfValue[0] : undefined;
22307 var j = i;
22308 var loopDist = stepSize;
22309
22310 if (edgeIsUnbundled) {
22311 j = 0;
22312 loopDist = ctrlptDist;
22313 }
22314
22315 var loopW = 50;
22316 var loopaPos = {
22317 x: srcPos.x - srcW / 2,
22318 y: srcPos.y - srcH / 2
22319 };
22320 var loopbPos = {
22321 x: tgtPos.x - tgtW / 2,
22322 y: tgtPos.y - tgtH / 2
22323 };
22324 var loopPos = {
22325 x: Math.min(loopaPos.x, loopbPos.x),
22326 y: Math.min(loopaPos.y, loopbPos.y)
22327 }; // avoids cases with impossible beziers
22328
22329 var minCompoundStretch = 0.5;
22330 var compoundStretchA = Math.max(minCompoundStretch, Math.log(srcW * 0.01));
22331 var compoundStretchB = Math.max(minCompoundStretch, Math.log(tgtW * 0.01));
22332 rs.ctrlpts = [loopPos.x, loopPos.y - (1 + Math.pow(loopW, 1.12) / 100) * loopDist * (j / 3 + 1) * compoundStretchA, loopPos.x - (1 + Math.pow(loopW, 1.12) / 100) * loopDist * (j / 3 + 1) * compoundStretchB, loopPos.y];
22333};
22334
22335BRp$c.findStraightEdgePoints = function (edge) {
22336 // Straight edge within bundle
22337 edge._private.rscratch.edgeType = 'straight';
22338};
22339
22340BRp$c.findBezierPoints = function (edge, pairInfo, i, edgeIsUnbundled, edgeIsSwapped) {
22341 var rs = edge._private.rscratch;
22342 var vectorNormInverse = pairInfo.vectorNormInverse,
22343 posPts = pairInfo.posPts,
22344 intersectionPts = pairInfo.intersectionPts;
22345 var edgeDistances = edge.pstyle('edge-distances').value;
22346 var stepSize = edge.pstyle('control-point-step-size').pfValue;
22347 var ctrlptDists = edge.pstyle('control-point-distances');
22348 var ctrlptWs = edge.pstyle('control-point-weights');
22349 var bezierN = ctrlptDists && ctrlptWs ? Math.min(ctrlptDists.value.length, ctrlptWs.value.length) : 1;
22350 var ctrlptDist = ctrlptDists ? ctrlptDists.pfValue[0] : undefined;
22351 var ctrlptWeight = ctrlptWs.value[0]; // (Multi)bezier
22352
22353 var multi = edgeIsUnbundled;
22354 rs.edgeType = multi ? 'multibezier' : 'bezier';
22355 rs.ctrlpts = [];
22356
22357 for (var b = 0; b < bezierN; b++) {
22358 var normctrlptDist = (0.5 - pairInfo.eles.length / 2 + i) * stepSize * (edgeIsSwapped ? -1 : 1);
22359 var manctrlptDist = void 0;
22360 var sign = signum(normctrlptDist);
22361
22362 if (multi) {
22363 ctrlptDist = ctrlptDists ? ctrlptDists.pfValue[b] : stepSize; // fall back on step size
22364
22365 ctrlptWeight = ctrlptWs.value[b];
22366 }
22367
22368 if (edgeIsUnbundled) {
22369 // multi or single unbundled
22370 manctrlptDist = ctrlptDist;
22371 } else {
22372 manctrlptDist = ctrlptDist !== undefined ? sign * ctrlptDist : undefined;
22373 }
22374
22375 var distanceFromMidpoint = manctrlptDist !== undefined ? manctrlptDist : normctrlptDist;
22376 var w1 = 1 - ctrlptWeight;
22377 var w2 = ctrlptWeight;
22378 var midptPts = edgeDistances === 'node-position' ? posPts : intersectionPts;
22379 var adjustedMidpt = {
22380 x: midptPts.x1 * w1 + midptPts.x2 * w2,
22381 y: midptPts.y1 * w1 + midptPts.y2 * w2
22382 };
22383 rs.ctrlpts.push(adjustedMidpt.x + vectorNormInverse.x * distanceFromMidpoint, adjustedMidpt.y + vectorNormInverse.y * distanceFromMidpoint);
22384 }
22385};
22386
22387BRp$c.findTaxiPoints = function (edge, pairInfo) {
22388 // Taxicab geometry with two turns maximum
22389 var rs = edge._private.rscratch;
22390 rs.edgeType = 'segments';
22391 var VERTICAL = 'vertical';
22392 var HORIZONTAL = 'horizontal';
22393 var LEFTWARD = 'leftward';
22394 var RIGHTWARD = 'rightward';
22395 var DOWNWARD = 'downward';
22396 var UPWARD = 'upward';
22397 var AUTO = 'auto';
22398 var posPts = pairInfo.posPts,
22399 srcW = pairInfo.srcW,
22400 srcH = pairInfo.srcH,
22401 tgtW = pairInfo.tgtW,
22402 tgtH = pairInfo.tgtH;
22403 var edgeDistances = edge.pstyle('edge-distances').value;
22404 var dIncludesNodeBody = edgeDistances !== 'node-position';
22405 var taxiDir = edge.pstyle('taxi-direction').value;
22406 var rawTaxiDir = taxiDir; // unprocessed value
22407
22408 var taxiTurn = edge.pstyle('taxi-turn');
22409 var turnIsPercent = taxiTurn.units === '%';
22410 var taxiTurnPfVal = taxiTurn.pfValue;
22411 var turnIsNegative = taxiTurnPfVal < 0; // i.e. from target side
22412
22413 var minD = edge.pstyle('taxi-turn-min-distance').pfValue;
22414 var dw = dIncludesNodeBody ? (srcW + tgtW) / 2 : 0;
22415 var dh = dIncludesNodeBody ? (srcH + tgtH) / 2 : 0;
22416 var pdx = posPts.x2 - posPts.x1;
22417 var pdy = posPts.y2 - posPts.y1; // take away the effective w/h from the magnitude of the delta value
22418
22419 var subDWH = function subDWH(dxy, dwh) {
22420 if (dxy > 0) {
22421 return Math.max(dxy - dwh, 0);
22422 } else {
22423 return Math.min(dxy + dwh, 0);
22424 }
22425 };
22426
22427 var dx = subDWH(pdx, dw);
22428 var dy = subDWH(pdy, dh);
22429 var isExplicitDir = false;
22430
22431 if (rawTaxiDir === AUTO) {
22432 taxiDir = Math.abs(dx) > Math.abs(dy) ? HORIZONTAL : VERTICAL;
22433 } else if (rawTaxiDir === UPWARD || rawTaxiDir === DOWNWARD) {
22434 taxiDir = VERTICAL;
22435 isExplicitDir = true;
22436 } else if (rawTaxiDir === LEFTWARD || rawTaxiDir === RIGHTWARD) {
22437 taxiDir = HORIZONTAL;
22438 isExplicitDir = true;
22439 }
22440
22441 var isVert = taxiDir === VERTICAL;
22442 var l = isVert ? dy : dx;
22443 var pl = isVert ? pdy : pdx;
22444 var sgnL = signum(pl);
22445 var forcedDir = false;
22446
22447 if (!(isExplicitDir && (turnIsPercent || turnIsNegative)) // forcing in this case would cause weird growing in the opposite direction
22448 && (rawTaxiDir === DOWNWARD && pl < 0 || rawTaxiDir === UPWARD && pl > 0 || rawTaxiDir === LEFTWARD && pl > 0 || rawTaxiDir === RIGHTWARD && pl < 0)) {
22449 sgnL *= -1;
22450 l = sgnL * Math.abs(l);
22451 forcedDir = true;
22452 }
22453
22454 var d;
22455
22456 if (turnIsPercent) {
22457 var p = taxiTurnPfVal < 0 ? 1 + taxiTurnPfVal : taxiTurnPfVal;
22458 d = p * l;
22459 } else {
22460 var k = taxiTurnPfVal < 0 ? l : 0;
22461 d = k + taxiTurnPfVal * sgnL;
22462 }
22463
22464 var getIsTooClose = function getIsTooClose(d) {
22465 return Math.abs(d) < minD || Math.abs(d) >= Math.abs(l);
22466 };
22467
22468 var isTooCloseSrc = getIsTooClose(d);
22469 var isTooCloseTgt = getIsTooClose(Math.abs(l) - Math.abs(d));
22470 var isTooClose = isTooCloseSrc || isTooCloseTgt;
22471
22472 if (isTooClose && !forcedDir) {
22473 // non-ideal routing
22474 if (isVert) {
22475 // vertical fallbacks
22476 var lShapeInsideSrc = Math.abs(pl) <= srcH / 2;
22477 var lShapeInsideTgt = Math.abs(pdx) <= tgtW / 2;
22478
22479 if (lShapeInsideSrc) {
22480 // horizontal Z-shape (direction not respected)
22481 var x = (posPts.x1 + posPts.x2) / 2;
22482 var y1 = posPts.y1,
22483 y2 = posPts.y2;
22484 rs.segpts = [x, y1, x, y2];
22485 } else if (lShapeInsideTgt) {
22486 // vertical Z-shape (distance not respected)
22487 var y = (posPts.y1 + posPts.y2) / 2;
22488 var x1 = posPts.x1,
22489 x2 = posPts.x2;
22490 rs.segpts = [x1, y, x2, y];
22491 } else {
22492 // L-shape fallback (turn distance not respected, but works well with tree siblings)
22493 rs.segpts = [posPts.x1, posPts.y2];
22494 }
22495 } else {
22496 // horizontal fallbacks
22497 var _lShapeInsideSrc = Math.abs(pl) <= srcW / 2;
22498
22499 var _lShapeInsideTgt = Math.abs(pdy) <= tgtH / 2;
22500
22501 if (_lShapeInsideSrc) {
22502 // vertical Z-shape (direction not respected)
22503 var _y = (posPts.y1 + posPts.y2) / 2;
22504
22505 var _x = posPts.x1,
22506 _x2 = posPts.x2;
22507 rs.segpts = [_x, _y, _x2, _y];
22508 } else if (_lShapeInsideTgt) {
22509 // horizontal Z-shape (turn distance not respected)
22510 var _x3 = (posPts.x1 + posPts.x2) / 2;
22511
22512 var _y2 = posPts.y1,
22513 _y3 = posPts.y2;
22514 rs.segpts = [_x3, _y2, _x3, _y3];
22515 } else {
22516 // L-shape (turn distance not respected, but works well for tree siblings)
22517 rs.segpts = [posPts.x2, posPts.y1];
22518 }
22519 }
22520 } else {
22521 // ideal routing
22522 if (isVert) {
22523 var _y4 = posPts.y1 + d + (dIncludesNodeBody ? srcH / 2 * sgnL : 0);
22524
22525 var _x4 = posPts.x1,
22526 _x5 = posPts.x2;
22527 rs.segpts = [_x4, _y4, _x5, _y4];
22528 } else {
22529 // horizontal
22530 var _x6 = posPts.x1 + d + (dIncludesNodeBody ? srcW / 2 * sgnL : 0);
22531
22532 var _y5 = posPts.y1,
22533 _y6 = posPts.y2;
22534 rs.segpts = [_x6, _y5, _x6, _y6];
22535 }
22536 }
22537};
22538
22539BRp$c.tryToCorrectInvalidPoints = function (edge, pairInfo) {
22540 var rs = edge._private.rscratch; // can only correct beziers for now...
22541
22542 if (rs.edgeType === 'bezier') {
22543 var srcPos = pairInfo.srcPos,
22544 tgtPos = pairInfo.tgtPos,
22545 srcW = pairInfo.srcW,
22546 srcH = pairInfo.srcH,
22547 tgtW = pairInfo.tgtW,
22548 tgtH = pairInfo.tgtH,
22549 srcShape = pairInfo.srcShape,
22550 tgtShape = pairInfo.tgtShape;
22551 var badStart = !number$1(rs.startX) || !number$1(rs.startY);
22552 var badAStart = !number$1(rs.arrowStartX) || !number$1(rs.arrowStartY);
22553 var badEnd = !number$1(rs.endX) || !number$1(rs.endY);
22554 var badAEnd = !number$1(rs.arrowEndX) || !number$1(rs.arrowEndY);
22555 var minCpADistFactor = 3;
22556 var arrowW = this.getArrowWidth(edge.pstyle('width').pfValue, edge.pstyle('arrow-scale').value) * this.arrowShapeWidth;
22557 var minCpADist = minCpADistFactor * arrowW;
22558 var startACpDist = dist({
22559 x: rs.ctrlpts[0],
22560 y: rs.ctrlpts[1]
22561 }, {
22562 x: rs.startX,
22563 y: rs.startY
22564 });
22565 var closeStartACp = startACpDist < minCpADist;
22566 var endACpDist = dist({
22567 x: rs.ctrlpts[0],
22568 y: rs.ctrlpts[1]
22569 }, {
22570 x: rs.endX,
22571 y: rs.endY
22572 });
22573 var closeEndACp = endACpDist < minCpADist;
22574 var overlapping = false;
22575
22576 if (badStart || badAStart || closeStartACp) {
22577 overlapping = true; // project control point along line from src centre to outside the src shape
22578 // (otherwise intersection will yield nothing)
22579
22580 var cpD = {
22581 // delta
22582 x: rs.ctrlpts[0] - srcPos.x,
22583 y: rs.ctrlpts[1] - srcPos.y
22584 };
22585 var cpL = Math.sqrt(cpD.x * cpD.x + cpD.y * cpD.y); // length of line
22586
22587 var cpM = {
22588 // normalised delta
22589 x: cpD.x / cpL,
22590 y: cpD.y / cpL
22591 };
22592 var radius = Math.max(srcW, srcH);
22593 var cpProj = {
22594 // *2 radius guarantees outside shape
22595 x: rs.ctrlpts[0] + cpM.x * 2 * radius,
22596 y: rs.ctrlpts[1] + cpM.y * 2 * radius
22597 };
22598 var srcCtrlPtIntn = srcShape.intersectLine(srcPos.x, srcPos.y, srcW, srcH, cpProj.x, cpProj.y, 0);
22599
22600 if (closeStartACp) {
22601 rs.ctrlpts[0] = rs.ctrlpts[0] + cpM.x * (minCpADist - startACpDist);
22602 rs.ctrlpts[1] = rs.ctrlpts[1] + cpM.y * (minCpADist - startACpDist);
22603 } else {
22604 rs.ctrlpts[0] = srcCtrlPtIntn[0] + cpM.x * minCpADist;
22605 rs.ctrlpts[1] = srcCtrlPtIntn[1] + cpM.y * minCpADist;
22606 }
22607 }
22608
22609 if (badEnd || badAEnd || closeEndACp) {
22610 overlapping = true; // project control point along line from tgt centre to outside the tgt shape
22611 // (otherwise intersection will yield nothing)
22612
22613 var _cpD = {
22614 // delta
22615 x: rs.ctrlpts[0] - tgtPos.x,
22616 y: rs.ctrlpts[1] - tgtPos.y
22617 };
22618
22619 var _cpL = Math.sqrt(_cpD.x * _cpD.x + _cpD.y * _cpD.y); // length of line
22620
22621
22622 var _cpM = {
22623 // normalised delta
22624 x: _cpD.x / _cpL,
22625 y: _cpD.y / _cpL
22626 };
22627
22628 var _radius = Math.max(srcW, srcH);
22629
22630 var _cpProj = {
22631 // *2 radius guarantees outside shape
22632 x: rs.ctrlpts[0] + _cpM.x * 2 * _radius,
22633 y: rs.ctrlpts[1] + _cpM.y * 2 * _radius
22634 };
22635 var tgtCtrlPtIntn = tgtShape.intersectLine(tgtPos.x, tgtPos.y, tgtW, tgtH, _cpProj.x, _cpProj.y, 0);
22636
22637 if (closeEndACp) {
22638 rs.ctrlpts[0] = rs.ctrlpts[0] + _cpM.x * (minCpADist - endACpDist);
22639 rs.ctrlpts[1] = rs.ctrlpts[1] + _cpM.y * (minCpADist - endACpDist);
22640 } else {
22641 rs.ctrlpts[0] = tgtCtrlPtIntn[0] + _cpM.x * minCpADist;
22642 rs.ctrlpts[1] = tgtCtrlPtIntn[1] + _cpM.y * minCpADist;
22643 }
22644 }
22645
22646 if (overlapping) {
22647 // recalc endpts
22648 this.findEndpoints(edge);
22649 }
22650 }
22651};
22652
22653BRp$c.storeAllpts = function (edge) {
22654 var rs = edge._private.rscratch;
22655
22656 if (rs.edgeType === 'multibezier' || rs.edgeType === 'bezier' || rs.edgeType === 'self' || rs.edgeType === 'compound') {
22657 rs.allpts = [];
22658 rs.allpts.push(rs.startX, rs.startY);
22659
22660 for (var b = 0; b + 1 < rs.ctrlpts.length; b += 2) {
22661 // ctrl pt itself
22662 rs.allpts.push(rs.ctrlpts[b], rs.ctrlpts[b + 1]); // the midpt between ctrlpts as intermediate destination pts
22663
22664 if (b + 3 < rs.ctrlpts.length) {
22665 rs.allpts.push((rs.ctrlpts[b] + rs.ctrlpts[b + 2]) / 2, (rs.ctrlpts[b + 1] + rs.ctrlpts[b + 3]) / 2);
22666 }
22667 }
22668
22669 rs.allpts.push(rs.endX, rs.endY);
22670 var m, mt;
22671
22672 if (rs.ctrlpts.length / 2 % 2 === 0) {
22673 m = rs.allpts.length / 2 - 1;
22674 rs.midX = rs.allpts[m];
22675 rs.midY = rs.allpts[m + 1];
22676 } else {
22677 m = rs.allpts.length / 2 - 3;
22678 mt = 0.5;
22679 rs.midX = qbezierAt(rs.allpts[m], rs.allpts[m + 2], rs.allpts[m + 4], mt);
22680 rs.midY = qbezierAt(rs.allpts[m + 1], rs.allpts[m + 3], rs.allpts[m + 5], mt);
22681 }
22682 } else if (rs.edgeType === 'straight') {
22683 // need to calc these after endpts
22684 rs.allpts = [rs.startX, rs.startY, rs.endX, rs.endY]; // default midpt for labels etc
22685
22686 rs.midX = (rs.startX + rs.endX + rs.arrowStartX + rs.arrowEndX) / 4;
22687 rs.midY = (rs.startY + rs.endY + rs.arrowStartY + rs.arrowEndY) / 4;
22688 } else if (rs.edgeType === 'segments') {
22689 rs.allpts = [];
22690 rs.allpts.push(rs.startX, rs.startY);
22691 rs.allpts.push.apply(rs.allpts, rs.segpts);
22692 rs.allpts.push(rs.endX, rs.endY);
22693
22694 if (rs.segpts.length % 4 === 0) {
22695 var i2 = rs.segpts.length / 2;
22696 var i1 = i2 - 2;
22697 rs.midX = (rs.segpts[i1] + rs.segpts[i2]) / 2;
22698 rs.midY = (rs.segpts[i1 + 1] + rs.segpts[i2 + 1]) / 2;
22699 } else {
22700 var _i = rs.segpts.length / 2 - 1;
22701
22702 rs.midX = rs.segpts[_i];
22703 rs.midY = rs.segpts[_i + 1];
22704 }
22705 }
22706};
22707
22708BRp$c.checkForInvalidEdgeWarning = function (edge) {
22709 var rs = edge[0]._private.rscratch;
22710
22711 if (rs.nodesOverlap || number$1(rs.startX) && number$1(rs.startY) && number$1(rs.endX) && number$1(rs.endY)) {
22712 rs.loggedErr = false;
22713 } else {
22714 if (!rs.loggedErr) {
22715 rs.loggedErr = true;
22716 warn('Edge `' + edge.id() + '` has invalid endpoints and so it is impossible to draw. Adjust your edge style (e.g. control points) accordingly or use an alternative edge type. This is expected behaviour when the source node and the target node overlap.');
22717 }
22718 }
22719};
22720
22721BRp$c.findEdgeControlPoints = function (edges) {
22722 var _this = this;
22723
22724 if (!edges || edges.length === 0) {
22725 return;
22726 }
22727
22728 var r = this;
22729 var cy = r.cy;
22730 var hasCompounds = cy.hasCompoundNodes();
22731 var hashTable = {
22732 map: new Map$1(),
22733 get: function get(pairId) {
22734 var map2 = this.map.get(pairId[0]);
22735
22736 if (map2 != null) {
22737 return map2.get(pairId[1]);
22738 } else {
22739 return null;
22740 }
22741 },
22742 set: function set(pairId, val) {
22743 var map2 = this.map.get(pairId[0]);
22744
22745 if (map2 == null) {
22746 map2 = new Map$1();
22747 this.map.set(pairId[0], map2);
22748 }
22749
22750 map2.set(pairId[1], val);
22751 }
22752 };
22753 var pairIds = [];
22754 var haystackEdges = []; // create a table of edge (src, tgt) => list of edges between them
22755
22756 for (var i = 0; i < edges.length; i++) {
22757 var edge = edges[i];
22758 var _p = edge._private;
22759 var curveStyle = edge.pstyle('curve-style').value; // ignore edges who are not to be displayed
22760 // they shouldn't take up space
22761
22762 if (edge.removed() || !edge.takesUpSpace()) {
22763 continue;
22764 }
22765
22766 if (curveStyle === 'haystack') {
22767 haystackEdges.push(edge);
22768 continue;
22769 }
22770
22771 var edgeIsUnbundled = curveStyle === 'unbundled-bezier' || curveStyle === 'segments' || curveStyle === 'straight' || curveStyle === 'straight-triangle' || curveStyle === 'taxi';
22772 var edgeIsBezier = curveStyle === 'unbundled-bezier' || curveStyle === 'bezier';
22773 var src = _p.source;
22774 var tgt = _p.target;
22775 var srcIndex = src.poolIndex();
22776 var tgtIndex = tgt.poolIndex();
22777 var pairId = [srcIndex, tgtIndex].sort();
22778 var tableEntry = hashTable.get(pairId);
22779
22780 if (tableEntry == null) {
22781 tableEntry = {
22782 eles: []
22783 };
22784 hashTable.set(pairId, tableEntry);
22785 pairIds.push(pairId);
22786 }
22787
22788 tableEntry.eles.push(edge);
22789
22790 if (edgeIsUnbundled) {
22791 tableEntry.hasUnbundled = true;
22792 }
22793
22794 if (edgeIsBezier) {
22795 tableEntry.hasBezier = true;
22796 }
22797 } // for each pair (src, tgt), create the ctrl pts
22798 // Nested for loop is OK; total number of iterations for both loops = edgeCount
22799
22800
22801 var _loop = function _loop(p) {
22802 var pairId = pairIds[p];
22803 var pairInfo = hashTable.get(pairId);
22804 var swappedpairInfo = void 0;
22805
22806 if (!pairInfo.hasUnbundled) {
22807 var pllEdges = pairInfo.eles[0].parallelEdges().filter(function (e) {
22808 return e.isBundledBezier();
22809 });
22810 clearArray(pairInfo.eles);
22811 pllEdges.forEach(function (edge) {
22812 return pairInfo.eles.push(edge);
22813 }); // for each pair id, the edges should be sorted by index
22814
22815 pairInfo.eles.sort(function (edge1, edge2) {
22816 return edge1.poolIndex() - edge2.poolIndex();
22817 });
22818 }
22819
22820 var firstEdge = pairInfo.eles[0];
22821 var src = firstEdge.source();
22822 var tgt = firstEdge.target(); // make sure src/tgt distinction is consistent w.r.t. pairId
22823
22824 if (src.poolIndex() > tgt.poolIndex()) {
22825 var temp = src;
22826 src = tgt;
22827 tgt = temp;
22828 }
22829
22830 var srcPos = pairInfo.srcPos = src.position();
22831 var tgtPos = pairInfo.tgtPos = tgt.position();
22832 var srcW = pairInfo.srcW = src.outerWidth();
22833 var srcH = pairInfo.srcH = src.outerHeight();
22834 var tgtW = pairInfo.tgtW = tgt.outerWidth();
22835 var tgtH = pairInfo.tgtH = tgt.outerHeight();
22836
22837 var srcShape = pairInfo.srcShape = r.nodeShapes[_this.getNodeShape(src)];
22838
22839 var tgtShape = pairInfo.tgtShape = r.nodeShapes[_this.getNodeShape(tgt)];
22840
22841 pairInfo.dirCounts = {
22842 'north': 0,
22843 'west': 0,
22844 'south': 0,
22845 'east': 0,
22846 'northwest': 0,
22847 'southwest': 0,
22848 'northeast': 0,
22849 'southeast': 0
22850 };
22851
22852 for (var _i2 = 0; _i2 < pairInfo.eles.length; _i2++) {
22853 var _edge = pairInfo.eles[_i2];
22854 var rs = _edge[0]._private.rscratch;
22855
22856 var _curveStyle = _edge.pstyle('curve-style').value;
22857
22858 var _edgeIsUnbundled = _curveStyle === 'unbundled-bezier' || _curveStyle === 'segments' || _curveStyle === 'taxi'; // whether the normalised pair order is the reverse of the edge's src-tgt order
22859
22860
22861 var edgeIsSwapped = !src.same(_edge.source());
22862
22863 if (!pairInfo.calculatedIntersection && src !== tgt && (pairInfo.hasBezier || pairInfo.hasUnbundled)) {
22864 pairInfo.calculatedIntersection = true; // pt outside src shape to calc distance/displacement from src to tgt
22865
22866 var srcOutside = srcShape.intersectLine(srcPos.x, srcPos.y, srcW, srcH, tgtPos.x, tgtPos.y, 0);
22867 var srcIntn = pairInfo.srcIntn = srcOutside; // pt outside tgt shape to calc distance/displacement from src to tgt
22868
22869 var tgtOutside = tgtShape.intersectLine(tgtPos.x, tgtPos.y, tgtW, tgtH, srcPos.x, srcPos.y, 0);
22870 var tgtIntn = pairInfo.tgtIntn = tgtOutside;
22871 var intersectionPts = pairInfo.intersectionPts = {
22872 x1: srcOutside[0],
22873 x2: tgtOutside[0],
22874 y1: srcOutside[1],
22875 y2: tgtOutside[1]
22876 };
22877 var posPts = pairInfo.posPts = {
22878 x1: srcPos.x,
22879 x2: tgtPos.x,
22880 y1: srcPos.y,
22881 y2: tgtPos.y
22882 };
22883 var dy = tgtOutside[1] - srcOutside[1];
22884 var dx = tgtOutside[0] - srcOutside[0];
22885 var l = Math.sqrt(dx * dx + dy * dy);
22886 var vector = pairInfo.vector = {
22887 x: dx,
22888 y: dy
22889 };
22890 var vectorNorm = pairInfo.vectorNorm = {
22891 x: vector.x / l,
22892 y: vector.y / l
22893 };
22894 var vectorNormInverse = {
22895 x: -vectorNorm.y,
22896 y: vectorNorm.x
22897 }; // if node shapes overlap, then no ctrl pts to draw
22898
22899 pairInfo.nodesOverlap = !number$1(l) || tgtShape.checkPoint(srcOutside[0], srcOutside[1], 0, tgtW, tgtH, tgtPos.x, tgtPos.y) || srcShape.checkPoint(tgtOutside[0], tgtOutside[1], 0, srcW, srcH, srcPos.x, srcPos.y);
22900 pairInfo.vectorNormInverse = vectorNormInverse;
22901 swappedpairInfo = {
22902 nodesOverlap: pairInfo.nodesOverlap,
22903 dirCounts: pairInfo.dirCounts,
22904 calculatedIntersection: true,
22905 hasBezier: pairInfo.hasBezier,
22906 hasUnbundled: pairInfo.hasUnbundled,
22907 eles: pairInfo.eles,
22908 srcPos: tgtPos,
22909 tgtPos: srcPos,
22910 srcW: tgtW,
22911 srcH: tgtH,
22912 tgtW: srcW,
22913 tgtH: srcH,
22914 srcIntn: tgtIntn,
22915 tgtIntn: srcIntn,
22916 srcShape: tgtShape,
22917 tgtShape: srcShape,
22918 posPts: {
22919 x1: posPts.x2,
22920 y1: posPts.y2,
22921 x2: posPts.x1,
22922 y2: posPts.y1
22923 },
22924 intersectionPts: {
22925 x1: intersectionPts.x2,
22926 y1: intersectionPts.y2,
22927 x2: intersectionPts.x1,
22928 y2: intersectionPts.y1
22929 },
22930 vector: {
22931 x: -vector.x,
22932 y: -vector.y
22933 },
22934 vectorNorm: {
22935 x: -vectorNorm.x,
22936 y: -vectorNorm.y
22937 },
22938 vectorNormInverse: {
22939 x: -vectorNormInverse.x,
22940 y: -vectorNormInverse.y
22941 }
22942 };
22943 }
22944
22945 var passedPairInfo = edgeIsSwapped ? swappedpairInfo : pairInfo;
22946 rs.nodesOverlap = passedPairInfo.nodesOverlap;
22947 rs.srcIntn = passedPairInfo.srcIntn;
22948 rs.tgtIntn = passedPairInfo.tgtIntn;
22949
22950 if (hasCompounds && (src.isParent() || src.isChild() || tgt.isParent() || tgt.isChild()) && (src.parents().anySame(tgt) || tgt.parents().anySame(src) || src.same(tgt) && src.isParent())) {
22951 _this.findCompoundLoopPoints(_edge, passedPairInfo, _i2, _edgeIsUnbundled);
22952 } else if (src === tgt) {
22953 _this.findLoopPoints(_edge, passedPairInfo, _i2, _edgeIsUnbundled);
22954 } else if (_curveStyle === 'segments') {
22955 _this.findSegmentsPoints(_edge, passedPairInfo);
22956 } else if (_curveStyle === 'taxi') {
22957 _this.findTaxiPoints(_edge, passedPairInfo);
22958 } else if (_curveStyle === 'straight' || !_edgeIsUnbundled && pairInfo.eles.length % 2 === 1 && _i2 === Math.floor(pairInfo.eles.length / 2)) {
22959 _this.findStraightEdgePoints(_edge);
22960 } else {
22961 _this.findBezierPoints(_edge, passedPairInfo, _i2, _edgeIsUnbundled, edgeIsSwapped);
22962 }
22963
22964 _this.findEndpoints(_edge);
22965
22966 _this.tryToCorrectInvalidPoints(_edge, passedPairInfo);
22967
22968 _this.checkForInvalidEdgeWarning(_edge);
22969
22970 _this.storeAllpts(_edge);
22971
22972 _this.storeEdgeProjections(_edge);
22973
22974 _this.calculateArrowAngles(_edge);
22975
22976 _this.recalculateEdgeLabelProjections(_edge);
22977
22978 _this.calculateLabelAngles(_edge);
22979 } // for pair edges
22980
22981 };
22982
22983 for (var p = 0; p < pairIds.length; p++) {
22984 _loop(p);
22985 } // for pair ids
22986 // haystacks avoid the expense of pairInfo stuff (intersections etc.)
22987
22988
22989 this.findHaystackPoints(haystackEdges);
22990};
22991
22992function getPts(pts) {
22993 var retPts = [];
22994
22995 if (pts == null) {
22996 return;
22997 }
22998
22999 for (var i = 0; i < pts.length; i += 2) {
23000 var x = pts[i];
23001 var y = pts[i + 1];
23002 retPts.push({
23003 x: x,
23004 y: y
23005 });
23006 }
23007
23008 return retPts;
23009}
23010
23011BRp$c.getSegmentPoints = function (edge) {
23012 var rs = edge[0]._private.rscratch;
23013 var type = rs.edgeType;
23014
23015 if (type === 'segments') {
23016 this.recalculateRenderedStyle(edge);
23017 return getPts(rs.segpts);
23018 }
23019};
23020
23021BRp$c.getControlPoints = function (edge) {
23022 var rs = edge[0]._private.rscratch;
23023 var type = rs.edgeType;
23024
23025 if (type === 'bezier' || type === 'multibezier' || type === 'self' || type === 'compound') {
23026 this.recalculateRenderedStyle(edge);
23027 return getPts(rs.ctrlpts);
23028 }
23029};
23030
23031BRp$c.getEdgeMidpoint = function (edge) {
23032 var rs = edge[0]._private.rscratch;
23033 this.recalculateRenderedStyle(edge);
23034 return {
23035 x: rs.midX,
23036 y: rs.midY
23037 };
23038};
23039
23040var BRp$b = {};
23041
23042BRp$b.manualEndptToPx = function (node, prop) {
23043 var r = this;
23044 var npos = node.position();
23045 var w = node.outerWidth();
23046 var h = node.outerHeight();
23047
23048 if (prop.value.length === 2) {
23049 var p = [prop.pfValue[0], prop.pfValue[1]];
23050
23051 if (prop.units[0] === '%') {
23052 p[0] = p[0] * w;
23053 }
23054
23055 if (prop.units[1] === '%') {
23056 p[1] = p[1] * h;
23057 }
23058
23059 p[0] += npos.x;
23060 p[1] += npos.y;
23061 return p;
23062 } else {
23063 var angle = prop.pfValue[0];
23064 angle = -Math.PI / 2 + angle; // start at 12 o'clock
23065
23066 var l = 2 * Math.max(w, h);
23067 var _p = [npos.x + Math.cos(angle) * l, npos.y + Math.sin(angle) * l];
23068 return r.nodeShapes[this.getNodeShape(node)].intersectLine(npos.x, npos.y, w, h, _p[0], _p[1], 0);
23069 }
23070};
23071
23072BRp$b.findEndpoints = function (edge) {
23073 var r = this;
23074 var intersect;
23075 var source = edge.source()[0];
23076 var target = edge.target()[0];
23077 var srcPos = source.position();
23078 var tgtPos = target.position();
23079 var tgtArShape = edge.pstyle('target-arrow-shape').value;
23080 var srcArShape = edge.pstyle('source-arrow-shape').value;
23081 var tgtDist = edge.pstyle('target-distance-from-node').pfValue;
23082 var srcDist = edge.pstyle('source-distance-from-node').pfValue;
23083 var curveStyle = edge.pstyle('curve-style').value;
23084 var rs = edge._private.rscratch;
23085 var et = rs.edgeType;
23086 var taxi = curveStyle === 'taxi';
23087 var self = et === 'self' || et === 'compound';
23088 var bezier = et === 'bezier' || et === 'multibezier' || self;
23089 var multi = et !== 'bezier';
23090 var lines = et === 'straight' || et === 'segments';
23091 var segments = et === 'segments';
23092 var hasEndpts = bezier || multi || lines;
23093 var overrideEndpts = self || taxi;
23094 var srcManEndpt = edge.pstyle('source-endpoint');
23095 var srcManEndptVal = overrideEndpts ? 'outside-to-node' : srcManEndpt.value;
23096 var tgtManEndpt = edge.pstyle('target-endpoint');
23097 var tgtManEndptVal = overrideEndpts ? 'outside-to-node' : tgtManEndpt.value;
23098 rs.srcManEndpt = srcManEndpt;
23099 rs.tgtManEndpt = tgtManEndpt;
23100 var p1; // last known point of edge on target side
23101
23102 var p2; // last known point of edge on source side
23103
23104 var p1_i; // point to intersect with target shape
23105
23106 var p2_i; // point to intersect with source shape
23107
23108 if (bezier) {
23109 var cpStart = [rs.ctrlpts[0], rs.ctrlpts[1]];
23110 var cpEnd = multi ? [rs.ctrlpts[rs.ctrlpts.length - 2], rs.ctrlpts[rs.ctrlpts.length - 1]] : cpStart;
23111 p1 = cpEnd;
23112 p2 = cpStart;
23113 } else if (lines) {
23114 var srcArrowFromPt = !segments ? [tgtPos.x, tgtPos.y] : rs.segpts.slice(0, 2);
23115 var tgtArrowFromPt = !segments ? [srcPos.x, srcPos.y] : rs.segpts.slice(rs.segpts.length - 2);
23116 p1 = tgtArrowFromPt;
23117 p2 = srcArrowFromPt;
23118 }
23119
23120 if (tgtManEndptVal === 'inside-to-node') {
23121 intersect = [tgtPos.x, tgtPos.y];
23122 } else if (tgtManEndpt.units) {
23123 intersect = this.manualEndptToPx(target, tgtManEndpt);
23124 } else if (tgtManEndptVal === 'outside-to-line') {
23125 intersect = rs.tgtIntn; // use cached value from ctrlpt calc
23126 } else {
23127 if (tgtManEndptVal === 'outside-to-node' || tgtManEndptVal === 'outside-to-node-or-label') {
23128 p1_i = p1;
23129 } else if (tgtManEndptVal === 'outside-to-line' || tgtManEndptVal === 'outside-to-line-or-label') {
23130 p1_i = [srcPos.x, srcPos.y];
23131 }
23132
23133 intersect = r.nodeShapes[this.getNodeShape(target)].intersectLine(tgtPos.x, tgtPos.y, target.outerWidth(), target.outerHeight(), p1_i[0], p1_i[1], 0);
23134
23135 if (tgtManEndptVal === 'outside-to-node-or-label' || tgtManEndptVal === 'outside-to-line-or-label') {
23136 var trs = target._private.rscratch;
23137 var lw = trs.labelWidth;
23138 var lh = trs.labelHeight;
23139 var lx = trs.labelX;
23140 var ly = trs.labelY;
23141 var lw2 = lw / 2;
23142 var lh2 = lh / 2;
23143 var va = target.pstyle('text-valign').value;
23144
23145 if (va === 'top') {
23146 ly -= lh2;
23147 } else if (va === 'bottom') {
23148 ly += lh2;
23149 }
23150
23151 var ha = target.pstyle('text-halign').value;
23152
23153 if (ha === 'left') {
23154 lx -= lw2;
23155 } else if (ha === 'right') {
23156 lx += lw2;
23157 }
23158
23159 var labelIntersect = polygonIntersectLine(p1_i[0], p1_i[1], [lx - lw2, ly - lh2, lx + lw2, ly - lh2, lx + lw2, ly + lh2, lx - lw2, ly + lh2], tgtPos.x, tgtPos.y);
23160
23161 if (labelIntersect.length > 0) {
23162 var refPt = srcPos;
23163 var intSqdist = sqdist(refPt, array2point(intersect));
23164 var labIntSqdist = sqdist(refPt, array2point(labelIntersect));
23165 var minSqDist = intSqdist;
23166
23167 if (labIntSqdist < intSqdist) {
23168 intersect = labelIntersect;
23169 minSqDist = labIntSqdist;
23170 }
23171
23172 if (labelIntersect.length > 2) {
23173 var labInt2SqDist = sqdist(refPt, {
23174 x: labelIntersect[2],
23175 y: labelIntersect[3]
23176 });
23177
23178 if (labInt2SqDist < minSqDist) {
23179 intersect = [labelIntersect[2], labelIntersect[3]];
23180 }
23181 }
23182 }
23183 }
23184 }
23185
23186 var arrowEnd = shortenIntersection(intersect, p1, r.arrowShapes[tgtArShape].spacing(edge) + tgtDist);
23187 var edgeEnd = shortenIntersection(intersect, p1, r.arrowShapes[tgtArShape].gap(edge) + tgtDist);
23188 rs.endX = edgeEnd[0];
23189 rs.endY = edgeEnd[1];
23190 rs.arrowEndX = arrowEnd[0];
23191 rs.arrowEndY = arrowEnd[1];
23192
23193 if (srcManEndptVal === 'inside-to-node') {
23194 intersect = [srcPos.x, srcPos.y];
23195 } else if (srcManEndpt.units) {
23196 intersect = this.manualEndptToPx(source, srcManEndpt);
23197 } else if (srcManEndptVal === 'outside-to-line') {
23198 intersect = rs.srcIntn; // use cached value from ctrlpt calc
23199 } else {
23200 if (srcManEndptVal === 'outside-to-node' || srcManEndptVal === 'outside-to-node-or-label') {
23201 p2_i = p2;
23202 } else if (srcManEndptVal === 'outside-to-line' || srcManEndptVal === 'outside-to-line-or-label') {
23203 p2_i = [tgtPos.x, tgtPos.y];
23204 }
23205
23206 intersect = r.nodeShapes[this.getNodeShape(source)].intersectLine(srcPos.x, srcPos.y, source.outerWidth(), source.outerHeight(), p2_i[0], p2_i[1], 0);
23207
23208 if (srcManEndptVal === 'outside-to-node-or-label' || srcManEndptVal === 'outside-to-line-or-label') {
23209 var srs = source._private.rscratch;
23210 var _lw = srs.labelWidth;
23211 var _lh = srs.labelHeight;
23212 var _lx = srs.labelX;
23213 var _ly = srs.labelY;
23214
23215 var _lw2 = _lw / 2;
23216
23217 var _lh2 = _lh / 2;
23218
23219 var _va = source.pstyle('text-valign').value;
23220
23221 if (_va === 'top') {
23222 _ly -= _lh2;
23223 } else if (_va === 'bottom') {
23224 _ly += _lh2;
23225 }
23226
23227 var _ha = source.pstyle('text-halign').value;
23228
23229 if (_ha === 'left') {
23230 _lx -= _lw2;
23231 } else if (_ha === 'right') {
23232 _lx += _lw2;
23233 }
23234
23235 var _labelIntersect = polygonIntersectLine(p2_i[0], p2_i[1], [_lx - _lw2, _ly - _lh2, _lx + _lw2, _ly - _lh2, _lx + _lw2, _ly + _lh2, _lx - _lw2, _ly + _lh2], srcPos.x, srcPos.y);
23236
23237 if (_labelIntersect.length > 0) {
23238 var _refPt = tgtPos;
23239
23240 var _intSqdist = sqdist(_refPt, array2point(intersect));
23241
23242 var _labIntSqdist = sqdist(_refPt, array2point(_labelIntersect));
23243
23244 var _minSqDist = _intSqdist;
23245
23246 if (_labIntSqdist < _intSqdist) {
23247 intersect = [_labelIntersect[0], _labelIntersect[1]];
23248 _minSqDist = _labIntSqdist;
23249 }
23250
23251 if (_labelIntersect.length > 2) {
23252 var _labInt2SqDist = sqdist(_refPt, {
23253 x: _labelIntersect[2],
23254 y: _labelIntersect[3]
23255 });
23256
23257 if (_labInt2SqDist < _minSqDist) {
23258 intersect = [_labelIntersect[2], _labelIntersect[3]];
23259 }
23260 }
23261 }
23262 }
23263 }
23264
23265 var arrowStart = shortenIntersection(intersect, p2, r.arrowShapes[srcArShape].spacing(edge) + srcDist);
23266 var edgeStart = shortenIntersection(intersect, p2, r.arrowShapes[srcArShape].gap(edge) + srcDist);
23267 rs.startX = edgeStart[0];
23268 rs.startY = edgeStart[1];
23269 rs.arrowStartX = arrowStart[0];
23270 rs.arrowStartY = arrowStart[1];
23271
23272 if (hasEndpts) {
23273 if (!number$1(rs.startX) || !number$1(rs.startY) || !number$1(rs.endX) || !number$1(rs.endY)) {
23274 rs.badLine = true;
23275 } else {
23276 rs.badLine = false;
23277 }
23278 }
23279};
23280
23281BRp$b.getSourceEndpoint = function (edge) {
23282 var rs = edge[0]._private.rscratch;
23283 this.recalculateRenderedStyle(edge);
23284
23285 switch (rs.edgeType) {
23286 case 'haystack':
23287 return {
23288 x: rs.haystackPts[0],
23289 y: rs.haystackPts[1]
23290 };
23291
23292 default:
23293 return {
23294 x: rs.arrowStartX,
23295 y: rs.arrowStartY
23296 };
23297 }
23298};
23299
23300BRp$b.getTargetEndpoint = function (edge) {
23301 var rs = edge[0]._private.rscratch;
23302 this.recalculateRenderedStyle(edge);
23303
23304 switch (rs.edgeType) {
23305 case 'haystack':
23306 return {
23307 x: rs.haystackPts[2],
23308 y: rs.haystackPts[3]
23309 };
23310
23311 default:
23312 return {
23313 x: rs.arrowEndX,
23314 y: rs.arrowEndY
23315 };
23316 }
23317};
23318
23319var BRp$a = {};
23320
23321function pushBezierPts(r, edge, pts) {
23322 var qbezierAt$1 = function qbezierAt$1(p1, p2, p3, t) {
23323 return qbezierAt(p1, p2, p3, t);
23324 };
23325
23326 var _p = edge._private;
23327 var bpts = _p.rstyle.bezierPts;
23328
23329 for (var i = 0; i < r.bezierProjPcts.length; i++) {
23330 var p = r.bezierProjPcts[i];
23331 bpts.push({
23332 x: qbezierAt$1(pts[0], pts[2], pts[4], p),
23333 y: qbezierAt$1(pts[1], pts[3], pts[5], p)
23334 });
23335 }
23336}
23337
23338BRp$a.storeEdgeProjections = function (edge) {
23339 var _p = edge._private;
23340 var rs = _p.rscratch;
23341 var et = rs.edgeType; // clear the cached points state
23342
23343 _p.rstyle.bezierPts = null;
23344 _p.rstyle.linePts = null;
23345 _p.rstyle.haystackPts = null;
23346
23347 if (et === 'multibezier' || et === 'bezier' || et === 'self' || et === 'compound') {
23348 _p.rstyle.bezierPts = [];
23349
23350 for (var i = 0; i + 5 < rs.allpts.length; i += 4) {
23351 pushBezierPts(this, edge, rs.allpts.slice(i, i + 6));
23352 }
23353 } else if (et === 'segments') {
23354 var lpts = _p.rstyle.linePts = [];
23355
23356 for (var i = 0; i + 1 < rs.allpts.length; i += 2) {
23357 lpts.push({
23358 x: rs.allpts[i],
23359 y: rs.allpts[i + 1]
23360 });
23361 }
23362 } else if (et === 'haystack') {
23363 var hpts = rs.haystackPts;
23364 _p.rstyle.haystackPts = [{
23365 x: hpts[0],
23366 y: hpts[1]
23367 }, {
23368 x: hpts[2],
23369 y: hpts[3]
23370 }];
23371 }
23372
23373 _p.rstyle.arrowWidth = this.getArrowWidth(edge.pstyle('width').pfValue, edge.pstyle('arrow-scale').value) * this.arrowShapeWidth;
23374};
23375
23376BRp$a.recalculateEdgeProjections = function (edges) {
23377 this.findEdgeControlPoints(edges);
23378};
23379
23380/* global document */
23381
23382var BRp$9 = {};
23383
23384BRp$9.recalculateNodeLabelProjection = function (node) {
23385 var content = node.pstyle('label').strValue;
23386
23387 if (emptyString(content)) {
23388 return;
23389 }
23390
23391 var textX, textY;
23392 var _p = node._private;
23393 var nodeWidth = node.width();
23394 var nodeHeight = node.height();
23395 var padding = node.padding();
23396 var nodePos = node.position();
23397 var textHalign = node.pstyle('text-halign').strValue;
23398 var textValign = node.pstyle('text-valign').strValue;
23399 var rs = _p.rscratch;
23400 var rstyle = _p.rstyle;
23401
23402 switch (textHalign) {
23403 case 'left':
23404 textX = nodePos.x - nodeWidth / 2 - padding;
23405 break;
23406
23407 case 'right':
23408 textX = nodePos.x + nodeWidth / 2 + padding;
23409 break;
23410
23411 default:
23412 // e.g. center
23413 textX = nodePos.x;
23414 }
23415
23416 switch (textValign) {
23417 case 'top':
23418 textY = nodePos.y - nodeHeight / 2 - padding;
23419 break;
23420
23421 case 'bottom':
23422 textY = nodePos.y + nodeHeight / 2 + padding;
23423 break;
23424
23425 default:
23426 // e.g. middle
23427 textY = nodePos.y;
23428 }
23429
23430 rs.labelX = textX;
23431 rs.labelY = textY;
23432 rstyle.labelX = textX;
23433 rstyle.labelY = textY;
23434 this.calculateLabelAngles(node);
23435 this.applyLabelDimensions(node);
23436};
23437
23438var lineAngleFromDelta = function lineAngleFromDelta(dx, dy) {
23439 var angle = Math.atan(dy / dx);
23440
23441 if (dx === 0 && angle < 0) {
23442 angle = angle * -1;
23443 }
23444
23445 return angle;
23446};
23447
23448var lineAngle = function lineAngle(p0, p1) {
23449 var dx = p1.x - p0.x;
23450 var dy = p1.y - p0.y;
23451 return lineAngleFromDelta(dx, dy);
23452};
23453
23454var bezierAngle = function bezierAngle(p0, p1, p2, t) {
23455 var t0 = bound(0, t - 0.001, 1);
23456 var t1 = bound(0, t + 0.001, 1);
23457 var lp0 = qbezierPtAt(p0, p1, p2, t0);
23458 var lp1 = qbezierPtAt(p0, p1, p2, t1);
23459 return lineAngle(lp0, lp1);
23460};
23461
23462BRp$9.recalculateEdgeLabelProjections = function (edge) {
23463 var p;
23464 var _p = edge._private;
23465 var rs = _p.rscratch;
23466 var r = this;
23467 var content = {
23468 mid: edge.pstyle('label').strValue,
23469 source: edge.pstyle('source-label').strValue,
23470 target: edge.pstyle('target-label').strValue
23471 };
23472
23473 if (content.mid || content.source || content.target) ; else {
23474 return; // no labels => no calcs
23475 } // add center point to style so bounding box calculations can use it
23476 //
23477
23478
23479 p = {
23480 x: rs.midX,
23481 y: rs.midY
23482 };
23483
23484 var setRs = function setRs(propName, prefix, value) {
23485 setPrefixedProperty(_p.rscratch, propName, prefix, value);
23486 setPrefixedProperty(_p.rstyle, propName, prefix, value);
23487 };
23488
23489 setRs('labelX', null, p.x);
23490 setRs('labelY', null, p.y);
23491 var midAngle = lineAngleFromDelta(rs.midDispX, rs.midDispY);
23492 setRs('labelAutoAngle', null, midAngle);
23493
23494 var createControlPointInfo = function createControlPointInfo() {
23495 if (createControlPointInfo.cache) {
23496 return createControlPointInfo.cache;
23497 } // use cache so only 1x per edge
23498
23499
23500 var ctrlpts = []; // store each ctrlpt info init
23501
23502 for (var i = 0; i + 5 < rs.allpts.length; i += 4) {
23503 var p0 = {
23504 x: rs.allpts[i],
23505 y: rs.allpts[i + 1]
23506 };
23507 var p1 = {
23508 x: rs.allpts[i + 2],
23509 y: rs.allpts[i + 3]
23510 }; // ctrlpt
23511
23512 var p2 = {
23513 x: rs.allpts[i + 4],
23514 y: rs.allpts[i + 5]
23515 };
23516 ctrlpts.push({
23517 p0: p0,
23518 p1: p1,
23519 p2: p2,
23520 startDist: 0,
23521 length: 0,
23522 segments: []
23523 });
23524 }
23525
23526 var bpts = _p.rstyle.bezierPts;
23527 var nProjs = r.bezierProjPcts.length;
23528
23529 function addSegment(cp, p0, p1, t0, t1) {
23530 var length = dist(p0, p1);
23531 var prevSegment = cp.segments[cp.segments.length - 1];
23532 var segment = {
23533 p0: p0,
23534 p1: p1,
23535 t0: t0,
23536 t1: t1,
23537 startDist: prevSegment ? prevSegment.startDist + prevSegment.length : 0,
23538 length: length
23539 };
23540 cp.segments.push(segment);
23541 cp.length += length;
23542 } // update each ctrlpt with segment info
23543
23544
23545 for (var _i = 0; _i < ctrlpts.length; _i++) {
23546 var cp = ctrlpts[_i];
23547 var prevCp = ctrlpts[_i - 1];
23548
23549 if (prevCp) {
23550 cp.startDist = prevCp.startDist + prevCp.length;
23551 }
23552
23553 addSegment(cp, cp.p0, bpts[_i * nProjs], 0, r.bezierProjPcts[0]); // first
23554
23555 for (var j = 0; j < nProjs - 1; j++) {
23556 addSegment(cp, bpts[_i * nProjs + j], bpts[_i * nProjs + j + 1], r.bezierProjPcts[j], r.bezierProjPcts[j + 1]);
23557 }
23558
23559 addSegment(cp, bpts[_i * nProjs + nProjs - 1], cp.p2, r.bezierProjPcts[nProjs - 1], 1); // last
23560 }
23561
23562 return createControlPointInfo.cache = ctrlpts;
23563 };
23564
23565 var calculateEndProjection = function calculateEndProjection(prefix) {
23566 var angle;
23567 var isSrc = prefix === 'source';
23568
23569 if (!content[prefix]) {
23570 return;
23571 }
23572
23573 var offset = edge.pstyle(prefix + '-text-offset').pfValue;
23574
23575 switch (rs.edgeType) {
23576 case 'self':
23577 case 'compound':
23578 case 'bezier':
23579 case 'multibezier':
23580 {
23581 var cps = createControlPointInfo();
23582 var selected;
23583 var startDist = 0;
23584 var totalDist = 0; // find the segment we're on
23585
23586 for (var i = 0; i < cps.length; i++) {
23587 var _cp = cps[isSrc ? i : cps.length - 1 - i];
23588
23589 for (var j = 0; j < _cp.segments.length; j++) {
23590 var _seg = _cp.segments[isSrc ? j : _cp.segments.length - 1 - j];
23591 var lastSeg = i === cps.length - 1 && j === _cp.segments.length - 1;
23592 startDist = totalDist;
23593 totalDist += _seg.length;
23594
23595 if (totalDist >= offset || lastSeg) {
23596 selected = {
23597 cp: _cp,
23598 segment: _seg
23599 };
23600 break;
23601 }
23602 }
23603
23604 if (selected) {
23605 break;
23606 }
23607 }
23608
23609 var cp = selected.cp;
23610 var seg = selected.segment;
23611 var tSegment = (offset - startDist) / seg.length;
23612 var segDt = seg.t1 - seg.t0;
23613 var t = isSrc ? seg.t0 + segDt * tSegment : seg.t1 - segDt * tSegment;
23614 t = bound(0, t, 1);
23615 p = qbezierPtAt(cp.p0, cp.p1, cp.p2, t);
23616 angle = bezierAngle(cp.p0, cp.p1, cp.p2, t);
23617 break;
23618 }
23619
23620 case 'straight':
23621 case 'segments':
23622 case 'haystack':
23623 {
23624 var d = 0,
23625 di,
23626 d0;
23627 var p0, p1;
23628 var l = rs.allpts.length;
23629
23630 for (var _i2 = 0; _i2 + 3 < l; _i2 += 2) {
23631 if (isSrc) {
23632 p0 = {
23633 x: rs.allpts[_i2],
23634 y: rs.allpts[_i2 + 1]
23635 };
23636 p1 = {
23637 x: rs.allpts[_i2 + 2],
23638 y: rs.allpts[_i2 + 3]
23639 };
23640 } else {
23641 p0 = {
23642 x: rs.allpts[l - 2 - _i2],
23643 y: rs.allpts[l - 1 - _i2]
23644 };
23645 p1 = {
23646 x: rs.allpts[l - 4 - _i2],
23647 y: rs.allpts[l - 3 - _i2]
23648 };
23649 }
23650
23651 di = dist(p0, p1);
23652 d0 = d;
23653 d += di;
23654
23655 if (d >= offset) {
23656 break;
23657 }
23658 }
23659
23660 var pD = offset - d0;
23661
23662 var _t = pD / di;
23663
23664 _t = bound(0, _t, 1);
23665 p = lineAt(p0, p1, _t);
23666 angle = lineAngle(p0, p1);
23667 break;
23668 }
23669 }
23670
23671 setRs('labelX', prefix, p.x);
23672 setRs('labelY', prefix, p.y);
23673 setRs('labelAutoAngle', prefix, angle);
23674 };
23675
23676 calculateEndProjection('source');
23677 calculateEndProjection('target');
23678 this.applyLabelDimensions(edge);
23679};
23680
23681BRp$9.applyLabelDimensions = function (ele) {
23682 this.applyPrefixedLabelDimensions(ele);
23683
23684 if (ele.isEdge()) {
23685 this.applyPrefixedLabelDimensions(ele, 'source');
23686 this.applyPrefixedLabelDimensions(ele, 'target');
23687 }
23688};
23689
23690BRp$9.applyPrefixedLabelDimensions = function (ele, prefix) {
23691 var _p = ele._private;
23692 var text = this.getLabelText(ele, prefix);
23693 var labelDims = this.calculateLabelDimensions(ele, text);
23694 var lineHeight = ele.pstyle('line-height').pfValue;
23695 var textWrap = ele.pstyle('text-wrap').strValue;
23696 var lines = getPrefixedProperty(_p.rscratch, 'labelWrapCachedLines', prefix) || [];
23697 var numLines = textWrap !== 'wrap' ? 1 : Math.max(lines.length, 1);
23698 var normPerLineHeight = labelDims.height / numLines;
23699 var labelLineHeight = normPerLineHeight * lineHeight;
23700 var width = labelDims.width;
23701 var height = labelDims.height + (numLines - 1) * (lineHeight - 1) * normPerLineHeight;
23702 setPrefixedProperty(_p.rstyle, 'labelWidth', prefix, width);
23703 setPrefixedProperty(_p.rscratch, 'labelWidth', prefix, width);
23704 setPrefixedProperty(_p.rstyle, 'labelHeight', prefix, height);
23705 setPrefixedProperty(_p.rscratch, 'labelHeight', prefix, height);
23706 setPrefixedProperty(_p.rscratch, 'labelLineHeight', prefix, labelLineHeight);
23707};
23708
23709BRp$9.getLabelText = function (ele, prefix) {
23710 var _p = ele._private;
23711 var pfd = prefix ? prefix + '-' : '';
23712 var text = ele.pstyle(pfd + 'label').strValue;
23713 var textTransform = ele.pstyle('text-transform').value;
23714
23715 var rscratch = function rscratch(propName, value) {
23716 if (value) {
23717 setPrefixedProperty(_p.rscratch, propName, prefix, value);
23718 return value;
23719 } else {
23720 return getPrefixedProperty(_p.rscratch, propName, prefix);
23721 }
23722 }; // for empty text, skip all processing
23723
23724
23725 if (!text) {
23726 return '';
23727 }
23728
23729 if (textTransform == 'none') ; else if (textTransform == 'uppercase') {
23730 text = text.toUpperCase();
23731 } else if (textTransform == 'lowercase') {
23732 text = text.toLowerCase();
23733 }
23734
23735 var wrapStyle = ele.pstyle('text-wrap').value;
23736
23737 if (wrapStyle === 'wrap') {
23738 var labelKey = rscratch('labelKey'); // save recalc if the label is the same as before
23739
23740 if (labelKey != null && rscratch('labelWrapKey') === labelKey) {
23741 return rscratch('labelWrapCachedText');
23742 }
23743
23744 var zwsp = "\u200B";
23745 var lines = text.split('\n');
23746 var maxW = ele.pstyle('text-max-width').pfValue;
23747 var overflow = ele.pstyle('text-overflow-wrap').value;
23748 var overflowAny = overflow === 'anywhere';
23749 var wrappedLines = [];
23750 var wordsRegex = /[\s\u200b]+/;
23751 var wordSeparator = overflowAny ? '' : ' ';
23752
23753 for (var l = 0; l < lines.length; l++) {
23754 var line = lines[l];
23755 var lineDims = this.calculateLabelDimensions(ele, line);
23756 var lineW = lineDims.width;
23757
23758 if (overflowAny) {
23759 var processedLine = line.split('').join(zwsp);
23760 line = processedLine;
23761 }
23762
23763 if (lineW > maxW) {
23764 // line is too long
23765 var words = line.split(wordsRegex);
23766 var subline = '';
23767
23768 for (var w = 0; w < words.length; w++) {
23769 var word = words[w];
23770 var testLine = subline.length === 0 ? word : subline + wordSeparator + word;
23771 var testDims = this.calculateLabelDimensions(ele, testLine);
23772 var testW = testDims.width;
23773
23774 if (testW <= maxW) {
23775 // word fits on current line
23776 subline += word + wordSeparator;
23777 } else {
23778 // word starts new line
23779 if (subline) {
23780 wrappedLines.push(subline);
23781 }
23782
23783 subline = word + wordSeparator;
23784 }
23785 } // if there's remaining text, put it in a wrapped line
23786
23787
23788 if (!subline.match(/^[\s\u200b]+$/)) {
23789 wrappedLines.push(subline);
23790 }
23791 } else {
23792 // line is already short enough
23793 wrappedLines.push(line);
23794 }
23795 } // for
23796
23797
23798 rscratch('labelWrapCachedLines', wrappedLines);
23799 text = rscratch('labelWrapCachedText', wrappedLines.join('\n'));
23800 rscratch('labelWrapKey', labelKey);
23801 } else if (wrapStyle === 'ellipsis') {
23802 var _maxW = ele.pstyle('text-max-width').pfValue;
23803 var ellipsized = '';
23804 var ellipsis = "\u2026";
23805 var incLastCh = false;
23806
23807 if (this.calculateLabelDimensions(ele, text).width < _maxW) {
23808 // the label already fits
23809 return text;
23810 }
23811
23812 for (var i = 0; i < text.length; i++) {
23813 var widthWithNextCh = this.calculateLabelDimensions(ele, ellipsized + text[i] + ellipsis).width;
23814
23815 if (widthWithNextCh > _maxW) {
23816 break;
23817 }
23818
23819 ellipsized += text[i];
23820
23821 if (i === text.length - 1) {
23822 incLastCh = true;
23823 }
23824 }
23825
23826 if (!incLastCh) {
23827 ellipsized += ellipsis;
23828 }
23829
23830 return ellipsized;
23831 } // if ellipsize
23832
23833
23834 return text;
23835};
23836
23837BRp$9.getLabelJustification = function (ele) {
23838 var justification = ele.pstyle('text-justification').strValue;
23839 var textHalign = ele.pstyle('text-halign').strValue;
23840
23841 if (justification === 'auto') {
23842 if (ele.isNode()) {
23843 switch (textHalign) {
23844 case 'left':
23845 return 'right';
23846
23847 case 'right':
23848 return 'left';
23849
23850 default:
23851 return 'center';
23852 }
23853 } else {
23854 return 'center';
23855 }
23856 } else {
23857 return justification;
23858 }
23859};
23860
23861BRp$9.calculateLabelDimensions = function (ele, text) {
23862 var r = this;
23863 var cacheKey = hashString(text, ele._private.labelDimsKey);
23864 var cache = r.labelDimCache || (r.labelDimCache = []);
23865 var existingVal = cache[cacheKey];
23866
23867 if (existingVal != null) {
23868 return existingVal;
23869 }
23870
23871 var padding = 0; // add padding around text dims, as the measurement isn't that accurate
23872
23873 var fStyle = ele.pstyle('font-style').strValue;
23874 var size = ele.pstyle('font-size').pfValue;
23875 var family = ele.pstyle('font-family').strValue;
23876 var weight = ele.pstyle('font-weight').strValue;
23877 var canvas = this.labelCalcCanvas;
23878 var c2d = this.labelCalcCanvasContext;
23879
23880 if (!canvas) {
23881 canvas = this.labelCalcCanvas = document.createElement('canvas');
23882 c2d = this.labelCalcCanvasContext = canvas.getContext('2d');
23883 var ds = canvas.style;
23884 ds.position = 'absolute';
23885 ds.left = '-9999px';
23886 ds.top = '-9999px';
23887 ds.zIndex = '-1';
23888 ds.visibility = 'hidden';
23889 ds.pointerEvents = 'none';
23890 }
23891
23892 c2d.font = "".concat(fStyle, " ").concat(weight, " ").concat(size, "px ").concat(family);
23893 var width = 0;
23894 var height = 0;
23895 var lines = text.split('\n');
23896
23897 for (var i = 0; i < lines.length; i++) {
23898 var line = lines[i];
23899 var metrics = c2d.measureText(line);
23900 var w = Math.ceil(metrics.width);
23901 var h = size;
23902 width = Math.max(w, width);
23903 height += h;
23904 }
23905
23906 width += padding;
23907 height += padding;
23908 return cache[cacheKey] = {
23909 width: width,
23910 height: height
23911 };
23912};
23913
23914BRp$9.calculateLabelAngle = function (ele, prefix) {
23915 var _p = ele._private;
23916 var rs = _p.rscratch;
23917 var isEdge = ele.isEdge();
23918 var prefixDash = prefix ? prefix + '-' : '';
23919 var rot = ele.pstyle(prefixDash + 'text-rotation');
23920 var rotStr = rot.strValue;
23921
23922 if (rotStr === 'none') {
23923 return 0;
23924 } else if (isEdge && rotStr === 'autorotate') {
23925 return rs.labelAutoAngle;
23926 } else if (rotStr === 'autorotate') {
23927 return 0;
23928 } else {
23929 return rot.pfValue;
23930 }
23931};
23932
23933BRp$9.calculateLabelAngles = function (ele) {
23934 var r = this;
23935 var isEdge = ele.isEdge();
23936 var _p = ele._private;
23937 var rs = _p.rscratch;
23938 rs.labelAngle = r.calculateLabelAngle(ele);
23939
23940 if (isEdge) {
23941 rs.sourceLabelAngle = r.calculateLabelAngle(ele, 'source');
23942 rs.targetLabelAngle = r.calculateLabelAngle(ele, 'target');
23943 }
23944};
23945
23946var BRp$8 = {};
23947var TOO_SMALL_CUT_RECT = 28;
23948var warnedCutRect = false;
23949
23950BRp$8.getNodeShape = function (node) {
23951 var r = this;
23952 var shape = node.pstyle('shape').value;
23953
23954 if (shape === 'cutrectangle' && (node.width() < TOO_SMALL_CUT_RECT || node.height() < TOO_SMALL_CUT_RECT)) {
23955 if (!warnedCutRect) {
23956 warn('The `cutrectangle` node shape can not be used at small sizes so `rectangle` is used instead');
23957 warnedCutRect = true;
23958 }
23959
23960 return 'rectangle';
23961 }
23962
23963 if (node.isParent()) {
23964 if (shape === 'rectangle' || shape === 'roundrectangle' || shape === 'round-rectangle' || shape === 'cutrectangle' || shape === 'cut-rectangle' || shape === 'barrel') {
23965 return shape;
23966 } else {
23967 return 'rectangle';
23968 }
23969 }
23970
23971 if (shape === 'polygon') {
23972 var points = node.pstyle('shape-polygon-points').value;
23973 return r.nodeShapes.makePolygon(points).name;
23974 }
23975
23976 return shape;
23977};
23978
23979var BRp$7 = {};
23980
23981BRp$7.registerCalculationListeners = function () {
23982 var cy = this.cy;
23983 var elesToUpdate = cy.collection();
23984 var r = this;
23985
23986 var enqueue = function enqueue(eles) {
23987 var dirtyStyleCaches = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
23988 elesToUpdate.merge(eles);
23989
23990 if (dirtyStyleCaches) {
23991 for (var i = 0; i < eles.length; i++) {
23992 var ele = eles[i];
23993 var _p = ele._private;
23994 var rstyle = _p.rstyle;
23995 rstyle.clean = false;
23996 rstyle.cleanConnected = false;
23997 }
23998 }
23999 };
24000
24001 r.binder(cy).on('bounds.* dirty.*', function onDirtyBounds(e) {
24002 var ele = e.target;
24003 enqueue(ele);
24004 }).on('style.* background.*', function onDirtyStyle(e) {
24005 var ele = e.target;
24006 enqueue(ele, false);
24007 });
24008
24009 var updateEleCalcs = function updateEleCalcs(willDraw) {
24010 if (willDraw) {
24011 var fns = r.onUpdateEleCalcsFns; // because we need to have up-to-date style (e.g. stylesheet mappers)
24012 // before calculating rendered style (and pstyle might not be called yet)
24013
24014 elesToUpdate.cleanStyle();
24015
24016 for (var i = 0; i < elesToUpdate.length; i++) {
24017 var ele = elesToUpdate[i];
24018 var rstyle = ele._private.rstyle;
24019
24020 if (ele.isNode() && !rstyle.cleanConnected) {
24021 enqueue(ele.connectedEdges());
24022 rstyle.cleanConnected = true;
24023 }
24024 }
24025
24026 if (fns) {
24027 for (var _i = 0; _i < fns.length; _i++) {
24028 var fn = fns[_i];
24029 fn(willDraw, elesToUpdate);
24030 }
24031 }
24032
24033 r.recalculateRenderedStyle(elesToUpdate);
24034 elesToUpdate = cy.collection();
24035 }
24036 };
24037
24038 r.flushRenderedStyleQueue = function () {
24039 updateEleCalcs(true);
24040 };
24041
24042 r.beforeRender(updateEleCalcs, r.beforeRenderPriorities.eleCalcs);
24043};
24044
24045BRp$7.onUpdateEleCalcs = function (fn) {
24046 var fns = this.onUpdateEleCalcsFns = this.onUpdateEleCalcsFns || [];
24047 fns.push(fn);
24048};
24049
24050BRp$7.recalculateRenderedStyle = function (eles, useCache) {
24051 var isCleanConnected = function isCleanConnected(ele) {
24052 return ele._private.rstyle.cleanConnected;
24053 };
24054
24055 var edges = [];
24056 var nodes = []; // the renderer can't be used for calcs when destroyed, e.g. ele.boundingBox()
24057
24058 if (this.destroyed) {
24059 return;
24060 } // use cache by default for perf
24061
24062
24063 if (useCache === undefined) {
24064 useCache = true;
24065 }
24066
24067 for (var i = 0; i < eles.length; i++) {
24068 var ele = eles[i];
24069 var _p = ele._private;
24070 var rstyle = _p.rstyle; // an edge may be implicitly dirty b/c of one of its connected nodes
24071 // (and a request for recalc may come in between frames)
24072
24073 if (ele.isEdge() && (!isCleanConnected(ele.source()) || !isCleanConnected(ele.target()))) {
24074 rstyle.clean = false;
24075 } // only update if dirty and in graph
24076
24077
24078 if (useCache && rstyle.clean || ele.removed()) {
24079 continue;
24080 } // only update if not display: none
24081
24082
24083 if (ele.pstyle('display').value === 'none') {
24084 continue;
24085 }
24086
24087 if (_p.group === 'nodes') {
24088 nodes.push(ele);
24089 } else {
24090 // edges
24091 edges.push(ele);
24092 }
24093
24094 rstyle.clean = true;
24095 } // update node data from projections
24096
24097
24098 for (var _i2 = 0; _i2 < nodes.length; _i2++) {
24099 var _ele = nodes[_i2];
24100 var _p2 = _ele._private;
24101 var _rstyle = _p2.rstyle;
24102
24103 var pos = _ele.position();
24104
24105 this.recalculateNodeLabelProjection(_ele);
24106 _rstyle.nodeX = pos.x;
24107 _rstyle.nodeY = pos.y;
24108 _rstyle.nodeW = _ele.pstyle('width').pfValue;
24109 _rstyle.nodeH = _ele.pstyle('height').pfValue;
24110 }
24111
24112 this.recalculateEdgeProjections(edges); // update edge data from projections
24113
24114 for (var _i3 = 0; _i3 < edges.length; _i3++) {
24115 var _ele2 = edges[_i3];
24116 var _p3 = _ele2._private;
24117 var _rstyle2 = _p3.rstyle;
24118 var rs = _p3.rscratch; // update rstyle positions
24119
24120 _rstyle2.srcX = rs.arrowStartX;
24121 _rstyle2.srcY = rs.arrowStartY;
24122 _rstyle2.tgtX = rs.arrowEndX;
24123 _rstyle2.tgtY = rs.arrowEndY;
24124 _rstyle2.midX = rs.midX;
24125 _rstyle2.midY = rs.midY;
24126 _rstyle2.labelAngle = rs.labelAngle;
24127 _rstyle2.sourceLabelAngle = rs.sourceLabelAngle;
24128 _rstyle2.targetLabelAngle = rs.targetLabelAngle;
24129 }
24130};
24131
24132var BRp$6 = {};
24133
24134BRp$6.updateCachedGrabbedEles = function () {
24135 var eles = this.cachedZSortedEles;
24136
24137 if (!eles) {
24138 // just let this be recalculated on the next z sort tick
24139 return;
24140 }
24141
24142 eles.drag = [];
24143 eles.nondrag = [];
24144 var grabTargets = [];
24145
24146 for (var i = 0; i < eles.length; i++) {
24147 var ele = eles[i];
24148 var rs = ele._private.rscratch;
24149
24150 if (ele.grabbed() && !ele.isParent()) {
24151 grabTargets.push(ele);
24152 } else if (rs.inDragLayer) {
24153 eles.drag.push(ele);
24154 } else {
24155 eles.nondrag.push(ele);
24156 }
24157 } // put the grab target nodes last so it's on top of its neighbourhood
24158
24159
24160 for (var i = 0; i < grabTargets.length; i++) {
24161 var ele = grabTargets[i];
24162 eles.drag.push(ele);
24163 }
24164};
24165
24166BRp$6.invalidateCachedZSortedEles = function () {
24167 this.cachedZSortedEles = null;
24168};
24169
24170BRp$6.getCachedZSortedEles = function (forceRecalc) {
24171 if (forceRecalc || !this.cachedZSortedEles) {
24172 var eles = this.cy.mutableElements().toArray();
24173 eles.sort(zIndexSort);
24174 eles.interactive = eles.filter(function (ele) {
24175 return ele.interactive();
24176 });
24177 this.cachedZSortedEles = eles;
24178 this.updateCachedGrabbedEles();
24179 } else {
24180 eles = this.cachedZSortedEles;
24181 }
24182
24183 return eles;
24184};
24185
24186var BRp$5 = {};
24187[BRp$e, BRp$d, BRp$c, BRp$b, BRp$a, BRp$9, BRp$8, BRp$7, BRp$6].forEach(function (props) {
24188 extend(BRp$5, props);
24189});
24190
24191var BRp$4 = {};
24192
24193BRp$4.getCachedImage = function (url, crossOrigin, onLoad) {
24194 var r = this;
24195 var imageCache = r.imageCache = r.imageCache || {};
24196 var cache = imageCache[url];
24197
24198 if (cache) {
24199 if (!cache.image.complete) {
24200 cache.image.addEventListener('load', onLoad);
24201 }
24202
24203 return cache.image;
24204 } else {
24205 cache = imageCache[url] = imageCache[url] || {};
24206 var image = cache.image = new Image(); // eslint-disable-line no-undef
24207
24208 image.addEventListener('load', onLoad);
24209 image.addEventListener('error', function () {
24210 image.error = true;
24211 }); // #1582 safari doesn't load data uris with crossOrigin properly
24212 // https://bugs.webkit.org/show_bug.cgi?id=123978
24213
24214 var dataUriPrefix = 'data:';
24215 var isDataUri = url.substring(0, dataUriPrefix.length).toLowerCase() === dataUriPrefix;
24216
24217 if (!isDataUri) {
24218 image.crossOrigin = crossOrigin; // prevent tainted canvas
24219 }
24220
24221 image.src = url;
24222 return image;
24223 }
24224};
24225
24226var BRp$3 = {};
24227/* global document, window, ResizeObserver, MutationObserver */
24228
24229BRp$3.registerBinding = function (target, event, handler, useCapture) {
24230 // eslint-disable-line no-unused-vars
24231 var args = Array.prototype.slice.apply(arguments, [1]); // copy
24232
24233 var b = this.binder(target);
24234 return b.on.apply(b, args);
24235};
24236
24237BRp$3.binder = function (tgt) {
24238 var r = this;
24239 var tgtIsDom = tgt === window || tgt === document || tgt === document.body || domElement(tgt);
24240
24241 if (r.supportsPassiveEvents == null) {
24242 // from https://github.com/WICG/EventListenerOptions/blob/gh-pages/explainer.md#feature-detection
24243 var supportsPassive = false;
24244
24245 try {
24246 var opts = Object.defineProperty({}, 'passive', {
24247 get: function get() {
24248 supportsPassive = true;
24249 return true;
24250 }
24251 });
24252 window.addEventListener('test', null, opts);
24253 } catch (err) {// not supported
24254 }
24255
24256 r.supportsPassiveEvents = supportsPassive;
24257 }
24258
24259 var on = function on(event, handler, useCapture) {
24260 var args = Array.prototype.slice.call(arguments);
24261
24262 if (tgtIsDom && r.supportsPassiveEvents) {
24263 // replace useCapture w/ opts obj
24264 args[2] = {
24265 capture: useCapture != null ? useCapture : false,
24266 passive: false,
24267 once: false
24268 };
24269 }
24270
24271 r.bindings.push({
24272 target: tgt,
24273 args: args
24274 });
24275 (tgt.addEventListener || tgt.on).apply(tgt, args);
24276 return this;
24277 };
24278
24279 return {
24280 on: on,
24281 addEventListener: on,
24282 addListener: on,
24283 bind: on
24284 };
24285};
24286
24287BRp$3.nodeIsDraggable = function (node) {
24288 return node && node.isNode() && !node.locked() && node.grabbable();
24289};
24290
24291BRp$3.nodeIsGrabbable = function (node) {
24292 return this.nodeIsDraggable(node) && node.interactive();
24293};
24294
24295BRp$3.load = function () {
24296 var r = this;
24297
24298 var isSelected = function isSelected(ele) {
24299 return ele.selected();
24300 };
24301
24302 var triggerEvents = function triggerEvents(target, names, e, position) {
24303 if (target == null) {
24304 target = r.cy;
24305 }
24306
24307 for (var i = 0; i < names.length; i++) {
24308 var name = names[i];
24309 target.emit({
24310 originalEvent: e,
24311 type: name,
24312 position: position
24313 });
24314 }
24315 };
24316
24317 var isMultSelKeyDown = function isMultSelKeyDown(e) {
24318 return e.shiftKey || e.metaKey || e.ctrlKey; // maybe e.altKey
24319 };
24320
24321 var allowPanningPassthrough = function allowPanningPassthrough(down, downs) {
24322 var allowPassthrough = true;
24323
24324 if (r.cy.hasCompoundNodes() && down && down.pannable()) {
24325 // a grabbable compound node below the ele => no passthrough panning
24326 for (var i = 0; downs && i < downs.length; i++) {
24327 var down = downs[i]; //if any parent node in event hierarchy isn't pannable, reject passthrough
24328
24329 if (down.isNode() && down.isParent() && !down.pannable()) {
24330 allowPassthrough = false;
24331 break;
24332 }
24333 }
24334 } else {
24335 allowPassthrough = true;
24336 }
24337
24338 return allowPassthrough;
24339 };
24340
24341 var setGrabbed = function setGrabbed(ele) {
24342 ele[0]._private.grabbed = true;
24343 };
24344
24345 var setFreed = function setFreed(ele) {
24346 ele[0]._private.grabbed = false;
24347 };
24348
24349 var setInDragLayer = function setInDragLayer(ele) {
24350 ele[0]._private.rscratch.inDragLayer = true;
24351 };
24352
24353 var setOutDragLayer = function setOutDragLayer(ele) {
24354 ele[0]._private.rscratch.inDragLayer = false;
24355 };
24356
24357 var setGrabTarget = function setGrabTarget(ele) {
24358 ele[0]._private.rscratch.isGrabTarget = true;
24359 };
24360
24361 var removeGrabTarget = function removeGrabTarget(ele) {
24362 ele[0]._private.rscratch.isGrabTarget = false;
24363 };
24364
24365 var addToDragList = function addToDragList(ele, opts) {
24366 var list = opts.addToList;
24367 var listHasEle = list.has(ele);
24368
24369 if (!listHasEle && ele.grabbable() && !ele.locked()) {
24370 list.merge(ele);
24371 setGrabbed(ele);
24372 }
24373 }; // helper function to determine which child nodes and inner edges
24374 // of a compound node to be dragged as well as the grabbed and selected nodes
24375
24376
24377 var addDescendantsToDrag = function addDescendantsToDrag(node, opts) {
24378 if (!node.cy().hasCompoundNodes()) {
24379 return;
24380 }
24381
24382 if (opts.inDragLayer == null && opts.addToList == null) {
24383 return;
24384 } // nothing to do
24385
24386
24387 var innerNodes = node.descendants();
24388
24389 if (opts.inDragLayer) {
24390 innerNodes.forEach(setInDragLayer);
24391 innerNodes.connectedEdges().forEach(setInDragLayer);
24392 }
24393
24394 if (opts.addToList) {
24395 addToDragList(innerNodes, opts);
24396 }
24397 }; // adds the given nodes and its neighbourhood to the drag layer
24398
24399
24400 var addNodesToDrag = function addNodesToDrag(nodes, opts) {
24401 opts = opts || {};
24402 var hasCompoundNodes = nodes.cy().hasCompoundNodes();
24403
24404 if (opts.inDragLayer) {
24405 nodes.forEach(setInDragLayer);
24406 nodes.neighborhood().stdFilter(function (ele) {
24407 return !hasCompoundNodes || ele.isEdge();
24408 }).forEach(setInDragLayer);
24409 }
24410
24411 if (opts.addToList) {
24412 nodes.forEach(function (ele) {
24413 addToDragList(ele, opts);
24414 });
24415 }
24416
24417 addDescendantsToDrag(nodes, opts); // always add to drag
24418 // also add nodes and edges related to the topmost ancestor
24419
24420 updateAncestorsInDragLayer(nodes, {
24421 inDragLayer: opts.inDragLayer
24422 });
24423 r.updateCachedGrabbedEles();
24424 };
24425
24426 var addNodeToDrag = addNodesToDrag;
24427
24428 var freeDraggedElements = function freeDraggedElements(grabbedEles) {
24429 if (!grabbedEles) {
24430 return;
24431 } // just go over all elements rather than doing a bunch of (possibly expensive) traversals
24432
24433
24434 r.getCachedZSortedEles().forEach(function (ele) {
24435 setFreed(ele);
24436 setOutDragLayer(ele);
24437 removeGrabTarget(ele);
24438 });
24439 r.updateCachedGrabbedEles();
24440 }; // helper function to determine which ancestor nodes and edges should go
24441 // to the drag layer (or should be removed from drag layer).
24442
24443
24444 var updateAncestorsInDragLayer = function updateAncestorsInDragLayer(node, opts) {
24445 if (opts.inDragLayer == null && opts.addToList == null) {
24446 return;
24447 } // nothing to do
24448
24449
24450 if (!node.cy().hasCompoundNodes()) {
24451 return;
24452 } // find top-level parent
24453
24454
24455 var parent = node.ancestors().orphans(); // no parent node: no nodes to add to the drag layer
24456
24457 if (parent.same(node)) {
24458 return;
24459 }
24460
24461 var nodes = parent.descendants().spawnSelf().merge(parent).unmerge(node).unmerge(node.descendants());
24462 var edges = nodes.connectedEdges();
24463
24464 if (opts.inDragLayer) {
24465 edges.forEach(setInDragLayer);
24466 nodes.forEach(setInDragLayer);
24467 }
24468
24469 if (opts.addToList) {
24470 nodes.forEach(function (ele) {
24471 addToDragList(ele, opts);
24472 });
24473 }
24474 };
24475
24476 var blurActiveDomElement = function blurActiveDomElement() {
24477 if (document.activeElement != null && document.activeElement.blur != null) {
24478 document.activeElement.blur();
24479 }
24480 };
24481
24482 var haveMutationsApi = typeof MutationObserver !== 'undefined';
24483 var haveResizeObserverApi = typeof ResizeObserver !== 'undefined'; // watch for when the cy container is removed from the dom
24484
24485 if (haveMutationsApi) {
24486 r.removeObserver = new MutationObserver(function (mutns) {
24487 // eslint-disable-line no-undef
24488 for (var i = 0; i < mutns.length; i++) {
24489 var mutn = mutns[i];
24490 var rNodes = mutn.removedNodes;
24491
24492 if (rNodes) {
24493 for (var j = 0; j < rNodes.length; j++) {
24494 var rNode = rNodes[j];
24495
24496 if (rNode === r.container) {
24497 r.destroy();
24498 break;
24499 }
24500 }
24501 }
24502 }
24503 });
24504
24505 if (r.container.parentNode) {
24506 r.removeObserver.observe(r.container.parentNode, {
24507 childList: true
24508 });
24509 }
24510 } else {
24511 r.registerBinding(r.container, 'DOMNodeRemoved', function (e) {
24512 // eslint-disable-line no-unused-vars
24513 r.destroy();
24514 });
24515 }
24516
24517 var onResize = debounce(function () {
24518 r.cy.resize();
24519 }, 100);
24520
24521 if (haveMutationsApi) {
24522 r.styleObserver = new MutationObserver(onResize); // eslint-disable-line no-undef
24523
24524 r.styleObserver.observe(r.container, {
24525 attributes: true
24526 });
24527 } // auto resize
24528
24529
24530 r.registerBinding(window, 'resize', onResize); // eslint-disable-line no-undef
24531
24532 if (haveResizeObserverApi) {
24533 r.resizeObserver = new ResizeObserver(onResize); // eslint-disable-line no-undef
24534
24535 r.resizeObserver.observe(r.container);
24536 }
24537
24538 var forEachUp = function forEachUp(domEle, fn) {
24539 while (domEle != null) {
24540 fn(domEle);
24541 domEle = domEle.parentNode;
24542 }
24543 };
24544
24545 var invalidateCoords = function invalidateCoords() {
24546 r.invalidateContainerClientCoordsCache();
24547 };
24548
24549 forEachUp(r.container, function (domEle) {
24550 r.registerBinding(domEle, 'transitionend', invalidateCoords);
24551 r.registerBinding(domEle, 'animationend', invalidateCoords);
24552 r.registerBinding(domEle, 'scroll', invalidateCoords);
24553 }); // stop right click menu from appearing on cy
24554
24555 r.registerBinding(r.container, 'contextmenu', function (e) {
24556 e.preventDefault();
24557 });
24558
24559 var inBoxSelection = function inBoxSelection() {
24560 return r.selection[4] !== 0;
24561 };
24562
24563 var eventInContainer = function eventInContainer(e) {
24564 // save cycles if mouse events aren't to be captured
24565 var containerPageCoords = r.findContainerClientCoords();
24566 var x = containerPageCoords[0];
24567 var y = containerPageCoords[1];
24568 var width = containerPageCoords[2];
24569 var height = containerPageCoords[3];
24570 var positions = e.touches ? e.touches : [e];
24571 var atLeastOnePosInside = false;
24572
24573 for (var i = 0; i < positions.length; i++) {
24574 var p = positions[i];
24575
24576 if (x <= p.clientX && p.clientX <= x + width && y <= p.clientY && p.clientY <= y + height) {
24577 atLeastOnePosInside = true;
24578 break;
24579 }
24580 }
24581
24582 if (!atLeastOnePosInside) {
24583 return false;
24584 }
24585
24586 var container = r.container;
24587 var target = e.target;
24588 var tParent = target.parentNode;
24589 var containerIsTarget = false;
24590
24591 while (tParent) {
24592 if (tParent === container) {
24593 containerIsTarget = true;
24594 break;
24595 }
24596
24597 tParent = tParent.parentNode;
24598 }
24599
24600 if (!containerIsTarget) {
24601 return false;
24602 } // if target is outisde cy container, then this event is not for us
24603
24604
24605 return true;
24606 }; // Primary key
24607
24608
24609 r.registerBinding(r.container, 'mousedown', function mousedownHandler(e) {
24610 if (!eventInContainer(e)) {
24611 return;
24612 }
24613
24614 e.preventDefault();
24615 blurActiveDomElement();
24616 r.hoverData.capture = true;
24617 r.hoverData.which = e.which;
24618 var cy = r.cy;
24619 var gpos = [e.clientX, e.clientY];
24620 var pos = r.projectIntoViewport(gpos[0], gpos[1]);
24621 var select = r.selection;
24622 var nears = r.findNearestElements(pos[0], pos[1], true, false);
24623 var near = nears[0];
24624 var draggedElements = r.dragData.possibleDragElements;
24625 r.hoverData.mdownPos = pos;
24626 r.hoverData.mdownGPos = gpos;
24627
24628 var checkForTaphold = function checkForTaphold() {
24629 r.hoverData.tapholdCancelled = false;
24630 clearTimeout(r.hoverData.tapholdTimeout);
24631 r.hoverData.tapholdTimeout = setTimeout(function () {
24632 if (r.hoverData.tapholdCancelled) {
24633 return;
24634 } else {
24635 var ele = r.hoverData.down;
24636
24637 if (ele) {
24638 ele.emit({
24639 originalEvent: e,
24640 type: 'taphold',
24641 position: {
24642 x: pos[0],
24643 y: pos[1]
24644 }
24645 });
24646 } else {
24647 cy.emit({
24648 originalEvent: e,
24649 type: 'taphold',
24650 position: {
24651 x: pos[0],
24652 y: pos[1]
24653 }
24654 });
24655 }
24656 }
24657 }, r.tapholdDuration);
24658 }; // Right click button
24659
24660
24661 if (e.which == 3) {
24662 r.hoverData.cxtStarted = true;
24663 var cxtEvt = {
24664 originalEvent: e,
24665 type: 'cxttapstart',
24666 position: {
24667 x: pos[0],
24668 y: pos[1]
24669 }
24670 };
24671
24672 if (near) {
24673 near.activate();
24674 near.emit(cxtEvt);
24675 r.hoverData.down = near;
24676 } else {
24677 cy.emit(cxtEvt);
24678 }
24679
24680 r.hoverData.downTime = new Date().getTime();
24681 r.hoverData.cxtDragged = false; // Primary button
24682 } else if (e.which == 1) {
24683 if (near) {
24684 near.activate();
24685 } // Element dragging
24686
24687
24688 {
24689 // If something is under the cursor and it is draggable, prepare to grab it
24690 if (near != null) {
24691 if (r.nodeIsGrabbable(near)) {
24692 var makeEvent = function makeEvent(type) {
24693 return {
24694 originalEvent: e,
24695 type: type,
24696 position: {
24697 x: pos[0],
24698 y: pos[1]
24699 }
24700 };
24701 };
24702
24703 var triggerGrab = function triggerGrab(ele) {
24704 ele.emit(makeEvent('grab'));
24705 };
24706
24707 setGrabTarget(near);
24708
24709 if (!near.selected()) {
24710 draggedElements = r.dragData.possibleDragElements = cy.collection();
24711 addNodeToDrag(near, {
24712 addToList: draggedElements
24713 });
24714 near.emit(makeEvent('grabon')).emit(makeEvent('grab'));
24715 } else {
24716 draggedElements = r.dragData.possibleDragElements = cy.collection();
24717 var selectedNodes = cy.$(function (ele) {
24718 return ele.isNode() && ele.selected() && r.nodeIsGrabbable(ele);
24719 });
24720 addNodesToDrag(selectedNodes, {
24721 addToList: draggedElements
24722 });
24723 near.emit(makeEvent('grabon'));
24724 selectedNodes.forEach(triggerGrab);
24725 }
24726
24727 r.redrawHint('eles', true);
24728 r.redrawHint('drag', true);
24729 }
24730 }
24731
24732 r.hoverData.down = near;
24733 r.hoverData.downs = nears;
24734 r.hoverData.downTime = new Date().getTime();
24735 }
24736 triggerEvents(near, ['mousedown', 'tapstart', 'vmousedown'], e, {
24737 x: pos[0],
24738 y: pos[1]
24739 });
24740
24741 if (near == null) {
24742 select[4] = 1;
24743 r.data.bgActivePosistion = {
24744 x: pos[0],
24745 y: pos[1]
24746 };
24747 r.redrawHint('select', true);
24748 r.redraw();
24749 } else if (near.pannable()) {
24750 select[4] = 1; // for future pan
24751 }
24752
24753 checkForTaphold();
24754 } // Initialize selection box coordinates
24755
24756
24757 select[0] = select[2] = pos[0];
24758 select[1] = select[3] = pos[1];
24759 }, false);
24760 r.registerBinding(window, 'mousemove', function mousemoveHandler(e) {
24761 // eslint-disable-line no-undef
24762 var capture = r.hoverData.capture;
24763
24764 if (!capture && !eventInContainer(e)) {
24765 return;
24766 }
24767
24768 var preventDefault = false;
24769 var cy = r.cy;
24770 var zoom = cy.zoom();
24771 var gpos = [e.clientX, e.clientY];
24772 var pos = r.projectIntoViewport(gpos[0], gpos[1]);
24773 var mdownPos = r.hoverData.mdownPos;
24774 var mdownGPos = r.hoverData.mdownGPos;
24775 var select = r.selection;
24776 var near = null;
24777
24778 if (!r.hoverData.draggingEles && !r.hoverData.dragging && !r.hoverData.selecting) {
24779 near = r.findNearestElement(pos[0], pos[1], true, false);
24780 }
24781
24782 var last = r.hoverData.last;
24783 var down = r.hoverData.down;
24784 var disp = [pos[0] - select[2], pos[1] - select[3]];
24785 var draggedElements = r.dragData.possibleDragElements;
24786 var isOverThresholdDrag;
24787
24788 if (mdownGPos) {
24789 var dx = gpos[0] - mdownGPos[0];
24790 var dx2 = dx * dx;
24791 var dy = gpos[1] - mdownGPos[1];
24792 var dy2 = dy * dy;
24793 var dist2 = dx2 + dy2;
24794 r.hoverData.isOverThresholdDrag = isOverThresholdDrag = dist2 >= r.desktopTapThreshold2;
24795 }
24796
24797 var multSelKeyDown = isMultSelKeyDown(e);
24798
24799 if (isOverThresholdDrag) {
24800 r.hoverData.tapholdCancelled = true;
24801 }
24802
24803 var updateDragDelta = function updateDragDelta() {
24804 var dragDelta = r.hoverData.dragDelta = r.hoverData.dragDelta || [];
24805
24806 if (dragDelta.length === 0) {
24807 dragDelta.push(disp[0]);
24808 dragDelta.push(disp[1]);
24809 } else {
24810 dragDelta[0] += disp[0];
24811 dragDelta[1] += disp[1];
24812 }
24813 };
24814
24815 preventDefault = true;
24816 triggerEvents(near, ['mousemove', 'vmousemove', 'tapdrag'], e, {
24817 x: pos[0],
24818 y: pos[1]
24819 });
24820
24821 var goIntoBoxMode = function goIntoBoxMode() {
24822 r.data.bgActivePosistion = undefined;
24823
24824 if (!r.hoverData.selecting) {
24825 cy.emit({
24826 originalEvent: e,
24827 type: 'boxstart',
24828 position: {
24829 x: pos[0],
24830 y: pos[1]
24831 }
24832 });
24833 }
24834
24835 select[4] = 1;
24836 r.hoverData.selecting = true;
24837 r.redrawHint('select', true);
24838 r.redraw();
24839 }; // trigger context drag if rmouse down
24840
24841
24842 if (r.hoverData.which === 3) {
24843 // but only if over threshold
24844 if (isOverThresholdDrag) {
24845 var cxtEvt = {
24846 originalEvent: e,
24847 type: 'cxtdrag',
24848 position: {
24849 x: pos[0],
24850 y: pos[1]
24851 }
24852 };
24853
24854 if (down) {
24855 down.emit(cxtEvt);
24856 } else {
24857 cy.emit(cxtEvt);
24858 }
24859
24860 r.hoverData.cxtDragged = true;
24861
24862 if (!r.hoverData.cxtOver || near !== r.hoverData.cxtOver) {
24863 if (r.hoverData.cxtOver) {
24864 r.hoverData.cxtOver.emit({
24865 originalEvent: e,
24866 type: 'cxtdragout',
24867 position: {
24868 x: pos[0],
24869 y: pos[1]
24870 }
24871 });
24872 }
24873
24874 r.hoverData.cxtOver = near;
24875
24876 if (near) {
24877 near.emit({
24878 originalEvent: e,
24879 type: 'cxtdragover',
24880 position: {
24881 x: pos[0],
24882 y: pos[1]
24883 }
24884 });
24885 }
24886 }
24887 } // Check if we are drag panning the entire graph
24888
24889 } else if (r.hoverData.dragging) {
24890 preventDefault = true;
24891
24892 if (cy.panningEnabled() && cy.userPanningEnabled()) {
24893 var deltaP;
24894
24895 if (r.hoverData.justStartedPan) {
24896 var mdPos = r.hoverData.mdownPos;
24897 deltaP = {
24898 x: (pos[0] - mdPos[0]) * zoom,
24899 y: (pos[1] - mdPos[1]) * zoom
24900 };
24901 r.hoverData.justStartedPan = false;
24902 } else {
24903 deltaP = {
24904 x: disp[0] * zoom,
24905 y: disp[1] * zoom
24906 };
24907 }
24908
24909 cy.panBy(deltaP);
24910 cy.emit('dragpan');
24911 r.hoverData.dragged = true;
24912 } // Needs reproject due to pan changing viewport
24913
24914
24915 pos = r.projectIntoViewport(e.clientX, e.clientY); // Checks primary button down & out of time & mouse not moved much
24916 } else if (select[4] == 1 && (down == null || down.pannable())) {
24917 if (isOverThresholdDrag) {
24918 if (!r.hoverData.dragging && cy.boxSelectionEnabled() && (multSelKeyDown || !cy.panningEnabled() || !cy.userPanningEnabled())) {
24919 goIntoBoxMode();
24920 } else if (!r.hoverData.selecting && cy.panningEnabled() && cy.userPanningEnabled()) {
24921 var allowPassthrough = allowPanningPassthrough(down, r.hoverData.downs);
24922
24923 if (allowPassthrough) {
24924 r.hoverData.dragging = true;
24925 r.hoverData.justStartedPan = true;
24926 select[4] = 0;
24927 r.data.bgActivePosistion = array2point(mdownPos);
24928 r.redrawHint('select', true);
24929 r.redraw();
24930 }
24931 }
24932
24933 if (down && down.pannable() && down.active()) {
24934 down.unactivate();
24935 }
24936 }
24937 } else {
24938 if (down && down.pannable() && down.active()) {
24939 down.unactivate();
24940 }
24941
24942 if ((!down || !down.grabbed()) && near != last) {
24943 if (last) {
24944 triggerEvents(last, ['mouseout', 'tapdragout'], e, {
24945 x: pos[0],
24946 y: pos[1]
24947 });
24948 }
24949
24950 if (near) {
24951 triggerEvents(near, ['mouseover', 'tapdragover'], e, {
24952 x: pos[0],
24953 y: pos[1]
24954 });
24955 }
24956
24957 r.hoverData.last = near;
24958 }
24959
24960 if (down) {
24961 if (isOverThresholdDrag) {
24962 // then we can take action
24963 if (cy.boxSelectionEnabled() && multSelKeyDown) {
24964 // then selection overrides
24965 if (down && down.grabbed()) {
24966 freeDraggedElements(draggedElements);
24967 down.emit('freeon');
24968 draggedElements.emit('free');
24969
24970 if (r.dragData.didDrag) {
24971 down.emit('dragfreeon');
24972 draggedElements.emit('dragfree');
24973 }
24974 }
24975
24976 goIntoBoxMode();
24977 } else if (down && down.grabbed() && r.nodeIsDraggable(down)) {
24978 // drag node
24979 var justStartedDrag = !r.dragData.didDrag;
24980
24981 if (justStartedDrag) {
24982 r.redrawHint('eles', true);
24983 }
24984
24985 r.dragData.didDrag = true; // indicate that we actually did drag the node
24986 // now, add the elements to the drag layer if not done already
24987
24988 if (!r.hoverData.draggingEles) {
24989 addNodesToDrag(draggedElements, {
24990 inDragLayer: true
24991 });
24992 }
24993
24994 var totalShift = {
24995 x: 0,
24996 y: 0
24997 };
24998
24999 if (number$1(disp[0]) && number$1(disp[1])) {
25000 totalShift.x += disp[0];
25001 totalShift.y += disp[1];
25002
25003 if (justStartedDrag) {
25004 var dragDelta = r.hoverData.dragDelta;
25005
25006 if (dragDelta && number$1(dragDelta[0]) && number$1(dragDelta[1])) {
25007 totalShift.x += dragDelta[0];
25008 totalShift.y += dragDelta[1];
25009 }
25010 }
25011 }
25012
25013 r.hoverData.draggingEles = true;
25014 draggedElements.silentShift(totalShift).emit('position drag');
25015 r.redrawHint('drag', true);
25016 r.redraw();
25017 }
25018 } else {
25019 // otherwise save drag delta for when we actually start dragging so the relative grab pos is constant
25020 updateDragDelta();
25021 }
25022 } // prevent the dragging from triggering text selection on the page
25023
25024
25025 preventDefault = true;
25026 }
25027
25028 select[2] = pos[0];
25029 select[3] = pos[1];
25030
25031 if (preventDefault) {
25032 if (e.stopPropagation) e.stopPropagation();
25033 if (e.preventDefault) e.preventDefault();
25034 return false;
25035 }
25036 }, false);
25037 var clickTimeout, didDoubleClick, prevClickTimeStamp;
25038 r.registerBinding(window, 'mouseup', function mouseupHandler(e) {
25039 // eslint-disable-line no-undef
25040 var capture = r.hoverData.capture;
25041
25042 if (!capture) {
25043 return;
25044 }
25045
25046 r.hoverData.capture = false;
25047 var cy = r.cy;
25048 var pos = r.projectIntoViewport(e.clientX, e.clientY);
25049 var select = r.selection;
25050 var near = r.findNearestElement(pos[0], pos[1], true, false);
25051 var draggedElements = r.dragData.possibleDragElements;
25052 var down = r.hoverData.down;
25053 var multSelKeyDown = isMultSelKeyDown(e);
25054
25055 if (r.data.bgActivePosistion) {
25056 r.redrawHint('select', true);
25057 r.redraw();
25058 }
25059
25060 r.hoverData.tapholdCancelled = true;
25061 r.data.bgActivePosistion = undefined; // not active bg now
25062
25063 if (down) {
25064 down.unactivate();
25065 }
25066
25067 if (r.hoverData.which === 3) {
25068 var cxtEvt = {
25069 originalEvent: e,
25070 type: 'cxttapend',
25071 position: {
25072 x: pos[0],
25073 y: pos[1]
25074 }
25075 };
25076
25077 if (down) {
25078 down.emit(cxtEvt);
25079 } else {
25080 cy.emit(cxtEvt);
25081 }
25082
25083 if (!r.hoverData.cxtDragged) {
25084 var cxtTap = {
25085 originalEvent: e,
25086 type: 'cxttap',
25087 position: {
25088 x: pos[0],
25089 y: pos[1]
25090 }
25091 };
25092
25093 if (down) {
25094 down.emit(cxtTap);
25095 } else {
25096 cy.emit(cxtTap);
25097 }
25098 }
25099
25100 r.hoverData.cxtDragged = false;
25101 r.hoverData.which = null;
25102 } else if (r.hoverData.which === 1) {
25103 triggerEvents(near, ['mouseup', 'tapend', 'vmouseup'], e, {
25104 x: pos[0],
25105 y: pos[1]
25106 });
25107
25108 if (!r.dragData.didDrag && // didn't move a node around
25109 !r.hoverData.dragged && // didn't pan
25110 !r.hoverData.selecting && // not box selection
25111 !r.hoverData.isOverThresholdDrag // didn't move too much
25112 ) {
25113 triggerEvents(down, ["click", "tap", "vclick"], e, {
25114 x: pos[0],
25115 y: pos[1]
25116 });
25117 didDoubleClick = false;
25118
25119 if (e.timeStamp - prevClickTimeStamp <= cy.multiClickDebounceTime()) {
25120 clickTimeout && clearTimeout(clickTimeout);
25121 didDoubleClick = true;
25122 prevClickTimeStamp = null;
25123 triggerEvents(down, ["dblclick", "dbltap", "vdblclick"], e, {
25124 x: pos[0],
25125 y: pos[1]
25126 });
25127 } else {
25128 clickTimeout = setTimeout(function () {
25129 if (didDoubleClick) return;
25130 triggerEvents(down, ["oneclick", "onetap", "voneclick"], e, {
25131 x: pos[0],
25132 y: pos[1]
25133 });
25134 }, cy.multiClickDebounceTime());
25135 prevClickTimeStamp = e.timeStamp;
25136 }
25137 } // Deselect all elements if nothing is currently under the mouse cursor and we aren't dragging something
25138
25139
25140 if (down == null // not mousedown on node
25141 && !r.dragData.didDrag // didn't move the node around
25142 && !r.hoverData.selecting // not box selection
25143 && !r.hoverData.dragged // didn't pan
25144 && !isMultSelKeyDown(e)) {
25145 cy.$(isSelected).unselect(['tapunselect']);
25146
25147 if (draggedElements.length > 0) {
25148 r.redrawHint('eles', true);
25149 }
25150
25151 r.dragData.possibleDragElements = draggedElements = cy.collection();
25152 } // Single selection
25153
25154
25155 if (near == down && !r.dragData.didDrag && !r.hoverData.selecting) {
25156 if (near != null && near._private.selectable) {
25157 if (r.hoverData.dragging) ; else if (cy.selectionType() === 'additive' || multSelKeyDown) {
25158 if (near.selected()) {
25159 near.unselect(['tapunselect']);
25160 } else {
25161 near.select(['tapselect']);
25162 }
25163 } else {
25164 if (!multSelKeyDown) {
25165 cy.$(isSelected).unmerge(near).unselect(['tapunselect']);
25166 near.select(['tapselect']);
25167 }
25168 }
25169
25170 r.redrawHint('eles', true);
25171 }
25172 }
25173
25174 if (r.hoverData.selecting) {
25175 var box = cy.collection(r.getAllInBox(select[0], select[1], select[2], select[3]));
25176 r.redrawHint('select', true);
25177
25178 if (box.length > 0) {
25179 r.redrawHint('eles', true);
25180 }
25181
25182 cy.emit({
25183 type: 'boxend',
25184 originalEvent: e,
25185 position: {
25186 x: pos[0],
25187 y: pos[1]
25188 }
25189 });
25190
25191 var eleWouldBeSelected = function eleWouldBeSelected(ele) {
25192 return ele.selectable() && !ele.selected();
25193 };
25194
25195 if (cy.selectionType() === 'additive') {
25196 box.emit('box').stdFilter(eleWouldBeSelected).select().emit('boxselect');
25197 } else {
25198 if (!multSelKeyDown) {
25199 cy.$(isSelected).unmerge(box).unselect();
25200 }
25201
25202 box.emit('box').stdFilter(eleWouldBeSelected).select().emit('boxselect');
25203 } // always need redraw in case eles unselectable
25204
25205
25206 r.redraw();
25207 } // Cancel drag pan
25208
25209
25210 if (r.hoverData.dragging) {
25211 r.hoverData.dragging = false;
25212 r.redrawHint('select', true);
25213 r.redrawHint('eles', true);
25214 r.redraw();
25215 }
25216
25217 if (!select[4]) {
25218 r.redrawHint('drag', true);
25219 r.redrawHint('eles', true);
25220 var downWasGrabbed = down && down.grabbed();
25221 freeDraggedElements(draggedElements);
25222
25223 if (downWasGrabbed) {
25224 down.emit('freeon');
25225 draggedElements.emit('free');
25226
25227 if (r.dragData.didDrag) {
25228 down.emit('dragfreeon');
25229 draggedElements.emit('dragfree');
25230 }
25231 }
25232 }
25233 } // else not right mouse
25234
25235
25236 select[4] = 0;
25237 r.hoverData.down = null;
25238 r.hoverData.cxtStarted = false;
25239 r.hoverData.draggingEles = false;
25240 r.hoverData.selecting = false;
25241 r.hoverData.isOverThresholdDrag = false;
25242 r.dragData.didDrag = false;
25243 r.hoverData.dragged = false;
25244 r.hoverData.dragDelta = [];
25245 r.hoverData.mdownPos = null;
25246 r.hoverData.mdownGPos = null;
25247 }, false);
25248
25249 var wheelHandler = function wheelHandler(e) {
25250 if (r.scrollingPage) {
25251 return;
25252 } // while scrolling, ignore wheel-to-zoom
25253
25254
25255 var cy = r.cy;
25256 var zoom = cy.zoom();
25257 var pan = cy.pan();
25258 var pos = r.projectIntoViewport(e.clientX, e.clientY);
25259 var rpos = [pos[0] * zoom + pan.x, pos[1] * zoom + pan.y];
25260
25261 if (r.hoverData.draggingEles || r.hoverData.dragging || r.hoverData.cxtStarted || inBoxSelection()) {
25262 // if pan dragging or cxt dragging, wheel movements make no zoom
25263 e.preventDefault();
25264 return;
25265 }
25266
25267 if (cy.panningEnabled() && cy.userPanningEnabled() && cy.zoomingEnabled() && cy.userZoomingEnabled()) {
25268 e.preventDefault();
25269 r.data.wheelZooming = true;
25270 clearTimeout(r.data.wheelTimeout);
25271 r.data.wheelTimeout = setTimeout(function () {
25272 r.data.wheelZooming = false;
25273 r.redrawHint('eles', true);
25274 r.redraw();
25275 }, 150);
25276 var diff;
25277
25278 if (e.deltaY != null) {
25279 diff = e.deltaY / -250;
25280 } else if (e.wheelDeltaY != null) {
25281 diff = e.wheelDeltaY / 1000;
25282 } else {
25283 diff = e.wheelDelta / 1000;
25284 }
25285
25286 diff = diff * r.wheelSensitivity;
25287 var needsWheelFix = e.deltaMode === 1;
25288
25289 if (needsWheelFix) {
25290 // fixes slow wheel events on ff/linux and ff/windows
25291 diff *= 33;
25292 }
25293
25294 var newZoom = cy.zoom() * Math.pow(10, diff);
25295
25296 if (e.type === 'gesturechange') {
25297 newZoom = r.gestureStartZoom * e.scale;
25298 }
25299
25300 cy.zoom({
25301 level: newZoom,
25302 renderedPosition: {
25303 x: rpos[0],
25304 y: rpos[1]
25305 }
25306 });
25307 cy.emit(e.type === 'gesturechange' ? 'pinchzoom' : 'scrollzoom');
25308 }
25309 }; // Functions to help with whether mouse wheel should trigger zooming
25310 // --
25311
25312
25313 r.registerBinding(r.container, 'wheel', wheelHandler, true); // disable nonstandard wheel events
25314 // r.registerBinding(r.container, 'mousewheel', wheelHandler, true);
25315 // r.registerBinding(r.container, 'DOMMouseScroll', wheelHandler, true);
25316 // r.registerBinding(r.container, 'MozMousePixelScroll', wheelHandler, true); // older firefox
25317
25318 r.registerBinding(window, 'scroll', function scrollHandler(e) {
25319 // eslint-disable-line no-unused-vars
25320 r.scrollingPage = true;
25321 clearTimeout(r.scrollingPageTimeout);
25322 r.scrollingPageTimeout = setTimeout(function () {
25323 r.scrollingPage = false;
25324 }, 250);
25325 }, true); // desktop safari pinch to zoom start
25326
25327 r.registerBinding(r.container, 'gesturestart', function gestureStartHandler(e) {
25328 r.gestureStartZoom = r.cy.zoom();
25329
25330 if (!r.hasTouchStarted) {
25331 // don't affect touch devices like iphone
25332 e.preventDefault();
25333 }
25334 }, true);
25335 r.registerBinding(r.container, 'gesturechange', function (e) {
25336 if (!r.hasTouchStarted) {
25337 // don't affect touch devices like iphone
25338 wheelHandler(e);
25339 }
25340 }, true); // Functions to help with handling mouseout/mouseover on the Cytoscape container
25341 // Handle mouseout on Cytoscape container
25342
25343 r.registerBinding(r.container, 'mouseout', function mouseOutHandler(e) {
25344 var pos = r.projectIntoViewport(e.clientX, e.clientY);
25345 r.cy.emit({
25346 originalEvent: e,
25347 type: 'mouseout',
25348 position: {
25349 x: pos[0],
25350 y: pos[1]
25351 }
25352 });
25353 }, false);
25354 r.registerBinding(r.container, 'mouseover', function mouseOverHandler(e) {
25355 var pos = r.projectIntoViewport(e.clientX, e.clientY);
25356 r.cy.emit({
25357 originalEvent: e,
25358 type: 'mouseover',
25359 position: {
25360 x: pos[0],
25361 y: pos[1]
25362 }
25363 });
25364 }, false);
25365 var f1x1, f1y1, f2x1, f2y1; // starting points for pinch-to-zoom
25366
25367 var distance1, distance1Sq; // initial distance between finger 1 and finger 2 for pinch-to-zoom
25368
25369 var center1, modelCenter1; // center point on start pinch to zoom
25370
25371 var offsetLeft, offsetTop;
25372 var containerWidth, containerHeight;
25373 var twoFingersStartInside;
25374
25375 var distance = function distance(x1, y1, x2, y2) {
25376 return Math.sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1));
25377 };
25378
25379 var distanceSq = function distanceSq(x1, y1, x2, y2) {
25380 return (x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1);
25381 };
25382
25383 var touchstartHandler;
25384 r.registerBinding(r.container, 'touchstart', touchstartHandler = function touchstartHandler(e) {
25385 r.hasTouchStarted = true;
25386
25387 if (!eventInContainer(e)) {
25388 return;
25389 }
25390
25391 blurActiveDomElement();
25392 r.touchData.capture = true;
25393 r.data.bgActivePosistion = undefined;
25394 var cy = r.cy;
25395 var now = r.touchData.now;
25396 var earlier = r.touchData.earlier;
25397
25398 if (e.touches[0]) {
25399 var pos = r.projectIntoViewport(e.touches[0].clientX, e.touches[0].clientY);
25400 now[0] = pos[0];
25401 now[1] = pos[1];
25402 }
25403
25404 if (e.touches[1]) {
25405 var pos = r.projectIntoViewport(e.touches[1].clientX, e.touches[1].clientY);
25406 now[2] = pos[0];
25407 now[3] = pos[1];
25408 }
25409
25410 if (e.touches[2]) {
25411 var pos = r.projectIntoViewport(e.touches[2].clientX, e.touches[2].clientY);
25412 now[4] = pos[0];
25413 now[5] = pos[1];
25414 } // record starting points for pinch-to-zoom
25415
25416
25417 if (e.touches[1]) {
25418 r.touchData.singleTouchMoved = true;
25419 freeDraggedElements(r.dragData.touchDragEles);
25420 var offsets = r.findContainerClientCoords();
25421 offsetLeft = offsets[0];
25422 offsetTop = offsets[1];
25423 containerWidth = offsets[2];
25424 containerHeight = offsets[3];
25425 f1x1 = e.touches[0].clientX - offsetLeft;
25426 f1y1 = e.touches[0].clientY - offsetTop;
25427 f2x1 = e.touches[1].clientX - offsetLeft;
25428 f2y1 = e.touches[1].clientY - offsetTop;
25429 twoFingersStartInside = 0 <= f1x1 && f1x1 <= containerWidth && 0 <= f2x1 && f2x1 <= containerWidth && 0 <= f1y1 && f1y1 <= containerHeight && 0 <= f2y1 && f2y1 <= containerHeight;
25430 var pan = cy.pan();
25431 var zoom = cy.zoom();
25432 distance1 = distance(f1x1, f1y1, f2x1, f2y1);
25433 distance1Sq = distanceSq(f1x1, f1y1, f2x1, f2y1);
25434 center1 = [(f1x1 + f2x1) / 2, (f1y1 + f2y1) / 2];
25435 modelCenter1 = [(center1[0] - pan.x) / zoom, (center1[1] - pan.y) / zoom]; // consider context tap
25436
25437 var cxtDistThreshold = 200;
25438 var cxtDistThresholdSq = cxtDistThreshold * cxtDistThreshold;
25439
25440 if (distance1Sq < cxtDistThresholdSq && !e.touches[2]) {
25441 var near1 = r.findNearestElement(now[0], now[1], true, true);
25442 var near2 = r.findNearestElement(now[2], now[3], true, true);
25443
25444 if (near1 && near1.isNode()) {
25445 near1.activate().emit({
25446 originalEvent: e,
25447 type: 'cxttapstart',
25448 position: {
25449 x: now[0],
25450 y: now[1]
25451 }
25452 });
25453 r.touchData.start = near1;
25454 } else if (near2 && near2.isNode()) {
25455 near2.activate().emit({
25456 originalEvent: e,
25457 type: 'cxttapstart',
25458 position: {
25459 x: now[0],
25460 y: now[1]
25461 }
25462 });
25463 r.touchData.start = near2;
25464 } else {
25465 cy.emit({
25466 originalEvent: e,
25467 type: 'cxttapstart',
25468 position: {
25469 x: now[0],
25470 y: now[1]
25471 }
25472 });
25473 }
25474
25475 if (r.touchData.start) {
25476 r.touchData.start._private.grabbed = false;
25477 }
25478
25479 r.touchData.cxt = true;
25480 r.touchData.cxtDragged = false;
25481 r.data.bgActivePosistion = undefined;
25482 r.redraw();
25483 return;
25484 }
25485 }
25486
25487 if (e.touches[2]) {
25488 // ignore
25489 // safari on ios pans the page otherwise (normally you should be able to preventdefault on touchmove...)
25490 if (cy.boxSelectionEnabled()) {
25491 e.preventDefault();
25492 }
25493 } else if (e.touches[1]) ; else if (e.touches[0]) {
25494 var nears = r.findNearestElements(now[0], now[1], true, true);
25495 var near = nears[0];
25496
25497 if (near != null) {
25498 near.activate();
25499 r.touchData.start = near;
25500 r.touchData.starts = nears;
25501
25502 if (r.nodeIsGrabbable(near)) {
25503 var draggedEles = r.dragData.touchDragEles = cy.collection();
25504 var selectedNodes = null;
25505 r.redrawHint('eles', true);
25506 r.redrawHint('drag', true);
25507
25508 if (near.selected()) {
25509 // reset drag elements, since near will be added again
25510 selectedNodes = cy.$(function (ele) {
25511 return ele.selected() && r.nodeIsGrabbable(ele);
25512 });
25513 addNodesToDrag(selectedNodes, {
25514 addToList: draggedEles
25515 });
25516 } else {
25517 addNodeToDrag(near, {
25518 addToList: draggedEles
25519 });
25520 }
25521
25522 setGrabTarget(near);
25523
25524 var makeEvent = function makeEvent(type) {
25525 return {
25526 originalEvent: e,
25527 type: type,
25528 position: {
25529 x: now[0],
25530 y: now[1]
25531 }
25532 };
25533 };
25534
25535 near.emit(makeEvent('grabon'));
25536
25537 if (selectedNodes) {
25538 selectedNodes.forEach(function (n) {
25539 n.emit(makeEvent('grab'));
25540 });
25541 } else {
25542 near.emit(makeEvent('grab'));
25543 }
25544 }
25545 }
25546
25547 triggerEvents(near, ['touchstart', 'tapstart', 'vmousedown'], e, {
25548 x: now[0],
25549 y: now[1]
25550 });
25551
25552 if (near == null) {
25553 r.data.bgActivePosistion = {
25554 x: pos[0],
25555 y: pos[1]
25556 };
25557 r.redrawHint('select', true);
25558 r.redraw();
25559 } // Tap, taphold
25560 // -----
25561
25562
25563 r.touchData.singleTouchMoved = false;
25564 r.touchData.singleTouchStartTime = +new Date();
25565 clearTimeout(r.touchData.tapholdTimeout);
25566 r.touchData.tapholdTimeout = setTimeout(function () {
25567 if (r.touchData.singleTouchMoved === false && !r.pinching // if pinching, then taphold unselect shouldn't take effect
25568 && !r.touchData.selecting // box selection shouldn't allow taphold through
25569 ) {
25570 triggerEvents(r.touchData.start, ['taphold'], e, {
25571 x: now[0],
25572 y: now[1]
25573 });
25574 }
25575 }, r.tapholdDuration);
25576 }
25577
25578 if (e.touches.length >= 1) {
25579 var sPos = r.touchData.startPosition = [];
25580
25581 for (var i = 0; i < now.length; i++) {
25582 sPos[i] = earlier[i] = now[i];
25583 }
25584
25585 var touch0 = e.touches[0];
25586 r.touchData.startGPosition = [touch0.clientX, touch0.clientY];
25587 }
25588 }, false);
25589 var touchmoveHandler;
25590 r.registerBinding(window, 'touchmove', touchmoveHandler = function touchmoveHandler(e) {
25591 // eslint-disable-line no-undef
25592 var capture = r.touchData.capture;
25593
25594 if (!capture && !eventInContainer(e)) {
25595 return;
25596 }
25597
25598 var select = r.selection;
25599 var cy = r.cy;
25600 var now = r.touchData.now;
25601 var earlier = r.touchData.earlier;
25602 var zoom = cy.zoom();
25603
25604 if (e.touches[0]) {
25605 var pos = r.projectIntoViewport(e.touches[0].clientX, e.touches[0].clientY);
25606 now[0] = pos[0];
25607 now[1] = pos[1];
25608 }
25609
25610 if (e.touches[1]) {
25611 var pos = r.projectIntoViewport(e.touches[1].clientX, e.touches[1].clientY);
25612 now[2] = pos[0];
25613 now[3] = pos[1];
25614 }
25615
25616 if (e.touches[2]) {
25617 var pos = r.projectIntoViewport(e.touches[2].clientX, e.touches[2].clientY);
25618 now[4] = pos[0];
25619 now[5] = pos[1];
25620 }
25621
25622 var startGPos = r.touchData.startGPosition;
25623 var isOverThresholdDrag;
25624
25625 if (capture && e.touches[0] && startGPos) {
25626 var disp = [];
25627
25628 for (var j = 0; j < now.length; j++) {
25629 disp[j] = now[j] - earlier[j];
25630 }
25631
25632 var dx = e.touches[0].clientX - startGPos[0];
25633 var dx2 = dx * dx;
25634 var dy = e.touches[0].clientY - startGPos[1];
25635 var dy2 = dy * dy;
25636 var dist2 = dx2 + dy2;
25637 isOverThresholdDrag = dist2 >= r.touchTapThreshold2;
25638 } // context swipe cancelling
25639
25640
25641 if (capture && r.touchData.cxt) {
25642 e.preventDefault();
25643 var f1x2 = e.touches[0].clientX - offsetLeft,
25644 f1y2 = e.touches[0].clientY - offsetTop;
25645 var f2x2 = e.touches[1].clientX - offsetLeft,
25646 f2y2 = e.touches[1].clientY - offsetTop; // var distance2 = distance( f1x2, f1y2, f2x2, f2y2 );
25647
25648 var distance2Sq = distanceSq(f1x2, f1y2, f2x2, f2y2);
25649 var factorSq = distance2Sq / distance1Sq;
25650 var distThreshold = 150;
25651 var distThresholdSq = distThreshold * distThreshold;
25652 var factorThreshold = 1.5;
25653 var factorThresholdSq = factorThreshold * factorThreshold; // cancel ctx gestures if the distance b/t the fingers increases
25654
25655 if (factorSq >= factorThresholdSq || distance2Sq >= distThresholdSq) {
25656 r.touchData.cxt = false;
25657 r.data.bgActivePosistion = undefined;
25658 r.redrawHint('select', true);
25659 var cxtEvt = {
25660 originalEvent: e,
25661 type: 'cxttapend',
25662 position: {
25663 x: now[0],
25664 y: now[1]
25665 }
25666 };
25667
25668 if (r.touchData.start) {
25669 r.touchData.start.unactivate().emit(cxtEvt);
25670 r.touchData.start = null;
25671 } else {
25672 cy.emit(cxtEvt);
25673 }
25674 }
25675 } // context swipe
25676
25677
25678 if (capture && r.touchData.cxt) {
25679 var cxtEvt = {
25680 originalEvent: e,
25681 type: 'cxtdrag',
25682 position: {
25683 x: now[0],
25684 y: now[1]
25685 }
25686 };
25687 r.data.bgActivePosistion = undefined;
25688 r.redrawHint('select', true);
25689
25690 if (r.touchData.start) {
25691 r.touchData.start.emit(cxtEvt);
25692 } else {
25693 cy.emit(cxtEvt);
25694 }
25695
25696 if (r.touchData.start) {
25697 r.touchData.start._private.grabbed = false;
25698 }
25699
25700 r.touchData.cxtDragged = true;
25701 var near = r.findNearestElement(now[0], now[1], true, true);
25702
25703 if (!r.touchData.cxtOver || near !== r.touchData.cxtOver) {
25704 if (r.touchData.cxtOver) {
25705 r.touchData.cxtOver.emit({
25706 originalEvent: e,
25707 type: 'cxtdragout',
25708 position: {
25709 x: now[0],
25710 y: now[1]
25711 }
25712 });
25713 }
25714
25715 r.touchData.cxtOver = near;
25716
25717 if (near) {
25718 near.emit({
25719 originalEvent: e,
25720 type: 'cxtdragover',
25721 position: {
25722 x: now[0],
25723 y: now[1]
25724 }
25725 });
25726 }
25727 } // box selection
25728
25729 } else if (capture && e.touches[2] && cy.boxSelectionEnabled()) {
25730 e.preventDefault();
25731 r.data.bgActivePosistion = undefined;
25732 this.lastThreeTouch = +new Date();
25733
25734 if (!r.touchData.selecting) {
25735 cy.emit({
25736 originalEvent: e,
25737 type: 'boxstart',
25738 position: {
25739 x: now[0],
25740 y: now[1]
25741 }
25742 });
25743 }
25744
25745 r.touchData.selecting = true;
25746 r.touchData.didSelect = true;
25747 select[4] = 1;
25748
25749 if (!select || select.length === 0 || select[0] === undefined) {
25750 select[0] = (now[0] + now[2] + now[4]) / 3;
25751 select[1] = (now[1] + now[3] + now[5]) / 3;
25752 select[2] = (now[0] + now[2] + now[4]) / 3 + 1;
25753 select[3] = (now[1] + now[3] + now[5]) / 3 + 1;
25754 } else {
25755 select[2] = (now[0] + now[2] + now[4]) / 3;
25756 select[3] = (now[1] + now[3] + now[5]) / 3;
25757 }
25758
25759 r.redrawHint('select', true);
25760 r.redraw(); // pinch to zoom
25761 } else if (capture && e.touches[1] && !r.touchData.didSelect // don't allow box selection to degrade to pinch-to-zoom
25762 && cy.zoomingEnabled() && cy.panningEnabled() && cy.userZoomingEnabled() && cy.userPanningEnabled()) {
25763 // two fingers => pinch to zoom
25764 e.preventDefault();
25765 r.data.bgActivePosistion = undefined;
25766 r.redrawHint('select', true);
25767 var draggedEles = r.dragData.touchDragEles;
25768
25769 if (draggedEles) {
25770 r.redrawHint('drag', true);
25771
25772 for (var i = 0; i < draggedEles.length; i++) {
25773 var de_p = draggedEles[i]._private;
25774 de_p.grabbed = false;
25775 de_p.rscratch.inDragLayer = false;
25776 }
25777 }
25778
25779 var _start = r.touchData.start; // (x2, y2) for fingers 1 and 2
25780
25781 var f1x2 = e.touches[0].clientX - offsetLeft,
25782 f1y2 = e.touches[0].clientY - offsetTop;
25783 var f2x2 = e.touches[1].clientX - offsetLeft,
25784 f2y2 = e.touches[1].clientY - offsetTop;
25785 var distance2 = distance(f1x2, f1y2, f2x2, f2y2); // var distance2Sq = distanceSq( f1x2, f1y2, f2x2, f2y2 );
25786 // var factor = Math.sqrt( distance2Sq ) / Math.sqrt( distance1Sq );
25787
25788 var factor = distance2 / distance1;
25789
25790 if (twoFingersStartInside) {
25791 // delta finger1
25792 var df1x = f1x2 - f1x1;
25793 var df1y = f1y2 - f1y1; // delta finger 2
25794
25795 var df2x = f2x2 - f2x1;
25796 var df2y = f2y2 - f2y1; // translation is the normalised vector of the two fingers movement
25797 // i.e. so pinching cancels out and moving together pans
25798
25799 var tx = (df1x + df2x) / 2;
25800 var ty = (df1y + df2y) / 2; // now calculate the zoom
25801
25802 var zoom1 = cy.zoom();
25803 var zoom2 = zoom1 * factor;
25804 var pan1 = cy.pan(); // the model center point converted to the current rendered pos
25805
25806 var ctrx = modelCenter1[0] * zoom1 + pan1.x;
25807 var ctry = modelCenter1[1] * zoom1 + pan1.y;
25808 var pan2 = {
25809 x: -zoom2 / zoom1 * (ctrx - pan1.x - tx) + ctrx,
25810 y: -zoom2 / zoom1 * (ctry - pan1.y - ty) + ctry
25811 }; // remove dragged eles
25812
25813 if (_start && _start.active()) {
25814 var draggedEles = r.dragData.touchDragEles;
25815 freeDraggedElements(draggedEles);
25816 r.redrawHint('drag', true);
25817 r.redrawHint('eles', true);
25818
25819 _start.unactivate().emit('freeon');
25820
25821 draggedEles.emit('free');
25822
25823 if (r.dragData.didDrag) {
25824 _start.emit('dragfreeon');
25825
25826 draggedEles.emit('dragfree');
25827 }
25828 }
25829
25830 cy.viewport({
25831 zoom: zoom2,
25832 pan: pan2,
25833 cancelOnFailedZoom: true
25834 });
25835 cy.emit('pinchzoom');
25836 distance1 = distance2;
25837 f1x1 = f1x2;
25838 f1y1 = f1y2;
25839 f2x1 = f2x2;
25840 f2y1 = f2y2;
25841 r.pinching = true;
25842 } // Re-project
25843
25844
25845 if (e.touches[0]) {
25846 var pos = r.projectIntoViewport(e.touches[0].clientX, e.touches[0].clientY);
25847 now[0] = pos[0];
25848 now[1] = pos[1];
25849 }
25850
25851 if (e.touches[1]) {
25852 var pos = r.projectIntoViewport(e.touches[1].clientX, e.touches[1].clientY);
25853 now[2] = pos[0];
25854 now[3] = pos[1];
25855 }
25856
25857 if (e.touches[2]) {
25858 var pos = r.projectIntoViewport(e.touches[2].clientX, e.touches[2].clientY);
25859 now[4] = pos[0];
25860 now[5] = pos[1];
25861 }
25862 } else if (e.touches[0] && !r.touchData.didSelect // don't allow box selection to degrade to single finger events like panning
25863 ) {
25864 var start = r.touchData.start;
25865 var last = r.touchData.last;
25866 var near;
25867
25868 if (!r.hoverData.draggingEles && !r.swipePanning) {
25869 near = r.findNearestElement(now[0], now[1], true, true);
25870 }
25871
25872 if (capture && start != null) {
25873 e.preventDefault();
25874 } // dragging nodes
25875
25876
25877 if (capture && start != null && r.nodeIsDraggable(start)) {
25878 if (isOverThresholdDrag) {
25879 // then dragging can happen
25880 var draggedEles = r.dragData.touchDragEles;
25881 var justStartedDrag = !r.dragData.didDrag;
25882
25883 if (justStartedDrag) {
25884 addNodesToDrag(draggedEles, {
25885 inDragLayer: true
25886 });
25887 }
25888
25889 r.dragData.didDrag = true;
25890 var totalShift = {
25891 x: 0,
25892 y: 0
25893 };
25894
25895 if (number$1(disp[0]) && number$1(disp[1])) {
25896 totalShift.x += disp[0];
25897 totalShift.y += disp[1];
25898
25899 if (justStartedDrag) {
25900 r.redrawHint('eles', true);
25901 var dragDelta = r.touchData.dragDelta;
25902
25903 if (dragDelta && number$1(dragDelta[0]) && number$1(dragDelta[1])) {
25904 totalShift.x += dragDelta[0];
25905 totalShift.y += dragDelta[1];
25906 }
25907 }
25908 }
25909
25910 r.hoverData.draggingEles = true;
25911 draggedEles.silentShift(totalShift).emit('position drag');
25912 r.redrawHint('drag', true);
25913
25914 if (r.touchData.startPosition[0] == earlier[0] && r.touchData.startPosition[1] == earlier[1]) {
25915 r.redrawHint('eles', true);
25916 }
25917
25918 r.redraw();
25919 } else {
25920 // otherise keep track of drag delta for later
25921 var dragDelta = r.touchData.dragDelta = r.touchData.dragDelta || [];
25922
25923 if (dragDelta.length === 0) {
25924 dragDelta.push(disp[0]);
25925 dragDelta.push(disp[1]);
25926 } else {
25927 dragDelta[0] += disp[0];
25928 dragDelta[1] += disp[1];
25929 }
25930 }
25931 } // touchmove
25932
25933
25934 {
25935 triggerEvents(start || near, ['touchmove', 'tapdrag', 'vmousemove'], e, {
25936 x: now[0],
25937 y: now[1]
25938 });
25939
25940 if ((!start || !start.grabbed()) && near != last) {
25941 if (last) {
25942 last.emit({
25943 originalEvent: e,
25944 type: 'tapdragout',
25945 position: {
25946 x: now[0],
25947 y: now[1]
25948 }
25949 });
25950 }
25951
25952 if (near) {
25953 near.emit({
25954 originalEvent: e,
25955 type: 'tapdragover',
25956 position: {
25957 x: now[0],
25958 y: now[1]
25959 }
25960 });
25961 }
25962 }
25963
25964 r.touchData.last = near;
25965 } // check to cancel taphold
25966
25967 if (capture) {
25968 for (var i = 0; i < now.length; i++) {
25969 if (now[i] && r.touchData.startPosition[i] && isOverThresholdDrag) {
25970 r.touchData.singleTouchMoved = true;
25971 }
25972 }
25973 } // panning
25974
25975
25976 if (capture && (start == null || start.pannable()) && cy.panningEnabled() && cy.userPanningEnabled()) {
25977 var allowPassthrough = allowPanningPassthrough(start, r.touchData.starts);
25978
25979 if (allowPassthrough) {
25980 e.preventDefault();
25981
25982 if (!r.data.bgActivePosistion) {
25983 r.data.bgActivePosistion = array2point(r.touchData.startPosition);
25984 }
25985
25986 if (r.swipePanning) {
25987 cy.panBy({
25988 x: disp[0] * zoom,
25989 y: disp[1] * zoom
25990 });
25991 cy.emit('dragpan');
25992 } else if (isOverThresholdDrag) {
25993 r.swipePanning = true;
25994 cy.panBy({
25995 x: dx * zoom,
25996 y: dy * zoom
25997 });
25998 cy.emit('dragpan');
25999
26000 if (start) {
26001 start.unactivate();
26002 r.redrawHint('select', true);
26003 r.touchData.start = null;
26004 }
26005 }
26006 } // Re-project
26007
26008
26009 var pos = r.projectIntoViewport(e.touches[0].clientX, e.touches[0].clientY);
26010 now[0] = pos[0];
26011 now[1] = pos[1];
26012 }
26013 }
26014
26015 for (var j = 0; j < now.length; j++) {
26016 earlier[j] = now[j];
26017 } // the active bg indicator should be removed when making a swipe that is neither for dragging nodes or panning
26018
26019
26020 if (capture && e.touches.length > 0 && !r.hoverData.draggingEles && !r.swipePanning && r.data.bgActivePosistion != null) {
26021 r.data.bgActivePosistion = undefined;
26022 r.redrawHint('select', true);
26023 r.redraw();
26024 }
26025 }, false);
26026 var touchcancelHandler;
26027 r.registerBinding(window, 'touchcancel', touchcancelHandler = function touchcancelHandler(e) {
26028 // eslint-disable-line no-unused-vars
26029 var start = r.touchData.start;
26030 r.touchData.capture = false;
26031
26032 if (start) {
26033 start.unactivate();
26034 }
26035 });
26036 var touchendHandler, didDoubleTouch, touchTimeout, prevTouchTimeStamp;
26037 r.registerBinding(window, 'touchend', touchendHandler = function touchendHandler(e) {
26038 // eslint-disable-line no-unused-vars
26039 var start = r.touchData.start;
26040 var capture = r.touchData.capture;
26041
26042 if (capture) {
26043 if (e.touches.length === 0) {
26044 r.touchData.capture = false;
26045 }
26046
26047 e.preventDefault();
26048 } else {
26049 return;
26050 }
26051
26052 var select = r.selection;
26053 r.swipePanning = false;
26054 r.hoverData.draggingEles = false;
26055 var cy = r.cy;
26056 var zoom = cy.zoom();
26057 var now = r.touchData.now;
26058 var earlier = r.touchData.earlier;
26059
26060 if (e.touches[0]) {
26061 var pos = r.projectIntoViewport(e.touches[0].clientX, e.touches[0].clientY);
26062 now[0] = pos[0];
26063 now[1] = pos[1];
26064 }
26065
26066 if (e.touches[1]) {
26067 var pos = r.projectIntoViewport(e.touches[1].clientX, e.touches[1].clientY);
26068 now[2] = pos[0];
26069 now[3] = pos[1];
26070 }
26071
26072 if (e.touches[2]) {
26073 var pos = r.projectIntoViewport(e.touches[2].clientX, e.touches[2].clientY);
26074 now[4] = pos[0];
26075 now[5] = pos[1];
26076 }
26077
26078 if (start) {
26079 start.unactivate();
26080 }
26081
26082 var ctxTapend;
26083
26084 if (r.touchData.cxt) {
26085 ctxTapend = {
26086 originalEvent: e,
26087 type: 'cxttapend',
26088 position: {
26089 x: now[0],
26090 y: now[1]
26091 }
26092 };
26093
26094 if (start) {
26095 start.emit(ctxTapend);
26096 } else {
26097 cy.emit(ctxTapend);
26098 }
26099
26100 if (!r.touchData.cxtDragged) {
26101 var ctxTap = {
26102 originalEvent: e,
26103 type: 'cxttap',
26104 position: {
26105 x: now[0],
26106 y: now[1]
26107 }
26108 };
26109
26110 if (start) {
26111 start.emit(ctxTap);
26112 } else {
26113 cy.emit(ctxTap);
26114 }
26115 }
26116
26117 if (r.touchData.start) {
26118 r.touchData.start._private.grabbed = false;
26119 }
26120
26121 r.touchData.cxt = false;
26122 r.touchData.start = null;
26123 r.redraw();
26124 return;
26125 } // no more box selection if we don't have three fingers
26126
26127
26128 if (!e.touches[2] && cy.boxSelectionEnabled() && r.touchData.selecting) {
26129 r.touchData.selecting = false;
26130 var box = cy.collection(r.getAllInBox(select[0], select[1], select[2], select[3]));
26131 select[0] = undefined;
26132 select[1] = undefined;
26133 select[2] = undefined;
26134 select[3] = undefined;
26135 select[4] = 0;
26136 r.redrawHint('select', true);
26137 cy.emit({
26138 type: 'boxend',
26139 originalEvent: e,
26140 position: {
26141 x: now[0],
26142 y: now[1]
26143 }
26144 });
26145
26146 var eleWouldBeSelected = function eleWouldBeSelected(ele) {
26147 return ele.selectable() && !ele.selected();
26148 };
26149
26150 box.emit('box').stdFilter(eleWouldBeSelected).select().emit('boxselect');
26151
26152 if (box.nonempty()) {
26153 r.redrawHint('eles', true);
26154 }
26155
26156 r.redraw();
26157 }
26158
26159 if (start != null) {
26160 start.unactivate();
26161 }
26162
26163 if (e.touches[2]) {
26164 r.data.bgActivePosistion = undefined;
26165 r.redrawHint('select', true);
26166 } else if (e.touches[1]) ; else if (e.touches[0]) ; else if (!e.touches[0]) {
26167 r.data.bgActivePosistion = undefined;
26168 r.redrawHint('select', true);
26169 var draggedEles = r.dragData.touchDragEles;
26170
26171 if (start != null) {
26172 var startWasGrabbed = start._private.grabbed;
26173 freeDraggedElements(draggedEles);
26174 r.redrawHint('drag', true);
26175 r.redrawHint('eles', true);
26176
26177 if (startWasGrabbed) {
26178 start.emit('freeon');
26179 draggedEles.emit('free');
26180
26181 if (r.dragData.didDrag) {
26182 start.emit('dragfreeon');
26183 draggedEles.emit('dragfree');
26184 }
26185 }
26186
26187 triggerEvents(start, ['touchend', 'tapend', 'vmouseup', 'tapdragout'], e, {
26188 x: now[0],
26189 y: now[1]
26190 });
26191 start.unactivate();
26192 r.touchData.start = null;
26193 } else {
26194 var near = r.findNearestElement(now[0], now[1], true, true);
26195 triggerEvents(near, ['touchend', 'tapend', 'vmouseup', 'tapdragout'], e, {
26196 x: now[0],
26197 y: now[1]
26198 });
26199 }
26200
26201 var dx = r.touchData.startPosition[0] - now[0];
26202 var dx2 = dx * dx;
26203 var dy = r.touchData.startPosition[1] - now[1];
26204 var dy2 = dy * dy;
26205 var dist2 = dx2 + dy2;
26206 var rdist2 = dist2 * zoom * zoom; // Tap event, roughly same as mouse click event for touch
26207
26208 if (!r.touchData.singleTouchMoved) {
26209 if (!start) {
26210 cy.$(':selected').unselect(['tapunselect']);
26211 }
26212
26213 triggerEvents(start, ['tap', 'vclick'], e, {
26214 x: now[0],
26215 y: now[1]
26216 });
26217 didDoubleTouch = false;
26218
26219 if (e.timeStamp - prevTouchTimeStamp <= cy.multiClickDebounceTime()) {
26220 touchTimeout && clearTimeout(touchTimeout);
26221 didDoubleTouch = true;
26222 prevTouchTimeStamp = null;
26223 triggerEvents(start, ['dbltap', 'vdblclick'], e, {
26224 x: now[0],
26225 y: now[1]
26226 });
26227 } else {
26228 touchTimeout = setTimeout(function () {
26229 if (didDoubleTouch) return;
26230 triggerEvents(start, ['onetap', 'voneclick'], e, {
26231 x: now[0],
26232 y: now[1]
26233 });
26234 }, cy.multiClickDebounceTime());
26235 prevTouchTimeStamp = e.timeStamp;
26236 }
26237 } // Prepare to select the currently touched node, only if it hasn't been dragged past a certain distance
26238
26239
26240 if (start != null && !r.dragData.didDrag // didn't drag nodes around
26241 && start._private.selectable && rdist2 < r.touchTapThreshold2 && !r.pinching // pinch to zoom should not affect selection
26242 ) {
26243 if (cy.selectionType() === 'single') {
26244 cy.$(isSelected).unmerge(start).unselect(['tapunselect']);
26245 start.select(['tapselect']);
26246 } else {
26247 if (start.selected()) {
26248 start.unselect(['tapunselect']);
26249 } else {
26250 start.select(['tapselect']);
26251 }
26252 }
26253
26254 r.redrawHint('eles', true);
26255 }
26256
26257 r.touchData.singleTouchMoved = true;
26258 }
26259
26260 for (var j = 0; j < now.length; j++) {
26261 earlier[j] = now[j];
26262 }
26263
26264 r.dragData.didDrag = false; // reset for next touchstart
26265
26266 if (e.touches.length === 0) {
26267 r.touchData.dragDelta = [];
26268 r.touchData.startPosition = null;
26269 r.touchData.startGPosition = null;
26270 r.touchData.didSelect = false;
26271 }
26272
26273 if (e.touches.length < 2) {
26274 if (e.touches.length === 1) {
26275 // the old start global pos'n may not be the same finger that remains
26276 r.touchData.startGPosition = [e.touches[0].clientX, e.touches[0].clientY];
26277 }
26278
26279 r.pinching = false;
26280 r.redrawHint('eles', true);
26281 r.redraw();
26282 } //r.redraw();
26283
26284 }, false); // fallback compatibility layer for ms pointer events
26285
26286 if (typeof TouchEvent === 'undefined') {
26287 var pointers = [];
26288
26289 var makeTouch = function makeTouch(e) {
26290 return {
26291 clientX: e.clientX,
26292 clientY: e.clientY,
26293 force: 1,
26294 identifier: e.pointerId,
26295 pageX: e.pageX,
26296 pageY: e.pageY,
26297 radiusX: e.width / 2,
26298 radiusY: e.height / 2,
26299 screenX: e.screenX,
26300 screenY: e.screenY,
26301 target: e.target
26302 };
26303 };
26304
26305 var makePointer = function makePointer(e) {
26306 return {
26307 event: e,
26308 touch: makeTouch(e)
26309 };
26310 };
26311
26312 var addPointer = function addPointer(e) {
26313 pointers.push(makePointer(e));
26314 };
26315
26316 var removePointer = function removePointer(e) {
26317 for (var i = 0; i < pointers.length; i++) {
26318 var p = pointers[i];
26319
26320 if (p.event.pointerId === e.pointerId) {
26321 pointers.splice(i, 1);
26322 return;
26323 }
26324 }
26325 };
26326
26327 var updatePointer = function updatePointer(e) {
26328 var p = pointers.filter(function (p) {
26329 return p.event.pointerId === e.pointerId;
26330 })[0];
26331 p.event = e;
26332 p.touch = makeTouch(e);
26333 };
26334
26335 var addTouchesToEvent = function addTouchesToEvent(e) {
26336 e.touches = pointers.map(function (p) {
26337 return p.touch;
26338 });
26339 };
26340
26341 var pointerIsMouse = function pointerIsMouse(e) {
26342 return e.pointerType === 'mouse' || e.pointerType === 4;
26343 };
26344
26345 r.registerBinding(r.container, 'pointerdown', function (e) {
26346 if (pointerIsMouse(e)) {
26347 return;
26348 } // mouse already handled
26349
26350
26351 e.preventDefault();
26352 addPointer(e);
26353 addTouchesToEvent(e);
26354 touchstartHandler(e);
26355 });
26356 r.registerBinding(r.container, 'pointerup', function (e) {
26357 if (pointerIsMouse(e)) {
26358 return;
26359 } // mouse already handled
26360
26361
26362 removePointer(e);
26363 addTouchesToEvent(e);
26364 touchendHandler(e);
26365 });
26366 r.registerBinding(r.container, 'pointercancel', function (e) {
26367 if (pointerIsMouse(e)) {
26368 return;
26369 } // mouse already handled
26370
26371
26372 removePointer(e);
26373 addTouchesToEvent(e);
26374 touchcancelHandler(e);
26375 });
26376 r.registerBinding(r.container, 'pointermove', function (e) {
26377 if (pointerIsMouse(e)) {
26378 return;
26379 } // mouse already handled
26380
26381
26382 e.preventDefault();
26383 updatePointer(e);
26384 addTouchesToEvent(e);
26385 touchmoveHandler(e);
26386 });
26387 }
26388};
26389
26390var BRp$2 = {};
26391
26392BRp$2.generatePolygon = function (name, points) {
26393 return this.nodeShapes[name] = {
26394 renderer: this,
26395 name: name,
26396 points: points,
26397 draw: function draw(context, centerX, centerY, width, height) {
26398 this.renderer.nodeShapeImpl('polygon', context, centerX, centerY, width, height, this.points);
26399 },
26400 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
26401 return polygonIntersectLine(x, y, this.points, nodeX, nodeY, width / 2, height / 2, padding);
26402 },
26403 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
26404 return pointInsidePolygon(x, y, this.points, centerX, centerY, width, height, [0, -1], padding);
26405 }
26406 };
26407};
26408
26409BRp$2.generateEllipse = function () {
26410 return this.nodeShapes['ellipse'] = {
26411 renderer: this,
26412 name: 'ellipse',
26413 draw: function draw(context, centerX, centerY, width, height) {
26414 this.renderer.nodeShapeImpl(this.name, context, centerX, centerY, width, height);
26415 },
26416 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
26417 return intersectLineEllipse(x, y, nodeX, nodeY, width / 2 + padding, height / 2 + padding);
26418 },
26419 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
26420 return checkInEllipse(x, y, width, height, centerX, centerY, padding);
26421 }
26422 };
26423};
26424
26425BRp$2.generateRoundPolygon = function (name, points) {
26426 // Pre-compute control points
26427 // Since these points depend on the radius length (which in turns depend on the width/height of the node) we will only pre-compute
26428 // the unit vectors.
26429 // For simplicity the layout will be:
26430 // [ p0, UnitVectorP0P1, p1, UniVectorP1P2, ..., pn, UnitVectorPnP0 ]
26431 var allPoints = new Array(points.length * 2);
26432
26433 for (var i = 0; i < points.length / 2; i++) {
26434 var sourceIndex = i * 2;
26435 var destIndex = void 0;
26436
26437 if (i < points.length / 2 - 1) {
26438 destIndex = (i + 1) * 2;
26439 } else {
26440 destIndex = 0;
26441 }
26442
26443 allPoints[i * 4] = points[sourceIndex];
26444 allPoints[i * 4 + 1] = points[sourceIndex + 1];
26445 var xDest = points[destIndex] - points[sourceIndex];
26446 var yDest = points[destIndex + 1] - points[sourceIndex + 1];
26447 var norm = Math.sqrt(xDest * xDest + yDest * yDest);
26448 allPoints[i * 4 + 2] = xDest / norm;
26449 allPoints[i * 4 + 3] = yDest / norm;
26450 }
26451
26452 return this.nodeShapes[name] = {
26453 renderer: this,
26454 name: name,
26455 points: allPoints,
26456 draw: function draw(context, centerX, centerY, width, height) {
26457 this.renderer.nodeShapeImpl('round-polygon', context, centerX, centerY, width, height, this.points);
26458 },
26459 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
26460 return roundPolygonIntersectLine(x, y, this.points, nodeX, nodeY, width, height);
26461 },
26462 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
26463 return pointInsideRoundPolygon(x, y, this.points, centerX, centerY, width, height);
26464 }
26465 };
26466};
26467
26468BRp$2.generateRoundRectangle = function () {
26469 return this.nodeShapes['round-rectangle'] = this.nodeShapes['roundrectangle'] = {
26470 renderer: this,
26471 name: 'round-rectangle',
26472 points: generateUnitNgonPointsFitToSquare(4, 0),
26473 draw: function draw(context, centerX, centerY, width, height) {
26474 this.renderer.nodeShapeImpl(this.name, context, centerX, centerY, width, height);
26475 },
26476 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
26477 return roundRectangleIntersectLine(x, y, nodeX, nodeY, width, height, padding);
26478 },
26479 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
26480 var cornerRadius = getRoundRectangleRadius(width, height);
26481 var diam = cornerRadius * 2; // Check hBox
26482
26483 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width, height - diam, [0, -1], padding)) {
26484 return true;
26485 } // Check vBox
26486
26487
26488 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width - diam, height, [0, -1], padding)) {
26489 return true;
26490 } // Check top left quarter circle
26491
26492
26493 if (checkInEllipse(x, y, diam, diam, centerX - width / 2 + cornerRadius, centerY - height / 2 + cornerRadius, padding)) {
26494 return true;
26495 } // Check top right quarter circle
26496
26497
26498 if (checkInEllipse(x, y, diam, diam, centerX + width / 2 - cornerRadius, centerY - height / 2 + cornerRadius, padding)) {
26499 return true;
26500 } // Check bottom right quarter circle
26501
26502
26503 if (checkInEllipse(x, y, diam, diam, centerX + width / 2 - cornerRadius, centerY + height / 2 - cornerRadius, padding)) {
26504 return true;
26505 } // Check bottom left quarter circle
26506
26507
26508 if (checkInEllipse(x, y, diam, diam, centerX - width / 2 + cornerRadius, centerY + height / 2 - cornerRadius, padding)) {
26509 return true;
26510 }
26511
26512 return false;
26513 }
26514 };
26515};
26516
26517BRp$2.generateCutRectangle = function () {
26518 return this.nodeShapes['cut-rectangle'] = this.nodeShapes['cutrectangle'] = {
26519 renderer: this,
26520 name: 'cut-rectangle',
26521 cornerLength: getCutRectangleCornerLength(),
26522 points: generateUnitNgonPointsFitToSquare(4, 0),
26523 draw: function draw(context, centerX, centerY, width, height) {
26524 this.renderer.nodeShapeImpl(this.name, context, centerX, centerY, width, height);
26525 },
26526 generateCutTrianglePts: function generateCutTrianglePts(width, height, centerX, centerY) {
26527 var cl = this.cornerLength;
26528 var hh = height / 2;
26529 var hw = width / 2;
26530 var xBegin = centerX - hw;
26531 var xEnd = centerX + hw;
26532 var yBegin = centerY - hh;
26533 var yEnd = centerY + hh; // points are in clockwise order, inner (imaginary) triangle pt on [4, 5]
26534
26535 return {
26536 topLeft: [xBegin, yBegin + cl, xBegin + cl, yBegin, xBegin + cl, yBegin + cl],
26537 topRight: [xEnd - cl, yBegin, xEnd, yBegin + cl, xEnd - cl, yBegin + cl],
26538 bottomRight: [xEnd, yEnd - cl, xEnd - cl, yEnd, xEnd - cl, yEnd - cl],
26539 bottomLeft: [xBegin + cl, yEnd, xBegin, yEnd - cl, xBegin + cl, yEnd - cl]
26540 };
26541 },
26542 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
26543 var cPts = this.generateCutTrianglePts(width + 2 * padding, height + 2 * padding, nodeX, nodeY);
26544 var pts = [].concat.apply([], [cPts.topLeft.splice(0, 4), cPts.topRight.splice(0, 4), cPts.bottomRight.splice(0, 4), cPts.bottomLeft.splice(0, 4)]);
26545 return polygonIntersectLine(x, y, pts, nodeX, nodeY);
26546 },
26547 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
26548 // Check hBox
26549 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width, height - 2 * this.cornerLength, [0, -1], padding)) {
26550 return true;
26551 } // Check vBox
26552
26553
26554 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width - 2 * this.cornerLength, height, [0, -1], padding)) {
26555 return true;
26556 }
26557
26558 var cutTrianglePts = this.generateCutTrianglePts(width, height, centerX, centerY);
26559 return pointInsidePolygonPoints(x, y, cutTrianglePts.topLeft) || pointInsidePolygonPoints(x, y, cutTrianglePts.topRight) || pointInsidePolygonPoints(x, y, cutTrianglePts.bottomRight) || pointInsidePolygonPoints(x, y, cutTrianglePts.bottomLeft);
26560 }
26561 };
26562};
26563
26564BRp$2.generateBarrel = function () {
26565 return this.nodeShapes['barrel'] = {
26566 renderer: this,
26567 name: 'barrel',
26568 points: generateUnitNgonPointsFitToSquare(4, 0),
26569 draw: function draw(context, centerX, centerY, width, height) {
26570 this.renderer.nodeShapeImpl(this.name, context, centerX, centerY, width, height);
26571 },
26572 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
26573 // use two fixed t values for the bezier curve approximation
26574 var t0 = 0.15;
26575 var t1 = 0.5;
26576 var t2 = 0.85;
26577 var bPts = this.generateBarrelBezierPts(width + 2 * padding, height + 2 * padding, nodeX, nodeY);
26578
26579 var approximateBarrelCurvePts = function approximateBarrelCurvePts(pts) {
26580 // approximate curve pts based on the two t values
26581 var m0 = qbezierPtAt({
26582 x: pts[0],
26583 y: pts[1]
26584 }, {
26585 x: pts[2],
26586 y: pts[3]
26587 }, {
26588 x: pts[4],
26589 y: pts[5]
26590 }, t0);
26591 var m1 = qbezierPtAt({
26592 x: pts[0],
26593 y: pts[1]
26594 }, {
26595 x: pts[2],
26596 y: pts[3]
26597 }, {
26598 x: pts[4],
26599 y: pts[5]
26600 }, t1);
26601 var m2 = qbezierPtAt({
26602 x: pts[0],
26603 y: pts[1]
26604 }, {
26605 x: pts[2],
26606 y: pts[3]
26607 }, {
26608 x: pts[4],
26609 y: pts[5]
26610 }, t2);
26611 return [pts[0], pts[1], m0.x, m0.y, m1.x, m1.y, m2.x, m2.y, pts[4], pts[5]];
26612 };
26613
26614 var pts = [].concat(approximateBarrelCurvePts(bPts.topLeft), approximateBarrelCurvePts(bPts.topRight), approximateBarrelCurvePts(bPts.bottomRight), approximateBarrelCurvePts(bPts.bottomLeft));
26615 return polygonIntersectLine(x, y, pts, nodeX, nodeY);
26616 },
26617 generateBarrelBezierPts: function generateBarrelBezierPts(width, height, centerX, centerY) {
26618 var hh = height / 2;
26619 var hw = width / 2;
26620 var xBegin = centerX - hw;
26621 var xEnd = centerX + hw;
26622 var yBegin = centerY - hh;
26623 var yEnd = centerY + hh;
26624 var curveConstants = getBarrelCurveConstants(width, height);
26625 var hOffset = curveConstants.heightOffset;
26626 var wOffset = curveConstants.widthOffset;
26627 var ctrlPtXOffset = curveConstants.ctrlPtOffsetPct * width; // points are in clockwise order, inner (imaginary) control pt on [4, 5]
26628
26629 var pts = {
26630 topLeft: [xBegin, yBegin + hOffset, xBegin + ctrlPtXOffset, yBegin, xBegin + wOffset, yBegin],
26631 topRight: [xEnd - wOffset, yBegin, xEnd - ctrlPtXOffset, yBegin, xEnd, yBegin + hOffset],
26632 bottomRight: [xEnd, yEnd - hOffset, xEnd - ctrlPtXOffset, yEnd, xEnd - wOffset, yEnd],
26633 bottomLeft: [xBegin + wOffset, yEnd, xBegin + ctrlPtXOffset, yEnd, xBegin, yEnd - hOffset]
26634 };
26635 pts.topLeft.isTop = true;
26636 pts.topRight.isTop = true;
26637 pts.bottomLeft.isBottom = true;
26638 pts.bottomRight.isBottom = true;
26639 return pts;
26640 },
26641 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
26642 var curveConstants = getBarrelCurveConstants(width, height);
26643 var hOffset = curveConstants.heightOffset;
26644 var wOffset = curveConstants.widthOffset; // Check hBox
26645
26646 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width, height - 2 * hOffset, [0, -1], padding)) {
26647 return true;
26648 } // Check vBox
26649
26650
26651 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width - 2 * wOffset, height, [0, -1], padding)) {
26652 return true;
26653 }
26654
26655 var barrelCurvePts = this.generateBarrelBezierPts(width, height, centerX, centerY);
26656
26657 var getCurveT = function getCurveT(x, y, curvePts) {
26658 var x0 = curvePts[4];
26659 var x1 = curvePts[2];
26660 var x2 = curvePts[0];
26661 var y0 = curvePts[5]; // var y1 = curvePts[ 3 ];
26662
26663 var y2 = curvePts[1];
26664 var xMin = Math.min(x0, x2);
26665 var xMax = Math.max(x0, x2);
26666 var yMin = Math.min(y0, y2);
26667 var yMax = Math.max(y0, y2);
26668
26669 if (xMin <= x && x <= xMax && yMin <= y && y <= yMax) {
26670 var coeff = bezierPtsToQuadCoeff(x0, x1, x2);
26671 var roots = solveQuadratic(coeff[0], coeff[1], coeff[2], x);
26672 var validRoots = roots.filter(function (r) {
26673 return 0 <= r && r <= 1;
26674 });
26675
26676 if (validRoots.length > 0) {
26677 return validRoots[0];
26678 }
26679 }
26680
26681 return null;
26682 };
26683
26684 var curveRegions = Object.keys(barrelCurvePts);
26685
26686 for (var i = 0; i < curveRegions.length; i++) {
26687 var corner = curveRegions[i];
26688 var cornerPts = barrelCurvePts[corner];
26689 var t = getCurveT(x, y, cornerPts);
26690
26691 if (t == null) {
26692 continue;
26693 }
26694
26695 var y0 = cornerPts[5];
26696 var y1 = cornerPts[3];
26697 var y2 = cornerPts[1];
26698 var bezY = qbezierAt(y0, y1, y2, t);
26699
26700 if (cornerPts.isTop && bezY <= y) {
26701 return true;
26702 }
26703
26704 if (cornerPts.isBottom && y <= bezY) {
26705 return true;
26706 }
26707 }
26708
26709 return false;
26710 }
26711 };
26712};
26713
26714BRp$2.generateBottomRoundrectangle = function () {
26715 return this.nodeShapes['bottom-round-rectangle'] = this.nodeShapes['bottomroundrectangle'] = {
26716 renderer: this,
26717 name: 'bottom-round-rectangle',
26718 points: generateUnitNgonPointsFitToSquare(4, 0),
26719 draw: function draw(context, centerX, centerY, width, height) {
26720 this.renderer.nodeShapeImpl(this.name, context, centerX, centerY, width, height);
26721 },
26722 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
26723 var topStartX = nodeX - (width / 2 + padding);
26724 var topStartY = nodeY - (height / 2 + padding);
26725 var topEndY = topStartY;
26726 var topEndX = nodeX + (width / 2 + padding);
26727 var topIntersections = finiteLinesIntersect(x, y, nodeX, nodeY, topStartX, topStartY, topEndX, topEndY, false);
26728
26729 if (topIntersections.length > 0) {
26730 return topIntersections;
26731 }
26732
26733 return roundRectangleIntersectLine(x, y, nodeX, nodeY, width, height, padding);
26734 },
26735 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
26736 var cornerRadius = getRoundRectangleRadius(width, height);
26737 var diam = 2 * cornerRadius; // Check hBox
26738
26739 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width, height - diam, [0, -1], padding)) {
26740 return true;
26741 } // Check vBox
26742
26743
26744 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width - diam, height, [0, -1], padding)) {
26745 return true;
26746 } // check non-rounded top side
26747
26748
26749 var outerWidth = width / 2 + 2 * padding;
26750 var outerHeight = height / 2 + 2 * padding;
26751 var points = [centerX - outerWidth, centerY - outerHeight, centerX - outerWidth, centerY, centerX + outerWidth, centerY, centerX + outerWidth, centerY - outerHeight];
26752
26753 if (pointInsidePolygonPoints(x, y, points)) {
26754 return true;
26755 } // Check bottom right quarter circle
26756
26757
26758 if (checkInEllipse(x, y, diam, diam, centerX + width / 2 - cornerRadius, centerY + height / 2 - cornerRadius, padding)) {
26759 return true;
26760 } // Check bottom left quarter circle
26761
26762
26763 if (checkInEllipse(x, y, diam, diam, centerX - width / 2 + cornerRadius, centerY + height / 2 - cornerRadius, padding)) {
26764 return true;
26765 }
26766
26767 return false;
26768 }
26769 };
26770};
26771
26772BRp$2.registerNodeShapes = function () {
26773 var nodeShapes = this.nodeShapes = {};
26774 var renderer = this;
26775 this.generateEllipse();
26776 this.generatePolygon('triangle', generateUnitNgonPointsFitToSquare(3, 0));
26777 this.generateRoundPolygon('round-triangle', generateUnitNgonPointsFitToSquare(3, 0));
26778 this.generatePolygon('rectangle', generateUnitNgonPointsFitToSquare(4, 0));
26779 nodeShapes['square'] = nodeShapes['rectangle'];
26780 this.generateRoundRectangle();
26781 this.generateCutRectangle();
26782 this.generateBarrel();
26783 this.generateBottomRoundrectangle();
26784 {
26785 var diamondPoints = [0, 1, 1, 0, 0, -1, -1, 0];
26786 this.generatePolygon('diamond', diamondPoints);
26787 this.generateRoundPolygon('round-diamond', diamondPoints);
26788 }
26789 this.generatePolygon('pentagon', generateUnitNgonPointsFitToSquare(5, 0));
26790 this.generateRoundPolygon('round-pentagon', generateUnitNgonPointsFitToSquare(5, 0));
26791 this.generatePolygon('hexagon', generateUnitNgonPointsFitToSquare(6, 0));
26792 this.generateRoundPolygon('round-hexagon', generateUnitNgonPointsFitToSquare(6, 0));
26793 this.generatePolygon('heptagon', generateUnitNgonPointsFitToSquare(7, 0));
26794 this.generateRoundPolygon('round-heptagon', generateUnitNgonPointsFitToSquare(7, 0));
26795 this.generatePolygon('octagon', generateUnitNgonPointsFitToSquare(8, 0));
26796 this.generateRoundPolygon('round-octagon', generateUnitNgonPointsFitToSquare(8, 0));
26797 var star5Points = new Array(20);
26798 {
26799 var outerPoints = generateUnitNgonPoints(5, 0);
26800 var innerPoints = generateUnitNgonPoints(5, Math.PI / 5); // Outer radius is 1; inner radius of star is smaller
26801
26802 var innerRadius = 0.5 * (3 - Math.sqrt(5));
26803 innerRadius *= 1.57;
26804
26805 for (var i = 0; i < innerPoints.length / 2; i++) {
26806 innerPoints[i * 2] *= innerRadius;
26807 innerPoints[i * 2 + 1] *= innerRadius;
26808 }
26809
26810 for (var i = 0; i < 20 / 4; i++) {
26811 star5Points[i * 4] = outerPoints[i * 2];
26812 star5Points[i * 4 + 1] = outerPoints[i * 2 + 1];
26813 star5Points[i * 4 + 2] = innerPoints[i * 2];
26814 star5Points[i * 4 + 3] = innerPoints[i * 2 + 1];
26815 }
26816 }
26817 star5Points = fitPolygonToSquare(star5Points);
26818 this.generatePolygon('star', star5Points);
26819 this.generatePolygon('vee', [-1, -1, 0, -0.333, 1, -1, 0, 1]);
26820 this.generatePolygon('rhomboid', [-1, -1, 0.333, -1, 1, 1, -0.333, 1]);
26821 this.nodeShapes['concavehexagon'] = this.generatePolygon('concave-hexagon', [-1, -0.95, -0.75, 0, -1, 0.95, 1, 0.95, 0.75, 0, 1, -0.95]);
26822 {
26823 var tagPoints = [-1, -1, 0.25, -1, 1, 0, 0.25, 1, -1, 1];
26824 this.generatePolygon('tag', tagPoints);
26825 this.generateRoundPolygon('round-tag', tagPoints);
26826 }
26827
26828 nodeShapes.makePolygon = function (points) {
26829 // use caching on user-specified polygons so they are as fast as native shapes
26830 var key = points.join('$');
26831 var name = 'polygon-' + key;
26832 var shape;
26833
26834 if (shape = this[name]) {
26835 // got cached shape
26836 return shape;
26837 } // create and cache new shape
26838
26839
26840 return renderer.generatePolygon(name, points);
26841 };
26842};
26843
26844var BRp$1 = {};
26845
26846BRp$1.timeToRender = function () {
26847 return this.redrawTotalTime / this.redrawCount;
26848};
26849
26850BRp$1.redraw = function (options) {
26851 options = options || staticEmptyObject();
26852 var r = this;
26853
26854 if (r.averageRedrawTime === undefined) {
26855 r.averageRedrawTime = 0;
26856 }
26857
26858 if (r.lastRedrawTime === undefined) {
26859 r.lastRedrawTime = 0;
26860 }
26861
26862 if (r.lastDrawTime === undefined) {
26863 r.lastDrawTime = 0;
26864 }
26865
26866 r.requestedFrame = true;
26867 r.renderOptions = options;
26868};
26869
26870BRp$1.beforeRender = function (fn, priority) {
26871 // the renderer can't add tick callbacks when destroyed
26872 if (this.destroyed) {
26873 return;
26874 }
26875
26876 if (priority == null) {
26877 error('Priority is not optional for beforeRender');
26878 }
26879
26880 var cbs = this.beforeRenderCallbacks;
26881 cbs.push({
26882 fn: fn,
26883 priority: priority
26884 }); // higher priority callbacks executed first
26885
26886 cbs.sort(function (a, b) {
26887 return b.priority - a.priority;
26888 });
26889};
26890
26891var beforeRenderCallbacks = function beforeRenderCallbacks(r, willDraw, startTime) {
26892 var cbs = r.beforeRenderCallbacks;
26893
26894 for (var i = 0; i < cbs.length; i++) {
26895 cbs[i].fn(willDraw, startTime);
26896 }
26897};
26898
26899BRp$1.startRenderLoop = function () {
26900 var r = this;
26901 var cy = r.cy;
26902
26903 if (r.renderLoopStarted) {
26904 return;
26905 } else {
26906 r.renderLoopStarted = true;
26907 }
26908
26909 var renderFn = function renderFn(requestTime) {
26910 if (r.destroyed) {
26911 return;
26912 }
26913
26914 if (cy.batching()) ; else if (r.requestedFrame && !r.skipFrame) {
26915 beforeRenderCallbacks(r, true, requestTime);
26916 var startTime = performanceNow();
26917 r.render(r.renderOptions);
26918 var endTime = r.lastDrawTime = performanceNow();
26919
26920 if (r.averageRedrawTime === undefined) {
26921 r.averageRedrawTime = endTime - startTime;
26922 }
26923
26924 if (r.redrawCount === undefined) {
26925 r.redrawCount = 0;
26926 }
26927
26928 r.redrawCount++;
26929
26930 if (r.redrawTotalTime === undefined) {
26931 r.redrawTotalTime = 0;
26932 }
26933
26934 var duration = endTime - startTime;
26935 r.redrawTotalTime += duration;
26936 r.lastRedrawTime = duration; // use a weighted average with a bias from the previous average so we don't spike so easily
26937
26938 r.averageRedrawTime = r.averageRedrawTime / 2 + duration / 2;
26939 r.requestedFrame = false;
26940 } else {
26941 beforeRenderCallbacks(r, false, requestTime);
26942 }
26943
26944 r.skipFrame = false;
26945 requestAnimationFrame(renderFn);
26946 };
26947
26948 requestAnimationFrame(renderFn);
26949};
26950
26951var BaseRenderer = function BaseRenderer(options) {
26952 this.init(options);
26953};
26954
26955var BR = BaseRenderer;
26956var BRp = BR.prototype;
26957BRp.clientFunctions = ['redrawHint', 'render', 'renderTo', 'matchCanvasSize', 'nodeShapeImpl', 'arrowShapeImpl'];
26958
26959BRp.init = function (options) {
26960 var r = this;
26961 r.options = options;
26962 r.cy = options.cy;
26963 var ctr = r.container = options.cy.container(); // prepend a stylesheet in the head such that
26964
26965 if (window$1) {
26966 var document = window$1.document;
26967 var head = document.head;
26968 var stylesheetId = '__________cytoscape_stylesheet';
26969 var className = '__________cytoscape_container';
26970 var stylesheetAlreadyExists = document.getElementById(stylesheetId) != null;
26971
26972 if (ctr.className.indexOf(className) < 0) {
26973 ctr.className = (ctr.className || '') + ' ' + className;
26974 }
26975
26976 if (!stylesheetAlreadyExists) {
26977 var stylesheet = document.createElement('style');
26978 stylesheet.id = stylesheetId;
26979 stylesheet.innerHTML = '.' + className + ' { position: relative; }';
26980 head.insertBefore(stylesheet, head.children[0]); // first so lowest priority
26981 }
26982
26983 var computedStyle = window$1.getComputedStyle(ctr);
26984 var position = computedStyle.getPropertyValue('position');
26985
26986 if (position === 'static') {
26987 warn('A Cytoscape container has style position:static and so can not use UI extensions properly');
26988 }
26989 }
26990
26991 r.selection = [undefined, undefined, undefined, undefined, 0]; // Coordinates for selection box, plus enabled flag
26992
26993 r.bezierProjPcts = [0.05, 0.225, 0.4, 0.5, 0.6, 0.775, 0.95]; //--Pointer-related data
26994
26995 r.hoverData = {
26996 down: null,
26997 last: null,
26998 downTime: null,
26999 triggerMode: null,
27000 dragging: false,
27001 initialPan: [null, null],
27002 capture: false
27003 };
27004 r.dragData = {
27005 possibleDragElements: []
27006 };
27007 r.touchData = {
27008 start: null,
27009 capture: false,
27010 // These 3 fields related to tap, taphold events
27011 startPosition: [null, null, null, null, null, null],
27012 singleTouchStartTime: null,
27013 singleTouchMoved: true,
27014 now: [null, null, null, null, null, null],
27015 earlier: [null, null, null, null, null, null]
27016 };
27017 r.redraws = 0;
27018 r.showFps = options.showFps;
27019 r.debug = options.debug;
27020 r.hideEdgesOnViewport = options.hideEdgesOnViewport;
27021 r.textureOnViewport = options.textureOnViewport;
27022 r.wheelSensitivity = options.wheelSensitivity;
27023 r.motionBlurEnabled = options.motionBlur; // on by default
27024
27025 r.forcedPixelRatio = number$1(options.pixelRatio) ? options.pixelRatio : null;
27026 r.motionBlur = options.motionBlur; // for initial kick off
27027
27028 r.motionBlurOpacity = options.motionBlurOpacity;
27029 r.motionBlurTransparency = 1 - r.motionBlurOpacity;
27030 r.motionBlurPxRatio = 1;
27031 r.mbPxRBlurry = 1; //0.8;
27032
27033 r.minMbLowQualFrames = 4;
27034 r.fullQualityMb = false;
27035 r.clearedForMotionBlur = [];
27036 r.desktopTapThreshold = options.desktopTapThreshold;
27037 r.desktopTapThreshold2 = options.desktopTapThreshold * options.desktopTapThreshold;
27038 r.touchTapThreshold = options.touchTapThreshold;
27039 r.touchTapThreshold2 = options.touchTapThreshold * options.touchTapThreshold;
27040 r.tapholdDuration = 500;
27041 r.bindings = [];
27042 r.beforeRenderCallbacks = [];
27043 r.beforeRenderPriorities = {
27044 // higher priority execs before lower one
27045 animations: 400,
27046 eleCalcs: 300,
27047 eleTxrDeq: 200,
27048 lyrTxrDeq: 150,
27049 lyrTxrSkip: 100
27050 };
27051 r.registerNodeShapes();
27052 r.registerArrowShapes();
27053 r.registerCalculationListeners();
27054};
27055
27056BRp.notify = function (eventName, eles) {
27057 var r = this;
27058 var cy = r.cy; // the renderer can't be notified after it's destroyed
27059
27060 if (this.destroyed) {
27061 return;
27062 }
27063
27064 if (eventName === 'init') {
27065 r.load();
27066 return;
27067 }
27068
27069 if (eventName === 'destroy') {
27070 r.destroy();
27071 return;
27072 }
27073
27074 if (eventName === 'add' || eventName === 'remove' || eventName === 'move' && cy.hasCompoundNodes() || eventName === 'load' || eventName === 'zorder' || eventName === 'mount') {
27075 r.invalidateCachedZSortedEles();
27076 }
27077
27078 if (eventName === 'viewport') {
27079 r.redrawHint('select', true);
27080 }
27081
27082 if (eventName === 'load' || eventName === 'resize' || eventName === 'mount') {
27083 r.invalidateContainerClientCoordsCache();
27084 r.matchCanvasSize(r.container);
27085 }
27086
27087 r.redrawHint('eles', true);
27088 r.redrawHint('drag', true);
27089 this.startRenderLoop();
27090 this.redraw();
27091};
27092
27093BRp.destroy = function () {
27094 var r = this;
27095 r.destroyed = true;
27096 r.cy.stopAnimationLoop();
27097
27098 for (var i = 0; i < r.bindings.length; i++) {
27099 var binding = r.bindings[i];
27100 var b = binding;
27101 var tgt = b.target;
27102 (tgt.off || tgt.removeEventListener).apply(tgt, b.args);
27103 }
27104
27105 r.bindings = [];
27106 r.beforeRenderCallbacks = [];
27107 r.onUpdateEleCalcsFns = [];
27108
27109 if (r.removeObserver) {
27110 r.removeObserver.disconnect();
27111 }
27112
27113 if (r.styleObserver) {
27114 r.styleObserver.disconnect();
27115 }
27116
27117 if (r.resizeObserver) {
27118 r.resizeObserver.disconnect();
27119 }
27120
27121 if (r.labelCalcDiv) {
27122 try {
27123 document.body.removeChild(r.labelCalcDiv); // eslint-disable-line no-undef
27124 } catch (e) {// ie10 issue #1014
27125 }
27126 }
27127};
27128
27129BRp.isHeadless = function () {
27130 return false;
27131};
27132
27133[BRp$f, BRp$5, BRp$4, BRp$3, BRp$2, BRp$1].forEach(function (props) {
27134 extend(BRp, props);
27135});
27136
27137var fullFpsTime = 1000 / 60; // assume 60 frames per second
27138
27139var defs = {
27140 setupDequeueing: function setupDequeueing(opts) {
27141 return function setupDequeueingImpl() {
27142 var self = this;
27143 var r = this.renderer;
27144
27145 if (self.dequeueingSetup) {
27146 return;
27147 } else {
27148 self.dequeueingSetup = true;
27149 }
27150
27151 var queueRedraw = debounce(function () {
27152 r.redrawHint('eles', true);
27153 r.redrawHint('drag', true);
27154 r.redraw();
27155 }, opts.deqRedrawThreshold);
27156
27157 var dequeue = function dequeue(willDraw, frameStartTime) {
27158 var startTime = performanceNow();
27159 var avgRenderTime = r.averageRedrawTime;
27160 var renderTime = r.lastRedrawTime;
27161 var deqd = [];
27162 var extent = r.cy.extent();
27163 var pixelRatio = r.getPixelRatio(); // if we aren't in a tick that causes a draw, then the rendered style
27164 // queue won't automatically be flushed before dequeueing starts
27165
27166 if (!willDraw) {
27167 r.flushRenderedStyleQueue();
27168 }
27169
27170 while (true) {
27171 // eslint-disable-line no-constant-condition
27172 var now = performanceNow();
27173 var duration = now - startTime;
27174 var frameDuration = now - frameStartTime;
27175
27176 if (renderTime < fullFpsTime) {
27177 // if we're rendering faster than the ideal fps, then do dequeueing
27178 // during all of the remaining frame time
27179 var timeAvailable = fullFpsTime - (willDraw ? avgRenderTime : 0);
27180
27181 if (frameDuration >= opts.deqFastCost * timeAvailable) {
27182 break;
27183 }
27184 } else {
27185 if (willDraw) {
27186 if (duration >= opts.deqCost * renderTime || duration >= opts.deqAvgCost * avgRenderTime) {
27187 break;
27188 }
27189 } else if (frameDuration >= opts.deqNoDrawCost * fullFpsTime) {
27190 break;
27191 }
27192 }
27193
27194 var thisDeqd = opts.deq(self, pixelRatio, extent);
27195
27196 if (thisDeqd.length > 0) {
27197 for (var i = 0; i < thisDeqd.length; i++) {
27198 deqd.push(thisDeqd[i]);
27199 }
27200 } else {
27201 break;
27202 }
27203 } // callbacks on dequeue
27204
27205
27206 if (deqd.length > 0) {
27207 opts.onDeqd(self, deqd);
27208
27209 if (!willDraw && opts.shouldRedraw(self, deqd, pixelRatio, extent)) {
27210 queueRedraw();
27211 }
27212 }
27213 };
27214
27215 var priority = opts.priority || noop$1;
27216 r.beforeRender(dequeue, priority(self));
27217 };
27218 }
27219};
27220
27221// Uses keys so elements may share the same cache.
27222
27223var ElementTextureCacheLookup = /*#__PURE__*/function () {
27224 function ElementTextureCacheLookup(getKey) {
27225 var doesEleInvalidateKey = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : falsify;
27226
27227 _classCallCheck(this, ElementTextureCacheLookup);
27228
27229 this.idsByKey = new Map$1();
27230 this.keyForId = new Map$1();
27231 this.cachesByLvl = new Map$1();
27232 this.lvls = [];
27233 this.getKey = getKey;
27234 this.doesEleInvalidateKey = doesEleInvalidateKey;
27235 }
27236
27237 _createClass(ElementTextureCacheLookup, [{
27238 key: "getIdsFor",
27239 value: function getIdsFor(key) {
27240 if (key == null) {
27241 error("Can not get id list for null key");
27242 }
27243
27244 var idsByKey = this.idsByKey;
27245 var ids = this.idsByKey.get(key);
27246
27247 if (!ids) {
27248 ids = new Set$1();
27249 idsByKey.set(key, ids);
27250 }
27251
27252 return ids;
27253 }
27254 }, {
27255 key: "addIdForKey",
27256 value: function addIdForKey(key, id) {
27257 if (key != null) {
27258 this.getIdsFor(key).add(id);
27259 }
27260 }
27261 }, {
27262 key: "deleteIdForKey",
27263 value: function deleteIdForKey(key, id) {
27264 if (key != null) {
27265 this.getIdsFor(key)["delete"](id);
27266 }
27267 }
27268 }, {
27269 key: "getNumberOfIdsForKey",
27270 value: function getNumberOfIdsForKey(key) {
27271 if (key == null) {
27272 return 0;
27273 } else {
27274 return this.getIdsFor(key).size;
27275 }
27276 }
27277 }, {
27278 key: "updateKeyMappingFor",
27279 value: function updateKeyMappingFor(ele) {
27280 var id = ele.id();
27281 var prevKey = this.keyForId.get(id);
27282 var currKey = this.getKey(ele);
27283 this.deleteIdForKey(prevKey, id);
27284 this.addIdForKey(currKey, id);
27285 this.keyForId.set(id, currKey);
27286 }
27287 }, {
27288 key: "deleteKeyMappingFor",
27289 value: function deleteKeyMappingFor(ele) {
27290 var id = ele.id();
27291 var prevKey = this.keyForId.get(id);
27292 this.deleteIdForKey(prevKey, id);
27293 this.keyForId["delete"](id);
27294 }
27295 }, {
27296 key: "keyHasChangedFor",
27297 value: function keyHasChangedFor(ele) {
27298 var id = ele.id();
27299 var prevKey = this.keyForId.get(id);
27300 var newKey = this.getKey(ele);
27301 return prevKey !== newKey;
27302 }
27303 }, {
27304 key: "isInvalid",
27305 value: function isInvalid(ele) {
27306 return this.keyHasChangedFor(ele) || this.doesEleInvalidateKey(ele);
27307 }
27308 }, {
27309 key: "getCachesAt",
27310 value: function getCachesAt(lvl) {
27311 var cachesByLvl = this.cachesByLvl,
27312 lvls = this.lvls;
27313 var caches = cachesByLvl.get(lvl);
27314
27315 if (!caches) {
27316 caches = new Map$1();
27317 cachesByLvl.set(lvl, caches);
27318 lvls.push(lvl);
27319 }
27320
27321 return caches;
27322 }
27323 }, {
27324 key: "getCache",
27325 value: function getCache(key, lvl) {
27326 return this.getCachesAt(lvl).get(key);
27327 }
27328 }, {
27329 key: "get",
27330 value: function get(ele, lvl) {
27331 var key = this.getKey(ele);
27332 var cache = this.getCache(key, lvl); // getting for an element may need to add to the id list b/c eles can share keys
27333
27334 if (cache != null) {
27335 this.updateKeyMappingFor(ele);
27336 }
27337
27338 return cache;
27339 }
27340 }, {
27341 key: "getForCachedKey",
27342 value: function getForCachedKey(ele, lvl) {
27343 var key = this.keyForId.get(ele.id()); // n.b. use cached key, not newly computed key
27344
27345 var cache = this.getCache(key, lvl);
27346 return cache;
27347 }
27348 }, {
27349 key: "hasCache",
27350 value: function hasCache(key, lvl) {
27351 return this.getCachesAt(lvl).has(key);
27352 }
27353 }, {
27354 key: "has",
27355 value: function has(ele, lvl) {
27356 var key = this.getKey(ele);
27357 return this.hasCache(key, lvl);
27358 }
27359 }, {
27360 key: "setCache",
27361 value: function setCache(key, lvl, cache) {
27362 cache.key = key;
27363 this.getCachesAt(lvl).set(key, cache);
27364 }
27365 }, {
27366 key: "set",
27367 value: function set(ele, lvl, cache) {
27368 var key = this.getKey(ele);
27369 this.setCache(key, lvl, cache);
27370 this.updateKeyMappingFor(ele);
27371 }
27372 }, {
27373 key: "deleteCache",
27374 value: function deleteCache(key, lvl) {
27375 this.getCachesAt(lvl)["delete"](key);
27376 }
27377 }, {
27378 key: "delete",
27379 value: function _delete(ele, lvl) {
27380 var key = this.getKey(ele);
27381 this.deleteCache(key, lvl);
27382 }
27383 }, {
27384 key: "invalidateKey",
27385 value: function invalidateKey(key) {
27386 var _this = this;
27387
27388 this.lvls.forEach(function (lvl) {
27389 return _this.deleteCache(key, lvl);
27390 });
27391 } // returns true if no other eles reference the invalidated cache (n.b. other eles may need the cache with the same key)
27392
27393 }, {
27394 key: "invalidate",
27395 value: function invalidate(ele) {
27396 var id = ele.id();
27397 var key = this.keyForId.get(id); // n.b. use stored key rather than current (potential key)
27398
27399 this.deleteKeyMappingFor(ele);
27400 var entireKeyInvalidated = this.doesEleInvalidateKey(ele);
27401
27402 if (entireKeyInvalidated) {
27403 // clear mapping for current key
27404 this.invalidateKey(key);
27405 }
27406
27407 return entireKeyInvalidated || this.getNumberOfIdsForKey(key) === 0;
27408 }
27409 }]);
27410
27411 return ElementTextureCacheLookup;
27412}();
27413
27414var minTxrH = 25; // the size of the texture cache for small height eles (special case)
27415
27416var txrStepH = 50; // the min size of the regular cache, and the size it increases with each step up
27417
27418var minLvl$1 = -4; // when scaling smaller than that we don't need to re-render
27419
27420var maxLvl$1 = 3; // when larger than this scale just render directly (caching is not helpful)
27421
27422var maxZoom$1 = 7.99; // beyond this zoom level, layered textures are not used
27423
27424var eleTxrSpacing = 8; // spacing between elements on textures to avoid blitting overlaps
27425
27426var defTxrWidth = 1024; // default/minimum texture width
27427
27428var maxTxrW = 1024; // the maximum width of a texture
27429
27430var maxTxrH = 1024; // the maximum height of a texture
27431
27432var minUtility = 0.2; // if usage of texture is less than this, it is retired
27433
27434var maxFullness = 0.8; // fullness of texture after which queue removal is checked
27435
27436var maxFullnessChecks = 10; // dequeued after this many checks
27437
27438var deqCost$1 = 0.15; // % of add'l rendering cost allowed for dequeuing ele caches each frame
27439
27440var deqAvgCost$1 = 0.1; // % of add'l rendering cost compared to average overall redraw time
27441
27442var deqNoDrawCost$1 = 0.9; // % of avg frame time that can be used for dequeueing when not drawing
27443
27444var deqFastCost$1 = 0.9; // % of frame time to be used when >60fps
27445
27446var deqRedrawThreshold$1 = 100; // time to batch redraws together from dequeueing to allow more dequeueing calcs to happen in the meanwhile
27447
27448var maxDeqSize$1 = 1; // number of eles to dequeue and render at higher texture in each batch
27449
27450var getTxrReasons = {
27451 dequeue: 'dequeue',
27452 downscale: 'downscale',
27453 highQuality: 'highQuality'
27454};
27455var initDefaults = defaults$g({
27456 getKey: null,
27457 doesEleInvalidateKey: falsify,
27458 drawElement: null,
27459 getBoundingBox: null,
27460 getRotationPoint: null,
27461 getRotationOffset: null,
27462 isVisible: trueify,
27463 allowEdgeTxrCaching: true,
27464 allowParentTxrCaching: true
27465});
27466
27467var ElementTextureCache = function ElementTextureCache(renderer, initOptions) {
27468 var self = this;
27469 self.renderer = renderer;
27470 self.onDequeues = [];
27471 var opts = initDefaults(initOptions);
27472 extend(self, opts);
27473 self.lookup = new ElementTextureCacheLookup(opts.getKey, opts.doesEleInvalidateKey);
27474 self.setupDequeueing();
27475};
27476
27477var ETCp = ElementTextureCache.prototype;
27478ETCp.reasons = getTxrReasons; // the list of textures in which new subtextures for elements can be placed
27479
27480ETCp.getTextureQueue = function (txrH) {
27481 var self = this;
27482 self.eleImgCaches = self.eleImgCaches || {};
27483 return self.eleImgCaches[txrH] = self.eleImgCaches[txrH] || [];
27484}; // the list of usused textures which can be recycled (in use in texture queue)
27485
27486
27487ETCp.getRetiredTextureQueue = function (txrH) {
27488 var self = this;
27489 var rtxtrQs = self.eleImgCaches.retired = self.eleImgCaches.retired || {};
27490 var rtxtrQ = rtxtrQs[txrH] = rtxtrQs[txrH] || [];
27491 return rtxtrQ;
27492}; // queue of element draw requests at different scale levels
27493
27494
27495ETCp.getElementQueue = function () {
27496 var self = this;
27497 var q = self.eleCacheQueue = self.eleCacheQueue || new Heap(function (a, b) {
27498 return b.reqs - a.reqs;
27499 });
27500 return q;
27501}; // queue of element draw requests at different scale levels (element id lookup)
27502
27503
27504ETCp.getElementKeyToQueue = function () {
27505 var self = this;
27506 var k2q = self.eleKeyToCacheQueue = self.eleKeyToCacheQueue || {};
27507 return k2q;
27508};
27509
27510ETCp.getElement = function (ele, bb, pxRatio, lvl, reason) {
27511 var self = this;
27512 var r = this.renderer;
27513 var zoom = r.cy.zoom();
27514 var lookup = this.lookup;
27515
27516 if (!bb || bb.w === 0 || bb.h === 0 || isNaN(bb.w) || isNaN(bb.h) || !ele.visible() || ele.removed()) {
27517 return null;
27518 }
27519
27520 if (!self.allowEdgeTxrCaching && ele.isEdge() || !self.allowParentTxrCaching && ele.isParent()) {
27521 return null;
27522 }
27523
27524 if (lvl == null) {
27525 lvl = Math.ceil(log2(zoom * pxRatio));
27526 }
27527
27528 if (lvl < minLvl$1) {
27529 lvl = minLvl$1;
27530 } else if (zoom >= maxZoom$1 || lvl > maxLvl$1) {
27531 return null;
27532 }
27533
27534 var scale = Math.pow(2, lvl);
27535 var eleScaledH = bb.h * scale;
27536 var eleScaledW = bb.w * scale;
27537 var scaledLabelShown = r.eleTextBiggerThanMin(ele, scale);
27538
27539 if (!this.isVisible(ele, scaledLabelShown)) {
27540 return null;
27541 }
27542
27543 var eleCache = lookup.get(ele, lvl); // if this get was on an unused/invalidated cache, then restore the texture usage metric
27544
27545 if (eleCache && eleCache.invalidated) {
27546 eleCache.invalidated = false;
27547 eleCache.texture.invalidatedWidth -= eleCache.width;
27548 }
27549
27550 if (eleCache) {
27551 return eleCache;
27552 }
27553
27554 var txrH; // which texture height this ele belongs to
27555
27556 if (eleScaledH <= minTxrH) {
27557 txrH = minTxrH;
27558 } else if (eleScaledH <= txrStepH) {
27559 txrH = txrStepH;
27560 } else {
27561 txrH = Math.ceil(eleScaledH / txrStepH) * txrStepH;
27562 }
27563
27564 if (eleScaledH > maxTxrH || eleScaledW > maxTxrW) {
27565 return null; // caching large elements is not efficient
27566 }
27567
27568 var txrQ = self.getTextureQueue(txrH); // first try the second last one in case it has space at the end
27569
27570 var txr = txrQ[txrQ.length - 2];
27571
27572 var addNewTxr = function addNewTxr() {
27573 return self.recycleTexture(txrH, eleScaledW) || self.addTexture(txrH, eleScaledW);
27574 }; // try the last one if there is no second last one
27575
27576
27577 if (!txr) {
27578 txr = txrQ[txrQ.length - 1];
27579 } // if the last one doesn't exist, we need a first one
27580
27581
27582 if (!txr) {
27583 txr = addNewTxr();
27584 } // if there's no room in the current texture, we need a new one
27585
27586
27587 if (txr.width - txr.usedWidth < eleScaledW) {
27588 txr = addNewTxr();
27589 }
27590
27591 var scalableFrom = function scalableFrom(otherCache) {
27592 return otherCache && otherCache.scaledLabelShown === scaledLabelShown;
27593 };
27594
27595 var deqing = reason && reason === getTxrReasons.dequeue;
27596 var highQualityReq = reason && reason === getTxrReasons.highQuality;
27597 var downscaleReq = reason && reason === getTxrReasons.downscale;
27598 var higherCache; // the nearest cache with a higher level
27599
27600 for (var l = lvl + 1; l <= maxLvl$1; l++) {
27601 var c = lookup.get(ele, l);
27602
27603 if (c) {
27604 higherCache = c;
27605 break;
27606 }
27607 }
27608
27609 var oneUpCache = higherCache && higherCache.level === lvl + 1 ? higherCache : null;
27610
27611 var downscale = function downscale() {
27612 txr.context.drawImage(oneUpCache.texture.canvas, oneUpCache.x, 0, oneUpCache.width, oneUpCache.height, txr.usedWidth, 0, eleScaledW, eleScaledH);
27613 }; // reset ele area in texture
27614
27615
27616 txr.context.setTransform(1, 0, 0, 1, 0, 0);
27617 txr.context.clearRect(txr.usedWidth, 0, eleScaledW, txrH);
27618
27619 if (scalableFrom(oneUpCache)) {
27620 // then we can relatively cheaply rescale the existing image w/o rerendering
27621 downscale();
27622 } else if (scalableFrom(higherCache)) {
27623 // then use the higher cache for now and queue the next level down
27624 // to cheaply scale towards the smaller level
27625 if (highQualityReq) {
27626 for (var _l = higherCache.level; _l > lvl; _l--) {
27627 oneUpCache = self.getElement(ele, bb, pxRatio, _l, getTxrReasons.downscale);
27628 }
27629
27630 downscale();
27631 } else {
27632 self.queueElement(ele, higherCache.level - 1);
27633 return higherCache;
27634 }
27635 } else {
27636 var lowerCache; // the nearest cache with a lower level
27637
27638 if (!deqing && !highQualityReq && !downscaleReq) {
27639 for (var _l2 = lvl - 1; _l2 >= minLvl$1; _l2--) {
27640 var _c = lookup.get(ele, _l2);
27641
27642 if (_c) {
27643 lowerCache = _c;
27644 break;
27645 }
27646 }
27647 }
27648
27649 if (scalableFrom(lowerCache)) {
27650 // then use the lower quality cache for now and queue the better one for later
27651 self.queueElement(ele, lvl);
27652 return lowerCache;
27653 }
27654
27655 txr.context.translate(txr.usedWidth, 0);
27656 txr.context.scale(scale, scale);
27657 this.drawElement(txr.context, ele, bb, scaledLabelShown, false);
27658 txr.context.scale(1 / scale, 1 / scale);
27659 txr.context.translate(-txr.usedWidth, 0);
27660 }
27661
27662 eleCache = {
27663 x: txr.usedWidth,
27664 texture: txr,
27665 level: lvl,
27666 scale: scale,
27667 width: eleScaledW,
27668 height: eleScaledH,
27669 scaledLabelShown: scaledLabelShown
27670 };
27671 txr.usedWidth += Math.ceil(eleScaledW + eleTxrSpacing);
27672 txr.eleCaches.push(eleCache);
27673 lookup.set(ele, lvl, eleCache);
27674 self.checkTextureFullness(txr);
27675 return eleCache;
27676};
27677
27678ETCp.invalidateElements = function (eles) {
27679 for (var i = 0; i < eles.length; i++) {
27680 this.invalidateElement(eles[i]);
27681 }
27682};
27683
27684ETCp.invalidateElement = function (ele) {
27685 var self = this;
27686 var lookup = self.lookup;
27687 var caches = [];
27688 var invalid = lookup.isInvalid(ele);
27689
27690 if (!invalid) {
27691 return; // override the invalidation request if the element key has not changed
27692 }
27693
27694 for (var lvl = minLvl$1; lvl <= maxLvl$1; lvl++) {
27695 var cache = lookup.getForCachedKey(ele, lvl);
27696
27697 if (cache) {
27698 caches.push(cache);
27699 }
27700 }
27701
27702 var noOtherElesUseCache = lookup.invalidate(ele);
27703
27704 if (noOtherElesUseCache) {
27705 for (var i = 0; i < caches.length; i++) {
27706 var _cache = caches[i];
27707 var txr = _cache.texture; // remove space from the texture it belongs to
27708
27709 txr.invalidatedWidth += _cache.width; // mark the cache as invalidated
27710
27711 _cache.invalidated = true; // retire the texture if its utility is low
27712
27713 self.checkTextureUtility(txr);
27714 }
27715 } // remove from queue since the old req was for the old state
27716
27717
27718 self.removeFromQueue(ele);
27719};
27720
27721ETCp.checkTextureUtility = function (txr) {
27722 // invalidate all entries in the cache if the cache size is small
27723 if (txr.invalidatedWidth >= minUtility * txr.width) {
27724 this.retireTexture(txr);
27725 }
27726};
27727
27728ETCp.checkTextureFullness = function (txr) {
27729 // if texture has been mostly filled and passed over several times, remove
27730 // it from the queue so we don't need to waste time looking at it to put new things
27731 var self = this;
27732 var txrQ = self.getTextureQueue(txr.height);
27733
27734 if (txr.usedWidth / txr.width > maxFullness && txr.fullnessChecks >= maxFullnessChecks) {
27735 removeFromArray(txrQ, txr);
27736 } else {
27737 txr.fullnessChecks++;
27738 }
27739};
27740
27741ETCp.retireTexture = function (txr) {
27742 var self = this;
27743 var txrH = txr.height;
27744 var txrQ = self.getTextureQueue(txrH);
27745 var lookup = this.lookup; // retire the texture from the active / searchable queue:
27746
27747 removeFromArray(txrQ, txr);
27748 txr.retired = true; // remove the refs from the eles to the caches:
27749
27750 var eleCaches = txr.eleCaches;
27751
27752 for (var i = 0; i < eleCaches.length; i++) {
27753 var eleCache = eleCaches[i];
27754 lookup.deleteCache(eleCache.key, eleCache.level);
27755 }
27756
27757 clearArray(eleCaches); // add the texture to a retired queue so it can be recycled in future:
27758
27759 var rtxtrQ = self.getRetiredTextureQueue(txrH);
27760 rtxtrQ.push(txr);
27761};
27762
27763ETCp.addTexture = function (txrH, minW) {
27764 var self = this;
27765 var txrQ = self.getTextureQueue(txrH);
27766 var txr = {};
27767 txrQ.push(txr);
27768 txr.eleCaches = [];
27769 txr.height = txrH;
27770 txr.width = Math.max(defTxrWidth, minW);
27771 txr.usedWidth = 0;
27772 txr.invalidatedWidth = 0;
27773 txr.fullnessChecks = 0;
27774 txr.canvas = self.renderer.makeOffscreenCanvas(txr.width, txr.height);
27775 txr.context = txr.canvas.getContext('2d');
27776 return txr;
27777};
27778
27779ETCp.recycleTexture = function (txrH, minW) {
27780 var self = this;
27781 var txrQ = self.getTextureQueue(txrH);
27782 var rtxtrQ = self.getRetiredTextureQueue(txrH);
27783
27784 for (var i = 0; i < rtxtrQ.length; i++) {
27785 var txr = rtxtrQ[i];
27786
27787 if (txr.width >= minW) {
27788 txr.retired = false;
27789 txr.usedWidth = 0;
27790 txr.invalidatedWidth = 0;
27791 txr.fullnessChecks = 0;
27792 clearArray(txr.eleCaches);
27793 txr.context.setTransform(1, 0, 0, 1, 0, 0);
27794 txr.context.clearRect(0, 0, txr.width, txr.height);
27795 removeFromArray(rtxtrQ, txr);
27796 txrQ.push(txr);
27797 return txr;
27798 }
27799 }
27800};
27801
27802ETCp.queueElement = function (ele, lvl) {
27803 var self = this;
27804 var q = self.getElementQueue();
27805 var k2q = self.getElementKeyToQueue();
27806 var key = this.getKey(ele);
27807 var existingReq = k2q[key];
27808
27809 if (existingReq) {
27810 // use the max lvl b/c in between lvls are cheap to make
27811 existingReq.level = Math.max(existingReq.level, lvl);
27812 existingReq.eles.merge(ele);
27813 existingReq.reqs++;
27814 q.updateItem(existingReq);
27815 } else {
27816 var req = {
27817 eles: ele.spawn().merge(ele),
27818 level: lvl,
27819 reqs: 1,
27820 key: key
27821 };
27822 q.push(req);
27823 k2q[key] = req;
27824 }
27825};
27826
27827ETCp.dequeue = function (pxRatio
27828/*, extent*/
27829) {
27830 var self = this;
27831 var q = self.getElementQueue();
27832 var k2q = self.getElementKeyToQueue();
27833 var dequeued = [];
27834 var lookup = self.lookup;
27835
27836 for (var i = 0; i < maxDeqSize$1; i++) {
27837 if (q.size() > 0) {
27838 var req = q.pop();
27839 var key = req.key;
27840 var ele = req.eles[0]; // all eles have the same key
27841
27842 var cacheExists = lookup.hasCache(ele, req.level); // clear out the key to req lookup
27843
27844 k2q[key] = null; // dequeueing isn't necessary with an existing cache
27845
27846 if (cacheExists) {
27847 continue;
27848 }
27849
27850 dequeued.push(req);
27851 var bb = self.getBoundingBox(ele);
27852 self.getElement(ele, bb, pxRatio, req.level, getTxrReasons.dequeue);
27853 } else {
27854 break;
27855 }
27856 }
27857
27858 return dequeued;
27859};
27860
27861ETCp.removeFromQueue = function (ele) {
27862 var self = this;
27863 var q = self.getElementQueue();
27864 var k2q = self.getElementKeyToQueue();
27865 var key = this.getKey(ele);
27866 var req = k2q[key];
27867
27868 if (req != null) {
27869 if (req.eles.length === 1) {
27870 // remove if last ele in the req
27871 // bring to front of queue
27872 req.reqs = MAX_INT$1;
27873 q.updateItem(req);
27874 q.pop(); // remove from queue
27875
27876 k2q[key] = null; // remove from lookup map
27877 } else {
27878 // otherwise just remove ele from req
27879 req.eles.unmerge(ele);
27880 }
27881 }
27882};
27883
27884ETCp.onDequeue = function (fn) {
27885 this.onDequeues.push(fn);
27886};
27887
27888ETCp.offDequeue = function (fn) {
27889 removeFromArray(this.onDequeues, fn);
27890};
27891
27892ETCp.setupDequeueing = defs.setupDequeueing({
27893 deqRedrawThreshold: deqRedrawThreshold$1,
27894 deqCost: deqCost$1,
27895 deqAvgCost: deqAvgCost$1,
27896 deqNoDrawCost: deqNoDrawCost$1,
27897 deqFastCost: deqFastCost$1,
27898 deq: function deq(self, pxRatio, extent) {
27899 return self.dequeue(pxRatio, extent);
27900 },
27901 onDeqd: function onDeqd(self, deqd) {
27902 for (var i = 0; i < self.onDequeues.length; i++) {
27903 var fn = self.onDequeues[i];
27904 fn(deqd);
27905 }
27906 },
27907 shouldRedraw: function shouldRedraw(self, deqd, pxRatio, extent) {
27908 for (var i = 0; i < deqd.length; i++) {
27909 var eles = deqd[i].eles;
27910
27911 for (var j = 0; j < eles.length; j++) {
27912 var bb = eles[j].boundingBox();
27913
27914 if (boundingBoxesIntersect(bb, extent)) {
27915 return true;
27916 }
27917 }
27918 }
27919
27920 return false;
27921 },
27922 priority: function priority(self) {
27923 return self.renderer.beforeRenderPriorities.eleTxrDeq;
27924 }
27925});
27926
27927var defNumLayers = 1; // default number of layers to use
27928
27929var minLvl = -4; // when scaling smaller than that we don't need to re-render
27930
27931var maxLvl = 2; // when larger than this scale just render directly (caching is not helpful)
27932
27933var maxZoom = 3.99; // beyond this zoom level, layered textures are not used
27934
27935var deqRedrawThreshold = 50; // time to batch redraws together from dequeueing to allow more dequeueing calcs to happen in the meanwhile
27936
27937var refineEleDebounceTime = 50; // time to debounce sharper ele texture updates
27938
27939var deqCost = 0.15; // % of add'l rendering cost allowed for dequeuing ele caches each frame
27940
27941var deqAvgCost = 0.1; // % of add'l rendering cost compared to average overall redraw time
27942
27943var deqNoDrawCost = 0.9; // % of avg frame time that can be used for dequeueing when not drawing
27944
27945var deqFastCost = 0.9; // % of frame time to be used when >60fps
27946
27947var maxDeqSize = 1; // number of eles to dequeue and render at higher texture in each batch
27948
27949var invalidThreshold = 250; // time threshold for disabling b/c of invalidations
27950
27951var maxLayerArea = 4000 * 4000; // layers can't be bigger than this
27952
27953var useHighQualityEleTxrReqs = true; // whether to use high quality ele txr requests (generally faster and cheaper in the longterm)
27954// var log = function(){ console.log.apply( console, arguments ); };
27955
27956var LayeredTextureCache = function LayeredTextureCache(renderer) {
27957 var self = this;
27958 var r = self.renderer = renderer;
27959 var cy = r.cy;
27960 self.layersByLevel = {}; // e.g. 2 => [ layer1, layer2, ..., layerN ]
27961
27962 self.firstGet = true;
27963 self.lastInvalidationTime = performanceNow() - 2 * invalidThreshold;
27964 self.skipping = false;
27965 self.eleTxrDeqs = cy.collection();
27966 self.scheduleElementRefinement = debounce(function () {
27967 self.refineElementTextures(self.eleTxrDeqs);
27968 self.eleTxrDeqs.unmerge(self.eleTxrDeqs);
27969 }, refineEleDebounceTime);
27970 r.beforeRender(function (willDraw, now) {
27971 if (now - self.lastInvalidationTime <= invalidThreshold) {
27972 self.skipping = true;
27973 } else {
27974 self.skipping = false;
27975 }
27976 }, r.beforeRenderPriorities.lyrTxrSkip);
27977
27978 var qSort = function qSort(a, b) {
27979 return b.reqs - a.reqs;
27980 };
27981
27982 self.layersQueue = new Heap(qSort);
27983 self.setupDequeueing();
27984};
27985
27986var LTCp = LayeredTextureCache.prototype;
27987var layerIdPool = 0;
27988var MAX_INT = Math.pow(2, 53) - 1;
27989
27990LTCp.makeLayer = function (bb, lvl) {
27991 var scale = Math.pow(2, lvl);
27992 var w = Math.ceil(bb.w * scale);
27993 var h = Math.ceil(bb.h * scale);
27994 var canvas = this.renderer.makeOffscreenCanvas(w, h);
27995 var layer = {
27996 id: layerIdPool = ++layerIdPool % MAX_INT,
27997 bb: bb,
27998 level: lvl,
27999 width: w,
28000 height: h,
28001 canvas: canvas,
28002 context: canvas.getContext('2d'),
28003 eles: [],
28004 elesQueue: [],
28005 reqs: 0
28006 }; // log('make layer %s with w %s and h %s and lvl %s', layer.id, layer.width, layer.height, layer.level);
28007
28008 var cxt = layer.context;
28009 var dx = -layer.bb.x1;
28010 var dy = -layer.bb.y1; // do the transform on creation to save cycles (it's the same for all eles)
28011
28012 cxt.scale(scale, scale);
28013 cxt.translate(dx, dy);
28014 return layer;
28015};
28016
28017LTCp.getLayers = function (eles, pxRatio, lvl) {
28018 var self = this;
28019 var r = self.renderer;
28020 var cy = r.cy;
28021 var zoom = cy.zoom();
28022 var firstGet = self.firstGet;
28023 self.firstGet = false; // log('--\nget layers with %s eles', eles.length);
28024 //log eles.map(function(ele){ return ele.id() }) );
28025
28026 if (lvl == null) {
28027 lvl = Math.ceil(log2(zoom * pxRatio));
28028
28029 if (lvl < minLvl) {
28030 lvl = minLvl;
28031 } else if (zoom >= maxZoom || lvl > maxLvl) {
28032 return null;
28033 }
28034 }
28035
28036 self.validateLayersElesOrdering(lvl, eles);
28037 var layersByLvl = self.layersByLevel;
28038 var scale = Math.pow(2, lvl);
28039 var layers = layersByLvl[lvl] = layersByLvl[lvl] || [];
28040 var bb;
28041 var lvlComplete = self.levelIsComplete(lvl, eles);
28042 var tmpLayers;
28043
28044 var checkTempLevels = function checkTempLevels() {
28045 var canUseAsTmpLvl = function canUseAsTmpLvl(l) {
28046 self.validateLayersElesOrdering(l, eles);
28047
28048 if (self.levelIsComplete(l, eles)) {
28049 tmpLayers = layersByLvl[l];
28050 return true;
28051 }
28052 };
28053
28054 var checkLvls = function checkLvls(dir) {
28055 if (tmpLayers) {
28056 return;
28057 }
28058
28059 for (var l = lvl + dir; minLvl <= l && l <= maxLvl; l += dir) {
28060 if (canUseAsTmpLvl(l)) {
28061 break;
28062 }
28063 }
28064 };
28065
28066 checkLvls(+1);
28067 checkLvls(-1); // remove the invalid layers; they will be replaced as needed later in this function
28068
28069 for (var i = layers.length - 1; i >= 0; i--) {
28070 var layer = layers[i];
28071
28072 if (layer.invalid) {
28073 removeFromArray(layers, layer);
28074 }
28075 }
28076 };
28077
28078 if (!lvlComplete) {
28079 // if the current level is incomplete, then use the closest, best quality layerset temporarily
28080 // and later queue the current layerset so we can get the proper quality level soon
28081 checkTempLevels();
28082 } else {
28083 // log('level complete, using existing layers\n--');
28084 return layers;
28085 }
28086
28087 var getBb = function getBb() {
28088 if (!bb) {
28089 bb = makeBoundingBox();
28090
28091 for (var i = 0; i < eles.length; i++) {
28092 updateBoundingBox(bb, eles[i].boundingBox());
28093 }
28094 }
28095
28096 return bb;
28097 };
28098
28099 var makeLayer = function makeLayer(opts) {
28100 opts = opts || {};
28101 var after = opts.after;
28102 getBb();
28103 var area = bb.w * scale * (bb.h * scale);
28104
28105 if (area > maxLayerArea) {
28106 return null;
28107 }
28108
28109 var layer = self.makeLayer(bb, lvl);
28110
28111 if (after != null) {
28112 var index = layers.indexOf(after) + 1;
28113 layers.splice(index, 0, layer);
28114 } else if (opts.insert === undefined || opts.insert) {
28115 // no after specified => first layer made so put at start
28116 layers.unshift(layer);
28117 } // if( tmpLayers ){
28118 //self.queueLayer( layer );
28119 // }
28120
28121
28122 return layer;
28123 };
28124
28125 if (self.skipping && !firstGet) {
28126 // log('skip layers');
28127 return null;
28128 } // log('do layers');
28129
28130
28131 var layer = null;
28132 var maxElesPerLayer = eles.length / defNumLayers;
28133 var allowLazyQueueing = !firstGet;
28134
28135 for (var i = 0; i < eles.length; i++) {
28136 var ele = eles[i];
28137 var rs = ele._private.rscratch;
28138 var caches = rs.imgLayerCaches = rs.imgLayerCaches || {}; // log('look at ele', ele.id());
28139
28140 var existingLayer = caches[lvl];
28141
28142 if (existingLayer) {
28143 // reuse layer for later eles
28144 // log('reuse layer for', ele.id());
28145 layer = existingLayer;
28146 continue;
28147 }
28148
28149 if (!layer || layer.eles.length >= maxElesPerLayer || !boundingBoxInBoundingBox(layer.bb, ele.boundingBox())) {
28150 // log('make new layer for ele %s', ele.id());
28151 layer = makeLayer({
28152 insert: true,
28153 after: layer
28154 }); // if now layer can be built then we can't use layers at this level
28155
28156 if (!layer) {
28157 return null;
28158 } // log('new layer with id %s', layer.id);
28159
28160 }
28161
28162 if (tmpLayers || allowLazyQueueing) {
28163 // log('queue ele %s in layer %s', ele.id(), layer.id);
28164 self.queueLayer(layer, ele);
28165 } else {
28166 // log('draw ele %s in layer %s', ele.id(), layer.id);
28167 self.drawEleInLayer(layer, ele, lvl, pxRatio);
28168 }
28169
28170 layer.eles.push(ele);
28171 caches[lvl] = layer;
28172 } // log('--');
28173
28174
28175 if (tmpLayers) {
28176 // then we only queued the current layerset and can't draw it yet
28177 return tmpLayers;
28178 }
28179
28180 if (allowLazyQueueing) {
28181 // log('lazy queue level', lvl);
28182 return null;
28183 }
28184
28185 return layers;
28186}; // a layer may want to use an ele cache of a higher level to avoid blurriness
28187// so the layer level might not equal the ele level
28188
28189
28190LTCp.getEleLevelForLayerLevel = function (lvl, pxRatio) {
28191 return lvl;
28192};
28193
28194LTCp.drawEleInLayer = function (layer, ele, lvl, pxRatio) {
28195 var self = this;
28196 var r = this.renderer;
28197 var context = layer.context;
28198 var bb = ele.boundingBox();
28199
28200 if (bb.w === 0 || bb.h === 0 || !ele.visible()) {
28201 return;
28202 }
28203
28204 lvl = self.getEleLevelForLayerLevel(lvl, pxRatio);
28205
28206 {
28207 r.setImgSmoothing(context, false);
28208 }
28209
28210 {
28211 r.drawCachedElement(context, ele, null, null, lvl, useHighQualityEleTxrReqs);
28212 }
28213
28214 {
28215 r.setImgSmoothing(context, true);
28216 }
28217};
28218
28219LTCp.levelIsComplete = function (lvl, eles) {
28220 var self = this;
28221 var layers = self.layersByLevel[lvl];
28222
28223 if (!layers || layers.length === 0) {
28224 return false;
28225 }
28226
28227 var numElesInLayers = 0;
28228
28229 for (var i = 0; i < layers.length; i++) {
28230 var layer = layers[i]; // if there are any eles needed to be drawn yet, the level is not complete
28231
28232 if (layer.reqs > 0) {
28233 return false;
28234 } // if the layer is invalid, the level is not complete
28235
28236
28237 if (layer.invalid) {
28238 return false;
28239 }
28240
28241 numElesInLayers += layer.eles.length;
28242 } // we should have exactly the number of eles passed in to be complete
28243
28244
28245 if (numElesInLayers !== eles.length) {
28246 return false;
28247 }
28248
28249 return true;
28250};
28251
28252LTCp.validateLayersElesOrdering = function (lvl, eles) {
28253 var layers = this.layersByLevel[lvl];
28254
28255 if (!layers) {
28256 return;
28257 } // if in a layer the eles are not in the same order, then the layer is invalid
28258 // (i.e. there is an ele in between the eles in the layer)
28259
28260
28261 for (var i = 0; i < layers.length; i++) {
28262 var layer = layers[i];
28263 var offset = -1; // find the offset
28264
28265 for (var j = 0; j < eles.length; j++) {
28266 if (layer.eles[0] === eles[j]) {
28267 offset = j;
28268 break;
28269 }
28270 }
28271
28272 if (offset < 0) {
28273 // then the layer has nonexistant elements and is invalid
28274 this.invalidateLayer(layer);
28275 continue;
28276 } // the eles in the layer must be in the same continuous order, else the layer is invalid
28277
28278
28279 var o = offset;
28280
28281 for (var j = 0; j < layer.eles.length; j++) {
28282 if (layer.eles[j] !== eles[o + j]) {
28283 // log('invalidate based on ordering', layer.id);
28284 this.invalidateLayer(layer);
28285 break;
28286 }
28287 }
28288 }
28289};
28290
28291LTCp.updateElementsInLayers = function (eles, update) {
28292 var self = this;
28293 var isEles = element(eles[0]); // collect udpated elements (cascaded from the layers) and update each
28294 // layer itself along the way
28295
28296 for (var i = 0; i < eles.length; i++) {
28297 var req = isEles ? null : eles[i];
28298 var ele = isEles ? eles[i] : eles[i].ele;
28299 var rs = ele._private.rscratch;
28300 var caches = rs.imgLayerCaches = rs.imgLayerCaches || {};
28301
28302 for (var l = minLvl; l <= maxLvl; l++) {
28303 var layer = caches[l];
28304
28305 if (!layer) {
28306 continue;
28307 } // if update is a request from the ele cache, then it affects only
28308 // the matching level
28309
28310
28311 if (req && self.getEleLevelForLayerLevel(layer.level) !== req.level) {
28312 continue;
28313 }
28314
28315 update(layer, ele, req);
28316 }
28317 }
28318};
28319
28320LTCp.haveLayers = function () {
28321 var self = this;
28322 var haveLayers = false;
28323
28324 for (var l = minLvl; l <= maxLvl; l++) {
28325 var layers = self.layersByLevel[l];
28326
28327 if (layers && layers.length > 0) {
28328 haveLayers = true;
28329 break;
28330 }
28331 }
28332
28333 return haveLayers;
28334};
28335
28336LTCp.invalidateElements = function (eles) {
28337 var self = this;
28338
28339 if (eles.length === 0) {
28340 return;
28341 }
28342
28343 self.lastInvalidationTime = performanceNow(); // log('update invalidate layer time from eles');
28344
28345 if (eles.length === 0 || !self.haveLayers()) {
28346 return;
28347 }
28348
28349 self.updateElementsInLayers(eles, function invalAssocLayers(layer, ele, req) {
28350 self.invalidateLayer(layer);
28351 });
28352};
28353
28354LTCp.invalidateLayer = function (layer) {
28355 // log('update invalidate layer time');
28356 this.lastInvalidationTime = performanceNow();
28357
28358 if (layer.invalid) {
28359 return;
28360 } // save cycles
28361
28362
28363 var lvl = layer.level;
28364 var eles = layer.eles;
28365 var layers = this.layersByLevel[lvl]; // log('invalidate layer', layer.id );
28366
28367 removeFromArray(layers, layer); // layer.eles = [];
28368
28369 layer.elesQueue = [];
28370 layer.invalid = true;
28371
28372 if (layer.replacement) {
28373 layer.replacement.invalid = true;
28374 }
28375
28376 for (var i = 0; i < eles.length; i++) {
28377 var caches = eles[i]._private.rscratch.imgLayerCaches;
28378
28379 if (caches) {
28380 caches[lvl] = null;
28381 }
28382 }
28383};
28384
28385LTCp.refineElementTextures = function (eles) {
28386 var self = this; // log('refine', eles.length);
28387
28388 self.updateElementsInLayers(eles, function refineEachEle(layer, ele, req) {
28389 var rLyr = layer.replacement;
28390
28391 if (!rLyr) {
28392 rLyr = layer.replacement = self.makeLayer(layer.bb, layer.level);
28393 rLyr.replaces = layer;
28394 rLyr.eles = layer.eles; // log('make replacement layer %s for %s with level %s', rLyr.id, layer.id, rLyr.level);
28395 }
28396
28397 if (!rLyr.reqs) {
28398 for (var i = 0; i < rLyr.eles.length; i++) {
28399 self.queueLayer(rLyr, rLyr.eles[i]);
28400 } // log('queue replacement layer refinement', rLyr.id);
28401
28402 }
28403 });
28404};
28405
28406LTCp.enqueueElementRefinement = function (ele) {
28407
28408 this.eleTxrDeqs.merge(ele);
28409 this.scheduleElementRefinement();
28410};
28411
28412LTCp.queueLayer = function (layer, ele) {
28413 var self = this;
28414 var q = self.layersQueue;
28415 var elesQ = layer.elesQueue;
28416 var hasId = elesQ.hasId = elesQ.hasId || {}; // if a layer is going to be replaced, queuing is a waste of time
28417
28418 if (layer.replacement) {
28419 return;
28420 }
28421
28422 if (ele) {
28423 if (hasId[ele.id()]) {
28424 return;
28425 }
28426
28427 elesQ.push(ele);
28428 hasId[ele.id()] = true;
28429 }
28430
28431 if (layer.reqs) {
28432 layer.reqs++;
28433 q.updateItem(layer);
28434 } else {
28435 layer.reqs = 1;
28436 q.push(layer);
28437 }
28438};
28439
28440LTCp.dequeue = function (pxRatio) {
28441 var self = this;
28442 var q = self.layersQueue;
28443 var deqd = [];
28444 var eleDeqs = 0;
28445
28446 while (eleDeqs < maxDeqSize) {
28447 if (q.size() === 0) {
28448 break;
28449 }
28450
28451 var layer = q.peek(); // if a layer has been or will be replaced, then don't waste time with it
28452
28453 if (layer.replacement) {
28454 // log('layer %s in queue skipped b/c it already has a replacement', layer.id);
28455 q.pop();
28456 continue;
28457 } // if this is a replacement layer that has been superceded, then forget it
28458
28459
28460 if (layer.replaces && layer !== layer.replaces.replacement) {
28461 // log('layer is no longer the most uptodate replacement; dequeued', layer.id)
28462 q.pop();
28463 continue;
28464 }
28465
28466 if (layer.invalid) {
28467 // log('replacement layer %s is invalid; dequeued', layer.id);
28468 q.pop();
28469 continue;
28470 }
28471
28472 var ele = layer.elesQueue.shift();
28473
28474 if (ele) {
28475 // log('dequeue layer %s', layer.id);
28476 self.drawEleInLayer(layer, ele, layer.level, pxRatio);
28477 eleDeqs++;
28478 }
28479
28480 if (deqd.length === 0) {
28481 // we need only one entry in deqd to queue redrawing etc
28482 deqd.push(true);
28483 } // if the layer has all its eles done, then remove from the queue
28484
28485
28486 if (layer.elesQueue.length === 0) {
28487 q.pop();
28488 layer.reqs = 0; // log('dequeue of layer %s complete', layer.id);
28489 // when a replacement layer is dequeued, it replaces the old layer in the level
28490
28491 if (layer.replaces) {
28492 self.applyLayerReplacement(layer);
28493 }
28494
28495 self.requestRedraw();
28496 }
28497 }
28498
28499 return deqd;
28500};
28501
28502LTCp.applyLayerReplacement = function (layer) {
28503 var self = this;
28504 var layersInLevel = self.layersByLevel[layer.level];
28505 var replaced = layer.replaces;
28506 var index = layersInLevel.indexOf(replaced); // if the replaced layer is not in the active list for the level, then replacing
28507 // refs would be a mistake (i.e. overwriting the true active layer)
28508
28509 if (index < 0 || replaced.invalid) {
28510 // log('replacement layer would have no effect', layer.id);
28511 return;
28512 }
28513
28514 layersInLevel[index] = layer; // replace level ref
28515 // replace refs in eles
28516
28517 for (var i = 0; i < layer.eles.length; i++) {
28518 var _p = layer.eles[i]._private;
28519 var cache = _p.imgLayerCaches = _p.imgLayerCaches || {};
28520
28521 if (cache) {
28522 cache[layer.level] = layer;
28523 }
28524 } // log('apply replacement layer %s over %s', layer.id, replaced.id);
28525
28526
28527 self.requestRedraw();
28528};
28529
28530LTCp.requestRedraw = debounce(function () {
28531 var r = this.renderer;
28532 r.redrawHint('eles', true);
28533 r.redrawHint('drag', true);
28534 r.redraw();
28535}, 100);
28536LTCp.setupDequeueing = defs.setupDequeueing({
28537 deqRedrawThreshold: deqRedrawThreshold,
28538 deqCost: deqCost,
28539 deqAvgCost: deqAvgCost,
28540 deqNoDrawCost: deqNoDrawCost,
28541 deqFastCost: deqFastCost,
28542 deq: function deq(self, pxRatio) {
28543 return self.dequeue(pxRatio);
28544 },
28545 onDeqd: noop$1,
28546 shouldRedraw: trueify,
28547 priority: function priority(self) {
28548 return self.renderer.beforeRenderPriorities.lyrTxrDeq;
28549 }
28550});
28551
28552var CRp$a = {};
28553var impl;
28554
28555function polygon(context, points) {
28556 for (var i = 0; i < points.length; i++) {
28557 var pt = points[i];
28558 context.lineTo(pt.x, pt.y);
28559 }
28560}
28561
28562function triangleBackcurve(context, points, controlPoint) {
28563 var firstPt;
28564
28565 for (var i = 0; i < points.length; i++) {
28566 var pt = points[i];
28567
28568 if (i === 0) {
28569 firstPt = pt;
28570 }
28571
28572 context.lineTo(pt.x, pt.y);
28573 }
28574
28575 context.quadraticCurveTo(controlPoint.x, controlPoint.y, firstPt.x, firstPt.y);
28576}
28577
28578function triangleTee(context, trianglePoints, teePoints) {
28579 if (context.beginPath) {
28580 context.beginPath();
28581 }
28582
28583 var triPts = trianglePoints;
28584
28585 for (var i = 0; i < triPts.length; i++) {
28586 var pt = triPts[i];
28587 context.lineTo(pt.x, pt.y);
28588 }
28589
28590 var teePts = teePoints;
28591 var firstTeePt = teePoints[0];
28592 context.moveTo(firstTeePt.x, firstTeePt.y);
28593
28594 for (var i = 1; i < teePts.length; i++) {
28595 var pt = teePts[i];
28596 context.lineTo(pt.x, pt.y);
28597 }
28598
28599 if (context.closePath) {
28600 context.closePath();
28601 }
28602}
28603
28604function circleTriangle(context, trianglePoints, rx, ry, r) {
28605 if (context.beginPath) {
28606 context.beginPath();
28607 }
28608
28609 context.arc(rx, ry, r, 0, Math.PI * 2, false);
28610 var triPts = trianglePoints;
28611 var firstTrPt = triPts[0];
28612 context.moveTo(firstTrPt.x, firstTrPt.y);
28613
28614 for (var i = 0; i < triPts.length; i++) {
28615 var pt = triPts[i];
28616 context.lineTo(pt.x, pt.y);
28617 }
28618
28619 if (context.closePath) {
28620 context.closePath();
28621 }
28622}
28623
28624function circle(context, rx, ry, r) {
28625 context.arc(rx, ry, r, 0, Math.PI * 2, false);
28626}
28627
28628CRp$a.arrowShapeImpl = function (name) {
28629 return (impl || (impl = {
28630 'polygon': polygon,
28631 'triangle-backcurve': triangleBackcurve,
28632 'triangle-tee': triangleTee,
28633 'circle-triangle': circleTriangle,
28634 'triangle-cross': triangleTee,
28635 'circle': circle
28636 }))[name];
28637};
28638
28639var CRp$9 = {};
28640
28641CRp$9.drawElement = function (context, ele, shiftToOriginWithBb, showLabel, showOverlay, showOpacity) {
28642 var r = this;
28643
28644 if (ele.isNode()) {
28645 r.drawNode(context, ele, shiftToOriginWithBb, showLabel, showOverlay, showOpacity);
28646 } else {
28647 r.drawEdge(context, ele, shiftToOriginWithBb, showLabel, showOverlay, showOpacity);
28648 }
28649};
28650
28651CRp$9.drawElementOverlay = function (context, ele) {
28652 var r = this;
28653
28654 if (ele.isNode()) {
28655 r.drawNodeOverlay(context, ele);
28656 } else {
28657 r.drawEdgeOverlay(context, ele);
28658 }
28659};
28660
28661CRp$9.drawElementUnderlay = function (context, ele) {
28662 var r = this;
28663
28664 if (ele.isNode()) {
28665 r.drawNodeUnderlay(context, ele);
28666 } else {
28667 r.drawEdgeUnderlay(context, ele);
28668 }
28669};
28670
28671CRp$9.drawCachedElementPortion = function (context, ele, eleTxrCache, pxRatio, lvl, reason, getRotation, getOpacity) {
28672 var r = this;
28673 var bb = eleTxrCache.getBoundingBox(ele);
28674
28675 if (bb.w === 0 || bb.h === 0) {
28676 return;
28677 } // ignore zero size case
28678
28679
28680 var eleCache = eleTxrCache.getElement(ele, bb, pxRatio, lvl, reason);
28681
28682 if (eleCache != null) {
28683 var opacity = getOpacity(r, ele);
28684
28685 if (opacity === 0) {
28686 return;
28687 }
28688
28689 var theta = getRotation(r, ele);
28690 var x1 = bb.x1,
28691 y1 = bb.y1,
28692 w = bb.w,
28693 h = bb.h;
28694 var x, y, sx, sy, smooth;
28695
28696 if (theta !== 0) {
28697 var rotPt = eleTxrCache.getRotationPoint(ele);
28698 sx = rotPt.x;
28699 sy = rotPt.y;
28700 context.translate(sx, sy);
28701 context.rotate(theta);
28702 smooth = r.getImgSmoothing(context);
28703
28704 if (!smooth) {
28705 r.setImgSmoothing(context, true);
28706 }
28707
28708 var off = eleTxrCache.getRotationOffset(ele);
28709 x = off.x;
28710 y = off.y;
28711 } else {
28712 x = x1;
28713 y = y1;
28714 }
28715
28716 var oldGlobalAlpha;
28717
28718 if (opacity !== 1) {
28719 oldGlobalAlpha = context.globalAlpha;
28720 context.globalAlpha = oldGlobalAlpha * opacity;
28721 }
28722
28723 context.drawImage(eleCache.texture.canvas, eleCache.x, 0, eleCache.width, eleCache.height, x, y, w, h);
28724
28725 if (opacity !== 1) {
28726 context.globalAlpha = oldGlobalAlpha;
28727 }
28728
28729 if (theta !== 0) {
28730 context.rotate(-theta);
28731 context.translate(-sx, -sy);
28732
28733 if (!smooth) {
28734 r.setImgSmoothing(context, false);
28735 }
28736 }
28737 } else {
28738 eleTxrCache.drawElement(context, ele); // direct draw fallback
28739 }
28740};
28741
28742var getZeroRotation = function getZeroRotation() {
28743 return 0;
28744};
28745
28746var getLabelRotation = function getLabelRotation(r, ele) {
28747 return r.getTextAngle(ele, null);
28748};
28749
28750var getSourceLabelRotation = function getSourceLabelRotation(r, ele) {
28751 return r.getTextAngle(ele, 'source');
28752};
28753
28754var getTargetLabelRotation = function getTargetLabelRotation(r, ele) {
28755 return r.getTextAngle(ele, 'target');
28756};
28757
28758var getOpacity = function getOpacity(r, ele) {
28759 return ele.effectiveOpacity();
28760};
28761
28762var getTextOpacity = function getTextOpacity(e, ele) {
28763 return ele.pstyle('text-opacity').pfValue * ele.effectiveOpacity();
28764};
28765
28766CRp$9.drawCachedElement = function (context, ele, pxRatio, extent, lvl, requestHighQuality) {
28767 var r = this;
28768 var _r$data = r.data,
28769 eleTxrCache = _r$data.eleTxrCache,
28770 lblTxrCache = _r$data.lblTxrCache,
28771 slbTxrCache = _r$data.slbTxrCache,
28772 tlbTxrCache = _r$data.tlbTxrCache;
28773 var bb = ele.boundingBox();
28774 var reason = requestHighQuality === true ? eleTxrCache.reasons.highQuality : null;
28775
28776 if (bb.w === 0 || bb.h === 0 || !ele.visible()) {
28777 return;
28778 }
28779
28780 if (!extent || boundingBoxesIntersect(bb, extent)) {
28781 var isEdge = ele.isEdge();
28782
28783 var badLine = ele.element()._private.rscratch.badLine;
28784
28785 r.drawElementUnderlay(context, ele);
28786 r.drawCachedElementPortion(context, ele, eleTxrCache, pxRatio, lvl, reason, getZeroRotation, getOpacity);
28787
28788 if (!isEdge || !badLine) {
28789 r.drawCachedElementPortion(context, ele, lblTxrCache, pxRatio, lvl, reason, getLabelRotation, getTextOpacity);
28790 }
28791
28792 if (isEdge && !badLine) {
28793 r.drawCachedElementPortion(context, ele, slbTxrCache, pxRatio, lvl, reason, getSourceLabelRotation, getTextOpacity);
28794 r.drawCachedElementPortion(context, ele, tlbTxrCache, pxRatio, lvl, reason, getTargetLabelRotation, getTextOpacity);
28795 }
28796
28797 r.drawElementOverlay(context, ele);
28798 }
28799};
28800
28801CRp$9.drawElements = function (context, eles) {
28802 var r = this;
28803
28804 for (var i = 0; i < eles.length; i++) {
28805 var ele = eles[i];
28806 r.drawElement(context, ele);
28807 }
28808};
28809
28810CRp$9.drawCachedElements = function (context, eles, pxRatio, extent) {
28811 var r = this;
28812
28813 for (var i = 0; i < eles.length; i++) {
28814 var ele = eles[i];
28815 r.drawCachedElement(context, ele, pxRatio, extent);
28816 }
28817};
28818
28819CRp$9.drawCachedNodes = function (context, eles, pxRatio, extent) {
28820 var r = this;
28821
28822 for (var i = 0; i < eles.length; i++) {
28823 var ele = eles[i];
28824
28825 if (!ele.isNode()) {
28826 continue;
28827 }
28828
28829 r.drawCachedElement(context, ele, pxRatio, extent);
28830 }
28831};
28832
28833CRp$9.drawLayeredElements = function (context, eles, pxRatio, extent) {
28834 var r = this;
28835 var layers = r.data.lyrTxrCache.getLayers(eles, pxRatio);
28836
28837 if (layers) {
28838 for (var i = 0; i < layers.length; i++) {
28839 var layer = layers[i];
28840 var bb = layer.bb;
28841
28842 if (bb.w === 0 || bb.h === 0) {
28843 continue;
28844 }
28845
28846 context.drawImage(layer.canvas, bb.x1, bb.y1, bb.w, bb.h);
28847 }
28848 } else {
28849 // fall back on plain caching if no layers
28850 r.drawCachedElements(context, eles, pxRatio, extent);
28851 }
28852};
28853
28854/* global Path2D */
28855var CRp$8 = {};
28856
28857CRp$8.drawEdge = function (context, edge, shiftToOriginWithBb) {
28858 var drawLabel = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true;
28859 var shouldDrawOverlay = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : true;
28860 var shouldDrawOpacity = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : true;
28861 var r = this;
28862 var rs = edge._private.rscratch;
28863
28864 if (shouldDrawOpacity && !edge.visible()) {
28865 return;
28866 } // if bezier ctrl pts can not be calculated, then die
28867
28868
28869 if (rs.badLine || rs.allpts == null || isNaN(rs.allpts[0])) {
28870 // isNaN in case edge is impossible and browser bugs (e.g. safari)
28871 return;
28872 }
28873
28874 var bb;
28875
28876 if (shiftToOriginWithBb) {
28877 bb = shiftToOriginWithBb;
28878 context.translate(-bb.x1, -bb.y1);
28879 }
28880
28881 var opacity = shouldDrawOpacity ? edge.pstyle('opacity').value : 1;
28882 var lineOpacity = shouldDrawOpacity ? edge.pstyle('line-opacity').value : 1;
28883 var curveStyle = edge.pstyle('curve-style').value;
28884 var lineStyle = edge.pstyle('line-style').value;
28885 var edgeWidth = edge.pstyle('width').pfValue;
28886 var lineCap = edge.pstyle('line-cap').value;
28887 var effectiveLineOpacity = opacity * lineOpacity; // separate arrow opacity would require arrow-opacity property
28888
28889 var effectiveArrowOpacity = opacity * lineOpacity;
28890
28891 var drawLine = function drawLine() {
28892 var strokeOpacity = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : effectiveLineOpacity;
28893
28894 if (curveStyle === 'straight-triangle') {
28895 r.eleStrokeStyle(context, edge, strokeOpacity);
28896 r.drawEdgeTrianglePath(edge, context, rs.allpts);
28897 } else {
28898 context.lineWidth = edgeWidth;
28899 context.lineCap = lineCap;
28900 r.eleStrokeStyle(context, edge, strokeOpacity);
28901 r.drawEdgePath(edge, context, rs.allpts, lineStyle);
28902 context.lineCap = 'butt'; // reset for other drawing functions
28903 }
28904 };
28905
28906 var drawOverlay = function drawOverlay() {
28907 if (!shouldDrawOverlay) {
28908 return;
28909 }
28910
28911 r.drawEdgeOverlay(context, edge);
28912 };
28913
28914 var drawUnderlay = function drawUnderlay() {
28915 if (!shouldDrawOverlay) {
28916 return;
28917 }
28918
28919 r.drawEdgeUnderlay(context, edge);
28920 };
28921
28922 var drawArrows = function drawArrows() {
28923 var arrowOpacity = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : effectiveArrowOpacity;
28924 r.drawArrowheads(context, edge, arrowOpacity);
28925 };
28926
28927 var drawText = function drawText() {
28928 r.drawElementText(context, edge, null, drawLabel);
28929 };
28930
28931 context.lineJoin = 'round';
28932 var ghost = edge.pstyle('ghost').value === 'yes';
28933
28934 if (ghost) {
28935 var gx = edge.pstyle('ghost-offset-x').pfValue;
28936 var gy = edge.pstyle('ghost-offset-y').pfValue;
28937 var ghostOpacity = edge.pstyle('ghost-opacity').value;
28938 var effectiveGhostOpacity = effectiveLineOpacity * ghostOpacity;
28939 context.translate(gx, gy);
28940 drawLine(effectiveGhostOpacity);
28941 drawArrows(effectiveGhostOpacity);
28942 context.translate(-gx, -gy);
28943 }
28944
28945 drawUnderlay();
28946 drawLine();
28947 drawArrows();
28948 drawOverlay();
28949 drawText();
28950
28951 if (shiftToOriginWithBb) {
28952 context.translate(bb.x1, bb.y1);
28953 }
28954};
28955
28956var drawEdgeOverlayUnderlay = function drawEdgeOverlayUnderlay(overlayOrUnderlay) {
28957 if (!['overlay', 'underlay'].includes(overlayOrUnderlay)) {
28958 throw new Error('Invalid state');
28959 }
28960
28961 return function (context, edge) {
28962 if (!edge.visible()) {
28963 return;
28964 }
28965
28966 var opacity = edge.pstyle("".concat(overlayOrUnderlay, "-opacity")).value;
28967
28968 if (opacity === 0) {
28969 return;
28970 }
28971
28972 var r = this;
28973 var usePaths = r.usePaths();
28974 var rs = edge._private.rscratch;
28975 var padding = edge.pstyle("".concat(overlayOrUnderlay, "-padding")).pfValue;
28976 var width = 2 * padding;
28977 var color = edge.pstyle("".concat(overlayOrUnderlay, "-color")).value;
28978 context.lineWidth = width;
28979
28980 if (rs.edgeType === 'self' && !usePaths) {
28981 context.lineCap = 'butt';
28982 } else {
28983 context.lineCap = 'round';
28984 }
28985
28986 r.colorStrokeStyle(context, color[0], color[1], color[2], opacity);
28987 r.drawEdgePath(edge, context, rs.allpts, 'solid');
28988 };
28989};
28990
28991CRp$8.drawEdgeOverlay = drawEdgeOverlayUnderlay('overlay');
28992CRp$8.drawEdgeUnderlay = drawEdgeOverlayUnderlay('underlay');
28993
28994CRp$8.drawEdgePath = function (edge, context, pts, type) {
28995 var rs = edge._private.rscratch;
28996 var canvasCxt = context;
28997 var path;
28998 var pathCacheHit = false;
28999 var usePaths = this.usePaths();
29000 var lineDashPattern = edge.pstyle('line-dash-pattern').pfValue;
29001 var lineDashOffset = edge.pstyle('line-dash-offset').pfValue;
29002
29003 if (usePaths) {
29004 var pathCacheKey = pts.join('$');
29005 var keyMatches = rs.pathCacheKey && rs.pathCacheKey === pathCacheKey;
29006
29007 if (keyMatches) {
29008 path = context = rs.pathCache;
29009 pathCacheHit = true;
29010 } else {
29011 path = context = new Path2D();
29012 rs.pathCacheKey = pathCacheKey;
29013 rs.pathCache = path;
29014 }
29015 }
29016
29017 if (canvasCxt.setLineDash) {
29018 // for very outofdate browsers
29019 switch (type) {
29020 case 'dotted':
29021 canvasCxt.setLineDash([1, 1]);
29022 break;
29023
29024 case 'dashed':
29025 canvasCxt.setLineDash(lineDashPattern);
29026 canvasCxt.lineDashOffset = lineDashOffset;
29027 break;
29028
29029 case 'solid':
29030 canvasCxt.setLineDash([]);
29031 break;
29032 }
29033 }
29034
29035 if (!pathCacheHit && !rs.badLine) {
29036 if (context.beginPath) {
29037 context.beginPath();
29038 }
29039
29040 context.moveTo(pts[0], pts[1]);
29041
29042 switch (rs.edgeType) {
29043 case 'bezier':
29044 case 'self':
29045 case 'compound':
29046 case 'multibezier':
29047 for (var i = 2; i + 3 < pts.length; i += 4) {
29048 context.quadraticCurveTo(pts[i], pts[i + 1], pts[i + 2], pts[i + 3]);
29049 }
29050
29051 break;
29052
29053 case 'straight':
29054 case 'segments':
29055 case 'haystack':
29056 for (var _i = 2; _i + 1 < pts.length; _i += 2) {
29057 context.lineTo(pts[_i], pts[_i + 1]);
29058 }
29059
29060 break;
29061 }
29062 }
29063
29064 context = canvasCxt;
29065
29066 if (usePaths) {
29067 context.stroke(path);
29068 } else {
29069 context.stroke();
29070 } // reset any line dashes
29071
29072
29073 if (context.setLineDash) {
29074 // for very outofdate browsers
29075 context.setLineDash([]);
29076 }
29077};
29078
29079CRp$8.drawEdgeTrianglePath = function (edge, context, pts) {
29080 // use line stroke style for triangle fill style
29081 context.fillStyle = context.strokeStyle;
29082 var edgeWidth = edge.pstyle('width').pfValue;
29083
29084 for (var i = 0; i + 1 < pts.length; i += 2) {
29085 var vector = [pts[i + 2] - pts[i], pts[i + 3] - pts[i + 1]];
29086 var length = Math.sqrt(vector[0] * vector[0] + vector[1] * vector[1]);
29087 var normal = [vector[1] / length, -vector[0] / length];
29088 var triangleHead = [normal[0] * edgeWidth / 2, normal[1] * edgeWidth / 2];
29089 context.beginPath();
29090 context.moveTo(pts[i] - triangleHead[0], pts[i + 1] - triangleHead[1]);
29091 context.lineTo(pts[i] + triangleHead[0], pts[i + 1] + triangleHead[1]);
29092 context.lineTo(pts[i + 2], pts[i + 3]);
29093 context.closePath();
29094 context.fill();
29095 }
29096};
29097
29098CRp$8.drawArrowheads = function (context, edge, opacity) {
29099 var rs = edge._private.rscratch;
29100 var isHaystack = rs.edgeType === 'haystack';
29101
29102 if (!isHaystack) {
29103 this.drawArrowhead(context, edge, 'source', rs.arrowStartX, rs.arrowStartY, rs.srcArrowAngle, opacity);
29104 }
29105
29106 this.drawArrowhead(context, edge, 'mid-target', rs.midX, rs.midY, rs.midtgtArrowAngle, opacity);
29107 this.drawArrowhead(context, edge, 'mid-source', rs.midX, rs.midY, rs.midsrcArrowAngle, opacity);
29108
29109 if (!isHaystack) {
29110 this.drawArrowhead(context, edge, 'target', rs.arrowEndX, rs.arrowEndY, rs.tgtArrowAngle, opacity);
29111 }
29112};
29113
29114CRp$8.drawArrowhead = function (context, edge, prefix, x, y, angle, opacity) {
29115 if (isNaN(x) || x == null || isNaN(y) || y == null || isNaN(angle) || angle == null) {
29116 return;
29117 }
29118
29119 var self = this;
29120 var arrowShape = edge.pstyle(prefix + '-arrow-shape').value;
29121
29122 if (arrowShape === 'none') {
29123 return;
29124 }
29125
29126 var arrowClearFill = edge.pstyle(prefix + '-arrow-fill').value === 'hollow' ? 'both' : 'filled';
29127 var arrowFill = edge.pstyle(prefix + '-arrow-fill').value;
29128 var edgeWidth = edge.pstyle('width').pfValue;
29129 var edgeOpacity = edge.pstyle('opacity').value;
29130
29131 if (opacity === undefined) {
29132 opacity = edgeOpacity;
29133 }
29134
29135 var gco = context.globalCompositeOperation;
29136
29137 if (opacity !== 1 || arrowFill === 'hollow') {
29138 // then extra clear is needed
29139 context.globalCompositeOperation = 'destination-out';
29140 self.colorFillStyle(context, 255, 255, 255, 1);
29141 self.colorStrokeStyle(context, 255, 255, 255, 1);
29142 self.drawArrowShape(edge, context, arrowClearFill, edgeWidth, arrowShape, x, y, angle);
29143 context.globalCompositeOperation = gco;
29144 } // otherwise, the opaque arrow clears it for free :)
29145
29146
29147 var color = edge.pstyle(prefix + '-arrow-color').value;
29148 self.colorFillStyle(context, color[0], color[1], color[2], opacity);
29149 self.colorStrokeStyle(context, color[0], color[1], color[2], opacity);
29150 self.drawArrowShape(edge, context, arrowFill, edgeWidth, arrowShape, x, y, angle);
29151};
29152
29153CRp$8.drawArrowShape = function (edge, context, fill, edgeWidth, shape, x, y, angle) {
29154 var r = this;
29155 var usePaths = this.usePaths() && shape !== 'triangle-cross';
29156 var pathCacheHit = false;
29157 var path;
29158 var canvasContext = context;
29159 var translation = {
29160 x: x,
29161 y: y
29162 };
29163 var scale = edge.pstyle('arrow-scale').value;
29164 var size = this.getArrowWidth(edgeWidth, scale);
29165 var shapeImpl = r.arrowShapes[shape];
29166
29167 if (usePaths) {
29168 var cache = r.arrowPathCache = r.arrowPathCache || [];
29169 var key = hashString(shape);
29170 var cachedPath = cache[key];
29171
29172 if (cachedPath != null) {
29173 path = context = cachedPath;
29174 pathCacheHit = true;
29175 } else {
29176 path = context = new Path2D();
29177 cache[key] = path;
29178 }
29179 }
29180
29181 if (!pathCacheHit) {
29182 if (context.beginPath) {
29183 context.beginPath();
29184 }
29185
29186 if (usePaths) {
29187 // store in the path cache with values easily manipulated later
29188 shapeImpl.draw(context, 1, 0, {
29189 x: 0,
29190 y: 0
29191 }, 1);
29192 } else {
29193 shapeImpl.draw(context, size, angle, translation, edgeWidth);
29194 }
29195
29196 if (context.closePath) {
29197 context.closePath();
29198 }
29199 }
29200
29201 context = canvasContext;
29202
29203 if (usePaths) {
29204 // set transform to arrow position/orientation
29205 context.translate(x, y);
29206 context.rotate(angle);
29207 context.scale(size, size);
29208 }
29209
29210 if (fill === 'filled' || fill === 'both') {
29211 if (usePaths) {
29212 context.fill(path);
29213 } else {
29214 context.fill();
29215 }
29216 }
29217
29218 if (fill === 'hollow' || fill === 'both') {
29219 context.lineWidth = (shapeImpl.matchEdgeWidth ? edgeWidth : 1) / (usePaths ? size : 1);
29220 context.lineJoin = 'miter';
29221
29222 if (usePaths) {
29223 context.stroke(path);
29224 } else {
29225 context.stroke();
29226 }
29227 }
29228
29229 if (usePaths) {
29230 // reset transform by applying inverse
29231 context.scale(1 / size, 1 / size);
29232 context.rotate(-angle);
29233 context.translate(-x, -y);
29234 }
29235};
29236
29237var CRp$7 = {};
29238
29239CRp$7.safeDrawImage = function (context, img, ix, iy, iw, ih, x, y, w, h) {
29240 // detect problematic cases for old browsers with bad images (cheaper than try-catch)
29241 if (iw <= 0 || ih <= 0 || w <= 0 || h <= 0) {
29242 return;
29243 }
29244
29245 try {
29246 context.drawImage(img, ix, iy, iw, ih, x, y, w, h);
29247 } catch (e) {
29248 warn(e);
29249 }
29250};
29251
29252CRp$7.drawInscribedImage = function (context, img, node, index, nodeOpacity) {
29253 var r = this;
29254 var pos = node.position();
29255 var nodeX = pos.x;
29256 var nodeY = pos.y;
29257 var styleObj = node.cy().style();
29258 var getIndexedStyle = styleObj.getIndexedStyle.bind(styleObj);
29259 var fit = getIndexedStyle(node, 'background-fit', 'value', index);
29260 var repeat = getIndexedStyle(node, 'background-repeat', 'value', index);
29261 var nodeW = node.width();
29262 var nodeH = node.height();
29263 var paddingX2 = node.padding() * 2;
29264 var nodeTW = nodeW + (getIndexedStyle(node, 'background-width-relative-to', 'value', index) === 'inner' ? 0 : paddingX2);
29265 var nodeTH = nodeH + (getIndexedStyle(node, 'background-height-relative-to', 'value', index) === 'inner' ? 0 : paddingX2);
29266 var rs = node._private.rscratch;
29267 var clip = getIndexedStyle(node, 'background-clip', 'value', index);
29268 var shouldClip = clip === 'node';
29269 var imgOpacity = getIndexedStyle(node, 'background-image-opacity', 'value', index) * nodeOpacity;
29270 var smooth = getIndexedStyle(node, 'background-image-smoothing', 'value', index);
29271 var imgW = img.width || img.cachedW;
29272 var imgH = img.height || img.cachedH; // workaround for broken browsers like ie
29273
29274 if (null == imgW || null == imgH) {
29275 document.body.appendChild(img); // eslint-disable-line no-undef
29276
29277 imgW = img.cachedW = img.width || img.offsetWidth;
29278 imgH = img.cachedH = img.height || img.offsetHeight;
29279 document.body.removeChild(img); // eslint-disable-line no-undef
29280 }
29281
29282 var w = imgW;
29283 var h = imgH;
29284
29285 if (getIndexedStyle(node, 'background-width', 'value', index) !== 'auto') {
29286 if (getIndexedStyle(node, 'background-width', 'units', index) === '%') {
29287 w = getIndexedStyle(node, 'background-width', 'pfValue', index) * nodeTW;
29288 } else {
29289 w = getIndexedStyle(node, 'background-width', 'pfValue', index);
29290 }
29291 }
29292
29293 if (getIndexedStyle(node, 'background-height', 'value', index) !== 'auto') {
29294 if (getIndexedStyle(node, 'background-height', 'units', index) === '%') {
29295 h = getIndexedStyle(node, 'background-height', 'pfValue', index) * nodeTH;
29296 } else {
29297 h = getIndexedStyle(node, 'background-height', 'pfValue', index);
29298 }
29299 }
29300
29301 if (w === 0 || h === 0) {
29302 return; // no point in drawing empty image (and chrome is broken in this case)
29303 }
29304
29305 if (fit === 'contain') {
29306 var scale = Math.min(nodeTW / w, nodeTH / h);
29307 w *= scale;
29308 h *= scale;
29309 } else if (fit === 'cover') {
29310 var scale = Math.max(nodeTW / w, nodeTH / h);
29311 w *= scale;
29312 h *= scale;
29313 }
29314
29315 var x = nodeX - nodeTW / 2; // left
29316
29317 var posXUnits = getIndexedStyle(node, 'background-position-x', 'units', index);
29318 var posXPfVal = getIndexedStyle(node, 'background-position-x', 'pfValue', index);
29319
29320 if (posXUnits === '%') {
29321 x += (nodeTW - w) * posXPfVal;
29322 } else {
29323 x += posXPfVal;
29324 }
29325
29326 var offXUnits = getIndexedStyle(node, 'background-offset-x', 'units', index);
29327 var offXPfVal = getIndexedStyle(node, 'background-offset-x', 'pfValue', index);
29328
29329 if (offXUnits === '%') {
29330 x += (nodeTW - w) * offXPfVal;
29331 } else {
29332 x += offXPfVal;
29333 }
29334
29335 var y = nodeY - nodeTH / 2; // top
29336
29337 var posYUnits = getIndexedStyle(node, 'background-position-y', 'units', index);
29338 var posYPfVal = getIndexedStyle(node, 'background-position-y', 'pfValue', index);
29339
29340 if (posYUnits === '%') {
29341 y += (nodeTH - h) * posYPfVal;
29342 } else {
29343 y += posYPfVal;
29344 }
29345
29346 var offYUnits = getIndexedStyle(node, 'background-offset-y', 'units', index);
29347 var offYPfVal = getIndexedStyle(node, 'background-offset-y', 'pfValue', index);
29348
29349 if (offYUnits === '%') {
29350 y += (nodeTH - h) * offYPfVal;
29351 } else {
29352 y += offYPfVal;
29353 }
29354
29355 if (rs.pathCache) {
29356 x -= nodeX;
29357 y -= nodeY;
29358 nodeX = 0;
29359 nodeY = 0;
29360 }
29361
29362 var gAlpha = context.globalAlpha;
29363 context.globalAlpha = imgOpacity;
29364 var smoothingEnabled = r.getImgSmoothing(context);
29365 var isSmoothingSwitched = false;
29366
29367 if (smooth === 'no' && smoothingEnabled) {
29368 r.setImgSmoothing(context, false);
29369 isSmoothingSwitched = true;
29370 } else if (smooth === 'yes' && !smoothingEnabled) {
29371 r.setImgSmoothing(context, true);
29372 isSmoothingSwitched = true;
29373 }
29374
29375 if (repeat === 'no-repeat') {
29376 if (shouldClip) {
29377 context.save();
29378
29379 if (rs.pathCache) {
29380 context.clip(rs.pathCache);
29381 } else {
29382 r.nodeShapes[r.getNodeShape(node)].draw(context, nodeX, nodeY, nodeTW, nodeTH);
29383 context.clip();
29384 }
29385 }
29386
29387 r.safeDrawImage(context, img, 0, 0, imgW, imgH, x, y, w, h);
29388
29389 if (shouldClip) {
29390 context.restore();
29391 }
29392 } else {
29393 var pattern = context.createPattern(img, repeat);
29394 context.fillStyle = pattern;
29395 r.nodeShapes[r.getNodeShape(node)].draw(context, nodeX, nodeY, nodeTW, nodeTH);
29396 context.translate(x, y);
29397 context.fill();
29398 context.translate(-x, -y);
29399 }
29400
29401 context.globalAlpha = gAlpha;
29402
29403 if (isSmoothingSwitched) {
29404 r.setImgSmoothing(context, smoothingEnabled);
29405 }
29406};
29407
29408var CRp$6 = {};
29409
29410CRp$6.eleTextBiggerThanMin = function (ele, scale) {
29411 if (!scale) {
29412 var zoom = ele.cy().zoom();
29413 var pxRatio = this.getPixelRatio();
29414 var lvl = Math.ceil(log2(zoom * pxRatio)); // the effective texture level
29415
29416 scale = Math.pow(2, lvl);
29417 }
29418
29419 var computedSize = ele.pstyle('font-size').pfValue * scale;
29420 var minSize = ele.pstyle('min-zoomed-font-size').pfValue;
29421
29422 if (computedSize < minSize) {
29423 return false;
29424 }
29425
29426 return true;
29427};
29428
29429CRp$6.drawElementText = function (context, ele, shiftToOriginWithBb, force, prefix) {
29430 var useEleOpacity = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : true;
29431 var r = this;
29432
29433 if (force == null) {
29434 if (useEleOpacity && !r.eleTextBiggerThanMin(ele)) {
29435 return;
29436 }
29437 } else if (force === false) {
29438 return;
29439 }
29440
29441 if (ele.isNode()) {
29442 var label = ele.pstyle('label');
29443
29444 if (!label || !label.value) {
29445 return;
29446 }
29447
29448 var justification = r.getLabelJustification(ele);
29449 context.textAlign = justification;
29450 context.textBaseline = 'bottom';
29451 } else {
29452 var badLine = ele.element()._private.rscratch.badLine;
29453
29454 var _label = ele.pstyle('label');
29455
29456 var srcLabel = ele.pstyle('source-label');
29457 var tgtLabel = ele.pstyle('target-label');
29458
29459 if (badLine || (!_label || !_label.value) && (!srcLabel || !srcLabel.value) && (!tgtLabel || !tgtLabel.value)) {
29460 return;
29461 }
29462
29463 context.textAlign = 'center';
29464 context.textBaseline = 'bottom';
29465 }
29466
29467 var applyRotation = !shiftToOriginWithBb;
29468 var bb;
29469
29470 if (shiftToOriginWithBb) {
29471 bb = shiftToOriginWithBb;
29472 context.translate(-bb.x1, -bb.y1);
29473 }
29474
29475 if (prefix == null) {
29476 r.drawText(context, ele, null, applyRotation, useEleOpacity);
29477
29478 if (ele.isEdge()) {
29479 r.drawText(context, ele, 'source', applyRotation, useEleOpacity);
29480 r.drawText(context, ele, 'target', applyRotation, useEleOpacity);
29481 }
29482 } else {
29483 r.drawText(context, ele, prefix, applyRotation, useEleOpacity);
29484 }
29485
29486 if (shiftToOriginWithBb) {
29487 context.translate(bb.x1, bb.y1);
29488 }
29489};
29490
29491CRp$6.getFontCache = function (context) {
29492 var cache;
29493 this.fontCaches = this.fontCaches || [];
29494
29495 for (var i = 0; i < this.fontCaches.length; i++) {
29496 cache = this.fontCaches[i];
29497
29498 if (cache.context === context) {
29499 return cache;
29500 }
29501 }
29502
29503 cache = {
29504 context: context
29505 };
29506 this.fontCaches.push(cache);
29507 return cache;
29508}; // set up canvas context with font
29509// returns transformed text string
29510
29511
29512CRp$6.setupTextStyle = function (context, ele) {
29513 var useEleOpacity = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
29514 // Font style
29515 var labelStyle = ele.pstyle('font-style').strValue;
29516 var labelSize = ele.pstyle('font-size').pfValue + 'px';
29517 var labelFamily = ele.pstyle('font-family').strValue;
29518 var labelWeight = ele.pstyle('font-weight').strValue;
29519 var opacity = useEleOpacity ? ele.effectiveOpacity() * ele.pstyle('text-opacity').value : 1;
29520 var outlineOpacity = ele.pstyle('text-outline-opacity').value * opacity;
29521 var color = ele.pstyle('color').value;
29522 var outlineColor = ele.pstyle('text-outline-color').value;
29523 context.font = labelStyle + ' ' + labelWeight + ' ' + labelSize + ' ' + labelFamily;
29524 context.lineJoin = 'round'; // so text outlines aren't jagged
29525
29526 this.colorFillStyle(context, color[0], color[1], color[2], opacity);
29527 this.colorStrokeStyle(context, outlineColor[0], outlineColor[1], outlineColor[2], outlineOpacity);
29528}; // TODO ensure re-used
29529
29530
29531function roundRect(ctx, x, y, width, height) {
29532 var radius = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : 5;
29533 ctx.beginPath();
29534 ctx.moveTo(x + radius, y);
29535 ctx.lineTo(x + width - radius, y);
29536 ctx.quadraticCurveTo(x + width, y, x + width, y + radius);
29537 ctx.lineTo(x + width, y + height - radius);
29538 ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height);
29539 ctx.lineTo(x + radius, y + height);
29540 ctx.quadraticCurveTo(x, y + height, x, y + height - radius);
29541 ctx.lineTo(x, y + radius);
29542 ctx.quadraticCurveTo(x, y, x + radius, y);
29543 ctx.closePath();
29544 ctx.fill();
29545}
29546
29547CRp$6.getTextAngle = function (ele, prefix) {
29548 var theta;
29549 var _p = ele._private;
29550 var rscratch = _p.rscratch;
29551 var pdash = prefix ? prefix + '-' : '';
29552 var rotation = ele.pstyle(pdash + 'text-rotation');
29553 var textAngle = getPrefixedProperty(rscratch, 'labelAngle', prefix);
29554
29555 if (rotation.strValue === 'autorotate') {
29556 theta = ele.isEdge() ? textAngle : 0;
29557 } else if (rotation.strValue === 'none') {
29558 theta = 0;
29559 } else {
29560 theta = rotation.pfValue;
29561 }
29562
29563 return theta;
29564};
29565
29566CRp$6.drawText = function (context, ele, prefix) {
29567 var applyRotation = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true;
29568 var useEleOpacity = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : true;
29569 var _p = ele._private;
29570 var rscratch = _p.rscratch;
29571 var parentOpacity = useEleOpacity ? ele.effectiveOpacity() : 1;
29572
29573 if (useEleOpacity && (parentOpacity === 0 || ele.pstyle('text-opacity').value === 0)) {
29574 return;
29575 } // use 'main' as an alias for the main label (i.e. null prefix)
29576
29577
29578 if (prefix === 'main') {
29579 prefix = null;
29580 }
29581
29582 var textX = getPrefixedProperty(rscratch, 'labelX', prefix);
29583 var textY = getPrefixedProperty(rscratch, 'labelY', prefix);
29584 var orgTextX, orgTextY; // used for rotation
29585
29586 var text = this.getLabelText(ele, prefix);
29587
29588 if (text != null && text !== '' && !isNaN(textX) && !isNaN(textY)) {
29589 this.setupTextStyle(context, ele, useEleOpacity);
29590 var pdash = prefix ? prefix + '-' : '';
29591 var textW = getPrefixedProperty(rscratch, 'labelWidth', prefix);
29592 var textH = getPrefixedProperty(rscratch, 'labelHeight', prefix);
29593 var marginX = ele.pstyle(pdash + 'text-margin-x').pfValue;
29594 var marginY = ele.pstyle(pdash + 'text-margin-y').pfValue;
29595 var isEdge = ele.isEdge();
29596 var halign = ele.pstyle('text-halign').value;
29597 var valign = ele.pstyle('text-valign').value;
29598
29599 if (isEdge) {
29600 halign = 'center';
29601 valign = 'center';
29602 }
29603
29604 textX += marginX;
29605 textY += marginY;
29606 var theta;
29607
29608 if (!applyRotation) {
29609 theta = 0;
29610 } else {
29611 theta = this.getTextAngle(ele, prefix);
29612 }
29613
29614 if (theta !== 0) {
29615 orgTextX = textX;
29616 orgTextY = textY;
29617 context.translate(orgTextX, orgTextY);
29618 context.rotate(theta);
29619 textX = 0;
29620 textY = 0;
29621 }
29622
29623 switch (valign) {
29624 case 'top':
29625 break;
29626
29627 case 'center':
29628 textY += textH / 2;
29629 break;
29630
29631 case 'bottom':
29632 textY += textH;
29633 break;
29634 }
29635
29636 var backgroundOpacity = ele.pstyle('text-background-opacity').value;
29637 var borderOpacity = ele.pstyle('text-border-opacity').value;
29638 var textBorderWidth = ele.pstyle('text-border-width').pfValue;
29639 var backgroundPadding = ele.pstyle('text-background-padding').pfValue;
29640
29641 if (backgroundOpacity > 0 || textBorderWidth > 0 && borderOpacity > 0) {
29642 var bgX = textX - backgroundPadding;
29643
29644 switch (halign) {
29645 case 'left':
29646 bgX -= textW;
29647 break;
29648
29649 case 'center':
29650 bgX -= textW / 2;
29651 break;
29652 }
29653
29654 var bgY = textY - textH - backgroundPadding;
29655 var bgW = textW + 2 * backgroundPadding;
29656 var bgH = textH + 2 * backgroundPadding;
29657
29658 if (backgroundOpacity > 0) {
29659 var textFill = context.fillStyle;
29660 var textBackgroundColor = ele.pstyle('text-background-color').value;
29661 context.fillStyle = 'rgba(' + textBackgroundColor[0] + ',' + textBackgroundColor[1] + ',' + textBackgroundColor[2] + ',' + backgroundOpacity * parentOpacity + ')';
29662 var styleShape = ele.pstyle('text-background-shape').strValue;
29663
29664 if (styleShape.indexOf('round') === 0) {
29665 roundRect(context, bgX, bgY, bgW, bgH, 2);
29666 } else {
29667 context.fillRect(bgX, bgY, bgW, bgH);
29668 }
29669
29670 context.fillStyle = textFill;
29671 }
29672
29673 if (textBorderWidth > 0 && borderOpacity > 0) {
29674 var textStroke = context.strokeStyle;
29675 var textLineWidth = context.lineWidth;
29676 var textBorderColor = ele.pstyle('text-border-color').value;
29677 var textBorderStyle = ele.pstyle('text-border-style').value;
29678 context.strokeStyle = 'rgba(' + textBorderColor[0] + ',' + textBorderColor[1] + ',' + textBorderColor[2] + ',' + borderOpacity * parentOpacity + ')';
29679 context.lineWidth = textBorderWidth;
29680
29681 if (context.setLineDash) {
29682 // for very outofdate browsers
29683 switch (textBorderStyle) {
29684 case 'dotted':
29685 context.setLineDash([1, 1]);
29686 break;
29687
29688 case 'dashed':
29689 context.setLineDash([4, 2]);
29690 break;
29691
29692 case 'double':
29693 context.lineWidth = textBorderWidth / 4; // 50% reserved for white between the two borders
29694
29695 context.setLineDash([]);
29696 break;
29697
29698 case 'solid':
29699 context.setLineDash([]);
29700 break;
29701 }
29702 }
29703
29704 context.strokeRect(bgX, bgY, bgW, bgH);
29705
29706 if (textBorderStyle === 'double') {
29707 var whiteWidth = textBorderWidth / 2;
29708 context.strokeRect(bgX + whiteWidth, bgY + whiteWidth, bgW - whiteWidth * 2, bgH - whiteWidth * 2);
29709 }
29710
29711 if (context.setLineDash) {
29712 // for very outofdate browsers
29713 context.setLineDash([]);
29714 }
29715
29716 context.lineWidth = textLineWidth;
29717 context.strokeStyle = textStroke;
29718 }
29719 }
29720
29721 var lineWidth = 2 * ele.pstyle('text-outline-width').pfValue; // *2 b/c the stroke is drawn centred on the middle
29722
29723 if (lineWidth > 0) {
29724 context.lineWidth = lineWidth;
29725 }
29726
29727 if (ele.pstyle('text-wrap').value === 'wrap') {
29728 var lines = getPrefixedProperty(rscratch, 'labelWrapCachedLines', prefix);
29729 var lineHeight = getPrefixedProperty(rscratch, 'labelLineHeight', prefix);
29730 var halfTextW = textW / 2;
29731 var justification = this.getLabelJustification(ele);
29732
29733 if (justification === 'auto') ; else if (halign === 'left') {
29734 // auto justification : right
29735 if (justification === 'left') {
29736 textX += -textW;
29737 } else if (justification === 'center') {
29738 textX += -halfTextW;
29739 } // else same as auto
29740
29741 } else if (halign === 'center') {
29742 // auto justfication : center
29743 if (justification === 'left') {
29744 textX += -halfTextW;
29745 } else if (justification === 'right') {
29746 textX += halfTextW;
29747 } // else same as auto
29748
29749 } else if (halign === 'right') {
29750 // auto justification : left
29751 if (justification === 'center') {
29752 textX += halfTextW;
29753 } else if (justification === 'right') {
29754 textX += textW;
29755 } // else same as auto
29756
29757 }
29758
29759 switch (valign) {
29760 case 'top':
29761 textY -= (lines.length - 1) * lineHeight;
29762 break;
29763
29764 case 'center':
29765 case 'bottom':
29766 textY -= (lines.length - 1) * lineHeight;
29767 break;
29768 }
29769
29770 for (var l = 0; l < lines.length; l++) {
29771 if (lineWidth > 0) {
29772 context.strokeText(lines[l], textX, textY);
29773 }
29774
29775 context.fillText(lines[l], textX, textY);
29776 textY += lineHeight;
29777 }
29778 } else {
29779 if (lineWidth > 0) {
29780 context.strokeText(text, textX, textY);
29781 }
29782
29783 context.fillText(text, textX, textY);
29784 }
29785
29786 if (theta !== 0) {
29787 context.rotate(-theta);
29788 context.translate(-orgTextX, -orgTextY);
29789 }
29790 }
29791};
29792
29793/* global Path2D */
29794var CRp$5 = {};
29795
29796CRp$5.drawNode = function (context, node, shiftToOriginWithBb) {
29797 var drawLabel = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true;
29798 var shouldDrawOverlay = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : true;
29799 var shouldDrawOpacity = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : true;
29800 var r = this;
29801 var nodeWidth, nodeHeight;
29802 var _p = node._private;
29803 var rs = _p.rscratch;
29804 var pos = node.position();
29805
29806 if (!number$1(pos.x) || !number$1(pos.y)) {
29807 return; // can't draw node with undefined position
29808 }
29809
29810 if (shouldDrawOpacity && !node.visible()) {
29811 return;
29812 }
29813
29814 var eleOpacity = shouldDrawOpacity ? node.effectiveOpacity() : 1;
29815 var usePaths = r.usePaths();
29816 var path;
29817 var pathCacheHit = false;
29818 var padding = node.padding();
29819 nodeWidth = node.width() + 2 * padding;
29820 nodeHeight = node.height() + 2 * padding; //
29821 // setup shift
29822
29823 var bb;
29824
29825 if (shiftToOriginWithBb) {
29826 bb = shiftToOriginWithBb;
29827 context.translate(-bb.x1, -bb.y1);
29828 } //
29829 // load bg image
29830
29831
29832 var bgImgProp = node.pstyle('background-image');
29833 var urls = bgImgProp.value;
29834 var urlDefined = new Array(urls.length);
29835 var image = new Array(urls.length);
29836 var numImages = 0;
29837
29838 for (var i = 0; i < urls.length; i++) {
29839 var url = urls[i];
29840 var defd = urlDefined[i] = url != null && url !== 'none';
29841
29842 if (defd) {
29843 var bgImgCrossOrigin = node.cy().style().getIndexedStyle(node, 'background-image-crossorigin', 'value', i);
29844 numImages++; // get image, and if not loaded then ask to redraw when later loaded
29845
29846 image[i] = r.getCachedImage(url, bgImgCrossOrigin, function () {
29847 _p.backgroundTimestamp = Date.now();
29848 node.emitAndNotify('background');
29849 });
29850 }
29851 } //
29852 // setup styles
29853
29854
29855 var darkness = node.pstyle('background-blacken').value;
29856 var borderWidth = node.pstyle('border-width').pfValue;
29857 var bgOpacity = node.pstyle('background-opacity').value * eleOpacity;
29858 var borderColor = node.pstyle('border-color').value;
29859 var borderStyle = node.pstyle('border-style').value;
29860 var borderOpacity = node.pstyle('border-opacity').value * eleOpacity;
29861 context.lineJoin = 'miter'; // so borders are square with the node shape
29862
29863 var setupShapeColor = function setupShapeColor() {
29864 var bgOpy = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : bgOpacity;
29865 r.eleFillStyle(context, node, bgOpy);
29866 };
29867
29868 var setupBorderColor = function setupBorderColor() {
29869 var bdrOpy = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : borderOpacity;
29870 r.colorStrokeStyle(context, borderColor[0], borderColor[1], borderColor[2], bdrOpy);
29871 }; //
29872 // setup shape
29873
29874
29875 var styleShape = node.pstyle('shape').strValue;
29876 var shapePts = node.pstyle('shape-polygon-points').pfValue;
29877
29878 if (usePaths) {
29879 context.translate(pos.x, pos.y);
29880 var pathCache = r.nodePathCache = r.nodePathCache || [];
29881 var key = hashStrings(styleShape === 'polygon' ? styleShape + ',' + shapePts.join(',') : styleShape, '' + nodeHeight, '' + nodeWidth);
29882 var cachedPath = pathCache[key];
29883
29884 if (cachedPath != null) {
29885 path = cachedPath;
29886 pathCacheHit = true;
29887 rs.pathCache = path;
29888 } else {
29889 path = new Path2D();
29890 pathCache[key] = rs.pathCache = path;
29891 }
29892 }
29893
29894 var drawShape = function drawShape() {
29895 if (!pathCacheHit) {
29896 var npos = pos;
29897
29898 if (usePaths) {
29899 npos = {
29900 x: 0,
29901 y: 0
29902 };
29903 }
29904
29905 r.nodeShapes[r.getNodeShape(node)].draw(path || context, npos.x, npos.y, nodeWidth, nodeHeight);
29906 }
29907
29908 if (usePaths) {
29909 context.fill(path);
29910 } else {
29911 context.fill();
29912 }
29913 };
29914
29915 var drawImages = function drawImages() {
29916 var nodeOpacity = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : eleOpacity;
29917 var inside = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
29918 var prevBging = _p.backgrounding;
29919 var totalCompleted = 0;
29920
29921 for (var _i = 0; _i < image.length; _i++) {
29922 var bgContainment = node.cy().style().getIndexedStyle(node, 'background-image-containment', 'value', _i);
29923
29924 if (inside && bgContainment === 'over' || !inside && bgContainment === 'inside') {
29925 totalCompleted++;
29926 continue;
29927 }
29928
29929 if (urlDefined[_i] && image[_i].complete && !image[_i].error) {
29930 totalCompleted++;
29931 r.drawInscribedImage(context, image[_i], node, _i, nodeOpacity);
29932 }
29933 }
29934
29935 _p.backgrounding = !(totalCompleted === numImages);
29936
29937 if (prevBging !== _p.backgrounding) {
29938 // update style b/c :backgrounding state changed
29939 node.updateStyle(false);
29940 }
29941 };
29942
29943 var drawPie = function drawPie() {
29944 var redrawShape = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
29945 var pieOpacity = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : eleOpacity;
29946
29947 if (r.hasPie(node)) {
29948 r.drawPie(context, node, pieOpacity); // redraw/restore path if steps after pie need it
29949
29950 if (redrawShape) {
29951 if (!usePaths) {
29952 r.nodeShapes[r.getNodeShape(node)].draw(context, pos.x, pos.y, nodeWidth, nodeHeight);
29953 }
29954 }
29955 }
29956 };
29957
29958 var darken = function darken() {
29959 var darkenOpacity = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : eleOpacity;
29960 var opacity = (darkness > 0 ? darkness : -darkness) * darkenOpacity;
29961 var c = darkness > 0 ? 0 : 255;
29962
29963 if (darkness !== 0) {
29964 r.colorFillStyle(context, c, c, c, opacity);
29965
29966 if (usePaths) {
29967 context.fill(path);
29968 } else {
29969 context.fill();
29970 }
29971 }
29972 };
29973
29974 var drawBorder = function drawBorder() {
29975 if (borderWidth > 0) {
29976 context.lineWidth = borderWidth;
29977 context.lineCap = 'butt';
29978
29979 if (context.setLineDash) {
29980 // for very outofdate browsers
29981 switch (borderStyle) {
29982 case 'dotted':
29983 context.setLineDash([1, 1]);
29984 break;
29985
29986 case 'dashed':
29987 context.setLineDash([4, 2]);
29988 break;
29989
29990 case 'solid':
29991 case 'double':
29992 context.setLineDash([]);
29993 break;
29994 }
29995 }
29996
29997 if (usePaths) {
29998 context.stroke(path);
29999 } else {
30000 context.stroke();
30001 }
30002
30003 if (borderStyle === 'double') {
30004 context.lineWidth = borderWidth / 3;
30005 var gco = context.globalCompositeOperation;
30006 context.globalCompositeOperation = 'destination-out';
30007
30008 if (usePaths) {
30009 context.stroke(path);
30010 } else {
30011 context.stroke();
30012 }
30013
30014 context.globalCompositeOperation = gco;
30015 } // reset in case we changed the border style
30016
30017
30018 if (context.setLineDash) {
30019 // for very outofdate browsers
30020 context.setLineDash([]);
30021 }
30022 }
30023 };
30024
30025 var drawOverlay = function drawOverlay() {
30026 if (shouldDrawOverlay) {
30027 r.drawNodeOverlay(context, node, pos, nodeWidth, nodeHeight);
30028 }
30029 };
30030
30031 var drawUnderlay = function drawUnderlay() {
30032 if (shouldDrawOverlay) {
30033 r.drawNodeUnderlay(context, node, pos, nodeWidth, nodeHeight);
30034 }
30035 };
30036
30037 var drawText = function drawText() {
30038 r.drawElementText(context, node, null, drawLabel);
30039 };
30040
30041 var ghost = node.pstyle('ghost').value === 'yes';
30042
30043 if (ghost) {
30044 var gx = node.pstyle('ghost-offset-x').pfValue;
30045 var gy = node.pstyle('ghost-offset-y').pfValue;
30046 var ghostOpacity = node.pstyle('ghost-opacity').value;
30047 var effGhostOpacity = ghostOpacity * eleOpacity;
30048 context.translate(gx, gy);
30049 setupShapeColor(ghostOpacity * bgOpacity);
30050 drawShape();
30051 drawImages(effGhostOpacity, true);
30052 setupBorderColor(ghostOpacity * borderOpacity);
30053 drawBorder();
30054 drawPie(darkness !== 0 || borderWidth !== 0);
30055 drawImages(effGhostOpacity, false);
30056 darken(effGhostOpacity);
30057 context.translate(-gx, -gy);
30058 }
30059
30060 if (usePaths) {
30061 context.translate(-pos.x, -pos.y);
30062 }
30063
30064 drawUnderlay();
30065
30066 if (usePaths) {
30067 context.translate(pos.x, pos.y);
30068 }
30069
30070 setupShapeColor();
30071 drawShape();
30072 drawImages(eleOpacity, true);
30073 setupBorderColor();
30074 drawBorder();
30075 drawPie(darkness !== 0 || borderWidth !== 0);
30076 drawImages(eleOpacity, false);
30077 darken();
30078
30079 if (usePaths) {
30080 context.translate(-pos.x, -pos.y);
30081 }
30082
30083 drawText();
30084 drawOverlay(); //
30085 // clean up shift
30086
30087 if (shiftToOriginWithBb) {
30088 context.translate(bb.x1, bb.y1);
30089 }
30090};
30091
30092var drawNodeOverlayUnderlay = function drawNodeOverlayUnderlay(overlayOrUnderlay) {
30093 if (!['overlay', 'underlay'].includes(overlayOrUnderlay)) {
30094 throw new Error('Invalid state');
30095 }
30096
30097 return function (context, node, pos, nodeWidth, nodeHeight) {
30098 var r = this;
30099
30100 if (!node.visible()) {
30101 return;
30102 }
30103
30104 var padding = node.pstyle("".concat(overlayOrUnderlay, "-padding")).pfValue;
30105 var opacity = node.pstyle("".concat(overlayOrUnderlay, "-opacity")).value;
30106 var color = node.pstyle("".concat(overlayOrUnderlay, "-color")).value;
30107 var shape = node.pstyle("".concat(overlayOrUnderlay, "-shape")).value;
30108
30109 if (opacity > 0) {
30110 pos = pos || node.position();
30111
30112 if (nodeWidth == null || nodeHeight == null) {
30113 var _padding = node.padding();
30114
30115 nodeWidth = node.width() + 2 * _padding;
30116 nodeHeight = node.height() + 2 * _padding;
30117 }
30118
30119 r.colorFillStyle(context, color[0], color[1], color[2], opacity);
30120 r.nodeShapes[shape].draw(context, pos.x, pos.y, nodeWidth + padding * 2, nodeHeight + padding * 2);
30121 context.fill();
30122 }
30123 };
30124};
30125
30126CRp$5.drawNodeOverlay = drawNodeOverlayUnderlay('overlay');
30127CRp$5.drawNodeUnderlay = drawNodeOverlayUnderlay('underlay'); // does the node have at least one pie piece?
30128
30129CRp$5.hasPie = function (node) {
30130 node = node[0]; // ensure ele ref
30131
30132 return node._private.hasPie;
30133};
30134
30135CRp$5.drawPie = function (context, node, nodeOpacity, pos) {
30136 node = node[0]; // ensure ele ref
30137
30138 pos = pos || node.position();
30139 var cyStyle = node.cy().style();
30140 var pieSize = node.pstyle('pie-size');
30141 var x = pos.x;
30142 var y = pos.y;
30143 var nodeW = node.width();
30144 var nodeH = node.height();
30145 var radius = Math.min(nodeW, nodeH) / 2; // must fit in node
30146
30147 var lastPercent = 0; // what % to continue drawing pie slices from on [0, 1]
30148
30149 var usePaths = this.usePaths();
30150
30151 if (usePaths) {
30152 x = 0;
30153 y = 0;
30154 }
30155
30156 if (pieSize.units === '%') {
30157 radius = radius * pieSize.pfValue;
30158 } else if (pieSize.pfValue !== undefined) {
30159 radius = pieSize.pfValue / 2;
30160 }
30161
30162 for (var i = 1; i <= cyStyle.pieBackgroundN; i++) {
30163 // 1..N
30164 var size = node.pstyle('pie-' + i + '-background-size').value;
30165 var color = node.pstyle('pie-' + i + '-background-color').value;
30166 var opacity = node.pstyle('pie-' + i + '-background-opacity').value * nodeOpacity;
30167 var percent = size / 100; // map integer range [0, 100] to [0, 1]
30168 // percent can't push beyond 1
30169
30170 if (percent + lastPercent > 1) {
30171 percent = 1 - lastPercent;
30172 }
30173
30174 var angleStart = 1.5 * Math.PI + 2 * Math.PI * lastPercent; // start at 12 o'clock and go clockwise
30175
30176 var angleDelta = 2 * Math.PI * percent;
30177 var angleEnd = angleStart + angleDelta; // ignore if
30178 // - zero size
30179 // - we're already beyond the full circle
30180 // - adding the current slice would go beyond the full circle
30181
30182 if (size === 0 || lastPercent >= 1 || lastPercent + percent > 1) {
30183 continue;
30184 }
30185
30186 context.beginPath();
30187 context.moveTo(x, y);
30188 context.arc(x, y, radius, angleStart, angleEnd);
30189 context.closePath();
30190 this.colorFillStyle(context, color[0], color[1], color[2], opacity);
30191 context.fill();
30192 lastPercent += percent;
30193 }
30194};
30195
30196var CRp$4 = {};
30197var motionBlurDelay = 100; // var isFirefox = typeof InstallTrigger !== 'undefined';
30198
30199CRp$4.getPixelRatio = function () {
30200 var context = this.data.contexts[0];
30201
30202 if (this.forcedPixelRatio != null) {
30203 return this.forcedPixelRatio;
30204 }
30205
30206 var backingStore = context.backingStorePixelRatio || context.webkitBackingStorePixelRatio || context.mozBackingStorePixelRatio || context.msBackingStorePixelRatio || context.oBackingStorePixelRatio || context.backingStorePixelRatio || 1;
30207 return (window.devicePixelRatio || 1) / backingStore; // eslint-disable-line no-undef
30208};
30209
30210CRp$4.paintCache = function (context) {
30211 var caches = this.paintCaches = this.paintCaches || [];
30212 var needToCreateCache = true;
30213 var cache;
30214
30215 for (var i = 0; i < caches.length; i++) {
30216 cache = caches[i];
30217
30218 if (cache.context === context) {
30219 needToCreateCache = false;
30220 break;
30221 }
30222 }
30223
30224 if (needToCreateCache) {
30225 cache = {
30226 context: context
30227 };
30228 caches.push(cache);
30229 }
30230
30231 return cache;
30232};
30233
30234CRp$4.createGradientStyleFor = function (context, shapeStyleName, ele, fill, opacity) {
30235 var gradientStyle;
30236 var usePaths = this.usePaths();
30237 var colors = ele.pstyle(shapeStyleName + '-gradient-stop-colors').value,
30238 positions = ele.pstyle(shapeStyleName + '-gradient-stop-positions').pfValue;
30239
30240 if (fill === 'radial-gradient') {
30241 if (ele.isEdge()) {
30242 var start = ele.sourceEndpoint(),
30243 end = ele.targetEndpoint(),
30244 mid = ele.midpoint();
30245 var d1 = dist(start, mid);
30246 var d2 = dist(end, mid);
30247 gradientStyle = context.createRadialGradient(mid.x, mid.y, 0, mid.x, mid.y, Math.max(d1, d2));
30248 } else {
30249 var pos = usePaths ? {
30250 x: 0,
30251 y: 0
30252 } : ele.position(),
30253 width = ele.paddedWidth(),
30254 height = ele.paddedHeight();
30255 gradientStyle = context.createRadialGradient(pos.x, pos.y, 0, pos.x, pos.y, Math.max(width, height));
30256 }
30257 } else {
30258 if (ele.isEdge()) {
30259 var _start = ele.sourceEndpoint(),
30260 _end = ele.targetEndpoint();
30261
30262 gradientStyle = context.createLinearGradient(_start.x, _start.y, _end.x, _end.y);
30263 } else {
30264 var _pos = usePaths ? {
30265 x: 0,
30266 y: 0
30267 } : ele.position(),
30268 _width = ele.paddedWidth(),
30269 _height = ele.paddedHeight(),
30270 halfWidth = _width / 2,
30271 halfHeight = _height / 2;
30272
30273 var direction = ele.pstyle('background-gradient-direction').value;
30274
30275 switch (direction) {
30276 case 'to-bottom':
30277 gradientStyle = context.createLinearGradient(_pos.x, _pos.y - halfHeight, _pos.x, _pos.y + halfHeight);
30278 break;
30279
30280 case 'to-top':
30281 gradientStyle = context.createLinearGradient(_pos.x, _pos.y + halfHeight, _pos.x, _pos.y - halfHeight);
30282 break;
30283
30284 case 'to-left':
30285 gradientStyle = context.createLinearGradient(_pos.x + halfWidth, _pos.y, _pos.x - halfWidth, _pos.y);
30286 break;
30287
30288 case 'to-right':
30289 gradientStyle = context.createLinearGradient(_pos.x - halfWidth, _pos.y, _pos.x + halfWidth, _pos.y);
30290 break;
30291
30292 case 'to-bottom-right':
30293 case 'to-right-bottom':
30294 gradientStyle = context.createLinearGradient(_pos.x - halfWidth, _pos.y - halfHeight, _pos.x + halfWidth, _pos.y + halfHeight);
30295 break;
30296
30297 case 'to-top-right':
30298 case 'to-right-top':
30299 gradientStyle = context.createLinearGradient(_pos.x - halfWidth, _pos.y + halfHeight, _pos.x + halfWidth, _pos.y - halfHeight);
30300 break;
30301
30302 case 'to-bottom-left':
30303 case 'to-left-bottom':
30304 gradientStyle = context.createLinearGradient(_pos.x + halfWidth, _pos.y - halfHeight, _pos.x - halfWidth, _pos.y + halfHeight);
30305 break;
30306
30307 case 'to-top-left':
30308 case 'to-left-top':
30309 gradientStyle = context.createLinearGradient(_pos.x + halfWidth, _pos.y + halfHeight, _pos.x - halfWidth, _pos.y - halfHeight);
30310 break;
30311 }
30312 }
30313 }
30314
30315 if (!gradientStyle) return null; // invalid gradient style
30316
30317 var hasPositions = positions.length === colors.length;
30318 var length = colors.length;
30319
30320 for (var i = 0; i < length; i++) {
30321 gradientStyle.addColorStop(hasPositions ? positions[i] : i / (length - 1), 'rgba(' + colors[i][0] + ',' + colors[i][1] + ',' + colors[i][2] + ',' + opacity + ')');
30322 }
30323
30324 return gradientStyle;
30325};
30326
30327CRp$4.gradientFillStyle = function (context, ele, fill, opacity) {
30328 var gradientStyle = this.createGradientStyleFor(context, 'background', ele, fill, opacity);
30329 if (!gradientStyle) return null; // error
30330
30331 context.fillStyle = gradientStyle;
30332};
30333
30334CRp$4.colorFillStyle = function (context, r, g, b, a) {
30335 context.fillStyle = 'rgba(' + r + ',' + g + ',' + b + ',' + a + ')'; // turn off for now, seems context does its own caching
30336 // var cache = this.paintCache(context);
30337 // var fillStyle = 'rgba(' + r + ',' + g + ',' + b + ',' + a + ')';
30338 // if( cache.fillStyle !== fillStyle ){
30339 // context.fillStyle = cache.fillStyle = fillStyle;
30340 // }
30341};
30342
30343CRp$4.eleFillStyle = function (context, ele, opacity) {
30344 var backgroundFill = ele.pstyle('background-fill').value;
30345
30346 if (backgroundFill === 'linear-gradient' || backgroundFill === 'radial-gradient') {
30347 this.gradientFillStyle(context, ele, backgroundFill, opacity);
30348 } else {
30349 var backgroundColor = ele.pstyle('background-color').value;
30350 this.colorFillStyle(context, backgroundColor[0], backgroundColor[1], backgroundColor[2], opacity);
30351 }
30352};
30353
30354CRp$4.gradientStrokeStyle = function (context, ele, fill, opacity) {
30355 var gradientStyle = this.createGradientStyleFor(context, 'line', ele, fill, opacity);
30356 if (!gradientStyle) return null; // error
30357
30358 context.strokeStyle = gradientStyle;
30359};
30360
30361CRp$4.colorStrokeStyle = function (context, r, g, b, a) {
30362 context.strokeStyle = 'rgba(' + r + ',' + g + ',' + b + ',' + a + ')'; // turn off for now, seems context does its own caching
30363 // var cache = this.paintCache(context);
30364 // var strokeStyle = 'rgba(' + r + ',' + g + ',' + b + ',' + a + ')';
30365 // if( cache.strokeStyle !== strokeStyle ){
30366 // context.strokeStyle = cache.strokeStyle = strokeStyle;
30367 // }
30368};
30369
30370CRp$4.eleStrokeStyle = function (context, ele, opacity) {
30371 var lineFill = ele.pstyle('line-fill').value;
30372
30373 if (lineFill === 'linear-gradient' || lineFill === 'radial-gradient') {
30374 this.gradientStrokeStyle(context, ele, lineFill, opacity);
30375 } else {
30376 var lineColor = ele.pstyle('line-color').value;
30377 this.colorStrokeStyle(context, lineColor[0], lineColor[1], lineColor[2], opacity);
30378 }
30379}; // Resize canvas
30380
30381
30382CRp$4.matchCanvasSize = function (container) {
30383 var r = this;
30384 var data = r.data;
30385 var bb = r.findContainerClientCoords();
30386 var width = bb[2];
30387 var height = bb[3];
30388 var pixelRatio = r.getPixelRatio();
30389 var mbPxRatio = r.motionBlurPxRatio;
30390
30391 if (container === r.data.bufferCanvases[r.MOTIONBLUR_BUFFER_NODE] || container === r.data.bufferCanvases[r.MOTIONBLUR_BUFFER_DRAG]) {
30392 pixelRatio = mbPxRatio;
30393 }
30394
30395 var canvasWidth = width * pixelRatio;
30396 var canvasHeight = height * pixelRatio;
30397 var canvas;
30398
30399 if (canvasWidth === r.canvasWidth && canvasHeight === r.canvasHeight) {
30400 return; // save cycles if same
30401 }
30402
30403 r.fontCaches = null; // resizing resets the style
30404
30405 var canvasContainer = data.canvasContainer;
30406 canvasContainer.style.width = width + 'px';
30407 canvasContainer.style.height = height + 'px';
30408
30409 for (var i = 0; i < r.CANVAS_LAYERS; i++) {
30410 canvas = data.canvases[i];
30411 canvas.width = canvasWidth;
30412 canvas.height = canvasHeight;
30413 canvas.style.width = width + 'px';
30414 canvas.style.height = height + 'px';
30415 }
30416
30417 for (var i = 0; i < r.BUFFER_COUNT; i++) {
30418 canvas = data.bufferCanvases[i];
30419 canvas.width = canvasWidth;
30420 canvas.height = canvasHeight;
30421 canvas.style.width = width + 'px';
30422 canvas.style.height = height + 'px';
30423 }
30424
30425 r.textureMult = 1;
30426
30427 if (pixelRatio <= 1) {
30428 canvas = data.bufferCanvases[r.TEXTURE_BUFFER];
30429 r.textureMult = 2;
30430 canvas.width = canvasWidth * r.textureMult;
30431 canvas.height = canvasHeight * r.textureMult;
30432 }
30433
30434 r.canvasWidth = canvasWidth;
30435 r.canvasHeight = canvasHeight;
30436};
30437
30438CRp$4.renderTo = function (cxt, zoom, pan, pxRatio) {
30439 this.render({
30440 forcedContext: cxt,
30441 forcedZoom: zoom,
30442 forcedPan: pan,
30443 drawAllLayers: true,
30444 forcedPxRatio: pxRatio
30445 });
30446};
30447
30448CRp$4.render = function (options) {
30449 options = options || staticEmptyObject();
30450 var forcedContext = options.forcedContext;
30451 var drawAllLayers = options.drawAllLayers;
30452 var drawOnlyNodeLayer = options.drawOnlyNodeLayer;
30453 var forcedZoom = options.forcedZoom;
30454 var forcedPan = options.forcedPan;
30455 var r = this;
30456 var pixelRatio = options.forcedPxRatio === undefined ? this.getPixelRatio() : options.forcedPxRatio;
30457 var cy = r.cy;
30458 var data = r.data;
30459 var needDraw = data.canvasNeedsRedraw;
30460 var textureDraw = r.textureOnViewport && !forcedContext && (r.pinching || r.hoverData.dragging || r.swipePanning || r.data.wheelZooming);
30461 var motionBlur = options.motionBlur !== undefined ? options.motionBlur : r.motionBlur;
30462 var mbPxRatio = r.motionBlurPxRatio;
30463 var hasCompoundNodes = cy.hasCompoundNodes();
30464 var inNodeDragGesture = r.hoverData.draggingEles;
30465 var inBoxSelection = r.hoverData.selecting || r.touchData.selecting ? true : false;
30466 motionBlur = motionBlur && !forcedContext && r.motionBlurEnabled && !inBoxSelection;
30467 var motionBlurFadeEffect = motionBlur;
30468
30469 if (!forcedContext) {
30470 if (r.prevPxRatio !== pixelRatio) {
30471 r.invalidateContainerClientCoordsCache();
30472 r.matchCanvasSize(r.container);
30473 r.redrawHint('eles', true);
30474 r.redrawHint('drag', true);
30475 }
30476
30477 r.prevPxRatio = pixelRatio;
30478 }
30479
30480 if (!forcedContext && r.motionBlurTimeout) {
30481 clearTimeout(r.motionBlurTimeout);
30482 }
30483
30484 if (motionBlur) {
30485 if (r.mbFrames == null) {
30486 r.mbFrames = 0;
30487 }
30488
30489 r.mbFrames++;
30490
30491 if (r.mbFrames < 3) {
30492 // need several frames before even high quality motionblur
30493 motionBlurFadeEffect = false;
30494 } // go to lower quality blurry frames when several m/b frames have been rendered (avoids flashing)
30495
30496
30497 if (r.mbFrames > r.minMbLowQualFrames) {
30498 //r.fullQualityMb = false;
30499 r.motionBlurPxRatio = r.mbPxRBlurry;
30500 }
30501 }
30502
30503 if (r.clearingMotionBlur) {
30504 r.motionBlurPxRatio = 1;
30505 } // b/c drawToContext() may be async w.r.t. redraw(), keep track of last texture frame
30506 // because a rogue async texture frame would clear needDraw
30507
30508
30509 if (r.textureDrawLastFrame && !textureDraw) {
30510 needDraw[r.NODE] = true;
30511 needDraw[r.SELECT_BOX] = true;
30512 }
30513
30514 var style = cy.style();
30515 var zoom = cy.zoom();
30516 var effectiveZoom = forcedZoom !== undefined ? forcedZoom : zoom;
30517 var pan = cy.pan();
30518 var effectivePan = {
30519 x: pan.x,
30520 y: pan.y
30521 };
30522 var vp = {
30523 zoom: zoom,
30524 pan: {
30525 x: pan.x,
30526 y: pan.y
30527 }
30528 };
30529 var prevVp = r.prevViewport;
30530 var viewportIsDiff = prevVp === undefined || vp.zoom !== prevVp.zoom || vp.pan.x !== prevVp.pan.x || vp.pan.y !== prevVp.pan.y; // we want the low quality motionblur only when the viewport is being manipulated etc (where it's not noticed)
30531
30532 if (!viewportIsDiff && !(inNodeDragGesture && !hasCompoundNodes)) {
30533 r.motionBlurPxRatio = 1;
30534 }
30535
30536 if (forcedPan) {
30537 effectivePan = forcedPan;
30538 } // apply pixel ratio
30539
30540
30541 effectiveZoom *= pixelRatio;
30542 effectivePan.x *= pixelRatio;
30543 effectivePan.y *= pixelRatio;
30544 var eles = r.getCachedZSortedEles();
30545
30546 function mbclear(context, x, y, w, h) {
30547 var gco = context.globalCompositeOperation;
30548 context.globalCompositeOperation = 'destination-out';
30549 r.colorFillStyle(context, 255, 255, 255, r.motionBlurTransparency);
30550 context.fillRect(x, y, w, h);
30551 context.globalCompositeOperation = gco;
30552 }
30553
30554 function setContextTransform(context, clear) {
30555 var ePan, eZoom, w, h;
30556
30557 if (!r.clearingMotionBlur && (context === data.bufferContexts[r.MOTIONBLUR_BUFFER_NODE] || context === data.bufferContexts[r.MOTIONBLUR_BUFFER_DRAG])) {
30558 ePan = {
30559 x: pan.x * mbPxRatio,
30560 y: pan.y * mbPxRatio
30561 };
30562 eZoom = zoom * mbPxRatio;
30563 w = r.canvasWidth * mbPxRatio;
30564 h = r.canvasHeight * mbPxRatio;
30565 } else {
30566 ePan = effectivePan;
30567 eZoom = effectiveZoom;
30568 w = r.canvasWidth;
30569 h = r.canvasHeight;
30570 }
30571
30572 context.setTransform(1, 0, 0, 1, 0, 0);
30573
30574 if (clear === 'motionBlur') {
30575 mbclear(context, 0, 0, w, h);
30576 } else if (!forcedContext && (clear === undefined || clear)) {
30577 context.clearRect(0, 0, w, h);
30578 }
30579
30580 if (!drawAllLayers) {
30581 context.translate(ePan.x, ePan.y);
30582 context.scale(eZoom, eZoom);
30583 }
30584
30585 if (forcedPan) {
30586 context.translate(forcedPan.x, forcedPan.y);
30587 }
30588
30589 if (forcedZoom) {
30590 context.scale(forcedZoom, forcedZoom);
30591 }
30592 }
30593
30594 if (!textureDraw) {
30595 r.textureDrawLastFrame = false;
30596 }
30597
30598 if (textureDraw) {
30599 r.textureDrawLastFrame = true;
30600
30601 if (!r.textureCache) {
30602 r.textureCache = {};
30603 r.textureCache.bb = cy.mutableElements().boundingBox();
30604 r.textureCache.texture = r.data.bufferCanvases[r.TEXTURE_BUFFER];
30605 var cxt = r.data.bufferContexts[r.TEXTURE_BUFFER];
30606 cxt.setTransform(1, 0, 0, 1, 0, 0);
30607 cxt.clearRect(0, 0, r.canvasWidth * r.textureMult, r.canvasHeight * r.textureMult);
30608 r.render({
30609 forcedContext: cxt,
30610 drawOnlyNodeLayer: true,
30611 forcedPxRatio: pixelRatio * r.textureMult
30612 });
30613 var vp = r.textureCache.viewport = {
30614 zoom: cy.zoom(),
30615 pan: cy.pan(),
30616 width: r.canvasWidth,
30617 height: r.canvasHeight
30618 };
30619 vp.mpan = {
30620 x: (0 - vp.pan.x) / vp.zoom,
30621 y: (0 - vp.pan.y) / vp.zoom
30622 };
30623 }
30624
30625 needDraw[r.DRAG] = false;
30626 needDraw[r.NODE] = false;
30627 var context = data.contexts[r.NODE];
30628 var texture = r.textureCache.texture;
30629 var vp = r.textureCache.viewport;
30630 context.setTransform(1, 0, 0, 1, 0, 0);
30631
30632 if (motionBlur) {
30633 mbclear(context, 0, 0, vp.width, vp.height);
30634 } else {
30635 context.clearRect(0, 0, vp.width, vp.height);
30636 }
30637
30638 var outsideBgColor = style.core('outside-texture-bg-color').value;
30639 var outsideBgOpacity = style.core('outside-texture-bg-opacity').value;
30640 r.colorFillStyle(context, outsideBgColor[0], outsideBgColor[1], outsideBgColor[2], outsideBgOpacity);
30641 context.fillRect(0, 0, vp.width, vp.height);
30642 var zoom = cy.zoom();
30643 setContextTransform(context, false);
30644 context.clearRect(vp.mpan.x, vp.mpan.y, vp.width / vp.zoom / pixelRatio, vp.height / vp.zoom / pixelRatio);
30645 context.drawImage(texture, vp.mpan.x, vp.mpan.y, vp.width / vp.zoom / pixelRatio, vp.height / vp.zoom / pixelRatio);
30646 } else if (r.textureOnViewport && !forcedContext) {
30647 // clear the cache since we don't need it
30648 r.textureCache = null;
30649 }
30650
30651 var extent = cy.extent();
30652 var vpManip = r.pinching || r.hoverData.dragging || r.swipePanning || r.data.wheelZooming || r.hoverData.draggingEles || r.cy.animated();
30653 var hideEdges = r.hideEdgesOnViewport && vpManip;
30654 var needMbClear = [];
30655 needMbClear[r.NODE] = !needDraw[r.NODE] && motionBlur && !r.clearedForMotionBlur[r.NODE] || r.clearingMotionBlur;
30656
30657 if (needMbClear[r.NODE]) {
30658 r.clearedForMotionBlur[r.NODE] = true;
30659 }
30660
30661 needMbClear[r.DRAG] = !needDraw[r.DRAG] && motionBlur && !r.clearedForMotionBlur[r.DRAG] || r.clearingMotionBlur;
30662
30663 if (needMbClear[r.DRAG]) {
30664 r.clearedForMotionBlur[r.DRAG] = true;
30665 }
30666
30667 if (needDraw[r.NODE] || drawAllLayers || drawOnlyNodeLayer || needMbClear[r.NODE]) {
30668 var useBuffer = motionBlur && !needMbClear[r.NODE] && mbPxRatio !== 1;
30669 var context = forcedContext || (useBuffer ? r.data.bufferContexts[r.MOTIONBLUR_BUFFER_NODE] : data.contexts[r.NODE]);
30670 var clear = motionBlur && !useBuffer ? 'motionBlur' : undefined;
30671 setContextTransform(context, clear);
30672
30673 if (hideEdges) {
30674 r.drawCachedNodes(context, eles.nondrag, pixelRatio, extent);
30675 } else {
30676 r.drawLayeredElements(context, eles.nondrag, pixelRatio, extent);
30677 }
30678
30679 if (r.debug) {
30680 r.drawDebugPoints(context, eles.nondrag);
30681 }
30682
30683 if (!drawAllLayers && !motionBlur) {
30684 needDraw[r.NODE] = false;
30685 }
30686 }
30687
30688 if (!drawOnlyNodeLayer && (needDraw[r.DRAG] || drawAllLayers || needMbClear[r.DRAG])) {
30689 var useBuffer = motionBlur && !needMbClear[r.DRAG] && mbPxRatio !== 1;
30690 var context = forcedContext || (useBuffer ? r.data.bufferContexts[r.MOTIONBLUR_BUFFER_DRAG] : data.contexts[r.DRAG]);
30691 setContextTransform(context, motionBlur && !useBuffer ? 'motionBlur' : undefined);
30692
30693 if (hideEdges) {
30694 r.drawCachedNodes(context, eles.drag, pixelRatio, extent);
30695 } else {
30696 r.drawCachedElements(context, eles.drag, pixelRatio, extent);
30697 }
30698
30699 if (r.debug) {
30700 r.drawDebugPoints(context, eles.drag);
30701 }
30702
30703 if (!drawAllLayers && !motionBlur) {
30704 needDraw[r.DRAG] = false;
30705 }
30706 }
30707
30708 if (r.showFps || !drawOnlyNodeLayer && needDraw[r.SELECT_BOX] && !drawAllLayers) {
30709 var context = forcedContext || data.contexts[r.SELECT_BOX];
30710 setContextTransform(context);
30711
30712 if (r.selection[4] == 1 && (r.hoverData.selecting || r.touchData.selecting)) {
30713 var zoom = r.cy.zoom();
30714 var borderWidth = style.core('selection-box-border-width').value / zoom;
30715 context.lineWidth = borderWidth;
30716 context.fillStyle = 'rgba(' + style.core('selection-box-color').value[0] + ',' + style.core('selection-box-color').value[1] + ',' + style.core('selection-box-color').value[2] + ',' + style.core('selection-box-opacity').value + ')';
30717 context.fillRect(r.selection[0], r.selection[1], r.selection[2] - r.selection[0], r.selection[3] - r.selection[1]);
30718
30719 if (borderWidth > 0) {
30720 context.strokeStyle = 'rgba(' + style.core('selection-box-border-color').value[0] + ',' + style.core('selection-box-border-color').value[1] + ',' + style.core('selection-box-border-color').value[2] + ',' + style.core('selection-box-opacity').value + ')';
30721 context.strokeRect(r.selection[0], r.selection[1], r.selection[2] - r.selection[0], r.selection[3] - r.selection[1]);
30722 }
30723 }
30724
30725 if (data.bgActivePosistion && !r.hoverData.selecting) {
30726 var zoom = r.cy.zoom();
30727 var pos = data.bgActivePosistion;
30728 context.fillStyle = 'rgba(' + style.core('active-bg-color').value[0] + ',' + style.core('active-bg-color').value[1] + ',' + style.core('active-bg-color').value[2] + ',' + style.core('active-bg-opacity').value + ')';
30729 context.beginPath();
30730 context.arc(pos.x, pos.y, style.core('active-bg-size').pfValue / zoom, 0, 2 * Math.PI);
30731 context.fill();
30732 }
30733
30734 var timeToRender = r.lastRedrawTime;
30735
30736 if (r.showFps && timeToRender) {
30737 timeToRender = Math.round(timeToRender);
30738 var fps = Math.round(1000 / timeToRender);
30739 context.setTransform(1, 0, 0, 1, 0, 0);
30740 context.fillStyle = 'rgba(255, 0, 0, 0.75)';
30741 context.strokeStyle = 'rgba(255, 0, 0, 0.75)';
30742 context.lineWidth = 1;
30743 context.fillText('1 frame = ' + timeToRender + ' ms = ' + fps + ' fps', 0, 20);
30744 var maxFps = 60;
30745 context.strokeRect(0, 30, 250, 20);
30746 context.fillRect(0, 30, 250 * Math.min(fps / maxFps, 1), 20);
30747 }
30748
30749 if (!drawAllLayers) {
30750 needDraw[r.SELECT_BOX] = false;
30751 }
30752 } // motionblur: blit rendered blurry frames
30753
30754
30755 if (motionBlur && mbPxRatio !== 1) {
30756 var cxtNode = data.contexts[r.NODE];
30757 var txtNode = r.data.bufferCanvases[r.MOTIONBLUR_BUFFER_NODE];
30758 var cxtDrag = data.contexts[r.DRAG];
30759 var txtDrag = r.data.bufferCanvases[r.MOTIONBLUR_BUFFER_DRAG];
30760
30761 var drawMotionBlur = function drawMotionBlur(cxt, txt, needClear) {
30762 cxt.setTransform(1, 0, 0, 1, 0, 0);
30763
30764 if (needClear || !motionBlurFadeEffect) {
30765 cxt.clearRect(0, 0, r.canvasWidth, r.canvasHeight);
30766 } else {
30767 mbclear(cxt, 0, 0, r.canvasWidth, r.canvasHeight);
30768 }
30769
30770 var pxr = mbPxRatio;
30771 cxt.drawImage(txt, // img
30772 0, 0, // sx, sy
30773 r.canvasWidth * pxr, r.canvasHeight * pxr, // sw, sh
30774 0, 0, // x, y
30775 r.canvasWidth, r.canvasHeight // w, h
30776 );
30777 };
30778
30779 if (needDraw[r.NODE] || needMbClear[r.NODE]) {
30780 drawMotionBlur(cxtNode, txtNode, needMbClear[r.NODE]);
30781 needDraw[r.NODE] = false;
30782 }
30783
30784 if (needDraw[r.DRAG] || needMbClear[r.DRAG]) {
30785 drawMotionBlur(cxtDrag, txtDrag, needMbClear[r.DRAG]);
30786 needDraw[r.DRAG] = false;
30787 }
30788 }
30789
30790 r.prevViewport = vp;
30791
30792 if (r.clearingMotionBlur) {
30793 r.clearingMotionBlur = false;
30794 r.motionBlurCleared = true;
30795 r.motionBlur = true;
30796 }
30797
30798 if (motionBlur) {
30799 r.motionBlurTimeout = setTimeout(function () {
30800 r.motionBlurTimeout = null;
30801 r.clearedForMotionBlur[r.NODE] = false;
30802 r.clearedForMotionBlur[r.DRAG] = false;
30803 r.motionBlur = false;
30804 r.clearingMotionBlur = !textureDraw;
30805 r.mbFrames = 0;
30806 needDraw[r.NODE] = true;
30807 needDraw[r.DRAG] = true;
30808 r.redraw();
30809 }, motionBlurDelay);
30810 }
30811
30812 if (!forcedContext) {
30813 cy.emit('render');
30814 }
30815};
30816
30817var CRp$3 = {}; // @O Polygon drawing
30818
30819CRp$3.drawPolygonPath = function (context, x, y, width, height, points) {
30820 var halfW = width / 2;
30821 var halfH = height / 2;
30822
30823 if (context.beginPath) {
30824 context.beginPath();
30825 }
30826
30827 context.moveTo(x + halfW * points[0], y + halfH * points[1]);
30828
30829 for (var i = 1; i < points.length / 2; i++) {
30830 context.lineTo(x + halfW * points[i * 2], y + halfH * points[i * 2 + 1]);
30831 }
30832
30833 context.closePath();
30834};
30835
30836CRp$3.drawRoundPolygonPath = function (context, x, y, width, height, points) {
30837 var halfW = width / 2;
30838 var halfH = height / 2;
30839 var cornerRadius = getRoundPolygonRadius(width, height);
30840
30841 if (context.beginPath) {
30842 context.beginPath();
30843 }
30844
30845 for (var _i = 0; _i < points.length / 4; _i++) {
30846 var sourceUv = void 0,
30847 destUv = void 0;
30848
30849 if (_i === 0) {
30850 sourceUv = points.length - 2;
30851 } else {
30852 sourceUv = _i * 4 - 2;
30853 }
30854
30855 destUv = _i * 4 + 2;
30856 var px = x + halfW * points[_i * 4];
30857 var py = y + halfH * points[_i * 4 + 1];
30858 var cosTheta = -points[sourceUv] * points[destUv] - points[sourceUv + 1] * points[destUv + 1];
30859 var offset = cornerRadius / Math.tan(Math.acos(cosTheta) / 2);
30860 var cp0x = px - offset * points[sourceUv];
30861 var cp0y = py - offset * points[sourceUv + 1];
30862 var cp1x = px + offset * points[destUv];
30863 var cp1y = py + offset * points[destUv + 1];
30864
30865 if (_i === 0) {
30866 context.moveTo(cp0x, cp0y);
30867 } else {
30868 context.lineTo(cp0x, cp0y);
30869 }
30870
30871 context.arcTo(px, py, cp1x, cp1y, cornerRadius);
30872 }
30873
30874 context.closePath();
30875}; // Round rectangle drawing
30876
30877
30878CRp$3.drawRoundRectanglePath = function (context, x, y, width, height) {
30879 var halfWidth = width / 2;
30880 var halfHeight = height / 2;
30881 var cornerRadius = getRoundRectangleRadius(width, height);
30882
30883 if (context.beginPath) {
30884 context.beginPath();
30885 } // Start at top middle
30886
30887
30888 context.moveTo(x, y - halfHeight); // Arc from middle top to right side
30889
30890 context.arcTo(x + halfWidth, y - halfHeight, x + halfWidth, y, cornerRadius); // Arc from right side to bottom
30891
30892 context.arcTo(x + halfWidth, y + halfHeight, x, y + halfHeight, cornerRadius); // Arc from bottom to left side
30893
30894 context.arcTo(x - halfWidth, y + halfHeight, x - halfWidth, y, cornerRadius); // Arc from left side to topBorder
30895
30896 context.arcTo(x - halfWidth, y - halfHeight, x, y - halfHeight, cornerRadius); // Join line
30897
30898 context.lineTo(x, y - halfHeight);
30899 context.closePath();
30900};
30901
30902CRp$3.drawBottomRoundRectanglePath = function (context, x, y, width, height) {
30903 var halfWidth = width / 2;
30904 var halfHeight = height / 2;
30905 var cornerRadius = getRoundRectangleRadius(width, height);
30906
30907 if (context.beginPath) {
30908 context.beginPath();
30909 } // Start at top middle
30910
30911
30912 context.moveTo(x, y - halfHeight);
30913 context.lineTo(x + halfWidth, y - halfHeight);
30914 context.lineTo(x + halfWidth, y);
30915 context.arcTo(x + halfWidth, y + halfHeight, x, y + halfHeight, cornerRadius);
30916 context.arcTo(x - halfWidth, y + halfHeight, x - halfWidth, y, cornerRadius);
30917 context.lineTo(x - halfWidth, y - halfHeight);
30918 context.lineTo(x, y - halfHeight);
30919 context.closePath();
30920};
30921
30922CRp$3.drawCutRectanglePath = function (context, x, y, width, height) {
30923 var halfWidth = width / 2;
30924 var halfHeight = height / 2;
30925 var cornerLength = getCutRectangleCornerLength();
30926
30927 if (context.beginPath) {
30928 context.beginPath();
30929 }
30930
30931 context.moveTo(x - halfWidth + cornerLength, y - halfHeight);
30932 context.lineTo(x + halfWidth - cornerLength, y - halfHeight);
30933 context.lineTo(x + halfWidth, y - halfHeight + cornerLength);
30934 context.lineTo(x + halfWidth, y + halfHeight - cornerLength);
30935 context.lineTo(x + halfWidth - cornerLength, y + halfHeight);
30936 context.lineTo(x - halfWidth + cornerLength, y + halfHeight);
30937 context.lineTo(x - halfWidth, y + halfHeight - cornerLength);
30938 context.lineTo(x - halfWidth, y - halfHeight + cornerLength);
30939 context.closePath();
30940};
30941
30942CRp$3.drawBarrelPath = function (context, x, y, width, height) {
30943 var halfWidth = width / 2;
30944 var halfHeight = height / 2;
30945 var xBegin = x - halfWidth;
30946 var xEnd = x + halfWidth;
30947 var yBegin = y - halfHeight;
30948 var yEnd = y + halfHeight;
30949 var barrelCurveConstants = getBarrelCurveConstants(width, height);
30950 var wOffset = barrelCurveConstants.widthOffset;
30951 var hOffset = barrelCurveConstants.heightOffset;
30952 var ctrlPtXOffset = barrelCurveConstants.ctrlPtOffsetPct * wOffset;
30953
30954 if (context.beginPath) {
30955 context.beginPath();
30956 }
30957
30958 context.moveTo(xBegin, yBegin + hOffset);
30959 context.lineTo(xBegin, yEnd - hOffset);
30960 context.quadraticCurveTo(xBegin + ctrlPtXOffset, yEnd, xBegin + wOffset, yEnd);
30961 context.lineTo(xEnd - wOffset, yEnd);
30962 context.quadraticCurveTo(xEnd - ctrlPtXOffset, yEnd, xEnd, yEnd - hOffset);
30963 context.lineTo(xEnd, yBegin + hOffset);
30964 context.quadraticCurveTo(xEnd - ctrlPtXOffset, yBegin, xEnd - wOffset, yBegin);
30965 context.lineTo(xBegin + wOffset, yBegin);
30966 context.quadraticCurveTo(xBegin + ctrlPtXOffset, yBegin, xBegin, yBegin + hOffset);
30967 context.closePath();
30968};
30969
30970var sin0 = Math.sin(0);
30971var cos0 = Math.cos(0);
30972var sin = {};
30973var cos = {};
30974var ellipseStepSize = Math.PI / 40;
30975
30976for (var i = 0 * Math.PI; i < 2 * Math.PI; i += ellipseStepSize) {
30977 sin[i] = Math.sin(i);
30978 cos[i] = Math.cos(i);
30979}
30980
30981CRp$3.drawEllipsePath = function (context, centerX, centerY, width, height) {
30982 if (context.beginPath) {
30983 context.beginPath();
30984 }
30985
30986 if (context.ellipse) {
30987 context.ellipse(centerX, centerY, width / 2, height / 2, 0, 0, 2 * Math.PI);
30988 } else {
30989 var xPos, yPos;
30990 var rw = width / 2;
30991 var rh = height / 2;
30992
30993 for (var i = 0 * Math.PI; i < 2 * Math.PI; i += ellipseStepSize) {
30994 xPos = centerX - rw * sin[i] * sin0 + rw * cos[i] * cos0;
30995 yPos = centerY + rh * cos[i] * sin0 + rh * sin[i] * cos0;
30996
30997 if (i === 0) {
30998 context.moveTo(xPos, yPos);
30999 } else {
31000 context.lineTo(xPos, yPos);
31001 }
31002 }
31003 }
31004
31005 context.closePath();
31006};
31007
31008/* global atob, ArrayBuffer, Uint8Array, Blob */
31009var CRp$2 = {};
31010
31011CRp$2.createBuffer = function (w, h) {
31012 var buffer = document.createElement('canvas'); // eslint-disable-line no-undef
31013
31014 buffer.width = w;
31015 buffer.height = h;
31016 return [buffer, buffer.getContext('2d')];
31017};
31018
31019CRp$2.bufferCanvasImage = function (options) {
31020 var cy = this.cy;
31021 var eles = cy.mutableElements();
31022 var bb = eles.boundingBox();
31023 var ctrRect = this.findContainerClientCoords();
31024 var width = options.full ? Math.ceil(bb.w) : ctrRect[2];
31025 var height = options.full ? Math.ceil(bb.h) : ctrRect[3];
31026 var specdMaxDims = number$1(options.maxWidth) || number$1(options.maxHeight);
31027 var pxRatio = this.getPixelRatio();
31028 var scale = 1;
31029
31030 if (options.scale !== undefined) {
31031 width *= options.scale;
31032 height *= options.scale;
31033 scale = options.scale;
31034 } else if (specdMaxDims) {
31035 var maxScaleW = Infinity;
31036 var maxScaleH = Infinity;
31037
31038 if (number$1(options.maxWidth)) {
31039 maxScaleW = scale * options.maxWidth / width;
31040 }
31041
31042 if (number$1(options.maxHeight)) {
31043 maxScaleH = scale * options.maxHeight / height;
31044 }
31045
31046 scale = Math.min(maxScaleW, maxScaleH);
31047 width *= scale;
31048 height *= scale;
31049 }
31050
31051 if (!specdMaxDims) {
31052 width *= pxRatio;
31053 height *= pxRatio;
31054 scale *= pxRatio;
31055 }
31056
31057 var buffCanvas = document.createElement('canvas'); // eslint-disable-line no-undef
31058
31059 buffCanvas.width = width;
31060 buffCanvas.height = height;
31061 buffCanvas.style.width = width + 'px';
31062 buffCanvas.style.height = height + 'px';
31063 var buffCxt = buffCanvas.getContext('2d'); // Rasterize the layers, but only if container has nonzero size
31064
31065 if (width > 0 && height > 0) {
31066 buffCxt.clearRect(0, 0, width, height);
31067 buffCxt.globalCompositeOperation = 'source-over';
31068 var zsortedEles = this.getCachedZSortedEles();
31069
31070 if (options.full) {
31071 // draw the full bounds of the graph
31072 buffCxt.translate(-bb.x1 * scale, -bb.y1 * scale);
31073 buffCxt.scale(scale, scale);
31074 this.drawElements(buffCxt, zsortedEles);
31075 buffCxt.scale(1 / scale, 1 / scale);
31076 buffCxt.translate(bb.x1 * scale, bb.y1 * scale);
31077 } else {
31078 // draw the current view
31079 var pan = cy.pan();
31080 var translation = {
31081 x: pan.x * scale,
31082 y: pan.y * scale
31083 };
31084 scale *= cy.zoom();
31085 buffCxt.translate(translation.x, translation.y);
31086 buffCxt.scale(scale, scale);
31087 this.drawElements(buffCxt, zsortedEles);
31088 buffCxt.scale(1 / scale, 1 / scale);
31089 buffCxt.translate(-translation.x, -translation.y);
31090 } // need to fill bg at end like this in order to fill cleared transparent pixels in jpgs
31091
31092
31093 if (options.bg) {
31094 buffCxt.globalCompositeOperation = 'destination-over';
31095 buffCxt.fillStyle = options.bg;
31096 buffCxt.rect(0, 0, width, height);
31097 buffCxt.fill();
31098 }
31099 }
31100
31101 return buffCanvas;
31102};
31103
31104function b64ToBlob(b64, mimeType) {
31105 var bytes = atob(b64);
31106 var buff = new ArrayBuffer(bytes.length);
31107 var buffUint8 = new Uint8Array(buff);
31108
31109 for (var i = 0; i < bytes.length; i++) {
31110 buffUint8[i] = bytes.charCodeAt(i);
31111 }
31112
31113 return new Blob([buff], {
31114 type: mimeType
31115 });
31116}
31117
31118function b64UriToB64(b64uri) {
31119 var i = b64uri.indexOf(',');
31120 return b64uri.substr(i + 1);
31121}
31122
31123function output(options, canvas, mimeType) {
31124 var getB64Uri = function getB64Uri() {
31125 return canvas.toDataURL(mimeType, options.quality);
31126 };
31127
31128 switch (options.output) {
31129 case 'blob-promise':
31130 return new Promise$1(function (resolve, reject) {
31131 try {
31132 canvas.toBlob(function (blob) {
31133 if (blob != null) {
31134 resolve(blob);
31135 } else {
31136 reject(new Error('`canvas.toBlob()` sent a null value in its callback'));
31137 }
31138 }, mimeType, options.quality);
31139 } catch (err) {
31140 reject(err);
31141 }
31142 });
31143
31144 case 'blob':
31145 return b64ToBlob(b64UriToB64(getB64Uri()), mimeType);
31146
31147 case 'base64':
31148 return b64UriToB64(getB64Uri());
31149
31150 case 'base64uri':
31151 default:
31152 return getB64Uri();
31153 }
31154}
31155
31156CRp$2.png = function (options) {
31157 return output(options, this.bufferCanvasImage(options), 'image/png');
31158};
31159
31160CRp$2.jpg = function (options) {
31161 return output(options, this.bufferCanvasImage(options), 'image/jpeg');
31162};
31163
31164var CRp$1 = {};
31165
31166CRp$1.nodeShapeImpl = function (name, context, centerX, centerY, width, height, points) {
31167 switch (name) {
31168 case 'ellipse':
31169 return this.drawEllipsePath(context, centerX, centerY, width, height);
31170
31171 case 'polygon':
31172 return this.drawPolygonPath(context, centerX, centerY, width, height, points);
31173
31174 case 'round-polygon':
31175 return this.drawRoundPolygonPath(context, centerX, centerY, width, height, points);
31176
31177 case 'roundrectangle':
31178 case 'round-rectangle':
31179 return this.drawRoundRectanglePath(context, centerX, centerY, width, height);
31180
31181 case 'cutrectangle':
31182 case 'cut-rectangle':
31183 return this.drawCutRectanglePath(context, centerX, centerY, width, height);
31184
31185 case 'bottomroundrectangle':
31186 case 'bottom-round-rectangle':
31187 return this.drawBottomRoundRectanglePath(context, centerX, centerY, width, height);
31188
31189 case 'barrel':
31190 return this.drawBarrelPath(context, centerX, centerY, width, height);
31191 }
31192};
31193
31194var CR = CanvasRenderer;
31195var CRp = CanvasRenderer.prototype;
31196CRp.CANVAS_LAYERS = 3; //
31197
31198CRp.SELECT_BOX = 0;
31199CRp.DRAG = 1;
31200CRp.NODE = 2;
31201CRp.BUFFER_COUNT = 3; //
31202
31203CRp.TEXTURE_BUFFER = 0;
31204CRp.MOTIONBLUR_BUFFER_NODE = 1;
31205CRp.MOTIONBLUR_BUFFER_DRAG = 2;
31206
31207function CanvasRenderer(options) {
31208 var r = this;
31209 r.data = {
31210 canvases: new Array(CRp.CANVAS_LAYERS),
31211 contexts: new Array(CRp.CANVAS_LAYERS),
31212 canvasNeedsRedraw: new Array(CRp.CANVAS_LAYERS),
31213 bufferCanvases: new Array(CRp.BUFFER_COUNT),
31214 bufferContexts: new Array(CRp.CANVAS_LAYERS)
31215 };
31216 var tapHlOffAttr = '-webkit-tap-highlight-color';
31217 var tapHlOffStyle = 'rgba(0,0,0,0)';
31218 r.data.canvasContainer = document.createElement('div'); // eslint-disable-line no-undef
31219
31220 var containerStyle = r.data.canvasContainer.style;
31221 r.data.canvasContainer.style[tapHlOffAttr] = tapHlOffStyle;
31222 containerStyle.position = 'relative';
31223 containerStyle.zIndex = '0';
31224 containerStyle.overflow = 'hidden';
31225 var container = options.cy.container();
31226 container.appendChild(r.data.canvasContainer);
31227 container.style[tapHlOffAttr] = tapHlOffStyle;
31228 var styleMap = {
31229 '-webkit-user-select': 'none',
31230 '-moz-user-select': '-moz-none',
31231 'user-select': 'none',
31232 '-webkit-tap-highlight-color': 'rgba(0,0,0,0)',
31233 'outline-style': 'none'
31234 };
31235
31236 if (ms()) {
31237 styleMap['-ms-touch-action'] = 'none';
31238 styleMap['touch-action'] = 'none';
31239 }
31240
31241 for (var i = 0; i < CRp.CANVAS_LAYERS; i++) {
31242 var canvas = r.data.canvases[i] = document.createElement('canvas'); // eslint-disable-line no-undef
31243
31244 r.data.contexts[i] = canvas.getContext('2d');
31245 Object.keys(styleMap).forEach(function (k) {
31246 canvas.style[k] = styleMap[k];
31247 });
31248 canvas.style.position = 'absolute';
31249 canvas.setAttribute('data-id', 'layer' + i);
31250 canvas.style.zIndex = String(CRp.CANVAS_LAYERS - i);
31251 r.data.canvasContainer.appendChild(canvas);
31252 r.data.canvasNeedsRedraw[i] = false;
31253 }
31254
31255 r.data.topCanvas = r.data.canvases[0];
31256 r.data.canvases[CRp.NODE].setAttribute('data-id', 'layer' + CRp.NODE + '-node');
31257 r.data.canvases[CRp.SELECT_BOX].setAttribute('data-id', 'layer' + CRp.SELECT_BOX + '-selectbox');
31258 r.data.canvases[CRp.DRAG].setAttribute('data-id', 'layer' + CRp.DRAG + '-drag');
31259
31260 for (var i = 0; i < CRp.BUFFER_COUNT; i++) {
31261 r.data.bufferCanvases[i] = document.createElement('canvas'); // eslint-disable-line no-undef
31262
31263 r.data.bufferContexts[i] = r.data.bufferCanvases[i].getContext('2d');
31264 r.data.bufferCanvases[i].style.position = 'absolute';
31265 r.data.bufferCanvases[i].setAttribute('data-id', 'buffer' + i);
31266 r.data.bufferCanvases[i].style.zIndex = String(-i - 1);
31267 r.data.bufferCanvases[i].style.visibility = 'hidden'; //r.data.canvasContainer.appendChild(r.data.bufferCanvases[i]);
31268 }
31269
31270 r.pathsEnabled = true;
31271 var emptyBb = makeBoundingBox();
31272
31273 var getBoxCenter = function getBoxCenter(bb) {
31274 return {
31275 x: (bb.x1 + bb.x2) / 2,
31276 y: (bb.y1 + bb.y2) / 2
31277 };
31278 };
31279
31280 var getCenterOffset = function getCenterOffset(bb) {
31281 return {
31282 x: -bb.w / 2,
31283 y: -bb.h / 2
31284 };
31285 };
31286
31287 var backgroundTimestampHasChanged = function backgroundTimestampHasChanged(ele) {
31288 var _p = ele[0]._private;
31289 var same = _p.oldBackgroundTimestamp === _p.backgroundTimestamp;
31290 return !same;
31291 };
31292
31293 var getStyleKey = function getStyleKey(ele) {
31294 return ele[0]._private.nodeKey;
31295 };
31296
31297 var getLabelKey = function getLabelKey(ele) {
31298 return ele[0]._private.labelStyleKey;
31299 };
31300
31301 var getSourceLabelKey = function getSourceLabelKey(ele) {
31302 return ele[0]._private.sourceLabelStyleKey;
31303 };
31304
31305 var getTargetLabelKey = function getTargetLabelKey(ele) {
31306 return ele[0]._private.targetLabelStyleKey;
31307 };
31308
31309 var drawElement = function drawElement(context, ele, bb, scaledLabelShown, useEleOpacity) {
31310 return r.drawElement(context, ele, bb, false, false, useEleOpacity);
31311 };
31312
31313 var drawLabel = function drawLabel(context, ele, bb, scaledLabelShown, useEleOpacity) {
31314 return r.drawElementText(context, ele, bb, scaledLabelShown, 'main', useEleOpacity);
31315 };
31316
31317 var drawSourceLabel = function drawSourceLabel(context, ele, bb, scaledLabelShown, useEleOpacity) {
31318 return r.drawElementText(context, ele, bb, scaledLabelShown, 'source', useEleOpacity);
31319 };
31320
31321 var drawTargetLabel = function drawTargetLabel(context, ele, bb, scaledLabelShown, useEleOpacity) {
31322 return r.drawElementText(context, ele, bb, scaledLabelShown, 'target', useEleOpacity);
31323 };
31324
31325 var getElementBox = function getElementBox(ele) {
31326 ele.boundingBox();
31327 return ele[0]._private.bodyBounds;
31328 };
31329
31330 var getLabelBox = function getLabelBox(ele) {
31331 ele.boundingBox();
31332 return ele[0]._private.labelBounds.main || emptyBb;
31333 };
31334
31335 var getSourceLabelBox = function getSourceLabelBox(ele) {
31336 ele.boundingBox();
31337 return ele[0]._private.labelBounds.source || emptyBb;
31338 };
31339
31340 var getTargetLabelBox = function getTargetLabelBox(ele) {
31341 ele.boundingBox();
31342 return ele[0]._private.labelBounds.target || emptyBb;
31343 };
31344
31345 var isLabelVisibleAtScale = function isLabelVisibleAtScale(ele, scaledLabelShown) {
31346 return scaledLabelShown;
31347 };
31348
31349 var getElementRotationPoint = function getElementRotationPoint(ele) {
31350 return getBoxCenter(getElementBox(ele));
31351 };
31352
31353 var addTextMargin = function addTextMargin(prefix, pt, ele) {
31354 var pre = prefix ? prefix + '-' : '';
31355 return {
31356 x: pt.x + ele.pstyle(pre + 'text-margin-x').pfValue,
31357 y: pt.y + ele.pstyle(pre + 'text-margin-y').pfValue
31358 };
31359 };
31360
31361 var getRsPt = function getRsPt(ele, x, y) {
31362 var rs = ele[0]._private.rscratch;
31363 return {
31364 x: rs[x],
31365 y: rs[y]
31366 };
31367 };
31368
31369 var getLabelRotationPoint = function getLabelRotationPoint(ele) {
31370 return addTextMargin('', getRsPt(ele, 'labelX', 'labelY'), ele);
31371 };
31372
31373 var getSourceLabelRotationPoint = function getSourceLabelRotationPoint(ele) {
31374 return addTextMargin('source', getRsPt(ele, 'sourceLabelX', 'sourceLabelY'), ele);
31375 };
31376
31377 var getTargetLabelRotationPoint = function getTargetLabelRotationPoint(ele) {
31378 return addTextMargin('target', getRsPt(ele, 'targetLabelX', 'targetLabelY'), ele);
31379 };
31380
31381 var getElementRotationOffset = function getElementRotationOffset(ele) {
31382 return getCenterOffset(getElementBox(ele));
31383 };
31384
31385 var getSourceLabelRotationOffset = function getSourceLabelRotationOffset(ele) {
31386 return getCenterOffset(getSourceLabelBox(ele));
31387 };
31388
31389 var getTargetLabelRotationOffset = function getTargetLabelRotationOffset(ele) {
31390 return getCenterOffset(getTargetLabelBox(ele));
31391 };
31392
31393 var getLabelRotationOffset = function getLabelRotationOffset(ele) {
31394 var bb = getLabelBox(ele);
31395 var p = getCenterOffset(getLabelBox(ele));
31396
31397 if (ele.isNode()) {
31398 switch (ele.pstyle('text-halign').value) {
31399 case 'left':
31400 p.x = -bb.w;
31401 break;
31402
31403 case 'right':
31404 p.x = 0;
31405 break;
31406 }
31407
31408 switch (ele.pstyle('text-valign').value) {
31409 case 'top':
31410 p.y = -bb.h;
31411 break;
31412
31413 case 'bottom':
31414 p.y = 0;
31415 break;
31416 }
31417 }
31418
31419 return p;
31420 };
31421
31422 var eleTxrCache = r.data.eleTxrCache = new ElementTextureCache(r, {
31423 getKey: getStyleKey,
31424 doesEleInvalidateKey: backgroundTimestampHasChanged,
31425 drawElement: drawElement,
31426 getBoundingBox: getElementBox,
31427 getRotationPoint: getElementRotationPoint,
31428 getRotationOffset: getElementRotationOffset,
31429 allowEdgeTxrCaching: false,
31430 allowParentTxrCaching: false
31431 });
31432 var lblTxrCache = r.data.lblTxrCache = new ElementTextureCache(r, {
31433 getKey: getLabelKey,
31434 drawElement: drawLabel,
31435 getBoundingBox: getLabelBox,
31436 getRotationPoint: getLabelRotationPoint,
31437 getRotationOffset: getLabelRotationOffset,
31438 isVisible: isLabelVisibleAtScale
31439 });
31440 var slbTxrCache = r.data.slbTxrCache = new ElementTextureCache(r, {
31441 getKey: getSourceLabelKey,
31442 drawElement: drawSourceLabel,
31443 getBoundingBox: getSourceLabelBox,
31444 getRotationPoint: getSourceLabelRotationPoint,
31445 getRotationOffset: getSourceLabelRotationOffset,
31446 isVisible: isLabelVisibleAtScale
31447 });
31448 var tlbTxrCache = r.data.tlbTxrCache = new ElementTextureCache(r, {
31449 getKey: getTargetLabelKey,
31450 drawElement: drawTargetLabel,
31451 getBoundingBox: getTargetLabelBox,
31452 getRotationPoint: getTargetLabelRotationPoint,
31453 getRotationOffset: getTargetLabelRotationOffset,
31454 isVisible: isLabelVisibleAtScale
31455 });
31456 var lyrTxrCache = r.data.lyrTxrCache = new LayeredTextureCache(r);
31457 r.onUpdateEleCalcs(function invalidateTextureCaches(willDraw, eles) {
31458 // each cache should check for sub-key diff to see that the update affects that cache particularly
31459 eleTxrCache.invalidateElements(eles);
31460 lblTxrCache.invalidateElements(eles);
31461 slbTxrCache.invalidateElements(eles);
31462 tlbTxrCache.invalidateElements(eles); // any change invalidates the layers
31463
31464 lyrTxrCache.invalidateElements(eles); // update the old bg timestamp so diffs can be done in the ele txr caches
31465
31466 for (var _i = 0; _i < eles.length; _i++) {
31467 var _p = eles[_i]._private;
31468 _p.oldBackgroundTimestamp = _p.backgroundTimestamp;
31469 }
31470 });
31471
31472 var refineInLayers = function refineInLayers(reqs) {
31473 for (var i = 0; i < reqs.length; i++) {
31474 lyrTxrCache.enqueueElementRefinement(reqs[i].ele);
31475 }
31476 };
31477
31478 eleTxrCache.onDequeue(refineInLayers);
31479 lblTxrCache.onDequeue(refineInLayers);
31480 slbTxrCache.onDequeue(refineInLayers);
31481 tlbTxrCache.onDequeue(refineInLayers);
31482}
31483
31484CRp.redrawHint = function (group, bool) {
31485 var r = this;
31486
31487 switch (group) {
31488 case 'eles':
31489 r.data.canvasNeedsRedraw[CRp.NODE] = bool;
31490 break;
31491
31492 case 'drag':
31493 r.data.canvasNeedsRedraw[CRp.DRAG] = bool;
31494 break;
31495
31496 case 'select':
31497 r.data.canvasNeedsRedraw[CRp.SELECT_BOX] = bool;
31498 break;
31499 }
31500}; // whether to use Path2D caching for drawing
31501
31502
31503var pathsImpld = typeof Path2D !== 'undefined';
31504
31505CRp.path2dEnabled = function (on) {
31506 if (on === undefined) {
31507 return this.pathsEnabled;
31508 }
31509
31510 this.pathsEnabled = on ? true : false;
31511};
31512
31513CRp.usePaths = function () {
31514 return pathsImpld && this.pathsEnabled;
31515};
31516
31517CRp.setImgSmoothing = function (context, bool) {
31518 if (context.imageSmoothingEnabled != null) {
31519 context.imageSmoothingEnabled = bool;
31520 } else {
31521 context.webkitImageSmoothingEnabled = bool;
31522 context.mozImageSmoothingEnabled = bool;
31523 context.msImageSmoothingEnabled = bool;
31524 }
31525};
31526
31527CRp.getImgSmoothing = function (context) {
31528 if (context.imageSmoothingEnabled != null) {
31529 return context.imageSmoothingEnabled;
31530 } else {
31531 return context.webkitImageSmoothingEnabled || context.mozImageSmoothingEnabled || context.msImageSmoothingEnabled;
31532 }
31533};
31534
31535CRp.makeOffscreenCanvas = function (width, height) {
31536 var canvas;
31537
31538 if ((typeof OffscreenCanvas === "undefined" ? "undefined" : _typeof(OffscreenCanvas)) !== ("undefined" )) {
31539 canvas = new OffscreenCanvas(width, height);
31540 } else {
31541 canvas = document.createElement('canvas'); // eslint-disable-line no-undef
31542
31543 canvas.width = width;
31544 canvas.height = height;
31545 }
31546
31547 return canvas;
31548};
31549
31550[CRp$a, CRp$9, CRp$8, CRp$7, CRp$6, CRp$5, CRp$4, CRp$3, CRp$2, CRp$1].forEach(function (props) {
31551 extend(CRp, props);
31552});
31553
31554var renderer = [{
31555 name: 'null',
31556 impl: NullRenderer
31557}, {
31558 name: 'base',
31559 impl: BR
31560}, {
31561 name: 'canvas',
31562 impl: CR
31563}];
31564
31565var incExts = [{
31566 type: 'layout',
31567 extensions: layout
31568}, {
31569 type: 'renderer',
31570 extensions: renderer
31571}];
31572
31573var extensions = {}; // registered modules for extensions, indexed by name
31574
31575var modules = {};
31576
31577function setExtension(type, name, registrant) {
31578 var ext = registrant;
31579
31580 var overrideErr = function overrideErr(field) {
31581 warn('Can not register `' + name + '` for `' + type + '` since `' + field + '` already exists in the prototype and can not be overridden');
31582 };
31583
31584 if (type === 'core') {
31585 if (Core.prototype[name]) {
31586 return overrideErr(name);
31587 } else {
31588 Core.prototype[name] = registrant;
31589 }
31590 } else if (type === 'collection') {
31591 if (Collection.prototype[name]) {
31592 return overrideErr(name);
31593 } else {
31594 Collection.prototype[name] = registrant;
31595 }
31596 } else if (type === 'layout') {
31597 // fill in missing layout functions in the prototype
31598 var Layout = function Layout(options) {
31599 this.options = options;
31600 registrant.call(this, options); // make sure layout has _private for use w/ std apis like .on()
31601
31602 if (!plainObject(this._private)) {
31603 this._private = {};
31604 }
31605
31606 this._private.cy = options.cy;
31607 this._private.listeners = [];
31608 this.createEmitter();
31609 };
31610
31611 var layoutProto = Layout.prototype = Object.create(registrant.prototype);
31612 var optLayoutFns = [];
31613
31614 for (var i = 0; i < optLayoutFns.length; i++) {
31615 var fnName = optLayoutFns[i];
31616
31617 layoutProto[fnName] = layoutProto[fnName] || function () {
31618 return this;
31619 };
31620 } // either .start() or .run() is defined, so autogen the other
31621
31622
31623 if (layoutProto.start && !layoutProto.run) {
31624 layoutProto.run = function () {
31625 this.start();
31626 return this;
31627 };
31628 } else if (!layoutProto.start && layoutProto.run) {
31629 layoutProto.start = function () {
31630 this.run();
31631 return this;
31632 };
31633 }
31634
31635 var regStop = registrant.prototype.stop;
31636
31637 layoutProto.stop = function () {
31638 var opts = this.options;
31639
31640 if (opts && opts.animate) {
31641 var anis = this.animations;
31642
31643 if (anis) {
31644 for (var _i = 0; _i < anis.length; _i++) {
31645 anis[_i].stop();
31646 }
31647 }
31648 }
31649
31650 if (regStop) {
31651 regStop.call(this);
31652 } else {
31653 this.emit('layoutstop');
31654 }
31655
31656 return this;
31657 };
31658
31659 if (!layoutProto.destroy) {
31660 layoutProto.destroy = function () {
31661 return this;
31662 };
31663 }
31664
31665 layoutProto.cy = function () {
31666 return this._private.cy;
31667 };
31668
31669 var getCy = function getCy(layout) {
31670 return layout._private.cy;
31671 };
31672
31673 var emitterOpts = {
31674 addEventFields: function addEventFields(layout, evt) {
31675 evt.layout = layout;
31676 evt.cy = getCy(layout);
31677 evt.target = layout;
31678 },
31679 bubble: function bubble() {
31680 return true;
31681 },
31682 parent: function parent(layout) {
31683 return getCy(layout);
31684 }
31685 };
31686 extend(layoutProto, {
31687 createEmitter: function createEmitter() {
31688 this._private.emitter = new Emitter(emitterOpts, this);
31689 return this;
31690 },
31691 emitter: function emitter() {
31692 return this._private.emitter;
31693 },
31694 on: function on(evt, cb) {
31695 this.emitter().on(evt, cb);
31696 return this;
31697 },
31698 one: function one(evt, cb) {
31699 this.emitter().one(evt, cb);
31700 return this;
31701 },
31702 once: function once(evt, cb) {
31703 this.emitter().one(evt, cb);
31704 return this;
31705 },
31706 removeListener: function removeListener(evt, cb) {
31707 this.emitter().removeListener(evt, cb);
31708 return this;
31709 },
31710 removeAllListeners: function removeAllListeners() {
31711 this.emitter().removeAllListeners();
31712 return this;
31713 },
31714 emit: function emit(evt, params) {
31715 this.emitter().emit(evt, params);
31716 return this;
31717 }
31718 });
31719 define.eventAliasesOn(layoutProto);
31720 ext = Layout; // replace with our wrapped layout
31721 } else if (type === 'renderer' && name !== 'null' && name !== 'base') {
31722 // user registered renderers inherit from base
31723 var BaseRenderer = getExtension('renderer', 'base');
31724 var bProto = BaseRenderer.prototype;
31725 var RegistrantRenderer = registrant;
31726 var rProto = registrant.prototype;
31727
31728 var Renderer = function Renderer() {
31729 BaseRenderer.apply(this, arguments);
31730 RegistrantRenderer.apply(this, arguments);
31731 };
31732
31733 var proto = Renderer.prototype;
31734
31735 for (var pName in bProto) {
31736 var pVal = bProto[pName];
31737 var existsInR = rProto[pName] != null;
31738
31739 if (existsInR) {
31740 return overrideErr(pName);
31741 }
31742
31743 proto[pName] = pVal; // take impl from base
31744 }
31745
31746 for (var _pName in rProto) {
31747 proto[_pName] = rProto[_pName]; // take impl from registrant
31748 }
31749
31750 bProto.clientFunctions.forEach(function (name) {
31751 proto[name] = proto[name] || function () {
31752 error('Renderer does not implement `renderer.' + name + '()` on its prototype');
31753 };
31754 });
31755 ext = Renderer;
31756 } else if (type === '__proto__' || type === 'constructor' || type === 'prototype') {
31757 // to avoid potential prototype pollution
31758 return error(type + ' is an illegal type to be registered, possibly lead to prototype pollutions');
31759 }
31760
31761 return setMap({
31762 map: extensions,
31763 keys: [type, name],
31764 value: ext
31765 });
31766}
31767
31768function getExtension(type, name) {
31769 return getMap({
31770 map: extensions,
31771 keys: [type, name]
31772 });
31773}
31774
31775function setModule(type, name, moduleType, moduleName, registrant) {
31776 return setMap({
31777 map: modules,
31778 keys: [type, name, moduleType, moduleName],
31779 value: registrant
31780 });
31781}
31782
31783function getModule(type, name, moduleType, moduleName) {
31784 return getMap({
31785 map: modules,
31786 keys: [type, name, moduleType, moduleName]
31787 });
31788}
31789
31790var extension = function extension() {
31791 // e.g. extension('renderer', 'svg')
31792 if (arguments.length === 2) {
31793 return getExtension.apply(null, arguments);
31794 } // e.g. extension('renderer', 'svg', { ... })
31795 else if (arguments.length === 3) {
31796 return setExtension.apply(null, arguments);
31797 } // e.g. extension('renderer', 'svg', 'nodeShape', 'ellipse')
31798 else if (arguments.length === 4) {
31799 return getModule.apply(null, arguments);
31800 } // e.g. extension('renderer', 'svg', 'nodeShape', 'ellipse', { ... })
31801 else if (arguments.length === 5) {
31802 return setModule.apply(null, arguments);
31803 } else {
31804 error('Invalid extension access syntax');
31805 }
31806}; // allows a core instance to access extensions internally
31807
31808
31809Core.prototype.extension = extension; // included extensions
31810
31811incExts.forEach(function (group) {
31812 group.extensions.forEach(function (ext) {
31813 setExtension(group.type, ext.name, ext.impl);
31814 });
31815});
31816
31817// (useful for init)
31818
31819var Stylesheet = function Stylesheet() {
31820 if (!(this instanceof Stylesheet)) {
31821 return new Stylesheet();
31822 }
31823
31824 this.length = 0;
31825};
31826
31827var sheetfn = Stylesheet.prototype;
31828
31829sheetfn.instanceString = function () {
31830 return 'stylesheet';
31831}; // just store the selector to be parsed later
31832
31833
31834sheetfn.selector = function (selector) {
31835 var i = this.length++;
31836 this[i] = {
31837 selector: selector,
31838 properties: []
31839 };
31840 return this; // chaining
31841}; // just store the property to be parsed later
31842
31843
31844sheetfn.css = function (name, value) {
31845 var i = this.length - 1;
31846
31847 if (string(name)) {
31848 this[i].properties.push({
31849 name: name,
31850 value: value
31851 });
31852 } else if (plainObject(name)) {
31853 var map = name;
31854 var propNames = Object.keys(map);
31855
31856 for (var j = 0; j < propNames.length; j++) {
31857 var key = propNames[j];
31858 var mapVal = map[key];
31859
31860 if (mapVal == null) {
31861 continue;
31862 }
31863
31864 var prop = Style.properties[key] || Style.properties[dash2camel(key)];
31865
31866 if (prop == null) {
31867 continue;
31868 }
31869
31870 var _name = prop.name;
31871 var _value = mapVal;
31872 this[i].properties.push({
31873 name: _name,
31874 value: _value
31875 });
31876 }
31877 }
31878
31879 return this; // chaining
31880};
31881
31882sheetfn.style = sheetfn.css; // generate a real style object from the dummy stylesheet
31883
31884sheetfn.generateStyle = function (cy) {
31885 var style = new Style(cy);
31886 return this.appendToStyle(style);
31887}; // append a dummy stylesheet object on a real style object
31888
31889
31890sheetfn.appendToStyle = function (style) {
31891 for (var i = 0; i < this.length; i++) {
31892 var context = this[i];
31893 var selector = context.selector;
31894 var props = context.properties;
31895 style.selector(selector); // apply selector
31896
31897 for (var j = 0; j < props.length; j++) {
31898 var prop = props[j];
31899 style.css(prop.name, prop.value); // apply property
31900 }
31901 }
31902
31903 return style;
31904};
31905
31906var version = "3.23.0";
31907
31908var cytoscape = function cytoscape(options) {
31909 // if no options specified, use default
31910 if (options === undefined) {
31911 options = {};
31912 } // create instance
31913
31914
31915 if (plainObject(options)) {
31916 return new Core(options);
31917 } // allow for registration of extensions
31918 else if (string(options)) {
31919 return extension.apply(extension, arguments);
31920 }
31921}; // e.g. cytoscape.use( require('cytoscape-foo'), bar )
31922
31923
31924cytoscape.use = function (ext) {
31925 var args = Array.prototype.slice.call(arguments, 1); // args to pass to ext
31926
31927 args.unshift(cytoscape); // cytoscape is first arg to ext
31928
31929 ext.apply(null, args);
31930 return this;
31931};
31932
31933cytoscape.warnings = function (bool) {
31934 return warnings(bool);
31935}; // replaced by build system
31936
31937
31938cytoscape.version = version; // expose public apis (mostly for extensions)
31939
31940cytoscape.stylesheet = cytoscape.Stylesheet = Stylesheet;
31941
31942export { cytoscape as default };